1

I added a git submodule with the following command:

git submodule add https://github.com/spaceship-prompt/spaceship-prompt.git --depth=1

This command mistook --depth=1 as a module name, rather than the repo depth. When I run git submodule I get:

a5d55898822e75cb34ee1c3cefe52822c820c220 zshrc/zsh_prompts/--depth=1 (v3.11.2-386-ga5d5589)

Now I want to remove this submodule. Since I have not run git submodule init, I can run git rm -f "--depth=1" to remove it, but get the following error:

error: unknown option `depth=1/'
usage: git rm [<options>] [--] <file>...

    -n, --dry-run         dry run
    -q, --quiet           do not list removed files
    --cached              only remove from the index
    -f, --force           override the up-to-date check
    -r                    allow recursive removal
    --ignore-unmatch      exit with a zero status even if nothing matched
    --pathspec-from-file <file>
                          read pathspec from file
    --pathspec-file-nul   with --pathspec-from-file, pathspec elements are separated with NUL character

How do I escape the -- in the submodule name and remove it?

user3156459
  • 1,123
  • 2
  • 9
  • 17

1 Answers1

2

You have discovered the reason that --, the "end of options" marker, exists in the first place.

Consider the more general problem of removing a file named -f. Running:

rm -f

doesn't work, because -f is a flag to rm. One solution is:

rm ./-f

which does work as it names the same file, but doesn't start with - and hence does not resemble the -f flag. (This also works for your git rm issue.)

In some cases, however, there's no "alternate name that means the same thing". For these cases, we'd like to have:

<cmd> <option> <argument>

be treated as the command with the option and argument, but:

<cmd> <argument-that-resembles-option> <argument>

be treated as the command, but with two arguments. If the command follows one common convention, we may be able to reverse the two arguments:

rm foo -f

for instance may treat the -f as a file name. But if it follows a different common convention, the -f is still an option.

So, enter the -- option, which means everything after this is not an option after all. We simply write:

<cmd> -- <argument-that-resembles-option> <argument>

For instance:

rm -- -f

or:

git rm -- --depth=1

The -- tells the command that subsequent arguments are not options even if they look like options.

This works well with other commands, where:

git checkout main --

means check out the branch named main while:

git checkout -- main

means discard my changes to the file named main in the current branch.

It's a good idea to learn about -- and get in the habit of using it, even when it's clearly not required, just so that you will use -- when it is required. (Or, in the case of git checkout, to switch to the two commands git switch and git restore, but that's another matter entirely. Fortunately, ever since Git 2.23, the ambiguous case where you expect DWIM or --guess to happen, but instead you get "wipe out my changes", now draws a diagnostic, so if you have bad habits and run git checkout dev and would have gotten the bad behavior, you now get an error. I haven't been personally bitten by this, but I do have this bad habit myself.)

torek
  • 448,244
  • 59
  • 642
  • 775
  • I played around with forward slash and managed to escape the -- by sticking one in front of it. The answer though is much more comprehensive. – user3156459 Jul 10 '22 at 11:46
  • `git rm -f "\--depth=1"` the command that worked for me. It is acutally a backslash. – user3156459 Jul 11 '22 at 10:20
  • @user3156459: that just means you're on Windows, where pathnames use backslashes instead of forward slashes at the OS level. (The OS also *accepts* forward slashes in many places, and Git does any translation required because Git internally uses only forward slashes.) The `./` syntax means "this directory" and `/` means "from the top level". The latter is a bit tricky since on a Unix system, the top level is where you find `/home` for instance, and yet in a Git repository in `/home/bob/src/foo/.git` the "top level" is `/home/bob/src/foo` so that `/bar.c` is `/home/bob/src/foo/bar.c`. – torek Jul 11 '22 at 21:41
  • This "what's the top level" confusion engenders all kinds of fun games where some paths are out of the repository, and some are just starting from the top of the repository, and nobody knows off-hand which ones are treated which way. :-) More seriously, see the definition of a *pathspec* in [the Git glossary](https://git-scm.com/docs/gitglossary). – torek Jul 11 '22 at 21:42
  • I'm on macOS 10.14 – user3156459 Jul 12 '22 at 06:03
  • Interesting. It seems that pathspec interpolation sometimes removes backslashes. I created a file named `\file` (literal backslash in the file name); `git rm '\file'`, `git rm '\\file'`, and `git rm '\\\file'` all want to remove it, but `git rm '\\\\file'` says that this pathspec does not match any files. I'm not sure this is the *intent* in Git, or an accident in the filespec code. – torek Jul 12 '22 at 06:41
  • This https://stackoverflow.com/a/36021991/3156459 suggests ‘:/‘ to specify git top level dir unambiguously. Couldn’t find in git docs, though. – user3156459 Jul 12 '22 at 08:11
  • It's in the pathspec description in the gitglossary (see [my earlier comment](https://stackoverflow.com/questions/72920630/how-to-escape-in-git-submodule-name/72924708?noredirect=1#comment128839234_72924708)). – torek Jul 12 '22 at 08:14