web-dev-qa-db-fra.com

Git Symlinks dans Windows

Nos développeurs utilisent un mélange de systèmes d'exploitation Windows et Unix. Par conséquent, les liens symboliques créés sur des machines Unix deviennent un problème pour les développeurs Windows. Dans Windows (msysgit), le lien symbolique est converti en un fichier texte avec un chemin d'accès au fichier vers lequel il pointe. Au lieu de cela, j'aimerais convertir le lien symbolique en un lien symbolique Windows réel.

La solution (mise à jour) dont je dispose est la suivante:

  • Écrivez un script de post-paiement qui recherchera de manière récursive des fichiers texte "lien symbolique".
  • Remplacez-les par le lien symbolique Windows (avec mklink) portant le même nom et la même extension que le nom factice "lien symbolique".
  • Ignorer ces liens symboliques Windows en ajoutant une entrée dans .git/info/exclude

Je n'ai pas implémenté ceci, mais je pense que c'est une approche solide à ce problème.

Des questions:

  1. Quels inconvénients, le cas échéant, voyez-vous dans cette approche?
  2. Ce script post-paiement est-il même implémentable? est-ce que je peux trouver récursivement le faux fichier "lien symbolique" créé par git?
  3. Quelqu'un a-t-il déjà travaillé sur un tel scénario?
223
Ken Hirakawa

Vous pouvez trouver les liens symboliques en recherchant les fichiers ayant le mode 120000, éventuellement avec cette commande:

git ls-files -s | awk '/120000/{print $4}'

Une fois que vous avez remplacé les liens, je vous recommande de les marquer comme non modifiés avec git update-index --assume-unchanged, plutôt que de les lister dans .git/info/exclude.

95
Josh Lee

Je posais cette même question il y a quelque temps (pas ici, mais en général) et j'ai fini par proposer une solution très similaire à la proposition de OP. Je vais d’abord répondre directement aux questions 1, 2 et 3, puis je posterai la solution que j’ai finalement utilisée.

  1. La solution proposée présente en effet quelques inconvénients, principalement en ce qui concerne un potentiel accru de pollution du référentiel ou l’ajout accidentel de fichiers en double alors qu’ils sont dans leur état "Windows symlink". (Plus à ce sujet sous "limitations" ci-dessous.)
  2. Oui, un script post-paiement est implémentable! Peut-être pas comme une étape post -git checkout littérale, mais la solution ci-dessous a assez bien répondu à mes besoins qu’un script de post-paiement n’a pas été nécessaire.
  3. Oui!

La solution:

Nos développeurs sont à peu près dans la même situation que les OP: un mélange d’hôtes, de référentiels et de sous-modules Windows et de type Unix avec de nombreux liens symboliques git, et aucun support natif (encore) dans la version finale de MsysGit pour la gestion intelligente de ces liens symboliques sur les hôtes Windows. .

