第 10 章 数据管理

# 第 10 章 数据管理

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

## 10.1. 共享，拷贝和存档

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

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

- 存档文件的建立

- 远程存储访问

- 复制

- 跟踪修改历史

- 促进数据共享

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

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

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

- 存档和压缩工具

- 复制和同步工具

- 网络文件系统

- 移动存储媒介

- 安全 shell

- 认证体系

- 版本控制系统工具

- 哈希算法和加密工具

### 10.1.1. 存档和压缩工具

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

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

软件包 | 流行度 | 大小 | 扩展名 | 命令 | 描述
--- | --- | --- | --- | --- | ---
`tar` | V:908, I:1000 | 3085 | `.tar` | tar(1) | 标准的归档工具（默认）
`cpio` | V:352, I:999 | 1201 | `.cpio` | cpio(1) | Unix System V 风格的归档器，与 find(1) 一起使用
`binutils` | V:190, I:640 | 1119 | `.ar` | ar(1) | 创建静态库的归档工具
`fastjar` | V:1.0, I:9.6 | 182 | `.jar` | fastjar(1) | Java 归档工具（类似 zip）
`pax` | V:5.5, I:9.6 | 167 | `.pax` | pax(1) | 新的 POSIX 归档工具，介于 `tar` 和 `cpio` 之间
`gzip` | V:904, I:1000 | 256 | `.gz` | gzip(1), zcat(1), … | GNU LZ77 压缩工具（默认）
`bzip2` | V:163, I:972 | 113 | `.bz2` | bzip2(1), bzcat(1), … | Burrows-Wheeler block-sorting 压缩工具有着比 gzip(1) 更高的压缩率 (跟 `gzip` 有着相似的语法但速度比它慢)
`lzma` | V:1, I:10 | 349 | `.lzma` | lzma(1) | LZMA 压缩工具有着比 gzip(1) 更高的压缩率（不推荐)
`xz-utils` | V:318, I:982 | 1520 | `.xz` | xz(1), xzdec(1), … | XZ 压缩工具有着比 bzip2(1) 更高的压缩率（压缩速度慢于 `gzip` 但是比 `bzip2` 快; LZMA 压缩工具的替代品)
`zstd` | V:318, I:803 | 2309 | `.zstd` | zstd(1), zstdcat(1), … | Zstandard 快速无损压缩工具
`p7zip` | V:6, I:198 | 8 | `.7z` | 7zr(1), p7zip(1) | 有着更高压缩率的 7-zip 文件归档器（LZMA 压缩）
`p7zip-full` | V:20, I:222 | 12 | `.7z` | 7z(1), 7za(1) | 有着更高压缩率的 7-Zip 文件归档器（LZMA 压缩和其他）
`lzop` | V:12, I:134 | 164 | `.lzo` | lzop(1) | LZO 压缩工具有着比 gzip(1) 更高的压缩和解压缩速度 (跟 `gzip` 有着相似的语法但压缩率比它低)
`zip` | V:49, I:361 | 627 | `.zip` | zip(1) | InfoZip：DOS 归档器和压缩工具
`unzip` | V:110, I:749 | 387 | `.zip` | unzip(1) | InfoZIP：DOS 解档器和解压缩工具

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

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

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

- FOSS 工具，例如 tar(1)，中的主流压缩方法已经按如下所示的迁移: `gzip` → `bzip2` → `xz`

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

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

- Libreoffice 数据文件的内部结构是 "`.jar`" 文件，它也可以使用 `unzip` 工具来打开。

- 事实上跨平台支持最好的存档工具是 `zip`。按照“`zip -rX`”的方式调用可以获得最大的兼容性。如果最大文件大小需要纳入考虑范围，请同时配合“`-s`”选项使用。

### 10.1.2. 复制和同步工具

以下是 Debian 系统上的可用的简单复制和备份工具的预览。

表 10.2. 复制和同步工具列表

软件包 | 流行度 | 大小 | 工具 | 功能
--- | --- | --- | --- | ---
`coreutils` | V:911, I:1000 | 17994 | GNU cp | 复制本地文件和目录("-a" 参数实现递归）
`openssh-client` | V:915, I:996 | 4210 | scp | 复制远端文件和目录（客户端，"`-r`" 参数实现递归)
`openssh-server` | V:768, I:819 | 3359 | sshd | 复制远端文件和目录（远程服务器）
`rsync` | V:195, I:541 | 835 |  | 单向远程同步和备份
`unison` | V:3, I:12 | 14 |  | 双向远程同步和备份

在复制文件的时候， rsync(8) 比其他工具提供了更多的特性。

- 差分传输算法只会发送源文件与已存在的目标文件之间的差异部分

- 快速检查算法 (默认) 会查找大小或者最后的修改时间有变化的文件

- "`--exclude`" 和 "`--exclude-from`" 选项类似于 tar(1)

- 在源目录中添加反斜杠的语法能够避免在目标文件中创建额外的目录级别。

| 提示
在 表 10.14 “其它版本控制系统工具列表” 中的版本控制系统 (VCS) 可以被认为是多路拷贝和同步工具。

### 10.1.3. 归档语法

以下是用不同的工具压缩和解压缩整个 "`./source`" 目录中的内容。

GNU tar(1):

    $ tar -cvJf archive.tar.xz ./source
    $ tar -xvJf archive.tar.xz

或者，如下所示。

    $ find ./source -xdev -print0 | tar -cvJf archive.tar.xz --null -T -

cpio(1):

    $ find ./source -xdev -print0 | cpio -ov --null > archive.cpio; xz archive.cpio
    $ zcat archive.cpio.xz | cpio -i

### 10.1.4. 复制语法

