diff options
74 files changed, 1225 insertions, 587 deletions
diff --git a/.gitignore b/.gitignore index bf66648e2c..f1acd3e777 100644 --- a/.gitignore +++ b/.gitignore @@ -186,6 +186,7 @@ /test-mktemp /test-parse-options /test-path-utils +/test-regex /test-revision-walking /test-run-command /test-sha1 diff --git a/Documentation/RelNotes/1.7.11.6.txt b/Documentation/RelNotes/1.7.11.6.txt index e548a59824..ba7d3c3966 100644 --- a/Documentation/RelNotes/1.7.11.6.txt +++ b/Documentation/RelNotes/1.7.11.6.txt @@ -4,31 +4,81 @@ Git v1.7.11.6 Release Notes Fixes since v1.7.11.5 --------------------- -This is primarily documentation and low-impact code clarification. - - - "ciabot" script (in contrib/) has been updated with extensive + * "ciabot" script (in contrib/) has been updated with extensive documentation. - - The "--rebase" option to "git pull" can be abbreviated to "-r", - but we didn't document it. + * "git foo" errored out with "Not a directory" when the user had a + non-directory on $PATH, and worse yet it masked an alias "foo" from + running. - - It was generally understood that "--long-option"s to many of our - subcommands can be abbreviated to the unique prefix, but it was not - easy to find it described for new readers of the documentation set. + * When the user exports a non-default IFS without HT, scripts that + rely on being able to parse "ls-files -s | while read a b c..." + started to fail. Protect them from such a misconfiguration. - - The "--topo-order", "--date-order" (and the lack of either means - the default order) options to "rev-list" and "log" family of - commands were poorly described in the documentation. + * When the user gives an argument that can be taken as both a + revision name and a pathname without disambiguating with "--", we + used to give a help message "Use '--' to separate". The message + has been clarified to show where that '--' goes on the command + line. + + * Documentation for the configuration file format had a confusing + example. - - Older parts of the documentation described as if having a regular + * Older parts of the documentation described as if having a regular file in .git/refs/ hierarchy were the only way to have branches and tags, which is not true for quite some time. - - A utility shell function test_seq has been added as a replacement - for the 'seq' utility found on some platforms. + * It was generally understood that "--long-option"s to many of our + subcommands can be abbreviated to the unique prefix, but it was not + easy to find it described for new readers of the documentation set. - - Fallback 'getpass' implementation made unportable use of stdio API. + * The "--topo-order", "--date-order" (and the lack of either means + the default order) options to "rev-list" and "log" family of + commands were poorly described in the documentation. - - "git commit --amend" let the user edit the log message and then + * "git commit --amend" let the user edit the log message and then died when the human-readable committer name was given insufficiently by getpwent(3). + + * The exit status code from "git config" was way overspecified while + being incorrect. The implementation has been updated to give the + documented status for a case that was documented, and introduce a + new code for "all other errors". + + * The output from "git diff -B" for a file that ends with an + incomplete line did not put "\ No newline..." on a line of its own. + + * "git diff" had a confusion between taking data from a path in the + working tree and taking data from an object that happens to have + name 0{40} recorded in a tree. + + * The "--rebase" option to "git pull" can be abbreviated to "-r", + but we didn't document it. + + * When "git push" triggered the automatic gc on the receiving end, a + message from "git prune" that said it was removing cruft leaked to + the standard output, breaking the communication protocol. + + * The reflog entries left by "git rebase" and "git rebase -i" were + inconsistent (the interactive one gave an abbreviated object name). + + * "git send-email" did not unquote encoded words that appear on the + header correctly, and lost "_" from strings. + + * "git stash apply/pop" did not trigger "rerere" upon conflicts + unlike other mergy operations. + + * "git submodule <cmd> path" did not error out when the path to the + submodule was misspelt. + + * "git submodule update -f" did not update paths in the working tree + that has local changes. + (merge 01d4721 sz/submodule-force-update later to maint). + + * "gitweb" when used with PATH_INFO failed to notice directories with + SP (and other characters that need URL-style quoting) in them. + + * Fallback 'getpass' implementation made unportable use of stdio API. + + * A utility shell function test_seq has been added as a replacement + for the 'seq' utility found on some platforms. diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf index a26d245ab4..1273a85c8a 100644 --- a/Documentation/asciidoc.conf +++ b/Documentation/asciidoc.conf @@ -36,7 +36,7 @@ ifndef::git-asciidoc-no-roff[] # v1.72 breaks with this because it replaces dots not in roff requests. [listingblock] <example><title>{title}</title> -<literallayout> +<literallayout class="monospaced"> ifdef::doctype-manpage[] .ft C endif::doctype-manpage[] @@ -53,7 +53,7 @@ ifdef::doctype-manpage[] # The following two small workarounds insert a simple paragraph after screen [listingblock] <example><title>{title}</title> -<literallayout> +<literallayout class="monospaced"> | </literallayout><simpara></simpara> {title#}</example> diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 63a251612a..7958a47006 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -84,11 +84,11 @@ entries; instead, unmerged entries are ignored. When checking out paths from the index, check out stage #2 ('ours') or #3 ('theirs') for unmerged paths. --b:: +-b <new_branch>:: Create a new branch named <new_branch> and start it at <start_point>; see linkgit:git-branch[1] for details. --B:: +-B <new_branch>:: Creates the branch <new_branch> and start it at <start_point>; if it already exists, then reset it to <start_point>. This is equivalent to running "git branch" with "-f"; see @@ -124,7 +124,7 @@ explicitly give a name with '-b' in such a case. <commit> is not a branch name. See the "DETACHED HEAD" section below for details. ---orphan:: +--orphan <new_branch>:: Create a new 'orphan' branch, named <new_branch>, started from <start_point> and switch to it. The first commit made on this new branch will have no parents and it will be the root of a new @@ -367,6 +367,18 @@ $ git checkout hello.c <3> <2> take a file out of another commit <3> restore hello.c from the index + +If you want to check out _all_ C source files out of the index, +you can say ++ +------------ +$ git checkout -- '*.c' +------------ ++ +Note the quotes around `*.c`. The file `hello.c` will also be +checked out, even though it is no longer in the working tree, +because the file globbing is used to match entries in the index +(not in the working tree by the shell). ++ If you have an unfortunate branch that is named `hello.c`, this step would be confused as an instruction to switch to that branch. You should instead write: diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index d9463cb387..21b8f6110b 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -54,16 +54,16 @@ configuration file by default, and options '--system', '--global', '--file <filename>' can be used to tell the command to write to that location (you can say '--local' but that is the default). -This command will fail (with exit code ret) if: +This command will fail with non-zero status upon error. Some exit +codes are: . The config file is invalid (ret=3), . can not write to the config file (ret=4), . no section or name was provided (ret=2), . the section or key is invalid (ret=1), . you try to unset an option which does not exist (ret=5), -. you try to unset/set an option for which multiple lines match (ret=5), -. you try to use an invalid regexp (ret=6), or -. you use '--global' option without $HOME being properly set (ret=128). +. you try to unset/set an option for which multiple lines match (ret=5), or +. you try to use an invalid regexp (ret=6). On success, the command returns the exit code 0. @@ -258,7 +258,7 @@ Given a .git/config like this: ; Proxy settings [core] - gitproxy="proxy-command" for kernel.org + gitproxy=proxy-command for kernel.org gitproxy=default-proxy ; for all the rest you can set the filemode to true with @@ -333,7 +333,7 @@ To actually match only values with an exclamation mark, you have to To add a new proxy, without altering any of the existing ones, use ------------ -% git config core.gitproxy '"proxy-command" for example.com' +% git config --add core.gitproxy '"proxy-command" for example.com' ------------ An example to use customized color from the configuration in your diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index fbbbcb282c..2de7bf0900 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -149,6 +149,11 @@ submodule with the `--init` option. + If `--recursive` is specified, this command will recurse into the registered submodules, and update any nested submodules within. ++ +If `--force` is specified, the submodule will be checked out (using +`git checkout --force` if appropriate), even if the commit specified in the +index of the containing repository already matches the commit checked out in +the submodule. summary:: Show commit summary between the given commit (defaults to HEAD) and @@ -210,7 +215,9 @@ OPTIONS This option is only valid for add and update commands. When running add, allow adding an otherwise ignored submodule path. When running update, throw away local changes in submodules when - switching to a different commit. + switching to a different commit; and always run a checkout operation + in the submodule, even if the commit listed in the index of the + containing repository matches the commit checked out in the submodule. --cached:: This option is only valid for status and summary commands. These diff --git a/Documentation/git.txt b/Documentation/git.txt index 677eb95995..165d13ad22 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -44,9 +44,10 @@ unreleased) version of git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v1.7.11.5/git.html[documentation for release 1.7.11.5] +* link:v1.7.11.6/git.html[documentation for release 1.7.11.6] * release notes for + link:RelNotes/1.7.11.6.txt[1.7.11.6], link:RelNotes/1.7.11.5.txt[1.7.11.5], link:RelNotes/1.7.11.4.txt[1.7.11.4], link:RelNotes/1.7.11.3.txt[1.7.11.3], diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt index 3e72a5d68e..f6ba90c2da 100644 --- a/Documentation/gitcli.txt +++ b/Documentation/gitcli.txt @@ -37,11 +37,28 @@ arguments. Here are the rules: file called HEAD in your work tree, `git diff HEAD` is ambiguous, and you have to say either `git diff HEAD --` or `git diff -- HEAD` to disambiguate. - ++ When writing a script that is expected to handle random user-input, it is a good practice to make it explicit which arguments are which by placing disambiguating `--` at appropriate places. + * Many commands allow wildcards in paths, but you need to protect + them from getting globbed by the shell. These two mean different + things: ++ +-------------------------------- +$ git checkout -- *.c +$ git checkout -- \*.c +-------------------------------- ++ +The former lets your shell expand the fileglob, and you are asking +the dot-C files in your working tree to be overwritten with the version +in the index. The latter passes the `*.c` to Git, and you are asking +the paths in the index that match the pattern to be checked out to your +working tree. After running `git add hello.c; rm hello.c`, you will _not_ +see `hello.c` in your working tree with the former, but with the latter +you will. + Here are the rules regarding the "flags" that you should follow when you are scripting git: diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt index dc0070bcb7..69d996bc38 100644 --- a/Documentation/revisions.txt +++ b/Documentation/revisions.txt @@ -213,6 +213,13 @@ of 'r1' and 'r2' and is defined as It is the set of commits that are reachable from either one of 'r1' or 'r2' but not from both. +In these two shorthands, you can omit one end and let it default to HEAD. +For example, 'origin..' is a shorthand for 'origin..HEAD' and asks "What +did I do since I forked from the origin branch?" Similarly, '..origin' +is a shorthand for 'HEAD..origin' and asks "What did the origin do since +I forked from them?" Note that '..' would mean 'HEAD..HEAD' which is an +empty range that is both reachable and unreachable from HEAD. + Two other shorthands for naming a set that is formed by a commit and its parent commits exist. The 'r1{caret}@' notation means all parents of 'r1'. 'r1{caret}!' includes commit 'r1' but excludes diff --git a/Documentation/user-manual.conf b/Documentation/user-manual.conf index 339b30919e..d87294de2f 100644 --- a/Documentation/user-manual.conf +++ b/Documentation/user-manual.conf @@ -14,7 +14,7 @@ ifdef::backend-docbook[] # "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this. [listingblock] <example><title>{title}</title> -<literallayout> +<literallayout class="monospaced"> | </literallayout> {title#}</example> diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index bab4056445..754b94e0d2 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.7.11.5 +DEF_VER=v1.7.11.6 LF=' ' @@ -495,6 +495,7 @@ TEST_PROGRAMS_NEED_X += test-mergesort TEST_PROGRAMS_NEED_X += test-mktemp TEST_PROGRAMS_NEED_X += test-parse-options TEST_PROGRAMS_NEED_X += test-path-utils +TEST_PROGRAMS_NEED_X += test-regex TEST_PROGRAMS_NEED_X += test-revision-walking TEST_PROGRAMS_NEED_X += test-run-command TEST_PROGRAMS_NEED_X += test-scrap-cache-tree @@ -44,7 +44,7 @@ extern int check_pager_config(const char *cmd); struct diff_options; extern void setup_diff_pager(struct diff_options *); -extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, char **buf, unsigned long *buf_size); +extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, int sha1_valid, char **buf, unsigned long *buf_size); extern int cmd_add(int argc, const char **argv, const char *prefix); extern int cmd_annotate(int argc, const char **argv, const char *prefix); diff --git a/builtin/apply.c b/builtin/apply.c index b4428ea34f..d2180b0374 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -184,7 +184,6 @@ struct patch { int is_new, is_delete; /* -1 = unknown, 0 = false, 1 = true */ int rejected; unsigned ws_rule; - unsigned long deflate_origlen; int lines_added, lines_deleted; int score; unsigned int is_toplevel_relative:1; @@ -1087,15 +1086,23 @@ static int gitdiff_unrecognized(const char *line, struct patch *patch) return -1; } -static const char *stop_at_slash(const char *line, int llen) +/* + * Skip p_value leading components from "line"; as we do not accept + * absolute paths, return NULL in that case. + */ +static const char *skip_tree_prefix(const char *line, int llen) { - int nslash = p_value; + int nslash; int i; + if (!p_value) + return (llen && line[0] == '/') ? NULL : line; + + nslash = p_value; for (i = 0; i < llen; i++) { int ch = line[i]; if (ch == '/' && --nslash <= 0) - return &line[i]; + return (i == 0) ? NULL : &line[i + 1]; } return NULL; } @@ -1125,12 +1132,11 @@ static char *git_header_name(const char *line, int llen) if (unquote_c_style(&first, line, &second)) goto free_and_fail1; - /* advance to the first slash */ - cp = stop_at_slash(first.buf, first.len); - /* we do not accept absolute paths */ - if (!cp || cp == first.buf) + /* strip the a/b prefix including trailing slash */ + cp = skip_tree_prefix(first.buf, first.len); + if (!cp) goto free_and_fail1; - strbuf_remove(&first, 0, cp + 1 - first.buf); + strbuf_remove(&first, 0, cp - first.buf); /* * second points at one past closing dq of name. @@ -1144,22 +1150,21 @@ static char *git_header_name(const char *line, int llen) if (*second == '"') { if (unquote_c_style(&sp, second, NULL)) goto free_and_fail1; - cp = stop_at_slash(sp.buf, sp.len); - if (!cp || cp == sp.buf) + cp = skip_tree_prefix(sp.buf, sp.len); + if (!cp) goto free_and_fail1; /* They must match, otherwise ignore */ - if (strcmp(cp + 1, first.buf)) + if (strcmp(cp, first.buf)) goto free_and_fail1; strbuf_release(&sp); return strbuf_detach(&first, NULL); } /* unquoted second */ - cp = stop_at_slash(second, line + llen - second); - if (!cp || cp == second) + cp = skip_tree_prefix(second, line + llen - second); + if (!cp) goto free_and_fail1; - cp++; - if (line + llen - cp != first.len + 1 || + if (line + llen - cp != first.len || memcmp(first.buf, cp, first.len)) goto free_and_fail1; return strbuf_detach(&first, NULL); @@ -1171,10 +1176,9 @@ static char *git_header_name(const char *line, int llen) } /* unquoted first name */ - name = stop_at_slash(line, llen); - if (!name || name == line) + name = skip_tree_prefix(line, llen); + if (!name) return NULL; - name++; /* * since the first name is unquoted, a dq if exists must be @@ -1188,10 +1192,9 @@ static char *git_header_name(const char *line, int llen) if (unquote_c_style(&sp, second, NULL)) goto free_and_fail2; - np = stop_at_slash(sp.buf, sp.len); - if (!np || np == sp.buf) + np = skip_tree_prefix(sp.buf, sp.len); + if (!np) goto free_and_fail2; - np++; len = sp.buf + sp.len - np; if (len < second - name && @@ -1223,13 +1226,27 @@ static char *git_header_name(const char *line, int llen) case '\n': return NULL; case '\t': case ' ': - second = stop_at_slash(name + len, line_len - len); + /* + * Is this the separator between the preimage + * and the postimage pathname? Again, we are + * only interested in the case where there is + * no rename, as this is only to set def_name + * and a rename patch has the names elsewhere + * in an unambiguous form. + */ + if (!name[len + 1]) + return NULL; /* no postimage name */ + second = skip_tree_prefix(name + len + 1, + line_len - (len + 1)); if (!second) return NULL; - second++; - if (second[len] == '\n' && !strncmp(name, second, len)) { + /* + * Does len bytes starting at "name" and "second" + * (that are separated by one HT or SP we just + * found) exactly match? + */ + if (second[len] == '\n' && !strncmp(name, second, len)) return xmemdupz(name, len); - } } } } diff --git a/builtin/blame.c b/builtin/blame.c index 960c58d855..cad4111a36 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -110,6 +110,7 @@ static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, long ctxlen, int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, + int sha1_valid, char **buf, unsigned long *buf_size) { @@ -117,7 +118,7 @@ int textconv_object(const char *path, struct userdiff_driver *textconv; df = alloc_filespec(path); - fill_filespec(df, sha1, mode); + fill_filespec(df, sha1, sha1_valid, mode); textconv = get_textconv(df); if (!textconv) { free_filespec(df); @@ -142,7 +143,7 @@ static void fill_origin_blob(struct diff_options *opt, num_read_blob++; if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) && - textconv_object(o->path, o->mode, o->blob_sha1, &file->ptr, &file_size)) + textconv_object(o->path, o->mode, o->blob_sha1, 1, &file->ptr, &file_size)) ; else file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size); @@ -406,8 +407,7 @@ static struct origin *find_origin(struct scoreboard *sb, paths[1] = NULL; diff_tree_setup_paths(paths, &diff_opts); - if (diff_setup_done(&diff_opts) < 0) - die("diff-setup"); + diff_setup_done(&diff_opts); if (is_null_sha1(origin->commit->object.sha1)) do_diff_cache(parent->tree->object.sha1, &diff_opts); @@ -493,8 +493,7 @@ static struct origin *find_rename(struct scoreboard *sb, diff_opts.single_follow = origin->path; paths[0] = NULL; diff_tree_setup_paths(paths, &diff_opts); - if (diff_setup_done(&diff_opts) < 0) - die("diff-setup"); + diff_setup_done(&diff_opts); if (is_null_sha1(origin->commit->object.sha1)) do_diff_cache(parent->tree->object.sha1, &diff_opts); @@ -1074,8 +1073,7 @@ static int find_copy_in_parent(struct scoreboard *sb, paths[0] = NULL; diff_tree_setup_paths(paths, &diff_opts); - if (diff_setup_done(&diff_opts) < 0) - die("diff-setup"); + diff_setup_done(&diff_opts); /* Try "find copies harder" on new path if requested; * we do not want to use diffcore_rename() actually to @@ -2123,7 +2121,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt, switch (st.st_mode & S_IFMT) { case S_IFREG: if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) && - textconv_object(read_from, mode, null_sha1, &buf_ptr, &buf_len)) + textconv_object(read_from, mode, null_sha1, 0, &buf_ptr, &buf_len)) strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1); else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size) die_errno("cannot open or read '%s'", read_from); @@ -2515,7 +2513,7 @@ parse_done: die("no such path %s in %s", path, final_commit_name); if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) && - textconv_object(path, o->mode, o->blob_sha1, (char **) &sb.final_buf, + textconv_object(path, o->mode, o->blob_sha1, 1, (char **) &sb.final_buf, &sb.final_buf_size)) ; else diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 36a9104433..60568f9c39 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -146,7 +146,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) die("git cat-file --textconv %s: <object> must be <sha1:path>", obj_name); - if (!textconv_object(obj_context.path, obj_context.mode, sha1, &buf, &size)) + if (!textconv_object(obj_context.path, obj_context.mode, sha1, 1, &buf, &size)) die("git cat-file --textconv: unable to run textconv on %s", obj_name); break; diff --git a/builtin/checkout.c b/builtin/checkout.c index e060efb2a2..45aa0a8701 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -315,8 +315,7 @@ static void show_local_changes(struct object *head, struct diff_options *opts) init_revisions(&rev, NULL); rev.diffopt.flags = opts->flags; rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS; - if (diff_setup_done(&rev.diffopt) < 0) - die(_("diff_setup_done failed")); + diff_setup_done(&rev.diffopt); add_pending_object(&rev, head, NULL); run_diff_index(&rev, 0); } diff --git a/builtin/config.c b/builtin/config.c index 33c8820af6..b44277c23e 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -160,7 +160,7 @@ static int show_config(const char *key_, const char *value_, void *cb) static int get_value(const char *key_, const char *regex_) { - int ret = -1; + int ret = CONFIG_GENERIC_ERROR; char *global = NULL, *repo_config = NULL; const char *system_wide = NULL, *local; struct config_include_data inc = CONFIG_INCLUDE_INIT; @@ -198,11 +198,14 @@ static int get_value(const char *key_, const char *regex_) if (regcomp(key_regexp, key, REG_EXTENDED)) { fprintf(stderr, "Invalid key pattern: %s\n", key_); free(key); + ret = CONFIG_INVALID_PATTERN; goto free_strings; } } else { - if (git_config_parse_key(key_, &key, NULL)) + if (git_config_parse_key(key_, &key, NULL)) { + ret = CONFIG_INVALID_KEY; goto free_strings; + } } if (regex_) { @@ -214,6 +217,7 @@ static int get_value(const char *key_, const char *regex_) regexp = (regex_t*)xmalloc(sizeof(regex_t)); if (regcomp(regexp, regex_, REG_EXTENDED)) { fprintf(stderr, "Invalid pattern: %s\n", regex_); + ret = CONFIG_INVALID_PATTERN; goto free_strings; } } diff --git a/builtin/diff.c b/builtin/diff.c index da8f6aac2b..9650be2c50 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -29,6 +29,8 @@ static void stuff_change(struct diff_options *opt, unsigned old_mode, unsigned new_mode, const unsigned char *old_sha1, const unsigned char *new_sha1, + int old_sha1_valid, + int new_sha1_valid, const char *old_name, const char *new_name) { @@ -54,8 +56,8 @@ static void stuff_change(struct diff_options *opt, one = alloc_filespec(old_name); two = alloc_filespec(new_name); - fill_filespec(one, old_sha1, old_mode); - fill_filespec(two, new_sha1, new_mode); + fill_filespec(one, old_sha1, old_sha1_valid, old_mode); + fill_filespec(two, new_sha1, new_sha1_valid, new_mode); diff_queue(&diff_queued_diff, one, two); } @@ -84,6 +86,7 @@ static int builtin_diff_b_f(struct rev_info *revs, stuff_change(&revs->diffopt, blob[0].mode, canon_mode(st.st_mode), blob[0].sha1, null_sha1, + 1, 0, path, path); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); @@ -108,6 +111,7 @@ static int builtin_diff_blobs(struct rev_info *revs, stuff_change(&revs->diffopt, blob[0].mode, blob[1].mode, blob[0].sha1, blob[1].sha1, + 1, 1, blob[0].name, blob[1].name); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); @@ -298,8 +302,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) argc = setup_revisions(argc, argv, &rev, NULL); if (!rev.diffopt.output_format) { rev.diffopt.output_format = DIFF_FORMAT_PATCH; - if (diff_setup_done(&rev.diffopt) < 0) - die(_("diff_setup_done failed")); + diff_setup_done(&rev.diffopt); } DIFF_OPT_SET(&rev.diffopt, RECURSIVE); diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index b01d76a243..0c5294e5e8 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -962,7 +962,9 @@ static int opt_parse_sort(const struct option *opt, const char *arg, int unset) if (!arg) /* should --no-sort void the list ? */ return -1; - *sort_tail = s = xcalloc(1, sizeof(*s)); + s = xcalloc(1, sizeof(*s)); + s->next = *sort_tail; + *sort_tail = s; if (*arg == '-') { s->reverse = 1; diff --git a/builtin/merge.c b/builtin/merge.c index dd50a0c57b..e81fde6d79 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -404,8 +404,7 @@ static void finish(struct commit *head_commit, opts.output_format |= DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; opts.detect_rename = DIFF_DETECT_RENAME; - if (diff_setup_done(&opts) < 0) - die(_("diff_setup_done failed")); + diff_setup_done(&opts); diff_tree_sha1(head, new_head, "", &opts); diffcore_std(&opts); diff_flush(&opts); diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 0afb8b2896..3f05d971ec 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -977,7 +977,8 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) const char *argv_gc_auto[] = { "gc", "--auto", "--quiet", NULL, }; - run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); + int opt = RUN_GIT_CMD | RUN_COMMAND_STDOUT_TO_STDERR; + run_command_v_opt(argv_gc_auto, opt); } if (auto_update_server_info) update_server_info(0); diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 13495b88f5..47b4e7adb9 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -224,6 +224,7 @@ static int try_difference(const char *arg) const char *next; const char *this; int symmetric; + static const char head_by_default[] = "HEAD"; if (!(dotdot = strstr(arg, ".."))) return 0; @@ -235,9 +236,20 @@ static int try_difference(const char *arg) next += symmetric; if (!*next) - next = "HEAD"; + next = head_by_default; if (dotdot == arg) - this = "HEAD"; + this = head_by_default; + + if (this == head_by_default && next == head_by_default && + !symmetric) { + /* + * Just ".."? That is not a range but the + * pathspec for the parent directory. + */ + *dotdot = '.'; + return 0; + } + if (!get_sha1(this, sha1) && !get_sha1(next, end)) { show_rev(NORMAL, end, next); show_rev(symmetric ? NORMAL : REVERSED, sha1, this); @@ -1103,6 +1103,7 @@ extern int update_server_info(int); #define CONFIG_NO_WRITE 4 #define CONFIG_NOTHING_SET 5 #define CONFIG_INVALID_PATTERN 6 +#define CONFIG_GENERIC_ERROR 7 typedef int (*config_fn_t)(const char *, const char *, void *); extern int git_default_config(const char *, const char *, void *); diff --git a/combine-diff.c b/combine-diff.c index 9786680368..bb1cc96c4e 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -111,7 +111,7 @@ static char *grab_blob(const unsigned char *sha1, unsigned int mode, return xcalloc(1, 1); } else if (textconv) { struct diff_filespec *df = alloc_filespec(path); - fill_filespec(df, sha1, mode); + fill_filespec(df, sha1, 1, mode); *size = fill_textconv(textconv, df, &blob); free_filespec(df); } else { @@ -823,7 +823,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent, &result_size, NULL, NULL); } else if (textconv) { struct diff_filespec *df = alloc_filespec(elem->path); - fill_filespec(df, null_sha1, st.st_mode); + fill_filespec(df, null_sha1, 0, st.st_mode); result_size = fill_textconv(textconv, df, &result); free_filespec(df); } else if (0 <= (fd = open(elem->path, O_RDONLY))) { diff --git a/diff-lib.c b/diff-lib.c index fc0dff31b5..f35de0ffa0 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -206,7 +206,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option) if (silent_on_removed) continue; diff_addremove(&revs->diffopt, '-', ce->ce_mode, - ce->sha1, ce->name, 0); + ce->sha1, !is_null_sha1(ce->sha1), + ce->name, 0); continue; } changed = match_stat_with_submodule(&revs->diffopt, ce, &st, @@ -220,6 +221,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option) newmode = ce_mode_from_stat(ce, st.st_mode); diff_change(&revs->diffopt, oldmode, newmode, ce->sha1, (changed ? null_sha1 : ce->sha1), + !is_null_sha1(ce->sha1), (changed ? 0 : !is_null_sha1(ce->sha1)), ce->name, 0, dirty_submodule); } @@ -236,11 +238,12 @@ int run_diff_files(struct rev_info *revs, unsigned int option) static void diff_index_show_file(struct rev_info *revs, const char *prefix, struct cache_entry *ce, - const unsigned char *sha1, unsigned int mode, + const unsigned char *sha1, int sha1_valid, + unsigned int mode, unsigned dirty_submodule) { diff_addremove(&revs->diffopt, prefix[0], mode, - sha1, ce->name, dirty_submodule); + sha1, sha1_valid, ce->name, dirty_submodule); } static int get_stat_data(struct cache_entry *ce, @@ -295,7 +298,7 @@ static void show_new_file(struct rev_info *revs, &dirty_submodule, &revs->diffopt) < 0) return; - diff_index_show_file(revs, "+", new, sha1, mode, dirty_submodule); + diff_index_show_file(revs, "+", new, sha1, !is_null_sha1(sha1), mode, dirty_submodule); } static int show_modified(struct rev_info *revs, @@ -312,7 +315,7 @@ static int show_modified(struct rev_info *revs, &dirty_submodule, &revs->diffopt) < 0) { if (report_missing) diff_index_show_file(revs, "-", old, - old->sha1, old->ce_mode, 0); + old->sha1, 1, old->ce_mode, 0); return -1; } @@ -347,7 +350,8 @@ static int show_modified(struct rev_info *revs, return 0; diff_change(&revs->diffopt, oldmode, mode, - old->sha1, sha1, old->name, 0, dirty_submodule); + old->sha1, sha1, 1, !is_null_sha1(sha1), + old->name, 0, dirty_submodule); return 0; } @@ -380,7 +384,7 @@ static void do_oneway_diff(struct unpack_trees_options *o, struct diff_filepair *pair; pair = diff_unmerge(&revs->diffopt, idx->name); if (tree) - fill_filespec(pair->one, tree->sha1, tree->ce_mode); + fill_filespec(pair->one, tree->sha1, 1, tree->ce_mode); return; } @@ -396,7 +400,7 @@ static void do_oneway_diff(struct unpack_trees_options *o, * Something removed from the tree? */ if (!idx) { - diff_index_show_file(revs, "-", tree, tree->sha1, tree->ce_mode, 0); + diff_index_show_file(revs, "-", tree, tree->sha1, 1, tree->ce_mode, 0); return; } diff --git a/diff-no-index.c b/diff-no-index.c index 7d805a06af..74da659368 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -82,7 +82,7 @@ static struct diff_filespec *noindex_filespec(const char *name, int mode) if (!name) name = "/dev/null"; s = alloc_filespec(name); - fill_filespec(s, null_sha1, mode); + fill_filespec(s, null_sha1, 0, mode); if (name == file_from_standard_input) populate_from_stdin(s); return s; @@ -258,8 +258,7 @@ void diff_no_index(struct rev_info *revs, DIFF_OPT_SET(&revs->diffopt, NO_INDEX); revs->max_count = -2; - if (diff_setup_done(&revs->diffopt) < 0) - die("diff_setup_done failed"); + diff_setup_done(&revs->diffopt); setup_diff_pager(&revs->diffopt); DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS); @@ -574,6 +574,7 @@ static void emit_rewrite_lines(struct emit_callback *ecb, if (!endp) { const char *plain = diff_get_color(ecb->color_diff, DIFF_PLAIN); + putc('\n', ecb->opt->file); emit_line_0(ecb->opt, plain, reset, '\\', nneof, strlen(nneof)); } @@ -2541,12 +2542,12 @@ void free_filespec(struct diff_filespec *spec) } void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1, - unsigned short mode) + int sha1_valid, unsigned short mode) { if (mode) { spec->mode = canon_mode(mode); hashcpy(spec->sha1, sha1); - spec->sha1_valid = !is_null_sha1(sha1); + spec->sha1_valid = sha1_valid; } } @@ -3187,7 +3188,7 @@ void diff_setup(struct diff_options *options) } } -int diff_setup_done(struct diff_options *options) +void diff_setup_done(struct diff_options *options) { int count = 0; @@ -3286,8 +3287,6 @@ int diff_setup_done(struct diff_options *options) options->output_format = DIFF_FORMAT_NO_OUTPUT; DIFF_OPT_SET(options, EXIT_WITH_STATUS); } - - return 0; } static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val) @@ -4693,6 +4692,7 @@ static int is_submodule_ignored(const char *path, struct diff_options *options) void diff_addremove(struct diff_options *options, int addremove, unsigned mode, const unsigned char *sha1, + int sha1_valid, const char *concatpath, unsigned dirty_submodule) { struct diff_filespec *one, *two; @@ -4724,9 +4724,9 @@ void diff_addremove(struct diff_options *options, two = alloc_filespec(concatpath); if (addremove != '+') - fill_filespec(one, sha1, mode); + fill_filespec(one, sha1, sha1_valid, mode); if (addremove != '-') { - fill_filespec(two, sha1, mode); + fill_filespec(two, sha1, sha1_valid, mode); two->dirty_submodule = dirty_submodule; } @@ -4739,6 +4739,7 @@ void diff_change(struct diff_options *options, unsigned old_mode, unsigned new_mode, const unsigned char *old_sha1, const unsigned char *new_sha1, + int old_sha1_valid, int new_sha1_valid, const char *concatpath, unsigned old_dirty_submodule, unsigned new_dirty_submodule) { @@ -4753,6 +4754,8 @@ void diff_change(struct diff_options *options, const unsigned char *tmp_c; tmp = old_mode; old_mode = new_mode; new_mode = tmp; tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c; + tmp = old_sha1_valid; old_sha1_valid = new_sha1_valid; + new_sha1_valid = tmp; tmp = old_dirty_submodule; old_dirty_submodule = new_dirty_submodule; new_dirty_submodule = tmp; } @@ -4763,8 +4766,8 @@ void diff_change(struct diff_options *options, one = alloc_filespec(concatpath); two = alloc_filespec(concatpath); - fill_filespec(one, old_sha1, old_mode); - fill_filespec(two, new_sha1, new_mode); + fill_filespec(one, old_sha1, old_sha1_valid, old_mode); + fill_filespec(two, new_sha1, new_sha1_valid, new_mode); one->dirty_submodule = old_dirty_submodule; two->dirty_submodule = new_dirty_submodule; @@ -19,12 +19,14 @@ typedef void (*change_fn_t)(struct diff_options *options, unsigned old_mode, unsigned new_mode, const unsigned char *old_sha1, const unsigned char *new_sha1, + int old_sha1_valid, int new_sha1_valid, const char *fullpath, unsigned old_dirty_submodule, unsigned new_dirty_submodule); typedef void (*add_remove_fn_t)(struct diff_options *options, int addremove, unsigned mode, const unsigned char *sha1, + int sha1_valid, const char *fullpath, unsigned dirty_submodule); typedef void (*diff_format_fn_t)(struct diff_queue_struct *q, @@ -214,12 +216,15 @@ extern void diff_addremove(struct diff_options *, int addremove, unsigned mode, const unsigned char *sha1, + int sha1_valid, const char *fullpath, unsigned dirty_submodule); extern void diff_change(struct diff_options *, unsigned mode1, unsigned mode2, const unsigned char *sha1, const unsigned char *sha2, + int sha1_valid, + int sha2_valid, const char *fullpath, unsigned dirty_submodule1, unsigned dirty_submodule2); @@ -241,7 +246,7 @@ extern int git_diff_ui_config(const char *var, const char *value, void *cb); extern int diff_use_color_default; extern void diff_setup(struct diff_options *); extern int diff_opt_parse(struct diff_options *, const char **, int); -extern int diff_setup_done(struct diff_options *); +extern void diff_setup_done(struct diff_options *); #define DIFF_DETECT_RENAME 1 #define DIFF_DETECT_COPY 2 diff --git a/diffcore-rename.c b/diffcore-rename.c index 216a7a4bbc..512d0ac5fd 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -48,7 +48,7 @@ static struct diff_rename_dst *locate_rename_dst(struct diff_filespec *two, memmove(rename_dst + first + 1, rename_dst + first, (rename_dst_nr - first - 1) * sizeof(*rename_dst)); rename_dst[first].two = alloc_filespec(two->path); - fill_filespec(rename_dst[first].two, two->sha1, two->mode); + fill_filespec(rename_dst[first].two, two->sha1, two->sha1_valid, two->mode); rename_dst[first].pair = NULL; return &(rename_dst[first]); } diff --git a/diffcore.h b/diffcore.h index be0739c5c4..1c16c8595b 100644 --- a/diffcore.h +++ b/diffcore.h @@ -55,7 +55,7 @@ struct diff_filespec { extern struct diff_filespec *alloc_filespec(const char *); extern void free_filespec(struct diff_filespec *); extern void fill_filespec(struct diff_filespec *, const unsigned char *, - unsigned short); + int, unsigned short); extern int diff_populate_filespec(struct diff_filespec *, int); extern void diff_free_filespec_data(struct diff_filespec *); @@ -139,6 +139,7 @@ static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, con static int fsck_tree(struct tree *item, int strict, fsck_error error_func) { int retval; + int has_null_sha1 = 0; int has_full_path = 0; int has_empty_name = 0; int has_zero_pad = 0; @@ -157,9 +158,12 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func) while (desc.size) { unsigned mode; const char *name; + const unsigned char *sha1; - tree_entry_extract(&desc, &name, &mode); + sha1 = tree_entry_extract(&desc, &name, &mode); + if (is_null_sha1(sha1)) + has_null_sha1 = 1; if (strchr(name, '/')) has_full_path = 1; if (!*name) @@ -207,6 +211,8 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func) } retval = 0; + if (has_null_sha1) + retval += error_func(&item->object, FSCK_WARN, "contains entries pointing to null sha1"); if (has_full_path) retval += error_func(&item->object, FSCK_WARN, "contains full pathnames"); if (has_empty_name) diff --git a/git-mergetool.sh b/git-mergetool.sh index 0db0c44845..c50e18a899 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -18,270 +18,301 @@ require_work_tree # Returns true if the mode reflects a symlink is_symlink () { - test "$1" = 120000 + test "$1" = 120000 } is_submodule () { - test "$1" = 160000 + test "$1" = 160000 } local_present () { - test -n "$local_mode" + test -n "$local_mode" } remote_present () { - test -n "$remote_mode" + test -n "$remote_mode" } base_present () { - test -n "$base_mode" + test -n "$base_mode" } cleanup_temp_files () { - if test "$1" = --save-backup ; then - rm -rf -- "$MERGED.orig" - test -e "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig" - rm -f -- "$LOCAL" "$REMOTE" "$BASE" - else - rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP" - fi + if test "$1" = --save-backup + then + rm -rf -- "$MERGED.orig" + test -e "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig" + rm -f -- "$LOCAL" "$REMOTE" "$BASE" + else + rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP" + fi } describe_file () { - mode="$1" - branch="$2" - file="$3" - - printf " {%s}: " "$branch" - if test -z "$mode"; then - echo "deleted" - elif is_symlink "$mode" ; then - echo "a symbolic link -> '$(cat "$file")'" - elif is_submodule "$mode" ; then - echo "submodule commit $file" - else - if base_present; then - echo "modified file" + mode="$1" + branch="$2" + file="$3" + + printf " {%s}: " "$branch" + if test -z "$mode" + then + echo "deleted" + elif is_symlink "$mode" + then + echo "a symbolic link -> '$(cat "$file")'" + elif is_submodule "$mode" + then + echo "submodule commit $file" + elif base_present + then + echo "modified file" else - echo "created file" + echo "created file" fi - fi } - resolve_symlink_merge () { - while true; do - printf "Use (l)ocal or (r)emote, or (a)bort? " - read ans || return 1 - case "$ans" in - [lL]*) - git checkout-index -f --stage=2 -- "$MERGED" - git add -- "$MERGED" - cleanup_temp_files --save-backup - return 0 - ;; - [rR]*) - git checkout-index -f --stage=3 -- "$MERGED" - git add -- "$MERGED" - cleanup_temp_files --save-backup - return 0 - ;; - [aA]*) - return 1 - ;; - esac + while true + do + printf "Use (l)ocal or (r)emote, or (a)bort? " + read ans || return 1 + case "$ans" in + [lL]*) + git checkout-index -f --stage=2 -- "$MERGED" + git add -- "$MERGED" + cleanup_temp_files --save-backup + return 0 + ;; + [rR]*) + git checkout-index -f --stage=3 -- "$MERGED" + git add -- "$MERGED" + cleanup_temp_files --save-backup + return 0 + ;; + [aA]*) + return 1 + ;; + esac done } resolve_deleted_merge () { - while true; do - if base_present; then - printf "Use (m)odified or (d)eleted file, or (a)bort? " - else - printf "Use (c)reated or (d)eleted file, or (a)bort? " - fi - read ans || return 1 - case "$ans" in - [mMcC]*) - git add -- "$MERGED" - cleanup_temp_files --save-backup - return 0 - ;; - [dD]*) - git rm -- "$MERGED" > /dev/null - cleanup_temp_files - return 0 - ;; - [aA]*) - return 1 - ;; - esac + while true + do + if base_present + then + printf "Use (m)odified or (d)eleted file, or (a)bort? " + else + printf "Use (c)reated or (d)eleted file, or (a)bort? " + fi + read ans || return 1 + case "$ans" in + [mMcC]*) + git add -- "$MERGED" + cleanup_temp_files --save-backup + return 0 + ;; + [dD]*) + git rm -- "$MERGED" > /dev/null + cleanup_temp_files + return 0 + ;; + [aA]*) + return 1 + ;; + esac done } resolve_submodule_merge () { - while true; do - printf "Use (l)ocal or (r)emote, or (a)bort? " - read ans || return 1 - case "$ans" in - [lL]*) - if ! local_present; then - if test -n "$(git ls-tree HEAD -- "$MERGED")"; then - # Local isn't present, but it's a subdirectory - git ls-tree --full-name -r HEAD -- "$MERGED" | git update-index --index-info || exit $? - else - test -e "$MERGED" && mv -- "$MERGED" "$BACKUP" - git update-index --force-remove "$MERGED" + while true + do + printf "Use (l)ocal or (r)emote, or (a)bort? " + read ans || return 1 + case "$ans" in + [lL]*) + if ! local_present + then + if test -n "$(git ls-tree HEAD -- "$MERGED")" + then + # Local isn't present, but it's a subdirectory + git ls-tree --full-name -r HEAD -- "$MERGED" | + git update-index --index-info || exit $? + else + test -e "$MERGED" && mv -- "$MERGED" "$BACKUP" + git update-index --force-remove "$MERGED" + cleanup_temp_files --save-backup + fi + elif is_submodule "$local_mode" + then + stage_submodule "$MERGED" "$local_sha1" + else + git checkout-index -f --stage=2 -- "$MERGED" + git add -- "$MERGED" + fi + return 0 + ;; + [rR]*) + if ! remote_present + then + if test -n "$(git ls-tree MERGE_HEAD -- "$MERGED")" + then + # Remote isn't present, but it's a subdirectory + git ls-tree --full-name -r MERGE_HEAD -- "$MERGED" | + git update-index --index-info || exit $? + else + test -e "$MERGED" && mv -- "$MERGED" "$BACKUP" + git update-index --force-remove "$MERGED" + fi + elif is_submodule "$remote_mode" + then + ! is_submodule "$local_mode" && + test -e "$MERGED" && + mv -- "$MERGED" "$BACKUP" + stage_submodule "$MERGED" "$remote_sha1" + else + test -e "$MERGED" && mv -- "$MERGED" "$BACKUP" + git checkout-index -f --stage=3 -- "$MERGED" + git add -- "$MERGED" + fi cleanup_temp_files --save-backup - fi - elif is_submodule "$local_mode"; then - stage_submodule "$MERGED" "$local_sha1" - else - git checkout-index -f --stage=2 -- "$MERGED" - git add -- "$MERGED" - fi - return 0 - ;; - [rR]*) - if ! remote_present; then - if test -n "$(git ls-tree MERGE_HEAD -- "$MERGED")"; then - # Remote isn't present, but it's a subdirectory - git ls-tree --full-name -r MERGE_HEAD -- "$MERGED" | git update-index --index-info || exit $? - else - test -e "$MERGED" && mv -- "$MERGED" "$BACKUP" - git update-index --force-remove "$MERGED" - fi - elif is_submodule "$remote_mode"; then - ! is_submodule "$local_mode" && test -e "$MERGED" && mv -- "$MERGED" "$BACKUP" - stage_submodule "$MERGED" "$remote_sha1" - else - test -e "$MERGED" && mv -- "$MERGED" "$BACKUP" - git checkout-index -f --stage=3 -- "$MERGED" - git add -- "$MERGED" - fi - cleanup_temp_files --save-backup - return 0 - ;; - [aA]*) - return 1 - ;; - esac + return 0 + ;; + [aA]*) + return 1 + ;; + esac done } stage_submodule () { - path="$1" - submodule_sha1="$2" - mkdir -p "$path" || die "fatal: unable to create directory for module at $path" - # Find $path relative to work tree - work_tree_root=$(cd_to_toplevel && pwd) - work_rel_path=$(cd "$path" && GIT_WORK_TREE="${work_tree_root}" git rev-parse --show-prefix) - test -n "$work_rel_path" || die "fatal: unable to get path of module $path relative to work tree" - git update-index --add --replace --cacheinfo 160000 "$submodule_sha1" "${work_rel_path%/}" || die + path="$1" + submodule_sha1="$2" + mkdir -p "$path" || + die "fatal: unable to create directory for module at $path" + # Find $path relative to work tree + work_tree_root=$(cd_to_toplevel && pwd) + work_rel_path=$(cd "$path" && + GIT_WORK_TREE="${work_tree_root}" git rev-parse --show-prefix + ) + test -n "$work_rel_path" || + die "fatal: unable to get path of module $path relative to work tree" + git update-index --add --replace --cacheinfo 160000 "$submodule_sha1" "${work_rel_path%/}" || die } checkout_staged_file () { - tmpfile=$(expr \ - "$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \ - : '\([^ ]*\) ') - - if test $? -eq 0 -a -n "$tmpfile" ; then - mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3" - else - >"$3" - fi + tmpfile=$(expr \ + "$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \ + : '\([^ ]*\) ') + + if test $? -eq 0 -a -n "$tmpfile" + then + mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3" + else + >"$3" + fi } merge_file () { - MERGED="$1" + MERGED="$1" - f=$(git ls-files -u -- "$MERGED") - if test -z "$f" ; then - if test ! -f "$MERGED" ; then - echo "$MERGED: file not found" - else - echo "$MERGED: file does not need merging" + f=$(git ls-files -u -- "$MERGED") + if test -z "$f" + then + if test ! -f "$MERGED" + then + echo "$MERGED: file not found" + else + echo "$MERGED: file does not need merging" + fi + return 1 fi - return 1 - fi - - ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')" - BACKUP="./$MERGED.BACKUP.$ext" - LOCAL="./$MERGED.LOCAL.$ext" - REMOTE="./$MERGED.REMOTE.$ext" - BASE="./$MERGED.BASE.$ext" - - base_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}') - local_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}') - remote_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}') - - if is_submodule "$local_mode" || is_submodule "$remote_mode"; then - echo "Submodule merge conflict for '$MERGED':" - local_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $2;}') - remote_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $2;}') - describe_file "$local_mode" "local" "$local_sha1" - describe_file "$remote_mode" "remote" "$remote_sha1" - resolve_submodule_merge - return - fi - - mv -- "$MERGED" "$BACKUP" - cp -- "$BACKUP" "$MERGED" - - checkout_staged_file 1 "$MERGED" "$BASE" - checkout_staged_file 2 "$MERGED" "$LOCAL" - checkout_staged_file 3 "$MERGED" "$REMOTE" - - if test -z "$local_mode" -o -z "$remote_mode"; then - echo "Deleted merge conflict for '$MERGED':" - describe_file "$local_mode" "local" "$LOCAL" - describe_file "$remote_mode" "remote" "$REMOTE" - resolve_deleted_merge - return - fi - if is_symlink "$local_mode" || is_symlink "$remote_mode"; then - echo "Symbolic link merge conflict for '$MERGED':" + ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')" + BACKUP="./$MERGED.BACKUP.$ext" + LOCAL="./$MERGED.LOCAL.$ext" + REMOTE="./$MERGED.REMOTE.$ext" + BASE="./$MERGED.BASE.$ext" + + base_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}') + local_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}') + remote_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}') + + if is_submodule "$local_mode" || is_submodule "$remote_mode" + then + echo "Submodule merge conflict for '$MERGED':" + local_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $2;}') + remote_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $2;}') + describe_file "$local_mode" "local" "$local_sha1" + describe_file "$remote_mode" "remote" "$remote_sha1" + resolve_submodule_merge + return + fi + + mv -- "$MERGED" "$BACKUP" + cp -- "$BACKUP" "$MERGED" + + checkout_staged_file 1 "$MERGED" "$BASE" + checkout_staged_file 2 "$MERGED" "$LOCAL" + checkout_staged_file 3 "$MERGED" "$REMOTE" + + if test -z "$local_mode" -o -z "$remote_mode" + then + echo "Deleted merge conflict for '$MERGED':" + describe_file "$local_mode" "local" "$LOCAL" + describe_file "$remote_mode" "remote" "$REMOTE" + resolve_deleted_merge + return + fi + + if is_symlink "$local_mode" || is_symlink "$remote_mode" + then + echo "Symbolic link merge conflict for '$MERGED':" + describe_file "$local_mode" "local" "$LOCAL" + describe_file "$remote_mode" "remote" "$REMOTE" + resolve_symlink_merge + return + fi + + echo "Normal merge conflict for '$MERGED':" describe_file "$local_mode" "local" "$LOCAL" describe_file "$remote_mode" "remote" "$REMOTE" - resolve_symlink_merge - return - fi - - echo "Normal merge conflict for '$MERGED':" - describe_file "$local_mode" "local" "$LOCAL" - describe_file "$remote_mode" "remote" "$REMOTE" - if "$prompt" = true; then - printf "Hit return to start merge resolution tool (%s): " "$merge_tool" - read ans || return 1 - fi - - if base_present; then - present=true - else - present=false - fi - - if ! run_merge_tool "$merge_tool" "$present"; then - echo "merge of $MERGED failed" 1>&2 - mv -- "$BACKUP" "$MERGED" - - if test "$merge_keep_temporaries" = "false"; then - cleanup_temp_files + if "$prompt" = true + then + printf "Hit return to start merge resolution tool (%s): " "$merge_tool" + read ans || return 1 fi - return 1 - fi + if base_present + then + present=true + else + present=false + fi + + if ! run_merge_tool "$merge_tool" "$present" + then + echo "merge of $MERGED failed" 1>&2 + mv -- "$BACKUP" "$MERGED" + + if test "$merge_keep_temporaries" = "false" + then + cleanup_temp_files + fi - if test "$merge_keep_backup" = "true"; then - mv -- "$BACKUP" "$MERGED.orig" - else - rm -- "$BACKUP" - fi + return 1 + fi - git add -- "$MERGED" - cleanup_temp_files - return 0 + if test "$merge_keep_backup" = "true" + then + mv -- "$BACKUP" "$MERGED.orig" + else + rm -- "$BACKUP" + fi + + git add -- "$MERGED" + cleanup_temp_files + return 0 } show_tool_help () { @@ -325,61 +356,61 @@ prompt=$(git config --bool mergetool.prompt || echo true) while test $# != 0 do - case "$1" in + case "$1" in --tool-help) show_tool_help ;; -t|--tool*) - case "$#,$1" in + case "$#,$1" in *,*=*) - merge_tool=$(expr "z$1" : 'z-[^=]*=\(.*\)') - ;; + merge_tool=$(expr "z$1" : 'z-[^=]*=\(.*\)') + ;; 1,*) - usage ;; + usage ;; *) - merge_tool="$2" - shift ;; - esac - ;; + merge_tool="$2" + shift ;; + esac + ;; -y|--no-prompt) - prompt=false - ;; + prompt=false + ;; --prompt) - prompt=true - ;; + prompt=true + ;; --) - shift - break - ;; + shift + break + ;; -*) - usage - ;; - *) - break - ;; - esac - shift -done - -prompt_after_failed_merge() { - while true; do - printf "Continue merging other unresolved paths (y/n) ? " - read ans || return 1 - case "$ans" in - - [yY]*) - return 0 + usage ;; - - [nN]*) - return 1 + *) + break ;; esac - done + shift +done + +prompt_after_failed_merge () { + while true + do + printf "Continue merging other unresolved paths (y/n) ? " + read ans || return 1 + case "$ans" in + [yY]*) + return 0 + ;; + [nN]*) + return 1 + ;; + esac + done } -if test -z "$merge_tool"; then - merge_tool=$(get_merge_tool "$merge_tool") || exit +if test -z "$merge_tool" +then + merge_tool=$(get_merge_tool "$merge_tool") || exit fi merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)" merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)" @@ -388,22 +419,24 @@ last_status=0 rollup_status=0 files= -if test $# -eq 0 ; then - cd_to_toplevel +if test $# -eq 0 +then + cd_to_toplevel - if test -e "$GIT_DIR/MERGE_RR" - then - files=$(git rerere remaining) - else - files=$(git ls-files -u | sed -e 's/^[^ ]* //' | sort -u) - fi + if test -e "$GIT_DIR/MERGE_RR" + then + files=$(git rerere remaining) + else + files=$(git ls-files -u | sed -e 's/^[^ ]* //' | sort -u) + fi else - files=$(git ls-files -u -- "$@" | sed -e 's/^[^ ]* //' | sort -u) + files=$(git ls-files -u -- "$@" | sed -e 's/^[^ ]* //' | sort -u) fi -if test -z "$files" ; then - echo "No files need merging" - exit 0 +if test -z "$files" +then + echo "No files need merging" + exit 0 fi printf "Merging:\n" @@ -413,15 +446,17 @@ IFS=' ' for i in $files do - if test $last_status -ne 0; then - prompt_after_failed_merge || exit 1 - fi - printf "\n" - merge_file "$i" - last_status=$? - if test $last_status -ne 0; then - rollup_status=1 - fi + if test $last_status -ne 0 + then + prompt_after_failed_merge || exit 1 + fi + printf "\n" + merge_file "$i" + last_status=$? + if test $last_status -ne 0 + then + rollup_status=1 + fi done exit $rollup_status diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 0c19b7c753..84926783d1 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -548,11 +548,10 @@ do_next () { test -s "$todo" && return comment_for_reflog finish && - shortonto=$(git rev-parse --short $onto) && newhead=$(git rev-parse HEAD) && case $head_name in refs/*) - message="$GIT_REFLOG_ACTION: $head_name onto $shortonto" && + message="$GIT_REFLOG_ACTION: $head_name onto $onto" && git update-ref -m "$message" $head_name $newhead $orig_head && git symbolic-ref \ -m "$GIT_REFLOG_ACTION: returning to $head_name" \ diff --git a/git-send-email.perl b/git-send-email.perl index ef30c557c7..664713709c 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -862,11 +862,13 @@ $time = time - scalar $#files; sub unquote_rfc2047 { local ($_) = @_; my $encoding; - if (s/=\?([^?]+)\?q\?(.*)\?=/$2/g) { + s{=\?([^?]+)\?q\?(.*?)\?=}{ $encoding = $1; - s/_/ /g; - s/=([0-9A-F]{2})/chr(hex($1))/eg; - } + my $e = $2; + $e =~ s/_/ /g; + $e =~ s/=([0-9A-F]{2})/chr(hex($1))/eg; + $e; + }eg; return wantarray ? ($_, $encoding) : $_; } diff --git a/git-sh-setup.sh b/git-sh-setup.sh index 770a86e2b7..ee0e0bc045 100644 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -9,8 +9,12 @@ # you would cause "cd" to be taken to unexpected places. If you # like CDPATH, define it for your interactive shell sessions without # exporting it. +# But we protect ourselves from such a user mistake nevertheless. unset CDPATH +# Similarly for IFS +unset IFS + git_broken_path_fix () { case ":$PATH:" in *:$1:*) : ok ;; diff --git a/git-stash.sh b/git-stash.sh index 4e2c7f8331..bbefdf6424 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -469,6 +469,7 @@ apply_stash () { else # Merge conflict; keep the exit status from merge-recursive status=$? + git rerere if test -n "$INDEX_OPTION" then gettextln "Index was not unstashed." >&2 diff --git a/git-submodule.sh b/git-submodule.sh index 30fa93a8a0..e89b516039 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -73,26 +73,48 @@ resolve_relative_url () # module_list() { - git ls-files --error-unmatch --stage -- "$@" | + ( + git ls-files --error-unmatch --stage -- "$@" || + echo "unmatched pathspec exists" + ) | perl -e ' my %unmerged = (); my ($null_sha1) = ("0" x 40); + my @out = (); + my $unmatched = 0; while (<STDIN>) { + if (/^unmatched pathspec/) { + $unmatched = 1; + next; + } chomp; my ($mode, $sha1, $stage, $path) = /^([0-7]+) ([0-9a-f]{40}) ([0-3])\t(.*)$/; next unless $mode eq "160000"; if ($stage ne "0") { if (!$unmerged{$path}++) { - print "$mode $null_sha1 U\t$path\n"; + push @out, "$mode $null_sha1 U\t$path\n"; } next; } - print "$_\n"; + push @out, "$_\n"; + } + if ($unmatched) { + print "#unmatched\n"; + } else { + print for (@out); } ' } +die_if_unmatched () +{ + if test "$1" = "#unmatched" + then + exit 1 + fi +} + # # Map submodule path to submodule name # @@ -346,6 +368,7 @@ cmd_foreach() module_list | while read mode sha1 stage sm_path do + die_if_unmatched "$mode" if test -e "$sm_path"/.git then say "$(eval_gettext "Entering '\$prefix\$sm_path'")" @@ -398,6 +421,7 @@ cmd_init() module_list "$@" | while read mode sha1 stage sm_path do + die_if_unmatched "$mode" name=$(module_name "$sm_path") || exit # Copy url setting when it is not set yet @@ -498,6 +522,7 @@ cmd_update() err= while read mode sha1 stage sm_path do + die_if_unmatched "$mode" if test "$stage" = U then echo >&2 "Skipping unmerged submodule $sm_path" @@ -539,7 +564,7 @@ Maybe you want to use 'update --init'?")" die "$(eval_gettext "Unable to find current revision in submodule path '\$sm_path'")" fi - if test "$subsha1" != "$sha1" + if test "$subsha1" != "$sha1" -o -n "$force" then subforce=$force # If we don't already have a -f flag and the submodule has never been checked out @@ -893,6 +918,7 @@ cmd_status() module_list "$@" | while read mode sha1 stage sm_path do + die_if_unmatched "$mode" name=$(module_name "$sm_path") || exit url=$(git config submodule."$name".url) displaypath="$prefix$sm_path" @@ -961,6 +987,7 @@ cmd_sync() module_list "$@" | while read mode sha1 stage sm_path do + die_if_unmatched "$mode" name=$(module_name "$sm_path") url=$(git config -f .gitmodules --get submodule."$name".url) diff --git a/gitk-git/gitk b/gitk-git/gitk index 22270ce46b..6f24f53d21 100755 --- a/gitk-git/gitk +++ b/gitk-git/gitk @@ -2038,7 +2038,7 @@ proc makewindow {} { set file { mc "File" cascade { {mc "Update" command updatecommits -accelerator F5} - {mc "Reload" command reloadcommits -accelerator Meta1-F5} + {mc "Reload" command reloadcommits -accelerator Shift-F5} {mc "Reread references" command rereadrefs} {mc "List references" command showrefs -accelerator F2} {xx "" separator} @@ -2495,7 +2495,7 @@ proc makewindow {} { bindkey ? {dofind -1 1} bindkey f nextfile bind . <F5> updatecommits - bind . <$M1B-F5> reloadcommits + bind . <Shift-F5> reloadcommits bind . <F2> showrefs bind . <Shift-F4> {newview 0} catch { bind . <Shift-Key-XF86_Switch_VT_4> {newview 0} } @@ -10599,7 +10599,7 @@ proc movedhead {hid head} { } proc changedrefs {} { - global cached_dheads cached_dtags cached_atags + global cached_dheads cached_dtags cached_atags cached_tagcontent global arctags archeads arcnos arcout idheads idtags foreach id [concat [array names idheads] [array names idtags]] { @@ -10611,6 +10611,7 @@ proc changedrefs {} { } } } + catch {unset cached_tagcontent} catch {unset cached_dtags} catch {unset cached_atags} catch {unset cached_dheads} @@ -10663,7 +10664,7 @@ proc listrefs {id} { } proc showtag {tag isnew} { - global ctext tagcontents tagids linknum tagobjid + global ctext cached_tagcontent tagids linknum tagobjid if {$isnew} { addtohistory [list showtag $tag 0] savectextpos @@ -10672,13 +10673,13 @@ proc showtag {tag isnew} { clear_ctext settabs 0 set linknum 0 - if {![info exists tagcontents($tag)]} { + if {![info exists cached_tagcontent($tag)]} { catch { - set tagcontents($tag) [exec git cat-file tag $tag] + set cached_tagcontent($tag) [exec git cat-file tag $tag] } } - if {[info exists tagcontents($tag)]} { - set text $tagcontents($tag) + if {[info exists cached_tagcontent($tag)]} { + set text $cached_tagcontent($tag) } else { set text "[mc "Tag"]: $tag\n[mc "Id"]: $tagids($tag)" } diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 55e0e9ea38..a40ed0ceb0 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -54,6 +54,11 @@ sub evaluate_uri { # to build the base URL ourselves: our $path_info = decode_utf8($ENV{"PATH_INFO"}); if ($path_info) { + # $path_info has already been URL-decoded by the web server, but + # $my_url and $my_uri have not. URL-decode them so we can properly + # strip $path_info. + $my_url = unescape($my_url); + $my_uri = unescape($my_uri); if ($my_url =~ s,\Q$path_info\E$,, && $my_uri =~ s,\Q$path_info\E$,, && defined $ENV{'SCRIPT_NAME'}) { @@ -744,6 +744,33 @@ char *get_remote_object_url(const char *url, const char *hex, return strbuf_detach(&buf, NULL); } +int handle_curl_result(struct active_request_slot *slot) +{ + struct slot_results *results = slot->results; + + if (results->curl_result == CURLE_OK) { + credential_approve(&http_auth); + return HTTP_OK; + } else if (missing_target(results)) + return HTTP_MISSING_TARGET; + else if (results->http_code == 401) { + if (http_auth.username && http_auth.password) { + credential_reject(&http_auth); + return HTTP_NOAUTH; + } else { + credential_fill(&http_auth); + init_curl_http_auth(slot->curl); + return HTTP_REAUTH; + } + } else { + if (!curl_errorstr[0]) + strlcpy(curl_errorstr, + curl_easy_strerror(results->curl_result), + sizeof(curl_errorstr)); + return HTTP_ERROR; + } +} + /* http_request() targets */ #define HTTP_REQUEST_STRBUF 0 #define HTTP_REQUEST_FILE 1 @@ -791,26 +818,7 @@ static int http_request(const char *url, void *result, int target, int options) if (start_active_slot(slot)) { run_active_slot(slot); - if (results.curl_result == CURLE_OK) - ret = HTTP_OK; - else if (missing_target(&results)) - ret = HTTP_MISSING_TARGET; - else if (results.http_code == 401) { - if (http_auth.username && http_auth.password) { - credential_reject(&http_auth); - ret = HTTP_NOAUTH; - } else { - credential_fill(&http_auth); - init_curl_http_auth(slot->curl); - ret = HTTP_REAUTH; - } - } else { - if (!curl_errorstr[0]) - strlcpy(curl_errorstr, - curl_easy_strerror(results.curl_result), - sizeof(curl_errorstr)); - ret = HTTP_ERROR; - } + ret = handle_curl_result(slot); } else { error("Unable to start HTTP request for %s", url); ret = HTTP_START_FAILED; @@ -819,9 +827,6 @@ static int http_request(const char *url, void *result, int target, int options) curl_slist_free_all(headers); strbuf_release(&buf); - if (ret == HTTP_OK) - credential_approve(&http_auth); - return ret; } @@ -78,6 +78,7 @@ extern int start_active_slot(struct active_request_slot *slot); extern void run_active_slot(struct active_request_slot *slot); extern void finish_active_slot(struct active_request_slot *slot); extern void finish_all_active_slots(void); +extern int handle_curl_result(struct active_request_slot *slot); #ifdef USE_CURL_MULTI extern void fill_active_slots(void); diff --git a/merge-recursive.c b/merge-recursive.c index 680937c39e..e02da3556d 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -493,8 +493,7 @@ static struct string_list *get_renames(struct merge_options *o, opts.rename_score = o->rename_score; opts.show_rename_progress = o->show_rename_progress; opts.output_format = DIFF_FORMAT_NO_OUTPUT; - if (diff_setup_done(&opts) < 0) - die("diff setup failed"); + diff_setup_done(&opts); diff_tree_sha1(o_tree->object.sha1, tree->object.sha1, "", &opts); diffcore_std(&opts); if (opts.needed_rename_limit > o->needed_rename_limit) @@ -614,23 +613,6 @@ static char *unique_path(struct merge_options *o, const char *path, const char * return newpath; } -static void flush_buffer(int fd, const char *buf, unsigned long size) -{ - while (size > 0) { - long ret = write_in_full(fd, buf, size); - if (ret < 0) { - /* Ignore epipe */ - if (errno == EPIPE) - break; - die_errno("merge-recursive"); - } else if (!ret) { - die("merge-recursive: disk full?"); - } - size -= ret; - buf += ret; - } -} - static int dir_in_way(const char *path, int check_working_copy) { int pos, pathlen = strlen(path); @@ -789,7 +771,7 @@ static void update_file_flags(struct merge_options *o, fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); if (fd < 0) die_errno("failed to open '%s'", path); - flush_buffer(fd, buf, size); + write_in_full(fd, buf, size); close(fd); } else if (S_ISLNK(mode)) { char *lnk = xmemdupz(buf, size); diff --git a/notes-merge.c b/notes-merge.c index 74aa77ce4b..7d62904f61 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -126,8 +126,7 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o, diff_setup(&opt); DIFF_OPT_SET(&opt, RECURSIVE); opt.output_format = DIFF_FORMAT_NO_OUTPUT; - if (diff_setup_done(&opt) < 0) - die("diff_setup_done failed"); + diff_setup_done(&opt); diff_tree_sha1(base, remote, "", &opt); diffcore_std(&opt); @@ -190,8 +189,7 @@ static void diff_tree_local(struct notes_merge_options *o, diff_setup(&opt); DIFF_OPT_SET(&opt, RECURSIVE); opt.output_format = DIFF_FORMAT_NO_OUTPUT; - if (diff_setup_done(&opt) < 0) - die("diff_setup_done failed"); + diff_setup_done(&opt); diff_tree_sha1(base, local, "", &opt); diffcore_std(&opt); diff --git a/patch-ids.c b/patch-ids.c index 5717257051..bc8a28fdd7 100644 --- a/patch-ids.c +++ b/patch-ids.c @@ -39,8 +39,7 @@ int init_patch_ids(struct patch_ids *ids) memset(ids, 0, sizeof(*ids)); diff_setup(&ids->diffopts); DIFF_OPT_SET(&ids->diffopts, RECURSIVE); - if (diff_setup_done(&ids->diffopts) < 0) - return error("diff_setup_done failed"); + diff_setup_done(&ids->diffopts); return 0; } diff --git a/read-cache.c b/read-cache.c index b645827c06..d8543e4d42 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1409,11 +1409,9 @@ int read_index_from(struct index_state *istate, const char *path) size_t mmap_size; struct strbuf previous_name_buf = STRBUF_INIT, *previous_name; - errno = EBUSY; if (istate->initialized) return istate->cache_nr; - errno = ENOENT; istate->timestamp.sec = 0; istate->timestamp.nsec = 0; fd = open(path, O_RDONLY); @@ -1426,15 +1424,14 @@ int read_index_from(struct index_state *istate, const char *path) if (fstat(fd, &st)) die_errno("cannot stat the open index"); - errno = EINVAL; mmap_size = xsize_t(st.st_size); if (mmap_size < sizeof(struct cache_header) + 20) die("index file smaller than expected"); mmap = xmmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - close(fd); if (mmap == MAP_FAILED) die_errno("unable to map index file"); + close(fd); hdr = mmap; if (verify_hdr(hdr, mmap_size) < 0) @@ -1490,7 +1487,6 @@ int read_index_from(struct index_state *istate, const char *path) unmap: munmap(mmap, mmap_size); - errno = EINVAL; die("index file corrupt"); } @@ -1790,6 +1786,8 @@ int write_index(struct index_state *istate, int newfd) continue; if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce)) ce_smudge_racily_clean_entry(ce); + if (is_null_sha1(ce->sha1)) + return error("cache entry has null sha1: %s", ce->name); if (ce_write_entry(&c, newfd, ce, previous_name) < 0) return -1; } diff --git a/remote-curl.c b/remote-curl.c index 04a9d6277d..3ec474fc63 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -362,16 +362,17 @@ static size_t rpc_in(char *ptr, size_t eltsize, static int run_slot(struct active_request_slot *slot) { - int err = 0; + int err; struct slot_results results; slot->results = &results; slot->curl_result = curl_easy_perform(slot->curl); finish_active_slot(slot); - if (results.curl_result != CURLE_OK) { - err |= error("RPC failed; result=%d, HTTP code = %ld", - results.curl_result, results.http_code); + err = handle_curl_result(slot); + if (err != HTTP_OK && err != HTTP_REAUTH) { + error("RPC failed; result=%d, HTTP code = %ld", + results.curl_result, results.http_code); } return err; @@ -436,9 +437,11 @@ static int post_rpc(struct rpc_state *rpc) } if (large_request) { - err = probe_rpc(rpc); - if (err) - return err; + do { + err = probe_rpc(rpc); + } while (err == HTTP_REAUTH); + if (err != HTTP_OK) + return -1; } slot = get_active_slot(); @@ -525,7 +528,11 @@ static int post_rpc(struct rpc_state *rpc) curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in); curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc); - err = run_slot(slot); + do { + err = run_slot(slot); + } while (err == HTTP_REAUTH && !large_request && !use_gzip); + if (err != HTTP_OK) + err = -1; curl_slist_free_all(headers); free(gzip_body); diff --git a/revision.c b/revision.c index 5b81a92e3a..72104962d3 100644 --- a/revision.c +++ b/revision.c @@ -345,6 +345,7 @@ static int tree_difference = REV_TREE_SAME; static void file_add_remove(struct diff_options *options, int addremove, unsigned mode, const unsigned char *sha1, + int sha1_valid, const char *fullpath, unsigned dirty_submodule) { int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD; @@ -358,6 +359,7 @@ static void file_change(struct diff_options *options, unsigned old_mode, unsigned new_mode, const unsigned char *old_sha1, const unsigned char *new_sha1, + int old_sha1_valid, int new_sha1_valid, const char *fullpath, unsigned old_dirty_submodule, unsigned new_dirty_submodule) { @@ -1132,15 +1134,27 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, const char *this = arg; int symmetric = *next == '.'; unsigned int flags_exclude = flags ^ UNINTERESTING; + static const char head_by_default[] = "HEAD"; unsigned int a_flags; *dotdot = 0; next += symmetric; if (!*next) - next = "HEAD"; + next = head_by_default; if (dotdot == arg) - this = "HEAD"; + this = head_by_default; + if (this == head_by_default && next == head_by_default && + !symmetric) { + /* + * Just ".."? That is not a range but the + * pathspec for the parent directory. + */ + if (!cant_be_filename) { + *dotdot = '.'; + return -1; + } + } if (!get_sha1(this, from_sha1) && !get_sha1(next, sha1)) { struct commit *a, *b; @@ -1853,8 +1867,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s if (revs->combine_merges) revs->ignore_merges = 0; revs->diffopt.abbrev = revs->abbrev; - if (diff_setup_done(&revs->diffopt) < 0) - die("diff_setup_done failed"); + diff_setup_done(&revs->diffopt); compile_grep_patterns(&revs->grep_filter); diff --git a/run-command.c b/run-command.c index 606791dc67..f9922b9ecc 100644 --- a/run-command.c +++ b/run-command.c @@ -139,6 +139,8 @@ int sane_execvp(const char *file, char * const argv[]) */ if (errno == EACCES && !strchr(file, '/')) errno = exists_in_PATH(file) ? EACCES : ENOENT; + else if (errno == ENOTDIR && !strchr(file, '/')) + errno = ENOENT; return -1; } @@ -82,7 +82,7 @@ static void NORETURN die_verify_filename(const char *prefix, if (!diagnose_misspelt_rev) die("%s: no such path in the working tree.\n" - "Use '-- <path>...' to specify paths that do not exist locally.", + "Use 'git <command> -- <path>...' to specify paths that do not exist locally.", arg); /* * Saying "'(icase)foo' does not exist in the index" when the @@ -96,7 +96,8 @@ static void NORETURN die_verify_filename(const char *prefix, /* ... or fall back the most general message. */ die("ambiguous argument '%s': unknown revision or path not in the working tree.\n" - "Use '--' to separate paths from revisions", arg); + "Use '--' to separate paths from revisions, like this:\n" + "'git <command> [<revision>...] -- [<file>...]'", arg); } @@ -145,7 +146,8 @@ void verify_non_filename(const char *prefix, const char *arg) if (!check_filename(prefix, arg)) return; die("ambiguous argument '%s': both revision and filename\n" - "Use '--' to separate filenames from revisions", arg); + "Use '--' to separate paths from revisions, like this:\n" + "'git <command> [<revision>...] -- [<file>...]'", arg); } /* diff --git a/submodule.c b/submodule.c index 959d349ea7..19dc6a6c0d 100644 --- a/submodule.c +++ b/submodule.c @@ -574,8 +574,7 @@ static void calculate_changed_submodule_paths(void) DIFF_OPT_SET(&diff_opts, RECURSIVE); diff_opts.output_format |= DIFF_FORMAT_CALLBACK; diff_opts.format_callback = submodule_collect_changed_cb; - if (diff_setup_done(&diff_opts) < 0) - die("diff_setup_done failed"); + diff_setup_done(&diff_opts); diff_tree_sha1(parent->item->object.sha1, commit->object.sha1, "", &diff_opts); diffcore_std(&diff_opts); diff_flush(&diff_opts); diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index 094d490893..0f31ef9be4 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -163,3 +163,42 @@ test_http_push_nonff() { test_i18ngrep "Updates were rejected because" output ' } + +setup_askpass_helper() { + test_expect_success 'setup askpass helper' ' + write_script "$TRASH_DIRECTORY/askpass" <<-\EOF && + echo >>"$TRASH_DIRECTORY/askpass-query" "askpass: $*" && + cat "$TRASH_DIRECTORY/askpass-response" + EOF + GIT_ASKPASS="$TRASH_DIRECTORY/askpass" && + export GIT_ASKPASS && + export TRASH_DIRECTORY + ' +} + +set_askpass() { + >"$TRASH_DIRECTORY/askpass-query" && + echo "$*" >"$TRASH_DIRECTORY/askpass-response" +} + +expect_askpass() { + dest=$HTTPD_DEST + { + case "$1" in + none) + ;; + pass) + echo "askpass: Password for 'http://$2@$dest': " + ;; + both) + echo "askpass: Username for 'http://$dest': " + echo "askpass: Password for 'http://$2@$dest': " + ;; + *) + false + ;; + esac + } >"$TRASH_DIRECTORY/askpass-expect" && + test_cmp "$TRASH_DIRECTORY/askpass-expect" \ + "$TRASH_DIRECTORY/askpass-query" +} diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index de3762e247..ec8618dfde 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -43,24 +43,22 @@ ErrorLog error.log </IfVersion> Alias /dumb/ www/ -Alias /auth/ www/auth/ +Alias /auth/dumb/ www/auth/dumb/ -<Location /smart/> +<LocationMatch /smart/> SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH} SetEnv GIT_HTTP_EXPORT_ALL -</Location> -<Location /smart_noexport/> +</LocationMatch> +<LocationMatch /smart_noexport/> SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH} -</Location> -<Location /smart_custom_env/> +</LocationMatch> +<LocationMatch /smart_custom_env/> SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH} SetEnv GIT_HTTP_EXPORT_ALL SetEnv GIT_COMMITTER_NAME "Custom User" SetEnv GIT_COMMITTER_EMAIL custom@example.com -</Location> -ScriptAlias /smart/ ${GIT_EXEC_PATH}/git-http-backend/ -ScriptAlias /smart_noexport/ ${GIT_EXEC_PATH}/git-http-backend/ -ScriptAlias /smart_custom_env/ ${GIT_EXEC_PATH}/git-http-backend/ +</LocationMatch> +ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1 <Directory ${GIT_EXEC_PATH}> Options None </Directory> @@ -91,6 +89,13 @@ SSLEngine On Require valid-user </Location> +<LocationMatch "^/auth-push/.*/git-receive-pack$"> + AuthType Basic + AuthName "git-auth" + AuthUserFile passwd + Require valid-user +</LocationMatch> + <IfDefine DAV> LoadModule dav_module modules/mod_dav.so LoadModule dav_fs_module modules/mod_dav_fs.so diff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh index 9bee8bfd2e..da2c504e53 100755 --- a/t/t0070-fundamental.sh +++ b/t/t0070-fundamental.sh @@ -25,4 +25,9 @@ test_expect_success POSIXPERM 'mktemp to unwritable directory prints filename' ' grep "cannotwrite/test" err ' +test_expect_success 'check for a bug in the regex routines' ' + # if this test fails, re-build git with NO_REGEX=1 + test-regex +' + test_done diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index 5b79c51b8c..bf7a2cd6fb 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -213,4 +213,30 @@ test_expect_success 'rev-list --verify-objects with bad sha1' ' grep -q "error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff" out ' +_bz='\0' +_bz5="$_bz$_bz$_bz$_bz$_bz" +_bz20="$_bz5$_bz5$_bz5$_bz5" + +test_expect_success 'fsck notices blob entry pointing to null sha1' ' + (git init null-blob && + cd null-blob && + sha=$(printf "100644 file$_bz$_bz20" | + git hash-object -w --stdin -t tree) && + git fsck 2>out && + cat out && + grep "warning.*null sha1" out + ) +' + +test_expect_success 'fsck notices submodule entry pointing to null sha1' ' + (git init null-commit && + cd null-commit && + sha=$(printf "160000 submodule$_bz$_bz20" | + git hash-object -w --stdin -t tree) && + git fsck 2>out && + cat out && + grep "warning.*null sha1" out + ) +' + test_done diff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh index c5cb77a0e1..f950c10128 100755 --- a/t/t1506-rev-parse-diagnosis.sh +++ b/t/t1506-rev-parse-diagnosis.sh @@ -182,4 +182,18 @@ test_expect_success '<commit>:file correctly diagnosed after a pathname' ' test_cmp expect actual ' +test_expect_success 'dotdot is not an empty set' ' + ( H=$(git rev-parse HEAD) && echo $H && echo ^$H ) >expect && + + git rev-parse HEAD.. >actual && + test_cmp expect actual && + + git rev-parse ..HEAD >actual && + test_cmp expect actual && + + echo .. >expect && + git rev-parse .. >actual && + test_cmp expect actual +' + test_done diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh index 809fafe208..0dbbb00d74 100755 --- a/t/t2107-update-index-basic.sh +++ b/t/t2107-update-index-basic.sh @@ -29,4 +29,23 @@ test_expect_success 'update-index -h with corrupt index' ' grep "[Uu]sage: git update-index" broken/usage ' +test_expect_success '--cacheinfo does not accept blob null sha1' ' + echo content >file && + git add file && + git rev-parse :file >expect && + test_must_fail git update-index --cacheinfo 100644 $_z40 file && + git rev-parse :file >actual && + test_cmp expect actual +' + +test_expect_success '--cacheinfo does not accept gitlink null sha1' ' + git init submodule && + (cd submodule && test_commit foo) && + git add submodule && + git rev-parse :submodule >expect && + test_must_fail git update-index --cacheinfo 160000 $_z40 submodule && + git rev-parse :submodule >actual && + test_cmp expect actual +' + test_done diff --git a/t/t3401-rebase-partial.sh b/t/t3401-rebase-partial.sh index 7f8693b928..58f4823783 100755 --- a/t/t3401-rebase-partial.sh +++ b/t/t3401-rebase-partial.sh @@ -47,7 +47,23 @@ test_expect_success 'rebase ignores empty commit' ' git commit --allow-empty -m empty && test_commit D && git rebase C && - test $(git log --format=%s C..) = "D" + test "$(git log --format=%s C..)" = "D" +' + +test_expect_success 'rebase --keep-empty' ' + git reset --hard D && + git rebase --keep-empty C && + test "$(git log --format=%s C..)" = "D +empty" +' + +test_expect_success 'rebase --keep-empty keeps empty even if already in upstream' ' + git reset --hard A && + git commit --allow-empty -m also-empty && + git rebase --keep-empty D && + test "$(git log --format=%s A..)" = "also-empty +D +empty" ' test_done diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh index c00a94b9ba..2d030a4ec3 100755 --- a/t/t4022-diff-rewrite.sh +++ b/t/t4022-diff-rewrite.sh @@ -66,5 +66,35 @@ test_expect_success 'suppress deletion diff with -B -D' ' grep -v "Linus Torvalds" actual ' +test_expect_success 'prepare a file that ends with an incomplete line' ' + test_seq 1 99 >seq && + printf 100 >>seq && + git add seq && + git commit seq -m seq +' + +test_expect_success 'rewrite the middle 90% of sequence file and terminate with newline' ' + test_seq 1 5 >seq && + test_seq 9331 9420 >>seq && + test_seq 96 100 >>seq +' + +test_expect_success 'confirm that sequence file is considered a rewrite' ' + git diff -B seq >res && + grep "dissimilarity index" res +' + +test_expect_success 'no newline at eof is on its own line without -B' ' + git diff seq >res && + grep "^\\\\ " res && + ! grep "^..*\\\\ " res +' + +test_expect_success 'no newline at eof is on its own line with -B' ' + git diff -B seq >res && + grep "^\\\\ " res && + ! grep "^..*\\\\ " res +' + test_done diff --git a/t/t4054-diff-bogus-tree.sh b/t/t4054-diff-bogus-tree.sh new file mode 100755 index 0000000000..0843c87890 --- /dev/null +++ b/t/t4054-diff-bogus-tree.sh @@ -0,0 +1,83 @@ +#!/bin/sh + +test_description='test diff with a bogus tree containing the null sha1' +. ./test-lib.sh + +empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904 + +test_expect_success 'create bogus tree' ' + bogus_tree=$( + printf "100644 fooQQQQQQQQQQQQQQQQQQQQQ" | + q_to_nul | + git hash-object -w --stdin -t tree + ) +' + +test_expect_success 'create tree with matching file' ' + echo bar >foo && + git add foo && + good_tree=$(git write-tree) + blob=$(git rev-parse :foo) +' + +test_expect_success 'raw diff shows null sha1 (addition)' ' + echo ":000000 100644 $_z40 $_z40 A foo" >expect && + git diff-tree $empty_tree $bogus_tree >actual && + test_cmp expect actual +' + +test_expect_success 'raw diff shows null sha1 (removal)' ' + echo ":100644 000000 $_z40 $_z40 D foo" >expect && + git diff-tree $bogus_tree $empty_tree >actual && + test_cmp expect actual +' + +test_expect_success 'raw diff shows null sha1 (modification)' ' + echo ":100644 100644 $blob $_z40 M foo" >expect && + git diff-tree $good_tree $bogus_tree >actual && + test_cmp expect actual +' + +test_expect_success 'raw diff shows null sha1 (other direction)' ' + echo ":100644 100644 $_z40 $blob M foo" >expect && + git diff-tree $bogus_tree $good_tree >actual && + test_cmp expect actual +' + +test_expect_success 'raw diff shows null sha1 (reverse)' ' + echo ":100644 100644 $_z40 $blob M foo" >expect && + git diff-tree -R $good_tree $bogus_tree >actual && + test_cmp expect actual +' + +test_expect_success 'raw diff shows null sha1 (index)' ' + echo ":100644 100644 $_z40 $blob M foo" >expect && + git diff-index $bogus_tree >actual && + test_cmp expect actual +' + +test_expect_success 'patch fails due to bogus sha1 (addition)' ' + test_must_fail git diff-tree -p $empty_tree $bogus_tree +' + +test_expect_success 'patch fails due to bogus sha1 (removal)' ' + test_must_fail git diff-tree -p $bogus_tree $empty_tree +' + +test_expect_success 'patch fails due to bogus sha1 (modification)' ' + test_must_fail git diff-tree -p $good_tree $bogus_tree +' + +test_expect_success 'patch fails due to bogus sha1 (other direction)' ' + test_must_fail git diff-tree -p $bogus_tree $good_tree +' + +test_expect_success 'patch fails due to bogus sha1 (reverse)' ' + test_must_fail git diff-tree -R -p $good_tree $bogus_tree +' + +test_expect_success 'patch fails due to bogus sha1 (index)' ' + test_must_fail git diff-index -p $bogus_tree +' + +test_done diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh index 99627bc6d6..b1b906b1bb 100755 --- a/t/t4103-apply-binary.sh +++ b/t/t4103-apply-binary.sh @@ -8,30 +8,28 @@ test_description='git apply handling binary patches ' . ./test-lib.sh -# setup - -cat >file1 <<EOF -A quick brown fox jumps over the lazy dog. -A tiny little penguin runs around in circles. -There is a flag with Linux written on it. -A slow black-and-white panda just sits there, -munching on his bamboo. -EOF -cat file1 >file2 -cat file1 >file4 - -test_expect_success 'setup' " +test_expect_success 'setup' ' + cat >file1 <<-\EOF && + A quick brown fox jumps over the lazy dog. + A tiny little penguin runs around in circles. + There is a flag with Linux written on it. + A slow black-and-white panda just sits there, + munching on his bamboo. + EOF + cat file1 >file2 && + cat file1 >file4 && + git update-index --add --remove file1 file2 file4 && - git commit -m 'Initial Version' 2>/dev/null && + git commit -m "Initial Version" 2>/dev/null && git checkout -b binary && - "$PERL_PATH" -pe 'y/x/\000/' <file1 >file3 && + "$PERL_PATH" -pe "y/x/\000/" <file1 >file3 && cat file3 >file4 && git add file2 && - "$PERL_PATH" -pe 'y/\000/v/' <file3 >file1 && + "$PERL_PATH" -pe "y/\000/v/" <file3 >file1 && rm -f file2 && git update-index --add --remove file1 file2 file3 file4 && - git commit -m 'Second Version' && + git commit -m "Second Version" && git diff-tree -p master binary >B.diff && git diff-tree -p -C master binary >C.diff && @@ -42,17 +40,25 @@ test_expect_success 'setup' " git diff-tree -p --full-index master binary >B-index.diff && git diff-tree -p -C --full-index master binary >C-index.diff && + git diff-tree -p --binary --no-prefix master binary -- file3 >B0.diff && + git init other-repo && - (cd other-repo && - git fetch .. master && - git reset --hard FETCH_HEAD + ( + cd other-repo && + git fetch .. master && + git reset --hard FETCH_HEAD ) -" +' test_expect_success 'stat binary diff -- should not fail.' \ 'git checkout master && git apply --stat --summary B.diff' +test_expect_success 'stat binary -p0 diff -- should not fail.' ' + git checkout master && + git apply --stat -p0 B0.diff +' + test_expect_success 'stat binary diff (copy) -- should not fail.' \ 'git checkout master && git apply --stat --summary C.diff' @@ -143,4 +149,10 @@ test_expect_success 'apply binary diff (copy).' \ git apply --allow-binary-replacement --index CF.diff && test -z "$(git diff --name-status binary)"' +test_expect_success 'apply binary -p0 diff' ' + do_reset && + git apply -p0 --index B0.diff && + test -z "$(git diff --name-status binary -- file3)" +' + test_done diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 71be59d446..45058cc8cb 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -806,4 +806,11 @@ test_expect_success 'log --graph with diff and stats' ' test_cmp expect actual.sanitized ' +test_expect_success 'dotdot is a parent directory' ' + mkdir -p a/b && + ( echo sixth && echo fifth ) >expect && + ( cd a/b && git log --format=%s .. ) >actual && + test_cmp expect actual +' + test_done diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh index 0eace37a03..250c720c14 100755 --- a/t/t5400-send-pack.sh +++ b/t/t5400-send-pack.sh @@ -145,6 +145,41 @@ test_expect_success 'push --all excludes remote-tracking hierarchy' ' ) ' +test_expect_success 'receive-pack runs auto-gc in remote repo' ' + rm -rf parent child && + git init parent && + ( + # Setup a repo with 2 packs + cd parent && + echo "Some text" >file.txt && + git add . && + git commit -m "Initial commit" && + git repack -adl && + echo "Some more text" >>file.txt && + git commit -a -m "Second commit" && + git repack + ) && + cp -a parent child && + ( + # Set the child to auto-pack if more than one pack exists + cd child && + git config gc.autopacklimit 1 && + git branch test_auto_gc && + # And create a file that follows the temporary object naming + # convention for the auto-gc to remove + : >.git/objects/tmp_test_object && + test-chmtime =-1209601 .git/objects/tmp_test_object + ) && + ( + cd parent && + echo "Even more text" >>file.txt && + git commit -a -m "Third commit" && + git send-pack ../child HEAD:refs/heads/test_auto_gc >output 2>&1 && + grep "Auto packing the repository for optimum performance." output + ) && + test ! -e child/.git/objects/tmp_test_object +' + rewound_push_setup() { rm -rf parent child && mkdir parent && diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh index 1eea647656..f141f2d1da 100755 --- a/t/t5540-http-push.sh +++ b/t/t5540-http-push.sh @@ -46,15 +46,7 @@ test_expect_success 'create password-protected repository' ' "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" ' -test_expect_success 'setup askpass helper' ' - cat >askpass <<-\EOF && - #!/bin/sh - echo user@host - EOF - chmod +x askpass && - GIT_ASKPASS="$PWD/askpass" && - export GIT_ASKPASS -' +setup_askpass_helper test_expect_success 'clone remote repository' ' cd "$ROOT_PATH" && @@ -162,6 +154,7 @@ test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \ test_expect_success 'push to password-protected repository (user in URL)' ' test_commit pw-user && + set_askpass user@host && git push "$HTTPD_URL_USER/auth/dumb/test_repo.git" HEAD && git rev-parse --verify HEAD >expect && git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \ @@ -169,9 +162,15 @@ test_expect_success 'push to password-protected repository (user in URL)' ' test_cmp expect actual ' +test_expect_failure 'user was prompted only once for password' ' + expect_askpass pass user@host +' + test_expect_failure 'push to password-protected repository (no user in URL)' ' test_commit pw-nouser && + set_askpass user@host && git push "$HTTPD_URL/auth/dumb/test_repo.git" HEAD && + expect_askpass both user@host git rev-parse --verify HEAD >expect && git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \ rev-parse --verify HEAD >actual && diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh index 312e484090..ef6d6b6e4e 100755 --- a/t/t5541-http-push.sh +++ b/t/t5541-http-push.sh @@ -36,6 +36,8 @@ test_expect_success 'setup remote repository' ' mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH" ' +setup_askpass_helper + cat >exp <<EOF GET /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200 POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200 @@ -266,5 +268,29 @@ test_expect_success 'http push respects GIT_COMMITTER_* in reflog' ' test_cmp expect actual ' +test_expect_success 'push over smart http with auth' ' + cd "$ROOT_PATH/test_repo_clone" && + echo push-auth-test >expect && + test_commit push-auth-test && + set_askpass user@host && + git push "$HTTPD_URL"/auth/smart/test_repo.git && + git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \ + log -1 --format=%s >actual && + expect_askpass both user@host && + test_cmp expect actual +' + +test_expect_success 'push to auth-only-for-push repo' ' + cd "$ROOT_PATH/test_repo_clone" && + echo push-half-auth >expect && + test_commit push-half-auth && + set_askpass user@host && + git push "$HTTPD_URL"/auth-push/smart/test_repo.git && + git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \ + log -1 --format=%s >actual && + expect_askpass both user@host && + test_cmp expect actual +' + stop_httpd test_done diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh index b06f817af3..16ef0419e9 100755 --- a/t/t5550-http-fetch.sh +++ b/t/t5550-http-fetch.sh @@ -41,68 +41,34 @@ test_expect_success 'clone http repository' ' ' test_expect_success 'create password-protected repository' ' - mkdir "$HTTPD_DOCUMENT_ROOT_PATH/auth/" && + mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" && cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \ - "$HTTPD_DOCUMENT_ROOT_PATH/auth/repo.git" -' - -test_expect_success 'setup askpass helpers' ' - cat >askpass <<-EOF && - #!/bin/sh - echo >>"$PWD/askpass-query" "askpass: \$*" && - cat "$PWD/askpass-response" - EOF - chmod +x askpass && - GIT_ASKPASS="$PWD/askpass" && - export GIT_ASKPASS -' - -expect_askpass() { - dest=$HTTPD_DEST - { - case "$1" in - none) - ;; - pass) - echo "askpass: Password for 'http://$2@$dest': " - ;; - both) - echo "askpass: Username for 'http://$dest': " - echo "askpass: Password for 'http://$2@$dest': " - ;; - *) - false - ;; - esac - } >askpass-expect && - test_cmp askpass-expect askpass-query -} + "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/repo.git" +' + +setup_askpass_helper test_expect_success 'cloning password-protected repository can fail' ' - >askpass-query && - echo wrong >askpass-response && - test_must_fail git clone "$HTTPD_URL/auth/repo.git" clone-auth-fail && + set_askpass wrong && + test_must_fail git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-fail && expect_askpass both wrong ' test_expect_success 'http auth can use user/pass in URL' ' - >askpass-query && - echo wrong >askpass-response && - git clone "$HTTPD_URL_USER_PASS/auth/repo.git" clone-auth-none && + set_askpass wrong && + git clone "$HTTPD_URL_USER_PASS/auth/dumb/repo.git" clone-auth-none && expect_askpass none ' test_expect_success 'http auth can use just user in URL' ' - >askpass-query && - echo user@host >askpass-response && - git clone "$HTTPD_URL_USER/auth/repo.git" clone-auth-pass && + set_askpass user@host && + git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-pass && expect_askpass pass user@host ' test_expect_success 'http auth can request both user and pass' ' - >askpass-query && - echo user@host >askpass-response && - git clone "$HTTPD_URL/auth/repo.git" clone-auth-both && + set_askpass user@host && + git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-both && expect_askpass both user@host ' @@ -112,25 +78,22 @@ test_expect_success 'http auth respects credential helper config' ' echo username=user@host echo password=user@host }; f" && - >askpass-query && - echo wrong >askpass-response && - git clone "$HTTPD_URL/auth/repo.git" clone-auth-helper && + set_askpass wrong && + git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-helper && expect_askpass none ' test_expect_success 'http auth can get username from config' ' test_config_global "credential.$HTTPD_URL.username" user@host && - >askpass-query && - echo user@host >askpass-response && - git clone "$HTTPD_URL/auth/repo.git" clone-auth-user && + set_askpass user@host && + git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-user && expect_askpass pass user@host ' test_expect_success 'configured username does not override URL' ' test_config_global "credential.$HTTPD_URL.username" wrong && - >askpass-query && - echo user@host >askpass-response && - git clone "$HTTPD_URL_USER/auth/repo.git" clone-auth-user2 && + set_askpass user@host && + git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-user2 && expect_askpass pass user@host ' diff --git a/t/t5551-http-fetch.sh b/t/t5551-http-fetch.sh index 91eaf53d1d..2db5c35641 100755 --- a/t/t5551-http-fetch.sh +++ b/t/t5551-http-fetch.sh @@ -27,6 +27,8 @@ test_expect_success 'create http-accessible bare repository' ' git push public master:master ' +setup_askpass_helper + cat >exp <<EOF > GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 > Accept: */* @@ -109,6 +111,24 @@ test_expect_success 'follow redirects (302)' ' git clone $HTTPD_URL/smart-redir-temp/repo.git --quiet repo-t ' +test_expect_success 'clone from password-protected repository' ' + echo two >expect && + set_askpass user@host && + git clone --bare "$HTTPD_URL/auth/smart/repo.git" smart-auth && + expect_askpass both user@host && + git --git-dir=smart-auth log -1 --format=%s >actual && + test_cmp expect actual +' + +test_expect_success 'clone from auth-only-for-push repository' ' + echo two >expect && + set_askpass wrong && + git clone --bare "$HTTPD_URL/auth-push/smart/repo.git" smart-noauth && + expect_askpass none && + git --git-dir=smart-noauth log -1 --format=%s >actual && + test_cmp expect actual +' + test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE test_expect_success EXPENSIVE 'create 50,000 tags in the repo' ' diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index 172178490a..752f5cb7d0 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -456,4 +456,14 @@ test_atom refs/tags/signed-long contents "subject line body contents $sig" +cat >expected <<\EOF +408fe76d02a785a006c2e9c669b7be5589ede96d <committer@example.com> refs/tags/master +90b5ebede4899eda64893bc2a4c8f1d6fb6dfc40 <committer@example.com> refs/tags/bogo +EOF + +test_expect_success 'Verify sort with multiple keys' ' + git for-each-ref --format="%(objectname) %(taggeremail) %(refname)" --sort=objectname --sort=taggeremail \ + refs/tags/bogo refs/tags/master > actual && + test_cmp expected actual +' test_done diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index 81827e696f..0278f48396 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -258,6 +258,27 @@ test_expect_success 'init should register submodule url in .git/config' ' test_cmp expect url ' +test_failure_with_unknown_submodule () { + test_must_fail git submodule $1 no-such-submodule 2>output.err && + grep "^error: .*no-such-submodule" output.err +} + +test_expect_success 'init should fail with unknown submodule' ' + test_failure_with_unknown_submodule init +' + +test_expect_success 'update should fail with unknown submodule' ' + test_failure_with_unknown_submodule update +' + +test_expect_success 'status should fail with unknown submodule' ' + test_failure_with_unknown_submodule status +' + +test_expect_success 'sync should fail with unknown submodule' ' + test_failure_with_unknown_submodule sync +' + test_expect_success 'update should fail when path is used by a file' ' echo hello >expect && @@ -418,10 +439,7 @@ test_expect_success 'moving to a commit without submodule does not leave empty d ' test_expect_success 'submodule <invalid-path> warns' ' - - git submodule no-such-submodule 2> output.err && - grep "^error: .*no-such-submodule" output.err - + test_failure_with_unknown_submodule ' test_expect_success 'add submodules without specifying an explicit path' ' diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index ce61d4c0fa..15426530e4 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -123,6 +123,18 @@ test_expect_success 'submodule update should throw away changes with --force ' ' ) ' +test_expect_success 'submodule update --force forcibly checks out submodules' ' + (cd super && + (cd submodule && + rm -f file + ) && + git submodule update --force submodule && + (cd submodule && + test "$(git status -s file)" = "" + ) + ) +' + test_expect_success 'submodule update --rebase staying on master' ' (cd super/submodule && git checkout master @@ -367,7 +379,7 @@ test_expect_success 'submodule update continues after checkout error' ' git submodule init && git commit -am "new_submodule" && (cd submodule2 && - git rev-parse --max-count=1 HEAD > ../expect + git rev-parse --verify HEAD >../expect ) && (cd submodule && test_commit "update_submodule" file @@ -384,7 +396,7 @@ test_expect_success 'submodule update continues after checkout error' ' git checkout HEAD^ && test_must_fail git submodule update && (cd submodule2 && - git rev-parse --max-count=1 HEAD > ../actual + git rev-parse --verify HEAD >../actual ) && test_cmp expect actual ) @@ -413,7 +425,7 @@ test_expect_success 'submodule update continues after recursive checkout error' test_commit "update_submodule_again_again" file ) && (cd submodule2 && - git rev-parse --max-count=1 HEAD > ../expect && + git rev-parse --verify HEAD >../expect && test_commit "update_submodule2_again" file ) && git add submodule && @@ -428,7 +440,7 @@ test_expect_success 'submodule update continues after recursive checkout error' ) && test_must_fail git submodule update --recursive && (cd submodule2 && - git rev-parse --max-count=1 HEAD > ../actual + git rev-parse --verify HEAD >../actual ) && test_cmp expect actual ) @@ -460,12 +472,12 @@ test_expect_success 'submodule update exit immediately in case of merge conflict ) && git checkout HEAD^ && (cd submodule2 && - git rev-parse --max-count=1 HEAD > ../expect + git rev-parse --verify HEAD >../expect ) && git config submodule.submodule.update merge && test_must_fail git submodule update && (cd submodule2 && - git rev-parse --max-count=1 HEAD > ../actual + git rev-parse --verify HEAD >../actual ) && test_cmp expect actual ) @@ -495,12 +507,12 @@ test_expect_success 'submodule update exit immediately after recursive rebase er ) && git checkout HEAD^ && (cd submodule2 && - git rev-parse --max-count=1 HEAD > ../expect + git rev-parse --verify HEAD >../expect ) && git config submodule.submodule.update rebase && test_must_fail git submodule update && (cd submodule2 && - git rev-parse --max-count=1 HEAD > ../actual + git rev-parse --verify HEAD >../actual ) && test_cmp expect actual ) diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh index f5e16fc7db..6fa0c70506 100755 --- a/t/t7610-mergetool.sh +++ b/t/t7610-mergetool.sh @@ -55,6 +55,16 @@ test_expect_success 'setup' ' git rm file12 && git commit -m "branch1 changes" && + git checkout -b stash1 master && + echo stash1 change file11 >file11 && + git add file11 && + git commit -m "stash1 changes" && + + git checkout -b stash2 master && + echo stash2 change file11 >file11 && + git add file11 && + git commit -m "stash2 changes" && + git checkout master && git submodule update -N && echo master updated >file1 && @@ -193,7 +203,35 @@ test_expect_success 'mergetool skips resolved paths when rerere is active' ' git reset --hard ' +test_expect_success 'conflicted stash sets up rerere' ' + git config rerere.enabled true && + git checkout stash1 && + echo "Conflicting stash content" >file11 && + git stash && + + git checkout --detach stash2 && + test_must_fail git stash apply && + + test -n "$(git ls-files -u)" && + conflicts="$(git rerere remaining)" && + test "$conflicts" = "file11" && + output="$(git mergetool --no-prompt)" && + test "$output" != "No files need merging" && + + git commit -am "save the stash resolution" && + + git reset --hard stash2 && + test_must_fail git stash apply && + + test -n "$(git ls-files -u)" && + conflicts="$(git rerere remaining)" && + test -z "$conflicts" && + output="$(git mergetool --no-prompt)" && + test "$output" = "No files need merging" +' + test_expect_success 'mergetool takes partial path' ' + git reset --hard git config rerere.enabled false && git checkout -b test12 branch1 && git submodule update -N && diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 8c12c65c72..035122808b 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -841,6 +841,19 @@ test_expect_success $PREREQ '--compose adds MIME for utf8 subject' ' grep "^Subject: =?UTF-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1 ' +test_expect_success $PREREQ 'utf8 author is correctly passed on' ' + clean_fake_sendmail && + test_commit weird_author && + test_when_finished "git reset --hard HEAD^" && + git commit --amend --author "Füñný Nâmé <odd_?=mail@example.com>" && + git format-patch --stdout -1 >funny_name.patch && + git send-email --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + funny_name.patch && + grep "^From: Füñný Nâmé <odd_?=mail@example.com>" msgtxt1 +' + test_expect_success $PREREQ 'detects ambiguous reference/file conflict' ' echo master > master && git add master && diff --git a/test-regex.c b/test-regex.c new file mode 100644 index 0000000000..b5bfd54139 --- /dev/null +++ b/test-regex.c @@ -0,0 +1,20 @@ +#include <git-compat-util.h> + +int main(int argc, char **argv) +{ + char *pat = "[^={} \t]+"; + char *str = "={}\nfred"; + regex_t r; + regmatch_t m[1]; + + if (regcomp(&r, pat, REG_EXTENDED | REG_NEWLINE)) + die("failed regcomp() for pattern '%s'", pat); + if (regexec(&r, str, 1, m, 0)) + die("no match of pattern '%s' to string '%s'", pat, str); + + /* http://sourceware.org/bugzilla/show_bug.cgi?id=3957 */ + if (m[0].rm_so == 3) /* matches '\n' when it should not */ + die("regex bug confirmed: re-build git with NO_REGEX=1"); + + exit(0); +} diff --git a/tree-diff.c b/tree-diff.c index 28ad6db9ff..ba01563a02 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -49,12 +49,12 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, if (DIFF_OPT_TST(opt, RECURSIVE) && S_ISDIR(mode1)) { if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE)) { opt->change(opt, mode1, mode2, - sha1, sha2, base->buf, 0, 0); + sha1, sha2, 1, 1, base->buf, 0, 0); } strbuf_addch(base, '/'); diff_tree_sha1(sha1, sha2, base->buf, opt); } else { - opt->change(opt, mode1, mode2, sha1, sha2, base->buf, 0, 0); + opt->change(opt, mode1, mode2, sha1, sha2, 1, 1, base->buf, 0, 0); } strbuf_setlen(base, old_baselen); return 0; @@ -100,7 +100,7 @@ static void show_entry(struct diff_options *opt, const char *prefix, die("corrupt tree sha %s", sha1_to_hex(sha1)); if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE)) - opt->add_remove(opt, *prefix, mode, sha1, base->buf, 0); + opt->add_remove(opt, *prefix, mode, sha1, 1, base->buf, 0); strbuf_addch(base, '/'); @@ -108,7 +108,7 @@ static void show_entry(struct diff_options *opt, const char *prefix, show_tree(opt, prefix, &inner, base); free(tree); } else - opt->add_remove(opt, prefix[0], mode, sha1, base->buf, 0); + opt->add_remove(opt, prefix[0], mode, sha1, 1, base->buf, 0); strbuf_setlen(base, old_baselen); } @@ -212,8 +212,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co diff_opts.rename_score = opt->rename_score; paths[0] = NULL; diff_tree_setup_paths(paths, &diff_opts); - if (diff_setup_done(&diff_opts) < 0) - die("unable to set up diff options to follow renames"); + diff_setup_done(&diff_opts); diff_tree(t1, t2, base, &diff_opts); diffcore_std(&diff_opts); diff_tree_release_paths(&diff_opts); |