How do I move recent commits from the master branch to a new branch and reset master to before those commits were made? For example, I want to transform this state:
master A - B - C - D - E
Into this state:
newbranch C - D - E
/
master A - B
What Git commands should I use to accomplish this workflow?
Brief Answer
To move recent commits from master to a new branch and reset master, first create a new branch from the current commit using git branch newbranch
, then reset master to the previous commit with git reset --hard HEAD~1
(for one commit) or git reset --hard <commit-hash>
for a specific commit. This will effectively move commits C, D, and E to the new branch while leaving master at commit B.
Contents
- Understanding the Git Workflow
- Step-by-Step Implementation
- Alternative Reset Approaches
- Verifying Your Branches
- Potential Pitfalls and Solutions
- When to Use This Workflow
- Conclusion
Understanding the Git Workflow
Before diving into the commands, it’s essential to understand what you’re trying to accomplish. In Git, branches are simply pointers to specific commits. When you want to “move” commits from one branch to another, you’re actually creating a new pointer and adjusting the existing pointer.
In your example:
master A - B - C - D - E
You want to:
- Create a new branch that points to commit E
- Move the master branch pointer back to commit B
The result will look like:
newbranch C - D - E
/
master A - B
This is a common workflow when you’ve made some commits on master that you realize should have been on a feature branch, or when you want to isolate recent work for separate review or deployment.
Step-by-Step Implementation
Here’s the exact sequence of commands to accomplish your goal:
1. Create the New Branch
First, create a new branch from your current HEAD (which is at commit E):
git branch newbranch
This creates a new branch pointer named “newbranch” that points to the same commit as your current HEAD (commit E).
2. Reset the Master Branch
Now, reset the master branch to the desired previous commit. You have several options depending on your exact needs:
Option A: Reset by number of commits
If you know exactly how many commits you want to remove (in this case, 3 commits: C, D, and E), you can use:
git checkout master git reset --hard HEAD~3
The HEAD~3
syntax refers to the commit that is three parents back from your current HEAD.
Option B: Reset by commit hash
If you know the exact hash of commit B, you can reset to that specific commit:
git checkout master git reset --hard <commit-hash-of-B>
You can find the commit hash using git log --oneline
or git log
.
Option C: Interactive Reset
If you’re not sure exactly how many commits to remove or want to be more selective:
git checkout master git reset --soft HEAD~3
The --soft
option keeps your changes staged but doesn’t remove them from the working directory, allowing you to selectively commit them.
3. Verify Your Branches
After running these commands, verify the state of your branches:
git log --oneline --graph --all
This should show you the branch structure you wanted:
* E (newbranch)
* D
* C
|
* B (master)
* A
Or if you used the hash method:
* E (newbranch)
* D
* C
|
* B (master)
* A
Alternative Reset Approaches
Using git checkout
and git reset
An alternative approach is to create and switch to the new branch in one step:
git checkout -b newbranch git checkout master git reset --hard HEAD~3
This sequence achieves the same result but with slightly different commands.
Using git cherry-pick
If you only want to move specific commits rather than all recent commits, you could use git cherry-pick
:
git checkout -b newbranch git checkout master git reset --hard HEAD~3 git cherry-pick C..E
This would only move the specific commits you want to preserve.
Verifying Your Branches
After performing these operations, it’s crucial to verify that your branches are in the expected state:
# Show commit history with branch pointers
git log --oneline --graph --decorate --all
# Show the current branch
git branch -v
# Show the last commit on each branch
git for-each-ref --format='%(refname:short) %(objectname:short) %(contents:subject)' refs/heads/
These commands will help you confirm that:
- The
newbranch
points to the latest commit (E) - The
master
branch now points to commit B - The commits C, D, and E are no longer part of the master branch history
Potential Pitfalls and Solutions
1. Losing Commits Forever
Problem: If you use git reset --hard
and haven’t pushed your commits yet, those commits will be permanently removed from your local repository’s master branch.
Solution: Always make sure you’ve either:
- Created the new branch first
- Or have a backup of your work in another repository
2. Already Pushed Commits
Problem: If you’ve already pushed commits C, D, and E to a remote repository, resetting master will create a divergence between your local and remote history.
Solution: After resetting locally, you’ll need to force push to update the remote branch:
git push origin master --force
Warning: Force pushing can overwrite other people’s work if they’ve based work on the commits you’re removing.
3. Uncommitted Changes
Problem: If you have uncommitted changes in your working directory, git reset --hard
will discard them.
Solution: Either commit your changes first or stash them:
git stash
# After reset
git stash pop
When to Use This Workflow
This type of branch manipulation is useful in several scenarios:
-
Isolating Feature Work: When you’ve accidentally committed feature work directly to master and want to move it to a proper feature branch.
-
Creating Release Branches: When you want to create a release branch from a specific point while continuing development on master.
-
Experimentation: When you want to isolate recent experimental work that might not be ready for the main branch.
-
Code Review: When you want to separate commits for different reviewers or review phases.
-
Hotfix Isolation: When you need to quickly create a hotfix branch while preserving the current state of master.
Conclusion
Moving recent commits from master to a new branch and resetting master is a common and useful Git workflow. The key commands are:
git branch newbranch
to create the new branchgit checkout master
to switch back to mastergit reset --hard HEAD~N
(where N is the number of commits to remove) to reset master
Remember to:
- Always verify your branch structure after making changes
- Be cautious with
--hard
resets as they can permanently discard changes - Consider the impact on team members if working in a shared repository
- Use
--soft
or--mixed
resets if you want to preserve changes in a different way
This workflow gives you precise control over your Git history and helps maintain clean, organized branching structures in your repositories.