How to selectively merge or pick changes from anot

2018-12-31 06:35发布

I'm using git on a new project that has two parallel -- but currently experimental -- development branches:

  • master: import of existing codebase plus a few mods that I'm generally sure of
  • exp1: experimental branch #1
  • exp2: experimental branch #2

exp1 and exp2 represent two very different architectural approaches. Until I get further along I have no way of knowing which one (if either) will work. As I make progress in one branch I sometimes have edits that would be useful in the other branch and would like to merge just those.

What is the best way to merge selective changes from one development branch to another while leaving behind everything else?

Approaches I've considered:

  1. git merge --no-commit followed by manual unstaging of a large number of edits that I don't want to make common between the branches.

  2. Manual copying of common files into a temp directory followed by git checkout to move to the other branch and then more manual copying out of the temp directory into the working tree.

  3. A variation on the above. Abandon the exp branches for now and use two additional local repositories for experimentation. This makes the manual copying of files much more straightforward.

All three of these approaches seem tedious and error-prone. I'm hoping there is a better approach; something akin to a filter path parameter that would make git-merge more selective.

24条回答
梦寄多情
2楼-- · 2018-12-31 06:58

Here is how you can replace Myclass.java file in master branch with Myclass.java in feature1 branch. It will work even if Myclass.java doesn't exist on master.

git checkout master
git checkout feature1 Myclass.java

Note this will overwrite - not merge - and ignore local changes in the master branch rather.

查看更多
姐姐魅力值爆表
3楼-- · 2018-12-31 06:58

I know this question is old and there are many other answers, but I wrote my own script called 'pmerge' to partially merge directories. It's a work in progress and I'm still learning both git and bash scripting.

This command uses git merge --no-commit and then unapplies changes that don't match the path provided.

Usage: git pmerge branch path
Example: git merge develop src/

I haven't tested it extensively. The working directory should be free of any uncommitted changes and untracked files.

#!/bin/bash

E_BADARGS=65

if [ $# -ne 2 ]
then
    echo "Usage: `basename $0` branch path"
    exit $E_BADARGS
fi

git merge $1 --no-commit
IFS=$'\n'
# list of changes due to merge | replace nulls w newlines | strip lines to just filenames | ensure lines are unique
for f in $(git status --porcelain -z -uno | tr '\000' '\n' | sed -e 's/^[[:graph:]][[:space:]]\{1,\}//' | uniq); do
    [[ $f == $2* ]] && continue
    if git reset $f >/dev/null 2>&1; then
        # reset failed... file was previously unversioned
        echo Deleting $f
        rm $f
    else
        echo Reverting $f
        git checkout -- $f >/dev/null 2>&1
    fi
done
unset IFS
查看更多
低头抚发
4楼-- · 2018-12-31 06:59

1800 INFORMATION's answer is completely correct. As a git noob, though, "use git cherry-pick" wasn't enough for me to figure this out without a bit more digging on the internet so I thought I'd post a more detailed guide in case anyone else is in a similar boat.

My use case was wanting to selectively pull changes from someone else's github branch into my own. If you already have a local branch with the changes you only need to do steps 2 and 5-7.

  1. Create (if not created) a local branch with the changes you want to bring in.

    $ git branch mybranch <base branch>

  2. Switch into it.

    $ git checkout mybranch

  3. Pull down the changes you want from the other person's account. If you haven't already you'll want to add them as a remote.

    $ git remote add repos-w-changes <git url>

  4. Pull down everything from their branch.

    $ git pull repos-w-changes branch-i-want

  5. View the commit logs to see which changes you want:

    $ git log

  6. Switch back to the branch you want to pull the changes into.

    $ git checkout originalbranch

  7. Cherry pick your commits, one by one, with the hashes.

    $ git cherry-pick -x hash-of-commit

Hat tip: http://www.sourcemage.org/Git_Guide

查看更多
低头抚发
5楼-- · 2018-12-31 06:59

I like the 'git-interactive-merge' answer, above, but there's one easier. Let git do this for you using a rebase combination of interactive and onto:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o master

So the case is you want C1 and C2 from 'feature' branch (branch point 'A'), but none of the rest for now.

# git branch temp feature
# git checkout master
# git rebase -i --onto HEAD A temp

Which, as above, drops you in to the interactive editor where you select the 'pick' lines for C1 and C2 (as above). Save and quit, and then it will proceed with the rebase and give you branch 'temp' and also HEAD at master + C1 + C2:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o-master--C1---C2 [HEAD, temp]

Then you can just update master to HEAD and delete the temp branch and you're good to go:

# git branch -f master HEAD
# git branch -d temp
查看更多
不流泪的眼
6楼-- · 2018-12-31 07:00

To selectively merge files from one branch into another branch, run

git merge --no-ff --no-commit branchX

where branchX is the branch you want to merge from into the current branch.

The --no-commit option will stage the files that have been merged by Git without actually committing them. This will give you the opportunity to modify the merged files however you want to and then commit them yourself.

Depending on how you want to merge files, there are four cases:

1) You want a true merge.

In this case, you accept the merged files the way Git merged them automatically and then commit them.

2) There are some files you don't want to merge.

For example, you want to retain the version in the current branch and ignore the version in the branch you are merging from.

To select the version in the current branch, run:

git checkout HEAD file1

This will retrieve the version of file1 in the current branch and overwrite the file1 automerged by Git.

3) If you want the version in branchX (and not a true merge).

Run:

git checkout branchX file1

This will retrieve the version of file1 in branchX and overwrite file1 auto-merged by Git.

4) The last case is if you want to select only specific merges in file1.

In this case, you can edit the modified file1 directly, update it to whatever you'd want the version of file1 to become, and then commit.

If Git cannot merge a file automatically, it will report the file as "unmerged" and produce a copy where you will need to resolve the conflicts manually.



To explain further with an example, let's say you want to merge branchX into the current branch:

git merge --no-ff --no-commit branchX

You then run the git status command to view the status of modified files.

For example:

git status

# On branch master
# Changes to be committed:
#
#       modified:   file1
#       modified:   file2
#       modified:   file3
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:      file4
#

Where file1, file2, and file3 are the files git have successfully auto-merged.

What this means is that changes in the master and branchX for all those three files have been combined together without any conflicts.

You can inspect how the merge was done by running the git diff --cached;

git diff --cached file1
git diff --cached file2
git diff --cached file3

If you find some merge undesirable then you can

  1. edit the file directly
  2. save
  3. git commit

If you don't want to merge file1 and want to retain the version in the current branch

Run

git checkout HEAD file1

If you don't want to merge file2 and only want the version in branchX

Run

git checkout branchX file2

If you want file3 to be merged automatically, don't do anything.

Git has already merged it at this point.


file4 above is a failed merge by Git. This means there are changes in both branches that occur on the same line. This is where you will need to resolve the conflicts manually. You can discard the merged done by editing the file directly or running the checkout command for the version in the branch you want file4 to become.


Finally, don't forget to git commit.

查看更多
骚的不知所云
7楼-- · 2018-12-31 07:00

Easiest way is to set your repo to the branch you want to merge with then run,

git checkout [branch with file] [path to file you would like to merge]

If you run

git status

you will see the file already staged...

Then run

git commit -m "Merge changes on '[branch]' to [file]"

Simple.

查看更多
登录 后发表回答