Automatyczne przechowywanie wersji po zmianie pliku (modyfikacja / tworzenie / usuwanie)

16

Szukam implementacji (w systemie Linux) mechanizmu, który automatycznie i przejrzyście aktualizowałby wszelkie zmiany w katalogu (rekurencyjnie). Ma to stanowić dodatek (ewentualnie zamiennik, jeśli wszystkie wymagane funkcje są dostępne) do standardowej wersji (SVN, git, ...)

Produktem w systemie MS Windows, który to robi, jest AutoVer (aby lepiej zrozumieć wymagania). Chciałbym mieć coś takiego, ale ukierunkowanego na Linuksa w środowisku nie graficznym.

Widziałem, że istnieją pewne próby posiadania tej funkcjonalności w Linuksie, najbliższą, którą znalazłem, jest autowersja w Subversion, ale nie jest oczywiste, aby zaimplementować ją w istniejących środowiskach (serwery, na przykład pliki konfiguracyjne są lokalne).

Może coś z czym współpracujesz inotify?

Z góry dziękuję za wszelkie wskazówki! WoJ

WoJ
źródło
powiązane: flashbake
Dan D.
Czy istnieje specjalne wymaganie dotyczące używanego oprogramowania? Ponieważ jeśli chcesz tylko śledzić zmiany, które robisz ręcznie (edytując pliki), Eclipse ma tę wbudowaną funkcję, która nazywa się „historią lokalną”.
Stefan Seidel
@StefanSeidel Nie jestem inicjatorem tematu, ale wolałbym rozwiązanie bez IDE.
Michael Pankov

Odpowiedzi:

6

1. Metoda ogólnego zastosowania przy użyciu bazaru i inotify

To nie jest testowane przeze mnie, ale znalazłem ten napis, który korzysta z bzr(bazar) i inotifywaitdo monitorowania katalogu i kontroli wersji plików w nim za pomocą bazaru.

Ten skrypt wykonuje całą pracę polegającą na obserwowaniu katalogu pod kątem zmian:

#!/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. Zarządzanie / etc

W specjalnym przypadku zarządzania /etckatalogiem systemu możesz użyć etckeeper aplikacji .

etckeeper to zbiór narzędzi do przechowywania / etc w repozytorium git, mercurial, darcs lub bzr. Łączy się z apt (i innymi menedżerami pakietów, w tym yum i pacman-g2), aby automatycznie zatwierdzać zmiany wprowadzone w / etc podczas aktualizacji pakietów. Śledzi metadane plików, które zwykle nie są obsługiwane przez systemy kontroli rewizji, ale jest to ważne dla / etc, takich jak uprawnienia / etc / shadow. Jest dość modułowy i konfigurowalny, a jednocześnie łatwy w użyciu, jeśli rozumiesz podstawy pracy z kontrolą wersji.

Oto dobry samouczek na początek.

3. Używanie git i incron

Ta technika wykorzystuje giti incron. W przypadku tej metody musisz wykonać następujące czynności:

A.Zrób repo

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

B. Utwórz $HOME/bin/git-autocommitskrypt

#!/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. Dodaj wpis do incrontab

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

4. Korzystanie z Flashbake

Inną opcją jest użycie narzędzia takiego jak Flashbake . Flashbake to system kontroli wersji, którego Cory Doctorow (znany z BoingBoinga) używa do pisania swoich książek.

Flashbake używa git pod maską do śledzenia zmian, ale jest gdzieś pomiędzy robieniem automatycznych kopii zapasowych a samodzielnym korzystaniem z prostego systemu kontroli wersji.

Cory chciał, aby wersja zawierała podpowiedzi, migawki z tego, gdzie był w momencie, gdy nastąpiło automatyczne zatwierdzenie i co myślał. Szybko naszkicowałem skrypt Pythona, aby pobrać potrzebne mu informacje kontekstowe, i zacząłem hakować skrypt powłoki, aby uzyskać git, używając danych wyjściowych skryptu Python jako komentarza do zatwierdzenia, gdy zadanie cron wywołało opakowanie powłoki.

