第 10 章 数据管理

目录

10.1. 共享,拷贝和存档
10.1.1. 存档和压缩工具
10.1.2. 复制和同步工具
10.1.3. 归档语法
10.1.4. 复制语法
10.1.5. 查找文件的语法
10.1.6. 归档媒体
10.1.7. 可移动存储设备
10.1.8. 选择用于分享数据的文件系统
10.1.9. 网络上的数据分享
10.2. 备份和恢复
10.2.1. 实用备份套件
10.2.2. 一个系统备份的脚本例子
10.2.3. 用于备份数据的复制脚本
10.3. 数据安全基础
10.3.1. GnuPG 密钥管理
10.3.2. 在文件上使用 GnuPG
10.3.3. 在 Mutt 中使用 GnuPG
10.3.4. 在 vim 中使用 GnuPG
10.3.5. MD5 校验和
10.4. 源代码合并工具
10.4.1. 从源代码文件导出差异
10.4.2. 源代码文件移植更新
10.4.3. 通过三方移植进行更新
10.5. 版本控制系统
10.5.1. VCS 命令的比较
10.6. Git
10.6.1. 配置 Git 客户端
10.6.2. Git 参考
10.6.3. Git 命令
10.6.4. 用于 Subversion 仓库的 Git
10.6.5. 记录配置历史的 Git
10.7. CVS
10.7.1. CVS 存储库的配置
10.7.2. 本地访问 CVS
10.7.3. 使用 pserver 远程访问 CVS
10.7.4. 使用 ssh 远程访问 CVS
10.7.5. 往 CVS 导入新的源
10.7.6. CVS 存储库中的文件权限
10.7.7. CVS 工作流
10.7.8. CVS 中最新的文件
10.7.9. CVS 的管理
10.7.10. 用于 CVS 签出时的可执行位
10.8. Subversion
10.8.1. Subversion 存储库的配置
10.8.2. 通过 Apache2 服务器访问 Subversion
10.8.3. 按组本地访问 Subversion
10.8.4. 通过 SSH 远程访问 Subversion
10.8.5. Subversion 目录结构
10.8.6. 往 Subversion 里导入一个新的源
10.8.7. Subversion 工作流

以下是关于在 Debian 系统上管理二进制和文本数据的工具及其相关提示。

[警告] 警告

为避免 竞争情况,不应当对正在进行写操作的设备和文件,多个进程进行不协调的写操作。采用flock(1) 文件锁定 机制可用于避免这种情况。

数据的安全和它的受控共享有如下几个方面。

  • 存档文件的建立

  • 远程存储访问

  • 复制

  • 跟踪修改历史

  • 促进数据共享

  • 防止未经授权的文件访问

  • 检测未经授权的文件修改

这些可以通过使用工具集来实现。

  • 存档和压缩工具

  • 复制和同步工具

  • 网络文件系统

  • 移动存储媒介

  • 安全 shell

  • 认证体系

  • 版本控制系统工具

  • 哈希算法和加密工具

以下是 Debian 系统上可用的存档和压缩工具的预览。

表 10.1. 存档和压缩工具列表

软件包 流行度 大小 扩展名 命令 描述
tar V:907, I:999 2770 .tar tar(1) 标准的归档工具(默认)
cpio V:320, I:998 720 .cpio cpio(1) Unix System V 风格的归档器,与 find(1) 一起使用
binutils V:169, I:702 223 .ar ar(1) 创建静态库的归档工具
fastjar V:5, I:53 172 .jar fastjar(1) Java 归档工具(类似 zip)
pax V:16, I:48 170 .pax pax(1) 新的 POSIX 归档工具,介于 tarcpio 之间
gzip V:877, I:999 231 .gz gzip(1), zcat(1), … GNU LZ77 压缩工具(默认)
bzip2 V:212, I:939 184 .bz2 bzip2(1), bzcat(1), … Burrows-Wheeler block-sorting 压缩工具有着比 gzip(1) 更高的压缩率 (跟 gzip 有着相似的语法但速度比它慢)
lzma V:4, I:54 126 .lzma lzma(1) LZMA 压缩工具有着比 gzip(1) 更高的压缩率(不推荐)
xz-utils V:401, I:955 515 .xz xz(1), xzdec(1), … XZ 压缩工具有着比 bzip2(1) 更高的压缩率(压缩速度慢于 gzip 但是比 bzip2 快; LZMA 压缩工具的替代品)
p7zip V:66, I:312 934 .7z 7zr(1), p7zip(1) 有着更高压缩率的 7-zip 文件归档器(LZMA 压缩)
p7zip-full V:155, I:528 4407 .7z 7z(1), 7za(1) 有着更高压缩率的 7-Zip 文件归档器(LZMA 压缩和其他)
lzop V:5, I:45 97 .lzo lzop(1) LZO 压缩工具有着比 gzip(1) 更高的压缩和解压缩速度 (跟 gzip 有着相似的语法但压缩率比它低)
zip V:50, I:415 608 .zip zip(1) InfoZip:DOS 归档器和压缩工具
unzip V:272, I:796 534 .zip unzip(1) InfoZIP:DOS 解档器和解压缩工具

[警告] 警告

除非你知道将会发生什么,否则不要设置 "$TAPE" 变量。它会改变 tar(1) 的行为。

[注意] 注意

gzipped tar(1) 归档器用于扩展名是 ".tgz" 或者 ".tar.gz" 的文件。

[注意] 注意

xz-compressed tar(1) 归档器用于扩展名是 ".txz" 或者 ".tar.xz" 的文件。

[注意] 注意

FOSS 工具,例如 tar(1),中的主流压缩方法已经按如下所示的迁移: gzipbzip2xz

[注意] 注意

