Merge branch 'js/objc-funchdr'
authorShawn O. Pearce <spearce@spearce.org>
Thu, 9 Oct 2008 18:01:51 +0000 (11:01 -0700)
committerShawn O. Pearce <spearce@spearce.org>
Thu, 9 Oct 2008 18:01:51 +0000 (11:01 -0700)
* js/objc-funchdr:
Teach git diff about Objective-C syntax

49 files changed:
Documentation/RelNotes-1.6.0.3.txt
Documentation/config.txt
Documentation/git-grep.txt
Documentation/git-prune.txt
Documentation/git-push.txt
Documentation/git-send-email.txt
Documentation/git-svn.txt
Makefile
builtin-add.c
builtin-apply.c
builtin-fmt-merge-msg.c
builtin-grep.c
builtin-init-db.c
builtin-merge-base.c
builtin-merge-file.c
builtin-merge.c
builtin-pack-objects.c
builtin-prune.c
builtin-push.c
builtin-rm.c
builtin-unpack-objects.c
cache.h
compat/cygwin.c [new file with mode: 0644]
compat/cygwin.h [new file with mode: 0644]
compat/mingw.c
compat/win32.h [new file with mode: 0644]
contrib/completion/git-completion.bash
date.c
environment.c
git-compat-util.h
git-rebase--interactive.sh
git-send-email.perl
git-stash.sh
git-svn.perl
gitweb/gitweb.css
gitweb/gitweb.perl
grep.c
grep.h
index-pack.c
merge-tree.c
read-cache.c
remote.c
sha1_file.c
t/t0001-init.sh
t/t2203-add-intent.sh [new file with mode: 0755]
t/t3600-rm.sh
t/t7600-merge.sh
t/t9128-git-svn-cmd-branch.sh [new file with mode: 0755]
xdiff-interface.c
index edd5e45c958b72f52a64471d598c60117d4df4ac..6cf8ae4ea1423d3dce3522890a666a424d163fb7 100644 (file)
@@ -53,7 +53,7 @@ Fixes since v1.6.0.2
 * Stale temporary files under $GIT_DIR/objects/pack are now cleaned up
   automatically by "git prune".
 
-* "git merge" once agrain removes directories after the last file has
+* "git merge" once again removes directories after the last file has
   been removed from it during the merge.
 
 * "git blame -C -C" no longer segfaults while trying to pass blame if
@@ -68,10 +68,10 @@ Fixes since v1.6.0.2
 * The "git commit" error message when there are still unmerged
   files present was clarified to match "git write-tree".
 
-* Some segfaults due to uncaught NULL pointers were fixed multiple
+* Some segfaults due to uncaught NULL pointers were fixed in multiple
   tools such as apply, reset, update-index.
 
-* Solaris bulds now default to OLD_ICONV=1 to avoid compile warnings.
+* Solaris builds now default to OLD_ICONV=1 to avoid compile warnings.
 
 * "Git.pm" tests relied on unnecessarily more recent version of Perl.
 
@@ -80,7 +80,7 @@ Fixes since v1.6.0.2
 * "gitweb" triggered undef warnings on missing trees.
 
 * "gitweb" now removes PATH_INFO from its URLs so users don't have
-  to manually set the url in the gitweb configuration.
+  to manually set the URL in the gitweb configuration.
 
 * Bash completion removed support for legacy "git-fetch", "git-push"
   and "git-pull" as these are no longer installed.  Dashless form
index bbe38ccaa2697f88936a8eff63a99b5c89435ac8..da18a5458e82e3d3d72e7ef1832cdb6461324ea4 100644 (file)
@@ -117,6 +117,15 @@ core.fileMode::
        the working copy are ignored; useful on broken filesystems like FAT.
        See linkgit:git-update-index[1]. True by default.
 
+core.ignoreCygwinFSTricks::
+       This option is only used by Cygwin implementation of Git. If false,
+       the Cygwin stat() and lstat() functions are used. This may be useful
+       if your repository consists of a few separate directories joined in
+       one hierarchy using Cygwin mount. If true, Git uses native Win32 API
+       whenever it is possible and falls back to Cygwin functions only to
+       handle symbol links. The native mode is more than twice faster than
+       normal Cygwin l/stat() functions. True by default.
+
 core.trustctime::
        If false, the ctime differences between the index and the
        working copy are ignored; useful when the inode change time
index fa4d133c1bccc088d3c8da65bce4e15cafd394aa..553da6cbb1777a94cb3d6b48dc77c445e18ca2b0 100644 (file)
@@ -15,6 +15,7 @@ SYNOPSIS
           [-E | --extended-regexp] [-G | --basic-regexp]
           [-F | --fixed-strings] [-n]
           [-l | --files-with-matches] [-L | --files-without-match]
+          [-z | --null]
           [-c | --count] [--all-match]
           [-A <post-context>] [-B <pre-context>] [-C <context>]
           [-f <file>] [-e] <pattern>
@@ -94,6 +95,11 @@ OPTIONS
        For better compatibility with 'git-diff', --name-only is a
        synonym for --files-with-matches.
 
+-z::
+--null::
+       Output \0 instead of the character that normally follows a
+       file name.
+
 -c::
 --count::
        Instead of showing every matched line, show the number of
index 54f1dab38de9e01d8452753ac6028875b91d5f6b..da6055d4b8cf78aff16fa553e684b0b3ed57138e 100644 (file)
@@ -8,7 +8,7 @@ git-prune - Prune all unreachable objects from the object database
 
 SYNOPSIS
 --------
-'git-prune' [-n] [--expire <expire>] [--] [<head>...]
+'git-prune' [-n] [-v] [--expire <expire>] [--] [<head>...]
 
 DESCRIPTION
 -----------
@@ -34,6 +34,9 @@ OPTIONS
        Do not remove anything; just report what it would
        remove.
 
+-v::
+       Report all removed objects.
+
 \--::
        Do not interpret any more arguments as options.
 
index 45c96435fa66ab4b1b57b6a860a2fc264321cfe4..6150b1b959e17655a2875d39ec3b70449684a0eb 100644 (file)
@@ -9,8 +9,8 @@ git-push - Update remote refs along with associated objects
 SYNOPSIS
 --------
 [verse]
-'git push' [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>]
-          [--repo=all] [-f | --force] [-v | --verbose]
+'git push' [--all | --mirror] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>]
+          [--repo=<repository>] [-f | --force] [-v | --verbose]
           [<repository> <refspec>...]
 
 DESCRIPTION
@@ -101,9 +101,23 @@ nor in any Push line of the corresponding remotes file---see below).
        This flag disables the check.  This can cause the
        remote repository to lose commits; use it with care.
 
---repo=<repo>::
-       When no repository is specified the command defaults to
-       "origin"; this overrides it.
+--repo=<repository>::
+       This option is only relevant if no <repository> argument is
+       passed in the invocation. In this case, 'git-push' derives the
+       remote name from the current branch: If it tracks a remote
+       branch, then that remote repository is pushed to. Otherwise,
+       the name "origin" is used. For this latter case, this option
+       can be used to override the name "origin". In other words,
+       the difference between these two commands
++
+--------------------------
+git push public         #1
+git push --repo=public  #2
+--------------------------
++
+is that #1 always pushes to "public" whereas #2 pushes to "public"
+only if the current branch does not track a remote branch. This is
+useful if you write an alias or script around 'git-push'.
 
 --thin::
 --no-thin::
index 3c3e1b0e77abe171ac7531b8098ea2af7d6809dd..82f505686e998d36b87bfe2c1a29031449fbdbc6 100644 (file)
@@ -11,7 +11,6 @@ SYNOPSIS
 'git send-email' [options] <file|directory> [... file|directory]
 
 
-
 DESCRIPTION
 -----------
 Takes the patches given on the command line and emails them out.
@@ -20,12 +19,16 @@ The header of the email is configurable by command line options.  If not
 specified on the command line, the user will be prompted with a ReadLine
 enabled interface to provide the necessary information.
 
+
 OPTIONS
 -------
-The options available are:
+
+Composing
+~~~~~~~~~
 
 --bcc::
-       Specify a "Bcc:" value for each email.
+       Specify a "Bcc:" value for each email. Default is the value of
+       'sendemail.bcc'.
 +
 The --bcc option must be repeated for each user you want on the bcc list.
 
@@ -34,22 +37,6 @@ The --bcc option must be repeated for each user you want on the bcc list.
 +
 The --cc option must be repeated for each user you want on the cc list.
 
---cc-cmd::
-       Specify a command to execute once per patch file which
-       should generate patch file specific "Cc:" entries.
-       Output of this command must be single email address per line.
-       Default is the value of 'sendemail.cccmd' configuration value.
-
---chain-reply-to::
---no-chain-reply-to::
-       If this is set, each email will be sent as a reply to the previous
-       email sent.  If disabled with "--no-chain-reply-to", all emails after
-       the first will be sent as replies to the first email sent.  When using
-       this, it is recommended that the first file given be an overview of the
-       entire patch series.
-       Default is the value of the 'sendemail.chainreplyto' configuration
-       value; if that is unspecified, default to --chain-reply-to.
-
 --compose::
        Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
        introductory message for the patch series.
@@ -66,22 +53,47 @@ The --cc option must be repeated for each user you want on the cc list.
        Only necessary if --compose is also set.  If --compose
        is not set, this will be prompted for.
 
---signed-off-by-cc::
---no-signed-off-by-cc::
-        If this is set, add emails found in Signed-off-by: or Cc: lines to the
-        cc list.
-        Default is the value of 'sendemail.signedoffcc' configuration value;
-        if that is unspecified, default to --signed-off-by-cc.
+--subject::
+       Specify the initial subject of the email thread.
+       Only necessary if --compose is also set.  If --compose
+       is not set, this will be prompted for.
+
+--to::
+       Specify the primary recipient of the emails generated. Generally, this
+       will be the upstream maintainer of the project involved. Default is the
+       value of the 'sendemail.to' configuration value; if that is unspecified,
+       this will be prompted for.
++
+The --to option must be repeated for each user you want on the to list.
 
---quiet::
-       Make git-send-email less verbose.  One line per email should be
-       all that is output.
 
---identity::
-       A configuration identity. When given, causes values in the
-       'sendemail.<identity>' subsection to take precedence over
-       values in the 'sendemail' section. The default identity is
-       the value of 'sendemail.identity'.
+Sending
+~~~~~~~
+
+--envelope-sender::
+       Specify the envelope sender used to send the emails.
+       This is useful if your default address is not the address that is
+       subscribed to a list. If you use the sendmail binary, you must have
+       suitable privileges for the -f parameter. Default is the value of
+       the 'sendemail.envelopesender' configuration variable; if that is
+       unspecified, choosing the envelope sender is left to your MTA.
+
+--smtp-encryption::
+       Specify the encryption to use, either 'ssl' or 'tls'.  Any other
+       value reverts to plain SMTP.  Default is the value of
+       'sendemail.smtpencryption'.
+
+--smtp-pass::
+       Password for SMTP-AUTH. The argument is optional: If no
+       argument is specified, then the empty string is used as
+       the password. Default is the value of 'sendemail.smtppass',
+       however '--smtp-pass' always overrides this value.
++
+Furthermore, passwords need not be specified in configuration files
+or on the command line. If a username has been specified (with
+'--smtp-user' or a 'sendemail.smtpuser'), but no password has been
+specified (with '--smtp-pass' or 'sendemail.smtppass'), then the
+user is prompted for a password while the input is masked for privacy.
 
 --smtp-server::
        If set, specifies the outgoing SMTP server to use (e.g.
@@ -96,61 +108,44 @@ The --cc option must be repeated for each user you want on the cc list.
 --smtp-server-port::
        Specifies a port different from the default port (SMTP
        servers typically listen to smtp port 25 and ssmtp port
-       465).
+       465). This can be set with 'sendemail.smtpserverport'.
+
+--smtp-ssl::
+       Legacy alias for '--smtp-encryption ssl'.
 
 --smtp-user::
