Repository: BobBuildTool/bob (language: python)

Issue:
Assertion error when changing checkout method for submodules
Originally I had a recipe, where I would initialize and checkout submodules in the `checkoutScript`:
```
checkoutSCM:
   scm: git
   url: git://repo/git/foo/bar.git

checkoutScript: |
   git submodule init && git submodule update
```
I replaced the `checkoutScript` part by setting `submodules: True` under `checkoutSCM`. When rebuilding the package I obtain the error:

```
.....
[   2] SWITCH    foo::bar - dev/src/foo/bar/1/workspace/bar
....

An internal Exception has occured. This should not have happenend.
Please open an issue at https://github.com/BobBuildTool/bob with the following backtrace:
Bob version 0.19.2
Traceback (most recent call last):
  File "/usr/local/lib/bob/lib/python3.7/site-packages/bob/scripts.py", line 147, in catchErrors
    ret = fun(*args, **kwargs)
  File "/usr/local/lib/bob/lib/python3.7/site-packages/bob/scripts.py", line 248, in cmd
    ret = cmd(args.args, bobRoot)
  File "/usr/local/lib/bob/lib/python3.7/site-packages/bob/scripts.py", line 29, in __develop
    doDevelop(*args, **kwargs)
  File "/usr/local/lib/bob/lib/python3.7/site-packages/bob/cmds/build/build.py", line 313, in doDevelop
    commonBuildDevelop(parser, argv, bobRoot, True)
  File "/usr/local/lib/bob/lib/python3.7/site-packages/bob/cmds/build/build.py", line 263, in commonBuildDevelop
    builder.cook(backlog, True if args.build_mode == 'checkout-only' else False)
  File "/usr/local/lib/bob/lib/python3.7/site-packages/bob/cmds/build/builder.py", line 828, in cook
    raise self.__buildErrors[0]
  File "/usr/local/lib/bob/lib/python3.7/site-packages/bob/cmds/build/builder.py", line 722, in __taskWrapper
    ret = await coro()
  File "/usr/local/lib/bob/lib/python3.7/site-packages/bob/cmds/build/builder.py", line 882, in _cookStep
    await self._cookCheckoutStep(step, depth)
  File "/usr/local/lib/bob/lib/python3.7/site-packages/bob/cmds/build/builder.py", line 1012, in _cookCheckoutStep
    scmPath, scmMap[scmDir], scmSpec)
  File "/usr/local/lib/bob/lib/python3.7/site-packages/bob/cmds/build/builder.py", line 1791, in __runScmSwitch
    ret = await invoker.executeScmSwitch(scm, oldSpec)
  File "/usr/local/lib/bob/lib/python3.7/site-packages/bob/invoker.py", line 515, in executeScmSwitch
    await scm.switch(self, oldSpec)
  File "/usr/local/lib/bob/lib/python3.7/site-packages/bob/scm/git.py", line 376, in switch
    await self.invoke(invoker, True)
  File "/usr/local/lib/bob/lib/python3.7/site-packages/bob/scm/git.py", line 177, in invoke
    await self.__checkoutBranch(invoker, fetchCmd, switch)
  File "/usr/local/lib/bob/lib/python3.7/site-packages/bob/scm/git.py", line 201, in __checkoutBranch
    assert not self.__submodules
AssertionError
```


Discussion / hints:
Oh, this case was intended to work but the logic was not implemented this way. I'll look into it...

Fix patch:
diff --git a/pym/bob/scm/git.py b/pym/bob/scm/git.py
index 6490181e9..3e2f03f63 100644
--- a/pym/bob/scm/git.py
+++ b/pym/bob/scm/git.py
@@ -219,18 +219,20 @@ async def __checkoutBranch(self, invoker, fetchCmd, switch):
         elif switch:
             # We're switching the ref. There we will actively change the branch which
             # is normally forbidden.
-            assert not self.__submodules
             if await invoker.callCommand(["git", "show-ref", "-q", "--verify",
                                           "refs/heads/" + self.__branch]):
                 # Branch does not exist. Create and checkout.
                 await invoker.checkCommand(["git", "checkout", "--no-recurse-submodules",
                     "-b", self.__branch, "remotes/origin/"+self.__branch], cwd=self.__dir)