Zasoby

slm
źródło
3
inotifywait + "git local" = gitwatch.sh, patrz tutaj: github.com/nevik/gitwatch/blob/master/gitwatch.sh
diyism
4

Natychmiast przychodzi na myśl ZFS . Może tworzyć migawki - i istnieją pewne projekty do automatycznego tworzenia migawek .

bdecaf
źródło
Czytam o ZFS, ale wygląda na to, że nie jest to stabilne rozwiązanie dla podstawowych systemów plików (przynajmniej w Linuksie)
WoJ
Naprawdę chciałbym, aby rozwiązanie przystawało do istniejącego FS.
Michael Pankov
Być może to? ext3cow.com
Zac B
3

Myślę, że jesteś na dobrej drodze inotify. W tym artykule szczegółowo opisano jego podstawowe użycie w przypadku podobnym do twojego. Sugerowałbym użycie go bezpośrednio lub skompilowanie narzędzia na poziomie jądra, takiego jak fschange . Jest to trochę kłopotliwe, ale możesz powiązać wykrycie zmian z git commitpodobnym lub podobnym.

Oba rozwiązania mają problem polegania na nieco niedoskonałych rozwiązaniach innych firm. Jeśli nie masz nic przeciwko zabrudzeniu rąk, NodeJS zapewnia doskonałe, międzyplatformowe narzędzie ( fs.watch ) do tego właśnie celu. Podstawowy samouczek na temat oglądania plików pod kątem zmian w NodeJS można znaleźć tutaj . W kilkudziesięciu wierszach lub mniej możesz napisać coś, co obejrzy katalog plików, a następnie wydostanie się (poprzez child_process ) i uruchomi git commitpodobny lub (lub nawet ręcznie zwiększy indeks pliku wersji, jeśli podoba ci się roll-your- własne podejście).

fs.watchjest wspierany przez inotifyLinux, ale jest o wiele bardziej intuicyjny w użyciu. Istnieją inne projekty NodeJS, które obejmują tę funkcję oglądania plików na różnych poziomach wygody, takich jak ten lub ten .

Zac B
źródło
Nadal nie jest to gotowe rozwiązanie i, cóż, prawdopodobnie wybrałbym rozwiązanie Pythona inotify. Ale dzięki.
Michael Pankov
3

inotify (2) w systemie Linux nie będzie w stanie oglądać dużego drzewa, ale prawdopodobnie system plików bezpieczników (zamontowany w oddzielnej lokalizacji) prawdopodobnie mógłby to obsłużyć, tłumacząc żądania systemu plików na wywołania svn lub git, lub bezpośrednio zmieniając metadane svn / git.

To bardzo interesujący pomysł, ale nie słyszałem o żadnych istniejących implementacjach.

Michaił Kupczik
źródło
Powiedzmy, że mam tylko kilka plików.
Michael Pankov
0

Taki skrypt nie jest trudny do napisania.

Moja ulubiona kontrola wersji to git.

powinien to zrobić następujący skrypt:

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

albo okresowo sprawdzaj katalog - albo czy twój edytor jest skryptowym wywołaniem po zapisaniu.

Ale jeśli zrobisz to w ten sposób, sensowne może być wykluczenie dużych plików i być może niektórych „bezużytecznych”, takich jak automatyczne zapisywanie.

bdecaf
źródło
Tak, wiem, że rozwiązanie oparte na cronie jest łatwe do wdrożenia. Jednak szukam czegoś, co można zapisać podczas zapisywania, bez względu na mechanizm zapisywania. Dlatego też wspomniałem autoversionninf na svn, a także inotify w moim pytaniu.
WoJ,
0

