249

I have created a folder common with a bunch of source files and folders.

Now I want to move the common folder into the include folder so it looks like include/common

I tried these:

  1. git add include

  2. git mv common/ include/

    but it fails with this error

    fatal: bad source, source=myrepo/common, destination=myrepo/include

  3. I tried git mv common/ include/common but I get the same error

Any idea how to achieve this?

Community
  • 1
  • 1
lurscher
  • 25,930
  • 29
  • 122
  • 185

15 Answers15

235

One of the nicest things about git is that you don't need to track file renames explicitly. Git will figure it out by comparing the contents of the files.

So, in your case, don't work so hard: Ref: Git mv docs

$ mkdir include
$ git mv common include
$ git rm -r common
$ git add include/common

Running git status should show you something like this:

$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   renamed:    common/file.txt -> include/common/file.txt
#
Aman Jain
  • 655
  • 5
  • 17
Andres Jaan Tack
  • 22,566
  • 11
  • 59
  • 78
  • 65
    This doesn't work for me (using Windows 7, 1.7.6.msysgit.0). Git thinks old files were deleted and new files were added. – Bart Nov 09 '11 at 13:18
  • Hmm, perhaps git uses some external utility to determine file sameness taht doesn't work on Windows? This was a core piece of git's implementation. – Andres Jaan Tack Nov 09 '11 at 15:01
  • 13
    @OliverF. Correction: `git mv` is equivalent. – Andres Jaan Tack Jul 06 '12 at 23:39
  • 1
    Same error here trying to rename a folder called testes to tests, but in my case, is because I do have a branch named tests. I think git try to move all folder contents to tests branch. Renaming by hand works OK. Is this a bug? – Fernando Kosh May 02 '13 at 03:26
  • @Bart try to git rm the old files/locations – jhasse May 31 '13 at 16:58
  • `git log --follow somefile.txt` shows the correct log for files that have undergone a rename. – Jondlm Jan 11 '14 at 00:52
  • 44
    _"One of the nicest things about git "_ — one of the downsides of this nice feature is that it starts failing when you also modify the file that was renamed to a substantial extent, which causes it to see a deletion and addition of a new file, so frankly, I'd prefer explicit rename support instead. – Erik Kaplun Oct 24 '15 at 10:07
  • 3
    If git converts line endings, this results in the problem described by @Bart. For this to work you have to do the following: git config --global core.autocrlf false – Mariano Dupont Dec 16 '16 at 20:50
  • 3
    @ErikAllik Just do it int two separate commits and problem solved – Joe Phillips Mar 30 '18 at 16:08
  • Interesting, I have used that way and I had issues with reloading the moved projects. So not sure why, but I had to modify the *.sln file to point out the correct location of the moved project. – Eru Aug 12 '19 at 10:34
  • Doesn't it lose history then? (and first commit be that of the move) – OverInflatedWalrus Jan 24 '20 at 16:58
  • 1
    @Bart Git may show the deletion and unstaged files, but once you stage both for the next commit, there's a good chance that it [shows the operation as rename](https://stackoverflow.com/a/67063342/1837006). – DanielTuzes Apr 12 '21 at 17:57
194
 git mv common include

should work.

From the git mv man page:

git mv [-f] [-n] [-k] <source> ... <destination directory>

In the second form, the last argument has to be an existing directory; the given sources will be moved into this directory.
The index is updated after successful completion, but the change must still be committed.

No "git add" should be done before the move.


Note: "git mv A B/", when B does not exist as a directory, should error out, but it didn't.

See commit c57f628 by Matthieu Moy (moy) for Git 1.9/2.0 (Q1 2014):

Git used to trim the trailing slash, and make the command equivalent to 'git mv file no-such-dir', which created the file no-such-dir (while the trailing slash explicitly stated that it could only be a directory).

This patch skips the trailing slash removal for the destination path.
The path with its trailing slash is passed to rename(2), which errors out with the appropriate message:

