¿Cómo puedo hacer que Git siga enlaces simbólicos?


217

¿Será mejor que sea un script de shell que reemplace enlaces simbólicos con copias, o hay otra forma de decirle a Git que siga los enlaces simbólicos?

PD: Sé que no es muy seguro, pero solo quiero hacerlo en algunos casos específicos.


55
¿Hay alguna desventaja en usar enlaces duros para algo como esto?
Ehtesh Choudhury

12
Con Windows 7, "mklink / d" (enlace simbólico de directorio) no funciona con git, pero "mklink / j" (juction) funciona bien.
yoyo

1
Si el archivo es autogenerado por una aplicación que lo regenera de tal manera que elimina el archivo y crea uno nuevo, entonces sí, este es un problema que los enlaces duros a los archivos no resolverán.
Martin Pecka

1
@EhteshChoudhury no puedes hacer enlaces duros para directorios
Gaurav Kansal

Respuestas:


46

NOTA: Este consejo ahora está desactualizado según el comentario desde Git 1.6.1. Git solía comportarse de esta manera, y ya no lo hace.


Git intenta de forma predeterminada almacenar enlaces simbólicos en lugar de seguirlos (por compacidad, y generalmente es lo que la gente quiere).

Sin embargo, accidentalmente logré agregar archivos más allá del enlace simbólico cuando el enlace simbólico es un directorio.

Es decir:

  /foo/
  /foo/baz
  /bar/foo --> /foo
  /bar/foo/baz

haciendo

 git add /bar/foo/baz

parecía funcionar cuando lo probé. Sin embargo, ese comportamiento no era deseado por mí en ese momento, por lo que no puedo darle más información.


72
Los commits 725b06050a083474e240a2436121e0a80bb9f175 y 806d13b1ccdbdde4bbdfb96902791c4b7ed125f6 introdujeron cambios que le impidieron agregar archivos más allá de los directorios enlazados, por lo que esto no funcionará en versiones de git desde 1.6.1
Mark Longair

1
$ git add src / main / path / ConvertSymlinkToDir fatal: 'src / main / path / ConvertSymlinkToDir' está más allá de un enlace simbólico
user1767316

2
@ user1767316 leyó todo y los comentarios. Solía ​​funcionar, ya no funciona. El software cambia, pero las respuestas aceptadas de desbordamiento de pila no. He aclarado que esto no funciona ya. Mira otra respuesta.
Kent Fredric

Sí @KentFrederic, pero el mensaje de error exacto devolvió la ayuda de la búsqueda del usuario para encontrar la solución a su problema. Intenté cancelar el voto negativo pero lo bloqueé. Por un lado, su respuesta es correcta dada la advertencia, por otro lado, se debe dar prioridad a la respuesta que funciona ahora más que en el pasado
user1767316

143

Lo que hice para agregar los archivos dentro de un enlace simbólico a Git (no usé un enlace simbólico pero):

sudo mount --bind SOURCEDIRECTORY TARGETDIRECTORY

Ejecute este comando en el directorio administrado por Git. TARGETDIRECTORYtiene que ser creado antes de que SOURCEDIRECTORYse monte en él.

¡Funciona bien en Linux, pero no en OS X! Ese truco también me ayudó con Subversion. Lo uso para incluir archivos de una cuenta de Dropbox, donde un diseñador web hace sus cosas.


8
Sería un enfoque muy agradable si no se requiere sudo.
MestreLion

13
Para deshacer este enlace, use umount [mydir]. (+1 por tu gran consejo, @ user252400)
JellicleCat

10
Esto solo funciona durante la sesión. ¿Cuál es la mejor manera de hacerlo "eterno"?
Adobe

17
@Adobe: ponlo en / etc / fstab, así: / sourcedir / targetdir none bind
Alexander Garden

8
sshfs puede lograr ese tipo de truco sin requerir el sudo, aquí.
PypeBros

75

