Reputation: 410562
Let's say I have a repo that includes this directory structure:
repo/
blog/
_posts/
some-post.html
another-file.txt
I want to move _posts
to the top level of the repo, so the structure will look like this:
repo/
_posts/
some-post.html
another-file.txt
This is simple enough with git mv
, but I want to make the history look as though _posts
always existed at the root of the repo, and I want to be able to get the entire history of some-post.html
via git log -- _posts/some-post.html
. I imagine I can use some magic with git filter-branch
to accomplish this, but I haven't figured out exactly how to do that. Any ideas?
Upvotes: 79
Views: 30726
Reputation: 141
git filter-branch
is discouraged. You can use git filter-repo
is much more convenient:
git filter-repo --path-rename blog/_posts:_posts
See here.
Also note that installation somehow is broken, and you have to install from pip and add git-filter-repo
to $PATH
Upvotes: 4
Reputation: 3482
Created a generic script for arbitrary moves/renames: https://gist.github.com/xkr47/f766f4082112c086af63ef8d378c4304
Examples:
git filter-mv 's!^!subdir/!'
➜ moves all files to a subdirectory "subdir/" in all commits of the current branch
git filter-mv 's!^foo/bar.txt$!foo/barbar.txt!'
➜ renames foo/bar.txt to foo/barbar.txt in all commits of the current branch
Upvotes: 4
Reputation: 3709
You can use the subdirectory filter to achieve this
$ git filter-branch --subdirectory-filter blog/ -- --all
EDIT 1: If you don't want to effectively make _posts
the root, use a tree-filter instead:
$ git filter-branch --tree-filter 'mv blog/_posts .' HEAD
EDIT 2: If blog/_posts
did not exist in some of the commits, the above will fail. Use this instead:
$ git filter-branch --tree-filter 'test -d blog/_posts && mv blog/_posts . || echo "Nothing to do"' HEAD
Upvotes: 109
Reputation: 3195
While Ramkumar's answer is very helpful and worthwile, it will not work in many situations. For example, when you want to move a directory with other subdirectories to a new location.
For this, the man page contains the perfect command:
git filter-branch --index-filter \
'git ls-files -s | sed "s-\t\"*-&NEWSUBDIR/-" |
GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
git update-index --index-info &&
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD
Just replace NEWSUBDIR with your desired new directory. You can also use nested dirs like dir1/dir2/dir3/-"
Upvotes: 40