Controle de versão automático após a alteração do arquivo (modificar / criar / excluir)

16

Estou procurando uma implementação (no Linux) de um mecanismo que versão automática e transparente de qualquer alteração em um diretório (recursivamente). Isso pretende ser uma adição (possivelmente uma substituição, se todos os recursos solicitados estiverem disponíveis) ao versionamento padrão (SVN, git, ...)

Um produto no MS Windows que faz isso é o AutoVer (para ter uma idéia melhor dos requisitos). Eu adoraria ter algo assim, mas voltado para o Linux em um ambiente não gráfico.

Vi que existem algumas tentativas de ter essa funcionalidade no Linux, a mais próxima que encontrei é a versão automática no Subversion, mas não é óbvio para implementar em ambientes existentes (servidores onde, por exemplo, os arquivos de configuração são locais).

Talvez algo trabalhando com inotify?

Agradecemos antecipadamente por qualquer indicação! WoJ

WoJ
fonte
relacionado: flashbake
Dan D.
Existe um requisito especial sobre qual software você usa? Como se você estiver apenas procurando rastrear alterações que você faz manualmente (editando arquivos), o Eclipse possui esse recurso incorporado, chamado "histórico local".
Stefan Seidel
@StefanSeidel Eu não sou o iniciador de tópicos, mas preferiria uma solução sem IDE.
Michael Pankov 06/02

Respostas:

5

1. Método de uso geral usando bazar e inotify

Isso não foi testado por mim, mas eu encontrei essa gravação que utiliza o bzr(bazar) e inotifywaitpara monitorar um diretório e controlar a versão dos arquivos nele usando o bazar.

Este script faz todo o trabalho de observar o diretório para alterações:

#!/bin/bash

# go to checkout repository folder you want to watch
cd path/to/www/parent/www
# start watching the directory for changes recusively, ignoring .bzr dir
# comment is made out of dir/filename
# no output is shown from this, but wrinting a filename instead of /dev/null 
# would allow logging
inotifywait –exclude \.bzr -r -q -m -e CLOSE_WRITE \
    –format=”bzr commit -m ‘autocommit for %w/%f’” ./ | \
    sh  2>/dev/null 1>&2 &
# disown the pid, so the inotify thread will get free from parent process
# and will not be terminated with it
PID=`ps aux | grep inotify | grep CLOSE_WRITE | grep -v grep | awk ‘{print $2}’`
disown $PID

# this is for new files, not modifications, optional
inotifywait –exclude \.bzr -r -q -m -e CREATE \
    –format=”bzr add *; bzr commit -m ‘new file added %w/%f’” ./ | \
    sh  2>/dev/null 1>&2 &
PID=`ps aux | grep inotify | grep CREATE | grep -v grep | awk ‘{print $2}’`
disown $PID

exit 0;

2. Gerenciando / etc

Para o caso especial de gerenciar o /etcdiretório do seu sistema , você pode usar o aplicativo etckeeper .

O etckeeper é uma coleção de ferramentas para permitir que o / etc seja armazenado em um repositório git, mercurial, darcs ou bzr. Ele se conecta ao apt (e outros gerenciadores de pacotes, incluindo yum e pacman-g2) para confirmar automaticamente as alterações feitas no / etc durante as atualizações do pacote. Ele rastreia os metadados de arquivo que os sistemas de controle de revisão normalmente não suportam, mas isso é importante para o / etc, como as permissões do / etc / shadow. É bastante modular e configurável, além de ser simples de usar, se você entender o básico de como trabalhar com controle de revisão.

Aqui está um bom tutorial para você começar a usá-lo.

3. Usando git e incron

Esta técnica utiliza gite incron. Para esse método, você precisa fazer o seguinte:

A. Faça um repo

% mkdir $HOME/git
% cd $HOME/git
% git init

B. Crie um $HOME/bin/git-autocommitscript

#!/bin/bash

REP_DIR="$HOME/git"       # repository directory
NOTIFY_DIR="$HOME/srv"    # directory to version

