Copying a specific file from one branch to another is a routine task in Git workflows, yet it often confuses developers who expect a simple git copy command that doesn't exist. Whether you are cherry-picking a hotfix, retrieving a deleted configuration, or merging a feature component without pulling in the entire branch history, understanding the precise commands saves time and prevents merge conflicts. This guide covers every reliable method to transfer files across branches, explains the underlying mechanics, and highlights common pitfalls to avoid The details matter here. Practical, not theoretical..
Understanding the Core Concept: git checkout vs. git restore
Before diving into specific scenarios, it is crucial to understand that Git does not "copy" files in the traditional filesystem sense. Instead, it updates your working directory (and optionally the staging area) to match the version of a file stored in a specific commit or branch pointer.
Not the most exciting part, but easily the most useful.
Historically, git checkout was the Swiss Army knife for this operation. That said, because checkout handles both switching branches and restoring files—two vastly different actions—Git introduced git restore and git switch in version 2.23 to separate these concerns Simple, but easy to overlook..
git checkout <branch> -- <file>: The classic, widely supported syntax. It updates both the working tree and the index (staging area).git restore -s <branch> -- <file>: The modern, specialized command. By default, it updates only the working tree. Add-S(capital S) or--stagedto update the index as well.
Both achieve the same result for the end user: the file in your current branch now looks exactly like it does in the source branch.
Method 1: The Modern Approach (git restore)
If you are on Git 2.23 or newer (released August 2019), this is the recommended way. It is explicit, safer, and easier to read in scripts.
Step-by-Step Workflow
-
Ensure you are on the destination branch. You must be checked out into the branch receiving the file.
git switch main # or git checkout main -
Run the restore command. Use the
-s(source) flag to specify the origin branch.git restore -s feature/login-page -- src/components/LoginForm.jsx -
Verify the change. The file is now updated in your working directory. Check the status:
git status # Output: Changes not staged for commit: modified: src/components/LoginForm.jsx -
Stage and Commit. Since
git restoredefaults to updating only the working tree (not the index), you must stage it manually.git add src/components/LoginForm.jsx git commit -m "feat: import LoginForm from feature/login-page branch"
Pro Tip: To restore and stage in one step (mimicking the old checkout behavior), use the --staged flag:
git restore -s feature/login-page -S -- src/components/LoginForm.jsx
Method 2: The Classic Approach (git checkout)
This method works on virtually any Git version. It remains popular in tutorials, CI/CD scripts, and muscle memory. It updates both the working directory and the staging index simultaneously.
Syntax
git checkout --
Practical Example
Imagine you are on develop and need a specific SQL migration script from the release/v2.0 branch.
git switch develop
git checkout release/v2.0 -- db/migrations/202310_add_index.sql
git status
# Output: Changes to be committed: new file: db/migrations/202310_add_index.sql
Notice the output says "Changes to be committed". The file is already staged. You can commit immediately:
git commit -m "chore: backport migration script from release/v2.
### Copying Multiple Files or Directories
You can use glob patterns or directory paths.
```bash
# Copy an entire directory structure
git checkout feature/ui-overhaul -- src/assets/styles/
# Copy multiple specific files
git checkout hotfix/bug-123 -- src/utils/helpers.js src/tests/utils.test.js
Method 3: Copying from a Specific Commit (Not Just Branch Tip)
Branches are just movable pointers to commits. Sometimes the file you need isn't at the tip of the source branch, but a few commits back. You can reference the commit hash directly Simple, but easy to overlook. Practical, not theoretical..
# Using restore (modern)
git restore -s a1b2c3d -- package.json
# Using checkout (classic)
git checkout a1b2c3d -- package.json
This is invaluable when a file was modified in the source branch after the version you actually want. Use git log <source_branch> -- <file_path> to find the specific commit hash where the file looked correct.
Method 4: Copying a File That Doesn't Exist Locally (New Files)
If the file exists in the source branch but has never existed in your current branch (or was deleted long ago), the commands above work perfectly. Git treats it as a "new file" addition.
On the flip side, if the file exists in your current branch but you want to completely replace it with the version from the other branch (discarding all local history for that file), the standard commands handle this automatically. They overwrite the working tree content.
Warning: If you have uncommitted local changes to that file, they will be overwritten without warning. Always run git status or git stash before restoring.
Method 5: Using git show for Inspection or Piping
Sometimes you don't want to write to the working tree immediately. You might want to inspect the content, pipe it to another command, or save it with a different name.
# View the file content from another branch in terminal
git show feature/new-api:src/config/api.yml
# Save to a different filename locally
git show feature/new-api:src/config/api.yml > src/config/api.v2.yml
# Pipe directly into a tool (e.g., diff against current version)
git show feature/new-api:src/config/api.yml | diff -u src/config/api.yml -
The syntax <branch>:<path> is the "tree-ish" notation. It tells Git: "Look at the tree object pointed to by <branch> and find <path> inside it."
Advanced Scenario: Copying with History (git cherry-pick vs. File Copy)
A common misconception is that copying a file copies its commit history. It does not.
- File Copy (
checkout/restore): Creates a new commit on the destination branch. The file's history in the new branch starts now.git log --follow <file>will not show the commits from the source branch. git cherry-pick <commit>: Applies an entire commit (which may touch 10 files) to your branch. This preserves the commit hash (mostly), author, date, and message.
When to use which?
- Use File Copy when you only need the current state of one file (e.g., a config file, a utility function, a design token).
- Use Cherry-pick when you need the logic of a specific change (e.g., a bug fix commit that modified 3 files) and want to preserve the "why" and "when" metadata.
Handling Merge Conflicts During File Copy
If you have modified the target file locally (and committed it), and the source branch has also modified that file, a simple restore/checkout will overwrite your local version silently (if uncom
If you have modified the target file locally (and committed it), and the source branch has also modified that file, a simple restore or checkout will overwrite your local version silently only when you explicitly ask it to. When both histories contain divergent changes, Git will instead stop and present you with a conflict marker. This is the point where you need to decide which version to keep, merge the changes, or abort the operation.
Resolving Conflicts with git restore
Starting with Git 2.23, git restore gained the --source (-s) option, which lets you pull a file from another ref while handling conflicts in a controlled way:
# Restore the file from the other branch, but stop on conflicts
git restore --source=other-branch --pathspec="src/config/api.yml" src/config/api.yml
If you want to automatically favor one side, you can combine --ours (keep your branch) or --theirs (take the other branch’s version):
# Overwrite with the other branch’s version, discarding local changes
git restore --source=other-branch --ours src/config/api.yml # keeps your version
git restore --source=other-branch --theirs src/config/api.yml # keeps the other branch
After running either command, Git will either complete the operation silently (if there were no conflicting hunks) or pause with a conflict marker inside the file:
<<<<<<< ours
# Your branch’s changes
key: value‑local
=======
# Other branch’s changes
key: value‑remote
>>>>>>> theirs
You can then edit the file, remove the markers, stage the resolution, and continue:
# Edit the file manually (or with your favorite tool)
vim src/config/api.yml
# Stage the resolved file
git add src/config/api.yml
If you decide you no longer want to apply the change, you can abort the restore with:
git restore --source=other-branch --pathspec="src/config/api.yml" --quiet --source=HEAD
Using git checkout for Conflict Resolution
The classic git checkout <branch> -- <path> works similarly, but it does not expose the new --ours/--theirs shortcuts. To achieve the same effect, you can combine it with --merge:
# Force‑fully take the other branch’s version
git checkout other-branch -- src/config/api.yml
If a conflict occurs, you can resolve it exactly as you would after a merge: edit the file, stage it, and then either git merge --continue (if you were in the middle of a merge) or simply git commit if you performed a standalone checkout.
People argue about this. Here's where I land on it Easy to understand, harder to ignore..
Merging Instead of Overwriting
Sometimes you want Git to merge the two versions rather than simply discarding one. This is useful when you are working on a feature branch and need to incorporate a config update from a release branch without losing your own tweaks. The safest way is to perform a merge on the target branch:
# Switch to the branch that currently holds the file
git checkout my