diff options
61 files changed, 1217 insertions, 294 deletions
@@ -73,6 +73,7 @@ Nguyễn Thái Ngọc Duy <pclouds@gmail.com> <nico@fluxnic.net> <nico@cam.org> Peter Krefting <peter@softwolves.pp.se> <peter@svarten.intern.softwolves.pp.se> Peter Krefting <peter@softwolves.pp.se> <peter@softwolves.pp.se> +Petr Baudis <pasky@ucw.cz> <pasky@suse.cz> Philippe Bruhat <book@cpan.org> Ralf Thielow <ralf.thielow@gmail.com> <ralf.thielow@googlemail.com> Ramsay Allan Jones <ramsay@ramsay1.demon.co.uk> diff --git a/Documentation/RelNotes/1.8.1.6.txt b/Documentation/RelNotes/1.8.1.6.txt new file mode 100644 index 0000000000..d9de639080 --- /dev/null +++ b/Documentation/RelNotes/1.8.1.6.txt @@ -0,0 +1,34 @@ +Git 1.8.1.6 Release Notes +========================= + +Fixes since v1.8.1.5 +-------------------- + + * The code to keep track of what directory names are known to Git on + platforms with case insensitive filesystems can get confused upon a + hash collision between these pathnames and looped forever. + + * When the "--prefix" option is used to "checkout-index", the code + did not pick the correct output filter based on the attribute + setting. + + * Annotated tags outside refs/tags/ hierarchy were not advertised + correctly to the ls-remote and fetch with recent version of Git. + + * The logic used by "git diff -M --stat" to shorten the names of + files before and after a rename did not work correctly when the + common prefix and suffix between the two filenames overlapped. + + * "git update-index -h" did not do the usual "-h(elp)" thing. + + * perl/Git.pm::cat_blob slurped everything in core only to write it + out to a file descriptor, which was not a very smart thing to do. + + * The SSL peer verification done by "git imap-send" did not ask for + Server Name Indication (RFC 4366), failing to connect SSL/TLS + sites that serve multiple hostnames on a single IP. + + * "git bundle verify" did not say "records a complete history" for a + bundle that does not have any prerequisites. + +Also contains various documentation fixes. diff --git a/Documentation/RelNotes/1.8.2.1.txt b/Documentation/RelNotes/1.8.2.1.txt index 2cd562233e..1ded500fc3 100644 --- a/Documentation/RelNotes/1.8.2.1.txt +++ b/Documentation/RelNotes/1.8.2.1.txt @@ -4,6 +4,41 @@ Git v1.8.2.1 Release Notes Fixes since v1.8.2 ------------------ + * Verification of signed tags were not done correctly when not in C + or en/US locale. + + * 'git commit -m "$msg"' used to add an extra newline even when + $msg already ended with one. + + * The "--match=<pattern>" option of "git describe", when used with + "--all" to allow refs that are not annotated tags to be used as a + base of description, did not restrict the output from the command + to those that match the given pattern. + + * An aliased command spawned from a bare repository that does not say + it is bare with "core.bare = yes" is treated as non-bare by mistake. + + * When "format-patch" quoted a non-ascii strings on the header files, + it incorrectly applied rfc2047 and chopped a single character in + the middle of it. + + * "git archive" reports a failure when asked to create an archive out + of an empty tree. It would be more intuitive to give an empty + archive back in such a case. + + * "git tag -f <tag>" always said "Updated tag '<tag>'" even when + creating a new tag (i.e. not overwriting nor updating). + + * "git cmd -- ':(top'" was not diagnosed as an invalid syntax, and + instead the parser kept reading beyond the end of the string. + + * Annotated tags outside refs/tags/ hierarchy were not advertised + correctly to the ls-remote and fetch with recent version of Git. + + * The code to keep track of what directory names are known to Git on + platforms with case insensitive filesystems can get confused upon a + hash collision between these pathnames and looped forever. + * The logic used by "git diff -M --stat" to shorten the names of files before and after a rename did not work correctly when the common prefix and suffix between the two filenames overlapped. diff --git a/Documentation/RelNotes/1.8.3.txt b/Documentation/RelNotes/1.8.3.txt index 4231b684d6..a05c70fe1d 100644 --- a/Documentation/RelNotes/1.8.3.txt +++ b/Documentation/RelNotes/1.8.3.txt @@ -70,6 +70,10 @@ UI, Workflows & Features * The new "--follow-tags" option tells "git push" to push relevant annotated tags when pushing branches out. + * "git merge" and "git pull" can optionally be told to inspect and + reject when merging a commit that does not carry a trusted GPG + signature. + * "git mergetool" now feeds files to the "p4merge" backend in the order that matches the p4 convention, where "theirs" is usually shown on the left side, which is the opposite from other backend @@ -81,6 +85,17 @@ UI, Workflows & Features * "git log" that shows the difference between the parent and the child has been optimized somewhat. + * "git difftool" allows the user to write into the temporary files + being shown; if the user makes changes to the working tree at the + same time, one of the changes has to be lost in such a case, but it + tells the user what happened and refrains from overwriting the copy + in the working tree. + + * There was no good way to ask "I have a random string that came from + outside world. I want to turn it into a 40-hex object name while + making sure such an object exists". A new peeling suffix ^{object} + can be used for that purpose, together with "rev-parse --verify". + Performance, Internal Implementation, etc. @@ -105,6 +120,10 @@ Performance, Internal Implementation, etc. codepaths, and setting permission bits to directories is a codepath that needs to use a more correct one. + * "git checkout" had repeated pathspec matches on the same paths, + which have been consolidated. Also a bug in "git checkout dir/" + that is started from an unmerged index has been fixed. + Also contains minor documentation updates and code clean-ups. @@ -116,6 +135,30 @@ Unless otherwise noted, all the fixes since v1.8.2 in the maintenance track are contained in this release (see release notes to them for details). + * "git merge $(git rev-parse v1.8.2)" behaved quite differently from + "git merge v1.8.2", as if v1.8.2 were written as v1.8.2^0 and did + not pay much attention to the annotated tag payload. Make the code + notice the type of the tag object, in addition to the dwim_ref() + based classification the current code uses (i.e. the name appears + in refs/tags/) to decide when to special case merging of tags. + (merge a38d3d7 jc/merge-tag-object later to maint). + + * Fix 1.8.1.x regression that stopped matching "dir" (without + trailing slash) to a directory "dir". + (merge efa5f82 jc/directory-attrs-regression-fix later to maint-1.8.1). + + * "git apply --whitespace=fix" was not prepared to see a line getting + longer after fixing whitespaces (e.g. tab-in-indent aka Python). + (merge 329b26e jc/apply-ws-fix-tab-in-indent later to maint-1.8.1). + + * The prompt string generator (in contrib/completion/) did not notice + when we are in a middle of a "git revert" session. + (merge 3ee4452 rr/prompt-revert-head later to maint). + + * "submodule summary --summary-limit" option did not support + "--option=value" form. + (merge 862ae6c rs/submodule-summary-limit later to maint). + * "index-pack --fix-thin" used uninitialize value to compute delta depths of objects it appends to the resulting pack. (merge 57165db jk/index-pack-correct-depth-fix later to maint). @@ -127,22 +170,18 @@ details). * The code to keep track of what directory names are known to Git on platforms with case insensitive filesystems can get confused upon a hash collision between these pathnames and looped forever. - (merge 2092678 kb/name-hash later to maint). * Annotated tags outside refs/tags/ hierarchy were not advertised correctly to the ls-remote and fetch with recent version of Git. - (merge c29c46f jk/fully-peeled-packed-ref later to maint). * Recent optimization broke shallow clones. (merge f59de5d jk/peel-ref later to maint). * "git cmd -- ':(top'" was not diagnosed as an invalid syntax, and instead the parser kept reading beyond the end of the string. - (merge f612a67 lf/setup-prefix-pathspec later to maint). * "git tag -f <tag>" always said "Updated tag '<tag>'" even when creating a new tag (i.e. not overwriting nor updating). - (merge 3ae851e ph/tag-force-no-warn-on-creation later to maint). * "git p4" did not behave well when the path to the root of the P4 client was not its real path. @@ -151,16 +190,13 @@ details). * "git archive" reports a failure when asked to create an archive out of an empty tree. It would be more intuitive to give an empty archive back in such a case. - (merge bd54cf1 jk/empty-archive later to maint). * When "format-patch" quoted a non-ascii strings on the header files, it incorrectly applied rfc2047 and chopped a single character in the middle of it. - (merge 6cd3c05 ks/rfc2047-one-char-at-a-time later to maint). * An aliased command spawned from a bare repository that does not say it is bare with "core.bare = yes" is treated as non-bare by mistake. - (merge 2cd83d1 jk/alias-in-bare later to maint). * In "git reflog expire", REACHABLE bit was not cleared from the correct objects. @@ -173,7 +209,6 @@ details). "--all" to allow refs that are not annotated tags to be used as a base of description, did not restrict the output from the command to those that match the given pattern. - (merge 46e1d6e jc/describe later to maint). * Clarify in the documentation "what" gets pushed to "where" when the command line to "git push" does not say these explicitly. @@ -210,7 +245,6 @@ details). * 'git commit -m "$msg"' used to add an extra newline even when $msg already ended with one. - (merge 46fbf75 bc/commit-complete-lines-given-via-m-option later to maint). * The SSL peer verification done by "git imap-send" did not ask for Server Name Indication (RFC 4366), failing to connect SSL/TLS @@ -224,7 +258,6 @@ details). * Verification of signed tags were not done correctly when not in C or en/US locale. - (merge 0174eea mg/gpg-interface-using-status later to maint). * Some platforms and users spell UTF-8 differently; retry with the most official "UTF-8" when the system does not understand the diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt index d6487e1ce0..feab7a3e4e 100644 --- a/Documentation/git-fast-export.txt +++ b/Documentation/git-fast-export.txt @@ -66,6 +66,8 @@ produced incorrect results if you gave these options. incremental runs. As <file> is only opened and truncated at completion, the same path can also be safely given to \--import-marks. + The file will not be written if no new object has been + marked/exported. --import-marks=<file>:: Before processing any input, load the marks specified in diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 10a116faf8..1f9ed6cfd2 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -60,8 +60,19 @@ OPTIONS instead. --verify:: - The parameter given must be usable as a single, valid - object name. Otherwise barf and abort. + Verify that exactly one parameter is provided, and that it + can be turned into a raw 20-byte SHA-1 that can be used to + access the object database. If so, emit it to the standard + output; otherwise, error out. ++ +If you want to make sure that the output actually names an object in +your object database and/or can be used as a specific type of object +you require, you can add "^{type}" peeling operator to the parmeter. +For example, `git rev-parse "$VAR^{commit}"` will make sure `$VAR` +names an existing object that is a commit-ish (i.e. a commit, or an +annotated tag that points at a commit). To make sure that `$VAR` +names an existing object of any type, `git rev-parse "$VAR^{object}"` +can be used. -q:: --quiet:: @@ -308,12 +319,12 @@ $ git rev-parse --verify HEAD * Print the commit object name from the revision in the $REV shell variable: + ------------ -$ git rev-parse --verify $REV +$ git rev-parse --verify $REV^{commit} ------------ + This will error out if $REV is empty or not a valid revision. -* Same as above: +* Similar to above: + ------------ $ git rev-parse --default master --verify $REV diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index e3032c46c6..b21aa87fe8 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -126,6 +126,12 @@ This option is only applicable when listing tags without annotation lines. linkgit:git-check-ref-format[1]. Some of these checks may restrict the characters allowed in a tag name. +<commit>:: +<object>:: + The object that the new tag will refer to, usually a commit. + Defaults to HEAD. + + CONFIGURATION ------------- By default, 'git tag' in sign-with-default mode (-s) will use your diff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt index 0c91aba861..f506031ae4 100644 --- a/Documentation/gitremote-helpers.txt +++ b/Documentation/gitremote-helpers.txt @@ -174,8 +174,8 @@ ref. This capability can be advertised multiple times. The first applicable refspec takes precedence. The left-hand of refspecs advertised with this capability must cover all refs reported by -the list command. If no 'refspec' capability is advertised, -there is an implied `refspec *:*`. +the list command. If a helper does not need a specific 'refspec' +capability then it should advertise `refspec *:*`. 'bidi-import':: This modifies the 'import' capability. diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt index 34a8445828..2adccf8fec 100644 --- a/Documentation/merge-options.txt +++ b/Documentation/merge-options.txt @@ -84,6 +84,11 @@ option can be used to override --squash. Pass merge strategy specific option through to the merge strategy. +--verify-signatures:: +--no-verify-signatures:: + Verify that the commits being merged have good and trusted GPG signatures + and abort the merge in case they do not. + --summary:: --no-summary:: Synonyms to --stat and --no-stat; these are deprecated and will be diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index 293965524e..afac703f21 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -131,7 +131,8 @@ The placeholders are: - '%B': raw body (unwrapped subject and body) - '%N': commit notes - '%GG': raw verification message from GPG for a signed commit -- '%G?': show either "G" for Good or "B" for Bad for a signed commit +- '%G?': show "G" for a Good signature, "B" for a Bad signature, "U" for a good, + untrusted signature and "N" for no signature - '%GS': show the name of the signer for a signed commit - '%GK': show the key used to sign a signed commit - '%gD': reflog selector, e.g., `refs/stash@{1}` diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt index 314e25da73..1707d451b6 100644 --- a/Documentation/revisions.txt +++ b/Documentation/revisions.txt @@ -116,6 +116,11 @@ some output processing may assume ref names in UTF-8. object of that type is found or the object cannot be dereferenced anymore (in which case, barf). '<rev>{caret}0' is a short-hand for '<rev>{caret}\{commit\}'. ++ +'rev{caret}\{object\}' can be used to make sure 'rev' names an +object that exists, without requiring 'rev' to be a tag, and +without dereferencing 'rev'; because a tag is already an object, +it does not have to be dereferenced even once to get to an object. '<rev>{caret}\{\}', e.g. 'v0.99.8{caret}\{\}':: A suffix '{caret}' followed by an empty brace pair @@ -657,24 +657,24 @@ static void prepare_attr_stack(const char *path, int dirlen) } static int path_matches(const char *pathname, int pathlen, - const char *basename, + int basename_offset, const struct pattern *pat, const char *base, int baselen) { const char *pattern = pat->pattern; int prefix = pat->nowildcardlen; + int isdir = (pathlen && pathname[pathlen - 1] == '/'); - if ((pat->flags & EXC_FLAG_MUSTBEDIR) && - ((!pathlen) || (pathname[pathlen-1] != '/'))) + if ((pat->flags & EXC_FLAG_MUSTBEDIR) && !isdir) return 0; if (pat->flags & EXC_FLAG_NODIR) { - return match_basename(basename, - pathlen - (basename - pathname), + return match_basename(pathname + basename_offset, + pathlen - basename_offset - isdir, pattern, prefix, pat->patternlen, pat->flags); } - return match_pathname(pathname, pathlen, + return match_pathname(pathname, pathlen - isdir, base, baselen, pattern, prefix, pat->patternlen, pat->flags); } @@ -703,7 +703,7 @@ static int fill_one(const char *what, struct match_attr *a, int rem) return rem; } -static int fill(const char *path, int pathlen, const char *basename, +static int fill(const char *path, int pathlen, int basename_offset, struct attr_stack *stk, int rem) { int i; @@ -713,7 +713,7 @@ static int fill(const char *path, int pathlen, const char *basename, struct match_attr *a = stk->attrs[i]; if (a->is_macro) continue; - if (path_matches(path, pathlen, basename, + if (path_matches(path, pathlen, basename_offset, &a->u.pat, base, stk->originlen)) rem = fill_one("fill", a, rem); } @@ -752,7 +752,8 @@ static void collect_all_attrs(const char *path) { struct attr_stack *stk; int i, pathlen, rem, dirlen; - const char *basename, *cp, *last_slash = NULL; + const char *cp, *last_slash = NULL; + int basename_offset; for (cp = path; *cp; cp++) { if (*cp == '/' && cp[1]) @@ -760,10 +761,10 @@ static void collect_all_attrs(const char *path) } pathlen = cp - path; if (last_slash) { - basename = last_slash + 1; + basename_offset = last_slash + 1 - path; dirlen = last_slash - path; } else { - basename = path; + basename_offset = 0; dirlen = 0; } @@ -773,7 +774,7 @@ static void collect_all_attrs(const char *path) rem = attr_nr; for (stk = attr_stack; 0 < rem && stk; stk = stk->prev) - rem = fill(path, pathlen, basename, stk, rem); + rem = fill(path, pathlen, basename_offset, stk, rem); } int git_check_attr(const char *path, int num, struct git_attr_check *check) diff --git a/builtin/apply.c b/builtin/apply.c index 06f5320b18..5b882d01f6 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -2117,10 +2117,10 @@ static void update_pre_post_images(struct image *preimage, /* * Adjust the common context lines in postimage. This can be - * done in-place when we are just doing whitespace fixing, - * which does not make the string grow, but needs a new buffer - * when ignoring whitespace causes the update, since in this case - * we could have e.g. tabs converted to multiple spaces. + * done in-place when we are shrinking it with whitespace + * fixing, but needs a new buffer when ignoring whitespace or + * expanding leading tabs to spaces. + * * We trust the caller to tell us if the update can be done * in place (postlen==0) or not. */ @@ -2185,7 +2185,7 @@ static int match_fragment(struct image *img, int i; char *fixed_buf, *buf, *orig, *target; struct strbuf fixed; - size_t fixed_len; + size_t fixed_len, postlen; int preimage_limit; if (preimage->nr + try_lno <= img->nr) { @@ -2335,6 +2335,7 @@ static int match_fragment(struct image *img, strbuf_init(&fixed, preimage->len + 1); orig = preimage->buf; target = img->buf + try; + postlen = 0; for (i = 0; i < preimage_limit; i++) { size_t oldlen = preimage->line[i].len; size_t tgtlen = img->line[try_lno + i].len; @@ -2362,6 +2363,7 @@ static int match_fragment(struct image *img, match = (tgtfix.len == fixed.len - fixstart && !memcmp(tgtfix.buf, fixed.buf + fixstart, fixed.len - fixstart)); + postlen += tgtfix.len; strbuf_release(&tgtfix); if (!match) @@ -2399,8 +2401,10 @@ static int match_fragment(struct image *img, * hunk match. Update the context lines in the postimage. */ fixed_buf = strbuf_detach(&fixed, &fixed_len); + if (postlen < postimage->len) + postlen = 0; update_pre_post_images(preimage, postimage, - fixed_buf, fixed_len, 0); + fixed_buf, fixed_len, postlen); return 1; unmatch_exit: diff --git a/builtin/branch.c b/builtin/branch.c index e09ce51c2e..3f0fbc082a 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -904,7 +904,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (edit_branch_description(branch_name)) return 1; } else if (rename) { - if (argc == 1) + if (!argc) + die(_("branch name required")); + else if (argc == 1) rename_branch(head, argv[0], rename > 1); else if (argc == 2) rename_branch(argv[0], argv[1], rename > 1); diff --git a/builtin/checkout.c b/builtin/checkout.c index a9c1b5a95f..f8033f446e 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -271,24 +271,55 @@ static int checkout_paths(const struct checkout_opts *opts, ; ps_matched = xcalloc(1, pos); + /* + * Make sure all pathspecs participated in locating the paths + * to be checked out. + */ for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; + ce->ce_flags &= ~CE_MATCHED; if (opts->source_tree && !(ce->ce_flags & CE_UPDATE)) + /* + * "git checkout tree-ish -- path", but this entry + * is in the original index; it will not be checked + * out to the working tree and it does not matter + * if pathspec matched this entry. We will not do + * anything to this entry at all. + */ continue; - match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), 0, ps_matched); + /* + * Either this entry came from the tree-ish we are + * checking the paths out of, or we are checking out + * of the index. + * + * If it comes from the tree-ish, we already know it + * matches the pathspec and could just stamp + * CE_MATCHED to it from update_some(). But we still + * need ps_matched and read_tree_recursive (and + * eventually tree_entry_interesting) cannot fill + * ps_matched yet. Once it can, we can avoid calling + * match_pathspec() for _all_ entries when + * opts->source_tree != NULL. + */ + if (match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), + 0, ps_matched)) + ce->ce_flags |= CE_MATCHED; } - if (report_path_error(ps_matched, opts->pathspec, opts->prefix)) + if (report_path_error(ps_matched, opts->pathspec, opts->prefix)) { + free(ps_matched); return 1; + } + free(ps_matched); /* "checkout -m path" to recreate conflicted state */ if (opts->merge) - unmerge_cache(opts->pathspec); + unmerge_marked_index(&the_index); /* Any unmerged paths? */ for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; - if (match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), 0, NULL)) { + if (ce->ce_flags & CE_MATCHED) { if (!ce_stage(ce)) continue; if (opts->force) { @@ -313,9 +344,7 @@ static int checkout_paths(const struct checkout_opts *opts, state.refresh_cache = 1; for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; - if (opts->source_tree && !(ce->ce_flags & CE_UPDATE)) - continue; - if (match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), 0, NULL)) { + if (ce->ce_flags & CE_MATCHED) { if (!ce_stage(ce)) { errs |= checkout_entry(ce, &state, NULL); continue; diff --git a/builtin/clone.c b/builtin/clone.c index e0aaf13583..f9c380eb6c 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -23,6 +23,7 @@ #include "branch.h" #include "remote.h" #include "run-command.h" +#include "connected.h" /* * Overall FIXMEs: @@ -376,10 +377,32 @@ static void clone_local(const char *src_repo, const char *dest_repo) static const char *junk_work_tree; static const char *junk_git_dir; static pid_t junk_pid; +enum { + JUNK_LEAVE_NONE, + JUNK_LEAVE_REPO, + JUNK_LEAVE_ALL +} junk_mode = JUNK_LEAVE_NONE; + +static const char junk_leave_repo_msg[] = +N_("Clone succeeded, but checkout failed.\n" + "You can inspect what was checked out with 'git status'\n" + "and retry the checkout with 'git checkout -f HEAD'\n"); static void remove_junk(void) { struct strbuf sb = STRBUF_INIT; + + switch (junk_mode) { + case JUNK_LEAVE_REPO: + warning("%s", _(junk_leave_repo_msg)); + /* fall-through */ + case JUNK_LEAVE_ALL: + return; + default: + /* proceed to removal */ + break; + } + if (getpid() != junk_pid) return; if (junk_git_dir) { @@ -485,12 +508,37 @@ static void write_followtags(const struct ref *refs, const char *msg) } } +static int iterate_ref_map(void *cb_data, unsigned char sha1[20]) +{ + struct ref **rm = cb_data; + struct ref *ref = *rm; + + /* + * Skip anything missing a peer_ref, which we are not + * actually going to write a ref for. + */ + while (ref && !ref->peer_ref) + ref = ref->next; + /* Returning -1 notes "end of list" to the caller. */ + if (!ref) + return -1; + + hashcpy(sha1, ref->old_sha1); + *rm = ref->next; + return 0; +} + static void update_remote_refs(const struct ref *refs, const struct ref *mapped_refs, const struct ref *remote_head_points_at, const char *branch_top, const char *msg) { + const struct ref *rm = mapped_refs; + + if (check_everything_connected(iterate_ref_map, 0, &rm)) + die(_("remote did not send all necessary objects")); + if (refs) { write_remote_refs(mapped_refs); if (option_single_branch) @@ -579,7 +627,8 @@ static int checkout(void) tree = parse_tree_indirect(sha1); parse_tree(tree); init_tree_desc(&t, tree->buffer, tree->size); - unpack_trees(1, &t, &opts); + if (unpack_trees(1, &t, &opts) < 0) + die(_("unable to checkout working tree")); if (write_cache(fd, active_cache, active_nr) || commit_locked_index(lock_file)) @@ -898,12 +947,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix) transport_unlock_pack(transport); transport_disconnect(transport); + junk_mode = JUNK_LEAVE_REPO; err = checkout(); strbuf_release(&reflog_msg); strbuf_release(&branch_top); strbuf_release(&key); strbuf_release(&value); - junk_pid = 0; + junk_mode = JUNK_LEAVE_ALL; return err; } diff --git a/builtin/fast-export.c b/builtin/fast-export.c index d380155d9c..f44b76cb33 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -618,9 +618,12 @@ static void import_marks(char *input_file) || *mark_end != ' ' || get_sha1(mark_end + 1, sha1)) die("corrupt mark line: %s", line); + if (last_idnum < mark) + last_idnum = mark; + object = parse_object(sha1); if (!object) - die ("Could not read blob %s", sha1_to_hex(sha1)); + continue; if (object->flags & SHOWN) error("Object %s already has a mark", sha1_to_hex(sha1)); @@ -630,8 +633,6 @@ static void import_marks(char *input_file) continue; mark_object(object, mark); - if (last_idnum < mark) - last_idnum = mark; object->flags |= SHOWN; } @@ -645,6 +646,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) struct string_list extra_refs = STRING_LIST_INIT_NODUP; struct commit *commit; char *export_filename = NULL, *import_filename = NULL; + uint32_t lastimportid; struct option options[] = { OPT_INTEGER(0, "progress", &progress, N_("show progress after <n> objects")), @@ -688,6 +690,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) if (import_filename) import_marks(import_filename); + lastimportid = last_idnum; if (import_filename && revs.prune_data.nr) full_tree = 1; @@ -710,7 +713,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) handle_tags_and_duplicates(&extra_refs); - if (export_filename) + if (export_filename && lastimportid != last_idnum) export_marks(export_filename); if (use_done_feature) diff --git a/builtin/merge.c b/builtin/merge.c index 7c8922c8b0..3e2daa37c3 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -49,7 +49,7 @@ static const char * const builtin_merge_usage[] = { static int show_diffstat = 1, shortlog_len = -1, squash; static int option_commit = 1, allow_fast_forward = 1; static int fast_forward_only, option_edit = -1; -static int allow_trivial = 1, have_message; +static int allow_trivial = 1, have_message, verify_signatures; static int overwrite_ignore = 1; static struct strbuf merge_msg = STRBUF_INIT; static struct strategy **use_strategies; @@ -199,6 +199,8 @@ static struct option builtin_merge_options[] = { OPT_BOOLEAN(0, "ff-only", &fast_forward_only, N_("abort if fast-forward is not possible")), OPT_RERERE_AUTOUPDATE(&allow_rerere_auto), + OPT_BOOL(0, "verify-signatures", &verify_signatures, + N_("Verify that the named commit has a valid GPG signature")), OPT_CALLBACK('s', "strategy", &use_strategies, N_("strategy"), N_("merge strategy to use"), option_parse_strategy), OPT_CALLBACK('X', "strategy-option", &xopts, N_("option=value"), @@ -516,6 +518,19 @@ static void merge_name(const char *remote, struct strbuf *msg) strbuf_release(&line); goto cleanup; } + + if (remote_head->util) { + struct merge_remote_desc *desc; + desc = merge_remote_util(remote_head); + if (desc && desc->obj && desc->obj->type == OBJ_TAG) { + strbuf_addf(msg, "%s\t\t%s '%s'\n", + sha1_to_hex(desc->obj->sha1), + typename(desc->obj->type), + remote); + goto cleanup; + } + } + strbuf_addf(msg, "%s\t\tcommit '%s'\n", sha1_to_hex(remote_head->object.sha1), remote); cleanup: @@ -1233,6 +1248,39 @@ int cmd_merge(int argc, const char **argv, const char *prefix) usage_with_options(builtin_merge_usage, builtin_merge_options); + if (verify_signatures) { + for (p = remoteheads; p; p = p->next) { + struct commit *commit = p->item; + char hex[41]; + struct signature_check signature_check; + memset(&signature_check, 0, sizeof(signature_check)); + + check_commit_signature(commit, &signature_check); + + strcpy(hex, find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV)); + switch (signature_check.result) { + case 'G': + break; + case 'U': + die(_("Commit %s has an untrusted GPG signature, " + "allegedly by %s."), hex, signature_check.signer); + case 'B': + die(_("Commit %s has a bad GPG signature " + "allegedly by %s."), hex, signature_check.signer); + default: /* 'N' */ + die(_("Commit %s does not have a GPG signature."), hex); + } + if (verbosity >= 0 && signature_check.result == 'G') + printf(_("Commit %s has a good GPG signature by %s\n"), + hex, signature_check.signer); + + free(signature_check.gpg_output); + free(signature_check.gpg_status); + free(signature_check.signer); + free(signature_check.key); + } + } + strbuf_addstr(&buf, "merge"); for (p = remoteheads; p; p = p->next) strbuf_addf(&buf, " %s", merge_remote_util(p->item)->name); @@ -162,6 +162,9 @@ struct cache_entry { #define CE_UNPACKED (1 << 24) #define CE_NEW_SKIP_WORKTREE (1 << 25) +/* used to temporarily mark paths matched by pathspecs */ +#define CE_MATCHED (1 << 26) + /* * Extended on-disk flags */ @@ -1041,6 +1041,76 @@ free_return: free(buf); } +static struct { + char result; + const char *check; +} sigcheck_gpg_status[] = { + { 'G', "\n[GNUPG:] GOODSIG " }, + { 'B', "\n[GNUPG:] BADSIG " }, + { 'U', "\n[GNUPG:] TRUST_NEVER" }, + { 'U', "\n[GNUPG:] TRUST_UNDEFINED" }, +}; + +static void parse_gpg_output(struct signature_check *sigc) +{ + const char *buf = sigc->gpg_status; + int i; + + /* Iterate over all search strings */ + for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) { + const char *found, *next; + + if (!prefixcmp(buf, sigcheck_gpg_status[i].check + 1)) { + /* At the very beginning of the buffer */ + found = buf + strlen(sigcheck_gpg_status[i].check + 1); + } else { + found = strstr(buf, sigcheck_gpg_status[i].check); + if (!found) + continue; + found += strlen(sigcheck_gpg_status[i].check); + } + sigc->result = sigcheck_gpg_status[i].result; + /* The trust messages are not followed by key/signer information */ + if (sigc->result != 'U') { + sigc->key = xmemdupz(found, 16); + found += 17; + next = strchrnul(found, '\n'); + sigc->signer = xmemdupz(found, next - found); + } + } +} + +void check_commit_signature(const struct commit* commit, struct signature_check *sigc) +{ + struct strbuf payload = STRBUF_INIT; + struct strbuf signature = STRBUF_INIT; + struct strbuf gpg_output = STRBUF_INIT; + struct strbuf gpg_status = STRBUF_INIT; + int status; + + sigc->result = 'N'; + + if (parse_signed_commit(commit->object.sha1, + &payload, &signature) <= 0) + goto out; + status = verify_signed_buffer(payload.buf, payload.len, + signature.buf, signature.len, + &gpg_output, &gpg_status); + if (status && !gpg_output.len) + goto out; + sigc->gpg_output = strbuf_detach(&gpg_output, NULL); + sigc->gpg_status = strbuf_detach(&gpg_status, NULL); + parse_gpg_output(sigc); + + out: + strbuf_release(&gpg_status); + strbuf_release(&gpg_output); + strbuf_release(&payload); + strbuf_release(&signature); +} + + + void append_merge_tag_headers(struct commit_list *parents, struct commit_extra_header ***tail) { @@ -5,6 +5,7 @@ #include "tree.h" #include "strbuf.h" #include "decorate.h" +#include "gpg-interface.h" struct commit_list { struct commit *item; @@ -232,4 +233,13 @@ extern void print_commit_list(struct commit_list *list, const char *format_cur, const char *format_last); +/* + * Check the signature of the given commit. The result of the check is stored + * in sig->check_result, 'G' for a good signature, 'U' for a good signature + * from an untrusted signer, 'B' for a bad signature and 'N' for no signature + * at all. This may allocate memory for sig->gpg_output, sig->gpg_status, + * sig->signer and sig->key. + */ +extern void check_commit_signature(const struct commit* commit, struct signature_check *sigc); + #endif /* COMMIT_H */ diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index 341422a766..756a951459 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -282,6 +282,8 @@ __git_ps1 () r="|MERGING" elif [ -f "$g/CHERRY_PICK_HEAD" ]; then r="|CHERRY-PICKING" + elif [ -f "$g/REVERT_HEAD" ]; then + r="|REVERTING" elif [ -f "$g/BISECT_LOG" ]; then r="|BISECTING" fi diff --git a/contrib/mw-to-git/git-remote-mediawiki.txt b/contrib/mw-to-git/git-remote-mediawiki.txt index 4d211f5b81..23b7ef9f62 100644 --- a/contrib/mw-to-git/git-remote-mediawiki.txt +++ b/contrib/mw-to-git/git-remote-mediawiki.txt @@ -4,4 +4,4 @@ objects from mediawiki just as one would do with a classic git repository thanks to remote-helpers. For more information, visit the wiki at -https://github.com/Bibzball/Git-Mediawiki/wiki +https://github.com/moy/Git-Mediawiki/wiki diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr index c5822e4ac9..fad4a48cdc 100755 --- a/contrib/remote-helpers/git-remote-bzr +++ b/contrib/remote-helpers/git-remote-bzr @@ -191,7 +191,13 @@ def get_filechanges(cur, prev): modified[path] = fid for oldpath, newpath, fid, kind, mod, _ in changes.renamed: removed[oldpath] = None - modified[newpath] = fid + if kind == 'directory': + lst = cur.list_files(from_dir=newpath, recursive=True) + for path, file_class, kind, fid, entry in lst: + if kind != 'directory': + modified[newpath + '/' + path] = fid + else: + modified[newpath] = fid return modified, removed @@ -217,7 +223,7 @@ def export_files(tree, files): # is the blog already exported? if h in filenodes: mark = filenodes[h] - final.append((mode, mark, path)) + final.append((mode, mark, path.encode('utf-8'))) continue d = tree.get_file_text(fid) @@ -234,7 +240,7 @@ def export_files(tree, files): print "data %d" % len(d) print d - final.append((mode, mark, path)) + final.append((mode, mark, path.encode('utf-8'))) return final @@ -260,7 +266,12 @@ def export_branch(branch, name): tz = rev.timezone committer = rev.committer.encode('utf-8') committer = "%s %u %s" % (fixup_user(committer), time, gittz(tz)) - author = committer + authors = rev.get_apparent_authors() + if authors: + author = authors[0].encode('utf-8') + author = "%s %u %s" % (fixup_user(author), time, gittz(tz)) + else: + author = committer msg = rev.message.encode('utf-8') msg += '\n' @@ -297,10 +308,10 @@ def export_branch(branch, name): else: print "merge :%s" % m + for f in removed: + print "D %s" % (f,) for f in modified_final: print "M %s :%u %s" % f - for f in removed: - print "D %s" % (f) print count += 1 @@ -501,6 +512,11 @@ class CustomTree(): def get_symlink_target(self, file_id): return self.updates[file_id]['data'] +def c_style_unescape(string): + if string[0] == string[-1] == '"': + return string.decode('string-escape')[1:-1] + return string + def parse_commit(parser): global marks, blob_marks, bmarks, parsed_refs global mode @@ -540,6 +556,7 @@ def parse_commit(parser): f = { 'deleted' : True } else: die('Unknown file command: %s' % line) + path = c_style_unescape(path).decode('utf-8') files[path] = f repo = parser.repo @@ -619,10 +636,9 @@ def do_export(parser): peer.import_last_revision_info_and_tags(repo, revno, revid) else: peer.import_last_revision_info(repo.repository, revno, revid) - wt = peer.bzrdir.open_workingtree() else: wt = repo.bzrdir.open_workingtree() - wt.update() + wt.update() print "ok %s" % ref print @@ -644,7 +660,11 @@ def do_capabilities(parser): def do_list(parser): global tags print "? refs/heads/%s" % 'master' + + history = parser.repo.revision_history() for tag, revid in parser.repo.tags.get_tag_dict().items(): + if revid not in history: + continue print "? refs/tags/%s" % tag tags[tag] = revid print "@refs/heads/%s HEAD" % 'master' diff --git a/contrib/remote-helpers/test-bzr.sh b/contrib/remote-helpers/test-bzr.sh index 70aa8a010a..4d71f711a6 100755 --- a/contrib/remote-helpers/test-bzr.sh +++ b/contrib/remote-helpers/test-bzr.sh @@ -140,4 +140,99 @@ test_expect_success 'special modes' ' test_cmp expected actual ' +cat > expected <<EOF +100644 blob 54f9d6da5c91d556e6b54340b1327573073030af content +100755 blob 68769579c3eaadbe555379b9c3538e6628bae1eb executable +120000 blob 6b584e8ece562ebffc15d38808cd6b98fc3d97ea link +040000 tree 35c0caa46693cef62247ac89a680f0c5ce32b37b movedir-new +EOF + +test_expect_success 'moving directory' ' + (cd bzrrepo && + mkdir movedir && + echo one > movedir/one && + echo two > movedir/two && + bzr add movedir && + bzr commit -m movedir && + bzr mv movedir movedir-new && + bzr commit -m movedir-new) && + + (cd gitrepo && + git pull && + git ls-tree HEAD > ../actual) && + + test_cmp expected actual +' + +test_expect_success 'different authors' ' + (cd bzrrepo && + echo john >> content && + bzr commit -m john \ + --author "Jane Rey <jrey@example.com>" \ + --author "John Doe <jdoe@example.com>") && + + (cd gitrepo && + git pull && + git show --format="%an <%ae>, %cn <%ce>" --quiet > ../actual) && + + echo "Jane Rey <jrey@example.com>, A U Thor <author@example.com>" > expected && + test_cmp expected actual +' + +test_expect_success 'fetch utf-8 filenames' ' + mkdir -p tmp && cd tmp && + test_when_finished "cd .. && rm -rf tmp && LC_ALL=C" && + + export LC_ALL=en_US.UTF-8 + + ( + bzr init bzrrepo && + cd bzrrepo && + + echo test >> "áéíóú" && + bzr add "áéíóú" && + bzr commit -m utf-8 + ) && + + ( + git clone "bzr::$PWD/bzrrepo" gitrepo && + cd gitrepo && + git ls-files > ../actual + ) && + + echo "\"\\303\\241\\303\\251\\303\\255\\303\\263\\303\\272\"" > expected && + test_cmp expected actual +' + +test_expect_success 'push utf-8 filenames' ' + mkdir -p tmp && cd tmp && + test_when_finished "cd .. && rm -rf tmp && LC_ALL=C" && + + export LC_ALL=en_US.UTF-8 + + ( + bzr init bzrrepo && + cd bzrrepo && + + echo one >> content && + bzr add content && + bzr commit -m one + ) && + + ( + git clone "bzr::$PWD/bzrrepo" gitrepo && + cd gitrepo && + + echo test >> "áéíóú" && + git add "áéíóú" && + git commit -m utf-8 && + + git push + ) && + + (cd bzrrepo && bzr ls > ../actual) && + echo -e "content\náéíóú" > expected && + test_cmp expected actual +' + test_done @@ -59,6 +59,35 @@ inline int git_fnmatch(const char *pattern, const char *string, return fnmatch(pattern, string, fnm_flags); } +static int fnmatch_icase_mem(const char *pattern, int patternlen, + const char *string, int stringlen, + int flags) +{ + int match_status; + struct strbuf pat_buf = STRBUF_INIT; + struct strbuf str_buf = STRBUF_INIT; + const char *use_pat = pattern; + const char *use_str = string; + + if (pattern[patternlen]) { + strbuf_add(&pat_buf, pattern, patternlen); + use_pat = pat_buf.buf; + } + if (string[stringlen]) { + strbuf_add(&str_buf, string, stringlen); + use_str = str_buf.buf; + } + + if (ignore_case) + flags |= WM_CASEFOLD; + match_status = wildmatch(use_pat, use_str, flags, NULL); + + strbuf_release(&pat_buf); + strbuf_release(&str_buf); + + return match_status; +} + static size_t common_prefix_len(const char **pathspec) { const char *n, *first; @@ -626,15 +655,20 @@ int match_basename(const char *basename, int basenamelen, int flags) { if (prefix == patternlen) { - if (!strcmp_icase(pattern, basename)) + if (patternlen == basenamelen && + !strncmp_icase(pattern, basename, basenamelen)) return 1; } else if (flags & EXC_FLAG_ENDSWITH) { + /* "*literal" matching against "fooliteral" */ if (patternlen - 1 <= basenamelen && - !strcmp_icase(pattern + 1, - basename + basenamelen - patternlen + 1)) + !strncmp_icase(pattern + 1, + basename + basenamelen - (patternlen - 1), + patternlen - 1)) return 1; } else { - if (fnmatch_icase(pattern, basename, 0) == 0) + if (fnmatch_icase_mem(pattern, patternlen, + basename, basenamelen, + 0) == 0) return 1; } return 0; @@ -654,6 +688,7 @@ int match_pathname(const char *pathname, int pathlen, */ if (*pattern == '/') { pattern++; + patternlen--; prefix--; } @@ -680,13 +715,22 @@ int match_pathname(const char *pathname, int pathlen, if (strncmp_icase(pattern, name, prefix)) return 0; pattern += prefix; + patternlen -= prefix; name += prefix; namelen -= prefix; + + /* + * If the whole pattern did not have a wildcard, + * then our prefix match is all we need; we + * do not need to call fnmatch at all. + */ + if (!patternlen && !namelen) + return 1; } - return wildmatch(pattern, name, - WM_PATHNAME | (ignore_case ? WM_CASEFOLD : 0), - NULL) == 0; + return fnmatch_icase_mem(pattern, patternlen, + name, namelen, + WM_PATHNAME) == 0; } /* @@ -120,16 +120,18 @@ static int streaming_write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile, int *fstat_done, struct stat *statbuf) { - int result = -1; + int result = 0; int fd; fd = open_output_fd(path, ce, to_tempfile); - if (0 <= fd) { - result = stream_blob_to_fd(fd, ce->sha1, filter, 1); - *fstat_done = fstat_output(fd, state, statbuf); - result = close(fd); - } - if (result && 0 <= fd) + if (fd < 0) + return -1; + + result |= stream_blob_to_fd(fd, ce->sha1, filter, 1); + *fstat_done = fstat_output(fd, state, statbuf); + result |= close(fd); + + if (result) unlink(path); return result; } diff --git a/git-difftool.perl b/git-difftool.perl index 663640d33c..67802922cc 100755 --- a/git-difftool.perl +++ b/git-difftool.perl @@ -13,9 +13,9 @@ use 5.008; use strict; use warnings; +use Error qw(:try); use File::Basename qw(dirname); use File::Copy; -use File::Compare; use File::Find; use File::stat; use File::Path qw(mkpath rmtree); @@ -88,14 +88,45 @@ sub use_wt_file my ($repo, $workdir, $file, $sha1, $symlinks) = @_; my $null_sha1 = '0' x 40; - if ($sha1 eq $null_sha1) { - return 1; - } elsif (not $symlinks) { + if ($sha1 ne $null_sha1 and not $symlinks) { return 0; } my $wt_sha1 = $repo->command_oneline('hash-object', "$workdir/$file"); - return $sha1 eq $wt_sha1; + my $use = ($sha1 eq $null_sha1) || ($sha1 eq $wt_sha1); + return ($use, $wt_sha1); +} + +sub changed_files +{ + my ($repo_path, $index, $worktree) = @_; + $ENV{GIT_INDEX_FILE} = $index; + $ENV{GIT_WORK_TREE} = $worktree; + my $must_unset_git_dir = 0; + if (not defined($ENV{GIT_DIR})) { + $must_unset_git_dir = 1; + $ENV{GIT_DIR} = $repo_path; + } + + my @refreshargs = qw/update-index --really-refresh -q --unmerged/; + my @gitargs = qw/diff-files --name-only -z/; + try { + Git::command_oneline(@refreshargs); + } catch Git::Error::Command with {}; + + my $line = Git::command_oneline(@gitargs); + my @files; + if (defined $line) { + @files = split('\0', $line); + } else { + @files = (); + } + + delete($ENV{GIT_INDEX_FILE}); + delete($ENV{GIT_WORK_TREE}); + delete($ENV{GIT_DIR}) if ($must_unset_git_dir); + + return map { $_ => 1 } @files; } sub setup_dir_diff @@ -121,6 +152,7 @@ sub setup_dir_diff my $null_sha1 = '0' x 40; my $lindex = ''; my $rindex = ''; + my $wtindex = ''; my %submodule; my %symlink; my @working_tree = (); @@ -174,8 +206,12 @@ EOF } if ($rmode ne $null_mode) { - if (use_wt_file($repo, $workdir, $dst_path, $rsha1, $symlinks)) { - push(@working_tree, $dst_path); + my ($use, $wt_sha1) = use_wt_file($repo, $workdir, + $dst_path, $rsha1, + $symlinks); + if ($use) { + push @working_tree, $dst_path; + $wtindex .= "$rmode $wt_sha1\t$dst_path\0"; } else { $rindex .= "$rmode $rsha1\t$dst_path\0"; } @@ -218,6 +254,12 @@ EOF $rc = system('git', 'checkout-index', '--all', "--prefix=$rdir/"); exit_cleanup($tmpdir, $rc) if $rc != 0; + $ENV{GIT_INDEX_FILE} = "$tmpdir/wtindex"; + ($inpipe, $ctx) = + $repo->command_input_pipe(qw(update-index --info-only -z --index-info)); + print($inpipe $wtindex); + $repo->command_close_pipe($inpipe, $ctx); + # If $GIT_DIR was explicitly set just for the update/checkout # commands, then it should be unset before continuing. delete($ENV{GIT_DIR}) if ($must_unset_git_dir); @@ -390,19 +432,34 @@ sub dir_diff # should be copied back to the working tree. # Do not copy back files when symlinks are used and the # external tool did not replace the original link with a file. + # + # These hashes are loaded lazily since they aren't needed + # in the common case of --symlinks and the difftool updating + # files through the symlink. + my %wt_modified; + my %tmp_modified; + my $indices_loaded = 0; + for my $file (@worktree) { next if $symlinks && -l "$b/$file"; next if ! -f "$b/$file"; - my $diff = compare("$b/$file", "$workdir/$file"); - if ($diff == 0) { - next; - } elsif ($diff == -1) { - my $errmsg = "warning: Could not compare "; - $errmsg += "'$b/$file' with '$workdir/$file'\n"; + if (!$indices_loaded) { + %wt_modified = changed_files($repo->repo_path(), + "$tmpdir/wtindex", "$workdir"); + %tmp_modified = changed_files($repo->repo_path(), + "$tmpdir/wtindex", "$b"); + $indices_loaded = 1; + } + + if (exists $wt_modified{$file} and exists $tmp_modified{$file}) { + my $errmsg = "warning: Both files modified: "; + $errmsg .= "'$workdir/$file' and '$b/$file'.\n"; + $errmsg .= "warning: Working tree file has been left.\n"; + $errmsg .= "warning:\n"; warn $errmsg; $error = 1; - } elsif ($diff == 1) { + } elsif (exists $tmp_modified{$file}) { my $mode = stat("$b/$file")->mode; copy("$b/$file", "$workdir/$file") or exit_cleanup($tmpdir, 1); diff --git a/git-filter-branch.sh b/git-filter-branch.sh index 53142492af..ac2a005fdb 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -199,6 +199,7 @@ t) test -d "$tempdir" && die "$tempdir already exists, please remove it" esac +orig_dir=$(pwd) mkdir -p "$tempdir/t" && tempdir="$(cd "$tempdir"; pwd)" && cd "$tempdir/t" && @@ -206,7 +207,7 @@ workdir="$(pwd)" || die "" # Remove tempdir on exit -trap 'cd ../..; rm -rf "$tempdir"' 0 +trap 'cd "$orig_dir"; rm -rf "$tempdir"' 0 ORIG_GIT_DIR="$GIT_DIR" ORIG_GIT_WORK_TREE="$GIT_WORK_TREE" @@ -469,7 +470,7 @@ if [ "$filter_tag_name" ]; then done fi -cd ../.. +cd "$orig_dir" rm -rf "$tempdir" trap - 0 diff --git a/git-pull.sh b/git-pull.sh index 5d97e97bd9..638aabb7b3 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -39,7 +39,7 @@ test -z "$(git ls-files -u)" || die_conflict test -f "$GIT_DIR/MERGE_HEAD" && die_merge strategy_args= diffstat= no_commit= squash= no_ff= ff_only= -log_arg= verbosity= progress= recurse_submodules= +log_arg= verbosity= progress= recurse_submodules= verify_signatures= merge_args= edit= curr_branch=$(git symbolic-ref -q HEAD) curr_branch_short="${curr_branch#refs/heads/}" @@ -125,6 +125,12 @@ do --no-recurse-submodules) recurse_submodules=--no-recurse-submodules ;; + --verify-signatures) + verify_signatures=--verify-signatures + ;; + --no-verify-signatures) + verify_signatures=--no-verify-signatures + ;; --d|--dr|--dry|--dry-|--dry-r|--dry-ru|--dry-run) dry_run=--dry-run ;; @@ -283,7 +289,7 @@ true) eval="$eval --onto $merge_head ${oldremoteref:-$merge_head}" ;; *) - eval="git-merge $diffstat $no_commit $edit $squash $no_ff $ff_only" + eval="git-merge $diffstat $no_commit $verify_signatures $edit $squash $no_ff $ff_only" eval="$eval $log_arg $strategy_args $merge_args $verbosity $progress" eval="$eval \"\$merge_name\" HEAD $merge_head" ;; diff --git a/git-send-email.perl b/git-send-email.perl index c3501d987e..7297472818 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -512,8 +512,9 @@ if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) { ($sender) = expand_aliases($sender) if defined $sender; -# returns 1 if the conflict must be solved using it as a format-patch argument -sub check_file_rev_conflict($) { +# is_format_patch_arg($f) returns 0 if $f names a patch, or 1 if +# $f is a revision list specification to be passed to format-patch. +sub is_format_patch_arg { return unless $repo; my $f = shift; try { @@ -529,6 +530,7 @@ to produce patches for. Please disambiguate by... * Giving --format-patch option if you mean a range. EOF } catch Git::Error::Command with { + # Not a valid revision. Treat it as a filename. return 0; } } @@ -540,14 +542,14 @@ while (defined(my $f = shift @ARGV)) { if ($f eq "--") { push @rev_list_opts, "--", @ARGV; @ARGV = (); - } elsif (-d $f and !check_file_rev_conflict($f)) { + } elsif (-d $f and !is_format_patch_arg($f)) { opendir my $dh, $f or die "Failed to opendir $f: $!"; push @files, grep { -f $_ } map { catfile($f, $_) } sort readdir $dh; closedir $dh; - } elsif ((-f $f or -p $f) and !check_file_rev_conflict($f)) { + } elsif ((-f $f or -p $f) and !is_format_patch_arg($f)) { push @files, $f; } else { push @rev_list_opts, $f; @@ -711,7 +713,7 @@ sub ask { } } } - return undef; + return; } my %broken_encoding; @@ -833,7 +835,7 @@ sub extract_valid_address { # less robust/correct than the monster regexp in Email::Valid, # but still does a 99% job, and one less dependency return $1 if $address =~ /($local_part_regexp\@$domain_regexp)/; - return undef; + return; } sub extract_valid_address_or_die { @@ -1453,7 +1455,7 @@ sub recipients_cmd { my $sanitized_sender = sanitize_address($sender); my @addresses = (); - open my $fh, "$cmd \Q$file\E |" + open my $fh, "-|", "$cmd \Q$file\E" or die "($prefix) Could not execute '$cmd'"; while (my $address = <$fh>) { $address =~ s/^\s*//g; @@ -1499,7 +1501,7 @@ sub validate_patch { return "$.: patch contains a line longer than 998 characters"; } } - return undef; + return; } sub file_has_nonascii { diff --git a/git-submodule.sh b/git-submodule.sh index 204bc78a9f..79bfaac9d4 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -267,6 +267,11 @@ module_clone() (clear_local_git_env; cd "$sm_path" && GIT_WORK_TREE=. git config core.worktree "$rel/$b") } +isnumber() +{ + n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1" +} + # # Add a new submodule to the working tree, .gitmodules and the index # @@ -601,10 +606,12 @@ cmd_deinit() if test -z "$force" then - git rm -n "$sm_path" || + git rm -qn "$sm_path" || die "$(eval_gettext "Submodule work tree '\$sm_path' contains local modifications; use '-f' to discard them")" fi - rm -rf "$sm_path" || say "$(eval_gettext "Could not remove submodule work tree '\$sm_path'")" + rm -rf "$sm_path" && + say "$(eval_gettext "Cleared directory '\$sm_path'")" || + say "$(eval_gettext "Could not remove submodule work tree '\$sm_path'")" fi mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$sm_path'")" @@ -889,14 +896,14 @@ cmd_summary() { for_status="$1" ;; -n|--summary-limit) - if summary_limit=$(($2 + 0)) 2>/dev/null && test "$summary_limit" = "$2" - then - : - else - usage - fi + summary_limit="$2" + isnumber "$summary_limit" || usage shift ;; + --summary-limit=*) + summary_limit="${1#--summary-limit=}" + isnumber "$summary_limit" || usage + ;; --) shift break diff --git a/git-web--browse.sh b/git-web--browse.sh index 1e827264b4..9f446798d4 100755 --- a/git-web--browse.sh +++ b/git-web--browse.sh @@ -119,8 +119,8 @@ if test -z "$browser" ; then browser_candidates="w3m elinks links lynx" fi # SECURITYSESSIONID indicates an OS X GUI login session - if test -n "$SECURITYSESSIONID" \ - -o "$TERM_PROGRAM" = "Apple_Terminal" ; then + if test -n "$SECURITYSESSIONID" || test -n "$TERM_PROGRAM" + then browser_candidates="open $browser_candidates" fi # /bin/start indicates MinGW diff --git a/gpg-interface.h b/gpg-interface.h index cf99021842..a85cb5bc97 100644 --- a/gpg-interface.h +++ b/gpg-interface.h @@ -1,6 +1,18 @@ #ifndef GPG_INTERFACE_H #define GPG_INTERFACE_H +struct signature_check { + char *gpg_output; + char *gpg_status; + char result; /* 0 (not checked), + * N (checked but no further result), + * U (untrusted good), + * G (good) + * B (bad) */ + char *signer; + char *key; +}; + extern int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key); extern int verify_signed_buffer(const char *payload, size_t payload_size, const char *signature, size_t signature_size, struct strbuf *gpg_output, struct strbuf *gpg_status); extern int git_gpg_config(const char *, const char *, void *); @@ -766,14 +766,7 @@ struct format_commit_context { const struct pretty_print_context *pretty_ctx; unsigned commit_header_parsed:1; unsigned commit_message_parsed:1; - unsigned commit_signature_parsed:1; - struct { - char *gpg_output; - char *gpg_status; - char good_bad; - char *signer; - char *key; - } signature; + struct signature_check signature_check; char *message; size_t width, indent1, indent2; @@ -956,64 +949,6 @@ static void rewrap_message_tail(struct strbuf *sb, c->indent2 = new_indent2; } -static struct { - char result; - const char *check; -} signature_check[] = { - { 'G', "\n[GNUPG:] GOODSIG " }, - { 'B', "\n[GNUPG:] BADSIG " }, -}; - -static void parse_signature_lines(struct format_commit_context *ctx) -{ - const char *buf = ctx->signature.gpg_status; - int i; - - for (i = 0; i < ARRAY_SIZE(signature_check); i++) { - const char *found = strstr(buf, signature_check[i].check); - const char *next; - if (!found) - continue; - ctx->signature.good_bad = signature_check[i].result; - found += strlen(signature_check[i].check); - ctx->signature.key = xmemdupz(found, 16); - found += 17; - next = strchrnul(found, '\n'); - ctx->signature.signer = xmemdupz(found, next - found); - break; - } -} - -static void parse_commit_signature(struct format_commit_context *ctx) -{ - struct strbuf payload = STRBUF_INIT; - struct strbuf signature = STRBUF_INIT; - struct strbuf gpg_output = STRBUF_INIT; - struct strbuf gpg_status = STRBUF_INIT; - int status; - - ctx->commit_signature_parsed = 1; - - if (parse_signed_commit(ctx->commit->object.sha1, - &payload, &signature) <= 0) - goto out; - status = verify_signed_buffer(payload.buf, payload.len, - signature.buf, signature.len, - &gpg_output, &gpg_status); - if (status && !gpg_output.len) - goto out; - ctx->signature.gpg_output = strbuf_detach(&gpg_output, NULL); - ctx->signature.gpg_status = strbuf_detach(&gpg_status, NULL); - parse_signature_lines(ctx); - - out: - strbuf_release(&gpg_status); - strbuf_release(&gpg_output); - strbuf_release(&payload); - strbuf_release(&signature); -} - - static int format_reflog_person(struct strbuf *sb, char part, struct reflog_walk_info *log, @@ -1199,27 +1134,29 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder, } if (placeholder[0] == 'G') { - if (!c->commit_signature_parsed) - parse_commit_signature(c); + if (!c->signature_check.result) + check_commit_signature(c->commit, &(c->signature_check)); switch (placeholder[1]) { case 'G': - if (c->signature.gpg_output) - strbuf_addstr(sb, c->signature.gpg_output); + if (c->signature_check.gpg_output) + strbuf_addstr(sb, c->signature_check.gpg_output); break; case '?': - switch (c->signature.good_bad) { + switch (c->signature_check.result) { case 'G': case 'B': - strbuf_addch(sb, c->signature.good_bad); + case 'U': + case 'N': + strbuf_addch(sb, c->signature_check.result); } break; case 'S': - if (c->signature.signer) - strbuf_addstr(sb, c->signature.signer); + if (c->signature_check.signer) + strbuf_addstr(sb, c->signature_check.signer); break; case 'K': - if (c->signature.key) - strbuf_addstr(sb, c->signature.key); + if (c->signature_check.key) + strbuf_addstr(sb, c->signature_check.key); break; } return 2; @@ -1357,8 +1294,8 @@ void format_commit_message(const struct commit *commit, rewrap_message_tail(sb, &context, 0, 0, 0); logmsg_free(context.message, commit); - free(context.signature.gpg_output); - free(context.signature.signer); + free(context.signature_check.gpg_output); + free(context.signature_check.signer); } static void pp_header(const struct pretty_print_context *pp, diff --git a/resolve-undo.c b/resolve-undo.c index 72b46125b7..639eb9c59f 100644 --- a/resolve-undo.c +++ b/resolve-undo.c @@ -118,7 +118,7 @@ int unmerge_index_entry_at(struct index_state *istate, int pos) struct cache_entry *ce; struct string_list_item *item; struct resolve_undo_info *ru; - int i, err = 0; + int i, err = 0, matched; if (!istate->resolve_undo) return pos; @@ -137,6 +137,7 @@ int unmerge_index_entry_at(struct index_state *istate, int pos) ru = item->util; if (!ru) return pos; + matched = ce->ce_flags & CE_MATCHED; remove_index_entry_at(istate, pos); for (i = 0; i < 3; i++) { struct cache_entry *nce; @@ -144,6 +145,8 @@ int unmerge_index_entry_at(struct index_state *istate, int pos) continue; nce = make_cache_entry(ru->mode[i], ru->sha1[i], ce->name, i + 1, 0); + if (matched) + nce->ce_flags |= CE_MATCHED; if (add_index_entry(istate, nce, ADD_CACHE_OK_TO_ADD)) { err = 1; error("cannot unmerge '%s'", ce->name); @@ -156,6 +159,20 @@ int unmerge_index_entry_at(struct index_state *istate, int pos) return unmerge_index_entry_at(istate, pos); } +void unmerge_marked_index(struct index_state *istate) +{ + int i; + + if (!istate->resolve_undo) + return; + + for (i = 0; i < istate->cache_nr; i++) { + struct cache_entry *ce = istate->cache[i]; + if (ce->ce_flags & CE_MATCHED) + i = unmerge_index_entry_at(istate, i); + } +} + void unmerge_index(struct index_state *istate, const char **pathspec) { int i; diff --git a/resolve-undo.h b/resolve-undo.h index 845876911d..7a30206aad 100644 --- a/resolve-undo.h +++ b/resolve-undo.h @@ -12,5 +12,6 @@ extern struct string_list *resolve_undo_read(const char *, unsigned long); extern void resolve_undo_clear_index(struct index_state *); extern int unmerge_index_entry_at(struct index_state *, int); extern void unmerge_index(struct index_state *, const char **); +extern void unmerge_marked_index(struct index_state *); #endif diff --git a/sha1_file.c b/sha1_file.c index 5f573d9b81..0ed23981b3 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1271,6 +1271,10 @@ int check_sha1_signature(const unsigned char *sha1, void *map, char buf[1024 * 16]; ssize_t readlen = read_istream(st, buf, sizeof(buf)); + if (readlen < 0) { + close_istream(st); + return -1; + } if (!readlen) break; git_SHA1_Update(&c, buf, readlen); diff --git a/sha1_name.c b/sha1_name.c index 2fbda48e02..3820f28ae7 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -594,7 +594,7 @@ struct object *peel_to_type(const char *name, int namelen, while (1) { if (!o || (!o->parsed && !parse_object(o->sha1))) return NULL; - if (o->type == expected_type) + if (expected_type == OBJ_ANY || o->type == expected_type) return o; if (o->type == OBJ_TAG) o = ((struct tag*) o)->tagged; @@ -645,6 +645,8 @@ static int peel_onion(const char *name, int len, unsigned char *sha1) expected_type = OBJ_TREE; else if (!strncmp(blob_type, sp, 4) && sp[4] == '}') expected_type = OBJ_BLOB; + else if (!prefixcmp(sp, "object}")) + expected_type = OBJ_ANY; else if (sp[0] == '}') expected_type = OBJ_NONE; else if (sp[0] == '/') @@ -654,6 +656,8 @@ static int peel_onion(const char *name, int len, unsigned char *sha1) if (expected_type == OBJ_COMMIT) lookup_flags = GET_SHA1_COMMITTISH; + else if (expected_type == OBJ_TREE) + lookup_flags = GET_SHA1_TREEISH; if (get_sha1_1(name, sp - name - 2, outer, lookup_flags)) return -1; diff --git a/streaming.c b/streaming.c index 4d978e54e4..cabcd9d157 100644 --- a/streaming.c +++ b/streaming.c @@ -237,7 +237,7 @@ static read_method_decl(filtered) if (!fs->input_finished) { fs->i_end = read_istream(fs->upstream, fs->ibuf, FILTER_BUFFER); if (fs->i_end < 0) - break; + return -1; if (fs->i_end) continue; } @@ -309,7 +309,7 @@ static read_method_decl(loose) st->z_state = z_done; break; } - if (status != Z_OK && status != Z_BUF_ERROR) { + if (status != Z_OK && (status != Z_BUF_ERROR || total_read < sz)) { git_inflate_end(&st->z); st->z_state = z_error; return -1; @@ -514,6 +514,8 @@ int stream_blob_to_fd(int fd, unsigned const char *sha1, struct stream_filter *f ssize_t wrote, holeto; ssize_t readlen = read_istream(st, buf, sizeof(buf)); + if (readlen < 0) + goto close_and_exit; if (!readlen) break; if (can_seek && sizeof(buf) == readlen) { @@ -92,17 +92,26 @@ appropriately before running "make". This causes additional long-running tests to be run (where available), for more exhaustive testing. ---valgrind:: - Execute all Git binaries with valgrind and exit with status - 126 on errors (just like regular tests, this will only stop - the test script when running under -i). Valgrind errors - go to stderr, so you might want to pass the -v option, too. +--valgrind=<tool>:: + Execute all Git binaries under valgrind tool <tool> and exit + with status 126 on errors (just like regular tests, this will + only stop the test script when running under -i). Since it makes no sense to run the tests with --valgrind and not see any output, this option implies --verbose. For convenience, it also implies --tee. - Note that valgrind is run with the option --leak-check=no, + <tool> defaults to 'memcheck', just like valgrind itself. + Other particularly useful choices include 'helgrind' and + 'drd', but you may use any tool recognized by your valgrind + installation. + + As a special case, <tool> can be 'memcheck-fast', which uses + memcheck but disables --track-origins. Use this if you are + running tests in bulk, to see if there are _any_ memory + issues. + + Note that memcheck is run with the option --leak-check=no, as the git process is short-lived and some errors are not interesting. In order to run a single command under the same conditions manually, you should set GIT_VALGRIND to point to diff --git a/t/lib-gpg/pubring.gpg b/t/lib-gpg/pubring.gpg Binary files differindex 83855fa4e1..1a3c2d487c 100644 --- a/t/lib-gpg/pubring.gpg +++ b/t/lib-gpg/pubring.gpg diff --git a/t/lib-gpg/random_seed b/t/lib-gpg/random_seed Binary files differindex 8fed1339ed..95d249f15f 100644 --- a/t/lib-gpg/random_seed +++ b/t/lib-gpg/random_seed diff --git a/t/lib-gpg/secring.gpg b/t/lib-gpg/secring.gpg Binary files differindex d831cd9eb3..82dca8f80b 100644 --- a/t/lib-gpg/secring.gpg +++ b/t/lib-gpg/secring.gpg diff --git a/t/lib-gpg/trustdb.gpg b/t/lib-gpg/trustdb.gpg Binary files differindex abace962b8..4879ae9a84 100644 --- a/t/lib-gpg/trustdb.gpg +++ b/t/lib-gpg/trustdb.gpg diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh new file mode 100755 index 0000000000..3f8705139d --- /dev/null +++ b/t/t1060-object-corruption.sh @@ -0,0 +1,104 @@ +#!/bin/sh + +test_description='see how we handle various forms of corruption' +. ./test-lib.sh + +# convert "1234abcd" to ".git/objects/12/34abcd" +obj_to_file() { + echo "$(git rev-parse --git-dir)/objects/$(git rev-parse "$1" | sed 's,..,&/,')" +} + +# Convert byte at offset "$2" of object "$1" into '\0' +corrupt_byte() { + obj_file=$(obj_to_file "$1") && + chmod +w "$obj_file" && + printf '\0' | dd of="$obj_file" bs=1 seek="$2" conv=notrunc +} + +test_expect_success 'setup corrupt repo' ' + git init bit-error && + ( + cd bit-error && + test_commit content && + corrupt_byte HEAD:content.t 10 + ) +' + +test_expect_success 'setup repo with missing object' ' + git init missing && + ( + cd missing && + test_commit content && + rm -f "$(obj_to_file HEAD:content.t)" + ) +' + +test_expect_success 'setup repo with misnamed object' ' + git init misnamed && + ( + cd misnamed && + test_commit content && + good=$(obj_to_file HEAD:content.t) && + blob=$(echo corrupt | git hash-object -w --stdin) && + bad=$(obj_to_file $blob) && + rm -f "$good" && + mv "$bad" "$good" + ) +' + +test_expect_success 'streaming a corrupt blob fails' ' + ( + cd bit-error && + test_must_fail git cat-file blob HEAD:content.t + ) +' + +test_expect_success 'read-tree -u detects bit-errors in blobs' ' + ( + cd bit-error && + rm -f content.t && + test_must_fail git read-tree --reset -u HEAD + ) +' + +test_expect_success 'read-tree -u detects missing objects' ' + ( + cd missing && + rm -f content.t && + test_must_fail git read-tree --reset -u HEAD + ) +' + +# We use --bare to make sure that the transport detects it, not the checkout +# phase. +test_expect_success 'clone --no-local --bare detects corruption' ' + test_must_fail git clone --no-local --bare bit-error corrupt-transport +' + +test_expect_success 'clone --no-local --bare detects missing object' ' + test_must_fail git clone --no-local --bare missing missing-transport +' + +test_expect_success 'clone --no-local --bare detects misnamed object' ' + test_must_fail git clone --no-local --bare misnamed misnamed-transport +' + +# We do not expect --local to detect corruption at the transport layer, +# so we are really checking the checkout() code path. +test_expect_success 'clone --local detects corruption' ' + test_must_fail git clone --local bit-error corrupt-checkout +' + +test_expect_success 'error detected during checkout leaves repo intact' ' + test_path_is_dir corrupt-checkout/.git +' + +test_expect_success 'clone --local detects missing objects' ' + test_must_fail git clone --local missing missing-checkout +' + +test_expect_failure 'clone --local detects misnamed objects' ' + test_must_fail git clone --local misnamed misnamed-checkout +' + +test_done diff --git a/t/t2022-checkout-paths.sh b/t/t2022-checkout-paths.sh index 56090d2eba..8e3545d868 100755 --- a/t/t2022-checkout-paths.sh +++ b/t/t2022-checkout-paths.sh @@ -39,4 +39,26 @@ test_expect_success 'checking out paths out of a tree does not clobber unrelated test_cmp expect.next2 dir/next2 ' +test_expect_success 'do not touch unmerged entries matching $path but not in $tree' ' + git checkout next && + git reset --hard && + + cat dir/common >expect.common && + EMPTY_SHA1=$(git hash-object -w --stdin </dev/null) && + git rm dir/next0 && + cat >expect.next0 <<-EOF && + 100644 $EMPTY_SHA1 1 dir/next0 + 100644 $EMPTY_SHA1 2 dir/next0 + EOF + git update-index --index-info <expect.next0 && + + git checkout master dir && + + test_cmp expect.common dir/common && + test_path_is_file dir/master && + git diff --exit-code master dir/master && + git ls-files -s dir/next0 >actual.next0 && + test_cmp expect.next0 actual.next0 +' + test_done diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 09f65f8358..d969f0ecd8 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -75,7 +75,7 @@ test_expect_success 'git branch l should work after branch l/m has been deleted' test_expect_success 'git branch -m dumps usage' ' test_expect_code 128 git branch -m 2>err && - test_i18ngrep "too many branches for a rename operation" err + test_i18ngrep "branch name required" err ' test_expect_success 'git branch -m m m/m should work' ' diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh index 6f6ee88b28..0bbcf0603d 100755 --- a/t/t4124-apply-ws-rule.sh +++ b/t/t4124-apply-ws-rule.sh @@ -486,4 +486,30 @@ test_expect_success 'same, but with CR-LF line endings && cr-at-eol unset' ' test_cmp one expect ' +test_expect_success 'whitespace=fix to expand' ' + qz_to_tab_space >preimage <<-\EOF && + QQa + QQb + QQc + ZZZZZZZZZZZZZZZZd + QQe + QQf + QQg + EOF + qz_to_tab_space >patch <<-\EOF && + diff --git a/preimage b/preimage + --- a/preimage + +++ b/preimage + @@ -1,7 +1,6 @@ + QQa + QQb + QQc + -QQd + QQe + QQf + QQg + EOF + git -c core.whitespace=tab-in-indent apply --whitespace=fix patch +' + test_done diff --git a/t/t4150-am.sh b/t/t4150-am.sh index cdafd7e7c1..12f6b027ac 100755 --- a/t/t4150-am.sh +++ b/t/t4150-am.sh @@ -17,7 +17,7 @@ test_expect_success 'setup: messages' ' vero eos et accusam et justo duo dolores et ea rebum. EOF - q_to_tab <<-\EOF >>msg && + qz_to_tab_space <<-\EOF >>msg && QDuis autem vel eum iriure dolor in hendrerit in vulputate velit Qesse molestie consequat, vel illum dolore eu feugiat nulla facilisis Qat vero eros et accumsan et iusto odio dignissim qui blandit diff --git a/t/t5002-archive-attr-pattern.sh b/t/t5002-archive-attr-pattern.sh index 0c847fb454..6667d159ab 100755 --- a/t/t5002-archive-attr-pattern.sh +++ b/t/t5002-archive-attr-pattern.sh @@ -27,6 +27,25 @@ test_expect_success 'setup' ' echo ignored-only-if-dir/ export-ignore >>.git/info/attributes && git add ignored-only-if-dir && + mkdir -p ignored-without-slash && + echo "ignored without slash" >ignored-without-slash/foo && + git add ignored-without-slash/foo && + echo "ignored-without-slash export-ignore" >>.git/info/attributes && + + mkdir -p wildcard-without-slash && + echo "ignored without slash" >wildcard-without-slash/foo && + git add wildcard-without-slash/foo && + echo "wild*-without-slash export-ignore" >>.git/info/attributes && + + mkdir -p deep/and/slashless && + echo "ignored without slash" >deep/and/slashless/foo && + git add deep/and/slashless/foo && + echo "deep/and/slashless export-ignore" >>.git/info/attributes && + + mkdir -p deep/with/wildcard && + echo "ignored without slash" >deep/with/wildcard/foo && + git add deep/with/wildcard/foo && + echo "deep/*t*/wildcard export-ignore" >>.git/info/attributes && mkdir -p one-level-lower/two-levels-lower/ignored-only-if-dir && echo ignored by ignored dir >one-level-lower/two-levels-lower/ignored-only-if-dir/ignored-by-ignored-dir && @@ -49,6 +68,14 @@ test_expect_exists archive/not-ignored-dir/ignored-only-if-dir test_expect_exists archive/not-ignored-dir/ test_expect_missing archive/ignored-only-if-dir/ test_expect_missing archive/ignored-ony-if-dir/ignored-by-ignored-dir +test_expect_missing archive/ignored-without-slash/ && +test_expect_missing archive/ignored-without-slash/foo && +test_expect_missing archive/wildcard-without-slash/ +test_expect_missing archive/wildcard-without-slash/foo && +test_expect_missing archive/deep/and/slashless/ && +test_expect_missing archive/deep/and/slashless/foo && +test_expect_missing archive/deep/with/wildcard/ && +test_expect_missing archive/deep/with/wildcard/foo && test_expect_exists archive/one-level-lower/ test_expect_missing archive/one-level-lower/two-levels-lower/ignored-only-if-dir/ test_expect_missing archive/one-level-lower/two-levels-lower/ignored-ony-if-dir/ignored-by-ignored-dir diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 383a2eb1ea..32ad89107b 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -217,10 +217,10 @@ test_expect_success 'push with pushInsteadOf' ' test_expect_success 'push with pushInsteadOf and explicit pushurl (pushInsteadOf should not rewrite)' ' mk_empty && - TRASH="$(pwd)/" && - test_config "url.trash2/.pushInsteadOf" trash/ && + test_config "url.trash2/.pushInsteadOf" testrepo/ && + test_config "url.trash3/.pusnInsteadOf" trash/wrong && test_config remote.r.url trash/wrong && - test_config remote.r.pushurl "$TRASH/testrepo" && + test_config remote.r.pushurl "testrepo/" && git push r refs/heads/master:refs/remotes/origin/master && ( cd testrepo && diff --git a/t/t5710-info-alternate.sh b/t/t5710-info-alternate.sh index aa045295de..8956c21617 100755 --- a/t/t5710-info-alternate.sh +++ b/t/t5710-info-alternate.sh @@ -58,13 +58,7 @@ test_expect_success 'creating too deep nesting' \ git clone -l -s D E && git clone -l -s E F && git clone -l -s F G && -git clone -l -s G H' - -test_expect_success 'invalidity of deepest repository' \ -'cd H && { - test_valid_repo - test $? -ne 0 -}' +test_must_fail git clone --bare -l -s G H' cd "$base_dir" diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh index 992c2a0467..f73eceabfb 100755 --- a/t/t6200-fmt-merge-msg.sh +++ b/t/t6200-fmt-merge-msg.sh @@ -112,8 +112,8 @@ test_expect_success '[merge] summary/log configuration' ' Common #1 EOF - git config merge.log true && - test_might_fail git config --unset-all merge.summary && + test_config merge.log true && + test_unconfig merge.summary && git checkout master && test_tick && @@ -121,8 +121,8 @@ test_expect_success '[merge] summary/log configuration' ' git fmt-merge-msg <.git/FETCH_HEAD >actual1 && - test_might_fail git config --unset-all merge.log && - git config merge.summary true && + test_unconfig merge.log && + test_config merge.summary true && git checkout master && test_tick && @@ -134,11 +134,6 @@ test_expect_success '[merge] summary/log configuration' ' test_cmp expected actual2 ' -test_expect_success 'setup: clear [merge] configuration' ' - test_might_fail git config --unset-all merge.log && - test_might_fail git config --unset-all merge.summary -' - test_expect_success 'setup FETCH_HEAD' ' git checkout master && test_tick && @@ -248,14 +243,14 @@ test_expect_success 'fmt-merge-msg -m' ' Common #1 EOF - test_might_fail git config --unset merge.log && - test_might_fail git config --unset merge.summary && + test_unconfig merge.log && + test_unconfig merge.summary && git checkout master && git fetch "$(pwd)" left && git fmt-merge-msg -m "Sync with left" <.git/FETCH_HEAD >actual && git fmt-merge-msg --log -m "Sync with left" \ <.git/FETCH_HEAD >actual.log && - git config merge.log true && + test_config merge.log true && git fmt-merge-msg -m "Sync with left" \ <.git/FETCH_HEAD >actual.log-config && git fmt-merge-msg --no-log -m "Sync with left" \ @@ -290,29 +285,29 @@ test_expect_success 'setup: expected shortlog for two branches' ' ' test_expect_success 'shortlog for two branches' ' - git config merge.log true && - test_might_fail git config --unset-all merge.summary && + test_config merge.log true && + test_unconfig merge.summary && git checkout master && test_tick && git fetch . left right && git fmt-merge-msg <.git/FETCH_HEAD >actual1 && - test_might_fail git config --unset-all merge.log && - git config merge.summary true && + test_unconfig merge.log && + test_config merge.summary true && git checkout master && test_tick && git fetch . left right && git fmt-merge-msg <.git/FETCH_HEAD >actual2 && - git config merge.log yes && - test_might_fail git config --unset-all merge.summary && + test_config merge.log yes && + test_unconfig merge.summary && git checkout master && test_tick && git fetch . left right && git fmt-merge-msg <.git/FETCH_HEAD >actual3 && - test_might_fail git config --unset-all merge.log && - git config merge.summary yes && + test_unconfig merge.log && + test_config merge.summary yes && git checkout master && test_tick && git fetch . left right && @@ -325,8 +320,8 @@ test_expect_success 'shortlog for two branches' ' ' test_expect_success 'merge-msg -F' ' - test_might_fail git config --unset-all merge.log && - git config merge.summary yes && + test_unconfig merge.log && + test_config merge.summary yes && git checkout master && test_tick && git fetch . left right && @@ -335,8 +330,8 @@ test_expect_success 'merge-msg -F' ' ' test_expect_success 'merge-msg -F in subdirectory' ' - test_might_fail git config --unset-all merge.log && - git config merge.summary yes && + test_unconfig merge.log && + test_config merge.summary yes && git checkout master && test_tick && git fetch . left right && @@ -350,8 +345,8 @@ test_expect_success 'merge-msg -F in subdirectory' ' ' test_expect_success 'merge-msg with nothing to merge' ' - test_might_fail git config --unset-all merge.log && - git config merge.summary yes && + test_unconfig merge.log && + test_config merge.summary yes && >empty && @@ -376,8 +371,8 @@ test_expect_success 'merge-msg tag' ' Common #1 EOF - test_might_fail git config --unset-all merge.log && - git config merge.summary yes && + test_unconfig merge.log && + test_config merge.summary yes && git checkout master && test_tick && @@ -406,8 +401,8 @@ test_expect_success 'merge-msg two tags' ' Common #1 EOF - test_might_fail git config --unset-all merge.log && - git config merge.summary yes && + test_unconfig merge.log && + test_config merge.summary yes && git checkout master && test_tick && @@ -436,8 +431,8 @@ test_expect_success 'merge-msg tag and branch' ' Common #1 EOF - test_might_fail git config --unset-all merge.log && - git config merge.summary yes && + test_unconfig merge.log && + test_config merge.summary yes && git checkout master && test_tick && @@ -464,6 +459,8 @@ test_expect_success 'merge-msg lots of commits' ' echo " ..." } >expected && + test_config merge.summary yes && + git checkout master && test_tick && git fetch . long && @@ -472,4 +469,43 @@ test_expect_success 'merge-msg lots of commits' ' test_cmp expected actual ' +test_expect_success 'merge-msg with "merging" an annotated tag' ' + test_config merge.log true && + + git checkout master^0 && + git commit --allow-empty -m "One step ahead" && + git tag -a -m "An annotated one" annote HEAD && + + git checkout master && + git fetch . annote && + + git fmt-merge-msg <.git/FETCH_HEAD >actual && + { + cat <<-\EOF + Merge tag '\''annote'\'' + + An annotated one + + * tag '\''annote'\'': + One step ahead + EOF + } >expected && + test_cmp expected actual && + + test_when_finished "git reset --hard" && + annote=$(git rev-parse annote) && + git merge --no-commit $annote && + { + cat <<-EOF + Merge tag '\''$annote'\'' + + An annotated one + + * tag '\''$annote'\'': + One step ahead + EOF + } >expected && + test_cmp expected .git/MERGE_MSG +' + test_done diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index 1e7a209efa..9496736a89 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -64,6 +64,20 @@ test_expect_success 'correct GIT_DIR while using -d' ' grep drepo "$TRASHDIR/backup-refs" ' +test_expect_success 'tree-filter works with -d' ' + git init drepo-tree && + ( + cd drepo-tree && + test_commit one && + git filter-branch -d "$TRASHDIR/dfoo" \ + --tree-filter "echo changed >one.t" && + echo changed >expect && + git cat-file blob HEAD:one.t >actual && + test_cmp expect actual && + test_cmp one.t actual + ) +' + test_expect_success 'Fail if commit filter fails' ' test_must_fail git filter-branch -f --commit-filter "exit 1" HEAD ' diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index 5030f1f1bc..ff265353a3 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -777,18 +777,22 @@ test_expect_success 'submodule deinit . deinits all initialized submodules' ' git config submodule.example.foo bar && git config submodule.example2.frotz nitfol && test_must_fail git submodule deinit && - git submodule deinit . && + git submodule deinit . >actual && test -z "$(git config --get-regexp "submodule\.example\.")" && test -z "$(git config --get-regexp "submodule\.example2\.")" && + test_i18ngrep "Cleared directory .init" actual && + test_i18ngrep "Cleared directory .example2" actual && rmdir init example2 ' test_expect_success 'submodule deinit deinits a submodule when its work tree is missing or empty' ' git submodule update --init && rm -rf init example2/* example2/.git && - git submodule deinit init example2 && + git submodule deinit init example2 >actual && test -z "$(git config --get-regexp "submodule\.example\.")" && test -z "$(git config --get-regexp "submodule\.example2\.")" && + test_i18ngrep ! "Cleared directory .init" actual && + test_i18ngrep "Cleared directory .example2" actual && rmdir init ' @@ -798,8 +802,9 @@ test_expect_success 'submodule deinit fails when the submodule contains modifica test_must_fail git submodule deinit init && test -n "$(git config --get-regexp "submodule\.example\.")" && test -f example2/.git && - git submodule deinit -f init && + git submodule deinit -f init >actual && test -z "$(git config --get-regexp "submodule\.example\.")" && + test_i18ngrep "Cleared directory .init" actual && rmdir init ' @@ -809,8 +814,9 @@ test_expect_success 'submodule deinit fails when the submodule contains untracke test_must_fail git submodule deinit init && test -n "$(git config --get-regexp "submodule\.example\.")" && test -f example2/.git && - git submodule deinit -f init && + git submodule deinit -f init >actual && test -z "$(git config --get-regexp "submodule\.example\.")" && + test_i18ngrep "Cleared directory .init" actual && rmdir init ' @@ -823,8 +829,9 @@ test_expect_success 'submodule deinit fails when the submodule HEAD does not mat test_must_fail git submodule deinit init && test -n "$(git config --get-regexp "submodule\.example\.")" && test -f example2/.git && - git submodule deinit -f init && + git submodule deinit -f init >actual && test -z "$(git config --get-regexp "submodule\.example\.")" && + test_i18ngrep "Cleared directory .init" actual && rmdir init ' @@ -832,14 +839,18 @@ test_expect_success 'submodule deinit is silent when used on an uninitialized su git submodule update --init && git submodule deinit init >actual && test_i18ngrep "Submodule .example. (.*) unregistered for path .init" actual && + test_i18ngrep "Cleared directory .init" actual && git submodule deinit init >actual && test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual && + test_i18ngrep "Cleared directory .init" actual && git submodule deinit . >actual && test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual && test_i18ngrep "Submodule .example2. (.*) unregistered for path .example2" actual && + test_i18ngrep "Cleared directory .init" actual && git submodule deinit . >actual && test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual && test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual && + test_i18ngrep "Cleared directory .init" actual && rmdir init example2 ' diff --git a/t/t7612-merge-verify-signatures.sh b/t/t7612-merge-verify-signatures.sh new file mode 100755 index 0000000000..21a0bf8fb8 --- /dev/null +++ b/t/t7612-merge-verify-signatures.sh @@ -0,0 +1,61 @@ +#!/bin/sh + +test_description='merge signature verification tests' +. ./test-lib.sh +. "$TEST_DIRECTORY/lib-gpg.sh" + +test_expect_success GPG 'create signed commits' ' + echo 1 >file && git add file && + test_tick && git commit -m initial && + git tag initial && + + git checkout -b side-signed && + echo 3 >elif && git add elif && + test_tick && git commit -S -m "signed on side" && + git checkout initial && + + git checkout -b side-unsigned && + echo 3 >foo && git add foo && + test_tick && git commit -m "unsigned on side" && + git checkout initial && + + git checkout -b side-bad && + echo 3 >bar && git add bar && + test_tick && git commit -S -m "bad on side" && + git cat-file commit side-bad >raw && + sed -e "s/bad/forged bad/" raw >forged && + git hash-object -w -t commit forged >forged.commit && + git checkout initial && + + git checkout -b side-untrusted && + echo 3 >baz && git add baz && + test_tick && git commit -SB7227189 -m "untrusted on side" + + git checkout master +' + +test_expect_success GPG 'merge unsigned commit with verification' ' + test_must_fail git merge --ff-only --verify-signatures side-unsigned 2>mergeerror && + test_i18ngrep "does not have a GPG signature" mergeerror +' + +test_expect_success GPG 'merge commit with bad signature with verification' ' + test_must_fail git merge --ff-only --verify-signatures $(cat forged.commit) 2>mergeerror && + test_i18ngrep "has a bad GPG signature" mergeerror +' + +test_expect_success GPG 'merge commit with untrusted signature with verification' ' + test_must_fail git merge --ff-only --verify-signatures side-untrusted 2>mergeerror && + test_i18ngrep "has an untrusted GPG signature" mergeerror +' + +test_expect_success GPG 'merge signed commit with verification' ' + git merge --verbose --ff-only --verify-signatures side-signed >mergeoutput && + test_i18ngrep "has a good GPG signature" mergeoutput +' + +test_expect_success GPG 'merge commit with bad signature without verification' ' + git merge $(cat forged.commit) +' + +test_done diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh index c6d6b1c99f..a6bd99eaf5 100755 --- a/t/t7800-difftool.sh +++ b/t/t7800-difftool.sh @@ -23,16 +23,6 @@ prompt_given () test "$prompt" = "Launch 'test-tool' [Y/n]: branch" } -stdin_contains () -{ - grep >/dev/null "$1" -} - -stdin_doesnot_contain () -{ - ! stdin_contains "$1" -} - # Create a file on master and change it on branch test_expect_success PERL 'setup' ' echo master >file && @@ -296,24 +286,24 @@ test_expect_success PERL 'setup with 2 files different' ' test_expect_success PERL 'say no to the first file' ' (echo n && echo) >input && git difftool -x cat branch <input >output && - stdin_contains m2 <output && - stdin_contains br2 <output && - stdin_doesnot_contain master <output && - stdin_doesnot_contain branch <output + grep m2 output && + grep br2 output && + ! grep master output && + ! grep branch output ' test_expect_success PERL 'say no to the second file' ' (echo && echo n) >input && git difftool -x cat branch <input >output && - stdin_contains master <output && - stdin_contains branch <output && - stdin_doesnot_contain m2 <output && - stdin_doesnot_contain br2 <output + grep master output && + grep branch output && + ! grep m2 output && + ! grep br2 output ' test_expect_success PERL 'difftool --tool-help' ' git difftool --tool-help >output && - stdin_contains tool <output + grep tool output ' test_expect_success PERL 'setup change in subdirectory' ' @@ -324,20 +314,46 @@ test_expect_success PERL 'setup change in subdirectory' ' git commit -m "added sub/sub" && echo test >>file && echo test >>sub/sub && - git add . && + git add file sub/sub && git commit -m "modified both" ' -test_expect_success PERL 'difftool -d' ' - git difftool -d --extcmd ls branch >output && - stdin_contains sub <output && - stdin_contains file <output +run_dir_diff_test () { + test_expect_success PERL "$1 --no-symlinks" " + symlinks=--no-symlinks && + $2 + " + test_expect_success PERL,SYMLINKS "$1 --symlinks" " + symlinks=--symlinks && + $2 + " +} + +run_dir_diff_test 'difftool -d' ' + git difftool -d $symlinks --extcmd ls branch >output && + grep sub output && + grep file output +' + +run_dir_diff_test 'difftool --dir-diff' ' + git difftool --dir-diff $symlinks --extcmd ls branch >output && + grep sub output && + grep file output ' -test_expect_success PERL 'difftool --dir-diff' ' - git difftool --dir-diff --extcmd ls branch >output && - stdin_contains sub <output && - stdin_contains file <output +run_dir_diff_test 'difftool --dir-diff ignores --prompt' ' + git difftool --dir-diff $symlinks --prompt --extcmd ls branch >output && + grep sub output && + grep file output +' + +run_dir_diff_test 'difftool --dir-diff from subdirectory' ' + ( + cd sub && + git difftool --dir-diff $symlinks --extcmd ls branch >output && + grep sub output && + grep file output + ) ' write_script .git/CHECK_SYMLINKS <<\EOF @@ -362,18 +378,33 @@ test_expect_success PERL,SYMLINKS 'difftool --dir-diff --symlink without unstage test_cmp actual expect ' -test_expect_success PERL 'difftool --dir-diff ignores --prompt' ' - git difftool --dir-diff --prompt --extcmd ls branch >output && - stdin_contains sub <output && - stdin_contains file <output +write_script modify-file <<\EOF +echo "new content" >file +EOF + +test_expect_success PERL 'difftool --no-symlinks does not overwrite working tree file ' ' + echo "orig content" >file && + git difftool --dir-diff --no-symlinks --extcmd "$(pwd)/modify-file" branch && + echo "new content" >expect && + test_cmp expect file ' -test_expect_success PERL 'difftool --dir-diff from subdirectory' ' +write_script modify-both-files <<\EOF +echo "wt content" >file && +echo "tmp content" >"$2/file" && +echo "$2" >tmpdir +EOF + +test_expect_success PERL 'difftool --no-symlinks detects conflict ' ' ( - cd sub && - git difftool --dir-diff --extcmd ls branch >output && - stdin_contains sub <output && - stdin_contains file <output + TMPDIR=$TRASH_DIRECTORY && + export TMPDIR && + echo "orig content" >file && + test_must_fail git difftool --dir-diff --no-symlinks --extcmd "$(pwd)/modify-both-files" branch && + echo "wt content" >expect && + test_cmp expect file && + echo "tmp content" >expect && + test_cmp expect "$(cat tmpdir)/file" ) ' diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index 61d0804435..52510094ad 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -91,6 +91,10 @@ q_to_tab () { tr Q '\011' } +qz_to_tab_space () { + tr QZ '\011\040' +} + append_cr () { sed -e 's/$/Q/' | tr Q '\015' } @@ -536,6 +540,9 @@ test_must_fail () { elif test $exit_code = 127; then echo >&2 "test_must_fail: command not found: $*" return 1 + elif test $exit_code = 126; then + echo >&2 "test_must_fail: valgrind error: $*" + return 1 fi return 0 } diff --git a/t/test-lib.sh b/t/test-lib.sh index 1f510252ad..da57a2f45c 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -193,7 +193,11 @@ do --no-color) color=; shift ;; --va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind) - valgrind=t; verbose=t; shift ;; + valgrind=memcheck + shift ;; + --valgrind=*) + valgrind=$(expr "z$1" : 'z[^=]*=\(.*\)') + shift ;; --tee) shift ;; # was handled already --root=*) @@ -204,6 +208,8 @@ do esac done +test -n "$valgrind" && verbose=t + if test -n "$color" then say_color () { @@ -530,6 +536,8 @@ then PATH=$GIT_VALGRIND/bin:$PATH GIT_EXEC_PATH=$GIT_VALGRIND/bin export GIT_VALGRIND + GIT_VALGRIND_MODE="$valgrind" + export GIT_VALGRIND_MODE elif test -n "$GIT_TEST_INSTALLED" then GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path) || diff --git a/t/valgrind/valgrind.sh b/t/valgrind/valgrind.sh index 582b4dca94..6b87c91b60 100755 --- a/t/valgrind/valgrind.sh +++ b/t/valgrind/valgrind.sh @@ -2,20 +2,27 @@ base=$(basename "$0") -TRACK_ORIGINS= +TOOL_OPTIONS='--leak-check=no' -VALGRIND_VERSION=$(valgrind --version) -VALGRIND_MAJOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*\([0-9]*\)') -VALGRIND_MINOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*[0-9]*\.\([0-9]*\)') -test 3 -gt "$VALGRIND_MAJOR" || -test 3 -eq "$VALGRIND_MAJOR" -a 4 -gt "$VALGRIND_MINOR" || -TRACK_ORIGINS=--track-origins=yes +case "$GIT_VALGRIND_MODE" in +memcheck-fast) + ;; +memcheck) + VALGRIND_VERSION=$(valgrind --version) + VALGRIND_MAJOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*\([0-9]*\)') + VALGRIND_MINOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*[0-9]*\.\([0-9]*\)') + test 3 -gt "$VALGRIND_MAJOR" || + test 3 -eq "$VALGRIND_MAJOR" -a 4 -gt "$VALGRIND_MINOR" || + TOOL_OPTIONS="$TOOL_OPTIONS --track-origins=yes" + ;; +*) + TOOL_OPTIONS="--tool=$GIT_VALGRIND_MODE" +esac exec valgrind -q --error-exitcode=126 \ - --leak-check=no \ - --suppressions="$GIT_VALGRIND/default.supp" \ --gen-suppressions=all \ - $TRACK_ORIGINS \ + --suppressions="$GIT_VALGRIND/default.supp" \ + $TOOL_OPTIONS \ --log-fd=4 \ --input-fd=4 \ $GIT_VALGRIND_OPTIONS \ |