cp(1),scp(1)tar(1) 工具可能并不适用于一些特殊的文件。cpio(1) 工具的适用范围是最广的。

[注意] 注意

cpio(1) 是被设计为与 find(1) 和其它命令一起使用,适合于创建备份脚本的场景,因此,脚本的文件选择部分能够被独立测试。

[注意] 注意

Internal structure of Libreoffice data files are ".jar" file which can be opened also by unzip.

[注意] 注意

The de-facto cross platform archive tool is zip. Use it as "zip -rX" to attain the maximum compatibility. Use also the "-s" option, if the maximum file size matters.

如下是用不同的工具复制整个 "./source" 目录中的内容。

  • 本地复制: "./source" 目录 → "/dest" 目录

  • 远程复制:本地主机上的 "./source" 目录 → "user@host.dom" 主机上的 "/dest" 目录

rsync(8):

# cd ./source; rsync -aHAXSv . /dest
# cd ./source; rsync -aHAXSv . user@host.dom:/dest

你能够选择使用“源目录上的反斜杠”语法。

# rsync -aHAXSv ./source/ /dest
# rsync -aHAXSv ./source/ user@host.dom:/dest

或者,如下所示。

# cd ./source; find . -print0 | rsync -aHAXSv0 --files-from=- . /dest
# cd ./source; find . -print0 | rsync -aHAXSv0 --files-from=- . user@host.dom:/dest

GNU cp(1) 和 openSSH scp(1):

# cd ./source; cp -a . /dest
# cd ./source; scp -pr . user@host.dom:/dest

GNU tar(1):

# (cd ./source && tar cf - . ) | (cd /dest && tar xvfp - )
# (cd ./source && tar cf - . ) | ssh user@host.dom '(cd /dest && tar xvfp - )'

cpio(1):

# cd ./source; find . -print0 | cpio -pvdm --null --sparse /dest

你能够在所有包含 "." 的例子里用 "foo" 替代 ".",这样就可以从 "./source/foo" 目录复制文件到 "/dest/foo" 目录。

在所有包含 "." 的列子里,你能够使用绝对路径 "/path/to/source/foo" 来代替 ".",这样可以去掉 "cd ./source;". 如下所示,这些文件会根据工具的不同,拷贝到不同的位置。

  • "/dest/foo": rsync(8), GNU cp(1), 和 scp(1)

  • "/dest/path/to/source/foo": GNU tar(1), 和 cpio(1)

[提示] 提示

rsync(8) 和 GNU cp(1) 可以用 "-u" 选项来忽略接受端上更新的文件。

find(1) 被用作从归档中筛选文件也被用作拷贝命令 (参见第 10.1.3 节 “归档语法”第 10.1.4 节 “复制语法”) 或者用于 xargs(1) (参见第 9.3.9 节 “使用文件循环来重复一个命令”)。通过 find 的命令行参数能够使其功能得到加强。

以下是 find(1)基本语法的总结。

  • find 条件参数的运算规则是从左到右。

  • 一旦输出是确定的,那么运算就会停止。

  • “逻辑 OR" (由条件之间的 "-o" 参数指定的)优先级低于 "逻辑 AND" (由 "-a" 参数指定或者条件之间没有任何参数)。

  • ”逻辑 NOT" (由条件前面的 "!" 指定) 优先级高于 “逻辑 AND”。

  • "-prune" 总是返回逻辑 TRUE 并且如果这个目录是存在的,将会搜索除这个目录以外的文件。

  • "-name" 选项匹配带有 shell 通配符 (参见第 1.5.6 节 “Shell 通配符”) 的文件名但也匹配带有类似 "*" 和 "?" 元字符的 ."。(新的 POSIX 特性)

  • "-regex" 匹配整个文件路径,默认采用 emacs 风格的 BRE (参见第 1.6.2 节 “正则表达式”)。

  • "-size" 根据文件大小来匹配 (值前面带有 "+" 号匹配更大的文件,值前面带有 "-" 号匹配更小的文件)

  • "-newer" 参数匹配比参数名中指定的文件还要新的文件。

  • "-print0" 参数总是返回逻辑 TRUE 并将完整文件名 (null terminated) 打印到标准输出设备上。

如下是 find(1) 语法格式。

# find /path/to \
    -xdev -regextype posix-extended \
    -type f -regex ".*\.cpio|.*~" -prune -o \
    -type d -regex ".*/\.git" -prune -o \
    -type f -size +99M -prune -o \
    -type f -newer /path/to/timestamp -print0

这些命令会执行如下动作。

  1. 查找 "/path/to" 下的所有文件

  2. 限定全局查找的文件系统并且使用的是 ERE (参见第 1.6.2 节 “正则表达式”)

  3. 通过停止处理的方式来排除匹配 ".*\.cpio" 或 ".*~" 正则表达式的文件

  4. 通过停止处理的方式来排除匹配 ".*/\.git" 正则表达式的目录

  5. 通过停止处理的方式来排除比 99MB (1048576字节单元) 更大的文件

  6. 显示文件名,满足以上搜索条件并且比 "/path/to/timestamp" 新的文件

请留心以上例子中的 "-prune -o" 排除文件的习惯用法。

[注意] 注意

对于非 Debian 系的 Unix-like 系统,有些参数可能不被 find(1) 命令所支持。在这种情况下,应该考虑调整匹配方法并用 "-print" 替代 "-print0"。你可能同样需要更改其他相关的命令。

为重要的数据存档寻找 存储设备 时,你应该注意它们的局限性。对于小型的个人数据备份,我使用品牌公司的 CD-R 和 DVD-R 然后把它放在阴凉、干燥、清洁的地方。(专业的一般使用磁带存档介质)

[注意] 注意

防火安全是对于纸质文档来说的,大多数的计算机数据存储媒介耐热性比纸差。我经常依赖存储在多个安全地点的加密拷贝。

