How can I selectively merge or pick changes from another branch in Git?

前端 未结 25 1555
慢半拍i
慢半拍i 2020-11-22 02:53

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

  • master: import of existing codebase pl
相关标签:
25条回答
  • 2020-11-22 03:13

    It is not exactly what you were looking for, but it was useful to me:

    git checkout -p <branch> -- <paths> ...
    

    It is a mix of some answers.

    0 讨论(0)
  • 2020-11-22 03:13

    For me, git reset --soft branch is the easiest way to selectively pick the changes from another branch, since, this command puts in my working tree, all the diff changes, and I can easily pick or revert which one I need.

    In this way, I have full control over the committed files.

    0 讨论(0)
  • 2020-11-22 03:13

    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 with 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
    
    0 讨论(0)
  • 2020-11-22 03:14

    The simple way, to actually merge specific files from two branches, not just replace specific files with ones from another branch.

    Step one: Diff the branches

    git diff branch_b > my_patch_file.patch

    Creates a patch file of the difference between the current branch and branch_b

    Step two: Apply the patch on files matching a pattern

    git apply -p1 --include=pattern/matching/the/path/to/file/or/folder my_patch_file.patch

    Useful notes on the options

    You can use * as a wildcard in the include pattern.

    Slashes don't need to be escaped.

    Also, you could use --exclude instead and apply it to everything except the files matching the pattern, or reverse the patch with -R

    The -p1 option is a holdover from the *Unix patch command and the fact that the patch file's contents prepend each file name with a/ or b/ (or more depending on how the patch file was generated) which you need to strip, so that it can figure out the real file to the path to the file the patch needs to be applied to.

    Check out the man page for git-apply for more options.

    Step three: there is no step three

    Obviously you'd want to commit your changes, but who's to say you don't have some other related tweaks you want to do before making your commit.

    0 讨论(0)
  • 2020-11-22 03:15

    It's strange that Git still does not have such a convenient tool "out of the box".

    I use it heavily when updating some old version branch (which still has a lot of software users) by just some bugfixes from the current version branch. In this case, it is often needed to quickly get just some lines of code from the file in trunk, ignoring a lot of other changes (that are not supposed to go into the old version)...

    And of course interactive three-way merge is needed in this case. git checkout --patch <branch> <file path> is not usable for this selective merge purpose.

    You can do it easily:

    Just add this line to [alias] section in your global .gitconfig or local .git/config file:

    [alias]
        mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; /C/BCompare3/BCompare.exe $2.theirs $2 $2.base $2; rm -f $2.theirs; rm -f $2.base;' -"
    

    It implies you use Beyond Compare. Just change to the software of your choice if needed. Or you can change it to three-way auto-merge if you don't need the interactive selective merging:

    [alias]
        mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; git merge-file $2 $2.base $2.theirs; rm -f $2.theirs; rm -f $2.base;' -"
    

    Then use it like this:

    git mergetool-file <source branch> <file path>
    

    This will give you the true selective tree-way merge opportunity of just any file in the other branch.

    0 讨论(0)
  • 2020-11-22 03:16

    I like the previous 'git-interactive-merge' answer, 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 the '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 in the previous answer, 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
    
    0 讨论(0)
提交回复
热议问题