Nick Williams
Nick Williams

Reputation: 3198

How do I delete a mistakenly-migrated .git directory from the tree of a Git repository?

Several months ago, we migrated a bunch of Subversion repositories to Git using svn-all-fast-export. (That information explains how this directory got in the Git directory in the first place, but probably isn't pertinent to removing it.) Today, one of our developers attempted to git checkout an old branch of one of those repositories, only to be confronted with an error:

$ git checkout 2.3.4
error: invalid path 'MPC/.git/HEAD'
error: invalid path 'MPC/.git/config'
error: invalid path 'MPC/.git/description'
error: invalid path 'MPC/.git/hooks/applypatch-msg.sample'
...
error: invalid path 'MPC/.git/packed-refs'
error: invalid path 'MPC/.git/shallow'
$

A quick bit of sleuthing in our archived Subversion repository revealed that someone had committed a .git directory (surely by mistake) to the MPC directory years ago. They deleted it in the next branch. When the repository and its branches and commit history were migrated, that .git directory got written to the Git tree, essentially "breaking" this branch. No big deal, right? Just delete the directory! Well ... how?

I can't check out the branch, so I can't simply git rm. I tried git clone --single-branch --branch 2.3.4 but, no surprise here, I got the same error. Exactly what I was expecting. So I cloned with git --sparse, but to my surprise, that still didn't work. The default sparse patterns:

/*
!/*/

Should mean that only the top-level files get checked out, but not anything under MPC. However, when I try to check out the branch, I still get the same errors about that .git directory, and the top-level of the working copy is completely empty. I've tried adding specific excludes such as !.git, !\.git, !.git/, !\.git/ !MCP, !/MPC, !/MPC/.git, !/MPC/\.git, !/MPC/.git/, !/MPC/\.git/ !/*/.git, !/*/\.git, !/*/.git/, and !/*/\.git/, and even more than that, but every pattern I try just gives me this error:

warning: unrecognized pattern: [the pattern I tried]
warning: disabling cone pattern matching
error: invalid path 'MPC/.git/HEAD'
...

Or:

warning: unrecognized negative pattern: [the pattern I tried]
warning: disabling cone pattern matching
error: invalid path 'MPC/.git/HEAD'
...

I do wonder if some combination of git mktree, git write-tree, and/or git commit-tree can help me here, but I don't even know where to start with those commands.

To be clear ... if this becomes a show-stopper issue for my team and I can't find an easier solution, I will archive all new commits to this repo, re-migrate it, and re-apply all new commits. But I don't want to do that if I don't absolutely have to. That's days of time down the drain. There's gotta be a way to delete this ... right?

Upvotes: 1

Views: 106

Answers (1)

torek
torek

Reputation: 489708

Unfortunately, no existing commit can ever be changed. Fixing this means rewriting history, e.g., using git filter-repo (the newfangled thing that replaces the oldfangled git filter-branch).

There's a pretty high (and clear) cost to this kind of history rewrite: everyone has to abandon the original repository, and all of its clones, in favor of the new rewritten repository. So if you can live with the bad commit continuing to exist, you might want to do that. On the other hand, the "fix the repository" thing is a one-time flag-day.

You can clone the repository without checking out the "bad" commit, then make a new "good" commit that's basically the bad commit minus the .git directory, with the parent being the "bad" commit at the tip of the old branch, and make the branch name select the new good commit. Actually achieving the new good commit is a bit of an exercise in frustration since you'll need to extract the bad commit without extracting the bad commit. I'd poke around with git archive and maybe even git fast-export here, and if all else fails, build a hacked up Git that has the "bad directory .git" checking snipped out, so that you can run hacked-git checkout to check out the bad commit, then git rm -r MPC/.git and git commit (or hacked-git if/as needed for the git rm step, depending on whether the code triggers here).

Upvotes: 1

Related Questions