SparkleShare ( http://sparkleshare.org ) jest oparty na git i implementuje funkcjonalność Dropbox-Like z kontrolą wersji, ale musisz skonfigurować serwer ssh (może być localhost).

FSMaxB
źródło
To jest niezręczne i wymaga dużo konfiguracji. Poza tym funkcjonalność Dropbox jest niepotrzebna.
Michael Pankov
0

Polecam wypróbować NILFS. Zajrzyj na stronę about, a szybko zdecydujesz, czy to jest to, czego szukasz, czy nie.

HTH

Nehal Dattani
źródło
0

Istnieje również sposób „biedaka” na robienie tego przy użyciu tylko rsync i zadania cron. Zasadniczo polegasz na narzędziu do tworzenia kopii zapasowych rsync i używasz dwóch oddzielnych ścieżek oraz prefiksu / sufiksu do śledzenia swoich plików.

Mniej więcej wygląda to tak: / usr / bin / rsync -a -A -X --backup --suffix = date +".%Y-%m-%d_%H-%M-%S"$ ścieżka_źródłowa $ ścieżka_kopii zapasowej

Wynik końcowy: zmiana pliku o nazwie test_rsync w ścieżce źródłowej po początkowym wykonaniu spowoduje utworzenie pliku o nazwie test_rsync.2017-02-09_11-00-01 w ścieżce kopii zapasowej.

Jest z tym wiele problemów (działa, jeśli masz tylko przyzwoitą liczbę plików i nie powiedzie się w przypadku zmian, które nastąpią między dwoma kolejnymi uruchomieniami rsync (w moim przypadku 1 minuta)), ale może to wystarczyć dla twoich potrzeb.

Jeśli mówimy tutaj o udostępnianiu samby, lista wykluczeń może być w porządku, nie mam jeszcze tego do czynienia, obawiam się.

Daj mi znać, jeśli to poprawisz.

Florin COJOCARU
źródło
0

Oto skrypt Python3, który wykonuje VMS, takie jak automatyczne wersjonowanie plików przy użyciu znacznika czasu dołączanego do oryginalnej nazwy pliku podczas zapisywania.

Umieściłem w skrypcie sporo komentarzy i uruchomiłem pół tuzina takich skryptów na mojej maszynie ubuntu, przy czym tylko katalogi są różne w każdej innej wersji skryptu, dzięki czemu jednocześnie wersjonuję wiele katalogów. Brak realnej kary dla wydajności maszyn.

! / usr / bin / env python3

print („ROZPOCZĘCIE WERSJI PLIKÓW PROJEKTOWYCH”) print („version_creation.py”) # umieść cały ten kod w skrypcie o tej nazwie print („uruchom jako .. 'python3 version_creation.py' z wiersza poleceń”) print („ctrl” c ', aby zatrzymać ”) print („ ”) print („ Aby uruchomić program w tle poniżej, do wiersza poleceń, a następnie zamknąć okno. ”) print („ nohup python3 version_creation.py ”) print („ .... to zatrzymaj proces przejdź do menu / administracja / monitor systemu ... i zabij python3 ”) drukuj („ ”) drukuj („ Zawsze zapisuj pliki w katalogu „ProjectFiles” i pliki wersji ”) drukuj (” również zostaną utworzone w tym katalogu . ”) print („ ”) print („ ”) print („ ”) print („ ”)

import shutil import os czas importu

--- ustaw przedział czasu, aby sprawdzić nowe pliki (w sekundach) poniżej

- interwał ten powinien być mniejszy niż interwał pojawiania się nowych plików!

t = 10

--- ustaw katalog źródłowy (dr1) i katalog docelowy (dr2)

dr1 = "/ path / to / source_directory"

dr2 = "/ path / to / target_directory"

import glob import os

dr1 = "/ home / michael / ProjectFiles" # oba oryginały i wersje zostaną zapisane w tym katalogu

dr2 = "/ home / michael / ProjectFileVersions"

podczas gdy prawda:

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

drukuj („Pusty”)

    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 Ścieżka_pliku_ najnowszych =”, ścieżka_pliku_ najnowszych)

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

print („2 originalname =”, originalname)

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

print („filecreation =”, tworzenie plików)

    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)

dzielić

poniższe zostało wstawione wcześniej i działa, ale podoba mi się powyższy skrypt Pythona znacznie lepiej ...... (używam Pythona przez około 3 godziny)

#!/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
źródło