Git checkout files from another branch is a fundamental skill that separates casual Git users from developers who can manage complex histories with precision. In practice, while merging or rebasing brings entire histories together, there are countless scenarios where you only need a specific file, a configuration snippet, or a bug fix from a parallel line of development without dragging along the rest of the branch's baggage. Mastering this technique keeps your history clean, your merges conflict-free, and your workflow agile.
Understanding the Core Concept
Before diving into commands, it helps to visualize what Git actually does. A branch in Git is simply a lightweight movable pointer to a commit. Think about it: when you "checkout a file from another branch," you are not switching branches. You are reaching into the object database, finding the version of that file as it existed at the tip of the target branch (or a specific commit), and writing that version into your current working directory and staging area That's the part that actually makes a difference..
This distinction is critical. And you are essentially saying, "Make my working copy of config. Your HEAD remains attached to your current branch. And yml look exactly like it does on feature/login-refactor right now. " This operation is surgical, precise, and reversible until you commit the change.
The Modern Approach: git restore
If you are using Git version 2.Because of that, to improve clarity, the Git maintainers split these responsibilities. Now, 23 or later (released in 2019), the recommended command for this task is git restore. In real terms, the older git checkout command is overloaded—it handles switching branches, restoring files, and discarding local changes all at once. git switch handles changing branches; git restore handles restoring file contents.
Basic Syntax
To grab a file from another branch and place it in your current working directory, the syntax is remarkably intuitive:
git restore --source --
--source(or-s): Specifies the commit-ish (branch name, tag, or commit hash) to pull the file from.--: Separates the options from the file path. This is best practice to avoid ambiguity if a file name matches a branch name.<file_path>: The path to the file relative to the repository root.
Practical Example
Imagine you are on main. Also, your colleague has fixed a tricky CSS bug on the branch fix/header-alignment. You want that specific styles/header.css file right now.
git restore --source fix/header-alignment -- styles/header.css
Run git status. On top of that, you will see styles/header. css listed under "Changes to be committed." The file is staged automatically.
# Places the file in working directory only (unstaged)
git restore --source fix/header-alignment --worktree -- styles/header.css
This flexibility allows you to diff the incoming changes against your current version using git diff before committing them to your branch history That's the whole idea..
The Classic Approach: git checkout
Despite the introduction of git restore, the classic git checkout syntax remains ubiquitous in tutorials, Stack Overflow answers, and muscle memory. It is still fully supported and will not be removed. For the specific task of grabbing a file, the syntax mirrors the restore command but uses the branch name as the first argument:
git checkout --
Using the previous example:
git checkout fix/header-alignment -- styles/header.css
This command does two things instantly:
- Updates the index (staging area) to match the version on
fix/header-alignment. - Updates the working tree to match.
Key Difference: Unlike git restore, the classic git checkout command always stages the file. There is no native --worktree flag to leave it unstaged. If you want it unstaged using the old command, you must run git reset HEAD -- <file_path> immediately after Worth keeping that in mind..
Advanced Scenarios: Beyond the Branch Tip
Real-world development rarely stays at the branch tip. You often need a file as it existed three commits ago, or from a specific tag, or from a commit hash you found in git log.
Restoring from a Specific Commit
Both commands accept any commit-ish for the source.
Using git restore:
git restore --source a1b2c3d -- src/utils/helpers.js
Using git checkout:
git checkout a1b2c3d -- src/utils/helpers.js
This is invaluable when bisecting a bug. You can "time travel" a single file to a known good state to verify a regression without resetting your entire branch.
Restoring a File to a Different Location or Name
Sometimes you need the content of a file from another branch but want to save it under a different name in your current branch (e.g.And , creating a backup or a variant). Standard restore and checkout write to the exact same path. To write elsewhere, you must pipe the git show output But it adds up..
This changes depending on context. Keep that in mind.
# Show the file from the other branch and redirect to a new path
git show feature/new-api:config/production.json > config/staging.json
Here, feature/new-api:config/production.This writes the raw blob content directly to your filesystem, bypassing the index entirely. Worth adding: json uses the <rev>:<path> syntax. Plus, you will need to git add config/staging. json manually.
Restoring Multiple Files or Directories
You can specify multiple paths or use glob patterns (though glob behavior depends on your shell).
# Restore multiple specific files
git restore --source develop -- src/index.js src/app.js
# Restore an entire directory recursively
git restore --source develop -- src/components/
Note: If you use git checkout develop -- src/components/, it restores the directory contents recursively by default.
Handling Conflicts and Overwrites
A common fear is losing local work. If you have uncommitted changes to styles/header.css in your working directory and you run the restore/checkout command, Git will overwrite your local changes without warning (unless the file has merge conflicts, which is rare for a straight restore).
Always run git status first. If you have precious uncommitted changes, you have three safe options:
- Commit them:
git add . && git commit -m "WIP: save local changes". - Stash them:
git stash push -m "backup before restore". You can pop them later and merge manually. - Backup manually: Copy the file outside the repo temporarily.
The "Patch Mode" Superpower: Interactive Selection
What if you only want part of a file from another branch? On the flip side, perhaps the branch has a massive refactor, but you only need the one bug fix inside a specific function. This is where git checkout --patch (or git restore --patch in newer versions) shines.
git checkout --patch feature/big-refactor -- src/core/engine.js
Git will open an interactive prompt, showing you "hunks" (chunks of changes) one by one. * n: Skip this hunk.
e: Manually edit the hunk (advanced). Day to day, *q: Quit (skip this and all remaining hunks). So for each hunk, you can press:y: Apply this hunk. *s: Split the hunk into smaller pieces.
This allows you to cherry-pick logic at the line level, creating a Frankenstein file that combines your current code with only the specific improvements from the other branch. It is one of the most powerful tools for surgical code integration.
Common Pitfalls
Common Pitfalls
1. Confusing Branch Names with Paths
If you have a branch named feature/login and a directory named feature/login, Git prioritizes the revision (branch/tag/commit) over the path.
# Ambiguous: Git thinks 'feature/login' is a branch/tag
git restore --source main -- feature/login
# Explicit: The '--' separator tells Git everything after is a path
git restore --source main -- feature/login/
Always use -- to separate revision arguments from file paths to avoid this ambiguity Not complicated — just consistent. And it works..
2. Forgetting the Working Directory vs. Staging Area Distinction
git restore (without --staged) updates the working directory. git restore --staged updates the index (staging area) Simple, but easy to overlook..
- Scenario: You restored a file from
main, butgit statusstill shows it as "modified" or "staged." - Cause: You likely had the file staged previously. The restore updated the working tree, but the index still holds the old version (or vice versa).
- Fix: Run
git restore --staged <file>to sync the index, orgit add <file>to stage the newly restored version.
3. Submodule Confusion
Restoring a submodule path (e.g., git restore --source main -- libs/vendor) checks out the commit hash recorded in the parent repo's index for that submodule. It does not automatically run git submodule update. You must run that command separately to sync the submodule's working directory to the restored commit.
4. Case Sensitivity on Case-Insensitive Filesystems (macOS/Windows)
If a branch renames Config.js to config.js, restoring config.js on a case-insensitive filesystem can result in confusing behavior where Git sees the file as modified but the content appears unchanged. Always perform case-sensitive renames via git mv on a Linux runner or CI to avoid this state.
Summary Cheat Sheet
| Goal | Modern Command (git restore) |
Legacy Command (git checkout) |
|---|---|---|
| Restore file from branch | git restore --source <branch> -- <file> |
git checkout <branch> -- <file> |
| Restore file from commit | git restore --source <sha> -- <file> |
git checkout <sha> -- <file> |
| Restore to new path (extract) | git show <branch>:<path> > new/path |
git show <branch>:<path> > new/path |
| Restore directory | git restore --source <branch> -- <dir>/ |
git checkout <branch> -- <dir>/ |
| Interactive hunk selection | git restore --patch --source <branch> -- <file> |
git checkout --patch <branch> -- <file> |
| Discard local changes (reset to HEAD) | git restore -- <file> |
git checkout -- <file> |
Conclusion
Mastering single-file restoration transforms Git from a blunt instrument into a precision tool. It liberates you from the "all-or-nothing" anxiety of git merge or git rebase, allowing you to treat branches as libraries of reusable solutions rather than rigid, mutually exclusive timelines Easy to understand, harder to ignore..
Whether you are backporting a hotfix to a maintenance branch, salvaging a deleted utility function from an abandoned experiment, or surgically applying a performance tweak from a massive refactor, the <rev>:<path> syntax and git restore --patch are the workflows that separate novice users from Git power users.
The next time you find yourself copying code snippets from a GitHub diff view in your browser, pause. Open your terminal. git restore --source is faster, preserves history, and keeps your workflow entirely local.