网上(主要是来源于供应商信息)可以查看存储介质的最大使用寿命。

  • 大于100年:用墨水的无酸纸

  • 100年:光盘存储(CD/DVD,CD/DVD-R)

  • 30年:磁带存储(磁带,软盘)

  • 20年:相变光盘存储(CD-RW)

这不包括由于人为导致的机械故障等等。

网上(主要来源于供应商信息)可以查看存储介质的最大的写次数。

  • 大于250,000次:硬盘驱动器

  • 大于10,000次:闪存

  • 1,000次:CD/DVD-RW

  • 1次:CD/DVD-R,纸

[小心] 小心

这里的存储寿命和写次数的数据不应该被用来决定任何用于关键数据的存储媒介,请翻阅制造商提供的特定产品的说明。

[提示] 提示

因为 CD/DVD-R 和 纸只能写一次,它们从根本上阻止了因为重写导致的数据意外丢失。这是优点!

[提示] 提示

如果你需要更快更频繁的进行大数据备份,那么通过高速网络连接的远端主机上的硬盘来实现备份,可能是唯一可行的方法。

可移动存储设备可能是以下的任何一种。

它们可以通过以下的方式来进行连接。

像 GNOME 和 KDE 这样的现代桌面环境能够在 "/etc/fstab" 文件中没有匹配条目的时候,自动挂载这些可移动设备。

  • udisks 包提供了守护进程和相关的实用程序来挂载和卸载这些设备。

  • D-bus 创建事件来触发自动处理。

  • PolicyKit 提供了所需的特权。

[提示] 提示

umount(8) 在自动挂载设备的时候可能会带有 "uhelper=" 参数。

[提示] 提示

只有当这些可移动设备没有在 "/etc/fstab" 文件中列出时,桌面环境下才会自动挂载。

现代桌面环境下的挂载点被选为 "/media/<disk_label>",它可以被如下所示的来定制。

  • FAT 格式的文件系统使用 mlabel(1) 命令

  • ISO9660 文件系统使用带有 "-V" 选项的 genisoimage(1) 命令

  • ext2/ext3/ext4 文件系统使用带有 "-L" 选项的 tune2fs(1) 命令

[提示] 提示

挂载时可能需要提供编码选项(参见 第 8.4.6 节 “文件名编码”)。

[提示] 提示

在图形界面菜单上移除文件系统,可能会移除它的动态设备节点例如 "/dev/sdc"。如果你想要保留它的设备节点,你应该在命令行提示符上输入 umount(8) 命令来卸载它。

当你通过可移动存储设备与其他系统分享数据的时候,你应该先把它格式化为被两种操作系统都支持的通用的 文件系统。下面是文件系统的列表。


[提示] 提示

查看第 9.8.1 节 “使用 dm-crypt/LUKS 加密移动磁盘”来获得关于使用设备级加密的跨平台的数据共享的信息。

FAT 文件系统被绝大多数的现代操作系统支持,它对于通过可移动硬盘进行的数据交换是非常有用的。

当格式化像装有 FAT 文件系统的跨平台数据共享的可移动设备时,以下应该是保险的选择。

当使用 FAT 或 ISO9660 文件系统分享数据时,如下是需要注意的安全事项。

  • tar(1),或cpio(1)命令压缩文件,目地是为了保留文件名,符号链接,原始的文件权限和文件所有者信息。

  • split(1) 命令把压缩文件分解成若干小于 2GiB的小文件,使其免受文件大小限制。

  • 加密压缩文件保护其内容免受未经授权的访问。

[注意] 注意

因为 FAT 文件系统的设计,最大的文件大小为 (2^32 - 1) bytes = (4GiB -1 byte)。对于一些老旧的 32 位系统上的应用程序而言,最大的文件大小甚至更小(2^31 -1) bytes = (2GiB -1 byte)。Debian 没有遇到后者的问题。

[注意] 注意

微软系统本身并不建议在超过 200MB 的分区或者驱动器上使用 FAT。他们的 " Overview of FAT, HPFS, and NTFS File Systems 这篇文章突出显示了微软系统的缺点,例如低效的磁盘空间利用。当然了,我们在 Linux 系统上还是应该使用 ext4 文件系统。

[提示] 提示

有关文件系统和访问文件系统的更多信息,请参考 "Filesystems HOWTO"。

我们都熟知计算机有时会出问题,或者由于人为的错误导致系统和数据损坏。备份和恢复操作是成功的系统管理中非常重要的一部分。可能有一天你的电脑就会出问题。

[提示] 提示

保持你的备份系统简洁并且经常备份你的系统,有备份数据比你采用的备份方法的技术先进要重要的多。

有3个关键的因素决定实际的备份和恢复策略。

  1. 知道要备份和恢复什么。

    • 你自己创建的数据文件:在 "~/" 下的数据

    • 你使用的应用程序创建的数据文件:在 "/var/" 下的数据(除了 "/var/cache/","/var/run/" 和 "/var/tmp/")

    • 系统配置文件:在 "/etc/” 下的数据

    • 本地软件:在 "/usr/local/" 或 "/opt/" 下的数据

    • 系统安装信息:关键步骤 (分区,...) 的纯文本备忘录

    • 验证数据结果:通过实验性的恢复操作来预先验证

  2. 知道怎样去备份和恢复。

    • 安全的数据存储:保护其免于覆盖和系统故障

    • 经常备份:有计划的备份

    • 冗余备份:数据镜像

    • 傻瓜式操作:单个简单命令备份

  3. 评估涉及的风险和成本。

    • 评估数据丢失的损失

    • 备份所需的资源:人力,硬件,软件,…

    • 数据丢失的方式及其可能性

[注意] 注意

