summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/SubmittingPatches2
-rw-r--r--Documentation/config/http.txt4
-rw-r--r--Documentation/config/trace2.txt2
-rw-r--r--Documentation/git-clone.txt4
-rw-r--r--Documentation/git-fsck.txt8
-rw-r--r--Documentation/git-ls-files.txt5
-rw-r--r--Documentation/git-maintenance.txt2
-rw-r--r--Documentation/git-p4.txt2
-rw-r--r--Documentation/git-send-email.txt6
-rw-r--r--Documentation/git-status.txt28
-rw-r--r--Documentation/git-update-index.txt5
-rw-r--r--Documentation/githooks.txt2
-rw-r--r--Documentation/gitmodules.txt30
-rw-r--r--Documentation/technical/reftable.txt2
-rw-r--r--Makefile3
-rw-r--r--builtin/for-each-repo.c7
-rw-r--r--builtin/rebase.c4
-rw-r--r--builtin/stash.c165
-rwxr-xr-xci/install-dependencies.sh8
-rw-r--r--connect.c2
-rw-r--r--fsck.c2
-rw-r--r--gettext.c94
-rw-r--r--git-mergetool--lib.sh6
-rw-r--r--patch-ids.c14
-rw-r--r--patch-ids.h20
-rw-r--r--revision.c6
-rw-r--r--submodule.c7
-rw-r--r--t/lib-submodule-update.sh16
-rwxr-xr-xt/perf/p7519-fsmonitor.sh7
-rwxr-xr-xt/t0068-for-each-repo.sh6
-rwxr-xr-xt/t4129-apply-samemode.sh2
-rwxr-xr-xt/t5516-fetch-push.sh4
-rwxr-xr-xt/t5526-fetch-submodules.sh117
-rwxr-xr-xt/t5570-git-daemon.sh5
-rwxr-xr-xt/t6007-rev-list-cherry-pick-file.sh12
-rwxr-xr-xt/t7012-skip-worktree-writing.sh88
-rwxr-xr-xt/t7416-submodule-dash-url.sh15
-rwxr-xr-xt/t7610-mergetool.sh11
-rw-r--r--t/test-lib-functions.sh9
39 files changed, 529 insertions, 203 deletions
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index d12094b..0452db2 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 3968fbb..7003661 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 01d3afd..fe1642f 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 876aedc..02d9c19 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 d72d15b..bd59661 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 cbcf526..0a3b526 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 d1f9b51..6fe1e5e 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 ec233ac..f89e68b 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 b7bbbea..93708ae 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 7731b45..c0764e8 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 1489cb0..2853f16 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 ffccfc7..1f3b57d 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 539b4e1..8e333dd 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 2951840..8095ab2 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/Makefile b/Makefile
index 7b64106..5a9a8a0 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/builtin/for-each-repo.c b/builtin/for-each-repo.c
index 5bba623..52be64a 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 19c7b37..840dbd7 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 e1f8235..9bc85f9 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 0229a77..0b1184e 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
diff --git a/connect.c b/connect.c
index 8b8f56c..9c97fee 100644
--- a/connect.c
+++ b/connect.c
@@ -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
diff --git a/fsck.c b/fsck.c
index f82e2fe..5e282b3 100644
--- a/fsck.c
+++ b/fsck.c
@@ -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;
diff --git a/gettext.c b/gettext.c
index 35d2c12..1b56421 100644
--- a/gettext.c
+++ b/gettext.c
@@ -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 7225abd..78f3647 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 21973e4..f51021a 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 03bb04e..ab6c6a6 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 9dff845..7ec9b63 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 b3bb59f..b561445 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 bd3fa3c..4b714e9 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 9b43342..1e20a18 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 136b4ec..4675e85 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 41818d8..576632f 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 01004ff..3ed121d 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 a877dd1..53d7b8e 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 8f69a78..0fbb194 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 f026837..8bf5ae2 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/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh
index 7476781..e5c6a03 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 eec96e0..d21dc8b 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 70afdd0..6ac75b5 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 999982f..2f08ce7 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.