ramas - git merge example
¿Cómo le digo a git que siempre seleccione mi versión local para las fusiones en conflicto en un archivo específico? (2)
Digamos que estoy colaborando con alguien a través de un repositorio de git, y hay un archivo en particular en el que no quiero aceptar ningún cambio externo.
¿Hay alguna forma de configurar mi repositorio local para que no me queje de una fusión conflictiva cada vez que lo hago? Me gustaría seleccionar siempre mi versión local al fusionar este archivo.
En la instancia específica de un archivo de configuración, estoy de acuerdo con la respuesta de Ron :
una configuración debe ser "privada" para su espacio de trabajo (por lo tanto "ignorado", como en "declarado en un archivo .gitignore
").
Puede tener una plantilla de archivo de configuración con valores tokenizados y un script que transforma ese archivo config.template
en un archivo de configuración privado (e ignorado).
Sin embargo, esa observación específica no responde a una pregunta más general, es decir, su pregunta (!):
¿Cómo le digo a git que siempre seleccione mi versión local para las fusiones en conflicto en un archivo específico? (para cualquier archivo o grupo de archivos)
Este tipo de fusión es una "combinación de copia", en la que siempre copiará la versión de un archivo "suya" o "suya" siempre que haya un conflicto.
(Como señala Brian Vandenberg en los comentarios , "
ours
" y "theirs
" se utilizan aquí para una fusión .
Se invierten para una rebase : ver "Why is the meaning of “ours” and “theirs” reversed with git-svn
se invierteWhy is the meaning of “ours” and “theirs” reversed with git-svn
", que utiliza una rebase, "git rebase
,git rebase
seguimiento de ''local'' y ''remoto'' ")
Para "un archivo" (un archivo en general, sin hablar de un archivo "config", ya que es un mal ejemplo), lo lograría con un script personalizado llamado a través de fusiones.
Git llamará a esa secuencia de comandos porque tendrá que definir un valor de gitattributes , que define un controlador de combinación personalizado .
El "controlador de combinación personalizado" es, en este caso, un script muy simple que básicamente mantendrá sin cambios la versión actual, lo que le permite seleccionar siempre su versión local.
Vamos a probar eso en un escenario simple, con un msysgit 1.6.3 en Windows, en una mera sesión de DOS:
cd f:/prog/git/test
mkdir copyMerge/dirWithConflicts
mkdir copyMerge/dirWithCopyMerge
cd copyMerge
git init
Initialized empty Git repository in F:/prog/git/test/copyMerge/.git/
Ahora, hagamos dos archivos, que tendrán conflictos, pero que se fusionarán de manera diferente.
echo a > dirWithConflicts/a.txt
echo b > dirWithCopyMerge/b.txt
git add -A
git commit -m "first commit with 2 directories and 2 files"
[master (root-commit) 0adaf8e] first commit with 2 directories and 2 files
Introduciremos un "conflicto" en el contenido de ambos archivos en dos ramas diferentes de git:
git checkout -b myBranch
Switched to a new branch ''myBranch''
echo myLineForA >> dirWithConflicts/a.txt
echo myLineForB >> dirWithCopyMerge/b.txt
git add -A
git commit -m "add modification in myBranch"
[myBranch 97eac61] add modification in myBranch
git checkout master
Switched to branch ''master''
git checkout -b hisBranch
Switched to a new branch ''hisBranch''
echo hisLineForA >> dirWithConflicts/a.txt
echo hisLineForB >> dirWithCopyMerge/b.txt
git add -A
git commit -m "add modification in hisBranch"
[hisBranch 658c31c] add modification in hisBranch
Ahora, intentemos fusionar "hisBranch" en "myBranch", con:
- resolución manual para fusiones conflictivas
- a excepción de
dirWithCopyMerge/b.txt
donde siempre quiero mantener mi versión deb.txt
.
Dado que la fusión se produce en '' MyBranch
'', volveremos a ella y agregaremos las directivas '' gitattributes
'' que personalizarán el comportamiento de fusión.
git checkout myBranch
Switched to branch ''myBranch''
echo b.txt merge=keepMine > dirWithCopyMerge/.gitattributes
git config merge.keepMine.name "always keep mine during merge"
git config merge.keepMine.driver "keepMine.sh %O %A %B"
git add -A
git commit -m "prepare myBranch with .gitattributes merge strategy"
[myBranch ec202aa] prepare myBranch with .gitattributes merge strategy
Tenemos un archivo .gitattributes
definido en el directorio dirWithCopyMerge
(definido solo en la rama donde se producirá la fusión: myBranch
), y tenemos un archivo .git/config
que ahora contiene un controlador de combinación.
[merge "keepMine"]
name = always keep mine during merge
driver = keepMine.sh %O %A %B
Si aún no define keepMine.sh e inicia la fusión de todos modos, esto es lo que obtiene.
git merge hisBranch
sh: keepMine.sh: command not found
fatal: Failed to execute internal merge
git st
# On branch myBranch
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: dirWithConflicts/a.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
type dirWithConflicts/a.txt
a
<<<<<<< HEAD:dirWithConflicts/a.txt
myLineForA
=======
hisLineForA
>>>>>>> hisBranch:dirWithConflicts/a.txt
Eso está bien:
-
a.txt
está listo para fusionarse y tiene conflicto en él -
b.txt
sigue intacto, ya que se supone que el controlador de combinación se ocupará de él (debido a la directiva en el archivo.gitattributes
en su directorio).
Defina un keepMine.sh
en cualquier parte de su %PATH%
(o $PATH
para nuestro amigo de Unix. Yo hago ambas cosas, por supuesto: tengo una sesión de Ubuntu en una sesión de VirtualBox)
Como lo commented lrkwz , y se describe en la sección " Merge Strategies " de Customizing Git-Git Attributes , puede reemplazar el script de shell con el comando de shell true
.
git config merge.keepMine.driver true
Pero en el caso general, puede definir un archivo de script:
keepMine.sh
# I want to keep MY version when there is a conflict
# Nothing to do: %A (the second parameter) already contains my version
# Just indicate the merge has been successfully "resolved" with the exit status
exit 0
(que era un controlador de combinación simple;) (Incluso más simple en ese caso, use true
)
(Si desea conservar la otra versión, solo agregue antes de la línea de exit 0
:
cp -f $3 $2
.
Eso es. El controlador de fusión siempre mantendrá la versión procedente de la otra rama, anulando cualquier cambio local)
Ahora, reintentemos la fusión desde el principio:
git reset --hard
HEAD is now at ec202aa prepare myBranch with .gitattributes merge strategy
git merge hisBranch
Auto-merging dirWithConflicts/a.txt
CONFLICT (content): Merge conflict in dirWithConflicts/a.txt
Auto-merging dirWithCopyMerge/b.txt
Automatic merge failed; fix conflicts and then commit the result.
La fusión falla ... solo para a.txt .
Edite a.txt y deje la línea de ''hisBranch'', luego:
git add -A
git commit -m "resolve a.txt by accepting hisBranch version"
[myBranch 77bc81f] resolve a.txt by accepting hisBranch version
Comprobemos que b.txt se haya conservado durante esta fusión
type dirWithCopyMerge/b.txt
b
myLineForB
La última confirmación representa la combinación completa :
git show -v 77bc81f5e
commit 77bc81f5ed585f90fc1ca5e2e1ddef24a6913a1d
Merge: ec202aa 658c31c
git merge hisBranch
Already up-to-date.
(La línea que comienza con Merge lo prueba)
Considere que puede definir, combinar y / o sobrescribir el controlador de combinación, ya que Git:
- examine
<dir>/.gitattributes
(que está en el mismo directorio que la ruta en cuestión): prevalecerá sobre el otro.gitattributes
en directorios - Luego examina
.gitattributes
(que está en el directorio principal), solo establecerá directivas si aún no se ha establecido - Finalmente, se analiza
$GIT_DIR/info/attributes
. Este archivo se usa para anular la configuración en el árbol.<dir>/.gitattributes
directivas<dir>/.gitattributes
.
Al "combinar", me refiero a un controlador de combinación múltiple "agregado".
Nick Green intenta, en los comentarios , combinar realmente los controladores de combinación: ver " Merge pom''s via python git driver ".
Sin embargo, como se menciona en su otra pregunta , solo funciona en caso de conflictos (modificación concurrente en ambas ramas).
Tenemos múltiples archivos de configuración que nunca queremos sobrescribir. Sin embargo, .gitignore y .gitattributes no funcionaron en nuestra situación. Nuestra solución fue almacenar los archivos de configuración en una rama de configuración. Luego, permite que los archivos se cambien durante la fusión de git, pero inmediatamente después de la fusión utiliza la "rama de git checkout -". para copiar nuestros archivos de configuración de la rama configs después de cada fusión. Respuesta detallada aquí