将文件系统与“版本化”文件名合并?

将文件系统与“版本化”文件名合并?

您是否知道 GNU/Linux 是否有联合文件系统,可以显示名称略有不同的“影子”文件?例如,如果我有两个文件系统,例如:

root1
+dir1
+dir2
 +file1
 +file2
 +file3

root2
+dir1
+dir2
 +file1
 +file2
 +file4

最终的“联合” fs 应结果为:

unioned
+dir1
+dir2
 +file1
 +file1.1
 +file2
 +file2.1
 +file3

这样就可以快速检查“联合”fs 之间的差异

UnionFS 和 Aufs 似乎不提供此选项

谢谢

答案1

另一种方法是使用git-annex

首先,我们将设置测试文件:

#!/bin/bash 
# faster than /dev/urandom
randfile='openssl enc -aes-256-ctr -pass pass:"$(dd if=/dev/urandom bs=128 count=1 2>/dev/null | base64)" -nosalt < /dev/zero'
dd='dd bs=1M count=5 iflag=fullblock'

for I in 1 2
do
  mkdir root$I
  cd root$I
  for J in 1 2
  do
    mkdir dir$J
    if [ -e dir2 ]
    then
      cd dir2
      eval $randfile | eval $dd of=file1
      eval $randfile | eval $dd of=file2
      if [ `pwd | grep root1` ]; then
        eval $randfile | eval $dd of=file3
      elif [ `pwd | grep root2` ]; then
        eval $randfile | eval $dd of=file4
      fi
      cd ..
    fi
  done
  cd ..
done

这将创建目录,其中包含包含随机数据的二进制文件。此时文件为:

user@host$ find root? -path '*/.git*' -prune -o -print | sort -n 
root1
root1/dir1
root1/dir2
root1/dir2/file1
root1/dir2/file2
root1/dir2/file3
root2
root2/dir1
root2/dir2
root2/dir2/file1
root2/dir2/file2
root2/dir2/file4

现在,我们初始化存储库并执行同步:

cd root1
  git init
  git annex init 'root1'
  git remote add root2 ../root2
  #git annex direct
  git annex add .
  git commit -a -m 'Files added.'
cd ..
cd root2
  git init
  git annex init 'root1'
  git remote add root1 ../root1
  #git annex direct
  git annex add .
  git commit -a -m 'Files added.'
cd ..
mkdir unioned
cd unioned
  git init
  git annex init 'unioned'
  git remote add root1 ../root1
  git remote add root2 ../root2
  git annex add . 
  git commit -a -m 'Files added.'
  git annex sync
cd ..

此时,的内容unioned/为:

user@host$ find root? unioned -path '*/.git*' -prune -o -print | sort -n
root1
root1/dir1
root1/dir2
root1/dir2/file1
root1/dir2/file2
root1/dir2/file3
root2
root2/dir1
root2/dir2
root2/dir2/file1
root2/dir2/file2
root2/dir2/file4
unioned
unioned/dir2
unioned/dir2/file1
unioned/dir2/file1.variant-065a
unioned/dir2/file1.variant-a33e
unioned/dir2/file2
unioned/dir2/file2.variant-08f3
unioned/dir2/file2.variant-75c4
unioned/dir2/file3
unioned/dir2/file4

链接*.variant-*回不同存储库中的不同文件。此外,unioned在我们进行之前,仍然不包含任何数据git annex get。目前,git annex list显示文件所在的位置和/或来源:

user@host$ cd unioned; git annex list
here
|root1
||root2
|||web
||||
__X_ dir2/file1.variant-065a
_X__ dir2/file1.variant-a33e
__X_ dir2/file2.variant-08f3
_X__ dir2/file2.variant-75c4
_X__ dir2/file3
__X_ dir2/file4

另一种更长的格式是git annex whereis。最后,为了解决冲突,并从内部传播合并unioned/dir2

cd unioned/dir2
git annex get # retrieve the actual content
git annex unlock # unlock the files - replace the symlinks with the actual repofiles
rm file1
git mv file1.variant-065a file1
git rm -f file1.variant-a33e
rm file2
git mv file2.variant-75c4 file2
git rm -f file2.variant-08f3
git annex add . # "commits" the changes, converts files back into symlinks
git annex sync  # propagates the changes back to the other repos

得出的结果是:

git annex sync
commit  ok
pull root2 
ok
pull root1 
ok
push root2 
Counting objects: 61, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (26/26), done.
Writing objects: 100% (37/37), 2.67 KiB | 0 bytes/s, done.
Total 37 (delta 14), reused 0 (delta 0)
To ../root2
   e5df80f..720b34b  git-annex -> synced/git-annex
   b055385..ad8c5c2  master -> synced/master
ok
push root1 
Counting objects: 61, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (26/26), done.
Writing objects: 100% (37/37), 2.67 KiB | 0 bytes/s, done.
Total 37 (delta 14), reused 0 (delta 0)
To ../root1
   e5df80f..720b34b  git-annex -> synced/git-annex
   b055385..ad8c5c2  master -> synced/master
ok

