diff options
57 files changed, 1057 insertions, 459 deletions
diff --git a/Documentation/RelNotes-1.6.0.3.txt b/Documentation/RelNotes-1.6.0.3.txt index 46e13a450a..6cf8ae4ea1 100644 --- a/Documentation/RelNotes-1.6.0.3.txt +++ b/Documentation/RelNotes-1.6.0.3.txt @@ -13,33 +13,83 @@ Fixes since v1.6.0.2 * Continuing "git rebase -i" was also very confused when the user left some staged changes in the index after "edit". +* "git rebase -i" now honors the pre-rebase hook, just like the + other rebase implementations "git rebase" and "git rebase -m". + * Behaviour of "git diff --quiet" was inconsistent with "diff --exit-code" with the output redirected to /dev/null. +* "git diff --no-index" on binary files no longer outputs a bogus + "diff --git" header line. + +* Hunk headers in "git diff" default to using extended regular + expressions, fixing some of the internal patterns on non-GNU + platforms. + +* New config "diff.*.xfuncname" exposes extended regular expressions + for user specified hunk header patterns. + * "git stash apply sash@{1}" was fixed to error out. Prior versions would have applied stash@{0} incorrectly. +* "git stash apply" now offers a better suggestion on how to continue + if the working tree is currently dirty. + * "git for-each-ref --format=%(subject)" fixed for commits with no no newline in the message body. * "git remote" fixed to protect printf from user input. +* "git remote show -v" now displays all URLs of a remote. + * "git checkout -q" once again suppresses the locally modified file list. +* "git clone -q", "git fetch -q" asks remote side to not send + progress messages, actually making their output quiet. + * Cross-directory renames are no longer used when creating packs. This allows more graceful behavior on filesystems like sshfs. * Stale temporary files under $GIT_DIR/objects/pack are now cleaned up automatically by "git prune". +* "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 + it encounters a submodule reference. + +* "git svn" fixed to display an error message when 'set-tree' failed, + instead of a Perl compile error. + +* "git submodule" fixed to handle checking out a different commit + than HEAD after initializing the submodule. + +* 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 in multiple + tools such as apply, reset, update-index. + +* Solaris builds now default to OLD_ICONV=1 to avoid compile warnings. + * "Git.pm" tests relied on unnecessarily more recent version of Perl. * "gitweb" triggered undef warning on commits without log messages. +* "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. + +* Bash completion removed support for legacy "git-fetch", "git-push" + and "git-pull" as these are no longer installed. Dashless form + ("git fetch") is still however supported. + Many other documentation updates. -- exec >/var/tmp/1 -O=v1.6.0.2-41-g7fe4a72 +O=v1.6.0.2-76-gd70b4a8 echo O=$(git describe maint) git shortlog --no-merges $O..maint diff --git a/Documentation/config.txt b/Documentation/config.txt index bbe38ccaa2..da18a5458e 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -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 diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt index fa4d133c1b..553da6cbb1 100644 --- a/Documentation/git-grep.txt +++ b/Documentation/git-grep.txt @@ -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 diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt index 54f1dab38d..da6055d4b8 100644 --- a/Documentation/git-prune.txt +++ b/Documentation/git-prune.txt @@ -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. diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 45c96435fa..6150b1b959 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -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:: diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 3c3e1b0e77..82f505686e 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -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 diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 82d03b4ced..84c8f3cde0 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -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 diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 7fefdb1f45..5faaaa5fed 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -130,6 +130,13 @@ parameter, and is invoked after a commit is made. This hook is meant primarily for notification, and cannot affect the outcome of 'git-commit'. +pre-rebase +---------- + +This hook is called by 'git-rebase' and can be used to prevent a branch +from getting rebased. + + post-checkout ----------- @@ -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 diff --git a/builtin-add.c b/builtin-add.c index 7c874e3115..ea4e77169a 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -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)); diff --git a/builtin-apply.c b/builtin-apply.c index e2c611bf96..bf80610506 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -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); @@ -2586,6 +2586,8 @@ static void build_fake_ancestor(struct patch *list, const char *filename) sha1_ptr = sha1; ce = make_cache_entry(patch->old_mode, sha1_ptr, name, 0, 0); + if (!ce) + die("make_cache_entry failed for path '%s'", name); if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD)) die ("Could not add %s to temporary index", name); } diff --git a/builtin-blame.c b/builtin-blame.c index 6b7b9f4466..df537593d0 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -1132,6 +1132,8 @@ static int find_copy_in_parent(struct scoreboard *sb, if (!DIFF_FILE_VALID(p->one)) continue; /* does not exist in parent */ + if (S_ISGITLINK(p->one->mode)) + continue; /* ignore git links */ if (porigin && !strcmp(p->one->path, porigin->path)) /* find_move already dealt with this path */ continue; diff --git a/builtin-checkout.c b/builtin-checkout.c index b572b3bf69..3762f71aae 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -206,6 +206,8 @@ static int checkout_merged(int pos, struct checkout *state) ce = make_cache_entry(create_ce_mode(active_cache[pos+1]->ce_mode), sha1, path, 2, 0); + if (!ce) + die("make_cache_entry failed for path '%s'", path); status = checkout_entry(ce, state, NULL); return status; } diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index df02ba7afd..c6324dc795 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -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; } diff --git a/builtin-grep.c b/builtin-grep.c index 3a51662a35..624f86e287 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -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; diff --git a/builtin-init-db.c b/builtin-init-db.c index 8140c1299a..d30c3fe2ca 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -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 diff --git a/builtin-merge-base.c b/builtin-merge-base.c index b08da516e4..03fc1c2114 100644 --- a/builtin-merge-base.c +++ b/builtin-merge-base.c @@ -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); } diff --git a/builtin-merge-file.c b/builtin-merge-file.c index 45c98538cd..9d4e874809 100644 --- a/builtin-merge-file.c +++ b/builtin-merge-file.c @@ -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) diff --git a/builtin-merge.c b/builtin-merge.c index 5c65a58699..38266baf5f 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -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); diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 1158e42cba..59c30d1caa 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -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++; diff --git a/builtin-prune.c b/builtin-prune.c index 1663f8bdb1..7b4ec80e62 100644 --- a/builtin-prune.c +++ b/builtin-prune.c @@ -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() diff --git a/builtin-push.c b/builtin-push.c index cc6666f75e..122fdcfbdc 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -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, }; diff --git a/builtin-reset.c b/builtin-reset.c index c24c219091..16e6bb20f1 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -121,6 +121,9 @@ static void update_index_from_diff(struct diff_queue_struct *q, struct cache_entry *ce; ce = make_cache_entry(one->mode, one->sha1, one->path, 0, 0); + if (!ce) + die("make_cache_entry failed for path '%s'", + one->path); add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); } else diff --git a/builtin-rm.c b/builtin-rm.c index 50ae6d5401..e06640cf8d 100644 --- a/builtin-rm.c +++ b/builtin-rm.c @@ -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; diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c index d2796b6309..9f4bdd3296 100644 --- a/builtin-unpack-objects.c +++ b/builtin-unpack-objects.c @@ -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); @@ -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 index 0000000000..423ff20b0e --- /dev/null +++ b/compat/cygwin.c @@ -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 index 0000000000..a3229f5b4f --- /dev/null +++ b/compat/cygwin.h @@ -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) diff --git a/compat/mingw.c b/compat/mingw.c index fc45d240f5..09858f6c59 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -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 index 0000000000..c26384e595 --- /dev/null +++ b/compat/win32.h @@ -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; + } +} diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 93f088189e..d192927c20 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -785,14 +785,9 @@ _git_fetch () { local cur="${COMP_WORDS[COMP_CWORD]}" - case "${COMP_WORDS[0]},$COMP_CWORD" in - git-fetch*,1) + if [ "$COMP_CWORD" = 2 ]; then __gitcomp "$(__git_remotes)" - ;; - git,2) - __gitcomp "$(__git_remotes)" - ;; - *) + else case "$cur" in *:*) local pfx="" @@ -811,8 +806,7 @@ _git_fetch () __gitcomp "$(__git_refs2 "$remote")" ;; esac - ;; - esac + fi } _git_format_patch () @@ -1049,36 +1043,25 @@ _git_pull () { local cur="${COMP_WORDS[COMP_CWORD]}" - case "${COMP_WORDS[0]},$COMP_CWORD" in - git-pull*,1) - __gitcomp "$(__git_remotes)" - ;; - git,2) + if [ "$COMP_CWORD" = 2 ]; then __gitcomp "$(__git_remotes)" - ;; - *) + else local remote case "${COMP_WORDS[0]}" in git-pull) remote="${COMP_WORDS[1]}" ;; git) remote="${COMP_WORDS[2]}" ;; esac __gitcomp "$(__git_refs "$remote")" - ;; - esac + fi } _git_push () { local cur="${COMP_WORDS[COMP_CWORD]}" - case "${COMP_WORDS[0]},$COMP_CWORD" in - git-push*,1) - __gitcomp "$(__git_remotes)" - ;; - git,2) + if [ "$COMP_CWORD" = 2 ]; then __gitcomp "$(__git_remotes)" - ;; - *) + else case "$cur" in *:*) local remote @@ -1102,8 +1085,7 @@ _git_push () __gitcomp "$(__git_refs)" ;; esac - ;; - esac + fi } _git_rebase () @@ -1141,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 @@ -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; @@ -463,6 +472,13 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt } /* + * 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", * unless we already have a mday.. @@ -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; } @@ -1509,6 +1509,10 @@ static void builtin_diff(const char *name_a, b_prefix = o->b_prefix; } + /* Never use a non-valid filename anywhere if at all possible */ + name_a = DIFF_FILE_VALID(one) ? name_a : name_b; + name_b = DIFF_FILE_VALID(two) ? name_b : name_a; + a_one = quote_two(a_prefix, name_a + (*name_a == '/')); b_two = quote_two(b_prefix, name_b + (*name_b == '/')); lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null"; diff --git a/environment.c b/environment.c index 0c6d11f6a0..0693cd9a42 100644 --- a/environment.c +++ b/environment.c @@ -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) diff --git a/git-compat-util.h b/git-compat-util.h index 2ac832f3b4..e20b1e858c 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -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> diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index edb6ec6ed0..bdec43c3f6 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -65,6 +65,16 @@ output () { esac } +run_pre_rebase_hook () { + if test -x "$GIT_DIR/hooks/pre-rebase" + then + "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || { + echo >&2 "The pre-rebase hook refused to rebase." + exit 1 + } + fi +} + require_clean_work_tree () { # test if working tree is dirty git rev-parse --verify HEAD > /dev/null && @@ -304,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 @@ -329,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 @@ -507,6 +522,7 @@ first and then run 'git rebase --continue' again." ;; --) shift + run_pre_rebase_hook ${1+"$@"} test $# -eq 1 -o $# -eq 2 || usage test -d "$DOTEST" && die "Interactive rebase already started" diff --git a/git-rebase.sh b/git-rebase.sh index 528b604cd5..a30d40c005 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -144,6 +144,16 @@ is_interactive () { done && test -n "$1" } +run_pre_rebase_hook () { + if test -x "$GIT_DIR/hooks/pre-rebase" + then + "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || { + echo >&2 "The pre-rebase hook refused to rebase." + exit 1 + } + fi +} + test -f "$GIT_DIR"/rebase-apply/applying && die 'It looks like git-am is in progress. Cannot rebase.' @@ -320,13 +330,7 @@ onto_name=${newbase-"$upstream_name"} onto=$(git rev-parse --verify "${onto_name}^0") || exit # If a hook exists, give it a chance to interrupt -if test -x "$GIT_DIR/hooks/pre-rebase" -then - "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || { - echo >&2 "The pre-rebase hook refused to rebase." - exit 1 - } -fi +run_pre_rebase_hook ${1+"$@"} # If the branch to rebase is given, that is the branch we will rebase # $branch_name -- branch being rebased, or HEAD (already detached) diff --git a/git-send-email.perl b/git-send-email.perl index d2fd899076..bdbfac6625 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -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 @@ if ($@) { 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 @@ my $rc = GetOptions("sender|from=s" => \$sender, "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 @@ if ($suppress_cc{'all'}) { # 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 @@ for my $f (@ARGV) { } } -if (!$no_validate) { +if ($validate) { foreach my $f (@files) { unless (-p $f) { my $error = validate_patch($f); diff --git a/git-stash.sh b/git-stash.sh index 42f626f9d5..b9ace99704 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -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 || diff --git a/git-svn.perl b/git-svn.perl index 7609a83741..ef6d773df1 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -66,7 +66,7 @@ my ($_stdin, $_help, $_edit, $_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 @@ my %cmd = ( '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"; @@ -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; } @@ -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; diff --git a/index-pack.c b/index-pack.c index 2e4c0885f2..73860bf3da 100644 --- a/index-pack.c +++ b/index-pack.c @@ -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); diff --git a/merge-tree.c b/merge-tree.c index 02fc10f7e6..2d1413efbb 100644 --- a/merge-tree.c +++ b/merge-tree.c @@ -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; diff --git a/read-cache.c b/read-cache.c index 901064bf1a..d7900f33ea 100644 --- a/read-cache.c +++ b/read-cache.c @@ -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; @@ -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; } @@ -110,9 +110,7 @@ const char *prefix_path(const char *prefix, int len, const char *path) if (strncmp(sanitized, work_tree, len) || (sanitized[len] != '\0' && sanitized[len] != '/')) { error_out: - error("'%s' is outside repository", orig); - free(sanitized); - return NULL; + die("'%s' is outside repository", orig); } if (sanitized[len] == '/') len++; @@ -216,10 +214,7 @@ const char **get_pathspec(const char *prefix, const char **pathspec) prefixlen = prefix ? strlen(prefix) : 0; while (*src) { const char *p = prefix_path(prefix, prefixlen, *src); - if (p) - *(dst++) = p; - else - exit(128); /* error message already given */ + *(dst++) = p; src++; } *dst = NULL; diff --git a/sha1_file.c b/sha1_file.c index 7515987868..ea6bd996b2 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -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; diff --git a/t/t0001-init.sh b/t/t0001-init.sh index 620da5b320..5ac0a273a9 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -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 index 0000000000..d4de35ea06 --- /dev/null +++ b/t/t2203-add-intent.sh @@ -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 + diff --git a/t/t3409-rebase-hook.sh b/t/t3409-rebase-hook.sh new file mode 100755 index 0000000000..bc93dda8fd --- /dev/null +++ b/t/t3409-rebase-hook.sh @@ -0,0 +1,126 @@ +#!/bin/sh + +test_description='git rebase with its hook(s)' + +. ./test-lib.sh + +test_expect_success setup ' + echo hello >file && + git add file && + test_tick && + git commit -m initial && + echo goodbye >file && + git add file && + test_tick && + git commit -m second && + git checkout -b side HEAD^ && + echo world >git && + git add git && + test_tick && + git commit -m side && + git checkout master && + git log --pretty=oneline --abbrev-commit --graph --all && + git branch test side +' + +test_expect_success 'rebase' ' + git checkout test && + git reset --hard side && + git rebase master && + test "z$(cat git)" = zworld +' + +test_expect_success 'rebase -i' ' + git checkout test && + git reset --hard side && + EDITOR=true git rebase -i master && + test "z$(cat git)" = zworld +' + +test_expect_success 'setup pre-rebase hook' ' + mkdir -p .git/hooks && + cat >.git/hooks/pre-rebase <<EOF && +#!$SHELL_PATH +echo "\$1,\$2" >.git/PRE-REBASE-INPUT +EOF + chmod +x .git/hooks/pre-rebase +' + +test_expect_success 'pre-rebase hook gets correct input (1)' ' + git checkout test && + git reset --hard side && + git rebase master && + test "z$(cat git)" = zworld && + test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster, + +' + +test_expect_success 'pre-rebase hook gets correct input (2)' ' + git checkout test && + git reset --hard side && + git rebase master test && + test "z$(cat git)" = zworld && + test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test +' + +test_expect_success 'pre-rebase hook gets correct input (3)' ' + git checkout test && + git reset --hard side && + git checkout master && + git rebase master test && + test "z$(cat git)" = zworld && + test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test +' + +test_expect_success 'pre-rebase hook gets correct input (4)' ' + git checkout test && + git reset --hard side && + EDITOR=true git rebase -i master && + test "z$(cat git)" = zworld && + test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster, + +' + +test_expect_success 'pre-rebase hook gets correct input (5)' ' + git checkout test && + git reset --hard side && + EDITOR=true git rebase -i master test && + test "z$(cat git)" = zworld && + test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test +' + +test_expect_success 'pre-rebase hook gets correct input (6)' ' + git checkout test && + git reset --hard side && + git checkout master && + EDITOR=true git rebase -i master test && + test "z$(cat git)" = zworld && + test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test +' + +test_expect_success 'setup pre-rebase hook that fails' ' + mkdir -p .git/hooks && + cat >.git/hooks/pre-rebase <<EOF && +#!$SHELL_PATH +false +EOF + chmod +x .git/hooks/pre-rebase +' + +test_expect_success 'pre-rebase hook stops rebase (1)' ' + git checkout test && + git reset --hard side && + test_must_fail git rebase master && + test "z$(git symbolic-ref HEAD)" = zrefs/heads/test && + test 0 = $(git rev-list HEAD...side | wc -l) +' + +test_expect_success 'pre-rebase hook stops rebase (2)' ' + git checkout test && + git reset --hard side && + EDITOR=true test_must_fail git rebase -i master && + test "z$(git symbolic-ref HEAD)" = zrefs/heads/test && + test 0 = $(git rev-list HEAD...side | wc -l) +' + +test_done diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index 558c80edbf..66aca99fd3 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -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 diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh index b8ec6e90af..421f4bba3f 100755 --- a/t/t4012-diff-binary.sh +++ b/t/t4012-diff-binary.sh @@ -77,4 +77,25 @@ test_expect_success 'apply binary patch' \ tree1=`git write-tree` && test "$tree1" = "$tree0"' +q_to_nul() { + perl -pe 'y/Q/\000/' +} + +nul_to_q() { + perl -pe 'y/\000/Q/' +} + +test_expect_success 'diff --no-index with binary creation' ' + echo Q | q_to_nul >binary && + (:# hide error code from diff, which just indicates differences + git diff --binary --no-index /dev/null binary >current || + true + ) && + rm binary && + git apply --binary <current && + echo Q >expected && + nul_to_q <binary >actual && + test_cmp expected actual +' + test_done diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 9516f541e9..7313ac278c 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -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 index 0000000000..47c4d4d938 --- /dev/null +++ b/t/t9128-git-svn-cmd-branch.sh @@ -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 diff --git a/transport.c b/transport.c index f7db5d9110..5110c56c4e 100644 --- a/transport.c +++ b/transport.c @@ -643,8 +643,8 @@ static int fetch_refs_via_pack(struct transport *transport, args.use_thin_pack = data->thin; args.include_tag = data->followtags; args.verbose = (transport->verbose > 0); - args.quiet = args.no_progress = (transport->verbose < 0); - args.no_progress = !isatty(1); + args.quiet = (transport->verbose < 0); + args.no_progress = args.quiet || !isatty(1); args.depth = data->depth; for (i = 0; i < nr_heads; i++) diff --git a/xdiff-interface.c b/xdiff-interface.c index 8bab82ed7f..f3f6db3297 100644 --- a/xdiff-interface.c +++ b/xdiff-interface.c @@ -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(®->re, line_buffer, 2, pmatch, 0)) { |