Skip to content

Instantly share code, notes, and snippets.

@davidmroth
Forked from katylava/git-selective-merge.md
Last active February 17, 2019 21:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davidmroth/b5dfaa1610f499e4b8406e17373c7a11 to your computer and use it in GitHub Desktop.
Save davidmroth/b5dfaa1610f499e4b8406e17373c7a11 to your computer and use it in GitHub Desktop.
git selective merge

Example: You have a branch refactor that is quite different from master. You can't merge all of the commits, or even every hunk in any single commit or master will break, but you have made a lot of improvements there that you would like to bring over to master.

Note: This will not preserve the original change authors. Only use if necessary, or if you don't mind losing that information, or if you are only merging your own work.

On master:

> git co -b [a new branch name]

On temp:

> git merge --no-commit --no-ff [branch to pick from]

… which stages everything, so:

> git reset head

Then begin adding the pieces you want:

> git add --interactive

The following is from an actual merge.

           staged     unstaged path
  1:    unchanged        +1/-1 deploy_settings.py
  2:    unchanged        +4/-3 requirements.txt
  3:    unchanged        +2/-1 settings/defaults.py
  4:    unchanged        +1/-1 settings/production.py.example

*** Commands ***
  1: status	  2: update	  3: revert	  4: add untracked
  5: patch	  6: diff	  7: quit	  8: help
What now> 

Choose p for patch.

           staged     unstaged path
  1:    unchanged        +1/-1 deploy_settings.py
  2:    unchanged        +4/-3 requirements.txt
  3:    unchanged        +2/-1 settings/defaults.py
  4:    unchanged        +1/-1 settings/production.py.example
Patch update>> 

Enter the number next to the file you want to process first. You can keep entering numbers until you've selected all the files, or you can do them one at a time. An asterisk will appear next to the files you select.

When you are finished selecting files, press 'enter' without entering a number (or anything) to continue to the next step.

You will see a single diff hunk and it will ask you whether to stage it or not.

diff --git a/deploy_settings.py b/deploy_settings.py
index 9b110f4..c5b228e 100644
--- a/deploy_settings.py
+++ b/deploy_settings.py
@@ -4,7 +4,7 @@ This file holds the Fabric deployment settings for this project
 from fabric.state import env

 #env.project = 'my_project'   #The name of this project
-#env.repo_base = 'git@git.oldsite.com:%s.git' % env.project
+#env.repo_base = 'git@git.newsite.com:%s.git' % env.project

Stage this hunk [y,n,q,a,d,/,e,?]? 

Enter y to stage or n to skip. This will go on for every diff hunk in the selected files until you get back to:

*** Commands ***
  1: status	  2: update	  3: revert	  4: add untracked
  5: patch	  6: diff	  7: quit	  8: help
What now> 

You can enter s to see what you changed

           staged     unstaged path
  1:    unchanged        +1/-1 deploy_settings.py
  2:        +4/-3      nothing requirements.txt
  3:        +2/-1      nothing settings/defaults.py
  4:    unchanged        +1/-1 settings/production.py.example

*** Commands ***
  1: status	  2: update	  3: revert	  4: add untracked
  5: patch	  6: diff	  7: quit	  8: help
What now> 

You can quit now, so enter q, Then do some status and diff commands to explore the staged vs unstaged changes and make sure it looks like you expected.

Don't forget to git add any untracked files as appropriate.

Commit the staged changes:

> git commit -m "merged selected patches from refactor branch"
# don't do commit -a here… you only want to commit the staged changes

Revert the unstaged changes

> git checkout .

And finally, merge to master:

> git checkout master
> git merge temp
> git branch -D temp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment