87

I have a git repository whose structure looks like:

+--repo.git
|
+----+bootstrap.py
+----+buildout.cfg
+----+.gitignore
+----+webapp
|
+---------+manage.py
+---------+modules
+---------+templates
+---------+static
+---------+...
+---------+...

I would like to move the contents of the webapp folder one level up. My resulting repo should look like:

+--repo.git
|
+----+bootstrap.py
+----+buildout.cfg
+----+.gitignore
+----+manage.py
+----+modules
+----+templates
+----+static
+----+...
+----+...

Can I do this by simply moving all the files of the webapp directory one level up, deleting the empty webapp directory and then committing the changes? Would this preserve the commit history of the files under the webapp directory?

Although a very simple question for many of you, I'd like to be sure. The last thing I'd want is a git soup.


I tried moving the files but I lost the commit history as git doesn't really handle a move or a rename. I do know that even though it shows up as a new file in the logs, it is still possible to view the commit history for the file using some options in git log.

From what I've read, the best way to accomplish this would be using git-filter. I'm not very good with shell or git so could someone tell me what I'd need to execute to do the aforementioned.

Stevoisiak
  • 16,510
  • 19
  • 94
  • 173
Mridang Agarwalla
  • 38,521
  • 65
  • 199
  • 353
  • 1
    If the hashes of your files remain the same, git should detect files moved without `git mv` as moved. You can check this after adding the new files with `git add` using `git status`. – Ninjakannon Feb 20 '19 at 19:42

9 Answers9

74

The right way to do this is:

