I have a git repo in /foo/bar/baz with a large commit history and multiple branches.
I now want /foo/qux to be in the same repo as /foo/bar/baz, which means that I need them both to be in a repo rooted at /foo. However, I want to preserve history of changes that I’ve made to /foo/bar/baz.
I first thought of git format-patch followed by apply, but commit messages aren’t preserved.
So,
I need to reroot the repo
(1) to an arbitrarily higher ancestor directory
(2) while preserving my commit history by making it look like I’ve been comitting to /foo/bar/baz all along
What you want is
git filter-branch, which can move a whole repository into a subtree, preserving history by making it look as if it’s always been that way. Back up your repository before using this!Here’s the magic. In
/foo/bar, run:That will make the
/foo/barrepository have another ‘bar’ subdirectory with all its contents throughout its whole history. Then you can move the entire repo up to thefoolevel and addbazcode to it.Update:
Okay, here’s what’s going on. A commit is a link to a “tree” (think of it as a SHA representing a whole filesystem subdirectory’s contents) plus some “parent” SHA’s and some metadata link author/message/etc. The
git commit-treecommand is the low-level bit that wraps all this together. The parameter to--commit-filtergets treated as a shell function and run in place ofgit commit-treeduring the filter process, and has to act like it.What I’m doing is taking the first parameter, the original tree to commit, and building a new “tree object” that says it’s in a subfolder via
git mktree, another low-level git command. To do that, I have to pipe into it something that looks like a git tree i.e. a set of (mode SP type SP SHA TAB filename) lines; thus the echo command. The output ofmktreeis then substituted for the first parameter when I chain to the realcommit-tree;"$@"is a way to pass all the other parameters intact, having stripped the first off withshift. Seegit help mktreeandgit help commit-treefor info.So, if you need multiple levels, you have to nest a few extra levels of tree objects (this isn’t tested but is the general idea):
That should shift the real contents down into
a/b/bar(note the reversed order).Update: Integrated improvements From Matthew Alpert’s answer below. Without
-- --allthis only works on the currently-checked out branch, but since the question is asking about a whole repo, it makes more sense to do it this way than branch-by-branch.