Git error when switching branch after replacing directory with submodule
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.