Merci à Josh Lee d'avoir signalé le fait que git envoie des liens symboliques avec le mode de fichier spécial 120000. Avec ces informations, il est possible d'ajouter quelques alias git permettant la création et la manipulation de liens symboliques git sur des hôtes Windows.

  1. Création de liens symboliques git sous Windows

    git config --global alias.add-symlink '!'"$(cat <<'ETX'
    __git_add_symlink() {
      if [ $# -ne 2 ] || [ "$1" = "-h" ]; then
        printf '%b\n' \
            'usage: git add-symlink <source_file_or_dir> <target_symlink>\n' \
            'Create a symlink in a git repository on a Windows Host.\n' \
            'Note: source MUST be a path relative to the location of target'
        [ "$1" = "-h" ] && return 0 || return 2
      fi
    
      source_file_or_dir=${1#./}
      source_file_or_dir=${source_file_or_dir%/}
    
      target_symlink=${2#./}
      target_symlink=${target_symlink%/}
      target_symlink="${GIT_PREFIX}${target_symlink}"
      target_symlink=${target_symlink%/.}
      : "${target_symlink:=.}"
    
      if [ -d "$target_symlink" ]; then
        target_symlink="${target_symlink%/}/${source_file_or_dir##*/}"
      fi
    
      case "$target_symlink" in
        (*/*) target_dir=${target_symlink%/*} ;;
        (*) target_dir=$GIT_PREFIX ;;
      esac
    
      target_dir=$(cd "$target_dir" && pwd)
    
      if [ ! -e "${target_dir}/${source_file_or_dir}" ]; then
        printf 'error: git-add-symlink: %s: No such file or directory\n' \
            "${target_dir}/${source_file_or_dir}" >&2
        printf '(Source MUST be a path relative to the location of target!)\n' >&2
        return 2
      fi
    
      git update-index --add --cacheinfo 120000 \
          "$(printf '%s' "$source_file_or_dir" | git hash-object -w --stdin)" \
          "${target_symlink}" \
        && git checkout -- "$target_symlink" \
        && printf '%s -> %s\n' "${target_symlink#$GIT_PREFIX}" "$source_file_or_dir" \
        || return $?
    }
    __git_add_symlink
    ETX
    )"
    

    Usage: git add-symlink <source_file_or_dir> <target_symlink>, où l’argument correspondant au fichier ou au répertoire source doit se présenter sous la forme d’un chemin relatif au lien symbolique cible. Vous pouvez utiliser cet alias de la même manière vous utiliseriez normalement ln.

    Par exemple, l'arborescence du référentiel:

    dir/
    dir/foo/
    dir/foo/bar/
    dir/foo/bar/baz      (file containing "I am baz")
    dir/foo/bar/lnk_file (symlink to ../../../file)
    file                 (file containing "I am file")
    lnk_bar              (symlink to dir/foo/bar/)
    

    Peut être créé sur Windows comme suit:

    git init
    mkdir -p dir/foo/bar/
    echo "I am baz" > dir/foo/bar/baz
    echo "I am file" > file
    git add -A
    git commit -m "Add files"
    git add-symlink ../../../file dir/foo/bar/lnk_file
    git add-symlink dir/foo/bar/ lnk_bar
    git commit -m "Add symlinks"
    
  2. Remplacement des liens symboliques git par des liens durs NTFS + jonctions

    git config --global alias.rm-symlinks '!'"$(cat <<'ETX'
    __git_rm_symlinks() {
      case "$1" in (-h)
        printf 'usage: git rm-symlinks [symlink] [symlink] [...]\n'
        return 0
      esac
      ppid=$$
      case $# in
        (0) git ls-files -s | grep -E '^120000' | cut -f2 ;;
        (*) printf '%s\n' "$@" ;;
      esac | while IFS= read -r symlink; do
        case "$symlink" in
          (*/*) symdir=${symlink%/*} ;;
          (*) symdir=. ;;
        esac
    
        git checkout -- "$symlink"
        src="${symdir}/$(cat "$symlink")"
    
        posix_to_dos_sed='s_^/\([A-Za-z]\)_\1:_;s_/_\\\\_g'
        doslnk=$(printf '%s\n' "$symlink" | sed "$posix_to_dos_sed")
        dossrc=$(printf '%s\n' "$src" | sed "$posix_to_dos_sed")
    
        if [ -f "$src" ]; then
          rm -f "$symlink"
          cmd //C mklink //H "$doslnk" "$dossrc"
        Elif [ -d "$src" ]; then
          rm -f "$symlink"
          cmd //C mklink //J "$doslnk" "$dossrc"
        else
          printf 'error: git-rm-symlink: Not a valid source\n' >&2
          printf '%s =/=> %s  (%s =/=> %s)...\n' \
              "$symlink" "$src" "$doslnk" "$dossrc" >&2
          false
        fi || printf 'ESC[%d]: %d\n' "$ppid" "$?"
    
        git update-index --assume-unchanged "$symlink"
      done | awk '
        BEGIN { status_code = 0 }
        /^ESC\['"$ppid"'\]: / { status_code = $2 ; next }
        { print }
        END { exit status_code }
      '
    }
    __git_rm_symlinks
    ETX
    )"
    
    git config --global alias.rm-symlink '!git rm-symlinks'  # for back-compat.
    

    Usage:

    git rm-symlinks [symlink] [symlink] [...]
    

    Cet alias peut supprimer les liens symboliques git un par un ou tous les deux à la fois. Les liens symboliques seront remplacés par des liens durs NTFS (dans le cas de fichiers) ou par des jonctions NTFS (dans le cas de répertoires). L'avantage d'utiliser des liens durs + des jonctions par rapport aux "vrais" liens symboliques NTFS est que les autorisations de contrôle de compte élevées ne sont pas nécessaires pour être créées.

    Pour supprimer les liens symboliques des sous-modules, utilisez simplement le support intégré de git pour les parcourir:

    git submodule foreach --recursive git rm-symlinks
    

    Mais, pour chaque action aussi drastique, un retournement est bien agréable à avoir ...

  3. Restauration des liens symboliques git sous Windows

    git config --global alias.checkout-symlinks '!'"$(cat <<'ETX'
    __git_checkout_symlinks() {
      case "$1" in (-h)
        printf 'usage: git checkout-symlinks [symlink] [symlink] [...]\n'
        return 0
      esac
      case $# in
        (0) git ls-files -s | grep -E '^120000' | cut -f2 ;;
        (*) printf '%s\n' "$@" ;;
      esac | while IFS= read -r symlink; do
        git update-index --no-assume-unchanged "$symlink"
        rmdir "$symlink" >/dev/null 2>&1
        git checkout -- "$symlink"
        printf 'Restored git symlink: %s -> %s\n' "$symlink" "$(cat "$symlink")"
      done
    }
    __git_checkout_symlinks
    ETX
    )"
    
    git config --global alias.co-symlinks '!git checkout-symlinks'
    

    Utilisation: git checkout-symlinks [symlink] [symlink] [...], qui annule git rm-symlinks, restaure effectivement le référentiel dans son état naturel (à l'exception de vos modifications, qui devraitrester intact).

    Et pour les sous-modules:

    git submodule foreach --recursive git checkout-symlinks
    
  4. Limitations:

    • Les répertoires/fichiers/liens symboliques avec des espaces dans leurs chemins devraient fonctionner. Mais des onglets ou des nouvelles lignes? YMMV… (Je veux dire par là: ne faites pas cela, car cela ne ne fonctionnera pastravail.)

    • Si vous-même ou les autres oubliez de git checkout-symlinks avant de faire quelque chose de lourd en conséquences, comme git add -A, le référentiel local pourrait se retrouver dans un état polluted.

      En utilisant notre "exemple de dépôt" d'avant:

      echo "I am nuthafile" > dir/foo/bar/nuthafile
      echo "Updating file" >> file
      git add -A
      git status
      # On branch master
      # Changes to be committed:
      #   (use "git reset HEAD <file>..." to unstage)
      #
      #       new file:   dir/foo/bar/nuthafile
      #       modified:   file
      #       deleted:    lnk_bar           # POLLUTION
      #       new file:   lnk_bar/baz       # POLLUTION
      #       new file:   lnk_bar/lnk_file  # POLLUTION
      #       new file:   lnk_bar/nuthafile # POLLUTION
      #
      

      Oups ...

      Pour cette raison, il est agréable d’inclure ces alias en tant qu’étapes à exécuter par les utilisateurs Windows avant et après la construction d’un projet, plutôt qu’après la fin du processus ou avant le transfert. Mais chaque situation est différente. Ces alias m'ont été suffisamment utiles pour qu'une véritable solution post-paiement ne soit pas nécessaire.

J'espère que ça t'as aidé!

Références:

http://git-scm.com/book/en/Git-Internals-Git-Objects

http://technet.Microsoft.com/en-us/library/cc753194

Dernière mise à jour: 2019-03-1

  • Conformité POSIX (enfin, sauf pour les appels mklink, bien sûr) - pas plus Bashisms !
  • Les répertoires et les fichiers contenant des espaces sont pris en charge.
  • Les codes d'état de sortie nuls et non nuls (pour communiquer la réussite/l'échec de la commande demandée, respectivement) sont maintenant correctement conservés/renvoyés.
  • L’alias add-symlink fonctionne maintenant plus comme ln (1) et peut être utilisé à partir de n’importe quel répertoire du référentiel, et pas uniquement du répertoire racine du référentiel.
  • Le rm-symlink alias (singulier) a été remplacé par le rm-symlinks alias (pluriel), qui accepte désormais plusieurs arguments (ou aucun argument, qui trouve tous les liens symboliques dans le référentiel, comme auparavant). pour transformer sélectivement les liens symboliques git en liens durs NTFS + jonctions.
  • L'alias checkout-symlinks a également été mis à jour pour accepter plusieurs arguments (ou aucun, == tout) afin d'inverser de manière sélective les transformations susmentionnées.

Note finale: Bien que j'ai testé le chargement et l'exécution de ces alias à l'aide de Bash 3.2 (et même de 3.1) pour ceux qui risquent de rester bloqués sur de telles versions anciennes pour un certain nombre de raisons, sachez que les versions aussi anciennes comme ceux-ci sont notoires pour leurs bogues d'analyse. Si vous rencontrez des problèmes lors de la tentative d'installation de l'un de ces alias, commencez par mettre à niveau votre shell (pour Bash, vérifiez la version avec CTRL + X, CTRL + V). Si vous essayez de les installer en les collant dans votre émulateur de terminal, vous aurez peut-être plus de chance de les coller dans un fichier et de les rechercher à la place, par exemple. comme

. ./git-win-symlinks.sh

Bonne chance!

178
Mark G.

La version la plus récente de git scm (testet 2.11.1) permet d'activer les liens symboliques. Mais vous devez à nouveau cloner le référentiel avec les liens symboliques git clone -c core.symlinks=true <URL>. Vous devez exécuter cette commande avec des droits d'administrateur. Il est également possible de créer des liens symboliques sous Windows avec mklink. Découvrez le wiki .

enter image description here

60
sirlunchalot

Cela devrait être implémenté dans msysgit, mais il y a deux inconvénients:

  • Les liens symboliques ne sont disponibles que dans Windows Vista et les versions ultérieures (cela ne devrait pas être un problème en 2011, et pourtant, ça l'est ...), car les anciennes versions ne prennent en charge que les jonctions de répertoires.
  • (le plus important) Microsoft considère les liens symboliques comme un risque pour la sécurité et seuls les administrateurs peuvent donc les créer par défaut. Vous devrez élever les privilèges du processus git ou utiliser fstool pour modifier ce comportement sur chaque machine sur laquelle vous travaillez.

J'ai fait une recherche rapide et des travaux sont en cours, voir la question 224 .

14
djs

donc, comme les choses ont changé avec GIT depuis que beaucoup de réponses ont été postées, voici les instructions correctes pour que les liens symboliques fonctionnent correctement dans les fenêtres à partir de

AOUT 2018


1. Assurez-vous que git est installé avec le support des liens symboliques

During the install of git on windows

2. Dites à Bash de créer des liens physiques à la place de liens symboliques

EDIT - (dossier git) /etc/bash.bashrc

AJOUTER AU BAS - MSYS=winsymlinks:nativestrict

3. Configurez git config pour utiliser des liens symboliques

git config core.symlinks true

ou

git clone -c core.symlinks=true <URL>

NOTE: J'ai essayé d'ajouter ceci à la configuration globale de git et pour le moment cela ne fonctionne pas pour moi, donc je recommande de l'ajouter à chaque rapport de suivi ...

4. tirer le repo

REMARQUE: sauf si vous avez activé le mode développeur dans la dernière version de Windows 10, vous devez exécuter bash en tant qu'administrateur pour créer des liens symboliques.

5. Réinitialiser tous les liens symboliques (facultatif) Si vous avez un référentiel existant ou utilisez des sous-modules, vous constaterez peut-être que les liens symboliques ne sont pas créés correctement. Vous pouvez donc les actualiser. commandes.

find -type l -delete
git reset --hard

NOTE: cela réinitialisera toutes les modifications depuis la dernière validation alors assurez-vous d’avoir commis la première

10
Simon

Je suggérerais que vous n'utilisiez pas de liens symboliques dans le repo '. Stockez le contenu réel dans le référentiel, puis placez des liens symboliques en dehors du référentiel qui pointe vers le contenu.

Supposons donc que vous utilisez un référentiel pour comparer l'hébergement de votre site sur * nix avec l'hébergement de Win. Stockez le contenu dans votre dépôt ', disons /httpRepoContent et c:\httpRepoContent, le dossier synchronisé via GIT, SVN, etc.

Ensuite, remplacez le dossier de contenu de votre serveur Web (/var/www et c:\program files\web server\www {les noms importent peu, modifiez-le si nécessaire}) par un lien symbolique vers le contenu de votre référentiel '. Les serveurs Web verront le contenu comme étant à la "bonne" place, mais vous utiliserez votre contrôle de source.

Cependant, si vous devez utiliser des liens symboliques dans le référentiel ', vous devrez vous pencher sur quelque chose comme une sorte de script avant/après validation. Je sais que vous pouvez les utiliser pour effectuer certaines tâches, telles que l'analyse de fichiers de code via un programme de formatage, afin de pouvoir convertir les liens symboliques entre les plates-formes.

si quelqu'un connaît un bon endroit pour apprendre à faire ces scripts pour les contrôles source communs, SVN GIT MG, alors veuillez ajouter un commentaire.

9
thecoshman

Réponse courte: ils sont désormais bien pris en charge, si vous pouvez activer le mode développeur.

De https://blogs.windows.com/buildingapps/2016/12/02/symlinks-windows-10/

Désormais, dans Windows 10 Creators Update, un utilisateur (avec des droits d'administrateur) peut d'abord activer le mode développeur, puis tout utilisateur de l'ordinateur peut exécuter la commande mklink sans élever la console de ligne de commande.

Qu'est-ce qui a motivé ce changement? La disponibilité et l'utilisation de liens symboliques est un gros problème pour les développeurs modernes:

De nombreux outils de développement populaires tels que git et les gestionnaires de paquets, tels que npm, reconnaissent et conservent les liens symboliques persistants lors de la création de dépôts ou de paquets, respectivement. Lorsque ces dépôts ou ces paquets sont ensuite restaurés ailleurs, les liens symboliques sont également restaurés, pour que l’espace disque (et le temps de l’utilisateur) ne soit pas perdu.

Il est facile d'ignorer toutes les autres annonces de la "mise à jour du créateur", mais si vous activez le mode Développeur, vous pouvez créer des liens symboliques sans privilèges élevés. Vous devrez peut-être réinstaller et vous assurer que le support est activé, car ce n'est pas le cas par défaut.

Symbolic Links aren't enabled by default

9
Orangutech

Pour ceux qui utilisent CygWin sur Vista, Win7 ou une version ultérieure, la commande native git peut créer des liens symboliques "appropriés" reconnus par les applications Windows, tels que Android Studio . Vous devez simplement définir la variable d’environnement CYGWIN pour inclure winsymlinks:native ou winsymlinks:nativestrict en tant que tel:

export CYGWIN="$CYGWIN winsymlinks:native"

L'inconvénient de ceci (et de son inconvénient majeur) est que le shell CygWin doit être "Exécuter en tant qu'administrateur" afin de disposer des autorisations du système d'exploitation requises pour créer ce type de lien symbolique. Cependant, une fois créés, aucune autorisation spéciale n'est requise pour les utiliser . Tant qu'ils ne sont pas modifiés dans le référentiel par un autre développeur, git fonctionne ensuite correctement avec les autorisations utilisateur normales.

Personnellement, j’utilise ceci uniquement pour les liens symboliques qui sont parcourus par les applications Windows (c’est-à-dire non-CygWin) en raison de cette difficulté supplémentaire.

Pour plus d'informations sur cette option, consultez cette SO question: Comment créer un lien symbolique avec cygwin dans Windows 7

7
Brian White

Voici un script de traitement par lots pour la conversion de liens symboliques dans un référentiel, pour les fichiers uniquement, basé sur la réponse de Josh Lee. Le script avec une vérification supplémentaire des droits d'administrateur est à https://Gist.github.com/Quazistax/8daf09080bf54b4c7641 .

@echo off
pushd "%~dp0"
setlocal EnableDelayedExpansion

for /f "tokens=3,*" %%e in ('git ls-files -s ^| findstr /R /C:"^120000"') do (
     call :processFirstLine %%f
)
REM pause
goto :eof

:processFirstLine
@echo.
@echo FILE:    %1

dir "%~f1" | find "<SYMLINK>" >NUL && (
  @echo FILE already is a symlink
  goto :eof
)

for /f "usebackq tokens=*" %%l in ("%~f1") do (
  @echo LINK TO: %%l

  del "%~f1"
  if not !ERRORLEVEL! == 0 (
    @echo FAILED: del
    goto :eof
  )

  setlocal
  call :expandRelative linkto "%1" "%%l"
  mklink "%~f1" "!linkto!"
  endlocal
  if not !ERRORLEVEL! == 0 (
    @echo FAILED: mklink
    @echo reverting deletion...
    git checkout -- "%~f1"
    goto :eof
  )

  git update-index --assume-unchanged "%1"
  if not !ERRORLEVEL! == 0 (
    @echo FAILED: git update-index --assume-unchanged
    goto :eof
  )
  @echo SUCCESS
  goto :eof
)
goto :eof

:: param1 = result variable
:: param2 = reference path from which relative will be resolved
:: param3 = relative path
:expandRelative
  pushd .
  cd "%~dp2"
  set %1=%~f3
  popd
goto :eof
5
Quazistax

Je recherchais une solution simple pour gérer les liens symboliques Unix sous Windows. Merci beaucoup pour les pseudonymes ci-dessus de Git. Il existe une petite optimisation qui peut être apportée à rm-symlinks afin qu’il ne supprime pas les fichiers du dossier de destination au cas où l’alias serait exécuté une deuxième fois par erreur. Veuillez observer la nouvelle condition if dans la boucle pour vous assurer que le fichier n'est pas déjà un lien vers un répertoire avant que la logique ne soit exécutée.

git config --global alias.rm-symlinks '!__git_rm_symlinks(){
for symlink in $(git ls-files -s | egrep "^120000" | cut -f2); do
    *if [ -d "$symlink" ]; then
      continue
    fi*
    git rm-symlink "$symlink"
    git update-index --assume-unchanged "$symlink"
done
}; __git_rm_symlinksenter 
2
prgrminglover

J'utilise des liens sym tout le temps entre la racine de mon document et le répertoire git repo. J'aime les garder séparés. Sur Windows, j'utilise l'option mklink/j. La jonction semble laisser git se comporter normalement:

>mklink /j <location(path) of link> <source of link>

par exemple:

>mklink /j c:\gitRepos\Posts C:\Bitnami\wamp\Apache2\htdocs\Posts

2
mmv_sat

Une astuce simple consiste à appeler git add --all deux fois de suite.

Par exemple, nos appels de script de validation Windows 7:

$ git add --all
$ git add --all

Le premier ajout traite le lien en tant que texte et ajoute les dossiers à supprimer.

Le deuxième ajout traverse correctement le lien et annule la suppression en restaurant les fichiers.

C'est moins élégant que certaines des solutions proposées, mais c'est une solution simple à certains de nos environnements hérités auxquels des liens symboliques ont été ajoutés.

1
Mike Panoff