git add –patch with difftool

Is it possible to configure Git to use my configured difftool with git add --patch?

I would like to pick the changes to add to the index via my own difftool.

  • Changed username to push with to atlassian stash - update-server-info?
  • git reset --hard <sha1>. Did that delete everything?
  • Undoing an old commit
  • Setup git in xcode 5
  • Git - how to force merge conflict and manual merge on selected file
  • Can I make git diff only show the changed file names?
  • git - how to hide changes for a particular file at a repo level
  • how to create a null branch in jgit
  • Git: Clone & update repository
  • How to run git log commands using libgit2 in GO?
  • How to force a merge to succeed when there are conflicts?
  • Git bash always says the branch is ((unknown)) in all directories
  • 3 Solutions collect form web for “git add –patch with difftool”

    No, unfortunately.

    I suppose I can see that working – Git generates a temporary file based on what’s currently in the index, hands it to the difftool along with a copy of the current work tree version (to protect you from making further changes), lets you use the difftool to move some of the changes to the index version, then once you save and quit, stages whatever content is in that modified index version. Note that this would require the difftool to also be a bit of an editor, and not all valid difftools are; some of them are just for viewing diffs. Note also that this is basically bypassing all of git add -p. You wouldn’t have any of the normal interface from it for moving between hunks, splitting hunks, and so on. The difftool would be entirely responsible for all of that.

    If your difftool is fully-featured enough to do this sort of thing, then I suppose you could write a script to do it. An outline, without really any error protection, handling of special cases (binary files?), and completely untested:

    #!/bin/bash
    tmpdir=$(mktemp -d)
    git diff --name-only |
    while read file; do
        cp "$file" $tmpdir
        # this has your changes in it
        work_tree_version="$tmpdir/$file"
        # this has the pristine version
        index_version=$(git checkout-index --temp "$file")
        # and now you bring changes from the work tree version into the index version,
        # within the difftool, and save the index version and quit when done
        my_difftool "$work_tree_version" "$index_version"
    
        # swap files around to run git add
        mv "$file" "$work_tree_version"
        mv "$index_version" "$file"
        git add "$file"
        mv "$work_tree_version" "$file"
        # you could also do this by calculating the diff and applying it directly to the index
        # git diff --no-index -- "$file" "$original_index_version" | git apply --cached
    
    rm -r $tmpdir
    

    Probably a lot of ways to improve that; sorry I don’t have time to be careful and thorough with it right now.

    Here’s my script for this, which opens kdiff3 for you to perform a 2-file merge. If you don’t like kdiff3, provide your own values for MERGETOOL and MERGECMD (but you’d be crazy not to like kdiff3).

    To avoid surprises, this script tries to mimic git add -p as far as arguments and error codes. (It handles both lists of files and directories.)

    Plus, it properly handles various corner cases, including:

    • The user tries to run the script in a non-git directory (abort with an error)
    • The user hits Ctrl+C before finishing (quit early)
    • The user declines to save the merge result within the difftool (then don’t use it, but move on to the next file)
    • The difftool has an unexpected error (stop early)

    Example usage:

    $ ## With kdiff3 (default):
    $ add-with-mergetool myfile1.txt
    $ add-with-mergetool some-directory
    
    $ ## ...or with custom mergetool:
    $ export MERGETOOL='opendiff'
    $ export MERGECMD='$LOCAL $REMOTE -merge $MERGED'
    $ add-with-mergetool some-directory/*.py
    
    #!/bin/bash
    #
    # add-with-mergetool
    # Author: Stuart Berg (http://github.com/stuarteberg)
    # 
    # This little script is like 'git add --patch', except that 
    # it launches a merge-tool to perform the merge.
    
    # TODO: For now, this script hard-codes MERGETOOL and MERGECMD for kdiff3.
    #       Modify those variables for your own tool if you wish.
    #       In the future, it would be nice if we could somehow read  
    #       MERGETOOL and MERGECMD from the user's git-config.
    
    # Configure for kdiff3
    # (and hide warnings on about modalSession, from kdiff3 on OSX)
    MERGETOOL=${MERGETOOL-kdiff3}
    MERGECMD=${MERGECMD-'"${MERGETOOL}" "${LOCAL}" "${REMOTE}" -o "${MERGED}"'\
                        2>&1 | grep -iv modalSession}
    
    main() {
        check_for_errors "$@"
        process_all "$@"
    }
    
    check_for_errors() {
        which "${MERGETOOL}" > /dev/null
        if [[ $? == 1 ]]; then
            echo "Error: Can't find mergetool: '${MERGETOOL}'" 1>&2
            exit 1
        fi
    
        if [[ "$1" == "-h" ]]; then
            echo "Usage: $(basename $0) [<pathspec>...]" 1>&2
            exit 0
        fi
    
        # Exit early if we're not in a git repo
        git status > /dev/null || exit $?
    }
    
    process_all() {
        repo_toplevel=$(git rev-parse --show-toplevel)
    
        # If no args given, add everything (like 'git add -p')
        if [[ $# == 0 ]]; then
            set -- "$repo_toplevel"
        fi
    
        # For each given file/directory...
        args=( "$@" )
        for arg in "${args[@]}"
        do
            # Find the modified file(s)
            changed_files=( $(git diff --name-only -- "$arg") )
            (
                # Switch to toplevel, to easily handle 'git diff' output
                cd "$repo_toplevel"
    
                # For each modified file...
                for f in "${changed_files[@]}"
                do
                    if [[ $startmsg_shown != "yes" ]]; then
                        echo "Starting $(basename $0).  Use Ctrl+C to stop early."
                        echo "To skip a file, quit ${MERGETOOL} without saving."
                        echo
                        startmsg_shown="yes"
                    fi
    
                    # This is where the magic happens.            
                    patch_file_and_add "$f"
                done
            ) || exit $? # exit early if loop body failed
        done
    }
    
    # This helper function launches the mergetool for a single file,
    #  and then adds it to the git index (if the user saved the new file).
    patch_file_and_add() {
        f="$1"
        git show :"$f" > "$f.from_index" # Copy from the index
        (
            set -e
            trap "echo && exit 130" INT # Ctrl+C should trigger abnormal exit
    
            # Execute 2-file merge
            echo "Launching ${MERGETOOL} for '$f'."
            LOCAL="$f.from_index"
            REMOTE="$f"
            MERGED="$f.to_add"
            eval "${MERGECMD}"
    
            if [[ -e "$f.to_add" ]]; then
                mv "$f" "$f.from_working" # Backup original from working-tree
                mv "$f.to_add" "$f"       # Replace with patched version
                git add "$f"              # Add to the index
                mv "$f.from_working" "$f" # Restore the working-tree version
            fi
        )
        status=$?
        rm "$f.from_index" # Discard the old index version
        if [ $status == 130 ]; then
            echo "User interrupted." 1>&2
            exit $status
        elif [ $status != 0 ]; then
            echo "Error: Interactive add-patch stopped early!" 1>&2
            exit $status
        fi
    }
    
    main "$@"
    

    Unfortunately not.

    The only UI I know of at the moment is part of git-gui when invoked as

    git gui citool
    

    The other UI is the interactive console UI when invoked as

    git add -i
    

    git difftool allows some different tool options, but not the add interface.

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