更改文件后自动进行版本控制(修改/创建/删除)


16

我正在寻找一种机制的实现(在Linux上),该机制将自动透明地对目录中的所有更改进行版本控制(递归)。这是对标准版本控制(SVN,git,...)的补充(如果有所有要求的功能,则可以替换)

在MS Windows上执行此操作的产品是AutoVer(以更好地了解要求)。我很想拥有类似的东西,但是针对非图形环境中的Linux。

我看到有一些尝试在Linux上具有此功能,我发现最接近的是在Subversion上进行自动版本化,但是在现有环境(例如,配置文件位于本地的服务器)上实现并不明显。

也许有什么用inotify吗?

预先感谢您提供任何指导!月刊


相关:flashbake
Dan D.


您使用哪种软件是否有特殊要求?因为如果您只想跟踪手动进行的更改(通过编辑文件),则Eclipse内置了此功能,称为“本地历史记录”。
Stefan Seidel

@StefanSeidel我不是主题入门者,但是我更喜欢no-IDE解决方案。
Michael Pankov

Answers:


6

1.使用市集和inotify的通用方法

这未经我测试,但我发现此文章使用bzr(bazaar)&inotifywait监视目录并使用bazaar对目录进行版本控制。

该脚本完成了监视目录更改的所有工作:

#!/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.管理/ etc

对于管理系统/etc目录的特殊情况,可以使用app etckeeper

etckeeper是一组工具,可以将/ etc存储在git,mercurial,darcs或bzr存储库中。它与apt(以及其他软件包管理器,包括yum和pacman-g2)挂钩,以在软件包升级期间自动提交对/ etc的更改。它跟踪版本控制系统通常不支持的文件元数据,但这对/ etc很重要,例如/ etc / shadow的权限。它是相当模块化和可配置的,同时如果您了解使用修订控制的基础知识,则也很容易使用。

这是一个很好的教程,可以帮助您入门。

3.使用git和incron

此技术使用gitincron。对于此方法,您需要执行以下操作:

A.进行回购

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

B.创建一个$HOME/bin/git-autocommit脚本

#!/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.将条目添加到incrontab

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

4.使用Flashbake

另一种选择是使用Flashbake之类的工具。Flashbake是BoingBoing名望的Cory Doctorow用来写书的版本控制系统。

Flashbake在后台使用git来跟踪更改,但是介于执行自动备份和自己使用普通版本控制系统之间。

Cory希望该版本带有提示,自动提交发生时他所在的位置以及他在想什么的快照。我迅速草绘了一个Python脚本,以提取他想要的上下文信息,并开始使用cron作业调用shell包装器时使用Python脚本的输出作为提交注释,从而共同编写了一个shell脚本来驱动git。

资源资源


3
inotifywait +“ git local” = gitwatch.sh,请看这里:github.com/nevik/gitwatch/blob/master/gitwatch.sh
diyism


3

我认为您在上走了正确的路inotify本文详细介绍了在与您类似的情况下的基本用法。我建议直接使用它,或编译诸如fschange之类的内核级实用程序。这有点麻烦,但是您可以将更改的检测绑定到a git commit或类似的东西。

这些解决方案都存在依赖于一些不完善的第三方解决方案的问题。如果您不介意动手,NodeJS会提供一个出色的跨平台工具(fs.watch)来实现此目的。可在此处找到有关监视文件中NodeJS更改的基本教程。在几十行或更短的时间内,您可以编写一些内容来监视目录中的文件,然后将其外壳(通过child_process)并运行一个git commit或类似文件(或者如果您喜欢roll-your-自己的方法)。

fs.watchinotify在linux上得到了支持,但是使用起来更加直观。还有其他NodeJS项目以各种便利级别包装了该文件监视功能,例如thisthis


仍然没有一个好的解决方案,而且,我可能会选择Python的inotify。但是,谢谢。
Michael Pankov

3

Linux上的inotify(2)无法观察大树,但是通过将文件系统请求转换为svn或git调用或直接更改svn / git元数据,fuse文件系统(安装在单独的位置)可能可以处理它。

这是一个非常有趣的想法,但是我没有听说过任何现有的实现。


假设我只有几个文件。
Michael Pankov

0

这样的脚本并不难编写。

我最喜欢的版本控制是git。

以下脚本应该这样做:

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

或者定期检查您的目录-保存后是否可以通过脚本调用您的编辑器。

但是,如果您这样做,则排除大型文件以及诸如自动保存之类的“无用”文件可能是有意义的。


是的,我知道基于cron的解决方案易于实现。但是,无论保存机制如何,我都在寻找可以保存的版本。这也是为什么我在svn上提到autoversionninf以及在我的问题中进行inotify的原因。
WoJ 2011年



0

仅使用rsync和cron作业也有“穷人”的方法。您基本上是依靠rsync的备份工具,并使用两个单独的路径以及一个前缀/后缀来跟踪文件。

它看起来更像是这样:/ usr / bin / rsync -a -A -X --backup --suffix = date +".%Y-%m-%d_%H-%M-%S"$ source_path $ backup_path

最终结果:在初始执行后在源路径中更改名为test_rsync的文件将导致在备份路径中创建名为test_rsync.2017-02-09_11-00-01的文件。

这样做有很多问题(如果您仅拥有相当数量的文件,并且在连续两次运行rsync(在我的情况下为1分钟)之间进行更改将失败,则可以使用该功能),但这可能足以满足您的需求。

如果我们在这里谈论samba共享,则可能是按顺序列出了一个排除列表,但还没有解决这个问题,我担心。

让我知道您是否对此有所改善。


0

这是一个Python3脚本,该脚本使用保存时附加到原始文件名的时间戳来执行VMS(例如自动文件版本控制)的VMS。

我在脚本中添加了一堆注释,并在ubuntu计算机上运行了六个这样的脚本,但每个脚本的不同版本中的目录都不同,因此我同时对多个目录进行了版本控制。不会对机器性能造成真正的损失。

!/ usr / bin / env python3

打印(“项目文件版本已开始”)打印(“ version_creation.py”)#将所有这些代码放入具有此名称的脚本中打印(“从命令行运行为..'python3 version_creation.py'”)打印(“ ctrl” c'停止”)print(“”)print(“要在后台在命令行中运行程序,然后关闭窗口。”)print(“ nohup python3 version_creation.py”)print(“ .... to停止进程进入菜单/管理/系统监视器...并杀死python3“)print(”“)print(”始终将文件保存到'ProjectFiles'目录,版本文件“)print(”也会在该目录中创建。“)打印(”“)打印(”“)打印(”“)打印(”“)

导入shutil导入os导入时间

---设置以下时间间隔来检查以下新文件(以秒为单位)

-此间隔应小于新文件出现的间隔!

t = 10

---设置源目录(dr1)和目标目录(dr2)

dr1 =“ / path / to / source_directory”

dr2 =“ / path / to / target_directory”

导入glob导入os

dr1 =“ / home / michael / ProjectFiles”#原始和版本都将保存到此目录

dr2 =“ / home / michael / ProjectFileVersions”

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

打印(“ 1 Latest_file_path =”,latest_file_path)

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

打印(“ 2 originalname =”,原始名称)

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

打印(“ filecreation =”,filecreation)

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

打印(“ fivesec_ago =”,Fivesec_ago)

    timedif = fivesec_ago - filecreation #time between file creation

打印(“ 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)

分享

下面是放在较早的工作,但我更喜欢上面的python脚本......(使用python约3个小时)

#!/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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.