git mv repo.git/webapp/* repo.git/.
git rm repo.git/webapp
git add * 
git commit -m "Folders moved out of webapp directory :-)"
Nikita Fedyashev
  • 15,938
  • 11
  • 41
  • 69
Sumeet Pareek
  • 2,469
  • 2
  • 20
  • 17
  • 5
    This doesn't work for me. What directory did you run this in? – wharding28 Mar 05 '14 at 22:25
  • 6
    This worked perfectly for me, thanks! Just to be clear - if you're currently in the git directory, you just need to do `git mv /path/new/old/* /path/new/.` where `path/new/old` is where the files currently are and `/path/new` is where you want them to be. The `*` represents the files themselves, rather than the directory. – Lucy Bain Jul 22 '14 at 03:55
  • Your welcome @LucyBain and +1 for leaving a helpful explanation for others. – Sumeet Pareek Jul 22 '14 at 07:06
  • 56
    I'm getting `fatal: bad source, source=webapp/*, destination=*` This is not working at all ;( – Andrew Savinykh Apr 27 '17 at 07:50
  • 5
    @AndrewSavinykh, as JLRishe mentioned in another answer, this seems to be an issue isolated to Windows shell, but if running in Git Bash it should work. – Alec Apr 03 '19 at 03:43
  • To be clear, `git mv` does nothing more than move the file and commit that move. When the files are staged, git compares the "new" files with the "old" ones and decides whether they are the same or not https://stackoverflow.com/questions/7938582/how-does-git-detect-similar-files-for-its-rename-detection – Sheraff Nov 02 '20 at 20:18
36

Another variant of Sumeet's answer - in the repository directory above "webapp" execute following command:

git mv webapp/* ./ -k

-k - Skip move or rename actions which would lead to an error condition, otherwise you get:

fatal: not under version control, source=webapp/somefile, destination=somefile
Matthias M
  • 8,008
  • 11
  • 67
  • 83
Alex
  • 479
  • 4
  • 6
  • 2
    Fatal. I first moved away those files manually before I saw your answer ;-) – GhostCat Mar 30 '17 at 09:27
  • 1
    What about other branches? Will I need to rebase them too? Thanks! – Matifou Oct 30 '18 at 05:20
  • 1
    > What about other branches? Will I need to rebase them too? - Answer: you are making these changes in a particular branch, therefore you would need to perform similar changes for each branch separately. – Alex Dec 19 '18 at 15:24
9

I was able to get it to work simply by doing this from the destination folder:

git mv webapp/* .

It seems this doesn't work in the Windows shell (fails with an error Bad source), but it will work in Windows if you use the Git Bash shell, which expands the * wildcard.

JLRishe
  • 90,548
  • 14
  • 117
  • 150
7

in windows you can do the below:

While you are in the child folder

for /f %f in ('dir /b') do git mv %f ../

The result all objects in child folder will be in the parent folder

Please note: some errors may raise when you have object in the child folder with name equals to the child folder

Ammaroff
  • 859
  • 6
  • 20
5

The solution you mentioned should work, as git tracks changes based on the hash of the files first and then their location.

This wont work if as part of moving the file, you change the contents of the files.

Bottom case, try it, and if it doesn't work, you can revert the changes, before pushing the changes to the master repo :). This is one of the reasons why I really like git.

Edit

I forgot to mention that to see the changes after a rename, you need to use the '--follow' parameter. Check this example

First, I created a new git repo

94:workspace augusto$ mkdir gittest
94:workspace augusto$ cd gittest/
94:gittest augusto$ git init
Initialized empty Git repository in /Volumes/Data/dev/workspace/gittest/.git/

Then created a file in folder/test

94:gittest augusto$ mkdir folder
94:gittest augusto$ vi folder/test
94:gittest augusto$ git add folder/test
94:gittest augusto$ git commit -am "added file"
[master (root-commit) 7128f82] added file
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 folder/test

Then moved the file to newfolder/test

94:gittest augusto$ mkdir newfolder
94:gittest augusto$ mv folder/test newfolder/
94:gittest augusto$ git add newfolder/test 
94:gittest augusto$ git commit -am "moved/renamed file"
[master 4da41f5] moved/renamed file
 1 files changed, 0 insertions(+), 0 deletions(-)
 rename {folder => newfolder}/test (100%)

And git log --follow newfolder/test shows the full history (I've added the parameter -p to show more information, such as the path).

94:gittest augusto$ git log --follow -p newfolder/test 
commit 4da41f5868ab12146e11820d9813e5a2ac29a591
Author: Augusto Rodriguez <xxxx@gmail.com>
Date:   Sat Aug 20 18:20:37 2011 +0100

    moved/renamed file

diff --git a/folder/test b/newfolder/test
similarity index 100%
rename from folder/test
rename to newfolder/test

commit 7128f8232be45fd76616f88d7e164a840e1917d5
Author: Augusto Rodriguez <xxxx@gmail.com>
Date:   Sat Aug 20 18:19:58 2011 +0100

    added file

diff --git a/folder/test b/folder/test
new file mode 100644
index 0000000..3b2aed8
--- /dev/null
+++ b/folder/test
@@ -0,0 +1 @@
+this is a new file

I hope this helps!

Community
  • 1
  • 1
Augusto
  • 26,525
  • 5
  • 52
  • 82
3

If you are using PowerShell you can run this command from your project root and it will place the content of webapp there.

Get-ChildItem .\webapp\ | ForEach-Object { git mv $_.FullName .\ }
Nick
  • 899
  • 1
  • 13
  • 26
1

In Windows , using Bash , following worked:

git mv /c/REPO_FOLDER/X_FOLDER/Y_FOLDER/Z_FOLDER/* /c/REPO_FOLDER/X_FOLDER/Y_FOLDER
barbsan
  • 3,238
  • 11
  • 18
  • 27
arupjbasu
  • 121
  • 4
1

Yes you can simply move the files over. However you do need to tell git that the old files in the webapp folder have gone, that is, git needs to update its index of finished/committed files.

So you can use git add -A . to make git notice all the changes, or use the git mv <files> to tell git to do the move itself. See the git mv man page.

--update.

You noted that you thought ".. git doesn't really handle a move or a rename.." - I was also confused at first and hadn't fully understood the way the Index works. On the one hand folk say git only takes snapshots and doesn't track renames, but then you get hit with it 'failing' if you update .gitignore, or mv a file, etc. This 'failure' is a confusion about how the Index works.

My visualisation is that the Index/Staging area is a place, like a storyboard wall, where you place a copy of your latest and greatest 'finished' file, including its path, (using git add), and it is that copy that is committed. If you don't take that copy down from the storyboard wall (i.e. git rm), then git will continue to commit it, and confusion abounds (see many SO questions...). The Index is also used by git during merges in a similar manner

Philip Oakley
  • 11,745
  • 8
  • 42
  • 63
0

Yes, git will track the changes in past content. It uses hashes of the file's content so regardless of where they are located in the directory structure, they will be the same file.

As a result, you should do the move in one commit, and then fix up any edits in a subsequent commit. That will enable Git to determine the redirection with maximum efficiency, and won't make any difference to the amount of data stored in the repository (since you'd be making those changes anyway).

AlBlue
  • 20,872
  • 14
  • 63
  • 85