git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Philippe Blain <levraiphilippeblain@gmail.com>
To: Damien Robert <damien.olivier.robert@gmail.com>
Cc: git@vger.kernel.org
Subject: Re: Nested submodule checkout
Date: Sun, 16 Feb 2020 23:51:43 -0500	[thread overview]
Message-ID: <0123F1ED-C421-4C1F-896B-E54C9D345A34@gmail.com> (raw)
In-Reply-To: <20200214224242.knmzkwx7ls4sote7@doriath>

Hi Damien,

I reported the same bug to the list back in September [1], and I’m glad to say I just finished (today!) a patch series [2] that fixes this bug.

> Le 14 févr. 2020 à 17:42, Damien Robert <damien.olivier.robert@gmail.com> a écrit :
> 
> Dear git developers,
> 
> I stumbled into this situation from which it was a bit painful to recover:
> 
> -> test script:
> 
>    mkdir ploum
>    cd ploum
>    git init
>    echo 'foo' > foo
>    git add foo
>    git commit -m foo
>    git branch nosubmodules
> 
>    mkdir plam
>    cd plam
>    git init
>    echo 'bar' > bar
>    git add bar
>    git commit -m bar
> 
>    mkdir plim
>    cd plim
>    git init
>    echo 'baz' > baz
>    git add baz
>    git commit -m baz
> 
>    cd ..
>    git submodule add ./plim
>    git commit -am 'Add submodule plim'
> 
>    cd ..
>    git submodule add ./plam
>    git commit -am 'Add submodule plam'
> 
>    git checkout nosubmodules
>    git checkout --recurse-submodules master
> 
> -> The result is as follow:
> 
> Initialized empty Git repository in /data/dams/var/tmp/ploum/.git/
> [master (root-commit) ec7c09a] foo
> 1 file changed, 1 insertion(+)
> create mode 100644 foo
> Branch 'nosubmodules' set up to track local branch 'master'.
> Initialized empty Git repository in /data/dams/var/tmp/ploum/plam/.git/
> [master (root-commit) 35e6696] bar
> 1 file changed, 1 insertion(+)
> create mode 100644 bar
> Initialized empty Git repository in /data/dams/var/tmp/ploum/plam/plim/.git/
> [master (root-commit) b4712c1] baz
> 1 file changed, 1 insertion(+)
> create mode 100644 baz
> Adding existing repo at 'plim' to the index
> [master 989c11d] Add submodule plim
> 2 files changed, 4 insertions(+)
> create mode 100644 .gitmodules
> create mode 160000 plim
> Adding existing repo at 'plam' to the index
> [master 5b34041] Add submodule plam
> 2 files changed, 4 insertions(+)
> create mode 100644 .gitmodules
> create mode 160000 plam

Here you just did ` git commit -am 'Add submodule plam’` so the next command according to your reproducer above would be `git checkout nosubmodules`

> Migrating git directory of 'plam' from
> '/data/dams/var/tmp/ploum/plam/.git' to
> '/data/dams/var/tmp/ploum/.git/modules/plam'
> Migrating git directory of 'plam/plim' from
> '/data/dams/var/tmp/ploum/plam/plim/.git' to
> '/data/dams/var/tmp/ploum/.git/modules/plam/modules/plim'
> Switched to branch 'nosubmodules'
> Your branch is behind 'master' by 1 commit, and can be fast-forwarded.
>  (use "git pull" to update your local branch)

Here, git is migrating the git directories of both submodules to the git directory of the superproject (ploum). This tells me you probably have the `submodule.recurse` config set somewhere, as this is the behaviour I get I if I do `git checkout --recurse-submodules nosubmodules`.
If I just do `git checkout nosubmodules`, I get 
    
    $ git checkout nosubmodules 
    warning: unable to rmdir 'plam': Directory not empty
    Switched to branch 'nosubmodules'
    Your branch is behind 'master' by 1 commit, and can be fast-forwarded.
      (use "git pull" to update your local branch)

and then doing `git checkout --recurse-submodules master` actually works. 