除非你知道自己做的是什么,否则不要备份 /proc, /sys, /tmp, 和 /run 目录下的伪文件系统(参见 第 1.2.12 节 “procfs 和 sysfs”第 1.2.13 节 “tmpfs”)。它们是庞大且无用的数据。

至于安全的数据存储,数据至少是应该在不同的磁盘分区上最好是在不同的磁盘和机器上,来承受文件系统发生的损坏。重要的数据最好存储在只能写一次的媒介上例如 CD/DVD-R 来防止覆盖事故。(参见 第 9.7 节 “二进制数据” 怎样在 shell 命令行写入存储媒介。GNOME 桌面图形环境可以让你轻松的通过菜单:“位置 → CD/DVD 刻录”来实现写入操作。)

[注意] 注意

当备份数据的时候,你可能希望停止一些应用程序的守护进程例如 MTA(参见第 6.3 节 “邮件传输代理 (MTA)”)。

[注意] 注意

你应该格外小心地备份和恢复身份认证相关的数据文件例如 "/etc/ssh/ssh_host_dsa_key", "/etc/ssh/ssh_host_rsa_key", "~/.gnupg/*", "~/.ssh/*", /etc/passwd", "/etc/shadow", "/etc/fetchmailrc", "popularity-contest.conf", "/etc/ppp/pap-secrets" 和 "/etc/exim4/passwd.client/"。 这些数据中的有一些文件是不能通过向系统输入同样的字符串来再生的。

[注意] 注意

如果你以用户进程的方式执行 cron job,你必须存储文件到 "/var/spool/cron/crontabs" 目录并且重启 cron(8)。参见第 9.3.14 节 “定时任务安排”来获得关于 cron(8)crontab(1) 的信息。

以下是 Debian 系统上值得注意的实用备份程序套件的列表。

表 10.5. 实用备份程序套件列表

软件包 流行度 大小 说明
dump V:1, I:7 341 4.4 BSD dump(8)restore(8) 命令用于 ext2/ext3/ext4 文件系统
xfsdump V:0, I:11 838 在 GNU/Linux 和 IRIX 上用 xfsdump(8)xfsrestore(8) 命令来备份和恢复 XFS 文件系统
backupninja V:4, I:4 329 轻量的可扩展的 meta-backup 系统
bacula-common V:10, I:19 2055 Bacula: 网络数据备份,恢复和核查-常见的支持文件
bacula-client I:5 162 Bacula: 网络数据备份,恢复和核查-客户端元软件包
bacula-console V:1, I:7 64 Bacula: 网络数据备份,恢复和核查-文本终端
bacula-server I:2 165 Bacula: 网络数据备份,恢复和核查-服务器端元软件包
amanda-common V:1, I:2 7419 Amanda: 马里兰大学开发的高级自动化网络磁盘归档器(库)
amanda-client V:1, I:2 999 Amanda: 马里兰大学开发的高级自动化网络磁盘归档器(客户端)
amanda-server V:0, I:0 1047 Amanda: 马里兰大学开发的高级自动化网络磁盘归档器(服务器端)
backup-manager V:1, I:2 543 命令行备份工具
backup2l V:1, I:1 113 用于可挂载媒介 (基于磁盘的) 的低维护的备份/恢复工具
backuppc V:4, I:5 2232 BackupPC 是用于备份 PC 机数据(基于磁盘)的高性能的企业级工具
duplicity V:8, I:15 1534 (远程) 增量备份
flexbackup V:0, I:0 242 (远程) 增量备份
rdiff-backup V:9, I:18 704 (远程) 增量备份
restic V:0, I:0 11149 (远程) 增量备份
rsnapshot V:6, I:12 452 (远程) 增量备份
slbackup V:0, I:0 146 (远程) 增量备份

备份工具有各自的专用的用途。

第 10.1.1 节 “存档和压缩工具”第 10.1.2 节 “复制和同步工具” 描述的基础工具能够通过自定义脚本来帮助系统备份。这些脚本的功能可以通过如下的工具来增强。

  • restic 软件包能够增量备份(远程)。

  • rdiff-backup 软件包能够增量备份(远程)。

  • dump 软件包用于高效增量的归档和恢复整个文件系统。

[提示] 提示

参见 "/usr/share/doc/dump/" 和 "Is dump really deprecated?" 来了解 dump 程序。

对于运行 unstable 套件的个人 Debian 桌面系统来说,只需要保护个人数据和关键数据。我不管怎样每年都会重新安装一次系统。因此没理由去备份整个系统或者安装全功能的备份实用程序。

我使用简单的脚本来制作用于备份的压缩文件并用 GUI 界面把它烧写到 CD/DVD 里。以下是关于这个的脚本例子。

#!/bin/sh -e
# Copyright (C) 2007-2008 Osamu Aoki <osamu@debian.org>, Public Domain
BUUID=1000; USER=osamu # UID and name of a user who accesses backup files
BUDIR="/var/backups"
XDIR0=".+/Mail|.+/Desktop"
XDIR1=".+/\.thumbnails|.+/\.?Trash|.+/\.?[cC]ache|.+/\.gvfs|.+/sessions"
XDIR2=".+/CVS|.+/\.git|.+/\.svn|.+/Downloads|.+/Archive|.+/Checkout|.+/tmp"
XSFX=".+\.iso|.+\.tgz|.+\.tar\.gz|.+\.tar\.bz2|.+\.cpio|.+\.tmp|.+\.swp|.+~"
SIZE="+99M"
DATE=$(date --utc +"%Y%m%d-%H%M")
[ -d "$BUDIR" ] || mkdir -p "BUDIR"
umask 077
dpkg --get-selections \* > /var/lib/dpkg/dpkg-selections.list
debconf-get-selections > /var/cache/debconf/debconf-selections