¿Por qué no crear enlaces simbólicos al revés? Es decir, en lugar de vincular desde el repositorio de Git al directorio de la aplicación, simplemente vincula al revés.

Por ejemplo, supongamos que estoy configurando una aplicación instalada ~/applicationque necesita un archivo de configuración config.conf:

  • Agrego config.confa mi repositorio Git, por ejemplo, en ~/repos/application/config.conf.
  • Luego creo un enlace simbólico ~/applicationejecutando ln -s ~/repos/application/config.conf.

Es posible que este enfoque no siempre funcione, pero hasta ahora funcionó bien para mí.


55
Parece ser la única forma, y ​​no es tan malo ... creo que el tuyo es un enfoque bastante elegante. git rastrea contenido, no archivos. Por lo tanto, mantener todo el contenido unido y hacer enlaces simbólicos desde allí a otros lugares tiene sentido
MestreLion

12
En mi caso, quería un enlace de un repositorio de git a otro, para poder editar archivos en cualquier ubicación y volver a comprometerme con sus controles remotos respectivos. En Windows 7, un cruce ("mklink / j") hizo el truco.
yoyo

3
por supuesto. a veces la respuesta es así de simple.
BBW Antes de Windows

¿Qué pasa si quieres obtener tanto el origen como el destino? (porque ambos pertenecen a diferentes códigos que desea tener en diferentes repositorios)
DrGC