> fatal: exec '--super-prefix=plam/plim/': cd to 'plim' failed: No such file or directory
> error: Submodule 'plim' could not be updated.
> error: Submodule 'plam/plim' cannot checkout new HEAD.
> error: Submodule 'plam' could not be updated.
> M	plam
> Switched to branch 'master'
> 
> As you can see, the nested plim submodules could not be recreated since the
> folder does not exists yet in the 'nosubmodules' branch.

That’s the cause of the bug: in fact git tries to change directory into ‘plim’ before the ‘plim’ directory is created in the filesystem.

> This makes the
> 'plam' submodule update fails, and in the following state
> 
> Unstaged changes after reset:
> D	.gitmodules
> D	bar
> D	plim

At this point, if you go into ‘plam’ and do `git ls-files -s` (to list the content of the index), you will see that the index is empty (the checkout died before the index could be populated.) 

> -> To recover
> 
> In the folder plam, do a `git reset` followed by a `git reset --hard`
> (`git reset --hard` directly does not work:
> fatal: exec '--super-prefix=plim/': cd to 'plim' failed: No such file or directory)

That’s another hint that you have `submodule.recurse` set. I don’t get this error doing `git reset --hard`, but I get it doing `git reset --hard --recurse-submodules` (or `git reset --hard --r`, which works and is quicker to type!). `git reset` populates the index, so now `git ls-files -s` would now show the correct content of ‘plam’.

> Indeed the first reset, which puts .gitmodules back in the index, is what
> allows to do the `git submodule update` implied by `git reset --hard`.

In fact, `git reset --hard` does not spawn `git submodule update`, it calls functions in unpack-trees.c (that actually spawn `git read-tree —recurse-submodules`) to update the submodules recursively. 

> Note that I wasn't able to reproduce in this small examples, but when
> trying to repair I also add some strange errors of the form
> '.git is not a git directory' (where .git was a pseudo symlink
> gitdir: ../.git/modules/plam).
> 
> -> Question
> 
> My usage is probably non standard (I have quite a lot of nested
> submodules), so I had a hard time to recover from this checkout. Is there a
> better way? Would it be possible to make nested submodules checkout of this
> form work out of the box?

It is supposed to work out of the box when using `--recurse-submodules` or the `submodule.recurse` config. Although, it’s always possible to run into some bugs. 
One thing you can do to get out of tricky situations is to temporarily deactivate the config (`git -c submodule.recurse=0 <command>`). For example, after the failed `git checkout --recurse-submodules master` above, issuing

    git -c submodule.recurse=0 submodule update --recursive --force

would have correctly checked out the submodules. I have a git alias ‘no-rs’ (for no recurse-submodules) that I use in these situations:

    git config --global alias.no-rs ‘-c submodule.recurse=0’

Then the `submodule update` call above could be shortened to 

    git no-rs submodule update --recursive --force

Note that using the `submodule.recurse` config also applies to internal calls to git commands (issued by other git commands), so using adding `--no-recurse-submodules` to the command line might not be enough to completely turn off the effect of that config, hence this handy alias.

> 
> Thanks!
> Damien Robert
> 

Cheers,
Philippe.

[1] https://lore.kernel.org/git/7437BB59-4605-48EC-B05E-E2BDB2D9DABC@gmail.com/
[2] https://github.com/gitgitgadget/git/pull/555

  reply	other threads:[~2020-02-17  4:51 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-02-14 22:42 Nested submodule checkout Damien Robert
2020-02-17  4:51 ` Philippe Blain [this message]
2020-02-18 17:08   ` Damien Robert
2020-02-19  4:19     ` Philippe Blain
2020-02-26 17:23   ` Nested submodule status bug Damien Robert
2020-02-27 10:05     ` Damien Robert
2020-02-27 10:43       ` Spurious GIT_DIR set when in a worktree [was Re: Nested submodule status bug] Damien Robert
2020-02-27 15:50         ` GIT_DIR in aliases [Re: " Damien Robert
2020-02-28 19:02           ` Jeff King
2020-02-28 20:22             ` Junio C Hamano
2020-03-03 22:44             ` Damien Robert
2020-02-29  1:42           ` Philippe Blain
2020-03-03 21:56             ` Damien Robert

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=0123F1ED-C421-4C1F-896B-E54C9D345A34@gmail.com \
    --to=levraiphilippeblain@gmail.com \
    --cc=damien.olivier.robert@gmail.com \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).