{
find /etc /usr/local /opt /var/lib/dpkg/dpkg-selections.list \
     /var/cache/debconf/debconf-selections -xdev -print0
find /home/$USER /root -xdev -regextype posix-extended \
  -type d -regex "$XDIR0|$XDIR1" -prune -o -type f -regex "$XSFX" -prune -o \
  -type f -size  "$SIZE" -prune -o -print0
find /home/$USER/Mail/Inbox /home/$USER/Mail/Outbox -print0
find /home/$USER/Desktop  -xdev -regextype posix-extended \
  -type d -regex "$XDIR2" -prune -o -type f -regex "$XSFX" -prune -o \
  -type f -size  "$SIZE" -prune -o -print0
} | cpio -ov --null -O $BUDIR/BU$DATE.cpio
chown $BUUID $BUDIR/BU$DATE.cpio
touch $BUDIR/backup.stamp

这是一个用 root 权限执行的脚本例子。

我建议你按照如下所示的去更改和执行这个脚本。

把事情简单化!

[提示] 提示

你能够用 "debconf-set-selections debconf-selections" 命令恢复 debconf 配置数据,可以用 "dpkg --set-selection <dpkg-selections.list" 命令恢复 dpkg 筛选数据。

对于目录树下面的数据集,"cp -a" 命令可以实现常规备份。

对于类似 "/var/cache/apt/packages/" 目录下面的大量不可覆盖的静态数据集,使用 "cp -al" 命令来创建硬链接是一种替代常规备份的方式,这样可以高效的利用磁盘空间。

以下是一个用于数据备份的名为 bkup 的复制脚本。它把当前目录下的所有 (non-VCS) 文件复制到父目录下的指定目录中或者远程主机上。

