diff options
44 files changed, 605 insertions, 229 deletions
diff --git a/Documentation/RelNotes/2.30.1.txt b/Documentation/RelNotes/2.30.1.txt new file mode 100644 index 0000000000..3760bd17f7 --- /dev/null +++ b/Documentation/RelNotes/2.30.1.txt @@ -0,0 +1,47 @@ +Git v2.30.1 Release Notes +========================= + +This release is primarily to merge fixes accumulated on the 'master' +front to prepare for 2.31 release that are still relevant to 2.30.x +maintenance track. + +Fixes since v2.30 +----------------- + + * "git fetch --recurse-submodules" failed to update a submodule + when it has an uninitialized (hence of no interest to the user) + sub-submodule, which has been corrected. + + * Command line error of "git rebase" are diagnosed earlier. + + * "git stash" did not work well in a sparsely checked out working + tree. + + * Some tests expect that "ls -l" output has either '-' or 'x' for + group executable bit, but setgid bit can be inherited from parent + directory and make these fields 'S' or 's' instead, causing test + failures. + + * "git for-each-repo --config=<var> <cmd>" should not run <cmd> for + any repository when the configuration variable <var> is not defined + even once. + + * "git mergetool --tool-help" was broken in 2.29 and failed to list + all the available tools. + + * Fix for procedure to building CI test environment for mac. + + * Newline characters in the host and path part of git:// URL are + now forbidden. + + * When more than one commit with the same patch ID appears on one + side, "git log --cherry-pick A...B" did not exclude them all when a + commit with the same patch ID appears on the other side. Now it + does. + + * Documentation for "git fsck" lost stale bits that has become + incorrect. + + * Doc for packfile URI feature has been clarified. + +Also contains minor documentation updates and code clean-ups. diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index d12094bac5..0452db2e67 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -307,7 +307,7 @@ wrote the patch or have the right to pass it on under the same license as ours, by "signing off" your patch. Without sign-off, we cannot accept your patches. -If you can certify the below D-C-O: +If (and only if) you certify the below D-C-O: [[dco]] .Developer's Certificate of Origin 1.1 diff --git a/Documentation/config/http.txt b/Documentation/config/http.txt index 3968fbb697..7003661c0d 100644 --- a/Documentation/config/http.txt +++ b/Documentation/config/http.txt @@ -42,12 +42,12 @@ http.proxySSLKey:: http.proxySSLCertPasswordProtected:: Enable Git's password prompt for the proxy SSL certificate. Otherwise OpenSSL will prompt the user, possibly many times, if the certificate or private key - is encrypted. Can be overriden by the `GIT_PROXY_SSL_CERT_PASSWORD_PROTECTED` + is encrypted. Can be overridden by the `GIT_PROXY_SSL_CERT_PASSWORD_PROTECTED` environment variable. http.proxySSLCAInfo:: Pathname to the file containing the certificate bundle that should be used to - verify the proxy with when using an HTTPS proxy. Can be overriden by the + verify the proxy with when using an HTTPS proxy. Can be overridden by the `GIT_PROXY_SSL_CAINFO` environment variable. http.emptyAuth:: diff --git a/Documentation/config/trace2.txt b/Documentation/config/trace2.txt index 01d3afd8a8..fe1642f0d4 100644 --- a/Documentation/config/trace2.txt +++ b/Documentation/config/trace2.txt @@ -54,7 +54,7 @@ trace2.envVars:: `GIT_HTTP_USER_AGENT,GIT_CONFIG` would cause the trace2 output to contain events listing the overrides for HTTP user agent and the location of the Git configuration file (assuming any are set). May be - overriden by the `GIT_TRACE2_ENV_VARS` environment variable. Unset by + overridden by the `GIT_TRACE2_ENV_VARS` environment variable. Unset by default. trace2.destinationDebug:: diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 876aedcd47..02d9c19cec 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -57,6 +57,10 @@ repository is specified as a URL, then this flag is ignored (and we never use the local optimizations). Specifying `--no-local` will override the default when `/path/to/repo` is given, using the regular Git transport instead. ++ +*NOTE*: this operation can race with concurrent modification to the +source repository, similar to running `cp -r src dst` while modifying +`src`. --no-hardlinks:: Force the cloning process from a repository on a local diff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt index d72d15be5b..bd596619c0 100644 --- a/Documentation/git-fsck.txt +++ b/Documentation/git-fsck.txt @@ -129,14 +129,6 @@ using 'git commit-graph verify'. See linkgit:git-commit-graph[1]. Extracted Diagnostics --------------------- -expect dangling commits - potential heads - due to lack of head information:: - You haven't specified any nodes as heads so it won't be - possible to differentiate between un-parented commits and - root nodes. - -missing sha1 directory '<dir>':: - The directory holding the sha1 objects is missing. - unreachable <type> <object>:: The <type> object <object>, isn't actually referred to directly or indirectly in any of the trees or commits seen. This can diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index cbcf5263dd..0a3b5265b3 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -23,9 +23,8 @@ SYNOPSIS DESCRIPTION ----------- -This merges the file listing in the directory cache index with the -actual working directory list, and shows different combinations of the -two. +This merges the file listing in the index with the actual working +directory list, and shows different combinations of the two. One or more of the options below may be used to determine the files shown: diff --git a/Documentation/git-maintenance.txt b/Documentation/git-maintenance.txt index d1f9b5172d..6fe1e5e105 100644 --- a/Documentation/git-maintenance.txt +++ b/Documentation/git-maintenance.txt @@ -38,7 +38,7 @@ register:: for running in the background without disrupting foreground processes. + -The `register` subcomand will also set the `maintenance.strategy` config +The `register` subcommand will also set the `maintenance.strategy` config value to `incremental`, if this value is not previously set. The `incremental` strategy uses the following schedule for each maintenance task: diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt index ec233ac0c1..f89e68b424 100644 --- a/Documentation/git-p4.txt +++ b/Documentation/git-p4.txt @@ -397,7 +397,7 @@ changelist text. Exiting with a non-zero status from the script will abort the process. The purpose of the hook is to edit the message file in place, -and it is not supressed by the `--no-verify` option. This hook +and it is not suppressed by the `--no-verify` option. This hook is called even if `--prepare-p4-only` is set. p4-changelist diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index b7bbbeadef..93708aefea 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -494,10 +494,14 @@ edit ~/.gitconfig to specify your account settings: smtpServerPort = 587 ---- -If you have multifactor authentication setup on your gmail account, you will +If you have multi-factor authentication set up on your Gmail account, you will need to generate an app-specific password for use with 'git send-email'. Visit https://security.google.com/settings/security/apppasswords to create it. +If you do not have multi-factor authentication set up on your Gmail account, +you will need to allow less secure app access. Visit +https://myaccount.google.com/lesssecureapps to enable it. + Once your commits are ready to be sent to the mailing list, run the following commands: diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt index 7731b45f07..c0764e850a 100644 --- a/Documentation/git-status.txt +++ b/Documentation/git-status.txt @@ -184,11 +184,26 @@ characters, that field will be quoted in the manner of a C string literal: surrounded by ASCII double quote (34) characters, and with interior special characters backslash-escaped. -For paths with merge conflicts, `X` and `Y` show the modification -states of each side of the merge. For paths that do not have merge -conflicts, `X` shows the status of the index, and `Y` shows the status -of the work tree. For untracked paths, `XY` are `??`. Other status -codes can be interpreted as follows: +There are three different types of states that are shown using this format, and +each one uses the `XY` syntax differently: + +* When a merge is occurring and the merge was successful, or outside of a merge + situation, `X` shows the status of the index and `Y` shows the status of the + working tree. +* When a merge conflict has occurred and has not yet been resolved, `X` and `Y` + show the state introduced by each head of the merge, relative to the common + ancestor. These paths are said to be _unmerged_. +* When a path is untracked, `X` and `Y` are always the same, since they are + unknown to the index. `??` is used for untracked paths. Ignored files are + not listed unless `--ignored` is used; if it is, ignored files are indicated + by `!!`. + +Note that the term _merge_ here also includes rebases using the default +`--merge` strategy, cherry-picks, and anything else using the merge machinery. + +In the following table, these three classes are shown in separate sections, and +these characters are used for `X` and `Y` fields for the first two sections that +show tracked paths: * ' ' = unmodified * 'M' = modified @@ -198,9 +213,6 @@ codes can be interpreted as follows: * 'C' = copied * 'U' = updated but unmerged -Ignored files are not listed, unless `--ignored` option is in effect, -in which case `XY` are `!!`. - .... X Y Meaning ------------------------------------------------- diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index 1489cb09a0..2853f168d9 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -30,9 +30,8 @@ SYNOPSIS DESCRIPTION ----------- -Modifies the index or directory cache. Each file mentioned is updated -into the index and any 'unmerged' or 'needs updating' state is -cleared. +Modifies the index. Each file mentioned is updated into the index and +any 'unmerged' or 'needs updating' state is cleared. See also linkgit:git-add[1] for a more user-friendly way to do some of the most common operations on the index. diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index ffccfc7760..1f3b57d04d 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -644,7 +644,7 @@ changelist text. Exiting with a non-zero status from the script will abort the process. The purpose of the hook is to edit the message file in place, -and it is not supressed by the `--no-verify` option. This hook +and it is not suppressed by the `--no-verify` option. This hook is called even if `--prepare-p4-only` is set. Run `git-p4 submit --help` for details. diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt index 539b4e1997..8e333dde1b 100644 --- a/Documentation/gitmodules.txt +++ b/Documentation/gitmodules.txt @@ -7,7 +7,7 @@ gitmodules - Defining submodule properties SYNOPSIS -------- -$GIT_WORK_DIR/.gitmodules +$GIT_WORK_TREE/.gitmodules DESCRIPTION @@ -27,19 +27,19 @@ submodule.<name>.path:: Defines the path, relative to the top-level directory of the Git working tree, where the submodule is expected to be checked out. The path name must not end with a `/`. All submodule paths must - be unique within the .gitmodules file. + be unique within the `.gitmodules` file. submodule.<name>.url:: Defines a URL from which the submodule repository can be cloned. This may be either an absolute URL ready to be passed to - linkgit:git-clone[1] or (if it begins with ./ or ../) a location + linkgit:git-clone[1] or (if it begins with `./` or `../`) a location relative to the superproject's origin repository. In addition, there are a number of optional keys: submodule.<name>.update:: Defines the default update procedure for the named submodule, - i.e. how the submodule is updated by "git submodule update" + i.e. how the submodule is updated by the `git submodule update` command in the superproject. This is only used by `git submodule init` to initialize the configuration variable of the same name. Allowed values here are 'checkout', 'rebase', @@ -49,7 +49,7 @@ submodule.<name>.update:: submodule.<name>.branch:: A remote branch name for tracking updates in the upstream submodule. - If the option is not specified, it defaults to the remote 'HEAD'. + If the option is not specified, it defaults to the remote `HEAD`. A special value of `.` is used to indicate that the name of the branch in the submodule should be the same name as the current branch in the current repository. See the `--remote` documentation in @@ -57,14 +57,14 @@ submodule.<name>.branch:: submodule.<name>.fetchRecurseSubmodules:: This option can be used to control recursive fetching of this - submodule. If this option is also present in the submodules entry in - .git/config of the superproject, the setting there will override the - one found in .gitmodules. + submodule. If this option is also present in the submodule's entry in + `.git/config` of the superproject, the setting there will override the + one found in `.gitmodules`. Both settings can be overridden on the command line by using the - "--[no-]recurse-submodules" option to "git fetch" and "git pull". + `--[no-]recurse-submodules` option to `git fetch` and `git pull`. submodule.<name>.ignore:: - Defines under what circumstances "git status" and the diff family show + Defines under what circumstances `git status` and the diff family show a submodule as modified. The following values are supported: + -- @@ -73,7 +73,7 @@ submodule.<name>.ignore:: been staged). dirty;; All changes to the submodule's work tree will be ignored, only - committed differences between the HEAD of the submodule and its + committed differences between the `HEAD` of the submodule and its recorded state in the superproject are taken into account. untracked;; Only untracked files in submodules will be ignored. @@ -84,12 +84,12 @@ submodule.<name>.ignore:: differences, and modifications to tracked and untracked files are shown. This is the default option. -If this option is also present in the submodules entry in .git/config +If this option is also present in the submodule's entry in `.git/config` of the superproject, the setting there will override the one found in -.gitmodules. +`.gitmodules`. Both settings can be overridden on the command line by using the -"--ignore-submodules" option. The 'git submodule' commands are not +`--ignore-submodules` option. The `git submodule` commands are not affected by this setting. -- @@ -102,7 +102,7 @@ submodule.<name>.shallow:: EXAMPLES -------- -Consider the following .gitmodules file: +Consider the following `.gitmodules` file: ---- [submodule "libfoo"] diff --git a/Documentation/technical/packfile-uri.txt b/Documentation/technical/packfile-uri.txt index 318713abc3..f7eabc6c76 100644 --- a/Documentation/technical/packfile-uri.txt +++ b/Documentation/technical/packfile-uri.txt @@ -37,8 +37,11 @@ at least so that we can test the client. This is the implementation: a feature, marked experimental, that allows the server to be configured by one or more `uploadpack.blobPackfileUri=<sha1> <uri>` entries. Whenever the list of objects to be sent is assembled, all such -blobs are excluded, replaced with URIs. The client will download those URIs, -expecting them to each point to packfiles containing single blobs. +blobs are excluded, replaced with URIs. As noted in "Future work" below, the +server can evolve in the future to support excluding other objects (or other +implementations of servers could be made that support excluding other objects) +without needing a protocol change, so clients should not expect that packfiles +downloaded in this way only contain single blobs. Client design ------------- diff --git a/Documentation/technical/reftable.txt b/Documentation/technical/reftable.txt index 2951840e9c..8095ab2590 100644 --- a/Documentation/technical/reftable.txt +++ b/Documentation/technical/reftable.txt @@ -446,7 +446,7 @@ especially if readers will not use the object name to ref mapping. Object blocks use unique, abbreviated 2-32 object name keys, mapping to ref blocks containing references pointing to that object directly, or as the peeled value of an annotated tag. Like ref blocks, object blocks use -the file's standard block size. The abbrevation length is available in +the file's standard block size. The abbreviation length is available in the footer as `obj_id_len`. To save space in small files, object blocks may be omitted if the ref diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 5085ef1390..224ae5b61f 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.30.0 +DEF_VER=v2.30.1pre LF=' ' @@ -777,20 +777,6 @@ BUILT_INS += git-status$X BUILT_INS += git-switch$X BUILT_INS += git-whatchanged$X -# what 'all' will build and 'install' will install in gitexecdir, -# excluding programs for built-in commands -ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) -ALL_COMMANDS_TO_INSTALL = $(ALL_PROGRAMS) -ifeq (,$(SKIP_DASHED_BUILT_INS)) -ALL_COMMANDS_TO_INSTALL += $(BUILT_INS) -else -# git-upload-pack, git-receive-pack and git-upload-archive are special: they -# are _expected_ to be present in the `bin/` directory in their dashed form. -ALL_COMMANDS_TO_INSTALL += git-receive-pack$(X) -ALL_COMMANDS_TO_INSTALL += git-upload-archive$(X) -ALL_COMMANDS_TO_INSTALL += git-upload-pack$(X) -endif - # what 'all' will build but not install in gitexecdir OTHER_PROGRAMS = git$X @@ -1226,6 +1212,20 @@ ifdef DEVELOPER include config.mak.dev endif +# what 'all' will build and 'install' will install in gitexecdir, +# excluding programs for built-in commands +ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) +ALL_COMMANDS_TO_INSTALL = $(ALL_PROGRAMS) +ifeq (,$(SKIP_DASHED_BUILT_INS)) +ALL_COMMANDS_TO_INSTALL += $(BUILT_INS) +else +# git-upload-pack, git-receive-pack and git-upload-archive are special: they +# are _expected_ to be present in the `bin/` directory in their dashed form. +ALL_COMMANDS_TO_INSTALL += git-receive-pack$(X) +ALL_COMMANDS_TO_INSTALL += git-upload-archive$(X) +ALL_COMMANDS_TO_INSTALL += git-upload-pack$(X) +endif + ALL_CFLAGS = $(DEVELOPER_CFLAGS) $(CPPFLAGS) $(CFLAGS) ALL_LDFLAGS = $(LDFLAGS) @@ -1554,9 +1554,6 @@ endif ifdef NO_SYMLINK_HEAD BASIC_CFLAGS += -DNO_SYMLINK_HEAD endif -ifdef GETTEXT_POISON -$(warning The GETTEXT_POISON option has been removed in favor of runtime GIT_TEST_GETTEXT_POISON. See t/README!) -endif ifdef NO_GETTEXT BASIC_CFLAGS += -DNO_GETTEXT USE_GETTEXT_SCHEME ?= fallthrough @@ -1 +1 @@ -Documentation/RelNotes/2.30.0.txt
\ No newline at end of file +Documentation/RelNotes/2.30.1.txt
\ No newline at end of file diff --git a/builtin/for-each-repo.c b/builtin/for-each-repo.c index 5bba623ff1..52be64a437 100644 --- a/builtin/for-each-repo.c +++ b/builtin/for-each-repo.c @@ -51,6 +51,13 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix) values = repo_config_get_value_multi(the_repository, config_key); + /* + * Do nothing on an empty list, which is equivalent to the case + * where the config variable does not exist at all. + */ + if (!values) + return 0; + for (i = 0; !result && i < values->nr; i++) result = run_command_on_repo(values->items[i].string, &args); diff --git a/builtin/rebase.c b/builtin/rebase.c index 19c7b377aa..840dbd7eb7 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -1917,7 +1917,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) die_if_checked_out(buf.buf, 1); options.head_name = xstrdup(buf.buf); /* If not is it a valid ref (branch or commit)? */ - } else if (!get_oid(branch_name, &options.orig_head)) + } else if (!get_oid(branch_name, &options.orig_head) && + lookup_commit_reference(the_repository, + &options.orig_head)) options.head_name = NULL; else die(_("fatal: no such branch/commit '%s'"), diff --git a/builtin/stash.c b/builtin/stash.c index e1f8235fdd..9bc85f91cd 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -325,35 +325,6 @@ static void add_diff_to_buf(struct diff_queue_struct *q, } } -static int get_newly_staged(struct strbuf *out, struct object_id *c_tree) -{ - struct child_process cp = CHILD_PROCESS_INIT; - const char *c_tree_hex = oid_to_hex(c_tree); - - /* - * diff-index is very similar to diff-tree above, and should be - * converted together with update_index. - */ - cp.git_cmd = 1; - strvec_pushl(&cp.args, "diff-index", "--cached", "--name-only", - "--diff-filter=A", NULL); - strvec_push(&cp.args, c_tree_hex); - return pipe_command(&cp, NULL, 0, out, 0, NULL, 0); -} - -static int update_index(struct strbuf *out) -{ - struct child_process cp = CHILD_PROCESS_INIT; - - /* - * Update-index is very complicated and may need to have a public - * function exposed in order to remove this forking. - */ - cp.git_cmd = 1; - strvec_pushl(&cp.args, "update-index", "--add", "--stdin", NULL); - return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0); -} - static int restore_untracked(struct object_id *u_tree) { int res; @@ -385,6 +356,121 @@ static int restore_untracked(struct object_id *u_tree) return res; } +static void unstage_changes_unless_new(struct object_id *orig_tree) +{ + /* + * When we enter this function, there has been a clean merge of + * relevant trees, and the merge logic always stages whatever merges + * cleanly. We want to unstage those changes, unless it corresponds + * to a file that didn't exist as of orig_tree. + * + * However, if any SKIP_WORKTREE path is modified relative to + * orig_tree, then we want to clear the SKIP_WORKTREE bit and write + * it to the worktree before unstaging. + */ + + struct checkout state = CHECKOUT_INIT; + struct diff_options diff_opts; + struct lock_file lock = LOCK_INIT; + int i; + + /* If any entries have skip_worktree set, we'll have to check 'em out */ + state.force = 1; + state.quiet = 1; + state.refresh_cache = 1; + state.istate = &the_index; + + /* + * Step 1: get a difference between orig_tree (which corresponding + * to the index before a merge was run) and the current index + * (reflecting the changes brought in by the merge). + */ + diff_setup(&diff_opts); + diff_opts.flags.recursive = 1; + diff_opts.detect_rename = 0; + diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; + diff_setup_done(&diff_opts); + + do_diff_cache(orig_tree, &diff_opts); + diffcore_std(&diff_opts); + + /* Iterate over the paths that changed due to the merge... */ + for (i = 0; i < diff_queued_diff.nr; i++) { + struct diff_filepair *p; + struct cache_entry *ce; + int pos; + + /* Look up the path's position in the current index. */ + p = diff_queued_diff.queue[i]; + pos = index_name_pos(&the_index, p->two->path, + strlen(p->two->path)); + + /* + * Step 2: Place changes in the working tree + * + * Stash is about restoring changes *to the working tree*. + * So if the merge successfully got a new version of some + * path, but left it out of the working tree, then clear the + * SKIP_WORKTREE bit and write it to the working tree. + */ + if (pos >= 0 && ce_skip_worktree(active_cache[pos])) { + struct stat st; + + ce = active_cache[pos]; + if (!lstat(ce->name, &st)) { + /* Conflicting path present; relocate it */ + struct strbuf new_path = STRBUF_INIT; + int fd; + + strbuf_addf(&new_path, + "%s.stash.XXXXXX", ce->name); + fd = xmkstemp(new_path.buf); + close(fd); + printf(_("WARNING: Untracked file in way of " + "tracked file! Renaming\n " + " %s -> %s\n" + " to make room.\n"), + ce->name, new_path.buf); + if (rename(ce->name, new_path.buf)) + die("Failed to move %s to %s\n", + ce->name, new_path.buf); + strbuf_release(&new_path); + } + checkout_entry(ce, &state, NULL, NULL); + ce->ce_flags &= ~CE_SKIP_WORKTREE; + } + + /* + * Step 3: "unstage" changes, as long as they are still tracked + */ + if (p->one->oid_valid) { + /* + * Path existed in orig_tree; restore index entry + * from that tree in order to "unstage" the changes. + */ + int option = ADD_CACHE_OK_TO_REPLACE; + if (pos < 0) + option = ADD_CACHE_OK_TO_ADD; + + ce = make_cache_entry(&the_index, + p->one->mode, + &p->one->oid, + p->one->path, + 0, 0); + add_index_entry(&the_index, ce, option); + } + } + diff_flush(&diff_opts); + + /* + * Step 4: write the new index to disk + */ + repo_hold_locked_index(the_repository, &lock, LOCK_DIE_ON_ERROR); + if (write_locked_index(&the_index, &lock, + COMMIT_LOCK | SKIP_IF_UNCHANGED)) + die(_("Unable to write index.")); +} + static int do_apply_stash(const char *prefix, struct stash_info *info, int index, int quiet) { @@ -467,26 +553,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info, if (reset_tree(&index_tree, 0, 0)) return -1; } else { - struct strbuf out = STRBUF_INIT; - - if (get_newly_staged(&out, &c_tree)) { - strbuf_release(&out); - return -1; - } - - if (reset_tree(&c_tree, 0, 1)) { - strbuf_release(&out); - return -1; - } - - ret = update_index(&out); - strbuf_release(&out); - if (ret) - return -1; - - /* read back the result of update_index() back from the disk */ - discard_cache(); - read_cache(); + unstage_changes_unless_new(&c_tree); } if (!quiet) { diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh index 0229a77f7d..0b1184e04a 100755 --- a/ci/install-dependencies.sh +++ b/ci/install-dependencies.sh @@ -44,13 +44,13 @@ osx-clang|osx-gcc) test -z "$BREW_INSTALL_PACKAGES" || brew install $BREW_INSTALL_PACKAGES brew link --force gettext - brew cask install --no-quarantine perforce || { + brew install --cask --no-quarantine perforce || { # Update the definitions and try again cask_repo="$(brew --repository)"/Library/Taps/homebrew/homebrew-cask && - git -C "$cask_repo" pull --no-stat && - brew cask install --no-quarantine perforce + git -C "$cask_repo" pull --no-stat --ff-only && + brew install --cask --no-quarantine perforce } || - brew install caskroom/cask/perforce + brew install homebrew/cask/perforce case "$jobname" in osx-gcc) brew install gcc@9 @@ -1160,6 +1160,8 @@ static struct child_process *git_connect_git(int fd[2], char *hostandport, target_host = xstrdup(hostandport); transport_check_allowed("git"); + if (strchr(target_host, '\n') || strchr(path, '\n')) + die(_("newline is forbidden in git:// hosts and repo paths")); /* * These underlying connection commands die() if they @@ -1082,7 +1082,7 @@ static int check_submodule_url(const char *url) if (looks_like_command_line_option(url)) return -1; - if (submodule_url_is_relative(url)) { + if (submodule_url_is_relative(url) || starts_with(url, "git://")) { char *decoded; const char *next; int has_nl; @@ -87,88 +87,24 @@ static int test_vsnprintf(const char *fmt, ...) static void init_gettext_charset(const char *domain) { - /* - This trick arranges for messages to be emitted in the user's - requested encoding, but avoids setting LC_CTYPE from the - environment for the whole program. - - This primarily done to avoid a bug in vsnprintf in the GNU C - Library [1]. which triggered a "your vsnprintf is broken" error - on Git's own repository when inspecting v0.99.6~1 under a UTF-8 - locale. - - That commit contains a ISO-8859-1 encoded author name, which - the locale aware vsnprintf(3) won't interpolate in the format - argument, due to mismatch between the data encoding and the - locale. - - Even if it wasn't for that bug we wouldn't want to use LC_CTYPE at - this point, because it'd require auditing all the code that uses C - functions whose semantics are modified by LC_CTYPE. - - But only setting LC_MESSAGES as we do creates a problem, since - we declare the encoding of our PO files[2] the gettext - implementation will try to recode it to the user's locale, but - without LC_CTYPE it'll emit something like this on 'git init' - under the Icelandic locale: - - Bj? til t?ma Git lind ? /hlagh/.git/ - - Gettext knows about the encoding of our PO file, but we haven't - told it about the user's encoding, so all the non-US-ASCII - characters get encoded to question marks. - - But we're in luck! We can set LC_CTYPE from the environment - only while we call nl_langinfo and - bind_textdomain_codeset. That suffices to tell gettext what - encoding it should emit in, so it'll now say: - - Bjó til tóma Git lind í /hlagh/.git/ - - And the equivalent ISO-8859-1 string will be emitted under a - ISO-8859-1 locale. - - With this change way we get the advantages of setting LC_CTYPE - (talk to the user in his language/encoding), without the major - drawbacks (changed semantics for C functions we rely on). - - However foreign functions using other message catalogs that - aren't using our neat trick will still have a problem, e.g. if - we have to call perror(3): - - #include <stdio.h> - #include <locale.h> - #include <errno.h> - - int main(void) - { - setlocale(LC_MESSAGES, ""); - setlocale(LC_CTYPE, "C"); - errno = ENODEV; - perror("test"); - return 0; - } - - Running that will give you a message with question marks: - - $ LANGUAGE= LANG=de_DE.utf8 ./test - test: Kein passendes Ger?t gefunden - - The vsnprintf bug has been fixed since glibc 2.17. - - Then we could simply set LC_CTYPE from the environment, which would - make things like the external perror(3) messages work. - - See t/t0203-gettext-setlocale-sanity.sh's "gettext.c" tests for - regression tests. - - 1. http://sourceware.org/bugzilla/show_bug.cgi?id=6530 - 2. E.g. "Content-Type: text/plain; charset=UTF-8\n" in po/is.po - */ setlocale(LC_CTYPE, ""); charset = locale_charset(); bind_textdomain_codeset(domain, charset); - /* the string is taken from v0.99.6~1 */ + + /* + * Work around an old bug fixed in glibc 2.17 (released on + * 2012-12-24), at the cost of potentially making translated + * messages from external functions like perror() emitted in + * the wrong encoding. + * + * The bug affected e.g. git.git's own 7eb93c89651 ([PATCH] + * Simplify git script, 2005-09-07), which is the origin of + * the "David_K\345gedal" test string. + * + * See a much longer comment added to this file in 5e9637c6297 + * (i18n: add infrastructure for translating Git with gettext, + * 2011-11-18) for more details. + */ if (test_vsnprintf("%.*s", 13, "David_K\345gedal") < 0) setlocale(LC_CTYPE, "C"); } diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh index 7225abd811..78f3647ed9 100644 --- a/git-mergetool--lib.sh +++ b/git-mergetool--lib.sh @@ -46,9 +46,11 @@ show_tool_names () { while read scriptname do setup_tool "$scriptname" 2>/dev/null - variants="$variants$(list_tool_variants)\n" + # We need an actual line feed here + variants="$variants +$(list_tool_variants)" done - variants="$(echo "$variants" | sort | uniq)" + variants="$(echo "$variants" | sort -u)" for toolname in $variants do diff --git a/patch-ids.c b/patch-ids.c index 21973e4933..f51021a0cb 100644 --- a/patch-ids.c +++ b/patch-ids.c @@ -89,7 +89,7 @@ static int init_patch_id_entry(struct patch_id *patch, return 0; } -struct patch_id *has_commit_patch_id(struct commit *commit, +struct patch_id *patch_id_iter_first(struct commit *commit, struct patch_ids *ids) { struct patch_id patch; @@ -104,6 +104,18 @@ struct patch_id *has_commit_patch_id(struct commit *commit, return hashmap_get_entry(&ids->patches, &patch, ent, NULL); } +struct patch_id *patch_id_iter_next(struct patch_id *cur, + struct patch_ids *ids) +{ + return hashmap_get_next_entry(&ids->patches, cur, ent); +} + +int has_commit_patch_id(struct commit *commit, + struct patch_ids *ids) +{ + return !!patch_id_iter_first(commit, ids); +} + struct patch_id *add_commit_patch_id(struct commit *commit, struct patch_ids *ids) { diff --git a/patch-ids.h b/patch-ids.h index 03bb04e707..ab6c6a6804 100644 --- a/patch-ids.h +++ b/patch-ids.h @@ -23,7 +23,25 @@ int commit_patch_id(struct commit *commit, struct diff_options *options, struct object_id *oid, int, int); int init_patch_ids(struct repository *, struct patch_ids *); int free_patch_ids(struct patch_ids *); + +/* Add a patch_id for a single commit to the set. */ struct patch_id *add_commit_patch_id(struct commit *, struct patch_ids *); -struct patch_id *has_commit_patch_id(struct commit *, struct patch_ids *); + +/* Returns true if the patch-id of "commit" is present in the set. */ +int has_commit_patch_id(struct commit *commit, struct patch_ids *); + +/* + * Iterate over all commits in the set whose patch id matches that of + * "commit", like: + * + * struct patch_id *cur; + * for (cur = patch_id_iter_first(commit, ids); + * cur; + * cur = patch_id_iter_next(cur, ids) { + * ... look at cur->commit + * } + */ +struct patch_id *patch_id_iter_first(struct commit *commit, struct patch_ids *); +struct patch_id *patch_id_iter_next(struct patch_id *cur, struct patch_ids *); #endif /* PATCH_IDS_H */ diff --git a/revision.c b/revision.c index 9dff845bed..7ec9b634e3 100644 --- a/revision.c +++ b/revision.c @@ -1241,12 +1241,14 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs) /* * Have we seen the same patch id? */ - id = has_commit_patch_id(commit, &ids); + id = patch_id_iter_first(commit, &ids); if (!id) continue; commit->object.flags |= cherry_flag; - id->commit->object.flags |= cherry_flag; + do { + id->commit->object.flags |= cherry_flag; + } while ((id = patch_id_iter_next(id, &ids))); } free_patch_ids(&ids); diff --git a/submodule.c b/submodule.c index b3bb59f066..b561445329 100644 --- a/submodule.c +++ b/submodule.c @@ -1477,6 +1477,7 @@ static int get_next_submodule(struct child_process *cp, strbuf_release(&submodule_prefix); return 1; } else { + struct strbuf empty_submodule_path = STRBUF_INIT; fetch_task_release(task); free(task); @@ -1485,13 +1486,17 @@ static int get_next_submodule(struct child_process *cp, * An empty directory is normal, * the submodule is not initialized */ + strbuf_addf(&empty_submodule_path, "%s/%s/", + spf->r->worktree, + ce->name); if (S_ISGITLINK(ce->ce_mode) && - !is_empty_dir(ce->name)) { + !is_empty_dir(empty_submodule_path.buf)) { spf->result = 1; strbuf_addf(err, _("Could not access submodule '%s'\n"), ce->name); } + strbuf_release(&empty_submodule_path); } } diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh index bd3fa3c6da..4b714e9308 100644 --- a/t/lib-submodule-update.sh +++ b/t/lib-submodule-update.sh @@ -316,14 +316,7 @@ test_submodule_switch_common () { command="$1" ######################### Appearing submodule ######################### # Switching to a commit letting a submodule appear creates empty dir ... - if test "$KNOWN_FAILURE_STASH_DOES_IGNORE_SUBMODULE_CHANGES" = 1 - then - # Restoring stash fails to restore submodule index entry - RESULT="failure" - else - RESULT="success" - fi - test_expect_$RESULT "$command: added submodule creates empty directory" ' + test_expect_success "$command: added submodule creates empty directory" ' prolog && reset_work_tree_to no_submodule && ( @@ -337,6 +330,13 @@ test_submodule_switch_common () { ) ' # ... and doesn't care if it already exists. + if test "$KNOWN_FAILURE_STASH_DOES_IGNORE_SUBMODULE_CHANGES" = 1 + then + # Restoring stash fails to restore submodule index entry + RESULT="failure" + else + RESULT="success" + fi test_expect_$RESULT "$command: added submodule leaves existing empty directory alone" ' prolog && reset_work_tree_to no_submodule && diff --git a/t/perf/p7519-fsmonitor.sh b/t/perf/p7519-fsmonitor.sh index 9b43342806..1e20a184c7 100755 --- a/t/perf/p7519-fsmonitor.sh +++ b/t/perf/p7519-fsmonitor.sh @@ -129,7 +129,12 @@ setup_for_fsmonitor() { git config core.fsmonitor "$INTEGRATION_SCRIPT" && git update-index --fsmonitor 2>error && - test_must_be_empty error # ensure no silent error + if test_have_prereq WATCHMAN + then + test_must_be_empty error # ensure no silent error + else + grep "Empty last update token" error + fi } test_perf_w_drop_caches () { diff --git a/t/t0068-for-each-repo.sh b/t/t0068-for-each-repo.sh index 136b4ec839..4675e85251 100755 --- a/t/t0068-for-each-repo.sh +++ b/t/t0068-for-each-repo.sh @@ -27,4 +27,10 @@ test_expect_success 'run based on configured value' ' grep again message ' +test_expect_success 'do nothing on empty config' ' + # the whole thing would fail if for-each-ref iterated even + # once, because "git help --no-such-option" would fail + git for-each-repo --config=bogus.config -- help --no-such-option +' + test_done diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh index 41818d8315..576632f868 100755 --- a/t/t4129-apply-samemode.sh +++ b/t/t4129-apply-samemode.sh @@ -78,7 +78,7 @@ test_expect_success POSIXPERM 'do not use core.sharedRepository for working tree test_config core.sharedRepository 0666 && ( # Remove a default ACL if possible. - (setfacl -k newdir 2>/dev/null || true) && + (setfacl -k . 2>/dev/null || true) && umask 0077 && # Test both files (f1) and leading dirs (d) diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 01004ff680..3ed121d0ce 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -1260,7 +1260,9 @@ do git cat-file commit $SHA1_2 && test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 \ git fetch ../testrepo/.git $SHA1_3 2>err && - test_i18ngrep "remote error:.*not our ref.*$SHA1_3\$" err + # ideally we would insist this be on a "remote error:" + # line, but it is racy; see the commit message + test_i18ngrep "not our ref.*$SHA1_3\$" err ) ' done diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh index a877dd145e..53d7b8ed75 100755 --- a/t/t5526-fetch-submodules.sh +++ b/t/t5526-fetch-submodules.sh @@ -722,4 +722,121 @@ test_expect_success 'fetch new submodule commit intermittently referenced by sup ) ' +add_commit_push () { + dir="$1" && + msg="$2" && + shift 2 && + git -C "$dir" add "$@" && + git -C "$dir" commit -a -m "$msg" && + git -C "$dir" push +} + +compare_refs_in_dir () { + fail= && + if test "x$1" = 'x!' + then + fail='!' && + shift + fi && + git -C "$1" rev-parse --verify "$2" >expect && + git -C "$3" rev-parse --verify "$4" >actual && + eval $fail test_cmp expect actual +} + + +test_expect_success 'setup nested submodule fetch test' ' + # does not depend on any previous test setups + + for repo in outer middle inner + do + git init --bare $repo && + git clone $repo ${repo}_content && + echo "$repo" >"${repo}_content/file" && + add_commit_push ${repo}_content "initial" file || + return 1 + done && + + git clone outer A && + git -C A submodule add "$pwd/middle" && + git -C A/middle/ submodule add "$pwd/inner" && + add_commit_push A/middle/ "adding inner sub" .gitmodules inner && + add_commit_push A/ "adding middle sub" .gitmodules middle && + + git clone outer B && + git -C B/ submodule update --init middle && + + compare_refs_in_dir A HEAD B HEAD && + compare_refs_in_dir A/middle HEAD B/middle HEAD && + test_path_is_file B/file && + test_path_is_file B/middle/file && + test_path_is_missing B/middle/inner/file && + + echo "change on inner repo of A" >"A/middle/inner/file" && + add_commit_push A/middle/inner "change on inner" file && + add_commit_push A/middle "change on inner" inner && + add_commit_push A "change on inner" middle +' + +test_expect_success 'fetching a superproject containing an uninitialized sub/sub project' ' + # depends on previous test for setup + + git -C B/ fetch && + compare_refs_in_dir A origin/HEAD B origin/HEAD +' + +fetch_with_recursion_abort () { + # In a regression the following git call will run into infinite recursion. + # To handle that, we connect the sed command to the git call by a pipe + # so that sed can kill the infinite recursion when detected. + # The recursion creates git output like: + # Fetching submodule sub + # Fetching submodule sub/sub <-- [1] + # Fetching submodule sub/sub/sub + # ... + # [1] sed will stop reading and cause git to eventually stop and die + + git -C "$1" fetch --recurse-submodules 2>&1 | + sed "/Fetching submodule $2[^$]/q" >out && + ! grep "Fetching submodule $2[^$]" out +} + +test_expect_success 'setup recursive fetch with uninit submodule' ' + # does not depend on any previous test setups + + test_create_repo super && + test_commit -C super initial && + test_create_repo sub && + test_commit -C sub initial && + git -C sub rev-parse HEAD >expect && + + git -C super submodule add ../sub && + git -C super commit -m "add sub" && + + git clone super superclone && + git -C superclone submodule status >out && + sed -e "s/^-//" -e "s/ sub.*$//" out >actual && + test_cmp expect actual +' + +test_expect_success 'recursive fetch with uninit submodule' ' + # depends on previous test for setup + + fetch_with_recursion_abort superclone sub && + git -C superclone submodule status >out && + sed -e "s/^-//" -e "s/ sub$//" out >actual && + test_cmp expect actual +' + +test_expect_success 'recursive fetch after deinit a submodule' ' + # depends on previous test for setup + + git -C superclone submodule update --init sub && + git -C superclone submodule deinit -f sub && + + fetch_with_recursion_abort superclone sub && + git -C superclone submodule status >out && + sed -e "s/^-//" -e "s/ sub$//" out >actual && + test_cmp expect actual +' + test_done diff --git a/t/t5570-git-daemon.sh b/t/t5570-git-daemon.sh index 8f69a7854f..0fbb194810 100755 --- a/t/t5570-git-daemon.sh +++ b/t/t5570-git-daemon.sh @@ -103,6 +103,11 @@ test_expect_success 'fetch notices corrupt idx' ' ) ' +test_expect_success 'client refuses to ask for repo with newline' ' + test_must_fail git clone "$GIT_DAEMON_URL/repo$LF.git" dst 2>stderr && + test_i18ngrep newline.is.forbidden stderr +' + test_remote_error() { do_export=YesPlease diff --git a/t/t6007-rev-list-cherry-pick-file.sh b/t/t6007-rev-list-cherry-pick-file.sh index f0268372d2..8bf5ae23c2 100755 --- a/t/t6007-rev-list-cherry-pick-file.sh +++ b/t/t6007-rev-list-cherry-pick-file.sh @@ -245,6 +245,18 @@ test_expect_success '--count --left-right' ' test_cmp expect actual ' +test_expect_success '--cherry-pick with duplicates on each side' ' + git checkout -b dup-orig && + test_commit dup-base && + git revert dup-base && + git cherry-pick dup-base && + git checkout -b dup-side HEAD~3 && + test_tick && + git cherry-pick -3 dup-orig && + git rev-list --cherry-pick dup-orig...dup-side >actual && + test_must_be_empty actual +' + # Corrupt the object store deliberately to make sure # the object is not even checked for its existence. remove_loose_object () { diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index aa226381be..52614eefc7 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -926,14 +926,14 @@ test_expect_success 'git bisect reset cleans bisection state properly' ' git bisect bad $HASH4 && git bisect reset && test -z "$(git for-each-ref "refs/bisect/*")" && - test_path_is_missing "$GIT_DIR/BISECT_EXPECTED_REV" && - test_path_is_missing "$GIT_DIR/BISECT_ANCESTORS_OK" && - test_path_is_missing "$GIT_DIR/BISECT_LOG" && - test_path_is_missing "$GIT_DIR/BISECT_RUN" && - test_path_is_missing "$GIT_DIR/BISECT_TERMS" && - test_path_is_missing "$GIT_DIR/head-name" && - test_path_is_missing "$GIT_DIR/BISECT_HEAD" && - test_path_is_missing "$GIT_DIR/BISECT_START" + test_path_is_missing ".git/BISECT_EXPECTED_REV" && + test_path_is_missing ".git/BISECT_ANCESTORS_OK" && + test_path_is_missing ".git/BISECT_LOG" && + test_path_is_missing ".git/BISECT_RUN" && + test_path_is_missing ".git/BISECT_TERMS" && + test_path_is_missing ".git/head-name" && + test_path_is_missing ".git/BISECT_HEAD" && + test_path_is_missing ".git/BISECT_START" ' test_done diff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh index 7476781979..e5c6a038fb 100755 --- a/t/t7012-skip-worktree-writing.sh +++ b/t/t7012-skip-worktree-writing.sh @@ -149,6 +149,94 @@ test_expect_success '--ignore-skip-worktree-entries leaves worktree alone' ' --diff-filter=D -- keep-me.t ' +test_expect_success 'stash restore in sparse checkout' ' + test_create_repo stash-restore && + ( + cd stash-restore && + + mkdir subdir && + echo A >subdir/A && + echo untouched >untouched && + echo removeme >removeme && + echo modified >modified && + git add . && + git commit -m Initial && + + echo AA >>subdir/A && + echo addme >addme && + echo tweaked >>modified && + rm removeme && + git add addme && + + git stash push && + + git sparse-checkout set subdir && + + # Ensure after sparse-checkout we only have expected files + cat >expect <<-EOF && + S modified + S removeme + H subdir/A + S untouched + EOF + git ls-files -t >actual && + test_cmp expect actual && + + test_path_is_missing addme && + test_path_is_missing modified && + test_path_is_missing removeme && + test_path_is_file subdir/A && + test_path_is_missing untouched && + + # Put a file in the working directory in the way + echo in the way >modified && + git stash apply && + + # Ensure stash vivifies modifies paths... + cat >expect <<-EOF && + H addme + H modified + H removeme + H subdir/A + S untouched + EOF + git ls-files -t >actual && + test_cmp expect actual && + + # ...and that the paths show up in status as changed... + cat >expect <<-EOF && + A addme + M modified + D removeme + M subdir/A + ?? actual + ?? expect + ?? modified.stash.XXXXXX + EOF + git status --porcelain | \ + sed -e s/stash......./stash.XXXXXX/ >actual && + test_cmp expect actual && + + # ...and that working directory reflects the files correctly + test_path_is_file addme && + test_path_is_file modified && + test_path_is_missing removeme && + test_path_is_file subdir/A && + test_path_is_missing untouched && + + # ...including that we have the expected "modified" file... + cat >expect <<-EOF && + modified + tweaked + EOF + test_cmp expect modified && + + # ...and that the other "modified" file is still present... + echo in the way >expect && + test_cmp expect modified.stash.* + ) +' + #TODO test_expect_failure 'git-apply adds file' false #TODO test_expect_failure 'git-apply updates file' false #TODO test_expect_failure 'git-apply removes file' false diff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh index eec96e0ba9..d21dc8b009 100755 --- a/t/t7416-submodule-dash-url.sh +++ b/t/t7416-submodule-dash-url.sh @@ -201,4 +201,19 @@ test_expect_success 'fsck rejects embedded newline in relative url' ' grep gitmodulesUrl err ' +test_expect_success 'fsck rejects embedded newline in git url' ' + git checkout --orphan git-newline && + cat >.gitmodules <<-\EOF && + [submodule "foo"] + url = "git://example.com:1234/repo%0a.git" + EOF + git add .gitmodules && + git commit -m "git url with newline" && + test_when_finished "rm -rf dst" && + git init --bare dst && + git -C dst config transfer.fsckObjects true && + test_must_fail git push dst HEAD 2>err && + grep gitmodulesUrl err +' + test_done diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh index 70afdd06fa..6ac75b5d4c 100755 --- a/t/t7610-mergetool.sh +++ b/t/t7610-mergetool.sh @@ -828,4 +828,15 @@ test_expect_success 'mergetool -Oorder-file is honored' ' test_cmp expect actual ' +test_expect_success 'mergetool --tool-help shows recognized tools' ' + # Check a few known tools are correctly shown + git mergetool --tool-help >mergetools && + grep vimdiff mergetools && + grep vimdiff3 mergetools && + grep gvimdiff2 mergetools && + grep araxis mergetools && + grep xxdiff mergetools && + grep meld mergetools +' + test_done diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index 999982fe4a..2f08ce7cba 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -367,9 +367,14 @@ test_chmod () { git update-index --add "--chmod=$@" } -# Get the modebits from a file or directory. +# Get the modebits from a file or directory, ignoring the setgid bit (g+s). +# This bit is inherited by subdirectories at their creation. So we remove it +# from the returning string to prevent callers from having to worry about the +# state of the bit in the test directory. +# test_modebits () { - ls -ld "$1" | sed -e 's|^\(..........\).*|\1|' + ls -ld "$1" | sed -e 's|^\(..........\).*|\1|' \ + -e 's|^\(......\)S|\1-|' -e 's|^\(......\)s|\1x|' } # Unset a configuration variable, but don't fail if it doesn't exist. |