How do I partially git cherry-pick without ruining a future merge?

I need to bring some bugfixes that were made to the develop branch into master. I’d like file A to be identical in develop and master.

Ordinarily, I would git cherry-pick the commits that affect A, but some of those commits also affect files B and C, where B is a file that exists in master but I don’t want to change yet, and C is a file that does not yet exist in master.

  • Configure a local branch for push to specific branch
  • Keeping git from requiring commit of changes
  • Finding the most frequent committer to a specific file
  • Restore previously removed code (not necessarily single or whole file) in git
  • Grep functionality with Git Log command
  • how to make Git ignore .idea files created by Rubymine
  • When the next release is made at some point in the future, develop will be merged into master. If I git cherry-pick -n the commits I want now, I’ll have to unstage changes to B, and resolve conflicts for the nonexistent C before committing. When the real merge from develop into master happens, I will want the changes to B and C, but I won’t actually get them, since I already messed with those changes during the cherry-pick, right?

    What should I do instead? If I git checkout <latest_commit_hash> A, won’t that also cause a nasty merge conflict in the future?

  • Can you view an aggregate changeset in git? If so, how?
  • Maintaining 3rd Party Django Apps as Git submodules
  • How to cache github repos so 2nd git clone will use the cache instead of github repo
  • Rails - Heroku - Git - Problems connecting to repo with os x 10.6.5
  • During git merge, in the case of merge conflict, how do I accept all changes from a certain branch?
  • Know GIT hash before committing?
  • 3 Solutions collect form web for “How do I partially git cherry-pick without ruining a future merge?”

    In general avoid git cherry-pick if those branches are merged at any time in the future.

    It seems to me that git rebase could actually be helping if the development branch is local and not publicly visible. If other people have seen your changes and those commits have different parents you cannot avoid a merge.

    assume you have n commits after mainline in development and you have m1,m2,m3 commits in n that you want to share with mainline.

    if you do:

    git checkout development # go to development branch
    git rebase -i mainline   # reorder all commits in development
    

    you will get an editor window that shows you one commit per line. if you put the commits of m1,m2,m3 to the top of the line and choose edit (instead of pick) git will reorder your commits as you requested (top commit oldest and the bottom one newest) and it will halt before each commit marked as edit.

    you can then do:

    git add file1
    git commit              # commit only file1
    git commit -a           # commit the rest
    git rebase --continue   # continue rebasing
    

    you can then rebase another time to move the unwanted commits further up in your change history. if you are happy with your log (i.e.: all your commits you need in mainline are on top of changes in mainline and nothing else; hence you can merge doing a fast forward) you pick the furthest commit you want to have from your development branch (by SHA) and merge that.

    git checkout mainline
    git log development   # assume acf123 is the last commit you want
    git merge acf123      # fast forward to the right commit
    

    see also:

    With git-cherry-pick there’s not a good way to avoid the merge conflict that will happen with the topic with the picked changes merges with master (where the changes were picked to).

    I recommend that you perform the cherry-pick operation, then do a test merge locally and discover if the conflicts are something Git cannot manage without user intervention before you worry about avoiding the conflicts.

    One of the tricks I tend to do to mimic partial cherry-picks is:

    git cherry-pick <commitish>
    git checkout HEAD~ -- <path of unwanted change in file>
    git checkout HEAD~ -- <path of unwanted change in file>
    git checkout HEAD~ -- <path of unwanted change in file>
    git checkout HEAD~ -- <path of unwanted change in file>
    git commit -am "squash me"
    git rebase -i HEAD~~
    

    The squash the cherry-pick and checkout/revert together.

    If I git checkout develop — A, won’t that also cause a nasty merge conflict in the future?

    No, not by itself it won’t. Merge works by comparing each tip against the merge base and then comparing the two sets of changes. If both branches make the same changes, there’s no conflict.

    Other changes, on either branch, to or too near to lines changed in common on both branches, can appear as conflicts. The way to forestall those is to give git an accurate merge base by recording a merge from the common content.

    # merging master and develop, but there are common changes and 
    # also changes that conflict only with those common changes.
    # supply git's merge with the common changes as part of the base,
    # so it will see that it needs to resolve only those other changes
    
    # check out the current merge base
    git checkout $(git merge-base master develop)
    
    # make the changes you're merging
    git checkout develop -- file_A
    git commit -m "cherrypicking file_A as of develop @ $(git rev-parse develop)"
    
    # (for ensuite)
    base=$(git rev-parse HEAD)
    
    # record that both branches have those changes
    git checkout develop
    git merge -s ours $base -m "recording common content from previous cherry-picks"
    
    git checkout master
    git merge -s ours $base -m "recording common content from previous cherry-picks"
    
    # and now this merge will get an accurate base:
    git merge develop
    

    Now: the only effect of those $base merges is to record the common content as ancestral to both branch tips, giving the merge from develop to master an accurate base from which to resolve the other changes.

    The new commit gets history back in line with the practice given in a widely-used successful git branching model.


    If, in terms of how your team interprets commits, leaving the origins of that common content recorded only in the text of commit messages is preferable, git’s got that covered too. Instead of recording the ancestry permanently, via the checkouts and merges after the base assignment above, you can also

    echo $(git rev-parse master  master~)  $base > .git/info/grafts
    echo $(git rev-parse develop develop~) $base >>.git/info/grafts
    git checkout master
    git merge develop
    
    # later, whenever you want -- the ancestry above is strictly repo-local
    rm .git/info/grafts
    

    Ancestry recorded in .git/info/grafts is repo-local. Your merge commands will see it, and the result will be correct. The only downside to this is since the base isn’t actually recorded others will have the same trouble repeating it — very unlikely unless you’re doing criss-cross merges or also cherrypicking to other branches.

    Git Baby is a git and github fan, let's start git clone.