+                await self.__checkoutSubmodules(invoker)
             else:
                 # Branch exists already. Checkout and fast forward...
                 await invoker.checkCommand(["git", "checkout", "--no-recurse-submodules",
                     self.__branch], cwd=self.__dir)
+                preUpdate = await self.__updateSubmodulesPre(invoker)
                 await invoker.checkCommand(["git", "-c", "submodule.recurse=0", "merge",
                     "--ff-only", "refs/remotes/origin/"+self.__branch], cwd=self.__dir)
+                await self.__updateSubmodulesPost(invoker, preUpdate)
         elif (await invoker.checkOutputCommand(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=self.__dir)) == self.__branch:
             # pull only if on original branch
             preUpdate = await self.__updateSubmodulesPre(invoker)


Test patch:
diff --git a/test/git-scm-switch/run.sh b/test/git-scm-switch/run.sh
index 36ff978bf..5f9a88b8f 100755
--- a/test/git-scm-switch/run.sh
+++ b/test/git-scm-switch/run.sh
@@ -7,17 +7,28 @@
 
 git_dir1=$(mktemp -d)
 git_dir2=$(mktemp -d)
-trap 'rm -rf "$git_dir1" "$git_dir2"' EXIT
+git_submod=$(mktemp -d)
+trap 'rm -rf "$git_dir1" "$git_dir2" "$git_submod"' EXIT
 cleanup
 
 # Prepare git repositories
 
+pushd "$git_submod"
+git init .
+git config user.email "bob@bob.bob"
+git config user.name test
+echo sub > sub.txt
+git add sub.txt
+git commit -m import
+popd
+
 pushd "$git_dir1"
 git init .
 git config user.email "bob@bob.bob"
 git config user.name test
 echo "hello world" > test.txt
 git add test.txt
+git submodule add "$git_submod" submod
 git commit -m "first commit"
 git tag -a -m "First Tag" tag1
 git checkout -b foobar
@@ -54,9 +65,20 @@ run_bob dev -DSCM_DIR="$git_dir1" -DSCM_REV="refs/heads/master" root
 expect_output "hello world" cat dev/src/root/1/workspace/test.txt
 expect_exist dev/src/root/1/workspace/canary.txt
 
+# Enabling submodules on branch is ok
+run_bob dev -c submodules -DSCM_DIR="$git_dir1" -DSCM_REV="refs/heads/master" root
+expect_exist dev/src/root/1/workspace/canary.txt
+expect_exist dev/src/root/1/workspace/submod/sub.txt
+
+# But disabling submodules on branch must trigger an attic move
+run_bob dev -DSCM_DIR="$git_dir1" -DSCM_REV="refs/heads/master" root
+expect_not_exist dev/src/root/1/workspace/canary.txt
+expect_not_exist dev/src/root/1/workspace/submod/sub.txt
+
 # Change repository but keep branch. This must move the dir into the attic
 # because they do not share a common history and the branch cannot be
 # forwarded.
+echo canary > dev/src/root/1/workspace/canary.txt
 run_bob dev -DSCM_DIR="$git_dir2" -DSCM_REV="refs/heads/master" root -j
 expect_not_exist dev/src/root/1/workspace/test.txt
 expect_output "hello bob" cat dev/src/root/1/workspace/bob.txt
@@ -71,13 +93,15 @@ expect_output "changed" cat dev/src/root/1/workspace/test.txt
 expect_not_exist dev/src/root/1/workspace/bob.txt
 expect_exist dev/src/root/1/workspace/canary.txt
 
-# Enabling submodules is ok
+# Enabling submodules on tags is ok
 run_bob dev -c submodules -DSCM_DIR="$git_dir1" -DSCM_REV="refs/tags/tag2" root
 expect_exist dev/src/root/1/workspace/canary.txt
+expect_exist dev/src/root/1/workspace/submod/sub.txt
 
-# But disabling submodules must trigger an attic move
+# But disabling submodules on tags must trigger an attic move
 run_bob dev -DSCM_DIR="$git_dir1" -DSCM_REV="refs/tags/tag2" root
 expect_not_exist dev/src/root/1/workspace/canary.txt
+expect_not_exist dev/src/root/1/workspace/submod/sub.txt
 
 # Trying to trigger an inline upgrade for dirty data will fail and move to attic.
 echo canary > dev/src/root/1/workspace/canary.txt