Git error when switching branch after replacing directory with submodule

Tree with many branches and leaves.
Do birds experience errors when they switch branches?

I’m working on updating several WordPress sites. Part of each site’s update includes converting the WordPress core and all frequently used plugins to Git submodules.

While many recommend staying away from submodules all together, I’ve been using them for a while now and never had any issues, that is, until today.

Luckily, it’s nothing severe, and in my case, pretty straight forward to fix (once you know the solution), but because the error didn’t make any sense, I’ve decided to document it here.

The main problem is that I’m unable to checkout the master branch after adding the submodules on the develop branch. Git complains with the following error:

error: The following untracked working tree files would be overwritten by checkout

That said, let’s look at the problem in detail and how we can solve it.

Problem

Let me setup the problem for you, in detail, using an example:

  • You’re working on the develop branch
  • You removed the wp-content/plugins/akismet directory
  • You committed the changes
  • You added akismet back as a submodule
  • You committed the changes

Attempt to checkout master branch

At this point, we’re on the develop branch and trying to checkout the master branch, but we’re greeted with this error:

# git checkout master

error: The following untracked working tree files would be overwritten by checkout:
  wp-content/plugins/akismet/admin.php
  wp-content/plugins/akismet/akismet.css
  wp-content/plugins/akismet/akismet.gif
  wp-content/plugins/akismet/akismet.js
  wp-content/plugins/akismet/akismet.php
  wp-content/plugins/akismet/index.php
  wp-content/plugins/akismet/legacy.php
  wp-content/plugins/akismet/readme.txt
  wp-content/plugins/akismet/widget.php
Aborting

For some reason, Git doesn’t recognize that those files have already been added and committed within the akismet submodule, so it’s aborting the checkout to prevent them from being overwritten.

Review Git status

If there were truly untracked files, running a git status would reveal them, but there aren’t any:

# git status

On branch develop
Your branch is up-to-date with 'origin/develop'.

nothing to commit, working directory clean

Checkout master branch

Since we can’t checkout the master branch the regular way, we’ll force it:

# git checkout master -f

warning: unable to rmdir wp-content/plugins/akismet: Directory not empty
Checking out files: 100% (1993/1993), done.
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.

Git displays a warning that it couldn’t remove our akismet directory, which doesn’t make any sense, because it also exists on the master branch, just not as a submodule.

Review Git status

We check the status again, just to find an untracked image directory:

# git status

On branch master
Your branch is up-to-date with 'origin/master'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)

  wp-content/plugins/akismet/img/

nothing added to commit but untracked files present (use "git add" to track)

To avoid tumbling down the rabbit hole deeper and deeper, let’s switch back to the develop branch.

Checkout develop branch

It seems that everything is back to normal, but after a closer look, all submodule directories are either empty or incomplete. Running another git status reveals that Git does recognize that something changed:

# git status

On branch develop
Your branch is up-to-date with 'origin/develop'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
  (commit or discard the untracked or modified content in submodules)

  modified:   wp-content/plugins/akismet (modified content)

no changes added to commit (use "git add" and/or "git commit -a")

But to avoid any more confusion, let’s just reset all submodules.

Reset submodules

We’ll go inside the akismet directory and hard reset it:

# cd wp-content/plugins/akismet
# git reset --hard

HEAD is now at 645e807 Version 2.5.9

We’re basically back to square one, and to save you all from hours of trial and error, let’s look at the solution.

Solution

The only reason I wanted to checkout the master branch in the first place, was to merge in the develop branch and apply my updates. So let’s go ahead and do that.

Remove submodules

First, we’re going to remove the submodules that caused the conflict:

rm -rf wp-content/plugins/akismet

Note that we’re removing it without Git (rm vs git rm); the goal here is just to get the problem directories out of the way. A git status should display this:

# git status

On branch develop
Your branch is up-to-date with 'origin/develop'.

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

  deleted:    wp-content/plugins/akismet

no changes added to commit (use "git add" and/or "git commit -a")

Not this:

# git status

On branch develop
Your branch is up-to-date with 'origin/develop'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

  modified:   .gitmodules
  deleted:    wp-content/plugins/akismet

Checkout master branch

Second, we’ll checkout the master branch:

# git checkout master

Checking out files: 100% (1993/1993), done.
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.

As you can see, without having to force it, we didn’t get the error we got before.

Merge develop into master branch

Third, we’ll merge our develop branch into the master branch:

# git merge develop

Updating 957cfdc..f3b9cfd
Fast-forward
...
wp-content/plugins/akismet                   |     1 +
wp-content/plugins/akismet/admin.php         |   850 ---
wp-content/plugins/akismet/akismet.css       |    12 -
wp-content/plugins/akismet/akismet.gif       |   Bin 2777 -> 0 bytes
wp-content/plugins/akismet/akismet.js        |   112 -
wp-content/plugins/akismet/akismet.php       |   608 --
wp-content/plugins/akismet/index.php         |     2 -
wp-content/plugins/akismet/legacy.php        |   396 --
wp-content/plugins/akismet/readme.txt        |   153 -
wp-content/plugins/akismet/widget.php        |   108 -
...
create mode 160000 wp-content/plugins/akismet
delete mode 100644 wp-content/plugins/akismet/admin.php
delete mode 100644 wp-content/plugins/akismet/akismet.css
delete mode 100644 wp-content/plugins/akismet/akismet.gif
delete mode 100644 wp-content/plugins/akismet/akismet.js
delete mode 100644 wp-content/plugins/akismet/akismet.php
delete mode 100644 wp-content/plugins/akismet/index.php
delete mode 100644 wp-content/plugins/akismet/legacy.php
delete mode 100644 wp-content/plugins/akismet/readme.txt
delete mode 100644 wp-content/plugins/akismet/widget.php
...

You’ll notice that our akismet directory is now empty, but that’s because the files for the submodule weren’t actually part of the develop branch, so this is expected.

Fetch and update submodules

Lastly, we’ll fetch and update our submodules:

git submodule foreach git fetch --tags
git submodule update --init --recursive

Which will checkout the very versions we chose on the develop branch:

# git submodule update --init --recursive

Submodule path 'wp-content/plugins/akismet': checked out '645e80750a543e2682b2369adfc77814d284b6f3'

That’s it. You’re now on the master branch with all the changes you’ve made on the develop branch.

Conclusion

Granted, this is not the most elegant way to accomplish the task, but it does, nevertheless, fix the problem… and with just a few commands:

rm -rf wp-content/plugins/akismet
git checkout master
git merge develop
git submodule foreach git fetch --tags
git submodule update --init --recursive

If you have any thoughts, questions or problems, let me know in the comments.

Featured image by Lucas van Oort.


Comments (6)

Previously posted in WordPress and transferred to Ghost.

yomguy
December 15, 2014 at 10:50 am

You save me from a strong headhache, thanks a lot!

Vide
March 5, 2015 at 12:09 pm

I’m always struggling with this cause from time to time I’m moving some in-house module to public github’s ones… but sometimes I need to go to master and do something there which is NOT merging back the new branch with the brand new external submodule, any idea on how to fix this?

Ryan Sechrest
March 5, 2015 at 1:59 pm

This is simply a pain point when dealing with submodules. I guess you either have to temporarily get them out of the way, as apposed to removing them permanently, or perhaps move from submodules to subtrees, which would, I believe, circumvent the issue altogether.

Miro
April 14, 2016 at 2:15 am

You just saved my day! Thank You!

aymen
July 2, 2019 at 11:02 am

Thank you

John McNelly
November 19, 2021 at 5:43 pm

Thank you! This was very helpful.