-1

Take a build machine with git repository and a branch checked out. After a while, things have changed, history rewritten, stuff squashed, cherry-picked and merged somewhere else on dev machines and pushed to remote.

Then the dev gets back to the build machine and hits the build script, which cleans, resets, fetches and then tries to pull some branch and build it. Unfortunately, this is the same branch that was already built on this machine. And since things have changed, it cannot be fast-forwarded and enters merge stage. But the dev does not care about the merge; if they could, they would just delete the repo and clone it again, then checkout the branch from remote.

However, cloning the repo and initializing all submodules will take ages. So, how we can help them? How do you get your git repository in pristine state as after cloning it?

Possible script to solve the problem

git fetch
git clean -dfх
git submodule update --init --recursive
git reset --hard
git checkout -b $1 --track origin/$1 || echo ""
git pull  # <-- nope, would not work

Theoretically the -b $1 could be changed to some -b someuniqueid checking out just "another" branch, and it would probably work. But this is not ideal. Ideas?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
kreuzerkrieg
  • 3,009
  • 3
  • 28
  • 59

2 Answers2

2
  1. If you use submodules and someone rewrote your Subproject commit (a commit you can see with git submodule status) with git push -f, you do not have a way to recover from that. Not even rm -rf ... && git clone ... would help.

Courtesy on git submodule status: https://stackoverflow.com/a/54238999/12118546

  1. The previous answer is not getting rid of any extra (untracked) stuff in the repo and its submodules, I would do this (assuming master branch):
git fetch origin
git reset --hard origin/master
git clean -ffxd
git submodule update --init --recursive --force
git submodule foreach --recursive 'git clean -ffxd'

Courtesy of first two steps: https://stackoverflow.com/a/14787801/12118546

Courtesy of submodule tips: https://www.vogella.com/tutorials/GitSubmodules/article.html

  1. I am not sure whether I did not miss any edge case. Wiping stuff out and cloning fresh repo including submodules is simply the best way to go...
Roman Pavelka
  • 3,736
  • 2
  • 11
  • 28
  • what do you mean by "someone forced out your "Subproject commit""? – kreuzerkrieg Aug 13 '20 at 07:55
  • BTW, `git submodule sync --recursive` could be a nice addition to your sequence. Maybe `--force` on `update` could help too – kreuzerkrieg Aug 13 '20 at 07:57
  • @kreuzerkrieg Submodule usually point to specific commit of repository. If someone did `git push -f` and rewrote that original commit in the repository you use as a submodule, there is no way how to recover from this as your superrepo points to commit that is no longer present in the original repo. It is difficult to say what is correct action in such situation. Sigh, people should not use `git push -f`, then only action you would need is `git reset --hard; git checkout whatever; git pull`. We had implementation of continuous delivery like this, the production did this sequence on every run. – Roman Pavelka Aug 14 '20 at 11:44
  • 1
    Hm... I think I got your point, not that I ever encountered it, but I did encounter cases when the submodule was pointed to a commit that was later squashed and history rewritten, so, technically your repo pointing to non-existing commit. And I'm not familiar with any solution with this situation except locally repointing submodule to a new commit – kreuzerkrieg Aug 15 '20 at 13:11
  • found a new addition to your script above. Look here https://gist.github.com/nicktoumpelis/11214362 sometimes second 'f' is needed – kreuzerkrieg Sep 21 '20 at 20:39
  • @kreuzerkrieg, thank you for addition, I would use `git submodule foreach --recursive 'git reset --hard HEAD` inspired by gist you have shared. However I do not have time now to test it. Double f is explained in documentation, It should replace `git submodule foreach --recursive 'git clean -ffxd'`, but I don't have time to think about all strange edge cases now :-/ – Roman Pavelka Sep 29 '20 at 11:46
  • Take your time, no rush, after all, if not you, then who? :) – kreuzerkrieg Sep 29 '20 at 12:31
0
git reset --hard

This returns all your source files to exactly those from the branch you are in. This carefully if you want to do this, because if you do have changes you want to keep, they can get overwritten.

Now you also described a situation where "git pull" created a new annoying merge version. At this point what you can do is:

git pull 
# oops, a merge created, what a mess
git reset HEAD^    # maybe need more ^'s - see below
git reset --hard 
git pull

The first git reset command will "remove" the last commit - the merge commit - without changing any of your files, so they will continue to contain a lot of crap. So the second command will get rid of all this crap. Then you can "get pull" again to get again the latest version.

You may actually need to do more than one "^" on this HEAD^, if your branch was ahead more than one from the master. Use "git status" to tell you how many revisions you are ahead of master.

Anyway, just do these commands one by one, trying to understand what's going on, and using the right command for the situation. It's hard to give you a specific list of commands (and the specific list I gave above is probably not exactly what you need).

Nadav Har'El
  • 11,785
  • 1
  • 24
  • 45