Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Move file and directory into a sub-directory along with commit history

Tags:

git

git-log

How can I move a directory and files to a sub-directory along with commit history?

For example:

  • Source directory structure: [project]/x/[files & sub-dirs]

  • Target directory structure: [project]/x/p/q/[files & sub-dirs]

like image 314
rst.ptl Avatar asked Sep 06 '13 22:09

rst.ptl


People also ask

How do I move a directory in git?

The 'git mv' command is used for moving or renaming a file or directory. When you provide <destination> as the same type (file or directory), it renames the <source> file/directory (which must exist) to <destination>. file/directory.

How do I move files into a folder in github?

In your repository, browse to the file you want to move. In the upper right corner of the file view, click to open the file editor. To move the file into a subfolder, type the name of the folder you want, followed by / . Your new folder name becomes a new item in the navigation breadcrumbs.


2 Answers

To add to bmargulies's comment, the complete sequence is:

mkdir -p x/p/q      # make sure the parent directories exist first git mv x/* x/p/q    # move folder, with history preserved git commit -m "changed the foldername x into x/p/q" 

Try it first to see a preview of the move:

git mv -n x/* x/p/q 

Wolfgang comments:

If you're using bash, you can avoid the issue of trying to move a folder into itself by using an extended glob like so (using the shopt built-in):

shopt -s extglob; git mv !(folder) folder 

Captain Man reports in the comments having to do:

mkdir temp  git mv x/* temp mkdir -p x/p/q git mv temp x/p/q rmdir temp; 

Context:

I am on Windows with Cygwin.
I just realized I did the shopt -s extglob example wrong so my way may not have be necessary, but I typically do use zsh instead of bash, and it didn't have the command shopt -s extglob (though I'm sure there is an alternative), so this approach should work across shells (subbing in your shell's mkdir and rmdir if it's especially foreign)


As an alternative, spanky mentions in the comments the -k option of git mv:

Skip move or rename actions which would lead to an error condition.

git mv -k * target/ 

That would avoid the "can not move directory into itself" error.

like image 187
VonC Avatar answered Sep 25 '22 04:09

VonC


Git does a very good job to track content even if it is moved around, so git mv is clearly the way to go if you move files because they used to belong in x, but now they belong in x/p/q because the project evolved that way.

Sometimes, however, there is a reason to move files to a subdirectory throughout the history of a project. For example if the files have been put somewhere by mistake and some more commits have been made since, but the intermittent commits don't even make sense with the files in the wrong place. If all that happened on a local branch, we want to clean up the mess before pushing.

The question states "along with commit history", which I would interpret as rewriting the history in exactly that way. This can be done with

git filter-branch --tree-filter "cd x; mkdir -p p/q; mv [files & sub-dirs] p/q" HEAD 

The files then appear in the p/q subdirectory throughout the history.

The tree filter is well suited for small projects, its advantage is that the command is simple and easy to understand. For large projects this solution does not scale very well, if performance matters then consider to use an index filter as outlined in this answer.

Please note that the result should not be pushed to a public server if the rewrite touches commits which were already pushed before.

like image 26
PiQuer Avatar answered Sep 26 '22 04:09

PiQuer