-       Username for SMTP-AUTH. In place of this option, the following
-       configuration variables can be specified:
-+
---
-               * sendemail.smtpuser
-               * sendemail.<identity>.smtpuser (see sendemail.identity).
---
-+
-However, --smtp-user always overrides these variables.
-+
-If a username is not specified (with --smtp-user or a
-configuration variable), then authentication is not attempted.
+       Username for SMTP-AUTH. Default is the value of 'sendemail.smtpuser';
+       if a username is not specified (with '--smtp-user' or 'sendemail.smtpuser'),
+       then authentication is not attempted.
 
---smtp-pass::
-       Password for SMTP-AUTH. The argument is optional: If no
-       argument is specified, then the empty string is used as
-       the password.
-+
-In place of this option, the following configuration variables
-can be specified:
-+
---
-               * sendemail.smtppass
-               * sendemail.<identity>.smtppass (see sendemail.identity).
---
-+
-However, --smtp-pass always overrides these variables.
-+
-Furthermore, passwords need not be specified in configuration files
-or on the command line. If a username has been specified (with
---smtp-user or a configuration variable), but no password has been
-specified (with --smtp-pass or a configuration variable), then the
-user is prompted for a password while the input is masked for privacy.
 
---smtp-encryption::
-       Specify the encryption to use, either 'ssl' or 'tls'.  Any other
-       value reverts to plain SMTP.  Default is the value of
-       'sendemail.smtpencryption'.
+Automating
+~~~~~~~~~~
 
---smtp-ssl::
-       Legacy alias for '--smtp-encryption=ssl'.
+--cc-cmd::
+       Specify a command to execute once per patch file which
+       should generate patch file specific "Cc:" entries.
+       Output of this command must be single email address per line.
+       Default is the value of 'sendemail.cccmd' configuration value.
 
---subject::
-       Specify the initial subject of the email thread.
-       Only necessary if --compose is also set.  If --compose
-       is not set, this will be prompted for.
+--[no-]chain-reply-to::
+       If this is set, each email will be sent as a reply to the previous
+       email sent.  If disabled with "--no-chain-reply-to", all emails after
+       the first will be sent as replies to the first email sent.  When using
+       this, it is recommended that the first file given be an overview of the
+       entire patch series. Default is the value of the 'sendemail.chainreplyto'
+       configuration value; if that is unspecified, default to --chain-reply-to.
+
+--identity::
+       A configuration identity. When given, causes values in the
+       'sendemail.<identity>' subsection to take precedence over
+       values in the 'sendemail' section. The default identity is
+       the value of 'sendemail.identity'.
 
---suppress-from::
---no-suppress-from::
-        If this is set, do not add the From: address to the cc: list.
-        Default is the value of 'sendemail.suppressfrom' configuration value;
-        if that is unspecified, default to --no-suppress-from.
+--[no-]signed-off-by-cc::
+       If this is set, add emails found in Signed-off-by: or Cc: lines to the
+       cc list. Default is the value of 'sendemail.signedoffbycc' configuration
+       value; if that is unspecified, default to --signed-off-by-cc.
 
 --suppress-cc::
        Specify an additional category of recipients to suppress the
@@ -163,44 +158,43 @@ user is prompted for a password while the input is masked for privacy.
        if that is unspecified, default to 'self' if --suppress-from is
        specified, as well as 'sob' if --no-signed-off-cc is specified.
 
---thread::
---no-thread::
+--[no-]suppress-from::
+       If this is set, do not add the From: address to the cc: list.
+       Default is the value of 'sendemail.suppressfrom' configuration
+       value; if that is unspecified, default to --no-suppress-from.
+
+--[no-]thread::
        If this is set, the In-Reply-To header will be set on each email sent.
        If disabled with "--no-thread", no emails will have the In-Reply-To
-       header set.
-       Default is the value of the 'sendemail.thread' configuration value;
-       if that is unspecified, default to --thread.
+       header set. Default is the value of the 'sendemail.thread' configuration
+       value; if that is unspecified, default to --thread.
+
+
+Administering
+~~~~~~~~~~~~~
 
 --dry-run::
        Do everything except actually send the emails.
 
---envelope-sender::
-       Specify the envelope sender used to send the emails.
-       This is useful if your default address is not the address that is
-       subscribed to a list. If you use the sendmail binary, you must have
-       suitable privileges for the -f parameter.
-       Default is the value of the 'sendemail.envelopesender' configuration
-       variable; if that is unspecified, choosing the envelope sender is left
-       to your MTA.
+--quiet::
+       Make git-send-email less verbose.  One line per email should be
+       all that is output.
 
---to::
-       Specify the primary recipient of the emails generated.
-       Generally, this will be the upstream maintainer of the
-       project involved.
-       Default is the value of the 'sendemail.to' configuration value;
-       if that is unspecified, this will be prompted for.
+--[no-]validate::
+       Perform sanity checks on patches.
+       Currently, validation means the following:
 +
-The --to option must be repeated for each user you want on the to list.
+--
+               *       Warn of patches that contain lines longer than 998 characters; this
+                       is due to SMTP limits as described by http://www.ietf.org/rfc/rfc2821.txt.
+--
++
+Default is the value of 'sendemail.validate'; if this is not set,
+default to '--validate'.
 
 
 CONFIGURATION
 -------------
-sendemail.identity::
-       The default configuration identity. When specified,
-       'sendemail.<identity>.<item>' will have higher precedence than
-       'sendemail.<item>'. This is useful to declare multiple SMTP
-       identities and to hoist sensitive authentication information
-       out of the repository and into the global configuration file.
 
 sendemail.aliasesfile::
        To avoid typing long email addresses, point this to one or more
@@ -210,38 +204,6 @@ sendemail.aliasfiletype::
        Format of the file(s) specified in sendemail.aliasesfile. Must be
        one of 'mutt', 'mailrc', 'pine', or 'gnus'.
 
-sendemail.to::
-       Email address (or alias) to always send to.
-
-sendemail.cccmd::
-       Command to execute to generate per patch file specific "Cc:"s.
-
-sendemail.bcc::
-       Email address (or alias) to always bcc.
-
-sendemail.chainreplyto::
-       Boolean value specifying the default to the '--chain_reply_to'
-       parameter.
-
-sendemail.smtpserver::
-       Default SMTP server to use.
-
-sendemail.smtpserverport::
-       Default SMTP server port to use.
-
-sendemail.smtpuser::
-       Default SMTP-AUTH username.
-
-sendemail.smtppass::
-       Default SMTP-AUTH password.
-
-sendemail.smtpencryption::
-       Default encryption method.  Use 'ssl' for SSL (and specify an
-       appropriate port), or 'tls' for TLS.  Takes precedence over
-       'smtpssl' if both are specified.
-
-sendemail.smtpssl::
-       Legacy boolean that sets 'smtpencryption=ssl' if enabled.
 
 Author
 ------
@@ -250,10 +212,12 @@ Written by Ryan Anderson <ryan@michonline.com>
 git-send-email is originally based upon
 send_lots_of_email.pl by Greg Kroah-Hartman.
 
+
 Documentation
 --------------
 Documentation by Ryan Anderson
 
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index 82d03b4ced9cb7e34feca325d0ac7b41923de976..84c8f3cde0b781ef1579b38a17430dc405115190 100644 (file)
@@ -149,6 +149,22 @@ and have no uncommitted changes.
        is very strongly discouraged.
 --
 
+'branch'::
+       Create a branch in the SVN repository.
+
+-m;;
+--message;;
+       Allows to specify the commit message.
+
+-t;;
+--tag;;
+       Create a tag by using the tags_subdir instead of the branches_subdir
+       specified during git svn init.
+
+'tag'::
+       Create a tag in the SVN repository. This is a shorthand for
+       'branch -t'.
+
 'log'::
        This should make it easy to look up svn log messages when svn
        users refer to -r/--revision numbers.
@@ -372,7 +388,8 @@ Passed directly to 'git-rebase' when using 'dcommit' if a
 -n::
 --dry-run::
 
-This can be used with the 'dcommit' and 'rebase' commands.
+This can be used with the 'dcommit', 'rebase', 'branch' and 'tag'
+commands.
 
 For 'dcommit', print out the series of git arguments that would show
 which diffs would be committed to SVN.
@@ -381,6 +398,9 @@ For 'rebase', display the local branch associated with the upstream svn
 repository associated with the current branch and the URL of svn
 repository that will be fetched from.
 
+For 'branch' and 'tag', display the urls that will be used for copying when
+creating the branch or tag.
+
 --
 
 ADVANCED OPTIONS
@@ -498,6 +518,8 @@ Tracking and contributing to an entire Subversion-managed project
        git svn clone http://svn.example.com/project -T trunk -b branches -t tags
 # View all branches and tags you have cloned:
        git branch -r
+# Create a new branch in SVN
+    git svn branch waldo
 # Reset your master to trunk (or any other branch, replacing 'trunk'
 # with the appropriate name):
        git reset --hard remotes/trunk
index 15ca6599a7b665d5d4888c00b0fecd7bbde28879..308dc70b5de118083a14bcb2665372a40ae6522c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -346,6 +346,7 @@ LIB_H += cache.h
 LIB_H += cache-tree.h
 LIB_H += commit.h
 LIB_H += compat/mingw.h
+LIB_H += compat/cygwin.h
 LIB_H += csum-file.h
 LIB_H += decorate.h
 LIB_H += delta.h
@@ -650,7 +651,6 @@ ifeq ($(uname_S),SunOS)
        NO_MKDTEMP = YesPlease
        OLD_ICONV = UnfortunatelyYes
        ifeq ($(uname_R),5.8)
-               NEEDS_LIBICONV = YesPlease
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
                NO_C99_FORMAT = YesPlease
@@ -749,6 +749,9 @@ ifeq ($(uname_S),HP-UX)
        NO_SYS_SELECT_H = YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
 endif
+ifneq (,$(findstring CYGWIN,$(uname_S)))
+       COMPAT_OBJS += compat/cygwin.o
+endif
 ifneq (,$(findstring MINGW,$(uname_S)))
        NO_MMAP = YesPlease
        NO_PREAD = YesPlease
index 7c874e31154a4c3f96b3403db1bdcb0b36fdec3e..ea4e77169a782ef38ed1c0524952a7220cbf739d 100644 (file)
@@ -166,7 +166,7 @@ static const char ignore_error[] =
 "The following paths are ignored by one of your .gitignore files:\n";
 
 static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
