Git Symlinks no Windows

Nossos desenvolvedores usam uma combinação de sistemas operacionais baseados no Windows e no Unix. Portanto, links simbólicos criados em máquinas Unix se tornam um problema para os desenvolvedores do Windows. No windows (msysgit), o link simbólico é convertido em um arquivo de texto com um caminho para o arquivo para o qual ele aponta. Em vez disso, gostaria de converter o link simbólico em um link simbólico atual do Windows.

A solução ( atualizada ) que tenho para isso é:

  • Escreva um script pós-checkout que procure recursivamente por arquivos de texto “symlink”.
  • Substitua-os por um link simbólico do Windows (usando o mklink) com o mesmo nome e extensão que o “symlink”.
  • Ignore este symlink de janelas adicionando input em .git / info / exclude

Eu não implementei isso, mas acredito que esta é uma abordagem sólida para esse problema.

Questões:

  1. Quais, se houver, desvantagens você vê para essa abordagem?
  2. Este script pós-pagamento é mesmo implementável? Por exemplo, posso recursivamente descobrir os arquivos “symlink” fictícios que git cria?
  3. Alguém já trabalhou em tal script?

Você pode encontrar os links simbólicos procurando arquivos que tenham um modo de 120000 , possivelmente com este comando:

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

Depois de replace os links, eu recomendaria marcá-los como inalterados com git update-index --assume-unchanged , em vez de listá-los em .git/info/exclude .

Eu estava fazendo exatamente a mesma pergunta há algum tempo (não aqui, apenas em geral) e acabei chegando a uma solução muito semelhante à proposta do OP. Primeiro eu vou fornecer respostas diretas para as questões 1 2 e 3, e depois vou postar a solução que acabei usando.

  1. Na verdade, existem algumas desvantagens na solução proposta, principalmente em relação ao aumento do potencial de poluição do repository ou a inclusão acidental de arquivos duplicados enquanto eles estão em seus estados “links simbólicos do Windows”. (Mais sobre isso em “limitações” abaixo.)
  2. Sim, um script pós-checkout é implementável! Talvez não como uma etapa de git checkout pós- git checkout literal, mas a solução abaixo atendeu às minhas necessidades bem o suficiente para que um script de pós-verificação literal não fosse necessário.
  3. Sim!

A solução:

Nossos desenvolvedores estão na mesma situação dos OPs: uma mistura de hosts baseados em Windows e Unix, repositorys e submodules com muitos symlinks git e nenhum suporte nativo (ainda) na versão de lançamento do MsysGit para manipulação inteligente desses symlinks em hosts Windows .

