diff options
56 files changed, 2045 insertions, 352 deletions
diff --git a/.travis.yml b/.travis.yml index 591cc57b80..c757a111ce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,6 +39,17 @@ env: matrix: include: + - env: Windows + os: linux + compiler: + addons: + before_install: + before_script: + script: + - > + test "$TRAVIS_REPO_SLUG" != "git/git" || + ci/run-windows-build.sh $TRAVIS_BRANCH $(git rev-parse HEAD) + after_failure: - env: Linux32 os: linux services: diff --git a/Documentation/RelNotes/2.13.0.txt b/Documentation/RelNotes/2.13.0.txt index 993e49341e..3c92403c6b 100644 --- a/Documentation/RelNotes/2.13.0.txt +++ b/Documentation/RelNotes/2.13.0.txt @@ -12,13 +12,19 @@ Backward compatibility notes. release (yet). * The historical argument order "git merge <msg> HEAD <commit>..." - has been deprecated for quite some time, and will be removed in a - future release. + has been deprecated for quite some time, and is now removed. * The default location "~/.git-credential-cache/socket" for the socket used to communicate with the credential-cache daemon has been moved to "~/.cache/git/credential/socket". + * Git now avoids blindly falling back to ".git" when the setup + sequence said we are _not_ in Git repository. A corner case that + happens to work right now may be broken by a call to die("BUG"). + We've tried hard to locate such cases and fixed them, but there + might still be cases that need to be addressed--bug reports are + greatly appreciated. + Updates since v2.12 ------------------- @@ -145,6 +151,31 @@ UI, Workflows & Features doing other things, output from reset seeped out). These, and other chattyness has been fixed. + * "git merge <message> HEAD <commit>" syntax that has been deprecated + since October 2007 has been removed. + + * The refs completion for large number of refs has been sped up, + partly by giving up disambiguating ambiguous refs and partly by + eliminating most of the shell processing between 'git for-each-ref' + and 'ls-remote' and Bash's completion facility. + + * On many keyboards, typing "@{" involves holding down SHIFT key and + one can easily end up with "@{Up..." when typing "@{upstream}". As + the upstream/push keywords do not appear anywhere else in the syntax, + we can safely accept them case insensitively without introducing + ambiguity or confusion to solve this. + + * "git tag/branch/for-each-ref" family of commands long allowed to + filter the refs by "--contains X" (show only the refs that are + descendants of X), "--merged X" (show only the refs that are + ancestors of X), "--no-merged X" (show only the refs that are not + ancestors of X). One curious omission, "--no-contains X" (show + only the refs that are not descendants of X) has been added to + them. + + * The default behaviour of "git log" in an interactive session has + been changed to enable "--decorate". + Performance, Internal Implementation, Development Support etc. @@ -228,6 +259,18 @@ Performance, Internal Implementation, Development Support etc. * An earlier version of sha1dc/sha1.c that was merged to 'master' compiled incorrectly on Windows, which has been fixed. + * "what URL do we want to update this submodule?" and "are we + interested in this submodule?" are split into two distinct + concepts, and then the way used to express the latter got extended, + paving a way to make it easier to manage a project with many + submodules and make it possible to later extend use of multiple + worktrees for a project with submodules. + + * Some debugging output from "git describe" were marked for l10n, + but some weren't. Mark missing ones for l10n. + + * Define a new task in .travis.yml that triggers a test session on + Windows run elsewhere. Also contains various documentation updates and code clean-ups. @@ -377,8 +420,41 @@ notes for details). * Fix for NO_PTHREADS option. (merge 2225e1ea20 bw/grep-recurse-submodules later to maint). + * Git now avoids blindly falling back to ".git" when the setup + sequence said we are _not_ in Git repository. A corner case that + happens to work right now may be broken by a call to die("BUG"). + (merge b1ef400eec jk/no-looking-at-dotgit-outside-repo-final later to maint). + + * A few commands that recently learned the "--recurse-submodule" + option misbehaved when started from a subdirectory of the + superproject. + (merge b2dfeb7c00 bw/recurse-submodules-relative-fix later to maint). + + * FreeBSD implementation of getcwd(3) behaved differently when an + intermediate directory is unreadable/unsearchable depending on the + length of the buffer provided, which our strbuf_getcwd() was not + aware of. strbuf_getcwd() has been taught to cope with it better. + (merge a54e938e5b rs/freebsd-getcwd-workaround later to maint). + + * A recent update to "rebase -i" stopped running hooks for the "git + commit" command during "reword" action, which has been fixed. + + * Removing an entry from a notes tree and then looking another note + entry from the resulting tree using the internal notes API + functions did not work as expected. No in-tree users of the API + has such access pattern, but it still is worth fixing. + + * "git receive-pack" could have been forced to die by attempting + allocate an unreasonably large amount of memory with a crafted push + certificate; this has been fixed. + (merge f2214dede9 bc/push-cert-receive-fix later to maint). + * Other minor doc, test and build updates and code cleanups. (merge df2a6e38b7 jk/pager-in-use later to maint). (merge 75ec4a6cb0 ab/branch-list-doc later to maint). (merge 3e5b36c637 sg/skip-prefix-in-prettify-refname later to maint). (merge 2c5e2865cc jk/fast-import-cleanup later to maint). + (merge 4473060bc2 ab/test-readme-updates later to maint). + (merge 48a96972fd ab/doc-submitting later to maint). + (merge f5c2bc2b96 jk/make-coccicheck-detect-errors later to maint). + (merge c105f563d1 cc/untracked later to maint). diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 3faf7eb884..bc8ad00473 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -98,12 +98,17 @@ should skip the full stop. It is also conventional in most cases to prefix the first line with "area: " where the area is a filename or identifier for the general area of the code being modified, e.g. - . archive: ustar header checksum is computed unsigned - . git-cherry-pick.txt: clarify the use of revision range notation + . doc: clarify distinction between sign-off and pgp-signing + . githooks.txt: improve the intro section If in doubt which identifier to use, run "git log --no-merges" on the files you are modifying to see the current conventions. +It's customary to start the remainder of the first line after "area: " +with a lower-case letter. E.g. "doc: clarify...", not "doc: +Clarify...", or "githooks.txt: improve...", not "githooks.txt: +Improve...". + The body should provide a meaningful commit message, which: . explains the problem the change tries to solve, iow, what is wrong @@ -129,8 +134,9 @@ with the subject enclosed in a pair of double-quotes, like this: noticed that ... The "Copy commit summary" command of gitk can be used to obtain this -format. +format, or this invocation of "git show": + git show -s --date=short --pretty='format:%h ("%s", %ad)' <commit> (3) Generate your patch using Git tools out of your commits. diff --git a/Documentation/config.txt b/Documentation/config.txt index 1df1965457..475e874d51 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -3013,8 +3013,9 @@ submodule.<name>.url:: The URL for a submodule. This variable is copied from the .gitmodules file to the git config via 'git submodule init'. The user can change the configured URL before obtaining the submodule via 'git submodule - update'. After obtaining the submodule, the presence of this variable - is used as a sign whether the submodule is of interest to git commands. + update'. If neither submodule.<name>.active or submodule.active are + set, the presence of this variable is used as a fallback to indicate + whether the submodule is of interest to git commands. See linkgit:git-submodule[1] and linkgit:gitmodules[5] for details. submodule.<name>.update:: @@ -3052,6 +3053,16 @@ submodule.<name>.ignore:: "--ignore-submodules" option. The 'git submodule' commands are not affected by this setting. +submodule.<name>.active:: + Boolean value indicating if the submodule is of interest to git + commands. This config option takes precedence over the + submodule.active config option. + +submodule.active:: + A repeated field which contains a pathspec used to match against a + submodule's path to determine if the submodule is of interest to git + commands. + submodule.fetchJobs:: Specifies how many submodules are fetched/cloned at the same time. A positive integer allows up to that number of submodules fetched diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index c203e8c073..81bd0a7b77 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -10,8 +10,9 @@ SYNOPSIS [verse] 'git branch' [--color[=<when>] | --no-color] [-r | -a] [--list] [-v [--abbrev=<length> | --no-abbrev]] - [--column[=<options>] | --no-column] - [(--merged | --no-merged | --contains) [<commit>]] [--sort=<key>] + [--column[=<options>] | --no-column] [--sort=<key>] + [(--merged | --no-merged) [<commit>]] + [--contains [<commit]] [--no-contains [<commit>]] [--points-at <object>] [--format=<format>] [<pattern>...] 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>] 'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>] @@ -35,11 +36,12 @@ as branch creation. With `--contains`, shows only the branches that contain the named commit (in other words, the branches whose tip commits are descendants of the -named commit). With `--merged`, only branches merged into the named -commit (i.e. the branches whose tip commits are reachable from the named -commit) will be listed. With `--no-merged` only branches not merged into -the named commit will be listed. If the <commit> argument is missing it -defaults to `HEAD` (i.e. the tip of the current branch). +named commit), `--no-contains` inverts it. With `--merged`, only branches +merged into the named commit (i.e. the branches whose tip commits are +reachable from the named commit) will be listed. With `--no-merged` only +branches not merged into the named commit will be listed. If the <commit> +argument is missing it defaults to `HEAD` (i.e. the tip of the current +branch). The command's second form creates a new branch head named <branchname> which points to the current `HEAD`, or <start-point> if given. @@ -218,13 +220,19 @@ start-point is either a local or remote-tracking branch. Only list branches which contain the specified commit (HEAD if not specified). Implies `--list`. +--no-contains [<commit>]:: + Only list branches which don't contain the specified commit + (HEAD if not specified). Implies `--list`. + --merged [<commit>]:: Only list branches whose tips are reachable from the - specified commit (HEAD if not specified). Implies `--list`. + specified commit (HEAD if not specified). Implies `--list`, + incompatible with `--no-merged`. --no-merged [<commit>]:: Only list branches whose tips are not reachable from the - specified commit (HEAD if not specified). Implies `--list`. + specified commit (HEAD if not specified). Implies `--list`, + incompatible with `--merged`. <branchname>:: The name of the branch to create or delete. @@ -301,13 +309,16 @@ If you are creating a branch that you want to checkout immediately, it is easier to use the git checkout command with its `-b` option to create a branch and check it out with a single command. -The options `--contains`, `--merged` and `--no-merged` serve three related -but different purposes: +The options `--contains`, `--no-contains`, `--merged` and `--no-merged` +serve four related but different purposes: - `--contains <commit>` is used to find all branches which will need special attention if <commit> were to be rebased or amended, since those branches contain the specified <commit>. +- `--no-contains <commit>` is the inverse of that, i.e. branches that don't + contain the specified <commit>. + - `--merged` is used to find all branches which can be safely deleted, since those branches are fully contained by HEAD. diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 35cc34b2fb..30052cce49 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -14,7 +14,7 @@ SYNOPSIS [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>] [--dissociate] [--separate-git-dir <git dir>] [--depth <depth>] [--[no-]single-branch] - [--recursive | --recurse-submodules] [--[no-]shallow-submodules] + [--recurse-submodules] [--[no-]shallow-submodules] [--jobs <n>] [--] <repository> [<directory>] DESCRIPTION @@ -215,10 +215,14 @@ objects from the source repository into a pack in the cloned repository. branch when `--single-branch` clone was made, no remote-tracking branch is created. ---recursive:: ---recurse-submodules:: - After the clone is created, initialize all submodules within, - using their default settings. This is equivalent to running +--recurse-submodules[=<pathspec]:: + After the clone is created, initialize and clone submodules + within based on the provided pathspec. If no pathspec is + provided, all submodules are initialized and cloned. + Submodules are initialized and cloned using their default + settings. The resulting clone has `submodule.active` set to + the provided pathspec, or "." (meaning all submodules) if no + pathspec is provided. This is equivalent to running `git submodule update --init --recursive` immediately after the clone is finished. This option is ignored if the cloned repository does not have a worktree/checkout (i.e. if any of diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 111e1be6f5..03e187a105 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -11,7 +11,7 @@ SYNOPSIS 'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl] [(--sort=<key>)...] [--format=<format>] [<pattern>...] [--points-at <object>] [(--merged | --no-merged) [<object>]] - [--contains [<object>]] + [--contains [<object>]] [--no-contains [<object>]] DESCRIPTION ----------- @@ -69,16 +69,22 @@ OPTIONS --merged [<object>]:: Only list refs whose tips are reachable from the - specified commit (HEAD if not specified). + specified commit (HEAD if not specified), + incompatible with `--no-merged`. --no-merged [<object>]:: Only list refs whose tips are not reachable from the - specified commit (HEAD if not specified). + specified commit (HEAD if not specified), + incompatible with `--merged`. --contains [<object>]:: Only list refs which contain the specified commit (HEAD if not specified). +--no-contains [<object>]:: + Only list refs which don't contain the specified commit (HEAD + if not specified). + --ignore-case:: Sorting and filtering refs are case insensitive. diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index ca3c27b88a..04fdd8cf08 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -13,7 +13,6 @@ SYNOPSIS [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]] [--[no-]allow-unrelated-histories] [--[no-]rerere-autoupdate] [-m <msg>] [<commit>...] -'git merge' <msg> HEAD <commit>... 'git merge' --abort 'git merge' --continue @@ -46,11 +45,7 @@ a log message from the user describing the changes. D---E---F---G---H master ------------ -The second syntax (<msg> `HEAD` <commit>...) is supported for -historical reasons. Do not use it from the command line or in -new scripts. It is the same as `git merge -m <msg> <commit>...`. - -The third syntax ("`git merge --abort`") can only be run after the +The second syntax ("`git merge --abort`") can only be run after the merge has resulted in conflicts. 'git merge --abort' will abort the merge process and try to reconstruct the pre-merge state. However, if there were uncommitted changes when the merge started (and diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index e05d0cddef..74bc6200d5 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -129,7 +129,9 @@ init [--] [<path>...]:: repository will be assumed to be upstream. + Optional <path> arguments limit which submodules will be initialized. -If no path is specified, all submodules are initialized. +If no path is specified and submodule.active has been configured, submodules +configured to be active will be initialized, otherwise all submodules are +initialized. + When present, it will also copy the value of `submodule.$name.update`. This command does not alter existing information in .git/config. diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index 525737a5d8..f8a0b787f4 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -12,9 +12,10 @@ SYNOPSIS 'git tag' [-a | -s | -u <keyid>] [-f] [-m <msg> | -F <file>] <tagname> [<commit> | <object>] 'git tag' -d <tagname>... -'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>] - [--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>] - [--format=<format>] [--[no-]merged [<commit>]] [<pattern>...] +'git tag' [-n[<num>]] -l [--contains <commit>] [--contains <commit>] + [--points-at <object>] [--column[=<options>] | --no-column] + [--create-reflog] [--sort=<key>] [--format=<format>] + [--[no-]merged [<commit>]] [<pattern>...] 'git tag' -v [--format=<format>] <tagname>... DESCRIPTION @@ -82,18 +83,24 @@ OPTIONS -n<num>:: <num> specifies how many lines from the annotation, if any, - are printed when using -l. - The default is not to print any annotation lines. - If no number is given to `-n`, only the first line is printed. - If the tag is not annotated, the commit message is displayed instead. - --l <pattern>:: ---list <pattern>:: - List tags with names that match the given pattern (or all if no - pattern is given). Running "git tag" without arguments also - lists all tags. The pattern is a shell wildcard (i.e., matched - using fnmatch(3)). Multiple patterns may be given; if any of - them matches, the tag is shown. + are printed when using -l. Implies `--list`. ++ +The default is not to print any annotation lines. +If no number is given to `-n`, only the first line is printed. +If the tag is not annotated, the commit message is displayed instead. + +-l:: +--list:: + List tags. With optional `<pattern>...`, e.g. `git tag --list + 'v-*'`, list only the tags that match the pattern(s). ++ +Running "git tag" without arguments also lists all tags. The pattern +is a shell wildcard (i.e., matched using fnmatch(3)). Multiple +patterns may be given; if any of them matches, the tag is shown. ++ +This option is implicitly supplied if any other list-like option such +as `--contains` is provided. See the documentation for each of those +options for details. --sort=<key>:: Sort based on the key given. Prefix `-` to sort in @@ -122,10 +129,23 @@ This option is only applicable when listing tags without annotation lines. --contains [<commit>]:: Only list tags which contain the specified commit (HEAD if not - specified). + specified). Implies `--list`. + +--no-contains [<commit>]:: + Only list tags which don't contain the specified commit (HEAD if + not specified). Implies `--list`. + +--merged [<commit>]:: + Only list tags whose commits are reachable from the specified + commit (`HEAD` if not specified), incompatible with `--no-merged`. + +--no-merged [<commit>]:: + Only list tags whose commits are not reachable from the specified + commit (`HEAD` if not specified), incompatible with `--merged`. --points-at <object>:: - Only list tags of the given object. + Only list tags of the given object (HEAD if not + specified). Implies `--list`. -m <msg>:: --message=<msg>:: @@ -173,11 +193,6 @@ This option is only applicable when listing tags without annotation lines. that of linkgit:git-for-each-ref[1]. When unspecified, defaults to `%(refname:strip=2)`. ---[no-]merged [<commit>]:: - Only list tags whose tips are reachable, or not reachable - if `--no-merged` is used, from the specified commit (`HEAD` - if not specified). - CONFIGURATION ------------- By default, 'git tag' in sign-with-default mode (-s) will use your diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt index ba11b9c95e..75d211f1a8 100644 --- a/Documentation/revisions.txt +++ b/Documentation/revisions.txt @@ -96,7 +96,8 @@ some output processing may assume ref names in UTF-8. refers to the branch that the branch specified by branchname is set to build on top of (configured with `branch.<name>.remote` and `branch.<name>.merge`). A missing branchname defaults to the - current one. + current one. These suffixes are also accepted when spelled in uppercase, and + they mean the same thing no matter the case. '<branchname>@\{push\}', e.g. 'master@\{push\}', '@\{push\}':: The suffix '@\{push}' reports the branch "where we would push to" if @@ -122,6 +123,9 @@ refs/remotes/myfork/mybranch Note in the example that we set up a triangular workflow, where we pull from one location and push to another. In a non-triangular workflow, '@\{push}' is the same as '@\{upstream}', and there is no need for it. ++ +This suffix is also accepted when spelled in uppercase, and means the same +thing no matter the case. '<rev>{caret}', e.g. 'HEAD{caret}, v1.5.1{caret}0':: A suffix '{caret}' to a revision parameter means the first parent of @@ -2348,9 +2348,17 @@ check: common-cmds.h C_SOURCES = $(patsubst %.o,%.c,$(C_OBJ)) %.cocci.patch: %.cocci $(C_SOURCES) @echo ' ' SPATCH $<; \ + ret=0; \ for f in $(C_SOURCES); do \ - $(SPATCH) --sp-file $< $$f $(SPATCH_FLAGS); \ - done >$@ 2>$@.log; \ + $(SPATCH) --sp-file $< $$f $(SPATCH_FLAGS) || \ + { ret=$$?; break; }; \ + done >$@+ 2>$@.log; \ + if test $$ret != 0; \ + then \ + cat $@.log; \ + exit 1; \ + fi; \ + mv $@+ $@; \ if test -s $@; \ then \ echo ' ' SPATCH result: $@; \ diff --git a/builtin/branch.c b/builtin/branch.c index 52688f2e1b..0552c42ad1 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -562,7 +562,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT_SET_INT('r', "remotes", &filter.kind, N_("act on remote-tracking branches"), FILTER_REFS_REMOTES), OPT_CONTAINS(&filter.with_commit, N_("print only branches that contain the commit")), + OPT_NO_CONTAINS(&filter.no_commit, N_("print only branches that don't contain the commit")), OPT_WITH(&filter.with_commit, N_("print only branches that contain the commit")), + OPT_WITHOUT(&filter.no_commit, N_("print only branches that don't contain the commit")), OPT__ABBREV(&filter.abbrev), OPT_GROUP(N_("Specific git-branch actions:")), @@ -618,7 +620,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0) list = 1; - if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr) + if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr || + filter.no_commit) list = 1; if (!!delete + !!rename + !!new_upstream + diff --git a/builtin/clone.c b/builtin/clone.c index b4c929bb8a..de85b85254 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -39,7 +39,7 @@ static const char * const builtin_clone_usage[] = { }; static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1; -static int option_local = -1, option_no_hardlinks, option_shared, option_recursive; +static int option_local = -1, option_no_hardlinks, option_shared; static int option_shallow_submodules; static int deepen; static char *option_template, *option_depth, *option_since; @@ -56,6 +56,21 @@ static struct string_list option_required_reference = STRING_LIST_INIT_NODUP; static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP; static int option_dissociate; static int max_jobs = -1; +static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP; + +static int recurse_submodules_cb(const struct option *opt, + const char *arg, int unset) +{ + if (unset) + string_list_clear((struct string_list *)opt->value, 0); + else if (arg) + string_list_append((struct string_list *)opt->value, arg); + else + string_list_append((struct string_list *)opt->value, + (const char *)opt->defval); + + return 0; +} static struct option builtin_clone_options[] = { OPT__VERBOSITY(&option_verbosity), @@ -74,10 +89,13 @@ static struct option builtin_clone_options[] = { N_("don't use local hardlinks, always copy")), OPT_BOOL('s', "shared", &option_shared, N_("setup as shared repository")), - OPT_BOOL(0, "recursive", &option_recursive, - N_("initialize submodules in the clone")), - OPT_BOOL(0, "recurse-submodules", &option_recursive, - N_("initialize submodules in the clone")), + { OPTION_CALLBACK, 0, "recursive", &option_recurse_submodules, + N_("pathspec"), N_("initialize submodules in the clone"), + PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, recurse_submodules_cb, + (intptr_t)"." }, + { OPTION_CALLBACK, 0, "recurse-submodules", &option_recurse_submodules, + N_("pathspec"), N_("initialize submodules in the clone"), + PARSE_OPT_OPTARG, recurse_submodules_cb, (intptr_t)"." }, OPT_INTEGER('j', "jobs", &max_jobs, N_("number of submodules cloned in parallel")), OPT_STRING(0, "template", &option_template, N_("template-directory"), @@ -733,7 +751,7 @@ static int checkout(int submodule_progress) err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1), oid_to_hex(&oid), "1", NULL); - if (!err && option_recursive) { + if (!err && (option_recurse_submodules.nr > 0)) { struct argv_array args = ARGV_ARRAY_INIT; argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL); @@ -957,7 +975,25 @@ int cmd_clone(int argc, const char **argv, const char *prefix) fprintf(stderr, _("Cloning into '%s'...\n"), dir); } - if (option_recursive) { + if (option_recurse_submodules.nr > 0) { + struct string_list_item *item; + struct strbuf sb = STRBUF_INIT; + + /* remove duplicates */ + string_list_sort(&option_recurse_submodules); + string_list_remove_duplicates(&option_recurse_submodules, 0); + + /* + * NEEDSWORK: In a multi-working-tree world, this needs to be + * set in the per-worktree config. + */ + for_each_string_list_item(item, &option_recurse_submodules) { + strbuf_addf(&sb, "submodule.active=%s", + item->string); + string_list_append(&option_config, + strbuf_detach(&sb, NULL)); + } + if (option_required_reference.nr && option_optional_reference.nr) die(_("clone --recursive is not compatible with " diff --git a/builtin/describe.c b/builtin/describe.c index 45adbf67d5..a5cd8c513f 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -50,7 +50,7 @@ struct commit_name { }; static const char *prio_names[] = { - "head", "lightweight", "annotated", + N_("head"), N_("lightweight"), N_("annotated"), }; static int commit_name_cmp(const struct commit_name *cn1, @@ -395,10 +395,19 @@ static void describe(const char *arg, int last_one) free_commit_list(list); if (debug) { + static int label_width = -1; + if (label_width < 0) { + int i, w; + for (i = 0; i < ARRAY_SIZE(prio_names); i++) { + w = strlen(_(prio_names[i])); + if (label_width < w) + label_width = w; + } + } for (cur_match = 0; cur_match < match_cnt; cur_match++) { struct possible_tag *t = &all_matches[cur_match]; - fprintf(stderr, " %-11s %8d %s\n", - prio_names[t->name->prio], + fprintf(stderr, " %-*s %8d %s\n", + label_width, _(prio_names[t->name->prio]), t->depth, t->name->path); } fprintf(stderr, _("traversed %lu commits\n"), seen_commits); diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index df41fa0350..eca365bf89 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -8,8 +8,8 @@ static char const * const for_each_ref_usage[] = { N_("git for-each-ref [<options>] [<pattern>]"), N_("git for-each-ref [--points-at <object>]"), - N_("git for-each-ref [(--merged | --no-merged) [<object>]]"), - N_("git for-each-ref [--contains [<object>]]"), + N_("git for-each-ref [(--merged | --no-merged) [<commit>]]"), + N_("git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"), NULL }; @@ -43,6 +43,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) OPT_MERGED(&filter, N_("print only refs that are merged")), OPT_NO_MERGED(&filter, N_("print only refs that are not merged")), OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")), + OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")), OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")), OPT_END(), }; diff --git a/builtin/grep.c b/builtin/grep.c index 3f3efa95de..65070c52fc 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -310,10 +310,7 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid, { struct strbuf pathbuf = STRBUF_INIT; - if (opt->relative && opt->prefix_length) { - quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf); - strbuf_insert(&pathbuf, 0, filename, tree_name_len); - } else if (super_prefix) { + if (super_prefix) { strbuf_add(&pathbuf, filename, tree_name_len); strbuf_addstr(&pathbuf, super_prefix); strbuf_addstr(&pathbuf, filename + tree_name_len); @@ -321,6 +318,13 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid, strbuf_addstr(&pathbuf, filename); } + if (opt->relative && opt->prefix_length) { + char *name = strbuf_detach(&pathbuf, NULL); + quote_path_relative(name + tree_name_len, opt->prefix, &pathbuf); + strbuf_insert(&pathbuf, 0, name, tree_name_len); + free(name); + } + #ifndef NO_PTHREADS if (num_threads) { add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, oid); @@ -345,12 +349,14 @@ static int grep_file(struct grep_opt *opt, const char *filename) { struct strbuf buf = STRBUF_INIT; + if (super_prefix) + strbuf_addstr(&buf, super_prefix); + strbuf_addstr(&buf, filename); + if (opt->relative && opt->prefix_length) { - quote_path_relative(filename, opt->prefix, &buf); - } else { - if (super_prefix) - strbuf_addstr(&buf, super_prefix); - strbuf_addstr(&buf, filename); + char *name = strbuf_detach(&buf, NULL); + quote_path_relative(name, opt->prefix, &buf); + free(name); } #ifndef NO_PTHREADS @@ -399,13 +405,12 @@ static void run_pager(struct grep_opt *opt, const char *prefix) } static void compile_submodule_options(const struct grep_opt *opt, - const struct pathspec *pathspec, + const char **argv, int cached, int untracked, int opt_exclude, int use_index, int pattern_type_arg) { struct grep_pat *pattern; - int i; if (recurse_submodules) argv_array_push(&submodule_options, "--recurse-submodules"); @@ -523,9 +528,8 @@ static void compile_submodule_options(const struct grep_opt *opt, /* Add Pathspecs */ argv_array_push(&submodule_options, "--"); - for (i = 0; i < pathspec->nr; i++) - argv_array_push(&submodule_options, - pathspec->items[i].original); + for (; *argv; argv++) + argv_array_push(&submodule_options, *argv); } /* @@ -549,6 +553,11 @@ static int grep_submodule_launch(struct grep_opt *opt, prepare_submodule_repo_env(&cp.env_array); argv_array_push(&cp.env_array, GIT_DIR_ENVIRONMENT); + if (opt->relative && opt->prefix_length) + argv_array_pushf(&cp.env_array, "%s=%s", + GIT_TOPLEVEL_PREFIX_ENVIRONMENT, + opt->prefix); + /* Add super prefix */ argv_array_pushf(&cp.args, "--super-prefix=%s%s/", super_prefix ? super_prefix : "", @@ -976,7 +985,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) OPT_SET_INT(0, "exclude-standard", &opt_exclude, N_("ignore files specified via '.gitignore'"), 1), OPT_BOOL(0, "recurse-submodules", &recurse_submodules, - N_("recursivley search in each submodule")), + N_("recursively search in each submodule")), OPT_STRING(0, "parent-basename", &parent_basename, N_("basename"), N_("prepend parent project's basename to output")), @@ -1233,7 +1242,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (recurse_submodules) { gitmodules_config(); - compile_submodule_options(&opt, &pathspec, cached, untracked, + compile_submodule_options(&opt, argv + i, cached, untracked, opt_exclude, use_index, pattern_type_arg); } diff --git a/builtin/log.c b/builtin/log.c index 670229cbb4..b3b10cc1ed 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -52,6 +52,11 @@ struct line_opt_callback_data { struct string_list args; }; +static int auto_decoration_style(void) +{ + return (isatty(1) || pager_in_use()) ? DECORATE_SHORT_REFS : 0; +} + static int parse_decoration_style(const char *var, const char *value) { switch (git_config_maybe_bool(var, value)) { @@ -67,7 +72,7 @@ static int parse_decoration_style(const char *var, const char *value) else if (!strcmp(value, "short")) return DECORATE_SHORT_REFS; else if (!strcmp(value, "auto")) - return (isatty(1) || pager_in_use()) ? DECORATE_SHORT_REFS : 0; + return auto_decoration_style(); return -1; } @@ -405,6 +410,8 @@ static int git_log_config(const char *var, const char *value, void *cb) if (decoration_style < 0) decoration_style = 0; /* maybe warn? */ return 0; + } else { + decoration_style = auto_decoration_style(); } if (!strcmp(var, "log.showroot")) { default_show_root = git_config_bool(var, value); diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 1c0f057d02..d449e46db5 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -30,7 +30,7 @@ static int line_terminator = '\n'; static int debug_mode; static int show_eol; static int recurse_submodules; -static struct argv_array submodules_options = ARGV_ARRAY_INIT; +static struct argv_array submodule_options = ARGV_ARRAY_INIT; static const char *prefix; static const char *super_prefix; @@ -172,20 +172,27 @@ static void show_killed_files(struct dir_struct *dir) /* * Compile an argv_array with all of the options supported by --recurse_submodules */ -static void compile_submodule_options(const struct dir_struct *dir, int show_tag) +static void compile_submodule_options(const char **argv, + const struct dir_struct *dir, + int show_tag) { if (line_terminator == '\0') - argv_array_push(&submodules_options, "-z"); + argv_array_push(&submodule_options, "-z"); if (show_tag) - argv_array_push(&submodules_options, "-t"); + argv_array_push(&submodule_options, "-t"); if (show_valid_bit) - argv_array_push(&submodules_options, "-v"); + argv_array_push(&submodule_options, "-v"); if (show_cached) - argv_array_push(&submodules_options, "--cached"); + argv_array_push(&submodule_options, "--cached"); if (show_eol) - argv_array_push(&submodules_options, "--eol"); + argv_array_push(&submodule_options, "--eol"); if (debug_mode) - argv_array_push(&submodules_options, "--debug"); + argv_array_push(&submodule_options, "--debug"); + + /* Add Pathspecs */ + argv_array_push(&submodule_options, "--"); + for (; *argv; argv++) + argv_array_push(&submodule_options, *argv); } /** @@ -195,8 +202,11 @@ static void show_gitlink(const struct cache_entry *ce) { struct child_process cp = CHILD_PROCESS_INIT; int status; - int i; + if (prefix_len) + argv_array_pushf(&cp.env_array, "%s=%s", + GIT_TOPLEVEL_PREFIX_ENVIRONMENT, + prefix); argv_array_pushf(&cp.args, "--super-prefix=%s%s/", super_prefix ? super_prefix : "", ce->name); @@ -204,16 +214,7 @@ static void show_gitlink(const struct cache_entry *ce) argv_array_push(&cp.args, "--recurse-submodules"); /* add supported options */ - argv_array_pushv(&cp.args, submodules_options.argv); - - /* - * Pass in the original pathspec args. The submodule will be - * responsible for prepending the 'submodule_prefix' prior to comparing - * against the pathspec for matches. - */ - argv_array_push(&cp.args, "--"); - for (i = 0; i < pathspec.nr; i++) - argv_array_push(&cp.args, pathspec.items[i].original); + argv_array_pushv(&cp.args, submodule_options.argv); cp.git_cmd = 1; cp.dir = ce->name; @@ -604,7 +605,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) setup_work_tree(); if (recurse_submodules) - compile_submodule_options(&dir, show_tag); + compile_submodule_options(argv, &dir, show_tag); if (recurse_submodules && (show_stage || show_deleted || show_others || show_unmerged || diff --git a/builtin/merge.c b/builtin/merge.c index 7554b8d412..95572b1810 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -44,7 +44,6 @@ struct strategy { static const char * const builtin_merge_usage[] = { N_("git merge [<options>] [<commit>...]"), - N_("git merge [<options>] <msg> HEAD <commit>"), N_("git merge --abort"), N_("git merge --continue"), NULL @@ -634,9 +633,10 @@ static void write_tree_trivial(struct object_id *oid) static int try_merge_strategy(const char *strategy, struct commit_list *common, struct commit_list *remoteheads, - struct commit *head, const char *head_arg) + struct commit *head) { static struct lock_file lock; + const char *head_arg = "HEAD"; hold_locked_index(&lock, LOCK_DIE_ON_ERROR); refresh_cache(REFRESH_QUIET); @@ -853,24 +853,6 @@ static int suggest_conflicts(void) return 1; } -static struct commit *is_old_style_invocation(int argc, const char **argv, - const struct object_id *head) -{ - struct commit *second_token = NULL; - if (argc > 2) { - struct object_id second_oid; - - if (get_oid(argv[1], &second_oid)) - return NULL; - second_token = lookup_commit_reference_gently(second_oid.hash, 0); - if (!second_token) - die(_("'%s' is not a commit"), argv[1]); - if (oidcmp(&second_token->object.oid, head)) - return NULL; - } - return second_token; -} - static int evaluate_result(void) { int cnt = 0; @@ -1120,7 +1102,6 @@ int cmd_merge(int argc, const char **argv, const char *prefix) struct object_id result_tree, stash, head_oid; struct commit *head_commit; struct strbuf buf = STRBUF_INIT; - const char *head_arg; int i, ret = 0, head_subsumed; int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0; struct commit_list *common = NULL; @@ -1260,34 +1241,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix) } /* - * This could be traditional "merge <msg> HEAD <commit>..." and - * the way we can tell it is to see if the second token is HEAD, - * but some people might have misused the interface and used a - * commit-ish that is the same as HEAD there instead. - * Traditional format never would have "-m" so it is an - * additional safety measure to check for it. + * All the rest are the commits being merged; prepare + * the standard merge summary message to be appended + * to the given message. */ - if (!have_message && - is_old_style_invocation(argc, argv, &head_commit->object.oid)) { - warning("old-style 'git merge <msg> HEAD <commit>' is deprecated."); - strbuf_addstr(&merge_msg, argv[0]); - head_arg = argv[1]; - argv += 2; - argc -= 2; - remoteheads = collect_parents(head_commit, &head_subsumed, - argc, argv, NULL); - } else { - /* We are invoked directly as the first-class UI. */ - head_arg = "HEAD"; - - /* - * All the rest are the commits being merged; prepare - * the standard merge summary message to be appended - * to the given message. - */ - remoteheads = collect_parents(head_commit, &head_subsumed, - argc, argv, &merge_msg); - } + remoteheads = collect_parents(head_commit, &head_subsumed, + argc, argv, &merge_msg); if (!head_commit || !argc) usage_with_options(builtin_merge_usage, @@ -1513,7 +1472,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) ret = try_merge_strategy(use_strategies[i]->name, common, remoteheads, - head_commit, head_arg); + head_commit); if (!option_commit && !ret) { merge_was_ok = 1; /* @@ -1583,7 +1542,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) printf(_("Using the %s to prepare resolving by hand.\n"), best_strategy); try_merge_strategy(best_strategy, common, remoteheads, - head_commit, head_arg); + head_commit); } if (squash) diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 2ca93adef5..7020278081 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -1524,7 +1524,7 @@ static void queue_commands_from_cert(struct command **tail, while (boc < eoc) { const char *eol = memchr(boc, '\n', eoc - boc); - tail = queue_command(tail, boc, eol ? eol - boc : eoc - eol); + tail = queue_command(tail, boc, eol ? eol - boc : eoc - boc); boc = eol ? eol + 1 : eoc; } } diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index be316bbbc8..85aafe46a4 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -270,6 +270,29 @@ static int module_list_compute(int argc, const char **argv, return result; } +static void module_list_active(struct module_list *list) +{ + int i; + struct module_list active_modules = MODULE_LIST_INIT; + + gitmodules_config(); + + for (i = 0; i < list->nr; i++) { + const struct cache_entry *ce = list->entries[i]; + + if (!is_submodule_initialized(ce->name)) + continue; + + ALLOC_GROW(active_modules.entries, + active_modules.nr + 1, + active_modules.alloc); + active_modules.entries[active_modules.nr++] = ce; + } + + free(list->entries); + *list = active_modules; +} + static int module_list(int argc, const char **argv, const char *prefix) { int i; @@ -334,6 +357,18 @@ static void init_submodule(const char *path, const char *prefix, int quiet) displaypath); /* + * NEEDSWORK: In a multi-working-tree world, this needs to be + * set in the per-worktree config. + * + * Set active flag for the submodule being initialized + */ + if (!is_submodule_initialized(path)) { + strbuf_reset(&sb); + strbuf_addf(&sb, "submodule.%s.active", sub->name); + git_config_set_gently(sb.buf, "true"); + } + + /* * Copy url setting when it is not set yet. * To look up the url in .git/config, we must not fall back to * .gitmodules, so look it up directly. @@ -420,6 +455,13 @@ static int module_init(int argc, const char **argv, const char *prefix) if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) return 1; + /* + * If there are no path args and submodule.active is set then, + * by default, only initialize 'active' modules. + */ + if (!argc && git_config_get_value_multi("submodule.active")) + module_list_active(&list); + for (i = 0; i < list.nr; i++) init_submodule(list.entries[i]->name, prefix, quiet); @@ -741,7 +783,6 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, struct strbuf displaypath_sb = STRBUF_INIT; struct strbuf sb = STRBUF_INIT; const char *displaypath = NULL; - char *url = NULL; int needs_cloning = 0; if (ce_stage(ce)) { @@ -775,15 +816,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, goto cleanup; } - /* - * Looking up the url in .git/config. - * We must not fall back to .gitmodules as we only want - * to process configured submodules. - */ - strbuf_reset(&sb); - strbuf_addf(&sb, "submodule.%s.url", sub->name); - git_config_get_string(sb.buf, &url); - if (!url) { + /* Check if the submodule has been initialized. */ + if (!is_submodule_initialized(ce->name)) { next_submodule_warn_missing(suc, out, displaypath); goto cleanup; } @@ -817,7 +851,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, argv_array_push(&child->args, "--depth=1"); argv_array_pushl(&child->args, "--path", sub->path, NULL); argv_array_pushl(&child->args, "--name", sub->name, NULL); - argv_array_pushl(&child->args, "--url", url, NULL); + argv_array_pushl(&child->args, "--url", sub->url, NULL); if (suc->references.nr) { struct string_list_item *item; for_each_string_list_item(item, &suc->references) @@ -827,7 +861,6 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, argv_array_push(&child->args, suc->depth); cleanup: - free(url); strbuf_reset(&displaypath_sb); strbuf_reset(&sb); @@ -1109,6 +1142,16 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix) return 0; } +static int is_active(int argc, const char **argv, const char *prefix) +{ + if (argc != 2) + die("submodule--helper is-active takes exactly 1 arguments"); + + gitmodules_config(); + + return !is_submodule_initialized(argv[1]); +} + #define SUPPORT_SUPER_PREFIX (1<<0) struct cmd_struct { @@ -1128,6 +1171,7 @@ static struct cmd_struct commands[] = { {"init", module_init, SUPPORT_SUPER_PREFIX}, {"remote-branch", resolve_remote_submodule_branch, 0}, {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX}, + {"is-active", is_active, 0}, }; int cmd_submodule__helper(int argc, const char **argv, const char *prefix) diff --git a/builtin/tag.c b/builtin/tag.c index 0eb4e25c6b..222404522f 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -22,7 +22,7 @@ static const char * const git_tag_usage[] = { N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]"), N_("git tag -d <tagname>..."), - N_("git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>]" + N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]" "\n\t\t[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]"), N_("git tag -v [--format=<format>] <tagname>..."), NULL @@ -418,14 +418,17 @@ int cmd_tag(int argc, const char **argv, const char *prefix) OPT_GROUP(N_("Tag listing options")), OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")), OPT_CONTAINS(&filter.with_commit, N_("print only tags that contain the commit")), + OPT_NO_CONTAINS(&filter.no_commit, N_("print only tags that don't contain the commit")), OPT_WITH(&filter.with_commit, N_("print only tags that contain the commit")), + OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")), OPT_MERGED(&filter, N_("print only tags that are merged")), OPT_NO_MERGED(&filter, N_("print only tags that are not merged")), OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"), N_("field name to sort on"), &parse_opt_ref_sorting), { OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"), - N_("print only tags of the object"), 0, parse_opt_object_name + N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT, + parse_opt_object_name, (intptr_t) "HEAD" }, OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")), OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")), @@ -448,8 +451,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix) } create_tag_object = (opt.sign || annotate || msg.given || msgfile); - if (argc == 0 && !cmdmode) - cmdmode = 'l'; + if (!cmdmode) { + if (argc == 0) + cmdmode = 'l'; + else if (filter.with_commit || filter.no_commit || + filter.points_at.nr || filter.merge_commit || + filter.lines != -1) + cmdmode = 'l'; + } if ((create_tag_object || force) && (cmdmode != 0)) usage_with_options(git_tag_usage, options); @@ -479,13 +488,15 @@ int cmd_tag(int argc, const char **argv, const char *prefix) return ret; } if (filter.lines != -1) - die(_("-n option is only allowed with -l.")); + die(_("-n option is only allowed in list mode")); if (filter.with_commit) - die(_("--contains option is only allowed with -l.")); + die(_("--contains option is only allowed in list mode")); + if (filter.no_commit) + die(_("--no-contains option is only allowed in list mode")); if (filter.points_at.nr) - die(_("--points-at option is only allowed with -l.")); + die(_("--points-at option is only allowed in list mode")); if (filter.merge_commit) - die(_("--merged and --no-merged option are only allowed with -l")); + die(_("--merged and --no-merged options are only allowed in list mode")); if (cmdmode == 'd') return for_each_tag_name(argv, delete_tag, NULL); if (cmdmode == 'v') { diff --git a/builtin/update-index.c b/builtin/update-index.c index d74d72cc7f..ebfc09faa0 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -125,12 +125,16 @@ static int test_if_untracked_cache_is_supported(void) struct stat st; struct stat_data base; int fd, ret = 0; + char *cwd; strbuf_addstr(&mtime_dir, "mtime-test-XXXXXX"); if (!mkdtemp(mtime_dir.buf)) die_errno("Could not make temporary directory"); - fprintf(stderr, _("Testing mtime in '%s' "), xgetcwd()); + cwd = xgetcwd(); + fprintf(stderr, _("Testing mtime in '%s' "), cwd); + free(cwd); + atexit(remove_test_directory); xstat_mtime_dir(&st); fill_stat_data(&base, &st); @@ -411,6 +411,7 @@ static inline enum object_type object_type(unsigned int mode) #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE" #define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX" #define GIT_SUPER_PREFIX_ENVIRONMENT "GIT_INTERNAL_SUPER_PREFIX" +#define GIT_TOPLEVEL_PREFIX_ENVIRONMENT "GIT_INTERNAL_TOPLEVEL_PREFIX" #define DEFAULT_GIT_DIR_ENVIRONMENT ".git" #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY" #define INDEX_ENVIRONMENT "GIT_INDEX_FILE" diff --git a/ci/run-windows-build.sh b/ci/run-windows-build.sh new file mode 100755 index 0000000000..4e3a50b60e --- /dev/null +++ b/ci/run-windows-build.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# +# Script to trigger the a Git for Windows build and test run. +# Set the $GFW_CI_TOKEN as environment variable. +# Pass the branch (only branches on https://github.com/git/git are +# supported) and a commit hash. +# + +test $# -ne 2 && echo "Unexpected number of parameters" && exit 1 +test -z "$GFW_CI_TOKEN" && echo "GFW_CI_TOKEN not defined" && exit + +BRANCH=$1 +COMMIT=$2 + +gfwci () { + local CURL_ERROR_CODE HTTP_CODE + exec 3>&1 + HTTP_CODE=$(curl \ + -H "Authentication: Bearer $GFW_CI_TOKEN" \ + --silent --retry 5 --write-out '%{HTTP_CODE}' \ + --output >(sed "$(printf '1s/^\xef\xbb\xbf//')" >cat >&3) \ + "https://git-for-windows-ci.azurewebsites.net/api/TestNow?$1" \ + ) + CURL_ERROR_CODE=$? + if test $CURL_ERROR_CODE -ne 0 + then + return $CURL_ERROR_CODE + fi + if test "$HTTP_CODE" -ge 400 && test "$HTTP_CODE" -lt 600 + then + return 127 + fi +} + +# Trigger build job +BUILD_ID=$(gfwci "action=trigger&branch=$BRANCH&commit=$COMMIT&skipTests=false") +if test $? -ne 0 +then + echo "Unable to trigger Visual Studio Team Services Build" + echo "$BUILD_ID" + exit 1 +fi + +# Check if the $BUILD_ID contains a number +case $BUILD_ID in +''|*[!0-9]*) echo "Unexpected build number: $BUILD_ID" && exit 1 +esac + +echo "Visual Studio Team Services Build #${BUILD_ID}" + +# Wait until build job finished +STATUS= +RESULT= +while true +do + LAST_STATUS=$STATUS + STATUS=$(gfwci "action=status&buildId=$BUILD_ID") + test "$STATUS" = "$LAST_STATUS" || printf "\nStatus: $STATUS " + printf "." + + case "$STATUS" in + inProgress|postponed|notStarted) sleep 10 ;; # continue + "completed: succeeded") RESULT="success"; break;; # success + *) echo "Unhandled status: $STATUS"; break;; # failure + esac +done + +# Print log +echo "" +echo "" +gfwci "action=log&buildId=$BUILD_ID" | cut -c 30- + +# Set exit code for TravisCI +test "$RESULT" = "success" diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index fc32286a43..1150164d5c 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -213,6 +213,20 @@ _get_comp_words_by_ref () } fi +# Fills the COMPREPLY array with prefiltered words without any additional +# processing. +# Callers must take care of providing only words that match the current word +# to be completed and adding any prefix and/or suffix (trailing space!), if +# necessary. +# 1: List of newline-separated matching completion words, complete with +# prefix and suffix. +__gitcomp_direct () +{ + local IFS=$'\n' + + COMPREPLY=($1) +} + __gitcompappend () { local x i=${#COMPREPLY[@]} @@ -338,14 +352,27 @@ __git_index_files () done | sort | uniq } +# Lists branches from the local repository. +# 1: A prefix to be added to each listed branch (optional). +# 2: List only branches matching this word (optional; list all branches if +# unset or empty). +# 3: A suffix to be appended to each listed branch (optional). __git_heads () { - __git for-each-ref --format='%(refname:short)' refs/heads + local pfx="${1-}" cur_="${2-}" sfx="${3-}" + + __git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \ + "refs/heads/$cur_*" "refs/heads/$cur_*/**" } +# Lists tags from the local repository. +# Accepts the same positional parameters as __git_heads() above. __git_tags () { - __git for-each-ref --format='%(refname:short)' refs/tags + local pfx="${1-}" cur_="${2-}" sfx="${3-}" + + __git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \ + "refs/tags/$cur_*" "refs/tags/$cur_*/**" } # Lists refs from the local (by default) or from a remote repository. @@ -354,11 +381,21 @@ __git_tags () # Can be the name of a configured remote, a path, or a URL. # 2: In addition to local refs, list unique branches from refs/remotes/ for # 'git checkout's tracking DWIMery (optional; ignored, if set but empty). +# 3: A prefix to be added to each listed ref (optional). +# 4: List only refs matching this word (optional; list all refs if unset or +# empty). +# 5: A suffix to be appended to each listed ref (optional; ignored, if set +# but empty). +# +# Use __git_complete_refs() instead. __git_refs () { local i hash dir track="${2-}" local list_refs_from=path remote="${1-}" - local format refs pfx + local format refs + local pfx="${3-}" cur_="${4-$cur}" sfx="${5-}" + local match="${4-}" + local fer_pfx="${pfx//\%/%%}" # "escape" for-each-ref format specifiers __git_find_repo_path dir="$__git_repo_path" @@ -382,63 +419,78 @@ __git_refs () fi if [ "$list_refs_from" = path ]; then - case "$cur" in + if [[ "$cur_" == ^* ]]; then + pfx="$pfx^" + fer_pfx="$fer_pfx^" + cur_=${cur_#^} + match=${match#^} + fi + case "$cur_" in refs|refs/*) format="refname" - refs="${cur%/*}" + refs=("$match*" "$match*/**") track="" ;; *) - [[ "$cur" == ^* ]] && pfx="^" for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do - if [ -e "$dir/$i" ]; then echo $pfx$i; fi + case "$i" in + $match*) + if [ -e "$dir/$i" ]; then + echo "$pfx$i$sfx" + fi + ;; + esac done - format="refname:short" - refs="refs/tags refs/heads refs/remotes" + format="refname:strip=2" + refs=("refs/tags/$match*" "refs/tags/$match*/**" + "refs/heads/$match*" "refs/heads/$match*/**" + "refs/remotes/$match*" "refs/remotes/$match*/**") ;; esac - __git_dir="$dir" __git for-each-ref --format="$pfx%($format)" \ - $refs + __git_dir="$dir" __git for-each-ref --format="$fer_pfx%($format)$sfx" \ + "${refs[@]}" if [ -n "$track" ]; then # employ the heuristic used by git checkout # Try to find a remote branch that matches the completion word # but only output if the branch name is unique - local ref entry - __git for-each-ref --shell --format="ref=%(refname:short)" \ - "refs/remotes/" | \ - while read -r entry; do - eval "$entry" - ref="${ref#*/}" - if [[ "$ref" == "$cur"* ]]; then - echo "$ref" - fi - done | sort | uniq -u + __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \ + --sort="refname:strip=3" \ + "refs/remotes/*/$match*" "refs/remotes/*/$match*/**" | \ + uniq -u fi return fi - case "$cur" in + case "$cur_" in refs|refs/*) - __git ls-remote "$remote" "$cur*" | \ + __git ls-remote "$remote" "$match*" | \ while read -r hash i; do case "$i" in *^{}) ;; - *) echo "$i" ;; + *) echo "$pfx$i$sfx" ;; esac done ;; *) if [ "$list_refs_from" = remote ]; then - echo "HEAD" - __git for-each-ref --format="%(refname:short)" \ - "refs/remotes/$remote/" | sed -e "s#^$remote/##" + case "HEAD" in + $match*) echo "${pfx}HEAD$sfx" ;; + esac + __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \ + "refs/remotes/$remote/$match*" \ + "refs/remotes/$remote/$match*/**" else - __git ls-remote "$remote" HEAD \ - "refs/tags/*" "refs/heads/*" "refs/remotes/*" | + local query_symref + case "HEAD" in + $match*) query_symref="HEAD" ;; + esac + __git ls-remote "$remote" $query_symref \ + "refs/tags/$match*" "refs/heads/$match*" \ + "refs/remotes/$match*" | while read -r hash i; do case "$i" in *^{}) ;; - refs/*) echo "${i#refs/*/}" ;; - *) echo "$i" ;; # symbolic refs + refs/*) echo "$pfx${i#refs/*/}$sfx" ;; + *) echo "$pfx$i$sfx" ;; # symbolic refs esac done fi @@ -446,7 +498,38 @@ __git_refs () esac } +# Completes refs, short and long, local and remote, symbolic and pseudo. +# +# Usage: __git_complete_refs [<option>]... +# --remote=<remote>: The remote to list refs from, can be the name of a +# configured remote, a path, or a URL. +# --track: List unique remote branches for 'git checkout's tracking DWIMery. +# --pfx=<prefix>: A prefix to be added to each ref. +# --cur=<word>: The current ref to be completed. Defaults to the current +# word to be completed. +# --sfx=<suffix>: A suffix to be appended to each ref instead of the default +# space. +__git_complete_refs () +{ + local remote track pfx cur_="$cur" sfx=" " + + while test $# != 0; do + case "$1" in + --remote=*) remote="${1##--remote=}" ;; + --track) track="yes" ;; + --pfx=*) pfx="${1##--pfx=}" ;; + --cur=*) cur_="${1##--cur=}" ;; + --sfx=*) sfx="${1##--sfx=}" ;; + *) return 1 ;; + esac + shift + done + + __gitcomp_direct "$(__git_refs "$remote" "$track" "$pfx" "$cur_" "$sfx")" +} + # __git_refs2 requires 1 argument (to pass to __git_refs) +# Deprecated: use __git_complete_fetch_refspecs() instead. __git_refs2 () { local i @@ -455,6 +538,24 @@ __git_refs2 () done } +# Completes refspecs for fetching from a remote repository. +# 1: The remote repository. +# 2: A prefix to be added to each listed refspec (optional). +# 3: The ref to be completed as a refspec instead of the current word to be +# completed (optional) +# 4: A suffix to be appended to each listed refspec instead of the default +# space (optional). +__git_complete_fetch_refspecs () +{ + local i remote="$1" pfx="${2-}" cur_="${3-$cur}" sfx="${4- }" + + __gitcomp_direct "$( + for i in $(__git_refs "$remote" "" "" "$cur_") ; do + echo "$pfx$i:$i$sfx" + done + )" +} + # __git_refs_remotes requires 1 argument (to pass to ls-remote) __git_refs_remotes () { @@ -554,15 +655,15 @@ __git_complete_revlist_file () *...*) pfx="${cur_%...*}..." cur_="${cur_#*...}" - __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_" + __git_complete_refs --pfx="$pfx" --cur="$cur_" ;; *..*) pfx="${cur_%..*}.." cur_="${cur_#*..}" - __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_" + __git_complete_refs --pfx="$pfx" --cur="$cur_" ;; *) - __gitcomp_nl "$(__git_refs)" + __git_complete_refs ;; esac } @@ -647,23 +748,23 @@ __git_complete_remote_or_refspec () case "$cmd" in fetch) if [ $lhs = 1 ]; then - __gitcomp_nl "$(__git_refs2 "$remote")" "$pfx" "$cur_" + __git_complete_fetch_refspecs "$remote" "$pfx" "$cur_" else - __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_" + __git_complete_refs --pfx="$pfx" --cur="$cur_" fi ;; pull|remote) if [ $lhs = 1 ]; then - __gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_" + __git_complete_refs --remote="$remote" --pfx="$pfx" --cur="$cur_" else - __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_" + __git_complete_refs --pfx="$pfx" --cur="$cur_" fi ;; push) if [ $lhs = 1 ]; then - __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_" + __git_complete_refs --pfx="$pfx" --cur="$cur_" else - __gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_" + __git_complete_refs --remote="$remote" --pfx="$pfx" --cur="$cur_" fi ;; esac @@ -1066,7 +1167,7 @@ _git_bisect () case "$subcommand" in bad|good|reset|skip|start) - __gitcomp_nl "$(__git_refs)" + __git_complete_refs ;; *) ;; @@ -1088,12 +1189,12 @@ _git_branch () case "$cur" in --set-upstream-to=*) - __gitcomp_nl "$(__git_refs)" "" "${cur##--set-upstream-to=}" + __git_complete_refs --cur="${cur##--set-upstream-to=}" ;; --*) __gitcomp " --color --no-color --verbose --abbrev= --no-abbrev - --track --no-track --contains --merged --no-merged + --track --no-track --contains --no-contains --merged --no-merged --set-upstream-to= --edit-description --list --unset-upstream --delete --move --remotes --column --no-column --sort= --points-at @@ -1101,9 +1202,9 @@ _git_branch () ;; *) if [ $only_local_ref = "y" -a $has_r = "n" ]; then - __gitcomp_nl "$(__git_heads)" + __gitcomp_direct "$(__git_heads "" "$cur" " ")" else - __gitcomp_nl "$(__git_refs)" + __git_complete_refs fi ;; esac @@ -1146,18 +1247,18 @@ _git_checkout () *) # check if --track, --no-track, or --no-guess was specified # if so, disable DWIM mode - local flags="--track --no-track --no-guess" track=1 + local flags="--track --no-track --no-guess" track_opt="--track" if [ -n "$(__git_find_on_cmdline "$flags")" ]; then - track='' + track_opt='' fi - __gitcomp_nl "$(__git_refs '' $track)" + __git_complete_refs $track_opt ;; esac } _git_cherry () { - __gitcomp_nl "$(__git_refs)" + __git_complete_refs } _git_cherry_pick () @@ -1172,7 +1273,7 @@ _git_cherry_pick () __gitcomp "--edit --no-commit --signoff --strategy= --mainline" ;; *) - __gitcomp_nl "$(__git_refs)" + __git_complete_refs ;; esac } @@ -1224,7 +1325,7 @@ _git_commit () { case "$prev" in -c|-C) - __gitcomp_nl "$(__git_refs)" "" "${cur}" + __git_complete_refs return ;; esac @@ -1237,7 +1338,7 @@ _git_commit () ;; --reuse-message=*|--reedit-message=*|\ --fixup=*|--squash=*) - __gitcomp_nl "$(__git_refs)" "" "${cur#*=}" + __git_complete_refs --cur="${cur#*=}" return ;; --untracked-files=*) @@ -1277,7 +1378,7 @@ _git_describe () " return esac - __gitcomp_nl "$(__git_refs)" + __git_complete_refs } __git_diff_algorithms="myers minimal patience histogram" @@ -1428,8 +1529,43 @@ _git_gitk () _gitk } -__git_match_ctag() { - awk "/^${1//\//\\/}/ { print \$1 }" "$2" +# Lists matching symbol names from a tag (as in ctags) file. +# 1: List symbol names matching this word. +# 2: The tag file to list symbol names from. +# 3: A prefix to be added to each listed symbol name (optional). +# 4: A suffix to be appended to each listed symbol name (optional). +__git_match_ctag () { + awk -v pfx="${3-}" -v sfx="${4-}" " + /^${1//\//\\/}/ { print pfx \$1 sfx } + " "$2" +} + +# Complete symbol names from a tag file. +# Usage: __git_complete_symbol [<option>]... +# --tags=<file>: The tag file to list symbol names from instead of the +# default "tags". +# --pfx=<prefix>: A prefix to be added to each symbol name. +# --cur=<word>: The current symbol name to be completed. Defaults to +# the current word to be completed. +# --sfx=<suffix>: A suffix to be appended to each symbol name instead +# of the default space. +__git_complete_symbol () { + local tags=tags pfx="" cur_="${cur-}" sfx=" " + + while test $# != 0; do + case "$1" in + --tags=*) tags="${1##--tags=}" ;; + --pfx=*) pfx="${1##--pfx=}" ;; + --cur=*) cur_="${1##--cur=}" ;; + --sfx=*) sfx="${1##--sfx=}" ;; + *) return 1 ;; + esac + shift + done + + if test -r "$tags"; then + __gitcomp_direct "$(__git_match_ctag "$cur_" "$tags" "$pfx" "$sfx")" + fi } _git_grep () @@ -1459,14 +1595,11 @@ _git_grep () case "$cword,$prev" in 2,*|*,-*) - if test -r tags; then - __gitcomp_nl "$(__git_match_ctag "$cur" tags)" - return - fi + __git_complete_symbol && return ;; esac - __gitcomp_nl "$(__git_refs)" + __git_complete_refs } _git_help () @@ -1573,6 +1706,19 @@ _git_log () if [ -f "$__git_repo_path/MERGE_HEAD" ]; then merge="--merge" fi + case "$prev,$cur" in + -L,:*:*) + return # fall back to Bash filename completion + ;; + -L,:*) + __git_complete_symbol --cur="${cur#:}" --sfx=":" + return + ;; + -G,*|-S,*) + __git_complete_symbol + return + ;; + esac case "$cur" in --pretty=*|--format=*) __gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases) @@ -1618,6 +1764,21 @@ _git_log () " return ;; + -L:*:*) + return # fall back to Bash filename completion + ;; + -L:*) + __git_complete_symbol --cur="${cur#-L:}" --sfx=":" + return + ;; + -G*) + __git_complete_symbol --pfx="-G" --cur="${cur#-G}" + return + ;; + -S*) + __git_complete_symbol --pfx="-S" --cur="${cur#-S}" + return + ;; esac __git_complete_revlist } @@ -1640,7 +1801,7 @@ _git_merge () --rerere-autoupdate --no-rerere-autoupdate --abort --continue" return esac - __gitcomp_nl "$(__git_refs)" + __git_complete_refs } _git_mergetool () @@ -1665,7 +1826,7 @@ _git_merge_base () return ;; esac - __gitcomp_nl "$(__git_refs)" + __git_complete_refs } _git_mv () @@ -1703,7 +1864,7 @@ _git_notes () ,*) case "$prev" in --ref) - __gitcomp_nl "$(__git_refs)" + __git_complete_refs ;; *) __gitcomp "$subcommands --ref" @@ -1712,7 +1873,7 @@ _git_notes () ;; add,--reuse-message=*|append,--reuse-message=*|\ add,--reedit-message=*|append,--reedit-message=*) - __gitcomp_nl "$(__git_refs)" "" "${cur#*=}" + __git_complete_refs --cur="${cur#*=}" ;; add,--*|append,--*) __gitcomp '--file= --message= --reedit-message= @@ -1731,7 +1892,7 @@ _git_notes () -m|-F) ;; *) - __gitcomp_nl "$(__git_refs)" + __git_complete_refs ;; esac ;; @@ -1769,10 +1930,10 @@ __git_complete_force_with_lease () --*=) ;; *:*) - __gitcomp_nl "$(__git_refs)" "" "${cur_#*:}" + __git_complete_refs --cur="${cur_#*:}" ;; *) - __gitcomp_nl "$(__git_refs)" "" "$cur_" + __git_complete_refs --cur="$cur_" ;; esac } @@ -1848,7 +2009,7 @@ _git_rebase () return esac - __gitcomp_nl "$(__git_refs)" + __git_complete_refs } _git_reflog () @@ -1859,7 +2020,7 @@ _git_reflog () if [ -z "$subcommand" ]; then __gitcomp "$subcommands" else - __gitcomp_nl "$(__git_refs)" + __git_complete_refs fi } @@ -2005,7 +2166,7 @@ _git_config () return ;; branch.*.merge) - __gitcomp_nl "$(__git_refs)" + __git_complete_refs return ;; branch.*.rebase) @@ -2109,7 +2270,7 @@ _git_config () ;; branch.*) local pfx="${cur%.*}." cur_="${cur#*.}" - __gitcomp_nl "$(__git_heads)" "$pfx" "$cur_" "." + __gitcomp_direct "$(__git_heads "$pfx" "$cur_" ".")" __gitcomp_nl_append $'autosetupmerge\nautosetuprebase\n' "$pfx" "$cur_" return ;; @@ -2516,7 +2677,7 @@ _git_replace () return ;; esac - __gitcomp_nl "$(__git_refs)" + __git_complete_refs } _git_rerere () @@ -2540,7 +2701,7 @@ _git_reset () return ;; esac - __gitcomp_nl "$(__git_refs)" + __git_complete_refs } _git_revert () @@ -2559,7 +2720,7 @@ _git_revert () return ;; esac - __gitcomp_nl "$(__git_refs)" + __git_complete_refs } _git_rm () @@ -2667,7 +2828,7 @@ _git_stash () ;; branch,*) if [ $cword -eq 3 ]; then - __gitcomp_nl "$(__git_refs)"; + __git_complete_refs else __gitcomp_nl "$(__git stash list \ | sed -n -e 's/:.*//p')" @@ -2834,7 +2995,7 @@ _git_tag () i="${words[c]}" case "$i" in -d|-v) - __gitcomp_nl "$(__git_tags)" + __gitcomp_direct "$(__git_tags "" "$cur" " ")" return ;; -f) @@ -2849,11 +3010,11 @@ _git_tag () ;; -*|tag) if [ $f = 1 ]; then - __gitcomp_nl "$(__git_tags)" + __gitcomp_direct "$(__git_tags "" "$cur" " ")" fi ;; *) - __gitcomp_nl "$(__git_refs)" + __git_complete_refs ;; esac @@ -2862,7 +3023,7 @@ _git_tag () __gitcomp " --list --delete --verify --annotate --message --file --sign --cleanup --local-user --force --column --sort= - --contains --points-at --merged --no-merged --create-reflog + --contains --no-contains --points-at --merged --no-merged --create-reflog " ;; esac @@ -3024,6 +3185,15 @@ if [[ -n ${ZSH_VERSION-} ]]; then esac } + __gitcomp_direct () + { + emulate -L zsh + + local IFS=$'\n' + compset -P '*[=:]' + compadd -Q -- ${=1} && _ret=0 + } + __gitcomp_nl () { emulate -L zsh diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh index e25541308a..c3521fbfc4 100644 --- a/contrib/completion/git-completion.zsh +++ b/contrib/completion/git-completion.zsh @@ -67,6 +67,15 @@ __gitcomp () esac } +__gitcomp_direct () +{ + emulate -L zsh + + local IFS=$'\n' + compset -P '*[=:]' + compadd -Q -- ${=1} && _ret=0 +} + __gitcomp_nl () { emulate -L zsh diff --git a/environment.c b/environment.c index a9bf5658ad..ff6e4f06e9 100644 --- a/environment.c +++ b/environment.c @@ -167,8 +167,11 @@ static void setup_git_env(void) const char *replace_ref_base; git_dir = getenv(GIT_DIR_ENVIRONMENT); - if (!git_dir) + if (!git_dir) { + if (!startup_info->have_repository) + die("BUG: setup_git_env called without repository"); git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; + } gitfile = read_gitfile(git_dir); git_dir = xstrdup(gitfile ? gitfile : git_dir); if (get_common_dir(&sb, git_dir)) diff --git a/git-submodule.sh b/git-submodule.sh index 136e26a2c8..6ec35e5fcd 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -278,6 +278,20 @@ or you are unsure what this means choose another name with the '--name' option." fi && git add --force .gitmodules || die "$(eval_gettext "Failed to register submodule '\$sm_path'")" + + # NEEDSWORK: In a multi-working-tree world, this needs to be + # set in the per-worktree config. + if git config --get submodule.active >/dev/null + then + # If the submodule being adding isn't already covered by the + # current configured pathspec, set the submodule's active flag + if ! git submodule--helper is-active "$sm_path" + then + git config submodule."$sm_name".active "true" + fi + else + git config submodule."$sm_name".active "true" + fi } # @@ -1010,14 +1024,13 @@ cmd_status() do die_if_unmatched "$mode" "$sha1" name=$(git submodule--helper name "$sm_path") || exit - url=$(git config submodule."$name".url) displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix") if test "$stage" = U then say "U$sha1 $displaypath" continue fi - if test -z "$url" || + if ! git submodule--helper is-active "$sm_path" || { ! test -d "$sm_path"/.git && ! test -f "$sm_path"/.git @@ -1090,6 +1103,13 @@ cmd_sync() while read mode sha1 stage sm_path do die_if_unmatched "$mode" "$sha1" + + # skip inactive submodules + if ! git submodule--helper is-active "$sm_path" + then + continue + fi + name=$(git submodule--helper name "$sm_path") url=$(git config -f .gitmodules --get submodule."$name".url) @@ -1112,27 +1132,24 @@ cmd_sync() ;; esac - if git config "submodule.$name.url" >/dev/null 2>/dev/null + displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix") + say "$(eval_gettext "Synchronizing submodule url for '\$displaypath'")" + git config submodule."$name".url "$super_config_url" + + if test -e "$sm_path"/.git then - displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix") - say "$(eval_gettext "Synchronizing submodule url for '\$displaypath'")" - git config submodule."$name".url "$super_config_url" + ( + sanitize_submodule_env + cd "$sm_path" + remote=$(get_default_remote) + git config remote."$remote".url "$sub_origin_url" - if test -e "$sm_path"/.git + if test -n "$recursive" then - ( - sanitize_submodule_env - cd "$sm_path" - remote=$(get_default_remote) - git config remote."$remote".url "$sub_origin_url" - - if test -n "$recursive" - then - prefix="$prefix$sm_path/" - eval cmd_sync - fi - ) + prefix="$prefix$sm_path/" + eval cmd_sync fi + ) fi done } @@ -361,8 +361,6 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) if (!help && get_super_prefix()) { if (!(p->option & SUPPORT_SUPER_PREFIX)) die("%s doesn't support --super-prefix", p->cmd); - if (prefix) - die("can't use --super-prefix from a subdirectory"); } if (!help && p->option & NEED_WORK_TREE) @@ -153,8 +153,8 @@ static struct leaf_node *note_tree_find(struct notes_tree *t, * How to consolidate an int_node: * If there are > 1 non-NULL entries, give up and return non-zero. * Otherwise replace the int_node at the given index in the given parent node - * with the only entry (or a NULL entry if no entries) from the given tree, - * and return 0. + * with the only NOTE entry (or a NULL entry if no entries) from the given + * tree, and return 0. */ static int note_tree_consolidate(struct int_node *tree, struct int_node *parent, unsigned char index) @@ -173,6 +173,8 @@ static int note_tree_consolidate(struct int_node *tree, } } + if (p && (GET_PTR_TYPE(p) != PTR_TYPE_NOTE)) + return -2; /* replace tree with p in parent[index] */ parent->a[index] = p; free(tree); diff --git a/parse-options.h b/parse-options.h index dcd8a0926c..af711227ae 100644 --- a/parse-options.h +++ b/parse-options.h @@ -258,7 +258,9 @@ extern int parse_opt_passthru_argv(const struct option *, const char *, int); PARSE_OPT_LASTARG_DEFAULT | flag, \ parse_opt_commits, (intptr_t) "HEAD" \ } -#define OPT_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("contains", v, h, 0) -#define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN) +#define OPT_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("contains", v, h, PARSE_OPT_NONEG) +#define OPT_NO_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("no-contains", v, h, PARSE_OPT_NONEG) +#define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG) +#define OPT_WITHOUT(v, h) _OPT_CONTAINS_OR_WITH("without", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG) #endif @@ -7530,7 +7530,19 @@ msgstr "git describe [<Optionen>] [<Commit-Angabe>...]" msgid "git describe [<options>] --dirty" msgstr "git describe [<Optionen>] --dirty" -#: builtin/describe.c:217 +#: builtin/describe.c:52 +msgid "head" +msgstr "Branch" + +#: builtin/describe.c:52 +msgid "lightweight" +msgstr "nicht-annotiert" + +#: builtin/describe.c:52 +msgid "annotated" +msgstr "annotiert" + +#: builtin/describe.c:249 #, c-format msgid "annotated tag %s not available" msgstr "annotiertes Tag %s ist nicht verfügbar" diff --git a/ref-filter.c b/ref-filter.c index 9c82b5b9d6..1e39273005 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1487,6 +1487,7 @@ struct ref_filter_cbdata { struct ref_array *array; struct ref_filter *filter; struct contains_cache contains_cache; + struct contains_cache no_contains_cache; }; /* @@ -1586,11 +1587,11 @@ static enum contains_result contains_tag_algo(struct commit *candidate, } static int commit_contains(struct ref_filter *filter, struct commit *commit, - struct contains_cache *cache) + struct commit_list *list, struct contains_cache *cache) { if (filter->with_commit_tag_algo) - return contains_tag_algo(commit, filter->with_commit, cache) == CONTAINS_YES; - return is_descendant_of(commit, filter->with_commit); + return contains_tag_algo(commit, list, cache) == CONTAINS_YES; + return is_descendant_of(commit, list); } /* @@ -1780,13 +1781,17 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid, * obtain the commit using the 'oid' available and discard all * non-commits early. The actual filtering is done later. */ - if (filter->merge_commit || filter->with_commit || filter->verbose) { + if (filter->merge_commit || filter->with_commit || filter->no_commit || filter->verbose) { commit = lookup_commit_reference_gently(oid->hash, 1); if (!commit) return 0; - /* We perform the filtering for the '--contains' option */ + /* We perform the filtering for the '--contains' option... */ if (filter->with_commit && - !commit_contains(filter, commit, &ref_cbdata->contains_cache)) + !commit_contains(filter, commit, filter->with_commit, &ref_cbdata->contains_cache)) + return 0; + /* ...or for the `--no-contains' option */ + if (filter->no_commit && + commit_contains(filter, commit, filter->no_commit, &ref_cbdata->no_contains_cache)) return 0; } @@ -1887,6 +1892,7 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int filter->kind = type & FILTER_REFS_KIND_MASK; init_contains_cache(&ref_cbdata.contains_cache); + init_contains_cache(&ref_cbdata.no_contains_cache); /* Simple per-ref filtering */ if (!filter->kind) @@ -1911,6 +1917,7 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int } clear_contains_cache(&ref_cbdata.contains_cache); + clear_contains_cache(&ref_cbdata.no_contains_cache); /* Filters that need revision walking */ if (filter->merge_commit) @@ -2084,8 +2091,17 @@ int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset) { struct ref_filter *rf = opt->value; unsigned char sha1[20]; + int no_merged = starts_with(opt->long_name, "no"); + + if (rf->merge) { + if (no_merged) { + return opterror(opt, "is incompatible with --merged", 0); + } else { + return opterror(opt, "is incompatible with --no-merged", 0); + } + } - rf->merge = starts_with(opt->long_name, "no") + rf->merge = no_merged ? REF_FILTER_MERGED_OMIT : REF_FILTER_MERGED_INCLUDE; diff --git a/ref-filter.h b/ref-filter.h index e738c5dfd3..dde40f6849 100644 --- a/ref-filter.h +++ b/ref-filter.h @@ -53,6 +53,7 @@ struct ref_filter { const char **name_patterns; struct sha1_array points_at; struct commit_list *with_commit; + struct commit_list *no_commit; enum { REF_FILTER_MERGED_NONE = 0, diff --git a/sequencer.c b/sequencer.c index d76dc9cb2b..77afecaebf 100644 --- a/sequencer.c +++ b/sequencer.c @@ -602,6 +602,12 @@ N_("you have staged changes in your working tree\n" "\n" " git rebase --continue\n"); +#define ALLOW_EMPTY (1<<0) +#define EDIT_MSG (1<<1) +#define AMEND_MSG (1<<2) +#define CLEANUP_MSG (1<<3) +#define VERIFY_MSG (1<<4) + /* * If we are cherry-pick, and if the merge did not result in * hand-editing, we will hit this commit and inherit the original @@ -615,8 +621,7 @@ N_("you have staged changes in your working tree\n" * author metadata. */ static int run_git_commit(const char *defmsg, struct replay_opts *opts, - int allow_empty, int edit, int amend, - int cleanup_commit_message) + unsigned int flags) { struct child_process cmd = CHILD_PROCESS_INIT; const char *value; @@ -624,7 +629,7 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts, cmd.git_cmd = 1; if (is_rebase_i(opts)) { - if (!edit) { + if (!(flags & EDIT_MSG)) { cmd.stdout_to_stderr = 1; cmd.err = -1; } @@ -638,9 +643,10 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts, } argv_array_push(&cmd.args, "commit"); - argv_array_push(&cmd.args, "-n"); - if (amend) + if (!(flags & VERIFY_MSG)) + argv_array_push(&cmd.args, "-n"); + if ((flags & AMEND_MSG)) argv_array_push(&cmd.args, "--amend"); if (opts->gpg_sign) argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign); @@ -648,16 +654,16 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts, argv_array_push(&cmd.args, "-s"); if (defmsg) argv_array_pushl(&cmd.args, "-F", defmsg, NULL); - if (cleanup_commit_message) + if ((flags & CLEANUP_MSG)) argv_array_push(&cmd.args, "--cleanup=strip"); - if (edit) + if ((flags & EDIT_MSG)) argv_array_push(&cmd.args, "-e"); - else if (!cleanup_commit_message && + else if (!(flags & CLEANUP_MSG) && !opts->signoff && !opts->record_origin && git_config_get_value("commit.cleanup", &value)) argv_array_push(&cmd.args, "--cleanup=verbatim"); - if (allow_empty) + if ((flags & ALLOW_EMPTY)) argv_array_push(&cmd.args, "--allow-empty"); if (opts->allow_empty_message) @@ -926,14 +932,14 @@ static void record_in_rewritten(struct object_id *oid, static int do_pick_commit(enum todo_command command, struct commit *commit, struct replay_opts *opts, int final_fixup) { - int edit = opts->edit, cleanup_commit_message = 0; - const char *msg_file = edit ? NULL : git_path_merge_msg(); + unsigned int flags = opts->edit ? EDIT_MSG : 0; + const char *msg_file = opts->edit ? NULL : git_path_merge_msg(); unsigned char head[20]; struct commit *base, *next, *parent; const char *base_label, *next_label; struct commit_message msg = { NULL, NULL, NULL, NULL }; struct strbuf msgbuf = STRBUF_INIT; - int res, unborn = 0, amend = 0, allow = 0; + int res, unborn = 0, allow; if (opts->no_commit) { /* @@ -991,7 +997,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, opts); if (res || command != TODO_REWORD) goto leave; - edit = amend = 1; + flags |= EDIT_MSG | AMEND_MSG; + if (command == TODO_REWORD) + flags |= VERIFY_MSG; msg_file = NULL; goto fast_forward_edit; } @@ -1046,15 +1054,15 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, } if (command == TODO_REWORD) - edit = 1; + flags |= EDIT_MSG | VERIFY_MSG; else if (is_fixup(command)) { if (update_squash_messages(command, commit, opts)) return -1; - amend = 1; + flags |= AMEND_MSG; if (!final_fixup) msg_file = rebase_path_squash_msg(); else if (file_exists(rebase_path_fixup_msg())) { - cleanup_commit_message = 1; + flags |= CLEANUP_MSG; msg_file = rebase_path_fixup_msg(); } else { const char *dest = git_path("SQUASH_MSG"); @@ -1064,7 +1072,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, rebase_path_squash_msg(), dest); unlink(git_path("MERGE_MSG")); msg_file = dest; - edit = 1; + flags |= EDIT_MSG; } } @@ -1123,11 +1131,11 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, if (allow < 0) { res = allow; goto leave; - } + } else if (allow) + flags |= ALLOW_EMPTY; if (!opts->no_commit) fast_forward_edit: - res = run_git_commit(msg_file, opts, allow, edit, amend, - cleanup_commit_message); + res = run_git_commit(msg_file, opts, flags); if (!res && final_fixup) { unlink(rebase_path_fixup_msg()); @@ -2154,7 +2162,7 @@ static int continue_single_pick(void) static int commit_staged_changes(struct replay_opts *opts) { - int amend = 0; + unsigned int flags = ALLOW_EMPTY | EDIT_MSG; if (has_unstaged_changes(1)) return error(_("cannot rebase: You have unstaged changes.")); @@ -2184,10 +2192,10 @@ static int commit_staged_changes(struct replay_opts *opts) "--continue' again.")); strbuf_release(&rev); - amend = 1; + flags |= AMEND_MSG; } - if (run_git_commit(rebase_path_message(), opts, 1, 1, amend, 0)) + if (run_git_commit(rebase_path_message(), opts, flags)) return error(_("could not commit staged changes.")); unlink(rebase_path_amend()); return 0; @@ -987,7 +987,7 @@ const char *setup_git_directory_gently(int *nongit_ok) { static struct strbuf cwd = STRBUF_INIT; struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT; - const char *prefix; + const char *prefix, *env_prefix; /* * We may have read an incomplete configuration before @@ -1045,6 +1045,10 @@ const char *setup_git_directory_gently(int *nongit_ok) die("BUG: unhandled setup_git_directory_1() result"); } + env_prefix = getenv(GIT_TOPLEVEL_PREFIX_ENVIRONMENT); + if (env_prefix) + prefix = env_prefix; + if (prefix) setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1); else diff --git a/sha1_name.c b/sha1_name.c index cda9e49b12..d9d1b2fce8 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -549,7 +549,7 @@ static inline int at_mark(const char *string, int len, for (i = 0; i < nr; i++) { int suffix_len = strlen(suffix[i]); if (suffix_len <= len - && !memcmp(string, suffix[i], suffix_len)) + && !strncasecmp(string, suffix[i], suffix_len)) return suffix_len; } return 0; @@ -449,6 +449,17 @@ int strbuf_getcwd(struct strbuf *sb) strbuf_setlen(sb, strlen(sb->buf)); return 0; } + + /* + * If getcwd(3) is implemented as a syscall that falls + * back to a regular lookup using readdir(3) etc. then + * we may be able to avoid EACCES by providing enough + * space to the syscall as it's not necessarily bound + * to the same restrictions as the fallback. + */ + if (errno == EACCES && guessed_len < PATH_MAX) + continue; + if (errno != ERANGE) break; } diff --git a/submodule.c b/submodule.c index c9f39ebbe5..83b13b8ff5 100644 --- a/submodule.c +++ b/submodule.c @@ -213,25 +213,59 @@ void gitmodules_config_sha1(const unsigned char *commit_sha1) } /* + * NEEDSWORK: With the addition of different configuration options to determine + * if a submodule is of interests, the validity of this function's name comes + * into question. Once the dust has settled and more concrete terminology is + * decided upon, come up with a more proper name for this function. One + * potential candidate could be 'is_submodule_active()'. + * * Determine if a submodule has been initialized at a given 'path' */ int is_submodule_initialized(const char *path) { int ret = 0; - const struct submodule *module = NULL; + char *key = NULL; + char *value = NULL; + const struct string_list *sl; + const struct submodule *module = submodule_from_path(null_sha1, path); - module = submodule_from_path(null_sha1, path); + /* early return if there isn't a path->module mapping */ + if (!module) + return 0; - if (module) { - char *key = xstrfmt("submodule.%s.url", module->name); - char *value = NULL; + /* submodule.<name>.active is set */ + key = xstrfmt("submodule.%s.active", module->name); + if (!git_config_get_bool(key, &ret)) { + free(key); + return ret; + } + free(key); - ret = !git_config_get_string(key, &value); + /* submodule.active is set */ + sl = git_config_get_value_multi("submodule.active"); + if (sl) { + struct pathspec ps; + struct argv_array args = ARGV_ARRAY_INIT; + const struct string_list_item *item; - free(value); - free(key); + for_each_string_list_item(item, sl) { + argv_array_push(&args, item->string); + } + + parse_pathspec(&ps, 0, 0, NULL, args.argv); + ret = match_pathspec(&ps, path, strlen(path), 0, NULL, 1); + + argv_array_clear(&args); + clear_pathspec(&ps); + return ret; } + /* fallback to checking if the URL is set */ + key = xstrfmt("submodule.%s.url", module->name); + ret = !git_config_get_string(key, &value); + + free(value); + free(key); return ret; } @@ -471,13 +471,13 @@ Don't: their output. You can glean some further possible issues from the TAP grammar - (see http://search.cpan.org/perldoc?TAP::Parser::Grammar#TAP_Grammar) + (see https://metacpan.org/pod/TAP::Parser::Grammar#TAP-GRAMMAR) but the best indication is to just run the tests with prove(1), it'll complain if anything is amiss. Keep in mind: - - Inside <script> part, the standard output and standard error + - Inside the <script> part, the standard output and standard error streams are discarded, and the test harness only reports "ok" or "not ok" to the end user running the tests. Under --verbose, they are shown to help debugging the tests. @@ -611,9 +611,11 @@ library for your script to use. - test_have_prereq <prereq> - Check if we have a prerequisite previously set with - test_set_prereq. The most common use of this directly is to skip - all the tests if we don't have some essential prerequisite: + Check if we have a prerequisite previously set with test_set_prereq. + The most common way to use this explicitly (as opposed to the + implicit use when an argument is passed to test_expect_*) is to skip + all the tests at the start of the test script if we don't have some + essential prerequisite: if ! test_have_prereq PERL then diff --git a/t/t0001-init.sh b/t/t0001-init.sh index e424de5363..c4814d248f 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -315,6 +315,20 @@ test_expect_success 'init with separate gitdir' ' test_path_is_dir realgitdir/refs ' +test_expect_success 'init in long base path' ' + # exceed initial buffer size of strbuf_getcwd() + component=123456789abcdef && + test_when_finished "chmod 0700 $component; rm -rf $component" && + p31=$component/$component && + p127=$p31/$p31/$p31/$p31 && + mkdir -p $p127 && + chmod 0111 $component && + ( + cd $p127 && + git init newdir + ) +' + test_expect_success 're-init on .git file' ' ( cd newdir && git init ) ' diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh index 46ef1f22dc..b23c4e3fab 100755 --- a/t/t1507-rev-parse-upstream.sh +++ b/t/t1507-rev-parse-upstream.sh @@ -46,11 +46,14 @@ error_message () { } test_expect_success '@{upstream} resolves to correct full name' ' - test refs/remotes/origin/master = "$(full_name @{upstream})" + test refs/remotes/origin/master = "$(full_name @{upstream})" && + test refs/remotes/origin/master = "$(full_name @{UPSTREAM})" && + test refs/remotes/origin/master = "$(full_name @{UpSTReam})" ' test_expect_success '@{u} resolves to correct full name' ' - test refs/remotes/origin/master = "$(full_name @{u})" + test refs/remotes/origin/master = "$(full_name @{u})" && + test refs/remotes/origin/master = "$(full_name @{U})" ' test_expect_success 'my-side@{upstream} resolves to correct full name' ' @@ -60,6 +63,8 @@ test_expect_success 'my-side@{upstream} resolves to correct full name' ' test_expect_success 'upstream of branch with @ in middle' ' full_name fun@ny@{u} >actual && echo refs/remotes/origin/side >expect && + test_cmp expect actual && + full_name fun@ny@{U} >actual && test_cmp expect actual ' @@ -96,12 +101,14 @@ test_expect_success 'not-tracking@{u} fails' ' test_expect_success '<branch>@{u}@{1} resolves correctly' ' test_commit 6 && (cd clone && git fetch) && - test 5 = $(commit_subject my-side@{u}@{1}) + test 5 = $(commit_subject my-side@{u}@{1}) && + test 5 = $(commit_subject my-side@{U}@{1}) ' test_expect_success '@{u} without specifying branch fails on a detached HEAD' ' git checkout HEAD^0 && - test_must_fail git rev-parse @{u} + test_must_fail git rev-parse @{u} && + test_must_fail git rev-parse @{U} ' test_expect_success 'checkout -b new my-side@{u} forks from the same' ' diff --git a/t/t1514-rev-parse-push.sh b/t/t1514-rev-parse-push.sh index 623a32aa6e..788cc91e45 100755 --- a/t/t1514-rev-parse-push.sh +++ b/t/t1514-rev-parse-push.sh @@ -24,12 +24,16 @@ test_expect_success 'setup' ' test_expect_success '@{push} with default=nothing' ' test_config push.default nothing && - test_must_fail git rev-parse master@{push} + test_must_fail git rev-parse master@{push} && + test_must_fail git rev-parse master@{PUSH} && + test_must_fail git rev-parse master@{PuSH} ' test_expect_success '@{push} with default=simple' ' test_config push.default simple && - resolve master@{push} refs/remotes/origin/master + resolve master@{push} refs/remotes/origin/master && + resolve master@{PUSH} refs/remotes/origin/master && + resolve master@{pUSh} refs/remotes/origin/master ' test_expect_success 'triangular @{push} fails with default=simple' ' diff --git a/t/t3007-ls-files-recurse-submodules.sh b/t/t3007-ls-files-recurse-submodules.sh index a5426171d3..4cf6ccf5a8 100755 --- a/t/t3007-ls-files-recurse-submodules.sh +++ b/t/t3007-ls-files-recurse-submodules.sh @@ -188,6 +188,45 @@ test_expect_success '--recurse-submodules and pathspecs' ' test_cmp expect actual ' +test_expect_success '--recurse-submodules and relative paths' ' + # From subdir + cat >expect <<-\EOF && + b + EOF + git -C b ls-files --recurse-submodules >actual && + test_cmp expect actual && + + # Relative path to top + cat >expect <<-\EOF && + ../.gitmodules + ../a + b + ../h.txt + ../sib/file + ../sub/file + ../submodule/.gitmodules + ../submodule/c + ../submodule/f.TXT + ../submodule/g.txt + ../submodule/subsub/d + ../submodule/subsub/e.txt + EOF + git -C b ls-files --recurse-submodules -- .. >actual && + test_cmp expect actual && + + # Relative path to submodule + cat >expect <<-\EOF && + ../submodule/.gitmodules + ../submodule/c + ../submodule/f.TXT + ../submodule/g.txt + ../submodule/subsub/d + ../submodule/subsub/e.txt + EOF + git -C b ls-files --recurse-submodules -- ../submodule >actual && + test_cmp expect actual +' + test_expect_success '--recurse-submodules does not support --error-unmatch' ' test_must_fail git ls-files --recurse-submodules --error-unmatch 2>actual && test_i18ngrep "does not support --error-unmatch" actual diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 9f353c0efc..fe62e7c775 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -978,6 +978,10 @@ test_expect_success '--merged catches invalid object names' ' test_must_fail git branch --merged 0000000000000000000000000000000000000000 ' +test_expect_success '--merged is incompatible with --no-merged' ' + test_must_fail git branch --merged HEAD --no-merged HEAD +' + test_expect_success 'tracking with unexpected .fetch refspec' ' rm -rf a b c d && git init a && diff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh index 7f3ec47241..0ef1b6fdcc 100755 --- a/t/t3201-branch-contains.sh +++ b/t/t3201-branch-contains.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='branch --contains <commit>, --merged, and --no-merged' +test_description='branch --contains <commit>, --no-contains <commit> --merged, and --no-merged' . ./test-lib.sh @@ -45,6 +45,22 @@ test_expect_success 'branch --contains master' ' ' +test_expect_success 'branch --no-contains=master' ' + + git branch --no-contains=master >actual && + >expect && + test_cmp expect actual + +' + +test_expect_success 'branch --no-contains master' ' + + git branch --no-contains master >actual && + >expect && + test_cmp expect actual + +' + test_expect_success 'branch --contains=side' ' git branch --contains=side >actual && @@ -55,6 +71,16 @@ test_expect_success 'branch --contains=side' ' ' +test_expect_success 'branch --no-contains=side' ' + + git branch --no-contains=side >actual && + { + echo " master" + } >expect && + test_cmp expect actual + +' + test_expect_success 'branch --contains with pattern implies --list' ' git branch --contains=master master >actual && @@ -65,6 +91,14 @@ test_expect_success 'branch --contains with pattern implies --list' ' ' +test_expect_success 'branch --no-contains with pattern implies --list' ' + + git branch --no-contains=master master >actual && + >expect && + test_cmp expect actual + +' + test_expect_success 'side: branch --merged' ' git branch --merged >actual && @@ -126,8 +160,20 @@ test_expect_success 'branch --no-merged with pattern implies --list' ' test_expect_success 'implicit --list conflicts with modification options' ' test_must_fail git branch --contains=master -d && - test_must_fail git branch --contains=master -m foo + test_must_fail git branch --contains=master -m foo && + test_must_fail git branch --no-contains=master -d && + test_must_fail git branch --no-contains=master -m foo + +' +test_expect_success 'Assert that --contains only works on commits, not trees & blobs' ' + test_must_fail git branch --contains master^{tree} && + blob=$(git hash-object -w --stdin <<-\EOF + Some blob + EOF + ) && + test_must_fail git branch --contains $blob && + test_must_fail git branch --no-contains $blob ' # We want to set up a case where the walk for the tracking info @@ -159,4 +205,15 @@ test_expect_success 'branch --merged with --verbose' ' test_i18ncmp expect actual ' +test_expect_success 'branch --contains combined with --no-contains' ' + git branch --contains zzz --no-contains topic >actual && + cat >expect <<-\EOF && + master + side + zzz + EOF + test_cmp expect actual + +' + test_done diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 48b55bfd27..f577990716 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -4,6 +4,7 @@ test_description='git log' . ./test-lib.sh . "$TEST_DIRECTORY/lib-gpg.sh" +. "$TEST_DIRECTORY/lib-terminal.sh" test_expect_success setup ' @@ -520,7 +521,7 @@ test_expect_success 'log --graph with merge' ' ' test_expect_success 'log.decorate configuration' ' - git log --oneline >expect.none && + git log --oneline --no-decorate >expect.none && git log --oneline --decorate >expect.short && git log --oneline --decorate=full >expect.full && @@ -576,6 +577,13 @@ test_expect_success 'log.decorate configuration' ' ' +test_expect_success TTY 'log output on a TTY' ' + git log --oneline --decorate >expect.short && + + test_terminal git log --oneline >actual && + test_cmp expect.short actual +' + test_expect_success 'reflog is expected format' ' git log -g --abbrev-commit --pretty=oneline >expect && git reflog >actual && diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh index a09a1a46ef..fc067ed672 100755 --- a/t/t6302-for-each-ref-filter.sh +++ b/t/t6302-for-each-ref-filter.sh @@ -93,6 +93,22 @@ test_expect_success 'filtering with --contains' ' test_cmp expect actual ' +test_expect_success 'filtering with --no-contains' ' + cat >expect <<-\EOF && + refs/tags/one + EOF + git for-each-ref --format="%(refname)" --no-contains=two >actual && + test_cmp expect actual +' + +test_expect_success 'filtering with --contains and --no-contains' ' + cat >expect <<-\EOF && + refs/tags/two + EOF + git for-each-ref --format="%(refname)" --contains=two --no-contains=three >actual && + test_cmp expect actual +' + test_expect_success '%(color) must fail' ' test_must_fail git for-each-ref --format="%(color)%(refname)" ' @@ -421,4 +437,8 @@ test_expect_success 'check %(if:notequals=<string>)' ' test_cmp expect actual ' +test_expect_success '--merged is incompatible with --no-merged' ' + test_must_fail git for-each-ref --merged HEAD --no-merged HEAD +' + test_done diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 772dc9ed96..bb2e4d704d 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -16,7 +16,6 @@ tag_exists () { git show-ref --quiet --verify refs/tags/"$1" } -# todo: git tag -l now returns always zero, when fixed, change this test test_expect_success 'listing all tags in an empty tree should succeed' ' git tag -l && git tag @@ -119,6 +118,18 @@ test_expect_success 'listing all tags if one exists should succeed' ' git tag ' +cat >expect <<EOF +mytag +EOF +test_expect_success 'Multiple -l or --list options are equivalent to one -l option' ' + git tag -l -l >actual && + test_cmp expect actual && + git tag --list --list >actual && + test_cmp expect actual && + git tag --list -l --list >actual && + test_cmp expect actual +' + test_expect_success 'listing all tags if one exists should output that tag' ' test $(git tag -l) = mytag && test $(git tag) = mytag @@ -136,9 +147,8 @@ test_expect_success \ 'listing a tag using a matching pattern should output that tag' \ 'test $(git tag -l mytag) = mytag' -# todo: git tag -l now returns always zero, when fixed, change this test test_expect_success \ - 'listing tags using a non-matching pattern should suceed' \ + 'listing tags using a non-matching pattern should succeed' \ 'git tag -l xxx' test_expect_success \ @@ -338,6 +348,19 @@ test_expect_success 'tag -l can accept multiple patterns' ' test_cmp expect actual ' +# Between v1.7.7 & v2.13.0 a fair reading of the git-tag documentation +# could leave you with the impression that "-l <pattern> -l <pattern>" +# was how we wanted to accept multiple patterns. +# +# This test should not imply that this is a sane thing to support. but +# since the documentation was worded like it was let's at least find +# out if we're going to break this long-documented form of taking +# multiple patterns. +test_expect_success 'tag -l <pattern> -l <pattern> works, as our buggy documentation previously suggested' ' + git tag -l "v1*" -l "v0*" >actual && + test_cmp expect actual +' + test_expect_success 'listing tags in column' ' COLUMNS=40 git tag -l --column=row >actual && cat >expected <<\EOF && @@ -620,6 +643,11 @@ test_expect_success \ git tag -n0 -l tag-one-line >actual && test_cmp expect actual && + git tag -n0 | grep "^tag-one-line" >actual && + test_cmp expect actual && + git tag -n0 tag-one-line >actual && + test_cmp expect actual && + echo "tag-one-line A msg" >expect && git tag -n1 -l | grep "^tag-one-line" >actual && test_cmp expect actual && @@ -633,6 +661,17 @@ test_expect_success \ test_cmp expect actual ' +test_expect_success 'The -n 100 invocation means -n --list 100, not -n100' ' + >expect && + git tag -n 100 >actual && + test_cmp expect actual && + + git tag -m "A msg" 100 && + echo "100 A msg" >expect && + git tag -n 100 >actual && + test_cmp expect actual +' + test_expect_success \ 'listing the zero-lines message of a non-signed tag should succeed' ' git tag -m "" tag-zero-lines && @@ -1383,6 +1422,23 @@ test_expect_success 'checking that first commit is in all tags (relative)' " test_cmp expected actual " +# All the --contains tests above, but with --no-contains +test_expect_success 'checking that first commit is not listed in any tag with --no-contains (hash)' " + >expected && + git tag -l --no-contains $hash1 v* >actual && + test_cmp expected actual +" + +test_expect_success 'checking that first commit is in all tags (tag)' " + git tag -l --no-contains v1.0 v* >actual && + test_cmp expected actual +" + +test_expect_success 'checking that first commit is in all tags (relative)' " + git tag -l --no-contains HEAD~2 v* >actual && + test_cmp expected actual +" + cat > expected <<EOF v2.0 EOF @@ -1392,6 +1448,17 @@ test_expect_success 'checking that second commit only has one tag' " test_cmp expected actual " +cat > expected <<EOF +v0.2.1 +v1.0 +v1.0.1 +v1.1.3 +EOF + +test_expect_success 'inverse of the last test, with --no-contains' " + git tag -l --no-contains $hash2 v* >actual && + test_cmp expected actual +" cat > expected <<EOF EOF @@ -1401,6 +1468,19 @@ test_expect_success 'checking that third commit has no tags' " test_cmp expected actual " +cat > expected <<EOF +v0.2.1 +v1.0 +v1.0.1 +v1.1.3 +v2.0 +EOF + +test_expect_success 'conversely --no-contains on the third commit lists all tags' " + git tag -l --no-contains $hash3 v* >actual && + test_cmp expected actual +" + # how about a simple merge? test_expect_success 'creating simple branch' ' @@ -1422,6 +1502,19 @@ test_expect_success 'checking that branch head only has one tag' " test_cmp expected actual " +cat > expected <<EOF +v0.2.1 +v1.0 +v1.0.1 +v1.1.3 +v2.0 +EOF + +test_expect_success 'checking that branch head with --no-contains lists all but one tag' " + git tag -l --no-contains $hash4 v* >actual && + test_cmp expected actual +" + test_expect_success 'merging original branch into this branch' ' git merge --strategy=ours master && git tag v4.0 @@ -1443,6 +1536,20 @@ v1.0.1 v1.1.3 v2.0 v3.0 +EOF + +test_expect_success 'checking that original branch head with --no-contains lists all but one tag now' " + git tag -l --no-contains $hash3 v* >actual && + test_cmp expected actual +" + +cat > expected <<EOF +v0.2.1 +v1.0 +v1.0.1 +v1.1.3 +v2.0 +v3.0 v4.0 EOF @@ -1451,21 +1558,76 @@ test_expect_success 'checking that initial commit is in all tags' " test_cmp expected actual " +test_expect_success 'checking that --contains can be used in non-list mode' ' + git tag --contains $hash1 v* >actual && + test_cmp expected actual +' + +test_expect_success 'checking that initial commit is in all tags with --no-contains' " + >expected && + git tag -l --no-contains $hash1 v* >actual && + test_cmp expected actual +" + # mixing modes and options: test_expect_success 'mixing incompatibles modes and options is forbidden' ' test_must_fail git tag -a && + test_must_fail git tag -a -l && + test_must_fail git tag -s && + test_must_fail git tag -s -l && + test_must_fail git tag -m && + test_must_fail git tag -m -l && + test_must_fail git tag -m "hlagh" && + test_must_fail git tag -m "hlagh" -l && + test_must_fail git tag -F && + test_must_fail git tag -F -l && + test_must_fail git tag -f && + test_must_fail git tag -f -l && + test_must_fail git tag -a -s -m -F && + test_must_fail git tag -a -s -m -F -l && test_must_fail git tag -l -v && - test_must_fail git tag -n 100 && + test_must_fail git tag -l -d && + test_must_fail git tag -l -v -d && + test_must_fail git tag -n 100 -v && test_must_fail git tag -l -m msg && test_must_fail git tag -l -F some file && - test_must_fail git tag -v -s -' + test_must_fail git tag -v -s && + test_must_fail git tag --contains tag-tree && + test_must_fail git tag --contains tag-blob && + test_must_fail git tag --no-contains tag-tree && + test_must_fail git tag --no-contains tag-blob && + test_must_fail git tag --contains --no-contains && + test_must_fail git tag --no-with HEAD && + test_must_fail git tag --no-without HEAD +' + +for option in --contains --with --no-contains --without --merged --no-merged --points-at +do + test_expect_success "mixing incompatible modes with $option is forbidden" " + test_must_fail git tag -d $option HEAD && + test_must_fail git tag -d $option HEAD some-tag && + test_must_fail git tag -v $option HEAD + " + test_expect_success "Doing 'git tag --list-like $option <commit> <pattern> is permitted" " + git tag -n $option HEAD HEAD && + git tag $option HEAD HEAD && + git tag $option + " +done # check points-at -test_expect_success '--points-at cannot be used in non-list mode' ' - test_must_fail git tag --points-at=v4.0 foo +test_expect_success '--points-at can be used in non-list mode' ' + echo v4.0 >expect && + git tag --points-at=v4.0 "v*" >actual && + test_cmp expect actual +' + +test_expect_success '--points-at is a synonym for --points-at HEAD' ' + echo v4.0 >expect && + git tag --points-at >actual && + test_cmp expect actual ' test_expect_success '--points-at finds lightweight tags' ' @@ -1707,7 +1869,7 @@ run_with_limited_stack () { test_lazy_prereq ULIMIT_STACK_SIZE 'run_with_limited_stack true' # we require ulimit, this excludes Windows -test_expect_success ULIMIT_STACK_SIZE '--contains works in a deep repo' ' +test_expect_success ULIMIT_STACK_SIZE '--contains and --no-contains work in a deep repo' ' >expect && i=1 && while test $i -lt 8000 @@ -1723,7 +1885,9 @@ EOF" git checkout master && git tag far-far-away HEAD^ && run_with_limited_stack git tag --contains HEAD >actual && - test_cmp expect actual + test_cmp expect actual && + run_with_limited_stack git tag --no-contains HEAD >actual && + test_line_count ">" 10 actual ' test_expect_success '--format should list tags as per format given' ' @@ -1742,8 +1906,17 @@ test_expect_success 'setup --merged test tags' ' git tag mergetest-3 HEAD ' -test_expect_success '--merged cannot be used in non-list mode' ' - test_must_fail git tag --merged=mergetest-2 foo +test_expect_success '--merged can be used in non-list mode' ' + cat >expect <<-\EOF && + mergetest-1 + mergetest-2 + EOF + git tag --merged=mergetest-2 "mergetest*" >actual && + test_cmp expect actual +' + +test_expect_success '--merged is incompatible with --no-merged' ' + test_must_fail git tag --merged HEAD --no-merged HEAD ' test_expect_success '--merged shows merged tags' ' @@ -1763,6 +1936,11 @@ test_expect_success '--no-merged show unmerged tags' ' test_cmp expect actual ' +test_expect_success '--no-merged can be used in non-list mode' ' + git tag --no-merged=mergetest-2 mergetest-* >actual && + test_cmp expect actual +' + test_expect_success 'ambiguous branch/tags not marked' ' git tag ambiguous && git branch ambiguous && @@ -1771,4 +1949,47 @@ test_expect_success 'ambiguous branch/tags not marked' ' test_cmp expect actual ' +test_expect_success '--contains combined with --no-contains' ' + ( + git init no-contains && + cd no-contains && + test_commit v0.1 && + test_commit v0.2 && + test_commit v0.3 && + test_commit v0.4 && + test_commit v0.5 && + cat >expected <<-\EOF && + v0.2 + v0.3 + v0.4 + EOF + git tag --contains v0.2 --no-contains v0.5 >actual && + test_cmp expected actual + ) +' + +# As the docs say, list tags which contain a specified *commit*. We +# don't recurse down to tags for trees or blobs pointed to by *those* +# commits. +test_expect_success 'Does --[no-]contains stop at commits? Yes!' ' + cd no-contains && + blob=$(git rev-parse v0.3:v0.3.t) && + tree=$(git rev-parse v0.3^{tree}) && + git tag tag-blob $blob && + git tag tag-tree $tree && + git tag --contains v0.3 >actual && + cat >expected <<-\EOF && + v0.3 + v0.4 + v0.5 + EOF + test_cmp expected actual && + git tag --no-contains v0.3 >actual && + cat >expected <<-\EOF && + v0.1 + v0.2 + EOF + test_cmp expected actual +' + test_done diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index c09ce0d4c1..cf77a3a357 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -1130,5 +1130,141 @@ test_expect_success 'submodule helper list is not confused by common prefixes' ' test_cmp expect actual ' +test_expect_success 'setup superproject with submodules' ' + git init sub1 && + test_commit -C sub1 test && + test_commit -C sub1 test2 && + git init multisuper && + git -C multisuper submodule add ../sub1 sub0 && + git -C multisuper submodule add ../sub1 sub1 && + git -C multisuper submodule add ../sub1 sub2 && + git -C multisuper submodule add ../sub1 sub3 && + git -C multisuper commit -m "add some submodules" +' + +cat >expect <<-EOF +-sub0 + sub1 (test2) + sub2 (test2) + sub3 (test2) +EOF + +test_expect_success 'submodule update --init with a specification' ' + test_when_finished "rm -rf multisuper_clone" && + pwd=$(pwd) && + git clone file://"$pwd"/multisuper multisuper_clone && + git -C multisuper_clone submodule update --init . ":(exclude)sub0" && + git -C multisuper_clone submodule status |cut -c 1,43- >actual && + test_cmp expect actual +' + +test_expect_success 'submodule update --init with submodule.active set' ' + test_when_finished "rm -rf multisuper_clone" && + pwd=$(pwd) && + git clone file://"$pwd"/multisuper multisuper_clone && + git -C multisuper_clone config submodule.active "." && + git -C multisuper_clone config --add submodule.active ":(exclude)sub0" && + git -C multisuper_clone submodule update --init && + git -C multisuper_clone submodule status |cut -c 1,43- >actual && + test_cmp expect actual +' + +test_expect_success 'submodule update and setting submodule.<name>.active' ' + test_when_finished "rm -rf multisuper_clone" && + pwd=$(pwd) && + git clone file://"$pwd"/multisuper multisuper_clone && + git -C multisuper_clone config --bool submodule.sub0.active "true" && + git -C multisuper_clone config --bool submodule.sub1.active "false" && + git -C multisuper_clone config --bool submodule.sub2.active "true" && + + cat >expect <<-\EOF && + sub0 (test2) + -sub1 + sub2 (test2) + -sub3 + EOF + git -C multisuper_clone submodule update && + git -C multisuper_clone submodule status |cut -c 1,43- >actual && + test_cmp expect actual +' + +test_expect_success 'clone --recurse-submodules with a pathspec works' ' + test_when_finished "rm -rf multisuper_clone" && + cat >expected <<-\EOF && + sub0 (test2) + -sub1 + -sub2 + -sub3 + EOF + + git clone --recurse-submodules="sub0" multisuper multisuper_clone && + git -C multisuper_clone submodule status |cut -c1,43- >actual && + test_cmp actual expected +' + +test_expect_success 'clone with multiple --recurse-submodules options' ' + test_when_finished "rm -rf multisuper_clone" && + cat >expect <<-\EOF && + -sub0 + sub1 (test2) + -sub2 + sub3 (test2) + EOF + + git clone --recurse-submodules="." \ + --recurse-submodules=":(exclude)sub0" \ + --recurse-submodules=":(exclude)sub2" \ + multisuper multisuper_clone && + git -C multisuper_clone submodule status |cut -c1,43- >actual && + test_cmp expect actual +' + +test_expect_success 'clone and subsequent updates correctly auto-initialize submodules' ' + test_when_finished "rm -rf multisuper_clone" && + cat <<-\EOF >expect && + -sub0 + sub1 (test2) + -sub2 + sub3 (test2) + EOF + + cat <<-\EOF >expect2 && + -sub0 + sub1 (test2) + -sub2 + sub3 (test2) + -sub4 + sub5 (test2) + EOF + + git clone --recurse-submodules="." \ + --recurse-submodules=":(exclude)sub0" \ + --recurse-submodules=":(exclude)sub2" \ + --recurse-submodules=":(exclude)sub4" \ + multisuper multisuper_clone && + + git -C multisuper_clone submodule status |cut -c1,43- >actual && + test_cmp expect actual && + + git -C multisuper submodule add ../sub1 sub4 && + git -C multisuper submodule add ../sub1 sub5 && + git -C multisuper commit -m "add more submodules" && + # obtain the new superproject + git -C multisuper_clone pull && + git -C multisuper_clone submodule update --init && + git -C multisuper_clone submodule status |cut -c1,43- >actual && + test_cmp expect2 actual +' + +test_expect_success 'init properly sets the config' ' + test_when_finished "rm -rf multisuper_clone" && + git clone --recurse-submodules="." \ + --recurse-submodules=":(exclude)sub0" \ + multisuper multisuper_clone && + + git -C multisuper_clone submodule init -- sub0 sub1 && + git -C multisuper_clone config --get submodule.sub0.active && + test_must_fail git -C multisuper_clone config --get submodule.sub1.active +' test_done diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh new file mode 100755 index 0000000000..9c785b07ec --- /dev/null +++ b/t/t7413-submodule-is-active.sh @@ -0,0 +1,107 @@ +#!/bin/sh + +test_description='Test submodule--helper is-active + +This test verifies that `git submodue--helper is-active` correclty identifies +submodules which are "active" and interesting to the user. +' + +. ./test-lib.sh + +test_expect_success 'setup' ' + git init sub && + test_commit -C sub initial && + git init super && + test_commit -C super initial && + git -C super submodule add ../sub sub1 && + git -C super submodule add ../sub sub2 && + + # Remove submodule.<name>.active entries in order to test in an + # environment where only URLs are present in the conifg + git -C super config --unset submodule.sub1.active && + git -C super config --unset submodule.sub2.active && + + git -C super commit -a -m "add 2 submodules at sub{1,2}" +' + +test_expect_success 'is-active works with urls' ' + git -C super submodule--helper is-active sub1 && + git -C super submodule--helper is-active sub2 && + + git -C super config --unset submodule.sub1.URL && + test_must_fail git -C super submodule--helper is-active sub1 && + git -C super config submodule.sub1.URL ../sub && + git -C super submodule--helper is-active sub1 +' + +test_expect_success 'is-active works with submodule.<name>.active config' ' + test_when_finished "git -C super config --unset submodule.sub1.active" && + test_when_finished "git -C super config submodule.sub1.URL ../sub" && + + git -C super config --bool submodule.sub1.active "false" && + test_must_fail git -C super submodule--helper is-active sub1 && + + git -C super config --bool submodule.sub1.active "true" && + git -C super config --unset submodule.sub1.URL && + git -C super submodule--helper is-active sub1 +' + +test_expect_success 'is-active works with basic submodule.active config' ' + test_when_finished "git -C super config submodule.sub1.URL ../sub" && + test_when_finished "git -C super config --unset-all submodule.active" && + + git -C super config --add submodule.active "." && + git -C super config --unset submodule.sub1.URL && + + git -C super submodule--helper is-active sub1 && + git -C super submodule--helper is-active sub2 +' + +test_expect_success 'is-active correctly works with paths that are not submodules' ' + test_when_finished "git -C super config --unset-all submodule.active" && + + test_must_fail git -C super submodule--helper is-active not-a-submodule && + + git -C super config --add submodule.active "." && + test_must_fail git -C super submodule--helper is-active not-a-submodule +' + +test_expect_success 'is-active works with exclusions in submodule.active config' ' + test_when_finished "git -C super config --unset-all submodule.active" && + + git -C super config --add submodule.active "." && + git -C super config --add submodule.active ":(exclude)sub1" && + + test_must_fail git -C super submodule--helper is-active sub1 && + git -C super submodule--helper is-active sub2 +' + +test_expect_success 'is-active with submodule.active and submodule.<name>.active' ' + test_when_finished "git -C super config --unset-all submodule.active" && + test_when_finished "git -C super config --unset submodule.sub1.active" && + test_when_finished "git -C super config --unset submodule.sub2.active" && + + git -C super config --add submodule.active "sub1" && + git -C super config --bool submodule.sub1.active "false" && + git -C super config --bool submodule.sub2.active "true" && + + test_must_fail git -C super submodule--helper is-active sub1 && + git -C super submodule--helper is-active sub2 +' + +test_expect_success 'is-active, submodule.active and submodule add' ' + test_when_finished "rm -rf super2" && + git init super2 && + test_commit -C super2 initial && + git -C super2 config --add submodule.active "sub*" && + + # submodule add should only add submodule.<name>.active + # to the config if not matched by the pathspec + git -C super2 submodule add ../sub sub1 && + test_must_fail git -C super2 config --get submodule.sub1.active && + + git -C super2 submodule add ../sub mod && + git -C super2 config --get submodule.mod.active +' + +test_done diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh index 8728db61d3..88d4cda299 100755 --- a/t/t7504-commit-msg-hook.sh +++ b/t/t7504-commit-msg-hook.sh @@ -220,4 +220,21 @@ test_expect_success "hook doesn't edit commit message (editor)" ' ' +# set up fake editor to replace `pick` by `reword` +cat > reword-editor <<'EOF' +#!/bin/sh +mv "$1" "$1".bup && +sed 's/^pick/reword/' <"$1".bup >"$1" +EOF +chmod +x reword-editor +REWORD_EDITOR="$(pwd)/reword-editor" +export REWORD_EDITOR + +test_expect_success 'hook is called for reword during `rebase -i`' ' + + GIT_SEQUENCE_EDITOR="\"$REWORD_EDITOR\"" git rebase -i HEAD^ && + commit_msg_is "new message" + +' + test_done diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh index 67247a01d6..5b6eb3a65e 100755 --- a/t/t7814-grep-recurse-submodules.sh +++ b/t/t7814-grep-recurse-submodules.sh @@ -227,6 +227,81 @@ test_expect_success 'grep history with moved submoules' ' test_cmp expect actual ' +test_expect_success 'grep using relative path' ' + test_when_finished "rm -rf parent sub" && + git init sub && + echo "foobar" >sub/file && + git -C sub add file && + git -C sub commit -m "add file" && + + git init parent && + echo "foobar" >parent/file && + git -C parent add file && + mkdir parent/src && + echo "foobar" >parent/src/file2 && + git -C parent add src/file2 && + git -C parent submodule add ../sub && + git -C parent commit -m "add files and submodule" && + + # From top works + cat >expect <<-\EOF && + file:foobar + src/file2:foobar + sub/file:foobar + EOF + git -C parent grep --recurse-submodules -e "foobar" >actual && + test_cmp expect actual && + + # Relative path to top + cat >expect <<-\EOF && + ../file:foobar + file2:foobar + ../sub/file:foobar + EOF + git -C parent/src grep --recurse-submodules -e "foobar" -- .. >actual && + test_cmp expect actual && + + # Relative path to submodule + cat >expect <<-\EOF && + ../sub/file:foobar + EOF + git -C parent/src grep --recurse-submodules -e "foobar" -- ../sub >actual && + test_cmp expect actual +' + +test_expect_success 'grep from a subdir' ' + test_when_finished "rm -rf parent sub" && + git init sub && + echo "foobar" >sub/file && + git -C sub add file && + git -C sub commit -m "add file" && + + git init parent && + mkdir parent/src && + echo "foobar" >parent/src/file && + git -C parent add src/file && + git -C parent submodule add ../sub src/sub && + git -C parent submodule add ../sub sub && + git -C parent commit -m "add files and submodules" && + + # Verify grep from root works + cat >expect <<-\EOF && + src/file:foobar + src/sub/file:foobar + sub/file:foobar + EOF + git -C parent grep --recurse-submodules -e "foobar" >actual && + test_cmp expect actual && + + # Verify grep from a subdir works + cat >expect <<-\EOF && + file:foobar + sub/file:foobar + EOF + git -C parent/src grep --recurse-submodules -e "foobar" >actual && + test_cmp expect actual +' + test_incompatible_with_recurse_submodules () { test_expect_success "--recurse-submodules and $1 are incompatible" " diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index d711bef240..5ed28135be 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -400,6 +400,22 @@ test_expect_success '__gitdir - remote as argument' ' test_cmp expected "$actual" ' +test_expect_success '__gitcomp_direct - puts everything into COMPREPLY as-is' ' + sed -e "s/Z$//g" >expected <<-EOF && + with-trailing-space Z + without-trailing-spaceZ + --option Z + --option=Z + $invalid_variable_name Z + EOF + ( + cur=should_be_ignored && + __gitcomp_direct "$(cat expected)" && + print_comp + ) && + test_cmp expected out +' + test_expect_success '__gitcomp - trailing space - options' ' test_gitcomp "--re" "--dry-run --reuse-message= --reedit-message= --reset-author" <<-EOF @@ -555,6 +571,9 @@ test_expect_success '__git_refs - full refs' ' cat >expected <<-EOF && refs/heads/master refs/heads/matching-branch + refs/remotes/other/branch-in-other + refs/remotes/other/master-in-other + refs/tags/matching-tag EOF ( cur=refs/heads/ && @@ -620,6 +639,7 @@ test_expect_success '__git_refs - configured remote' ' test_expect_success '__git_refs - configured remote - full refs' ' cat >expected <<-EOF && + HEAD refs/heads/branch-in-other refs/heads/master-in-other refs/tags/tag-in-other @@ -648,6 +668,7 @@ test_expect_success '__git_refs - configured remote - repo given on the command test_expect_success '__git_refs - configured remote - full refs - repo given on the command line' ' cat >expected <<-EOF && + HEAD refs/heads/branch-in-other refs/heads/master-in-other refs/tags/tag-in-other @@ -692,6 +713,7 @@ test_expect_success '__git_refs - URL remote' ' test_expect_success '__git_refs - URL remote - full refs' ' cat >expected <<-EOF && + HEAD refs/heads/branch-in-other refs/heads/master-in-other refs/tags/tag-in-other @@ -775,6 +797,371 @@ test_expect_success '__git_refs - unique remote branches for git checkout DWIMer test_cmp expected "$actual" ' +test_expect_success '__git_refs - after --opt=' ' + cat >expected <<-EOF && + HEAD + master + matching-branch + other/branch-in-other + other/master-in-other + matching-tag + EOF + ( + cur="--opt=" && + __git_refs "" "" "" "" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - after --opt= - full refs' ' + cat >expected <<-EOF && + refs/heads/master + refs/heads/matching-branch + refs/remotes/other/branch-in-other + refs/remotes/other/master-in-other + refs/tags/matching-tag + EOF + ( + cur="--opt=refs/" && + __git_refs "" "" "" refs/ >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git refs - exluding refs' ' + cat >expected <<-EOF && + ^HEAD + ^master + ^matching-branch + ^other/branch-in-other + ^other/master-in-other + ^matching-tag + EOF + ( + cur=^ && + __git_refs >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git refs - exluding full refs' ' + cat >expected <<-EOF && + ^refs/heads/master + ^refs/heads/matching-branch + ^refs/remotes/other/branch-in-other + ^refs/remotes/other/master-in-other + ^refs/tags/matching-tag + EOF + ( + cur=^refs/ && + __git_refs >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'setup for filtering matching refs' ' + git branch matching/branch && + git tag matching/tag && + git -C otherrepo branch matching/branch-in-other && + git fetch --no-tags other && + rm -f .git/FETCH_HEAD +' + +test_expect_success '__git_refs - dont filter refs unless told so' ' + cat >expected <<-EOF && + HEAD + master + matching-branch + matching/branch + other/branch-in-other + other/master-in-other + other/matching/branch-in-other + matching-tag + matching/tag + EOF + ( + cur=master && + __git_refs >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - only matching refs' ' + cat >expected <<-EOF && + matching-branch + matching/branch + matching-tag + matching/tag + EOF + ( + cur=mat && + __git_refs "" "" "" "$cur" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - only matching refs - full refs' ' + cat >expected <<-EOF && + refs/heads/matching-branch + refs/heads/matching/branch + EOF + ( + cur=refs/heads/mat && + __git_refs "" "" "" "$cur" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - only matching refs - remote on local file system' ' + cat >expected <<-EOF && + master-in-other + matching/branch-in-other + EOF + ( + cur=ma && + __git_refs otherrepo "" "" "$cur" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - only matching refs - configured remote' ' + cat >expected <<-EOF && + master-in-other + matching/branch-in-other + EOF + ( + cur=ma && + __git_refs other "" "" "$cur" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - only matching refs - remote - full refs' ' + cat >expected <<-EOF && + refs/heads/master-in-other + refs/heads/matching/branch-in-other + EOF + ( + cur=refs/heads/ma && + __git_refs other "" "" "$cur" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - only matching refs - checkout DWIMery' ' + cat >expected <<-EOF && + matching-branch + matching/branch + matching-tag + matching/tag + matching/branch-in-other + EOF + for remote_ref in refs/remotes/other/ambiguous \ + refs/remotes/remote/ambiguous \ + refs/remotes/remote/branch-in-remote + do + git update-ref $remote_ref master && + test_when_finished "git update-ref -d $remote_ref" + done && + ( + cur=mat && + __git_refs "" 1 "" "$cur" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'teardown after filtering matching refs' ' + git branch -d matching/branch && + git tag -d matching/tag && + git update-ref -d refs/remotes/other/matching/branch-in-other && + git -C otherrepo branch -D matching/branch-in-other +' + +test_expect_success '__git_refs - for-each-ref format specifiers in prefix' ' + cat >expected <<-EOF && + evil-%%-%42-%(refname)..master + EOF + ( + cur="evil-%%-%42-%(refname)..mas" && + __git_refs "" "" "evil-%%-%42-%(refname).." mas >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_complete_refs - simple' ' + sed -e "s/Z$//" >expected <<-EOF && + HEAD Z + master Z + matching-branch Z + other/branch-in-other Z + other/master-in-other Z + matching-tag Z + EOF + ( + cur= && + __git_complete_refs && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_refs - matching' ' + sed -e "s/Z$//" >expected <<-EOF && + matching-branch Z + matching-tag Z + EOF + ( + cur=mat && + __git_complete_refs && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_refs - remote' ' + sed -e "s/Z$//" >expected <<-EOF && + HEAD Z + branch-in-other Z + master-in-other Z + EOF + ( + cur= + __git_complete_refs --remote=other && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_refs - track' ' + sed -e "s/Z$//" >expected <<-EOF && + HEAD Z + master Z + matching-branch Z + other/branch-in-other Z + other/master-in-other Z + matching-tag Z + branch-in-other Z + master-in-other Z + EOF + ( + cur= + __git_complete_refs --track && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_refs - current word' ' + sed -e "s/Z$//" >expected <<-EOF && + matching-branch Z + matching-tag Z + EOF + ( + cur="--option=mat" && + __git_complete_refs --cur="${cur#*=}" && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_refs - prefix' ' + sed -e "s/Z$//" >expected <<-EOF && + v1.0..matching-branch Z + v1.0..matching-tag Z + EOF + ( + cur=v1.0..mat && + __git_complete_refs --pfx=v1.0.. --cur=mat && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_refs - suffix' ' + cat >expected <<-EOF && + HEAD. + master. + matching-branch. + other/branch-in-other. + other/master-in-other. + matching-tag. + EOF + ( + cur= && + __git_complete_refs --sfx=. && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_fetch_refspecs - simple' ' + sed -e "s/Z$//" >expected <<-EOF && + HEAD:HEAD Z + branch-in-other:branch-in-other Z + master-in-other:master-in-other Z + EOF + ( + cur= && + __git_complete_fetch_refspecs other && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_fetch_refspecs - matching' ' + sed -e "s/Z$//" >expected <<-EOF && + branch-in-other:branch-in-other Z + EOF + ( + cur=br && + __git_complete_fetch_refspecs other "" br && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_fetch_refspecs - prefix' ' + sed -e "s/Z$//" >expected <<-EOF && + +HEAD:HEAD Z + +branch-in-other:branch-in-other Z + +master-in-other:master-in-other Z + EOF + ( + cur="+" && + __git_complete_fetch_refspecs other "+" "" && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_fetch_refspecs - fully qualified' ' + sed -e "s/Z$//" >expected <<-EOF && + refs/heads/branch-in-other:refs/heads/branch-in-other Z + refs/heads/master-in-other:refs/heads/master-in-other Z + refs/tags/tag-in-other:refs/tags/tag-in-other Z + EOF + ( + cur=refs/ && + __git_complete_fetch_refspecs other "" refs/ && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_fetch_refspecs - fully qualified & prefix' ' + sed -e "s/Z$//" >expected <<-EOF && + +refs/heads/branch-in-other:refs/heads/branch-in-other Z + +refs/heads/master-in-other:refs/heads/master-in-other Z + +refs/tags/tag-in-other:refs/tags/tag-in-other Z + EOF + ( + cur=+refs/ && + __git_complete_fetch_refspecs other + refs/ && + print_comp + ) && + test_cmp expected out +' + test_expect_success 'teardown after ref completion' ' git branch -d matching-branch && git tag -d matching-tag && |