483

I have a git submodule (RestKit) which I have added to my repo.

I accidentally changed some files in there and I'd like to go back to the source version. In order to do that, I tried to run

Mac:app-ios user$ git submodule update RestKit 

But as you can see here, this did not work as it is still "modified content":

Mac:app-ios user$ git status ... # modified: RestKit (modified content) 

Even

Mac:app-ios user$ git submodule update -f RestKit 

doesn't revert locally modified files.
How do I reset the content of that submodule?

2
  • If git reset --hard doesn't work, first try specifying the remote branch with git reset --hard origin/<branch_name>. Commented Nov 1, 2019 at 19:38
  • I see a lot of answers with many upvotes suggeting different commands or combinations of commands. This is a bit confusing. One also wonders whether some commands are necessary with older versions of git while others only perhaps supported with newer ones. @VonC FYI as well. Commented Jul 27 at 7:46

17 Answers 17

570

A more fail-safe method than all previous answers:

git submodule deinit -f . git submodule update --init 

The first command completely "unbinds" all submodules, the second then makes a fresh checkout of them.
It takes longer than the other methods, but will work whatever the state of your submodules.

If your sub-modules have their own sub-modules, instead of second command, try:

git submodule update --init --recursive --checkout 

Where --recursive is obvious, but --checkout prevents "Skipping submodule ..." (see skip details).

Sign up to request clarification or add additional context in comments.

8 Comments

Regrettably this did not work in my case (with modified local files in a git submodule), the "update --init" command spews error: Your local changes to the following files would be overwritten by checkout
To update specific submodule do: $ git submodule deinit -f -- <submodule_path> and then $ git submodule update --init -- <submodule_path>
I tried all methods above until I got to this one. For me, this is the only one that got my git looking "clean" (without the * in my PS1 that git status -uno was unable to explain).
Leaving a comment for future me - This one worked. Try it first.
I had to run git submodule update --init --recursive instead.
|
408

If you want to do this for all submodules, without having to change directories, you can perform

git submodule foreach git reset --hard 

You can also use the recursive flag to apply to all submodules:

git submodule foreach --recursive git reset --hard 

6 Comments

this works so much better for automation than trying to cd into each submodule directory.
Note that you may also want to git submodule foreach --recursive git clean -x -f -d
on my machine (Windows using Git 2.22.0) I need single quotes around the second git command when using the --recursive flag or it won't work: git submodule foreach --recursive 'git clean -x -f -d '
You should call it with sudo, in case when there are write protected files.
doesn't work for me. new commits are still highlighted red. git restore doesn't work either
|
274

Move into the submodule's directory, then do a git reset --hard to reset all modified files to their last committed state. Be aware that this will discard all non-committed changes.

6 Comments

git submodule update (even without the --init) worked for me to abandon the submodule "changes" when I hadn't actually changed anything. If you go to the submodule directory and git status comes up empty, try this instead of the reset.
git submodule update --init worked for me; without --init it didn't work at all.
reset --hard didn't work for me, my submodule still couldn't be deinit because of local changes.
in addition to @markshiz, git submodule update -f --init for my case.
For me only git submodule update --init --recursive worked.
|
104

Well for me, having

git reset --hard 

just reset the submodule to the state where it checked out, not necessary to the main's repo referenced commit/state. I'll still have "modified contents" like OP said. So, in order to get the submodule back to the corrects commit, I run:

git submodule update --init 

Then when I do git status, it's clean on the submodule.

3 Comments

regretfully submodule update --init does not seem to revert local modifications in my case anyway :|
This was the right answer for me, since I had just done a git pull inside my submodule. Using update I was able to go back to the commit referenced by my main module
I should be able to upvote this answer every time I have to look it up, because I obviously can't remember it.
57

do 4 steps sequential:

git submodule foreach git reset --hard HEAD git submodule update git submodule foreach "git checkout master; git pull" git submodule foreach git clean -f 

3 Comments

question, if the submodule is new, there will not be a .git file inside that directory, correct? will the git command bubble to the parent repo?
@jiahut Even after doing this, I still have "(new commits") next to my submodule when doing 'git status' from the parent?
@DavidDoria git submodule update was what fixed the (new commits) for me.
55

If you want to discard all changes in the entire repository along with submodules, you can use:

git restore . --recurse-submodules 

