Controllo delle versioni automatico al cambio del file (modifica / crea / elimina)


16

Sto cercando un'implementazione (su Linux) di un meccanismo che verifichi in modo automatico e trasparente qualsiasi modifica in una directory (ricorsivamente). Questo dovrebbe essere un'aggiunta (eventualmente sostituzione se tutte le funzionalità richieste sono disponibili) al controllo delle versioni standard (SVN, git, ...)

Un prodotto su MS Windows che esegue questa operazione è AutoVer (per avere una migliore idea dei requisiti). Mi piacerebbe avere qualcosa del genere, ma mirato a Linux in un ambiente non grafico.

Ho visto che ci sono alcuni tentativi di avere questa funzionalità su Linux, il più vicino che ho trovato è autoversionning su Subversion ma non è ovvio implementarlo su ambienti esistenti (server dove, ad esempio, i file di configurazione sono locali).

Forse qualcosa con cui stai lavorando inotify?

Grazie in anticipo per eventuali suggerimenti! wOJ


collegati: flashbake
Dan D.


Esiste un requisito speciale su quale software utilizzi? Perché se stai solo cercando di tenere traccia delle modifiche apportate manualmente (modificando i file), Eclipse ha questa funzione integrata, si chiama "cronologia locale".
Stefan Seidel,

@StefanSeidel Non sono l'argomento di partenza, ma preferirei una soluzione senza IDE.
Michael Pankov,

Risposte:


5

1. Metodo di uso generale che utilizza bazaar e inotify

Questo non è stato testato da me, ma ho trovato questo articolo che utilizza bzr(bazaar) e inotifywaitper monitorare una directory e la versione controlla i file in esso usando bazaar.

Questo script fa tutto il lavoro di guardare la directory per le modifiche:

#!/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. Gestione / ecc

Per il caso speciale di gestione della /etcdirectory del tuo sistema , puoi usare l'app etckeeper .

etckeeper è una raccolta di strumenti per lasciare / etc in un repository git, mercurial, darcs o bzr. Si collega a apt (e ad altri gestori di pacchetti tra cui yum e pacman-g2) per eseguire automaticamente il commit delle modifiche apportate a / etc durante gli aggiornamenti del pacchetto. Tiene traccia dei metadati dei file che normalmente i sistemi di controllo revison non supportano, ma questo è importante per / etc, come i permessi di / etc / shadow. È abbastanza modulare e configurabile, pur essendo semplice da usare se si comprendono le basi per lavorare con il controllo di revisione.

Ecco un buon tutorial per iniziare.

3. Usare git e incron

Questa tecnica utilizza gite incron. Per questo metodo è necessario effettuare le seguenti operazioni:

A. Crea un repository

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

B. Crea uno $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. Aggiungi una voce a incrontab

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

4. Utilizzo di Flashbake

Un'altra opzione è quella di utilizzare uno strumento come Flashbake . Flashbake è il sistema di controllo della versione che Cory Doctorow (di fama BoingBoing) utilizza per scrivere i suoi libri.

Flashbake utilizza git sotto il cofano per tenere traccia delle modifiche, ma si trova a metà strada tra l'esecuzione di backup automatici e l'utilizzo di un semplice sistema di controllo della versione.

Cory voleva che la versione contenesse istruzioni, istantanee di dove si trovava al momento in cui si era verificato un commit automatico e di cosa stesse pensando. Ho rapidamente abbozzato uno script Python per estrarre le informazioni contestuali che desiderava e ho iniziato a hackerare uno script shell per guidare git, usando l'output dello script Python per il commento di commit quando un lavoro cron ha invocato il wrapper della shell.

risorse


3
inotifywait + "git local" = gitwatch.sh, guarda qui: github.com/nevik/gitwatch/blob/master/gitwatch.sh
diyism


3

Penso che tu sia sulla buona strada con inotify. Questo articolo descrive in dettaglio il suo utilizzo di base in un caso simile al tuo. Suggerirei di usarlo direttamente o compilando un'utilità a livello di kernel come fschange . Questa è una seccatura, ma potresti quindi associare il rilevamento di modifiche a una git commito simile.

Entrambe queste soluzioni hanno il problema di affidarsi a soluzioni di terze parti in qualche modo imperfette. Se non ti dispiace sporcarti le mani, NodeJS offre un'eccellente struttura multipiattaforma ( fs.watch ) per questo preciso scopo. Un tutorial di base sulla visione dei file per le modifiche in NodeJS è disponibile qui . In poche decine di righe o meno, potresti scrivere qualcosa che controlla una directory per i file e quindi esegue lo shell out (tramite child_process ) ed esegue uno git commito simile (o anche incrementa manualmente un indice del file di versione, se ti piace il roll-your- proprio approccio).

