Git - How to selectively apply changes from one branch to another?
Asked Answered
P

3

20

Is it possible to selectively apply changes from one branch to another with Git?

More specifically, I use a public dev branch for GitHub and a private master branch for deployment. When changes are made to one branch they will need to be applied to the other, but some lines of code need to stay different. In my case it's a few css classes and a feed.

I'm new to Git but I've made my research :

  • git merge --no-commit --no-ff can be used followed by a git mergetool to chose what I want in case of a conflict. The problem is that it only works for conflicts Git can't automatically merge, so what I want to stay different gets replaced before I get the chance to use my mergetool.

  • git difftool --cached is useful as it allows me to see the differences, but I need to copy what I want to keep from there and manually replace it with a text editor, as I cannot simply choose and save like I can with mergetool.

  • git cherry-pick seems to apply a specified commit to another, but what I want to stay different may be scattered to different commits, and these commits may not only include what I want to stay different. I cannot see this working unless I make millions of commits that would drive me mad.

Also to be clear, I don't want one branch to become another, what seems to be the case with a merge. I want two separate branches with their respective differences and apply changes from one to the other.

Is there a better workflow that would allow me to keep a development and a deployment version by applying their changes and keeping a few differences? I don't mind using separate repositories or different tools if it leads to a solution.

Progenitor answered 13/3, 2014 at 1:58 Comment(3)
So, essentially you may need just some part of the commit at times, which could be from some lines in a single file to a few files from many in a commit?Eriha
I need to keep a few lines of codes different when merging, for example a white background in a branch and a black background in the other. There may be other changes in the same file that I would like to merge so file exclusions wouldn't solve every situations.Progenitor
Possible duplicate of How do you merge selective files with git-merge?Tremml
E
4

I don't think there is a way to pick parts of a commit from the same file. I would say you need to simply re-factor your code to move such parts in different files.

BTW, if you want to take some files from a commit, you can use cherry-pick with a combination of other commands as explained here.

Eriha answered 13/3, 2014 at 10:34 Comment(2)
Doing git checkout -p branchname as explained in your link did the trick! This allows me to get selective changes interactively from the other branch without merging. Cherry-pick could also work but only gets changes per commits.Progenitor
:/ There's obviously a way - it muts might not be easy.Lyndy
P
21

I've also found out about patching :

To create the patch : git diff dev > master.patch

To apply it : patch < master.patch

Progenitor answered 13/3, 2014 at 20:32 Comment(1)
Thank you — upvoted! But one note — because my git is configured to put the throw-away imaginary directory names a/ and b/ in my diff, the second command is slightly different for me: patch -p1 < master.patchSteiermark
S
5

Take advantage of the fact that creating git branches is cheap and need have no effect on the actual content of the working directory.

  1. Check out your source branch dev.
  2. Create a temporary branch. It will point to the same commit that dev does: checkout -b for_master

    Presumably, you know (or can easily find out) which commit before the dev branch is the last one before the changes you (partially) want. In this example, let's say that that commit has the hash 1457B4 ('last before', get it?).

  3. Reset your for_master branch to that commit: git reset 1457B4. (Do not use the --hard switch!)

    Now, you have a working directory containing all the changes that are in dev, but from the POV of the for_master branch, those changes are unstaged and uncommitted (while the dev branch still points to commits that record all the changes, so the work is still safe).

  4. Using interactive staging (git add -p and/or git add -e), create a commit (or more than one, if you like) that contains all, and only, the changes you want to apply to your master branch.

    Make a note of the hash of the last commit (or give it a tag). In this example, I'll say that its hash is C0DA.

  5. Check out master.

  6. Cherry-pick the commit(s) you just made: git cherry-pick 1457B4..C0DA.

    (Note that cherry-picking a range is only available after git version 1.7.2. Otherwise, you'll need to individually cherry-pick all the commits you made in step 4.)

    (Also note that when you cherry-pick a range, the start of the range is the commit before the first one that will actually be picked.)

This process is kind of the inverse of using git checkout -p as mentioned in the selected answer. It might be useful for creating cherry-pick-able commits between two branches that share some code but also have a lot of differences (e.g. between two major versions of a project), and you don't want to have to spend a lot of time ignoring irrelevant files in a call to git checkout -p.

Personally, I've found it convenient to use two different worktree directories for the same repository (one for the source branch and one for the destination) and switch between two command shells, one using the source branch directory (worktree) and the other the destination. But if you aren't comfortable using the git worktree feature, that might not be for you.

Silique answered 1/11, 2018 at 19:22 Comment(1)
Excellent technique. Helped me rearrange a series of individual, clearly-scoped PRs out of a couple weeks' worth of messy commits covering multiple features in a single branch (shame on me - would have been a nightmare to review!). Thanks!Truly
E
4

I don't think there is a way to pick parts of a commit from the same file. I would say you need to simply re-factor your code to move such parts in different files.

BTW, if you want to take some files from a commit, you can use cherry-pick with a combination of other commands as explained here.

Eriha answered 13/3, 2014 at 10:34 Comment(2)
Doing git checkout -p branchname as explained in your link did the trick! This allows me to get selective changes interactively from the other branch without merging. Cherry-pick could also work but only gets changes per commits.Progenitor
:/ There's obviously a way - it muts might not be easy.Lyndy

© 2022 - 2024 — McMap. All rights reserved.