gitgit-submodules

How do I remove a submodule?


How do I remove a Git submodule? Why can't I do git submodule rm module_name?


Solution

  • Since git1.8.3 (April 22d, 2013):

    There was no Porcelain way to say "I no longer am interested in this submodule", once you express your interest in a submodule with "git submodule init".
    "git submodule deinit" is the way to do so.

    The deletion process also uses git rm (since git1.8.5 October 2013).

    Summary

    The 3-steps removal process would then be:

    0. mv a/submodule a/submodule_tmp
    
    1. git submodule deinit -f -- a/submodule    
    2. rm -rf .git/modules/a/submodule
    3. git rm -f a/submodule
    # Note: a/submodule (no trailing slash)
    
    # or, if you want to leave it in your working tree and have done step 0
    3.   git rm --cached a/submodule
    3bis mv a/submodule_tmp a/submodule
    

    Explanation

    rm -rf: This is mentioned in Daniel Schroeder's answer, and summarized by Eonil in the comments:

    This leaves .git/modules/<path-to-submodule>/ unchanged.
    So if you once delete a submodule with this method and re-add them again, it will not be possible because repository already been corrupted.


    git rm: See commit 95c16418:

    Currently using "git rm" on a submodule removes the submodule's work tree from that of the superproject and the gitlink from the index.
    But the submodule's section in .gitmodules is left untouched, which is a leftover of the now removed submodule and might irritate users (as opposed to the setting in .git/config, this must stay as a reminder that the user showed interest in this submodule so it will be repopulated later when an older commit is checked out).

    Let "git rm" help the user by not only removing the submodule from the work tree but by also removing the "submodule.<submodule name>" section from the .gitmodules file and stage both.


    git submodule deinit: It stems from this patch:

    With "git submodule init" the user is able to tell git they care about one or more submodules and wants to have it populated on the next call to "git submodule update".
    But currently there is no easy way they can tell git they do not care about a submodule anymore and wants to get rid of the local work tree (unless the user knows a lot about submodule internals and removes the "submodule.$name.url" setting from .git/config together with the work tree himself).

    Help those users by providing a 'deinit' command.
    This removes the whole submodule.<name> section from .git/config either for the given submodule(s) (or for all those which have been initialized if '.' is given).
    Fail if the current work tree contains modifications unless forced.
    Complain when for a submodule given on the command line the url setting can't be found in .git/config, but nonetheless don't fail.

    This takes care if the (de)initialization steps (.git/config and .git/modules/xxx)

    Since git1.8.5, the git rm takes also care of the:

    • 'add' step which records the url of a submodule in the .gitmodules file: it is need to removed for you.
    • the submodule special entry (as illustrated by this question): the git rm removes it from the index:
      git rm --cached path_to_submodule (no trailing slash)
      That will remove that directory stored in the index with a special mode "160000", marking it as a submodule root directory.

    If you forget that last step, and try to add what was a submodule as a regular directory, you would get error message like:

    git add mysubmodule/file.txt 
    Path 'mysubmodule/file.txt' is in submodule 'mysubmodule'
    

    Note: since Git 2.17 (Q2 2018), git submodule deinit is no longer a shell script.
    It is a call to a C function.

    See commit 2e61273, commit 1342476 (14 Jan 2018) by Prathamesh Chavan (pratham-pc).
    (Merged by Junio C Hamano -- gitster -- in commit ead8dbe, 13 Feb 2018)

    git ${wt_prefix:+-C "$wt_prefix"} submodule--helper deinit \
      ${GIT_QUIET:+--quiet} \
      ${prefix:+--prefix "$prefix"} \
      ${force:+--force} \
      ${deinit_all:+--all} "$@"