fs.watchè supportato da inotifyLinux, ma è molto più intuitivo da usare. Esistono altri progetti NodeJS che racchiudono questa funzionalità di visualizzazione dei file in vari livelli di praticità, come questo o questo .


Non è ancora una soluzione pronta e, beh, probabilmente andrei con Python inotify. Ma grazie.
Michael Pankov,

3

inotify (2) su Linux non sarà in grado di guardare un grande albero, ma probabilmente il filesystem di fusione (montato in una posizione separata) potrebbe gestirlo, traducendo le richieste del filesystem in chiamate svn o git o modificando direttamente i metadati svn / git.

Questa è un'idea molto interessante, ma non avevo sentito parlare di implementazioni esistenti.


Diciamo che ho solo un paio di file.
Michael Pankov,

0

Una tale sceneggiatura non è difficile da scrivere.

Il mio controllo della versione preferita è git.

il seguente script dovrebbe farlo:

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

o controlla periodicamente la tua directory - o se il tuo editor è programmabile, chiama dopo aver salvato.

Ma se lo fai in questo modo potrebbe avere senso escludere file di grandi dimensioni e forse alcuni "inutili" come i salvataggi automatici.


Sì, so che una soluzione basata su cron è semplice da implementare. Sto comunque cercando qualcosa che verrebbe versione su save, indipendentemente dal meccanismo di salvataggio. Questo è anche il motivo per cui ho menzionato autoversionninf su svn e inotify nella mia domanda.
WoJ,

0

SparkleShare ( http://sparkleshare.org ) si basa su git e implementa una funzionalità simile a Dropbox con controllo della versione, ma è necessario impostare un server ssh (può essere localhost).


Questa cosa è goffa e richiede molta configurazione. Inoltre, la funzionalità Dropbox non è necessaria.
Michael Pankov,


0

C'è anche un modo "povero" di farlo usando solo rsync e un cron job. Fondamentalmente fai affidamento sulla funzione di backup di rsync e usi due percorsi separati più un prefisso / suffisso per tenere traccia dei tuoi file.

Sembra più o meno così: / usr / bin / rsync -a -A -X --backup --suffix = date +".%Y-%m-%d_%H-%M-%S"$ source_path $ backup_path

Risultato finale: la modifica di un file chiamato test_rsync nel percorso di origine dopo l'esecuzione iniziale comporta la creazione di un file chiamato test_rsync.2017-02-09_11-00-01 nel percorso di backup.

Ci sono un sacco di problemi con questo (funziona se hai una discreta quantità di file e fallirà per le modifiche che si verificano tra due esecuzioni consecutive di rsync (1 minuto nel mio caso)) ma potrebbe essere sufficiente per le tue esigenze.

Se stiamo parlando qui di condivisioni di samba, un elenco di esclusione potrebbe essere in ordine, non ho ancora capito che temo.

Fatemi sapere se lo migliorate.


0

Ecco uno script Python3 che fa VMS come il versioning automatico dei file usando un timestamp aggiunto al nome del file originale quando salvato.

Ho inserito un sacco di commenti nello script ed eseguito una mezza dozzina di tali script sulla mia macchina Ubuntu con solo le directory che sono diverse in ogni diversa versione dello script in modo che io simultaneamente verifichi più directory. Nessuna vera penalità per le prestazioni delle macchine.

! / usr / bin / env python3

print ("VERSIONE DEI FILE DI PROGETTO INIZIATA") print ("version_creation.py") # inserire tutto questo codice nello script con questo nome print ("esegui come .. 'python3 version_creation.py' dalla riga di comando") print ("ctrl ' c 'per interrompere ") print (" ") print (" Per eseguire il programma in background in basso alla riga di comando e quindi chiudere la finestra. ") print (" nohup python3 version_creation.py ") print (" .... to ferma processo vai menu / amministrazione / monitor di sistema ... e uccidi python3 ") print (" ") print (" Salva sempre i file nella directory 'ProjectFiles' e i file della versione ") print (" verranno creati anche in quella directory . ") print (" ") print (" ") print (" ") print (" ")

import shutil import os tempo di importazione

--- imposta l'intervallo di tempo per verificare la presenza di nuovi file (in secondi) di seguito

- questo intervallo dovrebbe essere inferiore a quello visualizzato dai nuovi file!

t = 10

--- imposta la directory di origine (dr1) e la directory di destinazione (dr2)

dr1 = "/ path / to / source_directory"

dr2 = "/ path / to / target_directory"

import glob import os

dr1 = "/ home / michael / ProjectFiles" # entrambi gli originali e le versioni verranno salvati in questa directory

dr2 = "/ home / michael / ProjectFileVersions"

mentre vero:

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

stampa ("Vuoto")

    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]

stampa ("2 nome originale =", nome originale)

    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)

Condividere

il seguito è stato inserito in precedenza e funziona, ma mi piace molto meglio lo script python sopra ...... (sto usando python per circa 3 ore)

#!/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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.