1
No responde la pregunta :( Quería que parte de mi repositorio se sincronizara con mi iCloud. Desafortunadamente, iCloud no sigue los enlaces simbólicos, así que pensé que podría hacer que git siga los enlaces simbólicos y almacene los archivos originales en iCloud. Resulta que nadie sigue enlaces simbólicos: \
Jerry Green

49

Use enlaces duros en su lugar. Esto difiere de un enlace suave (simbólico). Todos los programas, incluido git, tratarán el archivo como un archivo normal. Tenga en cuenta que los contenidos pueden ser modificados por el cambio , ya sea el origen o el destino.

En macOS (antes de 10.13 High Sierra)

Si ya tiene instalado git y Xcode, instale hardlink . Es una herramienta microscópica para crear enlaces duros .

Para crear el enlace duro, simplemente:

hln source destination

Actualización de macOS High Sierra

¿Apple File System admite enlaces duros de directorio?

Los enlaces duros de directorio no son compatibles con Apple File System. Todos los enlaces duros de directorio se convierten en enlaces simbólicos o alias cuando convierte de HFS + a formatos de volumen APFS en macOS.

De las preguntas frecuentes de APFS en developer.apple.com

Siga https://github.com/selkhateeb/hardlink/issues/31 para futuras alternativas.

En Linux y otros sabores de Unix

El lncomando puede hacer enlaces duros:

ln source destination

En Windows (Vista, 7, 8, ...)

Alguien sugirió usar mklink para crear una unión en Windows, pero no lo he probado:

mklink /j "source" "destination"

77
Solo una nota: esto es básicamente lo que estaba buscando, pero luego aprendí que en Linux, un enlace duro desafortunadamente no puede cruzar los límites del sistema de archivos (que es mi caso de uso).
sdaau

26
No puedes enlazar a los directorios, ¿verdad?
Nanne

1
ln source destinationtambién funciona en OS X. Probado en El Capitan.
Mahdi Dibaiee

77
@Nanne no, pero se puede hacer: cp -al source destination. `-l 'significa archivos de enlace duro en lugar de copiar.
Paolo

66
Lamentablemente, no puede vincular directorios, ni a través de los límites del sistema de archivos. Esto hace que esta solución sea doblemente inviable para mí.
Konrad Rudolph el

25

Este es un enlace previo a la confirmación que reemplaza los blobs de enlaces simbólicos en el índice, con el contenido de esos enlaces simbólicos.

Pon esto en .git/hooks/pre-commit y hazlo ejecutable:

#!/bin/sh
# (replace "find ." with "find ./<path>" below, to work with only specific paths)

# (these lines are really all one line, on multiple lines for clarity)
# ...find symlinks which do not dereference to directories...
find . -type l -exec test '!' -d {} ';' -print -exec sh -c \
# ...remove the symlink blob, and add the content diff, to the index/cache
    'git rm --cached "$1"; diff -au /dev/null "$1" | git apply --cached -p1 -' \
# ...and call out to "sh".
    "process_links_to_nondir" {} ';'

# the end

Notas

Utilizamos la funcionalidad compatible con POSIX tanto como sea posible; sin embargo, diff -ano es compatible con POSIX, posiblemente entre otras cosas.

Puede haber algunos errores / errores en este código, aunque se haya probado un poco.


44
Es genial ver un intento de responder realmente la pregunta para archivos y no directorios. Sin embargo, nota que el anterior seguirá mostrando typechangeen git statuslos archivos que en realidad son enlaces simbólicos aunque git ahora las cosas que no son.
David Fraser

1
Gracias por esto; solo quería saber, ¿qué es process_links_to_nondir?
sdaau

@sdaau Es el nombre / argv[0]que se utiliza como nombre de comando para el shproceso. (Me tomó un poco entenderlo, ya que tampoco recordaba lo que era ☺😃)
Abbafei

3
@Abbafei ¿Puedes modificar el script para que funcione en Ubuntu (14.04)? Se está mostrando find: missing argument to -exec'. Puede ser necesario ejecutar un comando paso a paso en lugar de canalizar y combinar todo en una sola línea.
Khurshid Alam

1
@KhurshidAlam Para mí funcionó eliminar las líneas comentadas entre las líneas de comando. Sin embargo, el gancho no funciona como se esperaba (obtengo el me typechangegusta @DavidFraser, pero el archivo vinculado parece que ya no está en escena)
Scz

14

Encendido MacOS(tengo Mojave / 10.14, gitversión 2.7.1), use bindfs.

brew install bindfs

cd /path/to/git_controlled_dir

mkdir local_copy_dir

bindfs </full/path/to/source_dir> </full/path/to/local_copy_dir>

Ha sido insinuado por otros comentarios, pero no se proporciona claramente en otras respuestas. Esperemos que esto ahorre algo de tiempo.


Parece genial para los equipos que se ejecutan en Mac OS, creo que los enlaces duros que se describen a continuación deberían funcionar para Windows y Linux.
Devin G Rhode

Esto fue útil, y creo que la mejor solución para una capacidad útil pero faltante en MacOS. Sin embargo, tenga en cuenta que estaba recibiendo Failed to resolve... No such file or directoryerrores a menos que use nombres de ruta completos con el bindfscomando.
electromaggot

Gracias @electromaggot. Se agregó la aclaración de que los caminos completos son necesarios
ijoseph

1
¡Funciona muy bien en macos Catalina!
Jerry Green

13

Solía ​​agregar archivos más allá de los enlaces simbólicos desde hace bastante tiempo. Esto solía funcionar bien, sin hacer ningún arreglo especial. Desde que actualicé a Git 1.6.1, esto ya no funciona.

Es posible que pueda cambiar a Git 1.6.0 para que esto funcione. Espero que una versión futura de Git tenga una bandera git-addque le permita seguir enlaces simbólicos nuevamente.


12

Me cansé de que todas las soluciones aquí estuvieran desactualizadas o requirieran root, así que creé una solución basada en LD_PRELOAD (solo Linux).

Se engancha en las partes internas de Git, anulando el '¿es esto un enlace simbólico?' función, permitiendo que los enlaces simbólicos sean tratados como sus contenidos. Por defecto, todos los enlaces externos al repositorio están en línea; ver el enlace para más detalles.


Solución muy creativa que utiliza LD_PRELOADpara anular las funciones de la biblioteca.
iBug

Sí, pero debería elaborarse aquí. ¿Puedes hacer eso?
Peter Mortensen

No estoy seguro de cuánta elaboración ayudaría; a menos que copie el código fuente completo, esta respuesta siempre se basará en ese enlace, donde también se puede encontrar un archivo Léame. Pero sí, supongo que podría reproducir las partes importantes del archivo Léame.
Alcaro

Esto no se compila en OS X (Mojave 10.14.2). Obtenga cuatro errores quejándose de 'strchrnul' (¿quiso decir 'strchr'?) Y uno sobre '__xstat64' (quiso decir '__lxstat64'?). Finalmente obtengo un error de "acceso de miembro a tipo incompleto 'dirent64'". Ocurre independientemente si uso "make", "make OPT = 1" o "sh install.sh".
Erik Veland el

@ErikVeland Lo intenté un poco, pero parece que OSX no es compatible con LD_PRELOAD, ni nada similar. Varios documentos sugieren varias cosas, pero todos tienen varios años, y a Apple le encantan las cosas despreciables; No pude conseguir que ninguno de ellos funcionara. Lo siento.
Alcaro

6

Con Git 2.3.2+ (Q1 2015), hay otro caso en el que Git ya no seguirá el enlace simbólico: consulte commit e0d201b de Junio ​​C Hamano ( gitster) (principal mantenedor de Git)

apply: no toque un archivo más allá de un enlace simbólico

Debido a que Git rastrea enlaces simbólicos como enlaces simbólicos, una ruta que tiene un enlace simbólico en su parte principal (por ejemplo path/to/dir/file, dondepath/to/dir hay un enlace simbólico a otro lugar, ya sea dentro o fuera del árbol de trabajo) nunca puede aparecer en un parche que se aplique de manera válida , a menos que el mismo parche elimine primero el enlace simbólico para permitir que se cree un directorio allí.

Detectar y rechazar tal parche.

Del mismo modo, cuando una entrada crea un enlace simbólico path/to/diry luego crea un archivo path/to/dir/file, debemos marcarlo como un error sin crear realmente path/to/dirun enlace simbólico en el sistema de archivos.

En cambio, para cualquier parche en la entrada que deja una ruta (es decir, una no eliminación) en el resultado, verificamos todas las rutas principales contra el árbol resultante que el parche crearía al inspeccionar todos los parches en la entrada y luego el objetivo del parche aplicación (ya sea el índice o el árbol de trabajo).

De esta manera, nosotros:

  • detectar una travesura o un error al agregar un enlace simbólico path/to/diry un archivo path/to/dir/fileal mismo tiempo,
  • mientras permite un parche válido que elimina un símbolo link path/to/diry luego agrega un archivo path/to/dir/file.

Eso significa que, en ese caso, el mensaje de error no será genérico "%s: patch does not apply", sino más específico:

affected file '%s' is beyond a symbolic link

4

Hmmm, mount --bindno parece funcionar en Darwin.

¿Alguien tiene un truco que tenga?

[editado]

OK, encontré que la respuesta en Mac OS X es hacer un enlace duro. Excepto que esa API no está expuesta a través de ln, por lo que debe usar su propio pequeño programa para hacer esto. Aquí hay un enlace a ese programa:

Crear enlaces de directorio en Mac OS X

¡Disfrutar!


1
Si el directorio de destino de este enlace duro es un subdirectorio de otro repositorio git, esto sería un caos. Hacer operaciones git en el enlace rígido se aplicaría a este otro repositorio git. Solo revisa lo que estás haciendo.
yegle

44
Es posible hacer esto a través de code.google.com/p/bindfs que se puede instalar usando el puerto.
Kit Sunde

0

Estoy usando Git 1.5.4.3 y sigue el enlace simbólico pasado si tiene una barra inclinada final. P.ej

# Adds the symlink itself
$ git add symlink

# Follows symlink and adds the denoted directory's contents
$ git add symlink/

55
al menos en OSX esto da como resultadofatal: 'src/' is beyond a symbolic link
Dan Rosenstark

2
Como explicó @Mark Longair, esto solo funcionó hasta git 1.6.1
MestreLion

ese fue mi problema, gracias!
Mike Q

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.