Obrigado a Josh Lee por apontar o fato de que o git comete links simbólicos com o filemode especial 120000 . Com esta informação é possível adicionar alguns aliases git que permitem a criação e manipulação de links simbólicos git em hosts Windows.

  1. Criando links simbólicos no Windows

    ATUALIZADO 2014-11-12 (veja abaixo)

     git config --global alias.add-symlink '!__git_add_symlink(){ argv=($@) argc=${#argv[@]} # Look for options options=(" -h") o_help="false" case "${argv[@]}" in *" -h"*) o_help="true" ;; esac if [ "$o_help" == "true" -o "$argc" -lt "2" ]; then echo "\ Usage: git add-symlink   *  is a RELATIVE PATH, respective to . *  is a RELATIVE PATH, respective to the repository'\''s root dir. * Command must be run from the repository'\''s root dir." return 0 fi target_arg=${argv[0]} link_arg=${argv[1]} if [ ! -e "$target_arg" ]; then echo "ERROR: Target $target_arg does not exist; not creating invalid symlink." return 1 fi hash=$(echo -n "$target_arg" | git hash-object -w --stdin) git update-index --add --cacheinfo 120000 "$hash" "$link_arg" git checkout -- "$link_arg" }; __git_add_symlink "$@"' 

    Uso: git add-symlink , onde é uma referência relativa (com relação a ) para a localização atual do arquivo ou diretório ao qual se vincular, e é uma referência relativa (com relação à raiz do repository) para o destino desejado do link.

    Por exemplo, a tree do repository:

     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/) 

    Pode ser criado no Windows da seguinte maneira:

     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. Substituindo links simbólicos do git por hardlinks NTFS + junções

     git config --global alias.rm-symlink '!__git_rm_symlink(){ git checkout -- "$1" link=$(echo "$1") POS=$'\''/'\'' DOS=$'\''\\\\'\'' doslink=${link//$POS/$DOS} dest=$(dirname "$link")/$(cat "$link") dosdest=${dest//$POS/$DOS} if [ -f "$dest" ]; then rm -f "$link" cmd //C mklink //H "$doslink" "$dosdest" elif [ -d "$dest" ]; then rm -f "$link" cmd //C mklink //J "$doslink" "$dosdest" else echo "ERROR: Something went wrong when processing $1 . . ." echo " $dest may not actually exist as a valid target." fi }; __git_rm_symlink "$1"' git config --global alias.rm-symlinks '!__git_rm_symlinks(){ for symlink in $(git ls-files -s | egrep "^120000" | cut -f2); do git rm-symlink "$symlink" git update-index --assume-unchanged "$symlink" done }; __git_rm_symlinks' 

    Uso:

     git rm-symlink dir/foo/bar/lnk_file git rm-symlink lnk_bar git update-index --assume-unchanged dir/foo/bar/lnk_file git update-index --assume-unchanged lnk_bar 

    Isso remove os links simbólicos git, um a um, substituindo-os por hardlinks NTFS (no caso de arquivos) ou por junções NTFS (no caso de diretórios). O benefício de usar hardlinks + junções sobre links simbólicos NTFS “verdadeiros” é que as permissions elevadas do UAC não são necessárias para que sejam criadas. Finalmente, a seu próprio gosto, você pode optar por desmarcar como modificados (ou não) os links simbólicos “removidos” com git update-index .

    Por conveniência, você também pode simplesmente executar:

     git rm-symlinks 

    Isso remove TODOS os links simbólicos do git no repository atual, substituindo-os por hardlinks + junções, conforme necessário, e sinalizando automaticamente as alterações a serem ignoradas pelo git status .

    Para remover links simbólicos de submódulos, apenas use o suporte embutido do git para iterar sobre eles:

     git submodule foreach --recursive git rm-symlinks 

    Mas, para cada ação drástica como essa, é bom ter uma reversão …

  3. Restaurando links simbólicos do git no Windows

     git config --global alias.checkout-symlinks '!__git_checkout_symlinks(){ POS=$'\''/'\'' DOS=$'\''\\\\'\'' for symlink in $(git ls-files -s | egrep "^120000" | cut -f2); do git update-index --no-assume-unchanged "$symlink" dossymlink=${symlink//$POS/$DOS} cmd //C rmdir //Q "$dossymlink" 2>/dev/null git checkout -- "$symlink" echo "Restored git symlink $symlink <<===>> $(cat $symlink)" done }; __git_checkout_symlinks' 

    Uso: git checkout-symlinks , que desfaz git rm-symlinks , efetivamente restaurando o repository ao seu estado natural (exceto por suas alterações, que devem permanecer intactas).

    E para submódulos:

     git submodule foreach --recursive git checkout-symlinks 
  4. Limitações:

    1. Só pode ser executado a partir da raiz do repo, senão, a estranheza acontece …
    2. O preenchimento automático com base em guias é interrompido ao digitar um desses aliases
    3. Se as pessoas esquecerem de clicar em git checkout-symlinks para fazer o git checkout-symlinks antes de fazer algo como git add -A , elas podem poluir o repository!

      Usando nosso “exemplo de recompra” de antes:

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

      Ops …

      Por esse motivo, é bom include esses aliases como etapas a serem executadas para os usuários do Windows antes e depois da criação de um projeto, em vez de depois da finalização da compra ou antes do envio. Mas cada situação é diferente. Esses aliases foram úteis o suficiente para mim para que uma solução pós-pagamento verdadeira não tenha sido necessária.

Espero que ajude!

Referências:

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

http://technet.microsoft.com/pt-br/library/cc753194

ATUALIZAÇÃO 2014-11-12: Como eu, pessoalmente, usei apenas os aliases rm-symlinks e checkout-symlinks acima, consegui ignorar um erro bastante desagradável no alias add-symlink . Anteriormente, -n não estava sendo passado para a instrução echo responsável por criar o arquivo symlink do git que mais tarde seria adicionado à área de preparação como parte da operação add-symlink . Isso significa que uma nova linha ( 0x0D 0x0A em hosts do Windows) foi adicionada a todos os links simbólicos criados com o add-symlink . Embora esses links simbólicos git ainda fossem “removíveis” nos hosts Windows com rm-symlinks , se eles estivessem comprometidos com um repository público e depois clonados em um sistema genuíno baseado em posix, esses links sempre sairiam quebrados no outro. lado . Esse problema foi corrigido e o add-symlink agora deve funcionar como esperado.

A versão mais recente do git scm (testet 2.11.1) permite ativar links simbólicos. Mas você tem que clonar o repository com os links simbólicos novamente git clone -c core.symlinks=true . Você precisa executar este comando com direitos de administrador. Também é possível criar links simbólicos no Windows com o mklink. Confira o wiki .

insira a descrição da imagem aqui

Ele deve ser implementado no msysgit, mas há duas desvantagens:

  • Links simbólicos só estão disponíveis no Windows Vista e posteriores (não deve ser um problema em 2011, e ainda assim é …), já que versões mais antigas só suportam junções de diretório.
  • (a grande) A Microsoft considera os links simbólicos um risco de segurança e, portanto, somente os administradores podem criá-los por padrão. Você precisará elevar os privilégios do processo do git ou usar o fstool para alterar esse comportamento em todas as máquinas em que você trabalha.

Fiz uma pesquisa rápida e há trabalho sendo feito ativamente sobre isso, veja a questão 224 .

Eu sugiro que você não use links simbólicos dentro do repo ‘. Armazene o conteúdo real dentro do repository e coloque links simbólicos ao lado do repository que aponta para o conteúdo.

Então, digamos que você está usando um repo ‘para comparar a hospedagem do seu site no * nix com hospedagem no win. Armazene o conteúdo em seu repo ‘, digamos /httpRepoContent e c:\httpRepoContent com esta sendo a pasta que é sincronizada via GIT, SVN etc.

Em seguida, substitua a pasta de conteúdo do seu servidor web ( /var/www e c:\program files\web server\www {nomes realmente não importam, edite se for necessário}) com um link simbólico para o conteúdo em seu repo ‘. Os servidores da Web verão o conteúdo como realmente no lugar “certo”, mas você poderá usar o controle de origem.

No entanto, se você precisar usar links simbólicos no repo ‘, precisará procurar algo como algum tipo de script de pré / pós-commit. Eu sei que você pode usá-los para fazer coisas, como os arquivos de código de análise através de um formatador, por exemplo, por isso deve ser possível converter os links simbólicos entre as plataformas.

Se alguém sabe um bom lugar para aprender como fazer estes scripts para os controles de fonte comuns, então por favor adicione um comentário.

Para aqueles que usam o CygWin no Vista, Win7 ou acima, o comando git nativo pode criar links simbólicos “adequados” que são reconhecidos por aplicativos do Windows, como o Android Studio . Você só precisa definir a variável de ambiente CYGWIN para include winsymlinks:native ou winsymlinks:nativestrict como tal:

 export CYGWIN="$CYGWIN winsymlinks:native" 

A desvantagem disso (e significativo) é que o shell CygWin deve ser “Run as Administrator” para que seja necessário ter as permissions do SO para criar esses tipos de links simbólicos. Uma vez que eles são criados, nenhuma permissão especial é necessária para usá- los. Contanto que eles não sejam alterados no repository por outro desenvolvedor, o git depois disso é executado corretamente com permissions de usuário normais.

Pessoalmente, eu uso isso apenas para links simbólicos que são navegados por aplicativos do Windows (ou seja, não-CygWin) por causa dessa dificuldade adicional.

Para obter mais informações sobre essa opção, consulte esta questão da SO: Como criar um link simbólico com o cygwin no Windows 7

Aqui está um script em lote para converter links simbólicos no repository, apenas para arquivos, com base na resposta de Josh Lee. O script com alguma verificação adicional para direitos de administrador está em 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 "" >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 

Resposta curta: Agora eles são bem compatíveis, se você puder ativar o modo de desenvolvedor.

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

Agora, no Windows 10 Creators Update, um usuário (com direitos de administrador) pode ativar primeiro o modo de desenvolvedor e qualquer usuário na máquina pode executar o comando mklink sem elevar um console de linha de comando.

O que levou a essa mudança? A disponibilidade e o uso de links simbólicos é um grande negócio para os desenvolvedores modernos:

Muitas ferramentas de desenvolvimento populares como git e gerenciadores de pacotes, como o npm, reconhecem e persistem links simbólicos ao criar repos ou pacotes, respectivamente. Quando esses repositorys ou pacotes são restaurados em outro lugar, os links simbólicos também são restaurados, garantindo que o espaço em disco (e o tempo do usuário) não seja desperdiçado.

Fácil de ignorar com todos os outros anúncios da “Atualização do Criador”, mas se você ativar o Modo de Desenvolvedor, poderá criar links simbólicos sem privilégios elevados. Pode ser necessário reinstalar e garantir que o suporte esteja ativado, pois não é por padrão.

Links simbólicos não estão habilitados por padrão

Eu estava procurando uma solução fácil para lidar com os links simbólicos do Unix no Windows. Muito obrigado pelos aliases do Git acima. Há uma pequena otimização que pode ser feita nos links simbólicos rm para que não apague os arquivos na pasta de destino caso o alias seja executado uma segunda vez acidentalmente. Observe a condição new if no loop para garantir que o arquivo não seja um link para um diretório antes que a lógica seja executada.

 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 

Eu uso links sym o tempo todo entre o meu diretório original e o diretório git repo. Eu gosto de mantê-los separados. No windows eu uso a opção mklink / j. A junit parece deixar o git se comportar normalmente:

>mklink /j

por exemplo:

>mklink /jc:\gitRepos\Posts C:\Bitnami\wamp\apache2\htdocs\Posts

Um truque simples que usamos é apenas chamar git add --all duas vezes seguidas.

Por exemplo, nosso script de confirmação do Windows 7 chama:

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

O primeiro add trata o link como texto e adiciona as pastas para exclusão.

O segundo add percorre o link corretamente e desfaz a exclusão restaurando os arquivos.

É menos elegante do que algumas das outras soluções propostas, mas é uma solução simples para alguns de nossos ambientes legados que receberam links simbólicos.

Assim como as coisas mudaram com o GIT, uma vez que muitas dessas respostas foram postadas aqui são as instruções corretas para que os links simbólicos funcionem corretamente no Windows a partir de

AGOSTO DE 2018


1. Certifique-se de que o git esteja instalado com suporte a links simbólicos

Durante a instalação do git no windows

2. Diga ao Bash para criar hardlinks em vez de links simbólicos

EDIT – (pasta git) /etc/bash.bashrc

ADICIONAR AO INFERIOR – MSYS=winsymlinks:nativestrict

3. Configure git config para usar links simbólicos

 git config core.symlinks true 

ou

 git clone -c core.symlinks=true  

NOTA: Eu tentei adicionar isso ao global git config e no momento não está funcionando para mim, então eu recomendo adicionar isso a cada repo …

4. puxe o repo

NOTA: A menos que você tenha ativado o modo de desenvolvedor na versão mais recente do Windows 10, será necessário executar o bash como administrador para criar links simbólicos.

5. Redefinir todos os links simbólicos (opcional) Se você tiver um repository existente ou estiver usando submódulos, talvez descubra que os links simbólicos não estão sendo criados corretamente, portanto, para atualizar todos os links simbólicos no repository, você pode executar esses comandos.

 find -type l -delete git reset --hard 

OBSERVAÇÃO: isso redefinirá as alterações desde o último commit, por isso certifique-se de ter feito o primeiro commit