diff options
57 files changed, 903 insertions, 354 deletions
diff --git a/Documentation/RelNotes/2.13.0.txt b/Documentation/RelNotes/2.13.0.txt index b0b4c36f97..9d92ddb277 100644 --- a/Documentation/RelNotes/2.13.0.txt +++ b/Documentation/RelNotes/2.13.0.txt @@ -73,6 +73,21 @@ UI, Workflows & Features but caused renaming of the current branch to something else not to be logged in a useful way. + * "Cc:" on the trailer part does not have to conform to RFC strictly, + unlike in the e-mail header. "git send-email" has been updated to + ignore anything after '>' when picking addresses, to allow non-address + cruft like " # stable 4.4" after the address. + (merge 9d3343961b jh/send-email-one-cc later to maint). + + * When "git submodule init" decides that the submodule in the working + tree is its upstream, it now gives a warning as it is not a very + common setup. + (merge d1b3b81aab sb/submodule-init-url-selection later to maint). + + * "git stash save" takes a pathspec so that the local changes can be + stashed away only partially. + (merge 9e140909f6 tg/stash-push later to maint). + Performance, Internal Implementation, Development Support etc. @@ -96,6 +111,29 @@ Performance, Internal Implementation, Development Support etc. * Some warning() messages from "git clean" were updated to show the errno from failed system calls. + * The "parse_config_key()" API function has been cleaned up. + (merge ad8c7cdadd jk/parse-config-key-cleanup later to maint). + + * A test that creates a confusing branch whose name is HEAD has been + corrected not to do so. + (merge f0252ca23c jk/t6300-cleanup later to maint). + + * The code that parses header fields in the commit object has been + updated for (micro)performance and code hygiene. + (merge b072504ce1 rs/commit-parsing-optim later to maint). + + * An helper function to make it easier to append the result from + real_path() to a strbuf has been added. + (merge 33ad9ddd0b rs/strbuf-add-real-path later to maint). + + * Reduce authentication round-trip over HTTP when the server supports + just a single authentication method. This also improves the + behaviour when Git is misconfigured to enable http.emptyAuth + against a server that does not authenticate without a username + (i.e. not using Kerberos etc., which makes http.emptyAuth + pointless). + (merge 40a18fc77c jk/http-auth later to maint). + Also contains various documentation updates and code clean-ups. @@ -147,5 +185,32 @@ notes for details). default) by ignoring a stale "gc.log" file that is too old. (merge a831c06a2b dt/gc-ignore-old-gc-logs later to maint). + * The code to parse "git -c VAR=VAL cmd" and set configuration + variable for the duration of cmd had two small bugs, which have + been fixed. + (merge 1274a155af jc/config-case-cmdline-take-2 later to maint). + + * user.email that consists of only cruft chars should consistently + error out, but didn't. + (merge 94425552f3 jk/ident-empty later to maint). + + * "git upload-pack", which is a counter-part of "git fetch", did not + report a request for a ref that was not advertised as invalid. + This is generally not a problem (because "git fetch" will stop + before making such a request), but is the right thing to do. + (merge bdb31eada7 jt/upload-pack-error-report later to maint). + + * A leak in a codepath to read from a packed object in (rare) cases + has been plugged. + (merge 886ddf4777 rs/sha1-file-plug-fallback-base-leak later to maint). + + * When a redirected http transport gets an error during the + redirected request, we ignored the error we got from the server, + and ended up giving a not-so-useful error message. + (merge 8e27391a5f jt/http-base-url-update-upon-redirect later to maint). + * Other minor doc, test and build updates and code cleanups. (merge 2cfa83574c mm/two-more-xstrfmt later to maint). + (merge b803ae4427 ps/docs-diffcore later to maint). + (merge bcd886d897 ew/markdown-url-in-readme later to maint). + (merge b2d593a779 rj/remove-unused-mktemp later to maint). diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 2e9e344cd7..70191d06b6 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -13,8 +13,11 @@ SYNOPSIS 'git stash' drop [-q|--quiet] [<stash>] 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>] 'git stash' branch <branchname> [<stash>] -'git stash' [save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet] - [-u|--include-untracked] [-a|--all] [<message>]] +'git stash' save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet] + [-u|--include-untracked] [-a|--all] [<message>] +'git stash' [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet] + [-u|--include-untracked] [-a|--all] [-m|--message <message>]] + [--] [<pathspec>...]] 'git stash' clear 'git stash' create [<message>] 'git stash' store [-m|--message <message>] [-q|--quiet] <commit> @@ -46,14 +49,24 @@ OPTIONS ------- save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]:: +push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]:: Save your local modifications to a new 'stash' and roll them back to HEAD (in the working tree and in the index). The <message> part is optional and gives - the description along with the stashed state. For quickly making - a snapshot, you can omit _both_ "save" and <message>, but giving - only <message> does not trigger this action to prevent a misspelled - subcommand from making an unwanted stash. + the description along with the stashed state. ++ +For quickly making a snapshot, you can omit "push". In this mode, +non-option arguments are not allowed to prevent a misspelled +subcommand from making an unwanted stash. The two exceptions to this +are `stash -p` which acts as alias for `stash push -p` and pathspecs, +which are allowed after a double hyphen `--` for disambiguation. ++ +When pathspec is given to 'git stash push', the new stash records the +modified states only for the files that match the pathspec. The index +entries and working tree files are then rolled back to the state in +HEAD only for these files, too, leaving files that do not match the +pathspec intact. + If the `--keep-index` option is used, all changes already added to the index are left intact. diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 8acc72ebb8..e05d0cddef 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -73,13 +73,17 @@ configuration entries unless `--name` is used to specify a logical name. + <repository> is the URL of the new submodule's origin repository. This may be either an absolute URL, or (if it begins with ./ -or ../), the location relative to the superproject's origin +or ../), the location relative to the superproject's default remote repository (Please note that to specify a repository 'foo.git' which is located right next to a superproject 'bar.git', you'll have to use '../foo.git' instead of './foo.git' - as one might expect when following the rules for relative URLs - because the evaluation of relative URLs in Git is identical to that of relative directories). -If the superproject doesn't have an origin configured ++ +The default remote is the remote of the remote tracking branch +of the current branch. If no such remote tracking branch exists or +the HEAD is detached, "origin" is assumed to be the default remote. +If the superproject doesn't have a default remote configured the superproject is its own authoritative upstream and the current working directory is used instead. + @@ -118,18 +122,24 @@ too (and can also report changes to a submodule's work tree). init [--] [<path>...]:: Initialize the submodules recorded in the index (which were - added and committed elsewhere) by copying submodule - names and urls from .gitmodules to .git/config. - Optional <path> arguments limit which submodules will be initialized. - It will also copy the value of `submodule.$name.update` into - .git/config. - The key used in .git/config is `submodule.$name.url`. - This command does not alter existing information in .git/config. - You can then customize the submodule clone URLs in .git/config - for your local setup and proceed to `git submodule update`; - you can also just use `git submodule update --init` without - the explicit 'init' step if you do not intend to customize - any submodule locations. + added and committed elsewhere) by setting `submodule.$name.url` + in .git/config. It uses the same setting from .gitmodules as + a template. If the URL is relative, it will be resolved using + the default remote. If there is no default remote, the current + repository will be assumed to be upstream. ++ +Optional <path> arguments limit which submodules will be initialized. +If no path is specified, all submodules are initialized. ++ +When present, it will also copy the value of `submodule.$name.update`. +This command does not alter existing information in .git/config. +You can then customize the submodule clone URLs in .git/config +for your local setup and proceed to `git submodule update`; +you can also just use `git submodule update --init` without +the explicit 'init' step if you do not intend to customize +any submodule locations. ++ +See the add subcommand for the defintion of default remote. deinit [-f|--force] (--all|[--] <path>...):: Unregister the given submodules, i.e. remove the whole diff --git a/Documentation/gitdiffcore.txt b/Documentation/gitdiffcore.txt index 46bc6d077c..c0a60f3158 100644 --- a/Documentation/gitdiffcore.txt +++ b/Documentation/gitdiffcore.txt @@ -84,8 +84,8 @@ format sections of the manual for 'git diff-{asterisk}' commands) or diff-patch format. -diffcore-break: For Splitting Up "Complete Rewrites" ----------------------------------------------------- +diffcore-break: For Splitting Up Complete Rewrites +-------------------------------------------------- The second transformation in the chain is diffcore-break, and is controlled by the -B option to the 'git diff-{asterisk}' commands. This is @@ -119,7 +119,7 @@ the original is used), and can be customized by giving a number after "-B" option (e.g. "-B75" to tell it to use 75%). -diffcore-rename: For Detection Renames and Copies +diffcore-rename: For Detecting Renames and Copies ------------------------------------------------- This transformation is used to detect renames and copies, and is @@ -177,8 +177,8 @@ the expense of making it slower. Without `--find-copies-harder`, copied happened to have been modified in the same changeset. -diffcore-merge-broken: For Putting "Complete Rewrites" Back Together --------------------------------------------------------------------- +diffcore-merge-broken: For Putting Complete Rewrites Back Together +------------------------------------------------------------------ This transformation is used to merge filepairs broken by diffcore-break, and not transformed into rename/copy by @@ -102,8 +102,6 @@ all:: # # Define MKDIR_WO_TRAILING_SLASH if your mkdir() can't deal with trailing slash. # -# Define NO_MKSTEMPS if you don't have mkstemps in the C library. -# # Define NO_GECOS_IN_PWENT if you don't have pw_gecos in struct passwd # in the C library. # @@ -1282,9 +1280,6 @@ ifdef MKDIR_WO_TRAILING_SLASH COMPAT_CFLAGS += -DMKDIR_WO_TRAILING_SLASH COMPAT_OBJS += compat/mkdir.o endif -ifdef NO_MKSTEMPS - COMPAT_CFLAGS += -DNO_MKSTEMPS -endif ifdef NO_UNSETENV COMPAT_CFLAGS += -DNO_UNSETENV COMPAT_OBJS += compat/unsetenv.o @@ -12,7 +12,7 @@ Torvalds with help of a group of hackers around the net. Please read the file [INSTALL][] for installation instructions. -Many Git online resources are accessible from http://git-scm.com/ +Many Git online resources are accessible from <https://git-scm.com/> including full documentation and Git related tools. See [Documentation/gittutorial.txt][] to get started, then see @@ -33,8 +33,8 @@ requests, comments and patches to git@vger.kernel.org (read [Documentation/SubmittingPatches][] for instructions on patch submission). To subscribe to the list, send an email with just "subscribe git" in the body to majordomo@vger.kernel.org. The mailing list archives are -available at https://public-inbox.org/git, -http://marc.info/?l=git and other archival sites. +available at <https://public-inbox.org/git/>, +<http://marc.info/?l=git> and other archival sites. The maintainer frequently sends the "What's cooking" reports that list the current status of various development topics to the mailing @@ -214,12 +214,12 @@ const char *real_path_if_valid(const char *path) return strbuf_realpath(&realpath, path, 0); } -char *real_pathdup(const char *path) +char *real_pathdup(const char *path, int die_on_error) { struct strbuf realpath = STRBUF_INIT; char *retval = NULL; - if (strbuf_realpath(&realpath, path, 0)) + if (strbuf_realpath(&realpath, path, die_on_error)) retval = strbuf_detach(&realpath, NULL); strbuf_release(&realpath); diff --git a/builtin/init-db.c b/builtin/init-db.c index 1d4d6a0078..8a6acb0ec6 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -338,7 +338,7 @@ int init_db(const char *git_dir, const char *real_git_dir, { int reinit; int exist_ok = flags & INIT_DB_EXIST_OK; - char *original_git_dir = real_pathdup(git_dir); + char *original_git_dir = real_pathdup(git_dir, 1); if (real_git_dir) { struct stat st; @@ -489,7 +489,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0); if (real_git_dir && !is_absolute_path(real_git_dir)) - real_git_dir = real_pathdup(real_git_dir); + real_git_dir = real_pathdup(real_git_dir, 1); if (argc == 1) { int mkdir_tried = 0; @@ -560,7 +560,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) const char *git_dir_parent = strrchr(git_dir, '/'); if (git_dir_parent) { char *rel = xstrndup(git_dir, git_dir_parent - git_dir); - git_work_tree_cfg = real_pathdup(rel); + git_work_tree_cfg = real_pathdup(rel, 1); free(rel); } if (!git_work_tree_cfg) diff --git a/builtin/log.c b/builtin/log.c index 55d20cc2d8..281af8c1ec 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -989,8 +989,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, open_next_file(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet)) return; - log_write_email_headers(rev, head, &pp.subject, &pp.after_subject, - &need_8bit_cte); + log_write_email_headers(rev, head, &pp.after_subject, &need_8bit_cte); for (i = 0; !need_8bit_cte && i < nr; i++) { const char *buf = get_commit_buffer(list[i], NULL); @@ -1005,6 +1004,8 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, msg = body; pp.fmt = CMIT_FMT_EMAIL; pp.date_mode.type = DATE_RFC2822; + pp.rev = rev; + pp.print_email_subject = 1; pp_user_info(&pp, NULL, &sb, committer, encoding); pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte); pp_remainder(&pp, &msg, &sb, 0); diff --git a/builtin/shortlog.c b/builtin/shortlog.c index c9585d475d..f78bb4818d 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -148,7 +148,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit) ctx.fmt = CMIT_FMT_USERFORMAT; ctx.abbrev = log->abbrev; - ctx.subject = ""; + ctx.print_email_subject = 1; ctx.after_subject = ""; ctx.date_mode.type = DATE_NORMAL; ctx.output_encoding = get_log_output_encoding(); diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 899dc334e3..15a5430c00 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -356,12 +356,10 @@ static void init_submodule(const char *path, const char *prefix, int quiet) strbuf_addf(&remotesb, "remote.%s.url", remote); free(remote); - if (git_config_get_string(remotesb.buf, &remoteurl)) - /* - * The repository is its own - * authoritative upstream - */ + if (git_config_get_string(remotesb.buf, &remoteurl)) { + warning(_("could not lookup configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf); remoteurl = xgetcwd(); + } relurl = relative_url(remoteurl, url, NULL); strbuf_release(&remotesb); free(remoteurl); @@ -1045,9 +1045,6 @@ static inline int is_empty_tree_oid(const struct object_id *oid) return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN); } - -int git_mkstemp(char *path, size_t n, const char *template); - /* set default permissions by passing mode arguments to open(2) */ int git_mkstemps_mode(char *pattern, int suffix_len, int mode); int git_mkstemp_mode(char *pattern, int mode); @@ -1153,7 +1150,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path, int die_on_error); const char *real_path(const char *path); const char *real_path_if_valid(const char *path); -char *real_pathdup(const char *path); +char *real_pathdup(const char *path, int die_on_error); const char *absolute_path(const char *path); char *absolute_pathdup(const char *path); const char *remove_leading_path(const char *in, const char *prefix); @@ -1863,8 +1860,11 @@ extern int git_config_include(const char *name, const char *value, void *data); * * (i.e., what gets handed to a config_fn_t). The caller provides the section; * we return -1 if it does not match, 0 otherwise. The subsection and key - * out-parameters are filled by the function (and subsection is NULL if it is + * out-parameters are filled by the function (and *subsection is NULL if it is * missing). + * + * If the subsection pointer-to-pointer passed in is NULL, returns 0 only if + * there is no subsection at all. */ extern int parse_config_key(const char *var, const char *section, @@ -1307,11 +1307,11 @@ void for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data) static inline int standard_header_field(const char *field, size_t len) { - return ((len == 4 && !memcmp(field, "tree ", 5)) || - (len == 6 && !memcmp(field, "parent ", 7)) || - (len == 6 && !memcmp(field, "author ", 7)) || - (len == 9 && !memcmp(field, "committer ", 10)) || - (len == 8 && !memcmp(field, "encoding ", 9))); + return ((len == 4 && !memcmp(field, "tree", 4)) || + (len == 6 && !memcmp(field, "parent", 6)) || + (len == 6 && !memcmp(field, "author", 6)) || + (len == 9 && !memcmp(field, "committer", 9)) || + (len == 8 && !memcmp(field, "encoding", 8))); } static int excluded_header_field(const char *field, size_t len, const char **exclude) @@ -1321,8 +1321,7 @@ static int excluded_header_field(const char *field, size_t len, const char **exc while (*exclude) { size_t xlen = strlen(*exclude); - if (len == xlen && - !memcmp(field, *exclude, xlen) && field[xlen] == ' ') + if (len == xlen && !memcmp(field, *exclude, xlen)) return 1; exclude++; } @@ -1353,12 +1352,11 @@ static struct commit_extra_header *read_commit_extra_header_lines( strbuf_reset(&buf); it = NULL; - eof = strchr(line, ' '); - if (next <= eof) + eof = memchr(line, ' ', next - line); + if (!eof) eof = next; - - if (standard_header_field(line, eof - line) || - excluded_header_field(line, eof - line, exclude)) + else if (standard_header_field(line, eof - line) || + excluded_header_field(line, eof - line, exclude)) continue; it = xcalloc(1, sizeof(*it)); @@ -142,21 +142,24 @@ static inline int cmit_fmt_is_mail(enum cmit_fmt fmt) return (fmt == CMIT_FMT_EMAIL || fmt == CMIT_FMT_MBOXRD); } +struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */ + struct pretty_print_context { /* * Callers should tweak these to change the behavior of pp_* functions. */ enum cmit_fmt fmt; int abbrev; - const char *subject; const char *after_subject; int preserve_subject; struct date_mode date_mode; unsigned date_mode_explicit:1; + int print_email_subject; int expand_tabs_in_log; int need_8bit_cte; char *notes_message; struct reflog_walk_info *reflog_info; + struct rev_info *rev; const char *output_encoding; struct string_list *mailmap; int color; @@ -175,7 +178,6 @@ struct userformat_want { }; extern int has_non_ascii(const char *text); -struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */ extern const char *logmsg_reencode(const struct commit *commit, char **commit_encoding, const char *output_encoding); @@ -201,11 +201,105 @@ void git_config_push_parameter(const char *text) strbuf_release(&env); } +static inline int iskeychar(int c) +{ + return isalnum(c) || c == '-'; +} + +/* + * Auxiliary function to sanity-check and split the key into the section + * identifier and variable name. + * + * Returns 0 on success, -1 when there is an invalid character in the key and + * -2 if there is no section name in the key. + * + * store_key - pointer to char* which will hold a copy of the key with + * lowercase section and variable name + * baselen - pointer to int which will hold the length of the + * section + subsection part, can be NULL + */ +static int git_config_parse_key_1(const char *key, char **store_key, int *baselen_, int quiet) +{ + int i, dot, baselen; + const char *last_dot = strrchr(key, '.'); + + /* + * Since "key" actually contains the section name and the real + * key name separated by a dot, we have to know where the dot is. + */ + + if (last_dot == NULL || last_dot == key) { + if (!quiet) + error("key does not contain a section: %s", key); + return -CONFIG_NO_SECTION_OR_NAME; + } + + if (!last_dot[1]) { + if (!quiet) + error("key does not contain variable name: %s", key); + return -CONFIG_NO_SECTION_OR_NAME; + } + + baselen = last_dot - key; + if (baselen_) + *baselen_ = baselen; + + /* + * Validate the key and while at it, lower case it for matching. + */ + if (store_key) + *store_key = xmallocz(strlen(key)); + + dot = 0; + for (i = 0; key[i]; i++) { + unsigned char c = key[i]; + if (c == '.') + dot = 1; + /* Leave the extended basename untouched.. */ + if (!dot || i > baselen) { + if (!iskeychar(c) || + (i == baselen + 1 && !isalpha(c))) { + if (!quiet) + error("invalid key: %s", key); + goto out_free_ret_1; + } + c = tolower(c); + } else if (c == '\n') { + if (!quiet) + error("invalid key (newline): %s", key); + goto out_free_ret_1; + } + if (store_key) + (*store_key)[i] = c; + } + + return 0; + +out_free_ret_1: + if (store_key) { + free(*store_key); + *store_key = NULL; + } + return -CONFIG_INVALID_KEY; +} + +int git_config_parse_key(const char *key, char **store_key, int *baselen) +{ + return git_config_parse_key_1(key, store_key, baselen, 0); +} + +int git_config_key_is_valid(const char *key) +{ + return !git_config_parse_key_1(key, NULL, NULL, 1); +} + int git_config_parse_parameter(const char *text, config_fn_t fn, void *data) { const char *value; + char *canonical_name; struct strbuf **pair; + int ret; pair = strbuf_split_str(text, '=', 2); if (!pair[0]) @@ -223,13 +317,15 @@ int git_config_parse_parameter(const char *text, strbuf_list_free(pair); return error("bogus config parameter: %s", text); } - strbuf_tolower(pair[0]); - if (fn(pair[0]->buf, value, data) < 0) { - strbuf_list_free(pair); - return -1; + + if (git_config_parse_key(pair[0]->buf, &canonical_name, NULL)) { + ret = -1; + } else { + ret = (fn(canonical_name, value, data) < 0) ? -1 : 0; + free(canonical_name); } strbuf_list_free(pair); - return 0; + return ret; } int git_config_from_parameters(config_fn_t fn, void *data) @@ -356,11 +452,6 @@ static char *parse_value(void) } } -static inline int iskeychar(int c) -{ - return isalnum(c) || c == '-'; -} - static int get_value(config_fn_t fn, void *data, struct strbuf *name) { int c; @@ -1990,93 +2081,6 @@ void git_config_set(const char *key, const char *value) } /* - * Auxiliary function to sanity-check and split the key into the section - * identifier and variable name. - * - * Returns 0 on success, -1 when there is an invalid character in the key and - * -2 if there is no section name in the key. - * - * store_key - pointer to char* which will hold a copy of the key with - * lowercase section and variable name - * baselen - pointer to int which will hold the length of the - * section + subsection part, can be NULL - */ -static int git_config_parse_key_1(const char *key, char **store_key, int *baselen_, int quiet) -{ - int i, dot, baselen; - const char *last_dot = strrchr(key, '.'); - - /* - * Since "key" actually contains the section name and the real - * key name separated by a dot, we have to know where the dot is. - */ - - if (last_dot == NULL || last_dot == key) { - if (!quiet) - error("key does not contain a section: %s", key); - return -CONFIG_NO_SECTION_OR_NAME; - } - - if (!last_dot[1]) { - if (!quiet) - error("key does not contain variable name: %s", key); - return -CONFIG_NO_SECTION_OR_NAME; - } - - baselen = last_dot - key; - if (baselen_) - *baselen_ = baselen; - - /* - * Validate the key and while at it, lower case it for matching. - */ - if (store_key) - *store_key = xmallocz(strlen(key)); - - dot = 0; - for (i = 0; key[i]; i++) { - unsigned char c = key[i]; - if (c == '.') - dot = 1; - /* Leave the extended basename untouched.. */ - if (!dot || i > baselen) { - if (!iskeychar(c) || - (i == baselen + 1 && !isalpha(c))) { - if (!quiet) - error("invalid key: %s", key); - goto out_free_ret_1; - } - c = tolower(c); - } else if (c == '\n') { - if (!quiet) - error("invalid key (newline): %s", key); - goto out_free_ret_1; - } - if (store_key) - (*store_key)[i] = c; - } - - return 0; - -out_free_ret_1: - if (store_key) { - free(*store_key); - *store_key = NULL; - } - return -CONFIG_INVALID_KEY; -} - -int git_config_parse_key(const char *key, char **store_key, int *baselen) -{ - return git_config_parse_key_1(key, store_key, baselen, 0); -} - -int git_config_key_is_valid(const char *key) -{ - return !git_config_parse_key_1(key, NULL, NULL, 1); -} - -/* * If value==NULL, unset in (remove from) config, * if value_regex!=NULL, disregard key/value pairs where value does not match. * if value_regex==CONFIG_REGEX_NONE, do not match any existing values @@ -2536,11 +2540,10 @@ int parse_config_key(const char *var, const char **subsection, int *subsection_len, const char **key) { - int section_len = strlen(section); const char *dot; /* Does it start with "section." ? */ - if (!starts_with(var, section) || var[section_len] != '.') + if (!skip_prefix(var, section, &var) || *var != '.') return -1; /* @@ -2552,12 +2555,16 @@ int parse_config_key(const char *var, *key = dot + 1; /* Did we have a subsection at all? */ - if (dot == var + section_len) { - *subsection = NULL; - *subsection_len = 0; + if (dot == var) { + if (subsection) { + *subsection = NULL; + *subsection_len = 0; + } } else { - *subsection = var + section_len + 1; + if (!subsection) + return -1; + *subsection = var + 1; *subsection_len = dot - *subsection; } diff --git a/config.mak.uname b/config.mak.uname index 447f36ac2e..399fe19271 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -27,7 +27,6 @@ endif ifeq ($(uname_S),Linux) HAVE_ALLOCA_H = YesPlease NO_STRLCPY = YesPlease - NO_MKSTEMPS = YesPlease HAVE_PATHS_H = YesPlease LIBC_CONTAINS_LIBINTL = YesPlease HAVE_DEV_TTY = YesPlease @@ -41,7 +40,6 @@ endif ifeq ($(uname_S),GNU/kFreeBSD) HAVE_ALLOCA_H = YesPlease NO_STRLCPY = YesPlease - NO_MKSTEMPS = YesPlease HAVE_PATHS_H = YesPlease DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease LIBC_CONTAINS_LIBINTL = YesPlease @@ -55,7 +53,6 @@ ifeq ($(uname_S),UnixWare) SHELL_PATH = /usr/local/bin/bash NO_IPV6 = YesPlease NO_HSTRERROR = YesPlease - NO_MKSTEMPS = YesPlease BASIC_CFLAGS += -Kthread BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib @@ -79,7 +76,6 @@ ifeq ($(uname_S),SCO_SV) SHELL_PATH = /usr/bin/bash NO_IPV6 = YesPlease NO_HSTRERROR = YesPlease - NO_MKSTEMPS = YesPlease BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib NO_STRCASESTR = YesPlease @@ -122,7 +118,6 @@ ifeq ($(uname_S),SunOS) NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease NO_REGEX = YesPlease NO_MSGFMT_EXTENDED_OPTIONS = YesPlease HAVE_DEV_TTY = YesPlease @@ -168,7 +163,6 @@ ifeq ($(uname_O),Cygwin) NO_D_TYPE_IN_DIRENT = YesPlease NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease - NO_MKSTEMPS = YesPlease NO_SYMLINK_HEAD = YesPlease NO_IPV6 = YesPlease OLD_ICONV = UnfortunatelyYes @@ -233,7 +227,6 @@ ifeq ($(uname_S),NetBSD) BASIC_CFLAGS += -I/usr/pkg/include BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib USE_ST_TIMESPEC = YesPlease - NO_MKSTEMPS = YesPlease HAVE_PATHS_H = YesPlease HAVE_BSD_SYSCTL = YesPlease endif @@ -242,7 +235,6 @@ ifeq ($(uname_S),AIX) NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease NO_STRLCPY = YesPlease NO_NSEC = YesPlease FREAD_READS_DIRECTORIES = UnfortunatelyYes @@ -263,7 +255,6 @@ ifeq ($(uname_S),GNU) # GNU/Hurd HAVE_ALLOCA_H = YesPlease NO_STRLCPY = YesPlease - NO_MKSTEMPS = YesPlease HAVE_PATHS_H = YesPlease LIBC_CONTAINS_LIBINTL = YesPlease endif @@ -272,7 +263,6 @@ ifeq ($(uname_S),IRIX) NO_UNSETENV = YesPlease NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease - NO_MKSTEMPS = YesPlease NO_MKDTEMP = YesPlease # When compiled with the MIPSpro 7.4.4m compiler, and without pthreads # (i.e. NO_PTHREADS is set), and _with_ MMAP (i.e. NO_MMAP is not set), @@ -291,7 +281,6 @@ ifeq ($(uname_S),IRIX64) NO_UNSETENV = YesPlease NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease - NO_MKSTEMPS = YesPlease NO_MKDTEMP = YesPlease # When compiled with the MIPSpro 7.4.4m compiler, and without pthreads # (i.e. NO_PTHREADS is set), and _with_ MMAP (i.e. NO_MMAP is not set), @@ -311,7 +300,6 @@ ifeq ($(uname_S),HP-UX) NO_SETENV = YesPlease NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease - NO_MKSTEMPS = YesPlease NO_STRLCPY = YesPlease NO_MKDTEMP = YesPlease NO_UNSETENV = YesPlease @@ -352,7 +340,6 @@ ifeq ($(uname_S),Windows) NO_ICONV = YesPlease NO_STRTOUMAX = YesPlease NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease SNPRINTF_RETURNS_BOGUS = YesPlease NO_SVN_TESTS = YesPlease RUNTIME_PREFIX = YesPlease @@ -402,7 +389,6 @@ ifeq ($(uname_S),Interix) NO_MKDTEMP = YesPlease NO_STRTOUMAX = YesPlease NO_NSEC = YesPlease - NO_MKSTEMPS = YesPlease ifeq ($(uname_R),3.5) NO_INET_NTOP = YesPlease NO_INET_PTON = YesPlease @@ -461,7 +447,6 @@ ifeq ($(uname_S),NONSTOP_KERNEL) NO_SETENV = YesPlease NO_UNSETENV = YesPlease NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease # Currently libiconv-1.9.1. OLD_ICONV = UnfortunatelyYes NO_REGEX = YesPlease @@ -503,7 +488,6 @@ ifneq (,$(findstring MINGW,$(uname_S))) NEEDS_LIBICONV = YesPlease NO_STRTOUMAX = YesPlease NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease NO_SVN_TESTS = YesPlease NO_PERL_MAKEMAKER = YesPlease RUNTIME_PREFIX = YesPlease @@ -515,7 +499,6 @@ ifneq (,$(findstring MINGW,$(uname_S))) OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo NO_REGEX = YesPlease NO_PYTHON = YesPlease - BLK_SHA1 = YesPlease ETAGS_TARGET = ETAGS NO_INET_PTON = YesPlease NO_INET_NTOP = YesPlease @@ -584,7 +567,6 @@ ifeq ($(uname_S),QNX) NO_ICONV = YesPlease NO_MEMMEM = YesPlease NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease NO_NSEC = YesPlease NO_PTHREADS = YesPlease NO_R_TO_GCC_LINKER = YesPlease diff --git a/configure.ac b/configure.ac index 0b15f04b10..128165529f 100644 --- a/configure.ac +++ b/configure.ac @@ -1050,12 +1050,6 @@ GIT_CHECK_FUNC(mkdtemp, [NO_MKDTEMP=YesPlease]) GIT_CONF_SUBST([NO_MKDTEMP]) # -# Define NO_MKSTEMPS if you don't have mkstemps in the C library. -GIT_CHECK_FUNC(mkstemps, -[NO_MKSTEMPS=], -[NO_MKSTEMPS=YesPlease]) -GIT_CONF_SUBST([NO_MKSTEMPS]) -# # Define NO_INITGROUPS if you don't have initgroups in the C library. GIT_CHECK_FUNC(initgroups, [NO_INITGROUPS=], diff --git a/contrib/coccinelle/array.cocci b/contrib/coccinelle/array.cocci index 2d7f25d99f..4ba98b7eaf 100644 --- a/contrib/coccinelle/array.cocci +++ b/contrib/coccinelle/array.cocci @@ -24,3 +24,19 @@ expression n; @@ - memcpy(dst, src, n * sizeof(T)); + COPY_ARRAY(dst, src, n); + +@@ +type T; +T *ptr; +expression n; +@@ +- ptr = xmalloc(n * sizeof(*ptr)); ++ ALLOC_ARRAY(ptr, n); + +@@ +type T; +T *ptr; +expression n; +@@ +- ptr = xmalloc(n * sizeof(T)); ++ ALLOC_ARRAY(ptr, n); diff --git a/contrib/coccinelle/strbuf.cocci b/contrib/coccinelle/strbuf.cocci index 63995f22ff..1d580e49b0 100644 --- a/contrib/coccinelle/strbuf.cocci +++ b/contrib/coccinelle/strbuf.cocci @@ -38,3 +38,9 @@ expression E1, E2, E3; @@ - strbuf_addstr(E1, find_unique_abbrev(E2, E3)); + strbuf_add_unique_abbrev(E1, E2, E3); + +@@ +expression E1, E2; +@@ +- strbuf_addstr(E1, real_path(E2)); ++ strbuf_add_real_path(E1, E2); diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr index 712a1377e2..1c3d87f861 100755 --- a/contrib/remote-helpers/git-remote-bzr +++ b/contrib/remote-helpers/git-remote-bzr @@ -1,13 +1,11 @@ -#!/usr/bin/env python +#!/bin/sh -import sys - -sys.stderr.write('WARNING: git-remote-bzr is now maintained independently.\n') -sys.stderr.write('WARNING: For more information visit https://github.com/felipec/git-remote-bzr\n') - -sys.stderr.write('''WARNING: +cat >&2 <<'EOT' +WARNING: git-remote-bzr is now maintained independently. +WARNING: For more information visit https://github.com/felipec/git-remote-bzr +WARNING: WARNING: You can pick a directory on your $PATH and download it, e.g.: -WARNING: $ wget -O $HOME/bin/git-remote-bzr \\ +WARNING: $ wget -O $HOME/bin/git-remote-bzr \ WARNING: https://raw.github.com/felipec/git-remote-bzr/master/git-remote-bzr WARNING: $ chmod +x $HOME/bin/git-remote-bzr -''') +EOT diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg index 4255ad6312..8e9188364c 100755 --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@ -1,13 +1,11 @@ -#!/usr/bin/env python +#!/bin/sh -import sys - -sys.stderr.write('WARNING: git-remote-hg is now maintained independently.\n') -sys.stderr.write('WARNING: For more information visit https://github.com/felipec/git-remote-hg\n') - -sys.stderr.write('''WARNING: +cat >&2 <<'EOT' +WARNING: git-remote-hg is now maintained independently. +WARNING: For more information visit https://github.com/felipec/git-remote-hg +WARNING: WARNING: You can pick a directory on your $PATH and download it, e.g.: -WARNING: $ wget -O $HOME/bin/git-remote-hg \\ +WARNING: $ wget -O $HOME/bin/git-remote-hg \ WARNING: https://raw.github.com/felipec/git-remote-hg/master/git-remote-hg WARNING: $ chmod +x $HOME/bin/git-remote-hg -''') +EOT @@ -2870,8 +2870,25 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags) s->should_free = 1; return 0; } - if (size_only) + + /* + * Even if the caller would be happy with getting + * only the size, we cannot return early at this + * point if the path requires us to run the content + * conversion. + */ + if (size_only && !would_convert_to_git(s->path)) return 0; + + /* + * Note: this check uses xsize_t(st.st_size) that may + * not be the true size of the blob after it goes + * through convert_to_git(). This may not strictly be + * correct, but the whole point of big_file_threshold + * and is_binary check being that we want to avoid + * opening the file and inspecting the contents, this + * is probably fine. + */ if ((flags & CHECK_BINARY) && s->size > big_file_threshold && s->is_binary == -1) { s->is_binary = 1; @@ -2730,8 +2730,8 @@ void connect_work_tree_and_git_dir(const char *work_tree_, const char *git_dir_) { struct strbuf file_name = STRBUF_INIT; struct strbuf rel_path = STRBUF_INIT; - char *git_dir = real_pathdup(git_dir_); - char *work_tree = real_pathdup(work_tree_); + char *git_dir = real_pathdup(git_dir_, 1); + char *work_tree = real_pathdup(work_tree_, 1); /* Update gitfile */ strbuf_addf(&file_name, "%s/.git", work_tree); diff --git a/environment.c b/environment.c index c07fb17fb7..42dc3106d2 100644 --- a/environment.c +++ b/environment.c @@ -259,7 +259,7 @@ void set_git_work_tree(const char *new_work_tree) return; } git_work_tree_initialized = 1; - work_tree = real_pathdup(new_work_tree); + work_tree = real_pathdup(new_work_tree, 1); } const char *get_git_work_tree(void) diff --git a/ewah/ewah_io.c b/ewah/ewah_io.c index 61f6a43579..f73210973f 100644 --- a/ewah/ewah_io.c +++ b/ewah/ewah_io.c @@ -142,8 +142,8 @@ int ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len) * the endianness conversion in a separate pass to ensure * we're loading 8-byte aligned words. */ - memcpy(self->buffer, ptr, self->buffer_size * sizeof(uint64_t)); - ptr += self->buffer_size * sizeof(uint64_t); + memcpy(self->buffer, ptr, self->buffer_size * sizeof(eword_t)); + ptr += self->buffer_size * sizeof(eword_t); for (i = 0; i < self->buffer_size; ++i) self->buffer[i] = ntohll(self->buffer[i]); diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 982593c89e..f5c816e273 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -92,7 +92,7 @@ sub colored { } # command line options -my $cmd; +my $patch_mode_only; my $patch_mode; my $patch_mode_revision; @@ -1299,7 +1299,7 @@ sub patch_update_cmd { } return 0; } - if ($patch_mode) { + if ($patch_mode_only) { @them = @mods; } else { @@ -1721,7 +1721,7 @@ sub process_args { die sprintf(__("invalid argument %s, expecting --"), $arg) unless $arg eq "--"; %patch_mode_flavour = %{$patch_modes{$patch_mode}}; - $cmd = 1; + $patch_mode_only = 1; } elsif ($arg ne "--") { die sprintf(__("invalid argument %s, expecting --"), $arg); @@ -1758,7 +1758,7 @@ sub main_loop { process_args(); refresh(); -if ($cmd) { +if ($patch_mode_only) { patch_update_cmd(); } else { diff --git a/git-compat-util.h b/git-compat-util.h index ef6d560e15..e626851fe9 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -639,11 +639,6 @@ extern int gitsetenv(const char *, const char *, int); extern char *gitmkdtemp(char *); #endif -#ifdef NO_MKSTEMPS -#define mkstemps gitmkstemps -extern int gitmkstemps(char *, int); -#endif - #ifdef NO_UNSETENV #define unsetenv gitunsetenv extern void gitunsetenv(const char *); diff --git a/git-send-email.perl b/git-send-email.perl index 068d60b3e6..eea0a517f7 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -1563,7 +1563,7 @@ foreach my $t (@files) { # Now parse the message body while(<$fh>) { $message .= $_; - if (/^(Signed-off-by|Cc): (.*)$/i) { + if (/^(Signed-off-by|Cc): ([^>]*>?)/i) { chomp; my ($what, $c) = ($1, $2); chomp $c; diff --git a/git-stash.sh b/git-stash.sh index 10c284d1aa..9c70662cc8 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -7,8 +7,11 @@ USAGE="list [<options>] or: $dashless drop [-q|--quiet] [<stash>] or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>] or: $dashless branch <branchname> [<stash>] - or: $dashless [save [--patch] [-k|--[no-]keep-index] [-q|--quiet] - [-u|--include-untracked] [-a|--all] [<message>]] + or: $dashless save [--patch] [-k|--[no-]keep-index] [-q|--quiet] + [-u|--include-untracked] [-a|--all] [<message>] + or: $dashless [push [--patch] [-k|--[no-]keep-index] [-q|--quiet] + [-u|--include-untracked] [-a|--all] [-m <message>] + [-- <pathspec>...]] or: $dashless clear" SUBDIRECTORY_OK=Yes @@ -33,15 +36,15 @@ else fi no_changes () { - git diff-index --quiet --cached HEAD --ignore-submodules -- && - git diff-files --quiet --ignore-submodules && + git diff-index --quiet --cached HEAD --ignore-submodules -- "$@" && + git diff-files --quiet --ignore-submodules -- "$@" && (test -z "$untracked" || test -z "$(untracked_files)") } untracked_files () { excl_opt=--exclude-standard test "$untracked" = "all" && excl_opt= - git ls-files -o -z $excl_opt + git ls-files -o -z $excl_opt -- "$@" } clear_stash () { @@ -56,11 +59,29 @@ clear_stash () { } create_stash () { - stash_msg="$1" - untracked="$2" + stash_msg= + untracked= + while test $# != 0 + do + case "$1" in + -m|--message) + shift + stash_msg=${1?"BUG: create_stash () -m requires an argument"} + ;; + -u|--include-untracked) + shift + untracked=${1?"BUG: create_stash () -u requires an argument"} + ;; + --) + shift + break + ;; + esac + shift + done git update-index -q --refresh - if no_changes + if no_changes "$@" then exit 0 fi @@ -92,7 +113,7 @@ create_stash () { # Untracked files are stored by themselves in a parentless commit, for # ease of unpacking later. u_commit=$( - untracked_files | ( + untracked_files "$@" | ( GIT_INDEX_FILE="$TMPindex" && export GIT_INDEX_FILE && rm -f "$TMPindex" && @@ -115,7 +136,7 @@ create_stash () { git read-tree --index-output="$TMPindex" -m $i_tree && GIT_INDEX_FILE="$TMPindex" && export GIT_INDEX_FILE && - git diff-index --name-only -z HEAD -- >"$TMP-stagenames" && + git diff-index --name-only -z HEAD -- "$@" >"$TMP-stagenames" && git update-index -z --add --remove --stdin <"$TMP-stagenames" && git write-tree && rm -f "$TMPindex" @@ -129,7 +150,7 @@ create_stash () { # find out what the user wants GIT_INDEX_FILE="$TMP-index" \ - git add--interactive --patch=stash -- && + git add--interactive --patch=stash -- "$@" && # state of the working tree w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) || @@ -189,10 +210,11 @@ store_stash () { return $ret } -save_stash () { +push_stash () { keep_index= patch_mode= untracked= + stash_msg= while test $# != 0 do case "$1" in @@ -216,6 +238,11 @@ save_stash () { -a|--all) untracked=all ;; + -m|--message) + shift + test -z ${1+x} && usage + stash_msg=$1 + ;; --help) show_help ;; @@ -251,29 +278,38 @@ save_stash () { die "$(gettext "Can't use --patch and --include-untracked or --all at the same time")" fi - stash_msg="$*" + test -n "$untracked" || git ls-files --error-unmatch -- "$@" >/dev/null || exit 1 git update-index -q --refresh - if no_changes + if no_changes "$@" then say "$(gettext "No local changes to save")" exit 0 fi + git reflog exists $ref_stash || clear_stash || die "$(gettext "Cannot initialize stash")" - create_stash "$stash_msg" $untracked + create_stash -m "$stash_msg" -u "$untracked" -- "$@" store_stash -m "$stash_msg" -q $w_commit || die "$(gettext "Cannot save the current status")" say "$(eval_gettext "Saved working directory and index state \$stash_msg")" if test -z "$patch_mode" then - git reset --hard ${GIT_QUIET:+-q} + if test $# != 0 + then + git reset ${GIT_QUIET:+-q} -- "$@" + git ls-files -z --modified -- "$@" | + git checkout-index -z --force --stdin + git clean --force ${GIT_QUIET:+-q} -d -- "$@" + else + git reset --hard ${GIT_QUIET:+-q} + fi test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION= if test -n "$untracked" then - git clean --force --quiet -d $CLEAN_X_OPTION + git clean --force --quiet -d $CLEAN_X_OPTION -- "$@" fi if test "$keep_index" = "t" && test -n "$i_tree" @@ -291,6 +327,36 @@ save_stash () { fi } +save_stash () { + push_options= + while test $# != 0 + do + case "$1" in + --) + shift + break + ;; + -*) + # pass all options through to push_stash + push_options="$push_options $1" + ;; + *) + break + ;; + esac + shift + done + + stash_msg="$*" + + if test -z "$stash_msg" + then + push_stash $push_options + else + push_stash $push_options -m "$stash_msg" + fi +} + have_stash () { git rev-parse --verify --quiet $ref_stash >/dev/null } @@ -590,18 +656,21 @@ apply_to_branch () { } } +test "$1" = "-p" && set "push" "$@" + PARSE_CACHE='--not-parsed' -# The default command is "save" if nothing but options are given +# The default command is "push" if nothing but options are given seen_non_option= for opt do case "$opt" in + --) break ;; -*) ;; *) seen_non_option=t; break ;; esac done -test -n "$seen_non_option" || set "save" "$@" +test -n "$seen_non_option" || set "push" "$@" # Main command set case "$1" in @@ -617,6 +686,10 @@ save) shift save_stash "$@" ;; +push) + shift + push_stash "$@" + ;; apply) shift apply_stash "$@" @@ -627,7 +700,7 @@ clear) ;; create) shift - create_stash "$*" && echo "$w_commit" + create_stash -m "$*" && echo "$w_commit" ;; store) shift @@ -648,7 +721,7 @@ branch) *) case $# in 0) - save_stash && + push_stash && say "$(gettext "(To restore them type \"git stash apply\")")" ;; *) diff --git a/git-svn.perl b/git-svn.perl index fa42364785..d2404184ba 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1175,10 +1175,10 @@ sub cmd_branch { ::_req_svn(); require SVN::Client; + my ($config, $baton, undef) = Git::SVN::Ra::prepare_config_once(); my $ctx = SVN::Client->new( - config => SVN::Core::config_get_config( - $Git::SVN::Ra::config_dir - ), + auth => $baton, + config => $config, log_msg => sub { ${ $_[0] } = defined $_message ? $_message @@ -109,7 +109,7 @@ static int curl_save_cookies; struct credential http_auth = CREDENTIAL_INIT; static int http_proactive_auth; static const char *user_agent; -static int curl_empty_auth; +static int curl_empty_auth = -1; enum http_follow_config http_follow_config = HTTP_FOLLOW_INITIAL; @@ -125,6 +125,14 @@ static struct credential cert_auth = CREDENTIAL_INIT; static int ssl_cert_password_required; #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY static unsigned long http_auth_methods = CURLAUTH_ANY; +static int http_auth_methods_restricted; +/* Modes for which empty_auth cannot actually help us. */ +static unsigned long empty_auth_useless = + CURLAUTH_BASIC +#ifdef CURLAUTH_DIGEST_IE + | CURLAUTH_DIGEST_IE +#endif + | CURLAUTH_DIGEST; #endif static struct curl_slist *pragma_header; @@ -333,7 +341,10 @@ static int http_options(const char *var, const char *value, void *cb) return git_config_string(&user_agent, var, value); if (!strcmp("http.emptyauth", var)) { - curl_empty_auth = git_config_bool(var, value); + if (value && !strcmp("auto", value)) + curl_empty_auth = -1; + else + curl_empty_auth = git_config_bool(var, value); return 0; } @@ -382,10 +393,37 @@ static int http_options(const char *var, const char *value, void *cb) return git_default_config(var, value, cb); } +static int curl_empty_auth_enabled(void) +{ + if (curl_empty_auth >= 0) + return curl_empty_auth; + +#ifndef LIBCURL_CAN_HANDLE_AUTH_ANY + /* + * Our libcurl is too old to do AUTH_ANY in the first place; + * just default to turning the feature off. + */ +#else + /* + * In the automatic case, kick in the empty-auth + * hack as long as we would potentially try some + * method more exotic than "Basic" or "Digest". + * + * But only do this when this is our second or + * subsequent request, as by then we know what + * methods are available. + */ + if (http_auth_methods_restricted && + (http_auth_methods & ~empty_auth_useless)) + return 1; +#endif + return 0; +} + static void init_curl_http_auth(CURL *result) { if (!http_auth.username || !*http_auth.username) { - if (curl_empty_auth) + if (curl_empty_auth_enabled()) curl_easy_setopt(result, CURLOPT_USERPWD, ":"); return; } @@ -1079,7 +1117,7 @@ struct active_request_slot *get_active_slot(void) #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods); #endif - if (http_auth.password || curl_empty_auth) + if (http_auth.password || curl_empty_auth_enabled()) init_curl_http_auth(slot->curl); return slot; @@ -1347,6 +1385,10 @@ static int handle_curl_result(struct slot_results *results) } else { #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE; + if (results->auth_avail) { + http_auth_methods &= results->auth_avail; + http_auth_methods_restricted = 1; + } #endif return HTTP_REAUTH; } @@ -1727,6 +1769,9 @@ static int http_request_reauth(const char *url, { int ret = http_request(url, result, target, options); + if (ret != HTTP_OK && ret != HTTP_REAUTH) + return ret; + if (options && options->effective_url && options->base_url) { if (update_url_from_redirect(options->base_url, url, options->effective_url)) { @@ -153,7 +153,7 @@ static void copy_email(const struct passwd *pw, struct strbuf *email, const char *ident_default_name(void) { - if (!git_default_name.len) { + if (!(ident_config_given & IDENT_NAME_GIVEN) && !git_default_name.len) { copy_gecos(xgetpwuid_self(&default_name_is_bogus), &git_default_name); strbuf_trim(&git_default_name); } @@ -162,7 +162,7 @@ const char *ident_default_name(void) const char *ident_default_email(void) { - if (!git_default_email.len) { + if (!(ident_config_given & IDENT_MAIL_GIVEN) && !git_default_email.len) { const char *email = getenv("EMAIL"); if (email && email[0]) { @@ -203,6 +203,15 @@ static int crud(unsigned char c) c == '\''; } +static int has_non_crud(const char *str) +{ + for (; *str; str++) { + if (!crud(*str)) + return 1; + } + return 0; +} + /* * Copy over a string to the destination, but avoid special * characters ('\n', '<' and '>') and remove crud at the end @@ -351,19 +360,32 @@ const char *fmt_ident(const char *name, const char *email, int want_date = !(flag & IDENT_NO_DATE); int want_name = !(flag & IDENT_NO_NAME); + if (!email) { + if (strict && ident_use_config_only + && !(ident_config_given & IDENT_MAIL_GIVEN)) { + fputs(_(env_hint), stderr); + die(_("no email was given and auto-detection is disabled")); + } + email = ident_default_email(); + if (strict && default_email_is_bogus) { + fputs(_(env_hint), stderr); + die(_("unable to auto-detect email address (got '%s')"), email); + } + } + if (want_name) { int using_default = 0; if (!name) { if (strict && ident_use_config_only && !(ident_config_given & IDENT_NAME_GIVEN)) { fputs(_(env_hint), stderr); - die("no name was given and auto-detection is disabled"); + die(_("no name was given and auto-detection is disabled")); } name = ident_default_name(); using_default = 1; if (strict && default_name_is_bogus) { fputs(_(env_hint), stderr); - die("unable to auto-detect name (got '%s')", name); + die(_("unable to auto-detect name (got '%s')"), name); } } if (!*name) { @@ -371,24 +393,13 @@ const char *fmt_ident(const char *name, const char *email, if (strict) { if (using_default) fputs(_(env_hint), stderr); - die("empty ident name (for <%s>) not allowed", email); + die(_("empty ident name (for <%s>) not allowed"), email); } pw = xgetpwuid_self(NULL); name = pw->pw_name; } - } - - if (!email) { - if (strict && ident_use_config_only - && !(ident_config_given & IDENT_MAIL_GIVEN)) { - fputs(_(env_hint), stderr); - die("no email was given and auto-detection is disabled"); - } - email = ident_default_email(); - if (strict && default_email_is_bogus) { - fputs(_(env_hint), stderr); - die("unable to auto-detect email address (got '%s')", email); - } + if (strict && !has_non_crud(name)) + die(_("name consists only of disallowed characters: %s"), name); } strbuf_reset(&ident); @@ -403,7 +414,7 @@ const char *fmt_ident(const char *name, const char *email, strbuf_addch(&ident, ' '); if (date_str && date_str[0]) { if (parse_date(date_str, &ident) < 0) - die("invalid date format: %s", date_str); + die(_("invalid date format: %s"), date_str); } else strbuf_addstr(&ident, ident_default_date()); diff --git a/line-log.c b/line-log.c index 65f3558b3b..a23b910471 100644 --- a/line-log.c +++ b/line-log.c @@ -43,9 +43,10 @@ void range_set_release(struct range_set *rs) static void range_set_copy(struct range_set *dst, struct range_set *src) { range_set_init(dst, src->nr); - memcpy(dst->ranges, src->ranges, src->nr*sizeof(struct range_set)); + COPY_ARRAY(dst->ranges, src->ranges, src->nr); dst->nr = src->nr; } + static void range_set_move(struct range_set *dst, struct range_set *src) { range_set_release(dst); @@ -144,7 +145,7 @@ void sort_and_merge_range_set(struct range_set *rs) static void range_set_union(struct range_set *out, struct range_set *a, struct range_set *b) { - int i = 0, j = 0, o = 0; + int i = 0, j = 0; struct range *ra = a->ranges; struct range *rb = b->ranges; /* cannot make an alias of out->ranges: it may change during grow */ @@ -167,16 +168,15 @@ static void range_set_union(struct range_set *out, new = &rb[j++]; if (new->start == new->end) ; /* empty range */ - else if (!o || out->ranges[o-1].end < new->start) { + else if (!out->nr || out->ranges[out->nr-1].end < new->start) { range_set_grow(out, 1); - out->ranges[o].start = new->start; - out->ranges[o].end = new->end; - o++; - } else if (out->ranges[o-1].end < new->end) { - out->ranges[o-1].end = new->end; + out->ranges[out->nr].start = new->start; + out->ranges[out->nr].end = new->end; + out->nr++; + } else if (out->ranges[out->nr-1].end < new->end) { + out->ranges[out->nr-1].end = new->end; } } - out->nr = o; } /* diff --git a/log-tree.c b/log-tree.c index 8c2415747a..4618dd04ca 100644 --- a/log-tree.c +++ b/log-tree.c @@ -332,35 +332,31 @@ void fmt_output_commit(struct strbuf *filename, strbuf_release(&subject); } +void fmt_output_email_subject(struct strbuf *sb, struct rev_info *opt) +{ + if (opt->total > 0) { + strbuf_addf(sb, "Subject: [%s%s%0*d/%d] ", + opt->subject_prefix, + *opt->subject_prefix ? " " : "", + digits_in_number(opt->total), + opt->nr, opt->total); + } else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) { + strbuf_addf(sb, "Subject: [%s] ", + opt->subject_prefix); + } else { + strbuf_addstr(sb, "Subject: "); + } +} + void log_write_email_headers(struct rev_info *opt, struct commit *commit, - const char **subject_p, const char **extra_headers_p, int *need_8bit_cte_p) { - const char *subject = NULL; const char *extra_headers = opt->extra_headers; const char *name = oid_to_hex(opt->zero_commit ? &null_oid : &commit->object.oid); *need_8bit_cte_p = 0; /* unknown */ - if (opt->total > 0) { - static char buffer[64]; - snprintf(buffer, sizeof(buffer), - "Subject: [%s%s%0*d/%d] ", - opt->subject_prefix, - *opt->subject_prefix ? " " : "", - digits_in_number(opt->total), - opt->nr, opt->total); - subject = buffer; - } else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) { - static char buffer[256]; - snprintf(buffer, sizeof(buffer), - "Subject: [%s] ", - opt->subject_prefix); - subject = buffer; - } else { - subject = "Subject: "; - } fprintf(opt->diffopt.file, "From %s Mon Sep 17 00:00:00 2001\n", name); graph_show_oneline(opt->graph); @@ -417,7 +413,6 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit, opt->diffopt.stat_sep = buffer; strbuf_release(&filename); } - *subject_p = subject; *extra_headers_p = extra_headers; } @@ -602,8 +597,10 @@ void show_log(struct rev_info *opt) */ if (cmit_fmt_is_mail(opt->commit_format)) { - log_write_email_headers(opt, commit, &ctx.subject, &extra_headers, + log_write_email_headers(opt, commit, &extra_headers, &ctx.need_8bit_cte); + ctx.rev = opt; + ctx.print_email_subject = 1; } else if (opt->commit_format != CMIT_FMT_USERFORMAT) { fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), opt->diffopt.file); if (opt->commit_format != CMIT_FMT_ONELINE) diff --git a/log-tree.h b/log-tree.h index c8116e60cd..48f11fb740 100644 --- a/log-tree.h +++ b/log-tree.h @@ -22,7 +22,6 @@ void format_decorations_extended(struct strbuf *sb, const struct commit *commit, format_decorations_extended((strbuf), (commit), (color), " (", ", ", ")") void show_decorations(struct rev_info *opt, struct commit *commit); void log_write_email_headers(struct rev_info *opt, struct commit *commit, - const char **subject_p, const char **extra_headers_p, int *need_8bit_cte_p); void load_ref_decorations(int flags); @@ -30,5 +29,6 @@ void load_ref_decorations(int flags); #define FORMAT_PATCH_NAME_MAX 64 void fmt_output_commit(struct strbuf *, struct commit *, struct rev_info *); void fmt_output_subject(struct strbuf *, const char *subject, struct rev_info *); +void fmt_output_email_subject(struct strbuf *, struct rev_info *); #endif @@ -1607,8 +1607,9 @@ void pp_title_line(struct pretty_print_context *pp, pp->preserve_subject ? "\n" : " "); strbuf_grow(sb, title.len + 1024); - if (pp->subject) { - strbuf_addstr(sb, pp->subject); + if (pp->print_email_subject) { + if (pp->rev) + fmt_output_email_subject(sb, pp->rev); if (needs_rfc2047_encoding(title.buf, title.len, RFC2047_SUBJECT)) add_rfc2047(sb, title.buf, title.len, encoding, RFC2047_SUBJECT); @@ -1818,7 +1819,7 @@ void pretty_print_commit(struct pretty_print_context *pp, } pp_header(pp, encoding, commit, &msg, sb); - if (pp->fmt != CMIT_FMT_ONELINE && !pp->subject) { + if (pp->fmt != CMIT_FMT_ONELINE && !pp->print_email_subject) { strbuf_addch(sb, '\n'); } @@ -1035,10 +1035,10 @@ static struct string_list *hide_refs; int parse_hide_refs_config(const char *var, const char *value, const char *section) { + const char *key; if (!strcmp("transfer.hiderefs", var) || - /* NEEDSWORK: use parse_config_key() once both are merged */ - (starts_with(var, section) && var[strlen(section)] == '.' && - !strcmp(var + strlen(section), ".hiderefs"))) { + (!parse_config_key(var, section, NULL, NULL, &key) && + !strcmp(key, "hiderefs"))) { char *ref; int len; @@ -254,7 +254,7 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir) if (!is_absolute_path(data.buf)) strbuf_addf(&path, "%s/", gitdir); strbuf_addbuf(&path, &data); - strbuf_addstr(sb, real_path(path.buf)); + strbuf_add_real_path(sb, path.buf); ret = 1; } else { strbuf_addstr(sb, gitdir); @@ -698,7 +698,7 @@ static const char *setup_discovered_git_dir(const char *gitdir, /* --work-tree is set without --git-dir; use discovered one */ if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) { if (offset != cwd->len && !is_absolute_path(gitdir)) - gitdir = real_pathdup(gitdir); + gitdir = real_pathdup(gitdir, 1); if (chdir(cwd->buf)) die_errno("Could not come back to cwd"); return setup_explicit_git_dir(gitdir, cwd, nongit_ok); @@ -806,7 +806,7 @@ static int canonicalize_ceiling_entry(struct string_list_item *item, /* Keep entry but do not canonicalize it */ return 1; } else { - char *real_path = real_pathdup(ceil); + char *real_path = real_pathdup(ceil, 0); if (!real_path) { return 0; } diff --git a/sha1_file.c b/sha1_file.c index 6628f06da3..2ee3c617a6 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2606,6 +2606,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset, while (delta_stack_nr) { void *delta_data; void *base = data; + void *external_base = NULL; unsigned long delta_size, base_size = size; int i; @@ -2632,6 +2633,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset, p->pack_name); mark_bad_packed_object(p, base_sha1); base = read_object(base_sha1, &type, &base_size); + external_base = base; } } @@ -2650,6 +2652,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset, "at offset %"PRIuMAX" from %s", (uintmax_t)curpos, p->pack_name); data = NULL; + free(external_base); continue; } @@ -2669,6 +2672,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset, error("failed to apply delta"); free(delta_data); + free(external_base); } *final_type = type; @@ -707,6 +707,17 @@ void strbuf_add_absolute_path(struct strbuf *sb, const char *path) strbuf_addstr(sb, path); } +void strbuf_add_real_path(struct strbuf *sb, const char *path) +{ + if (sb->len) { + struct strbuf resolved = STRBUF_INIT; + strbuf_realpath(&resolved, path, 1); + strbuf_addbuf(sb, &resolved); + strbuf_release(&resolved); + } else + strbuf_realpath(sb, path, 1); +} + int printf_ln(const char *fmt, ...) { int ret; @@ -441,6 +441,20 @@ extern int strbuf_getcwd(struct strbuf *sb); */ extern void strbuf_add_absolute_path(struct strbuf *sb, const char *path); +/** + * Canonize `path` (make it absolute, resolve symlinks, remove extra + * slashes) and append it to `sb`. Die with an informative error + * message if there is a problem. + * + * The directory part of `path` (i.e., everything up to the last + * dir_sep) must denote a valid, existing directory, but the last + * component need not exist. + * + * Callers that don't mind links should use the more lightweight + * strbuf_add_absolute_path() instead. + */ +extern void strbuf_add_real_path(struct strbuf *sb, const char *path); + /** * Normalize in-place the path contained in the strbuf. See diff --git a/submodule.c b/submodule.c index 3b98766a6b..0a2831d846 100644 --- a/submodule.c +++ b/submodule.c @@ -1403,7 +1403,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix, /* If it is an actual gitfile, it doesn't need migration. */ return; - real_old_git_dir = real_pathdup(old_git_dir); + real_old_git_dir = real_pathdup(old_git_dir, 1); sub = submodule_from_path(null_sha1, path); if (!sub) @@ -1412,7 +1412,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix, new_git_dir = git_path("modules/%s", sub->name); if (safe_create_leading_directories_const(new_git_dir) < 0) die(_("could not create directory '%s'"), new_git_dir); - real_new_git_dir = real_pathdup(new_git_dir); + real_new_git_dir = real_pathdup(new_git_dir, 1); if (!prefix) prefix = get_super_prefix(); @@ -1472,14 +1472,14 @@ void absorb_git_dir_into_superproject(const char *prefix, new_git_dir = git_path("modules/%s", sub->name); if (safe_create_leading_directories_const(new_git_dir) < 0) die(_("could not create directory '%s'"), new_git_dir); - real_new_git_dir = real_pathdup(new_git_dir); + real_new_git_dir = real_pathdup(new_git_dir, 1); connect_work_tree_and_git_dir(path, real_new_git_dir); free(real_new_git_dir); } else { /* Is it already absorbed into the superprojects git dir? */ - char *real_sub_git_dir = real_pathdup(sub_git_dir); - char *real_common_git_dir = real_pathdup(get_git_common_dir()); + char *real_sub_git_dir = real_pathdup(sub_git_dir, 1); + char *real_common_git_dir = real_pathdup(get_git_common_dir(), 1); if (!starts_with(real_sub_git_dir, real_common_git_dir)) relocate_single_git_dir_into_superproject(prefix, path); diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index 69174c6e31..0642ae7e6e 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -133,6 +133,15 @@ RewriteRule ^/ftp-redir/(.*)$ ftp://localhost:1000/$1 [R=302] RewriteRule ^/loop-redir/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-(.*) /$1 [R=302] RewriteRule ^/loop-redir/(.*)$ /loop-redir/x-$1 [R=302] +# redir-to/502/x?y -> really-redir-to?path=502/x&qs=y which returns 502 +# redir-to/x?y -> really-redir-to?path=x&qs=y -> x?y +RewriteCond %{QUERY_STRING} ^(.*)$ +RewriteRule ^/redir-to/(.*)$ /really-redir-to?path=$1&qs=%1 [R=302] +RewriteCond %{QUERY_STRING} ^path=502/(.*)&qs=(.*)$ +RewriteRule ^/really-redir-to$ - [R=502,L] +RewriteCond %{QUERY_STRING} ^path=(.*)&qs=(.*)$ +RewriteRule ^/really-redir-to$ /%1?%2 [R=302] + # The first rule issues a client-side redirect to something # that _doesn't_ look like a git repo. The second rule is a # server-side rewrite, so that it turns out the odd-looking diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 052f120216..afcca0d52c 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -1097,6 +1097,68 @@ test_expect_success 'multiple git -c appends config' ' test_cmp expect actual ' +test_expect_success 'last one wins: two level vars' ' + + # sec.var and sec.VAR are the same variable, as the first + # and the last level of a configuration variable name is + # case insensitive. + + echo VAL >expect && + + git -c sec.var=val -c sec.VAR=VAL config --get sec.var >actual && + test_cmp expect actual && + git -c SEC.var=val -c sec.var=VAL config --get sec.var >actual && + test_cmp expect actual && + + git -c sec.var=val -c sec.VAR=VAL config --get SEC.var >actual && + test_cmp expect actual && + git -c SEC.var=val -c sec.var=VAL config --get sec.VAR >actual && + test_cmp expect actual +' + +test_expect_success 'last one wins: three level vars' ' + + # v.a.r and v.A.r are not the same variable, as the middle + # level of a three-level configuration variable name is + # case sensitive. + + echo val >expect && + git -c v.a.r=val -c v.A.r=VAL config --get v.a.r >actual && + test_cmp expect actual && + git -c v.a.r=val -c v.A.r=VAL config --get V.a.R >actual && + test_cmp expect actual && + + # v.a.r and V.a.R are the same variable, as the first + # and the last level of a configuration variable name is + # case insensitive. + + echo VAL >expect && + git -c v.a.r=val -c v.a.R=VAL config --get v.a.r >actual && + test_cmp expect actual && + git -c v.a.r=val -c V.a.r=VAL config --get v.a.r >actual && + test_cmp expect actual && + git -c v.a.r=val -c v.a.R=VAL config --get V.a.R >actual && + test_cmp expect actual && + git -c v.a.r=val -c V.a.r=VAL config --get V.a.R >actual && + test_cmp expect actual +' + +for VAR in a .a a. a.0b a."b c". a."b c".0d +do + test_expect_success "git -c $VAR=VAL rejects invalid '$VAR'" ' + test_must_fail git -c "$VAR=VAL" config -l + ' +done + +for VAR in a.b a."b c".d +do + test_expect_success "git -c $VAR=VAL works with valid '$VAR'" ' + echo VAL >expect && + git -c "$VAR=VAL" config --get "$VAR" >actual && + test_cmp expect actual + ' +done + test_expect_success 'git -c is not confused by empty environment' ' GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list ' diff --git a/t/t1501-work-tree.sh b/t/t1501-work-tree.sh index cc5b870e58..b06210ec5e 100755 --- a/t/t1501-work-tree.sh +++ b/t/t1501-work-tree.sh @@ -423,4 +423,12 @@ test_expect_success '$GIT_WORK_TREE overrides $GIT_DIR/common' ' ) ' +test_expect_success 'error out gracefully on invalid $GIT_WORK_TREE' ' + ( + GIT_WORK_TREE=/.invalid/work/tree && + export GIT_WORK_TREE && + test_expect_code 128 git rev-parse + ) +' + test_done diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 5ffe78e920..aaa258daa3 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -394,4 +394,22 @@ test_expect_success 'diffs can be colorized' ' grep "$(printf "\\033")" output ' +test_expect_success 'patch-mode via -i prompts for files' ' + git reset --hard && + + echo one >file && + echo two >test && + git add -i <<-\EOF && + patch + test + + y + quit + EOF + + echo test >expect && + git diff --cached --name-only >actual && + test_cmp expect actual +' + test_done diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index 2de3e18ce6..89877e4b52 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -274,9 +274,7 @@ test_expect_success 'stash --invalid-option' ' git add file2 && test_must_fail git stash --invalid-option && test_must_fail git stash save --invalid-option && - test bar5,bar6 = $(cat file),$(cat file2) && - git stash -- -message-starting-with-dash && - test bar,bar2 = $(cat file),$(cat file2) + test bar5,bar6 = $(cat file),$(cat file2) ' test_expect_success 'stash an added file' ' @@ -775,4 +773,138 @@ test_expect_success 'stash is not confused by partial renames' ' test_path_is_missing file ' +test_expect_success 'push -m shows right message' ' + >foo && + git add foo && + git stash push -m "test message" && + echo "stash@{0}: On master: test message" >expect && + git stash list -1 >actual && + test_cmp expect actual +' + +test_expect_success 'create stores correct message' ' + >foo && + git add foo && + STASH_ID=$(git stash create "create test message") && + echo "On master: create test message" >expect && + git show --pretty=%s -s ${STASH_ID} >actual && + test_cmp expect actual +' + +test_expect_success 'create with multiple arguments for the message' ' + >foo && + git add foo && + STASH_ID=$(git stash create test untracked) && + echo "On master: test untracked" >expect && + git show --pretty=%s -s ${STASH_ID} >actual && + test_cmp expect actual +' + +test_expect_success 'stash -- <pathspec> stashes and restores the file' ' + >foo && + >bar && + git add foo bar && + git stash push -- foo && + test_path_is_file bar && + test_path_is_missing foo && + git stash pop && + test_path_is_file foo && + test_path_is_file bar +' + +test_expect_success 'stash with multiple pathspec arguments' ' + >foo && + >bar && + >extra && + git add foo bar extra && + git stash push -- foo bar && + test_path_is_missing bar && + test_path_is_missing foo && + test_path_is_file extra && + git stash pop && + test_path_is_file foo && + test_path_is_file bar && + test_path_is_file extra +' + +test_expect_success 'stash with file including $IFS character' ' + >"foo bar" && + >foo && + >bar && + git add foo* && + git stash push -- "foo b*" && + test_path_is_missing "foo bar" && + test_path_is_file foo && + test_path_is_file bar && + git stash pop && + test_path_is_file "foo bar" && + test_path_is_file foo && + test_path_is_file bar +' + +test_expect_success 'stash with pathspec matching multiple paths' ' + echo original >file && + echo original >other-file && + git commit -m "two" file other-file && + echo modified >file && + echo modified >other-file && + git stash push -- "*file" && + echo original >expect && + test_cmp expect file && + test_cmp expect other-file && + git stash pop && + echo modified >expect && + test_cmp expect file && + test_cmp expect other-file +' + +test_expect_success 'stash push -p with pathspec shows no changes only once' ' + >foo && + git add foo && + git commit -m "tmp" && + git stash push -p foo >actual && + echo "No local changes to save" >expect && + git reset --hard HEAD~ && + test_cmp expect actual +' + +test_expect_success 'stash push with pathspec shows no changes when there are none' ' + >foo && + git add foo && + git commit -m "tmp" && + git stash push foo >actual && + echo "No local changes to save" >expect && + git reset --hard HEAD~ && + test_cmp expect actual +' + +test_expect_success 'stash push with pathspec not in the repository errors out' ' + >untracked && + test_must_fail git stash push untracked && + test_path_is_file untracked +' + +test_expect_success 'untracked files are left in place when -u is not given' ' + >file && + git add file && + >untracked && + git stash push file && + test_path_is_file untracked +' + +test_expect_success 'stash without verb with pathspec' ' + >"foo bar" && + >foo && + >bar && + git add foo* && + git stash -- "foo b*" && + test_path_is_missing "foo bar" && + test_path_is_file foo && + test_path_is_file bar && + git stash pop && + test_path_is_file "foo bar" && + test_path_is_file foo && + test_path_is_file bar +' + test_done diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh index f372fc8ca8..193adc7b68 100755 --- a/t/t3905-stash-include-untracked.sh +++ b/t/t3905-stash-include-untracked.sh @@ -185,4 +185,30 @@ test_expect_success 'stash save --all is stash poppable' ' test -s .gitignore ' +test_expect_success 'stash push --include-untracked with pathspec' ' + >foo && + >bar && + git stash push --include-untracked -- foo && + test_path_is_file bar && + test_path_is_missing foo && + git stash pop && + test_path_is_file bar && + test_path_is_file foo +' + +test_expect_success 'stash push with $IFS character' ' + >"foo bar" && + >foo && + >bar && + git add foo* && + git stash push --include-untracked -- "foo b*" && + test_path_is_missing "foo bar" && + test_path_is_file foo && + test_path_is_file bar && + git stash pop && + test_path_is_file "foo bar" && + test_path_is_file foo && + test_path_is_file bar +' + test_done diff --git a/t/t4035-diff-quiet.sh b/t/t4035-diff-quiet.sh index 461f4bb583..2f1737fcef 100755 --- a/t/t4035-diff-quiet.sh +++ b/t/t4035-diff-quiet.sh @@ -152,4 +152,13 @@ test_expect_success 'git diff --quiet ignores stat-change only entries' ' test_expect_code 1 git diff --quiet ' +test_expect_success 'git diff --quiet on a path that need conversion' ' + echo "crlf.txt text=auto" >.gitattributes && + printf "Hello\r\nWorld\r\n" >crlf.txt && + git add .gitattributes crlf.txt && + + printf "Hello\r\nWorld\n" >crlf.txt && + git diff --quiet crlf.txt +' + test_done diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh index 9d87777b59..d0377fae5c 100755 --- a/t/t4211-line-log.sh +++ b/t/t4211-line-log.sh @@ -106,4 +106,14 @@ test_expect_success '-L with --output' ' test_line_count = 70 log ' +test_expect_success 'range_set_union' ' + test_seq 500 > c.c && + git add c.c && + git commit -m "many lines" && + test_seq 1000 > c.c && + git add c.c && + git commit -m "modify many lines" && + git log $(for x in $(test_seq 200); do echo -L $((2*x)),+1:c.c; done) +' + test_done diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh index b69ece1d66..87308cdced 100755 --- a/t/t5550-http-fetch-dumb.sh +++ b/t/t5550-http-fetch-dumb.sh @@ -387,5 +387,14 @@ test_expect_success 'http-alternates triggers not-from-user protocol check' ' clone $HTTPD_URL/dumb/evil.git evil-user ' +test_expect_success 'can redirect through non-"info/refs?service=git-upload-pack" URL' ' + git clone "$HTTPD_URL/redir-to/dumb/repo.git" +' + +test_expect_success 'print HTTP error when any intermediate redirect throws error' ' + test_must_fail git clone "$HTTPD_URL/redir-to/502" 2> stderr && + test_i18ngrep "unable to access.*/redir-to/502" stderr +' + stop_httpd test_done diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index c87dc1f8bc..834a9ed168 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -584,7 +584,7 @@ test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' ' test_when_finished "git checkout master" && git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual && sed -e "s/^\* / /" actual >expect && - git checkout --orphan HEAD && + git checkout --orphan orphaned-branch && git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual && test_cmp expect actual ' diff --git a/t/t7518-ident-corner-cases.sh b/t/t7518-ident-corner-cases.sh new file mode 100755 index 0000000000..b22f631261 --- /dev/null +++ b/t/t7518-ident-corner-cases.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +test_description='corner cases in ident strings' +. ./test-lib.sh + +# confirm that we do not segfault _and_ that we do not say "(null)", as +# glibc systems will quietly handle our NULL pointer +# +# Note also that we can't use "env" here because we need to unset a variable, +# and "-u" is not portable. +test_expect_success 'empty name and missing email' ' + ( + sane_unset GIT_AUTHOR_EMAIL && + GIT_AUTHOR_NAME= && + test_must_fail git commit --allow-empty -m foo 2>err && + test_i18ngrep ! null err + ) +' + +test_expect_success 'commit rejects all-crud name' ' + test_must_fail env GIT_AUTHOR_NAME=" .;<>" \ + git commit --allow-empty -m foo +' + +# We must test the actual error message here, as an unwanted +# auto-detection could fail for other reasons. +test_expect_success 'empty configured name does not auto-detect' ' + ( + sane_unset GIT_AUTHOR_NAME && + test_must_fail \ + git -c user.name= commit --allow-empty -m foo 2>err && + test_i18ngrep "empty ident name" err + ) +' + +test_done diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 0f398dd160..60a80f60b2 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -148,7 +148,6 @@ cat >expected-cc <<\EOF !two@example.com! !three@example.com! !four@example.com! -!five@example.com! EOF " @@ -159,9 +158,9 @@ test_expect_success $PREREQ 'cc trailer with various syntax' ' Test Cc: trailers. Cc: one@example.com - Cc: <two@example.com> # this is part of the name - Cc: <three@example.com>, <four@example.com> # not.five@example.com - Cc: "Some # Body" <five@example.com> [part.of.name.too] + Cc: <two@example.com> # trailing comments are ignored + Cc: <three@example.com>, <not.four@example.com> one address per line + Cc: "Some # Body" <four@example.com> [ <also.a.comment> ] EOF clean_fake_sendmail && git send-email -1 --to=recipient@example.com \ diff --git a/upload-pack.c b/upload-pack.c index 7597ba3405..ffb028d623 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -822,9 +822,13 @@ static void receive_needs(void) use_include_tag = 1; o = parse_object(sha1_buf); - if (!o) + if (!o) { + packet_write_fmt(1, + "ERR upload-pack: not our ref %s", + sha1_to_hex(sha1_buf)); die("git upload-pack: not our ref %s", sha1_to_hex(sha1_buf)); + } if (!(o->flags & WANTED)) { o->flags |= WANTED; if (!((allow_unadvertised_object_request & ALLOW_ANY_SHA1) == ALLOW_ANY_SHA1 diff --git a/worktree.c b/worktree.c index d633761575..fa7bc67a50 100644 --- a/worktree.c +++ b/worktree.c @@ -175,7 +175,7 @@ struct worktree **get_worktrees(unsigned flags) struct dirent *d; int counter = 0, alloc = 2; - list = xmalloc(alloc * sizeof(struct worktree *)); + ALLOC_ARRAY(list, alloc); list[counter++] = get_main_worktree(); @@ -255,7 +255,7 @@ struct worktree *find_worktree(struct worktree **list, return wt; arg = prefix_filename(prefix, strlen(prefix), arg); - path = real_pathdup(arg); + path = real_pathdup(arg, 1); for (; *list; list++) if (!fspathcmp(path, real_path((*list)->path))) break; @@ -440,23 +440,6 @@ int xmkstemp(char *template) return fd; } -/* git_mkstemp() - create tmp file honoring TMPDIR variable */ -int git_mkstemp(char *path, size_t len, const char *template) -{ - const char *tmp; - size_t n; - - tmp = getenv("TMPDIR"); - if (!tmp) - tmp = "/tmp"; - n = snprintf(path, len, "%s/%s", tmp, template); - if (len <= n) { - errno = ENAMETOOLONG; - return -1; - } - return mkstemp(path); -} - /* Adapted from libiberty's mkstemp.c. */ #undef TMP_MAX @@ -531,13 +514,6 @@ int git_mkstemp_mode(char *pattern, int mode) return git_mkstemps_mode(pattern, 0, mode); } -#ifdef NO_MKSTEMPS -int gitmkstemps(char *pattern, int suffix_len) -{ - return git_mkstemps_mode(pattern, suffix_len, 0600); -} -#endif - int xmkstemp_mode(char *template, int mode) { int fd; |