cd $REP_DIR
GIT_WORK_TREE=$NOTIFY_DIR /usr/bin/git add .
GIT_WORK_TREE=$NOTIFY_DIR /usr/bin/git commit -a -m "auto"

C. Adicione uma entrada ao incrontab

% sudo incrontab -e $HOME/srv IN_MODIFY,IN_CREATE,IN_MOVED_FROM,IN_MOVED_TO $HOME/bin/git-autocommit

4. Usando o Flashbake

Outra opção é usar uma ferramenta como o Flashbake . Flashbake é o sistema de controle de versão que Cory Doctorow (da fama do BoingBoing) usa para escrever seus livros.

O Flashbake usa o git sob o capô para rastrear as alterações, mas está entre fazer backups automatizados e usar um sistema de controle de versão simples.

Cory queria que a versão levasse avisos, instantâneos de onde ele estava no momento em que ocorreu uma confirmação automática e o que ele estava pensando. Eu rapidamente desenhei um script Python para obter as informações contextuais que ele queria e comecei a hackear um script shell para direcionar o git, usando a saída do script Python para o comentário de confirmação quando um trabalho cron invocava o invólucro do shell.

Recursos

slm
fonte
3
inotifywait + "git local" = gitwatch.sh, veja aqui: github.com/nevik/gitwatch/blob/master/gitwatch.sh
diyism
4

Imediatamente o ZFS vem à mente. Ele pode criar instantâneos - e existem alguns projetos para criar automaticamente instantâneos .

bdecaf
fonte
Eu li sobre ZFS, mas parece que isso não é uma solução estável para sistemas de arquivos básicos (pelo menos em Linux)
woj
Eu realmente gostaria de uma solução para encaixar no FS existente.
Michael Pankov 06/02
Talvez isso? ext3cow.com
Zac B
3

Eu acho que você está no caminho certo inotify. Este artigo detalha seu uso básico em um caso semelhante ao seu. Eu sugeriria usá-lo diretamente ou compilar um utilitário no nível do kernel como o fschange . Isso é um aborrecimento, mas você pode vincular a detecção de alterações a um git commitou similar.

Essas soluções têm o problema de depender de soluções de terceiros um tanto imperfeitas. Se você não se importa de sujar as mãos, o NodeJS oferece um excelente recurso de plataforma cruzada ( fs.watch ) para esse objetivo exato. Um tutorial básico sobre a observação de arquivos para alterações no NodeJS pode ser encontrado aqui . Em algumas dezenas de linhas ou menos, você pode escrever algo que monitore um diretório para arquivos e, em seguida, extrair (via child_process ) e executar um git commitou semelhante (ou até incrementar manualmente um índice de arquivo de versão, se você gosta do roll-your- abordagem própria).

fs.watché suportado pelo inotifylinux, mas é muito mais intuitivo de usar. Existem outros projetos do NodeJS que agrupam essa funcionalidade de visualização de arquivos em vários níveis de conveniência, como este ou este .

Zac B
fonte
Ainda não é uma solução pronta e, bem, eu provavelmente usaria o Python inotify. Mas obrigada.
Michael Pankov 06/02
3

O inotify (2) no Linux não poderá assistir a uma árvore grande, mas o sistema de arquivos fusível (montado em local separado) provavelmente poderia lidar com isso, traduzindo solicitações do sistema de arquivos para chamadas svn ou git ou alterando diretamente os metadados svn / git.

Essa é uma ideia muito interessante, mas eu não tinha ouvido falar de nenhuma implementação existente.

Mikhail Kupchik
fonte
Digamos que eu tenho apenas alguns arquivos.
Michael Pankov 06/02
0

Esse script não é difícil de escrever.

Meu controle de versão favorito é o git.

O seguinte script deve fazer isso:

#!/bin/sh
git add .
git commit -am "my automatic commit"

faça com que verifique periodicamente seu diretório - ou se o seu editor é chamado por script depois de salvar.