最后,git annex list显示同步后这些文件的位置:unioned/目录包含所有文件的副本,这些文件的副本是从上述不同服务器中选择的。


git-annex还有直接模式它直接在文件系统上操作,无需使用符号链接。

在远程计算机上使用它的设置是使用标准 git 通过 ssh 设置远程的问题,但它的行为如下所述:http://git-annex.branchable.com/walkthrough/using_ssh_remotes/

总体演练git 附件位于此处:http://git-annex.branchable.com/walkthrough/

答案2

原始问题的答案似乎是“目前还没有”。

已经提出了一些解决方法,它们基于:

  • 在一个臭名昭著的工具上(rsync,激活备份选项)
  • 在提供“快照”功能的特殊文件系统(如 ZFS 或 btrfs)上

这两个提案都涉及大量 I/O,因为它们创建新的文件系统而不是将现有的文件系统映射到虚拟文件系统。

答案3

无需查看文件系统级别,因为您需要查看/维护版本之间的差异。如果它们是文本文件 - 那么git http://git-scm.com/将提供一个很好的解决方案。git是一个用于源代码的版本控制系统,既可以比较多个文件目录(又名存储库),也可以处理分支、差异和合并。

如果您无法使用git,那么 rsync 将提供解决方案,但您需要手动开发解决方案来检查要比较或合并的文件。使用 git 会自动跟踪这些差异。

如果它们大多是二进制文件 - 您可能需要使用 rsync 执行某些操作。例如,使用以下脚本:要设置测试:

set -x
for DIR in a b c ; do mkdir $DIR ; done
for DIR in `ls -d ?`; do
  echo "TESTMSG1-$DIR" >> $DIR/A;
  echo "TESTMSG2-$DIR" >> $DIR/B;
  echo "TESTMSG3-$DIR" >> $DIR/C;
  done
ls
ls -R
grep -r $ ?

-av并使用详细的存档副本执行测试,-c将文件与校验和进行比较,并-b --suffix=使用时间戳创建文件备份以供稍后比较:

rsync -avc -b --suffix=-$(date +%s.bk) a/ b
ls -R
grep -r $ ?
find . -name "*.bk"

我们显示文件被复制,添加了带有时间戳的.bk后缀,然后可以找到备份文件进行进一步分析:find . -name "*.bk"

$ sh test.sh
...output deleted for brevity...
+ ls
a  b  c  test.sh
+ ls -R
a  b  c  test.sh

./a:
A  B  C
./b:
A  B  C
./c:
A  B  C
+ grep -r $ a b c
a/A:TESTMSG1-a
a/B:TESTMSG2-a
a/C:TESTMSG3-a
b/A:TESTMSG1-b
b/B:TESTMSG2-b
b/C:TESTMSG3-b
c/A:TESTMSG1-c
c/B:TESTMSG2-c
c/C:TESTMSG3-c

+ date +%s.bk
+ rsync -avc -b --suffix=-1403746846.bk a/ b
sending incremental file list
A
B
C
sent 300 bytes  received 73 bytes  746.00 bytes/sec
total size is 33  speedup is 0.09
+ ls -R
a  b  c  test.sh

./a:
A  B  C
./b:
A  A-1403746846.bk  B  B-1403746846.bk  C  C-1403746846.bk
./c:
A  B  C
+ grep -r $ a b c
a/A:TESTMSG1-a
a/B:TESTMSG2-a
a/C:TESTMSG3-a
b/A:TESTMSG1-a
b/A-1403746846.bk:TESTMSG1-b
b/B:TESTMSG2-a
b/B-1403746846.bk:TESTMSG2-b
b/C:TESTMSG3-a
b/C-1403746846.bk:TESTMSG3-b
c/A:TESTMSG1-c
c/B:TESTMSG2-c
c/C:TESTMSG3-c

+find . -name "*.bk"
./b/B-1403746846.bk
./b/C-1403746846.bk
./b/A-1403746846.bk

另一种选择是使用 ZFS 快照将目录“镜像”到同一个“命名空间”。这些步骤的伪代码(因为我面前没有 zfs)将类似于:

for X in (a b c); do 
  zfs snapshot zfs-destination@baseline
  rsync -avc /src-$X/ zfs-destination
  zfs snapshot zfs-destination@$X
  diff -r zfs-destination/ zfs-destination/.zfs/snapshot/$X/
  # analyze diff files and validate changes to commit
  # restore files to not change from .zfs/snapshot/baseline
done

由于需要手动分析补丁,因此您不能将其作为脚本循环,但对每个源目录重复上述步骤将使您能够合并,并通过快照获得完整的历史记录。将“ab c”替换为类似于date -Is为快照添加时间戳的内容。

我忽略了zfs diff命令。上面的 diff 行应该是:

zfs diff -FH zfs-destination/ zfs-destination@baseline | \
  awk '/^[+MR]\tF/ {print $3}' > list

其中 awk 选择 (+) 表示添加的文件,(M) 表示修改的文件,(R) 表示重命名的文件。这比目录上的递归 diff 要快得多。

相关内容