Retendo as permissions de arquivo com o Git

Desejo controlar o servidor da minha versão, conforme descrito em Controle de versão do meu servidor da web , criando um repository git no meu /var/www directory . Minha esperança era que eu fosse capaz de empurrar o conteúdo web do nosso servidor dev para o github, puxá-lo para o nosso servidor de produção e passar o resto do dia no pool.

Aparentemente, uma torção no meu plano é que o Git não respeitará as permissions de arquivo (eu não tentei, apenas lendo sobre isso agora). Eu acho que isso faz sentido em checkboxs diferentes que podem ter diferentes configurações de usuário / grupo. Mas se eu quisesse forçar as permissions a se propagar, sabendo que meus servidores estão configurados da mesma forma, tenho alguma opção? Ou há uma maneira mais fácil de abordar o que estou tentando fazer?

    O git-cache-meta mencionado na pergunta SO ” git – como recuperar o arquivo de permissions git acha que o arquivo deve ser? ” (E o git FAQ ) é a abordagem mais staightforward.

    A idéia é armazenar em um arquivo .git_cache_meta as permissions dos arquivos e diretórios.
    É um arquivo separado não versionado diretamente no repository do Git.

    É por isso que o uso para isso é:

     $ git bundle create mybundle.bdl master; git-cache-meta --store $ scp mybundle.bdl .git_cache_meta machine2: #then on machine2: $ git init; git pull mybundle.bdl master; git-cache-meta --apply 

    Então você:

    • empacote seu repository e salve as permissions de arquivo associadas.
    • Copie esses dois arquivos no servidor remoto
    • restaurar o repository lá e aplicar a permissão

    O Git é o Version Control System, criado para desenvolvimento de software, portanto, de todo o conjunto de modos e permissions, ele armazena apenas bit executável (para arquivos comuns) e bit de link simbólico. Se você deseja armazenar permissions completas, você precisa de ferramentas de terceiros, como o git-cache-meta ( mencionado pelo VonC ), ou o Metastore (usado pelo etckeeper ). Ou você pode usar o IsiSetup , que o IIRC usa o git como backend.

    Veja a página Interfaces, frontends e tools no Git Wiki.

    Isso é bem tarde, mas pode ajudar alguns outros. Eu faço o que você quer fazer adicionando dois git hooks no meu repository.

    .git / hooks / pre-commit:

     #!/bin/bash # # A hook script called by "git commit" with no arguments. The hook should # exit with non-zero status after issuing an appropriate message if it wants # to stop the commit. SELF_DIR=`git rev-parse --show-toplevel` DATABASE=$SELF_DIR/.permissions # Clear the permissions database file > $DATABASE echo -n "Backing-up file permissions..." IFS_OLD=$IFS; IFS=$'\n' for FILE in `git ls-files` do # Save the permissions of all the files in the index echo $FILE";"`stat -c "%a;%U;%G" $FILE` >> $DATABASE done IFS=$IFS_OLD # Add the permissions database file to the index git add $DATABASE echo "OK" 

    .git / hooks / post-checkout:

     #!/bin/bash SELF_DIR=`git rev-parse --show-toplevel` DATABASE=$SELF_DIR/.permissions echo -n "Restoring file permissions..." IFS_OLD=$IFS; IFS=$'\n' while read -r LINE || [[ -n "$LINE" ]]; do FILE=`echo $LINE | cut -d ";" -f 1` PERMISSIONS=`echo $LINE | cut -d ";" -f 2` USER=`echo $LINE | cut -d ";" -f 3` GROUP=`echo $LINE | cut -d ";" -f 4` # Set the file permissions chmod $PERMISSIONS $FILE # Set the file owner and groups chown $USER:$GROUP $FILE done < $DATABASE IFS=$IFS_OLD echo "OK" exit 0 

    O primeiro gancho é chamado quando você "confirma" e lê a propriedade e as permissions de todos os arquivos no repository e os armazena em um arquivo na raiz do repository chamado .permissions e, em seguida, adiciona o arquivo .permissions ao commit.

    O segundo gancho é chamado quando você faz o "checkout" e passa pela lista de arquivos no arquivo .permissions e restaura a propriedade e as permissions desses arquivos.

    • Você pode precisar fazer o commit e checkout usando o sudo.
    • Certifique-se de que os scripts de pré-confirmação e pós-verificação tenham permissão de execução.

    No caso de você estar participando disso agora, eu acabei de passar por isso hoje e posso resumir onde isso está. Se você não tentou isso ainda, alguns detalhes aqui podem ajudar.

    Eu acho que a abordagem do @Omid Ariyan é o melhor caminho. Adicione os scripts pré-commit e pós-checkout. Não se esqueça de nomeá-los exatamente como Omid faz e não se esqueça de torná-los executáveis. Se você esquecer qualquer um desses, eles não terão efeito e você executará “git commit” repetidas vezes perguntando por que nada acontece 🙂 Além disso, se você recortar e colar o navegador, tenha cuidado para que as aspas e os ticks não sejam exibidos. alterado.

    Se você executar o script de pré-consolidação uma vez (executando um git commit), o arquivo .permissions será criado. Você pode adicioná-lo ao repository e acho desnecessário adicioná-lo repetidamente no final do script de pré-consolidação. Mas isso não faz mal, eu acho (espero).

    Existem alguns pequenos problemas sobre o nome do diretório e a existência de espaços nos nomes dos arquivos nos scripts do Omid. Os espaços foram um problema aqui e eu tive alguns problemas com a correção do IFS. Para o registro, este script de pré-consolidação funcionou corretamente para mim:

     #!/bin/bash SELF_DIR=`git rev-parse --show-toplevel` DATABASE=$SELF_DIR/.permissions # Clear the permissions database file > $DATABASE echo -n "Backing-up file permissions..." IFSold=$IFS IFS=$'\n' for FILE in `git ls-files` do # Save the permissions of all the files in the index echo $FILE";"`stat -c "%a;%U;%G" $FILE` >> $DATABASE done IFS=${IFSold} # Add the permissions database file to the index git add $DATABASE echo "OK" 

    Agora, o que nós tiramos disso?

    O arquivo .permissions está no nível superior do repository git. Tem uma linha por arquivo, aqui está o topo do meu exemplo:

     $ cat .permissions .gitignore;660;pauljohn;pauljohn 05.WhatToReport/05.WhatToReport.doc;664;pauljohn;pauljohn 05.WhatToReport/05.WhatToReport.pdf;664;pauljohn;pauljohn 

    Como você pode ver, nós temos

     filepath;perms;owner;group 

    Nos comentários sobre essa abordagem, um dos cartazes reclama que ele só funciona com o mesmo nome de usuário, e isso é tecnicamente verdadeiro, mas é muito fácil consertá-lo. Observe que o script pós-checkout tem duas partes de ação,

     # Set the file permissions chmod $PERMISSIONS $FILE # Set the file owner and groups chown $USER:$GROUP $FILE 

    Então eu estou apenas mantendo o primeiro, é tudo que eu preciso. Meu nome de usuário no servidor da Web é de fato diferente, mas o mais importante é que você não pode executar chown a menos que seja root. Pode executar “chgrp”, no entanto. É claro o suficiente como colocar isso em uso.

    Na primeira resposta deste post, a que é mais amplamente aceita, a sugestão é que use o git-cache-meta, um script que está fazendo o mesmo trabalho que os scripts de pré / pós gancho estão fazendo (parsing output from git ls-files ). Esses scripts são mais fáceis de entender, o código git-cache-meta é bem mais elaborado. É possível manter o git-cache-meta no caminho e escrever scripts pré-commit e pós-checkout que o usariam.

    Espaços em nomes de arquivos são um problema com os dois scripts do Omid. No script pós-checkout, você saberá que tem os espaços nos nomes dos arquivos, caso veja erros como este

     $ git checkout -- upload.sh Restoring file permissions...chmod: cannot access '04.StartingValuesInLISREL/Open': No such file or directory chmod: cannot access 'Notebook.onetoc2': No such file or directory chown: cannot access '04.StartingValuesInLISREL/Open': No such file or directory chown: cannot access 'Notebook.onetoc2': No such file or directory 

    Estou checando soluções para isso. Aqui está algo que parece funcionar, mas eu só testei em um caso

     #!/bin/bash SELF_DIR=`git rev-parse --show-toplevel` DATABASE=$SELF_DIR/.permissions echo -n "Restoring file permissions..." IFSold=${IFS} IFS=$ while read -r LINE || [[ -n "$LINE" ]]; do FILE=`echo $LINE | cut -d ";" -f 1` PERMISSIONS=`echo $LINE | cut -d ";" -f 2` USER=`echo $LINE | cut -d ";" -f 3` GROUP=`echo $LINE | cut -d ";" -f 4` # Set the file permissions chmod $PERMISSIONS $FILE # Set the file owner and groups chown $USER:$GROUP $FILE done < $DATABASE IFS=${IFSold} echo "OK" exit 0 

    Como as informações de permissions são uma linha de cada vez, defino o IFS como $, portanto, apenas as quebras de linha são vistas como coisas novas.

    Eu li que é MUITO IMPORTANTE configurar a variável de ambiente do IFS como estava! Você pode ver porque uma session de shell pode sair mal se você deixar $ como o único separador.

    No pré-commit / pós-checkout uma opção seria usar o utilitário “mtree” (FreeBSD), ou “fmtree” (Ubuntu) que “compara uma hierarquia de arquivos com uma especificação, cria uma especificação para uma hierarquia de arquivos, ou modifica uma especificação.”

    O conjunto padrão são flags, gid, link, mode, nlink, tamanho, hora, tipo e uid. Isso pode ser ajustado para o propósito específico com a chave -k.

    Eu estou rodando no FreeBSD 11.1, o conceito de virtualização freebsd jail torna o sistema operacional ótimo. A versão atual do Git que estou usando é 2.15.1, eu também prefiro executar tudo em scripts de shell. Com isso em mente, modifiquei as sugestões acima, como segue:

    git push: .git / hooks / pre-commit

     #! /bin/sh - # # A hook script called by "git commit" with no arguments. The hook should # exit with non-zero status after issuing an appropriate message if it wants # to stop the commit. SELF_DIR=$(git rev-parse --show-toplevel); DATABASE=$SELF_DIR/.permissions; # Clear the permissions database file > $DATABASE; printf "Backing-up file permissions...\n"; OLDIFS=$IFS; IFS=$'\n'; for FILE in $(git ls-files); do # Save the permissions of all the files in the index printf "%s;%s\n" $FILE $(stat -f "%Lp;%u;%g" $FILE) >> $DATABASE; done IFS=$OLDIFS; # Add the permissions database file to the index git add $DATABASE; printf "OK\n"; 

    git pull: .git / hooks / post-merge

     #! /bin/sh - SELF_DIR=$(git rev-parse --show-toplevel); DATABASE=$SELF_DIR/.permissions; printf "Restoring file permissions...\n"; OLDIFS=$IFS; IFS=$'\n'; while read -r LINE || [ -n "$LINE" ]; do FILE=$(printf "%s" $LINE | cut -d ";" -f 1); PERMISSIONS=$(printf "%s" $LINE | cut -d ";" -f 2); USER=$(printf "%s" $LINE | cut -d ";" -f 3); GROUP=$(printf "%s" $LINE | cut -d ";" -f 4); # Set the file permissions chmod $PERMISSIONS $FILE; # Set the file owner and groups chown $USER:$GROUP $FILE; done < $DATABASE IFS=$OLDIFS pritnf "OK\n"; exit 0; 

    Se por algum motivo você precisar recriar o script, a saída do arquivo .permissions deverá ter o seguinte formato:

     .gitignore;644;0;0 

    Para um arquivo .gitignore com 644 permissions dadas para root: roda

    Repare que eu tive que fazer algumas mudanças nas opções stat.

    Apreciar,

    Uma adição à resposta do @Omid Ariyan é permissions em diretórios. Adicione isto depois que o loop for for done em seu script de pre-commit .

     for DIR in $(find ./ -mindepth 1 -type d -not -path "./.git" -not -path "./.git/*" | sed 's@^\./@@') do # Save the permissions of all the files in the index echo $DIR";"`stat -c "%a;%U;%G" $DIR` >> $DATABASE done 

    Isso também salvará as permissions do diretório.