$ git mv file no-such-dir/
fatal: renaming 'file' failed: Not a directory
Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
30

Command:

$ git mv oldFolderName newFolderName

It usually works fine.

Error "bad source ..." typically indicates that after last commit there were some renames in the source directory and hence git mv cannot find the expected file.

The solution is simple - just commit before applying git mv.

Jacinda
  • 4,932
  • 3
  • 26
  • 37
Igor Zvorygin
  • 401
  • 4
  • 2
25

Make sure you have added all your changes to the staging area before running

git mv oldFolderName newFoldername

git fails with error

fatal: bad source, source=oldFolderName/somepath/somefile.foo, destination=newFolderName/somepath/somefile.foo

if there are any unadded files, so I just found out.

Kevin Pluck
  • 641
  • 5
  • 14
5

I'm sorry I don't have enough reputation to comment the "answer" of "Andres Jaan Tack".

I think my messege will be deleted (( But I just want to warn "lurscher" and others who got the same error: be carefull doing

$ mkdir include
$ mv common include
$ git rm -r common
$ git add include/common

It may cause you will not see the git history of your project in new folder.

I tryed

$ git mv oldFolderName newFolderName

got

fatal: bad source, source=oldFolderName/somepath/__init__.py, dest
ination=ESWProj_Base/ESWProj_DebugControlsMenu/somepath/__init__.py

I did

git rm -r oldFolderName

and

git add newFolderName

and I don't see old git history in my project. At least my project is not lost. Now I have my project in newFolderName, but without the history (

Just want to warn, be carefull using advice of "Andres Jaan Tack", if you dont want to lose your git hsitory.

Ivan F.
  • 109
  • 1
  • 8
  • Make sure all your changes have been added. Git fails saying "bad source" if there is unadded changes, so I just found out. – Kevin Pluck Mar 16 '15 at 13:49
4

I had a similar problem with git mv where I wanted to move the contents of one folder into an existing folder, and ended up with this "simple" script:

pushd common; for f in $(git ls-files); do newdir="../include/$(dirname $f)"; mkdir -p $newdir; git mv $f $newdir/$(basename "$f"); done; popd

Explanation

  • git ls-files: Find all files (in the common folder) checked into git
  • newdir="../include/$(dirname $f)"; mkdir -p $newdir;: Make a new folder inside the include folder, with the same directory structure as common
  • git mv $f $newdir/$(basename "$f"): Move the file into the newly created folder

The reason for doing this is that git seems to have problems moving files into existing folders, and it will also fail if you try to move a file into a non-existing folder (hence mkdir -p).

The nice thing about this approach is that it only touches files that are already checked in to git. By simply using git mv to move an entire folder, and the folder contains unstaged changes, git will not know what to do.

After moving the files you might want to clean the repository to remove any remaining unstaged changes - just remember to dry-run first!

git clean -fd -n
Community
  • 1
  • 1
loket
  • 131
  • 3
  • 7
3

Another way to move all files in a directory to a sub directory (keeps git history):

$ for file in $(ls | grep -v 'subDir'); do git mv $file subDir; done;

2

You can use this script.

# git mv a folder and sub folders in windows 

function Move-GitFolder {
    param (
        $target,
        $destination
    )
    
    Get-ChildItem $target -recurse |
    Where-Object { ! $_.PSIsContainer } |
    ForEach-Object { 
        $fullTargetFolder = [System.IO.Path]::GetFullPath((Join-Path (Get-Location) $target))
        $fullDestinationFolder = [System.IO.Path]::GetFullPath((Join-Path (Get-Location) $destination))
        $fileDestination = $_.Directory.FullName.Replace($fullTargetFolder.TrimEnd('\'), $fullDestinationFolder.TrimEnd('\'))

        New-Item -ItemType Directory -Force -Path $fileDestination | Out-Null

        $filePath = Join-Path $fileDestination $_.Name

        git mv $_.FullName $filePath
        
    }
}

Usage

Move-GitFolder <Target folder> <Destination folder>

The advantage of this solution over other solutions is that it moves folders and files recursively and even creates the folder structure if it doesn't exist.

Connor
  • 4,216
  • 2
  • 29
  • 40
Mohammad Hossein Amri
  • 1,842
  • 2
  • 23
  • 43
1

Move your files and use git stage

You can move your files using your favorite tools (command line, graphical file explorer) and git will figure out that you made move instead of deletion and creation once you staged your operations, as suggested by @Andres but if you make too many modifications, as Erik Kaplun pointed out, explicit git mv could help.

Stage is not used in some applications

Some GUI git tools like GitHub Desktop does not provide direct access to stage: you can select and deselect modifications, but it does not modify the underlying git stage. You need to use a more powerful git tool, like the command-line tool or the authentic git GUI to stage the changes, then GitHub Desktop too will show the operation as rename.

DanielTuzes
  • 2,494
  • 24
  • 40
0

I had similar problem, but in folder which I wanted to move I had files which I was not tracking.

let's say I had files

a/file1
a/untracked1
b/file2
b/untracked2

And I wanted to move only tracked files to subfolder subdir, so the goal was:

subdir/a/file1
subdir/a/untracked1
subdir/b/file2
subdir/b/untracked2

what I had done was:

  • I created new folder and moved all files that I was interested in moving: mkdir tmpdir && mv a b tmpdir
  • checked out old files git checkout a b
  • created new dir and moved clean folders (without untracked files) to new subdir: mkdir subdir && mv a b subdir
  • added all files from subdir (so Git could add only tracked previously files - it was somekind of git add --update with directory change trick): git add subdir (normally this would add even untracked files - this would require creating .gitignore file)
  • git status shows now only moved files
  • moved rest of files from tmpdir to subdir: mv tmpdir/* subdir
  • git status looks like we executed git mv :)
test30
  • 3,496
  • 34
  • 26
0

Another important note that I have missed, and fixed the "Bad Source" Error instantly:

(Just thought I will share my mistake just in case someone encounters it.. :))

Make sure the the correct path is selected in the git Console when you run the command:

- git mv Source Destination

If needed, use:

- cd SourceFolder

And then the mv command.

Eibi
  • 402
  • 4
  • 17
0

If you are using GitLab, its integrated Web-IDE, can be used for some file management tasks including move and rename ( without local clone )

Click on Web-IDE on the repository main window.

enter image description here

Right click on the folder and select Rename/Move ( for example moving src to foo/bar/src). The changes will be shown in the web-ide.

Remember to commit!

enter image description here

Alireza Fattahi
  • 42,517
  • 14
  • 123
  • 173
0

With Git Tortoise it is very easy.

Right drag source to destination ( select source, hold the right mouse key, drag to destination, release mouse ) .

A menu will be displayed which has Git Move versioned items and Git Move and rename versioned items.

Of course the git repo should be cloned locally

Git move with git tortoise

Alireza Fattahi
  • 42,517
  • 14
  • 123
  • 173
-1

I solved this on windows by doing this:

  • Open Power shell console
  • run dir
  • Alt-click and drag over the file/folder name column, then copy
  • Paste to notepad++
  • run replace with regex: replace (.*) with git mv ".\\\1" ".\\<New_Folder_Here>\"
  • copy all text from notepad++ into powershell
  • hit enter
FalcoGer
  • 2,278
  • 1
  • 12
  • 34
-1

I resolved this by using this command mv <parent_folder/folder_to_move> <new_folder> while in the working directory of <new_folder>. What this command does is rename the <folder_to_move> into the <new_folder> name. After which you can rename the target folder(folder_to_move) by using mv <new_folder>(formerly <folder_to_move>) <new_name_of_folder>

mv <new_folder> <new_name_of_folder>