Note that this only undoes all local-changes (in repository, and in all sub modules).

But git reset ... takes a target-commit-id as input, and can move HEAD to any commit.

1 Comment

this is my best answer as i dont wanna other submodule being downloaded, just the one in such folder
38

This worked for me, including recursively into submodules (perhaps that's why your -f didn't work, cause you changed a submodule inside the submodule):

git submodule update -f --recursive 

Comments

24

First try this, as others have said:

git submodule update --init 

If that doesn't work, change to the submodule directory and use the following command to see if there are any changes to the submodule:

git status 

If there are changes to your submodule, get rid of them. Verify that you can don't see any changes when you run "git status".

Next, go back to the main repository and run "git submodule update --init" again.

Comments

14

Since Git 2.14 (Q3 2017), you don't have to go into each submodule to do a git reset (as in git submodule foreach git reset --hard)

That is because git reset itself knows now how to recursively go into submodules.

See commit 35b96d1 (21 Apr 2017), and commit f2d4899, commit 823bab0, commit cd279e2 (18 Apr 2017) by Stefan Beller (stefanbeller).
(Merged by Junio C Hamano -- gitster -- in commit 5f074ca, 29 May 2017)

builtin/reset: add --recurse-submodules switch

git-reset is yet another working tree manipulator, which should be taught about submodules.

When a user uses git-reset and requests to recurse into submodules, this will reset the submodules to the object name as recorded in the superproject, detaching the HEADs.

Warning: the difference between:

  • git reset --hard --recurse-submodule and
  • git submodule foreach git reset --hard

is that the former will also reset your main parent repo working tree, as the latter would only reset the submodules working tree.
So use with caution.

Comments

12

It is simple:

cd /path/to/submodule/root git submodule update -f --init . 

Most answers are suggesting resetting all the submodule, which I think is not the best approach as there might be legit changes in them.

1 Comment

seems simply git submodule update -f --init /path/to/submodule would work
7

For git <= 2.13 these two commands combined should reset your repos with recursive submodules:

git submodule foreach --recursive git reset --hard git submodule update --recursive --init 

Comments

6

If there's changes in your submodules, then use

git submodule foreach --recursive git reset --hard 

If your changes are remote submodule changes, then use

git submodule update --init 

If these changes have been committed, then use

git reset --hard git submodule update --init 

Comments

4

My way to reset all submodules (without detaching & keeping their master branch):

git submodule foreach 'git checkout master && git reset --hard $sha1' 

1 Comment

What is $sha1 ?
3

This works with our libraries running GIT v1.7.1, where we have a DEV package repo and LIVE package repo. The repositories themselves are nothing but a shell to package the assets for a project. all submodules.

The LIVE is never updated intentionally, however cache files or accidents can occur, leaving the repo dirty. New submodules added to the DEV must be initialized within LIVE as well.

Package Repository in DEV

Here we want to pull all upstream changes that we are not yet aware of, then we will update our package repository.

# Recursively reset to the last HEAD git submodule foreach --recursive git reset --hard # Recursively cleanup all files and directories git submodule foreach --recursive git clean -fd # Recursively pull the upstream master git submodule foreach --recursive git pull origin master # Add / Commit / Push all updates to the package repo git add . git commit -m "Updates submodules" git push 

Package Repository in LIVE

Here we want to pull the changes that are committed to the DEV repository, but not unknown upstream changes.

# Pull changes git pull # Pull status (this is required for the submodule update to work) git status # Initialize / Update git submodule update --init --recursive 

Comments

3

The currently most voted answer (deinit and update) gets rid of every non tracked file.

A way to achieve the same result but keep non tracked files around is to directly checkout all submodules to the commit the parent is pointing to:

git submodule update --checkout --force 

The command is a bit faster too.

Comments

2

In my case, I do it this way:

  1. Go to the submodule path

    cd <submodule_path>

  2. Check the last commit hash

    git log

  3. Reset the change to that last commit hash

    git reset --hard <commit_hash>

Comments

0

I was a bit wary of the reset, hard and force commands, so I did it this way:

Run

git diff | grep Subproject -n2 

For each of the affected repo's the folder is above the change, copy the previous commit id, the minus one.

Change into their directory and

git checkout <commitid> 

When I check on my main repo after that I'm back to no diff's there.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.