352 votes

Symlinks Git sous Windows

Nos développeurs utilisent un mélange de systèmes d'exploitation basés sur 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 vers le fichier vers lequel il pointe. J'aimerais plutôt convertir le lien symbolique en un véritable lien symbolique Windows.

Le ( actualisé ) La solution que j'ai à ce problème est la suivante :

  • Ecrire un script post-checkout qui recherchera récursivement les fichiers textes "symlink".
  • Remplacez-les par un lien symbolique Windows (en utilisant mklink) avec le même nom et la même extension que le lien symbolique fictif.
  • Ignorez ces liens symboliques Windows en ajoutant une entrée dans .git/info/exclude.

Je ne l'ai pas mis en œuvre, mais je pense qu'il s'agit d'une approche solide de ce problème.

Questions :

  1. Quels inconvénients voyez-vous, le cas échéant, à cette approche ?
  2. Est-ce que ce script post-checkout est même implémentable ? C'est-à-dire que je peux trouver récursivement les fichiers "symlink" factices que git crée ?
  3. Quelqu'un a-t-il déjà travaillé sur un tel script ?

7 votes

Bien que Git supporte les liens symboliques, je vous déconseille fortement de les stocker comme liens dans votre dépôt, notamment si vous travaillez aussi avec ce code sous Windows.

4 votes

@Greg Hewgill - Je suis tout à fait d'accord avec vous. Malheureusement, la nature de notre base de code nécessite des liens symboliques... donc les supprimer n'est pas une option pour nous.

13 votes

Vous pouvez également demander sur la liste de diffusion msysgit pourquoi ils ne l'ont pas implémenté de cette manière dès le départ.

222voto

Mark Gollnick Points 791

J'ai posé exactement la même question il y a quelque temps (pas ici, mais en général) et j'ai fini par trouver une solution très similaire à celle proposée par l'OP. Je vais d'abord fournir des réponses directes aux questions 1, 2 et 3, puis je posterai la solution que j'ai fini par utiliser.

  1. Il y a effectivement quelques inconvénients à la solution proposée, 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 de "lien symbolique Windows". (Plus d'informations à ce sujet dans la section "limitations" ci-dessous).
  2. Oui, un script post-checkout est implémentable ! Peut-être pas comme un post-checkout littéral git checkout mais la solution ci-dessous a répondu à mes besoins suffisamment bien pour qu'un script littéral post-checkout ne soit pas nécessaire.
  3. Oui !

La solution :

Nos développeurs sont dans une situation similaire à celle de l'OP : un mélange d'hôtes Windows et Unix, des dépôts et des sous-modules avec de nombreux symlinks git, et aucun support natif (encore) dans la version de MsysGit pour gérer intelligemment ces symlinks sur les hôtes Windows.

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

  1. Créer des 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
    )"

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

    Par exemple, l'arbre 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/)

    Il peut être créé sous 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. Remplacer les 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.

    Utilisation :

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

    Cet alias permet de supprimer les liens symboliques git un par un ou tous ensemble d'un seul coup. Les symlinks seront remplacés par des hardlinks NTFS (dans le cas des fichiers) ou des junctions NTFS (dans le cas des répertoires). L'avantage d'utiliser les hardlinks+junctions par rapport aux "vrais" symlinks NTFS est que des autorisations UAC élevées ne sont pas nécessaires pour les créer.

    Pour supprimer les liens symboliques des sous-modules, il suffit d'utiliser le support intégré de git pour les itérer :

    git submodule foreach --recursive git rm-symlinks

    Mais, pour chaque action drastique comme celle-ci, un revirement est 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 défait git rm-symlinks ce qui a pour effet de restaurer le référentiel dans son état naturel (à l'exception de vos modifications, qui sont des debe restent intacts).

    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 les tabulations ou les nouvelles lignes ? YMMV (Je veux dire par là : ne faites pas ça, parce que ça ne fonctionne pas). ne sera pas travail.)

    • Si vous-même ou d'autres personnes oublient de git checkout-symlinks avant de faire quelque chose qui pourrait avoir de vastes conséquences comme git add -A le référentiel local pourrait se retrouver dans une état pollué.

      En utilisant notre "repo d'exemple" de tout à l'heure :

      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...

      C'est pourquoi il est intéressant d'inclure ces alias dans les étapes à effectuer pour les utilisateurs de Windows avant et après la construction d'un projet, plutôt qu'après le checkout ou avant le pushing. Mais chaque situation est différente. Ces alias ont été suffisamment utiles pour moi pour qu'une véritable solution post-checkout ne soit pas nécessaire.

J'espère que cela vous aidera !

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-13

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

Note finale : Bien que j'aie testé le chargement et l'exécution de ces alias en utilisant Bash 3.2 (et même 3.1) pour ceux qui sont encore bloqués sur ces anciennes versions pour de nombreuses raisons, sachez que les versions aussi anciennes sont connues pour leurs bogues d'analyseur. Si vous rencontrez des problèmes en essayant d'installer l'un de ces alias, la première chose à faire est de mettre à jour votre shell (pour Bash, vérifiez la version avec CTRL+X, CTRL+V). Alternativement, si vous essayez de les installer en les collant dans votre émulateur de terminal, vous aurez peut-être plus de chance en les collant dans un fichier et en le sourçant à la place, par exemple comme suit

