Image of Git Mv | How To Use Git-Mv To Rename And Move Files

ADVERTISEMENT

Table of Contents

Rename and move files in Git

Developers using Git often find they need to rename or move files or directories in their software development projects. This can be achieved using the standard Unix mv command, but Git offers a more efficient alternative git mv.

Although the concepts are quite simple, there is some nuance and a few edge-cases to be aware of. We recommend that you learn git mv to maximize Git understanding and collaboration, which can help you brand yourself as a strong asset to any team.

In this article we'll discuss:

  • Renaming files in Git
  • Moving files in Git
  • The difference between mv and git mv
  • Git mv options
  • Git mv examples
  • Using git mv on directories
  • Git mv case sensitivity

We consider git mv to be an intermediate Git command, on-par with others such as git rm, git diff, git tag, and other commands that are slightly more advanced than beginner Git commands such as git add and git commit.

What is git mv?

According to the Git FAQ, "The effect [of git mv] is indistinguishable from removing the file and adding another with different name and the same content" (1).

This means git mv is equivalent to the following sequence of three commands:

  1. mv old_file.ext new_file.ext
  2. git add new_file.ext
  3. git rm old_file.ext

These series of steps uses the Unix/Linux mv command to change the file name. Then it uses the git add command to stage the new version of the file and the git rm command to remove the file from Git's tracking.

So, the git mv command streamlines the process of renaming or moving files for us by inherently updating Git's index for our new paths.

How do I use git mv?

To move or rename a file using git mv, simply specify the existing and new paths of the file after the command:

git mv ./old_directory/old_file.ext ./new_directory/new_file.ext

In this case, we are both moving and renaming the file, and making Git aware of those changes.

Git mv vs mv

So, what’s the difference between running git mv vs a simple mv command? The answer lies in in the purpose of Git itself: to efficiently track changes.

Git mv automatically stages the moved file, so a simple git commit will suffice after your move or rename is complete.

Note, however, that if you make changes to a file prior to running git mv on it, these changes will not be staged until you run git add. This is because git does a great job of only staging intended changes. In the world of Git, changes to code take precedent over the renaming of a file.

In other words, Git doesn't presume you’re ready to stage your code updates simply because you renamed a file.

Another difference worth noting is that the regular mv command will overwrite a destination file of the same name without asking, whereas git mv will refuse the action, and notify you that the destination file already exists. That is, of course, unless you tell Git to do otherwise by using the options included with the git mv command.

Git mv options (-f, -k, -n, -v)

git mv -f: This option is one way to resolve the issue of the destination file already existing. By using the -f flag, we are telling Git to overwrite the destination with the source file we would like to move. Remember to use caution when using the -f option; if you overwrite a file only to find out later you need the contents, it could result in a time consuming search through Git's history.

git mv -k: This option will allow Git to simply skip any erroneous conditions resulting from a git mv call. Remember, the default behavior of Git is to throw an error if you attempt to move a file to a destination that already exist. With the -k flag, rather than throwing an error or overwriting the destination, Git will simply skip over this action.

git mv -n: Shorthand for --dry-run, and will show you the output of the command in question without actually performing any of the changes.

git mv -v: Shorthand for --verbose, this command will report the names of the files in question as they are moved.

See the git mv docs for more details.

Git mv Examples

Let’s take a look at a few examples; consider the following directory tree in a Git repository, with a folder named git-mv/ at the root, two files that Git knows about, and two empty subdirectories dir1/ and dir2/:

git-mv/
| simplefile1.ext
| simplefile2.ext
| dir1/  
| dir2/

Let's try out a simple rename command with git mv:

> git mv simplefile1.ext simplefile3.ext

This renames simplefile1.ext to simplefile3.ext, but let’s take a closer look:

> git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	renamed:    simplefile1.ext -> simplefile3.ext

As we can see, Git has staged the rename for us, saving us the extra effort. Let’s try moving our newly renamed file into the dir1/ directory, and take a look at how Git tracks that:

> git mv simplefile3.ext dir1/
>
> git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	renamed:    simplefile1.ext -> dir1/simplefile3.ext

Be careful using the git restore command to unstage the file, as specified in the [git status](/blog/git-status) output! This will unstage the file, but it won't undo the renaming or file move.

We can also run git diff --staged to get some additional perspective:

> git diff --staged
diff --git a/simplefile1.ext b/dir1/simplefile3.ext
similarity index 100%
rename from simplefile1.ext
rename to dir1/simplefile3.ext

Wait a minute! What happened to our initial rename (before moving the file into dir1/? Is Git’s tracking mechanism glitching out on us?

Quite the opposite - what Git is doing here is tracking the current state of the file, since the intermediate form was never committed. The simplefile1.ext now exists in dir1/ and is called simplefile3.ext. There is no need to track the fact the actual file rename happened separately from the move (in fact it could have all been done in 1 step). The git diff algorithm shows differences relative to the current state of the changed file, (which combines the result of the two changes), along with keeping it staged and ready to commit. Pretty cool.

Can I git mv a directory?

Another obvious need arises when we want to move a directory into (or out of) another directory within our project. Git mv comes equipped to handle this with ease; let’s try moving our dir1/ directory into dir2/ and see what happens:

> git mv dir1/ dir2/
>
> git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	renamed:    dir1/simplefile3.ext -> dir2/dir1/simplefile3.ext

As git status reports, dir1/ was moved into dir2/ on the filesystem and git is aware of this change.

But what about in the case of nested files and directories - is git mv recursive? Let’s find out:

> mkdir dir3
> 
> git mv dir2/ dir3/
> 
> git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	renamed:    dir1/simplefile3.ext -> dir3/dir2/dir1/simplefile3.ext

As you can see, our nested folders, along with their contents, were successfully moved. Git mv will move your directories recursively, without any extra flags or options.

Note that if the destination directory dir3/ in the previous example didn't exist yet, the source directory dir2/ would have been renamed instead of moved. But since we created dir3/ using the mkdir command, the whole directory tree under dir2/ was moved into dir3/.

Git mv case sensitivity

One more cool feature of Git's mv command is the ability to rename files with specific casing. For those of us who tend to stick with specific naming conventions such as camelCase, this can come in handy for cleaning up files in a project directory to suit your casing preference.

Let’s try changing simplefile2.ext to camel case and see what happens:

> git mv simplefile2.ext simpleFile2.ext
> 
> git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	renamed:    simplefile2.ext -> simpleFile2.ext

Nice! Our case change is locked and loaded and ready for a git commit. Compared with an mv command, this is excellent. The mv by itself not only lacks the ability to stage our changes, it’s also unwilling to entertain our casing quirks. The git mv is certainly the way to go.

Summary

Git, like many other command line interfaces, comes with some pretty neat features. However, as with most tools, it’s important to take a finer look in order to understand the strengths of what you’re working with. The git mv feature works seamlessly with the git workflow, and now you can move files and rename files with confidence!

Next Steps

If you're interested in learning more about how Git works under the hood, check out our Baby Git Guidebook for Developers, which dives into Git's code in an accessible way. We wrote it for curious developers to learn how Git works at the code level. To do this we documented the first version of Git's code and discuss it in detail.

We hope you enjoyed this post! Feel free to shoot me an email at jacob@initialcommit.io with any questions or comments.

References

  1. Git FAQ https://git.wiki.kernel.org/index.php/Git_FAQ#Why_does_Git_not_.22track.22_renames.3F

Final Notes