#!/bin/sh -e
# Copyright (C) 2007-2008 Osamu Aoki <osamu@debian.org>, Public Domain
fdot(){ find . -type d \( -iname ".?*" -o -iname "CVS" \) -prune -o -print0;}
fall(){ find . -print0;}
mkdircd(){ mkdir -p "$1";chmod 700 "$1";cd "$1">/dev/null;}
FIND="fdot";OPT="-a";MODE="CPIOP";HOST="localhost";EXTP="$(hostname -f)"
BKUP="$(basename $(pwd)).bkup";TIME="$(date  +%Y%m%d-%H%M%S)";BU="$BKUP/$TIME"
while getopts gcCsStrlLaAxe:h:T f; do case $f in
g)  MODE="GNUCP";; # cp (GNU)
c)  MODE="CPIOP";; # cpio -p
C)  MODE="CPIOI";; # cpio -i
s)  MODE="CPIOSSH";; # cpio/ssh
t)  MODE="TARSSH";; # tar/ssh
r)  MODE="RSYNCSSH";; # rsync/ssh
l)  OPT="-alv";; # hardlink (GNU cp)
L)  OPT="-av";;  # copy (GNU cp)
a)  FIND="fall";; # find all
A)  FIND="fdot";; # find non CVS/ .???/
x)  set -x;; # trace
e)  EXTP="${OPTARG}";; # hostname -f
h)  HOST="${OPTARG}";; # user@remotehost.example.com
T)  MODE="TEST";; # test find mode
\?) echo "use -x for trace."
esac; done
shift $(expr $OPTIND - 1)
if [ $# -gt 0 ]; then
  for x in $@; do cp $OPT $x $x.$TIME; done
elif [ $MODE = GNUCP ]; then
  mkdir -p "../$BU";chmod 700 "../$BU";cp $OPT . "../$BU/"
elif [ $MODE = CPIOP ]; then
  mkdir -p "../$BU";chmod 700 "../$BU"
  $FIND|cpio --null --sparse -pvd ../$BU
elif [ $MODE = CPIOI ]; then
  $FIND|cpio -ov --null | ( mkdircd "../$BU"&&cpio -i )
elif [ $MODE = CPIOSSH ]; then
  $FIND|cpio -ov --null|ssh -C $HOST "( mkdircd \"$EXTP/$BU\"&&cpio -i )"
elif [ $MODE = TARSSH ]; then
  (tar cvf - . )|ssh -C $HOST "( mkdircd \"$EXTP/$BU\"&& tar xvfp - )"
elif [ $MODE = RSYNCSSH ]; then
  rsync -aHAXSv ./ "${HOST}:${EXTP}-${BKUP}-${TIME}"
else
  echo "Any other idea to backup?"
  $FIND |xargs -0 -n 1 echo
fi

如上只是一个范例。在你自己使用脚本之前,请阅读此脚本并且修改它。

[提示] 提示

我把 bkup 保存在我的 "/usr/local/bin" 目录。我假定当需要临时快照备份的时候,能够在工作目录不带任何参数运行 bkup 命令。

[提示] 提示

如果是要制作源文件树或者配置文件树的快照历史的话,使用 git(7) (参见第 10.6.5 节 “记录配置历史的 Git”) 是更简便并且也是空间高效的。

数据安全基础设施是数据加密,讯息摘要和签名工具的结合。


参见 第 9.8 节 “数据加密提示”dm-cryptoecryptfs,它们通过 Linux 内核模块实现了自动数据加密架构。

如下是 GNU 隐私卫士 基本的密钥管理命令。


信任码含义.


如下命令上传我的 "1DD8D791" 公钥到主流的公钥服务器 "hkp://keys.gnupg.net"。

$ gpg --keyserver hkp://keys.gnupg.net --send-keys 1DD8D791

默认良好的公钥服务器在 "~/.gnupg/gpg.conf" (旧的位置在 "~/.gnupg/options")文件中设置,此文件包含了以下信息。

keyserver hkp://keys.gnupg.net

从钥匙服务器获取无名钥匙。

$ gpg --list-sigs --with-colons | grep '^sig.*\[User ID not found\]' |\
  cut -d ':' -f 5| sort | uniq | xargs gpg --recv-keys

有一个错误在 OpenPGP 公钥服务器 (先前的版本 0.9.6),会将键中断为 2 个以上的子键。新的 gnupg (>1.2.1-2) 软件包能够处理这些中断的子键。参见 gpg(1) 下的 "--repair-pks-subkey-bug" 选项.

md5sum(1) 提供了制作摘要文件的一个工具,它使用 rfc1321 里的方式制作摘要文件.

$ md5sum foo bar >baz.md5
$ cat baz.md5
d3b07384d113edec49eaa6238ad5ff00  foo
c157a79031e1c40f85931829bc5fc552  bar
$ md5sum -c baz.md5
foo: OK
bar: OK
[注意] 注意

MD5 校验和的 CPU 计算强度是比 GNU Privacy Guard (GnuPG) 加密签名要少的.在通常情况下,只有顶级的摘要文件才需要加密签名来确保数据完整性.

这里有许多源代码合并工具。如下的是我感兴趣的工具。

表 10.10. 源代码合并工具列表

软件包 流行度 大小 命令 说明
diffutils V:857, I:981 1394 diff(1) 逐行比较两个文件
diffutils V:857, I:981 1394 diff3(1) 逐行比较和合并三个文件
vim V:122, I:392 2470 vimdiff(1) 在 vim 中并排比较两个文件
patch V:98, I:857 216 patch(1) 给原文件打补丁
dpatch V:1, I:16 191 dpatch(1) 管理 Debian 软件包的系列补丁
diffstat V:21, I:186 70 diffstat(1) 通过 diff 生成一个改变柱状图
patchutils V:20, I:179 223 combinediff(1) 从两个增量补丁创建一个积累补丁
patchutils V:20, I:179 223 dehtmldiff(1) 从一个 HTML 页面提取出一个 diff
patchutils V:20, I:179 223 filterdiff(1) 从一个 diff 文件里面提取或者排除 diff 文件
patchutils V:20, I:179 223 fixcvsdiff(1) 修复由 CVS patch(1) 错误创建的 diff 文件
patchutils V:20, I:179 223 flipdiff(1) 交换两个补丁的顺序
patchutils V:20, I:179 223 grepdiff(1) 显示哪些文件是由匹配正则表达式的补丁修改
patchutils V:20, I:179 223 interdiff(1) 显示在两个统一格式 diff 文件(基于同一个文件的两个不同 diff 文件)之间的差异
patchutils V:20, I:179 223 lsdiff(1) 显示哪些文件由补丁修改
patchutils V:20, I:179 223 recountdiff(1) 重新计算通用内容 diff 文件的数量和偏移
patchutils V:20, I:179 223 rediff(1) 修复手工编辑 diff 文件的数量和偏移
patchutils V:20, I:179 223 splitdiff(1) 隔离出增量补丁
patchutils V:20, I:179 223 unwrapdiff(1) 识别已经被分词的补丁
wiggle V:0, I:0 166 wiggle(1) 应用被拒绝的补丁
quilt V:4, I:43 711 quilt(1) 管理系列补丁
meld V:15, I:41 3115 meld(1) 比较和移植文件(GTK)
dirdiff V:0, I:2 144 dirdiff(1) 显示目录树之间的不同并移植改变
docdiff V:0, I:0 573 docdiff(1) 逐词逐字的比较两个文件
imediff2 V:0, I:0 34 imediff2(1) 全屏交付式双向移植工具
makepatch V:0, I:0 102 makepatch(1) 生成扩展补丁文件
makepatch V:0, I:0 102 applypatch(1) 应用扩展补丁文件
wdiff V:5, I:81 643 wdiff(1) 在文本文件中,显示单词的不同

如下是 Debian 系统上可用的版本控制系统(VCS) 的摘要。

[注意] 注意

如果是刚接触版本控制系统,你应该从 git 入门,git 人气日益高涨。


VCS 有时被认为是修订控制系统 (RCS), 或者是软件配置管理程序 (SCM)。

像 Git 这样的分布式 VCS 是现在正在使用的工具。参加那些已经存在的开源软件的开发活动,掌握 CVS 和 Subversion 仍然是有用的。

通过 Debian Alioth service,Debian 能够提供免费的几乎所有的 VCS 服务。在 http://wiki.debian.org/Alioth 能找到它的说明文档。

这里有一些关于创建共享访问 VCS 归档的基础知识。

这里有原生 VCS 命令的简单比较来提供大图概要。典型的命令序列需要选项和参数。


[小心] 小心

从命令行通过 "git-xyz" 直接调用 git 子命令的方式,从 2006 年早期开始就被取消。

[提示] 提示

如果有一个可执行文件 git-foo 在路径环境变量 $PATH 里面,在命令行输入没有中划线的 "git foo",则将调用 git-foo.这是 git 命令的一个特性.

[提示] 提示

例如 tkcvs(1)gitk(1) 这样的图形界面工具有助于追踪文件的修改历史。许多公共的归档提供的用于浏览它们的存储库的 web 界面同样是很有用的。

[提示] 提示

Git 能够直接在不同的 VCS 仓库上工作,比如说 CVS 和 Subversion 提供的仓库, 通过 git-cvsgit-svn 软件包提供本地仓库的本地改变.参见 用于 CVS 用户的 git第 10.6.4 节 “用于 Subversion 仓库的 Git”.

[提示] 提示

Git 中的有些命令在 CVS 和 Subversion 中并没有对应的命令:"fetch","rebase","cherry-pick", …

Git 可以用来做本地和远程源代码管理的任何事情。这意味着,你能够在本地记录源代码修改,而不是必须要和远程仓库有网络连接。

参见下面内容。

git-gui(1)gitk(1) 命令使 Git 变得非常容易使用。

[警告] 警告

不要使用带空格的标签字符串。即使一些工具,如 gitk(1) 允许你使用它,但会阻碍其它 git 命令。

即使你的上游使用不同的版本控制系统,使用 git(1) 作为本地活动的版本控制系统,仍然是一个好的主意,因为 git 可以让你在没有上游网络连接的情况下,管理你的本地源代码树拷贝。这里有一些 git(1) 使用的包和命令。


[提示] 提示

git(1) 下,你在本地分支下进行了许多提交,稍后你可以使用 "git rebase -i master" 之类的命令来重新组织改变历史。这可以使你制作一个干净的改变历史。参见 git-rebase(1)git-cherry-pick(1).

[提示] 提示

当你想要回到一个干净的工作目录,并且不丢失工作目录当前的状态,你可以使用 "git stash".参见 git-stash(1).

你可以使用 Git 工具来手工记录按时间先后顺序的配置历史。这里是一个例子,让你练习记录"/etc/apt/" 内容。

$ cd /etc/apt/
$ sudo git init
$ sudo chmod 700 .git
$ sudo git add .
$ sudo git commit -a

提交配置,描述此次提交。

对配置文件进行修改。

$ cd /etc/apt/
$ sudo git commit -a

提交配置,说明提交,继续你的工作。

$ cd /etc/apt/
$ sudo gitk --all

你有全部的配置历史。

[注意] 注意

sudo(8) 是需要用于配置数据文件,任意文件权限的情况。 对于普通用户的配置数据,你需要省略 sudo

[注意] 注意

在上面例子里的 "chmod 700 .git" 命令,是用来保护文档数据不被未经授权的读访问。

[提示] 提示

要更加完整的建立配置历史记录,请参阅 etckeeper 包: 第 9.2.10 节 “记录配置文件的变更”

参见下面内容。

  • cvs(1)

  • "/usr/share/doc/cvs/html-cvsclient"

  • "/usr/share/doc/cvs/html-info"

  • "/usr/share/doc/cvsbook"

  • "info cvs"

许多公共 CVS 服务器可以通过 pserver 服务用 "anonymous" 账户远程只读访问。例如,Debian 网站的内容是 webwml project 通过 Debian alioth 服务中的 CVS 来维护。如下将建立用于远程访问 CVS 存储库的 "$CVSROOT"。

$ export CVSROOT=:pserver:anonymous@anonscm.debian.org:/cvs/webwml
$ cvs login
[注意] 注意

因为 pserver 容易被窃听攻击并且是不安全的,所以写访问通常是被服务器管理员禁用的。

如下所示将建立 webwml project 使用 SSH 远程访问 CVS 存储库所需的 "$CVS_RSH" 和 "$CVSROOT"。

$ export CVS_RSH=ssh
$ export CVSROOT=:ext:account@cvs.alioth.debian.org:/cvs/webwml

你也可以使用 SSH 的公钥认证,这能够去除远程密码提示。

这里有一个 CVS 典型工作流的例子。

按如下所示查看 "$CVSROOT" 所指的 CVS 项目上所有可用的模块。

$ cvs rls
CVSROOT
module1
module2
...

按如下所示签出 "module1" 到默认的目录 "./module1"。

$ cd ~/path/to
$ cvs co module1
$ cd module1

按需修改里面的内容。

通过如下所示的命令来检查改变,其作用相当于使用 "diff -u [repository] [local]"。

$ cvs diff -u

你发现自己改坏了 "file_to_undo" 文件,而其他的文件都是好的。

按如下所示用 CVS 中的原始副本覆盖 "file_to_undo" 文件。

$ cvs up -C file_to_undo

按如下所示把更新了的本地源目录树保存到 CVS。

$ cvs ci -m "Describe change"

按如下创建并添加 "file_to_add" 文件到 CVS。

$ vi file_to_add
$ cvs add file_to_add
$ cvs ci -m "Added file_to_add"

按如下所示合并 CVS 中的最新版本。

$ cvs up -d

当心以 "C filename" 开头的行,这意味着冲突的改变。

查看 ".#filename.version" 中未经修改的代码。

查找文件中的 "<<<<<<<" 和 ">>>>>>>" 来获得冲突的改变的信息。

按需更改文件来解决冲突。

按如下所示添加一个发布标签 "Release-1"。

$ cvs ci -m "last commit for Release-1"
$ cvs tag Release-1

继续编辑文件。

按如下所示移除发布分支 "Release-1"。

$ cvs tag -d Release-1

按如下所示把改变签入到 CVS。

$ cvs ci -m "real last commit for Release-1"

按如下所示给已经更新了的 CVS 主干中的 HEAD 重新添加 "Release-1" 发布标签。

$ cvs tag Release-1

按如下所示从 "Release-initial" 标签指向的初始版本中创建一个带有粘性标签的 "Release-initial-bugfixes" 分支,并把它签出到 "~/path/to/old" 目录。

$ cvs rtag -b -r Release-initial Release-initial-bugfixes module1
$ cd ~/path/to
$ cvs co -r Release-initial-bugfixes -d old module1
$ cd old
[提示] 提示

使用 "-D 2005-12-20" (ISO 8601 日期格式) 而不是 "-r Release-initial" 来指定某个特定日期作为分支点。

在基于原始版本的有 "Release-initial-bugfixes" 粘性标签的本地源目录树上工作。

独自在 "Release-initial-bugfixes" 分支上工作...直到有其他人加入到此分支。

当要创建新的目录时,按如下所示同步其他人在此分支上对文件所做的修改。

$ cvs up -d

按需更改文件来解决冲突。

按如下所示把改变签入到 CVS。

$ cvs ci -m "checked into this branch"

按如下所示更新本地目录树为主干的最新版本,同时移除粘性标签 ("-A") 并且不使用关键字扩展 ("-kk")。

$ cvs up -d -kk -A

按如下所示通过合并 "Release-initial-bugfixes" 分支并且不使用关键字扩展的方式来更新本地目录树 (内容为主干中的最新版本)。

$ cvs up -d -kk -j Release-initial-bugfixes

用编辑器来解决冲突。

按如下所示把改变签入到 CVS。

$ cvs ci -m "merged Release-initial-bugfixes"

按如下所示创建归档。

$ cd ..
$ mv old old-module1-bugfixes
$ tar -cvzf old-module1-bugfixes.tar.gz old-module1-bugfixes
$ rm -rf old-module1-bugfixes
[提示] 提示

"cvs up" 命令能够使用 "-d" 选项来创建新目录并且可以使用 "-P" 选项来删除空目录。

[提示] 提示

你可以通过形如 "cvs co module1/subdir" 这样的列出其名的方式,来签出 "module1" 的一个子目录。


Subversion 是替代旧的 CVS 的近代版本控制系统。除了标签和分支以外,它也拥有 CVS 的绝大多数特性。

你需要安装 subversionlibapache2-svnsubversion-tools 软件包来搭建 Subversion 服务器。

这里给出使用 Subversion 及其原生客户端的典型工作流示例。

[提示] 提示

git-svn 软件包提供的客户端命令,可以作为使用 git 命令的 Subversion 工作流的一个另外选择。参见 第 10.6.4 节 “用于 Subversion 仓库的 Git”.

查看如下所示的 URL "file:///srv/svn/project" 指向的 Subversion 项目上所有可用的模块。

$ svn list file:///srv/svn/project
module1
module2
...

按如下所示的检出 "module1/trunk" 到 "module1" 目录。

$ cd ~/path/to
$ svn co file:///srv/svn/project/module1/trunk module1
$ cd module1

按需修改里面的内容。

通过如下所示的命令来检查改变,其作用相当于使用 "diff -u [repository] [local]"。

$ svn diff

你发现自己改坏了 "file_to_undo" 文件,而其他的文件都是好的。

按如下所示的用 Subversion 中的干净副本来覆盖 "file_to_undo" 文件。

$ svn revert file_to_undo

按如下所示的把已经更新了的本地源目录树保存到 Subversion。

$ svn ci -m "Describe change"

按如下所示的创建 "file_to_add" 文件并把它添加到 Subversion。

$ vi file_to_add
$ svn add file_to_add
$ svn ci -m "Added file_to_add"

按如下所示更新工作拷贝到 Subversion 中的最新版本。

$ svn up

当心以 "C filename" 开头的行,这意味着冲突的改变。

查看文件中未经修改的代码,例如 "filename.r6", "filename.r9" 和 "filename.mine" 文件。

查找文件中的 "<<<<<<<" 和 ">>>>>>>" 来获得冲突的改变的信息。

按需更改文件来解决冲突。

按如下所示添加一个发布标签 "Release-1"。

$ svn ci -m "last commit for Release-1"
$ svn cp file:///srv/svn/project/module1/trunk file:///srv/svn/project/module1/tags/Release-1

继续编辑文件。

按如下所示移除发布分支 "Release-1"。

$ svn rm file:///srv/svn/project/module1/tags/Release-1

按如下所示把改变签入到 Subversion。

$ svn ci -m "real last commit for Release-1"

按如下所示在最新的 Subversion 主干的基础上重新添加发布分支 "Release-1"。

$ svn cp file:///srv/svn/project/module1/trunk file:///srv/svn/project/module1/tags/Release-1

按如下所示在 "module1/tags/Release-initial" 路径指定的最初版本的基础上再创建一个路径为 "module1/branches/Release-initial-bugfixes" 的分支,并把它签出到 "~/path/to/old" 目录。

$ svn cp file:///srv/svn/project/module1/tags/Release-initial file:///srv/svn/project/module1/branches/Release-initial-bugfixes
$ cd ~/path/to
$ svn co file:///srv/svn/project/module1/branches/Release-initial-bugfixes old
$ cd old
[提示] 提示

使用 "module1/trunk@{2005-12-20}" (ISO 8601 日期格式) 而不是 "module1/tags/Release-initial" 来指定分支创建时候的日期。

在基于原始版本的 "Release-initial-bugfixes" 分支的本地源目录树上工作。

独自在 "Release-initial-bugfixes" 分支上工作...直到有其他人加入到此分支。

按如下所示同步其他人在此分支上改动的文件。

$ svn up

按需更改文件来解决冲突。

按如下所示把改变签入到 Subversion。

$ svn ci -m "checked into this branch"

按如下所示更新本地目录树为主干的最新版本。

$ svn switch file:///srv/svn/project/module1/trunk

按如下所示通过合并 "Release-initial-bugfixes" 分支的方式来更新本地目录树 (内容为主干的最新版本)。

$ svn merge file:///srv/svn/project/module1/branches/Release-initial-bugfixes

用编辑器来解决冲突。

按如下所示把改变签入到 Subversion。

$ svn ci -m "merged Release-initial-bugfixes"

按如下所示创建归档。

$ cd ..
$ mv old old-module1-bugfixes
$ tar -cvzf old-module1-bugfixes.tar.gz old-module1-bugfixes
$ rm -rf old-module1-bugfixes
[提示] 提示

你能够用像 "http://…" 和 "svn+ssh://..." 这样格式的 URL 来替代 "file:///…" URL。

[提示] 提示

通过 "svn co file:///srv/svn/project/module1/trunk/subdir module1/subdir" 等命令,你可以只签出 "module1" 的一个子目录。