diff options
84 files changed, 1767 insertions, 549 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..c15cf2e805 --- /dev/null +++ b/Documentation/RelNotes/1.8.1.6.txt @@ -0,0 +1,39 @@ +Git 1.8.1.6 Release Notes +========================= + +Fixes since v1.8.1.5 +-------------------- + + * An earlier change to the attribute system introduced at v1.8.1.2 by + mistake stopped a pattern "dir" (without trailing slash) from + matching a directory "dir" (it only wanted to allow pattern "dir/" + to also match). + + * 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..1354ad03f5 100644 --- a/Documentation/RelNotes/1.8.2.1.txt +++ b/Documentation/RelNotes/1.8.2.1.txt @@ -4,6 +4,46 @@ Git v1.8.2.1 Release Notes Fixes since v1.8.2 ------------------ + * An earlier change to the attribute system introduced at v1.8.1.2 by + mistake stopped a pattern "dir" (without trailing slash) from + matching a directory "dir" (it only wanted to allow pattern "dir/" + to also match). + + * 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..ca76a2be58 100644 --- a/Documentation/RelNotes/1.8.3.txt +++ b/Documentation/RelNotes/1.8.3.txt @@ -32,6 +32,18 @@ Updates since v1.8.2 UI, Workflows & Features + * A triangular "pull from one place, push to another place" workflow + is supported better by new remote.pushdefault (overrides the + "origin" thing) and branch.*.pushremote (overrides the + branch.*.remote) configuration variables. + + * "git status" learned to report that you are in the middle of a + revert session, just like it does for a cherry-pick and a bisect + session. + + * The handing by "git branch --set-upstream-to" against various forms + of errorneous inputs was suboptimal and has been improved. + * When the interactive access to git-shell is not enabled, it issues a message meant to help the system admininstrator to enable it. An explicit way to help the end users who connect to the service by @@ -70,6 +82,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,11 +97,25 @@ 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. * Updates for building under msvc. + * The code to enforce permission bits on files in $GIT_DIR/ for + shared repositories have been simplified. + * A few codepaths knew how much data they need to put in the hashtables they use upfront, but still started from a small table repeatedly growing and rehashing. @@ -105,6 +135,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 +150,35 @@ 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). + * When used with "-d temporary-directory" option, "git filter-branch" + failed to come back to the original working tree to perform the + final clean-up procedure. + (merge 9727601 jk/filter-branch-come-back-to-original later to maint). + + * "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 +190,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 +210,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 +229,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 +265,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 +278,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/cat-texi.perl b/Documentation/cat-texi.perl index 828ec62554..87437f8a95 100755 --- a/Documentation/cat-texi.perl +++ b/Documentation/cat-texi.perl @@ -12,6 +12,7 @@ while (<STDIN>) { push @menu, $1; } s/\(\@pxref{\[(URLS|REMOTES)\]}\)//; + s/\@anchor\{[^{}]*\}//g; print TMP; } close TMP; diff --git a/Documentation/config.txt b/Documentation/config.txt index f79184c0a8..3d750e0452 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -727,9 +727,22 @@ branch.autosetuprebase:: This option defaults to never. branch.<name>.remote:: - When in branch <name>, it tells 'git fetch' and 'git push' which - remote to fetch from/push to. It defaults to `origin` if no remote is - configured. `origin` is also used if you are not on any branch. + When on branch <name>, it tells 'git fetch' and 'git push' + which remote to fetch from/push to. The remote to push to + may be overridden with `remote.pushdefault` (for all branches). + The remote to push to, for the current branch, may be further + overridden by `branch.<name>.pushremote`. If no remote is + configured, or if you are not on any branch, it defaults to + `origin` for fetching and `remote.pushdefault` for pushing. + +branch.<name>.pushremote:: + When on branch <name>, it overrides `branch.<name>.remote` for + pushing. It also overrides `remote.pushdefault` for pushing + from branch <name>. When you pull from one place (e.g. your + upstream) and push to another place (e.g. your own publishing + repository), you would want to set `remote.pushdefault` to + specify the remote to push to for all branches, and use this + option to override it for a specific branch. branch.<name>.merge:: Defines, together with branch.<name>.remote, the upstream branch @@ -1898,6 +1911,11 @@ receive.updateserverinfo:: If set to true, git-receive-pack will run git-update-server-info after receiving data from git-push and updating refs. +remote.pushdefault:: + The remote to push to by default. Overrides + `branch.<name>.remote` for all branches, and is overridden by + `branch.<name>.pushremote` for specific branches. + remote.<name>.url:: The URL of a remote repository. See linkgit:git-fetch[1] or linkgit:git-push[1]. diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 05f8297368..9b1be5581d 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -207,14 +207,15 @@ variable (see linkgit:git-config[1]). without changing its commit message. --amend:: - Used to amend the tip of the current branch. Prepare the tree - object you would want to replace the latest commit as usual - (this includes the usual -i/-o and explicit paths), and the - commit log editor is seeded with the commit message from the - tip of the current branch. The commit you create replaces the - current tip -- if it was a merge, it will have the parents of - the current tip as parents -- so the current top commit is - discarded. + Replace the tip of the current branch by creating a new + commit. The recorded tree is prepared as usual (including + the effect of the `-i` and `-o` options and explicit + pathspec), and the message from the original commit is used + as the starting point, instead of an empty message, when no + other message is specified from the command line via options + such as `-m`, `-F`, `-c`, etc. The new commit has the same + parents and author as the current one (the `--reset-author` + option can countermand this). + -- It is a rough equivalent for: 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/git.txt b/Documentation/git.txt index 4307d62bd4..6a875f2ade 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,14 +43,16 @@ unreleased) version of Git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v1.8.2/git.html[documentation for release 1.8.2] +* link:v1.8.2.1/git.html[documentation for release 1.8.2.1] * release notes for + link:RelNotes/1.8.2.1.txt[1.8.2.1]. link:RelNotes/1.8.2.txt[1.8.2]. -* link:v1.8.1.5/git.html[documentation for release 1.8.1.5] +* link:v1.8.1.6/git.html[documentation for release 1.8.1.6] * release notes for + link:RelNotes/1.8.1.6.txt[1.8.1.6], link:RelNotes/1.8.1.5.txt[1.8.1.5], link:RelNotes/1.8.1.4.txt[1.8.1.4], link:RelNotes/1.8.1.3.txt[1.8.1.3], 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 @@ -13,6 +13,7 @@ int advice_commit_before_merge = 1; int advice_resolve_conflict = 1; int advice_implicit_identity = 1; int advice_detached_head = 1; +int advice_set_upstream_failure = 1; static struct { const char *name; @@ -31,6 +32,7 @@ static struct { { "resolveconflict", &advice_resolve_conflict }, { "implicitidentity", &advice_implicit_identity }, { "detachedhead", &advice_detached_head }, + { "setupstreamfailure", &advice_set_upstream_failure }, /* make this an alias for backward compatibility */ { "pushnonfastforward", &advice_push_update_rejected } @@ -16,6 +16,7 @@ extern int advice_commit_before_merge; extern int advice_resolve_conflict; extern int advice_implicit_identity; extern int advice_detached_head; +extern int advice_set_upstream_failure; int git_default_advice_config(const char *var, const char *value); void advise(const char *advice, ...); @@ -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) @@ -525,9 +525,9 @@ struct commit_list *filter_skipped(struct commit_list *list, * is increased by one between each call, but that should not matter * for this application. */ -static int get_prn(int count) { +static unsigned get_prn(unsigned count) { count = count * 1103515245 + 12345; - return ((unsigned)(count/65536) % PRN_MODULO); + return (count/65536) % PRN_MODULO; } /* @@ -197,6 +197,20 @@ int validate_new_branchname(const char *name, struct strbuf *ref, return 1; } +static const char upstream_not_branch[] = +N_("Cannot setup tracking information; starting point '%s' is not a branch."); +static const char upstream_missing[] = +N_("the requested upstream branch '%s' does not exist"); +static const char upstream_advice[] = +N_("\n" +"If you are planning on basing your work on an upstream\n" +"branch that already exists at the remote, you may need to\n" +"run \"git fetch\" to retrieve it.\n" +"\n" +"If you are planning to push out a new local branch that\n" +"will track its remote counterpart, you may want to use\n" +"\"git push -u\" to set the upstream config as you push."); + void create_branch(const char *head, const char *name, const char *start_name, int force, int reflog, int clobber_head, @@ -224,21 +238,30 @@ void create_branch(const char *head, } real_ref = NULL; - if (get_sha1(start_name, sha1)) + if (get_sha1(start_name, sha1)) { + if (explicit_tracking) { + if (advice_set_upstream_failure) { + error(_(upstream_missing), start_name); + advise(_(upstream_advice)); + exit(1); + } + die(_(upstream_missing), start_name); + } die("Not a valid object name: '%s'.", start_name); + } switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) { case 0: /* Not branching from any existing branch */ if (explicit_tracking) - die("Cannot setup tracking information; starting point is not a branch."); + die(_(upstream_not_branch), start_name); break; case 1: /* Unique completion -- good, only if it is a real branch */ if (prefixcmp(real_ref, "refs/heads/") && prefixcmp(real_ref, "refs/remotes/")) { if (explicit_tracking) - die("Cannot setup tracking information; starting point is not a branch."); + die(_(upstream_not_branch), start_name); else real_ref = NULL; } 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); diff --git a/builtin/push.c b/builtin/push.c index 5e4a0e958f..909c34dfda 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -322,7 +322,7 @@ static int push_with_options(struct transport *transport, int flags) static int do_push(const char *repo, int flags) { int i, errs; - struct remote *remote = remote_get(repo); + struct remote *remote = pushremote_get(repo); const char **url; int url_nr; diff --git a/builtin/rm.c b/builtin/rm.c index dabfcf6890..7b91d52f39 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -110,7 +110,7 @@ static int check_local_mod(unsigned char *head, int index_only) ce = active_cache[pos]; if (lstat(ce->name, &st) < 0) { - if (errno != ENOENT) + if (errno != ENOENT && errno != ENOTDIR) warning("'%s': %s", ce->name, strerror(errno)); /* It already vanished from the working tree */ continue; @@ -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 */ @@ -716,8 +719,7 @@ enum sharedrepo { PERM_EVERYBODY = 0664 }; int git_config_perm(const char *var, const char *value); -int set_shared_perm(const char *path, int mode); -#define adjust_shared_perm(path) set_shared_perm((path), 0) +int adjust_shared_perm(const char *path); int safe_create_leading_directories(char *path); int safe_create_leading_directories_const(const char *path); int mkdir_in_gitdir(const char *path); @@ -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/Makefile b/contrib/remote-helpers/Makefile index 9a76575f78..239161de33 100644 --- a/contrib/remote-helpers/Makefile +++ b/contrib/remote-helpers/Makefile @@ -3,6 +3,7 @@ TESTS := $(wildcard test*.sh) export T := $(addprefix $(CURDIR)/,$(TESTS)) export MAKE := $(MAKE) -e export PATH := $(CURDIR):$(PATH) +export TEST_LINT := test-lint-executable test-lint-shell-syntax test: $(MAKE) -C ../../t $@ diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr index c5822e4ac9..bd25e08202 100755 --- a/contrib/remote-helpers/git-remote-bzr +++ b/contrib/remote-helpers/git-remote-bzr @@ -25,6 +25,7 @@ bzrlib.plugin.load_plugins() import bzrlib.generate_ids import bzrlib.transport +import bzrlib.errors import sys import os @@ -183,15 +184,24 @@ def get_filechanges(cur, prev): changes = cur.changes_from(prev) + def u(s): + return s.encode('utf-8') + for path, fid, kind in changes.added: - modified[path] = fid + modified[u(path)] = fid for path, fid, kind in changes.removed: - removed[path] = None + removed[u(path)] = None for path, fid, kind, mod, _ in changes.modified: - modified[path] = fid + modified[u(path)] = fid for oldpath, newpath, fid, kind, mod, _ in changes.renamed: - removed[oldpath] = None - modified[newpath] = fid + removed[u(oldpath)] = None + 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[u(newpath + '/' + path)] = fid + else: + modified[u(newpath)] = fid return modified, removed @@ -260,7 +270,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 +312,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 @@ -321,12 +336,9 @@ def export_branch(branch, name): def export_tag(repo, name): global tags - try: - print "reset refs/tags/%s" % name - print "from :%u" % rev_to_mark(tags[name]) - print - except KeyError: - warn("TODO: fetch tag '%s'" % name) + print "reset refs/tags/%s" % name + print "from :%u" % rev_to_mark(tags[name]) + print def do_import(parser): global dirname @@ -501,6 +513,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 +557,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 +637,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 @@ -641,12 +658,25 @@ def do_capabilities(parser): print +def ref_is_valid(name): + return not True in [c in name for c in '~^: \\'] + def do_list(parser): global tags print "? refs/heads/%s" % 'master' - for tag, revid in parser.repo.tags.get_tag_dict().items(): + + branch = parser.repo + branch.lock_read() + for tag, revid in branch.tags.get_tag_dict().items(): + try: + branch.revision_id_to_dotted_revno(revid) + except bzrlib.errors.NoSuchRevision: + continue + if not ref_is_valid(tag): + continue print "? refs/tags/%s" % tag tags[tag] = revid + branch.unlock() print "@refs/heads/%s HEAD" % 'master' print diff --git a/contrib/remote-helpers/test-bzr.sh b/contrib/remote-helpers/test-bzr.sh index 70aa8a010a..e800c97fcf 100755 --- a/contrib/remote-helpers/test-bzr.sh +++ b/contrib/remote-helpers/test-bzr.sh @@ -17,20 +17,6 @@ if ! "$PYTHON_PATH" -c 'import bzrlib'; then test_done fi -cmd=' -import bzrlib -bzrlib.initialize() -import bzrlib.plugin -bzrlib.plugin.load_plugins() -import bzrlib.plugins.fastimport -' - -if ! "$PYTHON_PATH" -c "$cmd"; then - echo "consider setting BZR_PLUGIN_PATH=$HOME/.bazaar/plugins" 1>&2 - skip_all='skipping remote-bzr tests; bzr-fastimport not available' - test_done -fi - check () { (cd $1 && git log --format='%s' -1 && @@ -136,7 +122,109 @@ test_expect_success 'special modes' ' (cd gitrepo && git cat-file -p HEAD:link > ../actual) && - echo -n content > expected && + printf content > expected && + 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 "áéíóú" && + echo test >> "îø∫∆" && + bzr add "îø∫∆" && + bzr commit -m utf-8 && + echo test >> "áéíóú" && + bzr commit -m utf-8 && + bzr rm "îø∫∆" && + bzr mv "áéíóú" "åß∂" && + bzr commit -m utf-8 + ) && + + ( + git clone "bzr::$PWD/bzrrepo" gitrepo && + cd gitrepo && + git ls-files > ../actual + ) && + + echo "\"\\303\\245\\303\\237\\342\\210\\202\"" > 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 ' diff --git a/contrib/remote-helpers/test-hg-hg-git.sh b/contrib/remote-helpers/test-hg-hg-git.sh index 7e3967f5b6..3f253b7de7 100755 --- a/contrib/remote-helpers/test-hg-hg-git.sh +++ b/contrib/remote-helpers/test-hg-hg-git.sh @@ -140,7 +140,6 @@ test_expect_success 'executable bit' ' git_clone_$x hgrepo-$x gitrepo2-$x && git_log gitrepo2-$x > log-$x done && - cp -r log-* output-* /tmp/foo/ && test_cmp output-hg output-git && test_cmp log-hg log-git diff --git a/contrib/remote-helpers/test-hg.sh b/contrib/remote-helpers/test-hg.sh index 5f81dfae6c..7bb81f2f8e 100755 --- a/contrib/remote-helpers/test-hg.sh +++ b/contrib/remote-helpers/test-hg.sh @@ -115,7 +115,7 @@ test_expect_success 'update bookmark' ' git push ) && - hg -R hgrepo bookmarks | grep "devel\s\+3:" + hg -R hgrepo bookmarks | egrep "devel[ ]+3:" ' test_done diff --git a/diffcore-break.c b/diffcore-break.c index 44f8678d22..1d9e530a84 100644 --- a/diffcore-break.c +++ b/diffcore-break.c @@ -68,6 +68,9 @@ static int should_break(struct diff_filespec *src, if (max_size < MINIMUM_BREAK_SIZE) return 0; /* we do not break too small filepair */ + if (!src->size) + return 0; /* we do not let empty files get renamed */ + if (diffcore_count_changes(src, dst, &src->cnt_data, &dst->cnt_data, 0, @@ -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; } /* @@ -1603,7 +1647,7 @@ int remove_path(const char *name) { char *slash; - if (unlink(name) && errno != ENOENT) + if (unlink(name) && errno != ENOENT && errno != ENOTDIR) return -1; slash = strrchr(name, '/'); @@ -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 *); @@ -1,14 +1,5 @@ /* - * I'm tired of doing "vsnprintf()" etc just to open a - * file, so here's a "return static buffer with printf" - * interface for paths. - * - * It's obviously not thread-safe. Sue me. But it's quite - * useful for doing things like - * - * f = open(mkpath("%s/%s.git", base, name), O_RDONLY); - * - * which is what it's designed for. + * Utilities for paths and pathnames */ #include "cache.h" #include "strbuf.h" @@ -405,26 +396,14 @@ const char *enter_repo(const char *path, int strict) return NULL; } -int set_shared_perm(const char *path, int mode) +static int calc_shared_perm(int mode) { - int tweak, shared, orig_mode; + int tweak; - if (!shared_repository) { - if (mode) - return chmod(path, mode & ~S_IFMT); - return 0; - } - if (!mode) { - if (get_st_mode_bits(path, &mode) < 0) - return -1; - orig_mode = mode; - } else - orig_mode = 0; if (shared_repository < 0) - shared = -shared_repository; + tweak = -shared_repository; else - shared = shared_repository; - tweak = shared; + tweak = shared_repository; if (!(mode & S_IWUSR)) tweak &= ~0222; @@ -436,16 +415,28 @@ int set_shared_perm(const char *path, int mode) else mode |= tweak; - if (S_ISDIR(mode)) { + return mode; +} + + +int adjust_shared_perm(const char *path) +{ + int old_mode, new_mode; + + if (!shared_repository) + return 0; + if (get_st_mode_bits(path, &old_mode) < 0) + return -1; + + new_mode = calc_shared_perm(old_mode); + if (S_ISDIR(old_mode)) { /* Copy read bits to execute bits */ - mode |= (shared & 0444) >> 2; - mode |= FORCE_DIR_SET_GID; + new_mode |= (new_mode & 0444) >> 2; + new_mode |= FORCE_DIR_SET_GID; } - if (((shared_repository < 0 - ? (orig_mode & (FORCE_DIR_SET_GID | 0777)) - : (orig_mode & mode)) != mode) && - chmod(path, (mode & ~S_IFMT)) < 0) + if (((old_mode ^ new_mode) & ~S_IFMT) && + chmod(path, (new_mode & ~S_IFMT)) < 0) return -2; return 0; } @@ -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, @@ -49,6 +49,7 @@ static int branches_nr; static struct branch *current_branch; static const char *default_remote_name; +static const char *pushremote_name; static int explicit_default_remote_name; static struct rewrites rewrites; @@ -357,13 +358,16 @@ static int handle_config(const char *key, const char *value, void *cb) return 0; branch = make_branch(name, subkey - name); if (!strcmp(subkey, ".remote")) { - if (!value) - return config_error_nonbool(key); - branch->remote_name = xstrdup(value); + if (git_config_string(&branch->remote_name, key, value)) + return -1; if (branch == current_branch) { default_remote_name = branch->remote_name; explicit_default_remote_name = 1; } + } else if (!strcmp(subkey, ".pushremote")) { + if (branch == current_branch) + if (git_config_string(&pushremote_name, key, value)) + return -1; } else if (!strcmp(subkey, ".merge")) { if (!value) return config_error_nonbool(key); @@ -389,9 +393,16 @@ static int handle_config(const char *key, const char *value, void *cb) add_instead_of(rewrite, xstrdup(value)); } } + if (prefixcmp(key, "remote.")) return 0; name = key + 7; + + /* Handle remote.* variables */ + if (!strcmp(name, "pushdefault")) + return git_config_string(&pushremote_name, key, value); + + /* Handle remote.<name>.* variables */ if (*name == '/') { warning("Config remote shorthand cannot begin with '/': %s", name); @@ -671,17 +682,21 @@ static int valid_remote_nick(const char *name) return !strchr(name, '/'); /* no slash */ } -struct remote *remote_get(const char *name) +static struct remote *remote_get_1(const char *name, const char *pushremote_name) { struct remote *ret; int name_given = 0; - read_config(); if (name) name_given = 1; else { - name = default_remote_name; - name_given = explicit_default_remote_name; + if (pushremote_name) { + name = pushremote_name; + name_given = 1; + } else { + name = default_remote_name; + name_given = explicit_default_remote_name; + } } ret = make_remote(name, 0); @@ -700,6 +715,18 @@ struct remote *remote_get(const char *name) return ret; } +struct remote *remote_get(const char *name) +{ + read_config(); + return remote_get_1(name, NULL); +} + +struct remote *pushremote_get(const char *name) +{ + read_config(); + return remote_get_1(name, pushremote_name); +} + int remote_is_configured(const char *name) { int i; @@ -51,6 +51,7 @@ struct remote { }; struct remote *remote_get(const char *name); +struct remote *pushremote_get(const char *name); int remote_is_configured(const char *name); typedef int each_remote_fn(struct remote *remote, void *priv); 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/revision.c b/revision.c index 71e62d8312..eb98128868 100644 --- a/revision.c +++ b/revision.c @@ -1970,22 +1970,6 @@ static struct merge_simplify_state *locate_simplify_state(struct rev_info *revs, return st; } -static void remove_treesame_parents(struct commit *commit) -{ - struct commit_list **pp, *p; - - pp = &commit->parents; - while ((p = *pp) != NULL) { - struct commit *parent = p->item; - if (parent->object.flags & TREESAME) { - *pp = p->next; - free(p); - continue; - } - pp = &p->next; - } -} - static struct commit_list **simplify_one(struct rev_info *revs, struct commit *commit, struct commit_list **tail) { struct commit_list *p; @@ -2039,17 +2023,10 @@ static struct commit_list **simplify_one(struct rev_info *revs, struct commit *c break; } - if (revs->first_parent_only) { + if (revs->first_parent_only) cnt = 1; - } else { - /* - * A merge with a tree-same parent is useless - */ - if (commit->parents && commit->parents->next) - remove_treesame_parents(commit); - + else cnt = remove_duplicate_parents(commit); - } /* * It is possible that we are a merge and one side branch 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 b08c9f2295..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' ' @@ -409,6 +409,18 @@ test_expect_success '--set-upstream-to fails on detached HEAD' ' git checkout - ' +test_expect_success '--set-upstream-to fails on a missing dst branch' ' + test_must_fail git branch --set-upstream-to master does-not-exist +' + +test_expect_success '--set-upstream-to fails on a missing src branch' ' + test_must_fail git branch --set-upstream-to does-not-exist master +' + +test_expect_success '--set-upstream-to fails on a non-ref' ' + test_must_fail git branch --set-upstream-to HEAD^{} +' + test_expect_success 'use --set-upstream-to modify HEAD' ' test_config branch.master.remote foo && test_config branch.master.merge foo && diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index 37bf5f13b0..0c44e9f5d0 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -622,4 +622,69 @@ test_expect_success 'rm of a populated nested submodule with a nested .git direc rm -rf submod ' +test_expect_success 'rm of d/f when d has become a non-directory' ' + rm -rf d && + mkdir d && + >d/f && + git add d && + rm -rf d && + >d && + git rm d/f && + test_must_fail git rev-parse --verify :d/f && + test_path_is_file d +' + +test_expect_success SYMLINKS 'rm of d/f when d has become a dangling symlink' ' + rm -rf d && + mkdir d && + >d/f && + git add d && + rm -rf d && + ln -s nonexistent d && + git rm d/f && + test_must_fail git rev-parse --verify :d/f && + test -h d && + test_path_is_missing d +' + +test_expect_success 'rm of file when it has become a directory' ' + rm -rf d && + >d && + git add d && + rm -f d && + mkdir d && + >d/f && + test_must_fail git rm d && + git rev-parse --verify :d && + test_path_is_file d/f +' + +test_expect_success SYMLINKS 'rm across a symlinked leading path (no index)' ' + rm -rf d e && + mkdir e && + echo content >e/f && + ln -s e d && + git add -A e d && + git commit -m "symlink d to e, e/f exists" && + test_must_fail git rm d/f && + git rev-parse --verify :d && + git rev-parse --verify :e/f && + test -h d && + test_path_is_file e/f +' + +test_expect_failure SYMLINKS 'rm across a symlinked leading path (w/ index)' ' + rm -rf d e && + mkdir d && + echo content >d/f && + git add -A e d && + git commit -m "d/f exists" && + mv d e && + ln -s e d && + test_must_fail git rm d/f && + git rev-parse --verify :d/f && + test -h d && + test_path_is_file e/f +' + test_done 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..838e71dafe 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -1,16 +1,28 @@ #!/bin/sh -test_description='fetching and pushing, with or without wildcard' +test_description='Basic fetch/push functionality. + +This test checks the following functionality: + +* command-line syntax +* refspecs +* fast-forward detection, and overriding it +* configuration +* hooks +* --porcelain output format +* hiderefs +' . ./test-lib.sh D=`pwd` mk_empty () { - rm -fr testrepo && - mkdir testrepo && + repo_name="$1" + rm -fr "$repo_name" && + mkdir "$repo_name" && ( - cd testrepo && + cd "$repo_name" && git init && git config receive.denyCurrentBranch warn && mv .git/hooks .git/hooks-disabled @@ -18,14 +30,17 @@ mk_empty () { } mk_test () { - mk_empty && + repo_name="$1" + shift + + mk_empty "$repo_name" && ( for ref in "$@" do - git push testrepo $the_first_commit:refs/$ref || + git push "$repo_name" $the_first_commit:refs/$ref || exit done && - cd testrepo && + cd "$repo_name" && for ref in "$@" do echo "$the_first_commit" >expect && @@ -38,9 +53,10 @@ mk_test () { } mk_test_with_hooks() { + repo_name=$1 mk_test "$@" && ( - cd testrepo && + cd "$repo_name" && mkdir .git/hooks && cd .git/hooks && @@ -72,13 +88,16 @@ mk_test_with_hooks() { } mk_child() { - rm -rf "$1" && - git clone testrepo "$1" + rm -rf "$2" && + git clone "$1" "$2" } check_push_result () { + repo_name="$1" + shift + ( - cd testrepo && + cd "$repo_name" && echo "$1" >expect && shift && for ref in "$@" @@ -108,7 +127,7 @@ test_expect_success setup ' ' test_expect_success 'fetch without wildcard' ' - mk_empty && + mk_empty testrepo && ( cd testrepo && git fetch .. refs/heads/master:refs/remotes/origin/master && @@ -120,7 +139,7 @@ test_expect_success 'fetch without wildcard' ' ' test_expect_success 'fetch with wildcard' ' - mk_empty && + mk_empty testrepo && ( cd testrepo && git config remote.up.url .. && @@ -134,7 +153,7 @@ test_expect_success 'fetch with wildcard' ' ' test_expect_success 'fetch with insteadOf' ' - mk_empty && + mk_empty testrepo && ( TRASH=$(pwd)/ && cd testrepo && @@ -150,7 +169,7 @@ test_expect_success 'fetch with insteadOf' ' ' test_expect_success 'fetch with pushInsteadOf (should not rewrite)' ' - mk_empty && + mk_empty testrepo && ( TRASH=$(pwd)/ && cd testrepo && @@ -166,7 +185,7 @@ test_expect_success 'fetch with pushInsteadOf (should not rewrite)' ' ' test_expect_success 'push without wildcard' ' - mk_empty && + mk_empty testrepo && git push testrepo refs/heads/master:refs/remotes/origin/master && ( @@ -178,7 +197,7 @@ test_expect_success 'push without wildcard' ' ' test_expect_success 'push with wildcard' ' - mk_empty && + mk_empty testrepo && git push testrepo "refs/heads/*:refs/remotes/origin/*" && ( @@ -190,7 +209,7 @@ test_expect_success 'push with wildcard' ' ' test_expect_success 'push with insteadOf' ' - mk_empty && + mk_empty testrepo && TRASH="$(pwd)/" && test_config "url.$TRASH.insteadOf" trash/ && git push trash/testrepo refs/heads/master:refs/remotes/origin/master && @@ -203,7 +222,7 @@ test_expect_success 'push with insteadOf' ' ' test_expect_success 'push with pushInsteadOf' ' - mk_empty && + mk_empty testrepo && TRASH="$(pwd)/" && test_config "url.$TRASH.pushInsteadOf" trash/ && git push trash/testrepo refs/heads/master:refs/remotes/origin/master && @@ -216,11 +235,11 @@ 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/ && + mk_empty testrepo && + 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 && @@ -232,237 +251,237 @@ test_expect_success 'push with pushInsteadOf and explicit pushurl (pushInsteadOf test_expect_success 'push with matching heads' ' - mk_test heads/master && + mk_test testrepo heads/master && git push testrepo && - check_push_result $the_commit heads/master + check_push_result testrepo $the_commit heads/master ' test_expect_success 'push with matching heads on the command line' ' - mk_test heads/master && + mk_test testrepo heads/master && git push testrepo : && - check_push_result $the_commit heads/master + check_push_result testrepo $the_commit heads/master ' test_expect_success 'failed (non-fast-forward) push with matching heads' ' - mk_test heads/master && + mk_test testrepo heads/master && git push testrepo : && git commit --amend -massaged && test_must_fail git push testrepo && - check_push_result $the_commit heads/master && + check_push_result testrepo $the_commit heads/master && git reset --hard $the_commit ' test_expect_success 'push --force with matching heads' ' - mk_test heads/master && + mk_test testrepo heads/master && git push testrepo : && git commit --amend -massaged && git push --force testrepo && - ! check_push_result $the_commit heads/master && + ! check_push_result testrepo $the_commit heads/master && git reset --hard $the_commit ' test_expect_success 'push with matching heads and forced update' ' - mk_test heads/master && + mk_test testrepo heads/master && git push testrepo : && git commit --amend -massaged && git push testrepo +: && - ! check_push_result $the_commit heads/master && + ! check_push_result testrepo $the_commit heads/master && git reset --hard $the_commit ' test_expect_success 'push with no ambiguity (1)' ' - mk_test heads/master && + mk_test testrepo heads/master && git push testrepo master:master && - check_push_result $the_commit heads/master + check_push_result testrepo $the_commit heads/master ' test_expect_success 'push with no ambiguity (2)' ' - mk_test remotes/origin/master && + mk_test testrepo remotes/origin/master && git push testrepo master:origin/master && - check_push_result $the_commit remotes/origin/master + check_push_result testrepo $the_commit remotes/origin/master ' test_expect_success 'push with colon-less refspec, no ambiguity' ' - mk_test heads/master heads/t/master && + mk_test testrepo heads/master heads/t/master && git branch -f t/master master && git push testrepo master && - check_push_result $the_commit heads/master && - check_push_result $the_first_commit heads/t/master + check_push_result testrepo $the_commit heads/master && + check_push_result testrepo $the_first_commit heads/t/master ' test_expect_success 'push with weak ambiguity (1)' ' - mk_test heads/master remotes/origin/master && + mk_test testrepo heads/master remotes/origin/master && git push testrepo master:master && - check_push_result $the_commit heads/master && - check_push_result $the_first_commit remotes/origin/master + check_push_result testrepo $the_commit heads/master && + check_push_result testrepo $the_first_commit remotes/origin/master ' test_expect_success 'push with weak ambiguity (2)' ' - mk_test heads/master remotes/origin/master remotes/another/master && + mk_test testrepo heads/master remotes/origin/master remotes/another/master && git push testrepo master:master && - check_push_result $the_commit heads/master && - check_push_result $the_first_commit remotes/origin/master remotes/another/master + check_push_result testrepo $the_commit heads/master && + check_push_result testrepo $the_first_commit remotes/origin/master remotes/another/master ' test_expect_success 'push with ambiguity' ' - mk_test heads/frotz tags/frotz && + mk_test testrepo heads/frotz tags/frotz && test_must_fail git push testrepo master:frotz && - check_push_result $the_first_commit heads/frotz tags/frotz + check_push_result testrepo $the_first_commit heads/frotz tags/frotz ' test_expect_success 'push with colon-less refspec (1)' ' - mk_test heads/frotz tags/frotz && + mk_test testrepo heads/frotz tags/frotz && git branch -f frotz master && git push testrepo frotz && - check_push_result $the_commit heads/frotz && - check_push_result $the_first_commit tags/frotz + check_push_result testrepo $the_commit heads/frotz && + check_push_result testrepo $the_first_commit tags/frotz ' test_expect_success 'push with colon-less refspec (2)' ' - mk_test heads/frotz tags/frotz && + mk_test testrepo heads/frotz tags/frotz && if git show-ref --verify -q refs/heads/frotz then git branch -D frotz fi && git tag -f frotz && git push -f testrepo frotz && - check_push_result $the_commit tags/frotz && - check_push_result $the_first_commit heads/frotz + check_push_result testrepo $the_commit tags/frotz && + check_push_result testrepo $the_first_commit heads/frotz ' test_expect_success 'push with colon-less refspec (3)' ' - mk_test && + mk_test testrepo && if git show-ref --verify -q refs/tags/frotz then git tag -d frotz fi && git branch -f frotz master && git push testrepo frotz && - check_push_result $the_commit heads/frotz && + check_push_result testrepo $the_commit heads/frotz && test 1 = $( cd testrepo && git show-ref | wc -l ) ' test_expect_success 'push with colon-less refspec (4)' ' - mk_test && + mk_test testrepo && if git show-ref --verify -q refs/heads/frotz then git branch -D frotz fi && git tag -f frotz && git push testrepo frotz && - check_push_result $the_commit tags/frotz && + check_push_result testrepo $the_commit tags/frotz && test 1 = $( cd testrepo && git show-ref | wc -l ) ' test_expect_success 'push head with non-existent, incomplete dest' ' - mk_test && + mk_test testrepo && git push testrepo master:branch && - check_push_result $the_commit heads/branch + check_push_result testrepo $the_commit heads/branch ' test_expect_success 'push tag with non-existent, incomplete dest' ' - mk_test && + mk_test testrepo && git tag -f v1.0 && git push testrepo v1.0:tag && - check_push_result $the_commit tags/tag + check_push_result testrepo $the_commit tags/tag ' test_expect_success 'push sha1 with non-existent, incomplete dest' ' - mk_test && + mk_test testrepo && test_must_fail git push testrepo `git rev-parse master`:foo ' test_expect_success 'push ref expression with non-existent, incomplete dest' ' - mk_test && + mk_test testrepo && test_must_fail git push testrepo master^:branch ' test_expect_success 'push with HEAD' ' - mk_test heads/master && + mk_test testrepo heads/master && git checkout master && git push testrepo HEAD && - check_push_result $the_commit heads/master + check_push_result testrepo $the_commit heads/master ' test_expect_success 'push with HEAD nonexisting at remote' ' - mk_test heads/master && + mk_test testrepo heads/master && git checkout -b local master && git push testrepo HEAD && - check_push_result $the_commit heads/local + check_push_result testrepo $the_commit heads/local ' test_expect_success 'push with +HEAD' ' - mk_test heads/master && + mk_test testrepo heads/master && git checkout master && git branch -D local && git checkout -b local && git push testrepo master local && - check_push_result $the_commit heads/master && - check_push_result $the_commit heads/local && + check_push_result testrepo $the_commit heads/master && + check_push_result testrepo $the_commit heads/local && # Without force rewinding should fail git reset --hard HEAD^ && test_must_fail git push testrepo HEAD && - check_push_result $the_commit heads/local && + check_push_result testrepo $the_commit heads/local && # With force rewinding should succeed git push testrepo +HEAD && - check_push_result $the_first_commit heads/local + check_push_result testrepo $the_first_commit heads/local ' test_expect_success 'push HEAD with non-existent, incomplete dest' ' - mk_test && + mk_test testrepo && git checkout master && git push testrepo HEAD:branch && - check_push_result $the_commit heads/branch + check_push_result testrepo $the_commit heads/branch ' test_expect_success 'push with config remote.*.push = HEAD' ' - mk_test heads/local && + mk_test testrepo heads/local && git checkout master && git branch -f local $the_commit && ( @@ -474,35 +493,62 @@ test_expect_success 'push with config remote.*.push = HEAD' ' test_config remote.there.push HEAD && test_config branch.master.remote there && git push && - check_push_result $the_commit heads/master && - check_push_result $the_first_commit heads/local + check_push_result testrepo $the_commit heads/master && + check_push_result testrepo $the_first_commit heads/local +' + +test_expect_success 'push with remote.pushdefault' ' + mk_test up_repo heads/master && + mk_test down_repo heads/master && + test_config remote.up.url up_repo && + test_config remote.down.url down_repo && + test_config branch.master.remote up && + test_config remote.pushdefault down && + git push && + check_push_result up_repo $the_first_commit heads/master && + check_push_result down_repo $the_commit heads/master ' test_expect_success 'push with config remote.*.pushurl' ' - mk_test heads/master && + mk_test testrepo heads/master && git checkout master && test_config remote.there.url test2repo && test_config remote.there.pushurl testrepo && git push there && - check_push_result $the_commit heads/master + check_push_result testrepo $the_commit heads/master +' + +test_expect_success 'push with config branch.*.pushremote' ' + mk_test up_repo heads/master && + mk_test side_repo heads/master && + mk_test down_repo heads/master && + test_config remote.up.url up_repo && + test_config remote.pushdefault side_repo && + test_config remote.down.url down_repo && + test_config branch.master.remote up && + test_config branch.master.pushremote down && + git push && + check_push_result up_repo $the_first_commit heads/master && + check_push_result side_repo $the_first_commit heads/master && + check_push_result down_repo $the_commit heads/master ' test_expect_success 'push with dry-run' ' - mk_test heads/master && + mk_test testrepo heads/master && ( cd testrepo && old_commit=$(git show-ref -s --verify refs/heads/master) ) && git push --dry-run testrepo && - check_push_result $old_commit heads/master + check_push_result testrepo $old_commit heads/master ' test_expect_success 'push updates local refs' ' - mk_test heads/master && - mk_child child && + mk_test testrepo heads/master && + mk_child testrepo child && ( cd child && git pull .. master && @@ -515,9 +561,9 @@ test_expect_success 'push updates local refs' ' test_expect_success 'push updates up-to-date local refs' ' - mk_test heads/master && - mk_child child1 && - mk_child child2 && + mk_test testrepo heads/master && + mk_child testrepo child1 && + mk_child testrepo child2 && (cd child1 && git pull .. master && git push) && ( cd child2 && @@ -531,8 +577,8 @@ test_expect_success 'push updates up-to-date local refs' ' test_expect_success 'push preserves up-to-date packed refs' ' - mk_test heads/master && - mk_child child && + mk_test testrepo heads/master && + mk_child testrepo child && ( cd child && git push && @@ -543,8 +589,8 @@ test_expect_success 'push preserves up-to-date packed refs' ' test_expect_success 'push does not update local refs on failure' ' - mk_test heads/master && - mk_child child && + mk_test testrepo heads/master && + mk_child testrepo child && mkdir testrepo/.git/hooks && echo "#!/no/frobnication/today" >testrepo/.git/hooks/pre-receive && chmod +x testrepo/.git/hooks/pre-receive && @@ -560,7 +606,7 @@ test_expect_success 'push does not update local refs on failure' ' test_expect_success 'allow deleting an invalid remote ref' ' - mk_test heads/master && + mk_test testrepo heads/master && rm -f testrepo/.git/objects/??/* && git push testrepo :refs/heads/master && (cd testrepo && test_must_fail git rev-parse --verify refs/heads/master) @@ -568,7 +614,7 @@ test_expect_success 'allow deleting an invalid remote ref' ' ' test_expect_success 'pushing valid refs triggers post-receive and post-update hooks' ' - mk_test_with_hooks heads/master heads/next && + mk_test_with_hooks testrepo heads/master heads/next && orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) && newmaster=$(git show-ref -s --verify refs/heads/master) && orgnext=$(cd testrepo && git show-ref -s --verify refs/heads/next) && @@ -604,7 +650,7 @@ test_expect_success 'pushing valid refs triggers post-receive and post-update ho ' test_expect_success 'deleting dangling ref triggers hooks with correct args' ' - mk_test_with_hooks heads/master && + mk_test_with_hooks testrepo heads/master && rm -f testrepo/.git/objects/??/* && git push testrepo :refs/heads/master && ( @@ -633,7 +679,7 @@ test_expect_success 'deleting dangling ref triggers hooks with correct args' ' ' test_expect_success 'deletion of a non-existent ref is not fed to post-receive and post-update hooks' ' - mk_test_with_hooks heads/master && + mk_test_with_hooks testrepo heads/master && orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) && newmaster=$(git show-ref -s --verify refs/heads/master) && git push testrepo master :refs/heads/nonexistent && @@ -665,7 +711,7 @@ test_expect_success 'deletion of a non-existent ref is not fed to post-receive a ' test_expect_success 'deletion of a non-existent ref alone does trigger post-receive and post-update hooks' ' - mk_test_with_hooks heads/master && + mk_test_with_hooks testrepo heads/master && git push testrepo :refs/heads/nonexistent && ( cd testrepo/.git && @@ -685,7 +731,7 @@ test_expect_success 'deletion of a non-existent ref alone does trigger post-rece ' test_expect_success 'mixed ref updates, deletes, invalid deletes trigger hooks with correct input' ' - mk_test_with_hooks heads/master heads/next heads/pu && + mk_test_with_hooks testrepo heads/master heads/next heads/pu && orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) && newmaster=$(git show-ref -s --verify refs/heads/master) && orgnext=$(cd testrepo && git show-ref -s --verify refs/heads/next) && @@ -731,14 +777,14 @@ test_expect_success 'mixed ref updates, deletes, invalid deletes trigger hooks w ' test_expect_success 'allow deleting a ref using --delete' ' - mk_test heads/master && + mk_test testrepo heads/master && (cd testrepo && git config receive.denyDeleteCurrent warn) && git push testrepo --delete master && (cd testrepo && test_must_fail git rev-parse --verify refs/heads/master) ' test_expect_success 'allow deleting a tag using --delete' ' - mk_test heads/master && + mk_test testrepo heads/master && git tag -a -m dummy_message deltag heads/master && git push testrepo --tags && (cd testrepo && git rev-parse --verify -q refs/tags/deltag) && @@ -747,17 +793,17 @@ test_expect_success 'allow deleting a tag using --delete' ' ' test_expect_success 'push --delete without args aborts' ' - mk_test heads/master && + mk_test testrepo heads/master && test_must_fail git push testrepo --delete ' test_expect_success 'push --delete refuses src:dest refspecs' ' - mk_test heads/master && + mk_test testrepo heads/master && test_must_fail git push testrepo --delete master:foo ' test_expect_success 'warn on push to HEAD of non-bare repository' ' - mk_test heads/master && + mk_test testrepo heads/master && ( cd testrepo && git checkout master && @@ -768,7 +814,7 @@ test_expect_success 'warn on push to HEAD of non-bare repository' ' ' test_expect_success 'deny push to HEAD of non-bare repository' ' - mk_test heads/master && + mk_test testrepo heads/master && ( cd testrepo && git checkout master && @@ -778,7 +824,7 @@ test_expect_success 'deny push to HEAD of non-bare repository' ' ' test_expect_success 'allow push to HEAD of bare repository (bare)' ' - mk_test heads/master && + mk_test testrepo heads/master && ( cd testrepo && git checkout master && @@ -790,7 +836,7 @@ test_expect_success 'allow push to HEAD of bare repository (bare)' ' ' test_expect_success 'allow push to HEAD of non-bare repository (config)' ' - mk_test heads/master && + mk_test testrepo heads/master && ( cd testrepo && git checkout master && @@ -801,7 +847,7 @@ test_expect_success 'allow push to HEAD of non-bare repository (config)' ' ' test_expect_success 'fetch with branches' ' - mk_empty && + mk_empty testrepo && git branch second $the_first_commit && git checkout second && echo ".." > testrepo/.git/branches/branch1 && @@ -816,7 +862,7 @@ test_expect_success 'fetch with branches' ' ' test_expect_success 'fetch with branches containing #' ' - mk_empty && + mk_empty testrepo && echo "..#second" > testrepo/.git/branches/branch2 && ( cd testrepo && @@ -829,7 +875,7 @@ test_expect_success 'fetch with branches containing #' ' ' test_expect_success 'push with branches' ' - mk_empty && + mk_empty testrepo && git checkout second && echo "testrepo" > .git/branches/branch1 && git push branch1 && @@ -842,7 +888,7 @@ test_expect_success 'push with branches' ' ' test_expect_success 'push with branches containing #' ' - mk_empty && + mk_empty testrepo && echo "testrepo#branch3" > .git/branches/branch2 && git push branch2 && ( @@ -855,9 +901,9 @@ test_expect_success 'push with branches containing #' ' ' test_expect_success 'push into aliased refs (consistent)' ' - mk_test heads/master && - mk_child child1 && - mk_child child2 && + mk_test testrepo heads/master && + mk_child testrepo child1 && + mk_child testrepo child2 && ( cd child1 && git branch foo && @@ -877,9 +923,9 @@ test_expect_success 'push into aliased refs (consistent)' ' ' test_expect_success 'push into aliased refs (inconsistent)' ' - mk_test heads/master && - mk_child child1 && - mk_child child2 && + mk_test testrepo heads/master && + mk_child testrepo child1 && + mk_child testrepo child2 && ( cd child1 && git branch foo && @@ -904,9 +950,9 @@ test_expect_success 'push into aliased refs (inconsistent)' ' ' test_expect_success 'push requires --force to update lightweight tag' ' - mk_test heads/master && - mk_child child1 && - mk_child child2 && + mk_test testrepo heads/master && + mk_child testrepo child1 && + mk_child testrepo child2 && ( cd child1 && git tag Tag && @@ -925,7 +971,7 @@ test_expect_success 'push requires --force to update lightweight tag' ' ' test_expect_success 'push --porcelain' ' - mk_empty && + mk_empty testrepo && echo >.git/foo "To testrepo" && echo >>.git/foo "* refs/heads/master:refs/remotes/origin/master [new branch]" && echo >>.git/foo "Done" && @@ -940,13 +986,13 @@ test_expect_success 'push --porcelain' ' ' test_expect_success 'push --porcelain bad url' ' - mk_empty && + mk_empty testrepo && test_must_fail git push >.git/bar --porcelain asdfasdfasd refs/heads/master:refs/remotes/origin/master && test_must_fail grep -q Done .git/bar ' test_expect_success 'push --porcelain rejected' ' - mk_empty && + mk_empty testrepo && git push testrepo refs/heads/master:refs/remotes/origin/master && (cd testrepo && git reset --hard origin/master^ @@ -960,7 +1006,7 @@ test_expect_success 'push --porcelain rejected' ' ' test_expect_success 'push --porcelain --dry-run rejected' ' - mk_empty && + mk_empty testrepo && git push testrepo refs/heads/master:refs/remotes/origin/master && (cd testrepo && git reset --hard origin/master @@ -975,25 +1021,25 @@ test_expect_success 'push --porcelain --dry-run rejected' ' ' test_expect_success 'push --prune' ' - mk_test heads/master heads/second heads/foo heads/bar && + mk_test testrepo heads/master heads/second heads/foo heads/bar && git push --prune testrepo && - check_push_result $the_commit heads/master && - check_push_result $the_first_commit heads/second && - ! check_push_result $the_first_commit heads/foo heads/bar + check_push_result testrepo $the_commit heads/master && + check_push_result testrepo $the_first_commit heads/second && + ! check_push_result testrepo $the_first_commit heads/foo heads/bar ' test_expect_success 'push --prune refspec' ' - mk_test tmp/master tmp/second tmp/foo tmp/bar && + mk_test testrepo tmp/master tmp/second tmp/foo tmp/bar && git push --prune testrepo "refs/heads/*:refs/tmp/*" && - check_push_result $the_commit tmp/master && - check_push_result $the_first_commit tmp/second && - ! check_push_result $the_first_commit tmp/foo tmp/bar + check_push_result testrepo $the_commit tmp/master && + check_push_result testrepo $the_first_commit tmp/second && + ! check_push_result testrepo $the_first_commit tmp/foo tmp/bar ' for configsection in transfer receive do test_expect_success "push to update a ref hidden by $configsection.hiderefs" ' - mk_test heads/master hidden/one hidden/two hidden/three && + mk_test testrepo heads/master hidden/one hidden/two hidden/three && ( cd testrepo && git config $configsection.hiderefs refs/hidden @@ -1001,32 +1047,32 @@ do # push to unhidden ref succeeds normally git push testrepo master:refs/heads/master && - check_push_result $the_commit heads/master && + check_push_result testrepo $the_commit heads/master && # push to update a hidden ref should fail test_must_fail git push testrepo master:refs/hidden/one && - check_push_result $the_first_commit hidden/one && + check_push_result testrepo $the_first_commit hidden/one && # push to delete a hidden ref should fail test_must_fail git push testrepo :refs/hidden/two && - check_push_result $the_first_commit hidden/two && + check_push_result testrepo $the_first_commit hidden/two && # idempotent push to update a hidden ref should fail test_must_fail git push testrepo $the_first_commit:refs/hidden/three && - check_push_result $the_first_commit hidden/three + check_push_result testrepo $the_first_commit hidden/three ' done test_expect_success 'fetch exact SHA1' ' - mk_test heads/master hidden/one && + mk_test testrepo heads/master hidden/one && git push testrepo master:refs/hidden/one && ( cd testrepo && git config transfer.hiderefs refs/hidden ) && - check_push_result $the_commit hidden/one && + check_push_result testrepo $the_commit hidden/one && - mk_child child && + mk_child testrepo child && ( cd child && @@ -1052,7 +1098,7 @@ test_expect_success 'fetch exact SHA1' ' ' test_expect_success 'fetch follows tags by default' ' - mk_test heads/master && + mk_test testrepo heads/master && rm -fr src dst && git init src && ( @@ -1079,7 +1125,7 @@ test_expect_success 'fetch follows tags by default' ' ' test_expect_success 'push does not follow tags by default' ' - mk_test heads/master && + mk_test testrepo heads/master && rm -fr src dst && git init src && git init --bare dst && @@ -1102,7 +1148,7 @@ test_expect_success 'push does not follow tags by default' ' ' test_expect_success 'push --follow-tag only pushes relevant tags' ' - mk_test heads/master && + mk_test testrepo heads/master && rm -fr src dst && git init src && git init --bare dst && 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/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh index 8e2ff13423..dd6dc844e7 100755 --- a/t/t6012-rev-list-simplify.sh +++ b/t/t6012-rev-list-simplify.sh @@ -77,14 +77,16 @@ test_expect_success setup ' FMT='tformat:%P %H | %s' -check_result () { +check_outcome () { + outcome=$1 + shift for c in $1 do echo "$c" done >expect && shift && param="$*" && - test_expect_success "log $param" ' + test_expect_$outcome "log $param" ' git log --pretty="$FMT" --parents $param | unnote >actual && sed -e "s/^.* \([^ ]*\) .*/\1/" >check <actual && @@ -95,11 +97,15 @@ check_result () { ' } +check_result () { + check_outcome success "$@" +} + check_result 'L K J I H G F E D C B A' --full-history check_result 'K I H E C B A' --full-history -- file check_result 'K I H E C B A' --full-history --topo-order -- file check_result 'K I H E C B A' --full-history --date-order -- file -check_result 'I E C B A' --simplify-merges -- file +check_outcome failure 'I E C B A' --simplify-merges -- file check_result 'I B A' -- file check_result 'I B A' --topo-order -- file check_result 'H' --first-parent -- another-file 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/t7512-status-help.sh b/t/t7512-status-help.sh index 06749a6aa0..bf08d4e098 100755 --- a/t/t7512-status-help.sh +++ b/t/t7512-status-help.sh @@ -678,4 +678,62 @@ test_expect_success 'status showing detached from a tag' ' test_i18ncmp expected actual ' +test_expect_success 'status while reverting commit (conflicts)' ' + git checkout master && + echo before >to-revert.txt && + test_commit before to-revert.txt && + echo old >to-revert.txt && + test_commit old to-revert.txt && + echo new >to-revert.txt && + test_commit new to-revert.txt && + TO_REVERT=$(git rev-parse --short HEAD^) && + test_must_fail git revert $TO_REVERT && + cat >expected <<-EOF + # On branch master + # You are currently reverting commit $TO_REVERT. + # (fix conflicts and run "git revert --continue") + # (use "git revert --abort" to cancel the revert operation) + # + # Unmerged paths: + # (use "git reset HEAD <file>..." to unstage) + # (use "git add <file>..." to mark resolution) + # + # both modified: to-revert.txt + # + no changes added to commit (use "git add" and/or "git commit -a") + EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + +test_expect_success 'status while reverting commit (conflicts resolved)' ' + echo reverted >to-revert.txt && + git add to-revert.txt && + cat >expected <<-EOF + # On branch master + # You are currently reverting commit $TO_REVERT. + # (all conflicts fixed: run "git revert --continue") + # (use "git revert --abort" to cancel the revert operation) + # + # Changes to be committed: + # (use "git reset HEAD <file>..." to unstage) + # + # modified: to-revert.txt + # + # Untracked files not listed (use -u option to show untracked files) + EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + +test_expect_success 'status after reverting commit' ' + git revert --continue && + cat >expected <<-\EOF + # On branch master + nothing to commit (use -u to show untracked files) + EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + test_done 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 \ diff --git a/wt-status.c b/wt-status.c index cea8e55d8b..09416db348 100644 --- a/wt-status.c +++ b/wt-status.c @@ -965,6 +965,25 @@ static void show_cherry_pick_in_progress(struct wt_status *s, wt_status_print_trailer(s); } +static void show_revert_in_progress(struct wt_status *s, + struct wt_status_state *state, + const char *color) +{ + status_printf_ln(s, color, _("You are currently reverting commit %s."), + find_unique_abbrev(state->revert_head_sha1, DEFAULT_ABBREV)); + if (advice_status_hints) { + if (has_unmerged(s)) + status_printf_ln(s, color, + _(" (fix conflicts and run \"git revert --continue\")")); + else + status_printf_ln(s, color, + _(" (all conflicts fixed: run \"git revert --continue\")")); + status_printf_ln(s, color, + _(" (use \"git revert --abort\" to cancel the revert operation)")); + } + wt_status_print_trailer(s); +} + static void show_bisect_in_progress(struct wt_status *s, struct wt_status_state *state, const char *color) @@ -1086,6 +1105,7 @@ void wt_status_get_state(struct wt_status_state *state, int get_detached_from) { struct stat st; + unsigned char sha1[20]; if (!stat(git_path("MERGE_HEAD"), &st)) { state->merge_in_progress = 1; @@ -1113,6 +1133,11 @@ void wt_status_get_state(struct wt_status_state *state, state->bisect_in_progress = 1; state->branch = read_and_strip_branch("BISECT_START"); } + if (!stat(git_path("REVERT_HEAD"), &st) && + !get_sha1("REVERT_HEAD", sha1)) { + state->revert_in_progress = 1; + hashcpy(state->revert_head_sha1, sha1); + } if (get_detached_from) wt_status_get_detached_from(state); @@ -1130,6 +1155,8 @@ static void wt_status_print_state(struct wt_status *s, show_rebase_in_progress(s, state, state_color); else if (state->cherry_pick_in_progress) show_cherry_pick_in_progress(s, state, state_color); + else if (state->revert_in_progress) + show_revert_in_progress(s, state, state_color); if (state->bisect_in_progress) show_bisect_in_progress(s, state, state_color); } diff --git a/wt-status.h b/wt-status.h index be7a016173..4121bc208d 100644 --- a/wt-status.h +++ b/wt-status.h @@ -80,10 +80,12 @@ struct wt_status_state { int rebase_interactive_in_progress; int cherry_pick_in_progress; int bisect_in_progress; + int revert_in_progress; char *branch; char *onto; char *detached_from; unsigned char detached_sha1[20]; + unsigned char revert_head_sha1[20]; }; void wt_status_prepare(struct wt_status *s); |