Skip to content
Technology

Git file operations: why git mv and git rm matter

By Victor Da Luz
git version-control development best-practices

I recently reorganized my infrastructure-as-code directory structure, moving from tool-based organization to service-based organization. During this work, I discovered I’d been using regular system commands instead of Git’s dedicated commands for file operations. This seemed like a minor detail, but it matters more than I realized.

When moving files around, I’d typically run mv and then manually stage the changes. Sometimes I’d forget to stage the deletion of the old path, leaving stale references in Git’s index. It worked, but it was inefficient and could confuse Git’s change tracking.

Git provides dedicated commands for file operations on tracked files. Using git mv and git rm instead of mv and rm preserves history, stages changes automatically, and makes diffs clearer.

What git mv and git rm do differently

Git doesn’t store rename operations in its database. Git stores snapshots of content and paths for each commit. What git mv does is prepare the state so Git’s comparison tools can detect the rename. When Git compares commits, it uses heuristics to detect that the content of an added file is very similar to the content of a deleted file, and then displays it as a rename.

git mv automatically stages both the deletion of the old path and the addition of the new path in one atomic operation. This ensures the changes are staged correctly and gives Git’s comparison algorithms the best chance to detect the rename. It’s cleaner than running mv and then manually staging both changes.

Similarly, git rm removes the file from both your working directory and stages the deletion for commit. You can’t forget to stage the removal because the command does both steps.

The difference shows up when Git compares commits. When I moved iac/traefik-ha/ to iac/traefik/ using git mv, Git’s comparison tools detected the rename and showed R status. This means git log --follow can track a file’s history across renames, and diffs show renames clearly instead of separate deletions and additions.

When regular commands are fine

For untracked files, regular system commands work fine. Since Git wasn’t tracking the file initially, there’s no history to preserve. But once a file is tracked, use Git commands.

If you’ve already moved files manually, you can stage everything with git add --all. Git’s comparison algorithms will usually figure out the rename anyway if the content is similar enough. But git mv is cleaner and guarantees the changes are staged correctly in a single atomic operation.

The practical benefit

During my reorganization, I moved several directories using git mv:

git mv iac/traefik-ha iac/traefik
git mv iac/scripts/dns/* iac/pihole/scripts/
git mv iac/configs/nebula-sync.env iac/nebula-sync/configs/

All these operations staged changes automatically and prepared the state so Git could detect them as renames. When Git compared the commits, it recognized these as renames, which makes code reviews clearer and makes it easier to track when files changed, even after reorganization. The file history remains accessible through tools like git log --follow because Git can connect the old and new paths.

This seems like a small detail, but it reflects a broader principle: use tools as they’re designed to be used. Git provides commands for common file operations because they solve real problems. Using git mv and git rm for tracked files is a small change that makes a meaningful difference in repository hygiene.

Ready to Transform Your Career?

Let's work together to unlock your potential and achieve your professional goals.