8

is it possible with Git's tools to move files into a new folder while modifying its full history, as if the files had been there from their first add?

I came up on this after merging some repos together: I moved the files from several repos to distinct folders inside one "super" repo, however the merged history behaves very bad with git rebase and git svn tools as totally different files may collide at their old locations of course.

dronus
  • 10,774
  • 8
  • 54
  • 80

3 Answers3

7

So now this got the job done:

git filter-branch --tree-filter '(ls -A; mkdir TheSubdir; echo TheSubdir) | xargs mv'

Strange but nice: the .git dir stays where it should; maybe git is preventing its movement somehow?

dronus
  • 10,774
  • 8
  • 54
  • 80
  • 1
    The 'filter' shell commands are run in an subdir .git-rewrite by git, with nothing but the current revision files inside. Cool. – dronus Apr 15 '11 at 00:26
  • We got error (probably because repo was empty on some commit). Solution was add `true` to pipeline's end to skip error like `git filter-branch --tree-filter '(ls -A; mkdir xid; echo xid) | xargs mv | true '` – Yuriy Alevohin Aug 09 '18 at 08:33
5

In order to modify your full history, you will need to rewrite every commit. git filter-branch is the best way to do this. In particular, for your case, git filter-branch --tree-filter some-command will check out every version of your history, run your command in which you can modify the working tree as you see fit (such as moving the directories in question), and then rewrite that commit to contain the new contents of the tree.

Brian Campbell
  • 322,767
  • 57
  • 360
  • 340
  • Ok cool. So basically, `git filter-branch` checks out every revision and checks it in again, replacing the old one? Sounds wild, I thought I would just rewrite the stored pathes.. Anyway, I give it a try! – dronus Apr 14 '11 at 20:16
  • @dronus Yep, that's pretty much what it does. – Brian Campbell Apr 14 '11 at 20:20
  • @dronus Actually, for your case, in which you're just moving things around, you should be able to do a `git filter-branch --index-filter`, which is like `--tree-filter` but doesn't actually check the files out, it requires you to manipulate everything in the index directly. It will be faster to run than `--tree-filter`, but will be a little harder to write since you'll need to be modifying the index directly instead of just moving files. There's an example towards the end of the `filter-branch` documentation that demonstrates how to do it. – Brian Campbell Apr 14 '11 at 20:29
  • I don't know why, but `--index-filter` doesn't iterate all revisions in my branch. Maybe has something to do with merges enroute etc. But `--tree-filter` worked very well; see below. – dronus Apr 15 '11 at 00:18
  • @dronus Yeah, as I said, it's a little harder with `--index-filter`, because it doesn't actually check out the revisions. You need to do all of the work via the index; there's an example in the documentation I linked to. Anyhow, the only reason you'd want to do that is because it's faster; if `--tree-filter` is fast enough for you, it's perfectly fine, and much simpler to user. – Brian Campbell Apr 15 '11 at 02:03
0

The command in the accepted answer will fail for files/directories containing spaces etc. The following version is safe:

git filter-branch -f --tree-filter 'mkdir TheSubdir; \
find . -maxdepth 1 -name TheSubdir -prune -o -name . -true -o -print0 | \
xargs -0 mv -t TheSubdir'
hlovdal
  • 26,565
  • 10
  • 94
  • 165