-static int ignore_add_errors, addremove;
+static int ignore_add_errors, addremove, intent_to_add;
 
 static struct option builtin_add_options[] = {
        OPT__DRY_RUN(&show_only),
@@ -176,6 +176,7 @@ static struct option builtin_add_options[] = {
        OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"),
        OPT_BOOLEAN('f', "force", &ignored_too, "allow adding otherwise ignored files"),
        OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"),
+       OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"),
        OPT_BOOLEAN('A', "all", &addremove, "add all, noticing removal of tracked files"),
        OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
        OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
@@ -246,6 +247,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
        flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
                 (show_only ? ADD_CACHE_PRETEND : 0) |
+                (intent_to_add ? ADD_CACHE_INTENT : 0) |
                 (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
                 (!(addremove || take_worktree_changes)
                  ? ADD_CACHE_IGNORE_REMOVAL : 0));
index 342f2fe5e6476db7cb7b4647aea45a42d7ac7642..bf806105062caa47ca168720e0e987ac8957c8af 100644 (file)
@@ -1697,7 +1697,7 @@ static int match_fragment(struct image *img,
                fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
 
                /* Try fixing the line in the target */
-               if (sizeof(tgtfixbuf) < tgtlen)
+               if (sizeof(tgtfixbuf) > tgtlen)
                        tgtfix = tgtfixbuf;
                else
                        tgtfix = xmalloc(tgtlen);
index df02ba7afdd615492361a1897a9dedd6ab233c96..c6324dc795bfd778470fbbe7b0720ce9ff83a34e 100644 (file)
@@ -5,8 +5,10 @@
 #include "revision.h"
 #include "tag.h"
 
-static const char *fmt_merge_msg_usage =
-       "git fmt-merge-msg [--log] [--no-log] [--file <file>]";
+static const char * const fmt_merge_msg_usage[] = {
+       "git fmt-merge-msg [--log|--no-log] [--file <file>]",
+       NULL
+};
 
 static int merge_summary;
 
@@ -347,38 +349,29 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
 
 int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
 {
+       const char *inpath = NULL;
+       struct option options[] = {
+               OPT_BOOLEAN(0, "log",     &merge_summary, "populate log with the shortlog"),
+               OPT_BOOLEAN(0, "summary", &merge_summary, "alias for --log"),
+               OPT_STRING('F', "file",   &inpath, "file", "file to read from"),
+               OPT_END()
+       };
+
        FILE *in = stdin;
        struct strbuf input, output;
        int ret;
 
        git_config(fmt_merge_msg_config, NULL);
-
-       while (argc > 1) {
-               if (!strcmp(argv[1], "--log") || !strcmp(argv[1], "--summary"))
-                       merge_summary = 1;
-               else if (!strcmp(argv[1], "--no-log")
-                               || !strcmp(argv[1], "--no-summary"))
-                       merge_summary = 0;
-               else if (!strcmp(argv[1], "-F") || !strcmp(argv[1], "--file")) {
-                       if (argc < 3)
-                               die ("Which file?");
-                       if (!strcmp(argv[2], "-"))
-                               in = stdin;
-                       else {
-                               fclose(in);
-                               in = fopen(argv[2], "r");
-                               if (!in)
-                                       die("cannot open %s", argv[2]);
-                       }
-                       argc--; argv++;
-               } else
-                       break;
-               argc--; argv++;
+       argc = parse_options(argc, argv, options, fmt_merge_msg_usage, 0);
+       if (argc > 0)
+               usage_with_options(fmt_merge_msg_usage, options);
+
+       if (inpath && strcmp(inpath, "-")) {
+               in = fopen(inpath, "r");
+               if (!in)
+                       die("cannot open %s", inpath);
        }
 
-       if (argc > 1)
-               usage(fmt_merge_msg_usage);
-
        strbuf_init(&input, 0);
        if (strbuf_read(&input, fileno(in), 0) < 0)
                die("could not read input file %s", strerror(errno));
@@ -387,6 +380,6 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
        ret = fmt_merge_msg(merge_summary, &input, &output);
        if (ret)
                return ret;
-       printf("%s", output.buf);
+       write_in_full(STDOUT_FILENO, output.buf, output.len);
        return 0;
 }
index 3a51662a35878ae6b5965c90abb747b5b27c5493..624f86e287cf6304d122850f8258444c5f916702 100644 (file)
@@ -295,6 +295,9 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
                push_arg("-l");
        if (opt->unmatch_name_only)
                push_arg("-L");
+       if (opt->null_following_name)
+               /* in GNU grep git's "-z" translates to "-Z" */
+               push_arg("-Z");
        if (opt->count)
                push_arg("-c");
        if (opt->post_context || opt->pre_context) {
@@ -599,6 +602,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        opt.unmatch_name_only = 1;
                        continue;
                }
+               if (!strcmp("-z", arg) ||
+                   !strcmp("--null", arg)) {
+                       opt.null_following_name = 1;
+                       continue;
+               }
                if (!strcmp("-c", arg) ||
                    !strcmp("--count", arg)) {
                        opt.count = 1;
index 8140c1299afe368266de1153f3bb891f000fe5f5..d30c3fe2ca542b061ab8b7a7696cdd5416e17147 100644 (file)
@@ -17,6 +17,9 @@
 #define TEST_FILEMODE 1
 #endif
 
+static int init_is_bare_repository = 0;
+static int init_shared_repository = -1;
+
 static void safe_create_dir(const char *dir, int share)
 {
        if (mkdir(dir, 0777) < 0) {
@@ -191,6 +194,9 @@ static int create_default_files(const char *template_path)
        copy_templates(template_path);
 
        git_config(git_default_config, NULL);
+       is_bare_repository_cfg = init_is_bare_repository;
+       if (init_shared_repository != -1)
+               shared_repository = init_shared_repository;
 
        /*
         * We would have created the above under user's umask -- under
@@ -277,6 +283,8 @@ int init_db(const char *template_dir, unsigned int flags)
 
        safe_create_dir(get_git_dir(), 0);
 
+       init_is_bare_repository = is_bare_repository();
+
        /* Check to see if the repository version is right.
         * Note that a newly created repository does not have
         * config file, so this will not fail.  What we are catching
@@ -381,9 +389,9 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                        setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir,
                                                sizeof(git_dir)), 0);
                } else if (!strcmp(arg, "--shared"))
-                       shared_repository = PERM_GROUP;
+                       init_shared_repository = PERM_GROUP;
                else if (!prefixcmp(arg, "--shared="))
-                       shared_repository = git_config_perm("arg", arg+9);
+                       init_shared_repository = git_config_perm("arg", arg+9);
                else if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet"))
                        flags |= INIT_DB_QUIET;
                else
index b08da516e491e7089b1bb178e9a4b05c2ab36539..03fc1c211453f1ed09ee2c6b71d438b0bfbf474f 100644 (file)
@@ -1,6 +1,7 @@
 #include "builtin.h"
 #include "cache.h"
 #include "commit.h"
+#include "parse-options.h"
 
 static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
 {
@@ -21,8 +22,10 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
        return 0;
 }
 
-static const char merge_base_usage[] =
-"git merge-base [--all] <commit-id> <commit-id>...";
+static const char * const merge_base_usage[] = {
+       "git merge-base [--all] <commit-id> <commit-id>...",
+       NULL
+};
 
 static struct commit *get_commit_reference(const char *arg)
 {
@@ -44,25 +47,17 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)
        int rev_nr = 0;
        int show_all = 0;
 
-       git_config(git_default_config, NULL);
-
-       while (1 < argc && argv[1][0] == '-') {
-               const char *arg = argv[1];
-               if (!strcmp(arg, "-a") || !strcmp(arg, "--all"))
-                       show_all = 1;
-               else
-                       usage(merge_base_usage);
-               argc--; argv++;
-       }
-       if (argc < 3)
-               usage(merge_base_usage);
-
-       rev = xmalloc((argc - 1) * sizeof(*rev));
-
-       do {
-               rev[rev_nr++] = get_commit_reference(argv[1]);
-               argc--; argv++;
-       } while (argc > 1);
+       struct option options[] = {
+               OPT_BOOLEAN('a', "all", &show_all, "outputs all common ancestors"),
+               OPT_END()
+       };
 
+       git_config(git_default_config, NULL);
+       argc = parse_options(argc, argv, options, merge_base_usage, 0);
+       if (argc < 2)
+               usage_with_options(merge_base_usage, options);
+       rev = xmalloc(argc * sizeof(*rev));
+       while (argc-- > 0)
+               rev[rev_nr++] = get_commit_reference(*argv++);
        return show_merge_base(rev, rev_nr, show_all);
 }
index 45c98538cdbf35352f7a3b5adf40fca9d7512ea5..9d4e874809f495ad685a7218584041fa001696e5 100644 (file)
@@ -2,21 +2,44 @@
 #include "cache.h"
 #include "xdiff/xdiff.h"
 #include "xdiff-interface.h"
+#include "parse-options.h"
 
-static const char merge_file_usage[] =
-"git merge-file [-p | --stdout] [--diff3] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2";
+static const char *const merge_file_usage[] = {
+       "git merge-file [options] [-L name1 [-L orig [-L name2]]] file1 orig_file file2",
+       NULL
+};
+
+static int label_cb(const struct option *opt, const char *arg, int unset)
+{
+       static int label_count = 0;
+       const char **names = (const char **)opt->value;
+
+       if (label_count >= 3)
+               return error("too many labels on the command line");
+       names[label_count++] = arg;
+       return 0;
+}
 
 int cmd_merge_file(int argc, const char **argv, const char *prefix)
 {
-       const char *names[3];
+       const char *names[3] = { NULL, NULL, NULL };
        mmfile_t mmfs[3];
        mmbuffer_t result = {NULL, 0};
        xpparam_t xpp = {XDF_NEED_MINIMAL};
        int ret = 0, i = 0, to_stdout = 0;
        int merge_level = XDL_MERGE_ZEALOUS_ALNUM;
-       int merge_style = 0;
+       int merge_style = 0, quiet = 0;
        int nongit;
 
+       struct option options[] = {
+               OPT_BOOLEAN('p', "stdout", &to_stdout, "send results to standard output"),
+               OPT_SET_INT(0, "diff3", &merge_style, "use a diff3 based merge", XDL_MERGE_DIFF3),
+               OPT__QUIET(&quiet),
+               OPT_CALLBACK('L', NULL, names, "name",
+                            "set labels for file1/orig_file/file2", &label_cb),
+               OPT_END(),
+       };
+
        prefix = setup_git_directory_gently(&nongit);
        if (!nongit) {
                /* Read the configuration file */
@@ -25,37 +48,20 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
                        merge_style = git_xmerge_style;
        }
 
-       while (argc > 4) {
-               if (!strcmp(argv[1], "-L") && i < 3) {
-                       names[i++] = argv[2];
-                       argc--;
-                       argv++;
-               } else if (!strcmp(argv[1], "-p") ||
-                               !strcmp(argv[1], "--stdout"))
-                       to_stdout = 1;
-               else if (!strcmp(argv[1], "-q") ||
-                               !strcmp(argv[1], "--quiet"))
-                       freopen("/dev/null", "w", stderr);
-               else if (!strcmp(argv[1], "--diff3"))
-                       merge_style = XDL_MERGE_DIFF3;
-               else
-                       usage(merge_file_usage);
-               argc--;
-               argv++;
-       }
-
-       if (argc != 4)
-               usage(merge_file_usage);
-
-       for (; i < 3; i++)
-               names[i] = argv[i + 1];
+       argc = parse_options(argc, argv, options, merge_file_usage, 0);
+       if (argc != 3)
+               usage_with_options(merge_file_usage, options);
+       if (quiet)
+               freopen("/dev/null", "w", stderr);
 
        for (i = 0; i < 3; i++) {
-               if (read_mmfile(mmfs + i, argv[i + 1]))
+               if (!names[i])
+                       names[i] = argv[i];
+               if (read_mmfile(mmfs + i, argv[i]))
                        return -1;
                if (buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
                        return error("Cannot merge binary files: %s\n",
-                                       argv[i + 1]);
+                                       argv[i]);
        }
 
        ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
@@ -65,7 +71,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
                free(mmfs[i].ptr);
 
        if (ret >= 0) {
-               const char *filename = argv[1];
+               const char *filename = argv[0];
                FILE *f = to_stdout ? stdout : fopen(filename, "wb");
 
                if (!f)
index 5c65a5869900ad1a1014fb3aad88c874a5410bf7..38266baf5fe9f54a0006baaae205b55f12ed15fc 100644 (file)
@@ -123,8 +123,7 @@ static struct strategy *get_strategy(const char *name)
                exit(1);
        }
 
-       ret = xmalloc(sizeof(struct strategy));
-       memset(ret, 0, sizeof(struct strategy));
+       ret = xcalloc(1, sizeof(struct strategy));
        ret->name = xstrdup(name);
        return ret;
 }
@@ -547,6 +546,16 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
        int i = 0, ret;
        struct commit_list *j;
        struct strbuf buf;
+       int index_fd;
+       struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+
+       index_fd = hold_locked_index(lock, 1);
+       refresh_cache(REFRESH_QUIET);
+       if (active_cache_changed &&
+                       (write_cache(index_fd, active_cache, active_nr) ||
+                        commit_locked_index(lock)))
+               return error("Unable to write index.");
+       rollback_lock_file(lock);
 
        if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
                int clean;
@@ -723,12 +732,12 @@ static void add_strategies(const char *string, unsigned attr)
 static int merge_trivial(void)
 {
        unsigned char result_tree[20], result_commit[20];
-       struct commit_list *parent = xmalloc(sizeof(struct commit_list *));
+       struct commit_list *parent = xmalloc(sizeof(*parent));
 
        write_tree_trivial(result_tree);
        printf("Wonderful.\n");
        parent->item = lookup_commit(head);
-       parent->next = xmalloc(sizeof(struct commit_list *));
+       parent->next = xmalloc(sizeof(*parent->next));
        parent->next->item = remoteheads->item;
        parent->next->next = NULL;
        commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL);
index 1158e42cba81e2bdb8640cd6c5a3835453d17e7f..59c30d1caa37416041177ff4aaf01b67f4e8add4 100644 (file)
@@ -1369,12 +1369,10 @@ static void find_deltas(struct object_entry **list, unsigned *list_size,
                        int window, int depth, unsigned *processed)
 {
        uint32_t i, idx = 0, count = 0;
-       unsigned int array_size = window * sizeof(struct unpacked);
        struct unpacked *array;
        unsigned long mem_usage = 0;
 
-       array = xmalloc(array_size);
-       memset(array, 0, array_size);
+       array = xcalloc(window, sizeof(struct unpacked));
 
        for (;;) {
                struct object_entry *entry = *list++;
index 1663f8bdb1e27713bab6cf7a16ca15cfcfc1abef..7b4ec80e62997fe70f53c92380cd4d66e80d62a0 100644 (file)
@@ -7,10 +7,11 @@
 #include "parse-options.h"
 
 static const char * const prune_usage[] = {
-       "git prune [-n] [--expire <time>] [--] [<head>...]",
+       "git prune [-n] [-v] [--expire <time>] [--] [<head>...]",
        NULL
 };
 static int show_only;
+static int verbose;
 static unsigned long expire;
 
 static int prune_tmp_object(const char *path, const char *filename)
@@ -39,11 +40,12 @@ static int prune_object(char *path, const char *filename, const unsigned char *s
                if (st.st_mtime > expire)
                        return 0;
        }
-       if (show_only) {
+       if (show_only || verbose) {
                enum object_type type = sha1_object_info(sha1, NULL);
                printf("%s %s\n", sha1_to_hex(sha1),
                       (type > 0) ? typename(type) : "unknown");
-       } else
+       }
+       if (!show_only)
                unlink(fullpath);
        return 0;
 }
@@ -135,6 +137,8 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
        const struct option options[] = {
                OPT_BOOLEAN('n', NULL, &show_only,
                            "do not remove, show only"),
+               OPT_BOOLEAN('v', NULL, &verbose,
+                       "report pruned objects"),
                OPT_DATE(0, "expire", &expire,
                         "expire objects older than <time>"),
                OPT_END()
index cc6666f75e7db8a546d4a1589335589190e414fe..122fdcfbdc13a11388a091e5ec33d077648fab0a 100644 (file)
@@ -10,7 +10,7 @@
 #include "parse-options.h"
 
 static const char * const push_usage[] = {
-       "git push [--all | --mirror] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]",
+       "git push [--all | --mirror] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=<repository>] [-f | --force] [-v] [<repository> <refspec>...]",
        NULL,
 };
 
index 50ae6d54011fad89387f0d56ce7a4591d07874f3..e06640cf8d3418cbbe177b8fdcdccd19e0f3379f 100644 (file)
@@ -137,6 +137,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 
        if (read_cache() < 0)
                die("index file corrupt");
+       refresh_cache(REFRESH_QUIET);
 
        pathspec = get_pathspec(prefix, argv);
        seen = NULL;
index d2796b6309d3b13a6c401b502aafd53d684328d8..9f4bdd3296d19b75211ca0f2434d227eafac44b6 100644 (file)
@@ -477,8 +477,7 @@ static void unpack_all(void)
 
        if (!quiet)
                progress = start_progress("Unpacking objects", nr_objects);
-       obj_list = xmalloc(nr_objects * sizeof(*obj_list));
-       memset(obj_list, 0, nr_objects * sizeof(*obj_list));
+       obj_list = xcalloc(nr_objects, sizeof(*obj_list));
        for (i = 0; i < nr_objects; i++) {
                unpack_one(i);
                display_progress(progress, i + 1);
diff --git a/cache.h b/cache.h
index 9f4e5c0d459dcf73c86ef977469ba4885cc8f4c2..991544cf0bd7e84c5db5bd6486e5922a53ec136f 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -319,6 +319,7 @@ extern int is_bare_repository(void);
 extern int is_inside_git_dir(void);
 extern char *git_work_tree_cfg;
 extern int is_inside_work_tree(void);
+extern int have_git_dir(void);
 extern const char *get_git_dir(void);
 extern char *get_object_directory(void);
 extern char *get_index_file(void);
@@ -377,6 +378,7 @@ extern int index_name_pos(const struct index_state *, const char *name, int name
 #define ADD_CACHE_OK_TO_REPLACE 2      /* Ok to replace file/directory */
 #define ADD_CACHE_SKIP_DFCHECK 4       /* Ok to skip DF conflict checks */
 #define ADD_CACHE_JUST_APPEND 8                /* Append only; tree.c::read_tree() */
+#define ADD_CACHE_NEW_ONLY 16          /* Do not replace existing ones */
 extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
 extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
 extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
@@ -386,6 +388,7 @@ extern int remove_file_from_index(struct index_state *, const char *path);
 #define ADD_CACHE_PRETEND 2
 #define ADD_CACHE_IGNORE_ERRORS        4
 #define ADD_CACHE_IGNORE_REMOVAL 8
+#define ADD_CACHE_INTENT 16
 extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
 extern int add_file_to_index(struct index_state *, const char *path, int flags);
 extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
diff --git a/compat/cygwin.c b/compat/cygwin.c
new file mode 100644 (file)
index 0000000..423ff20
--- /dev/null
@@ -0,0 +1,127 @@
+#define WIN32_LEAN_AND_MEAN
+#include "../git-compat-util.h"
+#include "win32.h"
+#include "../cache.h" /* to read configuration */
+
+static inline void filetime_to_timespec(const FILETIME *ft, struct timespec *ts)
+{
+       long long winTime = ((long long)ft->dwHighDateTime << 32) +
+                       ft->dwLowDateTime;
+       winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */
+       /* convert 100-nsecond interval to seconds and nanoseconds */
+       ts->tv_sec = (time_t)(winTime/10000000);
+       ts->tv_nsec = (long)(winTime - ts->tv_sec*10000000LL) * 100;
+}
+
+#define size_to_blocks(s) (((s)+511)/512)
+
+/* do_stat is a common implementation for cygwin_lstat and cygwin_stat.
+ *
+ * To simplify its logic, in the case of cygwin symlinks, this implementation
+ * falls back to the cygwin version of stat/lstat, which is provided as the
+ * last argument.
+ */
+static int do_stat(const char *file_name, struct stat *buf, stat_fn_t cygstat)
+{
+       WIN32_FILE_ATTRIBUTE_DATA fdata;
+
+       if (file_name[0] == '/')
+               return cygstat (file_name, buf);
+
+       if (!(errno = get_file_attr(file_name, &fdata))) {
+               /*
+                * If the system attribute is set and it is not a directory then
+                * it could be a symbol link created in the nowinsymlinks mode.
+                * Normally, Cygwin works in the winsymlinks mode, so this situation
+                * is very unlikely. For the sake of simplicity of our code, let's
+                * Cygwin to handle it.
+                */
+               if ((fdata.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) &&
+                   !(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+                       return cygstat(file_name, buf);
+
+               /* fill out the stat structure */
+               buf->st_dev = buf->st_rdev = 0; /* not used by Git */
+               buf->st_ino = 0;
+               buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
+               buf->st_nlink = 1;
+               buf->st_uid = buf->st_gid = 0;
+#ifdef __CYGWIN_USE_BIG_TYPES__
+               buf->st_size = ((_off64_t)fdata.nFileSizeHigh << 32) +
+                       fdata.nFileSizeLow;
+#else
+               buf->st_size = (off_t)fdata.nFileSizeLow;
+#endif
+               buf->st_blocks = size_to_blocks(buf->st_size);
+               filetime_to_timespec(&fdata.ftLastAccessTime, &buf->st_atim);
+               filetime_to_timespec(&fdata.ftLastWriteTime, &buf->st_mtim);
+               filetime_to_timespec(&fdata.ftCreationTime, &buf->st_ctim);
+               return 0;
+       } else if (errno == ENOENT) {
+               /*
+                * In the winsymlinks mode (which is the default), Cygwin
+                * emulates symbol links using Windows shortcut files. These
+                * files are formed by adding .lnk extension. So, if we have
+                * not found the specified file name, it could be that it is
+                * a symbol link. Let's Cygwin to deal with that.
+                */
+               return cygstat(file_name, buf);
+       }
+       return -1;
+}
+
+/* We provide our own lstat/stat functions, since the provided Cygwin versions
+ * of these functions are too slow. These stat functions are tailored for Git's
+ * usage, and therefore they are not meant to be complete and correct emulation
+ * of lstat/stat functionality.
+ */
+static int cygwin_lstat(const char *path, struct stat *buf)
+{
+       return do_stat(path, buf, lstat);
+}
+
+static int cygwin_stat(const char *path, struct stat *buf)
+{
+       return do_stat(path, buf, stat);
+}
+
+
+/*
+ * At start up, we are trying to determine whether Win32 API or cygwin stat
+ * functions should be used. The choice is determined by core.ignorecygwinfstricks.
+ * Reading this option is not always possible immediately as git_dir may be
+ * not be set yet. So until it is set, use cygwin lstat/stat functions.
+ */
+static int native_stat = 1;
+
+static int git_cygwin_config(const char *var, const char *value, void *cb)
+{
+       if (!strcmp(var, "core.ignorecygwinfstricks"))
+               native_stat = git_config_bool(var, value);
+       return 0;
+}
+
+static int init_stat(void)
+{
+       if (have_git_dir()) {
+               git_config(git_cygwin_config, NULL);
+               cygwin_stat_fn = native_stat ? cygwin_stat : stat;
+               cygwin_lstat_fn = native_stat ? cygwin_lstat : lstat;
+               return 0;
+       }
+       return 1;
+}
+
+static int cygwin_stat_stub(const char *file_name, struct stat *buf)
+{
+       return (init_stat() ? stat : *cygwin_stat_fn)(file_name, buf);
+}
+
+static int cygwin_lstat_stub(const char *file_name, struct stat *buf)
+{
+       return (init_stat() ? lstat : *cygwin_lstat_fn)(file_name, buf);
+}
+
+stat_fn_t cygwin_stat_fn = cygwin_stat_stub;
+stat_fn_t cygwin_lstat_fn = cygwin_lstat_stub;
+
diff --git a/compat/cygwin.h b/compat/cygwin.h
new file mode 100644 (file)
index 0000000..a3229f5
--- /dev/null
@@ -0,0 +1,9 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+
+typedef int (*stat_fn_t)(const char*, struct stat*);
+extern stat_fn_t cygwin_stat_fn;
+extern stat_fn_t cygwin_lstat_fn;
+
+#define stat(path, buf) (*cygwin_stat_fn)(path, buf)
+#define lstat(path, buf) (*cygwin_lstat_fn)(path, buf)
index fc45d240f579ab3e1e0f867859f24132c593410e..09858f6c593d9a8301c857c81e09bfa24b955eac 100644 (file)
@@ -1,4 +1,5 @@
 #include "../git-compat-util.h"
+#include "win32.h"
 #include "../strbuf.h"
 
 unsigned int _CRT_fmode = _O_BINARY;
@@ -39,46 +40,19 @@ static int do_lstat(const char *file_name, struct stat *buf)
 {
        WIN32_FILE_ATTRIBUTE_DATA fdata;
 
-       if (GetFileAttributesExA(file_name, GetFileExInfoStandard, &fdata)) {
-               int fMode = S_IREAD;
-               if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-                       fMode |= S_IFDIR;
-               else
-                       fMode |= S_IFREG;
-               if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
-                       fMode |= S_IWRITE;
-
+       if (!(errno = get_file_attr(file_name, &fdata))) {
                buf->st_ino = 0;
                buf->st_gid = 0;
                buf->st_uid = 0;
                buf->st_nlink = 1;
-               buf->st_mode = fMode;
+               buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
                buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
                buf->st_dev = buf->st_rdev = 0; /* not used by Git */
                buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
                buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
                buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
-               errno = 0;
                return 0;
        }
-
-       switch (GetLastError()) {
-       case ERROR_ACCESS_DENIED:
-       case ERROR_SHARING_VIOLATION:
-       case ERROR_LOCK_VIOLATION:
-       case ERROR_SHARING_BUFFER_EXCEEDED:
-               errno = EACCES;
-               break;
-       case ERROR_BUFFER_OVERFLOW:
-               errno = ENAMETOOLONG;
-               break;
-       case ERROR_NOT_ENOUGH_MEMORY:
-               errno = ENOMEM;
-               break;
-       default:
-               errno = ENOENT;
-               break;
-       }
        return -1;
 }
 
@@ -130,19 +104,11 @@ int mingw_fstat(int fd, struct stat *buf)
                return fstat(fd, buf);
 
        if (GetFileInformationByHandle(fh, &fdata)) {
-               int fMode = S_IREAD;
-               if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-                       fMode |= S_IFDIR;
-               else
-                       fMode |= S_IFREG;
-               if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
-                       fMode |= S_IWRITE;
-
                buf->st_ino = 0;
                buf->st_gid = 0;
                buf->st_uid = 0;
                buf->st_nlink = 1;
-               buf->st_mode = fMode;
+               buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
                buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
                buf->st_dev = buf->st_rdev = 0; /* not used by Git */
                buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
diff --git a/compat/win32.h b/compat/win32.h
new file mode 100644 (file)
index 0000000..c26384e
--- /dev/null
@@ -0,0 +1,34 @@
+/* common Win32 functions for MinGW and Cygwin */
+#include <windows.h>
+
+static inline int file_attr_to_st_mode (DWORD attr)
+{
+       int fMode = S_IREAD;
+       if (attr & FILE_ATTRIBUTE_DIRECTORY)
+               fMode |= S_IFDIR;
+       else
+               fMode |= S_IFREG;
+       if (!(attr & FILE_ATTRIBUTE_READONLY))
+               fMode |= S_IWRITE;
+       return fMode;
+}
+
+static inline int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fdata)
+{
+       if (GetFileAttributesExA(fname, GetFileExInfoStandard, fdata))
+               return 0;
+
+       switch (GetLastError()) {
+       case ERROR_ACCESS_DENIED:
+       case ERROR_SHARING_VIOLATION:
+       case ERROR_LOCK_VIOLATION:
+       case ERROR_SHARING_BUFFER_EXCEEDED:
+               return EACCES;
+       case ERROR_BUFFER_OVERFLOW:
+               return ENAMETOOLONG;
+       case ERROR_NOT_ENOUGH_MEMORY:
+               return ENOMEM;
+       default:
+               return ENOENT;
+       }
+}
index 15073d4c9fbccb06e9b17dff4a8d134e805aed6d..d192927c20a1b4bca97a6bea91cc10b0b67bdd29 100755 (executable)
@@ -1123,7 +1123,8 @@ _git_send_email ()
                        --no-suppress-from --no-thread --quiet
                        --signed-off-by-cc --smtp-pass --smtp-server
                        --smtp-server-port --smtp-ssl --smtp-user --subject
-                       --suppress-cc --suppress-from --thread --to"
+                       --suppress-cc --suppress-from --thread --to
+                       --validate --no-validate"
                return
                ;;
        esac
diff --git a/date.c b/date.c
index 35a52576c53e5e1406d40ed4402b8834a29b9f0e..950b88fdcf74f550a582684f1702ffb58c62c7f9 100644 (file)
--- a/date.c
+++ b/date.c
@@ -402,6 +402,15 @@ static int match_multi_number(unsigned long num, char c, const char *date, char
        return end - date;
 }
 
+/* Have we filled in any part of the time/date yet? */
+static inline int nodate(struct tm *tm)
+{
+       return tm->tm_year < 0 &&
+               tm->tm_mon < 0 &&
+               tm->tm_mday < 0 &&
+               !(tm->tm_hour | tm->tm_min | tm->tm_sec);
+}
+
 /*
  * We've seen a digit. Time? Year? Date?
  */
@@ -418,7 +427,7 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
         * more than 8 digits. This is because we don't want to rule out
         * numbers like 20070606 as a YYYYMMDD date.
         */
-       if (num >= 100000000) {
+       if (num >= 100000000 && nodate(tm)) {
                time_t time = num;
                if (gmtime_r(&time, tm)) {
                        *tm_gmt = 1;
@@ -462,6 +471,13 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
                return n;
        }
 
+       /*
+        * Ignore lots of numerals. We took care of 4-digit years above.
+        * Days or months must be one or two digits.
+        */
+       if (n > 2)
+               return n;
+
        /*
         * NOTE! We will give precedence to day-of-month over month or
         * year numbers in the 1-12 range. So 05 is always "mday 5",
@@ -488,10 +504,6 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
 
        if (num > 0 && num < 32) {
                tm->tm_mday = num;
-       } else if (num > 1900) {
-               tm->tm_year = num - 1900;
-       } else if (num > 70) {
-               tm->tm_year = num;
        } else if (num > 0 && num < 13) {
                tm->tm_mon = num-1;
        }
@@ -823,7 +835,9 @@ static const char *approxidate_digit(const char *date, struct tm *tm, int *num)
                }
        }
 
-       *num = number;
+       /* Accept zero-padding only for small numbers ("Dec 02", never "Dec 0002") */
+       if (date[0] != '0' || end - date <= 2)
+               *num = number;
        return end;
 }
 
index 0c6d11f6a0c6fa5dbab2f36c4b4ad4de5aba4ac9..0693cd9a42adf4914bb219ecf24fc6d5da7aafd3 100644 (file)
@@ -80,6 +80,11 @@ int is_bare_repository(void)
        return is_bare_repository_cfg && !get_git_work_tree();
 }
 
+int have_git_dir(void)
+{
+       return !!git_dir;
+}
+
 const char *get_git_dir(void)
 {
        if (!git_dir)
index 2ac832f3b4ad1566c7e744505205c13f7b05347f..e20b1e858cc715d1840da11c6198a1d6e6bec2a4 100644 (file)
@@ -85,6 +85,7 @@
 #undef _XOPEN_SOURCE
 #include <grp.h>
 #define _XOPEN_SOURCE 600
+#include "compat/cygwin.h"
 #else
 #undef _ALL_SOURCE /* AIX 5.3L defines a struct list with _ALL_SOURCE. */
 #include <grp.h>
index 3350f90cb1837238cd29786fa2b1541e1c4682bc..bdec43c3f604b22d29b52bd6a026785a6ac0f21b 100755 (executable)
@@ -314,23 +314,28 @@ do_next () {
 
                mark_action_done
                make_squash_message $sha1 > "$MSG"
+               failed=f
+               author_script=$(get_author_ident_from_commit HEAD)
+               output git reset --soft HEAD^
+               pick_one -n $sha1 || failed=t
                case "$(peek_next_command)" in
                squash|s)
                        EDIT_COMMIT=
                        USE_OUTPUT=output
+                       MSG_OPT=-F
+                       MSG_FILE="$MSG"
                        cp "$MSG" "$SQUASH_MSG"
                        ;;
                *)
                        EDIT_COMMIT=-e
                        USE_OUTPUT=
+                       MSG_OPT=
+                       MSG_FILE=
                        rm -f "$SQUASH_MSG" || exit
+                       cp "$MSG" "$GIT_DIR"/SQUASH_MSG
+                       rm -f "$GIT_DIR"/MERGE_MSG || exit
                        ;;
                esac
-
-               failed=f
-               author_script=$(get_author_ident_from_commit HEAD)
-               output git reset --soft HEAD^
-               pick_one -n $sha1 || failed=t
                echo "$author_script" > "$DOTEST"/author-script
                if test $failed = f
                then
@@ -339,7 +344,7 @@ do_next () {
                        GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
                        GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
                        GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
-                       $USE_OUTPUT git commit --no-verify -F "$MSG" $EDIT_COMMIT || failed=t
+                       $USE_OUTPUT git commit --no-verify $MSG_OPT "$MSG_FILE" $EDIT_COMMIT || failed=t
                fi
                if test $failed = t
                then
index d2fd89907688a044ffe0d2520744e00a9b33c942..bdbfac66256ffc29e26f5c83531c77a18a263808 100755 (executable)
@@ -39,75 +39,40 @@ package main;
 sub usage {
        print <<EOT;
 git send-email [options] <file | directory>...
-Options:
-   --from         Specify the "From:" line of the email to be sent.
 
-   --to           Specify the primary "To:" line of the email.
-
-   --cc           Specify an initial "Cc:" list for the entire series
-                  of emails.
-
-   --cc-cmd       Specify a command to execute per file which adds
-                  per file specific cc address entries
-
-   --bcc          Specify a list of email addresses that should be Bcc:
-                 on all the emails.
-
-   --compose      Use \$GIT_EDITOR, core.editor, \$EDITOR, or \$VISUAL to edit
-                 an introductory message for the patch series.
-
-   --subject      Specify the initial "Subject:" line.
-                  Only necessary if --compose is also set.  If --compose
-                 is not set, this will be prompted for.
-
-   --in-reply-to  Specify the first "In-Reply-To:" header line.
-                  Only used if --compose is also set.  If --compose is not
-                 set, this will be prompted for.
-
-   --chain-reply-to If set, the replies will all be to the previous
-                  email sent, rather than to the first email sent.
-                  Defaults to on.
-
-   --signed-off-cc Automatically add email addresses that appear in
-                 Signed-off-by: or Cc: lines to the cc: list. Defaults to on.
-
-   --identity     The configuration identity, a subsection to prioritise over
-                  the default section.
-
-   --smtp-server  If set, specifies the outgoing SMTP server to use.
-                  Defaults to localhost.  Port number can be specified here with
-                  hostname:port format or by using --smtp-server-port option.
-
-   --smtp-server-port Specify a port on the outgoing SMTP server to connect to.
-
-   --smtp-user    The username for SMTP-AUTH.
-
-   --smtp-pass    The password for SMTP-AUTH.
-
-   --smtp-encryption Specify 'tls' for STARTTLS encryption, or 'ssl' for SSL.
-                  Any other value disables the feature.
-
-   --smtp-ssl     Synonym for '--smtp-encryption=ssl'.  Deprecated.
-
-   --suppress-cc  Suppress the specified category of auto-CC.  The category
-                 can be one of 'author' for the patch author, 'self' to
-                 avoid copying yourself, 'sob' for Signed-off-by lines,
-                 'cccmd' for the output of the cccmd, or 'all' to suppress
-                 all of these.
-
-   --suppress-from Suppress sending emails to yourself. Defaults to off.
-
-   --thread       Specify that the "In-Reply-To:" header should be set on all
-                  emails. Defaults to on.
-
-   --quiet       Make git-send-email less verbose.  One line per email
-                  should be all that is output.
-
-   --dry-run     Do everything except actually send the emails.
-
-   --envelope-sender   Specify the envelope sender used to send the emails.
-
-   --no-validate       Don't perform any sanity checks on patches.
+  Composing:
+    --from                  <str>  * Email From:
+    --to                    <str>  * Email To:
+    --cc                    <str>  * Email Cc:
+    --bcc                   <str>  * Email Bcc:
+    --subject               <str>  * Email "Subject:"
+    --in-reply-to           <str>  * Email "In-Reply-To:"
+    --compose                      * Open an editor for introduction.
+
+  Sending:
+    --envelope-sender       <str>  * Email envelope sender.
+    --smtp-server       <str:int>  * Outgoing SMTP server to use. The port
+                                     is optional. Default 'localhost'.
+    --smtp-server-port      <int>  * Outgoing SMTP server port.
+    --smtp-user             <str>  * Username for SMTP-AUTH.
+    --smtp-pass             <str>  * Password for SMTP-AUTH; not necessary.
+    --smtp-encryption       <str>  * tls or ssl; anything else disables.
+    --smtp-ssl                     * Deprecated. Use '--smtp-encryption ssl'.
+
+  Automating:
+    --identity              <str>  * Use the sendemail.<id> options.
+    --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path`
+    --suppress-cc           <str>  * author, self, sob, cccmd, all.
+    --[no-]signed-off-by-cc        * Send to Cc: and Signed-off-by:
+                                     addresses. Default on.
+    --[no-]suppress-from           * Send to self. Default off.
+    --[no-]chain-reply-to          * Chain In-Reply-To: fields. Default on.
+    --[no-]thread                  * Use In-Reply-To: field. Default on.
+
+  Administering:
+    --quiet                        * Output one line of info per email.
+    --dry-run                      * Don't actually send the emails.
+    --[no-]validate                * Perform patch sanity checks. Default on.
 
 EOT
        exit(1);
@@ -186,17 +151,19 @@ sub format_2822_time {
 my ($quiet, $dry_run) = (0, 0);
 
 # Variables with corresponding config settings
-my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc, $cc_cmd);
+my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
 my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption);
 my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts);
-my ($no_validate);
+my ($validate);
 my (@suppress_cc);
 
 my %config_bool_settings = (
     "thread" => [\$thread, 1],
     "chainreplyto" => [\$chain_reply_to, 1],
     "suppressfrom" => [\$suppress_from, undef],
-    "signedoffcc" => [\$signed_off_cc, undef],
+    "signedoffbycc" => [\$signed_off_by_cc, undef],
+    "signedoffcc" => [\$signed_off_by_cc, undef],      # Deprecated
+    "validate" => [\$validate, 1],
 );
 
 my %config_settings = (
@@ -259,11 +226,11 @@ sub signal_handler {
                    "cc-cmd=s" => \$cc_cmd,
                    "suppress-from!" => \$suppress_from,
                    "suppress-cc=s" => \@suppress_cc,
-                   "signed-off-cc|signed-off-by-cc!" => \$signed_off_cc,
+                   "signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc,
                    "dry-run" => \$dry_run,
                    "envelope-sender=s" => \$envelope_sender,
                    "thread!" => \$thread,
-                   "no-validate" => \$no_validate,
+                   "validate!" => \$validate,
         );
 
 unless ($rc) {
@@ -335,7 +302,7 @@ sub read_config {
 
 # If explicit old-style ones are specified, they trump --suppress-cc.
 $suppress_cc{'self'} = $suppress_from if defined $suppress_from;
-$suppress_cc{'sob'} = !$signed_off_cc if defined $signed_off_cc;
+$suppress_cc{'sob'} = !$signed_off_by_cc if defined $signed_off_by_cc;
 
 # Debugging, print out the suppressions.
 if (0) {
@@ -416,7 +383,7 @@ sub read_config {
        }
 }
 
-if (!$no_validate) {
+if ($validate) {
        foreach my $f (@files) {
                unless (-p $f) {
                        my $error = validate_patch($f);
index 42f626f9d555f26f3e50441c0ab27de5cac19bf4..b9ace9970492aaf48472904d978d809d90ca33db 100755 (executable)
@@ -145,16 +145,8 @@ show_stash () {
                flags=--stat
        fi
 
-       if test $# = 0
-       then
-               set x "$ref_stash@{0}"
-               shift
-       fi
-
-       s=$(git rev-parse --revs-only --no-flags "$@")
-
-       w_commit=$(git rev-parse --verify "$s") &&
-       b_commit=$(git rev-parse --verify "$s^") &&
+       w_commit=$(git rev-parse --verify --default $ref_stash "$@") &&
+       b_commit=$(git rev-parse --verify "$w_commit^") &&
        git diff $flags $b_commit $w_commit
 }
 
@@ -170,19 +162,13 @@ apply_stash () {
                shift
        esac
 
-       if test $# = 0
-       then
-               set x "$ref_stash@{0}"
-               shift
-       fi
-
        # current index state
        c_tree=$(git write-tree) ||
                die 'Cannot apply a stash in the middle of a merge'
 
        # stash records the work tree, and is a merge between the
        # base commit (first parent) and the index tree (second parent).
-       s=$(git rev-parse --revs-only --no-flags "$@") &&
+       s=$(git rev-parse --verify --default $ref_stash "$@") &&
        w_tree=$(git rev-parse --verify "$s:") &&
        b_tree=$(git rev-parse --verify "$s^1:") &&
        i_tree=$(git rev-parse --verify "$s^2:") ||
@@ -242,7 +228,7 @@ drop_stash () {
                shift
        fi
        # Verify supplied argument looks like a stash entry
-       s=$(git rev-parse --revs-only --no-flags "$@") &&
+       s=$(git rev-parse --verify "$@") &&
        git rev-parse --verify "$s:"   > /dev/null 2>&1 &&
        git rev-parse --verify "$s^1:" > /dev/null 2>&1 &&
        git rev-parse --verify "$s^2:" > /dev/null 2>&1 ||
index 7609a837419012d94109cf8b4de1b9bbdb969c83..ef6d773df1143b32f0bc421b59421f7e0eab60bd 100755 (executable)
@@ -66,7 +66,7 @@ BEGIN
        $_version, $_fetch_all, $_no_rebase,
        $_merge, $_strategy, $_dry_run, $_local,
        $_prefix, $_no_checkout, $_url, $_verbose,
-       $_git_format, $_commit_url);
+       $_git_format, $_commit_url, $_tag);
 $Git::SVN::_follow_parent = 1;
 my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username,
                     'config-dir=s' => \$Git::SVN::Ra::config_dir,
@@ -131,6 +131,15 @@ BEGIN
                          'revision|r=i' => \$_revision,
                          'no-rebase' => \$_no_rebase,
                        %cmt_opts, %fc_opts } ],
+       branch => [ \&cmd_branch,
+                   'Create a branch in the SVN repository',
+                   { 'message|m=s' => \$_message,
+                     'dry-run|n' => \$_dry_run,
+                     'tag|t' => \$_tag } ],
+       tag => [ sub { $_tag = 1; cmd_branch(@_) },
+                'Create a tag in the SVN repository',
+                { 'message|m=s' => \$_message,
+                  'dry-run|n' => \$_dry_run } ],
        'set-tree' => [ \&cmd_set_tree,
                        "Set an SVN repository to a git tree-ish",
                        { 'stdin|' => \$_stdin, %cmt_opts, %fc_opts, } ],
@@ -537,6 +546,42 @@ sub cmd_dcommit {
        unlink $gs->{index};
 }
 
+sub cmd_branch {
+       my ($branch_name, $head) = @_;
+
+       unless (defined $branch_name && length $branch_name) {
+               die(($_tag ? "tag" : "branch") . " name required\n");
+       }
+       $head ||= 'HEAD';
+
+       my ($src, $rev, undef, $gs) = working_head_info($head);
+
+       my $remote = Git::SVN::read_all_remotes()->{svn};
+       my $glob = $remote->{ $_tag ? 'tags' : 'branches' };
+       my ($lft, $rgt) = @{ $glob->{path} }{qw/left right/};
+       my $dst = join '/', $remote->{url}, $lft, $branch_name, ($rgt || ());
+
+       my $ctx = SVN::Client->new(
+               auth    => Git::SVN::Ra::_auth_providers(),
+               log_msg => sub {
+                       ${ $_[0] } = defined $_message
+                               ? $_message
+                               : 'Create ' . ($_tag ? 'tag ' : 'branch ' )
+                               . $branch_name;
+               },
+       );
+
+       eval {
+               $ctx->ls($dst, 'HEAD', 0);
+       } and die "branch ${branch_name} already exists\n";
+
+       print "Copying ${src} at r${rev} to ${dst}...\n";
+       $ctx->copy($src, $rev, $dst)
+               unless $_dry_run;
+
+       $gs->fetch_all;
+}
+
 sub cmd_find_rev {
        my $revision_or_hash = shift or die "SVN or git revision required ",
                                            "as a command-line argument\n";
index 07f5b5378805a520e22b0f1b598aed7af4235538..a01eac814e51edd2115474f4d48a8d7fafa8c0e6 100644 (file)
@@ -435,6 +435,10 @@ div.search {
        right: 12px
 }
 
+p.projsearch {
+       text-align: center;
+}
+
 td.linenr {
        text-align: right;
 }
index 83f810ad46cd2dfcea37308791ba64870ebfbd3c..11168006cffe9bd2f6c8c27bd1d034c41667e39a 100755 (executable)
@@ -282,6 +282,44 @@ BEGIN
        'forks' => {
                'override' => 0,
                'default' => [0]},
+
+       # Insert custom links to the action bar of all project pages.
+       # This enables you mainly to link to third-party scripts integrating
+       # into gitweb; e.g. git-browser for graphical history representation
+       # or custom web-based repository administration interface.
+
+       # The 'default' value consists of a list of triplets in the form
+       # (label, link, position) where position is the label after which
+       # to inster the link and link is a format string where %n expands
+       # to the project name, %f to the project path within the filesystem,
+       # %h to the current hash (h gitweb parameter) and %b to the current
+       # hash base (hb gitweb parameter).
+
+       # To enable system wide have in $GITWEB_CONFIG e.g.
+       # $feature{'actions'}{'default'} = [('graphiclog',
+       #       '/git-browser/by-commit.html?r=%n', 'summary')];
+       # Project specific override is not supported.
+       'actions' => {
+               'override' => 0,
+               'default' => []},
+
+       # Allow gitweb scan project content tags described in ctags/
+       # of project repository, and display the popular Web 2.0-ish
+       # "tag cloud" near the project list. Note that this is something
+       # COMPLETELY different from the normal Git tags.
+
+       # gitweb by itself can show existing tags, but it does not handle
+       # tagging itself; you need an external application for that.
+       # For an example script, check Girocco's cgi/tagproj.cgi.
+       # You may want to install the HTML::TagCloud Perl module to get
+       # a pretty tag cloud instead of just a list of tags.
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'ctags'}{'default'} = ['path_to_tag_script'];
+       # Project specific override is not supported.
+       'ctags' => {
+               'override' => 0,
+               'default' => [0]},
 );
 
 sub gitweb_check_feature {
@@ -1762,6 +1800,67 @@ sub git_get_project_description {
        return $descr;
 }
 
+sub git_get_project_ctags {
+       my $path = shift;
+       my $ctags = {};
+
+       $git_dir = "$projectroot/$path";
+       foreach (<$git_dir/ctags/*>) {
+               open CT, $_ or next;
+               my $val = <CT>;
+               chomp $val;
+               close CT;
+               my $ctag = $_; $ctag =~ s#.*/##;
+               $ctags->{$ctag} = $val;
+       }
+       $ctags;
+}
+
+sub git_populate_project_tagcloud {
+       my $ctags = shift;
+
+       # First, merge different-cased tags; tags vote on casing
+       my %ctags_lc;
+       foreach (keys %$ctags) {
+               $ctags_lc{lc $_}->{count} += $ctags->{$_};
+               if (not $ctags_lc{lc $_}->{topcount}
+                   or $ctags_lc{lc $_}->{topcount} < $ctags->{$_}) {
+                       $ctags_lc{lc $_}->{topcount} = $ctags->{$_};
+                       $ctags_lc{lc $_}->{topname} = $_;
+               }
+       }
+
+       my $cloud;
+       if (eval { require HTML::TagCloud; 1; }) {
+               $cloud = HTML::TagCloud->new;
+               foreach (sort keys %ctags_lc) {
+                       # Pad the title with spaces so that the cloud looks
+                       # less crammed.
+                       my $title = $ctags_lc{$_}->{topname};
+                       $title =~ s/ /&nbsp;/g;
+                       $title =~ s/^/&nbsp;/g;
+                       $title =~ s/$/&nbsp;/g;
+                       $cloud->add($title, $home_link."?by_tag=".$_, $ctags_lc{$_}->{count});
+               }
+       } else {
+               $cloud = \%ctags_lc;
+       }
+       $cloud;
+}
+
+sub git_show_project_tagcloud {
+       my ($cloud, $count) = @_;
+       print STDERR ref($cloud)."..\n";
+       if (ref $cloud eq 'HTML::TagCloud') {
+               return $cloud->html_and_css($count);
+       } else {
+               my @tags = sort { $cloud->{$a}->{count} <=> $cloud->{$b}->{count} } keys %$cloud;
+               return '<p align="center">' . join (', ', map {
+                       "<a href=\"$home_link?by_tag=$_\">$cloud->{$_}->{topname}</a>"
+               } splice(@tags, 0, $count)) . '</p>';
+       }
+}
+
 sub git_get_project_url_list {
        my $path = shift;
 
@@ -1810,9 +1909,7 @@ sub git_get_projects_list {
 
                                my $subdir = substr($File::Find::name, $pfxlen + 1);
                                # we check related file in $projectroot
-                               if ($check_forks and $subdir =~ m#/.#) {
-                                       $File::Find::prune = 1;
-                               } elsif (check_export_ok("$projectroot/$filter/$subdir")) {
+                               if (check_export_ok("$projectroot/$filter/$subdir")) {
                                        push @list, { path => ($filter ? "$filter/" : '') . $subdir };
                                        $File::Find::prune = 1;
                                }
@@ -2764,13 +2861,26 @@ sub git_print_page_nav {
                        }
                }
        }
+
        $arg{'tree'}{'hash'} = $treehead if defined $treehead;
        $arg{'tree'}{'hash_base'} = $treebase if defined $treebase;
 
+       my @actions = gitweb_check_feature('actions');
+       while (@actions) {
+               my ($label, $link, $pos) = (shift(@actions), shift(@actions), shift(@actions));
+               @navs = map { $_ eq $pos ? ($_, $label) : $_ } @navs;
+               # munch munch
+               $link =~ s#%n#$project#g;
+               $link =~ s#%f#$git_dir#g;
+               $treehead ? $link =~ s#%h#$treehead#g : $link =~ s#%h##g;
+               $treebase ? $link =~ s#%b#$treebase#g : $link =~ s#%b##g;
+               $arg{$label}{'_href'} = $link;
+       }
+
        print "<div class=\"page_nav\">\n" .
                (join " | ",
                 map { $_ eq $current ?
-                      $_ : $cgi->a({-href => href(%{$arg{$_}})}, "$_")
+                      $_ : $cgi->a({-href => ($arg{$_}{_href} ? $arg{$_}{_href} : href(%{$arg{$_}}))}, "$_")
                 } @navs);
        print "<br/>\n$extra<br/>\n" .
              "</div>\n";
@@ -3580,6 +3690,7 @@ sub fill_project_list_info {
        my ($projlist, $check_forks) = @_;
        my @projects;
 
+       my $show_ctags = gitweb_check_feature('ctags');
  PROJECT:
        foreach my $pr (@$projlist) {
                my (@activity) = git_get_last_activity($pr->{'path'});
@@ -3606,25 +3717,20 @@ sub fill_project_list_info {
                                $pr->{'forks'} = 0;
                        }
                }
+               $show_ctags and $pr->{'ctags'} = git_get_project_ctags($pr->{'path'});
                push @projects, $pr;
        }
 
        return @projects;
 }
 
-# print 'sort by' <th> element, either sorting by $key if $name eq $order
-# (changing $list), or generating 'sort by $name' replay link otherwise
+# print 'sort by' <th> element, generating 'sort by $name' replay link
+# if that order is not selected
 sub print_sort_th {
-       my ($str_sort, $name, $order, $key, $header, $list) = @_;
-       $key    ||= $name;
+       my ($name, $order, $header) = @_;
        $header ||= ucfirst($name);
 
        if ($order eq $name) {
-               if ($str_sort) {
-                       @$list = sort {$a->{$key} cmp $b->{$key}} @$list;
-               } else {
-                       @$list = sort {$a->{$key} <=> $b->{$key}} @$list;
-               }
                print "<th>$header</th>\n";
        } else {
                print "<th>" .
@@ -3634,15 +3740,8 @@ sub print_sort_th {
        }
 }
 
-sub print_sort_th_str {
-       print_sort_th(1, @_);
-}
-
-sub print_sort_th_num {
-       print_sort_th(0, @_);
-}
-
 sub git_project_list_body {
+       # actually uses global variable $project
        my ($projlist, $order, $from, $to, $extra, $no_header) = @_;
 
        my ($check_forks) = gitweb_check_feature('forks');
@@ -3652,26 +3751,60 @@ sub git_project_list_body {
        $from = 0 unless defined $from;
        $to = $#projects if (!defined $to || $#projects < $to);
 
+       my %order_info = (
+               project => { key => 'path', type => 'str' },
+               descr => { key => 'descr_long', type => 'str' },
+               owner => { key => 'owner', type => 'str' },
+               age => { key => 'age', type => 'num' }
+       );
+       my $oi = $order_info{$order};
+       if ($oi->{'type'} eq 'str') {
+               @projects = sort {$a->{$oi->{'key'}} cmp $b->{$oi->{'key'}}} @projects;
+       } else {
+               @projects = sort {$a->{$oi->{'key'}} <=> $b->{$oi->{'key'}}} @projects;
+       }
+
+       my $show_ctags = gitweb_check_feature('ctags');
+       if ($show_ctags) {
+               my %ctags;
+               foreach my $p (@projects) {
+                       foreach my $ct (keys %{$p->{'ctags'}}) {
+                               $ctags{$ct} += $p->{'ctags'}->{$ct};
+                       }
+               }
+               my $cloud = git_populate_project_tagcloud(\%ctags);
+               print git_show_project_tagcloud($cloud, 64);
+       }
+
        print "<table class=\"project_list\">\n";
        unless ($no_header) {
                print "<tr>\n";
                if ($check_forks) {
                        print "<th></th>\n";
                }
-               print_sort_th_str('project', $order, 'path',
-                                 'Project', \@projects);
-               print_sort_th_str('descr', $order, 'descr_long',
-                                 'Description', \@projects);
-               print_sort_th_str('owner', $order, 'owner',
-                                 'Owner', \@projects);
-               print_sort_th_num('age', $order, 'age',
-                                 'Last Change', \@projects);
+               print_sort_th('project', $order, 'Project');
+               print_sort_th('descr', $order, 'Description');
+               print_sort_th('owner', $order, 'Owner');
+               print_sort_th('age', $order, 'Last Change');
                print "<th></th>\n" . # for links
                      "</tr>\n";
        }
        my $alternate = 1;
+       my $tagfilter = $cgi->param('by_tag');
        for (my $i = $from; $i <= $to; $i++) {
                my $pr = $projects[$i];
+
+               next if $tagfilter and $show_ctags and not grep { lc $_ eq lc $tagfilter } keys %{$pr->{'ctags'}};
+               next if $searchtext and not $pr->{'path'} =~ /$searchtext/
+                       and not $pr->{'descr_long'} =~ /$searchtext/;
+               # Weed out forks or non-matching entries of search
+               if ($check_forks) {
+                       my $forkbase = $project; $forkbase ||= ''; $forkbase =~ s#\.git$#/#;
+                       $forkbase="^$forkbase" if $forkbase;
+                       next if not $searchtext and not $tagfilter and $show_ctags
+                               and $pr->{'path'} =~ m#$forkbase.*/.*#; # regexp-safe
+               }
+
                if ($alternate) {
                        print "<tr class=\"dark\">\n";
                } else {
@@ -4006,6 +4139,11 @@ sub git_project_list {
                close $fd;
                print "</div>\n";
        }
+       print $cgi->startform(-method => "get") .
+             "<p class=\"projsearch\">Search:\n" .
+             $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
+             "</p>" .
+             $cgi->end_form() . "\n";
        git_project_list_body(\@list, $order);
        git_footer_html();
 }
@@ -4093,6 +4231,20 @@ sub git_summary {
                print "<tr class=\"metadata_url\"><td>$url_tag</td><td>$git_url</td></tr>\n";
                $url_tag = "";
        }
+
+       # Tag cloud
+       my $show_ctags = (gitweb_check_feature('ctags'))[0];
+       if ($show_ctags) {
+               my $ctags = git_get_project_ctags($project);
+               my $cloud = git_populate_project_tagcloud($ctags);
+               print "<tr id=\"metadata_ctags\"><td>Content tags:<br />";
+               print "</td>\n<td>" unless %$ctags;
+               print "<form action=\"$show_ctags\" method=\"post\"><input type=\"hidden\" name=\"p\" value=\"$project\" />Add: <input type=\"text\" name=\"t\" size=\"8\" /></form>";
+               print "</td>\n<td>" if %$ctags;
+               print git_show_project_tagcloud($cloud, 48);
+               print "</td></tr>";
+       }
+
        print "</table>\n";
 
        if (-s "$projectroot/$project/README.html") {
@@ -4131,10 +4283,10 @@ sub git_summary {
 
        if (@forklist) {
                git_print_header_div('forks');
-               git_project_list_body(\@forklist, undef, 0, 15,
+               git_project_list_body(\@forklist, 'age', 0, 15,
                                      $#forklist <= 15 ? undef :
                                      $cgi->a({-href => href(action=>"forks")}, "..."),
-                                     'noheader');
+                                     'no_header');
        }
 
        git_footer_html();
diff --git a/grep.c b/grep.c
index 706351197fc26efa10c4666d38433f8fbdf1d6b5..e2c190a7f66f9e9311f91d9a41b7dc3f295f00fb 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -239,6 +239,8 @@ static int word_char(char ch)
 static void show_line(struct grep_opt *opt, const char *bol, const char *eol,
                      const char *name, unsigned lno, char sign)
 {
+       if (opt->null_following_name)
+               sign = '\0';
        if (opt->pathname)
                printf("%s%c", name, sign);
        if (opt->linenum)
@@ -246,6 +248,11 @@ static void show_line(struct grep_opt *opt, const char *bol, const char *eol,
        printf("%.*s\n", (int)(eol-bol), bol);
 }
 
+static void show_name(struct grep_opt *opt, const char *name)
+{
+       printf("%s%c", name, opt->null_following_name ? '\0' : '\n');
+}
+
 static int fixmatch(const char *pattern, char *line, regmatch_t *match)
 {
        char *hit = strstr(line, pattern);
@@ -489,7 +496,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                                return 1;
                        }
                        if (opt->name_only) {
-                               printf("%s\n", name);
+                               show_name(opt, name);
                                return 1;
                        }
                        /* Hit at this line.  If we haven't shown the
@@ -555,7 +562,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                return 0;
        if (opt->unmatch_name_only) {
                /* We did not see any hit, so we want to show this */
-               printf("%s\n", name);
+               show_name(opt, name);
                return 1;
        }
 
@@ -565,7 +572,8 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
         * make it another option?  For now suppress them.
         */
        if (opt->count && count)
-               printf("%s:%u\n", name, count);
+               printf("%s%c%u\n", name,
+                      opt->null_following_name ? '\0' : ':', count);
        return !!last_hit;
 }
 
diff --git a/grep.h b/grep.h
index 59b3f871ea63619f8d3caae74c41b4da1e9a2b9f..45a222d904b5898758c3c84f6ef1e0119c81f6b2 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -74,6 +74,7 @@ struct grep_opt {
        unsigned extended:1;
        unsigned relative:1;
        unsigned pathname:1;
+       unsigned null_following_name:1;
        int regflags;
        unsigned pre_context;
        unsigned post_context;
index 2e4c0885f2c6f6907481dfefe7e0761c3957d8a6..73860bf3da062d283c266f6b83fdf3f613af26e7 100644 (file)
@@ -365,8 +365,11 @@ static void *get_data_from_pack(struct object_entry *obj)
        data = src;
        do {
                ssize_t n = pread(pack_fd, data + rdy, len - rdy, from + rdy);
-               if (n <= 0)
+               if (n < 0)
                        die("cannot pread pack file: %s", strerror(errno));
+               if (!n)
+                       die("premature end of pack file, %lu bytes missing",
+                           len - rdy);
                rdy += n;
        } while (rdy < len);
        data = xmalloc(obj->size);
index 02fc10f7e622ba1c53065e7cf4563ff10af0c41f..2d1413efbbc33c51fd4820933dcb54164e12d706 100644 (file)
@@ -158,9 +158,8 @@ static int same_entry(struct name_entry *a, struct name_entry *b)
 
 static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsigned char *sha1, const char *path)
 {
-       struct merge_list *res = xmalloc(sizeof(*res));
+       struct merge_list *res = xcalloc(1, sizeof(*res));
 
-       memset(res, 0, sizeof(*res));
        res->stage = stage;
        res->path = path;
        res->mode = mode;
index 901064bf1ae72aacb583ff735058eec953475ae3..d7900f33ea472e6781ba68b3943cb29ae0a1db34 100644 (file)
@@ -13,6 +13,7 @@
 #include "diff.h"
 #include "diffcore.h"
 #include "revision.h"
+#include "blob.h"
 
 /* Index extensions.
  *
@@ -511,6 +512,14 @@ static struct cache_entry *create_alias_ce(struct cache_entry *ce, struct cache_
        return new;
 }
 
+static void record_intent_to_add(struct cache_entry *ce)
+{
+       unsigned char sha1[20];
+       if (write_sha1_file("", 0, blob_type, sha1))
+               die("cannot create an empty blob in the object database");
+       hashcpy(ce->sha1, sha1);
+}
+
 int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags)
 {
        int size, namelen, was_same;
@@ -519,6 +528,9 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
        unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;
        int verbose = flags & (ADD_CACHE_VERBOSE | ADD_CACHE_PRETEND);
        int pretend = flags & ADD_CACHE_PRETEND;
+       int intent_only = flags & ADD_CACHE_INTENT;
+       int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|
+                         (intent_only ? ADD_CACHE_NEW_ONLY : 0));
 
        if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))
                return error("%s: can only add regular files, symbolic links or git-directories", path);
@@ -532,7 +544,8 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
        ce = xcalloc(1, size);
        memcpy(ce->name, path, namelen);
        ce->ce_flags = namelen;
-       fill_stat_cache_info(ce, st);
+       if (!intent_only)
+               fill_stat_cache_info(ce, st);
 
        if (trust_executable_bit && has_symlinks)
                ce->ce_mode = create_ce_mode(st_mode);
@@ -555,8 +568,12 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
                alias->ce_flags |= CE_ADDED;
                return 0;
        }
-       if (index_path(ce->sha1, path, st, 1))
-               return error("unable to index file %s", path);
+       if (!intent_only) {
+               if (index_path(ce->sha1, path, st, 1))
+                       return error("unable to index file %s", path);
+       } else
+               record_intent_to_add(ce);
+
        if (ignore_case && alias && different_name(ce, alias))
                ce = create_alias_ce(ce, alias);
        ce->ce_flags |= CE_ADDED;
@@ -569,7 +586,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
 
        if (pretend)
                ;
-       else if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
+       else if (add_index_entry(istate, ce, add_option))
                return error("unable to add %s to index",path);
        if (verbose && !was_same)
                printf("add '%s'\n", path);
@@ -848,13 +865,15 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
        int ok_to_add = option & ADD_CACHE_OK_TO_ADD;
        int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE;
        int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
+       int new_only = option & ADD_CACHE_NEW_ONLY;
 
        cache_tree_invalidate_path(istate->cache_tree, ce->name);
        pos = index_name_pos(istate, ce->name, ce->ce_flags);
 
        /* existing match? Just replace it. */
        if (pos >= 0) {
-               replace_index_entry(istate, pos, ce);
+               if (!new_only)
+                       replace_index_entry(istate, pos, ce);
                return 0;
        }
        pos = -pos-1;
index c45d96e98fffe3411284144b6d836cf499d42ba4..a2d7ab146ed6627cae5c845ef4801963635bf395 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -751,8 +751,7 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec)
 
 struct ref *alloc_ref(unsigned namelen)
 {
-       struct ref *ret = xmalloc(sizeof(struct ref) + namelen);
-       memset(ret, 0, sizeof(struct ref) + namelen);
+       struct ref *ret = xcalloc(1, sizeof(struct ref) + namelen);
        return ret;
 }
 
index 751598786822950766668b4d1c97198b183586f7..ea6bd996b2953e876a02fcf972229c97cb7d590a 100644 (file)
@@ -1571,11 +1571,9 @@ static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
        struct delta_base_cache_entry *ent = delta_base_cache + hash;
 
        ret = ent->data;
-       if (ret && ent->p == p && ent->base_offset == base_offset)
-               goto found_cache_entry;
-       return unpack_entry(p, base_offset, type, base_size);
+       if (!ret || ent->p != p || ent->base_offset != base_offset)
+               return unpack_entry(p, base_offset, type, base_size);
 
-found_cache_entry:
        if (!keep_cache) {
                ent->data = NULL;
                ent->lru.next->prev = ent->lru.prev;
index 620da5b32041b1ad69bfdcb6d139f2705386a5ff..5ac0a273a94c033fbb7c48cb9a22e44c389e0f7d 100755 (executable)
@@ -167,4 +167,36 @@ test_expect_success 'init with --template (blank)' '
        ! test -f template-blank/.git/info/exclude
 '
 
+test_expect_success 'init --bare/--shared overrides system/global config' '
+       (
+               HOME="`pwd`" &&
+               export HOME &&
+               test_config="$HOME"/.gitconfig &&
+               unset GIT_CONFIG_NOGLOBAL &&
+               git config -f "$test_config" core.bare false &&
+               git config -f "$test_config" core.sharedRepository 0640 &&
+               mkdir init-bare-shared-override &&
+               cd init-bare-shared-override &&
+               git init --bare --shared=0666
+       ) &&
+       check_config init-bare-shared-override true unset &&
+       test x0666 = \
+       x`git config -f init-bare-shared-override/config core.sharedRepository`
+'
+
+test_expect_success 'init honors global core.sharedRepository' '
+       (
+               HOME="`pwd`" &&
+               export HOME &&
+               test_config="$HOME"/.gitconfig &&
+               unset GIT_CONFIG_NOGLOBAL &&
+               git config -f "$test_config" core.sharedRepository 0666 &&
+               mkdir shared-honor-global &&
+               cd shared-honor-global &&
+               git init
+       ) &&
+       test x0666 = \
+       x`git config -f shared-honor-global/.git/config core.sharedRepository`
+'
+
 test_done
diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh
new file mode 100755 (executable)
index 0000000..d4de35e
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description='Intent to add'
+
+. ./test-lib.sh
+
+test_expect_success 'intent to add' '
+       echo hello >file &&
+       echo hello >elif &&
+       git add -N file &&
+       git add elif
+'
+
+test_expect_success 'check result of "add -N"' '
+       git ls-files -s file >actual &&
+       empty=$(git hash-object --stdin </dev/null) &&
+       echo "100644 $empty 0   file" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'intent to add is just an ordinary empty blob' '
+       git add -u &&
+       git ls-files -s file >actual &&
+       git ls-files -s elif | sed -e "s/elif/file/" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'intent to add does not clobber existing paths' '
+       git add -N file elif &&
+       empty=$(git hash-object --stdin </dev/null) &&
+       git ls-files -s >actual &&
+       ! grep "$empty" actual
+'
+
+test_done
+
index 558c80edbfa5c0c90566a0be94723e2783d6df9b..66aca99fd32c6b98f5e6e34a3cf7b096b3e236cf 100755 (executable)
@@ -219,14 +219,23 @@ test_expect_success 'Remove nonexistent file returns nonzero exit status' '
 
 test_expect_success 'Call "rm" from outside the work tree' '
        mkdir repo &&
-       cd repo &&
-       git init &&
-       echo something > somefile &&
-       git add somefile &&
-       git commit -m "add a file" &&
-       (cd .. &&
-        git --git-dir=repo/.git --work-tree=repo rm somefile) &&
-       test_must_fail git ls-files --error-unmatch somefile
+       (cd repo &&
+        git init &&
+        echo something > somefile &&
+        git add somefile &&
+        git commit -m "add a file" &&
+        (cd .. &&
+         git --git-dir=repo/.git --work-tree=repo rm somefile) &&
+       test_must_fail git ls-files --error-unmatch somefile)
+'
+
+test_expect_success 'refresh index before checking if it is up-to-date' '
+
+       git reset --hard &&
+       test-chmtime -86400 frotz/nitfol &&
+       git rm frotz/nitfol &&
+       test ! -f frotz/nitfol
+
 '
 
 test_done
index 9516f541e9c47f83fed2fc8d3baa065a9bb206de..7313ac278cb7cb1d741e49f261e65d929ddeffb7 100755 (executable)
@@ -511,4 +511,13 @@ test_expect_success 'in-index merge' '
 
 test_debug 'gitk --all'
 
+test_expect_success 'refresh the index before merging' '
+       git reset --hard c1 &&
+       sleep 1 &&
+       touch file &&
+       git merge c3
+'
+
+test_debug 'gitk --all'
+
 test_done
diff --git a/t/t9128-git-svn-cmd-branch.sh b/t/t9128-git-svn-cmd-branch.sh
new file mode 100755 (executable)
index 0000000..47c4d4d
--- /dev/null
@@ -0,0 +1,59 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Deskin Miller
+#
+
+test_description='git svn partial-rebuild tests'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize svnrepo' '
+       mkdir import &&
+       (
+               cd import &&
+               mkdir trunk branches tags &&
+               cd trunk &&
+               echo foo > foo &&
+               cd .. &&
+               svn import -m "import for git-svn" . "$svnrepo" >/dev/null &&
+               cd .. &&
+               rm -rf import &&
+               svn co "$svnrepo"/trunk trunk &&
+               cd trunk &&
+               echo bar >> foo &&
+               svn ci -m "updated trunk" &&
+               cd .. &&
+               rm -rf trunk
+       )
+'
+
+test_expect_success 'import into git' '
+       git svn init --stdlayout "$svnrepo" &&
+       git svn fetch &&
+       git checkout remotes/trunk
+'
+
+test_expect_success 'git svn branch tests' '
+       git svn branch a &&
+       base=$(git rev-parse HEAD:) &&
+       test $base = $(git rev-parse remotes/a:) &&
+       git svn branch -m "created branch b blah" b &&
+       test $base = $(git rev-parse remotes/b:) &&
+       test_must_fail git branch -m "no branchname" &&
+       git svn branch -n c &&
+       test_must_fail git rev-parse remotes/c &&
+       test_must_fail git svn branch a &&
+       git svn branch -t tag1 &&
+       test $base = $(git rev-parse remotes/tags/tag1:) &&
+       git svn branch --tag tag2 &&
+       test $base = $(git rev-parse remotes/tags/tag2:) &&
+       git svn tag tag3 &&
+       test $base = $(git rev-parse remotes/tags/tag3:) &&
+       git svn tag -m "created tag4 foo" tag4 &&
+       test $base = $(git rev-parse remotes/tags/tag4:) &&
+       test_must_fail git svn tag -m "no tagname" &&
+       git svn tag -n tag5 &&
+       test_must_fail git rev-parse remotes/tags/tag5 &&
+       test_must_fail git svn tag tag1
+'
+
+test_done
index 8bab82ed7fd1fcfea4dd13f6d020476758a18031..f3f6db3297e4fd367d7af05a4755037aaf2aaf42 100644 (file)
@@ -191,12 +191,22 @@ struct ff_regs {
 static long ff_regexp(const char *line, long len,
                char *buffer, long buffer_size, void *priv)
 {
-       char *line_buffer = xstrndup(line, len); /* make NUL terminated */
+       char *line_buffer;
        struct ff_regs *regs = priv;
        regmatch_t pmatch[2];
        int i;
        int result = -1;
 
+       /* Exclude terminating newline (and cr) from matching */
+       if (len > 0 && line[len-1] == '\n') {
+               if (len > 1 && line[len-2] == '\r')
+                       len -= 2;
+               else
+                       len--;
+       }
+
+       line_buffer = xstrndup(line, len); /* make NUL terminated */
+
        for (i = 0; i < regs->nr; i++) {
                struct ff_reg *reg = regs->array + i;
                if (!regexec(&reg->re, line_buffer, 2, pmatch, 0)) {