如下是用不同的工具复制整个 "`./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`" 选项来忽略接受端上更新的文件。

### 10.1.5. 查找文件的语法

find(1) 被用作从归档中筛选文件也被用作拷贝命令 (参见第 10.1.3 节 “归档语法”和第 10.1.4 节 “复制语法”) 或者用于 xargs(1) (参见第 9.4.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`"。你可能同样需要更改其他相关的命令。

### 10.1.6. 归档媒体

为重要的数据存档寻找 存储设备 时，你应该注意它们的局限性。对于小型的个人数据备份，我使用品牌公司的 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 和 纸只能写一次，它们从根本上阻止了因为重写导致的数据意外丢失。这是优点！

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

| 提示
如果你在使用一个可重复写入的介质作为你的备份介质，使用支持只读快照的 btrfs 或 zfs 文件系统，也许是一个好注意。

### 10.1.7. 可移动存储设备

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

- USB 闪存盘

- 硬盘驱动器

- 光盘驱动器

- 数码相机

- 数字音乐播放器

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

- USB

- IEEE 1394 / FireWire

- PC 卡

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

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

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

- PolicyKit 提供了所需的特权。

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

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

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

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

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

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

| 提示
挂载时可能需要提供编码选项（参见 第 8.1.3 节 “文件名编码”）。

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

### 10.1.8. 选择用于分享数据的文件系统

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

| 注意
对硬盘(HDD)的描述也适合用于其它存储设备，比如 SSD / U 盘 / 内存卡 /……。替换例子中 `/dev/sda` 的设备名称，用其适合的设备名称 `/dev/nvme0`、`/dev/mmcblk0`……。

表 10.3. 典型使用场景下可移动存储设备可选择的文件系统列表

文件系统名 | 典型使用场景
--- | ---
FAT12 | 软盘（<32MiB）上跨平台的数据分享
FAT16 | 在小硬盘(<2GiB)上的跨平台的数据分享
FAT32 | 在大硬盘（<8TiB，被 MS Windows95 OSR2 以上的操作系统所支持) 上的跨平台的数据分享
exFAT | 在大硬盘类设备上跨平台共享数据（<512TiB，被 WindowsXP, Mac OS X Snow Leopard 10.6.5 和 Linux 内核 5.4 版本以上的操作系统所支持)
NTFS | 在大硬盘类设备上的跨平台共享数据 (在 MS Windows NT 和后续版本原生支持；在 Linux 上，通过使用 FUSE 的 NTFS-3G 支持。)
ISO9660 | 在 CD-R 和 DVD+/-R 上的跨平台的静态数据分享
UDF | CD-R 和 DVD+/-R (新)上的增量数据写入
MINIX | 软盘上磁盘空间高利用率的 unix 文件数据存储
ext2 | 在装有老旧 linux 系统的硬盘上的数据分享
ext3 | 在装有老旧 linux 系统的硬盘上的数据分享
ext4 | 在装有较新的 linux 系统的硬盘上的数据分享
btrfs | 使用只读快照在装有较新的 Linux 系统的硬盘上共享数据

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

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

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

- 用 fdisk(8)，cfdisk(8) 或者 parted(8) 命令（参见第 9.6.2 节 “硬盘分区配置”）把它们格式化为单个的主分区并对把它做如下标记。

  - 标记小于 2GB 的 FAT 设备为 字符"6"。

  - 标记更大的 FAT32 设备为字符 "c"。

- 如下所示是用 mkfs.vfat(8) 命令格式化主分区的。

  - 它的设备名字，例如 "`/dev/sda1`" 用于 FAT16 设备

  - 明确的选项和它的设备名，例如 "`-F 32 /dev/sda1`" 用于 FAT32 设备

当使用 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"。

### 10.1.9. 网络上的数据分享

当使用网络来分享数据的时候，你应该使用通用的服务。这里有一些提示。

表 10.4. 典型使用场景下可选择的网络服务列表

网络服务 | 典型使用场景描述
--- | ---
SMB/CIFS 用 Samba 挂载网络文件系统 | 通过 “Microsoft Windows 网络” 分享文件，参见 smb.conf(5) 和 官方 Samba 3.x.x 指导和参考手册（The Official Samba 3.x.x HOWTO and Reference Guide） 或 `samba-doc` 软件包
NFS 用 Linux 内核挂载网络文件系统 | 通过 “Unix/Linux 网络" 分享文件，参见 exports(5) 和 Linux NFS-HOWTO
HTTP 服务 | 在 web 服务器/客户端之间分享文件
HTTPS 服务 | 在有加密的安全套接层 (SSL) 或者安全传输层 (TLS) 的网络服务器/客户端中分享文件
FTP 服务 | 在 FTP 服务器/客户端之间分享文件

尽管对于文件分享来说，通过网络挂载文件系统和传输文件是相当方便的，但这可能是不安全的。它们的网络连接必须通过如下所示的加强安全性。

- 用 SSL/TLS 加密

- 建立 SSH 通道

- 建立 VPN 通道

- 网络之间需要有安全的防火墙

参见 第 6.5 节 “其它网络应用服务” 和 第 6.6 节 “其它网络应用客户端”。

## 10.2. 备份和恢复

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

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

### 10.2.1. 备份和恢复策略

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

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

   - 你自己创建的数据文件：在 "`~/`" 下的数据

   - 你使用的应用程序创建的数据文件：在 "`/var/`" 下的数据（除了 "`/var/cache/`"，"`/var/run/`" 和 "`/var/tmp/`")

   - 系统配置文件：在 "`/etc/`” 下的数据

   - 本地程序：在 "`/usr/local/`" 或 "`/opt/`" 下的数据

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

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

     - 用户进程的 Cron 工作，文件在 "`/var/spool/cron/crontabs`" 目录，并且重启 cron(8)。参见第 9.4.14 节 “定时任务安排”来获得关于 cron(8) 和 crontab(1) 的信息。

     - 用户进程的 Systemd 计时器工作：文件在 "`~/.config/systemd/user`" 目录。参见 systemd.timer(5) 和 systemd.service(5)。

     - 用户进程的自动启动工作：文件在 "`~/.config/autostart`" 目录。参见 Desktop Application Autostart Specification。

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

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

   - 经常备份：有计划的备份

   - 冗余备份：数据镜像

   - 傻瓜式操作：单个简单命令备份

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

   - 数据丢失的风险

     - 数据至少是应该在不同的磁盘分区上，最好是在不同的磁盘和机器上，来承受文件系统发生的损坏。重要数据最好存储在一个只读文件系统上。[4]

   - 数据非法访问的风险

     - 敏感的身份数据，比如 "`/etc/ssh/ssh_host_*_key`", "`~/.gnupg/*`", "`~/.ssh/*`", "`~/.local/share/keyrings/*`", "`/etc/passwd`", "`/etc/shadow`", "`popularity-contest.conf`", "`/etc/ppp/pap-secrets`", and "`/etc/exim4/passwd.client`" 应当使用加密备份。[5] (参见 第 9.9 节 “数据加密提示”。)

     - 即使在信任的系统上，也不能够硬编码系统登录密码或者加密密码到任何脚本里面。(参见 第 10.3.6 节 “密码密钥环”。)

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

     - 硬件（特别是硬盘）将会损坏

     - 文件系统可能会损坏，里面的数据可能被丢失

     - 对违规安全访问而言，远程存储系统不能够被信任

     - 弱的密码保护能够被轻松的破解

     - 文件权限系统可以被破解

   - 备份所需的资源：人力，硬件，软件，…

     - 使用 cron 任务或者 systemd 计时器任务来自动化调度备份工作

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

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

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

### 10.2.2. 实用备份套件

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

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

软件包 | 流行度 | 大小 | 说明
--- | --- | --- | ---
`bacula-common` | V:6.3, I:7.2 | 2501 | Bacula: 网络数据备份，恢复和核查-常见的支持文件
`bacula-client` | V:0.3, I:1.9 | 199 | Bacula: 网络数据备份，恢复和核查-客户端元软件包
`bacula-console` | V:0.7, I:2.1 | 112 | Bacula: 网络数据备份，恢复和核查-文本终端
`bacula-server` | I:0.60 | 199 | Bacula: 网络数据备份，恢复和核查-服务器端元软件包
`amanda-common` | V:0.6, I:2.2 | 9851 | Amanda: 马里兰大学开发的高级自动化网络磁盘归档器（库）
`amanda-client` | V:0.6, I:2.2 | 1099 | Amanda: 马里兰大学开发的高级自动化网络磁盘归档器（客户端）
`amanda-server` | V:0.11, I:0.23 | 1093 | Amanda: 马里兰大学开发的高级自动化网络磁盘归档器（服务器端）
`backuppc` | V:1.4, I:1.7 | 3088 | BackupPC 是用于备份 PC 机数据（基于磁盘）的高性能的企业级工具
`duplicity` | V:6, I:47 | 2670 | (远程) 增量备份
`deja-dup` | V:29, I:43 | 5274 | duplicity 的 GUI（图形用户界面）前端
`borgbackup` | V:13, I:28 | 3485 | (远程) 去重备份
`borgmatic` | V:3.2, I:4.4 | 946 | borgbackup 备份软件的辅助软件
`rdiff-backup` | V:2.3, I:6.6 | 1207 | (远程) 增量备份
`restic` | V:6, I:12 | 41560 | (远程) 增量备份
`backupninja` | V:2.2, I:2.6 | 360 | 轻量的可扩展的 meta-backup 系统
`slbackup` | V:0.11, I:0.13 | 147 | (远程) 增量备份
`backup-manager` | V:0.43, I:0.77 | 573 | 命令行备份工具
`backup2l` | V:0.38, I:0.53 | 110 | 用于可挂载媒介 (基于磁盘的) 的低维护的备份/恢复工具

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

- Mondo Rescue 是一个备份系统，它能够方便的从备份 CD/DVD 等设备中快速恢复整个系统，而不需要经过常规的系统安装过程。

- Bacula，Amanda 和 BackupPC 是全功能的备份实用套件，主要用于联网的定期备份。

- Duplicity 和 Borg 是简单的备份工具用于典型的工作站。

### 10.2.3. 备份技巧

对于一个个人工作站，为服务器环境设计的全功能备份套件工具也行不是最合适的。与此同时，已有的用于工作站的备份工具有一些不足。

这里有一些技巧让备份更加容易，只需用户做最小的工作。这些技巧可以同任意备份工具一起使用。

出于演示的目的，让我们假设基本用户和组名为 `penguin`，创建一个备份和快照脚本例子"`/usr/local/bin/bkss.sh`"：

    #!/bin/sh -e
    SRC="$1" # source data path
    DSTFS="$2" # backup destination filesystem path
    DSTSV="$3" # backup destination subvolume name
    DSTSS="${DSTFS}/${DSTSV}-snapshot" # snapshot destination path
    if [ "$(stat -f -c %T "$DSTFS")" != "btrfs" ]; then
      echo "E: $DESTFS needs to be formatted to btrfs" >&2 ; exit 1
    fi
    MSGID=$(notify-send -p "bkup.sh $DSTSV" "in progress ...")
    if [ ! -d "$DSTFS/$DSTSV" ]; then
      btrfs subvolume create "$DSTFS/$DSTSV"
      mkdir -p "$DSTSS"
    fi
    rsync -aHxS --delete --mkpath "${SRC}/" "${DSTFS}/${DSTSV}"
    btrfs subvolume snapshot -r "${DSTFS}/${DSTSV}" ${DSTSS}/$(date -u --iso=min)
    notify-send -r "$MSGID" "bkup.sh $DSTSV" "finished!"

这里，只使用基本工具 rsync(1)来帮助备份，存储空间使用 Btrfs 来高效利用。

| 提示
提示：这个作者在他的工作站上使用他自己的类似 shell 脚本"bss: Btrfs Subvolume Snapshot Utility"。

#### 10.2.3.1. GUI（图形用户界面）备份

这里是一个创建单击 GUI（图形用户界面）图标备份的例子。

- 准备一个 USB 存储设备用来备份。

  - 格式化 USB 存储设备为一个分区，使用 btrfs 文件系统，卷标名为"`BKUP`"。这个 U 盘也能够加密 (参见 第 9.9.1 节 “使用 dm-crypt/LUKS 加密移动磁盘”)。

  - 把这个插入你的系统。桌面系统将自动挂载它到 "`/media/penguin/BKUP`"。

  - 执行"`sudo chown penguin:penguin /media/penguin/BKUP`"，让它可以由用户写。

- 创建如下的"`~/.local/share/applications/BKUP.desktop`"文件，按照 第 9.4.10 节 “从 GUI 启动一个程序” 里写的技术：

      [Desktop Entry]
      Name=bkss
      Comment=Backup and snapshot of ~/Documents
      Exec=/usr/local/bin/bkss.sh /home/penguin/Documents /media/penguin/BKUP Documents
      Type=Application

对于每一次图标单击，你的数据从"`~/Documents`"备份到 USB 存储设备，并创建了一个只读快照。

#### 10.2.3.2. 挂载事件触发的备份

这里是一个由挂载事件触发的自动备份例子。

- 按 第 10.2.3.1 节 “GUI（图形用户界面）备份” 的方式准备一个 USB 存储设备用于备份。

- 创建一个如下的 systemd 服务单元文件"`~/.config/systemd/user/back-BKUP.service`"：

      [Unit]
      Description=USB Disk backup
      Requires=media-%u-BKUP.mount
      After=media-%u-BKUP.mount

      [Service]
      ExecStart=/usr/local/bin/bkss.sh %h/Documents /media/%u/BKUP Documents
      StandardOutput=append:%h/.cache/systemd-snap.log
      StandardError=append:%h/.cache/systemd-snap.log

      [Install]
      WantedBy=media-%u-BKUP.mount

- 用下面的方式启用这个 systemd 单元配置：

       $ systemctl --user enable bkup-BKUP.service

对于每一次挂载事件，你的数据从"`~/Documents`"备份到 USB 存储设备，并创建了一个只读快照。

这里，当前 systemd 在内存中的 systemd 挂载单元的名字，用户使用 "`systemctl --user list-units --type=mount`" 来调用服务管理器来查询。

#### 10.2.3.3. 时间事件触发的备份

这里是一个由时间事件触发的自动备份例子。

- 按 第 10.2.3.1 节 “GUI（图形用户界面）备份” 的方式准备一个 USB 存储设备用于备份。

- 创建一个如下的 systemd 时间单元文件"`~/.config/systemd/user/snap-Documents.timer`"：

      [Unit]
      Description=Run btrfs subvolume snapshot on timer
      Documentation=man:btrfs(1)

      [Timer]
      OnStartupSec=30
      OnUnitInactiveSec=900

      [Install]
      WantedBy=timers.target

- 创建一个如下的 systemd 服务单元文件"`~/.config/systemd/user/snap-Documents.service`"：

      [Unit]
      Description=Run btrfs subvolume snapshot
      Documentation=man:btrfs(1)

      [Service]
      Type=oneshot
      Nice=15
      ExecStart=/usr/local/bin/bkss.sh %h/Documents /media/%u/BKUP Documents
      IOSchedulingClass=idle
      CPUSchedulingPolicy=idle
      StandardOutput=append:%h/.cache/systemd-snap.log
      StandardError=append:%h/.cache/systemd-snap.log

- 用下面的方式启用这个 systemd 单元配置：

       $ systemctl --user enable snap-Documents.timer

对于每一次时间事件，你的数据从"`~/Documents`"备份到 USB 存储设备，并创建了一个只读快照。

这里，当前 systemd 在内存中的 systemd 用户时间单元的名字，使用 "`systemctl --user list-units --type=timer`" 来调用服务管理器来查询。

对于现在的桌面系统，这个 systemd 方案，比起传统的 Unix at(1)、cron(8) 或 `anacron` 方式，能够提供更精致的细粒度控制。

## 10.3. 数据安全基础

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

表 10.6. 数据安全基础工具列表

软件包 | 流行度 | 大小 | 命令 | 说明
--- | --- | --- | --- | ---
`gnupg` | V:398, I:867 | 464 | gpg(1) | GNU 隐私卫士 - OpenPGP 加密和签名工具
`gpgv` | V:234, I:939 | 555 | gpgv(1) | GNU 隐私卫士 - OpenPGP 签名验证工具
`sq` | V:1, I:27 | 23948 | sq(1) | Sequoia-PGP - OpenPGP 加密和签名工具
`sqv` | V:416, I:487 | 1702 | sqv(1) | Sequoia-PGP - OpenPGP 签名验证工具
`paperkey` | V:1, I:13 | 58 | paperkey(1) | 从 OpenPGP 私钥里面，仅仅导出私密信息
`cryptsetup` | V:35, I:78 | 462 | cryptsetup(8), … | dm-crypt 块设备加密支持 LUKS 工具
`coreutils` | V:911, I:1000 | 17994 | md5sum(1) | 计算与校验 MD5 讯息摘要
`coreutils` | V:911, I:1000 | 17994 | sha1sum(1) | 计算与校验 SHA1 讯息摘要
`openssl` | V:847, I:996 | 2532 | `openssl`(1ssl) | 使用 "`openssl dgst`" (OpenSSL)计算信息摘要
`libsecret-tools` | V:1, I:11 | 45 | secret-tool(1) | 存储和取回密码 (CLI)
`seahorse` | V:75, I:261 | 7971 | seahorse(1) | 密钥管理工具(GNOME)

参见 第 9.9 节 “数据加密提示” 的 dm-crypt 和 fscrypt，它们通过 Linux 内核模块实现了自动数据加密架构。

### 10.3.1. GnuPG 密钥管理

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

表 10.7. GNU 隐私卫士密钥管理命令的列表

命令 | 说明
--- | ---
`gpg --gen-key` | 生成一副新的密钥对
`gpg --gen-revoke my_user_ID` | 生成 my_user_ID 的一份吊销证书
`gpg --edit-key user_ID` | 交互式的编辑密钥，输入 "help" 来获得帮助信息
`gpg -o file --export` | 把所有的公共密钥输出到文件
`gpg -o file --export-secret-keys` | 把所有的私有密钥输出到文件
`gpg --import file` | 从文件导入密钥
`gpg --send-keys user_ID` | 发送 user_ID 的公钥到公钥服务器
`gpg --recv-keys user_ID` | 从公钥服务器下载 user_ID 的公钥
`gpg --list-keys user_ID` | 列出 user_ID 的所有密钥
`gpg --list-sigs user_ID` | 列出 user_ID 的签字
`gpg --check-sigs user_ID` | 检查 user_ID 密钥签字
`gpg --fingerprint user_ID` | 检查 user_ID 的指纹
`gpg --refresh-keys` | 更新本地密钥

信任码含义.

表 10.8. 信任码含义列表

代码 | 信任描述
--- | ---
`-` | 没有所有者信任签名/没有计算
`e` | 信任计算失败
`q` | 没有足够的信息用于计算
`n` | 从不信任这个键
`m` | 最低限度的信任
`f` | 完全信任
`u` | 最终信任

如下命令生成我的公钥 "`9563FC29932C409F1A77F9C1AB8A1522D8234C6A`" 。

    $ gpg --gen-key
    gpg (GnuPG) 2.4.7; Copyright (C) 2024 g10 Code GmbH
    ...
    GnuPG needs to construct a user ID to identify your key.

    Real name: Osamu Aoki
    Email address: osamu@debian.org
    You selected this USER-ID:
        "Osamu Aoki <osamu@debian.org>"

    Change (N)ame, (E)mail, or (O)kay/(Q)uit? o
    ...
    public and secret key created and signed.

    pub   ed25519 2026-02-14 [SC] [expires: 2029-02-13]
          9563FC29932C409F1A77F9C1AB8A1522D8234C6A
    uid                      Osamu Aoki <osamu@debian.org>
    sub   cv25519 2026-02-14 [E] [expires: 2029-02-13]

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

    $ gpg --keyserver hkp://keys.gnupg.net --send-keys 9563FC29932C409F1A77F9C1AB8A1522D8234C6A

默认良好的公钥服务器在 "`~/.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`" 选项.

使用SHA-1作为哈希算法已弃用。如果基于openPGP的RSA使用了SHA-1作为哈希算法，可以使用 FixKeyWithSha1来修复。

| 提示
sq(1) 和 sqv(1) 命令是 openPGP 命令的替代命令集合。参见 sq 用户文档 和 使用 sq 的实践介绍 ，Sequoia PGP 命令行.

### 10.3.2. 在文件上使用 GnuPG

这里有一些在文件上使用 GNU 隐私卫士 命令的例子。

表 10.9. 在文件上使用的 GNU 隐私卫士的命令列表

命令 | 说明
--- | ---
`gpg -a -s file` | ASCII 封装的签名文件 file.asc
`gpg --armor --sign file` | 同上
`gpg --clearsign file` | 生成明文签字信息
`gpg --clearsign file|mail foo@example.org` | 发送一份明文签字到 `foo@example.org`
`gpg --clearsign --not-dash-escaped patchfile` | 明文签名的补丁文件
`gpg --verify file` | 验证明文文件
`gpg -o file.sig -b file` | 生成一份分离的签字
`gpg -o file.sig --detach-sign file` | 同上
`gpg --verify file.sig file` | 使用 file.sig 验证文件
`gpg -o crypt_file.gpg -r name -e file` | 公钥加密，从文件里面获取名字，生成二进制的 crypt_file.gpg
`gpg -o crypt_file.gpg --recipient name --encrypt file` | 同上
`gpg -o crypt_file.asc -a -r name -e file` | 公钥加密，从文件中获取名字，生成 ASCII 封装的 crypt_file.asc
`gpg -o crypt_file.gpg -c file` | 将文件对称加密到 crypt_file.gpg
`gpg -o crypt_file.gpg --symmetric file` | 同上
`gpg -o crypt_file.asc -a -c file` | 对称加密，从文件到 ASCII 封装的 crypt_file.asc
`gpg -o file -d crypt_file.gpg -r name` | 解密
`gpg -o file --decrypt crypt_file.gpg` | 同上

### 10.3.3. 在 Mutt 中使用 GnuPG

增加下面内容到 "`~/.muttrc`"，在自动启动时，避免一个慢的 GnuPG，在索引菜单中按 "`S`" 来允许它使用。

    macro index S ":toggle pgp_verify_sig\n"
    set pgp_verify_sig=no

### 10.3.4. 在 Vim 中使用 GnuPG

`gnupg` 插件可以让你对扩展名为 "`.gpg`", "`.asc`", 和 "`.pgp`"的文件可靠的运行 GnuPG。[6]

    $ sudo aptitude install vim-scripts
    $ echo "packadd! gnupg" >> ~/.vim/vimrc

### 10.3.5. MD5 校验和

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.3.6. 密码密钥环

在 GNOME 系统，GUI（图形用户界面）工具 seahorse(1) 管理密码，安全的在密钥环 `~/.local/share/keyrings/*` 里面保存它们。

secret-tool(1) 能够从命令行存储密码到钥匙环。

让我们存储 LUKS/dm-crypt 加密磁盘镜像用到的密码

    $ secret-tool store --label='LUKS passphrase for disk.img' LUKS my_disk.img
    Password: ********

这个存储的密码能够被获取并给到其它程序，比如 cryptsetup(8)。

    $ secret-tool lookup LUKS my_disk.img | \
      cryptsetup open disk.img disk_img --type luks --keyring -
    $ sudo mount /dev/mapper/disk_img /mnt

| 提示
无论何时，你需要在一个脚本里面提供密码时，使用 `secret-tool` 来避免将密码直接硬编码到脚本里面。

## 10.4. 源代码合并工具

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

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

软件包 | 流行度 | 大小 | 命令 | 说明
--- | --- | --- | --- | ---
`patch` | V:103, I:726 | 242 | patch(1) | 给原文件打补丁
`vim` | V:85, I:347 | 4135 | vimdiff(1) | 在 vim 中并排比较两个文件
`imediff` | V:0.03, I:0.26 | 329 | imediff(1) | 全屏交互式两路/三路合并工具
`meld` | V:5, I:24 | 3992 | meld(1) | 比较和移植文件(GTK)
`wiggle` | V:0.01, I:0.12 | 175 | wiggle(1) | 应用被拒绝的补丁
`diffutils` | V:896, I:998 | 1768 | diff(1) | 逐行比较两个文件
`diffutils` | V:896, I:998 | 1768 | diff3(1) | 逐行比较和合并三个文件
`quilt` | V:2, I:17 | 880 | quilt(1) | 管理系列补丁
`wdiff` | V:6, I:40 | 651 | wdiff(1) | 在文本文件中，显示单词的不同
`diffstat` | V:9, I:98 | 79 | diffstat(1) | 通过 diff 生成一个改变柱状图
`patchutils` | V:11, I:97 | 269 | combinediff(1) | 从两个增量补丁创建一个积累补丁
`patchutils` | V:11, I:97 | 269 | dehtmldiff(1) | 从一个 HTML 页面提取出一个 diff
`patchutils` | V:11, I:97 | 269 | filterdiff(1) | 从一个 diff 文件里面提取或者排除 diff 文件
`patchutils` | V:11, I:97 | 269 | fixcvsdiff(1) | 修复由 CVS patch(1) 错误创建的 diff 文件
`patchutils` | V:11, I:97 | 269 | flipdiff(1) | 交换两个补丁的顺序
`patchutils` | V:11, I:97 | 269 | grepdiff(1) | 显示哪些文件是由匹配正则表达式的补丁修改
`patchutils` | V:11, I:97 | 269 | interdiff(1) | 显示在两个统一格式 diff 文件（基于同一个文件的两个不同 diff 文件）之间的差异
`patchutils` | V:11, I:97 | 269 | lsdiff(1) | 显示哪些文件由补丁修改
`patchutils` | V:11, I:97 | 269 | recountdiff(1) | 重新计算通用内容 diff 文件的数量和偏移
`patchutils` | V:11, I:97 | 269 | rediff(1) | 修复手工编辑 diff 文件的数量和偏移
`patchutils` | V:11, I:97 | 269 | splitdiff(1) | 隔离出增量补丁
`patchutils` | V:11, I:97 | 269 | unwrapdiff(1) | 识别已经被分词的补丁
`dirdiff` | V:0.1, I:1.4 | 167 | dirdiff(1) | 显示目录树之间的不同并移植改变
`docdiff` | V:0.03, I:0.25 | 554 | docdiff(1) | 逐词逐字地比较两个文件
`makepatch` | V:0.02, I:0.16 | 99 | makepatch(1) | 生成扩展补丁文件
`makepatch` | V:0.02, I:0.16 | 99 | applypatch(1) | 应用扩展补丁文件

### 10.4.1. 从源代码文件导出差异

下面的操作，导出两个源文件的不同，并根据文件的位置，创建通用 diff 文件"`file.patch0`" 或 "`file.patch1`".

    $ diff -u file.old file.new > file.patch0
    $ diff -u old/file new/file > file.patch1

### 10.4.2. 源代码文件移植更新

diff 文件（通常被叫作 patch 补丁文件），用于发送一个程序更新。通过下面的方式，接收到的部分，应用这个更新到其它文件。

    $ patch -p0 file < file.patch0
    $ patch -p1 file < file.patch1

### 10.4.3. 交互式移植

如果一个源代码，你有两个版本，你可以通过下面的方式，使用 imediff(1) 执行两方交互式移植。

    $ imediff -o file.merged file.old file.new

如果一个源代码，你有三个版本，你可以通过下面的方式，使用 imediff(1) 执行交互式三方移植。

    $ imediff -o file.merged file.yours file.base file.theirs

## 10.5. Git

Git 是这些天选择的用于 版本控制系统 version control system (VCS) 的工具，因为 Git 能够同时在本地和远程源代码管理上，做任何事情。

通过 Debian Salsa 服务，Debian 能够提供免费的 Git 服务。在 https://wiki.debian.org/Salsa 能找到它的说明文档。

下面是一些 Git 相关软件包。

表 10.11. git 相关包和命令列表

软件包 | 流行度 | 大小 | 命令 | 说明
--- | --- | --- | --- | ---
`git` | V:406, I:616 | 50972 | git(7) | Git 快速、可扩展、分布式的版本控制系统
`gitk` | V:4, I:27 | 2022 | gitk(1) | 有历史功能的 Git 图形仓库浏览器
`qgit` | V:0.2, I:2.0 | 1431 | dgit(1) | 有历史功能的 Git 图形仓库浏览器
`git-cola` | V:0.7, I:4.6 | 5005 | git-cola(1) | 有历史功能的 Git 图形仓库浏览器
`tig` | V:2, I:12 | 1243 | tig(1) | 浏览 Git 历史仓库的控制台
`lazygit` | V:0.9, I:3.6 | 24167 | lazygit(1) | 浏览 Git 历史仓库的控制台
`git-gui` | V:1, I:17 | 2525 | git-gui(1) | Git 图形界面（无历史功能）
`git-email` | V:0.4, I:9.8 | 1204 | git-send-email(1) | 从 Git 用电子邮件发送收集到的补丁
`git-buildpackage` | V:1.2, I:7.1 | 2027 | git-buildpackage(1) | 用 Git 自动制作 Debian 包
`dgit` | V:0.2, I:1.0 | 738 | dgit(1) | Debian 档案库的 git 交互操作
`imediff` | V:0.03, I:0.26 | 329 | git-ime(1) | 交互式的分开 git 提交的辅助工具
`stgit` | V:0.08, I:0.50 | 6181 | stg(1) | 封装的 git (Python)
`git-doc` | I:11 | 14896 | N/A | Git 官方文档
`gitmagic` | I:0.52 | 721 | N/A | "Git 魔术"，易于理解的 Git 手册

### 10.5.1. 配置 Git 客户端

你可以在 "`~/.gitconfig`" 里面设置几个 Git 接下来需要使用的全局配置，比如说你的名字和电子邮件地址。

    $ git config --global user.name "Name Surname"
    $ git config --global user.email yourname@example.com

你也可以按如下所示定制 Git 的默认行为。

    $ git config --global init.defaultBranch main
    $ git config --global pull.rebase true
    $ git config --global push.default current

如果你习惯使用 CVS 或 Subversion 命令，你也许希望设置如下几个命令别名。

    $ git config --global alias.ci "commit -a"
    $ git config --global alias.co checkout

你能够通过如下方式检查你的全局配置：

    $ git config --global --list

### 10.5.2. 基本的 Git 命令

Git 操作涉及几个数据。

- 工作树目录保持面向用户的文件，你可以对这些文件做修改。

  - 需要被记录的改变，必须明确的被选择并暂存到索引。这是 `git add` 和 `git rm` 命令。

- 索引保持暂存文件。

  - 在接下来的请求之前，暂存文件将被提交到本地仓库。这个是 `git commit` 命令。

- 本地仓库保持已经提交的的文件。

  - Git 记录提交数据的链接历史并在仓库里面将它们作为分支组织。

  - 本地仓库通过 `git push` 命令发送数据到远程仓库。

  - 本地仓库能够通过 `git fetch` 和 `git pull` 命令从远程仓库接收数据。

    - `git pull` 命令在 `git fetch` 后执行 `git merge` 或 `git rebase` 命令。

    - 这里，`git merge` 联合两个独立分支的历史结尾到一个点。（在没有定制的 `git pull` ，这个是默认的，同时对上游作者发布分支到许多人时，也是好的 ）

    - 这里，`git rebase` 创建一个远程分支的序列历史的单个分支，跟着本地分支。（这是定制 `pull.rebase true` 的情况，对我们其余的用途有用。）

- 远程仓库保持已经提交的文件。

  - 到远程仓库的通信，使用安全的通信协议，比如 SSH 或 HTTPS。

工作树是在 `.git/` 目录之外的文件。在 `.git/` 目录里面的文件，包括索引、本地仓库数据和一些 git 配置的文本文件。

这里是主要的 Git 命令概览。

表 10.12. 主要的 Git 命令

Git 命令 | 功能
--- | ---
`git init` | 创建(本地)存储库
`git clone URL` | 克隆远程存储库到本地仓库工作目录树
`git pull origin main` | 通过远程仓库 `origin` 更新本地 `main` 分支
`git add .` | 增加工作树里面的文件仅作为预先存在的索引文件
`git add -A .` | 增加工作树里面的所有文件到索引（包括已经删除的）
`git rm filename` | 从工作树和索引中删除文件
`git commit` | 提交在索引中的暂存改变到本地存储库
`git commit -a` | 添加工作树里的所有的改变到索引并提交它们到本地仓库（添加 + 提交）
`git push -u origin branch_name` | 使用本地 `branch_name` 分支更新远程仓库 `origin`（初始启用）
`git push origin branch_name` | 使用本地 `branch_name` 分支更新远程仓库 `origin`（随后调用）
`git diff treeish1 treeish2` | 显示 treeish1 提交和 treeish2 提交的不同
`gitk` | VCS 存储库分支历史树的图形界面显示

### 10.5.3. Git 技巧

下面是一些 Git 技巧。

表 10.13. Git 技巧

Git 命令行 | 功能
--- | ---
`gitk --all` | 参看完整的 Git 历史和操作，比如重置 HEAD 到另外一个提交、挑选补丁、创建标签和分支……
`git stash` | 得到一个干净的工作树，不会丢失数据
`git remote -v` | 检查远程设置
`git branch -vv` | 检查分支设置
`git status` | 显示工作树状态
`git config -l` | 列出 git 设置
`git reset --hard HEAD; git clean -x -d -f` | 反转所有工作树的改变并完全清理它们
`git rm --cached filename` | 反转由 `git add filename` 改变的暂存索引
`git reflog` | 获取参考日志（对从删除的分支中恢复提交有用）
`git branch new_branch_name HEAD@{6}` | 从 reflog 信息创建一个新的分支
`git remote add new_remote URL` | 增加一个由 URL 指向的远程仓库 `new_remote`
`git remote rename origin upstream` | 远程仓库的名字从 `origin` 重命名到 `upstream`
`git branch -u upstream/branch_name` | 设置远程跟踪到远程仓库 `upstream` 和它的分支名 `branch_name`。
`git remote set-url origin https://foo/bar.git` | 改变 `origin` 的 URL
`git remote set-url --push upstream DISABLED` | 禁止推送到 `upstream`（编辑 `.git/config` 来重新启用）
`git remote update upstream` | 获取 `upstream` 仓库中所有远程分支更新
`git fetch upstream foo:upstream-foo` | 创建本地（可能是孤立的）`upstream-foo` 分支，作为`upstream` 仓库中 `foo` 分支的一个拷贝
`git checkout -b topic_branch ; git push -u topic_branch origin` | 制作一个新的 `topic_branch` 并把它推送到 `origin`
`git branch -m oldname newname` | 本地分支改名
`git push -d origin branch_to_be_removed` | 删除远程分支（新的方式）
`git push origin :branch_to_be_removed` | 删除远程分支（老的方式）
`git checkout --orphan unconnected` | 创建一个新的 `unconnected` 分支
`git rebase -i origin/main` | 从 `origin/main` 重新排序、删除、压缩提交到一个干净的分支历史
`git reset HEAD^; git commit --amend` | 压缩最后两个提交为一个
`git checkout topic_branch ; git merge --squash topic_branch` | 压缩整个 `topic_branch` 到一个提交
`git fetch --unshallow --update-head-ok origin '+refs/heads/*:refs/heads/*'` | 反转一个浅克隆到一个所有分支的完整克隆
`git ime` | 分开最后的提交到一系列单个逐一文件的小提交。（要求 `imediff`）
`git repack -a -d; git prune` | 本地仓库重新打包到一个单独的包中（这可能限制从删除分支里面恢复丢失数据等机会）

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

| 小心
如果一个本地分支推送到一个已经变基或者压缩过的仓库，推送这样的分支有风险，并要求 `--force` 选项。这通常对 `main` 分支来说不可接受，但对于一个移植到 `main` 分支前的特定分支，是可以接受的。

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

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

### 10.5.4. Git 参考

参见下面内容。

- man 手册： git(1) (`/usr/share/doc/git-doc/git.html`)

- Git 用户手册 (`/usr/share/doc/git-doc/user-manual.html`)

- git 介绍教程 (`/usr/share/doc/git-doc/gittutorial.html`)

- git 介绍教程：第二部 (`/usr/share/doc/git-doc/gittutorial-2.html`)

- GIT 每一天 20个左右的命令 (`/usr/share/doc/git-doc/giteveryday.html`)

- Git 魔术 (`/usr/share/doc/gitmagic/html/index.html`)

### 10.5.5. 其它的版本控制系统

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

这里是 Debian 系统上著名的其它非 Git 的 VCS 汇总。

表 10.14. 其它版本控制系统工具列表

软件包 | 流行度 | 大小 | 工具 | VCS 类型 | 描述
--- | --- | --- | --- | --- | ---
`mercurial` | V:3, I:26 | 2575 | Mercurial | 分布式 | mercurial 主要是用 Python 写的还有一部分是 C 写的
`darcs` | V:0.2, I:3.3 | 38856 | Darcs | 分布式 | 有智能代数补丁的 DVCS（慢）
`tla` | V:0.04, I:0.72 | 1022 | GNU arch | 分布式 | 主要由 Tom Lord 写的 DVCS (成为历史的)
`bzr` | V:0.1, I:4.3 | 28 | GNU Bazaar | 分布式 | 受 `tla` 启发并且是用 Python 写的 DVCS (历史)
`subversion` | V:10, I:57 | 4849 | Subversion | 远程 | ”比 CVS 做的好“，远程 VCS 的新标准（历史）
`cvs` | V:3, I:25 | 4835 | CVS | 远程 | 以前的远程 VCS 标准（历史）
`tkcvs` | V:0.12, I:0.88 | 34 | CVS, … | 远程 | VCS (CVS,Subversion,RCS) 存储库树的图形界面显示
`rcs` | V:1.7, I:9.5 | 578 | RCS | 本地 | "比 Unix SCCS 做的好"（历史）
`cssc` | V:0.01, I:0.35 | 2044 | CSSC | 本地 | Unix SCCS 的克隆（历史）

[4] 一个只能写一次的媒介，例如 CD/DVD-R， 能防止覆盖事故。（参见 第 9.8 节 “二进制数据” 怎样在 shell 命令行写入存储媒介。GNOME 桌面图形环境可以让你轻松的通过菜单：“位置 → CD/DVD 刻录”来实现写入操作。）

[5] 这些数据中的一些，不能够通过在系统里面输入同样的字符串来重新生成。

[6] 如果你使用 "`~/.vimrc`" 代替 "`~/.vim/vimrc`"，请进行相应的取代。

第 9 章 系统技巧 |  | 第 11 章 数据转换