2012-01-26 8 views
17

¿Es posible configurar Git para usar mi difftool configurado con git add --patch?git add --patch with difftool

Me gustaría elegir los cambios para agregar al índice a través de mi propio difftool.

+0

¿Se Git incluso cubiertos en StackOverflow? Creo que esta sería una mejor pregunta para [SuperUser] (http://superuser.com/). – qJake

+0

Puede que tengas razón. ¿Hay un botón migrar para moverlo? – HaxElit

+0

No, tengo que dejar que un mod lo haga, o simplemente preguntar de nuevo si no tienes ganas de esperar. – qJake

Respuesta

9

No, desafortunadamente.

Supongo que puedo ver que funciona - Git genera un archivo temporal basado en lo que está actualmente en el índice, se lo da a difftool junto con una copia de la versión actual del árbol de trabajo (para evitar más cambios), le permite usar el difftool para mover algunos de los cambios a la versión de índice, luego, una vez que lo guarda y lo deja, representa el contenido de esa versión de índice modificada. Tenga en cuenta que esto requeriría que difftool también sea un poco editor, y no todos los difftools válidos son; algunos de ellos son solo para ver diffs. Tenga en cuenta también que esto básicamente está pasando por alto todo de git add -p. No tendrías ninguna de las interfaces normales para moverse entre trozos, dividir trozos, y así sucesivamente. El difftool sería completamente responsable de todo eso.

Si su difftool está totalmente equipado-suficiente para hacer este tipo de cosas, entonces yo supongo que se podría escribir un script para hacerlo. Un esbozo, sin llegar a ningún tipo de protección de errores, manejo de casos especiales, y no está comprobado:

#!/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 

probablemente un montón de maneras de mejorar eso; (archivos binarios?) lo siento, no tengo tiempo para ser cuidadoso y completo con esto ahora mismo.

+0

Creo que tal vez lo pregunté mal. ¿Puedo usar mi difftool (Son lo mismo para mí)? De esta forma, puedo elegir todos los cambios que deseo agregar al índice. Actualizaré la pregunta. – HaxElit

+0

Esa es una idea genial. Podría hacer de esto un script y luego agregar un git alias 'git diffadd' o algo así. Intentaré limpiar un poco tu código y hacerlo un poco más robusto. ¡Gracias! – HaxElit

+0

@HaxElit: si se te ocurre algo sólido, no dudes en editarlo en mi respuesta o publicar el tuyo. – Cascabel

-1

Desafortunadamente no.

La única interfaz de usuario que conozco en este momento es parte de git-gui Cuando se invoca como

git gui citool 

La otra interfaz de usuario es la interfaz de usuario de la consola interactiva Cuando se invoca como

git add -i 

difftool Git permite algunas opciones de herramientas diferentes, pero no la interfaz de agregar.

+0

No estoy seguro Veo la relevancia de esto; el OP está preguntando por 'git add --patch | -p', que le permite seleccionar selectivamente trozos del parche al escenario. 'git gui citool' definitivamente no hace eso, por lo que es irrelevante. Y 'git add -i' es capaz de invocar' git add -p', por lo que es una forma indirecta de hacer lo que el OP ya sabe, y de lo contrario no hace lo que quiere. Entonces, el contenido de tu respuesta es "no", y creo que lo he cubierto bastante bien. – Cascabel

1

Aquí está my script para esto, que abre kdiff3 para que realice una fusión de 2 archivos. Si no te gusta kdiff3, proporciona tus propios valores para MERGETOOL y MERGECMD (pero estarás loco si no te gusta kdiff3).

Para evitar sorpresas, este script trata de imitar git add -p por lo que los argumentos y códigos de error. (Maneja ambas listas de archivos y directorios.)

Además, se maneja adecuadamente diversos casos de esquina, entre ellos:

  • El usuario intenta ejecutar el script en un directorio no Git (abortar con un error)
  • el usuario pulsa Ctrl+C antes de terminar (dejar de fumar antes de tiempo)
  • los descensos de usuario para guardar el resultado de la fusión dentro del difftool (entonces no lo use, pero pasar a la siguiente foto)
  • el difftool tiene un error inesperado (parada temprana)

Ejemplo de uso:

$ ## 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 "[email protected]" 
    process_all "[email protected]" 
} 

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=("[email protected]") 
    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 "[email protected]"