diff options
-rw-r--r-- | Documentation/SubmittingPatches | 2 | ||||
-rw-r--r-- | Documentation/config/http.txt | 4 | ||||
-rw-r--r-- | Documentation/config/trace2.txt | 2 | ||||
-rw-r--r-- | Documentation/git-maintenance.txt | 2 | ||||
-rw-r--r-- | Documentation/git-p4.txt | 2 | ||||
-rw-r--r-- | Documentation/githooks.txt | 2 | ||||
-rw-r--r-- | Documentation/gitmodules.txt | 30 | ||||
-rw-r--r-- | Documentation/technical/reftable.txt | 2 | ||||
-rw-r--r-- | builtin/rebase.c | 4 | ||||
-rw-r--r-- | builtin/stash.c | 165 | ||||
-rw-r--r-- | submodule.c | 7 | ||||
-rw-r--r-- | t/lib-submodule-update.sh | 16 | ||||
-rwxr-xr-x | t/perf/p7519-fsmonitor.sh | 7 | ||||
-rwxr-xr-x | t/t5526-fetch-submodules.sh | 117 | ||||
-rwxr-xr-x | t/t7012-skip-worktree-writing.sh | 88 | ||||
-rw-r--r-- | t/test-lib-functions.sh | 9 |
16 files changed, 374 insertions, 85 deletions
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-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/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/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/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/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/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/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/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. |