Mas se você fizer isso dessa maneira, pode fazer sentido excluir arquivos grandes e talvez alguns "inúteis" como salvamentos automáticos.

bdecaf
fonte
Sim, eu sei que uma solução baseada em cron é simples de implementar. No entanto, estou procurando por algo que seria versão no save, não importa o mecanismo de salvamento. É também por isso que mencionei autoversionninf no svn, além de inotify na minha pergunta.
WoJ
0

O SparkleShare ( http://sparkleshare.org ) é baseado no git e implementa uma funcionalidade semelhante ao Dropbox com controle de versão, mas você precisa configurar um servidor ssh (pode ser localhost).

FSMaxB
fonte
Essa coisa é desajeitada e requer muita configuração. Além disso, a funcionalidade do Dropbox é desnecessária.
Michael Pankov 06/02
0

Eu recomendo que você tente NILFS. Consulte a página sobre e você poderá decidir rapidamente se é esse o que procura ou não.

HTH

Nehal Dattani
fonte
0

Também existe um modo de "pobre homem" fazer isso usando apenas rsync e um trabalho cron. Você basicamente depende do recurso de backup do rsync e usa dois caminhos separados, além de um prefixo / sufixo para acompanhar seus arquivos.

É mais ou menos assim: / usr / bin / rsync -a -A -X --backup --suffix = date +".%Y-%m-%d_%H-%M-%S"$ source_path $ backup_path

Resultado final: alterar um arquivo chamado test_rsync no caminho de origem após a execução inicial resultará na criação de um arquivo chamado test_rsync.2017-02-09_11-00-01 no caminho de backup.

Há muitos problemas com isso (funciona se você tiver apenas uma quantidade razoável de arquivos e falhará nas alterações que ocorrem entre duas execuções consecutivas do rsync (1 minuto no meu caso)), mas pode ser suficiente para suas necessidades.

Se estamos falando aqui sobre compartilhamentos de samba, uma lista de exclusão pode estar em ordem, mas ainda não cheguei a isso.

Deixe-me saber se você melhorar isso.

Florin COJOCARU
fonte
0

Aqui está um script Python3 que executa o VMS como controle automático de versão de arquivo usando um carimbo de data / hora anexado ao nome do arquivo original quando salvo.

Eu coloquei um monte de comentários no script e executei meia dúzia desses scripts na minha máquina ubuntu, com apenas os diretórios diferentes em cada versão diferente do script, para que eu esteja simultaneamente versionando vários diretórios. Nenhuma penalidade real para o desempenho das máquinas.

! / usr / bin / env python3

print ("VERSIONING DE ARQUIVOS DE PROJETO INICIADOS") print ("version_creation.py") # coloque todo esse código no script com esse nome print ("execute como .. 'python3 version_creation.py' na linha de comando") print ("ctrl ' c 'para parar ") print (" ") print (" Para executar o programa em segundo plano, digite abaixo a linha de comando e feche a janela. ") print (" nohup python3 version_creation.py ") print (" .... to interrompa o processo, vá ao menu / administração / monitor do sistema ... e mate python3 ") print (" ") print (" Sempre salve os arquivos no diretório 'ProjectFiles' e os arquivos de versão ") print (" também serão criados nesse diretório . ") print (" ") print (" ") print (" ") print (" ")

importação shutil importação os tempo de importação

--- defina o intervalo de tempo para procurar novos arquivos (em segundos) abaixo

- este intervalo deve ser menor que o intervalo em que novos arquivos aparecem!

t = 10

--- defina o diretório de origem (dr1) e o diretório de destino (dr2)

dr1 = "/ caminho / para / diretório_de origem"

dr2 = "/ caminho / para / diretório_de destino"

importação glob importação os

dr1 = "/ home / michael / ProjectFiles" # os dois originais e versões serão salvos neste diretório

dr2 = "/ home / michael / ProjectFileVersions"

enquanto True:

if os.listdir(dr1) == []:

print ("Vazio")

    n = 100
else:
    list_of_files = glob.glob(dr1+'/*')   # * means all if need specific format then *.csv
    latest_file_path = max(list_of_files, key=os.path.getctime)

print ("1 Último_arquivo_do_arquivo =", Último_arquivo_do_arquivo)

    originalname = latest_file_path.split('/')[-1]

print ("2 nome original =", nome original)

    filecreation = (os.path.getmtime(latest_file_path))

print ("filecreation =", filecreation)

    now = time.time()
    fivesec_ago = now - 5 # Number of seconds

print ("fivesec_ago =", fivesec_ago)

    timedif = fivesec_ago - filecreation #time between file creation

print ("timedif =", timedif)

    if timedif <= 5: #if file created less than 5 seconds ago

        nameroot = originalname.split(".")[-0]
        print ("3 nameroot= ", nameroot)

        extension = os.path.splitext(originalname)[1][1:]
        print ("4 extension = ", extension)

        curdatetime = time.strftime('%Y%m%d-%H%M%S')
        print ("5 curdatetime = ", curdatetime)

        newassembledname = (nameroot + "_" + curdatetime + "." + extension)
        print ("6 newassembledname = ", newassembledname)



        source = dr1+"/"+originalname
        print ("7 source = ", source)

        target = dr1+"/"+newassembledname
        print ("8 target = ", target)

        shutil.copy(source, target)


    time.sleep(t)

compartilhar

o abaixo foi colocado anteriormente e funciona, mas eu gosto muito mais do script python acima ...... (usando python por cerca de 3 horas)

#!/usr/bin/env python3

print ("PROJECT FILES VERSIONING STARTED")
print ("projectfileversioning.py")
print ("run as..  'python3 projectfileversioning.py'       from command line")
print ("ctrl 'c'      to stop")
print (" ")
print ("To run program in background type below to command line and then close the window. ")
print ("nohup python3 projectfileversioning.py")
print ("....to stop process go menu/administration/system monitor... and kill python")
print (" ")
print ("Always save files to the 'ProjectFiles' directory and the file ")
print ("   will be redirected to the ProjectFileVersions where")
print ("   time stamped versions will also be created.")
print (" ")
print ("If you like you may then copy/move the versioned and original file from 'ProjectFileVersions' to ")
print ("any other directory you like.")

import shutil
import os
import time

#--- set the time interval to check for new files (in seconds) below 
#-   this interval should be smaller than the interval new files appear!
t = 10

#--- set the source directory (dr1) and target directory (dr2)
#dr1 = "/path/to/source_directory"
#dr2 = "/path/to/target_directory"

import glob
import os

dr1 = "/home/michael/ProjectFiles"
dr2 = "/home/michael/ProjectFileVersions"


while True:

    if os.listdir(dr1) == []:
        n = 100
    else:
        list_of_files = glob.glob(dr1+'/*')   # * means all if need specific format then *.csv
        latest_file_path = max(list_of_files, key=os.path.getctime)
        print ("1 Latest_file_path = ", latest_file_path)

        originalname = latest_file_path.split('/')[-1]
        print ("2 originalname = ", originalname)

        nameroot = originalname.split(".")[-0]
        print ("3 nameroot= ", nameroot)

        extension = os.path.splitext(originalname)[1][1:]
        print ("4 extension = ", extension)

        curdatetime = time.strftime('%Y%m%d-%H%M%S')
        print ("5 curdatetime = ", curdatetime)

        newassembledname = (nameroot + "_" + curdatetime + "." + extension)
        print ("6 newassembledname = ", newassembledname)




        source = dr1+"/"+originalname
        print ("7 source = ", source)

        target = dr2+"/"+originalname
        print ("8 target = ", target)

        shutil.copy(source, target)



        source = dr1+"/"+originalname
        print ("9 source = ", source)

        target = dr2+"/"+newassembledname
        print ("10 target = ", target)

        shutil.move(source, target)
        time.sleep(t)


#share
Michael
fonte