specific - git stash tracked files
git add--patch con diftool (3)
Aquí está mi script para esto, que abre kdiff3
para que pueda realizar una combinación de 2 archivos. Si no te gusta kdiff3
, proporciona tus propios valores para MERGETOOL
y MERGECMD
(pero estarías loco si no te gusta kdiff3
).
Para evitar sorpresas, este script intenta imitar git add -p
en cuanto a argumentos y códigos de error. (Maneja ambas listas de archivos y directorios.)
Además, maneja adecuadamente varios casos de esquina, incluyendo:
- El usuario intenta ejecutar el script en un directorio que no es git (cancelar con un error)
- El usuario pulsa
Ctrl+C
antes de terminar (salir antes) - El usuario se niega a guardar el resultado de la fusión dentro de la herramienta de datos (luego no lo use, sino que pase al siguiente archivo)
- El diftool tiene un error inesperado (parar antes)
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=''$MERGETOOL $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 "$@"
¿Es posible configurar Git para usar mi herramienta de datos configurada con git add --patch
?
Me gustaría seleccionar los cambios para agregarlos al índice a través de mi propia herramienta de datos.
Lamentablemente no.
La única UI 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
git difftool permite algunas opciones de herramientas diferentes, pero no la interfaz de agregar.
No Desafortunadamente.
Supongo que puedo ver que funciona: Git genera un archivo temporal basado en lo que está actualmente en el índice, lo entrega a la herramienta de datos junto con una copia de la versión actual del árbol de trabajo (para evitar que realice más cambios), le permite usar el difftool para mover algunos de los cambios a la versión del índice, luego, una vez que guarde y salga, enmarca el contenido de esa versión del índice modificado. Tenga en cuenta que esto requeriría que la herramienta también sea un poco editor, y no todas las herramientas válidas son; Algunos de ellos son sólo para ver diffs. Tenga en cuenta también que esto es básicamente eludir todo git add -p
. No tendrías ninguna de las interfaces normales para moverte entre los tíos, los tíos divididos, etc. El diftool sería completamente responsable de todo eso.
Si tu diftool tiene todas las funciones para hacer este tipo de cosas, supongo que podrías escribir un script para hacerlo. Un esquema, sin realmente ninguna protección contra errores, manejo de casos especiales (archivos binarios?), Y completamente sin probar:
#!/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 hay muchas maneras de mejorar eso; Lo siento, no tengo tiempo para ser cuidadoso y minucioso con eso ahora.