. ./git-win-symlinks.sh

Bonne chance !

0 votes

Pouvez-vous y jeter un coup d'œil ici ? stackoverflow.com/questions/21403772/

0 votes

C'est un grand et merveilleux script, mais y a-t-il une raison pour que cela appose le mot "git" de façon aléatoire à la fin de certains de mes fichiers que je crée avec git add-symlink ?

0 votes

De plus, si votre nom de fichier contient "-h", vous obtenez l'usage. Toujours très utile script !

154voto

Cameron Tacklind Points 343

2020/2021 Réponse TL;DR

  1. Activer le "mode développeur" dans Windows 10/11 -- donne mklink permissions
  2. S'assurer que les liens symboliques sont activés dans git
    • git config --global core.symlinks true
    • o
    • cocher la case lors de l'installation de msysgit

Le changement de branche forcera la recréation des liens symboliques manquants.

Soyez prudent. Le support des Symlinks dans git sous Windows est le suivant relativement nouveau. Il y a quelques bogues qui affectent encore certains clients git. Notamment, les liens symboliques avec des valeurs relatives ( .. ) sont déformés dans certains programmes en raison d'une erreur de manipulation de la part de l'utilisateur. (fixe) régression dans libgit2 . Par exemple, GitKraken est affecté par cela car il attend nodegit de mettre à jour libgit2 de v0.x a v1.x qui comprend le correctif.

0 votes

Tous mes dépôts locaux ont core.symlinks=false, ce qui annulerait votre solution. Une idée de ce qui produit automatiquement cette configuration locale ? Peut-être l'installation de Git pour Windows sans cocher la case ?

0 votes

@gravidThoughts quel(s) client(s) git avez-vous installé(s) ? Peut-être que certains outils font cela ? Est-ce vrai sur un clone récent ?

1 votes

Vous devez également re-cloner le dépôt pour que les liens symboliques fonctionnent avec lui.

121voto

Josh Lee Points 53741

Vous pouvez trouver les liens symboliques en recherchant les fichiers qui ont un mode de 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 inchangés avec git update-index --assume-unchanged plutôt que de les énumérer dans .git/info/exclude .

2 votes

J'ai dû remplacer awk par gawk pour msysgit, mais sinon cela a fonctionné parfaitement. Merci !

6 votes

Helo ken. pourriez-vous partager votre script qui vérifie les fichiers texte symlink et les remplace par des symlinks sous Windows en utilisant mklink. alors que cela fonctionne pour nous, la partie --assume-unchanged ne fonctionne pas. en passant à une autre branche, git dit que les fichiers symlink sont modifiés et doivent être commités d'abord, alors que git status dit qu'il n'y a pas de changements une idée ?

6 votes

Voici un PowerShell que je viens de mettre en place - gist.github.com/ferventcoder/7995025

107voto

sirlunchalot Points 379

La version la plus récente de git scm (testet 2.11.1) permet d'activer les liens symboliques. Mais vous devez cloner à nouveau le dépôt 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. Consultez le site wiki .

enter image description here

1 votes

Ça n'a pas marché pour moi. J'ai réinstallé git pour Windows, je n'ai pas oublié de cocher la case symlink et j'ai cloné mon projet à nouveau. Mon tslint.json référençant le fichier dans le répertoire parent contient toujours ../tslint.json . Dommage, car cette solution semblait vraiment être la plus simple de toutes celles proposées.

12 votes

@JanAagaard Vous devez le cloner comme ceci : git clone -c core.symlinks=true <URL> Et sous Windows, vous devez l'exécuter avec des droits d'administrateur.

1 votes

Avez-vous trouvé un moyen de faire en sorte que cela fonctionne pour les utilisateurs normaux ? D'après la page wiki, cela devrait être possible mais cela ne fonctionne pas pour moi.

30voto

Simon Points 465

Comme les choses ont changé avec GIT depuis que beaucoup de ces réponses ont été postées, voici les instructions correctes pour que les liens symboliques fonctionnent correctement sous Windows à partir du

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 durs au lieu de liens symboliques.

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

AJOUTER AU FOND - MSYS=winsymlinks:nativestrict

3. Configurer git pour utiliser les liens symboliques

git config core.symlinks true

o

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. Je recommande donc d'ajouter ceci à chaque repo...

4. Tirez le repo

NOTE : A moins que vous n'ayez 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 Symlinks (facultatif) Si vous avez un repo existant, ou si vous utilisez des submodules, vous pouvez constater que les liens symboliques ne sont pas créés correctement. Pour rafraîchir tous les liens symboliques dans le repo, vous pouvez exécuter ces commandes.

find -type l -delete
git reset --hard

NOTE : ceci réinitialisera tous les changements depuis le dernier commit, donc assurez-vous d'avoir committé en premier.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X