Pro Git 中文版

Size: px
Start display at page:

Download "Pro Git 中文版"

Transcription

1 Pro Git 中 文 版 Sco Chacon * Liam Huang * This is the PDF file for the Pro Git book contents. It is licensed under the Creative Commons A ribution-non Commercial-Share Alike 3.0 license. I hope you enjoy it, I hope it helps you learn Git, and I hope you ll support Apress and me by purchasing a print copy of the book at Amazon: L A T E X 校 订, 个 人 信 息 :h p://liam0205.me/about/

2

3 目 录 1 起 步 关 于 版 本 控 制 本 地 版 本 控 制 系 统 集 中 化 的 版 本 控 制 系 统 分 布 式 版 本 控 制 系 统 Git 简 史 Git 基 础 直 接 记 录 快 照, 而 非 差 异 比 较 近 乎 所 有 操 作 都 是 本 地 执 行 时 刻 保 持 数 据 完 整 性 多 数 操 作 仅 添 加 数 据 文 件 的 三 种 状 态 安 装 Git 从 源 代 码 安 装 在 Linux 上 安 装 在 Mac 上 安 装 在 Windows 上 安 装 初 次 运 行 Git 前 的 配 置 用 户 信 息 文 本 编 辑 器 差 异 分 析 工 具 查 看 配 置 信 息 获 取 帮 助 小 结 Git 基 础 取 得 项 目 的 Git 仓 库 在 工 作 目 录 中 初 始 化 新 仓 库 从 现 有 仓 库 克 隆 记 录 每 次 更 新 到 仓 库 检 查 当 前 文 件 状 态 跟 踪 新 文 件 iii

4 2.2.3 暂 存 已 修 改 文 件 忽 略 某 些 文 件 查 看 已 暂 存 和 未 暂 存 的 更 新 提 交 更 新 跳 过 使 用 暂 存 区 域 移 除 文 件 移 动 文 件 查 看 提 交 历 史 限 制 输 出 长 度 使 用 图 形 化 工 具 查 阅 提 交 历 史 撤 消 操 作 修 改 最 后 一 次 提 交 取 消 已 经 暂 存 的 文 件 取 消 对 文 件 的 修 改 远 程 仓 库 的 使 用 查 看 当 前 的 远 程 库 添 加 远 程 仓 库 从 远 程 仓 库 抓 取 数 据 推 送 数 据 到 远 程 仓 库 查 看 远 程 仓 库 信 息 远 程 仓 库 的 删 除 和 重 命 名 打 标 签 列 显 已 有 的 标 签 新 建 标 签 含 附 注 的 标 签 签 署 标 签 轻 量 级 标 签 验 证 标 签 后 期 加 注 标 签 分 享 标 签 技 巧 和 窍 门 自 动 补 全 Git 命 令 别 名 小 结 Git 分 支 何 谓 分 支 分 支 的 新 建 与 合 并 分 支 的 新 建 与 切 换 分 支 的 合 并 iv

5 3.2.3 遇 到 冲 突 时 的 分 支 合 并 分 支 的 管 理 利 用 分 支 进 行 开 发 的 工 作 流 程 长 期 分 支 特 性 分 支 远 程 分 支 推 送 本 地 分 支 跟 踪 远 程 分 支 删 除 远 程 分 支 分 支 的 衍 合 基 本 的 衍 合 操 作 有 趣 的 衍 合 衍 合 的 风 险 小 结 服 务 器 上 的 Git 协 议 本 地 协 议 优 点 缺 点 SSH 协 议 优 点 缺 点 Git 协 议 优 点 缺 点 HTTP/S 协 议 优 点 缺 点 在 服 务 器 上 部 署 Git 把 裸 仓 库 移 到 服 务 器 上 小 型 安 装 SSH 连 接 生 成 SSH 公 钥 架 设 服 务 器 公 共 访 问 GitWeb Gitosis Gitolite 安 装 v

6 4.8.2 定 制 安 装 配 置 文 件 和 访 问 规 则 带 拒 绝 的 高 级 访 问 控 制 通 过 改 变 文 件 限 制 push 个 人 分 支 通 配 符 仓 库 其 他 功 能 Git 守 护 进 程 Git 托 管 服 务 GitHub 建 立 新 账 户 建 立 新 仓 库 从 Subversion 导 入 项 目 添 加 协 作 开 发 者 项 目 页 面 派 生 项 目 GitHub 小 结 小 结 分 布 式 Git 分 布 式 工 作 流 程 集 中 式 工 作 流 集 成 管 理 员 工 作 流 司 令 官 与 副 官 工 作 流 为 项 目 作 贡 献 提 交 指 南 私 有 的 小 型 团 队 私 有 团 队 间 协 作 公 开 的 小 型 项 目 公 开 的 大 型 项 目 小 结 项 目 的 管 理 使 用 特 性 分 支 进 行 工 作 采 纳 来 自 邮 件 的 补 丁 使 用 apply 命 令 应 用 补 丁 使 用 am 命 令 应 用 补 丁 检 出 远 程 分 支 决 断 代 码 取 舍 代 码 集 成 合 并 流 程 vi

7 大 项 目 的 合 并 流 程 衍 合 与 挑 拣 (cherry-pick) 的 流 程 给 发 行 版 签 名 生 成 内 部 版 本 号 准 备 发 布 制 作 简 报 小 结 Git 工 具 修 订 版 本 (Revision) 选 择 单 个 修 订 版 本 简 短 的 SHA 关 于 SHA-1 的 简 短 说 明 分 支 引 用 引 用 日 志 里 的 简 称 祖 先 引 用 提 交 范 围 双 点 多 点 三 点 交 互 式 暂 存 暂 存 和 撤 回 文 件 暂 存 补 丁 储 藏 (Stashing) 储 藏 你 的 工 作 取 消 储 藏 (Un-applying a Stash) 从 储 藏 中 创 建 分 支 重 写 历 史 改 变 最 近 一 次 提 交 修 改 多 个 提 交 说 明 重 排 提 交 压 制 (Squashing) 提 交 拆 分 提 交 核 弹 级 选 项 : filter-branch 从 所 有 提 交 中 删 除 一 个 文 件 将 一 个 子 目 录 设 置 为 新 的 根 目 录 全 局 性 地 更 换 电 子 邮 件 地 址 使 用 Git 调 试 文 件 标 注 二 分 查 找 vii

8 6.6 子 模 块 子 模 块 初 步 克 隆 一 个 带 子 模 块 的 项 目 上 层 项 目 子 模 块 的 问 题 子 树 合 并 总 结 自 定 义 Git 配 置 Git 客 户 端 基 本 配 置 core.editor commit.template core.pager user.signingkey core.excludesfile help.autocorrect Git 中 的 着 色 color.ui color.* 外 部 的 合 并 与 比 较 工 具 格 式 化 与 空 白 core.autocrlf core.whitespace 服 务 器 端 配 置 receive.fsckobjects receive.denynonfastforwards receive.denydeletes Git 属 性 二 进 制 文 件 识 别 二 进 制 文 件 比 较 二 进 制 文 件 MS Word files OpenDocument Text files Image files 关 键 字 扩 展 导 出 仓 库 export-ignore export-subst 合 并 策 略 viii

9 7.3 Git 挂 钩 安 装 一 个 挂 钩 客 户 端 挂 钩 提 交 工 作 流 挂 钩 工 作 流 挂 钩 其 他 客 户 端 挂 钩 服 务 器 端 挂 钩 pre-receive 和 post-receive update Git 强 制 策 略 实 例 服 务 端 挂 钩 指 定 特 殊 的 提 交 信 息 格 式 实 现 基 于 用 户 的 访 问 权 限 控 制 列 表 (ACL) 系 统 只 允 许 Fast-Forward 类 型 的 推 送 客 户 端 挂 钩 总 结 Git 与 其 他 系 统 Git 与 Subversion git svn 初 始 设 定 入 门 提 交 到 Subversion 拉 取 最 新 进 展 Git 分 支 问 题 Subversion 分 支 创 建 新 的 SVN 分 支 切 换 当 前 分 支 对 应 Subversion 的 命 令 SVN 风 格 的 历 史 SVN 日 志 SVN 服 务 器 信 息 略 Subversion 之 所 略 Git-Svn 总 结 迁 移 到 Git 导 入 Subversion Perforce 自 定 导 入 脚 本 总 结 ix

10 9 Git 内 部 原 理 底 层 命 令 (Plumbing) 和 高 层 命 令 (Porcelain) Git 对 象 tree ( 树 ) 对 象 commit ( 提 交 ) 对 象 对 象 存 储 Git References HEAD 标 记 Tags Remotes Packfiles The Refspec 推 送 Refspec 删 除 引 用 传 输 协 议 哑 协 议 智 能 协 议 上 传 数 据 下 载 数 据 维 护 及 数 据 恢 复 维 护 数 据 恢 复 移 除 对 象 总 结 x

11 第 1 章 起 步 本 章 介 绍 开 始 使 用 Git 前 的 相 关 知 识 我 们 会 先 了 解 一 些 版 本 控 制 工 具 的 历 史 背 景, 然 后 试 着 让 Git 在 你 的 系 统 上 跑 起 来, 直 到 最 后 配 置 好, 可 以 正 常 开 始 开 发 工 作 读 完 本 章, 你 就 会 明 白 为 什 么 Git 会 如 此 流 行, 为 什 么 你 应 该 立 即 开 始 使 用 它 1.1 关 于 版 本 控 制 什 么 是 版 本 控 制? 我 为 什 么 要 关 心 它 呢? 版 本 控 制 是 一 种 记 录 一 个 或 若 干 文 件 内 容 变 化, 以 便 将 来 查 阅 特 定 版 本 修 订 情 况 的 系 统 在 本 书 所 展 示 的 例 子 中, 我 们 仅 对 保 存 着 软 件 源 代 码 的 文 本 文 件 作 版 本 控 制 管 理, 但 实 际 上, 你 可 以 对 任 何 类 型 的 文 件 进 行 版 本 控 制 如 果 你 是 位 图 形 或 网 页 设 计 师, 可 能 会 需 要 保 存 某 一 幅 图 片 或 页 面 布 局 文 件 的 所 有 修 订 版 本 ( 这 或 许 是 你 非 常 渴 望 拥 有 的 功 能 ) 采 用 版 本 控 制 系 统 (VCS) 是 个 明 智 的 选 择 有 了 它 你 就 可 以 将 某 个 文 件 回 溯 到 之 前 的 状 态, 甚 至 将 整 个 项 目 都 回 退 到 过 去 某 个 时 间 点 的 状 态 你 可 以 比 较 文 件 的 变 化 细 节, 查 出 最 后 是 谁 修 改 了 哪 个 地 方, 从 而 找 出 导 致 怪 异 问 题 出 现 的 原 因, 又 是 谁 在 何 时 报 告 了 某 个 功 能 缺 陷 等 等 使 用 版 本 控 制 系 统 通 常 还 意 味 着, 就 算 你 乱 来 一 气 把 整 个 项 目 中 的 文 件 改 的 改 删 的 删, 你 也 照 样 可 以 轻 松 恢 复 到 原 先 的 样 子 但 额 外 增 加 的 工 作 量 却 微 乎 其 微 本 地 版 本 控 制 系 统 许 多 人 习 惯 用 复 制 整 个 项 目 目 录 的 方 式 来 保 存 不 同 的 版 本, 或 许 还 会 改 名 加 上 备 份 时 间 以 示 区 别 这 么 做 唯 一 的 好 处 就 是 简 单 不 过 坏 处 也 不 少 : 有 时 候 会 混 淆 所 在 的 工 作 目 录, 一 旦 弄 错 文 件 丢 了 数 据 就 没 法 撤 销 恢 复 为 了 解 决 这 个 问 题, 人 们 很 久 以 前 就 开 发 了 许 多 种 本 地 版 本 控 制 系 统, 大 多 都 是 采 用 某 种 简 单 的 数 据 库 来 记 录 文 件 的 历 次 更 新 差 异 ( 见 图 1-1) 其 中 最 流 行 的 一 种 叫 做 rcs, 现 今 许 多 计 算 机 系 统 上 都 还 看 得 到 它 的 踪 影 甚 至 在 流 行 的 Mac OS X 系 统 上 安 装 了 开 发 者 工 具 包 之 后, 也 可 以 使 用 rcs 命 令 它 的 工 作 原 理 基 本 上 就 是 保 存 并 管 理 文 件 补 丁 (patch) 文 件 补 丁 是 一 种 特 定 格 式 的 文 本 文 件, 记 录 着 对 应 文 件 修 订 前 后 的 内 容 变 化 所 以, 根 据 每 次 修 订 后 的 补 丁,rcs 可 1

12 第 1 章 起 步 Sco Chacon Pro Git 中 文 版 图 1.1: 本 地 版 本 控 制 系 统 以 通 过 不 断 打 补 丁, 计 算 出 各 个 版 本 的 文 件 内 容 集 中 化 的 版 本 控 制 系 统 接 下 来 人 们 又 遇 到 一 个 问 题, 如 何 让 在 不 同 系 统 上 的 开 发 者 协 同 工 作? 于 是, 集 中 化 的 版 本 控 制 系 统 (Centralized Version Control Systems, 简 称 CVCS ) 应 运 而 生 这 类 系 统, 诸 如 CVS,Subversion 以 及 Perforce 等, 都 有 一 个 单 一 的 集 中 管 理 的 服 务 器, 保 存 所 有 文 件 的 修 订 版 本, 而 协 同 工 作 的 人 们 都 通 过 客 户 端 连 到 这 台 服 务 器, 取 出 最 新 的 文 件 或 者 提 交 更 新 多 年 以 来, 这 已 成 为 版 本 控 制 系 统 的 标 准 做 法 ( 见 图 1-2) 图 1.2: 集 中 化 的 版 本 控 制 系 统 这 种 做 法 带 来 了 许 多 好 处, 特 别 是 相 较 于 老 式 的 本 地 VCS 来 说 现 在, 每 个 人 都 可 以 在 一 定 程 度 上 看 到 项 目 中 的 其 他 人 正 在 做 些 什 么 而 管 理 员 也 可 以 轻 松 掌 控 每 个 开 发 者 的 权 限, 并 且 管 理 一 个 CVCS 要 远 比 在 各 个 客 户 端 上 维 护 本 地 数 据 库 来 得 轻 松 容 易 事 分 两 面, 有 好 有 坏 这 么 做 最 显 而 易 见 的 缺 点 是 中 央 服 务 器 的 单 点 故 障 如 2

13 Sco Chacon Pro Git 中 文 版 1.1 节 关 于 版 本 控 制 果 宕 机 一 小 时, 那 么 在 这 一 小 时 内, 谁 都 无 法 提 交 更 新, 也 就 无 法 协 同 工 作 要 是 中 央 服 务 器 的 磁 盘 发 生 故 障, 碰 巧 没 做 备 份, 或 者 备 份 不 够 及 时, 就 会 有 丢 失 数 据 的 风 险 最 坏 的 情 况 是 彻 底 丢 失 整 个 项 目 的 所 有 历 史 更 改 记 录, 而 被 客 户 端 偶 然 提 取 出 来 的 保 存 在 本 地 的 某 些 快 照 数 据 就 成 了 恢 复 数 据 的 希 望 但 这 样 的 话 依 然 是 个 问 题, 你 不 能 保 证 所 有 的 数 据 都 已 经 有 人 事 先 完 整 提 取 出 来 过 本 地 版 本 控 制 系 统 也 存 在 类 似 问 题, 只 要 整 个 项 目 的 历 史 记 录 被 保 存 在 单 一 位 置, 就 有 丢 失 所 有 历 史 更 新 记 录 的 风 险 分 布 式 版 本 控 制 系 统 于 是 分 布 式 版 本 控 制 系 统 (Distributed Version Control System, 简 称 DVCS ) 面 世 了 在 这 类 系 统 中, 像 Git,Mercurial,Bazaar 以 及 Darcs 等, 客 户 端 并 不 只 提 取 最 新 版 本 的 文 件 快 照, 而 是 把 代 码 仓 库 完 整 地 镜 像 下 来 这 么 一 来, 任 何 一 处 协 同 工 作 用 的 服 务 器 发 生 故 障, 事 后 都 可 以 用 任 何 一 个 镜 像 出 来 的 本 地 仓 库 恢 复 因 为 每 一 次 的 提 取 操 作, 实 际 上 都 是 一 次 对 代 码 仓 库 的 完 整 备 份 ( 见 图 1-3) 图 1.3: 分 布 式 版 本 控 制 系 统 更 进 一 步, 许 多 这 类 系 统 都 可 以 指 定 和 若 干 不 同 的 远 端 代 码 仓 库 进 行 交 互 籍 此, 你 就 可 以 在 同 一 个 项 目 中, 分 别 和 不 同 工 作 小 组 的 人 相 互 协 作 你 可 以 根 据 需 要 设 定 不 同 的 协 作 流 程, 比 如 层 次 模 型 式 的 工 作 流, 而 这 在 以 前 的 集 中 式 系 统 中 是 无 法 实 现 的 3

14 第 1 章 起 步 Sco Chacon Pro Git 中 文 版 1.2 Git 简 史 同 生 活 中 的 许 多 伟 大 事 件 一 样,Git 诞 生 于 一 个 极 富 纷 争 大 举 创 新 的 年 代 Linux 内 核 开 源 项 目 有 着 为 数 众 广 的 参 与 者 绝 大 多 数 的 Linux 内 核 维 护 工 作 都 花 在 了 提 交 补 丁 和 保 存 归 档 的 繁 琐 事 务 上 ( 年 间 ) 到 2002 年, 整 个 项 目 组 开 始 启 用 分 布 式 版 本 控 制 系 统 BitKeeper 来 管 理 和 维 护 代 码 到 了 2005 年, 开 发 BitKeeper 的 商 业 公 司 同 Linux 内 核 开 源 社 区 的 合 作 关 系 结 束, 他 们 收 回 了 免 费 使 用 BitKeeper 的 权 力 这 就 迫 使 Linux 开 源 社 区 ( 特 别 是 Linux 的 缔 造 者 Linus Torvalds ) 不 得 不 吸 取 教 训, 只 有 开 发 一 套 属 于 自 己 的 版 本 控 制 系 统 才 不 至 于 重 蹈 覆 辙 他 们 对 新 的 系 统 制 订 了 若 干 目 标 : 速 度 简 单 的 设 计 对 非 线 性 开 发 模 式 的 强 力 支 持 ( 允 许 上 千 个 并 行 开 发 的 分 支 ) 完 全 分 布 式 有 能 力 高 效 管 理 类 似 Linux 内 核 一 样 的 超 大 规 模 项 目 ( 速 度 和 数 据 量 ) 自 诞 生 于 2005 年 以 来,Git 日 臻 成 熟 完 善, 在 高 度 易 用 的 同 时, 仍 然 保 留 着 初 期 设 定 的 目 标 它 的 速 度 飞 快, 极 其 适 合 管 理 大 项 目, 它 还 有 着 令 人 难 以 置 信 的 非 线 性 分 支 管 理 系 统 ( 见 第 三 章 ), 可 以 应 付 各 种 复 杂 的 项 目 开 发 需 求 1.3 Git 基 础 那 么, 简 单 地 说,Git 究 竟 是 怎 样 的 一 个 系 统 呢? 请 注 意, 接 下 来 的 内 容 非 常 重 要, 若 是 理 解 了 Git 的 思 想 和 基 本 工 作 原 理, 用 起 来 就 会 知 其 所 以 然, 游 刃 有 余 在 开 始 学 习 Git 的 时 候, 请 不 要 尝 试 把 各 种 概 念 和 其 他 版 本 控 制 系 统 ( 诸 如 Subversion 和 Perforce 等 ) 相 比 拟, 否 则 容 易 混 淆 每 个 操 作 的 实 际 意 义 Git 在 保 存 和 处 理 各 种 信 息 的 时 候, 虽 然 操 作 起 来 的 命 令 形 式 非 常 相 近, 但 它 与 其 他 版 本 控 制 系 统 的 做 法 颇 为 不 同 理 解 这 些 差 异 将 有 助 于 你 准 确 地 使 用 Git 提 供 的 各 种 工 具 直 接 记 录 快 照, 而 非 差 异 比 较 Git 和 其 他 版 本 控 制 系 统 的 主 要 差 别 在 于,Git 只 关 心 文 件 数 据 的 整 体 是 否 发 生 变 化, 而 大 多 数 其 他 系 统 则 只 关 心 文 件 内 容 的 具 体 差 异 这 类 系 统 (CVS, Subversion,Perforce,Bazaar 等 等 ) 每 次 记 录 有 哪 些 文 件 作 了 更 新, 以 及 都 更 新 了 哪 些 行 的 什 么 内 容, 请 看 图 1-4 Git 并 不 保 存 这 些 前 后 变 化 的 差 异 数 据 实 际 上,Git 更 像 是 把 变 化 的 文 件 作 快 照 后, 记 录 在 一 个 微 型 的 文 件 系 统 中 每 次 提 交 更 新 时, 它 会 纵 览 一 遍 所 有 文 件 的 指 纹 信 息 并 对 文 件 作 一 快 照, 然 后 保 存 一 个 指 向 这 次 快 照 的 索 引 为 提 高 性 能, 若 文 件 没 有 变 化,Git 不 会 再 次 保 存, 而 只 对 上 次 保 存 的 快 照 作 一 链 接 Git 的 工 作 方 式 就 像 图 1-5 所 示 4

15 Sco Chacon Pro Git 中 文 版 1.3 节 Git 基 础 图 1.4: 其 他 系 统 在 每 个 版 本 中 记 录 着 各 个 文 件 的 具 体 差 异 图 1.5: Git 保 存 每 次 更 新 时 的 文 件 快 照 这 是 Git 同 其 他 系 统 的 重 要 区 别 它 完 全 颠 覆 了 传 统 版 本 控 制 的 套 路, 并 对 各 个 环 节 的 实 现 方 式 作 了 新 的 设 计 Git 更 像 是 个 小 型 的 文 件 系 统, 但 它 同 时 还 提 供 了 许 多 以 此 为 基 础 的 超 强 工 具, 而 不 只 是 一 个 简 单 的 VCS 稍 后 在 第 三 章 讨 论 Git 分 支 管 理 的 时 候, 我 们 会 再 看 看 这 样 的 设 计 究 竟 会 带 来 哪 些 好 处 近 乎 所 有 操 作 都 是 本 地 执 行 在 Git 中 的 绝 大 多 数 操 作 都 只 需 要 访 问 本 地 文 件 和 资 源, 不 用 连 网 但 如 果 用 CVCS 的 话, 差 不 多 所 有 操 作 都 需 要 连 接 网 络 因 为 Git 在 本 地 磁 盘 上 就 保 存 着 所 有 当 前 项 目 的 历 史 更 新, 所 以 处 理 起 来 速 度 飞 快 举 个 例 子, 如 果 要 浏 览 项 目 的 历 史 更 新 摘 要,Git 不 用 跑 到 外 面 的 服 务 器 上 去 取 数 据 回 来, 而 直 接 从 本 地 数 据 库 读 取 后 展 示 给 你 看 所 以 任 何 时 候 你 都 可 以 马 上 翻 阅, 无 需 等 待 如 果 想 要 看 当 前 版 本 的 文 件 和 一 个 月 前 的 版 本 之 间 有 何 差 异,Git 会 取 出 一 个 月 前 的 快 照 和 当 前 文 件 作 一 次 差 异 运 算, 而 不 用 请 求 远 程 服 务 器 来 做 这 件 事, 或 是 把 老 版 本 的 文 件 拉 到 本 地 来 作 比 较 用 CVCS 的 话, 没 有 网 络 或 者 断 开 VPN 你 就 无 法 做 任 何 事 情 但 用 Git 的 话, 就 算 你 在 飞 机 或 者 火 车 上, 都 可 以 非 常 愉 快 地 频 繁 提 交 更 新, 等 到 了 有 网 络 的 时 候 再 上 传 到 远 程 仓 库 同 样, 在 回 家 的 路 上, 不 用 连 接 VPN 你 也 可 以 继 续 工 作 换 作 其 他 版 本 控 制 系 统, 这 么 做 几 乎 不 可 能, 抑 或 非 常 麻 烦 比 如 Perforce, 如 果 不 连 到 服 务 器, 几 乎 什 么 都 做 不 了 ( 译 注 : 默 认 无 法 发 出 命 令 p4 edit file 开 始 编 辑 文 件, 因 为 Perforce 需 要 联 网 通 知 系 统 声 明 该 文 件 正 在 被 谁 修 订 但 实 际 上 手 工 修 改 文 件 权 限 可 以 绕 过 这 个 限 制, 只 是 完 成 后 还 是 无 法 提 交 更 新 ); 如 果 是 Subversion 或 CVS, 虽 然 可 以 编 辑 文 件, 但 无 法 提 交 更 新, 因 为 数 据 库 在 网 络 上 看 上 去 好 像 这 些 都 不 是 什 么 大 问 题, 但 实 际 体 验 过 之 后, 你 就 会 惊 喜 地 发 现, 这 其 实 是 会 带 来 5

16 第 1 章 起 步 Sco Chacon Pro Git 中 文 版 很 大 不 同 的 时 刻 保 持 数 据 完 整 性 在 保 存 到 Git 之 前, 所 有 数 据 都 要 进 行 内 容 的 校 验 和 (checksum) 计 算, 并 将 此 结 果 作 为 数 据 的 唯 一 标 识 和 索 引 换 句 话 说, 不 可 能 在 你 修 改 了 文 件 或 目 录 之 后, Git 一 无 所 知 这 项 特 性 作 为 Git 的 设 计 哲 学, 建 在 整 体 架 构 的 最 底 层 所 以 如 果 文 件 在 传 输 时 变 得 不 完 整, 或 者 磁 盘 损 坏 导 致 文 件 数 据 缺 失,Git 都 能 立 即 察 觉 Git 使 用 SHA-1 算 法 计 算 数 据 的 校 验 和, 通 过 对 文 件 的 内 容 或 目 录 的 结 构 计 算 出 一 个 SHA-1 哈 希 值, 作 为 指 纹 字 符 串 该 字 串 由 40 个 十 六 进 制 字 符 (0-9 及 a-f) 组 成, 看 起 来 就 像 是 : 24b9da aa493b52f8696cd6d3b00373 Git 的 工 作 完 全 依 赖 于 这 类 指 纹 字 串, 所 以 你 会 经 常 看 到 这 样 的 哈 希 值 实 际 上, 所 有 保 存 在 Git 数 据 库 中 的 东 西 都 是 用 此 哈 希 值 来 作 索 引 的, 而 不 是 靠 文 件 名 多 数 操 作 仅 添 加 数 据 常 用 的 Git 操 作 大 多 仅 仅 是 把 数 据 添 加 到 数 据 库 因 为 任 何 一 种 不 可 逆 的 操 作, 比 如 删 除 数 据, 都 会 使 回 退 或 重 现 历 史 版 本 变 得 困 难 重 重 在 别 的 VCS 中, 若 还 未 提 交 更 新, 就 有 可 能 丢 失 或 者 混 淆 一 些 修 改 的 内 容, 但 在 Git 里, 一 旦 提 交 快 照 之 后 就 完 全 不 用 担 心 丢 失 数 据, 特 别 是 养 成 定 期 推 送 到 其 他 仓 库 的 习 惯 的 话 这 种 高 可 靠 性 令 我 们 的 开 发 工 作 安 心 不 少, 尽 管 去 做 各 种 试 验 性 的 尝 试 好 了, 再 怎 样 也 不 会 弄 丢 数 据 至 于 Git 内 部 究 竟 是 如 何 保 存 和 恢 复 数 据 的, 我 们 会 在 第 九 章 讨 论 Git 内 部 原 理 时 再 作 详 述 文 件 的 三 种 状 态 好, 现 在 请 注 意, 接 下 来 要 讲 的 概 念 非 常 重 要 对 于 任 何 一 个 文 件, 在 Git 内 都 只 有 三 种 状 态 : 已 提 交 (commi ed), 已 修 改 (modified) 和 已 暂 存 (staged) 已 提 交 表 示 该 文 件 已 经 被 安 全 地 保 存 在 本 地 数 据 库 中 了 ; 已 修 改 表 示 修 改 了 某 个 文 件, 但 还 没 有 提 交 保 存 ; 已 暂 存 表 示 把 已 修 改 的 文 件 放 在 下 次 提 交 时 要 保 存 的 清 单 中 由 此 我 们 看 到 Git 管 理 项 目 时, 文 件 流 转 的 三 个 工 作 区 域 :Git 的 工 作 目 录, 暂 存 区 域, 以 及 本 地 仓 库 每 个 项 目 都 有 一 个 Git 目 录 ( 译 注 : 如 果 git clone 出 来 的 话, 就 是 其 中.git 的 目 录 ; 如 果 git clone --bare 的 话, 新 建 的 目 录 本 身 就 是 Git 目 录 ), 它 是 Git 用 来 保 存 元 数 据 和 对 象 数 据 库 的 地 方 该 目 录 非 常 重 要, 每 次 克 隆 镜 像 仓 库 的 时 候, 实 际 拷 贝 的 就 是 这 个 目 录 里 面 的 数 据 6

17 Sco Chacon Pro Git 中 文 版 1.4 节 安 装 Git 图 1.6: 工 作 目 录, 暂 存 区 域, 以 及 本 地 仓 库 从 项 目 中 取 出 某 个 版 本 的 所 有 文 件 和 目 录, 用 以 开 始 后 续 工 作 的 叫 做 工 作 目 录 这 些 文 件 实 际 上 都 是 从 Git 目 录 中 的 压 缩 对 象 数 据 库 中 提 取 出 来 的, 接 下 来 就 可 以 在 工 作 目 录 中 对 这 些 文 件 进 行 编 辑 所 谓 的 暂 存 区 域 只 不 过 是 个 简 单 的 文 件, 一 般 都 放 在 Git 目 录 中 有 时 候 人 们 会 把 这 个 文 件 叫 做 索 引 文 件, 不 过 标 准 说 法 还 是 叫 暂 存 区 域 基 本 的 Git 工 作 流 程 如 下 : 1. 在 工 作 目 录 中 修 改 某 些 文 件 2. 对 修 改 后 的 文 件 进 行 快 照, 然 后 保 存 到 暂 存 区 域 3. 提 交 更 新, 将 保 存 在 暂 存 区 域 的 文 件 快 照 永 久 转 储 到 Git 目 录 中 所 以, 我 们 可 以 从 文 件 所 处 的 位 置 来 判 断 状 态 : 如 果 是 Git 目 录 中 保 存 着 的 特 定 版 本 文 件, 就 属 于 已 提 交 状 态 ; 如 果 作 了 修 改 并 已 放 入 暂 存 区 域, 就 属 于 已 暂 存 状 态 ; 如 果 自 上 次 取 出 后, 作 了 修 改 但 还 没 有 放 到 暂 存 区 域, 就 是 已 修 改 状 态 到 第 二 章 的 时 候, 我 们 会 进 一 步 了 解 其 中 细 节, 并 学 会 如 何 根 据 文 件 状 态 实 施 后 续 操 作, 以 及 怎 样 跳 过 暂 存 直 接 提 交 1.4 安 装 Git 是 时 候 动 手 尝 试 下 Git 了, 不 过 得 先 安 装 好 它 有 许 多 种 安 装 方 式, 主 要 分 为 两 种, 一 种 是 通 过 编 译 源 代 码 来 安 装 ; 另 一 种 是 使 用 为 特 定 平 台 预 编 译 好 的 安 装 包 从 源 代 码 安 装 若 是 条 件 允 许, 从 源 代 码 安 装 有 很 多 好 处, 至 少 可 以 安 装 最 新 的 版 本 Git 的 每 个 版 本 都 在 不 断 尝 试 改 进 用 户 体 验, 所 以 能 通 过 源 代 码 自 己 编 译 安 装 最 新 版 本 就 再 7

18 第 1 章 起 步 Sco Chacon Pro Git 中 文 版 好 不 过 了 有 些 Linux 版 本 自 带 的 安 装 包 更 新 起 来 并 不 及 时, 所 以 除 非 你 在 用 最 新 的 distro 或 者 backports, 那 么 从 源 代 码 安 装 其 实 该 算 是 最 佳 选 择 Git 的 工 作 需 要 调 用 curl,zlib,openssl,expat,libiconv 等 库 的 代 码, 所 以 需 要 先 安 装 这 些 依 赖 工 具 在 有 yum 的 系 统 上 ( 比 如 Fedora) 或 者 有 apt-get 的 系 统 上 ( 比 如 Debian 体 系 ), 可 以 用 下 面 的 命 令 安 装 : $ yum install curl-devel expat-devel gettext-devel \ openssl-devel zlib-devel $ apt-get install libcurl4-gnutls-dev libexpat1-dev gettext \ libz-dev libssl-dev 之 后, 从 下 面 的 Git 官 方 站 点 下 载 最 新 版 本 源 代 码 : 然 后 编 译 并 安 装 : $ tar -zxf git tar.gz $ cd git $ make prefix=/usr/local all $ sudo make prefix=/usr/local install 时 更 新 : 现 在 已 经 可 以 用 git 命 令 了, 用 git 把 Git 项 目 仓 库 克 隆 到 本 地, 以 便 日 后 随 $ git clone git://git.kernel.org/pub/scm/git/git.git 在 Linux 上 安 装 如 果 要 在 Linux 上 安 装 预 编 译 好 的 Git 二 进 制 安 装 包, 可 以 直 接 用 系 统 提 供 的 包 管 理 工 具 在 Fedora 上 用 yum 安 装 : $ yum install git-core 8 在 Ubuntu 这 类 Debian 体 系 的 系 统 上, 可 以 用 apt-get 安 装 :

19 Sco Chacon Pro Git 中 文 版 1.4 节 安 装 Git $ apt-get install git 在 Mac 上 安 装 在 Mac 上 安 装 Git 有 两 种 方 式 最 容 易 的 当 属 使 用 图 形 化 的 Git 安 装 工 具, 界 面 如 图 1-7, 下 载 地 址 在 : 图 1.7: Git OS X 安 装 工 具 另 一 种 是 通 过 MacPorts ( 安 装 如 果 已 经 装 好 了 MacPorts, 用 下 面 的 命 令 安 装 Git: $ sudo port install git-core +svn +doc +bash_completion +gitweb 这 种 方 式 就 不 需 要 再 自 己 安 装 依 赖 库 了,Macports 会 帮 你 搞 定 这 些 麻 烦 事 一 般 上 面 列 出 的 安 装 选 项 已 经 够 用, 要 是 你 想 用 Git 连 接 Subversion 的 代 码 仓 库, 还 可 以 加 上 +svn 选 项, 具 体 将 在 第 八 章 作 介 绍 ( 译 注 : 还 有 一 种 是 使 用 homebrew ( install git ) 在 Windows 上 安 装 在 Windows 上 安 装 Git 同 样 轻 松, 有 个 叫 做 msysgit 的 项 目 提 供 了 安 装 包, 可 以 到 GitHub 的 页 面 上 下 载 exe 安 装 文 件 并 运 行 : 完 成 安 装 之 后, 就 可 以 使 用 命 令 行 的 git 工 具 ( 已 经 自 带 了 ssh 客 户 端 ) 了, 另 外 还 有 一 个 图 形 界 面 的 Git 项 目 管 理 工 具 9

20 第 1 章 起 步 Sco Chacon Pro Git 中 文 版 Note on Windows usage: you should use Git with the provided msysgit shell (Unix style), it allows to use the complex lines of command given in this book. If you need, for some reason, to use the native Windows shell / command line console, you have to use double quotes instead of simple quotes (for parameters with spaces in them) and you must quote the parameters ending with the circumflex accent (ˆ) if they are last on the line, as it is a continuation symbol in Windows. 1.5 初 次 运 行 Git 前 的 配 置 一 般 在 新 的 系 统 上, 我 们 都 需 要 先 配 置 下 自 己 的 Git 工 作 环 境 配 置 工 作 只 需 一 次, 以 后 升 级 时 还 会 沿 用 现 在 的 配 置 当 然, 如 果 需 要, 你 随 时 可 以 用 相 同 的 命 令 修 改 已 有 的 配 置 Git 提 供 了 一 个 叫 做 git config 的 工 具 ( 译 注 : 实 际 是 git-config 命 令, 只 不 过 可 以 通 过 git 加 一 个 名 字 来 呼 叫 此 命 令 ), 专 门 用 来 配 置 或 读 取 相 应 的 工 作 环 境 变 量 而 正 是 由 这 些 环 境 变 量, 决 定 了 Git 在 各 个 环 节 的 具 体 工 作 方 式 和 行 为 这 些 变 量 可 以 存 放 在 以 下 三 个 不 同 的 地 方 : /etc/gitconfig 文 件 : 系 统 中 对 所 有 用 户 都 普 遍 适 用 的 配 置 若 使 用 git config 时 用 --system 选 项, 读 写 的 就 是 这 个 文 件 ~/.gitconfig 文 件 : 用 户 目 录 下 的 配 置 文 件 只 适 用 于 该 用 户 若 使 用 git config 时 用 --global 选 项, 读 写 的 就 是 这 个 文 件 当 前 项 目 的 git 目 录 中 的 配 置 文 件 ( 也 就 是 工 作 目 录 中 的.git/config 文 件 ): 这 里 的 配 置 仅 仅 针 对 当 前 项 目 有 效 每 一 个 级 别 的 配 置 都 会 覆 盖 上 层 的 相 同 配 置, 所 以.git/config 里 的 配 置 会 覆 盖 /etc/gitconfig 中 的 同 名 变 量 在 Windows 系 统 上,Git 会 找 寻 用 户 主 目 录 下 的.gitconfig 文 件 主 目 录 即 $HOME 变 量 指 定 的 目 录, 一 般 都 是 C:\Documents and Settings\$USER 此 外, Git 还 会 尝 试 找 寻 /etc/gitconfig 文 件, 只 不 过 看 当 初 Git 装 在 什 么 目 录, 就 以 此 作 为 根 目 录 来 定 位 用 户 信 息 第 一 个 要 配 置 的 是 你 个 人 的 用 户 名 称 和 电 子 邮 件 地 址 这 两 条 配 置 很 重 要, 每 次 Git 提 交 时 都 会 引 用 这 两 条 信 息, 说 明 是 谁 提 交 了 更 新, 所 以 会 随 更 新 内 容 一 起 被 永 久 纳 入 历 史 记 录 : $ git config --global user.name "John Doe" $ git config --global user. johndoe@example.com 10

21 Sco Chacon Pro Git 中 文 版 1.5 节 初 次 运 行 Git 前 的 配 置 如 果 用 了 --global 选 项, 那 么 更 改 的 配 置 文 件 就 是 位 于 你 用 户 主 目 录 下 的 那 个, 以 后 你 所 有 的 项 目 都 会 默 认 使 用 这 里 配 置 的 用 户 信 息 如 果 要 在 某 个 特 定 的 项 目 中 使 用 其 他 名 字 或 者 电 邮, 只 要 去 掉 --global 选 项 重 新 配 置 即 可, 新 的 设 定 保 存 在 当 前 项 目 的.git/config 文 件 里 文 本 编 辑 器 接 下 来 要 设 置 的 是 默 认 使 用 的 文 本 编 辑 器 Git 需 要 你 输 入 一 些 额 外 消 息 的 时 候, 会 自 动 调 用 一 个 外 部 文 本 编 辑 器 给 你 用 默 认 会 使 用 操 作 系 统 指 定 的 默 认 编 辑 器, 一 般 可 能 会 是 Vi 或 者 Vim 如 果 你 有 其 他 偏 好, 比 如 Emacs 的 话, 可 以 重 新 设 置 : $ git config --global core.editor emacs 差 异 分 析 工 具 还 有 一 个 比 较 常 用 的 是, 在 解 决 合 并 冲 突 时 使 用 哪 种 差 异 分 析 工 具 比 如 要 改 用 vimdiff 的 话 : $ git config --global merge.tool vimdiff Git 可 以 理 解 kdiff3,tkdiff,meld,xxdiff,emerge,vimdiff,gvimdiff,ecmerge, 和 opendiff 等 合 并 工 具 的 输 出 信 息 当 然, 你 也 可 以 指 定 使 用 自 己 开 发 的 工 具, 具 体 怎 么 做 可 以 参 阅 第 七 章 查 看 配 置 信 息 要 检 查 已 有 的 配 置 信 息, 可 以 使 用 git config --list 命 令 : $ git config --list user.name=scott Chacon user. =schacon@gmail.com color.status=auto color.branch=auto color.interactive=auto color.diff=auto... 11

22 第 1 章 起 步 Sco Chacon Pro Git 中 文 版 有 时 候 会 看 到 重 复 的 变 量 名, 那 就 说 明 它 们 来 自 不 同 的 配 置 文 件 ( 比 如 /etc/gitconfig 和 ~/.gitconfig), 不 过 最 终 Git 实 际 采 用 的 是 最 后 一 个 也 可 以 直 接 查 阅 某 个 环 境 变 量 的 设 定, 只 要 把 特 定 的 名 字 跟 在 后 面 即 可, 像 这 样 : $ git config user.name Scott Chacon 1.6 获 取 帮 助 想 了 解 Git 的 各 式 工 具 该 怎 么 用, 可 以 阅 读 它 们 的 使 用 帮 助, 方 法 有 三 : $ git help <verb> $ git <verb> --help $ man git-<verb> 比 如, 要 学 习 config 命 令 可 以 怎 么 用, 运 行 : $ git help config 我 们 随 时 都 可 以 浏 览 这 些 帮 助 信 息 而 无 需 连 网 不 过, 要 是 你 觉 得 还 不 够, 可 以 到 Freenode IRC 服 务 器 (irc.freenode.net) 上 的 #git 或 #github 频 道 寻 求 他 人 帮 助 这 两 个 频 道 上 总 有 着 上 百 号 人, 大 多 都 有 着 丰 富 的 git 知 识, 并 且 乐 于 助 人 1.7 小 结 至 此, 你 该 对 Git 有 了 点 基 本 认 识, 包 括 它 和 以 前 你 使 用 的 CVCS 之 间 的 差 别 现 在, 在 你 的 系 统 上 应 该 已 经 装 好 了 Git, 设 置 了 自 己 的 名 字 和 电 邮 接 下 来 让 我 们 继 续 学 习 Git 的 基 础 知 识 12

23 第 2 章 Git 基 础 读 完 本 章 你 就 能 上 手 使 用 Git 了 本 章 将 介 绍 几 个 最 基 本 的, 也 是 最 常 用 的 Git 命 令, 以 后 绝 大 多 数 时 间 里 用 到 的 也 就 是 这 几 个 命 令 读 完 本 章, 你 就 能 初 始 化 一 个 新 的 代 码 仓 库, 做 一 些 适 当 配 置 ; 开 始 或 停 止 跟 踪 某 些 文 件 ; 暂 存 或 提 交 某 些 更 新 我 们 还 会 展 示 如 何 让 Git 忽 略 某 些 文 件, 或 是 名 称 符 合 特 定 模 式 的 文 件 ; 如 何 既 快 且 容 易 地 撤 消 犯 下 的 小 错 误 ; 如 何 浏 览 项 目 的 更 新 历 史, 查 看 某 两 次 更 新 之 间 的 差 异 ; 以 及 如 何 从 远 程 仓 库 拉 数 据 下 来 或 者 推 数 据 上 去 2.1 取 得 项 目 的 Git 仓 库 有 两 种 取 得 Git 项 目 仓 库 的 方 法 第 一 种 是 在 现 存 的 目 录 下, 通 过 导 入 所 有 文 件 来 创 建 新 的 Git 仓 库 第 二 种 是 从 已 有 的 Git 仓 库 克 隆 出 一 个 新 的 镜 像 仓 库 来 在 工 作 目 录 中 初 始 化 新 仓 库 要 对 现 有 的 某 个 项 目 开 始 用 Git 管 理, 只 需 到 此 项 目 所 在 的 目 录, 执 行 : $ git init 初 始 化 后, 在 当 前 目 录 下 会 出 现 一 个 名 为.git 的 目 录, 所 有 Git 需 要 的 数 据 和 资 源 都 存 放 在 这 个 目 录 中 不 过 目 前, 仅 仅 是 按 照 既 有 的 结 构 框 架 初 始 化 好 了 里 边 所 有 的 文 件 和 目 录, 但 我 们 还 没 有 开 始 跟 踪 管 理 项 目 中 的 任 何 一 个 文 件 ( 在 第 九 章 我 们 会 详 细 说 明 刚 才 创 建 的.git 目 录 中 究 竟 有 哪 些 文 件, 以 及 都 起 些 什 么 作 用 ) 如 果 当 前 目 录 下 有 几 个 文 件 想 要 纳 入 版 本 控 制, 需 要 先 用 git add 命 令 告 诉 Git 开 始 对 这 些 文 件 进 行 跟 踪, 然 后 提 交 : $ git add *.c $ git add README 13

24 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 $ git commit -m 'initial project version' 稍 后 我 们 再 逐 一 解 释 每 条 命 令 的 意 思 不 过 现 在, 你 已 经 得 到 了 一 个 实 际 维 护 着 若 干 文 件 的 Git 仓 库 从 现 有 仓 库 克 隆 如 果 想 对 某 个 开 源 项 目 出 一 份 力, 可 以 先 把 该 项 目 的 Git 仓 库 复 制 一 份 出 来, 这 就 需 要 用 到 git clone 命 令 如 果 你 熟 悉 其 他 的 VCS 比 如 Subversion, 你 可 能 已 经 注 意 到 这 里 使 用 的 是 clone 而 不 是 checkout 这 是 个 非 常 重 要 的 差 别,Git 收 取 的 是 项 目 历 史 的 所 有 数 据 ( 每 一 个 文 件 的 每 一 个 版 本 ), 服 务 器 上 有 的 数 据 克 隆 之 后 本 地 也 都 有 了 实 际 上, 即 便 服 务 器 的 磁 盘 发 生 故 障, 用 任 何 一 个 克 隆 出 来 的 客 户 端 都 可 以 重 建 服 务 器 上 的 仓 库, 回 到 当 初 克 隆 时 的 状 态 ( 虽 然 可 能 会 丢 失 某 些 服 务 器 端 的 挂 钩 设 置, 但 所 有 版 本 的 数 据 仍 旧 还 在, 有 关 细 节 请 参 考 第 四 章 ) 克 隆 仓 库 的 命 令 格 式 为 git clone [url] 比 如, 要 克 隆 Ruby 语 言 的 Git 代 码 仓 库 Grit, 可 以 用 下 面 的 命 令 : $ git clone git://github.com/schacon/grit.git 这 会 在 当 前 目 录 下 创 建 一 个 名 为 grit 的 目 录, 其 中 包 含 一 个.git 的 目 录, 用 于 保 存 下 载 下 来 的 所 有 版 本 记 录, 然 后 从 中 取 出 最 新 版 本 的 文 件 拷 贝 如 果 进 入 这 个 新 建 的 grit 目 录, 你 会 看 到 项 目 中 的 所 有 文 件 已 经 在 里 边 了, 准 备 好 后 续 的 开 发 和 使 用 如 果 希 望 在 克 隆 的 时 候, 自 己 定 义 要 新 建 的 项 目 目 录 名 称, 可 以 在 上 面 的 命 令 末 尾 指 定 新 的 名 字 : $ git clone git://github.com/schacon/grit.git mygrit 唯 一 的 差 别 就 是, 现 在 新 建 的 目 录 成 了 mygrit, 其 他 的 都 和 上 边 的 一 样 Git 支 持 许 多 数 据 传 输 协 议 之 前 的 例 子 使 用 的 是 git:// 协 议, 不 过 你 也 可 以 用 http(s):// 或 者 user@server:/path.git 表 示 的 SSH 传 输 协 议 我 们 会 在 第 四 章 详 细 介 绍 所 有 这 些 协 议 在 服 务 器 端 该 如 何 配 置 使 用, 以 及 各 种 方 式 之 间 的 利 弊 2.2 记 录 每 次 更 新 到 仓 库 现 在 我 们 手 上 已 经 有 了 一 个 真 实 项 目 的 Git 仓 库, 并 从 这 个 仓 库 中 取 出 了 所 有 文 件 的 工 作 拷 贝 接 下 来, 对 这 些 文 件 作 些 修 改, 在 完 成 了 一 个 阶 段 的 目 标 之 后, 提 交 本 次 更 新 到 仓 库 14

25 Sco Chacon Pro Git 中 文 版 2.2 节 记 录 每 次 更 新 到 仓 库 请 记 住, 工 作 目 录 下 面 的 所 有 文 件 都 不 外 乎 这 两 种 状 态 : 已 跟 踪 或 未 跟 踪 已 跟 踪 的 文 件 是 指 本 来 就 被 纳 入 版 本 控 制 管 理 的 文 件, 在 上 次 快 照 中 有 它 们 的 记 录, 工 作 一 段 时 间 后, 它 们 的 状 态 可 能 是 未 更 新, 已 修 改 或 者 已 放 入 暂 存 区 而 所 有 其 他 文 件 都 属 于 未 跟 踪 文 件 它 们 既 没 有 上 次 更 新 时 的 快 照, 也 不 在 当 前 的 暂 存 区 域 初 次 克 隆 某 个 仓 库 时, 工 作 目 录 中 的 所 有 文 件 都 属 于 已 跟 踪 文 件, 且 状 态 为 未 修 改 在 编 辑 过 某 些 文 件 之 后,Git 将 这 些 文 件 标 为 已 修 改 我 们 逐 步 把 这 些 修 改 过 的 文 件 放 到 暂 存 区 域, 直 到 最 后 一 次 性 提 交 所 有 这 些 暂 存 起 来 的 文 件, 如 此 重 复 所 以 使 用 Git 时 的 文 件 状 态 变 化 周 期 如 图 2-1 所 示 图 2.1: 文 件 的 状 态 变 化 周 期 检 查 当 前 文 件 状 态 要 确 定 哪 些 文 件 当 前 处 于 什 么 状 态, 可 以 用 git status 命 令 如 果 在 克 隆 仓 库 之 后 立 即 执 行 此 命 令, 会 看 到 类 似 这 样 的 输 出 : $ git status On branch master nothing to commit, working directory clean 这 说 明 你 现 在 的 工 作 目 录 相 当 干 净 换 句 话 说, 所 有 已 跟 踪 文 件 在 上 次 提 交 后 都 未 被 更 改 过 此 外, 上 面 的 信 息 还 表 明, 当 前 目 录 下 没 有 出 现 任 何 处 于 未 跟 踪 的 新 文 件, 否 则 Git 会 在 这 里 列 出 来 最 后, 该 命 令 还 显 示 了 当 前 所 在 的 分 支 是 master, 这 是 默 认 的 分 支 名 称, 实 际 是 可 以 修 改 的, 现 在 先 不 用 考 虑 下 一 章 我 们 就 会 详 细 讨 论 分 支 和 引 用 现 在 让 我 们 用 vim 创 建 一 个 新 文 件 README, 保 存 退 出 后 运 行 git status 会 看 到 该 文 件 出 现 在 未 跟 踪 文 件 列 表 中 : 15

26 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 $ vim README $ git status On branch master Untracked files: (use "git add <file>..." to include in what will be committed) README nothing added to commit but untracked files present (use "git add" to track) 在 状 态 报 告 中 可 以 看 到 新 建 的 README 文 件 出 现 在 Untracked files 下 面 未 跟 踪 的 文 件 意 味 着 Git 在 之 前 的 快 照 ( 提 交 ) 中 没 有 这 些 文 件 ;Git 不 会 自 动 将 之 纳 入 跟 踪 范 围, 除 非 你 明 明 白 白 地 告 诉 它 我 需 要 跟 踪 该 文 件, 因 而 不 用 担 心 把 临 时 文 件 什 么 的 也 归 入 版 本 管 理 不 过 现 在 的 例 子 中, 我 们 确 实 想 要 跟 踪 管 理 README 这 个 文 件 跟 踪 新 文 件 使 用 命 令 git add 开 始 跟 踪 一 个 新 文 件 所 以, 要 跟 踪 README 文 件, 运 行 : $ git add README 态 : 此 时 再 运 行 git status 命 令, 会 看 到 README 文 件 已 被 跟 踪, 并 处 于 暂 存 状 $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: README 只 要 在 Changes to be commi ed 这 行 下 面 的, 就 说 明 是 已 暂 存 状 态 如 果 此 时 提 交, 那 么 该 文 件 此 时 此 刻 的 版 本 将 被 留 存 在 历 史 记 录 中 你 可 能 会 想 起 之 前 我 们 使 用 git init 后 就 运 行 了 git add 命 令, 开 始 跟 踪 当 前 目 录 下 的 文 件 在 git add 后 面 可 以 指 明 要 跟 踪 的 文 件 或 目 录 路 径 如 果 是 目 录 的 话, 就 说 明 要 递 归 跟 踪 16

27 Sco Chacon Pro Git 中 文 版 2.2 节 记 录 每 次 更 新 到 仓 库 该 目 录 下 的 所 有 文 件 ( 译 注 : 其 实 git add 的 潜 台 词 就 是 把 目 标 文 件 快 照 放 入 暂 存 区 域, 也 就 是 add file into staged area, 同 时 未 曾 跟 踪 过 的 文 件 标 记 为 需 要 跟 踪 这 样 就 好 理 解 后 续 add 操 作 的 实 际 意 义 了 ) 暂 存 已 修 改 文 件 现 在 我 们 修 改 下 之 前 已 跟 踪 过 的 文 件 benchmarks.rb, 然 后 再 次 运 行 status 命 令, 会 看 到 这 样 的 状 态 报 告 : $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: README Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: benchmarks.rb 文 件 benchmarks.rb 出 现 在 Changes not staged for commit 这 行 下 面, 说 明 已 跟 踪 文 件 的 内 容 发 生 了 变 化, 但 还 没 有 放 到 暂 存 区 要 暂 存 这 次 更 新, 需 要 运 行 git add 命 令 ( 这 是 个 多 功 能 命 令, 根 据 目 标 文 件 的 状 态 不 同, 此 命 令 的 效 果 也 不 同 : 可 以 用 它 开 始 跟 踪 新 文 件, 或 者 把 已 跟 踪 的 文 件 放 到 暂 存 区, 还 能 用 于 合 并 时 把 有 冲 突 的 文 件 标 记 为 已 解 决 状 态 等 ) 现 在 让 我 们 运 行 git add 将 benchmarks.rb 放 到 暂 存 区, 然 后 再 看 看 git status 的 输 出 : $ git add benchmarks.rb $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: modified: README benchmarks.rb 17

28 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 现 在 两 个 文 件 都 已 暂 存, 下 次 提 交 时 就 会 一 并 记 录 到 仓 库 假 设 此 时, 你 想 要 在 benchmarks.rb 里 再 加 条 注 释, 重 新 编 辑 存 盘 后, 准 备 好 提 交 不 过 且 慢, 再 运 行 git status 看 看 : $ vim benchmarks.rb $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: modified: README benchmarks.rb Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: benchmarks.rb 怎 么 回 事?benchmarks.rb 文 件 出 现 了 两 次! 一 次 算 未 暂 存, 一 次 算 已 暂 存, 这 怎 么 可 能 呢? 好 吧, 实 际 上 Git 只 不 过 暂 存 了 你 运 行 git add 命 令 时 的 版 本, 如 果 现 在 提 交, 那 么 提 交 的 是 添 加 注 释 前 的 版 本, 而 非 当 前 工 作 目 录 中 的 版 本 所 以, 运 行 了 git add 之 后 又 作 了 修 订 的 文 件, 需 要 重 新 运 行 git add 把 最 新 版 本 重 新 暂 存 起 来 : $ git add benchmarks.rb $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: modified: README benchmarks.rb 忽 略 某 些 文 件 一 般 我 们 总 会 有 些 文 件 无 需 纳 入 Git 的 管 理, 也 不 希 望 它 们 总 出 现 在 未 跟 踪 文 件 列 表 通 常 都 是 些 自 动 生 成 的 文 件, 比 如 日 志 文 件, 或 者 编 译 过 程 中 创 建 的 临 时 18

29 Sco Chacon Pro Git 中 文 版 2.2 节 记 录 每 次 更 新 到 仓 库 文 件 等 我 们 可 以 创 建 一 个 名 为.gitignore 的 文 件, 列 出 要 忽 略 的 文 件 模 式 来 看 一 个 实 际 的 例 子 : $ cat.gitignore *.[oa] *~ 第 一 行 告 诉 Git 忽 略 所 有 以.o 或.a 结 尾 的 文 件 一 般 这 类 对 象 文 件 和 存 档 文 件 都 是 编 译 过 程 中 出 现 的, 我 们 用 不 着 跟 踪 它 们 的 版 本 第 二 行 告 诉 Git 忽 略 所 有 以 波 浪 符 (~) 结 尾 的 文 件, 许 多 文 本 编 辑 软 件 ( 比 如 Emacs) 都 用 这 样 的 文 件 名 保 存 副 本 此 外, 你 可 能 还 需 要 忽 略 log,tmp 或 者 pid 目 录, 以 及 自 动 生 成 的 文 档 等 等 要 养 成 一 开 始 就 设 置 好.gitignore 文 件 的 习 惯, 以 免 将 来 误 提 交 这 类 无 用 的 文 件 文 件.gitignore 的 格 式 规 范 如 下 : 所 有 空 行 或 者 以 注 释 符 号 # 开 头 的 行 都 会 被 Git 忽 略 可 以 使 用 标 准 的 glob 模 式 匹 配 匹 配 模 式 最 后 跟 反 斜 杠 (/) 说 明 要 忽 略 的 是 目 录 要 忽 略 指 定 模 式 以 外 的 文 件 或 目 录, 可 以 在 模 式 前 加 上 惊 叹 号 (!) 取 反 所 谓 的 glob 模 式 是 指 shell 所 使 用 的 简 化 了 的 正 则 表 达 式 星 号 (*) 匹 配 零 个 或 多 个 任 意 字 符 ;[abc] 匹 配 任 何 一 个 列 在 方 括 号 中 的 字 符 ( 这 个 例 子 要 么 匹 配 一 个 a, 要 么 匹 配 一 个 b, 要 么 匹 配 一 个 c); 问 号 (?) 只 匹 配 一 个 任 意 字 符 ; 如 果 在 方 括 号 中 使 用 短 划 线 分 隔 两 个 字 符, 表 示 所 有 在 这 两 个 字 符 范 围 内 的 都 可 以 匹 配 ( 比 如 [0-9] 表 示 匹 配 所 有 0 到 9 的 数 字 ) 我 们 再 看 一 个.gitignore 文 件 的 例 子 : # 此 为 注 释 将 被 Git 忽 略 # 忽 略 所 有.a 结 尾 的 文 件 *.a # 但 lib.a 除 外!lib.a # 仅 仅 忽 略 项 目 根 目 录 下 的 TODO 文 件, 不 包 括 subdir/todo /TODO # 忽 略 build/ 目 录 下 的 所 有 文 件 build/ # 会 忽 略 doc/notes.txt 但 不 包 括 doc/server/arch.txt doc/*.txt 19

30 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 # ignore all.txt files in the doc/ directory doc/**/*.txt A **/ pa ern is available in Git since version 查 看 已 暂 存 和 未 暂 存 的 更 新 实 际 上 git status 的 显 示 比 较 简 单, 仅 仅 是 列 出 了 修 改 过 的 文 件, 如 果 要 查 看 具 体 修 改 了 什 么 地 方, 可 以 用 git diff 命 令 稍 后 我 们 会 详 细 介 绍 git diff, 不 过 现 在, 它 已 经 能 回 答 我 们 的 两 个 问 题 了 : 当 前 做 的 哪 些 更 新 还 没 有 暂 存? 有 哪 些 更 新 已 经 暂 存 起 来 准 备 好 了 下 次 提 交?git diff 会 使 用 文 件 补 丁 的 格 式 显 示 具 体 添 加 和 删 除 的 行 假 如 再 次 修 改 README 文 件 后 暂 存, 然 后 编 辑 benchmarks.rb 文 件 后 先 别 暂 存, 运 行 status 命 令 将 会 看 到 : $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: README Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: benchmarks.rb 要 查 看 尚 未 暂 存 的 文 件 更 新 了 哪 些 部 分, 不 加 参 数 直 接 输 入 git diff: $ git diff diff --git a/benchmarks.rb b/benchmarks.rb index 3cb747f..da a/benchmarks.rb ,6 def 20

31 Sco Chacon Pro Git 中 文 版 2.2 节 记 录 每 次 更 新 到 仓 库 end + run_code(x, 'commits 1') do + git.commits.size + end + run_code(x, 'commits 2') do log = git.commits('master', 15) log.size 此 命 令 比 较 的 是 工 作 目 录 中 当 前 文 件 和 暂 存 区 域 快 照 之 间 的 差 异, 也 就 是 修 改 之 后 还 没 有 暂 存 起 来 的 变 化 内 容 若 要 看 已 经 暂 存 起 来 的 文 件 和 上 次 提 交 时 的 快 照 之 间 的 差 异, 可 以 用 git diff --cached 命 令 (Git 及 更 高 版 本 还 允 许 使 用 git diff --staged, 效 果 是 相 同 的, 但 更 好 记 些 ) 来 看 看 实 际 的 效 果 : $ git diff --cached diff --git a/readme b/readme new file mode index a1 --- /dev/null ,0 +grit + by Tom Preston-Werner, Chris Wanstrath Grit is a Ruby library for extracting information from a Git repository 请 注 意, 单 单 git diff 不 过 是 显 示 还 没 有 暂 存 起 来 的 改 动, 而 不 是 这 次 工 作 和 上 次 提 交 之 间 的 差 异 所 以 有 时 候 你 一 下 子 暂 存 了 所 有 更 新 过 的 文 件 后, 运 行 git diff 后 却 什 么 也 没 有, 就 是 这 个 原 因 像 之 前 说 的, 暂 存 benchmarks.rb 后 再 编 辑, 运 行 git status 会 看 到 暂 存 前 后 的 两 个 版 本 : $ git add benchmarks.rb 21

32 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 $ echo '# test line' >> benchmarks.rb $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: benchmarks.rb Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: benchmarks.rb 现 在 运 行 git diff 看 暂 存 前 后 的 变 化 : $ git diff diff --git a/benchmarks.rb b/benchmarks.rb index e445e28..86b2f7c a/benchmarks.rb ,3 end main() ##pp Grit::GitRuby.cache_client.stats +# test line 然 后 用 git diff --cached 查 看 已 经 暂 存 起 来 的 变 化 : $ git diff --cached diff --git a/benchmarks.rb b/benchmarks.rb index 3cb747f..e445e a/benchmarks.rb ,6 def 22

33 Sco Chacon Pro Git 中 文 版 2.2 节 记 录 每 次 更 新 到 仓 库 end + run_code(x, 'commits 1') do + git.commits.size + end + run_code(x, 'commits 2') do log = git.commits('master', 15) log.size 提 交 更 新 现 在 的 暂 存 区 域 已 经 准 备 妥 当 可 以 提 交 了 在 此 之 前, 请 一 定 要 确 认 还 有 什 么 修 改 过 的 或 新 建 的 文 件 还 没 有 git add 过, 否 则 提 交 的 时 候 不 会 记 录 这 些 还 没 暂 存 起 来 的 变 化 所 以, 每 次 准 备 提 交 前, 先 用 git status 看 下, 是 不 是 都 已 暂 存 起 来 了, 然 后 再 运 行 提 交 命 令 git commit: $ git commit 这 种 方 式 会 启 动 文 本 编 辑 器 以 便 输 入 本 次 提 交 的 说 明 ( 默 认 会 启 用 shell 的 环 境 变 量 $EDITOR 所 指 定 的 软 件, 一 般 都 是 vim 或 emacs 当 然 也 可 以 按 照 第 一 章 介 绍 的 方 式, 使 用 git config --global core.editor 命 令 设 定 你 喜 欢 的 编 辑 软 件 ) 编 辑 器 会 显 示 类 似 下 面 的 文 本 信 息 ( 本 例 选 用 Vim 的 屏 显 方 式 展 示 ): # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch master # Changes to be committed: # new file: README # modified: benchmarks.rb # ~ ~ ~ ".git/commit_editmsg" 10L, 283C 23

34 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 可 以 看 到, 默 认 的 提 交 消 息 包 含 最 后 一 次 运 行 git status 的 输 出, 放 在 注 释 行 里, 另 外 开 头 还 有 一 空 行, 供 你 输 入 提 交 说 明 你 完 全 可 以 去 掉 这 些 注 释 行, 不 过 留 着 也 没 关 系, 多 少 能 帮 你 回 想 起 这 次 更 新 的 内 容 有 哪 些 ( 如 果 觉 得 这 还 不 够, 可 以 用 -v 选 项 将 修 改 差 异 的 每 一 行 都 包 含 到 注 释 中 来 ) 退 出 编 辑 器 时,Git 会 丢 掉 注 释 行, 将 说 明 内 容 和 本 次 更 新 提 交 到 仓 库 另 外 也 可 以 用 -m 参 数 后 跟 提 交 说 明 的 方 式, 在 一 行 命 令 中 提 交 更 新 : $ git commit -m "Story 182: Fix benchmarks for speed" [master 463dc4f] Fix benchmarks for speed 2 files changed, 3 insertions(+) create mode README 好, 现 在 你 已 经 创 建 了 第 一 个 提 交! 可 以 看 到, 提 交 后 它 会 告 诉 你, 当 前 是 在 哪 个 分 支 (master) 提 交 的, 本 次 提 交 的 完 整 SHA-1 校 验 和 是 什 么 (463dc4f), 以 及 在 本 次 提 交 中, 有 多 少 文 件 修 订 过, 多 少 行 添 改 和 删 改 过 记 住, 提 交 时 记 录 的 是 放 在 暂 存 区 域 的 快 照, 任 何 还 未 暂 存 的 仍 然 保 持 已 修 改 状 态, 可 以 在 下 次 提 交 时 纳 入 版 本 管 理 每 一 次 运 行 提 交 操 作, 都 是 对 你 项 目 作 一 次 快 照, 以 后 可 以 回 到 这 个 状 态, 或 者 进 行 比 较 跳 过 使 用 暂 存 区 域 尽 管 使 用 暂 存 区 域 的 方 式 可 以 精 心 准 备 要 提 交 的 细 节, 但 有 时 候 这 么 做 略 显 繁 琐 Git 提 供 了 一 个 跳 过 使 用 暂 存 区 域 的 方 式, 只 要 在 提 交 的 时 候, 给 git commit 加 上 -a 选 项,Git 就 会 自 动 把 所 有 已 经 跟 踪 过 的 文 件 暂 存 起 来 一 并 提 交, 从 而 跳 过 git add 步 骤 : $ git status On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: benchmarks.rb no changes added to commit (use "git add" and/or "git commit -a") $ git commit -a -m 'added new benchmarks' [master 83e38c7] added new benchmarks 1 files changed, 5 insertions(+) 24

35 Sco Chacon Pro Git 中 文 版 2.2 节 记 录 每 次 更 新 到 仓 库 看 到 了 吗? 提 交 之 前 不 再 需 要 git add 文 件 benchmarks.rb 了 移 除 文 件 要 从 Git 中 移 除 某 个 文 件, 就 必 须 要 从 已 跟 踪 文 件 清 单 中 移 除 ( 确 切 地 说, 是 从 暂 存 区 域 移 除 ), 然 后 提 交 可 以 用 git rm 命 令 完 成 此 项 工 作, 并 连 带 从 工 作 目 录 中 删 除 指 定 的 文 件, 这 样 以 后 就 不 会 出 现 在 未 跟 踪 文 件 清 单 中 了 如 果 只 是 简 单 地 从 工 作 目 录 中 手 工 删 除 文 件, 运 行 git status 时 就 会 在 Changes not staged for commit 部 分 ( 也 就 是 未 暂 存 清 单 ) 看 到 : $ rm grit.gemspec $ git status On branch master Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) deleted: grit.gemspec no changes added to commit (use "git add" and/or "git commit -a") 然 后 再 运 行 git rm 记 录 此 次 移 除 文 件 的 操 作 : $ git rm grit.gemspec rm 'grit.gemspec' $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) deleted: grit.gemspec 最 后 提 交 的 时 候, 该 文 件 就 不 再 纳 入 版 本 管 理 了 如 果 删 除 之 前 修 改 过 并 且 已 经 放 到 暂 存 区 域 的 话, 则 必 须 要 用 强 制 删 除 选 项 -f( 译 注 : 即 force 的 首 字 母 ), 以 防 误 删 除 文 件 后 丢 失 修 改 的 内 容 另 外 一 种 情 况 是, 我 们 想 把 文 件 从 Git 仓 库 中 删 除 ( 亦 即 从 暂 存 区 域 移 除 ), 但 仍 然 希 望 保 留 在 当 前 工 作 目 录 中 换 句 话 说, 仅 是 从 跟 踪 清 单 中 删 除 比 如 一 些 大 25

36 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 型 日 志 文 件 或 者 一 堆.a 编 译 文 件, 不 小 心 纳 入 仓 库 后, 要 移 除 跟 踪 但 不 删 除 文 件, 以 便 稍 后 在.gitignore 文 件 中 补 上, 用 --cached 选 项 即 可 : $ git rm --cached readme.txt 后 面 可 以 列 出 文 件 或 者 目 录 的 名 字, 也 可 以 使 用 glob 模 式 比 方 说 : $ git rm log/\*.log 注 意 到 星 号 * 之 前 的 反 斜 杠 \, 因 为 Git 有 它 自 己 的 文 件 模 式 扩 展 匹 配 方 式, 所 以 我 们 不 用 shell 来 帮 忙 展 开 ( 译 注 : 实 际 上 不 加 反 斜 杠 也 可 以 运 行, 只 不 过 按 照 shell 扩 展 的 话, 仅 仅 删 除 指 定 目 录 下 的 文 件 而 不 会 递 归 匹 配 上 面 的 例 子 本 来 就 指 定 了 目 录, 所 以 效 果 等 同, 但 下 面 的 例 子 就 会 用 递 归 方 式 匹 配, 所 以 必 须 加 反 斜 杠 ) 此 命 令 删 除 所 有 log/ 目 录 下 扩 展 名 为.log 的 文 件 类 似 的 比 如 : $ git rm \*~ 会 递 归 删 除 当 前 目 录 及 其 子 目 录 中 所 有 ~ 结 尾 的 文 件 移 动 文 件 不 像 其 他 的 VCS 系 统,Git 并 不 跟 踪 文 件 移 动 操 作 如 果 在 Git 中 重 命 名 了 某 个 文 件, 仓 库 中 存 储 的 元 数 据 并 不 会 体 现 出 这 是 一 次 改 名 操 作 不 过 Git 非 常 聪 明, 它 会 推 断 出 究 竟 发 生 了 什 么, 至 于 具 体 是 如 何 做 到 的, 我 们 稍 后 再 谈 既 然 如 此, 当 你 看 到 Git 的 mv 命 令 时 一 定 会 困 惑 不 已 要 在 Git 中 对 文 件 改 名, 可 以 这 么 做 : $ git mv file_from file_to 它 会 恰 如 预 期 般 正 常 工 作 实 际 上, 即 便 此 时 查 看 状 态 信 息, 也 会 明 白 无 误 地 看 到 关 于 重 命 名 操 作 的 说 明 : 26

37 Sco Chacon Pro Git 中 文 版 2.3 节 查 看 提 交 历 史 $ git mv README.txt README $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) renamed: README.txt -> README 其 实, 运 行 git mv 就 相 当 于 运 行 了 下 面 三 条 命 令 : $ mv README.txt README $ git rm README.txt $ git add README 如 此 分 开 操 作,Git 也 会 意 识 到 这 是 一 次 改 名, 所 以 不 管 何 种 方 式 都 一 样 当 然, 直 接 用 git mv 轻 便 得 多, 不 过 有 时 候 用 其 他 工 具 批 处 理 改 名 的 话, 要 记 得 在 提 交 前 删 除 老 的 文 件 名, 再 添 加 新 的 文 件 名 2.3 查 看 提 交 历 史 在 提 交 了 若 干 更 新 之 后, 又 或 者 克 隆 了 某 个 项 目, 想 回 顾 下 提 交 历 史, 可 以 使 用 git log 命 令 查 看 接 下 来 的 例 子 会 用 我 专 门 用 于 演 示 的 simplegit 项 目, 运 行 下 面 的 命 令 获 取 该 项 目 源 代 码 : git clone git://github.com/schacon/simplegit-progit.git 然 后 在 此 项 目 中 运 行 git log, 应 该 会 看 到 下 面 的 输 出 : $ git log commit ca82a6dff817ec66f a Author: Scott Chacon <schacon@ge .com> Date: Mon Mar 17 21:52:

38 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 changed the version number commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 Author: Scott Chacon <schacon@ge .com> Date: Sat Mar 15 16:40: removed unnecessary test code commit a11bef06a3f659402fe7563abf99ad00de2209e6 Author: Scott Chacon <schacon@ge .com> Date: Sat Mar 15 10:31: first commit 默 认 不 用 任 何 参 数 的 话,git log 会 按 提 交 时 间 列 出 所 有 的 更 新, 最 近 的 更 新 排 在 最 上 面 看 到 了 吗, 每 次 更 新 都 有 一 个 SHA-1 校 验 和 作 者 的 名 字 和 电 子 邮 件 地 址 提 交 时 间, 最 后 缩 进 一 个 段 落 显 示 提 交 说 明 git log 有 许 多 选 项 可 以 帮 助 你 搜 寻 感 兴 趣 的 提 交, 接 下 来 我 们 介 绍 些 最 常 用 的 我 们 常 用 -p 选 项 展 开 显 示 每 次 提 交 的 内 容 差 异, 用 -2 则 仅 显 示 最 近 的 两 次 更 新 : $ git log -p -2 commit ca82a6dff817ec66f a Author: Scott Chacon <schacon@ge .com> Date: Mon Mar 17 21:52: changed the version number diff --git a/rakefile b/rakefile index a874b73..8f a/rakefile ,5 require 'rake/gempackagetask' spec = Gem::Specification.new do s s.name = "simplegit" - s.version = "0.1.0" + s.version = "0.1.1" 28

39 Sco Chacon Pro Git 中 文 版 2.3 节 查 看 提 交 历 史 s.author = "Scott Chacon" s. = "schacon@ge .com commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 Author: Scott Chacon <schacon@ge .com> Date: Sat Mar 15 16:40: removed unnecessary test code diff --git a/lib/simplegit.rb b/lib/simplegit.rb index a0a60ae..47c a/lib/simplegit.rb ,8 class SimpleGit end end - -if $0 == FILE - git = SimpleGit.new - puts git.show -end \ No newline at end of file This option displays the same information but with a diff directly following each entry. This is very helpful for code review or to quickly browse what happened during a series of commits that a collaborator has added. Sometimes it s easier to review changes on the word level rather than on the line level. There is a --word-diff option available in Git, that you can append to the git log -p command to get word diff instead of normal line by line diff. Word diff format is quite useless when applied to source code, but it comes in handy when applied to large text files, like books or your dissertation. Here is an example: $ git log -U1 --word-diff commit ca82a6dff817ec66f a Author: Scott Chacon <schacon@ge .com> Date: Mon Mar 17 21:52:

40 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 changed the version number diff --git a/rakefile b/rakefile index a874b73..8f a/rakefile ,3 spec = Gem::Specification.new do s s.name = "simplegit" s.version = [-"0.1.0"-]{+"0.1.1"+} s.author = "Scott Chacon" As you can see, there is no added and removed lines in this output as in a normal diff. Changes are shown inline instead. You can see the added word enclosed in {+ +} and removed one enclosed in [- -]. You may also want to reduce the usual three lines context in diff output to only one line, as the context is now words, not lines. You can do this with -U1 as we did in the example above. 在 做 代 码 审 查, 或 者 要 快 速 浏 览 其 他 协 作 者 提 交 的 更 新 都 作 了 哪 些 改 动 时, 就 可 以 用 这 个 选 项 此 外, 还 有 许 多 摘 要 选 项 可 以 用, 比 如 --stat, 仅 显 示 简 要 的 增 改 行 数 统 计 : $ git log --stat commit ca82a6dff817ec66f a Author: Scott Chacon <schacon@ge .com> Date: Mon Mar 17 21:52: changed the version number Rakefile file changed, 1 insertion(+), 1 deletion(-) commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 Author: Scott Chacon <schacon@ge .com> Date: Sat Mar 15 16:40: removed unnecessary test code lib/simplegit.rb file changed, 5 deletions(-) 30

41 Sco Chacon Pro Git 中 文 版 2.3 节 查 看 提 交 历 史 commit a11bef06a3f659402fe7563abf99ad00de2209e6 Author: Scott Chacon Date: Sat Mar 15 10:31: first commit README Rakefile lib/simplegit.rb files changed, 54 insertions(+) 每 个 提 交 都 列 出 了 修 改 过 的 文 件, 以 及 其 中 添 加 和 移 除 的 行 数, 并 在 最 后 列 出 所 有 增 减 行 数 小 计 还 有 个 常 用 的 --pretty 选 项, 可 以 指 定 使 用 完 全 不 同 于 默 认 格 式 的 方 式 展 示 提 交 历 史 比 如 用 oneline 将 每 个 提 交 放 在 一 行 显 示, 这 在 提 交 数 很 大 时 非 常 有 用 另 外 还 有 short,full 和 fuller 可 以 用, 展 示 的 信 息 或 多 或 少 有 些 不 同, 请 自 己 动 手 实 践 一 下 看 看 效 果 如 何 $ git log --pretty=oneline ca82a6dff817ec66f a changed the version number 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test code a11bef06a3f659402fe7563abf99ad00de2209e6 first commit 但 最 有 意 思 的 是 format, 可 以 定 制 要 显 示 的 记 录 格 式, 这 样 的 输 出 便 于 后 期 编 程 提 取 分 析, 像 这 样 : $ git log --pretty=format:"%h - %an, %ar : %s" ca82a6d - Scott Chacon, 11 months ago : changed the version number 085bb3b - Scott Chacon, 11 months ago : removed unnecessary test code a11bef0 - Scott Chacon, 11 months ago : first commit 表 2-1 列 出 了 常 用 的 格 式 占 位 符 写 法 及 其 代 表 的 意 义 表 2.1: 选 项 说 明 %H 提 交 对 象 (commit) 的 完 整 哈 希 字 串 31

42 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 表 2.1: 选 项 说 明 %h 提 交 对 象 的 简 短 哈 希 字 串 %T 树 对 象 (tree) 的 完 整 哈 希 字 串 %t 树 对 象 的 简 短 哈 希 字 串 %P 父 对 象 (parent) 的 完 整 哈 希 字 串 %p 父 对 象 的 简 短 哈 希 字 串 %an %ae 作 者 (author) 的 名 字 作 者 的 电 子 邮 件 地 址 %ad 作 者 修 订 日 期 ( 可 以 用 -date= 选 项 定 制 格 式 ) %ar %cn %ce %cd %cr 作 者 修 订 日 期, 按 多 久 以 前 的 方 式 显 示 提 交 者 (commi er) 的 名 字 提 交 者 的 电 子 邮 件 地 址 提 交 日 期 提 交 日 期, 按 多 久 以 前 的 方 式 显 示 %s 提 交 说 明 你 一 定 奇 怪 作 者 (author) 和 提 交 者 (commi er) 之 间 究 竟 有 何 差 别, 其 实 作 者 指 的 是 实 际 作 出 修 改 的 人, 提 交 者 指 的 是 最 后 将 此 工 作 成 果 提 交 到 仓 库 的 人 所 以, 当 你 为 某 个 项 目 发 布 补 丁, 然 后 某 个 核 心 成 员 将 你 的 补 丁 并 入 项 目 时, 你 就 是 作 者, 而 那 个 核 心 成 员 就 是 提 交 者 我 们 会 在 第 五 章 再 详 细 介 绍 两 者 之 间 的 细 微 差 别 用 oneline 或 format 时 结 合 --graph 选 项, 可 以 看 到 开 头 多 出 一 些 ASCII 字 符 串 表 示 的 简 单 图 形, 形 象 地 展 示 了 每 个 提 交 所 在 的 分 支 及 其 分 化 衍 合 情 况 在 我 们 之 前 提 到 的 Grit 项 目 仓 库 中 可 以 看 到 : $ git log --pretty=format:"%h %s" --graph * 2d3acf9 ignore errors from SIGCHLD on trap * 5e3ee11 Merge branch 'master' of git://github.com/dustin/grit \ * 420eac9 Added a method for getting the current branch. * 30e367c timeout code and tests * 5a09431 add timeout protection to grit * e1193f8 support for heads with slashes in them 32

43 Sco Chacon Pro Git 中 文 版 2.3 节 查 看 提 交 历 史 / * d6016bc require time for xmlschema * 11d191e Merge branch 'defunkt' into local 以 上 只 是 简 单 介 绍 了 一 些 git log 命 令 支 持 的 选 项 表 2-2 还 列 出 了 一 些 其 他 常 用 的 选 项 及 其 释 义 表 2.2: 选 项 说 明 -p 按 补 丁 格 式 显 示 每 个 更 新 之 间 的 差 异 --word-diff 按 word diff 格 式 显 示 差 异 --stat 显 示 每 次 更 新 的 文 件 修 改 统 计 信 息 --shortstat 只 显 示 stat 中 最 后 的 行 数 修 改 添 加 移 除 统 计 --name-only 仅 在 提 交 信 息 后 显 示 已 修 改 的 文 件 清 单 --name-status 显 示 新 增 修 改 删 除 的 文 件 清 单 --abbrev-commit 仅 显 示 SHA-1 的 前 几 个 字 符, 而 非 所 有 的 40 个 字 符 --relative-date 使 用 较 短 的 相 对 时 间 显 示 ( 比 如, 2 weeks ago ) --graph 显 示 ASCII 图 形 表 示 的 分 支 合 并 历 史 --pretty 使 用 其 他 格 式 显 示 历 史 提 交 信 息 可 用 的 选 项 包 括 oneline, short,full,fuller 和 format( 后 跟 指 定 格 式 ) --oneline --pretty=oneline --abbrev-commit 的 简 化 用 法 限 制 输 出 长 度 除 了 定 制 输 出 格 式 的 选 项 之 外,git log 还 有 许 多 非 常 实 用 的 限 制 输 出 长 度 的 选 项, 也 就 是 只 输 出 部 分 提 交 信 息 之 前 我 们 已 经 看 到 过 -2 了, 它 只 显 示 最 近 的 两 条 提 交, 实 际 上, 这 是 -<n> 选 项 的 写 法, 其 中 的 n 可 以 是 任 何 自 然 数, 表 示 仅 显 示 最 近 的 若 干 条 提 交 不 过 实 践 中 我 们 是 不 太 用 这 个 选 项 的,Git 在 输 出 所 有 提 交 时 会 自 动 调 用 分 页 程 序 (less), 要 看 更 早 的 更 新 只 需 翻 到 下 页 即 可 另 外 还 有 按 照 时 间 作 限 制 的 选 项, 比 如 --since 和 --until 下 面 的 命 令 列 出 所 有 最 近 两 周 内 的 提 交 : $ git log --since=2.weeks 33

44 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 你 可 以 给 出 各 种 时 间 格 式, 比 如 说 具 体 的 某 一 天 ( ), 或 者 是 多 久 以 前 ( 2 years 1 day 3 minutes ago ) 还 可 以 给 出 若 干 搜 索 条 件, 列 出 符 合 的 提 交 用 --author 选 项 显 示 指 定 作 者 的 提 交, 用 --grep 选 项 搜 索 提 交 说 明 中 的 关 键 字 ( 请 注 意, 如 果 要 得 到 同 时 满 足 这 两 个 选 项 搜 索 条 件 的 提 交, 就 必 须 用 --all-match 选 项 否 则, 满 足 任 意 一 个 条 件 的 提 交 都 会 被 匹 配 出 来 ) 另 一 个 真 正 实 用 的 git log 选 项 是 路 径 (path), 如 果 只 关 心 某 些 文 件 或 者 目 录 的 历 史 提 交, 可 以 在 git log 选 项 的 最 后 指 定 它 们 的 路 径 因 为 是 放 在 最 后 位 置 上 的 选 项, 所 以 用 两 个 短 划 线 (--) 隔 开 之 前 的 选 项 和 后 面 限 定 的 路 径 名 表 2-3 还 列 出 了 其 他 常 用 的 类 似 选 项 表 2.3: 选 项 说 -(n) 明 仅 显 示 最 近 的 n 条 提 交 --since, --after 仅 显 示 指 定 时 间 之 后 的 提 交 --until, --before 仅 显 示 指 定 时 间 之 前 的 提 交 --author 仅 显 示 指 定 作 者 相 关 的 提 交 --committer 仅 显 示 指 定 提 交 者 相 关 的 提 交 来 看 一 个 实 际 的 例 子, 如 果 要 查 看 Git 仓 库 中,2008 年 10 月 期 间,Junio Hamano 提 交 的 但 未 合 并 的 测 试 脚 本 ( 位 于 项 目 的 t/ 目 录 下 的 文 件 ), 可 以 用 下 面 的 查 询 命 令 : $ git log --pretty="%h - %s" --author=gitster --since=" " \ --before=" " --no-merges -- t/ 5610e3b - Fix testcase failure when extended attribute acd3b9e - Enhance hold_lock_file_for_{update,append}() f demonstrate breakage of detached checkout wi d1a43f2 - reset --hard/read-tree --reset -u: remove un 51a94af - Fix "checkout --track -b newbranch" on detac b0ad11e - pull: allow "git pull origin $something:$cur 的 6 条 Git 项 目 有 20,000 多 条 提 交, 但 我 们 给 出 搜 索 选 项 后, 仅 列 出 了 其 中 满 足 条 件 使 用 图 形 化 工 具 查 阅 提 交 历 史 有 时 候 图 形 化 工 具 更 容 易 展 示 历 史 提 交 的 变 化, 随 Git 一 同 发 布 的 gitk 就 是 这 样 一 种 工 具 它 是 用 Tcl/Tk 写 成 的, 基 本 上 相 当 于 git log 命 令 的 可 视 化 版 本, 凡 34

45 Sco Chacon Pro Git 中 文 版 2.4 节 撤 消 操 作 是 git log 可 以 用 的 选 项 也 都 能 用 在 gitk 上 在 项 目 工 作 目 录 中 输 入 gitk 命 令 后, 就 会 启 动 图 2-2 所 示 的 界 面 图 2.2: gitk 的 图 形 界 面 上 半 个 窗 口 显 示 的 是 历 次 提 交 的 分 支 祖 先 图 谱, 下 半 个 窗 口 显 示 当 前 点 选 的 提 交 对 应 的 具 体 差 异 2.4 撤 消 操 作 任 何 时 候, 你 都 有 可 能 需 要 撤 消 刚 才 所 做 的 某 些 操 作 接 下 来, 我 们 会 介 绍 一 些 基 本 的 撤 消 操 作 相 关 的 命 令 请 注 意, 有 些 撤 销 操 作 是 不 可 逆 的, 所 以 请 务 必 谨 慎 小 心, 一 旦 失 误, 就 有 可 能 丢 失 部 分 工 作 成 果 修 改 最 后 一 次 提 交 有 时 候 我 们 提 交 完 了 才 发 现 漏 掉 了 几 个 文 件 没 有 加, 或 者 提 交 信 息 写 错 了 想 要 撤 消 刚 才 的 提 交 操 作, 可 以 使 用 --amend 选 项 重 新 提 交 : $ git commit --amend 此 命 令 将 使 用 当 前 的 暂 存 区 域 快 照 提 交 如 果 刚 才 提 交 完 没 有 作 任 何 改 动, 直 接 运 行 此 命 令 的 话, 相 当 于 有 机 会 重 新 编 辑 提 交 说 明, 但 将 要 提 交 的 文 件 快 照 和 之 前 的 一 样 启 动 文 本 编 辑 器 后, 会 看 到 上 次 提 交 时 的 说 明, 编 辑 它 确 认 没 问 题 后 保 存 退 出, 就 会 使 用 新 的 提 交 说 明 覆 盖 刚 才 失 误 的 提 交 如 果 刚 才 提 交 时 忘 了 暂 存 某 些 修 改, 可 以 先 补 上 暂 存 操 作, 然 后 再 运 行 --amend 提 交 : 35

46 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 $ git commit -m 'initial commit' $ git add forgotten_file $ git commit --amend 内 容 上 面 的 三 条 命 令 最 终 只 是 产 生 一 个 提 交, 第 二 个 提 交 命 令 修 正 了 第 一 个 的 提 交 取 消 已 经 暂 存 的 文 件 接 下 来 的 两 个 小 节 将 演 示 如 何 取 消 暂 存 区 域 中 的 文 件, 以 及 如 何 取 消 工 作 目 录 中 已 修 改 的 文 件 不 用 担 心, 查 看 文 件 状 态 的 时 候 就 提 示 了 该 如 何 撤 消, 所 以 不 需 要 死 记 硬 背 来 看 下 面 的 例 子, 有 两 个 修 改 过 的 文 件, 我 们 想 要 分 开 提 交, 但 不 小 心 用 git add. 全 加 到 了 暂 存 区 域 该 如 何 撤 消 暂 存 其 中 的 一 个 文 件 呢? 其 实,git status 的 命 令 输 出 已 经 告 诉 了 我 们 该 怎 么 做 : $ git add. $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: modified: README.txt benchmarks.rb 就 在 Changes to be commi ed 下 面, 括 号 中 有 提 示, 可 以 使 用 git reset HEAD <file>... 的 方 式 取 消 暂 存 好 吧, 我 们 来 试 试 取 消 暂 存 benchmarks.rb 文 件 : $ git reset HEAD benchmarks.rb Unstaged changes after reset: M benchmarks.rb $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) 36

47 Sco Chacon Pro Git 中 文 版 2.4 节 撤 消 操 作 modified: README.txt Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: benchmarks.rb 这 条 命 令 看 起 来 有 些 古 怪, 先 别 管, 能 用 就 行 现 在 benchmarks.rb 文 件 又 回 到 了 之 前 已 修 改 未 暂 存 的 状 态 取 消 对 文 件 的 修 改 如 果 觉 得 刚 才 对 benchmarks.rb 的 修 改 完 全 没 有 必 要, 该 如 何 取 消 修 改, 回 到 之 前 的 状 态 ( 也 就 是 修 改 之 前 的 版 本 ) 呢?git status 同 样 提 示 了 具 体 的 撤 消 方 法, 接 着 上 面 的 例 子, 现 在 未 暂 存 区 域 看 起 来 像 这 样 : Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: benchmarks.rb 在 第 二 个 括 号 中, 我 们 看 到 了 抛 弃 文 件 修 改 的 命 令 ( 至 少 在 Git 以 及 更 高 版 本 中 会 这 样 提 示, 如 果 你 还 在 用 老 版 本, 我 们 强 烈 建 议 你 升 级, 以 获 取 最 佳 的 用 户 体 验 ), 让 我 们 试 试 看 : $ git checkout -- benchmarks.rb $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: README.txt 可 以 看 到, 该 文 件 已 经 恢 复 到 修 改 前 的 版 本 你 可 能 已 经 意 识 到 了, 这 条 命 令 有 些 危 险, 所 有 对 文 件 的 修 改 都 没 有 了, 因 为 我 们 刚 刚 把 之 前 版 本 的 文 件 复 制 过 来 37

48 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 重 写 了 此 文 件 所 以 在 用 这 条 命 令 前, 请 务 必 确 定 真 的 不 再 需 要 保 留 刚 才 的 修 改 如 果 只 是 想 回 退 版 本, 同 时 保 留 刚 才 的 修 改 以 便 将 来 继 续 工 作, 可 以 用 下 章 介 绍 的 stashing 和 分 支 来 处 理, 应 该 会 更 好 些 记 住, 任 何 已 经 提 交 到 Git 的 都 可 以 被 恢 复 即 便 在 已 经 删 除 的 分 支 中 的 提 交, 或 者 用 --amend 重 新 改 写 的 提 交, 都 可 以 被 恢 复 ( 关 于 数 据 恢 复 的 内 容 见 第 九 章 ) 所 以, 你 可 能 失 去 的 数 据, 仅 限 于 没 有 提 交 过 的, 对 Git 来 说 它 们 就 像 从 未 存 在 过 一 样 2.5 远 程 仓 库 的 使 用 要 参 与 任 何 一 个 Git 项 目 的 协 作, 必 须 要 了 解 该 如 何 管 理 远 程 仓 库 远 程 仓 库 是 指 托 管 在 网 络 上 的 项 目 仓 库, 可 能 会 有 好 多 个, 其 中 有 些 你 只 能 读, 另 外 有 些 可 以 写 同 他 人 协 作 开 发 某 个 项 目 时, 需 要 管 理 这 些 远 程 仓 库, 以 便 推 送 或 拉 取 数 据, 分 享 各 自 的 工 作 进 展 管 理 远 程 仓 库 的 工 作, 包 括 添 加 远 程 库, 移 除 废 弃 的 远 程 库, 管 理 各 式 远 程 库 分 支, 定 义 是 否 跟 踪 这 些 分 支, 等 等 本 节 我 们 将 详 细 讨 论 远 程 库 的 管 理 和 使 用 查 看 当 前 的 远 程 库 要 查 看 当 前 配 置 有 哪 些 远 程 仓 库, 可 以 用 git remote 命 令, 它 会 列 出 每 个 远 程 库 的 简 短 名 字 在 克 隆 完 某 个 项 目 后, 至 少 可 以 看 到 一 个 名 为 origin 的 远 程 库, Git 默 认 使 用 这 个 名 字 来 标 识 你 所 克 隆 的 原 始 仓 库 : $ git clone git://github.com/schacon/ticgit.git Cloning into 'ticgit'... remote: Reusing existing pack: 1857, done. remote: Total 1857 (delta 0), reused 0 (delta 0) Receiving objects: 100% (1857/1857), KiB KiB/s, done. Resolving deltas: 100% (772/772), done. Checking connectivity... done. $ cd ticgit $ git remote origin 也 可 以 加 上 -v 选 项 ( 译 注 : 此 为 --verbose 的 简 写, 取 首 字 母 ), 显 示 对 应 的 克 隆 地 址 : 38

49 Sco Chacon Pro Git 中 文 版 2.5 节 远 程 仓 库 的 使 用 $ git remote -v origin git://github.com/schacon/ticgit.git (fetch) origin git://github.com/schacon/ticgit.git (push) 如 果 有 多 个 远 程 仓 库, 此 命 令 将 全 部 列 出 比 如 在 我 的 Grit 项 目 中, 可 以 看 到 : $ cd grit $ git remote -v bakkdoor git://github.com/bakkdoor/grit.git cho45 git://github.com/cho45/grit.git defunkt git://github.com/defunkt/grit.git koke git://github.com/koke/grit.git origin git@github.com:mojombo/grit.git 这 样 一 来, 我 就 可 以 非 常 轻 松 地 从 这 些 用 户 的 仓 库 中, 拉 取 他 们 的 提 交 到 本 地 请 注 意, 上 面 列 出 的 地 址 只 有 origin 用 的 是 SSH URL 链 接, 所 以 也 只 有 这 个 仓 库 我 能 推 送 数 据 上 去 ( 我 们 会 在 第 四 章 解 释 原 因 ) 添 加 远 程 仓 库 要 添 加 一 个 新 的 远 程 仓 库, 可 以 指 定 一 个 简 单 的 名 字, 以 便 将 来 引 用, 运 行 git remote add [shortname] [url]: $ git remote origin $ git remote add pb git://github.com/paulboone/ticgit.git $ git remote -v origin git://github.com/schacon/ticgit.git pb git://github.com/paulboone/ticgit.git 现 在 可 以 用 字 符 串 pb 指 代 对 应 的 仓 库 地 址 了 比 如 说, 要 抓 取 所 有 Paul 有 的, 但 本 地 仓 库 没 有 的 信 息, 可 以 运 行 git fetch pb: $ git fetch pb remote: Counting objects: 58, done. 39

50 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 remote: Compressing objects: 100% (41/41), done. remote: Total 44 (delta 24), reused 1 (delta 0) Unpacking objects: 100% (44/44), done. From git://github.com/paulboone/ticgit * [new branch] master -> pb/master * [new branch] ticgit -> pb/ticgit 现 在,Paul 的 主 干 分 支 (master) 已 经 完 全 可 以 在 本 地 访 问 了, 对 应 的 名 字 是 pb/master, 你 可 以 将 它 合 并 到 自 己 的 某 个 分 支, 或 者 切 换 到 这 个 分 支, 看 看 有 些 什 么 有 趣 的 更 新 从 远 程 仓 库 抓 取 数 据 正 如 之 前 所 看 到 的, 可 以 用 下 面 的 命 令 从 远 程 仓 库 抓 取 数 据 到 本 地 : $ git fetch [remote-name] 此 命 令 会 到 远 程 仓 库 中 拉 取 所 有 你 本 地 仓 库 中 还 没 有 的 数 据 运 行 完 成 后, 你 就 可 以 在 本 地 访 问 该 远 程 仓 库 中 的 所 有 分 支, 将 其 中 某 个 分 支 合 并 到 本 地, 或 者 只 是 取 出 某 个 分 支, 一 探 究 竟 ( 我 们 会 在 第 三 章 详 细 讨 论 关 于 分 支 的 概 念 和 操 作 ) 如 果 是 克 隆 了 一 个 仓 库, 此 命 令 会 自 动 将 远 程 仓 库 归 于 origin 名 下 所 以,git fetch origin 会 抓 取 从 你 上 次 克 隆 以 来 别 人 上 传 到 此 远 程 仓 库 中 的 所 有 更 新 ( 或 是 上 次 fetch 以 来 别 人 提 交 的 更 新 ) 有 一 点 很 重 要, 需 要 记 住,fetch 命 令 只 是 将 远 端 的 数 据 拉 到 本 地 仓 库, 并 不 自 动 合 并 到 当 前 工 作 分 支, 只 有 当 你 确 实 准 备 好 了, 才 能 手 工 合 并 如 果 设 置 了 某 个 分 支 用 于 跟 踪 某 个 远 端 仓 库 的 分 支 ( 参 见 下 节 及 第 三 章 的 内 容 ), 可 以 使 用 git pull 命 令 自 动 抓 取 数 据 下 来, 然 后 将 远 端 分 支 自 动 合 并 到 本 地 仓 库 中 当 前 分 支 在 日 常 工 作 中 我 们 经 常 这 么 用, 既 快 且 好 实 际 上, 默 认 情 况 下 git clone 命 令 本 质 上 就 是 自 动 创 建 了 本 地 的 master 分 支 用 于 跟 踪 远 程 仓 库 中 的 master 分 支 ( 假 设 远 程 仓 库 确 实 有 master 分 支 ) 所 以 一 般 我 们 运 行 git pull, 目 的 都 是 要 从 原 始 克 隆 的 远 端 仓 库 中 抓 取 数 据 后, 合 并 到 工 作 目 录 中 的 当 前 分 支 推 送 数 据 到 远 程 仓 库 项 目 进 行 到 一 个 阶 段, 要 同 别 人 分 享 目 前 的 成 果, 可 以 将 本 地 仓 库 中 的 数 据 推 送 到 远 程 仓 库 实 现 这 个 任 务 的 命 令 很 简 单 :git push [remote-name] [branch-name] 如 果 要 把 本 地 的 master 分 支 推 送 到 origin 服 务 器 上 ( 再 次 说 明 下, 克 隆 操 作 会 自 动 使 用 默 认 的 master 和 origin 名 字 ), 可 以 运 行 下 面 的 命 令 : 40

51 Sco Chacon Pro Git 中 文 版 2.5 节 远 程 仓 库 的 使 用 $ git push origin master 只 有 在 所 克 隆 的 服 务 器 上 有 写 权 限, 或 者 同 一 时 刻 没 有 其 他 人 在 推 数 据, 这 条 命 令 才 会 如 期 完 成 任 务 如 果 在 你 推 数 据 前, 已 经 有 其 他 人 推 送 了 若 干 更 新, 那 你 的 推 送 操 作 就 会 被 驳 回 你 必 须 先 把 他 们 的 更 新 抓 取 到 本 地, 合 并 到 自 己 的 项 目 中, 然 后 才 可 以 再 次 推 送 有 关 推 送 数 据 到 远 程 仓 库 的 详 细 内 容 见 第 三 章 查 看 远 程 仓 库 信 息 我 们 可 以 通 过 命 令 git remote show [remote-name] 查 看 某 个 远 程 仓 库 的 详 细 信 息, 比 如 要 看 所 克 隆 的 origin 仓 库, 可 以 运 行 : $ git remote show origin * remote origin URL: git://github.com/schacon/ticgit.git Remote branch merged with 'git pull' while on branch master master Tracked remote branches master ticgit 除 了 对 应 的 克 隆 地 址 外, 它 还 给 出 了 许 多 额 外 的 信 息 它 友 善 地 告 诉 你 如 果 是 在 master 分 支, 就 可 以 用 git pull 命 令 抓 取 数 据 合 并 到 本 地 另 外 还 列 出 了 所 有 处 于 跟 踪 状 态 中 的 远 端 分 支 上 面 的 例 子 非 常 简 单, 而 随 着 使 用 Git 的 深 入,git remote show 给 出 的 信 息 可 能 会 像 这 样 : $ git remote show origin * remote origin URL: git@github.com:defunkt/github.git Remote branch merged with 'git pull' while on branch issues issues Remote branch merged with 'git pull' while on branch master master New remote branches (next fetch will store in remotes/origin) caching 41

52 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 Stale tracking branches (use 'git remote prune') libwalker walker2 Tracked remote branches acl apiv2 dashboard2 issues master postgres Local branch pushed with 'git push' master:master 它 告 诉 我 们, 运 行 git push 时 缺 省 推 送 的 分 支 是 什 么 ( 译 注 : 最 后 两 行 ) 它 还 显 示 了 有 哪 些 远 端 分 支 还 没 有 同 步 到 本 地 ( 译 注 : 第 六 行 的 caching 分 支 ), 哪 些 已 同 步 到 本 地 的 远 端 分 支 在 远 端 服 务 器 上 已 被 删 除 ( 译 注 :Stale tracking branches 下 面 的 两 个 分 支 ), 以 及 运 行 git pull 时 将 自 动 合 并 哪 些 分 支 ( 译 注 : 前 四 行 中 列 出 的 issues 和 master 分 支 ) 远 程 仓 库 的 删 除 和 重 命 名 在 新 版 Git 中 可 以 用 git remote rename 命 令 修 改 某 个 远 程 仓 库 在 本 地 的 简 称, 比 如 想 把 pb 改 成 paul, 可 以 这 么 运 行 : $ git remote rename pb paul $ git remote origin paul 注 意, 对 远 程 仓 库 的 重 命 名, 也 会 使 对 应 的 分 支 名 称 发 生 变 化, 原 来 的 pb/master 分 支 现 在 成 了 paul/master 碰 到 远 端 仓 库 服 务 器 迁 移, 或 者 原 来 的 克 隆 镜 像 不 再 使 用, 又 或 者 某 个 参 与 者 不 再 贡 献 代 码, 那 么 需 要 移 除 对 应 的 远 端 仓 库, 可 以 运 行 git remote rm 命 令 : $ git remote rm paul $ git remote origin 42

53 Sco Chacon Pro Git 中 文 版 2.6 节 打 标 签 2.6 打 标 签 同 大 多 数 VCS 一 样,Git 也 可 以 对 某 一 时 间 点 上 的 版 本 打 上 标 签 人 们 在 发 布 某 个 软 件 版 本 ( 比 如 v1.0 等 等 ) 的 时 候, 经 常 这 么 做 本 节 我 们 一 起 来 学 习 如 何 列 出 所 有 可 用 的 标 签, 如 何 新 建 标 签, 以 及 各 种 不 同 类 型 标 签 之 间 的 差 别 列 显 已 有 的 标 签 列 出 现 有 标 签 的 命 令 非 常 简 单, 直 接 运 行 git tag 即 可 : $ git tag v0.1 v1.3 显 示 的 标 签 按 字 母 顺 序 排 列, 所 以 标 签 的 先 后 并 不 表 示 重 要 程 度 的 轻 重 我 们 可 以 用 特 定 的 搜 索 模 式 列 出 符 合 条 件 的 标 签 在 Git 自 身 项 目 仓 库 中, 有 着 超 过 240 个 标 签, 如 果 你 只 对 系 列 的 版 本 感 兴 趣, 可 以 运 行 下 面 的 命 令 : $ git tag -l 'v1.4.2.*' v v v v 新 建 标 签 Git 使 用 的 标 签 有 两 种 类 型 : 轻 量 级 的 (lightweight) 和 含 附 注 的 (annotated) 轻 量 级 标 签 就 像 是 个 不 会 变 化 的 分 支, 实 际 上 它 就 是 个 指 向 特 定 提 交 对 象 的 引 用 而 含 附 注 标 签, 实 际 上 是 存 储 在 仓 库 中 的 一 个 独 立 对 象, 它 有 自 身 的 校 验 和 信 息, 包 含 着 标 签 的 名 字, 电 子 邮 件 地 址 和 日 期, 以 及 标 签 说 明, 标 签 本 身 也 允 许 使 用 GNU Privacy Guard (GPG) 来 签 署 或 验 证 一 般 我 们 都 建 议 使 用 含 附 注 型 的 标 签, 以 便 保 留 相 关 信 息 ; 当 然, 如 果 只 是 临 时 性 加 注 标 签, 或 者 不 需 要 旁 注 额 外 信 息, 用 轻 量 级 标 签 也 没 问 题 含 附 注 的 标 签 创 建 一 个 含 附 注 类 型 的 标 签 非 常 简 单, 用 -a ( 译 注 : 取 annotated 的 首 字 母 ) 指 定 标 签 名 字 即 可 : 43

54 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 $ git tag -a v1.4 -m 'my version 1.4' $ git tag v0.1 v1.3 v1.4 而 -m 选 项 则 指 定 了 对 应 的 标 签 说 明,Git 会 将 此 说 明 一 同 保 存 在 标 签 对 象 中 如 果 没 有 给 出 该 选 项,Git 会 启 动 文 本 编 辑 软 件 供 你 输 入 标 签 说 明 可 以 使 用 git show 命 令 查 看 相 应 标 签 的 版 本 信 息, 并 连 同 显 示 打 标 签 时 的 提 交 对 象 $ git show v1.4 tag v1.4 Tagger: Scott Chacon <schacon@ge .com> Date: Mon Feb 9 14:45: my version 1.4 commit b64cf874c3557a0f3547bd83b3ff6 Merge: 4a447f7... a6b4c97... Author: Scott Chacon <schacon@ge .com> Date: Sun Feb 8 19:02: Merge branch 'experiment' 我 们 可 以 看 到 在 提 交 对 象 信 息 上 面, 列 出 了 此 标 签 的 提 交 者 和 提 交 时 间, 以 及 相 应 的 标 签 说 明 签 署 标 签 如 果 你 有 自 己 的 私 钥, 还 可 以 用 GPG 来 签 署 标 签, 只 需 要 把 之 前 的 -a 改 为 -s ( 译 注 : 取 signed 的 首 字 母 ) 即 可 : $ git tag -s v1.5 -m 'my signed 1.5 tag' You need a passphrase to unlock the secret key for user: "Scott Chacon <schacon@ge .com>" 1024-bit DSA key, ID F721C45A, created

55 Sco Chacon Pro Git 中 文 版 2.6 节 打 标 签 现 在 再 运 行 git show 会 看 到 对 应 的 GPG 签 名 也 附 在 其 内 : $ git show v1.5 tag v1.5 Tagger: Scott Chacon <schacon@ge .com> Date: Mon Feb 9 15:22: my signed 1.5 tag -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.8 (Darwin) ieyeabecaayfakmquriacgkqon3dxfchxfr5caceimn+zxlkggjqf0qyiqbwgysn Ki0An2JeAVUCAiJ7Ox6ZEtK+NvZAj82/ =WryJ -----END PGP SIGNATURE----- commit b64cf874c3557a0f3547bd83b3ff6 Merge: 4a447f7... a6b4c97... Author: Scott Chacon <schacon@ge .com> Date: Sun Feb 8 19:02: Merge branch 'experiment' 稍 后 我 们 再 学 习 如 何 验 证 已 经 签 署 的 标 签 轻 量 级 标 签 轻 量 级 标 签 实 际 上 就 是 一 个 保 存 着 对 应 提 交 对 象 的 校 验 和 信 息 的 文 件 要 创 建 这 样 的 标 签, 一 个 -a,-s 或 -m 选 项 都 不 用, 直 接 给 出 标 签 名 字 即 可 : $ git tag v1.4-lw $ git tag v0.1 v1.3 v1.4 v1.4-lw v1.5 45

56 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 现 在 运 行 git show 查 看 此 标 签 信 息, 就 只 有 相 应 的 提 交 对 象 摘 要 : $ git show v1.4-lw commit b64cf874c3557a0f3547bd83b3ff6 Merge: 4a447f7... a6b4c97... Author: Scott Chacon <schacon@ge .com> Date: Sun Feb 8 19:02: Merge branch 'experiment' 验 证 标 签 可 以 使 用 git tag -v [tag-name] ( 译 注 : 取 verify 的 首 字 母 ) 的 方 式 验 证 已 经 签 署 的 标 签 此 命 令 会 调 用 GPG 来 验 证 签 名, 所 以 你 需 要 有 签 署 者 的 公 钥, 存 放 在 keyring 中, 才 能 验 证 : $ git tag -v v object babd8ee7ea23e6a5c392bb739348b1eb61 type commit tag v tagger Junio C Hamano <junkio@cox.net> GIT Minor fixes since 1.4.2, including git-mv and git-http with alternates. gpg: Signature made Wed Sep 13 02:08: PDT using DSA key ID F3119B9A gpg: Good signature from "Junio C Hamano <junkio@cox.net>" gpg: aka "[jpeg image of size 1513]" Primary key fingerprint: A E066 C9A7 4A7D C0C6 D9A4 F311 9B9A 若 是 没 有 签 署 者 的 公 钥, 会 报 告 类 似 下 面 这 样 的 错 误 : gpg: Signature made Wed Sep 13 02:08: PDT using DSA key ID F3119B9A gpg: Can't check signature: public key not found error: could not verify the tag 'v ' 46

57 Sco Chacon Pro Git 中 文 版 2.6 节 打 标 签 后 期 加 注 标 签 你 甚 至 可 以 在 后 期 对 早 先 的 某 次 提 交 加 注 标 签 比 如 在 下 面 展 示 的 提 交 历 史 中 : $ git log --pretty=oneline b64cf874c3557a0f3547bd83b3ff6 Merge branch 'experiment' a6b4c97498bd301d84096da251c98a07c7723e65 beginning write support 0d52aaab da7686c15f77a3d64d one more thing 6d52a271eda dd79daabbc4d9b6008e Merge branch 'experiment' 0b7434d86859cc7b8c3d5e1dddfed66ff742fcbc added a commit function 4682c bdd616e23b64b0857d832627b added a todo file 166ae0c4d3f420721acbb115cc33848dfcc2121a started write support 9fceb02d0ae598e95dc970b74767f19372d61af8 updated rakefile 964f16d36dfccde844893cac5b347e7b3d44abbc commit the todo 8a5cbc430f1a9c3d00faaeffd a updated readme 我 们 忘 了 在 提 交 updated rakefile 后 为 此 项 目 打 上 版 本 号 v1.2, 没 关 系, 现 在 也 能 做 只 要 在 打 标 签 的 时 候 跟 上 对 应 提 交 对 象 的 校 验 和 ( 或 前 几 位 字 符 ) 即 可 : $ git tag -a v1.2 9fceb02 可 以 看 到 我 们 已 经 补 上 了 标 签 : $ git tag v0.1 v1.2 v1.3 v1.4 v1.4-lw v1.5 $ git show v1.2 tag v1.2 Tagger: Scott Chacon <schacon@ge .com> Date: Mon Feb 9 15:32: version

58 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 commit 9fceb02d0ae598e95dc970b74767f19372d61af8 Author: Magnus Chacon <mchacon@ge .com> Date: Sun Apr 27 20:43: updated rakefile 分 享 标 签 默 认 情 况 下,git push 并 不 会 把 标 签 传 送 到 远 端 服 务 器 上, 只 有 通 过 显 式 命 令 才 能 分 享 标 签 到 远 端 仓 库 其 命 令 格 式 如 同 推 送 分 支, 运 行 git push origin [tagname] 即 可 : $ git push origin v1.5 Counting objects: 50, done. Compressing objects: 100% (38/38), done. Writing objects: 100% (44/44), 4.56 KiB, done. Total 44 (delta 18), reused 8 (delta 1) To git@github.com:schacon/simplegit.git * [new tag] v1.5 -> v1.5 如 果 要 一 次 推 送 所 有 本 地 新 增 的 标 签 上 去, 可 以 使 用 --tags 选 项 : $ git push origin --tags Counting objects: 50, done. Compressing objects: 100% (38/38), done. Writing objects: 100% (44/44), 4.56 KiB, done. Total 44 (delta 18), reused 8 (delta 1) To git@github.com:schacon/simplegit.git * [new tag] v0.1 -> v0.1 * [new tag] v1.2 -> v1.2 * [new tag] v1.4 -> v1.4 * [new tag] v1.4-lw -> v1.4-lw * [new tag] v1.5 -> v 现 在, 其 他 人 克 隆 共 享 仓 库 或 拉 取 数 据 同 步 后, 也 会 看 到 这 些 标 签

59 Sco Chacon Pro Git 中 文 版 2.7 节 技 巧 和 窍 门 2.7 技 巧 和 窍 门 在 结 束 本 章 之 前, 我 还 想 和 大 家 分 享 一 些 Git 使 用 的 技 巧 和 窍 门 很 多 使 用 Git 的 开 发 者 可 能 根 本 就 没 用 过 这 些 技 巧, 我 们 也 不 是 说 在 读 过 本 书 后 非 得 用 这 些 技 巧 不 可, 但 至 少 应 该 有 所 了 解 吧 说 实 话, 有 了 这 些 小 窍 门, 我 们 的 工 作 可 以 变 得 更 简 单, 更 轻 松, 更 高 效 自 动 补 全 如 果 你 用 的 是 Bash shell, 可 以 试 试 看 Git 提 供 的 自 动 补 全 脚 本 下 载 Git 的 源 代 码, 进 入 contrib/completion 目 录, 会 看 到 一 个 git-completion.bash 文 件 将 此 文 件 复 制 到 你 自 己 的 用 户 主 目 录 中 ( 译 注 : 按 照 下 面 的 示 例, 还 应 改 名 加 上 点 : cp git-completion.bash ~/.git-completion.bash), 并 把 下 面 一 行 内 容 添 加 到 你 的.bashrc 文 件 中 : source ~/.git-completion.bash 也 可 以 为 系 统 上 所 有 用 户 都 设 置 默 认 使 用 此 脚 本 Mac 上 将 此 脚 本 复 制 到 /opt/local/etc/bash_completion.d 目 录 中,Linux 上 则 复 制 到 /etc/bash_completion.d/ 目 录 中 这 两 处 目 录 中 的 脚 本, 都 会 在 Bash 启 动 时 自 动 加 载 如 果 在 Windows 上 安 装 了 msysgit, 默 认 使 用 的 Git Bash 就 已 经 配 好 了 这 个 自 动 补 全 脚 本, 可 以 直 接 使 用 在 输 入 Git 命 令 的 时 候 可 以 敲 两 次 跳 格 键 (Tab), 就 会 看 到 列 出 所 有 匹 配 的 可 用 命 令 建 议 : $ git co<tab><tab> commit config 此 例 中, 键 入 git co 然 后 连 按 两 次 Tab 键, 会 看 到 两 个 相 关 的 建 议 ( 命 令 ) commit 和 config 继 而 输 入 m<tab> 会 自 动 完 成 git commit 命 令 的 输 入 命 令 的 选 项 也 可 以 用 这 种 方 式 自 动 完 成, 其 实 这 种 情 况 更 实 用 些 比 如 运 行 git log 的 时 候 忘 了 相 关 选 项 的 名 字, 可 以 输 入 开 头 的 几 个 字 母, 然 后 敲 Tab 键 看 看 有 哪 些 匹 配 的 : $ git log --s<tab> --shortstat --since= --src-prefix= --stat --summary 这 个 技 巧 不 错 吧, 可 以 节 省 很 多 输 入 和 查 阅 文 档 的 时 间 49

60 第 2 章 Git 基 础 Sco Chacon Pro Git 中 文 版 Git 命 令 别 名 Git 并 不 会 推 断 你 输 入 的 几 个 字 符 将 会 是 哪 条 命 令, 不 过 如 果 想 偷 懒, 少 敲 几 个 命 令 的 字 符, 可 以 用 git config 为 命 令 设 置 别 名 来 看 看 下 面 的 例 子 : $ git config --global alias.co checkout $ git config --global alias.br branch $ git config --global alias.ci commit $ git config --global alias.st status 现 在, 如 果 要 输 入 git commit 只 需 键 入 git ci 即 可 而 随 着 Git 使 用 的 深 入, 会 有 很 多 经 常 要 用 到 的 命 令, 遇 到 这 种 情 况, 不 妨 建 个 别 名 提 高 效 率 使 用 这 种 技 术 还 可 以 创 造 出 新 的 命 令, 比 方 说 取 消 暂 存 文 件 时 的 输 入 比 较 繁 琐, 可 以 自 己 设 置 一 下 : $ git config --global alias.unstage 'reset HEAD --' 这 样 一 来, 下 面 的 两 条 命 令 完 全 等 同 : $ git unstage filea $ git reset HEAD filea 显 然, 使 用 别 名 的 方 式 看 起 来 更 清 楚 另 外, 我 们 还 经 常 设 置 last 命 令 : $ git config --global alias.last 'log -1 HEAD' 然 后 要 看 最 后 一 次 的 提 交 信 息, 就 变 得 简 单 多 了 : $ git last commit 66938dae3329c7aebe598c2246a8e6af90d04646 Author: Josh Goebel <dreamer3@example.com> Date: Tue Aug 26 19:48:

61 Sco Chacon Pro Git 中 文 版 2.8 节 小 结 test for current head Signed-off-by: Scott Chacon 可 以 看 出, 实 际 上 Git 只 是 简 单 地 在 命 令 中 替 换 了 你 设 置 的 别 名 不 过 有 时 候 我 们 希 望 运 行 某 个 外 部 命 令, 而 非 Git 的 子 命 令, 这 个 好 办, 只 需 要 在 命 令 前 加 上! 就 行 如 果 你 自 己 写 了 些 处 理 Git 仓 库 信 息 的 脚 本 的 话, 就 可 以 用 这 种 技 术 包 装 起 来 作 为 演 示, 我 们 可 以 设 置 用 git visual 启 动 gitk: $ git config --global alias.visual '!gitk' 2.8 小 结 到 目 前 为 止, 你 已 经 学 会 了 最 基 本 的 Git 本 地 操 作 : 创 建 和 克 隆 仓 库, 做 出 修 改, 暂 存 并 提 交 这 些 修 改, 以 及 查 看 所 有 历 史 修 改 记 录 接 下 来, 我 们 将 学 习 Git 的 必 杀 技 特 性 : 分 支 模 型 51

62

63 第 3 章 Git 分 支 几 乎 每 一 种 版 本 控 制 系 统 都 以 某 种 形 式 支 持 分 支 使 用 分 支 意 味 着 你 可 以 从 开 发 主 线 上 分 离 开 来, 然 后 在 不 影 响 主 线 的 同 时 继 续 工 作 在 很 多 版 本 控 制 系 统 中, 这 是 个 昂 贵 的 过 程, 常 常 需 要 创 建 一 个 源 代 码 目 录 的 完 整 副 本, 对 大 型 项 目 来 说 会 花 费 很 长 时 间 有 人 把 Git 的 分 支 模 型 称 为 必 杀 技 特 性, 而 正 是 因 为 它, 将 Git 从 版 本 控 制 系 统 家 族 里 区 分 出 来 Git 有 何 特 别 之 处 呢?Git 的 分 支 可 谓 是 难 以 置 信 的 轻 量 级, 它 的 新 建 操 作 几 乎 可 以 在 瞬 间 完 成, 并 且 在 不 同 分 支 间 切 换 起 来 也 差 不 多 一 样 快 和 许 多 其 他 版 本 控 制 系 统 不 同,Git 鼓 励 在 工 作 流 程 中 频 繁 使 用 分 支 与 合 并, 哪 怕 一 天 之 内 进 行 许 多 次 都 没 有 关 系 理 解 分 支 的 概 念 并 熟 练 运 用 后, 你 才 会 意 识 到 为 什 么 Git 是 一 个 如 此 强 大 而 独 特 的 工 具, 并 从 此 真 正 改 变 你 的 开 发 方 式 3.1 何 谓 分 支 为 了 理 解 Git 分 支 的 实 现 方 式, 我 们 需 要 回 顾 一 下 Git 是 如 何 储 存 数 据 的 或 许 你 还 记 得 第 一 章 的 内 容,Git 保 存 的 不 是 文 件 差 异 或 者 变 化 量, 而 只 是 一 系 列 文 件 快 照 在 Git 中 提 交 时, 会 保 存 一 个 提 交 (commit) 对 象, 该 对 象 包 含 一 个 指 向 暂 存 内 容 快 照 的 指 针, 包 含 本 次 提 交 的 作 者 等 相 关 附 属 信 息, 包 含 零 个 或 多 个 指 向 该 提 交 对 象 的 父 对 象 指 针 : 首 次 提 交 是 没 有 直 接 祖 先 的, 普 通 提 交 有 一 个 祖 先, 由 两 个 或 多 个 分 支 合 并 产 生 的 提 交 则 有 多 个 祖 先 为 直 观 起 见, 我 们 假 设 在 工 作 目 录 中 有 三 个 文 件, 准 备 将 它 们 暂 存 后 提 交 暂 存 操 作 会 对 每 一 个 文 件 计 算 校 验 和 ( 即 第 一 章 中 提 到 的 SHA-1 哈 希 字 串 ), 然 后 把 当 前 版 本 的 文 件 快 照 保 存 到 Git 仓 库 中 (Git 使 用 blob 类 型 的 对 象 存 储 这 些 快 照 ), 并 将 校 验 和 加 入 暂 存 区 域 : $ git add README test.rb LICENSE $ git commit -m 'initial commit of my project' 53

64 第 3 章 Git 分 支 Sco Chacon Pro Git 中 文 版 当 使 用 git commit 新 建 一 个 提 交 对 象 前,Git 会 先 计 算 每 一 个 子 目 录 ( 本 例 中 就 是 项 目 根 目 录 ) 的 校 验 和, 然 后 在 Git 仓 库 中 将 这 些 目 录 保 存 为 树 (tree) 对 象 之 后 Git 创 建 的 提 交 对 象, 除 了 包 含 相 关 提 交 信 息 以 外, 还 包 含 着 指 向 这 个 树 对 象 ( 项 目 根 目 录 ) 的 指 针, 如 此 它 就 可 以 在 将 来 需 要 的 时 候, 重 现 此 次 快 照 的 内 容 了 现 在,Git 仓 库 中 有 五 个 对 象 : 三 个 表 示 文 件 快 照 内 容 的 blob 对 象 ; 一 个 记 录 着 目 录 树 内 容 及 其 中 各 个 文 件 对 应 blob 对 象 索 引 的 tree 对 象 ; 以 及 一 个 包 含 指 向 tree 对 象 ( 根 目 录 ) 的 索 引 和 其 他 提 交 信 息 元 数 据 的 commit 对 象 概 念 上 来 说, 仓 库 中 的 各 个 对 象 保 存 的 数 据 和 相 互 关 系 看 起 来 如 图 3-1 所 示 : 图 3.1: 单 个 提 交 对 象 在 仓 库 中 的 数 据 结 构 作 些 修 改 后 再 次 提 交, 那 么 这 次 的 提 交 对 象 会 包 含 一 个 指 向 上 次 提 交 对 象 的 指 针 ( 译 注 : 即 下 图 中 的 parent 对 象 ) 两 次 提 交 后, 仓 库 历 史 会 变 成 图 3-2 的 样 子 : 图 3.2: 多 个 提 交 对 象 之 间 的 链 接 关 系 现 在 来 谈 分 支 Git 中 的 分 支, 其 实 本 质 上 仅 仅 是 个 指 向 commit 对 象 的 可 变 指 针 Git 会 使 用 master 作 为 分 支 的 默 认 名 字 在 若 干 次 提 交 后, 你 其 实 已 经 有 了 一 个 指 向 最 后 一 次 提 交 对 象 的 master 分 支, 它 在 每 次 提 交 的 时 候 都 会 自 动 向 前 移 动 那 么,Git 又 是 如 何 创 建 一 个 新 的 分 支 的 呢? 答 案 很 简 单, 创 建 一 个 新 的 分 支 指 针 比 如 新 建 一 个 testing 分 支, 可 以 使 用 git branch 命 令 : $ git branch testing 54

65 Sco Chacon Pro Git 中 文 版 3.1 节 何 谓 分 支 图 3.3: 分 支 其 实 就 是 从 某 个 提 交 对 象 往 回 看 的 历 史 这 会 在 当 前 commit 对 象 上 新 建 一 个 分 支 指 针 ( 见 图 3-4) 图 3.4: 多 个 分 支 指 向 提 交 数 据 的 历 史 那 么,Git 是 如 何 知 道 你 当 前 在 哪 个 分 支 上 工 作 的 呢? 其 实 答 案 也 很 简 单, 它 保 存 着 一 个 名 为 HEAD 的 特 别 指 针 请 注 意 它 和 你 熟 知 的 许 多 其 他 版 本 控 制 系 统 ( 比 如 Subversion 或 CVS) 里 的 HEAD 概 念 大 不 相 同 在 Git 中, 它 是 一 个 指 向 你 正 在 工 作 中 的 本 地 分 支 的 指 针 ( 译 注 : 将 HEAD 想 象 为 当 前 分 支 的 别 名 ) 运 行 git branch 命 令, 仅 仅 是 建 立 了 一 个 新 的 分 支, 但 不 会 自 动 切 换 到 这 个 分 支 中 去, 所 以 在 这 个 例 子 中, 我 们 依 然 还 在 master 分 支 里 工 作 ( 参 考 图 3-5) 图 3.5: HEAD 指 向 当 前 所 在 的 分 支 要 切 换 到 其 他 分 支, 可 以 执 行 git checkout 命 令 我 们 现 在 转 换 到 新 建 的 testing 分 支 : 55

66 第 3 章 Git 分 支 Sco Chacon Pro Git 中 文 版 $ git checkout testing 这 样 HEAD 就 指 向 了 testing 分 支 ( 见 图 3-6) 图 3.6: HEAD 在 你 转 换 分 支 时 指 向 新 的 分 支 这 样 的 实 现 方 式 会 给 我 们 带 来 什 么 好 处 呢? 好 吧, 现 在 不 妨 再 提 交 一 次 : $ vim test.rb $ git commit -a -m 'made a change' 图 3-7 展 示 了 提 交 后 的 结 果 图 3.7: 每 次 提 交 后 HEAD 随 着 分 支 一 起 向 前 移 动 非 常 有 趣, 现 在 testing 分 支 向 前 移 动 了 一 格, 而 master 分 支 仍 然 指 向 原 先 git checkout 时 所 在 的 commit 对 象 现 在 我 们 回 到 master 分 支 看 看 : $ git checkout master 56

67 Sco Chacon Pro Git 中 文 版 3.1 节 何 谓 分 支 图 3-8 显 示 了 结 果 图 3.8: HEAD 在 一 次 checkout 之 后 移 动 到 了 另 一 个 分 支 这 条 命 令 做 了 两 件 事 它 把 HEAD 指 针 移 回 到 master 分 支, 并 把 工 作 目 录 中 的 文 件 换 成 了 master 分 支 所 指 向 的 快 照 内 容 也 就 是 说, 现 在 开 始 所 做 的 改 动, 将 始 于 本 项 目 中 一 个 较 老 的 版 本 它 的 主 要 作 用 是 将 testing 分 支 里 作 出 的 修 改 暂 时 取 消, 这 样 你 就 可 以 向 另 一 个 方 向 进 行 开 发 我 们 作 些 修 改 后 再 次 提 交 : $ vim test.rb $ git commit -a -m 'made other changes' 现 在 我 们 的 项 目 提 交 历 史 产 生 了 分 叉 ( 如 图 3-9 所 示 ), 因 为 刚 才 我 们 创 建 了 一 个 分 支, 转 换 到 其 中 进 行 了 一 些 工 作, 然 后 又 回 到 原 来 的 主 分 支 进 行 了 另 外 一 些 工 作 这 些 改 变 分 别 孤 立 在 不 同 的 分 支 里 : 我 们 可 以 在 不 同 分 支 里 反 复 切 换, 并 在 时 机 成 熟 时 把 它 们 合 并 到 一 起 而 所 有 这 些 工 作, 仅 仅 需 要 branch 和 checkout 这 两 条 命 令 就 可 以 完 成 图 3.9: 不 同 流 向 的 分 支 历 史 57

68 第 3 章 Git 分 支 Sco Chacon Pro Git 中 文 版 由 于 Git 中 的 分 支 实 际 上 仅 是 一 个 包 含 所 指 对 象 校 验 和 (40 个 字 符 长 度 SHA-1 字 串 ) 的 文 件, 所 以 创 建 和 销 毁 一 个 分 支 就 变 得 非 常 廉 价 说 白 了, 新 建 一 个 分 支 就 是 向 一 个 文 件 写 入 41 个 字 节 ( 外 加 一 个 换 行 符 ) 那 么 简 单, 当 然 也 就 很 快 了 这 和 大 多 数 版 本 控 制 系 统 形 成 了 鲜 明 对 比, 它 们 管 理 分 支 大 多 采 取 备 份 所 有 项 目 文 件 到 特 定 目 录 的 方 式, 所 以 根 据 项 目 文 件 数 量 和 大 小 不 同, 可 能 花 费 的 时 间 也 会 有 相 当 大 的 差 别, 快 则 几 秒, 慢 则 数 分 钟 而 Git 的 实 现 与 项 目 复 杂 度 无 关, 它 永 远 可 以 在 几 毫 秒 的 时 间 内 完 成 分 支 的 创 建 和 切 换 同 时, 因 为 每 次 提 交 时 都 记 录 了 祖 先 信 息 ( 译 注 : 即 parent 对 象 ), 将 来 要 合 并 分 支 时, 寻 找 恰 当 的 合 并 基 础 ( 译 注 : 即 共 同 祖 先 ) 的 工 作 其 实 已 经 自 然 而 然 地 摆 在 那 里 了, 所 以 实 现 起 来 非 常 容 易 Git 鼓 励 开 发 者 频 繁 使 用 分 支, 正 是 因 为 有 着 这 些 特 性 作 保 障 接 下 来 看 看, 我 们 为 什 么 应 该 频 繁 使 用 分 支 3.2 分 支 的 新 建 与 合 并 现 在 让 我 们 来 看 一 个 简 单 的 分 支 与 合 并 的 例 子, 实 际 工 作 中 大 体 也 会 用 到 这 样 的 工 作 流 程 : 1. 开 发 某 个 网 站 2. 为 实 现 某 个 新 的 需 求, 创 建 一 个 分 支 3. 在 这 个 分 支 上 开 展 工 作 假 设 此 时, 你 突 然 接 到 一 个 电 话 说 有 个 很 严 重 的 问 题 需 要 紧 急 修 补, 那 么 可 以 按 照 下 面 的 方 式 处 理 : 1. 返 回 到 原 先 已 经 发 布 到 生 产 服 务 器 上 的 分 支 2. 为 这 次 紧 急 修 补 建 立 一 个 新 分 支, 并 在 其 中 修 复 问 题 3. 通 过 测 试 后, 回 到 生 产 服 务 器 所 在 的 分 支, 将 修 补 分 支 合 并 进 来, 然 后 再 推 送 到 生 产 服 务 器 上 4. 切 换 到 之 前 实 现 新 需 求 的 分 支, 继 续 工 作 分 支 的 新 建 与 切 换 3-10) 首 先, 我 们 假 设 你 正 在 项 目 中 愉 快 地 工 作, 并 且 已 经 提 交 了 几 次 更 新 ( 见 图 图 3.10: 一 个 简 短 的 提 交 历 史 58

69 Sco Chacon Pro Git 中 文 版 3.2 节 分 支 的 新 建 与 合 并 现 在, 你 决 定 要 修 补 问 题 追 踪 系 统 上 的 #53 问 题 顺 带 说 明 下,Git 并 不 同 任 何 特 定 的 问 题 追 踪 系 统 打 交 道 这 里 为 了 说 明 要 解 决 的 问 题, 才 把 新 建 的 分 支 取 名 为 iss53 要 新 建 并 切 换 到 该 分 支, 运 行 git checkout 并 加 上 -b 参 数 : $ git checkout -b iss53 Switched to a new branch 'iss53' 这 相 当 于 执 行 下 面 这 两 条 命 令 : $ git branch iss53 $ git checkout iss53 图 3-11 示 意 该 命 令 的 执 行 结 果 图 3.11: 创 建 了 一 个 新 分 支 的 指 针 接 着 你 开 始 尝 试 修 复 问 题, 在 提 交 了 若 干 次 更 新 后,iss53 分 支 的 指 针 也 会 随 着 向 前 推 进, 因 为 它 就 是 当 前 分 支 ( 换 句 话 说, 当 前 的 HEAD 指 针 正 指 向 iss53, 见 图 3-12): $ vim index.html $ git commit -a -m 'added a new footer [issue 53]' 图 3.12: iss53 分 支 随 工 作 进 展 向 前 推 进 59

70 第 3 章 Git 分 支 Sco Chacon Pro Git 中 文 版 现 在 你 就 接 到 了 那 个 网 站 问 题 的 紧 急 电 话, 需 要 马 上 修 补 有 了 Git, 我 们 就 不 需 要 同 时 发 布 这 个 补 丁 和 iss53 里 作 出 的 修 改, 也 不 需 要 在 创 建 和 发 布 该 补 丁 到 服 务 器 之 前 花 费 大 力 气 来 复 原 这 些 修 改 唯 一 需 要 的 仅 仅 是 切 换 回 master 分 支 不 过 在 此 之 前, 留 心 你 的 暂 存 区 或 者 工 作 目 录 里, 那 些 还 没 有 提 交 的 修 改, 它 会 和 你 即 将 检 出 的 分 支 产 生 冲 突 从 而 阻 止 Git 为 你 切 换 分 支 切 换 分 支 的 时 候 最 好 保 持 一 个 清 洁 的 工 作 区 域 稍 后 会 介 绍 几 个 绕 过 这 种 问 题 的 办 法 ( 分 别 叫 做 stashing 和 commit amending) 目 前 已 经 提 交 了 所 有 的 修 改, 所 以 接 下 来 可 以 正 常 转 换 到 master 分 支 : $ git checkout master Switched to branch 'master' 此 时 工 作 目 录 中 的 内 容 和 你 在 解 决 问 题 #53 之 前 一 模 一 样, 你 可 以 集 中 精 力 进 行 紧 急 修 补 这 一 点 值 得 牢 记 :Git 会 把 工 作 目 录 的 内 容 恢 复 为 检 出 某 分 支 时 它 所 指 向 的 那 个 提 交 对 象 的 快 照 它 会 自 动 添 加 删 除 和 修 改 文 件 以 确 保 目 录 的 内 容 和 你 当 时 提 交 时 完 全 一 样 接 下 来, 你 得 进 行 紧 急 修 补 我 们 创 建 一 个 紧 急 修 补 分 支 hotfix 来 开 展 工 作, 直 到 搞 定 ( 见 图 3-13): $ git checkout -b hotfix Switched to a new branch 'hotfix' $ vim index.html $ git commit -a -m 'fixed the broken address' [hotfix 3a0874c] fixed the broken address 1 files changed, 1 deletion(-) 图 3.13: hotfix 分 支 是 从 master 分 支 所 在 点 分 化 出 来 的 有 必 要 作 些 测 试, 确 保 修 补 是 成 功 的, 然 后 回 到 master 分 支 并 把 它 合 并 进 来, 然 后 发 布 到 生 产 服 务 器 用 git merge 命 令 来 进 行 合 并 : 60

71 Sco Chacon Pro Git 中 文 版 3.2 节 分 支 的 新 建 与 合 并 $ git checkout master $ git merge hotfix Updating f42c576..3a0874c Fast-forward README 1-1 file changed, 1 deletion(-) 请 注 意, 合 并 时 出 现 了 Fast forward 的 提 示 由 于 当 前 master 分 支 所 在 的 提 交 对 象 是 要 并 入 的 hotfix 分 支 的 直 接 上 游,Git 只 需 把 master 分 支 指 针 直 接 右 移 换 句 话 说, 如 果 顺 着 一 个 分 支 走 下 去 可 以 到 达 另 一 个 分 支 的 话, 那 么 Git 在 合 并 两 者 时, 只 会 简 单 地 把 指 针 右 移, 因 为 这 种 单 线 的 历 史 分 支 不 存 在 任 何 需 要 解 决 的 分 歧, 所 以 这 种 合 并 过 程 可 以 称 为 快 进 (Fast forward) 现 在 最 新 的 修 改 已 经 在 当 前 master 分 支 所 指 向 的 提 交 对 象 中 了, 可 以 部 署 到 生 产 服 务 器 上 去 了 ( 见 图 3-14) 图 3.14: 合 并 之 后,master 分 支 和 hotfix 分 支 指 向 同 一 位 置 在 那 个 超 级 重 要 的 修 补 发 布 以 后, 你 想 要 回 到 被 打 扰 之 前 的 工 作 由 于 当 前 hotfix 分 支 和 master 都 指 向 相 同 的 提 交 对 象, 所 以 hotfix 已 经 完 成 了 历 史 使 命, 可 以 删 掉 了 使 用 git branch 的 -d 选 项 执 行 删 除 操 作 : $ git branch -d hotfix Deleted branch hotfix (was 3a0874c). 现 在 回 到 之 前 未 完 成 的 #53 问 题 修 复 分 支 上 继 续 工 作 ( 图 3-15): $ git checkout iss53 61

72 第 3 章 Git 分 支 Sco Chacon Pro Git 中 文 版 Switched to branch 'iss53' $ vim index.html $ git commit -a -m 'finished the new footer [issue 53]' [iss53 ad82d7a] finished the new footer [issue 53] 1 file changed, 1 insertion(+) 图 3.15: iss53 分 支 可 以 不 受 影 响 继 续 推 进 值 得 注 意 的 是 之 前 hotfix 分 支 的 修 改 内 容 尚 未 包 含 到 iss53 中 来 如 果 需 要 纳 入 此 次 修 补, 可 以 用 git merge master 把 master 分 支 合 并 到 iss53; 或 者 等 iss53 完 成 之 后, 再 将 iss53 分 支 中 的 更 新 并 入 master 分 支 的 合 并 在 问 题 #53 相 关 的 工 作 完 成 之 后, 可 以 合 并 回 master 分 支 实 际 操 作 同 前 面 合 并 hotfix 分 支 差 不 多, 只 需 回 到 master 分 支, 运 行 git merge 命 令 指 定 要 合 并 进 来 的 分 支 : $ git checkout master $ git merge iss53 Auto-merging README Merge made by the 'recursive' strategy. README file changed, 1 insertion(+) 请 注 意, 这 次 合 并 操 作 的 底 层 实 现, 并 不 同 于 之 前 hotfix 的 并 入 方 式 因 为 这 次 你 的 开 发 历 史 是 从 更 早 的 地 方 开 始 分 叉 的 由 于 当 前 master 分 支 所 指 向 的 提 交 对 象 (C4) 并 不 是 iss53 分 支 的 直 接 祖 先,Git 不 得 不 进 行 一 些 额 外 处 理 就 此 例 而 言,Git 会 用 两 个 分 支 的 末 端 (C4 和 C5) 以 及 它 们 的 共 同 祖 先 (C2) 进 行 一 次 简 单 的 三 方 合 并 计 算 图 3-16 用 红 框 标 出 了 Git 用 于 合 并 的 三 个 提 交 对 象 : 62

73 Sco Chacon Pro Git 中 文 版 3.2 节 分 支 的 新 建 与 合 并 图 3.16: Git 为 分 支 合 并 自 动 识 别 出 最 佳 的 同 源 合 并 点 这 次,Git 没 有 简 单 地 把 分 支 指 针 右 移, 而 是 对 三 方 合 并 后 的 结 果 重 新 做 一 个 新 的 快 照, 并 自 动 创 建 一 个 指 向 它 的 提 交 对 象 (C6)( 见 图 3-17) 这 个 提 交 对 象 比 较 特 殊, 它 有 两 个 祖 先 (C4 和 C5) 值 得 一 提 的 是 Git 可 以 自 己 裁 决 哪 个 共 同 祖 先 才 是 最 佳 合 并 基 础 ; 这 和 CVS 或 Subversion(1.5 以 后 的 版 本 ) 不 同, 它 们 需 要 开 发 者 手 工 指 定 合 并 基 础 所 以 此 特 性 让 Git 的 合 并 操 作 比 其 他 系 统 都 要 简 单 不 少 图 3.17: Git 自 动 创 建 了 一 个 包 含 了 合 并 结 果 的 提 交 对 象 既 然 之 前 的 工 作 成 果 已 经 合 并 到 master 了, 那 么 iss53 也 就 没 用 了 你 可 以 就 此 删 除 它, 并 在 问 题 追 踪 系 统 里 关 闭 该 问 题 $ git branch -d iss 遇 到 冲 突 时 的 分 支 合 并 有 时 候 合 并 操 作 并 不 会 如 此 顺 利 如 果 在 不 同 的 分 支 中 都 修 改 了 同 一 个 文 件 的 同 一 部 分,Git 就 无 法 干 净 地 把 两 者 合 到 一 起 ( 译 注 : 逻 辑 上 说, 这 种 问 题 只 能 由 人 63

74 第 3 章 Git 分 支 Sco Chacon Pro Git 中 文 版 来 裁 决 ) 如 果 你 在 解 决 问 题 #53 的 过 程 中 修 改 了 hotfix 中 修 改 的 部 分, 将 得 到 类 似 下 面 的 结 果 : $ git merge iss53 Auto-merging index.html CONFLICT (content): Merge conflict in index.html Automatic merge failed; fix conflicts and then commit the result. Git 作 了 合 并, 但 没 有 提 交, 它 会 停 下 来 等 你 解 决 冲 突 要 看 看 哪 些 文 件 在 合 并 时 发 生 冲 突, 可 以 用 git status 查 阅 : $ git status On branch master You have unmerged paths. (fix conflicts and run "git commit") Unmerged paths: (use "git add <file>..." to mark resolution) both modified: index.html no changes added to commit (use "git add" and/or "git commit -a") 任 何 包 含 未 解 决 冲 突 的 文 件 都 会 以 未 合 并 (unmerged) 的 状 态 列 出 Git 会 在 有 冲 突 的 文 件 里 加 入 标 准 的 冲 突 解 决 标 记, 可 以 通 过 它 们 来 手 工 定 位 并 解 决 这 些 冲 突 可 以 看 到 此 文 件 包 含 类 似 下 面 这 样 的 部 分 : <<<<<<< HEAD <div id="footer">contact : .support@github.com</div> ======= <div id="footer"> please contact us at support@github.com </div> >>>>>>> iss53 可 以 看 到 ======= 隔 开 的 上 半 部 分, 是 HEAD( 即 master 分 支, 在 运 行 merge 命 令 时 所 切 换 到 的 分 支 ) 中 的 内 容, 下 半 部 分 是 在 iss53 分 支 中 的 内 容 解 决 冲 突 64

75 Sco Chacon Pro Git 中 文 版 3.2 节 分 支 的 新 建 与 合 并 的 办 法 无 非 是 二 者 选 其 一 或 者 由 你 亲 自 整 合 到 一 起 比 如 你 可 以 通 过 把 这 段 内 容 替 换 为 下 面 这 样 来 解 决 : <div id="footer"> please contact us at .support@github.com </div> 这 个 解 决 方 案 各 采 纳 了 两 个 分 支 中 的 一 部 分 内 容, 而 且 我 还 删 除 了 <<<<<<<, ======= 和 >>>>>>> 这 些 行 在 解 决 了 所 有 文 件 里 的 所 有 冲 突 后, 运 行 git add 将 把 它 们 标 记 为 已 解 决 状 态 ( 译 注 : 实 际 上 就 是 来 一 次 快 照 保 存 到 暂 存 区 域 ) 因 为 一 旦 暂 存, 就 表 示 冲 突 已 经 解 决 如 果 你 想 用 一 个 有 图 形 界 面 的 工 具 来 解 决 这 些 问 题, 不 妨 运 行 git mergetool, 它 会 调 用 一 个 可 视 化 的 合 并 工 具 并 引 导 你 解 决 所 有 冲 突 : $ git mergetool This message is displayed because 'merge.tool' is not configured. See 'git mergetool --tool-help' or 'git help config' for more details. 'git mergetool' will now attempt to use one of the following tools: opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge ecmerge p4m Merging: index.html Normal merge conflict for 'index.html': {local}: modified file {remote}: modified file Hit return to start merge resolution tool (opendiff): 如 果 不 想 用 默 认 的 合 并 工 具 (Git 为 我 默 认 选 择 了 opendiff, 因 为 我 在 Mac 上 运 行 了 该 命 令 ), 你 可 以 在 上 方 merge tool candidates 里 找 到 可 用 的 合 并 工 具 列 表, 输 入 你 想 用 的 工 具 名 我 们 将 在 第 七 章 讨 论 怎 样 改 变 环 境 中 的 默 认 值 退 出 合 并 工 具 以 后,Git 会 询 问 你 合 并 是 否 成 功 如 果 回 答 是, 它 会 为 你 把 相 关 文 件 暂 存 起 来, 以 表 明 状 态 为 已 解 决 再 运 行 一 次 git status 来 确 认 所 有 冲 突 都 已 解 决 : $ git status On branch master 65

76 第 3 章 Git 分 支 Sco Chacon Pro Git 中 文 版 Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: index.html 如 果 觉 得 满 意 了, 并 且 确 认 所 有 冲 突 都 已 解 决, 也 就 是 进 入 了 暂 存 区, 就 可 以 用 git commit 来 完 成 这 次 合 并 提 交 提 交 的 记 录 差 不 多 是 这 样 : Merge branch 'iss53' Conflicts: index.html # # It looks like you may be committing a merge. # If this is not correct, please remove the file #.git/merge_head # and try again. # 如 果 想 给 将 来 看 这 次 合 并 的 人 一 些 方 便, 可 以 修 改 该 信 息, 提 供 更 多 合 并 细 节 比 如 你 都 作 了 哪 些 改 动, 以 及 这 么 做 的 原 因 有 时 候 裁 决 冲 突 的 理 由 并 不 直 接 或 明 显, 有 必 要 略 加 注 解 3.3 分 支 的 管 理 到 目 前 为 止, 你 已 经 学 会 了 如 何 创 建 合 并 和 删 除 分 支 除 此 之 外, 我 们 还 需 要 学 习 如 何 管 理 分 支, 在 日 后 的 常 规 工 作 中 会 经 常 用 到 下 面 介 绍 的 管 理 命 令 git branch 命 令 不 仅 仅 能 创 建 和 删 除 分 支, 如 果 不 加 任 何 参 数, 它 会 给 出 当 前 所 有 分 支 的 清 单 : $ git branch iss53 * master testing 66

77 Sco Chacon Pro Git 中 文 版 3.3 节 分 支 的 管 理 注 意 看 master 分 支 前 的 * 字 符 : 它 表 示 当 前 所 在 的 分 支 也 就 是 说, 如 果 现 在 提 交 更 新,master 分 支 将 随 着 开 发 进 度 前 移 若 要 查 看 各 个 分 支 最 后 一 个 提 交 对 象 的 信 息, 运 行 git branch -v: $ git branch -v iss53 93b412c fix javascript issue * master 7a98805 Merge branch 'iss53' testing 782fd34 add scott to the author list in the readmes 要 从 该 清 单 中 筛 选 出 你 已 经 ( 或 尚 未 ) 与 当 前 分 支 合 并 的 分 支, 可 以 用 --merged 和 --no-merged 选 项 (Git 以 上 版 本 ) 比 如 用 git branch --merged 查 看 哪 些 分 支 已 被 并 入 当 前 分 支 ( 译 注 : 也 就 是 说 哪 些 分 支 是 当 前 分 支 的 直 接 上 游 ): $ git branch --merged iss53 * master 之 前 我 们 已 经 合 并 了 iss53, 所 以 在 这 里 会 看 到 它 一 般 来 说, 列 表 中 没 有 * 的 分 支 通 常 都 可 以 用 git branch -d 来 删 掉 原 因 很 简 单, 既 然 已 经 把 它 们 所 包 含 的 工 作 整 合 到 了 其 他 分 支, 删 掉 也 不 会 损 失 什 么 另 外 可 以 用 git branch --no-merged 查 看 尚 未 合 并 的 工 作 : $ git branch --no-merged testing 它 会 显 示 还 未 合 并 进 来 的 分 支 由 于 这 些 分 支 中 还 包 含 着 尚 未 合 并 进 来 的 工 作 成 果, 所 以 简 单 地 用 git branch -d 删 除 该 分 支 会 提 示 错 误, 因 为 那 样 做 会 丢 失 数 据 : $ git branch -d testing error: The branch 'testing' is not fully merged. If you are sure you want to delete it, run 'git branch -D testing'. 不 过, 如 果 你 确 实 想 要 删 除 该 分 支 上 的 改 动, 可 以 用 大 写 的 删 除 选 项 -D 强 制 执 行, 就 像 上 面 提 示 信 息 中 给 出 的 那 样 67

78 第 3 章 Git 分 支 Sco Chacon Pro Git 中 文 版 3.4 利 用 分 支 进 行 开 发 的 工 作 流 程 现 在 我 们 已 经 学 会 了 新 建 分 支 和 合 并 分 支, 可 以 ( 或 应 该 ) 用 它 来 做 点 什 么 呢? 在 本 节, 我 们 会 介 绍 一 些 利 用 分 支 进 行 开 发 的 工 作 流 程 而 正 是 由 于 分 支 管 理 的 便 捷, 才 衍 生 出 了 这 类 典 型 的 工 作 模 式, 你 可 以 根 据 项 目 的 实 际 情 况 选 择 一 种 用 用 看 长 期 分 支 由 于 Git 使 用 简 单 的 三 方 合 并, 所 以 就 算 在 较 长 一 段 时 间 内, 反 复 多 次 把 某 个 分 支 合 并 到 另 一 分 支, 也 不 是 什 么 难 事 也 就 是 说, 你 可 以 同 时 拥 有 多 个 开 放 的 分 支, 每 个 分 支 用 于 完 成 特 定 的 任 务, 随 着 开 发 的 推 进, 你 可 以 随 时 把 某 个 特 性 分 支 的 成 果 并 到 其 他 分 支 中 许 多 使 用 Git 的 开 发 者 都 喜 欢 用 这 种 方 式 来 开 展 工 作, 比 如 仅 在 master 分 支 中 保 留 完 全 稳 定 的 代 码, 即 已 经 发 布 或 即 将 发 布 的 代 码 与 此 同 时, 他 们 还 有 一 个 名 为 develop 或 next 的 平 行 分 支, 专 门 用 于 后 续 的 开 发, 或 仅 用 于 稳 定 性 测 试 当 然 并 不 是 说 一 定 要 绝 对 稳 定, 不 过 一 旦 进 入 某 种 稳 定 状 态, 便 可 以 把 它 合 并 到 master 里 这 样, 在 确 保 这 些 已 完 成 的 特 性 分 支 ( 短 期 分 支, 比 如 之 前 的 iss53 分 支 ) 能 够 通 过 所 有 测 试, 并 且 不 会 引 入 更 多 错 误 之 后, 就 可 以 并 到 主 干 分 支 中, 等 待 下 一 次 的 发 布 本 质 上 我 们 刚 才 谈 论 的, 是 随 着 提 交 对 象 不 断 右 移 的 指 针 稳 定 分 支 的 指 针 总 是 在 提 交 历 史 中 落 后 一 大 截, 而 前 沿 分 支 总 是 比 较 靠 前 ( 见 图 3-18) 图 3.18: 稳 定 分 支 总 是 比 较 老 旧 或 者 把 它 们 想 象 成 工 作 流 水 线, 或 许 更 好 理 解 一 些, 经 过 测 试 的 提 交 对 象 集 合 被 遴 选 到 更 稳 定 的 流 水 线 ( 见 图 3-19) 图 3.19: 想 象 成 流 水 线 可 能 会 容 易 点 你 可 以 用 这 招 维 护 不 同 层 次 的 稳 定 性 某 些 大 项 目 还 会 有 个 proposed( 建 议 ) 或 pu(proposed updates, 建 议 更 新 ) 分 支, 它 包 含 着 那 些 可 能 还 没 有 成 熟 到 进 入 68

79 Sco Chacon Pro Git 中 文 版 3.4 节 利 用 分 支 进 行 开 发 的 工 作 流 程 next 或 master 的 内 容 这 么 做 的 目 的 是 拥 有 不 同 层 次 的 稳 定 性 : 当 这 些 分 支 进 入 到 更 稳 定 的 水 平 时, 再 把 它 们 合 并 到 更 高 层 分 支 中 去 再 次 说 明 下, 使 用 多 个 长 期 分 支 的 做 法 并 非 必 需, 不 过 一 般 来 说, 对 于 特 大 型 项 目 或 特 复 杂 的 项 目, 这 么 做 确 实 更 容 易 管 理 特 性 分 支 在 任 何 规 模 的 项 目 中 都 可 以 使 用 特 性 (Topic) 分 支 一 个 特 性 分 支 是 指 一 个 短 期 的, 用 来 实 现 单 一 特 性 或 与 其 相 关 工 作 的 分 支 可 能 你 在 以 前 的 版 本 控 制 系 统 里 从 未 做 过 类 似 这 样 的 事 情, 因 为 通 常 创 建 与 合 并 分 支 消 耗 太 大 然 而 在 Git 中, 一 天 之 内 建 立 使 用 合 并 再 删 除 多 个 分 支 是 常 见 的 事 我 们 在 上 节 的 例 子 里 已 经 见 过 这 种 用 法 了 我 们 创 建 了 iss53 和 hotfix 这 两 个 特 性 分 支, 在 提 交 了 若 干 更 新 后, 把 它 们 合 并 到 主 干 分 支, 然 后 删 除 该 技 术 允 许 你 迅 速 且 完 全 的 进 行 语 境 切 换 因 为 你 的 工 作 分 散 在 不 同 的 流 水 线 里, 每 个 分 支 里 的 改 变 都 和 它 的 目 标 特 性 相 关, 浏 览 代 码 之 类 的 事 情 因 而 变 得 更 简 单 了 你 可 以 把 作 出 的 改 变 保 持 在 特 性 分 支 中 几 分 钟, 几 天 甚 至 几 个 月, 等 它 们 成 熟 以 后 再 合 并, 而 不 用 在 乎 它 们 建 立 的 顺 序 或 者 进 度 现 在 我 们 来 看 一 个 实 际 的 例 子 请 看 图 3-20, 由 下 往 上, 起 先 我 们 在 master 工 作 到 C1, 然 后 开 始 一 个 新 分 支 iss91 尝 试 修 复 91 号 缺 陷, 提 交 到 C6 的 时 候, 又 冒 出 一 个 解 决 该 问 题 的 新 办 法, 于 是 从 之 前 C4 的 地 方 又 分 出 一 个 分 支 iss91v2, 干 到 C8 的 时 候, 又 回 到 主 干 master 中 提 交 了 C9 和 C10, 再 回 到 iss91v2 继 续 工 作, 提 交 C11, 接 着, 又 冒 出 个 不 太 确 定 的 想 法, 从 master 的 最 新 提 交 C10 处 开 了 个 新 的 分 支 dumbidea 做 些 试 验 图 3.20: 拥 有 多 个 特 性 分 支 的 提 交 历 史 现 在, 假 定 两 件 事 情 : 我 们 最 终 决 定 使 用 第 二 个 解 决 方 案, 即 iss91v2 中 的 办 法 ; 另 外, 我 们 把 dumbidea 分 支 拿 给 同 事 们 看 了 以 后, 发 现 它 竟 然 是 个 天 才 之 作 69

80 第 3 章 Git 分 支 Sco Chacon Pro Git 中 文 版 所 以 接 下 来, 我 们 准 备 抛 弃 原 来 的 iss91 分 支 ( 实 际 上 会 丢 弃 C5 和 C6), 直 接 在 主 干 中 并 入 另 外 两 个 分 支 最 终 的 提 交 历 史 将 变 成 图 3-21 这 样 : 图 3.21: 合 并 了 dumbidea 和 iss91v2 后 的 分 支 历 史 请 务 必 牢 记 这 些 分 支 全 部 都 是 本 地 分 支, 这 一 点 很 重 要 当 你 在 使 用 分 支 及 合 并 的 时 候, 一 切 都 是 在 你 自 己 的 Git 仓 库 中 进 行 的 完 全 不 涉 及 与 服 务 器 的 交 互 3.5 远 程 分 支 远 程 分 支 (remote branch) 是 对 远 程 仓 库 中 的 分 支 的 索 引 它 们 是 一 些 无 法 移 动 的 本 地 分 支 ; 只 有 在 Git 进 行 网 络 交 互 时 才 会 更 新 远 程 分 支 就 像 是 书 签, 提 醒 着 你 上 次 连 接 远 程 仓 库 时 上 面 各 分 支 的 位 置 我 们 用 ( 远 程 仓 库 名 )/( 分 支 名 ) 这 样 的 形 式 表 示 远 程 分 支 比 如 我 们 想 看 看 上 次 同 origin 仓 库 通 讯 时 master 分 支 的 样 子, 就 应 该 查 看 origin/master 分 支 如 果 你 和 同 伴 一 起 修 复 某 个 问 题, 但 他 们 先 推 送 了 一 个 iss53 分 支 到 远 程 仓 库, 虽 然 你 可 能 也 有 一 个 本 地 的 iss53 分 支, 但 指 向 服 务 器 上 最 新 更 新 的 却 应 该 是 origin/iss53 分 支 可 能 有 点 乱, 我 们 不 妨 举 例 说 明 假 设 你 们 团 队 有 个 地 址 为 git.ourcompany.com 的 Git 服 务 器 如 果 你 从 这 里 克 隆,Git 会 自 动 为 你 将 此 远 程 仓 库 命 名 为 origin, 并 下 载 其 中 所 有 的 数 据, 建 立 一 个 指 向 它 的 master 分 支 的 指 针, 在 本 地 命 名 为 origin/master, 但 你 无 法 在 本 地 更 改 其 数 据 接 着,Git 建 立 一 个 属 于 你 自 己 的 本 地 master 分 支, 始 于 origin 上 master 分 支 相 同 的 位 置, 你 可 以 就 此 开 始 工 作 ( 见 图 3-22): 如 果 你 在 本 地 master 分 支 做 了 些 改 动, 与 此 同 时, 其 他 人 向 git.ourcompany.com 推 送 了 他 们 的 更 新, 那 么 服 务 器 上 的 master 分 支 就 会 向 前 推 进, 而 于 此 同 时, 你 70

81 Sco Chacon Pro Git 中 文 版 3.5 节 远 程 分 支 图 3.22: 一 次 Git 克 隆 会 建 立 你 自 己 的 本 地 分 支 master 和 远 程 分 支 origin/master, 并 且 将 它 们 都 指 向 origin 上 的 master 分 支 在 本 地 的 提 交 历 史 正 朝 向 不 同 方 向 发 展 不 过 只 要 你 不 和 服 务 器 通 讯, 你 的 origin/master 指 针 仍 然 保 持 原 位 不 会 移 动 ( 见 图 3-23) 图 3.23: 在 本 地 工 作 的 同 时 有 人 向 远 程 仓 库 推 送 内 容 会 让 提 交 历 史 开 始 分 流 可 以 运 行 git fetch origin 来 同 步 远 程 服 务 器 上 的 数 据 到 本 地 该 命 令 首 先 找 到 origin 是 哪 个 服 务 器 ( 本 例 为 git.ourcompany.com), 从 上 面 获 取 你 尚 未 拥 有 的 数 据, 更 新 你 本 地 的 数 据 库, 然 后 把 origin/master 的 指 针 移 到 它 最 新 的 位 置 上 ( 见 图 3-24) 为 了 演 示 拥 有 多 个 远 程 分 支 ( 在 不 同 的 远 程 服 务 器 上 ) 的 项 目 是 如 何 工 作 的, 我 们 假 设 你 还 有 另 一 个 仅 供 你 的 敏 捷 开 发 小 组 使 用 的 内 部 服 务 器 git.team1.ourcompany.com 可 以 用 第 二 章 中 提 到 的 git remote add 命 令 把 它 加 为 当 前 项 目 的 远 程 分 支 之 一 我 们 把 它 命 名 为 teamone, 以 便 代 替 完 整 的 Git URL 以 方 便 使 用 ( 见 图 3-25) 现 在 你 可 以 用 git fetch teamone 来 获 取 小 组 服 务 器 上 你 还 没 有 的 数 据 了 71

82 第 3 章 Git 分 支 Sco Chacon Pro Git 中 文 版 图 3.24: git fetch 命 令 会 更 新 remote 索 引 图 3.25: 把 另 一 个 服 务 器 加 为 远 程 仓 库 由 于 当 前 该 服 务 器 上 的 内 容 是 你 origin 服 务 器 上 的 子 集,Git 不 会 下 载 任 何 数 据, 而 只 是 简 单 地 创 建 一 个 名 为 teamone/master 的 远 程 分 支, 指 向 teamone 服 务 器 上 master 分 支 所 在 的 提 交 对 象 31b8e( 见 图 3-26) 推 送 本 地 分 支 要 想 和 其 他 人 分 享 某 个 本 地 分 支, 你 需 要 把 它 推 送 到 一 个 你 拥 有 写 权 限 的 远 程 仓 库 你 创 建 的 本 地 分 支 不 会 因 为 你 的 写 入 操 作 而 被 自 动 同 步 到 你 引 入 的 远 程 服 务 器 上, 你 需 要 明 确 地 执 行 推 送 分 支 的 操 作 换 句 话 说, 对 于 无 意 分 享 的 分 支, 你 尽 管 保 留 为 私 人 分 支 好 了, 而 只 推 送 那 些 协 同 工 作 要 用 到 的 特 性 分 支 72

83 Sco Chacon Pro Git 中 文 版 3.5 节 远 程 分 支 图 3.26: 你 在 本 地 有 了 一 个 指 向 teamone 服 务 器 上 master 分 支 的 索 引 如 果 你 有 个 叫 serverfix 的 分 支 需 要 和 他 人 一 起 开 发, 可 以 运 行 git push ( 远 程 仓 库 名 ) ( 分 支 名 ): $ git push origin serverfix Counting objects: 20, done. Compressing objects: 100% (14/14), done. Writing objects: 100% (15/15), 1.74 KiB, done. Total 15 (delta 5), reused 0 (delta 0) To git@github.com:schacon/simplegit.git * [new branch] serverfix -> serverfix 这 里 其 实 走 了 一 点 捷 径 Git 自 动 把 serverfix 分 支 名 扩 展 为 refs/heads/serverfix:refs/heads 意 为 取 出 我 在 本 地 的 serverfix 分 支, 推 送 到 远 程 仓 库 的 serverfix 分 支 中 去 我 们 将 在 第 九 章 进 一 步 介 绍 refs/heads/ 部 分 的 细 节, 不 过 一 般 使 用 的 时 候 都 可 以 省 略 它 也 可 以 运 行 git push origin serverfix:serverfix 来 实 现 相 同 的 效 果, 它 的 意 思 是 上 传 我 本 地 的 serverfix 分 支 到 远 程 仓 库 中 去, 仍 旧 称 它 为 serverfix 分 支 通 过 此 语 法, 你 可 以 把 本 地 分 支 推 送 到 某 个 命 名 不 同 的 远 程 分 支 : 若 想 把 远 程 分 支 叫 作 awesomebranch, 可 以 用 git push origin serverfix:awesomebranch 来 推 送 数 据 接 下 来, 当 你 的 协 作 者 再 次 从 服 务 器 上 获 取 数 据 时, 他 们 将 得 到 一 个 新 的 远 程 分 支 origin/serverfix, 并 指 向 服 务 器 上 serverfix 所 指 向 的 版 本 : $ git fetch origin remote: Counting objects: 20, done. 73

84 第 3 章 Git 分 支 Sco Chacon Pro Git 中 文 版 remote: Compressing objects: 100% (14/14), done. remote: Total 15 (delta 5), reused 0 (delta 0) Unpacking objects: 100% (15/15), done. From git@github.com:schacon/simplegit * [new branch] serverfix -> origin/serverfix 值 得 注 意 的 是, 在 fetch 操 作 下 载 好 新 的 远 程 分 支 之 后, 你 仍 然 无 法 在 本 地 编 辑 该 远 程 仓 库 中 的 分 支 换 句 话 说, 在 本 例 中, 你 不 会 有 一 个 新 的 serverfix 分 支, 有 的 只 是 一 个 你 无 法 移 动 的 origin/serverfix 指 针 如 果 要 把 该 远 程 分 支 的 内 容 合 并 到 当 前 分 支, 可 以 运 行 git merge origin/serverfix 如 果 想 要 一 份 自 己 的 serverfix 来 开 发, 可 以 在 远 程 分 支 的 基 础 上 分 化 出 一 个 新 的 分 支 来 : $ git checkout -b serverfix origin/serverfix Branch serverfix set up to track remote branch serverfix from origin. Switched to a new branch 'serverfix' 这 会 切 换 到 新 建 的 serverfix 本 地 分 支, 其 内 容 同 远 程 分 支 origin/serverfix 一 致, 这 样 你 就 可 以 在 里 面 继 续 开 发 了 跟 踪 远 程 分 支 从 远 程 分 支 checkout 出 来 的 本 地 分 支, 称 为 跟 踪 分 支 (tracking branch) 跟 踪 分 支 是 一 种 和 某 个 远 程 分 支 有 直 接 联 系 的 本 地 分 支 在 跟 踪 分 支 里 输 入 git push, Git 会 自 行 推 断 应 该 向 哪 个 服 务 器 的 哪 个 分 支 推 送 数 据 同 样, 在 这 些 分 支 里 运 行 git pull 会 获 取 所 有 远 程 索 引, 并 把 它 们 的 数 据 都 合 并 到 本 地 分 支 中 来 在 克 隆 仓 库 时,Git 通 常 会 自 动 创 建 一 个 名 为 master 的 分 支 来 跟 踪 origin/master 这 正 是 git push 和 git pull 一 开 始 就 能 正 常 工 作 的 原 因 当 然, 你 可 以 随 心 所 欲 地 设 定 为 其 它 跟 踪 分 支, 比 如 origin 上 除 了 master 之 外 的 其 它 分 支 刚 才 我 们 已 经 看 到 了 这 样 的 一 个 例 子 :git checkout -b [ 分 支 名 ] [ 远 程 名 ]/[ 分 支 名 ] 如 果 你 有 以 上 版 本 的 Git, 还 可 以 用 --track 选 项 简 化 : $ git checkout --track origin/serverfix Branch serverfix set up to track remote branch serverfix from origin. Switched to a new branch 'serverfix' 74

85 Sco Chacon Pro Git 中 文 版 3.6 节 分 支 的 衍 合 字 : 要 为 本 地 分 支 设 定 不 同 于 远 程 分 支 的 名 字, 只 需 在 第 一 个 版 本 的 命 令 里 换 个 名 $ git checkout -b sf origin/serverfix Branch sf set up to track remote branch serverfix from origin. Switched to a new branch 'sf' 了 现 在 你 的 本 地 分 支 sf 会 自 动 将 推 送 和 抓 取 数 据 的 位 置 定 位 到 origin/serverfix 删 除 远 程 分 支 如 果 不 再 需 要 某 个 远 程 分 支 了, 比 如 搞 定 了 某 个 特 性 并 把 它 合 并 进 了 远 程 的 master 分 支 ( 或 任 何 其 他 存 放 稳 定 代 码 的 分 支 ), 可 以 用 这 个 非 常 无 厘 头 的 语 法 来 删 除 它 :git push [ 远 程 名 ] :[ 分 支 名 ] 如 果 想 在 服 务 器 上 删 除 serverfix 分 支, 运 行 下 面 的 命 令 : $ git push origin :serverfix To git@github.com:schacon/simplegit.git - [deleted] serverfix 咚! 服 务 器 上 的 分 支 没 了 你 最 好 特 别 留 心 这 一 页, 因 为 你 一 定 会 用 到 那 个 命 令, 而 且 你 很 可 能 会 忘 掉 它 的 语 法 有 种 方 便 记 忆 这 条 命 令 的 方 法 : 记 住 我 们 不 久 前 见 过 的 git push [ 远 程 名 ] [ 本 地 分 支 ]:[ 远 程 分 支 ] 语 法, 如 果 省 略 [ 本 地 分 支 ], 那 就 等 于 是 在 说 在 这 里 提 取 空 白 然 后 把 它 变 成 [ 远 程 分 支 ] 3.6 分 支 的 衍 合 把 一 个 分 支 中 的 修 改 整 合 到 另 一 个 分 支 的 办 法 有 两 种 :merge 和 rebase( 译 注 : rebase 的 翻 译 暂 定 为 衍 合, 大 家 知 道 就 可 以 了 ) 在 本 章 我 们 会 学 习 什 么 是 衍 合, 如 何 使 用 衍 合, 为 什 么 衍 合 操 作 如 此 富 有 魅 力, 以 及 我 们 应 该 在 什 么 情 况 下 使 用 衍 合 基 本 的 衍 合 操 作 请 回 顾 之 前 有 关 合 并 的 一 节 ( 见 图 3-27), 你 会 看 到 开 发 进 程 分 叉 到 两 个 不 同 分 支, 又 各 自 提 交 了 更 新 75

86 第 3 章 Git 分 支 Sco Chacon Pro Git 中 文 版 图 3.27: 最 初 分 叉 的 提 交 历 史 之 前 介 绍 过, 最 容 易 的 整 合 分 支 的 方 法 是 merge 命 令, 它 会 把 两 个 分 支 最 新 的 快 照 (C3 和 C4) 以 及 二 者 最 新 的 共 同 祖 先 (C2) 进 行 三 方 合 并, 合 并 的 结 果 是 产 生 一 个 新 的 提 交 对 象 (C5) 如 图 3-28 所 示 : 图 3.28: 通 过 合 并 一 个 分 支 来 整 合 分 叉 了 的 历 史 其 实, 还 有 另 外 一 个 选 择 : 你 可 以 把 在 C3 里 产 生 的 变 化 补 丁 在 C4 的 基 础 上 重 新 打 一 遍 在 Git 里, 这 种 操 作 叫 做 衍 合 (rebase) 有 了 rebase 命 令, 就 可 以 把 在 一 个 分 支 里 提 交 的 改 变 移 到 另 一 个 分 支 里 重 放 一 遍 在 上 面 这 个 例 子 中, 运 行 : $ git checkout experiment $ git rebase master First, rewinding head to replay your work on top of it... Applying: added staged command 它 的 原 理 是 回 到 两 个 分 支 最 近 的 共 同 祖 先, 根 据 当 前 分 支 ( 也 就 是 要 进 行 衍 合 的 分 支 experiment) 后 续 的 历 次 提 交 对 象 ( 这 里 只 有 一 个 C3), 生 成 一 系 列 文 件 补 丁, 然 后 以 基 底 分 支 ( 也 就 是 主 干 分 支 master) 最 后 一 个 提 交 对 象 (C4) 为 新 的 出 发 点, 逐 个 应 用 之 前 准 备 好 的 补 丁 文 件, 最 后 会 生 成 一 个 新 的 合 并 提 交 对 象 (C3 ), 从 而 改 写 experiment 的 提 交 历 史, 使 它 成 为 master 分 支 的 直 接 下 游, 如 图 3-29 所 示 : 现 在 回 到 master 分 支, 进 行 一 次 快 进 合 并 ( 见 图 3-30): 76

87 Sco Chacon Pro Git 中 文 版 3.6 节 分 支 的 衍 合 图 3.29: 把 C3 里 产 生 的 改 变 到 C4 上 重 演 一 遍 图 3.30: master 分 支 的 快 进 现 在 的 C3 对 应 的 快 照, 其 实 和 普 通 的 三 方 合 并, 即 上 个 例 子 中 的 C5 对 应 的 快 照 内 容 一 模 一 样 了 虽 然 最 后 整 合 得 到 的 结 果 没 有 任 何 区 别, 但 衍 合 能 产 生 一 个 更 为 整 洁 的 提 交 历 史 如 果 视 察 一 个 衍 合 过 的 分 支 的 历 史 记 录, 看 起 来 会 更 清 楚 : 仿 佛 所 有 修 改 都 是 在 一 根 线 上 先 后 进 行 的, 尽 管 实 际 上 它 们 原 本 是 同 时 并 行 发 生 的 一 般 我 们 使 用 衍 合 的 目 的, 是 想 要 得 到 一 个 能 在 远 程 分 支 上 干 净 应 用 的 补 丁 比 如 某 些 项 目 你 不 是 维 护 者, 但 想 帮 点 忙 的 话, 最 好 用 衍 合 : 先 在 自 己 的 一 个 分 支 里 进 行 开 发, 当 准 备 向 主 项 目 提 交 补 丁 的 时 候, 根 据 最 新 的 origin/master 进 行 一 次 衍 合 操 作 然 后 再 提 交, 这 样 维 护 者 就 不 需 要 做 任 何 整 合 工 作 ( 译 注 : 实 际 上 是 把 解 决 分 支 补 丁 同 最 新 主 干 代 码 之 间 冲 突 的 责 任, 化 转 为 由 提 交 补 丁 的 人 来 解 决 ), 只 需 根 据 你 提 供 的 仓 库 地 址 作 一 次 快 进 合 并, 或 者 直 接 采 纳 你 提 交 的 补 丁 请 注 意, 合 并 结 果 中 最 后 一 次 提 交 所 指 向 的 快 照, 无 论 是 通 过 衍 合, 还 是 三 方 合 并, 都 会 得 到 相 同 的 快 照 内 容, 只 不 过 提 交 历 史 不 同 罢 了 衍 合 是 按 照 每 行 的 修 改 次 序 重 演 一 遍 修 改, 而 合 并 是 把 最 终 结 果 合 在 一 起 有 趣 的 衍 合 衍 合 也 可 以 放 到 其 他 分 支 进 行, 并 不 一 定 非 得 根 据 分 化 之 前 的 分 支 以 图 3-31 的 历 史 为 例, 我 们 为 了 给 服 务 器 端 代 码 添 加 一 些 功 能 而 创 建 了 特 性 分 支 server, 然 后 提 交 C3 和 C4 然 后 又 从 C3 的 地 方 再 增 加 一 个 client 分 支 来 对 客 户 端 代 码 进 行 一 些 相 应 修 改, 所 以 提 交 了 C8 和 C9 最 后, 又 回 到 server 分 支 提 交 了 C10 假 设 在 接 下 来 的 一 次 软 件 发 布 中, 我 们 决 定 先 把 客 户 端 的 修 改 并 到 主 线 中, 而 暂 缓 并 入 服 务 端 软 件 的 修 改 ( 因 为 还 需 要 进 一 步 测 试 ) 这 个 时 候, 我 们 就 可 以 把 基 于 server 分 支 而 非 master 分 支 的 改 变 ( 即 C8 和 C9), 跳 过 server 直 接 放 到 master 分 支 中 重 演 一 遍, 但 这 需 要 用 git rebase 的 --onto 选 项 指 定 新 的 基 底 分 支 master: 77

88 第 3 章 Git 分 支 Sco Chacon Pro Git 中 文 版 图 3.31: 从 一 个 特 性 分 支 里 再 分 出 一 个 特 性 分 支 的 历 史 $ git rebase --onto master server client 这 好 比 在 说 : 取 出 client 分 支, 找 出 client 分 支 和 server 分 支 的 共 同 祖 先 之 后 的 变 化, 然 后 把 它 们 在 master 上 重 演 一 遍 是 不 是 有 点 复 杂? 不 过 它 的 结 果 如 图 3-32 所 示, 非 常 酷 ( 译 注 : 虽 然 client 里 的 C8, C9 在 C3 之 后, 但 这 仅 表 明 时 间 上 的 先 后, 而 非 在 C3 修 改 的 基 础 上 进 一 步 改 动, 因 为 server 和 client 这 两 个 分 支 对 应 的 代 码 应 该 是 两 套 文 件, 虽 然 这 么 说 不 是 很 严 格, 但 应 理 解 为 在 C3 时 间 点 之 后, 对 另 外 的 文 件 所 做 的 C8,C9 修 改, 放 到 主 干 重 演 ): 图 3.32: 将 特 性 分 支 上 的 另 一 个 特 性 分 支 衍 合 到 其 他 分 支 现 在 可 以 快 进 master 分 支 了 ( 见 图 3-33): 78

89 Sco Chacon Pro Git 中 文 版 3.6 节 分 支 的 衍 合 $ git checkout master $ git merge client 图 3.33: 快 进 master 分 支, 使 之 包 含 client 分 支 的 变 化 现 在 我 们 决 定 把 server 分 支 的 变 化 也 包 含 进 来 我 们 可 以 直 接 把 server 分 支 衍 合 到 master, 而 不 用 手 工 切 换 到 server 分 支 后 再 执 行 衍 合 操 作 git rebase [ 主 分 支 ] [ 特 性 分 支 ] 命 令 会 先 取 出 特 性 分 支 server, 然 后 在 主 分 支 master 上 重 演 : $ git rebase master server 于 是,server 的 进 度 应 用 到 master 的 基 础 上, 如 图 3-34 所 示 : 图 3.34: 在 master 分 支 上 衍 合 server 分 支 然 后 就 可 以 快 进 主 干 分 支 master 了 : $ git checkout master $ git merge server 现 在 client 和 server 分 支 的 变 化 都 已 经 集 成 到 主 干 分 支 来 了, 可 以 删 掉 它 们 了 最 终 我 们 的 提 交 历 史 会 变 成 图 3-35 的 样 子 : 79

90 第 3 章 Git 分 支 Sco Chacon Pro Git 中 文 版 $ git branch -d client $ git branch -d server 图 3.35: 最 终 的 提 交 历 史 衍 合 的 风 险 呃, 奇 妙 的 衍 合 也 并 非 完 美 无 缺, 要 用 它 得 遵 守 一 条 准 则 : 一 旦 分 支 中 的 提 交 对 象 发 布 到 公 共 仓 库, 就 千 万 不 要 对 该 分 支 进 行 衍 合 操 作 如 果 你 遵 循 这 条 金 科 玉 律, 就 不 会 出 差 错 否 则, 人 民 群 众 会 仇 恨 你, 你 的 朋 友 和 家 人 也 会 嘲 笑 你, 唾 弃 你 在 进 行 衍 合 的 时 候, 实 际 上 抛 弃 了 一 些 现 存 的 提 交 对 象 而 创 造 了 一 些 类 似 但 不 同 的 新 的 提 交 对 象 如 果 你 把 原 来 分 支 中 的 提 交 对 象 发 布 出 去, 并 且 其 他 人 更 新 下 载 后 在 其 基 础 上 开 展 工 作, 而 稍 后 你 又 用 git rebase 抛 弃 这 些 提 交 对 象, 把 新 的 重 演 后 的 提 交 对 象 发 布 出 去 的 话, 你 的 合 作 者 就 不 得 不 重 新 合 并 他 们 的 工 作, 这 样 当 你 再 次 从 他 们 那 里 获 取 内 容 时, 提 交 历 史 就 会 变 得 一 团 糟 下 面 我 们 用 一 个 实 际 例 子 来 说 明 为 什 么 公 开 的 衍 合 会 带 来 问 题 假 设 你 从 一 个 中 央 服 务 器 克 隆 然 后 在 它 的 基 础 上 搞 了 一 些 开 发, 提 交 历 史 类 似 图 3-36 所 示 : 图 3.36: 克 隆 一 个 仓 库, 在 其 基 础 上 工 作 一 番 现 在, 某 人 在 C1 的 基 础 上 做 了 些 改 变, 并 合 并 他 自 己 的 分 支 得 到 结 果 C6, 推 送 到 中 央 服 务 器 当 你 抓 取 并 合 并 这 些 数 据 到 你 本 地 的 开 发 分 支 中 后, 会 得 到 合 并 结 果 C7, 历 史 提 交 会 变 成 图 3-37 这 样 : 80

91 Sco Chacon Pro Git 中 文 版 3.6 节 分 支 的 衍 合 图 3.37: 抓 取 他 人 提 交, 并 入 自 己 主 干 接 下 来, 那 个 推 送 C6 上 来 的 人 决 定 用 衍 合 取 代 之 前 的 合 并 操 作 ; 继 而 又 用 git push --force 覆 盖 了 服 务 器 上 的 历 史, 得 到 C4 而 之 后 当 你 再 从 服 务 器 上 下 载 最 新 提 交 后, 会 得 到 : 图 3.38: 有 人 推 送 了 衍 合 后 得 到 的 C4, 丢 弃 了 你 作 为 开 发 基 础 的 C4 和 C6 下 载 更 新 后 需 要 合 并, 但 此 时 衍 合 产 生 的 提 交 对 象 C4 的 SHA-1 校 验 值 和 之 前 C4 完 全 不 同, 所 以 Git 会 把 它 们 当 作 新 的 提 交 对 象 处 理, 而 实 际 上 此 刻 你 的 提 交 历 史 C7 中 早 已 经 包 含 了 C4 的 修 改 内 容, 于 是 合 并 操 作 会 把 C7 和 C4 合 并 为 C8( 见 图 3-39): C8 这 一 步 的 合 并 是 迟 早 会 发 生 的, 因 为 只 有 这 样 你 才 能 和 其 他 协 作 者 提 交 的 内 容 保 持 同 步 而 在 C8 之 后, 你 的 提 交 历 史 里 就 会 同 时 包 含 C4 和 C4, 两 者 有 着 不 同 的 SHA-1 校 验 值, 如 果 用 git log 查 看 历 史, 会 看 到 两 个 提 交 拥 有 相 同 的 作 者 日 期 与 说 明, 令 人 费 解 而 更 糟 的 是, 当 你 把 这 样 的 历 史 推 送 到 服 务 器 后, 会 再 次 把 这 些 衍 合 后 的 提 交 引 入 到 中 央 服 务 器, 进 一 步 困 扰 其 他 人 ( 译 注 : 这 个 例 子 中, 出 问 题 的 责 任 方 是 那 个 发 布 了 C6 后 又 用 衍 合 发 布 C4 的 人, 其 他 人 会 因 此 反 馈 双 重 历 史 到 共 享 主 干, 从 而 混 淆 大 家 的 视 听 ) 81

92 第 3 章 Git 分 支 Sco Chacon Pro Git 中 文 版 图 3.39: 你 把 相 同 的 内 容 又 合 并 了 一 遍, 生 成 一 个 新 的 提 交 C8 如 果 把 衍 合 当 成 一 种 在 推 送 之 前 清 理 提 交 历 史 的 手 段, 而 且 仅 仅 衍 合 那 些 尚 未 公 开 的 提 交 对 象, 就 没 问 题 如 果 衍 合 那 些 已 经 公 开 的 提 交 对 象, 并 且 已 经 有 人 基 于 这 些 提 交 对 象 开 展 了 后 续 开 发 工 作 的 话, 就 会 出 现 叫 人 沮 丧 的 麻 烦 3.7 小 结 读 到 这 里, 你 应 该 已 经 学 会 了 如 何 创 建 分 支 并 切 换 到 新 分 支, 在 不 同 分 支 间 转 换, 合 并 本 地 分 支, 把 分 支 推 送 到 共 享 服 务 器 上, 使 用 共 享 分 支 与 他 人 协 作, 以 及 在 分 享 之 前 进 行 衍 合 82

93 第 4 章 服 务 器 上 的 Git 到 目 前 为 止, 你 应 该 已 经 学 会 了 使 用 Git 来 完 成 日 常 工 作 然 而, 如 果 想 与 他 人 合 作, 还 需 要 一 个 远 程 的 Git 仓 库 尽 管 技 术 上 可 以 从 个 人 的 仓 库 里 推 送 和 拉 取 修 改 内 容, 但 我 们 不 鼓 励 这 样 做, 因 为 一 不 留 心 就 很 容 易 弄 混 其 他 人 的 进 度 另 外, 你 也 一 定 希 望 合 作 者 们 即 使 在 自 己 不 开 机 的 时 候 也 能 从 仓 库 获 取 数 据 拥 有 一 个 更 稳 定 的 公 共 仓 库 十 分 有 用 因 此, 更 好 的 合 作 方 式 是 建 立 一 个 大 家 都 可 以 访 问 的 共 享 仓 库, 从 那 里 推 送 和 拉 取 数 据 我 们 将 把 这 个 仓 库 称 为 Git 服 务 器 ; 代 理 一 个 Git 仓 库 只 需 要 花 费 很 少 的 资 源, 几 乎 从 不 需 要 整 个 服 务 器 来 支 持 它 的 运 行 架 设 一 台 Git 服 务 器 并 不 难 第 一 步 是 选 择 与 服 务 器 通 讯 的 协 议 本 章 第 一 节 将 介 绍 可 用 的 协 议 以 及 各 自 优 缺 点 下 面 一 节 将 介 绍 一 些 针 对 各 个 协 议 典 型 的 设 置 以 及 如 何 在 服 务 器 上 实 施 最 后, 如 果 你 不 介 意 在 他 人 服 务 器 上 保 存 你 的 代 码, 又 想 免 去 自 己 架 设 和 维 护 服 务 器 的 麻 烦, 倒 可 以 试 试 我 们 介 绍 的 几 个 仓 库 托 管 服 务 如 果 你 对 架 设 自 己 的 服 务 器 没 兴 趣, 可 以 跳 到 本 章 最 后 一 节 去 看 看 如 何 申 请 一 个 代 码 托 管 服 务 的 账 户 然 后 继 续 下 一 章, 我 们 会 在 那 里 讨 论 分 布 式 源 码 控 制 环 境 的 林 林 总 总 远 程 仓 库 通 常 只 是 一 个 裸 仓 库 (bare repository) 即 一 个 没 有 当 前 工 作 目 录 的 仓 库 因 为 该 仓 库 只 是 一 个 合 作 媒 介, 所 以 不 需 要 从 硬 盘 上 取 出 最 新 版 本 的 快 照 ; 仓 库 里 存 放 的 仅 仅 是 Git 的 数 据 简 单 地 说, 裸 仓 库 就 是 你 工 作 目 录 中.git 子 目 录 内 的 内 容 4.1 协 议 Git 可 以 使 用 四 种 主 要 的 协 议 来 传 输 数 据 : 本 地 传 输,SSH 协 议,Git 协 议 和 HTTP 协 议 下 面 分 别 介 绍 一 下 哪 些 情 形 应 该 使 用 ( 或 避 免 使 用 ) 这 些 协 议 值 得 注 意 的 是, 除 了 HTTP 协 议 外, 其 他 所 有 协 议 都 要 求 在 服 务 器 端 安 装 并 运 行 Git 83

94 第 4 章 服 务 器 上 的 Git Sco Chacon Pro Git 中 文 版 本 地 协 议 最 基 本 的 就 是 本 地 协 议 (Local protocol), 所 谓 的 远 程 仓 库 在 该 协 议 中 的 表 示, 就 是 硬 盘 上 的 另 一 个 目 录 这 常 见 于 团 队 每 一 个 成 员 都 对 一 个 共 享 的 文 件 系 统 ( 例 如 NFS) 拥 有 访 问 权, 或 者 比 较 少 见 的 多 人 共 用 同 一 台 电 脑 的 情 况 后 面 一 种 情 况 并 不 安 全, 因 为 所 有 代 码 仓 库 实 例 都 储 存 在 同 一 台 电 脑 里, 增 加 了 灾 难 性 数 据 损 失 的 可 能 性 如 果 你 使 用 一 个 共 享 的 文 件 系 统, 就 可 以 在 一 个 本 地 文 件 系 统 中 克 隆 仓 库, 推 送 和 获 取 克 隆 的 时 候 只 需 要 将 远 程 仓 库 的 路 径 作 为 URL 使 用, 比 如 下 面 这 样 : $ git clone /opt/git/project.git 或 者 这 样 : $ git clone file:///opt/git/project.git 如 果 在 URL 开 头 明 确 使 用 file://, 那 么 Git 会 以 一 种 略 微 不 同 的 方 式 运 行 如 果 你 只 给 出 路 径,Git 会 尝 试 使 用 硬 链 接 或 直 接 复 制 它 所 需 要 的 文 件 如 果 使 用 了 file://,git 会 调 用 它 平 时 通 过 网 络 来 传 输 数 据 的 工 序, 而 这 种 方 式 的 效 率 相 对 较 低 使 用 file:// 前 缀 的 主 要 原 因 是 当 你 需 要 一 个 不 包 含 无 关 引 用 或 对 象 的 干 净 仓 库 副 本 的 时 候 般 指 从 其 他 版 本 控 制 系 统 导 入 的, 或 类 似 情 形 ( 参 见 第 9 章 的 维 护 任 务 ) 我 们 这 里 仅 仅 使 用 普 通 路 径, 这 样 更 快 要 添 加 一 个 本 地 仓 库 作 为 现 有 Git 项 目 的 远 程 仓 库, 可 以 这 样 做 : $ git remote add local_proj /opt/git/project.git 然 后 就 可 以 像 在 网 络 上 一 样 向 这 个 远 程 仓 库 推 送 和 获 取 数 据 了 优 点 基 于 文 件 仓 库 的 优 点 在 于 它 的 简 单, 同 时 保 留 了 现 存 文 件 的 权 限 和 网 络 访 问 权 限 如 果 你 的 团 队 已 经 有 一 个 全 体 共 享 的 文 件 系 统, 建 立 仓 库 就 十 分 容 易 了 你 只 需 把 一 份 裸 仓 库 的 副 本 放 在 大 家 都 能 访 问 的 地 方, 然 后 像 对 其 他 共 享 目 录 一 样 设 置 读 写 权 限 就 可 以 了 我 们 将 在 下 一 节 在 服 务 器 上 部 署 Git 中 讨 论 如 何 导 出 一 个 裸 仓 库 的 副 本 这 也 是 从 别 人 工 作 目 录 中 获 取 工 作 成 果 的 快 捷 方 法 假 如 你 和 你 的 同 事 在 一 个 项 目 中 合 作, 他 们 想 让 你 检 出 一 些 东 西 的 时 候, 运 行 类 似 git pull 84

95 Sco Chacon Pro Git 中 文 版 4.1 节 协 议 /home/john/project 通 常 会 比 他 们 推 送 到 服 务 器, 而 你 再 从 服 务 器 获 取 简 单 得 多 缺 点 这 种 方 法 的 缺 点 是, 与 基 本 的 网 络 连 接 访 问 相 比, 难 以 控 制 从 不 同 位 置 来 的 访 问 权 限 如 果 你 想 从 家 里 的 笔 记 本 电 脑 上 推 送, 就 要 先 挂 载 远 程 硬 盘, 这 和 基 于 网 络 连 接 的 访 问 相 比 更 加 困 难 和 缓 慢 另 一 个 很 重 要 的 问 题 是 该 方 法 不 一 定 就 是 最 快 的, 尤 其 是 对 于 共 享 挂 载 的 文 件 系 统 本 地 仓 库 只 有 在 你 对 数 据 访 问 速 度 快 的 时 候 才 快 在 同 一 个 服 务 器 上, 如 果 二 者 同 时 允 许 Git 访 问 本 地 硬 盘, 通 过 NFS 访 问 仓 库 通 常 会 比 SSH 慢 SSH 协 议 Git 使 用 的 传 输 协 议 中 最 常 见 的 可 能 就 是 SSH 了 这 是 因 为 大 多 数 环 境 已 经 支 持 通 过 SSH 对 服 务 器 的 访 问 即 便 还 没 有, 架 设 起 来 也 很 容 易 SSH 也 是 唯 一 一 个 同 时 支 持 读 写 操 作 的 网 络 协 议 另 外 两 个 网 络 协 议 (HTTP 和 Git) 通 常 都 是 只 读 的, 所 以 虽 然 二 者 对 大 多 数 人 都 可 用, 但 执 行 写 操 作 时 还 是 需 要 SSH SSH 同 时 也 是 一 个 验 证 授 权 的 网 络 协 议 ; 而 因 为 其 普 遍 性, 一 般 架 设 和 使 用 都 很 容 易 通 过 SSH 克 隆 一 个 Git 仓 库, 你 可 以 像 下 面 这 样 给 出 ssh:// 的 URL: $ git clone ssh://user@server/project.git 或 者 不 指 明 某 个 协 议 这 时 Git 会 默 认 使 用 SSH : $ git clone user@server:project.git 如 果 不 指 明 用 户,Git 会 默 认 使 用 当 前 登 录 的 用 户 名 连 接 服 务 器 优 点 使 用 SSH 的 好 处 有 很 多 首 先, 如 果 你 想 拥 有 对 网 络 仓 库 的 写 权 限, 基 本 上 不 可 能 不 使 用 SSH 其 次,SSH 架 设 相 对 比 较 简 单 SSH 守 护 进 程 很 常 见, 很 多 网 络 管 理 员 都 有 一 些 使 用 经 验, 而 且 很 多 操 作 系 统 都 自 带 了 它 或 者 相 关 的 管 理 工 具 再 次, 通 过 SSH 进 行 访 问 是 安 全 的 所 有 数 据 传 输 都 是 加 密 和 授 权 的 最 后, 和 Git 及 本 地 协 议 一 样,SSH 也 很 高 效, 会 在 传 输 之 前 尽 可 能 压 缩 数 据 85

96 第 4 章 服 务 器 上 的 Git Sco Chacon Pro Git 中 文 版 缺 点 SSH 的 限 制 在 于 你 不 能 通 过 它 实 现 仓 库 的 匿 名 访 问 即 使 仅 为 读 取 数 据, 人 们 也 必 须 在 能 通 过 SSH 访 问 主 机 的 前 提 下 才 能 访 问 仓 库, 这 使 得 SSH 不 利 于 开 源 的 项 目 如 果 你 仅 仅 在 公 司 网 络 里 使 用,SSH 可 能 是 你 唯 一 需 要 使 用 的 协 议 如 果 想 允 许 对 项 目 的 匿 名 只 读 访 问, 那 么 除 了 为 自 己 推 送 而 架 设 SSH 协 议 之 外, 还 需 要 支 持 其 他 协 议 以 便 他 人 访 问 读 取 Git 协 议 接 下 来 是 Git 协 议 这 是 一 个 包 含 在 Git 软 件 包 中 的 特 殊 守 护 进 程 ; 它 会 监 听 一 个 提 供 类 似 于 SSH 服 务 的 特 定 端 口 (9418), 而 无 需 任 何 授 权 打 算 支 持 Git 协 议 的 仓 库, 需 要 先 创 建 git-daemon-export-ok 文 件 它 是 协 议 进 程 提 供 仓 库 服 务 的 必 要 条 件 但 除 此 之 外 该 服 务 没 有 什 么 安 全 措 施 要 么 所 有 人 都 能 克 隆 Git 仓 库, 要 么 谁 也 不 能 这 也 意 味 着 该 协 议 通 常 不 能 用 来 进 行 推 送 你 可 以 允 许 推 送 操 作 ; 然 而 由 于 没 有 授 权 机 制, 一 旦 允 许 该 操 作, 网 络 上 任 何 一 个 知 道 项 目 URL 的 人 将 都 有 推 送 权 限 不 用 说, 这 是 十 分 罕 见 的 情 况 优 点 Git 协 议 是 现 存 最 快 的 传 输 协 议 如 果 你 在 提 供 一 个 有 很 大 访 问 量 的 公 共 项 目, 或 者 一 个 不 需 要 对 读 操 作 进 行 授 权 的 庞 大 项 目, 架 设 一 个 Git 守 护 进 程 来 供 应 仓 库 是 个 不 错 的 选 择 它 使 用 与 SSH 协 议 相 同 的 数 据 传 输 机 制, 但 省 去 了 加 密 和 授 权 的 开 销 缺 点 Git 协 议 消 极 的 一 面 是 缺 少 授 权 机 制 用 Git 协 议 作 为 访 问 项 目 的 唯 一 方 法 通 常 是 不 可 取 的 一 般 的 做 法 是, 同 时 提 供 SSH 接 口, 让 几 个 开 发 者 拥 有 推 送 ( 写 ) 权 限, 其 他 人 通 过 git:// 拥 有 只 读 权 限 Git 协 议 可 能 也 是 最 难 架 设 的 协 议 它 要 求 有 单 独 的 守 护 进 程, 需 要 定 制 我 们 将 在 本 章 的 Gitosis 一 节 详 细 介 绍 它 的 架 设 需 要 设 定 xinetd 或 类 似 的 程 序, 而 这 些 工 作 就 没 那 么 轻 松 了 该 协 议 还 要 求 防 火 墙 开 放 9418 端 口, 而 企 业 级 防 火 墙 一 般 不 允 许 对 这 个 非 标 准 端 口 的 访 问 大 型 企 业 级 防 火 墙 通 常 会 封 锁 这 个 少 见 的 端 口 HTTP/S 协 议 最 后 还 有 HTTP 协 议 HTTP 或 HTTPS 协 议 的 优 美 之 处 在 于 架 设 的 简 便 性 基 本 上, 只 需 要 把 Git 的 裸 仓 库 文 件 放 在 HTTP 的 根 目 录 下, 配 置 一 个 特 定 的 post-update 挂 钩 (hook) 就 可 以 搞 定 (Git 挂 钩 的 细 节 见 第 7 章 ) 此 后, 每 个 能 访 问 Git 仓 库 所 在 服 务 器 上 web 服 务 的 人 都 可 以 进 行 克 隆 操 作 下 面 的 操 作 可 以 允 许 通 过 HTTP 对 仓 库 进 行 读 取 : 86

97 Sco Chacon Pro Git 中 文 版 4.1 节 协 议 $ cd /var/www/htdocs/ $ git clone --bare /path/to/git_project gitproject.git $ cd gitproject.git $ mv hooks/post-update.sample hooks/post-update $ chmod a+x hooks/post-update 这 样 就 可 以 了 Git 附 带 的 post-update 挂 钩 会 默 认 运 行 合 适 的 命 令 (git update-server-info) 来 确 保 通 过 HTTP 的 获 取 和 克 隆 正 常 工 作 这 条 命 令 在 你 用 SSH 向 仓 库 推 送 内 容 时 运 行 ; 之 后, 其 他 人 就 可 以 用 下 面 的 命 令 来 克 隆 仓 库 : $ git clone 在 本 例 中, 我 们 使 用 了 Apache 设 定 中 常 用 的 /var/www/htdocs 路 径, 不 过 你 可 以 使 用 任 何 静 态 web 服 务 把 裸 仓 库 放 在 它 的 目 录 里 就 行 Git 的 数 据 是 以 最 基 本 的 静 态 文 件 的 形 式 提 供 的 ( 关 于 如 何 提 供 文 件 的 详 情 见 第 9 章 ) 通 过 HTTP 进 行 推 送 操 作 也 是 可 能 的, 不 过 这 种 做 法 不 太 常 见, 并 且 牵 扯 到 复 杂 的 WebDAV 设 定 由 于 很 少 用 到, 本 书 将 略 过 对 该 内 容 的 讨 论 如 果 对 HTTP 推 送 协 议 感 兴 趣, 不 妨 打 开 这 个 地 址 看 一 下 操 作 方 法 : pub/software/scm/git/docs/howto/setup-git-server-over-http.txt 通 过 HTTP 推 送 的 好 处 之 一 是 你 可 以 使 用 任 何 WebDAV 服 务 器, 不 需 要 为 Git 设 定 特 殊 环 境 ; 所 以 如 果 主 机 提 供 商 支 持 通 过 WebDAV 更 新 网 站 内 容, 你 也 可 以 使 用 这 项 功 能 优 点 使 用 HTTP 协 议 的 好 处 是 易 于 架 设 几 条 必 要 的 命 令 就 可 以 让 全 世 界 读 取 到 仓 库 的 内 容 花 费 不 过 几 分 钟 HTTP 协 议 不 会 占 用 过 多 服 务 器 资 源 因 为 它 一 般 只 用 到 静 态 的 HTTP 服 务 提 供 所 有 数 据, 普 通 的 Apache 服 务 器 平 均 每 秒 能 支 撑 数 千 个 文 件 的 并 发 访 问 哪 怕 让 一 个 小 型 服 务 器 超 载 都 很 难 你 也 可 以 通 过 HTTPS 提 供 只 读 的 仓 库, 这 意 味 着 你 可 以 加 密 传 输 内 容 ; 你 甚 至 可 以 要 求 客 户 端 使 用 特 定 签 名 的 SSL 证 书 一 般 情 况 下, 如 果 到 了 这 一 步, 使 用 SSH 公 共 密 钥 可 能 是 更 简 单 的 方 案 ; 不 过 也 存 在 一 些 特 殊 情 况, 这 时 通 过 HTTPS 使 用 带 签 名 的 SSL 证 书 或 者 其 他 基 于 HTTP 的 只 读 连 接 授 权 方 式 是 更 好 的 解 决 方 案 HTTP 还 有 个 额 外 的 好 处 :HTTP 是 一 个 如 此 常 见 的 协 议, 以 至 于 企 业 级 防 火 墙 通 常 都 允 许 其 端 口 的 通 信 87

98 第 4 章 服 务 器 上 的 Git Sco Chacon Pro Git 中 文 版 缺 点 HTTP 协 议 的 消 极 面 在 于, 相 对 来 说 客 户 端 效 率 更 低 克 隆 或 者 下 载 仓 库 内 容 可 能 会 花 费 更 多 时 间, 而 且 HTTP 传 输 的 体 积 和 网 络 开 销 比 其 他 任 何 一 个 协 议 都 大 因 为 它 没 有 按 需 供 应 的 能 力 传 输 过 程 中 没 有 服 务 端 的 动 态 计 算 因 而 HTTP 协 议 经 常 会 被 称 为 傻 瓜 (dumb) 协 议 更 多 HTTP 协 议 和 其 他 协 议 效 率 上 的 差 异 见 第 9 章 4.2 在 服 务 器 上 部 署 Git 开 始 架 设 Git 服 务 器 前, 需 要 先 把 现 有 仓 库 导 出 为 裸 仓 库 即 一 个 不 包 含 当 前 工 作 目 录 的 仓 库 做 法 直 截 了 当, 克 隆 时 用 --bare 选 项 即 可 裸 仓 库 的 目 录 名 一 般 以.git 结 尾, 像 这 样 : $ git clone --bare my_project my_project.git Cloning into bare repository 'my_project.git'... done. 该 命 令 的 输 出 或 许 会 让 人 有 些 不 解 其 实 clone 操 作 基 本 上 相 当 于 git init 加 git fetch, 所 以 这 里 出 现 的 其 实 是 git init 的 输 出, 先 由 它 建 立 一 个 空 目 录, 而 之 后 传 输 数 据 对 象 的 操 作 并 无 任 何 输 出, 只 是 悄 悄 在 幕 后 执 行 现 在 my_project.git 目 录 中 已 经 有 了 一 份 Git 目 录 数 据 的 副 本 整 体 上 的 效 果 大 致 相 当 于 : $ cp -Rf my_project/.git my_project.git 但 在 配 置 文 件 中 有 若 干 小 改 动, 不 过 对 用 户 来 讲, 使 用 方 式 都 一 样, 不 会 有 什 么 影 响 它 仅 取 出 Git 仓 库 的 必 要 原 始 数 据, 存 放 在 该 目 录 中, 而 不 会 另 外 创 建 工 作 目 录 把 裸 仓 库 移 到 服 务 器 上 有 了 裸 仓 库 的 副 本 后, 剩 下 的 就 是 把 它 放 到 服 务 器 上 并 设 定 相 关 协 议 假 设 一 个 域 名 为 git.example.com 的 服 务 器 已 经 架 设 好, 并 可 以 通 过 SSH 访 问, 我 们 打 算 把 所 有 Git 仓 库 储 存 在 /opt/git 目 录 下 只 要 把 裸 仓 库 复 制 过 去 : $ scp -r my_project.git user@git.example.com:/opt/git 88

99 Sco Chacon Pro Git 中 文 版 4.2 节 在 服 务 器 上 部 署 Git 现 在, 所 有 对 该 服 务 器 有 SSH 访 问 权 限, 并 可 读 取 /opt/git 目 录 的 用 户 都 可 以 用 下 面 的 命 令 克 隆 该 项 目 : $ git clone user@git.example.com:/opt/git/my_project.git 如 果 某 个 SSH 用 户 对 /opt/git/my_project.git 目 录 有 写 权 限, 那 他 就 有 推 送 权 限 如 果 到 该 项 目 目 录 中 运 行 git init 命 令, 并 加 上 --shared 选 项, 那 么 Git 会 自 动 修 改 该 仓 库 目 录 的 组 权 限 为 可 写 ( 译 注 : 实 际 上 --shared 可 以 指 定 其 他 行 为, 只 是 默 认 为 将 组 权 限 改 为 可 写 并 执 行 g+sx, 所 以 最 后 会 得 到 rws ) $ ssh user@git.example.com $ cd /opt/git/my_project.git $ git init --bare --shared 由 此 可 见, 根 据 现 有 的 Git 仓 库 创 建 一 个 裸 仓 库, 然 后 把 它 放 上 你 和 同 事 都 有 SSH 访 问 权 的 服 务 器 是 多 么 容 易 现 在 已 经 可 以 开 始 在 同 一 项 目 上 密 切 合 作 了 值 得 注 意 的 是, 这 的 的 确 确 是 架 设 一 个 少 数 人 具 有 连 接 权 的 Git 服 务 的 全 部 只 要 在 服 务 器 上 加 入 可 以 用 SSH 登 录 的 帐 号, 然 后 把 裸 仓 库 放 在 大 家 都 有 读 写 权 限 的 地 方 一 切 都 准 备 停 当, 无 需 更 多 下 面 的 几 节 中, 你 会 了 解 如 何 扩 展 到 更 复 杂 的 设 定 这 些 内 容 包 含 如 何 避 免 为 每 一 个 用 户 建 立 一 个 账 户, 给 仓 库 添 加 公 共 读 取 权 限, 架 设 网 页 界 面, 使 用 Gitosis 工 具 等 等 然 而, 只 是 和 几 个 人 在 一 个 不 公 开 的 项 目 上 合 作 的 话, 仅 仅 是 一 个 SSH 服 务 器 和 裸 仓 库 就 足 够 了, 记 住 这 点 就 可 以 了 小 型 安 装 如 果 设 备 较 少 或 者 你 只 想 在 小 型 开 发 团 队 里 尝 试 Git, 那 么 一 切 都 很 简 单 架 设 Git 服 务 最 复 杂 的 地 方 在 于 账 户 管 理 如 果 需 要 仓 库 对 特 定 的 用 户 可 读, 而 给 另 一 部 分 用 户 读 写 权 限, 那 么 访 问 和 许 可 的 安 排 就 比 较 困 难 SSH 连 接 如 果 已 经 有 了 一 个 所 有 开 发 成 员 都 可 以 用 SSH 访 问 的 服 务 器, 架 设 第 一 个 服 务 器 将 变 得 异 常 简 单, 几 乎 什 么 都 不 用 做 ( 正 如 上 节 中 介 绍 的 那 样 ) 如 果 需 要 对 仓 库 进 行 更 复 杂 的 访 问 控 制, 只 要 使 用 服 务 器 操 作 系 统 的 本 地 文 件 访 问 许 可 机 制 就 行 了 如 果 需 要 团 队 里 的 每 个 人 都 对 仓 库 有 写 权 限, 又 不 能 给 每 个 人 在 服 务 器 上 建 立 账 户, 那 么 提 供 SSH 连 接 就 是 唯 一 的 选 择 了 我 们 假 设 用 来 共 享 仓 库 的 服 务 器 已 经 安 装 了 SSH 服 务, 而 且 你 通 过 它 访 问 服 务 器 89

100 第 4 章 服 务 器 上 的 Git Sco Chacon Pro Git 中 文 版 有 好 几 个 办 法 可 以 让 团 队 的 每 个 人 都 有 访 问 权 第 一 个 办 法 是 给 每 个 人 建 立 一 个 账 户, 直 截 了 当 但 略 过 繁 琐 反 复 运 行 adduser 并 给 所 有 人 设 定 临 时 密 码 可 不 是 好 玩 的 第 二 个 办 法 是 在 主 机 上 建 立 一 个 git 账 户, 让 每 个 需 要 写 权 限 的 人 发 送 一 个 SSH 公 钥, 然 后 将 其 加 入 git 账 户 的 ~/.ssh/authorized_keys 文 件 这 样 一 来, 所 有 人 都 将 通 过 git 账 户 访 问 主 机 这 丝 毫 不 会 影 响 提 交 的 数 据 访 问 主 机 用 的 身 份 不 会 影 响 提 交 对 象 的 提 交 者 信 息 另 一 个 办 法 是 让 SSH 服 务 器 通 过 某 个 LDAP 服 务, 或 者 其 他 已 经 设 定 好 的 集 中 授 权 机 制, 来 进 行 授 权 只 要 每 个 人 都 能 获 得 主 机 的 shell 访 问 权, 任 何 可 用 的 SSH 授 权 机 制 都 能 达 到 相 同 效 果 4.3 生 成 SSH 公 钥 大 多 数 Git 服 务 器 都 会 选 择 使 用 SSH 公 钥 来 进 行 授 权 系 统 中 的 每 个 用 户 都 必 须 提 供 一 个 公 钥 用 于 授 权, 没 有 的 话 就 要 生 成 一 个 生 成 公 钥 的 过 程 在 所 有 操 作 系 统 上 都 差 不 多 首 先 先 确 认 一 下 是 否 已 经 有 一 个 公 钥 了 SSH 公 钥 默 认 储 存 在 账 户 的 主 目 录 下 的 ~/.ssh 目 录 进 去 看 看 : $ cd ~/.ssh $ ls authorized_keys2 id_dsa known_hosts config id_dsa.pub 关 键 是 看 有 没 有 用 something 和 something.pub 来 命 名 的 一 对 文 件, 这 个 something 通 常 就 是 id_dsa 或 id_rsa 有.pub 后 缀 的 文 件 就 是 公 钥, 另 一 个 文 件 则 是 密 钥 假 如 没 有 这 些 文 件, 或 者 干 脆 连.ssh 目 录 都 没 有, 可 以 用 ssh-keygen 来 创 建 该 程 序 在 Linux/Mac 系 统 上 由 SSH 包 提 供, 而 在 Windows 上 则 包 含 在 MSysGit 包 里 : $ ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/Users/schacon/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /Users/schacon/.ssh/id_rsa. Your public key has been saved in /Users/schacon/.ssh/id_rsa.pub. The key fingerprint is: 43:c5:5b:5f:b1:f1:50:43:ad:20:a6:92:6a:1f:9a:3a schacon@agadorlaptop.local 90

101 Sco Chacon Pro Git 中 文 版 4.4 节 架 设 服 务 器 它 先 要 求 你 确 认 保 存 公 钥 的 位 置 (.ssh/id_rsa), 然 后 它 会 让 你 重 复 一 个 密 码 两 次, 如 果 不 想 在 使 用 公 钥 的 时 候 输 入 密 码, 可 以 留 空 现 在, 所 有 做 过 这 一 步 的 用 户 都 得 把 它 们 的 公 钥 给 你 或 者 Git 服 务 器 的 管 理 员 ( 假 设 SSH 服 务 被 设 定 为 使 用 公 钥 机 制 ) 他 们 只 需 要 复 制.pub 文 件 的 内 容 然 后 发 邮 件 给 管 理 员 公 钥 的 样 子 大 致 如 下 : $ cat ~/.ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3 Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA t3faojoasncm1q9x5+3v0ww68/eifmb1zuufljqjkprrx88xypndvjynby6vw/pb0rwert/en mz+aw4ozpntpi89zpmvmluayrd2ce86z/il8b+gw3r3+1nkatmikjn2so1d01qratlmqvssbx NrRFi9wrf+M7Q== schacon@agadorlaptop.local 关 于 在 多 个 操 作 系 统 上 设 立 相 同 SSH 公 钥 的 教 程, 可 以 查 阅 GitHub 上 有 关 SSH 公 钥 的 向 导 : 4.4 架 设 服 务 器 现 在 我 们 过 一 边 服 务 器 端 架 设 SSH 访 问 的 流 程 本 例 将 使 用 authorized_keys 方 法 来 给 用 户 授 权 我 们 还 将 假 定 使 用 类 似 Ubuntu 这 样 的 标 准 Linux 发 行 版 首 先, 创 建 一 个 名 为 git 的 用 户, 并 为 其 创 建 一 个.ssh 目 录 $ sudo adduser git $ su git $ cd $ mkdir.ssh 接 下 来, 把 开 发 者 的 SSH 公 钥 添 加 到 这 个 用 户 的 authorized_keys 文 件 中 假 设 你 通 过 电 邮 收 到 了 几 个 公 钥 并 存 到 了 临 时 文 件 里 重 复 一 下, 公 钥 大 致 看 起 来 是 这 个 样 子 : $ cat /tmp/id_rsa.john.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCB007n/ww+ouN4gSLKssMxXnBOvf9LGt4L ojg6rs6hpb09j9r/t17/x4lhja0f3fr1rp6kybrswj2athgw6hxlm9/5zytk6ztg3rpkk+4k Yjh6541NYsnEAZuXz0jTTyAUfrtU3Z5E003C4oxOj6H0rfIF1kKI9MAQLMdpGW1GYEIgS9Ez 91

102 第 4 章 服 务 器 上 的 Git Sco Chacon Pro Git 中 文 版 Sdfd8AcCIicTDWbqLAcU4UpkaX8KyGlLwsNuuGztobF8m72ALC/nLF6JLtPofwFBlgc+myiv O7TCUSBdLQlgMVOFq1I2uPWQOkOWQAHukEOmfjy2jctxSDBQ220ymjaNsHT4kgtZg2AYYgPq dav8jggjicuvax2t9va5 gsg-keypair 只 要 把 它 们 逐 个 追 加 到 authorized_keys 文 件 尾 部 即 可 : $ cat /tmp/id_rsa.john.pub >> ~/.ssh/authorized_keys $ cat /tmp/id_rsa.josie.pub >> ~/.ssh/authorized_keys $ cat /tmp/id_rsa.jessica.pub >> ~/.ssh/authorized_keys 现 在 可 以 用 --bare 选 项 运 行 git init 来 建 立 一 个 裸 仓 库, 这 会 初 始 化 一 个 不 包 含 工 作 目 录 的 仓 库 $ cd /opt/git $ mkdir project.git $ cd project.git $ git --bare init 这 时,Join,Josie 或 者 Jessica 就 可 以 把 它 加 为 远 程 仓 库, 推 送 一 个 分 支, 从 而 把 第 一 个 版 本 的 项 目 文 件 上 传 到 仓 库 里 了 值 得 注 意 的 是, 每 次 添 加 一 个 新 项 目 都 需 要 通 过 shell 登 入 主 机 并 创 建 一 个 裸 仓 库 目 录 我 们 不 妨 以 gitserver 作 为 git 用 户 及 项 目 仓 库 所 在 的 主 机 名 如 果 在 网 络 内 部 运 行 该 主 机, 并 在 DNS 中 设 定 gitserver 指 向 该 主 机, 那 么 以 下 这 些 命 令 都 是 可 用 的 : # 在 John 的 电 脑 上 $ cd myproject $ git init $ git add. $ git commit -m 'initial commit' $ git remote add origin git@gitserver:/opt/git/project.git $ git push origin master 92 这 样, 其 他 人 的 克 隆 和 推 送 也 一 样 变 得 很 简 单 :

103 Sco Chacon Pro Git 中 文 版 4.5 节 公 共 访 问 $ git clone git@gitserver:/opt/git/project.git $ cd project $ vim README $ git commit -am 'fix for the README file' $ git push origin master 用 这 个 方 法 可 以 很 快 捷 地 为 少 数 几 个 开 发 者 架 设 一 个 可 读 写 的 Git 服 务 作 为 一 个 额 外 的 防 范 措 施, 你 可 以 用 Git 自 带 的 git-shell 工 具 限 制 git 用 户 的 活 动 范 围 只 要 把 它 设 为 git 用 户 登 入 的 shell, 那 么 该 用 户 就 无 法 使 用 普 通 的 bash 或 者 csh 什 么 的 shell 程 序 编 辑 /etc/passwd 文 件 : $ sudo vim /etc/passwd 在 文 件 末 尾, 你 应 该 能 找 到 类 似 这 样 的 行 : git:x:1000:1000::/home/git:/bin/sh 把 bin/sh 改 为 /usr/bin/git-shell ( 或 者 用 which git-shell 查 看 它 的 实 际 安 装 路 径 ) 该 行 修 改 后 的 样 子 如 下 : git:x:1000:1000::/home/git:/usr/bin/git-shell 现 在 git 用 户 只 能 用 SSH 连 接 来 推 送 和 获 取 Git 仓 库, 而 不 能 直 接 使 用 主 机 shell 尝 试 普 通 SSH 登 录 的 话, 会 看 到 下 面 这 样 的 拒 绝 信 息 : $ ssh git@gitserver fatal: What do you think I am? A shell? Connection to gitserver closed. 4.5 公 共 访 问 匿 名 的 读 取 权 限 该 怎 么 实 现 呢? 也 许 除 了 内 部 私 有 的 项 目 之 外, 你 还 需 要 托 管 一 些 开 源 项 目 或 者 因 为 要 用 一 些 自 动 化 的 服 务 器 来 进 行 编 译, 或 者 有 一 些 经 常 变 93

104 第 4 章 服 务 器 上 的 Git Sco Chacon Pro Git 中 文 版 化 的 服 务 器 群 组, 而 又 不 想 整 天 生 成 新 的 SSH 密 钥 总 之, 你 需 要 简 单 的 匿 名 读 取 权 限 或 许 对 小 型 的 配 置 来 说 最 简 单 的 办 法 就 是 运 行 一 个 静 态 web 服 务, 把 它 的 根 目 录 设 定 为 Git 仓 库 所 在 的 位 置, 然 后 开 启 本 章 第 一 节 提 到 的 post-update 挂 钩 这 里 继 续 使 用 之 前 的 例 子 假 设 仓 库 处 于 /opt/git 目 录, 主 机 上 运 行 着 Apache 服 务 重 申 一 下, 任 何 web 服 务 程 序 都 可 以 达 到 相 同 效 果 ; 作 为 范 例, 我 们 将 用 一 些 基 本 的 Apache 设 定 来 展 示 大 体 需 要 的 步 骤 首 先, 开 启 挂 钩 : $ cd project.git $ mv hooks/post-update.sample hooks/post-update $ chmod a+x hooks/post-update post-update 挂 钩 是 做 什 么 的 呢? 其 内 容 大 致 如 下 : $ cat.git/hooks/post-update #!/bin/sh # # An example hook script to prepare a packed repository for use over # dumb transports. # # To enable this hook, rename this file to "post-update". # exec git-update-server-info 意 思 是 当 通 过 SSH 向 服 务 器 推 送 时,Git 将 运 行 这 个 git-update-server-info 命 令 来 更 新 匿 名 HTTP 访 问 获 取 数 据 时 所 需 要 的 文 件 接 下 来, 在 Apache 配 置 文 件 中 添 加 一 个 VirtualHost 条 目, 把 文 档 根 目 录 设 为 Git 项 目 所 在 的 根 目 录 这 里 我 们 假 定 DNS 服 务 已 经 配 置 好, 会 把 对.gitserver 的 请 求 发 送 到 这 台 主 机 : <VirtualHost *:80> ServerName git.gitserver DocumentRoot /opt/git <Directory /opt/git/> 94

105 Sco Chacon Pro Git 中 文 版 4.6 节 GitWeb Order allow, deny allow from all </Directory> </VirtualHost> 另 外, 需 要 把 /opt/git 目 录 的 Unix 用 户 组 设 定 为 www-data, 这 样 web 服 务 才 可 以 读 取 仓 库 内 容, 因 为 运 行 CGI 脚 本 的 Apache 实 例 进 程 默 认 就 是 以 该 用 户 的 身 份 起 来 的 : $ chgrp -R www-data /opt/git 重 启 Apache 之 后, 就 可 以 通 过 项 目 的 URL 来 克 隆 该 目 录 下 的 仓 库 了 $ git clone 这 一 招 可 以 让 你 在 几 分 钟 内 为 相 当 数 量 的 用 户 架 设 好 基 于 HTTP 的 读 取 权 限 另 一 个 提 供 非 授 权 访 问 的 简 单 方 法 是 开 启 一 个 Git 守 护 进 程, 不 过 这 将 要 求 该 进 程 作 为 后 台 进 程 常 驻 接 下 来 的 这 一 节 就 要 讨 论 这 方 面 的 细 节 4.6 GitWeb 现 在 我 们 的 项 目 已 经 有 了 可 读 可 写 和 只 读 的 连 接 方 式, 不 过 如 果 能 有 一 个 简 单 的 web 界 面 访 问 就 更 好 了 Git 自 带 一 个 叫 做 GitWeb 的 CGI 脚 本, 运 行 效 果 可 以 到 这 样 的 站 点 体 验 下 ( 见 图 4-1) 如 果 想 看 看 自 己 项 目 的 效 果, 不 妨 用 Git 自 带 的 一 个 命 令, 可 以 使 用 类 似 lighttpd 或 webrick 这 样 轻 量 级 的 服 务 器 启 动 一 个 临 时 进 程 如 果 是 在 Linux 主 机 上, 通 常 都 预 装 了 lighttpd, 可 以 到 项 目 目 录 中 键 入 git instaweb 来 启 动 如 果 用 的 是 Mac,Leopard 预 装 了 Ruby, 所 以 webrick 应 该 是 最 好 的 选 择 如 果 要 用 ligh pd 以 外 的 程 序 来 启 动 git instaweb, 可 以 通 过 --httpd 选 项 指 定 : $ git instaweb --httpd=webrick [ :02:21] INFO WEBrick [ :02:21] INFO ruby ( ) [universal-darwin9.0] 95

106 第 4 章 服 务 器 上 的 Git Sco Chacon Pro Git 中 文 版 图 4.1: 基 于 网 页 的 GitWeb 用 户 界 面 这 会 在 1234 端 口 开 启 一 个 HTTPD 服 务, 随 之 在 浏 览 器 中 显 示 该 页, 十 分 简 单 关 闭 服 务 时, 只 需 在 原 来 的 命 令 后 面 加 上 --stop 选 项 就 可 以 了 : $ git instaweb --httpd=webrick --stop 如 果 需 要 为 团 队 或 者 某 个 开 源 项 目 长 期 运 行 GitWeb, 那 么 CGI 脚 本 就 要 由 正 常 的 网 页 服 务 来 运 行 一 些 Linux 发 行 版 可 以 通 过 apt 或 yum 安 装 一 个 叫 做 gitweb 的 软 件 包, 不 妨 首 先 尝 试 一 下 我 们 将 快 速 介 绍 一 下 手 动 安 装 GitWeb 的 流 程 首 先, 你 需 要 Git 的 源 码, 其 中 带 有 GitWeb, 并 能 生 成 定 制 的 CGI 脚 本 : $ git clone git://git.kernel.org/pub/scm/git/git.git $ cd git/ $ make GITWEB_PROJECTROOT="/opt/git" \ prefix=/usr gitweb $ sudo cp -Rf gitweb /var/www/ 注 意, 通 过 指 定 GITWEB_PROJECTROOT 变 量 告 诉 编 译 命 令 Git 仓 库 的 位 置 然 后, 设 置 Apache 以 CGI 方 式 运 行 该 脚 本, 添 加 一 个 VirtualHost 配 置 : <VirtualHost *:80> ServerName gitserver DocumentRoot /var/www/gitweb <Directory /var/www/gitweb> Options ExecCGI +FollowSymLinks +SymLinksIfOwnerMatch 96

107 Sco Chacon Pro Git 中 文 版 4.7 节 Gitosis AllowOverride All order allow,deny Allow from all AddHandler cgi-script cgi DirectoryIndex gitweb.cgi </Directory> </VirtualHost> 不 难 想 象,GitWeb 可 以 使 用 任 何 兼 容 CGI 的 网 页 服 务 来 运 行 ; 如 果 偏 向 使 用 其 他 web 服 务 器, 配 置 也 不 会 很 麻 烦 现 在, 通 过 就 可 以 在 线 访 问 仓 库 了, 在 上 还 可 以 通 过 HTTP 克 隆 和 获 取 仓 库 的 内 容 4.7 Gitosis 把 所 有 用 户 的 公 钥 保 存 在 authorized_keys 文 件 的 做 法, 只 能 凑 和 一 阵 子, 当 用 户 数 量 达 到 几 百 人 的 规 模 时, 管 理 起 来 就 会 十 分 痛 苦 每 次 改 删 用 户 都 必 须 登 录 服 务 器 不 去 说, 这 种 做 法 还 缺 少 必 要 的 权 限 管 理 每 个 人 都 对 所 有 项 目 拥 有 完 整 的 读 写 权 限 幸 好 我 们 还 可 以 选 择 应 用 广 泛 的 Gitosis 项 目 简 单 地 说,Gitosis 就 是 一 套 用 来 管 理 authorized_keys 文 件 和 实 现 简 单 连 接 限 制 的 脚 本 有 趣 的 是, 用 来 添 加 用 户 和 设 定 权 限 的 并 非 通 过 网 页 程 序, 而 只 是 管 理 一 个 特 殊 的 Git 仓 库 你 只 需 要 在 这 个 特 殊 仓 库 内 做 好 相 应 的 设 定, 然 后 推 送 到 服 务 器 上,Gitosis 就 会 随 之 改 变 运 行 策 略, 听 起 来 就 很 酷, 对 吧? Gitosis 的 安 装 算 不 上 傻 瓜 化, 但 也 不 算 太 难 用 Linux 服 务 器 架 设 起 来 最 简 单 以 下 例 子 中, 我 们 使 用 装 有 Ubuntu 8.10 系 统 的 服 务 器 Gitosis 的 工 作 依 赖 于 某 些 Python 工 具, 所 以 首 先 要 安 装 Python 的 setuptools 包, 在 Ubuntu 上 称 为 python-setuptools: $ apt-get install python-setuptools 接 下 来, 从 Gitosis 项 目 主 页 克 隆 并 安 装 : $ git clone $ cd gitosis $ sudo python setup.py install 97

108 第 4 章 服 务 器 上 的 Git Sco Chacon Pro Git 中 文 版 这 会 安 装 几 个 供 Gitosis 使 用 的 工 具 默 认 Gitosis 会 把 /home/git 作 为 存 储 所 有 Git 仓 库 的 根 目 录, 这 没 什 么 不 好, 不 过 我 们 之 前 已 经 把 项 目 仓 库 都 放 在 /opt/git 里 面 了, 所 以 为 方 便 起 见, 我 们 可 以 做 一 个 符 号 连 接, 直 接 划 转 过 去, 而 不 必 重 新 配 置 : $ ln -s /opt/git /home/git/repositories Gitosis 将 会 帮 我 们 管 理 用 户 公 钥, 所 以 先 把 当 前 控 制 文 件 改 名 备 份, 以 便 稍 后 重 新 添 加, 准 备 好 让 Gitosis 自 动 管 理 authorized_keys 文 件 : $ mv /home/git/.ssh/authorized_keys /home/git/.ssh/ak.bak 接 下 来, 如 果 之 前 把 git 用 户 的 登 录 shell 改 为 git-shell 命 令 的 话, 先 恢 复 git 用 户 的 登 录 shell 改 过 之 后, 大 家 仍 然 无 法 通 过 该 帐 号 登 录 ( 译 注 : 因 为 authorized_keys 文 件 已 经 没 有 了 ), 不 过 不 用 担 心, 这 会 交 给 Gitosis 来 实 现 所 以 现 在 先 打 开 /etc/passwd 文 件, 把 这 行 : git:x:1000:1000::/home/git:/usr/bin/git-shell 改 回 : git:x:1000:1000::/home/git:/bin/sh 好 了, 现 在 可 以 初 始 化 Gitosis 了 你 可 以 用 自 己 的 公 钥 执 行 gitosis-init 命 令, 要 是 公 钥 不 在 服 务 器 上, 先 临 时 复 制 一 份 : $ sudo -H -u git gitosis-init < /tmp/id_dsa.pub Initialized empty Git repository in /opt/git/gitosis-admin.git/ Reinitialized existing Git repository in /opt/git/gitosis-admin.git/ 这 样 该 公 钥 的 拥 有 者 就 能 修 改 用 于 配 置 Gitosis 的 那 个 特 殊 Git 仓 库 了 接 下 来, 需 要 手 工 对 该 仓 库 中 的 post-update 脚 本 加 上 可 执 行 权 限 : 98

109 Sco Chacon Pro Git 中 文 版 4.7 节 Gitosis $ sudo chmod 755 /opt/git/gitosis-admin.git/hooks/post-update 基 本 上 就 算 是 好 了 如 果 设 定 过 程 没 出 什 么 差 错, 现 在 可 以 试 一 下 用 初 始 化 Gitosis 的 公 钥 的 拥 有 者 身 份 SSH 登 录 服 务 器, 应 该 会 看 到 类 似 下 面 这 样 : $ ssh git@gitserver PTY allocation request failed on channel 0 ERROR:gitosis.serve.main:Need SSH_ORIGINAL_COMMAND in environment. Connection to gitserver closed. 说 明 Gitosis 认 出 了 该 用 户 的 身 份, 但 由 于 没 有 运 行 任 何 Git 命 令, 所 以 它 切 断 了 连 接 那 么, 现 在 运 行 一 个 实 际 的 Git 命 令 克 隆 Gitosis 的 控 制 仓 库 : # 在 你 本 地 计 算 机 上 $ git clone git@gitserver:gitosis-admin.git 这 会 得 到 一 个 名 为 gitosis-admin 的 工 作 目 录, 主 要 由 两 部 分 组 成 : $ cd gitosis-admin $ find../gitosis.conf./keydir./keydir/scott.pub gitosis.conf 文 件 是 用 来 设 置 用 户 仓 库 和 权 限 的 控 制 文 件 keydir 目 录 则 是 保 存 所 有 具 有 访 问 权 限 用 户 公 钥 的 地 方 每 人 一 个 在 keydir 里 的 文 件 名 ( 比 如 上 面 的 scott.pub) 应 该 跟 你 的 不 一 样 Gitosis 会 自 动 从 使 用 gitosis-init 脚 本 导 入 的 公 钥 尾 部 的 描 述 中 获 取 该 名 字 看 一 下 gitosis.conf 文 件 的 内 容, 它 应 该 只 包 含 与 刚 刚 克 隆 的 gitosis-admin 相 关 的 信 息 : $ cat gitosis.conf 99

110 第 4 章 服 务 器 上 的 Git Sco Chacon Pro Git 中 文 版 [gitosis] [group gitosis-admin] members = scott writable = gitosis-admin 它 显 示 用 户 scott 初 始 化 Gitosis 公 钥 的 拥 有 者 是 唯 一 能 管 理 gitosis-admin 项 目 的 人 现 在 我 们 来 添 加 一 个 新 项 目 为 此 我 们 要 建 立 一 个 名 为 mobile 的 新 段 落, 在 其 中 罗 列 手 机 开 发 团 队 的 开 发 者, 以 及 他 们 拥 有 写 权 限 的 项 目 由 于 sco 是 系 统 中 的 唯 一 用 户, 我 们 把 他 设 为 唯 一 用 户, 并 允 许 他 读 写 名 为 iphone_project 的 新 项 目 : [group mobile] members = scott writable = iphone_project 修 改 完 之 后, 提 交 gitosis-admin 里 的 改 动, 并 推 送 到 服 务 器 使 其 生 效 : $ git commit -am 'add iphone_project and mobile group' [master 8962da8] add iphone_project and mobile group 1 file changed, 4 insertions(+) $ git push origin master Counting objects: 5, done. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 272 bytes 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To git@gitserver:gitosis-admin.git fb27aec..8962da8 master -> master 在 新 工 程 iphone_project 里 首 次 推 送 数 据 到 服 务 器 前, 得 先 设 定 该 服 务 器 地 址 为 远 程 仓 库 但 你 不 用 事 先 到 服 务 器 上 手 工 创 建 该 项 目 的 裸 仓 库 Gitosis 会 在 第 一 次 遇 到 推 送 时 自 动 创 建 : 100

111 Sco Chacon Pro Git 中 文 版 4.7 节 Gitosis $ git remote add origin git@gitserver:iphone_project.git $ git push origin master Initialized empty Git repository in /opt/git/iphone_project.git/ Counting objects: 3, done. Writing objects: 100% (3/3), 230 bytes 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To git@gitserver:iphone_project.git * [new branch] master -> master 请 注 意, 这 里 不 用 指 明 完 整 路 径 ( 实 际 上, 如 果 加 上 反 而 没 用 ), 只 需 要 一 个 冒 号 加 项 目 名 字 即 可 Gitosis 会 自 动 帮 你 映 射 到 实 际 位 置 要 和 朋 友 们 在 一 个 项 目 上 协 同 工 作, 就 得 重 新 添 加 他 们 的 公 钥 不 过 这 次 不 用 在 服 务 器 上 一 个 一 个 手 工 添 加 到 ~/.ssh/authorized_keys 文 件 末 端, 而 只 需 管 理 keydir 目 录 中 的 公 钥 文 件 文 件 的 命 名 将 决 定 在 gitosis.conf 中 对 用 户 的 标 识 现 在 我 们 为 John,Josie 和 Jessica 添 加 公 钥 : $ cp /tmp/id_rsa.john.pub keydir/john.pub $ cp /tmp/id_rsa.josie.pub keydir/josie.pub $ cp /tmp/id_rsa.jessica.pub keydir/jessica.pub 然 后 把 他 们 都 加 进 mobile 团 队, 让 他 们 对 iphone_project 具 有 读 写 权 限 : [group mobile] members = scott john josie jessica writable = iphone_project 如 果 你 提 交 并 推 送 这 个 修 改, 四 个 用 户 将 同 时 具 有 该 项 目 的 读 写 权 限 Gitosis 也 具 有 简 单 的 访 问 控 制 功 能 如 果 想 让 John 只 有 读 权 限, 可 以 这 样 做 : [group mobile] members = scott josie jessica writable = iphone_project [group mobile_ro] 101

112 第 4 章 服 务 器 上 的 Git Sco Chacon Pro Git 中 文 版 members = john readonly = iphone_project 现 在 John 可 以 克 隆 和 获 取 更 新, 但 Gitosis 不 会 允 许 他 向 项 目 推 送 任 何 内 容 像 这 样 的 组 可 以 随 意 创 建, 多 少 不 限, 每 个 都 可 以 包 含 若 干 不 同 的 用 户 和 项 目 甚 至 还 可 以 指 定 某 个 组 为 成 员 之 一 ( 在 组 名 前 加 前 缀 ), 自 动 继 承 该 组 的 成 员 : [group mobile_committers] members = scott josie jessica [group mobile] members writable = iphone_project [group mobile_2] members john writable = another_iphone_project 如 果 遇 到 意 外 问 题, 试 试 看 把 loglevel=debug 加 到 [gitosis] 的 段 落 ( 译 注 : 把 日 志 设 置 为 调 试 级 别, 记 录 更 详 细 的 运 行 信 息 ) 如 果 一 不 小 心 搞 错 了 配 置, 失 去 了 推 送 权 限, 也 可 以 手 工 修 改 服 务 器 上 的 /home/git/.gitosis.conf 文 件 Gitosis 实 际 是 从 该 文 件 读 取 信 息 的 它 在 得 到 推 送 数 据 时, 会 把 新 的 gitosis.conf 存 到 该 路 径 上 所 以 如 果 你 手 工 编 辑 该 文 件 的 话, 它 会 一 直 保 持 到 下 次 向 gitosis-admin 推 送 新 版 本 的 配 置 内 容 为 止 4.8 Gitolite This section serves as a quick introduction to Gitolite, and provides basic installation and setup instructions. 不 能 完 全 替 代 随 gitolite 自 带 的 大 量 文 档 There may also be occasional changes to this section itself, so you may also want to look at the latest version here. Gitolite is an authorization layer on top of Git, relying on sshd or httpd for authentication. (Recap: authentication is identifying who the user is, authorization is deciding if he is allowed to do what he is a empting to). Gitolite 允 许 你 定 义 访 问 许 可 而 不 只 作 用 于 仓 库, 而 同 样 于 仓 库 中 的 每 个 branch 和 tag name 你 可 以 定 义 确 切 的 人 ( 或 一 组 人 ) 只 能 push 特 定 的 refs ( 或 者 branches 或 者 tags) 而 不 是 其 他 人 102

113 Sco Chacon Pro Git 中 文 版 4.8 节 Gitolite 安 装 安 装 Gitolite 非 常 简 单, 你 甚 至 不 用 读 自 带 的 那 一 大 堆 文 档 你 需 要 一 个 unix 服 务 器 上 的 账 户 ; 许 多 linux 变 种 和 solaris 10 都 已 经 试 过 了 你 不 需 要 root 访 问, 假 设 git,perl, 和 一 个 openssh 兼 容 的 ssh 服 务 器 已 经 装 好 了 在 下 面 的 例 子 里, 我 们 会 用 git 账 户 在 gitserver 上. Gitolite 是 不 同 于 server 的 软 件 通 过 ssh 访 问, 而 且 每 个 在 服 务 器 上 的 userid 都 是 一 个 潜 在 的 gitolite host. We will describe the simplest install method in this article; for the other methods please see the documentation. To begin, create a user called git on your server and login to this user. Copy your SSH public key (a file called ~/.ssh/id_rsa.pub if you did a plain ssh-keygen with all the defaults) from your workstation, renaming it to <yourname>.pub (we ll use scott.pub in our examples). Then run these commands: $ git clone git://github.com/sitaramc/gitolite $ gitolite/install -ln # assumes $HOME/bin exists and is in your $PATH $ gitolite setup -pk $HOME/scott.pub That last command creates new Git repository called gitolite-admin on the server. Finally, back on your workstation, run git clone git@gitserver:gitolite-admin. And you re done! Gitolite has now been installed on the server, and you now have a brand new repository called gitolite-admin in your workstation. You administer your Gitolite setup by making changes to this repository and pushing 定 制 安 装 默 认 快 速 安 装 对 大 多 数 人 都 管 用, 还 有 一 些 定 制 安 装 方 法 如 果 你 用 的 上 的 话 Some changes can be made simply by editing the rc file, but if that is not sufficient, there s documentation on customising Gitolite 配 置 文 件 和 访 问 规 则 安 装 结 束 后, 你 切 换 到 gitolite-admin 仓 库 ( 放 在 你 的 HOME 目 录 ) 然 后 看 看 都 有 啥 : $ cd ~/gitolite-admin/ $ ls 103

114 第 4 章 服 务 器 上 的 Git Sco Chacon Pro Git 中 文 版 conf/ keydir/ $ find conf keydir -type f conf/gitolite.conf keydir/scott.pub $ cat conf/gitolite.conf repo gitolite-admin RW+ = scott repo testing RW+ 注 意 sco ( 之 前 用 gl-setup 命 令 时 候 的 pubkey 名 稱 ) 有 读 写 权 限 而 且 在 gitolite-admin 仓 库 里 有 一 个 同 名 的 公 钥 文 件 Adding users is easy. To add a user called alice, obtain her public key, name it alice.pub, and put it in the keydir directory of the clone of the gitolite-admin repo you just made on your workstation. Add, commit, and push the change, and the user has been added. gitolite 配 置 文 件 的 语 法 在 conf/example.conf 里, 我 们 只 会 提 到 一 些 主 要 的 你 可 以 给 用 户 或 者 仓 库 分 组 分 组 名 就 像 一 些 宏 ; 定 义 的 时 候, 无 所 谓 他 们 是 工 程 还 是 用 户 ; 区 别 在 于 你 使 用 宏 的 = linux perl rakudo git gitolite = = scott = ashok = sitaram dilbert wally @interns 你 可 以 控 制 许 可 在 ref 级 别 在 下 面 的 例 子 里, 实 习 生 可 以 push int branch. 工 程 师 可 以 push 任 何 有 eng- 开 头 的 branch, 还 有 refs/tags 下 面 用 rc 开 头 的 后 面 跟 数 字 的 而 且 管 理 员 可 以 随 便 改 ( 包 括 rewind) 对 任 何 参 考 名. RW int$ 104

115 Sco Chacon Pro Git 中 文 版 4.8 节 Gitolite RW eng- RW refs/tags/rc[0-9] RW+ 在 RW or RW+ 之 后 的 表 达 式 是 正 则 表 达 式 (regex) 对 应 着 后 面 的 push 用 的 参 考 名 字 (ref) 所 以 我 们 叫 它 参 考 正 则 (refex)! 当 然, 一 个 refex 可 以 比 这 里 表 现 的 更 强 大, 所 以 如 果 你 对 perl 的 正 则 表 达 式 不 熟 的 话 就 不 要 改 过 头 同 样, 你 可 能 猜 到 了,Gitolite 字 头 refs/heads/ 是 一 个 便 捷 句 法 如 果 参 考 正 则 没 有 用 refs/ 开 头 一 个 这 个 配 置 文 件 语 法 的 重 要 功 能 是, 所 有 的 仓 库 的 规 则 不 需 要 在 同 一 个 位 置 你 能 报 所 有 普 通 的 东 西 放 在 一 起, 就 像 上 面 的 对 所 有 oss_repos 的 规 则 那 样, 然 后 建 一 个 特 殊 的 规 则 对 后 面 的 特 殊 案 例, 就 像 : repo gitolite RW+ = sitaram 那 条 规 则 刚 刚 加 入 规 则 集 的 gitolite 仓 库. 这 次 你 可 能 会 想 要 知 道 访 问 控 制 规 则 是 如 何 应 用 的, 我 们 简 要 介 绍 一 下 在 gitolite 里 有 两 级 访 问 控 制 第 一 是 在 仓 库 级 别 ; 如 果 你 已 经 读 或 者 写 访 问 过 了 任 何 在 仓 库 里 的 参 考, 那 么 你 已 经 读 或 者 写 访 问 仓 库 了 第 二 级, 应 用 只 能 写 访 问, 通 过 在 仓 库 里 的 branch 或 者 tag 用 户 名 如 果 尝 试 过 访 问 (W 或 +), 参 考 名 被 更 新 为 已 知 访 问 规 则 检 查 是 否 出 现 在 配 置 文 件 里, 为 这 个 联 合 寻 找 匹 配 ( 但 是 记 得 参 考 名 是 正 则 匹 配 的, 不 是 字 符 串 匹 配 的 ) 如 果 匹 配 被 找 到 了,push 就 成 功 了 不 匹 配 的 访 问 会 被 拒 绝 带 拒 绝 的 高 级 访 问 控 制 目 前, 我 们 只 看 过 了 许 可 是 R, RW, 或 者 RW+ 这 样 子 的 但 是 gitolite 还 允 许 另 外 一 种 许 可 :-, 代 表 拒 绝 这 个 给 了 你 更 多 的 能 力, 当 然 也 有 一 点 复 杂, 因 为 不 匹 配 并 不 是 唯 一 的 拒 绝 访 问 的 方 法, 因 此 规 则 的 顺 序 变 得 无 关 了! 这 么 说 好 了, 在 前 面 的 情 况 中, 我 们 想 要 工 程 师 可 以 rewind 任 意 branch 除 了 master 和 integ 这 里 是 如 何 做 到 的 RW master integ - master integ RW+ 105

116 第 4 章 服 务 器 上 的 Git Sco Chacon Pro Git 中 文 版 你 再 一 次 简 单 跟 随 规 则 从 上 至 下 知 道 你 找 到 一 个 匹 配 你 的 访 问 模 式 的, 或 者 拒 绝 非 rewind push 到 master 或 者 integ 被 第 一 条 规 则 允 许 一 个 rewind push 到 那 些 refs 不 匹 配 第 一 条 规 则, 掉 到 第 二 条, 因 此 被 拒 绝 任 何 push (rewind 或 非 rewind) 到 参 考 或 者 其 他 master 或 者 integ 不 会 被 前 两 条 规 则 匹 配, 即 被 第 三 条 规 则 允 许 通 过 改 变 文 件 限 制 push 此 外 限 制 用 户 push 改 变 到 哪 条 branch 的, 你 也 可 以 限 制 哪 个 文 件 他 们 可 以 碰 的 到 比 如, 可 能 Makefile ( 或 者 其 他 哪 些 程 序 ) 真 的 不 能 被 任 何 人 做 任 何 改 动, 因 为 好 多 东 西 都 靠 着 它 呢, 或 者 如 果 某 些 改 变 刚 好 不 对 就 会 崩 溃 你 可 以 告 诉 gitolite: repo foo - VREF/NAME/Makefile 这 是 一 个 强 力 的 公 能 写 在 conf/example.conf 里 个 人 分 支 Gitolite 也 支 持 一 个 叫 个 人 分 支 的 功 能 ( 或 者 叫, 个 人 分 支 命 名 空 间 ) 在 合 作 环 境 里 非 常 有 用 在 git 世 界 里 许 多 代 码 交 换 通 过 pull 请 求 发 生 然 而 在 合 作 环 境 里, 委 任 制 的 访 问 是 绝 不, 一 个 开 发 者 工 作 站 不 能 认 证, 你 必 须 push 到 中 心 服 务 器 并 且 叫 其 他 人 从 那 里 pull 这 个 通 常 会 引 起 一 些 branch 名 称 簇 变 成 像 VCS 里 一 样 集 中 化, 加 上 设 置 许 可 变 成 管 理 员 的 苦 差 事 Gitolite 让 你 定 义 一 个 个 人 的 或 者 乱 七 八 糟 的 命 名 空 间 字 首 给 每 个 开 发 人 员 ( 比 如,refs/personal/<devname>/*); 看 在 doc/3-faq-tips-etc.mkd 里 的 personal branches 一 段 获 取 细 节 通 配 符 仓 库 Gitolite 允 许 你 定 义 带 通 配 符 的 仓 库 ( 其 实 还 是 perl 正 则 式 ), 比 如 随 便 整 个 例 子 的 话 assignments/s[0-9][0-9]/a[0-9][0-9] 这 是 一 个 非 常 有 用 的 功 能, 需 要 通 过 设 置 $GL_WILDREPOS = 1; 在 rc 文 件 中 启 用 允 许 你 安 排 一 个 新 许 可 模 式 ( C ) 允 许 用 户 创 建 仓 库 基 于 通 配 符, 自 动 分 配 拥 有 权 对 特 定 用 户 - 创 建 者, 允 许 他 交 出 R 和 RW 许 可 给 其 他 合 作 用 户 等 等 这 个 功 能 在 doc/4-wildcard-repositories.mkd 文 档 里 106

117 Sco Chacon Pro Git 中 文 版 4.9 节 Git 守 护 进 程 其 他 功 能 我 们 用 一 些 其 他 功 能 的 例 子 结 束 这 段 讨 论, 这 些 以 及 其 他 功 能 都 在 faqs, tips, etc 和 其 他 文 档 里 记 录 : Gitolite 记 录 所 有 成 功 的 访 问 如 果 你 太 放 松 给 了 别 人 rewind 许 可 (RW+) 和 其 他 孩 子 弄 没 了 master, 记 录 文 件 会 救 你 的 命, 如 果 其 他 简 单 快 速 的 找 到 SHA 都 不 管 用 访 问 权 报 告 : 另 一 个 方 便 的 功 能 是 你 尝 试 用 ssh 连 接 到 服 务 器 的 时 候 发 生 了 什 么 Gitolite 告 诉 你 哪 个 repos 你 访 问 过, 那 个 访 问 可 能 是 什 么 这 里 是 例 子 : hello scott, this is git@git running gitolite3 v g on git R anu-wsd R entrans R W git-notes R W gitolite R W gitolite-admin R indic_web_input R shreelipi_converter 委 托 : 真 正 的 大 安 装, 你 可 以 把 责 任 委 托 给 一 组 仓 库 给 不 同 的 人 然 后 让 他 们 独 立 管 理 那 些 部 分 这 个 减 少 了 主 管 理 者 的 负 担, 让 他 瓶 颈 更 小 这 个 功 能 在 他 自 己 的 文 档 目 录 里 的 doc/ 下 面 镜 像 : Gitolite 可 以 帮 助 你 维 护 多 个 镜 像, 如 果 主 服 务 器 挂 掉 的 话 在 他 们 之 间 很 容 易 切 换 4.9 Git 守 护 进 程 对 于 提 供 公 共 的, 非 授 权 的 只 读 访 问, 我 们 可 以 抛 弃 HTTP 协 议, 改 用 Git 自 己 的 协 议, 这 主 要 是 出 于 性 能 和 速 度 的 考 虑 Git 协 议 远 比 HTTP 协 议 高 效, 因 而 访 问 速 度 也 快, 所 以 它 能 节 省 很 多 用 户 的 时 间 重 申 一 下, 这 一 点 只 适 用 于 非 授 权 的 只 读 访 问 如 果 建 在 防 火 墙 之 外 的 服 务 器 上, 那 么 它 所 提 供 的 服 务 应 该 只 是 那 些 公 开 的 只 读 项 目 如 果 是 在 防 火 墙 之 内 的 服 务 器 上, 可 用 于 支 撑 大 量 参 与 人 员 或 自 动 系 统 ( 用 于 持 续 集 成 或 编 译 的 主 机 ) 只 读 访 问 的 项 目, 这 样 可 以 省 去 逐 一 配 置 SSH 公 钥 的 麻 烦 但 不 管 哪 种 情 形,Git 协 议 的 配 置 设 定 都 很 简 单 基 本 上, 只 要 以 守 护 进 程 的 形 式 运 行 该 命 令 即 可 : 107

118 第 4 章 服 务 器 上 的 Git Sco Chacon Pro Git 中 文 版 git daemon --reuseaddr --base-path=/opt/git/ /opt/git/ 这 里 的 --reuseaddr 选 项 表 示 在 重 启 服 务 前, 不 等 之 前 的 连 接 超 时 就 立 即 重 启 而 --base-path 选 项 则 允 许 克 隆 项 目 时 不 必 给 出 完 整 路 径 最 后 面 的 路 径 告 诉 Git 守 护 进 程 允 许 开 放 给 用 户 访 问 的 仓 库 目 录 假 如 有 防 火 墙, 则 需 要 为 该 主 机 的 9418 端 口 设 置 为 允 许 通 信 以 守 护 进 程 的 形 式 运 行 该 进 程 的 方 法 有 很 多, 但 主 要 还 得 看 用 的 是 什 么 操 作 系 统 在 Ubuntu 主 机 上, 可 以 用 Upstart 脚 本 达 成 编 辑 该 文 件 : /etc/event.d/local-git-daemon 加 入 以 下 内 容 : start on startup stop on shutdown exec /usr/bin/git daemon \ --user=git --group=git \ --reuseaddr \ --base-path=/opt/git/ \ /opt/git/ respawn 出 于 安 全 考 虑, 强 烈 建 议 用 一 个 对 仓 库 只 有 读 取 权 限 的 用 户 身 份 来 运 行 该 进 程 只 需 要 简 单 地 新 建 一 个 名 为 git-ro 的 用 户 ( 译 注 : 新 建 用 户 默 认 对 仓 库 文 件 不 具 备 写 权 限, 但 这 取 决 于 仓 库 目 录 的 权 限 设 定 务 必 确 认 git-ro 对 仓 库 只 能 读 不 能 写 ), 并 用 它 的 身 份 来 启 动 进 程 这 里 为 了 简 化, 后 面 我 们 还 是 用 之 前 运 行 Gitosis 的 用 户 git 这 样 一 来, 当 你 重 启 计 算 机 时,Git 进 程 也 会 自 动 启 动 要 是 进 程 意 外 退 出 或 者 被 杀 掉, 也 会 自 行 重 启 在 设 置 完 成 后, 不 重 启 计 算 机 就 启 动 该 守 护 进 程, 可 以 运 行 : initctl start local-git-daemon 而 在 其 他 操 作 系 统 上, 可 以 用 xinetd, 或 者 sysvinit 系 统 的 脚 本, 或 者 其 他 类 似 的 脚 本 只 要 能 让 那 个 命 令 变 为 守 护 进 程 并 可 监 控 108

119 Sco Chacon Pro Git 中 文 版 4.9 节 Git 守 护 进 程 接 下 来, 我 们 必 须 告 诉 Gitosis 哪 些 仓 库 允 许 通 过 Git 协 议 进 行 匿 名 只 读 访 问 如 果 每 个 仓 库 都 设 有 各 自 的 段 落, 可 以 分 别 指 定 是 否 允 许 Git 进 程 开 放 给 用 户 匿 名 读 取 比 如 允 许 通 过 Git 协 议 访 问 iphone_project, 可 以 把 下 面 两 行 加 到 gitosis.conf 文 件 的 末 尾 : [repo iphone_project] daemon = yes 在 提 交 和 推 送 完 成 后, 运 行 中 的 Git 守 护 进 程 就 会 响 应 来 自 9418 端 口 对 该 项 目 的 访 问 请 求 如 果 不 考 虑 Gitosis, 单 单 起 了 Git 守 护 进 程 的 话, 就 必 须 到 每 一 个 允 许 匿 名 只 读 访 问 的 仓 库 目 录 内, 创 建 一 个 特 殊 名 称 的 空 文 件 作 为 标 志 : $ cd /path/to/project.git $ touch git-daemon-export-ok 该 文 件 的 存 在, 表 明 允 许 Git 守 护 进 程 开 放 对 该 项 目 的 匿 名 只 读 访 问 Gitosis 还 能 设 定 哪 些 项 目 允 许 放 在 GitWeb 上 显 示 先 打 开 GitWeb 的 配 置 文 件 /etc/gitweb.conf, 添 加 以 下 四 行 : $projects_list = "/home/git/gitosis/projects.list"; $projectroot = "/home/git/repositories"; $export_ok = = ('git://gitserver'); 接 下 来, 只 要 配 置 各 个 项 目 在 Gitosis 中 的 gitweb 参 数, 便 能 达 成 是 否 允 许 GitWeb 用 户 浏 览 该 项 目 比 如, 要 让 iphone_project 项 目 在 GitWeb 里 出 现, 把 repo 的 设 定 改 成 下 面 的 样 子 : [repo iphone_project] daemon = yes gitweb = yes 历 史 在 提 交 并 推 送 过 之 后,GitWeb 就 会 自 动 开 始 显 示 iphone_project 项 目 的 细 节 和 109

120 第 4 章 服 务 器 上 的 Git Sco Chacon Pro Git 中 文 版 4.10 Git 托 管 服 务 如 果 不 想 经 历 自 己 架 设 Git 服 务 器 的 麻 烦, 网 络 上 有 几 个 专 业 的 仓 库 托 管 服 务 可 供 选 择 这 样 做 有 几 大 优 点 : 托 管 账 户 的 建 立 通 常 比 较 省 时, 方 便 项 目 的 启 动, 而 且 不 涉 及 服 务 器 的 维 护 和 监 控 即 使 内 部 创 建 并 运 行 着 自 己 的 服 务 器, 同 时 为 开 源 项 目 提 供 一 个 公 共 托 管 站 点 还 是 有 好 处 的 让 开 源 社 区 更 方 便 地 找 到 该 项 目, 并 给 予 帮 助 目 前, 可 供 选 择 的 托 管 服 务 数 量 繁 多, 各 有 利 弊 在 Git 官 方 wiki 上 的 Githosting 页 面 有 一 个 最 新 的 托 管 服 务 列 表 : 由 于 本 书 无 法 全 部 一 一 介 绍, 而 本 人 ( 译 注 : 指 本 书 作 者 Sco Chacon ) 刚 好 在 其 中 一 家 公 司 工 作, 所 以 接 下 来 我 们 将 会 介 绍 如 何 在 GitHub 上 建 立 新 账 户 并 启 动 项 目 至 于 其 他 托 管 服 务 大 体 也 是 这 么 一 个 过 程, 基 本 的 想 法 都 是 差 不 多 的 GitHub 是 目 前 为 止 最 大 的 开 源 Git 托 管 服 务, 并 且 还 是 少 数 同 时 提 供 公 共 代 码 和 私 有 代 码 托 管 服 务 的 站 点 之 一, 所 以 你 可 以 在 上 面 同 时 保 存 开 源 和 商 业 代 码 事 实 上, 本 书 就 是 放 在 GitHub 上 合 作 编 著 的 ( 译 注 : 本 书 的 翻 译 也 是 放 在 GitHub 上 广 泛 协 作 的 ) GitHub GitHub 和 大 多 数 的 代 码 托 管 站 点 在 处 理 项 目 命 名 空 间 的 方 式 上 略 有 不 同 GitHub 的 设 计 更 侧 重 于 用 户, 而 不 是 完 全 基 于 项 目 也 就 是 说, 如 果 我 在 GitHub 上 托 管 一 个 名 为 grit 的 项 目 的 话, 它 的 地 址 不 会 是 github.com/grit, 而 是 按 在 用 户 底 下 github.com/shacon/grit ( 译 注 : 本 书 作 者 Sco Chacon 在 GitHub 上 的 用 户 名 是 shacon ) 不 存 在 所 谓 某 个 项 目 的 官 方 版 本, 所 以 假 如 第 一 作 者 放 弃 了 某 个 项 目, 它 可 以 无 缝 转 移 到 其 它 用 户 的 名 下 GitHub 同 时 也 是 一 个 向 使 用 私 有 仓 库 的 用 户 收 取 费 用 的 商 业 公 司, 但 任 何 人 都 可 以 方 便 快 捷 地 申 请 到 一 个 免 费 账 户, 并 在 上 面 托 管 数 量 不 限 的 开 源 项 目 接 下 来 我 们 快 速 介 绍 一 下 GitHub 的 基 本 使 用 建 立 新 账 户 首 先 注 册 一 个 免 费 账 户 访 问 Pricing and Signup 页 面 plans 并 点 击 Free acount 里 的 Sign Up 按 钮 ( 见 图 4-2), 进 入 注 册 页 面 选 择 一 个 系 统 中 尚 未 使 用 的 用 户 名, 提 供 一 个 与 之 相 关 联 的 电 邮 地 址, 并 输 入 密 码 ( 见 图 4-3): 如 果 方 便, 现 在 就 可 以 提 供 你 的 SSH 公 钥 我 们 在 前 文 的 小 型 安 装 一 节 介 绍 过 生 成 新 公 钥 的 方 法 把 新 生 成 的 公 钥 复 制 粘 贴 到 SSH Public Key 文 本 框 中 即 可 110

121 Sco Chacon Pro Git 中 文 版 4.10 节 Git 托 管 服 务 图 4.2: GitHub 服 务 简 介 页 面 图 4.3: GitHub 用 户 注 册 表 单 要 是 对 生 成 公 钥 的 步 骤 不 太 清 楚, 也 可 以 点 击 explain ssh keys 链 接, 会 显 示 各 个 主 流 操 作 系 统 上 完 成 该 步 骤 的 介 绍 点 击 I agree,sign me up 按 钮 完 成 用 户 注 册, 并 转 到 该 用 户 的 dashboard 页 面 ( 见 图 4-4): 图 4.4: GitHub 的 用 户 面 板 接 下 来 就 可 以 建 立 新 仓 库 了 建 立 新 仓 库 点 击 用 户 面 板 上 仓 库 旁 边 的 create a new one 链 接, 显 示 Create a New Repository 的 表 单 ( 见 图 4-5): 当 然, 项 目 名 称 是 必 不 可 少 的, 此 外 也 可 以 适 当 描 述 一 下 项 目 的 情 况 或 者 给 出 官 方 站 点 的 地 址 然 后 点 击 Create Repository 按 钮, 新 仓 库 就 建 立 起 来 了 ( 见 图 4-6): 111

122 第 4 章 服 务 器 上 的 Git Sco Chacon Pro Git 中 文 版 图 4.5: 在 GitHub 上 建 立 新 仓 库 图 4.6: GitHub 上 各 个 项 目 的 概 要 信 息 由 于 尚 未 提 交 代 码, 点 击 项 目 地 址 后 GitHub 会 显 示 一 个 简 要 的 指 南, 告 诉 你 如 何 新 建 一 个 项 目 并 推 送 上 来, 如 何 从 现 有 项 目 推 送, 以 及 如 何 从 一 个 公 共 的 Subversion 仓 库 导 入 项 目 ( 见 图 4-7): 图 4.7: 新 仓 库 指 南 该 指 南 和 本 书 前 文 介 绍 的 类 似, 对 于 新 的 项 目, 需 要 先 在 本 地 初 始 化 为 Git 项 目, 添 加 要 管 理 的 文 件 并 作 首 次 提 交 : $ git init $ git add. $ git commit -m 'initial commit' 112 然 后 在 这 个 本 地 仓 库 内 把 GitHub 添 加 为 远 程 仓 库, 并 推 送 master 分 支 上 来 :

123 Sco Chacon Pro Git 中 文 版 4.10 节 Git 托 管 服 务 $ git remote add origin git@github.com:testinguser/iphone_project.git $ git push origin master 现 在 该 项 目 就 托 管 在 GitHub 上 了 你 可 以 把 它 的 URL 分 享 给 每 位 对 此 项 目 感 兴 趣 的 人 本 例 的 URL 是 而 在 项 目 页 面 的 摘 要 部 分, 你 会 发 现 有 两 个 Git URL 地 址 ( 见 图 4-8): 图 4.8: 项 目 摘 要 中 的 公 共 URL 和 私 有 URL Public Clone URL 是 一 个 公 开 的, 只 读 的 Git URL, 任 何 人 都 可 以 通 过 它 克 隆 该 项 目 可 以 随 意 散 播 这 个 URL, 比 如 发 布 到 个 人 网 站 之 类 的 地 方 等 等 Your Clone URL 是 一 个 基 于 SSH 协 议 的 可 读 可 写 URL, 只 有 使 用 与 上 传 的 SSH 公 钥 对 应 的 密 钥 来 连 接 时, 才 能 通 过 它 进 行 读 写 操 作 其 他 用 户 访 问 该 项 目 页 面 时 只 能 看 到 之 前 那 个 公 共 的 URL, 看 不 到 这 个 私 有 的 URL 从 Subversion 导 入 项 目 如 果 想 把 某 个 公 共 Subversion 项 目 导 入 Git,GitHub 可 以 帮 忙 在 指 南 的 最 后 有 一 个 指 向 导 入 Subversion 页 面 的 链 接 点 击 它 会 看 到 一 个 表 单, 包 含 有 关 导 入 流 程 的 信 息 以 及 一 个 用 来 粘 贴 公 共 Subversion 项 目 连 接 的 文 本 框 ( 见 图 4-9): 图 4.9: Subversion 导 入 界 面 如 果 项 目 很 大, 采 用 非 标 准 结 构, 或 者 是 私 有 的, 那 就 无 法 借 助 该 工 具 实 现 导 入 到 第 7 章, 我 们 会 介 绍 如 何 手 工 导 入 复 杂 工 程 的 具 体 方 法 113

124 第 4 章 服 务 器 上 的 Git Sco Chacon Pro Git 中 文 版 添 加 协 作 开 发 者 现 在 把 团 队 里 的 其 他 人 也 加 进 来 如 果 John,Josie 和 Jessica 都 在 GitHub 注 册 了 账 户, 要 赋 予 他 们 对 该 仓 库 的 推 送 权 限, 可 以 把 他 们 加 为 项 目 协 作 者 这 样 他 们 就 可 以 通 过 各 自 的 公 钥 访 问 我 的 这 个 仓 库 了 点 击 项 目 页 面 上 方 的 edit 按 钮 或 者 顶 部 的 Admin 标 签, 进 入 该 项 目 的 管 理 页 面 ( 见 图 4-10): 图 4.10: GitHub 的 项 目 管 理 页 面 为 了 给 另 一 个 用 户 添 加 项 目 的 写 权 限, 点 击 Add another collaborator 链 接, 出 现 一 个 用 于 输 入 用 户 名 的 表 单 在 输 入 的 同 时, 它 会 自 动 跳 出 一 个 符 合 条 件 的 候 选 名 单 找 到 正 确 用 户 名 之 后, 点 Add 按 钮, 把 该 用 户 设 为 项 目 协 作 者 ( 见 图 4-11): 图 4.11: 为 项 目 添 加 协 作 者 添 加 完 协 作 者 之 后, 就 可 以 在 Repository Collaborators 区 域 看 到 他 们 的 名 单 ( 见 图 4-12): 图 4.12: 项 目 协 作 者 名 单 如 果 要 取 消 某 人 的 访 问 权, 点 击 revoke 即 可 取 消 他 的 推 送 权 限 对 于 将 来 的 项 目, 你 可 以 从 现 有 项 目 复 制 协 作 者 名 单, 或 者 直 接 借 用 协 作 者 群 组 114

125 Sco Chacon Pro Git 中 文 版 4.10 节 Git 托 管 服 务 项 目 页 面 在 推 送 或 从 Subversion 导 入 项 目 之 后, 你 会 看 到 一 个 类 似 图 4-13 的 项 目 主 页 : 图 4.13: GitHub 上 的 项 目 主 页 别 人 访 问 你 的 项 目 时 看 到 的 就 是 这 个 页 面 它 有 若 干 导 航 标 签,Commits 标 签 用 于 显 示 提 交 历 史, 最 新 的 提 交 位 于 最 上 方, 这 和 git log 命 令 的 输 出 类 似 Network 标 签 展 示 所 有 派 生 了 该 项 目 并 做 出 贡 献 的 用 户 的 关 系 图 谱 Downloads 标 签 允 许 你 上 传 项 目 的 二 进 制 文 件, 提 供 下 载 该 项 目 各 个 版 本 的 tar/zip 包 Wiki 标 签 提 供 了 一 个 用 于 撰 写 文 档 或 其 他 项 目 相 关 信 息 的 wiki 站 点 Graphs 标 签 包 含 了 一 些 可 视 化 的 项 目 信 息 与 数 据 默 认 打 开 的 Source 标 签 页 面, 则 列 出 了 该 项 目 的 目 录 结 构 和 概 要 信 息, 并 在 下 方 自 动 展 示 README 文 件 的 内 容 ( 如 果 该 文 件 存 在 的 话 ), 此 外 还 会 显 示 最 近 一 次 提 交 的 相 关 信 息 派 生 项 目 如 果 要 为 一 个 自 己 没 有 推 送 权 限 的 项 目 贡 献 代 码,GitHub 鼓 励 使 用 派 生 (fork) 到 那 个 感 兴 趣 的 项 目 主 页 上, 点 击 页 面 上 方 的 fork 按 钮,GitHub 就 会 为 你 复 制 一 份 该 项 目 的 副 本 到 你 的 仓 库 中, 这 样 你 就 可 以 向 自 己 的 这 个 副 本 推 送 数 据 了 采 取 这 种 办 法 的 好 处 是, 项 目 拥 有 者 不 必 忙 于 应 付 赋 予 他 人 推 送 权 限 的 工 作 随 便 谁 都 可 以 通 过 派 生 得 到 一 个 项 目 副 本 并 在 其 中 展 开 工 作, 事 后 只 需 要 项 目 维 护 者 将 这 些 副 本 仓 库 加 为 远 程 仓 库, 然 后 提 取 更 新 合 并 即 可 要 派 生 一 个 项 目, 到 原 始 项 目 的 页 面 ( 本 例 中 是 mojombo/chronic) 点 击 fork 按 钮 ( 见 图 4-14): 几 秒 钟 之 后, 你 将 进 入 新 建 的 项 目 页 面, 会 显 示 该 项 目 派 生 自 哪 一 个 项 目 ( 见 图 4-15): GitHub 小 结 关 于 GitHub 就 先 介 绍 这 么 多, 能 够 快 速 达 成 这 些 事 情 非 常 重 要 ( 译 注 : 门 槛 的 降 低 和 完 成 基 本 任 务 的 简 单 高 效, 对 于 推 动 开 源 项 目 的 协 作 发 展 有 着 举 足 轻 重 的 意 115

126 第 4 章 服 务 器 上 的 Git Sco Chacon Pro Git 中 文 版 图 4.14: 点 击 fork 按 钮 获 得 任 意 项 目 的 可 写 副 本 图 4.15: 派 生 后 得 到 的 项 目 副 本 义 ) 短 短 几 分 钟 内, 你 就 能 创 建 一 个 新 账 户, 添 加 一 个 项 目 并 开 始 推 送 如 果 项 目 是 开 源 的, 整 个 庞 大 的 开 发 者 社 区 都 可 以 立 即 访 问 它, 提 供 各 式 各 样 的 帮 助 和 贡 献 最 起 码, 这 也 是 一 种 Git 新 手 立 即 体 验 尝 试 Git 的 捷 径 4.11 小 结 我 们 讨 论 并 介 绍 了 一 些 建 立 远 程 Git 仓 库 的 方 法, 接 下 来 你 可 以 通 过 这 些 仓 库 同 他 人 分 享 或 合 作 运 行 自 己 的 服 务 器 意 味 着 更 多 的 控 制 权 以 及 在 防 火 墙 内 部 操 作 的 可 能 性, 当 然 这 样 的 服 务 器 通 常 需 要 投 入 一 定 的 时 间 精 力 来 架 设 维 护 如 果 直 接 托 管, 虽 然 能 免 去 这 部 分 工 作, 但 有 时 出 于 安 全 或 版 权 的 考 虑, 有 些 公 司 禁 止 将 商 业 代 码 托 管 到 第 三 方 服 务 商 所 以 究 竟 采 取 哪 种 方 案, 并 不 是 个 难 以 取 舍 的 问 题, 或 者 其 一, 或 者 相 互 配 合, 哪 种 合 适 就 用 哪 种 116

127 第 5 章 分 布 式 Git 为 了 便 于 项 目 中 的 所 有 开 发 者 分 享 代 码, 我 们 准 备 好 了 一 台 服 务 器 存 放 远 程 Git 仓 库 经 过 前 面 几 章 的 学 习, 我 们 已 经 学 会 了 一 些 基 本 的 本 地 工 作 流 程 中 所 需 用 到 的 命 令 接 下 来, 我 们 要 学 习 下 如 何 利 用 Git 来 组 织 和 完 成 分 布 式 工 作 流 程 特 别 是, 当 作 为 项 目 贡 献 者 时, 我 们 该 怎 么 做 才 能 方 便 维 护 者 采 纳 更 新 ; 或 者 作 为 项 目 维 护 者 时, 又 该 怎 样 有 效 管 理 大 量 贡 献 者 的 提 交 5.1 分 布 式 工 作 流 程 同 传 统 的 集 中 式 版 本 控 制 系 统 (CVCS) 不 同, 开 发 者 之 间 的 协 作 方 式 因 着 Git 的 分 布 式 特 性 而 变 得 更 为 灵 活 多 样 在 集 中 式 系 统 上, 每 个 开 发 者 就 像 是 连 接 在 集 线 器 上 的 节 点, 彼 此 的 工 作 方 式 大 体 相 像 而 在 Git 网 络 中, 每 个 开 发 者 同 时 扮 演 着 节 点 和 集 线 器 的 角 色, 这 就 是 说, 每 一 个 开 发 者 都 可 以 将 自 己 的 代 码 贡 献 到 另 外 一 个 开 发 者 的 仓 库 中, 或 者 建 立 自 己 的 公 共 仓 库, 让 其 他 开 发 者 基 于 自 己 的 工 作 开 始, 为 自 己 的 仓 库 贡 献 代 码 于 是,Git 的 分 布 式 协 作 便 可 以 衍 生 出 种 种 不 同 的 工 作 流 程, 我 会 在 接 下 来 的 章 节 介 绍 几 种 常 见 的 应 用 方 式, 并 分 别 讨 论 各 自 的 优 缺 点 你 可 以 选 择 其 中 的 一 种, 或 者 结 合 起 来, 应 用 到 你 自 己 的 项 目 中 集 中 式 工 作 流 通 常, 集 中 式 工 作 流 程 使 用 的 都 是 单 点 协 作 模 型 一 个 存 放 代 码 仓 库 的 中 心 服 务 器, 可 以 接 受 所 有 开 发 者 提 交 的 代 码 所 有 的 开 发 者 都 是 普 通 的 节 点, 作 为 中 心 集 线 器 的 消 费 者, 平 时 的 工 作 就 是 和 中 心 仓 库 同 步 数 据 ( 见 图 5-1) 如 果 两 个 开 发 者 从 中 心 仓 库 克 隆 代 码 下 来, 同 时 作 了 一 些 修 订, 那 么 只 有 第 一 个 开 发 者 可 以 顺 利 地 把 数 据 推 送 到 共 享 服 务 器 第 二 个 开 发 者 在 提 交 他 的 修 订 之 前, 必 须 先 下 载 合 并 服 务 器 上 的 数 据, 解 决 冲 突 之 后 才 能 推 送 数 据 到 共 享 服 务 器 上 在 Git 中 这 么 用 也 决 无 问 题, 这 就 好 比 是 在 用 Subversion( 或 其 他 CVCS) 一 样, 可 以 很 好 地 工 作 如 果 你 的 团 队 不 是 很 大, 或 者 大 家 都 已 经 习 惯 了 使 用 集 中 式 工 作 流 程, 完 全 可 以 采 用 这 种 简 单 的 模 式 只 需 要 配 置 好 一 台 中 心 服 务 器, 并 给 每 个 人 推 送 数 据 的 权 117

128 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 图 5.1: 集 中 式 工 作 流 限, 就 可 以 开 展 工 作 了 但 如 果 提 交 代 码 时 有 冲 突,Git 根 本 就 不 会 让 用 户 覆 盖 他 人 代 码, 它 直 接 驳 回 第 二 个 人 的 提 交 操 作 这 就 等 于 告 诉 提 交 者, 你 所 作 的 修 订 无 法 通 过 快 近 (fast-forward) 来 合 并, 你 必 须 先 拉 取 最 新 数 据 下 来, 手 工 解 决 冲 突 合 并 后, 才 能 继 续 推 送 新 的 提 交 绝 大 多 数 人 都 熟 悉 和 了 解 这 种 模 式 的 工 作 方 式, 所 以 使 用 也 非 常 广 泛 集 成 管 理 员 工 作 流 由 于 Git 允 许 使 用 多 个 远 程 仓 库, 开 发 者 便 可 以 建 立 自 己 的 公 共 仓 库, 往 里 面 写 数 据 并 共 享 给 他 人, 而 同 时 又 可 以 从 别 人 的 仓 库 中 提 取 他 们 的 更 新 过 来 这 种 情 形 通 常 都 会 有 个 代 表 着 官 方 发 布 的 项 目 仓 库 (blessed repository), 开 发 者 们 由 此 仓 库 克 隆 出 一 个 自 己 的 公 共 仓 库 (developer public), 然 后 将 自 己 的 提 交 推 送 上 去, 请 求 官 方 仓 库 的 维 护 者 拉 取 更 新 合 并 到 主 项 目 维 护 者 在 自 己 的 本 地 也 有 个 克 隆 仓 库 (integration manager), 他 可 以 将 你 的 公 共 仓 库 作 为 远 程 仓 库 添 加 进 来, 经 过 测 试 无 误 后 合 并 到 主 干 分 支, 然 后 再 推 送 到 官 方 仓 库 工 作 流 程 看 起 来 就 像 图 5-2 所 示 : 1. 项 目 维 护 者 可 以 推 送 数 据 到 公 共 仓 库 blessed repository 2. 贡 献 者 克 隆 此 仓 库, 修 订 或 编 写 新 代 码 3. 贡 献 者 推 送 数 据 到 自 己 的 公 共 仓 库 developer public 4. 贡 献 者 给 维 护 者 发 送 邮 件, 请 求 拉 取 自 己 的 最 新 修 订 5. 维 护 者 在 自 己 本 地 的 integration manger 仓 库 中, 将 贡 献 者 的 仓 库 加 为 远 程 仓 库, 合 并 更 新 并 做 测 试 6. 维 护 者 将 合 并 后 的 更 新 推 送 到 主 仓 库 blessed repository 图 5.2: 集 成 管 理 员 工 作 流 118

129 Sco Chacon Pro Git 中 文 版 5.1 节 分 布 式 工 作 流 程 在 GitHub 网 站 上 使 用 得 最 多 的 就 是 这 种 工 作 流 人 们 可 以 复 制 (fork 亦 即 克 隆 ) 某 个 项 目 到 自 己 的 列 表 中, 成 为 自 己 的 公 共 仓 库 随 后 将 自 己 的 更 新 提 交 到 这 个 仓 库, 所 有 人 都 可 以 看 到 你 的 每 次 更 新 这 么 做 最 主 要 的 优 点 在 于, 你 可 以 按 照 自 己 的 节 奏 继 续 工 作, 而 不 必 等 待 维 护 者 处 理 你 提 交 的 更 新 ; 而 维 护 者 也 可 以 按 照 自 己 的 节 奏, 任 何 时 候 都 可 以 过 来 处 理 接 纳 你 的 贡 献 司 令 官 与 副 官 工 作 流 这 其 实 是 上 一 种 工 作 流 的 变 体 一 般 超 大 型 的 项 目 才 会 用 到 这 样 的 工 作 方 式, 像 是 拥 有 数 百 协 作 开 发 者 的 Linux 内 核 项 目 就 是 如 此 各 个 集 成 管 理 员 分 别 负 责 集 成 项 目 中 的 特 定 部 分, 所 以 称 为 副 官 (lieutenant) 而 所 有 这 些 集 成 管 理 员 头 上 还 有 一 位 负 责 统 筹 的 总 集 成 管 理 员, 称 为 司 令 官 (dictator) 司 令 官 维 护 的 仓 库 用 于 提 供 所 有 协 作 者 拉 取 最 新 集 成 的 项 目 代 码 整 个 流 程 看 起 来 如 图 5-3 所 示 : 1. 一 般 的 开 发 者 在 自 己 的 特 性 分 支 上 工 作, 并 不 定 期 地 根 据 主 干 分 支 (dictator 上 的 master) 衍 合 2. 副 官 (lieutenant) 将 普 通 开 发 者 的 特 性 分 支 合 并 到 自 己 的 master 分 支 中 3. 司 令 官 (dictator) 将 所 有 副 官 的 master 分 支 并 入 自 己 的 master 分 支 4. 司 令 官 (dictator) 将 集 成 后 的 master 分 支 推 送 到 共 享 仓 库 blessed repository 中, 以 便 所 有 其 他 开 发 者 以 此 为 基 础 进 行 衍 合 图 5.3: 司 令 官 与 副 官 工 作 流 这 种 工 作 流 程 并 不 常 用, 只 有 当 项 目 极 为 庞 杂, 或 者 需 要 多 级 别 管 理 时, 才 会 体 现 出 优 势 利 用 这 种 方 式, 项 目 总 负 责 人 ( 即 司 令 官 ) 可 以 把 大 量 分 散 的 集 成 工 作 委 托 给 不 同 的 小 组 负 责 人 分 别 处 理, 最 后 再 统 筹 起 来, 如 此 各 人 的 职 责 清 晰 明 确, 也 不 易 出 错 ( 译 注 : 此 乃 分 而 治 之 ) 以 上 介 绍 的 是 常 见 的 分 布 式 系 统 可 以 应 用 的 工 作 流 程, 当 然 不 止 于 Git 在 实 际 的 开 发 工 作 中, 你 可 能 会 遇 到 各 种 为 了 满 足 特 定 需 求 而 有 所 变 化 的 工 作 方 式 我 想 现 在 你 应 该 已 经 清 楚, 接 下 来 自 己 需 要 用 哪 种 方 式 开 展 工 作 了 下 节 我 还 会 再 举 些 例 子, 看 看 各 式 工 作 流 中 的 每 个 角 色 具 体 应 该 如 何 操 作 119

130 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 5.2 为 项 目 作 贡 献 接 下 来, 我 们 来 学 习 一 下 作 为 项 目 贡 献 者, 会 有 哪 些 常 见 的 工 作 模 式 不 过 要 说 清 楚 整 个 协 作 过 程 真 的 很 难,Git 如 此 灵 活, 人 们 的 协 作 方 式 便 可 以 各 式 各 样, 没 有 固 定 不 变 的 范 式 可 循, 而 每 个 项 目 的 具 体 情 况 又 多 少 会 有 些 不 同, 比 如 说 参 与 者 的 规 模, 所 选 择 的 工 作 流 程, 每 个 人 的 提 交 权 限, 以 及 Git 以 外 贡 献 等 等, 都 会 影 响 到 具 体 操 作 的 细 节 首 当 其 冲 的 是 参 与 者 规 模 项 目 中 有 多 少 开 发 者 是 经 常 提 交 代 码 的? 经 常 又 是 多 久 呢? 大 多 数 两 至 三 人 的 小 团 队, 一 天 大 约 只 有 几 次 提 交, 如 果 不 是 什 么 热 门 项 目 的 话 就 更 少 了 可 要 是 在 大 公 司 里, 或 者 大 项 目 中, 参 与 者 可 以 多 到 上 千, 每 天 都 会 有 十 几 个 上 百 个 补 丁 提 交 上 来 这 种 差 异 带 来 的 影 响 是 显 著 的, 越 是 多 的 人 参 与 进 来, 就 越 难 保 证 每 次 合 并 正 确 无 误 你 正 在 工 作 的 代 码, 可 能 会 因 为 合 并 进 来 其 他 人 的 更 新 而 变 得 过 时, 甚 至 受 创 无 法 运 行 而 已 经 提 交 上 去 的 更 新, 也 可 能 在 等 着 审 核 合 并 的 过 程 中 变 得 过 时 那 么, 我 们 该 怎 样 做 才 能 确 保 代 码 是 最 新 的, 提 交 的 补 丁 也 是 可 用 的 呢? 接 下 来 便 是 项 目 所 采 用 的 工 作 流 是 集 中 式 的, 每 个 开 发 者 都 具 有 等 同 的 写 权 限? 项 目 是 否 有 专 人 负 责 检 查 所 有 补 丁? 是 不 是 所 有 补 丁 都 做 过 同 行 复 阅 (peer-review) 再 通 过 审 核 的? 你 是 否 参 与 审 核 过 程? 如 果 使 用 副 官 系 统, 那 你 是 不 是 限 定 于 只 能 向 此 副 官 提 交? 还 有 你 的 提 交 权 限 有 或 没 有 向 主 项 目 提 交 更 新 的 权 限, 结 果 完 全 不 同, 直 接 决 定 最 终 采 用 怎 样 的 工 作 流 如 果 不 能 直 接 提 交 更 新, 那 该 如 何 贡 献 自 己 的 代 码 呢? 是 不 是 该 有 个 什 么 策 略? 你 每 次 贡 献 代 码 会 有 多 少 量? 提 交 频 率 呢? 所 有 以 上 这 些 问 题 都 会 或 多 或 少 影 响 到 最 终 采 用 的 工 作 流 接 下 来, 我 会 在 一 系 列 由 简 入 繁 的 具 体 用 例 中, 逐 一 阐 述 此 后 在 实 践 时, 应 该 可 以 借 鉴 这 里 的 例 子, 略 作 调 整, 以 满 足 实 际 需 要 构 建 自 己 的 工 作 流 提 交 指 南 开 始 分 析 特 定 用 例 之 前, 先 来 了 解 下 如 何 撰 写 提 交 说 明 一 份 好 的 提 交 指 南 可 以 帮 助 协 作 者 更 轻 松 更 有 效 地 配 合 Git 项 目 本 身 就 提 供 了 一 份 文 档 (Git 项 目 源 代 码 目 录 中 Documentation/SubmittingPatches), 列 数 了 大 量 提 示, 从 如 何 编 撰 提 交 说 明 到 提 交 补 丁, 不 一 而 足 首 先, 请 不 要 在 更 新 中 提 交 多 余 的 白 字 符 (whitespace) Git 有 种 检 查 此 类 问 题 的 方 法, 在 提 交 之 前, 先 运 行 git diff --check, 会 把 可 能 的 多 余 白 字 符 修 正 列 出 来 下 面 的 示 例, 我 已 经 把 终 端 中 显 示 为 红 色 的 白 字 符 用 X 替 换 掉 : $ git diff --check lib/simplegit.rb:5: trailing whitespace. = File.expand_path(git_dir)XX lib/simplegit.rb:7: trailing whitespace. 120

131 Sco Chacon Pro Git 中 文 版 5.2 节 为 项 目 作 贡 献 + XXXXXXXXXXX lib/simplegit.rb:26: trailing whitespace. + def command(git_cmd)xxxx 这 样 在 提 交 之 前 你 就 可 以 看 到 这 类 问 题, 及 时 解 决 以 免 困 扰 其 他 开 发 者 接 下 来, 请 将 每 次 提 交 限 定 于 完 成 一 次 逻 辑 功 能 并 且 可 能 的 话, 适 当 地 分 解 为 多 次 小 更 新, 以 便 每 次 小 型 提 交 都 更 易 于 理 解 请 不 要 在 周 末 穷 追 猛 打 一 次 性 解 决 五 个 问 题, 而 最 后 拖 到 周 一 再 提 交 就 算 是 这 样 也 请 尽 可 能 利 用 暂 存 区 域, 将 之 前 的 改 动 分 解 为 每 次 修 复 一 个 问 题, 再 分 别 提 交 和 加 注 说 明 如 果 针 对 两 个 问 题 改 动 的 是 同 一 个 文 件, 可 以 试 试 看 git add --patch 的 方 式 将 部 分 内 容 置 入 暂 存 区 域 ( 我 们 会 在 第 六 章 再 详 细 介 绍 ) 无 论 是 五 次 小 提 交 还 是 混 杂 在 一 起 的 大 提 交, 最 终 分 支 末 端 的 项 目 快 照 应 该 还 是 一 样 的, 但 分 解 开 来 之 后, 更 便 于 其 他 开 发 者 复 阅 这 么 做 也 方 便 自 己 将 来 取 消 某 个 特 定 问 题 的 修 复 我 们 将 在 第 六 章 介 绍 一 些 重 写 提 交 历 史, 同 暂 存 区 域 交 互 的 技 巧 和 工 具, 以 便 最 终 得 到 一 个 干 净 有 意 义, 且 易 于 理 解 的 提 交 历 史 最 后 需 要 谨 记 的 是 提 交 说 明 的 撰 写 写 得 好 可 以 让 大 家 协 作 起 来 更 轻 松 一 般 来 说, 提 交 说 明 最 好 限 制 在 一 行 以 内,50 个 字 符 以 下, 简 明 扼 要 地 描 述 更 新 内 容, 空 开 一 行 后, 再 展 开 详 细 注 解 Git 项 目 本 身 需 要 开 发 者 撰 写 详 尽 注 解, 包 括 本 次 修 订 的 因 由, 以 及 前 后 不 同 实 现 之 间 的 比 较, 我 们 也 该 借 鉴 这 种 做 法 另 外, 提 交 说 明 应 该 用 祈 使 现 在 式 语 态, 比 如, 不 要 说 成 I added tests for 或 Adding tests for 而 应 该 用 Add tests for 下 面 是 来 自 tpope.net 的 Tim Pope 原 创 的 提 交 说 明 格 式 模 版, 供 参 考 : 本 次 更 新 的 简 要 描 述 (50 个 字 符 以 内 ) 如 果 必 要, 此 处 展 开 详 尽 阐 述 段 落 宽 度 限 定 在 72 个 字 符 以 内 某 些 情 况 下, 第 一 行 的 简 要 描 述 将 用 作 邮 件 标 题, 其 余 部 分 作 为 邮 件 正 文 其 间 的 空 行 是 必 要 的, 以 区 分 两 者 ( 当 然 没 有 正 文 另 当 别 论 ) 如 果 并 在 一 起,rebase 这 样 的 工 具 就 可 能 会 迷 惑 另 起 空 行 后, 再 进 一 步 补 充 其 他 说 明 - 可 以 使 用 这 样 的 条 目 列 举 式 - 一 般 以 单 个 空 格 紧 跟 短 划 线 或 者 星 号 作 为 每 项 条 目 的 起 始 符 每 个 条 目 间 用 一 空 行 隔 开 不 过 这 里 按 自 己 项 目 的 约 定, 可 以 略 作 变 化 如 果 你 的 提 交 说 明 都 用 这 样 的 格 式 来 书 写, 好 多 事 情 就 可 以 变 得 十 分 简 单 Git 项 121

132 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 目 本 身 就 是 这 样 要 求 的, 我 强 烈 建 议 你 到 Git 项 目 仓 库 下 运 行 git log --no-merges 看 看, 所 有 提 交 历 史 的 说 明 是 怎 样 撰 写 的 ( 译 注 : 如 果 现 在 还 没 有 克 隆 git 项 目 源 代 码, 是 时 候 git clone git://git.kernel.org/pub/scm/git/git.git 了 ) 为 简 单 起 见, 在 接 下 来 的 例 子 ( 及 本 书 随 后 的 所 有 演 示 ) 中, 我 都 不 会 用 这 种 格 式, 而 使 用 -m 选 项 提 交 git commit 不 过 请 还 是 按 照 我 之 前 讲 的 做, 别 学 我 这 里 偷 懒 的 方 式 私 有 的 小 型 团 队 我 们 从 最 简 单 的 情 况 开 始, 一 个 私 有 项 目, 与 你 一 起 协 作 的 还 有 另 外 一 到 两 位 开 发 者 这 里 说 私 有, 是 指 源 代 码 不 公 开, 其 他 人 无 法 访 问 项 目 仓 库 而 你 和 其 他 开 发 者 则 都 具 有 推 送 数 据 到 仓 库 的 权 限 这 种 情 况 下, 你 们 可 以 用 Subversion 或 其 他 集 中 式 版 本 控 制 系 统 类 似 的 工 作 流 来 协 作 你 仍 然 可 以 得 到 Git 带 来 的 其 他 好 处 : 离 线 提 交, 快 速 分 支 与 合 并 等 等, 但 工 作 流 程 还 是 差 不 多 的 主 要 区 别 在 于, 合 并 操 作 发 生 在 客 户 端 而 非 服 务 器 上 让 我 们 来 看 看, 两 个 开 发 者 一 起 使 用 同 一 个 共 享 仓 库, 会 发 生 些 什 么 第 一 个 人,John, 克 隆 了 仓 库, 作 了 些 更 新, 在 本 地 提 交 ( 下 面 的 例 子 中 省 略 了 常 规 提 示, 用... 代 替 以 节 约 版 面 ) # John's Machine $ git clone john@githost:simplegit.git Initialized empty Git repository in /home/john/simplegit/.git/... $ cd simplegit/ $ vim lib/simplegit.rb $ git commit -am 'removed invalid default value' [master 738ee87] removed invalid default value 1 files changed, 1 insertions(+), 1 deletions(-) 第 二 个 开 发 者,Jessica, 一 样 这 么 做 : 克 隆 仓 库, 提 交 更 新 : # Jessica's Machine $ git clone jessica@githost:simplegit.git Initialized empty Git repository in /home/jessica/simplegit/.git/... $ cd simplegit/ $ vim TODO $ git commit -am 'add reset task' 122

133 Sco Chacon Pro Git 中 文 版 5.2 节 为 项 目 作 贡 献 [master fbff5bc] add reset task 1 files changed, 1 insertions(+), 0 deletions(-) 现 在,Jessica 将 她 的 工 作 推 送 到 服 务 器 上 : # Jessica's Machine $ git push origin master... To jessica@githost:simplegit.git 1edee6b..fbff5bc master -> master John 也 尝 试 推 送 自 己 的 工 作 上 去 : # John's Machine $ git push origin master To john@githost:simplegit.git! [rejected] master -> master (non-fast forward) error: failed to push some refs to 'john@githost:simplegit.git' John 的 推 送 操 作 被 驳 回, 因 为 Jessica 已 经 推 送 了 新 的 数 据 上 去 请 注 意, 特 别 是 你 用 惯 了 Subversion 的 话, 这 里 其 实 修 改 的 是 两 个 文 件, 而 不 是 同 一 个 文 件 的 同 一 个 地 方 Subversion 会 在 服 务 器 端 自 动 合 并 提 交 上 来 的 更 新, 而 Git 则 必 须 先 在 本 地 合 并 后 才 能 推 送 于 是,John 不 得 不 先 把 Jessica 的 更 新 拉 下 来 : $ git fetch origin... From john@githost:simplegit + 049d078...fbff5bc master -> origin/master 此 刻,John 的 本 地 仓 库 如 图 5-4 所 示 : 虽 然 John 下 载 了 Jessica 推 送 到 服 务 器 的 最 近 更 新 ( ff5), 但 目 前 只 是 origin/master 指 针 指 向 它, 而 当 前 的 本 地 分 支 master 仍 然 指 向 自 己 的 更 新 (738ee), 所 以 需 要 先 把 她 的 提 交 合 并 过 来, 才 能 继 续 推 送 数 据 : 123

134 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 图 5.4: John 的 仓 库 历 史 $ git merge origin/master Merge made by recursive. TODO files changed, 1 insertions(+), 0 deletions(-) 还 好, 合 并 过 程 非 常 顺 利, 没 有 冲 突, 现 在 John 的 提 交 历 史 如 图 5-5 所 示 : 图 5.5: 合 并 origin/master 后 John 的 仓 库 历 史 现 在,John 应 该 再 测 试 一 下 代 码 是 否 仍 然 正 常 工 作, 然 后 将 合 并 结 果 (72bbc) 推 送 到 服 务 器 上 : $ git push origin master... To john@githost:simplegit.git 124

135 Sco Chacon Pro Git 中 文 版 5.2 节 为 项 目 作 贡 献 fbff5bc..72bbc59 master -> master 最 终,John 的 提 交 历 史 变 为 图 5-6 所 示 : 图 5.6: 推 送 后 John 的 仓 库 历 史 而 在 这 段 时 间,Jessica 已 经 开 始 在 另 一 个 特 性 分 支 工 作 了 她 创 建 了 issue54 并 提 交 了 三 次 更 新 她 还 没 有 下 载 John 提 交 的 合 并 结 果, 所 以 提 交 历 史 如 图 5-7 所 示 : 图 5.7: Jessica 的 提 交 历 史 Jessica 想 要 先 和 服 务 器 上 的 数 据 同 步, 所 以 先 下 载 数 据 : # Jessica's Machine $ git fetch origin... From jessica@githost:simplegit fbff5bc..72bbc59 master -> origin/master 于 是 Jessica 的 本 地 仓 库 历 史 多 出 了 John 的 两 次 提 交 (738ee 和 72bbc), 如 图 5-8 所 示 : 此 时,Jessica 在 特 性 分 支 上 的 工 作 已 经 完 成, 但 她 想 在 推 送 数 据 之 前, 先 确 认 下 要 并 进 来 的 数 据 究 竟 是 什 么, 于 是 运 行 git log 查 看 : $ git log --no-merges origin/master ^issue54 125

136 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 图 5.8: 获 取 John 的 更 新 之 后 Jessica 的 提 交 历 史 commit 738ee872852dfaa9d6634e0dea7a Author: John Smith <jsmith@example.com> Date: Fri May 29 16:01: removed invalid default value 现 在,Jessica 可 以 将 特 性 分 支 上 的 工 作 并 到 master 分 支, 然 后 再 并 入 John 的 工 作 (origin/master) 到 自 己 的 master 分 支, 最 后 再 推 送 回 服 务 器 当 然, 得 先 切 回 主 分 支 才 能 集 成 所 有 数 据 : $ git checkout master Switched to branch "master" Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded. 要 合 并 origin/master 或 issue54 分 支, 谁 先 谁 后 都 没 有 关 系, 因 为 它 们 都 在 上 游 (upstream)( 译 注 : 想 像 分 叉 的 更 新 像 是 汇 流 成 河 的 源 头, 所 以 上 游 upstream 是 指 最 新 的 提 交 ), 所 以 无 所 谓 先 后 顺 序, 最 终 合 并 后 的 内 容 快 照 都 是 一 样 的, 而 仅 是 提 交 历 史 看 起 来 会 有 些 先 后 差 别 Jessica 选 择 先 合 并 issue54: $ git merge issue54 Updating fbff5bc..4af4298 Fast forward README 1 + lib/simplegit.rb files changed, 6 insertions(+), 1 deletions(-) 正 如 所 见, 没 有 冲 突 发 生, 仅 是 一 次 简 单 快 进 现 在 Jessica 开 始 合 并 John 的 工 作 (origin/master): 126

137 Sco Chacon Pro Git 中 文 版 5.2 节 为 项 目 作 贡 献 $ git merge origin/master Auto-merging lib/simplegit.rb Merge made by recursive. lib/simplegit.rb files changed, 1 insertions(+), 1 deletions(-) 所 有 的 合 并 都 非 常 干 净 现 在 Jessica 的 提 交 历 史 如 图 5-9 所 示 : 图 5.9: 合 并 John 的 更 新 后 Jessica 的 提 交 历 史 现 在 Jessica 已 经 可 以 在 自 己 的 master 分 支 中 访 问 origin/master 的 最 新 改 动 了, 所 以 她 应 该 可 以 成 功 推 送 最 后 的 合 并 结 果 到 服 务 器 上 ( 假 设 John 此 时 没 再 推 送 新 数 据 上 来 ): $ git push origin master... To jessica@githost:simplegit.git 72bbc c15 master -> master 至 此, 每 个 开 发 者 都 提 交 了 若 干 次, 且 成 功 合 并 了 对 方 的 工 作 成 果, 最 新 的 提 交 历 史 如 图 5-10 所 示 : 图 5.10: Jessica 推 送 数 据 后 的 提 交 历 史 以 上 就 是 最 简 单 的 协 作 方 式 之 一 : 先 在 自 己 的 特 性 分 支 中 工 作 一 段 时 间, 完 成 后 合 并 到 自 己 的 master 分 支 ; 然 后 下 载 合 并 origin/master 上 的 更 新 ( 如 果 有 的 话 ), 再 推 回 远 程 服 务 器 一 般 的 协 作 流 程 如 图 5-11 所 示 : 127

138 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 图 5.11: 多 用 户 共 享 仓 库 协 作 方 式 的 一 般 工 作 流 程 时 序 私 有 团 队 间 协 作 现 在 我 们 来 看 更 大 一 点 规 模 的 私 有 团 队 协 作 如 果 有 几 个 小 组 分 头 负 责 若 干 特 性 的 开 发 和 集 成, 那 他 们 之 间 的 协 作 过 程 是 怎 样 的 假 设 John 和 Jessica 一 起 负 责 开 发 某 项 特 性 A, 而 同 时 Jessica 和 Josie 一 起 负 责 开 发 另 一 项 功 能 B 公 司 使 用 典 型 的 集 成 管 理 员 式 工 作 流, 每 个 组 都 有 一 名 管 理 员 负 责 集 成 本 组 代 码, 及 更 新 项 目 主 仓 库 的 master 分 支 所 有 开 发 都 在 代 表 小 组 的 分 支 上 进 行 让 我 们 跟 随 Jessica 的 视 角 看 看 她 的 工 作 流 程 她 参 与 开 发 两 项 特 性, 同 时 和 不 同 小 组 的 开 发 者 一 起 协 作 克 隆 生 成 本 地 仓 库 后, 她 打 算 先 着 手 开 发 特 性 A 于 是 创 建 了 新 的 featurea 分 支, 继 而 编 写 代 码 : # Jessica's Machine $ git checkout -b featurea Switched to a new branch "featurea" $ vim lib/simplegit.rb $ git commit -am 'add limit to log function' [featurea ] add limit to log function 1 files changed, 1 insertions(+), 1 deletions(-) 128 此 刻, 她 需 要 分 享 目 前 的 进 展 给 John, 于 是 她 将 自 己 的 featurea 分 支 提 交 到

139 Sco Chacon Pro Git 中 文 版 5.2 节 为 项 目 作 贡 献 服 务 器 由 于 Jessica 没 有 权 限 推 送 数 据 到 主 仓 库 的 master 分 支 ( 只 有 集 成 管 理 员 有 此 权 限 ), 所 以 只 能 将 此 分 支 推 上 去 同 John 共 享 协 作 : $ git push origin featurea... To jessica@githost:simplegit.git * [new branch] featurea -> featurea Jessica 发 邮 件 给 John 让 他 上 来 看 看 featurea 分 支 上 的 进 展 在 等 待 他 的 反 馈 之 前,Jessica 决 定 继 续 工 作, 和 Josie 一 起 开 发 featureb 上 的 特 性 B 当 然, 先 创 建 此 分 支, 分 叉 点 以 服 务 器 上 的 master 为 起 点 : # Jessica's Machine $ git fetch origin $ git checkout -b featureb origin/master Switched to a new branch "featureb" 随 后,Jessica 在 featureb 上 提 交 了 若 干 更 新 : $ vim lib/simplegit.rb $ git commit -am 'made the ls-tree function recursive' [featureb e5b0fdc] made the ls-tree function recursive 1 files changed, 1 insertions(+), 1 deletions(-) $ vim lib/simplegit.rb $ git commit -am 'add ls-files' [featureb ] add ls-files 1 files changed, 5 insertions(+), 0 deletions(-) 现 在 Jessica 的 更 新 历 史 如 图 5-12 所 示 : Jessica 正 准 备 推 送 自 己 的 进 展 上 去, 却 收 到 Josie 的 来 信, 说 是 她 已 经 将 自 己 的 工 作 推 到 服 务 器 上 的 featurebee 分 支 了 这 样,Jessica 就 必 须 先 将 Josie 的 代 码 合 并 到 自 己 本 地 分 支 中, 才 能 再 一 起 推 送 回 服 务 器 她 用 git fetch 下 载 Josie 的 最 新 代 码 : 129

140 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 图 5.12: Jessica 的 更 新 历 史 $ git fetch origin... From jessica@githost:simplegit * [new branch] featurebee -> origin/featurebee 然 后 Jessica 使 用 git merge 将 此 分 支 合 并 到 自 己 分 支 中 : $ git merge origin/featurebee Auto-merging lib/simplegit.rb Merge made by recursive. lib/simplegit.rb files changed, 4 insertions(+), 0 deletions(-) 合 并 很 顺 利, 但 另 外 有 个 小 问 题 : 她 要 推 送 自 己 的 featureb 分 支 到 服 务 器 上 的 featurebee 分 支 上 去 当 然, 她 可 以 使 用 冒 号 (:) 格 式 指 定 目 标 分 支 : $ git push origin featureb:featurebee... To jessica@githost:simplegit.git fba9af8..cd685d1 featureb -> featurebee 细 阐 述 130 我 们 称 此 为 refspec 更 多 有 关 于 Git refspec 的 讨 论 和 使 用 方 式 会 在 第 九 章 作 详

141 Sco Chacon Pro Git 中 文 版 5.2 节 为 项 目 作 贡 献 接 下 来,John 发 邮 件 给 Jessica 告 诉 她, 他 看 了 之 后 作 了 些 修 改, 已 经 推 回 服 务 器 featurea 分 支, 请 她 过 目 下 于 是 Jessica 运 行 git fetch 下 载 最 新 数 据 : $ git fetch origin... From jessica@githost:simplegit aad881d featurea -> origin/featurea 接 下 来 便 可 以 用 git log 查 看 更 新 了 些 什 么 : $ git log origin/featurea ^featurea commit aad881d154acdaeb2b6b18ea0e827ed8a6d671e6 Author: John Smith <jsmith@example.com> Date: Fri May 29 19:57: changed log output to 30 from 25 最 后, 她 将 John 的 工 作 合 并 到 自 己 的 featurea 分 支 中 : $ git checkout featurea Switched to branch "featurea" $ git merge origin/featurea Updating aad881d Fast forward lib/simplegit.rb files changed, 9 insertions(+), 1 deletions(-) Jessica 稍 做 一 番 修 整 后 同 步 到 服 务 器 : $ git commit -am 'small tweak' [featurea 774b3ed] small tweak 1 files changed, 1 insertions(+), 1 deletions(-) $ git push origin featurea

142 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 To jessica@githost:simplegit.git b3ed featurea -> featurea 现 在 的 Jessica 提 交 历 史 如 图 5-13 所 示 : 图 5.13: 在 特 性 分 支 中 提 交 更 新 后 的 提 交 历 史 现 在,Jessica,Josie 和 John 通 知 集 成 管 理 员 服 务 器 上 的 featurea 及 featurebee 分 支 已 经 准 备 好, 可 以 并 入 主 线 了 在 管 理 员 完 成 集 成 工 作 后, 主 分 支 上 便 多 出 一 个 新 的 合 并 提 交 (5399e), 用 fetch 命 令 更 新 到 本 地 后, 提 交 历 史 如 图 5-14 所 示 : 图 5.14: 合 并 特 性 分 支 后 的 Jessica 提 交 历 史 许 多 开 发 小 组 改 用 Git 就 是 因 为 它 允 许 多 个 小 组 间 并 行 工 作, 而 在 稍 后 恰 当 时 机 再 行 合 并 通 过 共 享 远 程 分 支 的 方 式, 无 需 干 扰 整 体 项 目 代 码 便 可 以 开 展 工 作, 因 此 使 用 Git 的 小 型 团 队 间 协 作 可 以 变 得 非 常 灵 活 自 由 以 上 工 作 流 程 的 时 序 如 图 5-15 所 示 : 公 开 的 小 型 项 目 上 面 说 的 是 私 有 项 目 协 作, 但 要 给 公 开 项 目 作 贡 献, 情 况 就 有 些 不 同 了 因 为 你 没 有 直 接 更 新 主 仓 库 分 支 的 权 限, 得 寻 求 其 它 方 式 把 工 作 成 果 交 给 项 目 维 护 人 132

143 Sco Chacon Pro Git 中 文 版 5.2 节 为 项 目 作 贡 献 图 5.15: 团 队 间 协 作 工 作 流 程 基 本 时 序 下 面 会 介 绍 两 种 方 法, 第 一 种 使 用 git 托 管 服 务 商 提 供 的 仓 库 复 制 功 能, 一 般 称 作 fork, 比 如 repo.or.cz 和 GitHub 都 支 持 这 样 的 操 作, 而 且 许 多 项 目 管 理 员 都 希 望 大 家 使 用 这 样 的 方 式 另 一 种 方 法 是 通 过 电 子 邮 件 寄 送 文 件 补 丁 但 不 管 哪 种 方 式, 起 先 我 们 总 需 要 克 隆 原 始 仓 库, 而 后 创 建 特 性 分 支 开 展 工 作 基 本 工 作 流 程 如 下 : $ git clone (url) $ cd project $ git checkout -b featurea $ (work) $ git commit $ (work) $ git commit 你 可 能 想 到 用 rebase -i 将 所 有 更 新 先 变 作 单 个 提 交, 又 或 者 想 重 新 安 排 提 交 之 间 的 差 异 补 丁, 以 方 便 项 目 维 护 者 审 阅 有 关 交 互 式 衍 合 操 作 的 细 节 见 第 六 章 在 完 成 了 特 性 分 支 开 发, 提 交 给 项 目 维 护 者 之 前, 先 到 原 始 项 目 的 页 面 上 点 击 Fork 按 钮, 创 建 一 个 自 己 可 写 的 公 共 仓 库 ( 译 注 : 即 下 面 的 url 部 分, 参 照 后 续 的 例 子, 应 该 是 git://githost/simplegit.git) 然 后 将 此 仓 库 添 加 为 本 地 的 第 二 个 远 端 仓 库, 姑 且 称 为 myfork: 133

144 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 $ git remote add myfork (url) 你 需 要 将 本 地 更 新 推 送 到 这 个 仓 库 要 是 将 远 端 master 合 并 到 本 地 再 推 回 去, 还 不 如 把 整 个 特 性 分 支 推 上 去 来 得 干 脆 直 接 而 且, 假 若 项 目 维 护 者 未 采 纳 你 的 贡 献 的 话 ( 不 管 是 直 接 合 并 还 是 cherry pick), 都 不 用 回 退 (rewind) 自 己 的 master 分 支 但 若 维 护 者 合 并 或 cherry-pick 了 你 的 工 作, 最 后 总 还 可 以 从 他 们 的 更 新 中 同 步 这 些 代 码 好 吧, 现 在 先 把 featurea 分 支 整 个 推 上 去 : $ git push myfork featurea 然 后 通 知 项 目 管 理 员, 让 他 来 抓 取 你 的 代 码 通 常 我 们 把 这 件 事 叫 做 pull request 可 以 直 接 用 GitHub 等 网 站 提 供 的 pull request 按 钮 自 动 发 送 请 求 通 知 ; 或 手 工 把 git request-pull 命 令 输 出 结 果 电 邮 给 项 目 管 理 员 request-pull 命 令 接 受 两 个 参 数, 第 一 个 是 本 地 特 性 分 支 开 始 前 的 原 始 分 支, 第 二 个 是 请 求 对 方 来 抓 取 的 Git 仓 库 URL( 译 注 : 即 下 面 myfork 所 指 的, 自 己 可 写 的 公 共 仓 库 ) 比 如 现 在 Jessica 准 备 要 给 John 发 一 个 pull requst, 她 之 前 在 自 己 的 特 性 分 支 上 提 交 了 两 次 更 新, 并 把 分 支 整 个 推 到 了 服 务 器 上, 所 以 运 行 该 命 令 会 看 到 : $ git request-pull origin/master myfork The following changes since commit 1edee6b1d61823a2de3b09c160d7080b8d1b3a40: John Smith (1): added a new function are available in the git repository at: git://githost/simplegit.git featurea Jessica Smith (2): add limit to log function change log output to 30 from 25 lib/simplegit.rb files changed, 9 insertions(+), 1 deletions(-) 134

145 Sco Chacon Pro Git 中 文 版 5.2 节 为 项 目 作 贡 献 输 出 的 内 容 可 以 直 接 发 邮 件 给 管 理 者, 他 们 就 会 明 白 这 是 从 哪 次 提 交 开 始 旁 支 出 去 的, 该 到 哪 里 去 抓 取 新 的 代 码, 以 及 新 的 代 码 增 加 了 哪 些 功 能 等 等 像 这 样 随 时 保 持 自 己 的 master 分 支 和 官 方 origin/master 同 步, 并 将 自 己 的 工 作 限 制 在 特 性 分 支 上 的 做 法, 既 方 便 又 灵 活, 采 纳 和 丢 弃 都 轻 而 易 举 就 算 原 始 主 干 发 生 变 化, 我 们 也 能 重 新 衍 合 提 供 新 的 补 丁 比 如 现 在 要 开 始 第 二 项 特 性 的 开 发, 不 要 在 原 来 已 推 送 的 特 性 分 支 上 继 续, 还 是 按 原 始 master 开 始 : $ git checkout -b featureb origin/master $ (work) $ git commit $ git push myfork featureb $ ( maintainer) $ git fetch origin 现 在,A B 两 个 特 性 分 支 各 不 相 扰, 如 同 竹 筒 里 的 两 颗 豆 子, 队 列 中 的 两 个 补 丁, 你 随 时 都 可 以 分 别 从 头 写 过, 或 者 衍 合, 或 者 修 改, 而 不 用 担 心 特 性 代 码 的 交 叉 混 杂 如 图 5-16 所 示 : 图 5.16: featureb 以 后 的 提 交 历 史 假 设 项 目 管 理 员 接 纳 了 许 多 别 人 提 交 的 补 丁 后, 准 备 要 采 纳 你 提 交 的 第 一 个 分 支, 却 发 现 因 为 代 码 基 准 不 一 致, 合 并 工 作 无 法 正 确 干 净 地 完 成 这 就 需 要 你 再 次 衍 合 到 最 新 的 origin/master, 解 决 相 关 冲 突, 然 后 重 新 提 交 你 的 修 改 : $ git checkout featurea $ git rebase origin/master $ git push -f myfork featurea 自 然, 这 会 重 写 提 交 历 史, 如 图 5-17 所 示 : 注 意, 此 时 推 送 分 支 必 须 使 用 -f 选 项 ( 译 注 : 表 示 force, 不 作 检 查 强 制 重 写 ) 135

146 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 图 5.17: featurea 重 新 衍 合 后 的 提 交 历 史 替 换 远 程 已 有 的 featurea 分 支, 因 为 新 的 commit 并 非 原 来 的 后 续 更 新 当 然 你 也 可 以 直 接 推 送 到 另 一 个 新 的 分 支 上 去, 比 如 称 作 featureav2 再 考 虑 另 一 种 情 形 : 管 理 员 看 过 第 二 个 分 支 后 觉 得 思 路 新 颖, 但 想 请 你 改 下 具 体 实 现 我 们 只 需 以 当 前 origin/master 分 支 为 基 准, 开 始 一 个 新 的 特 性 分 支 featurebv2, 然 后 把 原 来 的 featureb 的 更 新 拿 过 来, 解 决 冲 突, 按 要 求 重 新 实 现 部 分 代 码, 然 后 将 此 特 性 分 支 推 送 上 去 : $ git checkout -b featurebv2 origin/master $ git merge --no-commit --squash featureb $ (change implementation) $ git commit $ git push myfork featurebv2 这 里 的 --squash 选 项 将 目 标 分 支 上 的 所 有 更 改 全 拿 来 应 用 到 当 前 分 支 上, 而 --no-commit 选 项 告 诉 Git 此 时 无 需 自 动 生 成 和 记 录 ( 合 并 ) 提 交 这 样, 你 就 可 以 在 原 来 代 码 基 础 上, 继 续 工 作, 直 到 最 后 一 起 提 交 好 了, 现 在 可 以 请 管 理 员 抓 取 featurebv2 上 的 最 新 代 码 了, 如 图 5-18 所 示 : 图 5.18: featurebv2 之 后 的 提 交 历 史 公 开 的 大 型 项 目 许 多 大 型 项 目 都 会 立 有 一 套 自 己 的 接 受 补 丁 流 程, 你 应 该 注 意 下 其 中 细 节 但 多 数 项 目 都 允 许 通 过 开 发 者 邮 件 列 表 接 受 补 丁, 现 在 我 们 来 看 具 体 例 子 136

147 Sco Chacon Pro Git 中 文 版 5.2 节 为 项 目 作 贡 献 整 个 工 作 流 程 类 似 上 面 的 情 形 : 为 每 个 补 丁 创 建 独 立 的 特 性 分 支, 而 不 同 之 处 在 于 如 何 提 交 这 些 补 丁 不 需 要 创 建 自 己 可 写 的 公 共 仓 库, 也 不 用 将 自 己 的 更 新 推 送 到 自 己 的 服 务 器, 你 只 需 将 每 次 提 交 的 差 异 内 容 以 电 子 邮 件 的 方 式 依 次 发 送 到 邮 件 列 表 中 即 可 $ git checkout -b topica $ (work) $ git commit $ (work) $ git commit 如 此 一 番 后, 有 了 两 个 提 交 要 发 到 邮 件 列 表 我 们 可 以 用 git format-patch 命 令 来 生 成 mbox 格 式 的 文 件 然 后 作 为 附 件 发 送 每 个 提 交 都 会 封 装 为 一 个.patch 后 缀 的 mbox 文 件, 但 其 中 只 包 含 一 封 邮 件, 邮 件 标 题 就 是 提 交 消 息 ( 译 注 : 额 外 有 前 缀, 看 例 子 ), 邮 件 内 容 包 含 补 丁 正 文 和 Git 版 本 号 这 种 方 式 的 妙 处 在 于 接 受 补 丁 时 仍 可 保 留 原 来 的 提 交 消 息, 请 看 接 下 来 的 例 子 : $ git format-patch -M origin/master 0001-add-limit-to-log-function.patch 0002-changed-log-output-to-30-from-25.patch format-patch 命 令 依 次 创 建 补 丁 文 件, 并 输 出 文 件 名 上 面 的 -M 选 项 允 许 Git 检 查 是 否 有 对 文 件 重 命 名 的 提 交 我 们 来 看 看 补 丁 文 件 的 内 容 : $ cat 0001-add-limit-to-log-function.patch From d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00: From: Jessica Smith <jessica@example.com> Date: Sun, 6 Apr :17: Subject: [PATCH 1/2] add limit to log function Limit log functionality to the first lib/simplegit.rb files changed, 1 insertions(+), 1 deletions(-) 137

148 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 diff --git a/lib/simplegit.rb b/lib/simplegit.rb index 76f47bc..f9815f a/lib/simplegit.rb ,7 class SimpleGit end def log(treeish = 'master') - command("git log #{treeish}") + command("git log -n 20 #{treeish}") end -- def ls_tree(treeish = 'master') rc1.20.g8c5b.dirty 如 果 有 额 外 信 息 需 要 补 充, 但 又 不 想 放 在 提 交 消 息 中 说 明, 可 以 编 辑 这 些 补 丁 文 件, 在 第 一 个 --- 行 之 前 添 加 说 明, 但 不 要 修 改 下 面 的 补 丁 正 文, 比 如 例 子 中 的 Limit log functionality to the first 20 部 分 这 样, 其 它 开 发 者 能 阅 读, 但 在 采 纳 补 丁 时 不 会 将 此 合 并 进 来 你 可 以 用 邮 件 客 户 端 软 件 发 送 这 些 补 丁 文 件, 也 可 以 直 接 在 命 令 行 发 送 有 些 所 谓 智 能 的 邮 件 客 户 端 软 件 会 自 作 主 张 帮 你 调 整 格 式, 所 以 粘 贴 补 丁 到 邮 件 正 文 时, 有 可 能 会 丢 失 换 行 符 和 若 干 空 格 Git 提 供 了 一 个 通 过 IMAP 发 送 补 丁 文 件 的 工 具 接 下 来 我 会 演 示 如 何 通 过 Gmail 的 IMAP 服 务 器 发 送 另 外, 在 Git 源 代 码 中 有 个 Documentation/SubmittingPatches 文 件, 可 以 仔 细 读 读, 看 看 其 它 邮 件 程 序 的 相 关 导 引 首 先 在 ~/.gitconfig 文 件 中 配 置 imap 项 每 个 选 项 都 可 用 git config 命 令 分 别 设 置, 当 然 直 接 编 辑 文 件 添 加 以 下 内 容 更 便 捷 : [imap] folder = "[Gmail]/Drafts" host = imaps://imap.gmail.com user = user@gmail.com pass = p4ssw0rd port = 993 sslverify = false 138 如 果 你 的 IMAP 服 务 器 没 有 启 用 SSL, 就 无 需 配 置 最 后 那 两 行, 并 且 host

149 Sco Chacon Pro Git 中 文 版 5.2 节 为 项 目 作 贡 献 应 该 以 imap:// 开 头 而 不 再 是 有 s 的 imaps:// 保 存 配 置 文 件 后, 就 能 用 git send- 命 令 把 补 丁 作 为 邮 件 依 次 发 送 到 指 定 的 IMAP 服 务 器 上 的 文 件 夹 中 ( 译 注 : 这 里 就 是 Gmail 的 [Gmail]/Drafts 文 件 夹 但 如 果 你 的 语 言 设 置 不 是 英 文, 此 处 的 文 件 夹 Drafts 字 样 会 变 为 对 应 的 语 言 ): $ cat *.patch git imap-send Resolving imap.gmail.com... ok Connecting to [ ]: ok Logging in... sending 2 messages 100% (2/2) done At this point, you should be able to go to your Drafts folder, change the To field to the mailing list you re sending the patch to, possibly CC the maintainer or person responsible for that section, and send it off. You can also send the patches through an SMTP server. As before, you can set each value separately with a series of git config commands, or you can add them manually in the send section in your ~/.gitconfig file: [send ] smtpencryption = tls smtpserver = smtp.gmail.com smtpuser = user@gmail.com smtpserverport = 587 After this is done, you can use git send- to send your patches: $ git send- *.patch 0001-added-limit-to-log-function.patch 0002-changed-log-output-to-30-from-25.patch Who should the s appear to be from? [Jessica Smith <jessica@example.com>] s will be sent from: Jessica Smith <jessica@example.com> Who should the s be sent to? jessica@example.com Message-ID to be used as In-Reply-To for the first ? y 接 下 来,Git 会 根 据 每 个 补 丁 依 次 输 出 类 似 下 面 的 日 志 : 139

150 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 (mbox) Adding cc: Jessica Smith <jessica@example.com> from \line 'From: Jessica Smith <jessica@example.com>' OK. Log says: Sendmail: /usr/sbin/sendmail -i jessica@example.com From: Jessica Smith <jessica@example.com> To: jessica@example.com Subject: [PATCH 1/2] added limit to log function Date: Sat, 30 May :29: Message-Id: < git-send- -jessica@example.com> X-Mailer: git-send rc1.20.g8c5b.dirty In-Reply-To: <y> References: <y> Result: OK 小 结 本 节 主 要 介 绍 了 常 见 Git 项 目 协 作 的 工 作 流 程, 还 有 一 些 帮 助 处 理 这 些 工 作 的 命 令 和 工 具 接 下 来 我 们 要 看 看 如 何 维 护 Git 项 目, 并 成 为 一 个 合 格 的 项 目 管 理 员, 或 是 集 成 经 理 5.3 项 目 的 管 理 既 然 是 相 互 协 作, 在 贡 献 代 码 的 同 时, 也 免 不 了 要 维 护 管 理 自 己 的 项 目 像 是 怎 么 处 理 别 人 用 format-patch 生 成 的 补 丁, 或 是 集 成 远 端 仓 库 上 某 个 分 支 上 的 变 化 等 等 但 无 论 是 管 理 代 码 仓 库, 还 是 帮 忙 审 核 收 到 的 补 丁, 都 需 要 同 贡 献 者 约 定 某 种 长 期 可 持 续 的 工 作 方 式 使 用 特 性 分 支 进 行 工 作 如 果 想 要 集 成 新 的 代 码 进 来, 最 好 局 限 在 特 性 分 支 上 做 临 时 的 特 性 分 支 可 以 让 你 随 意 尝 试, 进 退 自 如 比 如 碰 上 无 法 正 常 工 作 的 补 丁, 可 以 先 搁 在 那 边, 直 到 有 时 间 仔 细 核 查 修 复 为 止 创 建 的 分 支 可 以 用 相 关 的 主 题 关 键 字 命 名, 比 如 ruby_client 或 者 其 它 类 似 的 描 述 性 词 语, 以 帮 助 将 来 回 忆 Git 项 目 本 身 还 时 常 把 分 支 名 称 分 置 于 不 同 命 名 空 间 下, 比 如 sc/ruby_client 就 说 明 这 是 sc 这 个 人 贡 献 的 现 在 从 当 前 主 干 分 支 为 基 础, 新 建 临 时 分 支 : 140

151 Sco Chacon Pro Git 中 文 版 5.3 节 项 目 的 管 理 $ git branch sc/ruby_client master 另 外, 如 果 你 希 望 立 即 转 到 分 支 上 去 工 作, 可 以 用 checkout -b: $ git checkout -b sc/ruby_client master 好 了, 现 在 已 经 准 备 妥 当, 可 以 试 着 将 别 人 贡 献 的 代 码 合 并 进 来 了 之 后 评 估 一 下 有 没 有 问 题, 最 后 再 决 定 是 不 是 真 的 要 并 入 主 干 采 纳 来 自 邮 件 的 补 丁 如 果 收 到 一 个 通 过 电 邮 发 来 的 补 丁, 你 应 该 先 把 它 应 用 到 特 性 分 支 上 进 行 评 估 有 两 种 应 用 补 丁 的 方 法 :git apply 或 者 git am 使 用 apply 命 令 应 用 补 丁 如 果 收 到 的 补 丁 文 件 是 用 git diff 或 由 其 它 Unix 的 diff 命 令 生 成, 就 该 用 git apply 命 令 来 应 用 补 丁 假 设 补 丁 文 件 存 在 /tmp/patch-ruby-client.patch, 可 以 这 样 运 行 : $ git apply /tmp/patch-ruby-client.patch 这 会 修 改 当 前 工 作 目 录 下 的 文 件, 效 果 基 本 与 运 行 patch -p1 打 补 丁 一 样, 但 它 更 为 严 格, 且 不 会 出 现 混 乱 如 果 是 git diff 格 式 描 述 的 补 丁, 此 命 令 还 会 相 应 地 添 加, 删 除, 重 命 名 文 件 当 然, 普 通 的 patch 命 令 是 不 会 这 么 做 的 另 外 请 注 意,git apply 是 一 个 事 务 性 操 作 的 命 令, 也 就 是 说, 要 么 所 有 补 丁 都 打 上 去, 要 么 全 部 放 弃 所 以 不 会 出 现 patch 命 令 那 样, 一 部 分 文 件 打 上 了 补 丁 而 另 一 部 分 却 没 有, 这 样 一 种 不 上 不 下 的 修 订 状 态 所 以 总 的 来 说,git apply 要 比 patch 严 谨 许 多 因 为 仅 仅 是 更 新 当 前 的 文 件, 所 以 此 命 令 不 会 自 动 生 成 提 交 对 象, 你 得 手 工 缓 存 相 应 文 件 的 更 新 状 态 并 执 行 提 交 命 令 在 实 际 打 补 丁 之 前, 可 以 先 用 git apply --check 查 看 补 丁 是 否 能 够 干 净 顺 利 地 应 用 到 当 前 分 支 中 : $ git apply --check 0001-seeing-if-this-helps-the-gem.patch error: patch failed: ticgit.gemspec:1 error: ticgit.gemspec: patch does not apply 141

152 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 如 果 没 有 任 何 输 出, 表 示 我 们 可 以 顺 利 采 纳 该 补 丁 如 果 有 问 题, 除 了 报 告 错 误 信 息 之 外, 该 命 令 还 会 返 回 一 个 非 零 的 状 态, 所 以 在 shell 脚 本 里 可 用 于 检 测 状 态 使 用 am 命 令 应 用 补 丁 如 果 贡 献 者 也 用 Git, 且 擅 于 制 作 format-patch 补 丁, 那 你 的 合 并 工 作 将 会 非 常 轻 松 因 为 这 些 补 丁 中 除 了 文 件 内 容 差 异 外, 还 包 含 了 作 者 信 息 和 提 交 消 息 所 以 请 鼓 励 贡 献 者 用 format-patch 生 成 补 丁 对 于 传 统 的 diff 命 令 生 成 的 补 丁, 则 只 能 用 git apply 处 理 对 于 format-patch 制 作 的 新 式 补 丁, 应 当 使 用 git am 命 令 从 技 术 上 来 说, git am 能 够 读 取 mbox 格 式 的 文 件 这 是 种 简 单 的 纯 文 本 文 件, 可 以 包 含 多 封 电 邮, 格 式 上 用 From 加 空 格 以 及 随 便 什 么 辅 助 信 息 所 组 成 的 行 作 为 分 隔 行, 以 区 分 每 封 邮 件, 就 像 这 样 : From d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00: From: Jessica Smith <jessica@example.com> Date: Sun, 6 Apr :17: Subject: [PATCH 1/2] add limit to log function Limit log functionality to the first 20 这 是 format-patch 命 令 输 出 的 开 头 几 行, 也 是 一 个 有 效 的 mbox 文 件 格 式 如 果 有 人 用 git send- 给 你 发 了 一 个 补 丁, 你 可 以 将 此 邮 件 下 载 到 本 地, 然 后 运 行 git am 命 令 来 应 用 这 个 补 丁 如 果 你 的 邮 件 客 户 端 能 将 多 封 电 邮 导 出 为 mbox 格 式 的 文 件, 就 可 以 用 git am 一 次 性 应 用 所 有 导 出 的 补 丁 如 果 贡 献 者 将 format-patch 生 成 的 补 丁 文 件 上 传 到 类 似 Request Ticket 一 样 的 任 务 处 理 系 统, 那 么 可 以 先 下 载 到 本 地, 继 而 使 用 git am 应 用 该 补 丁 : $ git am 0001-limit-log-function.patch Applying: add limit to log function 你 会 看 到 它 被 干 净 地 应 用 到 本 地 分 支, 并 自 动 创 建 了 新 的 提 交 对 象 作 者 信 息 取 自 邮 件 头 From 和 Date, 提 交 消 息 则 取 自 Subject 以 及 正 文 中 补 丁 之 前 的 内 容 来 看 具 体 实 例, 采 纳 之 前 展 示 的 那 个 mbox 电 邮 补 丁 后, 最 新 的 提 交 对 象 为 : $ git log --pretty=fuller

153 Sco Chacon Pro Git 中 文 版 5.3 节 项 目 的 管 理 commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0 Author: Jessica Smith AuthorDate: Sun Apr 6 10:17: Commit: Scott Chacon <schacon@gmail.com> CommitDate: Thu Apr 9 09:19: add limit to log function Limit log functionality to the first 20 Commit 部 分 显 示 的 是 采 纳 补 丁 的 人, 以 及 采 纳 的 时 间 而 Author 部 分 则 显 示 的 是 原 作 者, 以 及 创 建 补 丁 的 时 间 有 时, 我 们 也 会 遇 到 打 不 上 补 丁 的 情 况 这 多 半 是 因 为 主 干 分 支 和 补 丁 的 基 础 分 支 相 差 太 远, 但 也 可 能 是 因 为 某 些 依 赖 补 丁 还 未 应 用 这 种 情 况 下,git am 会 报 错 并 询 问 该 怎 么 做 : $ git am 0001-seeing-if-this-helps-the-gem.patch Applying: seeing if this helps the gem error: patch failed: ticgit.gemspec:1 error: ticgit.gemspec: patch does not apply Patch failed at When you have resolved this problem run "git am --resolved". If you would prefer to skip this patch, instead run "git am --skip". To restore the original branch and stop patching run "git am --abort". Git 会 在 有 冲 突 的 文 件 里 加 入 冲 突 解 决 标 记, 这 同 合 并 或 衍 合 操 作 一 样 解 决 的 办 法 也 一 样, 先 编 辑 文 件 消 除 冲 突, 然 后 暂 存 文 件, 最 后 运 行 git am --resolved 提 交 修 正 结 果 : $ (fix the file) $ git add ticgit.gemspec $ git am --resolved Applying: seeing if this helps the gem 如 果 想 让 Git 更 智 能 地 处 理 冲 突, 可 以 用 -3 选 项 进 行 三 方 合 并 如 果 当 前 分 支 未 包 含 该 补 丁 的 基 础 代 码 或 其 祖 先, 那 么 三 方 合 并 就 会 失 败, 所 以 该 选 项 默 认 为 关 143

154 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 闭 状 态 一 般 来 说, 如 果 该 补 丁 是 基 于 某 个 公 开 的 提 交 制 作 而 成 的 话, 你 总 是 可 以 通 过 同 步 来 获 取 这 个 共 同 祖 先, 所 以 用 三 方 合 并 选 项 可 以 解 决 很 多 麻 烦 : $ git am seeing-if-this-helps-the-gem.patch Applying: seeing if this helps the gem error: patch failed: ticgit.gemspec:1 error: ticgit.gemspec: patch does not apply Using index info to reconstruct a base tree... Falling back to patching base and 3-way merge... No changes -- Patch already applied. 像 上 面 的 例 子, 对 于 打 过 的 补 丁 我 又 再 打 一 遍, 自 然 会 产 生 冲 突, 但 因 为 加 上 了 -3 选 项, 所 以 它 很 聪 明 地 告 诉 我, 无 需 更 新, 原 有 的 补 丁 已 经 应 用 对 于 一 次 应 用 多 个 补 丁 时 所 用 的 mbox 格 式 文 件, 可 以 用 am 命 令 的 交 互 模 式 选 项 -i, 这 样 就 会 在 打 每 个 补 丁 前 停 住, 询 问 该 如 何 操 作 : $ git am -3 -i mbox Commit Body is: seeing if this helps the gem Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all 在 多 个 补 丁 要 打 的 情 况 下, 这 是 个 非 常 好 的 办 法, 一 方 面 可 以 预 览 下 补 丁 内 容, 同 时 也 可 以 有 选 择 性 的 接 纳 或 跳 过 某 些 补 丁 打 完 所 有 补 丁 后, 如 果 测 试 下 来 新 特 性 可 以 正 常 工 作, 那 就 可 以 安 心 地 将 当 前 特 性 分 支 合 并 到 长 期 分 支 中 去 了 检 出 远 程 分 支 如 果 贡 献 者 有 自 己 的 Git 仓 库, 并 将 修 改 推 送 到 此 仓 库 中, 那 么 当 你 拿 到 仓 库 的 访 问 地 址 和 对 应 分 支 的 名 称 后, 就 可 以 加 为 远 程 分 支, 然 后 在 本 地 进 行 合 并 比 如,Jessica 发 来 一 封 邮 件, 说 在 她 代 码 库 中 的 ruby-client 分 支 上 已 经 实 现 了 某 个 非 常 棒 的 新 功 能, 希 望 我 们 能 帮 忙 测 试 一 下 我 们 可 以 先 把 她 的 仓 库 加 为 远 程 仓 库, 然 后 抓 取 数 据, 完 了 再 将 她 所 说 的 分 支 检 出 到 本 地 来 测 试 : 144

155 Sco Chacon Pro Git 中 文 版 5.3 节 项 目 的 管 理 $ git remote add jessica git://github.com/jessica/myproject.git $ git fetch jessica $ git checkout -b rubyclient jessica/ruby-client 若 是 不 久 她 又 发 来 邮 件, 说 还 有 个 很 棒 的 功 能 实 现 在 另 一 分 支 上, 那 我 们 只 需 重 新 抓 取 下 最 新 数 据, 然 后 检 出 那 个 分 支 到 本 地 就 可 以 了, 无 需 重 复 设 置 远 程 仓 库 这 种 做 法 便 于 同 别 人 保 持 长 期 的 合 作 关 系 但 前 提 是 要 求 贡 献 者 有 自 己 的 服 务 器, 而 我 们 也 需 要 为 每 个 人 建 一 个 远 程 分 支 有 些 贡 献 者 提 交 代 码 补 丁 并 不 是 很 频 繁, 所 以 通 过 邮 件 接 收 补 丁 效 率 会 更 高 同 时 我 们 自 己 也 不 会 希 望 建 上 百 来 个 分 支, 却 只 从 每 个 分 支 取 一 两 个 补 丁 但 若 是 用 脚 本 程 序 来 管 理, 或 直 接 使 用 代 码 仓 库 托 管 服 务, 就 可 以 简 化 此 过 程 当 然, 选 择 何 种 方 式 取 决 于 你 和 贡 献 者 的 喜 好 使 用 远 程 分 支 的 另 外 一 个 好 处 是 能 够 得 到 提 交 历 史 不 管 代 码 合 并 是 不 是 会 有 问 题, 至 少 我 们 知 道 该 分 支 的 历 史 分 叉 点, 所 以 默 认 会 从 共 同 祖 先 开 始 自 动 进 行 三 方 合 并, 无 需 -3 选 项, 也 不 用 像 打 补 丁 那 样 祈 祷 存 在 共 同 的 基 准 点 如 果 只 是 临 时 合 作, 只 需 用 git pull 命 令 抓 取 远 程 仓 库 上 的 数 据, 合 并 到 本 地 临 时 分 支 就 可 以 了 一 次 性 的 抓 取 动 作 自 然 不 会 把 该 仓 库 地 址 加 为 远 程 仓 库 $ git pull git://github.com/onetimeguy/project.git From git://github.com/onetimeguy/project * branch HEAD -> FETCH_HEAD Merge made by recursive 决 断 代 码 取 舍 现 在 特 性 分 支 上 已 合 并 好 了 贡 献 者 的 代 码, 是 时 候 决 断 取 舍 了 本 节 将 回 顾 一 些 之 前 学 过 的 命 令, 以 看 清 将 要 合 并 到 主 干 的 是 哪 些 代 码, 从 而 理 解 它 们 到 底 做 了 些 什 么, 是 否 真 的 要 并 入 一 般 我 们 会 先 看 下, 特 性 分 支 上 都 有 哪 些 新 增 的 提 交 比 如 在 contrib 特 性 分 支 上 打 了 两 个 补 丁, 仅 查 看 这 两 个 补 丁 的 提 交 信 息, 可 以 用 --not 选 项 指 定 要 屏 蔽 的 分 支 master, 这 样 就 会 剔 除 重 复 的 提 交 历 史 : $ git log contrib --not master commit 5b6235bd efc4d73316f0a68d484f118 Author: Scott Chacon <schacon@gmail.com> Date: Fri Oct 24 09:53:

156 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 seeing if this helps the gem commit 7482e0d16d04bea79d0dba8988cc78df655f16a0 Author: Scott Chacon <schacon@gmail.com> Date: Mon Oct 22 19:38: updated the gemspec to hopefully work better 还 可 以 查 看 每 次 提 交 的 具 体 修 改 请 牢 记, 在 git log 后 加 -p 选 项 将 展 示 每 次 提 交 的 内 容 差 异 如 果 想 看 当 前 分 支 同 其 他 分 支 合 并 时 的 完 整 内 容 差 异, 有 个 小 窍 门 : $ git diff master 虽 然 能 得 到 差 异 内 容, 但 请 记 住, 结 果 有 可 能 和 我 们 的 预 期 不 同 一 旦 主 干 master 在 特 性 分 支 创 建 之 后 有 所 修 改, 那 么 通 过 diff 命 令 来 比 较 的, 是 最 新 主 干 上 的 提 交 快 照 显 然, 这 不 是 我 们 所 要 的 比 方 在 master 分 支 中 某 个 文 件 里 添 了 一 行, 然 后 运 行 上 面 的 命 令, 简 单 的 比 较 最 新 快 照 所 得 到 的 结 论 只 能 是, 特 性 分 支 中 删 除 了 这 一 行 这 个 很 好 理 解 : 如 果 master 是 特 性 分 支 的 直 接 祖 先, 不 会 产 生 任 何 问 题 ; 如 果 它 们 的 提 交 历 史 在 不 同 的 分 叉 上, 那 么 产 生 的 内 容 差 异, 看 起 来 就 像 是 增 加 了 特 性 分 支 上 的 新 代 码, 同 时 删 除 了 master 分 支 上 的 新 代 码 实 际 上 我 们 真 正 想 要 看 的, 是 新 加 入 到 特 性 分 支 的 代 码, 也 就 是 合 并 时 会 并 入 主 干 的 代 码 所 以, 准 确 地 讲, 我 们 应 该 比 较 特 性 分 支 和 它 同 master 分 支 的 共 同 祖 先 之 间 的 差 异 我 们 可 以 手 工 定 位 它 们 的 共 同 祖 先, 然 后 与 之 比 较 : $ git merge-base contrib master 36c7dba2c95e6bbb78dfa822519ecfec6e1ca649 $ git diff 36c7db 但 这 么 做 很 麻 烦, 所 以 Git 提 供 了 便 捷 的... 语 法 对 于 diff 命 令, 可 以 把... 加 在 原 始 分 支 ( 拥 有 共 同 祖 先 ) 和 当 前 分 支 之 间 : 146

157 Sco Chacon Pro Git 中 文 版 5.3 节 项 目 的 管 理 $ git diff master...contrib 记 现 在 看 到 的, 就 是 实 际 将 要 引 入 的 新 代 码 这 是 一 个 非 常 有 用 的 命 令, 应 该 牢 代 码 集 成 一 旦 特 性 分 支 准 备 停 当, 接 下 来 的 问 题 就 是 如 何 集 成 到 更 靠 近 主 线 的 分 支 中 此 外 还 要 考 虑 维 护 项 目 的 总 体 步 骤 是 什 么 虽 然 有 很 多 选 择, 不 过 我 们 这 里 只 介 绍 其 中 一 部 分 合 并 流 程 一 般 最 简 单 的 情 形, 是 在 master 分 支 中 维 护 稳 定 代 码, 然 后 在 特 性 分 支 上 开 发 新 功 能, 或 是 审 核 测 试 别 人 贡 献 的 代 码, 接 着 将 它 并 入 主 干, 最 后 删 除 这 个 特 性 分 支, 如 此 反 复 来 看 示 例, 假 设 当 前 代 码 库 中 有 两 个 分 支, 分 别 为 ruby_client 和 php_client, 如 图 5-19 所 示 然 后 先 把 ruby_client 合 并 进 主 干, 再 合 并 php_client, 最 后 的 提 交 历 史 如 图 5-20 所 示 图 5.19: 多 个 特 性 分 支 这 是 最 简 单 的 流 程, 所 以 在 处 理 大 一 些 的 项 目 时 可 能 会 有 问 题 对 于 大 型 项 目, 至 少 需 要 维 护 两 个 长 期 分 支 master 和 develop 新 代 码 ( 图 5-21 中 的 ruby_client) 将 首 先 并 入 develop 分 支 ( 图 5-22 中 的 C8), 经 过 一 个 147

158 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 图 5.20: 合 并 特 性 分 支 之 后 阶 段, 确 认 develop 中 的 代 码 已 稳 定 到 可 发 行 时, 再 将 master 分 支 快 进 到 稳 定 点 ( 图 5-23 中 的 C8) 而 平 时 这 两 个 分 支 都 会 被 推 送 到 公 开 的 代 码 库 图 5.21: 特 性 分 支 合 并 前 图 5.22: 特 性 分 支 合 并 后 148 这 样, 在 人 们 克 隆 仓 库 时 就 有 两 种 选 择 : 既 可 检 出 最 新 稳 定 版 本, 确 保 正 常 使

159 Sco Chacon Pro Git 中 文 版 5.3 节 项 目 的 管 理 图 5.23: 特 性 分 支 发 布 后 用 ; 也 能 检 出 开 发 版 本, 试 用 最 前 沿 的 新 特 性 你 也 可 以 扩 展 这 个 概 念, 先 将 所 有 新 代 码 合 并 到 临 时 特 性 分 支, 等 到 该 分 支 稳 定 下 来 并 通 过 测 试 后, 再 并 入 develop 分 支 然 后, 让 时 间 检 验 一 切, 如 果 这 些 代 码 确 实 可 以 正 常 工 作 相 当 长 一 段 时 间, 那 就 有 理 由 相 信 它 已 经 足 够 稳 定, 可 以 放 心 并 入 主 干 分 支 发 布 大 项 目 的 合 并 流 程 Git 项 目 本 身 有 四 个 长 期 分 支 : 用 于 发 布 的 master 分 支 用 于 合 并 基 本 稳 定 特 性 的 next 分 支 用 于 合 并 仍 需 改 进 特 性 的 pu 分 支 (pu 是 proposed updates 的 缩 写 ), 以 及 用 于 除 错 维 护 的 maint 分 支 (maint 取 自 maintenance) 维 护 者 可 以 按 照 之 前 介 绍 的 方 法, 将 贡 献 者 的 代 码 引 入 为 不 同 的 特 性 分 支 ( 如 图 5-24 所 示 ), 然 后 测 试 评 估, 看 哪 些 特 性 能 稳 定 工 作, 哪 些 还 需 改 进 稳 定 的 特 性 可 以 并 入 next 分 支, 然 后 再 推 送 到 公 共 仓 库, 以 供 其 他 人 试 用 图 5.24: 管 理 复 杂 的 并 行 贡 献 仍 需 改 进 的 特 性 可 以 先 并 入 pu 分 支 直 到 它 们 完 全 稳 定 后 再 并 入 master 同 时 一 并 检 查 下 next 分 支, 将 足 够 稳 定 的 特 性 也 并 入 master 所 以 一 般 来 说,master 始 终 是 在 快 进,next 偶 尔 做 下 衍 合, 而 pu 则 是 频 繁 衍 合, 如 图 5-25 所 示 : 并 入 master 后 的 特 性 分 支, 已 经 无 需 保 留 分 支 索 引, 放 心 删 除 好 了 Git 项 目 还 有 一 个 maint 分 支, 它 是 以 最 近 一 次 发 行 版 为 基 础 分 化 而 来 的, 用 于 维 护 除 错 补 149

160 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 图 5.25: 将 特 性 并 入 长 期 分 支 丁 所 以 克 隆 Git 项 目 仓 库 后 会 得 到 这 四 个 分 支, 通 过 检 出 不 同 分 支 可 以 了 解 各 自 进 展, 或 是 试 用 前 沿 特 性, 或 是 贡 献 代 码 而 维 护 者 则 通 过 管 理 这 些 分 支, 逐 步 有 序 地 并 入 第 三 方 贡 献 衍 合 与 挑 拣 (cherry-pick) 的 流 程 一 些 维 护 者 更 喜 欢 衍 合 或 者 挑 拣 贡 献 者 的 代 码, 而 不 是 简 单 的 合 并, 因 为 这 样 能 够 保 持 线 性 的 提 交 历 史 如 果 你 完 成 了 一 个 特 性 的 开 发, 并 决 定 将 它 引 入 到 主 干 代 码 中, 你 可 以 转 到 那 个 特 性 分 支 然 后 执 行 衍 合 命 令, 好 在 你 的 主 干 分 支 上 ( 也 可 能 是 develop 分 支 之 类 的 ) 重 新 提 交 这 些 修 改 如 果 这 些 代 码 工 作 得 很 好, 你 就 可 以 快 进 master 分 支, 得 到 一 个 线 性 的 提 交 历 史 另 一 个 引 入 代 码 的 方 法 是 挑 拣 挑 拣 类 似 于 针 对 某 次 特 定 提 交 的 衍 合 它 首 先 提 取 某 次 提 交 的 补 丁, 然 后 试 着 应 用 在 当 前 分 支 上 如 果 某 个 特 性 分 支 上 有 多 个 commits, 但 你 只 想 引 入 其 中 之 一 就 可 以 使 用 这 种 方 法 也 可 能 仅 仅 是 因 为 你 喜 欢 用 挑 拣, 讨 厌 衍 合 假 设 你 有 一 个 类 似 图 5-26 的 工 程 图 5.26: 挑 拣 (cherry-pick) 之 前 的 历 史 如 果 你 希 望 拉 取 e43a6 到 你 的 主 干 分 支, 可 以 这 样 : $ git cherry-pick e43a6fd3e94888d76779ad79fb568ed180e5fcdf 150

161 Sco Chacon Pro Git 中 文 版 5.3 节 项 目 的 管 理 Finished one cherry-pick. [master]: created a0a41a9: "More friendly message when locking the index fails." 3 files changed, 17 insertions(+), 3 deletions(-) 这 将 会 引 入 e43a6 的 代 码, 但 是 会 得 到 不 同 的 SHA-1 值, 因 为 应 用 日 期 不 同 现 在 你 的 历 史 看 起 来 像 图 图 5.27: 挑 拣 (cherry-pick) 之 后 的 历 史 现 在, 你 可 以 删 除 这 个 特 性 分 支 并 丢 弃 你 不 想 引 入 的 那 些 commit 给 发 行 版 签 名 你 可 以 删 除 上 次 发 布 的 版 本 并 重 新 打 标 签, 也 可 以 像 第 二 章 所 说 的 那 样 建 立 一 个 新 的 标 签 如 果 你 决 定 以 维 护 者 的 身 份 给 发 行 版 签 名, 应 该 这 样 做 : $ git tag -s v1.5 -m 'my signed 1.5 tag' You need a passphrase to unlock the secret key for user: "Scott Chacon <schacon@gmail.com>" 1024-bit DSA key, ID F721C45A, created 完 成 签 名 之 后, 如 何 分 发 PGP 公 钥 (public key) 是 个 问 题 ( 译 者 注 : 分 发 公 钥 是 为 了 验 证 标 签 ) 还 好,Git 的 设 计 者 想 到 了 解 决 办 法 : 可 以 把 key( 既 公 钥 ) 作 为 blob 变 量 写 入 Git 库, 然 后 把 它 的 内 容 直 接 写 在 标 签 里 gpg --list-keys 命 令 可 以 显 示 出 你 所 拥 有 的 key: $ gpg --list-keys /Users/schacon/.gnupg/pubring.gpg 151

162 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 pub 1024D/F721C45A [expires: ] uid Scott Chacon <schacon@gmail.com> sub 2048g/45D [expires: ] 然 后, 导 出 key 的 内 容 并 经 由 管 道 符 传 递 给 git hash-object, 之 后 钥 匙 会 以 blob 类 型 写 入 Git 中, 最 后 返 回 这 个 blob 量 的 SHA-1 值 : $ gpg -a --export F721C45A git hash-object -w --stdin 659ef797d181633c87ec71ac3f9ba29fe5775b92 现 在 你 的 Git 已 经 包 含 了 这 个 key 的 内 容 了, 可 以 通 过 不 同 的 SHA-1 值 指 定 不 同 的 key 来 创 建 标 签 $ git tag -a maintainer-pgp-pub 659ef797d181633c87ec71ac3f9ba29fe5775b92 在 运 行 git push --tags 命 令 之 后,maintainer-pgp-pub 标 签 就 会 公 布 给 所 有 人 如 果 有 人 想 要 校 验 标 签, 他 可 以 使 用 如 下 命 令 导 入 你 的 key: $ git show maintainer-pgp-pub gpg --import 人 们 可 以 用 这 个 key 校 验 你 签 名 的 所 有 标 签 另 外, 你 也 可 以 在 标 签 信 息 里 写 入 一 个 操 作 向 导, 用 户 只 需 要 运 行 git show <tag> 查 看 标 签 信 息, 然 后 按 照 你 的 向 导 就 能 完 成 校 验 生 成 内 部 版 本 号 因 为 Git 不 会 为 每 次 提 交 自 动 附 加 类 似 v123 的 递 增 序 列, 所 以 如 果 你 想 要 得 到 一 个 便 于 理 解 的 提 交 号 可 以 运 行 git describe 命 令 Git 将 会 返 回 一 个 字 符 串, 由 三 部 分 组 成 : 最 近 一 次 标 定 的 版 本 号, 加 上 自 那 次 标 定 之 后 的 提 交 次 数, 再 加 上 一 段 所 描 述 的 提 交 的 SHA-1 值 : $ git describe master v1.6.2-rc1-20-g8c5b85c 152

163 Sco Chacon Pro Git 中 文 版 5.3 节 项 目 的 管 理 这 个 字 符 串 可 以 作 为 快 照 的 名 字, 方 便 人 们 理 解 如 果 你 的 Git 是 你 自 己 下 载 源 码 然 后 编 译 安 装 的, 你 会 发 现 git --version 命 令 的 输 出 和 这 个 字 符 串 差 不 多 如 果 在 一 个 刚 刚 打 完 标 签 的 提 交 上 运 行 describe 命 令, 只 会 得 到 这 次 标 定 的 版 本 号, 而 没 有 后 面 两 项 信 息 git describe 命 令 只 适 用 于 有 标 注 的 标 签 ( 通 过 -a 或 者 -s 选 项 创 建 的 标 签 ), 所 以 发 行 版 的 标 签 都 应 该 是 带 有 标 注 的, 以 保 证 git describe 能 够 正 确 的 执 行 你 也 可 以 把 这 个 字 符 串 作 为 checkout 或 者 show 命 令 的 目 标, 因 为 他 们 最 终 都 依 赖 于 一 个 简 短 的 SHA-1 值, 当 然 如 果 这 个 SHA-1 值 失 效 他 们 也 跟 着 失 效 最 近 Linux 内 核 为 了 保 证 SHA-1 值 的 唯 一 性, 将 位 数 由 8 位 扩 展 到 10 位, 这 就 导 致 扩 展 之 前 的 git describe 输 出 完 全 失 效 了 准 备 发 布 现 在 可 以 发 布 一 个 新 的 版 本 了 首 先 要 将 代 码 的 压 缩 包 归 档, 方 便 那 些 可 怜 的 还 没 有 使 用 Git 的 人 们 可 以 使 用 git archive: $ git archive master --prefix='project/' gzip > `git describe master`.tar.gz $ ls *.tar.gz v1.6.2-rc1-20-g8c5b85c.tar.gz 这 个 压 缩 包 解 压 出 来 的 是 一 个 文 件 夹, 里 面 是 你 项 目 的 最 新 代 码 快 照 你 也 可 以 用 类 似 的 方 法 建 立 一 个 zip 压 缩 包, 在 git archive 加 上 --format=zip 选 项 : $ git archive master --prefix='project/' --format=zip > `git describe master`.zip 现 在 你 有 了 一 个 tar.gz 压 缩 包 和 一 个 zip 压 缩 包, 可 以 把 他 们 上 传 到 你 网 站 上 或 者 用 发 给 别 人 制 作 简 报 是 时 候 通 知 邮 件 列 表 里 的 朋 友 们 来 检 验 你 的 成 果 了 使 用 git shortlog 命 令 可 以 方 便 快 捷 的 制 作 一 份 修 改 日 志 (changelog), 告 诉 大 家 上 次 发 布 之 后 又 增 加 了 哪 些 特 性 和 修 复 了 哪 些 bug 实 际 上 这 个 命 令 能 够 统 计 给 定 范 围 内 的 所 有 提 交 ; 假 如 你 上 一 次 发 布 的 版 本 是 v1.0.1, 下 面 的 命 令 将 给 出 自 从 上 次 发 布 之 后 的 所 有 提 交 的 简 介 : 153

164 第 5 章 分 布 式 Git Sco Chacon Pro Git 中 文 版 $ git shortlog --no-merges master --not v1.0.1 Chris Wanstrath (8): Add support for annotated tags to Grit::Tag Add packed-refs annotated tag support. Add Grit::Commit#to_patch Update version and History.txt Remove stray `puts` Make ls_tree ignore nils Tom Preston-Werner (4): fix dates in history dynamic version method Version bump to Regenerated gemspec for version 这 就 是 自 从 v1.0.1 版 本 以 来 的 所 有 提 交 的 简 介, 内 容 按 照 作 者 分 组, 以 便 你 能 快 速 的 发 给 他 们 5.4 小 结 你 学 会 了 如 何 使 用 Git 为 项 目 做 贡 献, 也 学 会 了 如 何 使 用 Git 维 护 你 的 项 目 恭 喜! 你 已 经 成 为 一 名 高 效 的 开 发 者 在 下 一 章 你 将 学 到 更 强 大 的 工 具 来 处 理 更 加 复 杂 的 问 题, 之 后 你 会 变 成 一 位 Git 大 师 154

165 第 6 章 Git 工 具 现 在, 你 已 经 学 习 了 管 理 或 者 维 护 Git 仓 库, 实 现 代 码 控 制 所 需 的 大 多 数 日 常 命 令 和 工 作 流 程 你 已 经 完 成 了 跟 踪 和 提 交 文 件 的 基 本 任 务, 并 且 发 挥 了 暂 存 区 和 轻 量 级 的 特 性 分 支 及 合 并 的 威 力 接 下 来 你 将 领 略 到 一 些 Git 可 以 实 现 的 非 常 强 大 的 功 能, 这 些 功 能 你 可 能 并 不 会 在 日 常 操 作 中 使 用, 但 在 某 些 时 候 你 也 许 会 需 要 6.1 修 订 版 本 (Revision) 选 择 Git 允 许 你 通 过 几 种 方 法 来 指 明 特 定 的 或 者 一 定 范 围 内 的 提 交 了 解 它 们 并 不 是 必 需 的, 但 是 了 解 一 下 总 没 坏 处 单 个 修 订 版 本 显 然 你 可 以 使 用 给 出 的 SHA-1 值 来 指 明 一 次 提 交, 不 过 也 有 更 加 人 性 化 的 方 法 来 做 同 样 的 事 本 节 概 述 了 指 明 单 个 提 交 的 诸 多 方 法 简 短 的 SHA Git 很 聪 明, 它 能 够 通 过 你 提 供 的 前 几 个 字 符 来 识 别 你 想 要 的 那 次 提 交, 只 要 你 提 供 的 那 部 分 SHA-1 不 短 于 四 个 字 符, 并 且 没 有 歧 义 也 就 是 说, 当 前 仓 库 中 只 有 一 个 对 象 以 这 段 SHA-1 开 头 例 如, 想 要 查 看 一 次 指 定 的 提 交, 假 设 你 运 行 git log 命 令 并 找 到 你 增 加 了 功 能 的 那 次 提 交 : $ git log commit bc047d87bf7eac ae793478c50d3 Author: Scott Chacon <schacon@gmail.com> Date: Fri Jan 2 18:32:

166 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 fixed refs handling, added gc auto, updated tests commit d921970aadf03b3cf0e71becdaab3147ba71cdef Merge: 1c002dd... 35cfb2b... Author: Scott Chacon <schacon@gmail.com> Date: Thu Dec 11 15:08: Merge commit 'phedders/rdocs' commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b Author: Scott Chacon <schacon@gmail.com> Date: Thu Dec 11 14:58: added some blame and merge stuff 假 设 是 1c002dd... 如 果 你 想 git show 这 次 提 交, 下 面 的 命 令 是 等 价 的 ( 假 设 简 短 的 版 本 没 有 歧 义 ): $ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b $ git show 1c002dd4b536e7479f $ git show 1c002d Git 可 以 为 你 的 SHA-1 值 生 成 出 简 短 且 唯 一 的 缩 写 如 果 你 传 递 --abbrev-commit 给 git log 命 令, 输 出 结 果 里 就 会 使 用 简 短 且 唯 一 的 值 ; 它 默 认 使 用 七 个 字 符 来 表 示, 不 过 必 要 时 为 了 避 免 SHA-1 的 歧 义, 会 增 加 字 符 数 : $ git log --abbrev-commit --pretty=oneline ca82a6d changed the version number 085bb3b removed unnecessary test code a11bef0 first commit 通 常 在 一 个 项 目 中, 使 用 八 到 十 个 字 符 来 避 免 SHA-1 歧 义 已 经 足 够 了 最 大 的 Git 项 目 之 一,Linux 内 核, 目 前 也 只 需 要 最 长 40 个 字 符 中 的 12 个 字 符 来 保 持 唯 一 性 156

167 Sco Chacon Pro Git 中 文 版 6.1 节 修 订 版 本 (Revision) 选 择 关 于 SHA-1 的 简 短 说 明 许 多 人 可 能 会 担 心 一 个 问 题 : 在 随 机 的 偶 然 情 况 下, 在 他 们 的 仓 库 里 会 出 现 两 个 具 有 相 同 SHA-1 值 的 对 象 那 会 怎 么 样 呢? 如 果 你 真 的 向 仓 库 里 提 交 了 一 个 跟 之 前 的 某 个 对 象 具 有 相 同 SHA-1 值 的 对 象, Git 将 会 发 现 之 前 的 那 个 对 象 已 经 存 在 在 Git 数 据 库 中, 并 认 为 它 已 经 被 写 入 了 如 果 什 么 时 候 你 想 再 次 检 出 那 个 对 象 时, 你 会 总 是 得 到 先 前 的 那 个 对 象 的 数 据 不 过, 你 应 该 了 解 到, 这 种 情 况 发 生 的 概 率 是 多 么 微 小 SHA-1 摘 要 长 度 是 20 字 节, 也 就 是 160 位 为 了 保 证 有 50% 的 概 率 出 现 一 次 冲 突, 需 要 2 80 个 随 机 哈 希 的 对 象 ( 计 算 冲 突 机 率 的 公 式 是 p = (n(n-1)/2) * (1/2ˆ160)) 2 80 是 , 也 就 是 一 亿 亿 亿, 那 是 地 球 上 沙 粒 总 数 的 1200 倍 现 在 举 例 说 一 下 怎 样 才 能 产 生 一 次 SHA-1 冲 突 如 果 地 球 上 65 亿 的 人 类 都 在 编 程, 每 人 每 秒 都 在 产 生 等 价 于 整 个 Linux 内 核 历 史 ( 一 百 万 个 Git 对 象 ) 的 代 码, 并 将 之 提 交 到 一 个 巨 大 的 Git 仓 库 里 面, 那 将 花 费 5 年 的 时 间 才 会 产 生 足 够 的 对 象, 使 其 拥 有 50% 的 概 率 产 生 一 次 SHA-1 对 象 冲 突 这 要 比 你 编 程 团 队 的 成 员 同 一 个 晚 上 在 互 不 相 干 的 意 外 中 被 狼 袭 击 并 杀 死 的 机 率 还 要 小 分 支 引 用 指 明 一 次 提 交 的 最 直 接 的 方 法 要 求 有 一 个 指 向 它 的 分 支 引 用 这 样, 你 就 可 以 在 任 何 需 要 一 个 提 交 对 象 或 者 SHA-1 值 的 Git 命 令 中 使 用 该 分 支 名 称 了 如 果 你 想 要 显 示 一 个 分 支 的 最 后 一 次 提 交 的 对 象, 例 如 假 设 topic1 分 支 指 向 ca82a6d, 那 么 下 面 的 命 令 是 等 价 的 : $ git show ca82a6dff817ec66f a $ git show topic1 如 果 你 想 知 道 某 个 分 支 指 向 哪 个 特 定 的 SHA, 或 者 想 看 任 何 一 个 例 子 中 被 简 写 的 SHA-1, 你 可 以 使 用 一 个 叫 做 rev-parse 的 Git 探 测 工 具 在 第 9 章 你 可 以 看 到 关 于 探 测 工 具 的 更 多 信 息 ; 简 单 来 说,rev-parse 是 为 了 底 层 操 作 而 不 是 日 常 操 作 设 计 的 不 过, 有 时 你 想 看 Git 现 在 到 底 处 于 什 么 状 态 时, 它 可 能 会 很 有 用 这 里 你 可 以 对 你 的 分 支 运 执 行 rev-parse $ git rev-parse topic1 ca82a6dff817ec66f a

168 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 引 用 日 志 里 的 简 称 在 你 工 作 的 同 时,Git 在 后 台 的 工 作 之 一 就 是 保 存 一 份 引 用 日 志 一 份 记 录 最 近 几 个 月 你 的 HEAD 和 分 支 引 用 的 日 志 你 可 以 使 用 git reflog 来 查 看 引 用 日 志 : $ git reflog b... HEAD@{0}: commit: fixed refs handling, added gc auto, updated d HEAD@{1}: merge phedders/rdocs: Merge made by recursive. 1c002dd... HEAD@{2}: commit: added some blame and merge stuff 1c HEAD@{3}: rebase -i (squash): updating HEAD 95df HEAD@{4}: commit: # This is a combination of two commits. 1c HEAD@{5}: rebase -i (squash): updating HEAD 7e05da5... HEAD@{6}: rebase -i (pick): updating HEAD 每 次 你 的 分 支 顶 端 因 为 某 些 原 因 被 修 改 时,Git 就 会 为 你 将 信 息 保 存 在 这 个 临 时 历 史 记 录 里 面 你 也 可 以 使 用 这 份 数 据 来 指 明 更 早 的 分 支 如 果 你 想 查 看 仓 库 中 HEAD 在 五 次 前 的 值, 你 可 以 使 用 引 用 日 志 的 输 出 中 引 用 : $ git show HEAD@{5} 你 也 可 以 使 用 这 个 语 法 来 查 看 某 个 分 支 在 一 定 时 间 前 的 位 置 例 如, 想 看 你 的 master 分 支 昨 天 在 哪, 你 可 以 输 入 $ git show master@{yesterday} 它 就 会 显 示 昨 天 分 支 的 顶 端 在 哪 这 项 技 术 只 对 还 在 你 引 用 日 志 里 的 数 据 有 用, 所 以 不 能 用 来 查 看 比 几 个 月 前 还 早 的 提 交 想 要 看 类 似 于 git log 输 出 格 式 的 引 用 日 志 信 息, 你 可 以 运 行 git log -g: $ git log -g master commit bc047d87bf7eac ae793478c50d3 Reflog: master@{0} (Scott Chacon <schacon@gmail.com>) Reflog message: commit: fixed refs handling, added gc auto, updated Author: Scott Chacon <schacon@gmail.com> 158

169 Sco Chacon Pro Git 中 文 版 6.1 节 修 订 版 本 (Revision) 选 择 Date: Fri Jan 2 18:32: fixed refs handling, added gc auto, updated tests commit d921970aadf03b3cf0e71becdaab3147ba71cdef Reflog: master@{1} (Scott Chacon <schacon@gmail.com>) Reflog message: merge phedders/rdocs: Merge made by recursive. Author: Scott Chacon <schacon@gmail.com> Date: Thu Dec 11 15:08: Merge commit 'phedders/rdocs' 需 要 注 意 的 是, 引 用 日 志 信 息 只 存 在 于 本 地 这 是 一 个 记 录 你 在 你 自 己 的 仓 库 里 做 过 什 么 的 日 志 其 他 人 拷 贝 的 仓 库 里 的 引 用 日 志 不 会 和 你 的 相 同 ; 而 你 新 克 隆 一 个 仓 库 的 时 候, 引 用 日 志 是 空 的, 因 为 你 在 仓 库 里 还 没 有 操 作 git show HEAD@{2.months.ago} 这 条 命 令 只 有 在 你 克 隆 了 一 个 项 目 至 少 两 个 月 时 才 会 有 用 如 果 你 是 五 分 钟 前 克 隆 的 仓 库, 那 么 它 将 不 会 有 结 果 返 回 祖 先 引 用 另 一 种 指 明 某 次 提 交 的 常 用 方 法 是 通 过 它 的 祖 先 如 果 你 在 引 用 最 后 加 上 一 个 ˆ,Git 将 其 理 解 为 此 次 提 交 的 父 提 交 假 设 你 的 工 程 历 史 是 这 样 的 : $ git log --pretty=format:'%h %s' --graph * b fixed refs handling, added gc auto, updated tests * d Merge commit 'phedders/rdocs' \ * 35cfb2b Some rdoc changes * 1c002dd added some blame and merge stuff / * 1c36188 ignore *.gem * 9b29157 add open3_detach to gemspec file list 那 么, 想 看 上 一 次 提 交, 你 可 以 使 用 HEADˆ, 意 思 是 HEAD 的 父 提 交 : $ git show HEAD^ commit d921970aadf03b3cf0e71becdaab3147ba71cdef 159

170 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 Merge: 1c002dd... 35cfb2b... Author: Scott Chacon <schacon@gmail.com> Date: Thu Dec 11 15:08: Merge commit 'phedders/rdocs' 你 也 可 以 在 ˆ 后 添 加 一 个 数 字 例 如,d921970ˆ2 意 思 是 d 的 第 二 父 提 交 这 种 语 法 只 在 合 并 提 交 时 有 用, 因 为 合 并 提 交 可 能 有 多 个 父 提 交 第 一 父 提 交 是 你 合 并 时 所 在 分 支, 而 第 二 父 提 交 是 你 所 合 并 的 分 支 : $ git show d921970^ commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b Author: Scott Chacon <schacon@gmail.com> Date: Thu Dec 11 14:58: added some blame and merge stuff $ git show d921970^2 commit 35cfb2b795a55793d7cc56a6cc2060b4bb Author: Paul Hedderly <paul+git@mjr.org> Date: Wed Dec 10 22:22: Some rdoc changes 另 外 一 个 指 明 祖 先 提 交 的 方 法 是 ~ 这 也 是 指 向 第 一 父 提 交, 所 以 HEAD~ 和 HEADˆ 是 等 价 的 当 你 指 定 数 字 的 时 候 就 明 显 不 一 样 了 HEAD~2 是 指 第 一 父 提 交 的 第 一 父 提 交, 也 就 是 祖 父 提 交 它 会 根 据 你 指 定 的 次 数 检 索 第 一 父 提 交 例 如, 在 上 面 列 出 的 历 史 记 录 里 面,HEAD~3 会 是 $ git show HEAD~3 commit 1c afb5fbcbea25b7c013f4e b8d Author: Tom Preston-Werner <tom@mojombo.com> Date: Fri Nov 7 13:47: ignore *.gem 160

171 Sco Chacon Pro Git 中 文 版 6.1 节 修 订 版 本 (Revision) 选 择 也 可 以 写 成 HEADˆˆˆ, 同 样 是 第 一 父 提 交 的 第 一 父 提 交 的 第 一 父 提 交 : $ git show HEAD^^^ commit 1c afb5fbcbea25b7c013f4e b8d Author: Tom Preston-Werner <tom@mojombo.com> Date: Fri Nov 7 13:47: ignore *.gem 你 也 可 以 混 合 使 用 这 些 语 法 你 可 以 通 过 HEAD~3ˆ2 指 明 先 前 引 用 的 第 二 父 提 交 ( 假 设 它 是 一 个 合 并 提 交 ) 提 交 范 围 现 在 你 已 经 可 以 指 明 单 次 的 提 交, 让 我 们 来 看 看 怎 样 指 明 一 定 范 围 的 提 交 这 在 你 管 理 分 支 的 时 候 尤 显 重 要 如 果 你 有 很 多 分 支, 你 可 以 指 明 范 围 来 圈 定 一 些 问 题 的 答 案, 比 如 : 这 个 分 支 上 我 有 哪 些 工 作 还 没 合 并 到 主 分 支 的? 双 点 最 常 用 的 指 明 范 围 的 方 法 是 双 点 的 语 法 这 种 语 法 主 要 是 让 Git 区 分 出 可 从 一 个 分 支 中 获 得 而 不 能 从 另 一 个 分 支 中 获 得 的 提 交 例 如, 假 设 你 有 类 似 于 图 6-1 的 提 交 历 史 图 6.1: 范 围 选 择 的 提 交 历 史 实 例 你 想 要 查 看 你 的 试 验 分 支 上 哪 些 没 有 被 提 交 到 主 分 支, 那 么 你 就 可 以 使 用 master..experiment 来 让 Git 显 示 这 些 提 交 的 日 志 这 句 话 的 意 思 是 所 有 可 从 experiment 分 支 中 获 得 而 不 能 从 master 分 支 中 获 得 的 提 交 为 了 使 例 子 简 单 明 了, 我 使 用 了 图 标 中 提 交 对 象 的 字 母 来 代 替 真 实 日 志 的 输 出, 所 以 会 显 示 : $ git log master..experiment D C 161

172 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 另 一 方 面, 如 果 你 想 看 相 反 的 所 有 在 master 而 不 在 experiment 中 的 分 支 你 可 以 交 换 分 支 的 名 字 experiment..master 显 示 所 有 可 在 master 获 得 而 在 experiment 中 不 能 的 提 交 : $ git log experiment..master F E 这 在 你 想 保 持 experiment 分 支 最 新 和 预 览 你 将 合 并 的 提 交 的 时 候 特 别 有 用 这 个 语 法 的 另 一 种 常 见 用 途 是 查 看 你 将 把 什 么 推 送 到 远 程 : $ git log origin/master..head 这 条 命 令 显 示 任 何 在 你 当 前 分 支 上 而 不 在 远 程 origin 上 的 提 交 如 果 你 运 行 git push 并 且 的 你 的 当 前 分 支 正 在 跟 踪 origin/master, 被 git log origin/master..head 列 出 的 提 交 就 是 将 被 传 输 到 服 务 器 上 的 提 交 你 也 可 以 留 空 语 法 中 的 一 边 来 让 Git 来 假 定 它 是 HEAD 例 如, 输 入 git log origin/master.. 将 得 到 和 上 面 的 例 子 一 样 的 结 果 Git 使 用 HEAD 来 代 替 不 存 在 的 一 边 多 点 双 点 语 法 就 像 速 记 一 样 有 用 ; 但 是 你 也 许 会 想 针 对 两 个 以 上 的 分 支 来 指 明 修 订 版 本, 比 如 查 看 哪 些 提 交 被 包 含 在 某 些 分 支 中 的 一 个, 但 是 不 在 你 当 前 的 分 支 上 Git 允 许 你 在 引 用 前 使 用 ˆ 字 符 或 者 --not 指 明 你 不 希 望 提 交 被 包 含 其 中 的 分 支 因 此 下 面 三 个 命 令 是 等 同 的 : $ git log refa..refb $ git log ^refa refb $ git log refb --not refa 这 样 很 好, 因 为 它 允 许 你 在 查 询 中 指 定 多 于 两 个 的 引 用, 而 这 是 双 点 语 法 所 做 不 到 的 例 如, 如 果 你 想 查 找 所 有 从 refa 或 refb 包 含 的 但 是 不 被 refc 包 含 的 提 交, 你 可 以 输 入 下 面 中 的 一 个 $ git log refa refb ^refc $ git log refa refb --not refc 162

173 Sco Chacon Pro Git 中 文 版 6.2 节 交 互 式 暂 存 这 建 立 了 一 个 非 常 强 大 的 修 订 版 本 查 询 系 统, 应 该 可 以 帮 助 你 解 决 分 支 里 包 含 了 什 么 这 个 问 题 三 点 最 后 一 种 主 要 的 范 围 选 择 语 法 是 三 点 语 法, 这 个 可 以 指 定 被 两 个 引 用 中 的 一 个 包 含 但 又 不 被 两 者 同 时 包 含 的 分 支 回 过 头 来 看 一 下 图 6-1 里 所 列 的 提 交 历 史 的 例 子 如 果 你 想 查 看 master 或 者 experiment 中 包 含 的 但 不 是 两 者 共 有 的 引 用, 你 可 以 运 行 $ git log master...experiment F E D C 这 个 再 次 给 出 你 普 通 的 log 输 出 但 是 只 显 示 那 四 次 提 交 的 信 息, 按 照 传 统 的 提 交 日 期 排 列 这 种 情 形 下,log 命 令 的 一 个 常 用 参 数 是 --left-right, 它 会 显 示 每 个 提 交 到 底 处 于 哪 一 侧 的 分 支 这 使 得 数 据 更 加 有 用 $ git log --left-right master...experiment < F < E > D > C 有 了 以 上 工 具, 让 Git 知 道 你 要 察 看 哪 些 提 交 就 容 易 得 多 了 6.2 交 互 式 暂 存 Git 提 供 了 很 多 脚 本 来 辅 助 某 些 命 令 行 任 务 这 里, 你 将 看 到 一 些 交 互 式 命 令, 它 们 帮 助 你 方 便 地 构 建 只 包 含 特 定 组 合 和 部 分 文 件 的 提 交 在 你 修 改 了 一 大 批 文 件 然 后 决 定 将 这 些 变 更 分 布 在 几 个 各 有 侧 重 的 提 交 而 不 是 单 个 又 大 又 乱 的 提 交 时, 这 些 工 具 非 常 有 用 用 这 种 方 法, 你 可 以 确 保 你 的 提 交 在 逻 辑 上 划 分 为 相 应 的 变 更 集, 以 便 于 供 和 你 一 起 工 作 的 开 发 者 审 阅 如 果 你 运 行 git add 时 加 上 -i 或 者 --interactive 选 项,Git 就 进 入 了 一 个 交 互 式 的 shell 模 式, 显 示 一 些 类 似 于 下 面 的 信 息 : 163

174 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 $ git add -i staged unstaged path 1: unchanged +0/-1 TODO 2: unchanged +1/-1 index.html 3: unchanged +5/-1 lib/simplegit.rb *** Commands *** 1: status 2: update 3: revert 4: add untracked 5: patch 6: diff 7: quit 8: help What now> 你 会 看 到 这 个 命 令 以 一 个 完 全 不 同 的 视 图 显 示 了 你 的 暂 存 区 主 要 是 你 通 过 git status 得 到 的 那 些 信 息 但 是 稍 微 简 洁 但 信 息 更 加 丰 富 一 些 它 在 左 侧 列 出 了 你 暂 存 的 变 更, 在 右 侧 列 出 了 未 被 暂 存 的 变 更 在 这 之 后 是 一 个 命 令 区 这 里 你 可 以 做 很 多 事 情, 包 括 暂 存 文 件, 撤 回 文 件, 暂 存 部 分 文 件, 加 入 未 被 追 踪 的 文 件, 查 看 暂 存 文 件 的 差 别 暂 存 和 撤 回 文 件 如 果 你 在 What now> 的 提 示 后 输 入 2 或 者 u, 这 个 脚 本 会 提 示 你 那 些 文 件 你 想 要 暂 存 : What now> 2 staged unstaged path 1: unchanged +0/-1 TODO 2: unchanged +1/-1 index.html 3: unchanged +5/-1 lib/simplegit.rb Update>> 如 果 想 暂 存 TODO 和 index.html, 你 可 以 输 入 相 应 的 编 号 : Update>> 1,2 staged unstaged path * 1: unchanged +0/-1 TODO * 2: unchanged +1/-1 index.html 3: unchanged +5/-1 lib/simplegit.rb Update>> 164

175 Sco Chacon Pro Git 中 文 版 6.2 节 交 互 式 暂 存 每 个 文 件 旁 边 的 * 表 示 选 中 的 文 件 将 被 暂 存 如 果 你 在 update>> 提 示 后 直 接 敲 入 回 车,Git 会 替 你 把 所 有 选 中 的 内 容 暂 存 : Update>> updated 2 paths *** Commands *** 1: status 2: update 3: revert 4: add untracked 5: patch 6: diff 7: quit 8: help What now> 1 staged unstaged path 1: +0/-1 nothing TODO 2: +1/-1 nothing index.html 3: unchanged +5/-1 lib/simplegit.rb 现 在 你 可 以 看 到 TODO 和 index.html 文 件 被 暂 存 了 同 时 simplegit.rb 文 件 仍 然 未 被 暂 存 如 果 这 时 你 想 要 撤 回 TODO 文 件, 就 使 用 3 或 者 r( 代 表 revert, 恢 复 ) 选 项 : *** Commands *** 1: status 2: update 3: revert 4: add untracked 5: patch 6: diff 7: quit 8: help What now> 3 staged unstaged path 1: +0/-1 nothing TODO 2: +1/-1 nothing index.html 3: unchanged +5/-1 lib/simplegit.rb Revert>> 1 staged unstaged path * 1: +0/-1 nothing TODO 2: +1/-1 nothing index.html 3: unchanged +5/-1 lib/simplegit.rb Revert>> [enter] reverted one path 再 次 查 看 Git 的 状 态, 你 会 看 到 你 已 经 撤 回 了 TODO 文 件 165

176 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 *** Commands *** 1: status 2: update 3: revert 4: add untracked 5: patch 6: diff 7: quit 8: help What now> 1 staged unstaged path 1: unchanged +0/-1 TODO 2: +1/-1 nothing index.html 3: unchanged +5/-1 lib/simplegit.rb 要 查 看 你 暂 存 内 容 的 差 异, 你 可 以 使 用 6 或 者 d( 表 示 diff) 命 令 它 会 显 示 你 暂 存 文 件 的 列 表, 你 可 以 选 择 其 中 的 几 个, 显 示 其 被 暂 存 的 差 异 这 跟 你 在 命 令 行 下 指 定 git diff --cached 非 常 相 似 : *** Commands *** 1: status 2: update 3: revert 4: add untracked 5: patch 6: diff 7: quit 8: help What now> 6 staged unstaged path 1: +1/-1 nothing index.html Review diff>> 1 diff --git a/index.html b/index.html index 4d f a/index.html ,7 Date Finder <p id="out">...</p> -<div id="footer">contact : support@github.com</div> +<div id="footer">contact : .support@github.com</div> <script type="text/javascript"> 166 通 过 这 些 基 本 命 令, 你 可 以 使 用 交 互 式 增 加 模 式 更 加 方 便 地 处 理 暂 存 区

177 Sco Chacon Pro Git 中 文 版 6.2 节 交 互 式 暂 存 暂 存 补 丁 只 让 Git 暂 存 文 件 的 某 些 部 分 而 忽 略 其 他 也 是 有 可 能 的 例 如, 你 对 simplegit.rb 文 件 作 了 两 处 修 改 但 是 只 想 暂 存 其 中 一 个 而 忽 略 另 一 个, 在 Git 中 实 现 这 一 点 非 常 容 易 在 交 互 式 的 提 示 符 下, 输 入 5 或 者 p( 表 示 patch, 补 丁 ) Git 会 询 问 哪 些 文 件 你 希 望 部 分 暂 存 ; 然 后 对 于 被 选 中 文 件 的 每 一 节, 他 会 逐 个 显 示 文 件 的 差 异 区 块 并 询 问 你 是 否 希 望 暂 存 他 们 : diff --git a/lib/simplegit.rb b/lib/simplegit.rb index dd5ecc e a/lib/simplegit.rb ,7 class SimpleGit end def log(treeish = 'master') - command("git log -n 25 #{treeish}") + command("git log -n 30 #{treeish}") end def blame(path) Stage this hunk [y,n,a,d,/,j,j,g,e,?]? 此 处 你 有 很 多 选 择 输 入? 可 以 显 示 列 表 : Stage this hunk [y,n,a,d,/,j,j,g,e,?]?? y - stage this hunk n - do not stage this hunk a - stage this and all the remaining hunks in the file d - do not stage this hunk nor any of the remaining hunks in the file g - select a hunk to go to / - search for a hunk matching the given regex j - leave this hunk undecided, see next undecided hunk J - leave this hunk undecided, see next hunk k - leave this hunk undecided, see previous undecided hunk K - leave this hunk undecided, see previous hunk s - split the current hunk into smaller hunks e - manually edit the current hunk? - print help 167

178 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 如 果 你 想 暂 存 各 个 区 块, 通 常 你 会 输 入 y 或 者 n, 但 是 暂 存 特 定 文 件 里 的 全 部 区 块 或 者 暂 时 跳 过 对 一 个 区 块 的 处 理 同 样 也 很 有 用 如 果 你 暂 存 了 文 件 的 一 个 部 分 而 保 留 另 外 一 个 部 分 不 被 暂 存, 你 的 状 态 输 出 看 起 来 会 是 这 样 : What now> 1 staged unstaged path 1: unchanged +0/-1 TODO 2: +1/-1 nothing index.html 3: +1/-1 +4/-0 lib/simplegit.rb simplegit.rb 的 状 态 非 常 有 意 思 它 显 示 有 几 行 被 暂 存 了, 有 几 行 没 有 你 部 分 地 暂 存 了 这 个 文 件 在 这 时, 你 可 以 退 出 交 互 式 脚 本 然 后 运 行 git commit 来 提 交 部 分 暂 存 的 文 件 最 后 你 也 可 以 不 通 过 交 互 式 增 加 的 模 式 来 实 现 部 分 文 件 暂 存 你 可 以 在 命 令 行 下 通 过 git add -p 或 者 git add --patch 来 启 动 同 样 的 脚 本 6.3 储 藏 (Stashing) 经 常 有 这 样 的 事 情 发 生, 当 你 正 在 进 行 项 目 中 某 一 部 分 的 工 作, 里 面 的 东 西 处 于 一 个 比 较 杂 乱 的 状 态, 而 你 想 转 到 其 他 分 支 上 进 行 一 些 工 作 问 题 是, 你 不 想 提 交 进 行 了 一 半 的 工 作, 否 则 以 后 你 无 法 回 到 这 个 工 作 点 解 决 这 个 问 题 的 办 法 就 是 git stash 命 令 储 藏 可 以 获 取 你 工 作 目 录 的 中 间 状 态 也 就 是 你 修 改 过 的 被 追 踪 的 文 件 和 暂 存 的 变 更 并 将 它 保 存 到 一 个 未 完 结 变 更 的 堆 栈 中, 随 时 可 以 重 新 应 用 储 藏 你 的 工 作 为 了 演 示 这 一 功 能, 你 可 以 进 入 你 的 项 目, 在 一 些 文 件 上 进 行 工 作, 有 可 能 还 暂 存 其 中 一 个 变 更 如 果 你 运 行 git status, 你 可 以 看 到 你 的 中 间 状 态 : $ git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: index.html 168

179 Sco Chacon Pro Git 中 文 版 6.3 节 储 藏 (Stashing) # # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # # modified: lib/simplegit.rb # 现 在 你 想 切 换 分 支, 但 是 你 还 不 想 提 交 你 正 在 进 行 中 的 工 作 ; 所 以 你 储 藏 这 些 变 更 为 了 往 堆 栈 推 送 一 个 新 的 储 藏, 只 要 运 行 git stash: $ git stash Saved working directory and index state \ "WIP on master: 049d078 added the index file" HEAD is now at 049d078 added the index file (To restore them type "git stash apply") 你 的 工 作 目 录 就 干 净 了 : $ git status # On branch master nothing to commit (working directory clean) 这 时, 你 可 以 方 便 地 切 换 到 其 他 分 支 工 作 ; 你 的 变 更 都 保 存 在 栈 上 要 查 看 现 有 的 储 藏, 你 可 以 使 用 git stash list: $ git stash list stash@{0}: WIP on master: 049d078 added the index file stash@{1}: WIP on master: c Revert "added file_size" stash@{2}: WIP on master: 21d80a5... added number to log 在 这 个 案 例 中, 之 前 已 经 进 行 了 两 次 储 藏, 所 以 你 可 以 访 问 到 三 个 不 同 的 储 藏 你 可 以 重 新 应 用 你 刚 刚 实 施 的 储 藏, 所 采 用 的 命 令 就 是 之 前 在 原 始 的 stash 命 令 的 帮 助 输 出 里 提 示 的 :git stash apply 如 果 你 想 应 用 更 早 的 储 藏, 你 可 以 通 过 名 字 指 定 它, 像 这 样 :git stash apply stash@{2} 如 果 你 不 指 明,Git 默 认 使 用 最 近 的 储 藏 并 尝 试 应 用 它 : 169

180 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 $ git stash apply # On branch master # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # # modified: index.html # modified: lib/simplegit.rb # 你 可 以 看 到 Git 重 新 修 改 了 你 所 储 藏 的 那 些 当 时 尚 未 提 交 的 文 件 在 这 个 案 例 里, 你 尝 试 应 用 储 藏 的 工 作 目 录 是 干 净 的, 并 且 属 于 同 一 分 支 ; 但 是 一 个 干 净 的 工 作 目 录 和 应 用 到 相 同 的 分 支 上 并 不 是 应 用 储 藏 的 必 要 条 件 你 可 以 在 其 中 一 个 分 支 上 保 留 一 份 储 藏, 随 后 切 换 到 另 外 一 个 分 支, 再 重 新 应 用 这 些 变 更 在 工 作 目 录 里 包 含 已 修 改 和 未 提 交 的 文 件 时, 你 也 可 以 应 用 储 藏 Git 会 给 出 归 并 冲 突 如 果 有 任 何 变 更 无 法 干 净 地 被 应 用 对 文 件 的 变 更 被 重 新 应 用, 但 是 被 暂 存 的 文 件 没 有 重 新 被 暂 存 想 那 样 的 话, 你 必 须 在 运 行 git stash apply 命 令 时 带 上 一 个 --index 的 选 项 来 告 诉 命 令 重 新 应 用 被 暂 存 的 变 更 如 果 你 是 这 么 做 的, 你 应 该 已 经 回 到 你 原 来 的 位 置 : $ git stash apply --index # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: index.html # # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # # modified: lib/simplegit.rb # apply 选 项 只 尝 试 应 用 储 藏 的 工 作 储 藏 的 内 容 仍 然 在 栈 上 要 移 除 它, 你 可 以 运 行 git stash drop, 加 上 你 希 望 移 除 的 储 藏 的 名 字 : 170

181 Sco Chacon Pro Git 中 文 版 6.3 节 储 藏 (Stashing) $ git stash list stash@{0}: WIP on master: 049d078 added the index file stash@{1}: WIP on master: c Revert "added file_size" stash@{2}: WIP on master: 21d80a5... added number to log $ git stash drop stash@{0} Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43) 你 也 可 以 运 行 git stash pop 来 重 新 应 用 储 藏, 同 时 立 刻 将 其 从 堆 栈 中 移 走 取 消 储 藏 (Un-applying a Stash) 在 某 些 情 况 下, 你 可 能 想 应 用 储 藏 的 修 改, 在 进 行 了 一 些 其 他 的 修 改 后, 又 要 取 消 之 前 所 应 用 储 藏 的 修 改 Git 没 有 提 供 类 似 于 stash unapply 的 命 令, 但 是 可 以 通 过 取 消 该 储 藏 的 补 丁 达 到 同 样 的 效 果 : $ git stash show -p stash@{0} git apply -R 同 样 的, 如 果 你 沒 有 指 定 具 体 的 某 个 储 藏,Git 会 选 择 最 近 的 储 藏 : $ git stash show -p git apply -R 你 可 能 会 想 要 新 建 一 个 別 名, 在 你 的 Git 里 增 加 一 个 stash-unapply 命 令, 这 样 更 有 效 率 例 如 : $ git config --global alias.stash-unapply '!git stash show -p git apply -R' $ git stash $ #... work work work $ git stash-unapply 从 储 藏 中 创 建 分 支 如 果 你 储 藏 了 一 些 工 作, 暂 时 不 去 理 会, 然 后 继 续 在 你 储 藏 工 作 的 分 支 上 工 作, 你 在 重 新 应 用 工 作 时 可 能 会 碰 到 一 些 问 题 如 果 尝 试 应 用 的 变 更 是 针 对 一 个 你 那 之 后 修 改 过 的 文 件, 你 会 碰 到 一 个 归 并 冲 突 并 且 必 须 去 化 解 它 如 果 你 想 用 更 方 便 的 171

182 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 方 法 来 重 新 检 验 你 储 藏 的 变 更, 你 可 以 运 行 git stash branch, 这 会 创 建 一 个 新 的 分 支, 检 出 你 储 藏 工 作 时 的 所 处 的 提 交, 重 新 应 用 你 的 工 作, 如 果 成 功, 将 会 丢 弃 储 藏 $ git stash branch testchanges Switched to a new branch "testchanges" # On branch testchanges # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: index.html # # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # # modified: lib/simplegit.rb # Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359) 这 是 一 个 很 棒 的 捷 径 来 恢 复 储 藏 的 工 作 然 后 在 新 的 分 支 上 继 续 当 时 的 工 作 6.4 重 写 历 史 很 多 时 候, 在 Git 上 工 作 的 时 候, 你 也 许 会 由 于 某 种 原 因 想 要 修 订 你 的 提 交 历 史 Git 的 一 个 卓 越 之 处 就 是 它 允 许 你 在 最 后 可 能 的 时 刻 再 作 决 定 你 可 以 在 你 即 将 提 交 暂 存 区 时 决 定 什 么 文 件 归 入 哪 一 次 提 交, 你 可 以 使 用 stash 命 令 来 决 定 你 暂 时 搁 置 的 工 作, 你 可 以 重 写 已 经 发 生 的 提 交 以 使 它 们 看 起 来 是 另 外 一 种 样 子 这 个 包 括 改 变 提 交 的 次 序 改 变 说 明 或 者 修 改 提 交 中 包 含 的 文 件, 将 提 交 归 并 拆 分 或 者 完 全 删 除 这 一 切 在 你 尚 未 开 始 将 你 的 工 作 和 别 人 共 享 前 都 是 可 以 的 在 这 一 节 中, 你 会 学 到 如 何 完 成 这 些 很 有 用 的 任 务 以 使 你 的 提 交 历 史 在 你 将 其 共 享 给 别 人 之 前 变 成 你 想 要 的 样 子 改 变 最 近 一 次 提 交 改 变 最 近 一 次 提 交 也 许 是 最 常 见 的 重 写 历 史 的 行 为 对 于 你 的 最 近 一 次 提 交, 你 经 常 想 做 两 件 基 本 事 情 : 改 变 提 交 说 明, 或 者 改 变 你 刚 刚 通 过 增 加, 改 变, 删 除 而 记 录 的 快 照 如 果 你 只 想 修 改 最 近 一 次 提 交 说 明, 这 非 常 简 单 : 172

183 Sco Chacon Pro Git 中 文 版 6.4 节 重 写 历 史 $ git commit --amend 这 会 把 你 带 入 文 本 编 辑 器, 里 面 包 含 了 你 最 近 一 次 提 交 说 明, 供 你 修 改 当 你 保 存 并 退 出 编 辑 器, 这 个 编 辑 器 会 写 入 一 个 新 的 提 交, 里 面 包 含 了 那 个 说 明, 并 且 让 它 成 为 你 的 新 的 最 近 一 次 提 交 如 果 你 完 成 提 交 后 又 想 修 改 被 提 交 的 快 照, 增 加 或 者 修 改 其 中 的 文 件, 可 能 因 为 你 最 初 提 交 时, 忘 了 添 加 一 个 新 建 的 文 件, 这 个 过 程 基 本 上 一 样 你 通 过 修 改 文 件 然 后 对 其 运 行 git add 或 对 一 个 已 被 记 录 的 文 件 运 行 git rm, 随 后 的 git commit --amend 会 获 取 你 当 前 的 暂 存 区 并 将 它 作 为 新 提 交 对 应 的 快 照 使 用 这 项 技 术 的 时 候 你 必 须 小 心, 因 为 修 正 会 改 变 提 交 的 SHA-1 值 这 个 很 像 是 一 次 非 常 小 的 rebase 不 要 在 你 最 近 一 次 提 交 被 推 送 后 还 去 修 正 它 修 改 多 个 提 交 说 明 要 修 改 历 史 中 更 早 的 提 交, 你 必 须 采 用 更 复 杂 的 工 具 Git 没 有 一 个 修 改 历 史 的 工 具, 但 是 你 可 以 使 用 rebase 工 具 来 衍 合 一 系 列 的 提 交 到 它 们 原 来 所 在 的 HEAD 上 而 不 是 移 到 新 的 上 依 靠 这 个 交 互 式 的 rebase 工 具, 你 就 可 以 停 留 在 每 一 次 提 交 后, 如 果 你 想 修 改 或 改 变 说 明 增 加 文 件 或 任 何 其 他 事 情 你 可 以 通 过 给 git rebase 增 加 -i 选 项 来 以 交 互 方 式 地 运 行 rebase 你 必 须 通 过 告 诉 命 令 衍 合 到 哪 次 提 交, 来 指 明 你 需 要 重 写 的 提 交 的 回 溯 深 度 例 如, 你 想 修 改 最 近 三 次 的 提 交 说 明, 或 者 其 中 任 意 一 次, 你 必 须 给 git rebase -i 提 供 一 个 参 数, 指 明 你 想 要 修 改 的 提 交 的 父 提 交, 例 如 HEAD~2 或 者 HEAD~3 可 能 记 住 ~3 更 加 容 易, 因 为 你 想 修 改 最 近 三 次 提 交 ; 但 是 请 记 住 你 事 实 上 所 指 的 是 四 次 提 交 之 前, 即 你 想 修 改 的 提 交 的 父 提 交 $ git rebase -i HEAD~3 再 次 提 醒 这 是 一 个 衍 合 命 令 HEAD~3..HEAD 范 围 内 的 每 一 次 提 交 都 会 被 重 写, 无 论 你 是 否 修 改 说 明 不 要 涵 盖 你 已 经 推 送 到 中 心 服 务 器 的 提 交 这 么 做 会 使 其 他 开 发 者 产 生 混 乱, 因 为 你 提 供 了 同 样 变 更 的 不 同 版 本 运 行 这 个 命 令 会 为 你 的 文 本 编 辑 器 提 供 一 个 提 交 列 表, 看 起 来 像 下 面 这 样 pick f7f3f6d changed my name a bit pick e updated README formatting and added blame pick a5f4a0d added cat-file 173

184 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 # Rebase 710f0f8..a5f4a0d onto 710f0f8 # # Commands: # p, pick = use commit # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. # 很 重 要 的 一 点 是 你 得 注 意 这 些 提 交 的 顺 序 与 你 通 常 通 过 log 命 令 看 到 的 是 相 反 的 如 果 你 运 行 log, 你 会 看 到 下 面 这 样 的 结 果 : $ git log --pretty=format:"%h %s" HEAD~3..HEAD a5f4a0d added cat-file e updated README formatting and added blame f7f3f6d changed my name a bit 请 注 意 这 里 的 倒 序 交 互 式 的 rebase 给 了 你 一 个 即 将 运 行 的 脚 本 它 会 从 你 在 命 令 行 上 指 明 的 提 交 开 始 (HEAD~3) 然 后 自 上 至 下 重 播 每 次 提 交 里 引 入 的 变 更 它 将 最 早 的 列 在 顶 上 而 不 是 最 近 的, 因 为 这 是 第 一 个 需 要 重 播 的 你 需 要 修 改 这 个 脚 本 来 让 它 停 留 在 你 想 修 改 的 变 更 上 要 做 到 这 一 点, 你 只 要 将 你 想 修 改 的 每 一 次 提 交 前 面 的 pick 改 为 edit 例 如, 只 想 修 改 第 三 次 提 交 说 明 的 话, 你 就 像 下 面 这 样 修 改 文 件 : edit f7f3f6d changed my name a bit pick e updated README formatting and added blame pick a5f4a0d added cat-file 当 你 保 存 并 退 出 编 辑 器,Git 会 倒 回 至 列 表 中 的 最 后 一 次 提 交, 然 后 把 你 送 到 命 令 行 中, 同 时 显 示 以 下 信 息 : $ git rebase -i HEAD~3 Stopped at 7482e0d... updated the gemspec to hopefully work better 174

185 Sco Chacon Pro Git 中 文 版 6.4 节 重 写 历 史 You can amend the commit now, with git commit --amend Once you re satisfied with your changes, run git rebase --continue 这 些 指 示 很 明 确 地 告 诉 了 你 该 干 什 么 输 入 $ git commit --amend 修 改 提 交 说 明, 退 出 编 辑 器 然 后, 运 行 $ git rebase --continue 这 个 命 令 会 自 动 应 用 其 他 两 次 提 交, 你 就 完 成 任 务 了 如 果 你 将 更 多 行 的 pick 改 为 edit, 你 就 能 对 你 想 修 改 的 提 交 重 复 这 些 步 骤 Git 每 次 都 会 停 下, 让 你 修 正 提 交, 完 成 后 继 续 运 行 重 排 提 交 你 也 可 以 使 用 交 互 式 的 衍 合 来 彻 底 重 排 或 删 除 提 交 如 果 你 想 删 除 added cat-file 这 个 提 交 并 且 修 改 其 他 两 次 提 交 引 入 的 顺 序, 你 将 rebase 脚 本 从 这 个 pick f7f3f6d changed my name a bit pick e updated README formatting and added blame pick a5f4a0d added cat-file 改 为 这 个 : pick e updated README formatting and added blame pick f7f3f6d changed my name a bit 175

186 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 当 你 保 存 并 退 出 编 辑 器,Git 将 分 支 倒 回 至 这 些 提 交 的 父 提 交, 应 用 e, 然 后 f7f3f6d, 接 着 停 止 你 有 效 地 修 改 了 这 些 提 交 的 顺 序 并 且 彻 底 删 除 了 added cat-file 这 次 提 交 压 制 (Squashing) 提 交 交 互 式 的 衍 合 工 具 还 可 以 将 一 系 列 提 交 压 制 为 单 一 提 交 脚 本 在 rebase 的 信 息 里 放 了 一 些 有 用 的 指 示 : # # Commands: # p, pick = use commit # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. # 如 果 不 用 pick 或 者 edit, 而 是 指 定 squash,git 会 同 时 应 用 那 个 变 更 和 它 之 前 的 变 更 并 将 提 交 说 明 归 并 因 此, 如 果 你 想 将 这 三 个 提 交 合 并 为 单 一 提 交, 你 可 以 将 脚 本 修 改 成 这 样 : pick f7f3f6d changed my name a bit squash e updated README formatting and added blame squash a5f4a0d added cat-file 当 你 保 存 并 退 出 编 辑 器,Git 会 应 用 全 部 三 次 变 更 然 后 将 你 送 回 编 辑 器 来 归 并 三 次 提 交 说 明 # This is a combination of 3 commits. # The first commit's message is: changed my name a bit # This is the 2nd commit message: updated README formatting and added blame 176

187 Sco Chacon Pro Git 中 文 版 6.4 节 重 写 历 史 # This is the 3rd commit message: added cat-file 当 你 保 存 之 后, 你 就 拥 有 了 一 个 包 含 前 三 次 提 交 的 全 部 变 更 的 单 一 提 交 拆 分 提 交 拆 分 提 交 就 是 撤 销 一 次 提 交, 然 后 多 次 部 分 地 暂 存 或 提 交 直 到 结 束 例 如, 假 设 你 想 将 三 次 提 交 中 的 中 间 一 次 拆 分 将 updated README forma ing and added blame 拆 分 成 两 次 提 交 : 第 一 次 为 updated README forma ing, 第 二 次 为 added blame 你 可 以 在 rebase -i 脚 本 中 修 改 你 想 拆 分 的 提 交 前 的 指 令 为 edit : pick f7f3f6d changed my name a bit edit e updated README formatting and added blame pick a5f4a0d added cat-file 然 后, 这 个 脚 本 就 将 你 带 入 命 令 行, 你 重 置 那 次 提 交, 提 取 被 重 置 的 变 更, 从 中 创 建 多 次 提 交 当 你 保 存 并 退 出 编 辑 器,Git 倒 回 到 列 表 中 第 一 次 提 交 的 父 提 交, 应 用 第 一 次 提 交 (f7f3f6d), 应 用 第 二 次 提 交 (310154e), 然 后 将 你 带 到 控 制 台 那 里 你 可 以 用 git reset HEADˆ 对 那 次 提 交 进 行 一 次 混 合 的 重 置, 这 将 撤 销 那 次 提 交 并 且 将 修 改 的 文 件 撤 回 此 时 你 可 以 暂 存 并 提 交 文 件, 直 到 你 拥 有 多 次 提 交, 结 束 后, 运 行 git rebase --continue $ git reset HEAD^ $ git add README $ git commit -m 'updated README formatting' $ git add lib/simplegit.rb $ git commit -m 'added blame' $ git rebase --continue Git 在 脚 本 中 应 用 了 最 后 一 次 提 交 (a5f4a0d), 你 的 历 史 看 起 来 就 像 这 样 了 : 177

188 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 $ git log -4 --pretty=format:"%h %s" 1c002dd added cat-file 9b29157 added blame 35cfb2b updated README formatting f3cc40e changed my name a bit 再 次 提 醒, 这 会 修 改 你 列 表 中 的 提 交 的 SHA 值, 所 以 请 确 保 这 个 列 表 里 不 包 含 你 已 经 推 送 到 共 享 仓 库 的 提 交 核 弹 级 选 项 : filter-branch 如 果 你 想 用 脚 本 的 方 式 修 改 大 量 的 提 交, 还 有 一 个 重 写 历 史 的 选 项 可 以 用 例 如, 全 局 性 地 修 改 电 子 邮 件 地 址 或 者 将 一 个 文 件 从 所 有 提 交 中 删 除 这 个 命 令 是 filter-branch, 这 个 会 大 面 积 地 修 改 你 的 历 史, 所 以 你 很 有 可 能 不 该 去 用 它, 除 非 你 的 项 目 尚 未 公 开, 没 有 其 他 人 在 你 准 备 修 改 的 提 交 的 基 础 上 工 作 尽 管 如 此, 这 个 可 以 非 常 有 用 你 会 学 习 一 些 常 见 用 法, 借 此 对 它 的 能 力 有 所 认 识 从 所 有 提 交 中 删 除 一 个 文 件 这 个 经 常 发 生 有 些 人 不 经 思 考 使 用 git add., 意 外 地 提 交 了 一 个 巨 大 的 二 进 制 文 件, 你 想 将 它 从 所 有 地 方 删 除 也 许 你 不 小 心 提 交 了 一 个 包 含 密 码 的 文 件, 而 你 想 让 你 的 项 目 开 源 filter-branch 大 概 会 是 你 用 来 清 理 整 个 历 史 的 工 具 要 从 整 个 历 史 中 删 除 一 个 名 叫 password.txt 的 文 件, 你 可 以 在 filter-branch 上 使 用 --tree-filter 选 项 : $ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21) Ref 'refs/heads/master' was rewritten --tree-filter 选 项 会 在 每 次 检 出 项 目 时 先 执 行 指 定 的 命 令 然 后 重 新 提 交 结 果 在 这 个 例 子 中, 你 会 在 所 有 快 照 中 删 除 一 个 名 叫 password.txt 的 文 件, 无 论 它 是 否 存 在 如 果 你 想 删 除 所 有 不 小 心 提 交 上 去 的 编 辑 器 备 份 文 件, 你 可 以 运 行 类 似 git filter-branch --tree-filter "find * -type f -name '*~' -delete" HEAD 的 命 令 你 可 以 观 察 到 Git 重 写 目 录 树 并 且 提 交, 然 后 将 分 支 指 针 移 到 末 尾 一 个 比 较 好 的 办 法 是 在 一 个 测 试 分 支 上 做 这 些 然 后 在 你 确 定 产 物 真 的 是 你 所 要 的 之 后, 再 hard-reset 你 的 主 分 支 要 在 你 所 有 的 分 支 上 运 行 filter-branch 的 话, 你 可 以 传 递 一 个 --all 给 命 令 178

189 Sco Chacon Pro Git 中 文 版 6.5 节 使 用 Git 调 试 将 一 个 子 目 录 设 置 为 新 的 根 目 录 假 设 你 完 成 了 从 另 外 一 个 代 码 控 制 系 统 的 导 入 工 作, 得 到 了 一 些 没 有 意 义 的 子 目 录 (trunk, tags 等 等 ) 如 果 你 想 让 trunk 子 目 录 成 为 每 一 次 提 交 的 新 的 项 目 根 目 录,filter-branch 也 可 以 帮 你 做 到 : $ git filter-branch --subdirectory-filter trunk HEAD Rewrite 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12) Ref 'refs/heads/master' was rewritten 现 在 你 的 项 目 根 目 录 就 是 trunk 子 目 录 了 Git 会 自 动 地 删 除 不 对 这 个 子 目 录 产 生 影 响 的 提 交 全 局 性 地 更 换 电 子 邮 件 地 址 另 一 个 常 见 的 案 例 是 你 在 开 始 时 忘 了 运 行 git config 来 设 置 你 的 姓 名 和 电 子 邮 件 地 址, 也 许 你 想 开 源 一 个 项 目, 把 你 所 有 的 工 作 电 子 邮 件 地 址 修 改 为 个 人 地 址 无 论 哪 种 情 况 你 都 可 以 用 filter-branch 来 更 换 多 次 提 交 里 的 电 子 邮 件 地 址 你 必 须 小 心 一 些, 只 改 变 属 于 你 的 电 子 邮 件 地 址, 所 以 你 使 用 --commit-filter: $ git filter-branch --commit-filter ' if [ "$GIT_AUTHOR_ " = "schacon@localhost" ]; then GIT_AUTHOR_NAME="Scott Chacon"; GIT_AUTHOR_ ="schacon@example.com"; git commit-tree "$@"; else git commit-tree "$@"; fi' HEAD 这 个 会 遍 历 并 重 写 所 有 提 交 使 之 拥 有 你 的 新 地 址 因 为 提 交 里 包 含 了 它 们 的 父 提 交 的 SHA-1 值, 这 个 命 令 会 修 改 你 的 历 史 中 的 所 有 提 交, 而 不 仅 仅 是 包 含 了 匹 配 的 电 子 邮 件 地 址 的 那 些 6.5 使 用 Git 调 试 Git 同 样 提 供 了 一 些 工 具 来 帮 助 你 调 试 项 目 中 遇 到 的 问 题 由 于 Git 被 设 计 为 可 应 用 于 几 乎 任 何 类 型 的 项 目, 这 些 工 具 是 通 用 型, 但 是 在 遇 到 问 题 时 可 以 经 常 帮 助 你 查 找 缺 陷 所 在 179

190 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 文 件 标 注 如 果 你 在 追 踪 代 码 中 的 缺 陷 想 知 道 这 是 什 么 时 候 为 什 么 被 引 进 来 的, 文 件 标 注 会 是 你 的 最 佳 工 具 它 会 显 示 文 件 中 对 每 一 行 进 行 修 改 的 最 近 一 次 提 交 因 此, 如 果 你 发 现 自 己 代 码 中 的 一 个 方 法 存 在 缺 陷, 你 可 以 用 git blame 来 标 注 文 件, 查 看 那 个 方 法 的 每 一 行 分 别 是 由 谁 在 哪 一 天 修 改 的 下 面 这 个 例 子 使 用 了 -L 选 项 来 限 制 输 出 范 围 在 第 12 至 22 行 : $ git blame -L 12,22 simplegit.rb ^4832fe2 (Scott Chacon :31: ) def show(tree = 'master') ^4832fe2 (Scott Chacon :31: ) command("git show #{tree}") ^4832fe2 (Scott Chacon :31: ) end ^4832fe2 (Scott Chacon :31: ) 9f6560e4 (Scott Chacon :52: ) def log(tree = 'master') 79eaf55d (Scott Chacon :15: ) command("git log #{tree}") 9f6560e4 (Scott Chacon :52: ) end 9f6560e4 (Scott Chacon :52: ) 42cf2861 (Magnus Chacon :45: ) def blame(path) 42cf2861 (Magnus Chacon :45: ) command("git blame #{path}") 42cf2861 (Magnus Chacon :45: ) end 请 注 意 第 一 个 域 里 是 最 后 一 次 修 改 该 行 的 那 次 提 交 的 SHA-1 值 接 下 去 的 两 个 域 是 从 那 次 提 交 中 抽 取 的 值 作 者 姓 名 和 日 期 所 以 你 可 以 方 便 地 获 知 谁 在 什 么 时 候 修 改 了 这 一 行 在 这 后 面 是 行 号 和 文 件 的 内 容 请 注 意 ˆ4832fe2 提 交 的 那 些 行, 这 些 指 的 是 文 件 最 初 提 交 的 那 些 行 那 个 提 交 是 文 件 第 一 次 被 加 入 这 个 项 目 时 存 在 的, 自 那 以 后 未 被 修 改 过 这 会 带 来 小 小 的 困 惑, 因 为 你 已 经 至 少 看 到 了 Git 使 用 ˆ 来 修 饰 一 个 提 交 的 SHA 值 的 三 种 不 同 的 意 义, 但 这 里 确 实 就 是 这 个 意 思 另 一 件 很 酷 的 事 情 是 在 Git 中 你 不 需 要 显 式 地 记 录 文 件 的 重 命 名 它 会 记 录 快 照 然 后 根 据 现 实 尝 试 找 出 隐 式 的 重 命 名 动 作 这 其 中 有 一 个 很 有 意 思 的 特 性 就 是 你 可 以 让 它 找 出 所 有 的 代 码 移 动 如 果 你 在 git blame 后 加 上 -C,Git 会 分 析 你 在 标 注 的 文 件 然 后 尝 试 找 出 其 中 代 码 片 段 的 原 始 出 处, 如 果 它 是 从 其 他 地 方 拷 贝 过 来 的 话 最 近, 我 在 将 一 个 名 叫 GITServerHandler.m 的 文 件 分 解 到 多 个 文 件 中, 其 中 一 个 是 GITPackUpload.m 通 过 对 GITPackUpload.m 执 行 带 -C 参 数 的 blame 命 令, 我 可 以 看 到 代 码 块 的 原 始 出 处 : $ git blame -C -L 141,153 GITPackUpload.m f344f58d GITServerHandler.m (Scott ) f344f58d GITServerHandler.m (Scott ) - (void) gatherobjectshasfromc 180

191 Sco Chacon Pro Git 中 文 版 6.5 节 使 用 Git 调 试 f344f58d GITServerHandler.m (Scott ) { 70befddd GITServerHandler.m (Scott ) //NSLog(@"GATHER COMMI ad11ac80 GITPackUpload.m (Scott ) ad11ac80 GITPackUpload.m (Scott ) NSString *parentsha; ad11ac80 GITPackUpload.m (Scott ) GITCommit *commit = [g ad11ac80 GITPackUpload.m (Scott ) ad11ac80 GITPackUpload.m (Scott ) //NSLog(@"GATHER COMMI ad11ac80 GITPackUpload.m (Scott ) 56ef2caf GITServerHandler.m (Scott ) if(commit) { 56ef2caf GITServerHandler.m (Scott ) [refdict setob 56ef2caf GITServerHandler.m (Scott ) 这 真 的 非 常 有 用 通 常, 你 会 把 你 拷 贝 代 码 的 那 次 提 交 作 为 原 始 提 交, 因 为 这 是 你 在 这 个 文 件 中 第 一 次 接 触 到 那 几 行 Git 可 以 告 诉 你 编 写 那 些 行 的 原 始 提 交, 即 便 是 在 另 一 个 文 件 里 二 分 查 找 标 注 文 件 在 你 知 道 问 题 是 哪 里 引 入 的 时 候 会 有 帮 助 如 果 你 不 知 道, 并 且 自 上 次 代 码 可 用 的 状 态 已 经 经 历 了 上 百 次 的 提 交, 你 可 能 就 要 求 助 于 bisect 命 令 了 bisect 会 在 你 的 提 交 历 史 中 进 行 二 分 查 找 来 尽 快 地 确 定 哪 一 次 提 交 引 入 了 错 误 例 如 你 刚 刚 推 送 了 一 个 代 码 发 布 版 本 到 产 品 环 境 中, 对 代 码 为 什 么 会 表 现 成 那 样 百 思 不 得 其 解 你 回 到 你 的 代 码 中, 还 好 你 可 以 重 现 那 个 问 题, 但 是 找 不 到 在 哪 里 你 可 以 对 代 码 执 行 bisect 来 寻 找 首 先 你 运 行 git bisect start 启 动, 然 后 你 用 git bisect bad 来 告 诉 系 统 当 前 的 提 交 已 经 有 问 题 了 然 后 你 必 须 告 诉 bisect 已 知 的 最 后 一 次 正 常 状 态 是 哪 次 提 交, 使 用 git bisect good [good_commit]: $ git bisect start $ git bisect bad $ git bisect good v1.0 Bisecting: 6 revisions left to test after this [ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] error handling on repo Git 发 现 在 你 标 记 为 正 常 的 提 交 (v1.0) 和 当 前 的 错 误 版 本 之 间 有 大 约 12 次 提 交, 于 是 它 检 出 中 间 的 一 个 在 这 里, 你 可 以 运 行 测 试 来 检 查 问 题 是 否 存 在 于 这 次 提 交 如 果 是, 那 么 它 是 在 这 个 中 间 提 交 之 前 的 某 一 次 引 入 的 ; 如 果 否, 那 么 问 题 是 在 中 间 提 交 之 后 引 入 的 假 设 这 里 是 没 有 错 误 的, 那 么 你 就 通 过 git bisect good 来 告 诉 Git 然 后 继 续 你 的 旅 程 : 181

192 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 $ git bisect good Bisecting: 3 revisions left to test after this [b047b02ea83310a70fd603dc8cd7a6cd13d15c04] secure this thing 现 在 你 在 另 外 一 个 提 交 上 了, 在 你 刚 刚 测 试 通 过 的 和 一 个 错 误 提 交 的 中 点 处 你 再 次 运 行 测 试 然 后 发 现 这 次 提 交 是 错 误 的, 因 此 你 通 过 git bisect bad 来 告 诉 Git: $ git bisect bad Bisecting: 1 revisions left to test after this [f71ce38690acf49c1f3c9bea38e09d82a5ce6014] drop exceptions table 这 次 提 交 是 好 的, 那 么 Git 就 获 得 了 确 定 问 题 引 入 位 置 所 需 的 所 有 信 息 它 告 诉 你 第 一 个 错 误 提 交 的 SHA-1 值 并 且 显 示 一 些 提 交 说 明 以 及 哪 些 文 件 在 那 次 提 交 里 修 改 过, 这 样 你 可 以 找 出 缺 陷 被 引 入 的 根 源 : $ git bisect good b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04 Author: PJ Hyett <pjhyett@example.com> Date: Tue Jan 27 14:48: secure this thing : ee3e7821b895e52c db9bdc4c61d1730 f24d3c6ebcfc639b1a e62d60b8e68a8e4 M config 当 你 完 成 之 后, 你 应 该 运 行 git bisect reset 来 重 设 你 的 HEAD 到 你 开 始 前 的 地 方, 否 则 你 会 处 于 一 个 诡 异 的 地 方 : $ git bisect reset 这 是 个 强 大 的 工 具, 可 以 帮 助 你 检 查 上 百 的 提 交, 在 几 分 钟 内 找 出 缺 陷 引 入 的 位 置 事 实 上, 如 果 你 有 一 个 脚 本 会 在 工 程 正 常 时 返 回 0, 错 误 时 返 回 非 0 的 话, 你 182

193 Sco Chacon Pro Git 中 文 版 6.6 节 子 模 块 可 以 完 全 自 动 地 执 行 git bisect 首 先 你 需 要 提 供 已 知 的 错 误 和 正 确 提 交 来 告 诉 它 二 分 查 找 的 范 围 你 可 以 通 过 bisect start 命 令 来 列 出 它 们, 先 列 出 已 知 的 错 误 提 交 再 列 出 已 知 的 正 确 提 交 : $ git bisect start HEAD v1.0 $ git bisect run test-error.sh 这 样 会 自 动 地 在 每 一 个 检 出 的 提 交 里 运 行 test-error.sh 直 到 Git 找 出 第 一 个 破 损 的 提 交 你 也 可 以 运 行 像 make 或 者 make tests 或 者 任 何 你 所 拥 有 的 来 为 你 执 行 自 动 化 的 测 试 6.6 子 模 块 经 常 有 这 样 的 事 情, 当 你 在 一 个 项 目 上 工 作 时, 你 需 要 在 其 中 使 用 另 外 一 个 项 目 也 许 它 是 一 个 第 三 方 开 发 的 库 或 者 是 你 独 立 开 发 和 并 在 多 个 父 项 目 中 使 用 的 这 个 场 景 下 一 个 常 见 的 问 题 产 生 了 : 你 想 将 两 个 项 目 单 独 处 理 但 是 又 需 要 在 其 中 一 个 中 使 用 另 外 一 个 这 里 有 一 个 例 子 假 设 你 在 开 发 一 个 网 站, 为 之 创 建 Atom 源 你 不 想 编 写 一 个 自 己 的 Atom 生 成 代 码, 而 是 决 定 使 用 一 个 库 你 可 能 不 得 不 像 CPAN install 或 者 Ruby gem 一 样 包 含 来 自 共 享 库 的 代 码, 或 者 将 代 码 拷 贝 到 你 的 项 目 树 中 如 果 采 用 包 含 库 的 办 法, 那 么 不 管 用 什 么 办 法 都 很 难 去 定 制 这 个 库, 部 署 它 就 更 加 困 难 了, 因 为 你 必 须 确 保 每 个 客 户 都 拥 有 那 个 库 把 代 码 包 含 到 你 自 己 的 项 目 中 带 来 的 问 题 是, 当 上 游 被 修 改 时, 任 何 你 进 行 的 定 制 化 的 修 改 都 很 难 归 并 Git 通 过 子 模 块 处 理 这 个 问 题 子 模 块 允 许 你 将 一 个 Git 仓 库 当 作 另 外 一 个 Git 仓 库 的 子 目 录 这 允 许 你 克 隆 另 外 一 个 仓 库 到 你 的 项 目 中 并 且 保 持 你 的 提 交 相 对 独 立 子 模 块 初 步 假 设 你 想 把 Rack 库 ( 一 个 Ruby 的 web 服 务 器 网 关 接 口 ) 加 入 到 你 的 项 目 中, 可 能 既 要 保 持 你 自 己 的 变 更, 又 要 延 续 上 游 的 变 更 首 先 你 要 把 外 部 的 仓 库 克 隆 到 你 的 子 目 录 中 你 通 过 git submodule add 将 外 部 项 目 加 为 子 模 块 : $ git submodule add git://github.com/chneukirchen/rack.git rack Initialized empty Git repository in /opt/subtest/rack/.git/ remote: Counting objects: 3181, done. remote: Compressing objects: 100% (1534/1534), done. remote: Total 3181 (delta 1951), reused 2623 (delta 1603) 183

194 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 Receiving objects: 100% (3181/3181), KiB 422 KiB/s, done. Resolving deltas: 100% (1951/1951), done. 现 在 你 就 在 项 目 里 的 rack 子 目 录 下 有 了 一 个 Rack 项 目 你 可 以 进 入 那 个 子 目 录, 进 行 变 更, 加 入 你 自 己 的 远 程 可 写 仓 库 来 推 送 你 的 变 更, 从 原 始 仓 库 拉 取 和 归 并 等 等 如 果 你 在 加 入 子 模 块 后 立 刻 运 行 git status, 你 会 看 到 下 面 两 项 : $ git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # new file:.gitmodules # new file: rack # 首 先 你 注 意 到 有 一 个.gitmodules 文 件 这 是 一 个 配 置 文 件, 保 存 了 项 目 URL 和 你 拉 取 到 的 本 地 子 目 录 $ cat.gitmodules [submodule "rack"] path = rack url = git://github.com/chneukirchen/rack.git 如 果 你 有 多 个 子 模 块, 这 个 文 件 里 会 有 多 个 条 目 很 重 要 的 一 点 是 这 个 文 件 跟 其 他 文 件 一 样 也 是 处 于 版 本 控 制 之 下 的, 就 像 你 的.gitignore 文 件 一 样 它 跟 项 目 里 的 其 他 文 件 一 样 可 以 被 推 送 和 拉 取 这 是 其 他 克 隆 此 项 目 的 人 获 知 子 模 块 项 目 来 源 的 途 径 git status 的 输 出 里 所 列 的 另 一 项 目 是 rack 如 果 你 运 行 在 那 上 面 运 行 git diff, 会 发 现 一 些 有 趣 的 东 西 : $ git diff --cached rack diff --git a/rack b/rack new file mode index d709f 184

195 Sco Chacon Pro Git 中 文 版 6.6 节 子 模 块 --- /dev/null +++ b/rack -0,0 +1 +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 尽 管 rack 是 你 工 作 目 录 里 的 子 目 录, 但 Git 把 它 视 作 一 个 子 模 块, 当 你 不 在 那 个 目 录 里 时 并 不 记 录 它 的 内 容 取 而 代 之 的 是,Git 将 它 记 录 成 来 自 那 个 仓 库 的 一 个 特 殊 的 提 交 当 你 在 那 个 子 目 录 里 修 改 并 提 交 时, 子 项 目 会 通 知 那 里 的 HEAD 已 经 发 生 变 更 并 记 录 你 当 前 正 在 工 作 的 那 个 提 交 ; 通 过 那 样 的 方 法, 当 其 他 人 克 隆 此 项 目, 他 们 可 以 重 新 创 建 一 致 的 环 境 这 是 关 于 子 模 块 的 重 要 一 点 : 你 记 录 他 们 当 前 确 切 所 处 的 提 交 你 不 能 记 录 一 个 子 模 块 的 master 或 者 其 他 的 符 号 引 用 当 你 提 交 时, 会 看 到 类 似 下 面 的 : $ git commit -m 'first commit with submodule rack' [master ] first commit with submodule rack 2 files changed, 4 insertions(+), 0 deletions(-) create mode gitmodules create mode rack 注 意 rack 条 目 的 模 式 这 在 Git 中 是 一 个 特 殊 模 式, 基 本 意 思 是 你 将 一 个 提 交 记 录 为 一 个 目 录 项 而 不 是 子 目 录 或 者 文 件 你 可 以 将 rack 目 录 当 作 一 个 独 立 的 项 目, 保 持 一 个 指 向 子 目 录 的 最 新 提 交 的 指 针 然 后 反 复 地 更 新 上 层 项 目 所 有 的 Git 命 令 都 在 两 个 子 目 录 里 独 立 工 作 : $ git log -1 commit a aad6331e620cd bb Author: Scott Chacon <schacon@gmail.com> Date: Thu Apr 9 09:03: first commit with submodule rack $ cd rack/ $ git log -1 commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 Author: Christian Neukirchen <chneukirchen@gmail.com> Date: Wed Mar 25 14:49:

196 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 Document version change 克 隆 一 个 带 子 模 块 的 项 目 这 里 你 将 克 隆 一 个 带 子 模 块 的 项 目 当 你 接 收 到 这 样 一 个 项 目, 你 将 得 到 了 包 含 子 项 目 的 目 录, 但 里 面 没 有 文 件 : $ git clone git://github.com/schacon/myproject.git Initialized empty Git repository in /opt/myproject/.git/ remote: Counting objects: 6, done. remote: Compressing objects: 100% (4/4), done. remote: Total 6 (delta 0), reused 0 (delta 0) Receiving objects: 100% (6/6), done. $ cd myproject $ ls -l total 8 -rw-r-r-- 1 schacon admin 3 Apr 9 09:11 README drwxr-xr-x 2 schacon admin 68 Apr 9 09:11 rack $ ls rack/ $ rack 目 录 存 在 了, 但 是 是 空 的 你 必 须 运 行 两 个 命 令 :git submodule init 来 初 始 化 你 的 本 地 配 置 文 件,git submodule update 来 从 那 个 项 目 拉 取 所 有 数 据 并 检 出 你 上 层 项 目 里 所 列 的 合 适 的 提 交 : $ git submodule init Submodule 'rack' (git://github.com/chneukirchen/rack.git) registered for path 'rack' $ git submodule update Initialized empty Git repository in /opt/myproject/rack/.git/ remote: Counting objects: 3181, done. remote: Compressing objects: 100% (1534/1534), done. remote: Total 3181 (delta 1951), reused 2623 (delta 1603) Receiving objects: 100% (3181/3181), KiB 173 KiB/s, done. Resolving deltas: 100% (1951/1951), done. Submodule path 'rack': checked out '08d709f78b8c5b0fbeb7821e37fa53e69afcf433' 186

197 Sco Chacon Pro Git 中 文 版 6.6 节 子 模 块 现 在 你 的 rack 子 目 录 就 处 于 你 先 前 提 交 的 确 切 状 态 了 如 果 另 外 一 个 开 发 者 变 更 了 rack 的 代 码 并 提 交, 你 拉 取 那 个 引 用 然 后 归 并 之, 将 得 到 稍 有 点 怪 异 的 东 西 : $ git merge origin/master Updating a3eee Fast forward rack files changed, 1 insertions(+), 1 deletions(-) [master*]$ git status # On branch master # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: rack # 你 归 并 来 的 仅 仅 上 是 一 个 指 向 你 的 子 模 块 的 指 针 ; 但 是 它 并 不 更 新 你 子 模 块 目 录 里 的 代 码, 所 以 看 起 来 你 的 工 作 目 录 处 于 一 个 临 时 状 态 : $ git diff diff --git a/rack b/rack index 6c5e70b..08d709f a/rack Subproject commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0 +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 事 情 就 是 这 样, 因 为 你 所 拥 有 的 指 向 子 模 块 的 指 针 和 子 模 块 目 录 的 真 实 状 态 并 不 匹 配 为 了 修 复 这 一 点, 你 必 须 再 次 运 行 git submodule update: $ git submodule update remote: Counting objects: 5, done. remote: Compressing objects: 100% (3/3), done. remote: Total 3 (delta 1), reused 2 (delta 0) 187

198 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 Unpacking objects: 100% (3/3), done. From git@github.com:schacon/rack 08d709f..6c5e70b master -> origin/master Submodule path 'rack': checked out '6c5e70b984a60b3cecd395edd5b48a7575bf58e0' 每 次 你 从 主 项 目 中 拉 取 一 个 子 模 块 的 变 更 都 必 须 这 样 做 看 起 来 很 怪 但 是 管 用 一 个 常 见 问 题 是 当 开 发 者 对 子 模 块 做 了 一 个 本 地 的 变 更 但 是 并 没 有 推 送 到 公 共 服 务 器 然 后 他 们 提 交 了 一 个 指 向 那 个 非 公 开 状 态 的 指 针 然 后 推 送 上 层 项 目 当 其 他 开 发 者 试 图 运 行 git submodule update, 那 个 子 模 块 系 统 会 找 不 到 所 引 用 的 提 交, 因 为 它 只 存 在 于 第 一 个 开 发 者 的 系 统 中 如 果 发 生 那 种 情 况, 你 会 看 到 类 似 这 样 的 错 误 : $ git submodule update fatal: reference isn t a tree: 6c5e70b984a60b3cecd395edd5b48a7575bf58e0 Unable to checkout '6c5e70b984a60b3cecd395edd5ba7575bf58e0' in submodule path 'rack' 你 不 得 不 去 查 看 谁 最 后 变 更 了 子 模 块 $ git log -1 rack commit 85a3eee996800fcfa91e dd4172bf76678 Author: Scott Chacon <schacon@gmail.com> Date: Thu Apr 9 09:19: added a submodule reference I will never make public. hahahahaha! 然 后, 你 给 那 个 家 伙 发 电 子 邮 件 说 他 一 通 上 层 项 目 有 时 候, 开 发 者 想 按 照 他 们 的 分 组 获 取 一 个 大 项 目 的 子 目 录 的 子 集 如 果 你 是 从 CVS 或 者 Subversion 迁 移 过 来 的 话 这 个 很 常 见, 在 那 些 系 统 中 你 已 经 定 义 了 一 个 模 块 或 者 子 目 录 的 集 合, 而 你 想 延 续 这 种 类 型 的 工 作 流 程 在 Git 中 实 现 这 个 的 一 个 好 办 法 是 你 将 每 一 个 子 目 录 都 做 成 独 立 的 Git 仓 库, 然 后 创 建 一 个 上 层 项 目 的 Git 仓 库 包 含 多 个 子 模 块 这 个 办 法 的 一 个 优 势 是 你 可 以 在 上 层 项 目 中 通 过 标 签 和 分 支 更 为 明 确 地 定 义 项 目 之 间 的 关 系 188

199 Sco Chacon Pro Git 中 文 版 6.6 节 子 模 块 子 模 块 的 问 题 使 用 子 模 块 并 非 没 有 任 何 缺 点 首 先, 你 在 子 模 块 目 录 中 工 作 时 必 须 相 对 小 心 当 你 运 行 git submodule update, 它 会 检 出 项 目 的 指 定 版 本, 但 是 不 在 分 支 内 这 叫 做 获 得 一 个 分 离 的 头 这 意 味 着 HEAD 文 件 直 接 指 向 一 次 提 交, 而 不 是 一 个 符 号 引 用 问 题 在 于 你 通 常 并 不 想 在 一 个 分 离 的 头 的 环 境 下 工 作, 因 为 太 容 易 丢 失 变 更 了 如 果 你 先 执 行 了 一 次 submodule update, 然 后 在 那 个 子 模 块 目 录 里 不 创 建 分 支 就 进 行 提 交, 然 后 再 次 从 上 层 项 目 里 运 行 git submodule update 同 时 不 进 行 提 交,Git 会 毫 无 提 示 地 覆 盖 你 的 变 更 技 术 上 讲 你 不 会 丢 失 工 作, 但 是 你 将 失 去 指 向 它 的 分 支, 因 此 会 很 难 取 到 为 了 避 免 这 个 问 题, 当 你 在 子 模 块 目 录 里 工 作 时 应 使 用 git checkout -b work 创 建 一 个 分 支 当 你 再 次 在 子 模 块 里 更 新 的 时 候, 它 仍 然 会 覆 盖 你 的 工 作, 但 是 至 少 你 拥 有 一 个 可 以 回 溯 的 指 针 切 换 带 有 子 模 块 的 分 支 同 样 也 很 有 技 巧 如 果 你 创 建 一 个 新 的 分 支, 增 加 了 一 个 子 模 块, 然 后 切 换 回 不 带 该 子 模 块 的 分 支, 你 仍 然 会 拥 有 一 个 未 被 追 踪 的 子 模 块 的 目 录 $ git checkout -b rack Switched to a new branch "rack" $ git submodule add git@github.com:schacon/rack.git rack Initialized empty Git repository in /opt/myproj/rack/.git/... Receiving objects: 100% (3184/3184), KiB 34 KiB/s, done. Resolving deltas: 100% (1952/1952), done. $ git commit -am 'added rack submodule' [rack cc49a69] added rack submodule 2 files changed, 4 insertions(+), 0 deletions(-) create mode gitmodules create mode rack $ git checkout master Switched to branch "master" $ git status # On branch master # Untracked files: # (use "git add <file>..." to include in what will be committed) # # rack/ 你 将 不 得 不 将 它 移 走 或 者 删 除, 这 样 的 话 当 你 切 换 回 去 的 时 候 必 须 重 新 克 隆 它 你 可 能 会 丢 失 你 未 推 送 的 本 地 的 变 更 或 分 支 189

200 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 最 后 一 个 需 要 引 起 注 意 的 是 关 于 从 子 目 录 切 换 到 子 模 块 的 如 果 你 已 经 跟 踪 了 你 项 目 中 的 一 些 文 件 但 是 想 把 它 们 移 到 子 模 块 去, 你 必 须 非 常 小 心, 否 则 Git 会 生 你 的 气 假 设 你 的 项 目 中 有 一 个 子 目 录 里 放 了 rack 的 文 件, 然 后 你 想 将 它 转 换 为 子 模 块 如 果 你 删 除 子 目 录 然 后 运 行 submodule add,git 会 向 你 大 吼 : $ rm -Rf rack/ $ git submodule add git@github.com:schacon/rack.git rack 'rack' already exists in the index 你 必 须 先 将 rack 目 录 撤 回 然 后 你 才 能 加 入 子 模 块 : $ git rm -r rack $ git submodule add git@github.com:schacon/rack.git rack Initialized empty Git repository in /opt/testsub/rack/.git/ remote: Counting objects: 3184, done. remote: Compressing objects: 100% (1465/1465), done. remote: Total 3184 (delta 1952), reused 2770 (delta 1675) Receiving objects: 100% (3184/3184), KiB 88 KiB/s, done. Resolving deltas: 100% (1952/1952), done. 现 在 假 设 你 在 一 个 分 支 里 那 样 做 了 如 果 你 尝 试 切 换 回 一 个 仍 然 在 目 录 里 保 留 那 些 文 件 而 不 是 子 模 块 的 分 支 时 你 会 得 到 下 面 的 错 误 : $ git checkout master error: Untracked working tree file 'rack/authors' would be overwritten by merge. 你 必 须 先 移 除 rack 子 模 块 的 目 录 才 能 切 换 到 不 包 含 它 的 分 支 : $ mv rack /tmp/ $ git checkout master Switched to branch "master" $ ls README rack 然 后, 当 你 切 换 回 来, 你 会 得 到 一 个 空 的 rack 目 录 你 可 以 运 行 git submodule update 重 新 克 隆, 也 可 以 将 /tmp/rack 目 录 重 新 移 回 空 目 录 190

201 Sco Chacon Pro Git 中 文 版 6.7 节 子 树 合 并 6.7 子 树 合 并 现 在 你 已 经 看 到 了 子 模 块 系 统 的 麻 烦 之 处, 让 我 们 来 看 一 下 解 决 相 同 问 题 的 另 一 途 径 当 Git 归 并 时, 它 会 检 查 需 要 归 并 的 内 容 然 后 选 择 一 个 合 适 的 归 并 策 略 如 果 你 归 并 的 分 支 是 两 个,Git 使 用 一 个 递 归 策 略 如 果 你 归 并 的 分 支 超 过 两 个,Git 采 用 章 鱼 策 略 这 些 策 略 是 自 动 选 择 的, 因 为 递 归 策 略 可 以 处 理 复 杂 的 三 路 归 并 情 况 比 如 多 于 一 个 共 同 祖 先 的 但 是 它 只 能 处 理 两 个 分 支 的 归 并 章 鱼 归 并 可 以 处 理 多 个 分 支 但 是 但 必 须 更 加 小 心 以 避 免 冲 突 带 来 的 麻 烦, 因 此 它 被 选 中 作 为 归 并 两 个 以 上 分 支 的 默 认 策 略 实 际 上, 你 也 可 以 选 择 其 他 策 略 其 中 的 一 个 就 是 子 树 归 并, 你 可 以 用 它 来 处 理 子 项 目 问 题 这 里 你 会 看 到 如 何 换 用 子 树 归 并 的 方 法 来 实 现 前 一 节 里 所 做 的 rack 的 嵌 入 子 树 归 并 的 思 想 是 你 拥 有 两 个 工 程, 其 中 一 个 项 目 映 射 到 另 外 一 个 项 目 的 子 目 录 中, 反 过 来 也 一 样 当 你 指 定 一 个 子 树 归 并,Git 可 以 聪 明 地 探 知 其 中 一 个 是 另 外 一 个 的 子 树 从 而 实 现 正 确 的 归 并 这 相 当 神 奇 首 先 你 将 Rack 应 用 加 入 到 项 目 中 你 将 Rack 项 目 当 作 你 项 目 中 的 一 个 远 程 引 用, 然 后 将 它 检 出 到 它 自 身 的 分 支 : $ git remote add rack_remote git@github.com:schacon/rack.git $ git fetch rack_remote warning: no common commits remote: Counting objects: 3184, done. remote: Compressing objects: 100% (1465/1465), done. remote: Total 3184 (delta 1952), reused 2770 (delta 1675) Receiving objects: 100% (3184/3184), KiB 4 KiB/s, done. Resolving deltas: 100% (1952/1952), done. From git@github.com:schacon/rack * [new branch] build -> rack_remote/build * [new branch] master -> rack_remote/master * [new branch] rack-0.4 -> rack_remote/rack-0.4 * [new branch] rack-0.9 -> rack_remote/rack-0.9 $ git checkout -b rack_branch rack_remote/master Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master. Switched to a new branch "rack_branch" 现 在 在 你 的 rack_branch 分 支 中 就 有 了 Rack 项 目 的 根 目 录, 而 你 自 己 的 项 目 在 master 分 支 中 如 果 你 先 检 出 其 中 一 个 然 后 另 外 一 个, 你 会 看 到 它 们 有 不 同 的 项 目 根 目 录 : 191

202 第 6 章 Git 工 具 Sco Chacon Pro Git 中 文 版 $ ls AUTHORS KNOWN-ISSUES Rakefile contrib lib COPYING README bin example test $ git checkout master Switched to branch "master" $ ls README 要 将 Rack 项 目 当 作 子 目 录 拉 取 到 你 的 master 项 目 中 你 可 以 在 Git 中 用 git read-tree 来 实 现 你 会 在 第 9 章 学 到 更 多 与 read-tree 和 它 的 朋 友 相 关 的 东 西, 当 前 你 会 知 道 它 读 取 一 个 分 支 的 根 目 录 树 到 当 前 的 暂 存 区 和 工 作 目 录 你 只 要 切 换 回 你 的 master 分 支, 然 后 拉 取 rack_branch 到 你 主 项 目 的 master 分 支 的 rack 子 目 录 : $ git read-tree --prefix=rack/ -u rack_branch 当 你 提 交 的 时 候, 看 起 来 就 像 你 在 那 个 子 目 录 下 拥 有 Rack 的 文 件 就 像 你 从 一 个 tarball 里 拷 贝 的 一 样 有 意 思 的 是 你 可 以 比 较 容 易 地 归 并 其 中 一 个 分 支 的 变 更 到 另 外 一 个 因 此, 如 果 Rack 项 目 更 新 了, 你 可 以 通 过 切 换 到 那 个 分 支 并 执 行 拉 取 来 获 得 上 游 的 变 更 : $ git checkout rack_branch $ git pull 然 后, 你 可 以 将 那 些 变 更 归 并 回 你 的 master 分 支 你 可 以 使 用 git merge -s subtree, 它 会 工 作 的 很 好 ; 但 是 Git 同 时 会 把 历 史 归 并 到 一 起, 这 可 能 不 是 你 想 要 的 为 了 拉 取 变 更 并 预 置 提 交 说 明, 需 要 在 -s subtree 策 略 选 项 的 同 时 使 用 --squash 和 --no-commit 选 项 $ git checkout master $ git merge --squash -s subtree --no-commit rack_branch Squash commit -- not updating HEAD Automatic merge went well; stopped before committing as requested 192

203 Sco Chacon Pro Git 中 文 版 6.8 节 总 结 所 有 Rack 项 目 的 变 更 都 被 归 并 可 以 进 行 本 地 提 交 你 也 可 以 做 相 反 的 事 情 在 你 主 分 支 的 rack 目 录 里 进 行 变 更 然 后 归 并 回 rack_branch 分 支, 然 后 将 它 们 提 交 给 维 护 者 或 者 推 送 到 上 游 为 了 得 到 rack 子 目 录 和 你 rack_branch 分 支 的 区 别 以 决 定 你 是 否 需 要 归 并 它 们 你 不 能 使 用 一 般 的 diff 命 令 而 是 对 你 想 比 较 的 分 支 运 行 git diff-tree: $ git diff-tree -p rack_branch 以 运 行 或 者, 为 了 比 较 你 的 rack 子 目 录 和 服 务 器 上 你 拉 取 时 的 master 分 支, 你 可 $ git diff-tree -p rack_remote/master 6.8 总 结 你 已 经 看 到 了 很 多 高 级 的 工 具, 允 许 你 更 加 精 确 地 操 控 你 的 提 交 和 暂 存 区 当 你 碰 到 问 题 时, 你 应 该 可 以 很 容 易 找 出 是 哪 个 分 支 什 么 时 候 由 谁 引 入 了 它 们 如 果 你 想 在 项 目 中 使 用 子 项 目, 你 也 已 经 学 会 了 一 些 方 法 来 满 足 这 些 需 求 到 此, 你 应 该 能 够 完 成 日 常 里 你 需 要 用 命 令 行 在 Git 下 做 的 大 部 分 事 情, 并 且 感 到 比 较 顺 手 193

204

205 第 7 章 自 定 义 Git 到 目 前 为 止, 我 阐 述 了 Git 基 本 的 运 作 机 制 和 使 用 方 式, 介 绍 了 Git 提 供 的 许 多 工 具 来 帮 助 你 简 单 且 有 效 地 使 用 它 在 本 章, 我 将 会 介 绍 Git 的 一 些 重 要 的 配 置 方 法 和 钩 子 机 制 以 满 足 自 定 义 的 要 求 通 过 这 些 工 具, 它 会 和 你 和 公 司 或 团 队 配 合 得 天 衣 无 缝 7.1 配 置 Git 箱 地 址 : 如 第 一 章 所 言, 用 git config 配 置 Git, 要 做 的 第 一 件 事 就 是 设 置 名 字 和 邮 $ git config --global user.name "John Doe" $ git config --global user. johndoe@example.com 从 现 在 开 始, 你 会 了 解 到 一 些 类 似 以 上 但 更 为 有 趣 的 设 置 选 项 来 自 定 义 Git 先 过 一 遍 第 一 章 中 提 到 的 Git 配 置 细 节 Git 使 用 一 系 列 的 配 置 文 件 来 存 储 你 定 义 的 偏 好, 它 首 先 会 查 找 /etc/gitconfig 文 件, 该 文 件 含 有 对 系 统 上 所 有 用 户 及 他 们 所 拥 有 的 仓 库 都 生 效 的 配 置 值 ( 译 注 :gitconfig 是 全 局 配 置 文 件 ), 如 果 传 递 --system 选 项 给 git config 命 令,Git 会 读 写 这 个 文 件 接 下 来 Git 会 查 找 每 个 用 户 的 ~/.gitconfig 文 件, 你 能 传 递 --global 选 项 让 Git 读 写 该 文 件 最 后 Git 会 查 找 由 用 户 定 义 的 各 个 库 中 Git 目 录 下 的 配 置 文 件 (.git/config), 该 文 件 中 的 值 只 对 属 主 库 有 效 以 上 阐 述 的 三 层 配 置 从 一 般 到 特 殊 层 层 推 进, 如 果 定 义 的 值 有 冲 突, 以 后 面 层 中 定 义 的 为 准, 例 如 : 在.git/config 和 /etc/gitconfig 的 较 量 中,.git/config 取 得 了 胜 利 虽 然 你 也 可 以 直 接 手 动 编 辑 这 些 配 置 文 件, 但 是 运 行 git config 命 令 将 会 来 得 简 单 些 195

206 第 7 章 自 定 义 Git Sco Chacon Pro Git 中 文 版 客 户 端 基 本 配 置 Git 能 够 识 别 的 配 置 项 被 分 为 了 两 大 类 : 客 户 端 和 服 务 器 端, 其 中 大 部 分 基 于 你 个 人 工 作 偏 好, 属 于 客 户 端 配 置 尽 管 有 数 不 尽 的 选 项, 但 我 只 阐 述 其 中 经 常 使 用 或 者 会 对 你 的 工 作 流 产 生 巨 大 影 响 的 选 项, 如 果 你 想 观 察 你 当 前 的 Git 能 识 别 的 选 项 列 表, 请 运 行 $ git config --help git config 的 手 册 页 ( 译 注 : 以 man 命 令 的 显 示 方 式 ) 非 常 细 致 地 罗 列 了 所 有 可 用 的 配 置 项 core.editor Git 默 认 会 调 用 你 的 环 境 变 量 editor 定 义 的 值 作 为 文 本 编 辑 器, 如 果 没 有 定 义 的 话, 会 调 用 Vi 来 创 建 和 编 辑 提 交 以 及 标 签 信 息, 你 可 以 使 用 core.editor 改 变 默 认 编 辑 器 : $ git config --global core.editor emacs 现 在 无 论 你 的 环 境 变 量 editor 被 定 义 成 什 么,Git 都 会 调 用 Emacs 编 辑 信 息 commit.template 如 果 把 此 项 指 定 为 你 系 统 上 的 一 个 文 件, 当 你 提 交 的 时 候,Git 会 默 认 使 用 该 文 件 定 义 的 内 容 例 如 : 你 创 建 了 一 个 模 板 文 件 $HOME/.gitmessage.txt, 它 看 起 来 像 这 样 : subject line what happened [ticket: X] 设 置 commit.template, 当 运 行 git commit 时,Git 会 在 你 的 编 辑 器 中 显 示 以 上 的 内 容, 设 置 commit.template 如 下 : 196

207 Sco Chacon Pro Git 中 文 版 7.1 节 配 置 Git $ git config --global commit.template $HOME/.gitmessage.txt $ git commit 然 后 当 你 提 交 时, 在 编 辑 器 中 显 示 的 提 交 信 息 如 下 : subject line what happened [ticket: X] # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: lib/test.rb # ~ ~ ".git/commit_editmsg" 14L, 297C 如 果 你 有 特 定 的 策 略 要 运 用 在 提 交 信 息 上, 在 系 统 上 创 建 一 个 模 板 文 件, 设 置 Git 默 认 使 用 它, 这 样 当 提 交 时, 你 的 策 略 每 次 都 会 被 运 用 core.pager core.pager 指 定 Git 运 行 诸 如 log diff 等 所 使 用 的 分 页 器, 你 能 设 置 成 用 more 或 者 任 何 你 喜 欢 的 分 页 器 ( 默 认 用 的 是 less), 当 然 你 也 可 以 什 么 都 不 用, 设 置 空 字 符 串 : $ git config --global core.pager '' 这 样 不 管 命 令 的 输 出 量 多 少, 都 会 在 一 页 显 示 所 有 内 容 197

208 第 7 章 自 定 义 Git Sco Chacon Pro Git 中 文 版 user.signingkey 如 果 你 要 创 建 经 签 署 的 含 附 注 的 标 签 ( 正 如 第 二 章 所 述 ), 那 么 把 你 的 GPG 签 署 密 钥 设 置 为 配 置 项 会 更 好, 设 置 密 钥 ID 如 下 : $ git config --global user.signingkey <gpg-key-id> 现 在 你 能 够 签 署 标 签, 从 而 不 必 每 次 运 行 git tag 命 令 时 定 义 密 钥 : $ git tag -s <tag-name> core.excludesfile 正 如 第 二 章 所 述, 你 能 在 项 目 库 的.gitignore 文 件 里 头 用 模 式 来 定 义 那 些 无 需 纳 入 Git 管 理 的 文 件, 这 样 它 们 不 会 出 现 在 未 跟 踪 列 表, 也 不 会 在 你 运 行 git add 后 被 暂 存 然 而, 如 果 你 想 用 项 目 库 之 外 的 文 件 来 定 义 那 些 需 被 忽 略 的 文 件 的 话, 用 core.excludesfile 通 知 Git 该 文 件 所 处 的 位 置, 文 件 内 容 和.gitignore 类 似 help.autocorrect 会 显 示 : 该 配 置 项 只 在 Git 及 以 上 版 本 有 效, 假 如 你 在 Git 1.6 中 错 打 了 一 条 命 令, $ git com git: 'com' is not a git-command. See 'git --help'. Did you mean this? commit 如 果 你 把 help.autocorrect 设 置 成 1( 译 注 : 启 动 自 动 修 正 ), 那 么 在 只 有 一 个 命 令 被 模 糊 匹 配 到 的 情 况 下,Git 会 自 动 运 行 该 命 令 Git 中 的 着 色 Git 能 够 为 输 出 到 你 终 端 的 内 容 着 色, 以 便 你 可 以 凭 直 观 进 行 快 速 简 单 地 分 析, 有 许 多 选 项 能 供 你 使 用 以 符 合 你 的 偏 好 198

209 Sco Chacon Pro Git 中 文 版 7.1 节 配 置 Git color.ui Git 会 按 照 你 需 要 自 动 为 大 部 分 的 输 出 加 上 颜 色, 你 能 明 确 地 规 定 哪 些 需 要 着 色 以 及 怎 样 着 色, 设 置 color.ui 为 true 来 打 开 所 有 的 默 认 终 端 着 色 $ git config --global color.ui true 设 置 好 以 后, 当 输 出 到 终 端 时,Git 会 为 之 加 上 颜 色 其 他 的 参 数 还 有 false 和 always,false 意 味 着 不 为 输 出 着 色, 而 always 则 表 明 在 任 何 情 况 下 都 要 着 色, 即 使 Git 命 令 被 重 定 向 到 文 件 或 管 道 Git 版 本 引 进 了 此 项 配 置, 如 果 你 拥 有 的 版 本 更 老, 你 必 须 对 颜 色 有 关 选 项 各 自 进 行 详 细 地 设 置 你 会 很 少 用 到 color.ui = always, 在 大 多 数 情 况 下, 如 果 你 想 在 被 重 定 向 的 输 出 中 插 入 颜 色 码, 你 能 传 递 --color 标 志 给 Git 命 令 来 迫 使 它 这 么 做,color.ui = true 应 该 是 你 的 首 选 color.* 想 要 具 体 到 哪 些 命 令 输 出 需 要 被 着 色 以 及 怎 样 着 色 或 者 Git 的 版 本 很 老, 你 就 要 用 到 和 具 体 命 令 有 关 的 颜 色 配 置 选 项, 它 们 都 能 被 置 为 true false 或 always: color.branch color.diff color.interactive color.status 除 此 之 外, 以 上 每 个 选 项 都 有 子 选 项, 可 以 被 用 来 覆 盖 其 父 设 置, 以 达 到 为 输 出 的 各 个 部 分 着 色 的 目 的 例 如, 让 diff 输 出 的 改 变 信 息 以 粗 体 蓝 色 前 景 和 黑 色 背 景 的 形 式 显 示 : $ git config --global color.diff.meta "blue black bold" 你 能 设 置 的 颜 色 值 如 :normal black red green yellow blue magenta cyan white, 正 如 以 上 例 子 设 置 的 粗 体 属 性, 想 要 设 置 字 体 属 性 的 话, 可 以 选 择 如 : bold dim ul blink reverse 如 果 你 想 配 置 子 选 项 的 话, 可 以 参 考 git config 帮 助 页 199

210 第 7 章 自 定 义 Git Sco Chacon Pro Git 中 文 版 外 部 的 合 并 与 比 较 工 具 虽 然 Git 自 己 实 现 了 diff, 而 且 到 目 前 为 止 你 一 直 在 使 用 它, 但 你 能 够 用 一 个 外 部 的 工 具 替 代 它, 除 此 以 外, 你 还 能 用 一 个 图 形 化 的 工 具 来 合 并 和 解 决 冲 突 从 而 不 必 自 己 手 动 解 决 有 一 个 不 错 且 免 费 的 工 具 可 以 被 用 来 做 比 较 和 合 并 工 作, 它 就 是 P4Merge( 译 注 :Perforce 图 形 化 合 并 工 具 ), 我 会 展 示 它 的 安 装 过 程 P4Merge 可 以 在 所 有 主 流 平 台 上 运 行, 现 在 开 始 大 胆 尝 试 吧 对 于 向 你 展 示 的 例 子, 在 Mac 和 Linux 系 统 上, 我 会 使 用 路 径 名, 在 Windows 上,/usr/local/bin 应 该 被 改 为 你 环 境 中 的 可 执 行 路 径 下 载 P4Merge: 首 先 把 你 要 运 行 的 命 令 放 入 外 部 包 装 脚 本 中, 我 会 使 用 Mac 系 统 上 的 路 径 来 指 定 该 脚 本 的 位 置, 在 其 他 系 统 上, 它 应 该 被 放 置 在 二 进 制 文 件 p4merge 所 在 的 目 录 中 创 建 一 个 merge 包 装 脚 本, 名 字 叫 作 extmerge, 让 它 带 参 数 调 用 p4merge 二 进 制 文 件 : $ cat /usr/local/bin/extmerge #!/bin/sh /Applications/p4merge.app/Contents/MacOS/p4merge $* diff 包 装 脚 本 首 先 确 定 传 递 过 来 7 个 参 数, 随 后 把 其 中 2 个 传 递 给 merge 包 装 脚 本, 默 认 情 况 下,Git 传 递 以 下 参 数 给 diff: path old-file old-hex old-mode new-file new-hex new-mode 由 于 你 仅 仅 需 要 old-file 和 new-file 参 数, 用 diff 包 装 脚 本 来 传 递 它 们 吧 $ cat /usr/local/bin/extdiff #!/bin/sh [ $# -eq 7 ] && /usr/local/bin/extmerge "$2" "$5" 确 认 这 两 个 脚 本 是 可 执 行 的 : $ sudo chmod +x /usr/local/bin/extmerge $ sudo chmod +x /usr/local/bin/extdiff 200

211 Sco Chacon Pro Git 中 文 版 7.1 节 配 置 Git 现 在 来 配 置 使 用 你 自 定 义 的 比 较 和 合 并 工 具 吧 这 需 要 许 多 自 定 义 设 置 : merge.tool 通 知 Git 使 用 哪 个 合 并 工 具 ;mergetool.*.cmd 规 定 命 令 运 行 的 方 式 ;mergetool.trustexitcode 会 通 知 Git 程 序 的 退 出 是 否 指 示 合 并 操 作 成 功 ; diff.external 通 知 Git 用 什 么 命 令 做 比 较 因 此, 你 能 运 行 以 下 4 条 配 置 命 令 : $ git config --global merge.tool extmerge $ git config --global mergetool.extmerge.cmd \ 'extmerge "$BASE" "$LOCAL" "$REMOTE" "$MERGED"' $ git config --global mergetool.trustexitcode false $ git config --global diff.external extdiff 或 者 直 接 编 辑 ~/.gitconfig 文 件 如 下 : [merge] tool = extmerge [mergetool "extmerge"] cmd = extmerge \"$BASE\" \"$LOCAL\" \"$REMOTE\" \"$MERGED\" trustexitcode = false [diff] external = extdiff 设 置 完 毕 后, 运 行 diff 命 令 : $ git diff 32d1776b1^ 32d1776b1 命 令 行 居 然 没 有 发 现 diff 命 令 的 输 出, 其 实,Git 调 用 了 刚 刚 设 置 的 P4Merge, 它 看 起 来 像 图 7-1 这 样 : 当 你 设 法 合 并 两 个 分 支, 结 果 却 有 冲 突 时, 运 行 git mergetool,git 会 调 用 P4Merge 让 你 通 过 图 形 界 面 来 解 决 冲 突 设 置 包 装 脚 本 的 好 处 是 你 能 简 单 地 改 变 diff 和 merge 工 具, 例 如 把 extdiff 和 extmerge 改 成 KDiff3, 要 做 的 仅 仅 是 编 辑 extmerge 脚 本 文 件 : $ cat /usr/local/bin/extmerge #!/bin/sh /Applications/kdiff3.app/Contents/MacOS/kdiff3 $* 201

212 第 7 章 自 定 义 Git Sco Chacon Pro Git 中 文 版 图 7.1: P4Merge. 现 在 Git 会 使 用 KDiff3 来 做 比 较 合 并 和 解 决 冲 突 Git 预 先 设 置 了 许 多 其 他 的 合 并 和 解 决 冲 突 的 工 具, 而 你 不 必 设 置 cmd 可 以 把 合 并 工 具 设 置 为 :kdiff3 opendiff tkdiff meld xxdiff emerge vimdiff gvimdiff 如 果 你 不 想 用 到 KDiff3 的 所 有 功 能, 只 是 想 用 它 来 合 并, 那 么 kdiff3 正 符 合 你 的 要 求, 运 行 : $ git config --global merge.tool kdiff3 如 果 运 行 了 以 上 命 令, 没 有 设 置 extmerge 和 extdiff 文 件,Git 会 用 KDiff3 做 合 并, 让 通 常 内 设 的 比 较 工 具 来 做 比 较 格 式 化 与 空 白 格 式 化 与 空 白 是 许 多 开 发 人 员 在 协 作 时, 特 别 是 在 跨 平 台 情 况 下, 遇 到 的 令 人 头 疼 的 细 小 问 题 由 于 编 辑 器 的 不 同 或 者 Windows 程 序 员 在 跨 平 台 项 目 中 的 文 件 行 尾 加 入 了 回 车 换 行 符, 一 些 细 微 的 空 格 变 化 会 不 经 意 地 进 入 大 家 合 作 的 工 作 或 提 交 的 补 丁 中 不 用 怕,Git 的 一 些 配 置 选 项 会 帮 助 你 解 决 这 些 问 题 core.autocrlf 假 如 你 正 在 Windows 上 写 程 序, 又 或 者 你 正 在 和 其 他 人 合 作, 他 们 在 Windows 上 编 程, 而 你 却 在 其 他 系 统 上, 在 这 些 情 况 下, 你 可 能 会 遇 到 行 尾 结 束 符 问 题 这 是 因 为 Windows 使 用 回 车 和 换 行 两 个 字 符 来 结 束 一 行, 而 Mac 和 Linux 只 使 用 换 行 一 个 字 符 虽 然 这 是 小 问 题, 但 它 会 极 大 地 扰 乱 跨 平 台 协 作 Git 可 以 在 你 提 交 时 自 动 地 把 行 结 束 符 CRLF 转 换 成 LF, 而 在 签 出 代 码 时 把 LF 转 换 成 CRLF 用 core.autocrlf 来 打 开 此 项 功 能, 如 果 是 在 Windows 系 统 上, 把 它 设 置 成 true, 这 样 当 签 出 代 码 时,LF 会 被 转 换 成 CRLF: 202

213 Sco Chacon Pro Git 中 文 版 7.1 节 配 置 Git $ git config --global core.autocrlf true Linux 或 Mac 系 统 使 用 LF 作 为 行 结 束 符, 因 此 你 不 想 Git 在 签 出 文 件 时 进 行 自 动 的 转 换 ; 当 一 个 以 CRLF 为 行 结 束 符 的 文 件 不 小 心 被 引 入 时 你 肯 定 想 进 行 修 正, 把 core.autocrlf 设 置 成 input 来 告 诉 Git 在 提 交 时 把 CRLF 转 换 成 LF, 签 出 时 不 转 换 : $ git config --global core.autocrlf input 这 样 会 在 Windows 系 统 上 的 签 出 文 件 中 保 留 CRLF, 会 在 Mac 和 Linux 系 统 上, 包 括 仓 库 中 保 留 LF 如 果 你 是 Windows 程 序 员, 且 正 在 开 发 仅 运 行 在 Windows 上 的 项 目, 可 以 设 置 false 取 消 此 功 能, 把 回 车 符 记 录 在 库 中 : $ git config --global core.autocrlf false core.whitespace Git 预 先 设 置 了 一 些 选 项 来 探 测 和 修 正 空 白 问 题, 其 4 种 主 要 选 项 中 的 2 个 默 认 被 打 开, 另 2 个 被 关 闭, 你 可 以 自 由 地 打 开 或 关 闭 它 们 默 认 被 打 开 的 2 个 选 项 是 trailing-space 和 space-before-tab,trailing-space 会 查 找 每 行 结 尾 的 空 格,space-before-tab 会 查 找 每 行 开 头 的 制 表 符 前 的 空 格 默 认 被 关 闭 的 2 个 选 项 是 indent-with-non-tab 和 cr-at-eol,indent-with-non-tab 会 查 找 8 个 以 上 空 格 ( 非 制 表 符 ) 开 头 的 行,cr-at-eol 让 Git 知 道 行 尾 回 车 符 是 合 法 的 设 置 core.whitespace, 按 照 你 的 意 图 来 打 开 或 关 闭 选 项, 选 项 以 逗 号 分 割 通 过 逗 号 分 割 的 链 中 去 掉 选 项 或 在 选 项 前 加 - 来 关 闭, 例 如, 如 果 你 想 要 打 开 除 了 cr-at-eol 之 外 的 所 有 选 项 : $ git config --global core.whitespace \ trailing-space,space-before-tab,indent-with-non-tab 当 你 运 行 git diff 命 令 且 为 输 出 着 色 时,Git 探 测 到 这 些 问 题, 因 此 你 也 许 在 提 交 前 能 修 复 它 们, 当 你 用 git apply 打 补 丁 时 同 样 也 会 从 中 受 益 如 果 正 准 备 运 用 的 补 丁 有 特 别 的 空 白 问 题, 你 可 以 让 Git 发 警 告 : 203

214 第 7 章 自 定 义 Git Sco Chacon Pro Git 中 文 版 $ git apply --whitespace=warn <patch> 或 者 让 Git 在 打 上 补 丁 前 自 动 修 正 此 问 题 : $ git apply --whitespace=fix <patch> 这 些 选 项 也 能 运 用 于 衍 合 如 果 提 交 了 有 空 白 问 题 的 文 件 但 还 没 推 送 到 上 流, 你 可 以 运 行 带 有 --whitespace=fix 选 项 的 rebase 来 让 Git 在 重 写 补 丁 时 自 动 修 正 它 们 服 务 器 端 配 置 Git 服 务 器 端 的 配 置 选 项 并 不 多, 但 仍 有 一 些 饶 有 生 趣 的 选 项 值 得 你 一 看 receive.fsckobjects Git 默 认 情 况 下 不 会 在 推 送 期 间 检 查 所 有 对 象 的 一 致 性 虽 然 会 确 认 每 个 对 象 的 有 效 性 以 及 是 否 仍 然 匹 配 SHA-1 检 验 和, 但 Git 不 会 在 每 次 推 送 时 都 检 查 一 致 性 对 于 Git 来 说, 库 或 推 送 的 文 件 越 大, 这 个 操 作 代 价 就 相 对 越 高, 每 次 推 送 会 消 耗 更 多 时 间, 如 果 想 在 每 次 推 送 时 Git 都 检 查 一 致 性, 设 置 receive.fsckobjects 为 true 来 强 迫 它 这 么 做 : $ git config --system receive.fsckobjects true 现 在 Git 会 在 每 次 推 送 生 效 前 检 查 库 的 完 整 性, 确 保 有 问 题 的 客 户 端 没 有 引 入 破 坏 性 的 数 据 receive.denynonfastforwards 如 果 对 已 经 被 推 送 的 提 交 历 史 做 衍 合, 继 而 再 推 送, 又 或 者 以 其 它 方 式 推 送 一 个 提 交 历 史 至 远 程 分 支, 且 该 提 交 历 史 没 在 这 个 远 程 分 支 中, 这 样 的 推 送 会 被 拒 绝 这 通 常 是 个 很 好 的 禁 止 策 略, 但 有 时 你 在 做 衍 合 并 确 定 要 更 新 远 程 分 支, 可 以 在 push 命 令 后 加 -f 标 志 来 强 制 更 新 要 禁 用 这 样 的 强 制 更 新 功 能, 可 以 设 置 receive.denynonfastforwards: 204

215 Sco Chacon Pro Git 中 文 版 7.2 节 Git 属 性 $ git config --system receive.denynonfastforwards true 稍 后 你 会 看 到, 用 服 务 器 端 的 接 收 钩 子 也 能 达 到 同 样 的 目 的 这 个 方 法 可 以 做 更 细 致 的 控 制, 例 如 : 禁 用 特 定 的 用 户 做 强 制 更 新 receive.denydeletes 规 避 denynonfastforwards 策 略 的 方 法 之 一 就 是 用 户 删 除 分 支, 然 后 推 回 新 的 引 用 在 更 新 的 Git 版 本 中 ( 从 版 本 开 始 ), 把 receive.denydeletes 设 置 为 true: $ git config --system receive.denydeletes true 这 样 会 在 推 送 过 程 中 阻 止 删 除 分 支 和 标 签 没 有 用 户 能 够 这 么 做 要 删 除 远 程 分 支, 必 须 从 服 务 器 手 动 删 除 引 用 文 件 通 过 用 户 访 问 控 制 列 表 也 能 这 么 做, 在 本 章 结 尾 将 会 介 绍 这 些 有 趣 的 方 式 7.2 Git 属 性 一 些 设 置 项 也 能 被 运 用 于 特 定 的 路 径 中, 这 样,Git 以 对 一 个 特 定 的 子 目 录 或 子 文 件 集 运 用 那 些 设 置 项 这 些 设 置 项 被 称 为 Git 属 性, 可 以 在 你 目 录 中 的.gitattributes 文 件 内 进 行 设 置 ( 通 常 是 你 项 目 的 根 目 录 ), 也 可 以 当 你 不 想 让 这 些 属 性 文 件 和 项 目 文 件 一 同 提 交 时, 在.git/info/attributes 进 行 设 置 使 用 属 性, 你 可 以 对 个 别 文 件 或 目 录 定 义 不 同 的 合 并 策 略, 让 Git 知 道 怎 样 比 较 非 文 本 文 件, 在 你 提 交 或 签 出 前 让 Git 过 滤 内 容 你 将 在 这 部 分 了 解 到 能 在 自 己 的 项 目 中 使 用 的 属 性, 以 及 一 些 实 例 二 进 制 文 件 你 可 以 用 Git 属 性 让 其 知 道 哪 些 是 二 进 制 文 件 ( 以 防 Git 没 有 识 别 出 来 ), 以 及 指 示 怎 样 处 理 这 些 文 件, 这 点 很 酷 例 如, 一 些 文 本 文 件 是 由 机 器 产 生 的, 而 且 无 法 比 较, 而 一 些 二 进 制 文 件 可 以 比 较 你 将 会 了 解 到 怎 样 让 Git 识 别 这 些 文 件 识 别 二 进 制 文 件 一 些 文 件 看 起 来 像 是 文 本 文 件, 但 其 实 是 作 为 二 进 制 数 据 被 对 待 例 如, 在 Mac 上 的 Xcode 项 目 含 有 一 个 以.pbxproj 结 尾 的 文 件, 它 是 由 记 录 设 置 项 的 IDE 写 到 磁 盘 的 JSON 数 据 集 ( 纯 文 本 javascript 数 据 类 型 ) 虽 然 技 术 上 看 它 是 由 ASCII 字 205

216 第 7 章 自 定 义 Git Sco Chacon Pro Git 中 文 版 符 组 成 的 文 本 文 件, 但 你 并 不 认 为 如 此, 因 为 它 确 实 是 一 个 轻 量 级 数 据 库 如 果 有 2 人 改 变 了 它, 你 通 常 无 法 合 并 和 比 较 内 容, 只 有 机 器 才 能 进 行 识 别 和 操 作, 于 是, 你 想 把 它 当 成 二 进 制 文 件 让 Git 把 所 有 pbxproj 文 件 当 成 二 进 制 文 件, 在.gitattributes 文 件 中 设 置 如 下 : *.pbxproj -crlf -diff 现 在,Git 会 尝 试 转 换 和 修 正 CRLF( 回 车 换 行 ) 问 题, 也 不 会 当 你 在 项 目 中 运 行 git show 或 git diff 时, 比 较 不 同 的 内 容 在 Git 1.6 及 之 后 的 版 本 中, 可 以 用 一 个 宏 代 替 -crlf -diff: *.pbxproj binary 比 较 二 进 制 文 件 在 Git 中, 你 能 利 用 Git 属 性 来 有 效 地 比 较 二 进 制 文 件 可 以 设 置 Git 把 二 进 制 数 据 转 换 成 文 本 格 式, 用 通 常 的 diff 来 比 较 We ll make use of the both described approaches to get usable diffs for some widely used binary formats. Side note: There are different kinds of binary formats with a text content, which are hard to find usable converter for. In such a case you could try to extract a text from your file with the strings program. Some of these files may use an UTF-16 encoding or other codepages and strings won t find anything useful in there. Your mileage may vary. However, strings is available on most Mac and Linux systems, so it may be a good first try to do this with many binary formats. MS Word files 这 个 特 性 很 酷, 而 且 鲜 为 人 知, 因 此 我 会 结 合 实 例 来 讲 解 首 先, 要 解 决 的 是 最 令 人 头 疼 的 问 题 : 对 Word 文 档 进 行 版 本 控 制 很 多 人 对 Word 文 档 又 恨 又 爱, 如 果 想 对 其 进 行 版 本 控 制, 你 可 以 把 文 件 加 入 到 Git 库 中, 每 次 修 改 后 提 交 即 可 但 这 样 做 没 有 一 点 实 际 意 义, 因 为 运 行 git diff 命 令 后, 你 只 能 得 到 如 下 的 结 果 : $ git diff diff --git a/chapter1.doc b/chapter1.doc index 88839c4..4afcb7c Binary files a/chapter1.doc and b/chapter1.doc differ 206

217 Sco Chacon Pro Git 中 文 版 7.2 节 Git 属 性 你 不 能 直 接 比 较 两 个 不 同 版 本 的 Word 文 件, 除 非 进 行 手 动 扫 描, 不 是 吗?Git 属 性 能 很 好 地 解 决 此 问 题, 把 下 面 的 行 加 到.gitattributes 文 件 : *.doc diff=word 当 你 要 看 比 较 结 果 时, 如 果 文 件 扩 展 名 是 doc,git 调 用 word 过 滤 器 什 么 是 word 过 滤 器 呢? 其 实 就 是 Git 使 用 strings 程 序, 把 Word 文 档 转 换 成 可 读 的 文 本 文 件, 之 后 再 进 行 比 较 : $ git config diff.word.textconv catdoc This command adds a section to your.git/config that looks like this: [diff "word"] textconv = catdoc 现 在 如 果 在 两 个 快 照 之 间 比 较 以.doc 结 尾 的 文 件,Git 对 这 些 文 件 运 用 word 过 滤 器, 在 比 较 前 把 Word 文 件 转 换 成 文 本 文 件 下 面 展 示 了 一 个 实 例, 我 把 此 书 的 第 一 章 纳 入 Git 管 理, 在 一 个 段 落 中 加 入 了 一 些 文 本 后 保 存, 之 后 运 行 git diff 命 令, 得 到 结 果 如 下 : $ git diff diff --git a/chapter1.doc b/chapter1.doc index c1c8a0a..b93c9e a/chapter1.doc ,7 and data size) Since its birth in 2005, Git has evolved and matured to be easy to use and yet retain these initial qualities. It s incredibly fast, it s very efficient with large projects, and it has an incredible branching -system for non-linear development. +system for non-linear development (See Chapter 3). 207

218 第 7 章 自 定 义 Git Sco Chacon Pro Git 中 文 版 Git 成 功 且 简 洁 地 显 示 出 我 增 加 的 文 本 Let s see if this works 虽 然 有 些 瑕 疵, 在 末 尾 显 示 了 一 些 随 机 的 内 容, 但 确 实 可 以 比 较 了 如 果 你 能 找 到 或 自 己 写 个 Word 到 纯 文 本 的 转 换 器 的 话, 效 果 可 能 会 更 好 strings 可 以 在 大 部 分 Mac 和 Linux 系 统 上 运 行, 所 以 它 是 处 理 二 进 制 格 式 的 第 一 选 择 OpenDocument Text files The same approach that we used for MS Word files (*.doc) can be used for OpenDocument Text files (*.odt) created by OpenOffice.org. Add the following line to your.gitattributes file: *.odt diff=odt Now set up the odt diff filter in.git/config: [diff "odt"] binary = true textconv = /usr/local/bin/odt-to-txt OpenDocument files are actually zip ped directories containing multiple files (the content in an XML format, stylesheets, images, etc.). We ll need to write a script to extract the content and return it as plain text. Create a file /usr/local/bin/odt-to-txt (you are free to put it into a different directory) with the following content: #! /usr/bin/env perl # Simplistic OpenDocument Text (.odt) to plain text converter. # Author: Philipp Kempgen if (! defined($argv[0])) { print STDERR "No filename given!\n"; print STDERR "Usage: $0 filename\n"; exit 1; } my $content = ''; open my $fh, '- ', 'unzip', '-qq', '-p', $ARGV[0], 'content.xml' or die $!; { local $/ = undef; # slurp mode 208

219 Sco Chacon Pro Git 中 文 版 7.2 节 Git 属 性 $content = <$fh>; } close $fh; $_ = $content; s/<text:span\b[^>]*>//g; # remove spans s/<text:h\b[^>]*>/\n\n***** /g; # headers s/<text:list-item\b[^>]*>\s*<text:p\b[^>]*>/\n -- /g; # list items s/<text:list\b[^>]*>/\n\n/g; # lists s/<text:p\b[^>]*>/\n /g; # paragraphs s/<[^>]+>//g; # remove all XML tags s/\n{2,}/\n\n/g; # remove multiple blank lines s/\a\n+//; # remove leading blank lines print "\n", $_, "\n\n"; And make it executable chmod +x /usr/local/bin/odt-to-txt Now git diff will be able to tell you what changed in.odt files. Image files 你 还 能 用 这 个 方 法 比 较 图 像 文 件 当 比 较 时, 对 JPEG 文 件 运 用 一 个 过 滤 器, 它 能 提 炼 出 EXIF 信 息 大 部 分 图 像 格 式 使 用 的 元 数 据 如 果 你 下 载 并 安 装 了 exiftool 程 序, 可 以 用 它 参 照 元 数 据 把 图 像 转 换 成 文 本 比 较 的 不 同 结 果 将 会 用 文 本 向 你 展 示 : $ echo '*.png diff=exif' >>.gitattributes $ git config diff.exif.textconv exiftool 如 果 在 项 目 中 替 换 了 一 个 图 像 文 件, 运 行 git diff 命 令 的 结 果 如 下 : diff --git a/image.png b/image.png index 88839c4..4afcb7c a/image.png ,12 209

220 第 7 章 自 定 义 Git Sco Chacon Pro Git 中 文 版 ExifTool Version Number : File Size : 70 kb -File Modification Date/Time : 2009:04:17 10:12:35-07:00 +File Size : 94 kb +File Modification Date/Time : 2009:04:21 07:02:43-07:00 File Type : PNG MIME Type : image/png -Image Width : Image Height : 889 +Image Width : Image Height : 827 Bit Depth : 8 Color Type : RGB with Alpha 你 会 发 现 文 件 的 尺 寸 大 小 发 生 了 改 变 关 键 字 扩 展 使 用 SVN 或 CVS 的 开 发 人 员 经 常 要 求 关 键 字 扩 展 在 Git 中, 你 无 法 在 一 个 文 件 被 提 交 后 修 改 它, 因 为 Git 会 先 对 该 文 件 计 算 校 验 和 然 而, 你 可 以 在 签 出 时 注 入 文 本, 在 提 交 前 删 除 它 Git 属 性 提 供 了 2 种 方 式 这 么 做 首 先, 你 能 够 把 blob 的 SHA-1 校 验 和 自 动 注 入 文 件 的 $Id$ 字 段 如 果 在 一 个 或 多 个 文 件 上 设 置 了 此 字 段, 当 下 次 你 签 出 分 支 的 时 候,Git 用 blob 的 SHA-1 值 替 换 那 个 字 段 注 意, 这 不 是 提 交 对 象 的 SHA 校 验 和, 而 是 blob 本 身 的 校 验 和 : $ echo '*.txt ident' >>.gitattributes $ echo '$Id$' > test.txt 下 次 签 出 文 件 时,Git 入 了 blob 的 SHA 值 : $ rm test.txt $ git checkout -- test.txt $ cat test.txt $Id: 42812b7653c7b88933f8a9d6cad0ca16714b9bb3 $ 210 然 而, 这 样 的 显 示 结 果 没 有 多 大 的 实 际 意 义 这 个 SHA 的 值 相 当 地 随 机, 无 法

221 Sco Chacon Pro Git 中 文 版 7.2 节 Git 属 性 区 分 日 期 的 前 后, 所 以, 如 果 你 在 CVS 或 Subversion 中 用 过 关 键 字 替 换, 一 定 会 包 含 一 个 日 期 值 因 此, 你 能 写 自 己 的 过 滤 器, 在 提 交 文 件 到 暂 存 区 或 签 出 文 件 时 替 换 关 键 字 有 2 种 过 滤 器, clean 和 smudge 在.gitattributes 文 件 中, 你 能 对 特 定 的 路 径 设 置 一 个 过 滤 器, 然 后 设 置 处 理 文 件 的 脚 本, 这 些 脚 本 会 在 文 件 签 出 前 ( smudge, 见 图 7-2) 和 提 交 到 暂 存 区 前 ( clean, 见 图 7-3) 被 调 用 这 些 过 滤 器 能 够 做 各 种 有 趣 的 事 图 7.2: 签 出 时, smudge 过 滤 器 被 触 发 图 7.3: 提 交 到 暂 存 区 时, clean 过 滤 器 被 触 发 这 里 举 一 个 简 单 的 例 子 : 在 暂 存 前, 用 indent( 缩 进 ) 程 序 过 滤 所 有 C 源 代 码 在.gitattributes 文 件 中 设 置 indent 过 滤 器 过 滤 *.c 文 件 : *.c filter=indent 然 后, 通 过 以 下 配 置, 让 Git 知 道 indent 过 滤 器 在 遇 到 smudge 和 clean 时 分 别 该 做 什 么 : 211

222 第 7 章 自 定 义 Git Sco Chacon Pro Git 中 文 版 $ git config --global filter.indent.clean indent $ git config --global filter.indent.smudge cat 于 是, 当 你 暂 存 *.c 文 件 时,indent 程 序 会 被 触 发, 在 把 它 们 签 出 之 前,cat 程 序 会 被 触 发 但 cat 程 序 在 这 里 没 什 么 实 际 作 用 这 样 的 组 合, 使 C 源 代 码 在 暂 存 前 被 indent 程 序 过 滤, 非 常 有 效 另 一 个 例 子 是 类 似 RCS 的 $Date$ 关 键 字 扩 展 为 了 演 示, 需 要 一 个 小 脚 本, 接 受 文 件 名 参 数, 得 到 项 目 的 最 新 提 交 日 期, 最 后 把 日 期 写 入 该 文 件 下 面 用 Ruby 脚 本 来 实 现 : #! /usr/bin/env ruby data = STDIN.read last_date = `git log --pretty=format:"%ad" -1` puts data.gsub('$date$', '$Date: ' + last_date.to_s + '$') 该 脚 本 从 git log 命 令 中 得 到 最 新 提 交 日 期, 找 到 文 件 中 的 所 有 $Date$ 字 符 串, 最 后 把 该 日 期 填 充 到 $Date$ 字 符 串 中 此 脚 本 很 简 单, 你 可 以 选 择 你 喜 欢 的 编 程 语 言 来 实 现 把 该 脚 本 命 名 为 expand_date, 放 到 正 确 的 路 径 中, 之 后 需 要 在 Git 中 设 置 一 个 过 滤 器 (dater), 让 它 在 签 出 文 件 时 调 用 expand_date, 在 暂 存 文 件 时 用 Perl 清 除 之 : $ git config filter.dater.smudge expand_date $ git config filter.dater.clean 'perl -pe "s/\\\$date[^\\\$]*\\\$/\\\$date\\\$/"' 这 个 Perl 小 程 序 会 删 除 $Date$ 字 符 串 里 多 余 的 字 符, 恢 复 $Date$ 原 貌 到 目 前 为 止, 你 的 过 滤 器 已 经 设 置 完 毕, 可 以 开 始 测 试 了 打 开 一 个 文 件, 在 文 件 中 输 入 $Date$ 关 键 字, 然 后 设 置 Git 属 性 : $ echo '# $Date$' > date_test.txt $ echo 'date*.txt filter=dater' >>.gitattributes 如 果 暂 存 该 文 件, 之 后 再 签 出, 你 会 发 现 关 键 字 被 替 换 了 : 212

223 Sco Chacon Pro Git 中 文 版 7.2 节 Git 属 性 $ git add date_test.txt.gitattributes $ git commit -m "Testing date expansion in Git" $ rm date_test.txt $ git checkout date_test.txt $ cat date_test.txt # $Date: Tue Apr 21 07:26: $ 虽 说 这 项 技 术 对 自 定 义 应 用 来 说 很 有 用, 但 还 是 要 小 心, 因 为.gitattributes 文 件 会 随 着 项 目 一 起 提 交, 而 过 滤 器 ( 例 如 :dater) 不 会, 所 以, 过 滤 器 不 会 在 所 有 地 方 都 生 效 当 你 在 设 计 这 些 过 滤 器 时 要 注 意, 即 使 它 们 无 法 正 常 工 作, 也 要 让 整 个 项 目 运 作 下 去 导 出 仓 库 Git 属 性 在 导 出 项 目 归 档 时 也 能 发 挥 作 用 export-ignore 当 产 生 一 个 归 档 时, 可 以 设 置 Git 不 导 出 某 些 文 件 和 目 录 如 果 你 不 想 在 归 档 中 包 含 一 个 子 目 录 或 文 件, 但 想 他 们 纳 入 项 目 的 版 本 管 理 中, 你 能 对 应 地 设 置 export-ignore 属 性 例 如, 在 test/ 子 目 录 中 有 一 些 测 试 文 件, 在 项 目 的 压 缩 包 中 包 含 他 们 是 没 有 意 义 的 因 此, 可 以 增 加 下 面 这 行 到 Git 属 性 文 件 中 : test/ export-ignore 现 在, 当 运 行 git archive 来 创 建 项 目 的 压 缩 包 时, 那 个 目 录 不 会 在 归 档 中 出 现 export-subst 还 能 对 归 档 做 一 些 简 单 的 关 键 字 替 换 在 第 2 章 中 已 经 可 以 看 到, 可 以 以 --pretty=format 形 式 的 简 码 在 任 何 文 件 中 放 入 $Format:$ 字 符 串 例 如, 如 果 想 在 项 目 中 包 含 一 个 叫 作 LAST_COMMIT 的 文 件, 当 运 行 git archive 时, 最 后 提 交 日 期 自 动 地 注 入 进 该 文 件, 可 以 这 样 设 置 : $ echo 'Last commit date: $Format:%cd$' > LAST_COMMIT $ echo "LAST_COMMIT export-subst" >>.gitattributes 213

224 第 7 章 自 定 义 Git Sco Chacon Pro Git 中 文 版 $ git add LAST_COMMIT.gitattributes $ git commit -am 'adding LAST_COMMIT file for archives' 运 行 git archive 后, 打 开 该 文 件, 会 发 现 其 内 容 如 下 : $ cat LAST_COMMIT Last commit date: $Format:Tue Apr 21 08:38: $ 合 并 策 略 通 过 Git 属 性, 还 能 对 项 目 中 的 特 定 文 件 使 用 不 同 的 合 并 策 略 一 个 非 常 有 用 的 选 项 就 是, 当 一 些 特 定 文 件 发 生 冲 突,Git 会 尝 试 合 并 他 们, 而 使 用 你 这 边 的 合 并 如 果 项 目 的 一 个 分 支 有 歧 义 或 比 较 特 别, 但 你 想 从 该 分 支 合 并, 而 且 需 要 忽 略 其 中 某 些 文 件, 这 样 的 合 并 策 略 是 有 用 的 例 如, 你 有 一 个 数 据 库 设 置 文 件 database.xml, 在 2 个 分 支 中 他 们 是 不 同 的, 你 想 合 并 一 个 分 支 到 另 一 个, 而 不 弄 乱 该 数 据 库 文 件, 可 以 设 置 属 性 如 下 : database.xml merge=ours 如 果 合 并 到 另 一 个 分 支,database.xml 文 件 不 会 有 合 并 冲 突, 显 示 如 下 : $ git merge topic Auto-merging database.xml Merge made by recursive. 这 样,database.xml 会 保 持 原 样 7.3 Git 挂 钩 和 其 他 版 本 控 制 系 统 一 样, 当 某 些 重 要 事 件 发 生 时,Git 以 调 用 自 定 义 脚 本 有 两 组 挂 钩 : 客 户 端 和 服 务 器 端 客 户 端 挂 钩 用 于 客 户 端 的 操 作, 如 提 交 和 合 并 服 务 器 端 挂 钩 用 于 Git 服 务 器 端 的 操 作, 如 接 收 被 推 送 的 提 交 你 可 以 随 意 地 使 用 这 些 挂 钩, 下 面 会 讲 解 其 中 一 些 214

225 Sco Chacon Pro Git 中 文 版 7.3 节 Git 挂 钩 安 装 一 个 挂 钩 挂 钩 都 被 存 储 在 Git 目 录 下 的 hooks 子 目 录 中, 即 大 部 分 项 目 中 的.git/hooks Git 默 认 会 放 置 一 些 脚 本 样 本 在 这 个 目 录 中, 除 了 可 以 作 为 挂 钩 使 用, 这 些 样 本 本 身 是 可 以 独 立 使 用 的 所 有 的 样 本 都 是 shell 脚 本, 其 中 一 些 还 包 含 了 Perl 的 脚 本, 不 过, 任 何 正 确 命 名 的 可 执 行 脚 本 都 可 以 正 常 使 用 可 以 用 Ruby 或 Python, 或 其 他 在 Git 1.6 版 本 之 后, 这 些 样 本 名 都 是 以.sample 结 尾, 因 此, 你 必 须 重 新 命 名 在 Git 1.6 版 本 之 前, 这 些 样 本 名 都 是 正 确 的, 但 这 些 样 本 不 是 可 执 行 文 件 把 一 个 正 确 命 名 且 可 执 行 的 文 件 放 入 Git 目 录 下 的 hooks 子 目 录 中, 可 以 激 活 该 挂 钩 脚 本, 因 此, 之 后 他 一 直 会 被 Git 调 用 随 后 会 讲 解 主 要 的 挂 钩 脚 本 客 户 端 挂 钩 有 许 多 客 户 端 挂 钩, 以 下 把 他 们 分 为 : 提 交 工 作 流 挂 钩 电 子 邮 件 工 作 流 挂 钩 及 其 他 客 户 端 挂 钩 提 交 工 作 流 挂 钩 有 4 个 挂 钩 被 用 来 处 理 提 交 的 过 程 pre-commit 挂 钩 在 键 入 提 交 信 息 前 运 行, 被 用 来 检 查 即 将 提 交 的 快 照, 例 如, 检 查 是 否 有 东 西 被 遗 漏, 确 认 测 试 是 否 运 行, 以 及 检 查 代 码 当 从 该 挂 钩 返 回 非 零 值 时,Git 放 弃 此 次 提 交, 但 可 以 用 git commit --no-verify 来 忽 略 该 挂 钩 可 以 被 用 来 检 查 代 码 错 误 ( 运 行 类 似 lint 的 程 序 ), 检 查 尾 部 空 白 ( 默 认 挂 钩 是 这 么 做 的 ), 检 查 新 方 法 ( 译 注 : 程 序 的 函 数 ) 的 说 明 prepare-commit-msg 挂 钩 在 提 交 信 息 编 辑 器 显 示 之 前, 默 认 信 息 被 创 建 之 后 运 行 因 此, 可 以 有 机 会 在 提 交 作 者 看 到 默 认 信 息 前 进 行 编 辑 该 挂 钩 接 收 一 些 选 项 : 拥 有 提 交 信 息 的 文 件 路 径, 提 交 类 型, 如 果 是 一 次 修 订 的 话, 提 交 的 SHA-1 校 验 和 该 挂 钩 对 通 常 的 提 交 来 说 不 是 很 有 用, 只 在 自 动 产 生 的 默 认 提 交 信 息 的 情 况 下 有 作 用, 如 提 交 信 息 模 板 合 并 压 缩 和 修 订 提 交 等 可 以 和 提 交 模 板 配 合 使 用, 以 编 程 的 方 式 插 入 信 息 commit-msg 挂 钩 接 收 一 个 参 数, 此 参 数 是 包 含 最 近 提 交 信 息 的 临 时 文 件 的 路 径 如 果 该 挂 钩 脚 本 以 非 零 退 出,Git 放 弃 提 交, 因 此, 可 以 用 来 在 提 交 通 过 前 验 证 项 目 状 态 或 提 交 信 息 本 章 上 一 小 节 已 经 展 示 了 使 用 该 挂 钩 核 对 提 交 信 息 是 否 符 合 特 定 的 模 式 post-commit 挂 钩 在 整 个 提 交 过 程 完 成 后 运 行, 他 不 会 接 收 任 何 参 数, 但 可 以 运 行 git log -1 HEAD 来 获 得 最 后 的 提 交 信 息 总 之, 该 挂 钩 是 作 为 通 知 之 类 使 用 的 提 交 工 作 流 的 客 户 端 挂 钩 脚 本 可 以 在 任 何 工 作 流 中 使 用, 他 们 经 常 被 用 来 实 施 某 些 策 略, 但 值 得 注 意 的 是, 这 些 脚 本 在 clone 期 间 不 会 被 传 送 可 以 在 服 务 器 端 实 施 策 略 来 拒 绝 不 符 合 某 些 策 略 的 推 送, 但 这 完 全 取 决 于 开 发 者 在 客 户 端 使 用 这 些 脚 本 的 情 况 所 以, 这 些 脚 本 对 开 发 者 是 有 用 的, 由 他 们 自 己 设 置 和 维 护, 而 且 在 任 何 时 候 都 可 以 覆 盖 或 修 改 这 些 脚 本 215

226 第 7 章 自 定 义 Git Sco Chacon Pro Git 中 文 版 工 作 流 挂 钩 有 3 个 可 用 的 客 户 端 挂 钩 用 于 工 作 流 当 运 行 git am 命 令 时, 会 调 用 他 们, 因 此, 如 果 你 没 有 在 工 作 流 中 用 到 此 命 令, 可 以 跳 过 本 节 如 果 你 通 过 接 收 由 git format-patch 产 生 的 补 丁, 这 些 挂 钩 也 许 对 你 有 用 首 先 运 行 的 是 applypatch-msg 挂 钩, 他 接 收 一 个 参 数 : 包 含 被 建 议 提 交 信 息 的 临 时 文 件 名 如 果 该 脚 本 非 零 退 出,Git 放 弃 此 补 丁 可 以 使 用 这 个 脚 本 确 认 提 交 信 息 是 否 被 正 确 格 式 化, 或 让 脚 本 编 辑 信 息 以 达 到 标 准 化 下 一 个 在 git am 运 行 期 间 调 用 是 pre-applypatch 挂 钩 该 挂 钩 不 接 收 参 数, 在 补 丁 被 运 用 之 后 运 行, 因 此, 可 以 被 用 来 在 提 交 前 检 查 快 照 你 能 用 此 脚 本 运 行 测 试, 检 查 工 作 树 如 果 有 些 什 么 遗 漏, 或 测 试 没 通 过, 脚 本 会 以 非 零 退 出, 放 弃 此 次 git am 的 运 行, 补 丁 不 会 被 提 交 最 后 在 git am 运 行 期 间 调 用 的 是 post-applypatch 挂 钩 你 可 以 用 他 来 通 知 一 个 小 组 或 获 取 的 补 丁 的 作 者, 但 无 法 阻 止 打 补 丁 的 过 程 其 他 客 户 端 挂 钩 pre-rebase 挂 钩 在 衍 合 前 运 行, 脚 本 以 非 零 退 出 可 以 中 止 衍 合 的 过 程 你 可 以 使 用 这 个 挂 钩 来 禁 止 衍 合 已 经 推 送 的 提 交 对 象,Git pre-rebase 挂 钩 样 本 就 是 这 么 做 的 该 样 本 假 定 next 是 你 定 义 的 分 支 名, 因 此, 你 可 能 要 修 改 样 本, 把 next 改 成 你 定 义 过 且 稳 定 的 分 支 名 在 git checkout 成 功 运 行 后,post-checkout 挂 钩 会 被 调 用 他 可 以 用 来 为 你 的 项 目 环 境 设 置 合 适 的 工 作 目 录 例 如 : 放 入 大 的 二 进 制 文 件 自 动 产 生 的 文 档 或 其 他 一 切 你 不 想 纳 入 版 本 控 制 的 文 件 最 后, 在 merge 命 令 成 功 执 行 后,post-merge 挂 钩 会 被 调 用 他 可 以 用 来 在 Git 无 法 跟 踪 的 工 作 树 中 恢 复 数 据, 诸 如 权 限 数 据 该 挂 钩 同 样 能 够 验 证 在 Git 控 制 之 外 的 文 件 是 否 存 在, 因 此, 当 工 作 树 改 变 时, 你 想 这 些 文 件 可 以 被 复 制 服 务 器 端 挂 钩 除 了 客 户 端 挂 钩, 作 为 系 统 管 理 员, 你 还 可 以 使 用 两 个 服 务 器 端 的 挂 钩 对 项 目 实 施 各 种 类 型 的 策 略 这 些 挂 钩 脚 本 可 以 在 提 交 对 象 推 送 到 服 务 器 前 被 调 用, 也 可 以 在 推 送 到 服 务 器 后 被 调 用 推 送 到 服 务 器 前 调 用 的 挂 钩 可 以 在 任 何 时 候 以 非 零 退 出, 拒 绝 推 送, 返 回 错 误 消 息 给 客 户 端, 还 可 以 如 你 所 愿 设 置 足 够 复 杂 的 推 送 策 略 pre-receive 和 post-receive 处 理 来 自 客 户 端 的 推 送 (push) 操 作 时 最 先 执 行 的 脚 本 就 是 pre-receive 它 从 标 准 输 入 (stdin) 获 取 被 推 送 引 用 的 列 表 ; 如 果 它 退 出 时 的 返 回 值 不 是 0, 所 有 推 送 内 容 都 不 会 被 接 受 利 用 此 挂 钩 脚 本 可 以 实 现 类 似 保 证 最 新 的 索 引 中 不 包 含 非 fast-forward 类 型 的 这 类 效 果 ; 抑 或 检 查 执 行 推 送 操 作 的 用 户 拥 有 创 建, 删 除 或 者 推 送 的 权 限 或 者 他 是 否 对 将 要 修 改 的 每 一 个 文 件 都 有 访 问 权 限 216

227 Sco Chacon Pro Git 中 文 版 7.4 节 Git 强 制 策 略 实 例 post-receive 挂 钩 在 整 个 过 程 完 结 以 后 运 行, 可 以 用 来 更 新 其 他 系 统 服 务 或 者 通 知 用 户 它 接 受 与 pre-receive 相 同 的 标 准 输 入 数 据 应 用 实 例 包 括 给 某 邮 件 列 表 发 信, 通 知 实 时 整 合 数 据 的 服 务 器, 或 者 更 新 软 件 项 目 的 问 题 追 踪 系 统 甚 至 可 以 通 过 分 析 提 交 信 息 来 决 定 某 个 问 题 是 否 应 该 被 开 启, 修 改 或 者 关 闭 该 脚 本 无 法 组 织 推 送 进 程, 不 过 客 户 端 在 它 完 成 运 行 之 前 将 保 持 连 接 状 态 ; 所 以 在 用 它 作 一 些 消 耗 时 间 的 操 作 之 前 请 三 思 update update 脚 本 和 pre-receive 脚 本 十 分 类 似 不 同 之 处 在 于 它 会 为 推 送 者 更 新 的 每 一 个 分 支 运 行 一 次 假 如 推 送 者 同 时 向 多 个 分 支 推 送 内 容,pre-receive 只 运 行 一 次, 相 比 之 下 update 则 会 为 每 一 个 更 新 的 分 支 运 行 一 次 它 不 会 从 标 准 输 入 读 取 内 容, 而 是 接 受 三 个 参 数 : 索 引 的 名 字 ( 分 支 ), 推 送 前 索 引 指 向 的 内 容 的 SHA-1 值, 以 及 用 户 试 图 推 送 内 容 的 SHA-1 值 如 果 update 脚 本 以 退 出 时 返 回 非 零 值, 只 有 相 应 的 那 一 个 索 引 会 被 拒 绝 ; 其 余 的 依 然 会 得 到 更 新 7.4 Git 强 制 策 略 实 例 在 本 节 中, 我 们 应 用 前 面 学 到 的 知 识 建 立 这 样 一 个 Git 工 作 流 程 : 检 查 提 交 信 息 的 格 式, 只 接 受 纯 fast-forward 内 容 的 推 送, 并 且 指 定 用 户 只 能 修 改 项 目 中 的 特 定 子 目 录 我 们 将 写 一 个 客 户 端 脚 本 来 提 示 开 发 人 员 他 们 推 送 的 内 容 是 否 会 被 拒 绝, 以 及 一 个 服 务 端 脚 本 来 实 际 执 行 这 些 策 略 这 些 脚 本 使 用 Ruby 写 成, 一 半 由 于 它 是 作 者 倾 向 的 脚 本 语 言, 另 外 作 者 觉 得 它 是 最 接 近 伪 代 码 的 脚 本 语 言 ; 因 而 即 便 你 不 使 用 Ruby 也 能 大 致 看 懂 不 过 任 何 其 他 语 言 也 一 样 适 用 所 有 Git 自 带 的 样 例 脚 本 都 是 用 Perl 或 Bash 写 的 所 以 从 这 些 脚 本 中 能 找 到 相 当 多 的 这 两 种 语 言 的 挂 钩 样 例 服 务 端 挂 钩 所 有 服 务 端 的 工 作 都 在 hooks( 挂 钩 ) 目 录 的 update( 更 新 ) 脚 本 中 制 定 update 脚 本 为 每 一 个 得 到 推 送 的 分 支 运 行 一 次 ; 它 接 受 推 送 目 标 的 索 引, 该 分 支 原 来 指 向 的 位 置, 以 及 被 推 送 的 新 内 容 如 果 推 送 是 通 过 SSH 进 行 的, 还 可 以 获 取 发 出 此 次 操 作 的 用 户 如 果 设 定 所 有 操 作 都 通 过 公 匙 授 权 的 单 一 帐 号 ( 比 如 " git ") 进 行, 就 有 必 要 通 过 一 个 shell 包 装 依 据 公 匙 来 判 断 用 户 的 身 份, 并 且 设 定 环 境 变 量 来 表 示 该 用 户 的 身 份 下 面 假 设 尝 试 连 接 的 用 户 储 存 在 $USER 环 境 变 量 里, 我 们 的 update 脚 本 首 先 搜 集 一 切 需 要 的 信 息 : #!/usr/bin/env ruby $refname = ARGV[0] 217

228 第 7 章 自 定 义 Git Sco Chacon Pro Git 中 文 版 $oldrev $newrev $user = ARGV[1] = ARGV[2] = ENV['USER'] puts "Enforcing Policies... \n(#{$refname}) (#{$oldrev[0,6]}) (#{$newrev[0,6]})" 没 错, 我 在 用 全 局 变 量 别 鄙 视 我 这 样 比 较 利 于 演 示 过 程 指 定 特 殊 的 提 交 信 息 格 式 我 们 的 第 一 项 任 务 是 指 定 每 一 条 提 交 信 息 都 必 须 遵 循 某 种 特 殊 的 格 式 作 为 演 示, 假 定 每 一 条 信 息 必 须 包 含 一 条 形 似 ref: 1234 这 样 的 字 符 串, 因 为 我 们 需 要 把 每 一 次 提 交 和 项 目 的 问 题 追 踪 系 统 我 们 要 逐 一 检 查 每 一 条 推 送 上 来 的 提 交 内 容, 看 看 提 交 信 息 是 否 包 含 这 么 一 个 字 符 串, 然 后, 如 果 该 提 交 里 不 包 含 这 个 字 符 串, 以 非 零 返 回 值 退 出 从 而 拒 绝 此 次 推 送 把 $newrev 和 $oldrev 变 量 的 值 传 给 一 个 叫 做 git rev-list 的 Git plumbing 命 令 可 以 获 取 所 有 提 交 内 容 的 SHA-1 值 列 表 git rev-list 基 本 类 似 git log 命 令, 但 它 默 认 只 输 出 SHA-1 值 而 已, 没 有 其 他 信 息 所 以 要 获 取 由 SHA 值 表 示 的 从 一 次 提 交 到 另 一 次 提 交 之 间 的 所 有 SHA 值, 可 以 运 行 : $ git rev-list 538c33..d14fc7 d14fc7c847ab946ec39590d87783c69b031bdfb7 9f585da4401b0a3999e d15245c13f0be a1be950e2a8d078e6141f5cd20c1e61ad3 dfa04c9ef3d f13fb5b9b1fb7717d2222a 17716ec0f1ff5c77eff40b7fe912f9f6cfd0e475 截 取 这 些 输 出 内 容, 循 环 遍 历 其 中 每 一 个 SHA 值, 找 出 与 之 对 应 的 提 交 信 息, 然 后 用 正 则 表 达 式 来 测 试 该 信 息 包 含 的 格 式 话 的 内 容 下 面 要 搞 定 如 何 从 所 有 的 提 交 内 容 中 提 取 出 提 交 信 息 使 用 另 一 个 叫 做 git cat-file 的 Git plumbing 工 具 可 以 获 得 原 始 的 提 交 数 据 我 们 将 在 第 九 章 了 解 到 这 些 plumbing 工 具 的 细 节 ; 现 在 暂 时 先 看 一 下 这 条 命 令 的 输 出 : $ git cat-file commit ca82a6 tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf parent 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 author Scott Chacon <schacon@gmail.com>

229 Sco Chacon Pro Git 中 文 版 7.4 节 Git 强 制 策 略 实 例 committer Scott Chacon changed the version number 通 过 SHA-1 值 获 得 提 交 内 容 中 的 提 交 信 息 的 一 个 简 单 办 法 是 找 到 提 交 的 第 一 行, 然 后 取 从 它 往 后 的 所 有 内 容 可 以 使 用 Unix 系 统 的 sed 命 令 来 实 现 该 效 果 : $ git cat-file commit ca82a6 sed '1,/^$/d' changed the version number 这 条 咒 语 从 每 一 个 待 提 交 内 容 里 提 取 提 交 信 息, 并 且 会 在 提 取 信 息 不 符 合 要 求 的 情 况 下 退 出 为 了 退 出 脚 本 和 拒 绝 此 次 推 送, 返 回 一 个 非 零 值 整 个 脚 本 大 致 如 下 : $regex = /\[ref: (\d+)\]/ # 指 定 提 交 信 息 格 式 def check_message_format missed_revs = `git rev-list #{$oldrev}..#{$newrev}`.split("\n") missed_revs.each do rev message = `git cat-file commit #{rev} sed '1,/^$/d'` if!$regex.match(message) puts "[POLICY] Your message is not formatted correctly" exit 1 end end end check_message_format 把 这 一 段 放 在 update 脚 本 里, 所 有 包 含 不 符 合 指 定 规 则 的 提 交 都 会 遭 到 拒 绝 实 现 基 于 用 户 的 访 问 权 限 控 制 列 表 (ACL) 系 统 假 设 你 需 要 添 加 一 个 使 用 访 问 权 限 控 制 列 表 的 机 制 来 指 定 哪 些 用 户 对 项 目 的 哪 些 部 分 有 推 送 权 限 某 些 用 户 具 有 全 部 的 访 问 权, 其 他 人 只 对 某 些 子 目 录 或 者 特 定 的 文 件 具 有 推 送 权 限 要 搞 定 这 一 点, 所 有 的 规 则 将 被 写 入 一 个 位 于 服 务 器 的 原 始 219

230 第 7 章 自 定 义 Git Sco Chacon Pro Git 中 文 版 Git 仓 库 的 acl 文 件 我 们 让 update 挂 钩 检 阅 这 些 规 则, 审 视 推 送 的 提 交 内 容 中 需 要 修 改 的 所 有 文 件, 然 后 决 定 执 行 推 送 的 用 户 是 否 对 所 有 这 些 文 件 都 有 权 限 我 们 首 先 要 创 建 这 个 列 表 这 里 使 用 的 格 式 和 CVS 的 ACL 机 制 十 分 类 似 : 它 由 若 干 行 构 成, 第 一 项 内 容 是 avail 或 者 unavail, 接 着 是 逗 号 分 隔 的 规 则 生 效 用 户 列 表, 最 后 一 项 是 规 则 生 效 的 目 录 ( 空 白 表 示 开 放 访 问 ) 这 些 项 目 由 字 符 隔 开 下 例 中, 我 们 指 定 几 个 管 理 员, 几 个 对 doc 目 录 具 有 权 限 的 文 档 作 者, 以 及 一 个 对 lib 和 tests 目 录 具 有 权 限 的 开 发 人 员, 相 应 的 ACL 文 件 如 下 : avail nickh,pjhyett,defunkt,tpw avail usinclair,cdickens,ebronte doc avail schacon lib avail schacon tests 首 先 把 这 些 数 据 读 入 你 编 写 的 数 据 结 构 本 例 中, 为 保 持 简 洁, 我 们 暂 时 只 实 现 avail 的 规 则 ( 译 注 : 也 就 是 省 略 了 unavail 部 分 ) 下 面 这 个 方 法 生 成 一 个 关 联 数 组, 它 的 主 键 是 用 户 名, 值 是 一 个 该 用 户 有 写 权 限 的 所 有 目 录 组 成 的 数 组 : def get_acl_access_data(acl_file) # read in ACL data acl_file = File.read(acl_file).split("\n").reject { line line == '' } access = {} acl_file.each do line avail, users, path = line.split(' ') next unless avail == 'avail' users.split(',').each do user access[user] = [] access[user] << path end end access end 针 对 之 前 给 出 的 ACL 规 则 文 件, 这 个 get_acl_access_data 方 法 返 回 的 数 据 结 构 如 下 : {"defunkt"=>[nil], 220

231 Sco Chacon Pro Git 中 文 版 7.4 节 Git 强 制 策 略 实 例 "tpw"=>[nil], "nickh"=>[nil], "pjhyett"=>[nil], "schacon"=>["lib", "tests"], "cdickens"=>["doc"], "usinclair"=>["doc"], "ebronte"=>["doc"]} 搞 定 了 用 户 权 限 的 数 据, 下 面 需 要 找 出 哪 些 位 置 将 要 被 提 交 的 内 容 修 改, 从 而 确 保 试 图 推 送 的 用 户 对 这 些 位 置 有 全 部 的 权 限 使 用 git log 的 --name-only 选 项 ( 在 第 二 章 里 简 单 的 提 过 ) 我 们 可 以 轻 而 易 举 的 找 出 一 次 提 交 里 修 改 的 文 件 : $ git log -1 --name-only --pretty=format:'' 9f585d README lib/test.rb 使 用 get_acl_access_data 返 回 的 ACL 结 构 来 一 一 核 对 每 一 次 提 交 修 改 的 文 件 列 表, 就 能 找 出 该 用 户 是 否 有 权 限 推 送 所 有 的 提 交 内 容 : # 仅 允 许 特 定 用 户 修 改 项 目 中 的 特 定 子 目 录 def check_directory_perms access = get_acl_access_data('acl') # 检 查 是 否 有 人 在 向 他 没 有 权 限 的 地 方 推 送 内 容 new_commits = `git rev-list #{$oldrev}..#{$newrev}`.split("\n") new_commits.each do rev files_modified = `git log -1 --name-only --pretty=format:'' #{rev}`.split("\n") files_modified.each do path next if path.size == 0 has_file_access = false access[$user].each do access_path if!access_path # 用 户 拥 有 完 全 访 问 权 限 (path.index(access_path) == 0) # 或 者 对 此 位 置 有 访 问 权 限 has_file_access = true 221

232 第 7 章 自 定 义 Git Sco Chacon Pro Git 中 文 版 end end if!has_file_access puts "[POLICY] You do not have access to push to #{path}" exit 1 end end end end check_directory_perms 以 上 的 大 部 分 内 容 应 该 都 比 较 容 易 理 解 通 过 git rev-list 获 取 推 送 到 服 务 器 内 容 的 提 交 列 表 然 后, 针 对 其 中 每 一 项, 找 出 它 试 图 修 改 的 文 件 然 后 确 保 执 行 推 送 的 用 户 对 这 些 文 件 具 有 权 限 一 个 不 太 容 易 理 解 的 Ruby 技 巧 是 path.index(access_path) ==0 这 句, 它 的 返 回 真 值 如 果 路 径 以 access_path 开 头 这 是 为 了 确 保 access_path 并 不 是 只 在 允 许 的 路 径 之 一, 而 是 所 有 准 许 全 选 的 目 录 都 在 该 目 录 之 下 现 在 你 的 用 户 没 法 推 送 带 有 不 正 确 的 提 交 信 息 的 内 容, 也 不 能 在 准 许 他 们 访 问 范 围 之 外 的 位 置 做 出 修 改 只 允 许 Fast-Forward 类 型 的 推 送 剩 下 的 最 后 一 项 任 务 是 指 定 只 接 受 fast-forward 的 推 送 在 Git 1.6 或 者 更 新 版 本 里, 只 需 要 设 定 receive.denydeletes 和 receive.denynonfastforwards 选 项 就 可 以 了 但 是 通 过 挂 钩 的 实 现 可 以 在 旧 版 本 的 Git 上 工 作, 并 且 通 过 一 定 的 修 改 它 它 可 以 做 到 只 针 对 某 些 用 户 执 行, 或 者 更 多 以 后 可 能 用 的 到 的 规 则 检 查 这 一 项 的 逻 辑 是 看 看 提 交 里 是 否 包 含 从 旧 版 本 里 能 找 到 但 在 新 版 本 里 却 找 不 到 的 内 容 如 果 没 有, 那 这 是 一 次 纯 fast-forward 的 推 送 ; 如 果 有, 那 我 们 拒 绝 此 次 推 送 : # 只 允 许 纯 fast-forward 推 送 def check_fast_forward missed_refs = `git rev-list #{$newrev}..#{$oldrev}` missed_ref_count = missed_refs.split("\n").size if missed_ref_count > 0 puts "[POLICY] Cannot push a non fast-forward reference" exit 1 222

233 Sco Chacon Pro Git 中 文 版 7.4 节 Git 强 制 策 略 实 例 end end check_fast_forward 一 切 都 设 定 好 了 如 果 现 在 运 行 chmod u+x.git/hooks/update 修 改 包 含 以 上 内 容 文 件 的 权 限, 然 后 尝 试 推 送 一 个 包 含 非 fast-forward 类 型 的 索 引, 会 得 到 一 下 提 示 : $ git push -f origin master Counting objects: 5, done. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 323 bytes, done. Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. Enforcing Policies... (refs/heads/master) (8338c5) (c5b616) [POLICY] Cannot push a non fast-forward reference error: hooks/update exited with error code 1 error: hook declined to update refs/heads/master To git@gitserver:project.git! [remote rejected] master -> master (hook declined) error: failed to push some refs to 'git@gitserver:project.git' 这 里 有 几 个 有 趣 的 信 息 首 先, 我 们 可 以 看 到 挂 钩 运 行 的 起 点 : Enforcing Policies... (refs/heads/master) (8338c5) (c5b616) 注 意 这 是 从 update 脚 本 开 头 输 出 到 标 准 你 输 出 的 所 有 从 脚 本 输 出 的 提 示 都 会 发 送 到 客 户 端, 这 点 很 重 要 下 一 个 值 得 注 意 的 部 分 是 错 误 信 息 [POLICY] Cannot push a non fast-forward reference error: hooks/update exited with error code 1 223

234 第 7 章 自 定 义 Git Sco Chacon Pro Git 中 文 版 error: hook declined to update refs/heads/master 第 一 行 是 我 们 的 脚 本 输 出 的, 在 往 下 是 Git 在 告 诉 我 们 update 脚 本 退 出 时 返 回 了 非 零 值 因 而 推 送 遭 到 了 拒 绝 最 后 一 点 : To git@gitserver:project.git! [remote rejected] master -> master (hook declined) error: failed to push some refs to 'git@gitserver:project.git' 我 们 将 为 每 一 个 被 挂 钩 拒 之 门 外 的 索 引 受 到 一 条 远 程 信 息, 解 释 它 被 拒 绝 是 因 为 一 个 挂 钩 的 原 因 而 且, 如 果 那 个 ref 字 符 串 没 有 包 含 在 任 何 的 提 交 里, 我 们 将 看 到 前 面 脚 本 里 输 出 的 错 误 信 息 : [POLICY] Your message is not formatted correctly 又 或 者 某 人 想 修 改 一 个 自 己 不 具 备 权 限 的 文 件 然 后 推 送 了 一 个 包 含 它 的 提 交, 他 将 看 到 类 似 的 提 示 比 如, 一 个 文 档 作 者 尝 试 推 送 一 个 修 改 到 lib 目 录 的 提 交, 他 会 看 到 [POLICY] You do not have access to push to lib/test.rb 全 在 这 了 从 这 里 开 始, 只 要 update 脚 本 存 在 并 且 可 执 行, 我 们 的 仓 库 永 远 都 不 会 遭 到 回 转 或 者 包 含 不 符 合 要 求 信 息 的 提 交 内 容, 并 且 用 户 都 被 锁 在 了 沙 箱 里 面 客 户 端 挂 钩 这 种 手 段 的 缺 点 在 于 用 户 推 送 内 容 遭 到 拒 绝 后 几 乎 无 法 避 免 的 抱 怨 辛 辛 苦 苦 写 成 的 代 码 在 最 后 时 刻 惨 遭 拒 绝 是 十 分 悲 剧 切 具 迷 惑 性 的 ; 更 可 怜 的 是 他 们 不 得 不 修 改 提 交 历 史 来 解 决 问 题, 这 怎 么 也 算 不 上 王 道 逃 离 这 种 两 难 境 地 的 法 宝 是 给 用 户 一 些 客 户 端 的 挂 钩, 在 他 们 作 出 可 能 悲 剧 的 事 情 的 时 候 给 以 警 告 然 后 呢, 用 户 们 就 能 在 提 交 问 题 变 得 更 难 修 正 之 前 解 除 隐 患 由 于 挂 钩 本 身 不 跟 随 克 隆 的 项 目 副 本 分 发, 所 以 必 须 通 过 其 他 途 径 把 这 些 挂 钩 分 发 到 用 户 的.git/hooks 目 录 并 设 为 可 执 行 文 件 虽 然 可 以 在 相 同 或 单 独 的 项 目 内 容 里 加 入 并 分 发 它 们, 全 自 动 的 解 决 方 案 是 不 存 在 的 224

235 Sco Chacon Pro Git 中 文 版 7.4 节 Git 强 制 策 略 实 例 首 先, 你 应 该 在 每 次 提 交 前 核 查 你 的 提 交 注 释 信 息, 这 样 你 才 能 确 保 服 务 器 不 会 因 为 不 合 条 件 的 提 交 注 释 信 息 而 拒 绝 你 的 更 改 为 了 达 到 这 个 目 的, 你 可 以 增 加 commit-msg 挂 钩 如 果 你 使 用 该 挂 钩 来 阅 读 作 为 第 一 个 参 数 传 递 给 git 的 提 交 注 释 信 息, 并 且 与 规 定 的 模 式 作 对 比, 你 就 可 以 使 git 在 提 交 注 释 信 息 不 符 合 条 件 的 情 况 下, 拒 绝 执 行 提 交 #!/usr/bin/env ruby message_file = ARGV[0] message = File.read(message_file) $regex = /\[ref: (\d+)\]/ if!$regex.match(message) puts "[POLICY] Your message is not formatted correctly" exit 1 end 如 果 这 个 脚 本 放 在 这 个 位 置 (.git/hooks/commit-msg) 并 且 是 可 执 行 的, 并 且 你 的 提 交 注 释 信 息 不 是 符 合 要 求 的, 你 会 看 到 : $ git commit -am 'test' [POLICY] Your message is not formatted correctly 在 这 个 实 例 中, 提 交 没 有 成 功 然 而 如 果 你 的 提 交 注 释 信 息 是 符 合 要 求 的,git 会 允 许 你 提 交 : $ git commit -am 'test [ref: 132]' [master e05c914] test [ref: 132] 1 files changed, 1 insertions(+), 0 deletions(-) 接 下 来 我 们 要 保 证 没 有 修 改 到 ACL 允 许 范 围 之 外 的 文 件 加 入 你 的.git 目 录 里 有 前 面 使 用 过 的 ACL 文 件, 那 么 以 下 的 pre-commit 脚 本 将 把 里 面 的 规 定 执 行 起 来 : #!/usr/bin/env ruby 225

236 第 7 章 自 定 义 Git Sco Chacon Pro Git 中 文 版 $user = ENV['USER'] # [ insert acl_access_data method from above ] # 只 允 许 特 定 用 户 修 改 项 目 重 特 定 子 目 录 的 内 容 def check_directory_perms access = get_acl_access_data('.git/acl') files_modified = `git diff-index --cached --name-only HEAD`.split("\n") files_modified.each do path next if path.size == 0 has_file_access = false access[$user].each do access_path if!access_path (path.index(access_path) == 0) has_file_access = true end if!has_file_access puts "[POLICY] You do not have access to push to #{path}" exit 1 end end end check_directory_perms 这 和 服 务 端 的 脚 本 几 乎 一 样, 除 了 两 个 重 要 区 别 第 一,ACL 文 件 的 位 置 不 同, 因 为 这 个 脚 本 在 当 前 工 作 目 录 运 行, 而 非 Git 目 录 ACL 文 件 的 目 录 必 须 从 access = get_acl_access_data('acl') 修 改 成 : access = get_acl_access_data('.git/acl') 另 一 个 重 要 区 别 是 获 取 被 修 改 文 件 列 表 的 方 式 在 服 务 端 的 时 候 使 用 了 查 看 提 交 纪 录 的 方 式, 可 是 目 前 的 提 交 都 还 没 被 记 录 下 来 呢, 所 以 这 个 列 表 只 能 从 暂 存 区 域 获 取 和 原 来 的 226

237 Sco Chacon Pro Git 中 文 版 7.4 节 Git 强 制 策 略 实 例 files_modified = `git log -1 --name-only --pretty=format:'' #{ref}` 不 同, 现 在 要 用 files_modified = `git diff-index --cached --name-only HEAD` 不 同 的 就 只 有 这 两 点 除 此 之 外, 该 脚 本 完 全 相 同 一 个 小 陷 阱 在 于 它 假 设 在 本 地 运 行 的 账 户 和 推 送 到 远 程 服 务 端 的 相 同 如 果 这 二 者 不 一 样, 则 需 要 手 动 设 置 一 下 $user 变 量 最 后 一 项 任 务 是 检 查 确 认 推 送 内 容 中 不 包 含 非 fast-forward 类 型 的 索 引, 不 过 这 个 需 求 比 较 少 见 要 找 出 一 个 非 fast-forward 类 型 的 索 引, 要 么 衍 合 超 过 某 个 已 经 推 送 过 的 提 交, 要 么 从 本 地 不 同 分 支 推 送 到 远 程 相 同 的 分 支 上 既 然 服 务 器 将 给 出 无 法 推 送 非 fast-forward 内 容 的 提 示, 而 且 上 面 的 挂 钩 也 能 阻 止 强 制 的 推 送, 唯 一 剩 下 的 潜 在 问 题 就 是 衍 合 一 次 已 经 推 送 过 的 提 交 内 容 下 面 是 一 个 检 查 这 个 问 题 的 pre-rabase 脚 本 的 例 子 它 获 取 一 个 所 有 即 将 重 写 的 提 交 内 容 的 列 表, 然 后 检 查 它 们 是 否 在 远 程 的 索 引 里 已 经 存 在 一 旦 发 现 某 个 提 交 可 以 从 远 程 索 引 里 衍 变 过 来, 它 就 放 弃 衍 合 操 作 : #!/usr/bin/env ruby base_branch = ARGV[0] if ARGV[1] topic_branch = ARGV[1] else topic_branch = "HEAD" end target_shas = `git rev-list #{base_branch}..#{topic_branch}`.split("\n") remote_refs = `git branch -r`.split("\n").map { r r.strip } target_shas.each do sha remote_refs.each do remote_ref shas_pushed = `git rev-list ^#{sha}^@ refs/remotes/#{remote_ref}` if shas_pushed.split("\n").include?(sha) puts "[POLICY] Commit #{sha} has already been pushed to #{remote_ref}" exit 1 227

238 第 7 章 自 定 义 Git Sco Chacon Pro Git 中 文 版 end end end 这 个 脚 本 利 用 了 一 个 第 六 章 修 订 版 本 选 择 一 节 中 不 曾 提 到 的 语 法 通 过 这 一 句 可 以 获 得 一 个 所 有 已 经 完 成 推 送 的 提 交 的 列 表 : git rev-list ^#{sha}^@ refs/remotes/#{remote_ref} SHAˆ@ 语 法 解 析 该 次 提 交 的 所 有 祖 先 这 里 我 们 从 检 查 远 程 最 后 一 次 提 交 能 够 衍 变 获 得 但 从 所 有 我 们 尝 试 推 送 的 提 交 的 SHA 值 祖 先 无 法 衍 变 获 得 的 提 交 内 容 也 就 是 fast-forward 的 内 容 这 个 解 决 方 案 的 硬 伤 在 于 它 有 可 能 很 慢 而 且 常 常 没 有 必 要 只 要 不 用 -f 来 强 制 推 送, 服 务 器 会 自 动 给 出 警 告 并 且 拒 绝 推 送 内 容 然 而, 这 是 个 不 错 的 练 习 而 且 理 论 上 能 帮 助 用 户 避 免 一 次 将 来 不 得 不 折 回 来 修 改 的 衍 合 操 作 7.5 总 结 你 已 经 见 识 过 绝 大 多 数 通 过 自 定 义 Git 客 户 端 和 服 务 端 来 来 适 应 自 己 工 作 流 程 和 项 目 内 容 的 方 式 了 无 论 你 创 造 出 了 什 么 样 的 工 作 流 程,Git 都 能 用 的 顺 手 228

239 第 8 章 Git 与 其 他 系 统 世 界 不 是 完 美 的 大 多 数 时 候, 将 所 有 接 触 到 的 项 目 全 部 转 向 Git 是 不 可 能 的 有 时 我 们 不 得 不 为 某 个 项 目 使 用 其 他 的 版 本 控 制 系 统 (VCS, Version Control System ), 其 中 比 较 常 见 的 是 Subversion 你 将 在 本 章 的 第 一 部 分 学 习 使 用 git svn,git 为 Subversion 附 带 的 双 向 桥 接 工 具 或 许 现 在 你 已 经 在 考 虑 将 先 前 的 项 目 转 向 Git 本 章 的 第 二 部 分 将 介 绍 如 何 将 项 目 迁 移 到 Git: 先 介 绍 从 Subversion 的 迁 移, 然 后 是 Perforce, 最 后 介 绍 如 何 使 用 自 定 义 的 脚 本 进 行 非 标 准 的 导 入 8.1 Git 与 Subversion 当 前, 大 多 数 开 发 中 的 开 源 项 目 以 及 大 量 的 商 业 项 目 都 使 用 Subversion 来 管 理 源 码 作 为 最 流 行 的 开 源 版 本 控 制 系 统,Subversion 已 经 存 在 了 接 近 十 年 的 时 间 它 在 许 多 方 面 与 CVS 十 分 类 似, 后 者 是 前 者 出 现 之 前 代 码 控 制 世 界 的 霸 主 Git 最 为 重 要 的 特 性 之 一 是 名 为 git svn 的 Subversion 双 向 桥 接 工 具 该 工 具 把 Git 变 成 了 Subversion 服 务 的 客 户 端, 从 而 让 你 在 本 地 享 受 到 Git 所 有 的 功 能, 而 后 直 接 向 Subversion 服 务 器 推 送 内 容, 仿 佛 在 本 地 使 用 了 Subversion 客 户 端 也 就 是 说, 在 其 他 人 忍 受 古 董 的 同 时, 你 可 以 在 本 地 享 受 分 支 合 并, 使 暂 存 区 域, 衍 合 以 及 单 项 挑 拣 等 等 这 是 个 让 Git 偷 偷 潜 入 合 作 开 发 环 境 的 好 东 西, 在 帮 助 你 的 开 发 同 伴 们 提 高 效 率 的 同 时, 它 还 能 帮 你 劝 说 团 队 让 整 个 项 目 框 架 转 向 对 Git 的 支 持 这 个 Subversion 之 桥 是 通 向 分 布 式 版 本 控 制 系 统 (DVCS, Distributed VCS ) 世 界 的 神 奇 隧 道 git svn Git 中 所 有 Subversion 桥 接 命 令 的 基 础 是 git svn 所 有 的 命 令 都 从 它 开 始 相 关 的 命 令 数 目 不 少, 你 将 通 过 几 个 简 单 的 工 作 流 程 了 解 到 其 中 常 见 的 一 些 值 得 警 戒 的 是, 在 使 用 git svn 的 时 候, 你 实 际 是 在 与 Subversion 交 互,Git 比 它 要 高 级 复 杂 的 多 尽 管 可 以 在 本 地 随 意 的 进 行 分 支 和 合 并, 最 好 还 是 通 过 衍 合 保 持 线 性 的 提 交 历 史, 尽 量 避 免 类 似 与 远 程 Git 仓 库 动 态 交 互 这 样 的 操 作 229

240 第 8 章 Git 与 其 他 系 统 Sco Chacon Pro Git 中 文 版 避 免 修 改 历 史 再 重 新 推 送 的 做 法, 也 不 要 同 时 推 送 到 并 行 的 Git 仓 库 来 试 图 与 其 他 Git 用 户 合 作 Subersion 只 能 保 存 单 一 的 线 性 提 交 历 史, 一 不 小 心 就 会 被 搞 糊 涂 合 作 团 队 中 同 时 有 人 用 SVN 和 Git, 一 定 要 确 保 所 有 人 都 使 用 SVN 服 务 来 协 作 这 会 让 生 活 轻 松 很 多 初 始 设 定 为 了 展 示 功 能, 先 要 一 个 具 有 写 权 限 的 SVN 仓 库 如 果 想 尝 试 这 个 范 例, 你 必 须 复 制 一 份 其 中 的 测 试 仓 库 比 较 简 单 的 做 法 是 使 用 一 个 名 为 svnsync 的 工 具 较 新 的 Subversion 版 本 中 都 带 有 该 工 具, 它 将 数 据 编 码 为 用 于 网 络 传 输 的 格 式 要 尝 试 本 例, 先 在 本 地 新 建 一 个 Subversion 仓 库 : $ mkdir /tmp/test-svn $ svnadmin create /tmp/test-svn 然 后, 允 许 所 有 用 户 修 改 revprop 简 单 的 做 法 是 添 加 一 个 总 是 以 0 作 为 返 回 值 的 pre-revprop-change 脚 本 : $ cat /tmp/test-svn/hooks/pre-revprop-change #!/bin/sh exit 0; $ chmod +x /tmp/test-svn/hooks/pre-revprop-change 现 在 可 以 调 用 svnsync init 加 目 标 仓 库, 再 加 源 仓 库 的 格 式 来 把 该 项 目 同 步 到 本 地 了 : $ svnsync init file:///tmp/test-svn 这 将 建 立 进 行 同 步 所 需 的 属 性 可 以 通 过 运 行 以 下 命 令 来 克 隆 代 码 : $ svnsync sync file:///tmp/test-svn Committed revision 1. Copied properties for revision 1. Committed revision 2. Copied properties for revision

241 Sco Chacon Pro Git 中 文 版 8.1 节 Git 与 Subversion Committed revision 别 看 这 个 操 作 只 花 掉 几 分 钟, 要 是 你 想 把 源 仓 库 复 制 到 另 一 个 远 程 仓 库, 而 不 是 本 地 仓 库, 那 将 花 掉 接 近 一 个 小 时, 尽 管 项 目 中 只 有 不 到 100 次 的 提 交 Subversion 每 次 只 复 制 一 次 修 改, 把 它 推 送 到 另 一 个 仓 库 里, 然 后 周 而 复 始 惊 人 的 低 效, 但 是 我 们 别 无 选 择 入 门 有 了 可 以 写 入 的 Subversion 仓 库 以 后, 就 可 以 尝 试 一 下 典 型 的 工 作 流 程 了 我 们 从 git svn clone 命 令 开 始, 它 会 把 整 个 Subversion 仓 库 导 入 到 一 个 本 地 的 Git 仓 库 中 提 醒 一 下, 这 里 导 入 的 是 一 个 货 真 价 实 的 Subversion 仓 库, 所 以 应 该 把 下 面 的 file:///tmp/test-svn 换 成 你 所 用 的 Subversion 仓 库 的 URL: $ git svn clone file:///tmp/test-svn -T trunk -b branches -t tags Initialized empty Git repository in /Users/schacon/projects/testsvnsync/svn/.git/ r1 = b4e387bc68740b5af56c2a5faf4003ae42bd135c (trunk) A m4/acx_pthread.m4 A m4/stl_hash.m4... r75 = d1957f3b eec6314e15bcda59e3d9610 (trunk) Found possible branch point: file:///tmp/test-svn/trunk => \ file:///tmp/test-svn /branches/my-calc-branch, 75 Found branch parent: (my-calc-branch) d1957f3b eec6314e15bcda59e3d9610 Following parent with do_switch Successfully followed parent r76 = ecc0badd73f40ea2f01fce b01 (my-calc-branch) Checked out HEAD: file:///tmp/test-svn/branches/my-calc-branch r76 这 相 当 于 针 对 所 提 供 的 URL 运 行 了 两 条 命 令 git svn init 加 上 git svn fetch 可 能 会 花 上 一 段 时 间 我 们 所 用 的 测 试 项 目 仅 仅 包 含 75 次 提 交 并 且 它 的 代 码 量 不 算 大, 所 以 只 有 几 分 钟 而 已 不 过,Git 仍 然 需 要 提 取 每 一 个 版 本, 每 次 一 个, 再 逐 个 提 交 对 于 一 个 包 含 成 百 上 千 次 提 交 的 项 目, 花 掉 的 时 间 则 可 能 是 几 小 时 甚 至 数 天 -T trunk -b branches -t tags 告 诉 Git 该 Subversion 仓 库 遵 循 了 基 本 的 分 支 和 标 签 命 名 法 则 如 果 你 的 主 干 ( 译 注 :trunk, 相 当 于 非 分 布 式 版 本 控 制 里 的 231

242 第 8 章 Git 与 其 他 系 统 Sco Chacon Pro Git 中 文 版 master 分 支, 代 表 开 发 的 主 线 ), 分 支 或 者 标 签 以 不 同 的 方 式 命 名, 则 应 做 出 相 应 改 变 由 于 该 法 则 的 常 见 性, 可 以 使 用 -s 来 代 替 整 条 命 令, 它 意 味 着 标 准 布 局 (s 是 Standard layout 的 首 字 母 ), 也 就 是 前 面 选 项 的 内 容 下 面 的 命 令 有 相 同 的 效 果 : $ git svn clone file:///tmp/test-svn -s 现 在, 你 有 了 一 个 有 效 的 Git 仓 库, 包 含 着 导 入 的 分 支 和 标 签 : $ git branch -a * master my-calc-branch tags/2.0.2 tags/release tags/release tags/release-2.0.2rc1 trunk 值 得 注 意 的 是, 该 工 具 分 配 命 名 空 间 时 和 远 程 引 用 的 方 式 不 尽 相 同 克 隆 普 通 的 Git 仓 库 时, 可 以 以 origin/[branch] 的 形 式 获 取 远 程 服 务 器 上 所 有 可 用 的 分 支 分 配 到 远 程 服 务 的 名 称 下 然 而 git svn 假 定 不 存 在 多 个 远 程 服 务 器, 所 以 把 所 有 指 向 远 程 服 务 的 引 用 不 加 区 分 的 保 存 下 来 可 以 用 Git 探 测 命 令 show-ref 来 查 看 所 有 引 用 的 全 名 $ git show-ref 1cbd4904d9982f386d87f88fce1c24ad7c0f0471 refs/heads/master aee1ecc f355a883f5d99cff0c852d3c4 refs/remotes/my-calc-branch 03d09b0e2aad427e34a6d50ff147128e76c0e0f5 refs/remotes/tags/ d02cc0adc9da4319eeba ba219b9c376 refs/remotes/tags/release caaa711a50c77879a91b8b f672745cb refs/remotes/tags/release c4cb508144c513ff1214c3488abe66dcb92916f refs/remotes/tags/release-2.0.2rc1 1cbd4904d9982f386d87f88fce1c24ad7c0f0471 refs/remotes/trunk 而 普 通 的 Git 仓 库 应 该 是 这 个 模 样 : 232

243 Sco Chacon Pro Git 中 文 版 8.1 节 Git 与 Subversion $ git show-ref 83e38c7a0af325a9722f2fdc56b d83a1 refs/heads/master 3e15e38c198baac84223acfc6224bb8b99ff2281 refs/remotes/gitserver/master 0a30dd3b0c795b80212ae723640d4e5d48cabdff refs/remotes/origin/master fdd55f916652be4881c6f11600d6f refs/remotes/origin/testing 这 里 有 两 个 远 程 服 务 器 : 一 个 名 为 gitserver, 具 有 一 个 master 分 支 ; 另 一 个 叫 origin, 具 有 master 和 testing 两 个 分 支 注 意 本 例 中 通 过 git svn 导 入 的 远 程 引 用,(Subversion 的 ) 标 签 是 当 作 远 程 分 支 添 加 的, 而 不 是 真 正 的 Git 标 签 导 入 的 Subversion 仓 库 仿 佛 是 有 一 个 带 有 不 同 分 支 的 tags 远 程 服 务 器 提 交 到 Subversion 有 了 可 以 开 展 工 作 的 ( 本 地 ) 仓 库 以 后, 你 可 以 开 始 对 该 项 目 做 出 贡 献 并 向 上 游 仓 库 提 交 内 容 了,Git 这 时 相 当 于 一 个 SVN 客 户 端 假 如 编 辑 了 一 个 文 件 并 进 行 提 交, 那 么 这 次 提 交 仅 存 在 于 本 地 的 Git 而 非 Subversion 服 务 器 上 $ git commit -am 'Adding git-svn instructions to the README' [master 97031e5] Adding git-svn instructions to the README 1 files changed, 1 insertions(+), 1 deletions(-) 接 下 来, 可 以 将 作 出 的 修 改 推 送 到 上 游 值 得 注 意 的 是,Subversion 的 使 用 流 程 也 因 此 改 变 了 你 可 以 在 离 线 状 态 下 进 行 多 次 提 交 然 后 一 次 性 的 推 送 到 Subversion 的 服 务 器 上 向 Subversion 服 务 器 推 送 的 命 令 是 git svn dcommit: $ git svn dcommit Committing to file:///tmp/test-svn/trunk... M README.txt Committed r79 M README.txt r79 = 938b1a547c2cc92033b74d32030e a5c8 (trunk) No changes between current HEAD and refs/remotes/trunk Resetting to the latest refs/remotes/trunk 233

244 第 8 章 Git 与 其 他 系 统 Sco Chacon Pro Git 中 文 版 所 有 在 原 Subversion 数 据 基 础 上 提 交 的 commit 会 一 一 提 交 到 Subversion, 然 后 你 本 地 Git 的 commit 将 被 重 写, 加 入 一 个 特 别 标 识 这 一 步 很 重 要, 因 为 它 意 味 着 所 有 commit 的 SHA-1 指 都 会 发 生 变 化 这 也 是 同 时 使 用 Git 和 Subversion 两 种 服 务 作 为 远 程 服 务 不 是 个 好 主 意 的 原 因 之 一 检 视 以 下 最 后 一 个 commit, 你 会 找 到 新 添 加 的 git-svn-id ( 译 注 : 即 本 段 开 头 所 说 的 特 别 标 识 ): $ git log -1 commit 938b1a547c2cc92033b74d32030e a5c8 Author: schacon <schacon@4c93b f-11de-be05-5f7a > Date: Sat May 2 22:06: Adding git-svn instructions to the README git-svn-id: file:///tmp/test-svn/trunk@79 4c93b f-11de-be05-5f7a 注 意 看, 原 本 以 97031e5 开 头 的 SHA-1 校 验 值 在 提 交 完 成 以 后 变 成 了 938b1a5 如 果 既 要 向 Git 远 程 服 务 器 推 送 内 容, 又 要 推 送 到 Subversion 远 程 服 务 器, 则 必 须 先 向 Subversion 推 送 (dcommit), 因 为 该 操 作 会 改 变 所 提 交 的 数 据 内 容 拉 取 最 新 进 展 如 果 要 与 其 他 开 发 者 协 作, 总 有 那 么 一 天 你 推 送 完 毕 之 后, 其 他 人 发 现 他 们 推 送 自 己 修 改 的 时 候 ( 与 你 推 送 的 内 容 ) 产 生 冲 突 这 些 修 改 在 你 合 并 之 前 将 一 直 被 拒 绝 在 git svn 里 这 种 情 况 形 似 : $ git svn dcommit Committing to file:///tmp/test-svn/trunk... Merge conflict during commit: Your file or directory 'README.txt' is probably \ out-of-date: resource out of date; try updating at /Users/schacon/libexec/git-\ core/git-svn line 482 为 了 解 决 该 问 题, 可 以 运 行 git svn rebase, 它 会 拉 取 服 务 器 上 所 有 最 新 的 改 变, 再 次 基 础 上 衍 合 你 的 修 改 : $ git svn rebase M README.txt 234

245 Sco Chacon Pro Git 中 文 版 8.1 节 Git 与 Subversion r80 = ff829ab914e8775c7c025d741beb3d523ee30bc4 (trunk) First, rewinding head to replay your work on top of it... Applying: first user change 现 在, 你 做 出 的 修 改 都 发 生 在 服 务 器 内 容 之 后, 所 以 可 以 顺 利 的 运 行 dcommit : $ git svn dcommit Committing to file:///tmp/test-svn/trunk... M README.txt Committed r81 M README.txt r81 = 456cbe6337abe49154db70106d1836bc1332deed (trunk) No changes between current HEAD and refs/remotes/trunk Resetting to the latest refs/remotes/trunk 需 要 牢 记 的 一 点 是,Git 要 求 我 们 在 推 送 之 前 先 合 并 上 游 仓 库 中 最 新 的 内 容, 而 git svn 只 要 求 存 在 冲 突 的 时 候 才 这 样 做 假 如 有 人 向 一 个 文 件 推 送 了 一 些 修 改, 这 时 你 要 向 另 一 个 文 件 推 送 一 些 修 改, 那 么 dcommit 将 正 常 工 作 : $ git svn dcommit Committing to file:///tmp/test-svn/trunk... M configure.ac Committed r84 M autogen.sh r83 = 8aa54a74d452f82eee10076ab2584c1fc424853b (trunk) M configure.ac r84 = cdbac939211ccb18aa744e581e46563af5d962d0 (trunk) W: d2f23b80f67aaaa1f6f5aaef48fce3263ac71a92 and refs/remotes/trunk differ, \ using rebase: : efa5a59965fbbb5b2b0a12890f1b351bb5493c18 \ 015e4c98c482f0fa71e4d b37fa6 M autogen.sh First, rewinding head to replay your work on top of it... Nothing to do. 这 一 点 需 要 牢 记, 因 为 它 的 结 果 是 推 送 之 后 项 目 处 于 一 个 不 完 整 存 在 与 任 何 主 机 上 的 状 态 如 果 做 出 的 修 改 无 法 兼 容 但 没 有 产 生 冲 突, 则 可 能 造 成 一 些 很 难 确 诊 235

246 第 8 章 Git 与 其 他 系 统 Sco Chacon Pro Git 中 文 版 的 难 题 这 和 使 用 Git 服 务 器 是 不 同 的 在 Git 世 界 里, 发 布 之 前, 你 可 以 在 客 户 端 系 统 里 完 整 的 测 试 项 目 的 状 态, 而 在 SVN 永 远 都 没 法 确 保 提 交 前 后 项 目 的 状 态 完 全 一 样 即 使 还 没 打 算 进 行 提 交, 你 也 应 该 用 这 个 命 令 从 Subversion 服 务 器 拉 取 最 新 修 改 sit svn fetch 能 获 取 最 新 的 数 据, 不 过 git svn rebase 才 会 在 获 取 之 后 在 本 地 进 行 更 新 $ git svn rebase M generate_descriptor_proto.sh r82 = bd16df9173e424c6f52c337ab6efa7f f1 (trunk) First, rewinding head to replay your work on top of it... Fast-forwarded master to refs/remotes/trunk. 不 时 地 运 行 一 下 git svn rebase 可 以 确 保 你 的 代 码 没 有 过 时 不 过, 运 行 该 命 令 时 需 要 确 保 工 作 目 录 的 整 洁 如 果 在 本 地 做 了 修 改, 则 必 须 在 运 行 git svn rebase 之 前 或 暂 存 工 作, 或 暂 时 提 交 内 容 否 则, 该 命 令 会 发 现 衍 合 的 结 果 包 含 着 冲 突 因 而 终 止 Git 分 支 问 题 习 惯 了 Git 的 工 作 流 程 以 后, 你 可 能 会 创 建 一 些 特 性 分 支, 完 成 相 关 的 开 发 工 作, 然 后 合 并 他 们 如 果 要 用 git svn 向 Subversion 推 送 内 容, 那 么 最 好 是 每 次 用 衍 合 来 并 入 一 个 单 一 分 支, 而 不 是 直 接 合 并 使 用 衍 合 的 原 因 是 Subversion 只 有 一 个 线 性 的 历 史 而 不 像 Git 那 样 处 理 合 并, 所 以 Git svn 在 把 快 照 转 换 为 Subversion 的 commit 时 只 能 包 含 第 一 个 祖 先 假 设 分 支 历 史 如 下 : 创 建 一 个 experiment 分 支, 进 行 两 次 提 交, 然 后 合 并 到 master 在 dcommit 的 时 候 会 得 到 如 下 输 出 : $ git svn dcommit Committing to file:///tmp/test-svn/trunk... M CHANGES.txt Committed r85 M CHANGES.txt r85 = 4bfebeec434d156c36f2bcd18f4e3d97dc3269a2 (trunk) No changes between current HEAD and refs/remotes/trunk Resetting to the latest refs/remotes/trunk COPYING.txt: locally modified INSTALL.txt: locally modified 236

247 Sco Chacon Pro Git 中 文 版 8.1 节 Git 与 Subversion M COPYING.txt M INSTALL.txt Committed r86 M INSTALL.txt M COPYING.txt r86 = 2647f6b86ccfcaad4ec58c520e369ec81f7c283c (trunk) No changes between current HEAD and refs/remotes/trunk Resetting to the latest refs/remotes/trunk 在 一 个 包 含 了 合 并 历 史 的 分 支 上 使 用 dcommit 可 以 成 功 运 行, 不 过 在 Git 项 目 的 历 史 中, 它 没 有 重 写 你 在 experiment 分 支 中 的 两 个 commit 另 一 方 面, 这 些 改 变 却 出 现 在 了 SVN 版 本 中 同 一 个 合 并 commit 中 在 别 人 克 隆 该 项 目 的 时 候, 只 能 看 到 这 个 合 并 commit 包 含 了 所 有 发 生 过 的 修 改 ; 他 们 无 法 获 知 修 改 的 作 者 和 时 间 等 提 交 信 息 Subversion 分 支 Subversion 的 分 支 和 Git 中 的 不 尽 相 同 ; 避 免 过 多 的 使 用 可 能 是 最 好 方 案 不 过, 用 git svn 创 建 和 提 交 不 同 的 Subversion 分 支 仍 是 可 行 的 创 建 新 的 SVN 分 支 要 在 Subversion 中 建 立 一 个 新 分 支, 需 要 运 行 git svn branch [ 分 支 名 ] : $ git svn branch opera Copying file:///tmp/test-svn/trunk at r87 to file:///tmp/test-svn/branches/opera... Found possible branch point: file:///tmp/test-svn/trunk => \ file:///tmp/test-svn/branches/opera, 87 Found branch parent: (opera) 1f6bfe471083cbca06ac8d4176f7ad4de0d62e5f Following parent with do_switch Successfully followed parent r89 = 9b6fe0b90c5c9adf9165f dbc54a7cbf (opera) 这 相 当 于 在 Subversion 中 的 svn copy trunk branches/opera 命 令, 并 会 对 Subversion 服 务 器 进 行 相 关 操 作 值 得 注 意 的 是 它 没 有 检 出 和 转 换 到 那 个 分 支 ; 如 果 现 在 进 行 提 交, 将 提 交 到 服 务 器 上 的 trunk, 而 非 opera 237

248 第 8 章 Git 与 其 他 系 统 Sco Chacon Pro Git 中 文 版 切 换 当 前 分 支 Git 通 过 搜 寻 提 交 历 史 中 Subversion 分 支 的 头 部 来 决 定 dcommit 的 目 的 地 而 它 应 该 只 有 一 个, 那 就 是 当 前 分 支 历 史 中 最 近 一 次 包 含 git-svn-id 的 提 交 如 果 需 要 同 时 在 多 个 分 支 上 提 交, 可 以 通 过 导 入 Subversion 上 某 个 其 他 分 支 的 commit 来 建 立 以 该 分 支 为 dcommit 目 的 地 的 本 地 分 支 比 如 你 想 拥 有 一 个 并 行 维 护 的 opera 分 支, 可 以 运 行 $ git branch opera remotes/opera 然 后, 如 果 要 把 opera 分 支 并 入 trunk ( 本 地 的 master 分 支 ), 可 以 使 用 普 通 的 git merge 不 过 最 好 提 供 一 条 描 述 提 交 的 信 息 ( 通 过 -m), 否 则 这 次 合 并 的 记 录 是 Merge branch opera, 而 不 是 任 何 有 用 的 东 西 记 住, 虽 然 使 用 了 git merge 来 进 行 这 次 操 作, 并 且 合 并 过 程 可 能 比 使 用 Subversion 简 单 一 些 ( 因 为 Git 会 自 动 找 到 适 合 的 合 并 基 础 ), 这 并 不 是 一 次 普 通 的 Git 合 并 提 交 最 终 它 将 被 推 送 回 commit 无 法 包 含 多 个 祖 先 的 Subversion 服 务 器 上 ; 因 而 在 推 送 之 后, 它 将 变 成 一 个 包 含 了 所 有 在 其 他 分 支 上 做 出 的 改 变 的 单 一 commit 把 一 个 分 支 合 并 到 另 一 个 分 支 以 后, 你 没 法 像 在 Git 中 那 样 轻 易 的 回 到 那 个 分 支 上 继 续 工 作 提 交 时 运 行 的 dcommit 命 令 擦 除 了 全 部 有 关 哪 个 分 支 被 并 入 的 信 息, 因 而 以 后 的 合 并 基 础 计 算 将 是 不 正 确 的 dcommit 让 git merge 的 结 果 变 得 类 似 于 git merge --squash 不 幸 的 是, 我 们 没 有 什 么 好 办 法 来 避 免 该 情 况 Subversion 无 法 储 存 这 个 信 息, 所 以 在 使 用 它 作 为 服 务 器 的 时 候 你 将 永 远 为 这 个 缺 陷 所 困 为 了 不 出 现 这 种 问 题, 在 把 本 地 分 支 ( 本 例 中 的 opera) 并 入 trunk 以 后 应 该 立 即 将 其 删 除 对 应 Subversion 的 命 令 git svn 工 具 集 合 了 若 干 个 与 Subversion 类 似 的 功 能, 对 应 的 命 令 可 以 简 化 向 Git 的 转 化 过 程 下 面 这 些 命 令 能 实 现 Subversion 的 这 些 功 能 SVN 风 格 的 历 史 习 惯 了 Subversion 的 人 可 能 想 以 SVN 的 风 格 显 示 历 史, 运 行 git svn log 可 以 让 提 交 历 史 显 示 为 SVN 格 式 : $ git svn log r87 schacon :07: (Sat, 02 May 2009) 2 lines 238

249 Sco Chacon Pro Git 中 文 版 8.1 节 Git 与 Subversion autogen change r86 schacon :00: (Sat, 02 May 2009) 2 lines Merge branch 'experiment' r85 schacon :00: (Sat, 02 May 2009) 2 lines updated the changelog 关 于 git svn log, 有 两 点 需 要 注 意 首 先, 它 可 以 离 线 工 作, 不 像 svn log 命 令, 需 要 向 Subversion 服 务 器 索 取 数 据 其 次, 它 仅 仅 显 示 已 经 提 交 到 Subversion 服 务 器 上 的 commit 在 本 地 尚 未 dcommit 的 Git 数 据 不 会 出 现 在 这 里 ; 其 他 人 向 Subversion 服 务 器 新 提 交 的 数 据 也 不 会 显 示 等 于 说 是 显 示 了 最 近 已 知 Subversion 服 务 器 上 的 状 态 SVN 日 志 类 似 git svn log 对 git log 的 模 拟,svn annotate 的 等 效 命 令 是 git svn blame [ 文 件 名 ] 其 输 出 如 下 : $ git svn blame README.txt 2 temporal Protocol Buffers - Google's data interchange format 2 temporal Copyright 2008 Google Inc. 2 temporal 2 temporal 22 temporal C++ Installation - Unix 22 temporal ======================= 2 temporal 79 schacon Committing in git-svn. 78 schacon 2 temporal To build and install the C++ Protocol Buffer runtime and the Protocol 2 temporal Buffer compiler (protoc) execute the following: 2 temporal 同 样, 它 不 显 示 本 地 的 Git 提 交 以 及 Subversion 上 后 来 更 新 的 内 容 239

250 第 8 章 Git 与 其 他 系 统 Sco Chacon Pro Git 中 文 版 SVN 服 务 器 信 息 还 可 以 使 用 git svn info 来 获 取 与 运 行 svn info 类 似 的 信 息 : $ git svn info Path:. URL: Repository Root: Repository UUID: 4c93b f-11de-be05-5f7a Revision: 87 Node Kind: directory Schedule: normal Last Changed Author: schacon Last Changed Rev: 87 Last Changed Date: :07: (Sat, 02 May 2009) 它 与 blame 和 log 的 相 同 点 在 于 离 线 运 行 以 及 只 更 新 到 最 后 一 次 与 Subversion 服 务 器 通 信 的 状 态 略 Subversion 之 所 略 假 如 克 隆 了 一 个 包 含 了 svn:ignore 属 性 的 Subversion 仓 库, 就 有 必 要 建 立 对 应 的.gitignore 文 件 来 防 止 意 外 提 交 一 些 不 应 该 提 交 的 文 件 git svn 有 两 个 有 益 于 改 善 该 问 题 的 命 令 第 一 个 是 git svn create-ignore, 它 自 动 建 立 对 应 的.gitignore 文 件, 以 便 下 次 提 交 的 时 候 可 以 包 含 它 第 二 个 命 令 是 git svn show-ignore, 它 把 需 要 放 进.gitignore 文 件 中 的 内 容 打 印 到 标 准 输 出, 方 便 我 们 把 输 出 重 定 向 到 项 目 的 黑 名 单 文 件 : $ git svn show-ignore >.git/info/exclude 这 样 一 来, 避 免 了.gitignore 对 项 目 的 干 扰 如 果 你 是 一 个 Subversion 团 队 里 唯 一 的 Git 用 户, 而 其 他 队 友 不 喜 欢 项 目 包 含.gitignore, 该 方 法 是 你 的 不 二 之 选 Git-Svn 总 结 git svn 工 具 集 在 当 前 不 得 不 使 用 Subversion 服 务 器 或 者 开 发 环 境 要 求 使 用 Subversion 服 务 器 的 时 候 格 外 有 用 不 妨 把 它 看 成 一 个 跛 脚 的 Git, 然 而, 你 还 是 有 240

251 Sco Chacon Pro Git 中 文 版 8.2 节 迁 移 到 Git 可 能 在 转 换 过 程 中 碰 到 一 些 困 惑 你 和 合 作 者 们 的 迷 题 为 了 避 免 麻 烦, 试 着 遵 守 如 下 守 则 : 保 持 一 个 不 包 含 由 git merge 生 成 的 commit 的 线 性 提 交 历 史 将 在 主 线 分 支 外 进 行 的 开 发 通 通 衍 合 回 主 线 ; 避 免 直 接 合 并 不 要 单 独 建 立 和 使 用 一 个 Git 服 务 来 搞 合 作 可 以 为 了 加 速 新 开 发 者 的 克 隆 进 程 建 立 一 个, 但 是 不 要 向 它 提 供 任 何 不 包 含 git-svn-id 条 目 的 内 容 甚 至 可 以 添 加 一 个 pre-receive 挂 钩 来 在 每 一 个 提 交 信 息 中 查 找 git-svn-id 并 拒 绝 提 交 那 些 不 包 含 它 的 commit 如 果 遵 循 这 些 守 则, 在 Subversion 上 工 作 还 可 以 接 受 然 而, 如 果 能 迁 徙 到 真 正 的 Git 服 务 器, 则 能 为 团 队 带 来 更 多 好 处 8.2 迁 移 到 Git 如 果 在 其 他 版 本 控 制 系 统 中 保 存 了 某 项 目 的 代 码 而 后 决 定 转 而 使 用 Git, 那 么 该 项 目 必 须 经 历 某 种 形 式 的 迁 移 本 节 将 介 绍 Git 中 包 含 的 一 些 针 对 常 见 系 统 的 导 入 脚 本, 并 将 展 示 编 写 自 定 义 的 导 入 脚 本 的 方 法 导 入 你 将 学 习 到 如 何 从 专 业 重 量 级 的 版 本 控 制 系 统 中 导 入 数 据 Subversion 和 Perforce 因 为 据 我 所 知 这 二 者 的 用 户 是 ( 向 Git) 转 换 的 主 要 群 体, 而 且 Git 为 此 二 者 附 带 了 高 质 量 的 转 换 工 具 Subversion 读 过 前 一 节 有 关 git svn 的 内 容 以 后, 你 应 该 能 轻 而 易 举 的 根 据 其 中 的 指 导 来 git svn clone 一 个 仓 库 了 ; 然 后, 停 止 Subversion 的 使 用, 向 一 个 新 Git server 推 送, 并 开 始 使 用 它 想 保 留 历 史 记 录, 所 花 的 时 间 应 该 不 过 就 是 从 Subversion 服 务 器 拉 取 数 据 的 时 间 ( 可 能 要 等 上 好 一 会 就 是 了 ) 然 而, 这 样 的 导 入 并 不 完 美 ; 而 且 还 要 花 那 么 多 时 间, 不 如 干 脆 一 次 把 它 做 对! 首 当 其 冲 的 任 务 是 作 者 信 息 在 Subversion, 每 个 提 交 者 在 都 在 主 机 上 有 一 个 用 户 名, 记 录 在 提 交 信 息 中 上 节 例 子 中 多 处 显 示 了 schacon, 比 如 blame 的 输 出 以 及 git svn log 如 果 想 让 这 条 信 息 更 好 的 映 射 到 Git 作 者 数 据 里, 则 需 要 从 Subversion 用 户 名 到 Git 作 者 的 一 个 映 射 关 系 建 立 一 个 叫 做 user.txt 的 文 件, 用 如 下 格 式 表 示 映 射 关 系 : schacon = Scott Chacon <schacon@ge .com> selse = Someo Nelse <selse@ge .com> 241

252 第 8 章 Git 与 其 他 系 统 Sco Chacon Pro Git 中 文 版 通 过 该 命 令 可 以 获 得 SVN 作 者 的 列 表 : $ svn log ^/ --xml grep -P "^<author" sort -u \ perl -pe 's/<author>(.*?)<\/author>/$1 = /' > users.txt 它 将 输 出 XML 格 式 的 日 志 你 可 以 找 到 作 者, 建 立 一 个 单 独 的 列 表, 然 后 从 XML 中 抽 取 出 需 要 的 信 息 ( 显 而 易 见, 本 方 法 要 求 主 机 上 安 装 了 grep,sort 和 perl.) 然 后 把 输 出 重 定 向 到 user.txt 文 件, 然 后 就 可 以 在 每 一 项 的 后 面 添 加 相 应 的 Git 用 户 数 据 为 git svn 提 供 该 文 件 可 以 然 它 更 精 确 的 映 射 作 者 数 据 你 还 可 以 在 clone 或 者 init 后 面 添 加 --no-metadata 来 阻 止 git svn 包 含 那 些 Subversion 的 附 加 信 息 这 样 import 命 令 就 变 成 了 : $ git svn clone \ --authors-file=users.txt --no-metadata -s my_project 现 在 my_project 目 录 下 导 入 的 Subversion 应 该 比 原 来 整 洁 多 了 原 来 的 commit 看 上 去 是 这 样 : commit 37efa680e8473b615de980fa a35a Author: schacon <schacon@4c93b f-11de-be05-5f7a > Date: Sun May 3 00:12: fixed install - go to trunk git-svn-id: 4c93b f-11debe05-5f7a 现 在 是 这 样 : commit 03a8785f44c8ea5cdb0e8834b7c8e6c469be2ff2 Author: Scott Chacon <schacon@ge .com> Date: Sun May 3 00:12: fixed install - go to trunk 242

253 Sco Chacon Pro Git 中 文 版 8.2 节 迁 移 到 Git 不 仅 作 者 一 项 干 净 了 不 少,git-svn-id 也 就 此 消 失 了 你 还 需 要 一 点 post-import( 导 入 后 ) 清 理 工 作 最 起 码 的, 应 该 清 理 一 下 git svn 创 建 的 那 些 怪 异 的 索 引 结 构 首 先 要 移 动 标 签, 把 它 们 从 奇 怪 的 远 程 分 支 变 成 实 际 的 标 签, 然 后 把 剩 下 的 分 支 移 动 到 本 地 要 把 标 签 变 成 合 适 的 Git 标 签, 运 行 $ git for-each-ref refs/remotes/tags cut -d / -f 4- grep while read tagname; d 该 命 令 将 原 本 以 tag/ 开 头 的 远 程 分 支 的 索 引 变 成 真 正 的 ( 轻 巧 的 ) 标 签 接 下 来, 把 refs/remotes 下 面 剩 下 的 索 引 变 成 本 地 分 支 : $ git for-each-ref refs/remotes cut -d / -f 3- grep while read branchname; do g 现 在 所 有 的 旧 分 支 都 变 成 真 正 的 Git 分 支, 所 有 的 旧 标 签 也 变 成 真 正 的 Git 标 签 最 后 一 项 工 作 就 是 把 新 建 的 Git 服 务 器 添 加 为 远 程 服 务 器 并 且 向 它 推 送 下 面 是 新 增 远 程 服 务 器 的 例 子 : $ git remote add origin git@my-git-server:myrepository.git 为 了 让 所 有 的 分 支 和 标 签 都 得 到 上 传, 我 们 使 用 这 条 命 令 : $ git push origin --all $ git push origin --tags 所 有 的 分 支 和 标 签 现 在 都 应 该 整 齐 干 净 的 躺 在 新 的 Git 服 务 器 里 了 Perforce 你 将 了 解 到 的 下 一 个 被 导 入 的 系 统 是 Perforce. Git 发 行 的 时 候 同 时 也 附 带 了 一 个 Perforce 导 入 脚 本, 不 过 它 是 包 含 在 源 码 的 contrib 部 分 而 不 像 git svn 那 样 默 认 可 用 运 行 它 之 前 必 须 获 取 Git 的 源 码, 可 以 在 git.kernel.org 下 载 : $ git clone git://git.kernel.org/pub/scm/git/git.git $ cd git/contrib/fast-import 243

254 第 8 章 Git 与 其 他 系 统 Sco Chacon Pro Git 中 文 版 在 这 个 fast-import 目 录 下, 应 该 有 一 个 叫 做 git-p4 的 Python 可 执 行 脚 本 主 机 上 必 须 装 有 Python 和 p4 工 具 该 导 入 才 能 正 常 进 行 例 如, 你 要 从 Perforce 公 共 代 码 仓 库 ( 译 注 :Perforce Public Depot,Perforce 官 方 提 供 的 代 码 寄 存 服 务 ) 导 入 Jam 工 程 为 了 设 定 客 户 端, 我 们 要 把 P4PORT 环 境 变 量 export 到 Perforce 仓 库 : $ export P4PORT=public.perforce.com:1666 运 行 git-p4 clone 命 令 将 从 Perforce 服 务 器 导 入 Jam 项 目, 我 们 需 要 给 出 仓 库 和 项 目 的 路 径 以 及 导 入 的 目 标 路 径 : $ git-p4 clone //public/jam/src@all /opt/p4import Importing from //public/jam/src@all into /opt/p4import Reinitialized existing Git repository in /opt/p4import/.git/ Import destination: refs/remotes/p4/master Importing revision 4409 (100%) 现 在 去 /opt/p4import 目 录 运 行 一 下 git log, 就 能 看 到 导 入 的 成 果 : $ git log -2 commit 1fd4ec efd2db83548b85b1bbbc07dc2 Author: Perforce staff <support@perforce.com> Date: Thu Aug 19 10:18: Drop 'rc3' moniker of jam-2.5. the main part of the document. Folded rc2 and rc3 RELNOTES into Built new tar/zip balls. Only 16 months later. [git-p4: depot-paths = "//public/jam/src/": change = 4409] commit ca8870db541a23ed867f38847eda65bf d Author: Richard Geiger <rmg@perforce.com> Date: Tue Apr 22 20:51: Update derived jamgram.c 244

255 Sco Chacon Pro Git 中 文 版 8.2 节 迁 移 到 Git [git-p4: depot-paths = "//public/jam/src/": change = 3108] 每 一 个 commit 里 都 有 一 个 git-p4 标 识 符 这 个 标 识 符 可 以 保 留, 以 防 以 后 需 要 引 用 Perforce 的 修 改 版 本 号 然 而, 如 果 想 删 除 这 些 标 识 符, 现 在 正 是 时 候 在 开 启 新 仓 库 之 前 可 以 通 过 git filter-branch 来 批 量 删 除 这 些 标 识 符 : $ git filter-branch --msg-filter ' sed -e "/^\[git-p4:/d" ' Rewrite 1fd4ec efd2db83548b85b1bbbc07dc2 (123/123) Ref 'refs/heads/master' was rewritten 现 在 运 行 一 下 git log, 你 会 发 现 这 些 commit 的 SHA-1 校 验 值 都 发 生 了 改 变, 而 那 些 git-p4 字 串 则 从 提 交 信 息 里 消 失 了 : $ git log -2 commit 10a16d60cffca14d454a15c f4082bc5b0 Author: Perforce staff <support@perforce.com> Date: Thu Aug 19 10:18: Drop 'rc3' moniker of jam-2.5. the main part of the document. Folded rc2 and rc3 RELNOTES into Built new tar/zip balls. Only 16 months later. commit 2b6c6db311dd76c34c66ec1c40a49405e6b527b2 Author: Richard Geiger <rmg@perforce.com> Date: Tue Apr 22 20:51: Update derived jamgram.c 至 此 导 入 已 经 完 成, 可 以 开 始 向 新 的 Git 服 务 器 推 送 了 自 定 导 入 脚 本 如 果 先 前 的 系 统 不 是 Subversion 或 Perforce 之 一, 先 上 网 找 一 下 有 没 有 与 之 对 应 的 导 入 脚 本 导 入 CVS,Clear Case,Visual Source Safe, 甚 至 存 档 目 录 的 导 入 245

256 第 8 章 Git 与 其 他 系 统 Sco Chacon Pro Git 中 文 版 脚 本 已 经 存 在 假 如 这 些 工 具 都 不 适 用, 或 者 使 用 的 工 具 很 少 见, 抑 或 你 需 要 导 入 过 程 具 有 更 多 可 制 定 性, 则 应 该 使 用 git fast-import 该 命 令 从 标 准 输 入 读 取 简 单 的 指 令 来 写 入 具 体 的 Git 数 据 这 样 创 建 Git 对 象 比 运 行 纯 Git 命 令 或 者 手 动 写 对 象 要 简 单 的 多 ( 更 多 相 关 内 容 见 第 九 章 ) 通 过 它, 你 可 以 编 写 一 个 导 入 脚 本 来 从 导 入 源 读 取 必 要 的 信 息, 同 时 在 标 准 输 出 直 接 输 出 相 关 指 示 你 可 以 运 行 该 脚 本 并 把 它 的 输 出 管 道 连 接 到 git fast-import 下 面 演 示 一 下 如 何 编 写 一 个 简 单 的 导 入 脚 本 假 设 你 在 进 行 一 项 工 作, 并 且 按 时 通 过 把 工 作 目 录 复 制 为 以 时 间 戳 back_yy_mm_dd 命 名 的 目 录 来 进 行 备 份, 现 在 你 需 要 把 它 们 导 入 Git 目 录 结 构 如 下 : $ ls /opt/import_from back_2009_01_02 back_2009_01_04 back_2009_01_14 back_2009_02_03 current 为 了 导 入 到 一 个 Git 目 录, 我 们 首 先 回 顾 一 下 Git 储 存 数 据 的 方 式 你 可 能 还 记 得,Git 本 质 上 是 一 个 commit 对 象 的 链 表, 每 一 个 对 象 指 向 一 个 内 容 的 快 照 而 这 里 需 要 做 的 工 作 就 是 告 诉 fast-import 内 容 快 照 的 位 置, 什 么 样 的 commit 数 据 指 向 它 们, 以 及 它 们 的 顺 序 我 们 采 取 一 次 处 理 一 个 快 照 的 策 略, 为 每 一 个 内 容 目 录 建 立 对 应 的 commit, 每 一 个 commit 与 之 前 的 建 立 链 接 正 如 在 第 七 章 Git 执 行 策 略 一 例 一 节 中 一 样, 我 们 将 使 用 Ruby 来 编 写 这 个 脚 本, 因 为 它 是 我 日 常 使 用 的 语 言 而 且 阅 读 起 来 简 单 一 些 你 可 以 用 任 何 其 他 熟 悉 的 语 言 来 重 写 这 个 例 子 它 仅 需 要 把 必 要 的 信 息 打 印 到 标 准 输 出 而 已 同 时, 如 果 你 在 使 用 Windows, 这 意 味 着 你 要 特 别 留 意 不 要 在 换 行 的 时 候 引 入 回 车 符 ( 译 注 : carriage returns,windows 换 行 时 加 入 的 符 号, 通 常 说 的 \r ) Git 的 fast-import 对 仅 使 用 换 行 符 (LF) 而 非 Windows 的 回 车 符 (CRLF) 要 求 非 常 严 格 首 先, 进 入 目 标 目 录 并 且 找 到 所 有 子 目 录, 每 一 个 子 目 录 将 作 为 一 个 快 照 被 导 入 为 一 个 commit 我 们 将 依 次 进 入 每 一 个 子 目 录 并 打 印 所 需 的 命 令 来 导 出 它 们 脚 本 的 主 循 环 大 致 是 这 样 : last_mark = nil # 循 环 遍 历 所 有 目 录 Dir.chdir(ARGV[0]) do Dir.glob("*").each do dir next if File.file?(dir) 246

257 Sco Chacon Pro Git 中 文 版 8.2 节 迁 移 到 Git # 进 入 目 标 目 录 Dir.chdir(dir) do last_mark = print_export(dir, last_mark) end end end 我 们 在 每 一 个 目 录 里 运 行 print_export, 它 会 取 出 上 一 个 快 照 的 索 引 和 标 记 并 返 回 本 次 快 照 的 索 引 和 标 记 ; 由 此 我 们 就 可 以 正 确 的 把 二 者 连 接 起 来 标 记 (mark) 是 fast-import 中 对 commit 标 识 符 的 叫 法 ; 在 创 建 commit 的 同 时, 我 们 逐 一 赋 予 一 个 标 记 以 便 以 后 在 把 它 连 接 到 其 他 commit 时 使 用 因 此, 在 print_export 方 法 中 要 做 的 第 一 件 事 就 是 根 据 目 录 名 生 成 一 个 标 记 : mark = convert_dir_to_mark(dir) 实 现 该 函 数 的 方 法 是 建 立 一 个 目 录 的 数 组 序 列 并 使 用 数 组 的 索 引 值 作 为 标 记, 因 为 标 记 必 须 是 一 个 整 数 这 个 方 法 大 致 是 这 样 的 : $marks = [] def convert_dir_to_mark(dir) if!$marks.include?(dir) $marks << dir end ($marks.index(dir) + 1).to_s end 有 了 整 数 来 代 表 每 个 commit, 我 们 现 在 需 要 提 交 附 加 信 息 中 的 日 期 由 于 日 期 是 用 目 录 名 表 示 的, 我 们 就 从 中 解 析 出 来 print_export 文 件 的 下 一 行 将 是 : date = convert_dir_to_date(dir) 而 convert_dir_to_date 则 定 义 为 247

258 第 8 章 Git 与 其 他 系 统 Sco Chacon Pro Git 中 文 版 def convert_dir_to_date(dir) if dir == 'current' return Time.now().to_i else dir = dir.gsub('back_', '') (year, month, day) = dir.split('_') return Time.local(year, month, day).to_i end end 它 为 每 个 目 录 返 回 一 个 整 型 值 提 交 附 加 信 息 里 最 后 一 项 所 需 的 是 提 交 者 数 据, 我 们 在 一 个 全 局 变 量 中 直 接 定 义 之 : $author = 'Scott Chacon <schacon@example.com>' 我 们 差 不 多 可 以 开 始 为 导 入 脚 本 输 出 提 交 数 据 了 第 一 项 信 息 指 明 我 们 定 义 的 是 一 个 commit 对 象 以 及 它 所 在 的 分 支, 随 后 是 我 们 生 成 的 标 记, 提 交 者 信 息 以 及 提 交 备 注, 然 后 是 前 一 个 commit 的 索 引, 如 果 有 的 话 代 码 大 致 这 样 : # 打 印 导 入 所 需 的 信 息 puts 'commit refs/heads/master' puts 'mark :' + mark puts "committer #{$author} #{date} -0700" export_data('imported from ' + dir) puts 'from :' + last_mark if last_mark 时 区 (-0700) 处 于 简 化 目 的 使 用 硬 编 码 如 果 是 从 其 他 版 本 控 制 系 统 导 入, 则 必 须 以 变 量 的 形 式 指 明 时 区 提 交 备 注 必 须 以 特 定 格 式 给 出 : data (size)\n(contents) 该 格 式 包 含 了 单 词 data, 所 读 取 数 据 的 大 小, 一 个 换 行 符, 最 后 是 数 据 本 身 由 于 随 后 指 明 文 件 内 容 的 时 候 要 用 到 相 同 的 格 式, 我 们 写 一 个 辅 助 方 法,export_data: 248

259 Sco Chacon Pro Git 中 文 版 8.2 节 迁 移 到 Git def export_data(string) end print "data #{string.size}\n#{string}" 唯 一 剩 下 的 就 是 每 一 个 快 照 的 内 容 了 这 简 单 的 很, 因 为 它 们 分 别 处 于 一 个 目 录 你 可 以 输 出 deleeall 命 令, 随 后 是 目 录 中 每 个 文 件 的 内 容 Git 会 正 确 的 记 录 每 一 个 快 照 : puts 'deleteall' Dir.glob("**/*").each do file next if!file.file?(file) inline_data(file) end 注 意 : 由 于 很 多 系 统 把 每 次 修 订 看 作 一 个 commit 到 另 一 个 commit 的 变 化 量, fast-import 也 可 以 依 据 每 次 提 交 获 取 一 个 命 令 来 指 出 哪 些 文 件 被 添 加, 删 除 或 者 修 改 过, 以 及 修 改 的 内 容 我 们 将 需 要 计 算 快 照 之 间 的 差 别 并 且 仅 仅 给 出 这 项 数 据, 不 过 该 做 法 要 复 杂 很 多 还 如 不 直 接 把 所 有 数 据 丢 给 Git 然 它 自 己 搞 清 楚 假 如 前 面 这 个 方 法 更 适 用 于 你 的 数 据, 参 考 fast-import 的 man 帮 助 页 面 来 了 解 如 何 以 这 种 方 式 提 供 数 据 列 举 新 文 件 内 容 或 者 指 明 带 有 新 内 容 的 已 修 改 文 件 的 格 式 如 下 : M 644 inline path/to/file data (size) (file contents) 这 里,644 是 权 限 模 式 ( 加 入 有 可 执 行 文 件, 则 需 要 探 测 之 并 设 定 为 755), 而 inline 说 明 我 们 在 本 行 结 束 之 后 立 即 列 出 文 件 的 内 容 我 们 的 inline_data 方 法 大 致 是 : def inline_data(file, code = 'M', mode = '644') content = File.read(file) puts "#{code} #{mode} inline #{file}" export_data(content) end 249

260 第 8 章 Git 与 其 他 系 统 Sco Chacon Pro Git 中 文 版 一 辙 我 们 重 用 了 前 面 定 义 过 的 export_data, 因 为 这 里 和 指 明 提 交 注 释 的 格 式 如 出 最 后 一 项 工 作 是 返 回 当 前 的 标 记 以 便 下 次 循 环 的 使 用 return mark 注 意 : 如 果 你 在 用 Windows, 一 定 记 得 添 加 一 项 额 外 的 步 骤 前 面 提 过, Windows 使 用 CRLF 作 为 换 行 字 符 而 Git fast-import 只 接 受 LF 为 了 绕 开 这 个 问 题 来 满 足 git fast-import, 你 需 要 让 ruby 用 LF 取 代 CRLF: $stdout.binmode 搞 定 了 现 在 运 行 该 脚 本, 你 将 得 到 如 下 内 容 : $ ruby import.rb /opt/import_from commit refs/heads/master mark :1 committer Scott Chacon <schacon@ge .com> data 29 imported from back_2009_01_02deleteall M 644 inline file.rb data 12 version two commit refs/heads/master mark :2 committer Scott Chacon <schacon@ge .com> data 29 imported from back_2009_01_04from :1 deleteall M 644 inline file.rb data 14 version three M 644 inline new.rb data 16 new version one (...) 250

261 Sco Chacon Pro Git 中 文 版 8.2 节 迁 移 到 Git 要 运 行 导 入 脚 本, 在 需 要 导 入 的 目 录 把 该 内 容 用 管 道 定 向 到 git fast-import 你 可 以 建 立 一 个 空 目 录 然 后 运 行 git init 作 为 开 头, 然 后 运 行 该 脚 本 : $ git init Initialized empty Git repository in /opt/import_to/.git/ $ ruby import.rb /opt/import_from git fast-import git-fast-import statistics: Alloc'd objects: 5000 Total objects: 18 ( 1 duplicates ) blobs : 7 ( 1 duplicates 0 deltas) trees : 6 ( 0 duplicates 1 deltas) commits: 5 ( 0 duplicates 0 deltas) tags : 0 ( 0 duplicates 0 deltas) Total branches: 1 ( 1 loads ) marks: 1024 ( 5 unique ) atoms: 3 Memory total: 2255 KiB pools: 2098 KiB objects: 156 KiB pack_report: getpagesize() = 4096 pack_report: core.packedgitwindowsize = pack_report: core.packedgitlimit = pack_report: pack_used_ctr = 9 pack_report: pack_mmap_calls = 5 pack_report: pack_open_windows = 1 / 1 pack_report: pack_mapped = 1356 / 你 会 发 现, 在 它 成 功 执 行 完 毕 以 后, 会 给 出 一 堆 有 关 已 完 成 工 作 的 数 据 上 例 在 一 个 分 支 导 入 了 5 次 提 交 数 据, 包 含 了 18 个 对 象 现 在 可 以 运 行 git log 来 检 视 新 的 历 史 : $ git log -2 commit 10bfe7d22ce15ee25b60a824c ca593d41 Author: Scott Chacon <schacon@example.com> Date: Sun May 3 12:57:

262 第 8 章 Git 与 其 他 系 统 Sco Chacon Pro Git 中 文 版 imported from current commit 7e519590de754d079dd73b44d695a42c9d2df452 Author: Scott Chacon <schacon@example.com> Date: Tue Feb 3 01:00: imported from back_2009_02_03 就 它 了 一 个 干 净 整 洁 的 Git 仓 库 需 要 注 意 的 是 此 时 没 有 任 何 内 容 被 检 出 刚 开 始 当 前 目 录 里 没 有 任 何 文 件 要 获 取 它 们, 你 得 转 到 master 分 支 的 所 在 : $ ls $ git reset --hard master HEAD is now at 10bfe7d imported from current $ ls file.rb lib fast-import 还 可 以 做 更 多 处 理 不 同 的 文 件 模 式, 二 进 制 文 件, 多 重 分 支 与 合 并, 标 签, 进 展 标 识 等 等 一 些 更 加 复 杂 的 实 例 可 以 在 Git 源 码 的 contib/fast-import 目 录 里 找 到 ; 其 中 较 为 出 众 的 是 前 面 提 过 的 git-p4 脚 本 8.3 总 结 现 在 的 你 应 该 掌 握 了 在 Subversion 上 使 用 Git 以 及 把 几 乎 任 何 先 存 仓 库 无 损 失 的 导 入 为 Git 仓 库 下 一 章 将 介 绍 Git 内 部 的 原 始 数 据 格 式, 从 而 是 使 你 能 亲 手 锻 造 其 中 的 每 一 个 字 节, 如 果 必 要 的 话 252

263 第 9 章 Git 内 部 原 理 不 管 你 是 从 前 面 的 章 节 直 接 跳 到 了 本 章, 还 是 读 完 了 其 余 各 章 一 直 到 这, 你 都 将 在 本 章 见 识 Git 的 内 部 工 作 原 理 和 实 现 方 式 我 个 人 发 现 学 习 这 些 内 容 对 于 理 解 Git 的 用 处 和 强 大 是 非 常 重 要 的, 不 过 也 有 人 认 为 这 些 内 容 对 于 初 学 者 来 说 可 能 难 以 理 解 且 过 于 复 杂 正 因 如 此 我 把 这 部 分 内 容 放 在 最 后 一 章, 你 在 学 习 过 程 中 可 以 先 阅 读 这 部 分, 也 可 以 晚 点 阅 读 这 部 分, 这 完 全 取 决 于 你 自 己 既 然 已 经 读 到 这 了, 就 让 我 们 开 始 吧 首 先 要 弄 明 白 一 点, 从 根 本 上 来 讲 Git 是 一 套 内 容 寻 址 (content-addressable) 文 件 系 统, 在 此 之 上 提 供 了 一 个 VCS 用 户 界 面 马 上 你 就 会 学 到 这 意 味 着 什 么 早 期 的 Git ( 主 要 是 1.5 之 前 版 本 ) 的 用 户 界 面 要 比 现 在 复 杂 得 多, 这 是 因 为 它 更 侧 重 于 成 为 文 件 系 统 而 不 是 一 套 更 精 致 的 VCS 最 近 几 年 改 进 了 UI 从 而 使 它 跟 其 他 任 何 系 统 一 样 清 晰 易 用 即 便 如 此, 还 是 经 常 会 有 一 些 陈 腔 滥 调 提 到 早 期 Git 的 UI 复 杂 又 难 学 内 容 寻 址 文 件 系 统 层 相 当 酷, 在 本 章 中 我 会 先 讲 解 这 部 分 随 后 你 会 学 到 传 输 机 制 和 最 终 要 使 用 的 各 种 库 管 理 任 务 9.1 底 层 命 令 (Plumbing) 和 高 层 命 令 (Porcelain) 本 书 讲 解 了 使 用 checkout, branch, remote 等 共 约 30 个 Git 命 令 然 而 由 于 Git 一 开 始 被 设 计 成 供 VCS 使 用 的 工 具 集 而 不 是 一 整 套 用 户 友 好 的 VCS, 它 还 包 含 了 许 多 底 层 命 令, 这 些 命 令 用 于 以 UNIX 风 格 使 用 或 由 脚 本 调 用 这 些 命 令 一 般 被 称 为 plumbing 命 令 ( 底 层 命 令 ), 其 他 的 更 友 好 的 命 令 则 被 称 为 porcelain 命 令 ( 高 层 命 令 ) 本 书 前 八 章 主 要 专 门 讨 论 高 层 命 令 本 章 将 主 要 讨 论 底 层 命 令 以 理 解 Git 的 内 部 工 作 机 制 演 示 Git 如 何 及 为 何 要 以 这 种 方 式 工 作 这 些 命 令 主 要 不 是 用 来 从 命 令 行 手 工 使 用 的, 更 多 的 是 用 来 为 其 他 工 具 和 自 定 义 脚 本 服 务 的 当 你 在 一 个 新 目 录 或 已 有 目 录 内 执 行 git init 时,Git 会 创 建 一 个.git 目 录, 几 乎 所 有 Git 存 储 和 操 作 的 内 容 都 位 于 该 目 录 下 如 果 你 要 备 份 或 复 制 一 个 库, 基 本 上 将 这 一 目 录 拷 贝 至 其 他 地 方 就 可 以 了 本 章 基 本 上 都 讨 论 该 目 录 下 的 内 容 该 目 录 结 构 如 下 : 253

264 第 9 章 Git 内 部 原 理 Sco Chacon Pro Git 中 文 版 $ ls HEAD branches/ config description hooks/ index info/ objects/ refs/ 该 目 录 下 有 可 能 还 有 其 他 文 件, 但 这 是 一 个 全 新 的 git init 生 成 的 库, 所 以 默 认 情 况 下 这 些 就 是 你 能 看 到 的 结 构 新 版 本 的 Git 不 再 使 用 branches 目 录, description 文 件 仅 供 GitWeb 程 序 使 用, 所 以 不 用 关 心 这 些 内 容 config 文 件 包 含 了 项 目 特 有 的 配 置 选 项,info 目 录 保 存 了 一 份 不 希 望 在.gitignore 文 件 中 管 理 的 忽 略 模 式 (ignored pa erns) 的 全 局 可 执 行 文 件 hooks 目 录 保 存 了 第 七 章 详 细 介 绍 了 的 客 户 端 或 服 务 端 钩 子 脚 本 另 外 还 有 四 个 重 要 的 文 件 或 目 录 :HEAD 及 index 文 件,objects 及 refs 目 录 这 些 是 Git 的 核 心 部 分 objects 目 录 存 储 所 有 数 据 内 容,refs 目 录 存 储 指 向 数 据 ( 分 支 ) 的 提 交 对 象 的 指 针,HEAD 文 件 指 向 当 前 分 支,index 文 件 保 存 了 暂 存 区 域 信 息 马 上 你 将 详 细 了 解 Git 是 如 何 操 纵 这 些 内 容 的 9.2 Git 对 象 Git 是 一 套 内 容 寻 址 文 件 系 统 很 不 错 不 过 这 是 什 么 意 思 呢? 这 种 说 法 的 意 思 是,Git 从 核 心 上 来 看 不 过 是 简 单 地 存 储 键 值 对 (key-value) 它 允 许 插 入 任 意 类 型 的 内 容, 并 会 返 回 一 个 键 值, 通 过 该 键 值 可 以 在 任 何 时 候 再 取 出 该 内 容 可 以 通 过 底 层 命 令 hash-object 来 示 范 这 点, 传 一 些 数 据 给 该 命 令, 它 会 将 数 据 保 存 在.git 目 录 并 返 回 表 示 这 些 数 据 的 键 值 首 先 初 使 化 一 个 Git 仓 库 并 确 认 objects 目 录 是 空 的 : $ mkdir test $ cd test $ git init Initialized empty Git repository in /tmp/test/.git/ $ find.git/objects.git/objects 254

265 Sco Chacon Pro Git 中 文 版 9.2 节 Git 对 象.git/objects/info.git/objects/pack $ find.git/objects -type f $ Git 初 始 化 了 objects 目 录, 同 时 在 该 目 录 下 创 建 了 pack 和 info 子 目 录, 但 是 该 目 录 下 没 有 其 他 常 规 文 件 我 们 往 这 个 Git 数 据 库 里 存 储 一 些 文 本 : $ echo 'test content' git hash-object -w --stdin d670460b4b4aece5915caf5c68d12f560a9fe3e4 参 数 -w 指 示 hash-object 命 令 存 储 ( 数 据 ) 对 象, 若 不 指 定 这 个 参 数 该 命 令 仅 仅 返 回 键 值 --stdin 指 定 从 标 准 输 入 设 备 (stdin) 来 读 取 内 容, 若 不 指 定 这 个 参 数 则 需 指 定 一 个 要 存 储 的 文 件 的 路 径 该 命 令 输 出 长 度 为 40 个 字 符 的 校 验 和 这 是 个 SHA-1 哈 希 值 其 值 为 要 存 储 的 数 据 加 上 你 马 上 会 了 解 到 的 一 种 头 信 息 的 校 验 和 现 在 可 以 查 看 到 Git 已 经 存 储 了 数 据 : $ find.git/objects -type f.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4 可 以 在 objects 目 录 下 看 到 一 个 文 件 这 便 是 Git 存 储 数 据 内 容 的 方 式 为 每 份 内 容 生 成 一 个 文 件, 取 得 该 内 容 与 头 信 息 的 SHA-1 校 验 和, 创 建 以 该 校 验 和 前 两 个 字 符 为 名 称 的 子 目 录, 并 以 ( 校 验 和 ) 剩 下 38 个 字 符 为 文 件 命 名 ( 保 存 至 子 目 录 下 ) 通 过 cat-file 命 令 可 以 将 数 据 内 容 取 回 该 命 令 是 查 看 Git 对 象 的 瑞 士 军 刀 传 入 -p 参 数 可 以 让 该 命 令 输 出 数 据 内 容 的 类 型 : $ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4 test content 可 以 往 Git 中 添 加 更 多 内 容 并 取 回 了 也 可 以 直 接 添 加 文 件 比 方 说 可 以 对 一 个 文 件 进 行 简 单 的 版 本 控 制 首 先, 创 建 一 个 新 文 件, 并 把 文 件 内 容 存 储 到 数 据 库 中 : 255

266 第 9 章 Git 内 部 原 理 Sco Chacon Pro Git 中 文 版 $ echo 'version 1' > test.txt $ git hash-object -w test.txt 83baae61804e65cc73a7201a c76066a30 接 着 往 该 文 件 中 写 入 一 些 新 内 容 并 再 次 保 存 : $ echo 'version 2' > test.txt $ git hash-object -w test.txt 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a 数 据 库 中 已 经 将 文 件 的 两 个 新 版 本 连 同 一 开 始 的 内 容 保 存 下 来 了 : $ find.git/objects -type f.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a.git/objects/83/baae61804e65cc73a7201a c76066a30.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4 再 将 文 件 恢 复 到 第 一 个 版 本 : $ git cat-file -p 83baae61804e65cc73a7201a c76066a30 > test.txt $ cat test.txt version 1 或 恢 复 到 第 二 个 版 本 : $ git cat-file -p 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a > test.txt $ cat test.txt version 2 需 要 记 住 的 是 几 个 版 本 的 文 件 SHA-1 值 可 能 与 实 际 的 值 不 同, 其 次, 存 储 的 并 不 是 文 件 名 而 仅 仅 是 文 件 内 容 这 种 对 象 类 型 称 为 blob 通 过 传 递 SHA-1 值 给 cat-file -t 命 令 可 以 让 Git 返 回 任 何 对 象 的 类 型 : 256

267 Sco Chacon Pro Git 中 文 版 9.2 节 Git 对 象 $ git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a blob tree ( 树 ) 对 象 接 下 去 来 看 tree 对 象,tree 对 象 可 以 存 储 文 件 名, 同 时 也 允 许 存 储 一 组 文 件 Git 以 一 种 类 似 UNIX 文 件 系 统 但 更 简 单 的 方 式 来 存 储 内 容 所 有 内 容 以 tree 或 blob 对 象 存 储, 其 中 tree 对 象 对 应 于 UNIX 中 的 目 录,blob 对 象 则 大 致 对 应 于 inodes 或 文 件 内 容 一 个 单 独 的 tree 对 象 包 含 一 条 或 多 条 tree 记 录, 每 一 条 记 录 含 有 一 个 指 向 blob 或 子 tree 对 象 的 SHA-1 指 针, 并 附 有 该 对 象 的 权 限 模 式 (mode) 类 型 和 文 件 名 信 息 以 simplegit 项 目 为 例, 最 新 的 tree 可 能 是 这 个 样 子 : $ git cat-file -p master^{tree} blob a906cb2a4a904a152e80877d daad0c859 README blob 8f f9404f26296befa88755fc2598c289 Rakefile tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0 lib masterˆ{tree} 表 示 branch 分 支 上 最 新 提 交 指 向 的 tree 对 象 请 注 意 lib 子 目 录 并 非 一 个 blob 对 象, 而 是 一 个 指 向 另 一 个 tree 对 象 的 指 针 : $ git cat-file -p 99f1a6d12cb4b6f19c8655fca46c3ecf317074e blob 47c6340d6459e05787f644c2447d2595f5d3a54b simplegit.rb 从 概 念 上 来 讲,Git 保 存 的 数 据 如 图 9-1 所 示 你 可 以 自 己 创 建 tree 通 常 Git 根 据 你 的 暂 存 区 域 或 index 来 创 建 并 写 入 一 个 tree 因 此 要 创 建 一 个 tree 对 象 的 话 首 先 要 通 过 将 一 些 文 件 暂 存 从 而 创 建 一 个 index 可 以 使 用 plumbing 命 令 update-index 为 一 个 单 独 文 件 test.txt 文 件 的 第 一 个 版 本 创 建 一 个 index 通 过 该 命 令 人 为 的 将 test.txt 文 件 的 首 个 版 本 加 入 到 了 一 个 新 的 暂 存 区 域 中 由 于 该 文 件 原 先 并 不 在 暂 存 区 域 中 ( 甚 至 就 连 暂 存 区 域 也 还 没 被 创 建 出 来 呢 ), 必 须 传 入 --add 参 数 ; 由 于 要 添 加 的 文 件 并 不 在 当 前 目 录 下 而 是 在 数 据 库 中, 必 须 传 入 --cacheinfo 参 数 同 时 指 定 了 文 件 模 式,SHA-1 值 和 文 件 名 : $ git update-index --add --cacheinfo \ 83baae61804e65cc73a7201a c76066a30 test.txt 257

268 第 9 章 Git 内 部 原 理 Sco Chacon Pro Git 中 文 版 图 9.1: Git 对 象 模 型 的 简 化 版 在 本 例 中, 指 定 了 文 件 模 式 为 , 表 明 这 是 一 个 普 通 文 件 其 他 可 用 的 模 式 有 : 表 示 可 执 行 文 件, 表 示 符 号 链 接 文 件 模 式 是 从 常 规 的 UNIX 文 件 模 式 中 参 考 来 的, 但 是 没 有 那 么 灵 活 上 述 三 种 模 式 仅 对 Git 中 的 文 件 (blobs) 有 效 ( 虽 然 也 有 其 他 模 式 用 于 目 录 和 子 模 块 ) 现 在 可 以 用 write-tree 命 令 将 暂 存 区 域 的 内 容 写 到 一 个 tree 对 象 了 无 需 -w 参 数 如 果 目 标 tree 不 存 在, 调 用 write-tree 会 自 动 根 据 index 状 态 创 建 一 个 tree 对 象 $ git write-tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 $ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f blob 83baae61804e65cc73a7201a c76066a30 test.txt 可 以 这 样 验 证 这 确 实 是 一 个 tree 对 象 : $ git cat-file -t d8329fc1cc938780ffdd9f94e0d364e0ea74f579 tree 再 根 据 test.txt 的 第 二 个 版 本 以 及 一 个 新 文 件 创 建 一 个 新 tree 对 象 : $ echo 'new file' > new.txt $ git update-index test.txt $ git update-index --add new.txt 258

269 Sco Chacon Pro Git 中 文 版 9.2 节 Git 对 象 这 时 暂 存 区 域 中 包 含 了 test.txt 的 新 版 本 及 一 个 新 文 件 new.txt 创 建 ( 写 ) 该 tree 对 象 ( 将 暂 存 区 域 或 index 状 态 写 入 到 一 个 tree 对 象 ), 然 后 瞧 瞧 它 的 样 子 : $ git write-tree 0155eb a0f03eb265b69f5a2d56f341 $ git cat-file -p 0155eb a0f03eb265b69f5a2d56f blob fa49b ad f2a75f74e3671e92 new.txt blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt 请 注 意 该 tree 对 象 包 含 了 两 个 文 件 记 录, 且 test.txt 的 SHA 值 是 早 先 值 的 第 二 版 (1f7a7a) 来 点 更 有 趣 的, 你 将 把 第 一 个 tree 对 象 作 为 一 个 子 目 录 加 进 该 tree 中 可 以 用 read-tree 命 令 将 tree 对 象 读 到 暂 存 区 域 中 去 在 这 时, 通 过 传 一 个 --prefix 参 数 给 read-tree, 将 一 个 已 有 的 tree 对 象 作 为 一 个 子 tree 读 到 暂 存 区 域 中 : $ git read-tree --prefix=bak d8329fc1cc938780ffdd9f94e0d364e0ea74f579 $ git write-tree 3c4e9cd789d88d8d89c c3585e41b0e614 $ git cat-file -p 3c4e9cd789d88d8d89c c3585e41b0e tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 bak blob fa49b ad f2a75f74e3671e92 new.txt blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt 如 果 从 刚 写 入 的 新 tree 对 象 创 建 一 个 工 作 目 录, 将 得 到 位 于 工 作 目 录 顶 级 的 两 个 文 件 和 一 个 名 为 bak 的 子 目 录, 该 子 目 录 包 含 了 test.txt 文 件 的 第 一 个 版 本 可 以 将 Git 用 来 包 含 这 些 内 容 的 数 据 想 象 成 如 图 9-2 所 示 的 样 子 图 9.2: 当 前 Git 数 据 的 内 容 结 构 259

270 第 9 章 Git 内 部 原 理 Sco Chacon Pro Git 中 文 版 commit ( 提 交 ) 对 象 你 现 在 有 三 个 tree 对 象, 它 们 指 向 了 你 要 跟 踪 的 项 目 的 不 同 快 照, 可 是 先 前 的 问 题 依 然 存 在 : 必 须 记 往 三 个 SHA-1 值 以 获 得 这 些 快 照 你 也 没 有 关 于 谁 何 时 以 及 为 何 保 存 了 这 些 快 照 的 信 息 commit 对 象 为 你 保 存 了 这 些 基 本 信 息 要 创 建 一 个 commit 对 象, 使 用 commit-tree 命 令, 指 定 一 个 tree 的 SHA-1, 如 果 有 任 何 前 继 提 交 对 象, 也 可 以 指 定 从 你 写 的 第 一 个 tree 开 始 : $ echo 'first commit' git commit-tree d8329f fdf4fc3344e67ab068f836878b6c4951e3b15f3d 通 过 cat-file 查 看 这 个 新 commit 对 象 : $ git cat-file -p fdf4fc3 tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 author Scott Chacon <schacon@gmail.com> committer Scott Chacon <schacon@gmail.com> first commit commit 对 象 有 格 式 很 简 单 : 指 明 了 该 时 间 点 项 目 快 照 的 顶 层 树 对 象 作 者 / 提 交 者 信 息 ( 从 Git 设 置 的 user.name 和 user. 中 获 得 ) 以 及 当 前 时 间 戳 一 个 空 行, 以 及 提 交 注 释 信 息 接 着 再 写 入 另 外 两 个 commit 对 象, 每 一 个 都 指 定 其 之 前 的 那 个 commit 对 象 : $ echo 'second commit' git commit-tree 0155eb -p fdf4fc3 cac0cab538b970a37ea1e769cbbde608743bc96d $ echo 'third commit' git commit-tree 3c4e9c -p cac0cab 1a410efbd13591db ebc7a059dd55cfe9 每 一 个 commit 对 象 都 指 向 了 你 创 建 的 树 对 象 快 照 出 乎 意 料 的 是, 现 在 已 经 有 了 真 实 的 Git 历 史 了, 所 以 如 果 运 行 git log 命 令 并 指 定 最 后 那 个 commit 对 象 的 SHA-1 便 可 以 查 看 历 史 : $ git log --stat 1a410e 260

271 Sco Chacon Pro Git 中 文 版 9.2 节 Git 对 象 commit 1a410efbd13591db ebc7a059dd55cfe9 Author: Scott Chacon Date: Fri May 22 18:15: third commit bak/test.txt files changed, 1 insertions(+), 0 deletions(-) commit cac0cab538b970a37ea1e769cbbde608743bc96d Author: Scott Chacon <schacon@gmail.com> Date: Fri May 22 18:14: second commit new.txt 1 + test.txt files changed, 2 insertions(+), 1 deletions(-) commit fdf4fc3344e67ab068f836878b6c4951e3b15f3d Author: Scott Chacon <schacon@gmail.com> Date: Fri May 22 18:09: first commit test.txt files changed, 1 insertions(+), 0 deletions(-) 真 棒 你 刚 刚 通 过 使 用 低 级 操 作 而 不 是 那 些 普 通 命 令 创 建 了 一 个 Git 历 史 这 基 本 上 就 是 运 行 git add 和 git commit 命 令 时 Git 进 行 的 工 作 保 存 修 改 了 的 文 件 的 blob, 更 新 索 引, 创 建 tree 对 象, 最 后 创 建 commit 对 象, 这 些 commit 对 象 指 向 了 顶 层 tree 对 象 以 及 先 前 的 commit 对 象 这 三 类 Git 对 象 blob,tree 以 及 commit 都 各 自 以 文 件 的 方 式 保 存 在.git/objects 目 录 下 以 下 所 列 是 目 前 为 止 样 例 中 的 所 有 对 象, 每 个 对 象 后 面 的 注 释 里 标 明 了 它 们 保 存 的 内 容 : $ find.git/objects -type f.git/objects/01/55eb a0f03eb265b69f5a2d56f341 # tree 2.git/objects/1a/410efbd13591db ebc7a059dd55cfe9 # commit 3 261

272 第 9 章 Git 内 部 原 理 Sco Chacon Pro Git 中 文 版.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a # test.txt v2.git/objects/3c/4e9cd789d88d8d89c c3585e41b0e614 # tree 3.git/objects/83/baae61804e65cc73a7201a c76066a30 # test.txt v1.git/objects/ca/c0cab538b970a37ea1e769cbbde608743bc96d # commit 2.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4 # 'test content'.git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579 # tree 1.git/objects/fa/49b ad f2a75f74e3671e92 # new.txt.git/objects/fd/f4fc3344e67ab068f836878b6c4951e3b15f3d # commit 1 如 果 你 按 照 以 上 描 述 进 行 了 操 作, 可 以 得 到 如 图 9-3 所 示 的 对 象 图 图 9.3: Git 目 录 下 的 所 有 对 象 对 象 存 储 之 前 我 提 到 当 存 储 数 据 内 容 时, 同 时 会 有 一 个 文 件 头 被 存 储 起 来 我 们 花 些 时 间 来 看 看 Git 是 如 何 存 储 对 象 的 你 将 看 来 如 何 通 过 Ruby 脚 本 语 言 存 储 一 个 blob 对 象 ( 这 里 以 字 符 串 what is up, doc? 为 例 ) 使 用 irb 命 令 进 入 Ruby 交 互 式 模 式 : $ irb >> content = "what is up, doc?" => "what is up, doc?" Git 以 对 象 类 型 为 起 始 内 容 构 造 一 个 文 件 头, 本 例 中 是 一 个 blob 然 后 添 加 一 个 空 格, 接 着 是 数 据 内 容 的 长 度, 最 后 是 一 个 空 字 节 (null byte): 262

273 Sco Chacon Pro Git 中 文 版 9.2 节 Git 对 象 >> header = "blob #{content.length}\0" => "blob 16\000" Git 将 文 件 头 与 原 始 数 据 内 容 拼 接 起 来, 并 计 算 拼 接 后 的 新 内 容 的 SHA- 1 校 验 和 可 以 在 Ruby 中 使 用 require 语 句 导 入 SHA1 digest 库, 然 后 调 用 Digest::SHA1.hexdigest() 方 法 计 算 字 符 串 的 SHA-1 值 : >> store = header + content => "blob 16\000what is up, doc?" >> require 'digest/sha1' => true >> sha1 = Digest::SHA1.hexdigest(store) => "bd9dbf5aae1a3862dd b20206e5fc37" Git 用 zlib 对 数 据 内 容 进 行 压 缩, 在 Ruby 中 可 以 用 zlib 库 来 实 现 首 先 需 要 导 入 该 库, 然 后 用 Zlib::Deflate.deflate() 对 数 据 进 行 压 缩 : >> require 'zlib' => true >> zlib_content = Zlib::Deflate.deflate(store) => "x\234k\312\311or04c(\317h,q\310,v(-\320qh\311o\266\a\000_\034\a\235" 最 后 将 用 zlib 压 缩 后 的 内 容 写 入 磁 盘 需 要 指 定 保 存 对 象 的 路 径 (SHA-1 值 的 头 两 个 字 符 作 为 子 目 录 名 称, 剩 余 38 个 字 符 作 为 文 件 名 保 存 至 该 子 目 录 中 ) 在 Ruby 中, 如 果 子 目 录 不 存 在 可 以 用 FileUtils.mkdir_p() 函 数 创 建 它 接 着 用 File.open 方 法 打 开 文 件, 并 用 write() 方 法 将 之 前 压 缩 的 内 容 写 入 该 文 件 : >> path = '.git/objects/' + sha1[0,2] + '/' + sha1[2,38] => ".git/objects/bd/9dbf5aae1a3862dd b20206e5fc37" >> require 'fileutils' => true >> FileUtils.mkdir_p(File.dirname(path)) => ".git/objects/bd" >> File.open(path, 'w') { f f.write zlib_content } =>

274 第 9 章 Git 内 部 原 理 Sco Chacon Pro Git 中 文 版 这 就 行 了 你 已 经 创 建 了 一 个 正 确 的 blob 对 象 所 有 的 Git 对 象 都 以 这 种 方 式 存 储, 惟 一 的 区 别 是 类 型 不 同 除 了 字 符 串 blob, 文 件 头 起 始 内 容 还 可 以 是 commit 或 tree 不 过 虽 然 blob 几 乎 可 以 是 任 意 内 容,commit 和 tree 的 数 据 却 是 有 固 定 格 式 的 9.3 Git References 你 可 以 执 行 像 git log 1a410e 这 样 的 命 令 来 查 看 完 整 的 历 史, 但 是 这 样 你 就 要 记 得 1a410e 是 你 最 后 一 次 提 交, 这 样 才 能 在 提 交 历 史 中 找 到 这 些 对 象 你 需 要 一 个 文 件 来 用 一 个 简 单 的 名 字 来 记 录 这 些 SHA-1 值, 这 样 你 就 可 以 用 这 些 指 针 而 不 是 原 来 的 SHA-1 值 去 检 索 了 在 Git 中, 我 们 称 之 为 引 用 (references 或 者 refs, 译 者 注 ) 你 可 以 在.git/refs 目 录 下 面 找 到 这 些 包 含 SHA-1 值 的 文 件 在 这 个 项 目 里, 这 个 目 录 还 没 不 包 含 任 何 文 件, 但 是 包 含 这 样 一 个 简 单 的 结 构 : $ find.git/refs.git/refs.git/refs/heads.git/refs/tags $ find.git/refs -type f $ 如 果 想 要 创 建 一 个 新 的 引 用 帮 助 你 记 住 最 后 一 次 提 交, 技 术 上 你 可 以 这 样 做 : $ echo "1a410efbd13591db ebc7a059dd55cfe9" >.git/refs/heads/master 现 在, 你 就 可 以 在 Git 命 令 中 使 用 你 刚 才 创 建 的 引 用 而 不 是 SHA-1 值 : $ git log --pretty=oneline master 1a410efbd13591db ebc7a059dd55cfe9 third commit cac0cab538b970a37ea1e769cbbde608743bc96d second commit fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit 当 然, 我 们 并 不 鼓 励 你 直 接 修 改 这 些 引 用 文 件 如 果 你 确 实 需 要 更 新 一 个 引 用, Git 提 供 了 一 个 安 全 的 命 令 update-ref: 264

275 Sco Chacon Pro Git 中 文 版 9.3 节 Git References $ git update-ref refs/heads/master 1a410efbd13591db ebc7a059dd55cfe9 基 本 上 Git 中 的 一 个 分 支 其 实 就 是 一 个 指 向 某 个 工 作 版 本 一 条 HEAD 记 录 的 指 针 或 引 用 你 可 以 用 这 条 命 令 创 建 一 个 指 向 第 二 次 提 交 的 分 支 : $ git update-ref refs/heads/test cac0ca 这 样 你 的 分 支 将 会 只 包 含 那 次 提 交 以 及 之 前 的 工 作 : $ git log --pretty=oneline test cac0cab538b970a37ea1e769cbbde608743bc96d second commit fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit 现 在, 你 的 Git 数 据 库 应 该 看 起 来 像 图 9-4 一 样 图 9.4: 包 含 分 支 引 用 的 Git 目 录 对 象 每 当 你 执 行 git branch ( 分 支 名 称 ) 这 样 的 命 令,Git 基 本 上 就 是 执 行 update-ref 命 令, 把 你 现 在 所 在 分 支 中 最 后 一 次 提 交 的 SHA-1 值, 添 加 到 你 要 创 建 的 分 支 的 引 用 HEAD 标 记 现 在 的 问 题 是, 当 你 执 行 git branch ( 分 支 名 称 ) 这 条 命 令 的 时 候,Git 怎 么 知 道 最 后 一 次 提 交 的 SHA-1 值 呢? 答 案 就 是 HEAD 文 件 HEAD 文 件 是 一 个 指 向 你 当 前 所 在 分 支 的 引 用 标 识 符 这 样 的 引 用 标 识 符 它 看 起 来 并 不 像 一 个 普 通 的 引 用 其 实 并 不 包 含 SHA-1 值, 而 是 一 个 指 向 另 外 一 个 引 用 的 指 针 如 果 你 看 一 下 这 个 文 件, 通 常 你 将 会 看 到 这 样 的 内 容 : 265

修改版-操作手册.doc

修改版-操作手册.doc 职 称 信 息 系 统 升 级 指 南 须 使 用 IE9 及 其 以 上 版 本 浏 览 器 或 谷 歌 浏 览 器 登 录 www.njrs.gov.cn 南 京 市 职 称 ( 职 业 资 格 ) 工 作 领 导 小 组 办 公 室 2016 年 5 月 目 录 一 申 报 人 员 操 作 指 南...1 1.1 职 称 初 定 申 报...1 1.1.1 职 称 初 定 基 础 信 息 填

More information

说 明 为 了 反 映 教 运 行 的 基 本 状 态, 为 校 和 院 制 定 相 关 政 策 和 进 行 教 建 设 与 改 革 提 供 据 依 据, 校 从 程 资 源 ( 开 类 别 开 量 规 模 ) 教 师 结 构 程 考 核 等 维 度, 对 2015 年 春 季 期 教 运 行 基

说 明 为 了 反 映 教 运 行 的 基 本 状 态, 为 校 和 院 制 定 相 关 政 策 和 进 行 教 建 设 与 改 革 提 供 据 依 据, 校 从 程 资 源 ( 开 类 别 开 量 规 模 ) 教 师 结 构 程 考 核 等 维 度, 对 2015 年 春 季 期 教 运 行 基 内 部 资 料 东 北 师 范 大 教 运 行 基 本 状 态 据 报 告 2015 年 春 季 期 教 务 处 2015 年 10 月 27 日 说 明 为 了 反 映 教 运 行 的 基 本 状 态, 为 校 和 院 制 定 相 关 政 策 和 进 行 教 建 设 与 改 革 提 供 据 依 据, 校 从 程 资 源 ( 开 类 别 开 量 规 模 ) 教 师 结 构 程 考 核 等 维 度,

More information

<433A5C446F63756D656E747320616E642053657474696E67735C41646D696E6973747261746F725CD7C0C3E65CC2DBCEC4CFB5CDB3CAB9D3C3D6B8C4CFA3A8BCF2BBAFA3A95CCAB9D3C3D6B8C4CF31302D31392E646F63>

<433A5C446F63756D656E747320616E642053657474696E67735C41646D696E6973747261746F725CD7C0C3E65CC2DBCEC4CFB5CDB3CAB9D3C3D6B8C4CFA3A8BCF2BBAFA3A95CCAB9D3C3D6B8C4CF31302D31392E646F63> ( 一 ) 系 统 整 体 操 作 流 程 简 述 3 ( 二 ) 系 统 中 各 角 色 操 作 功 能 说 明 5 1. 学 院 管 理 员 5 2. 教 学 院 长 8 3. 指 导 教 师 10 4. 答 辩 组 组 长 12 5. 学 生 12 6. 系 统 管 理 员 15 ( 一 ) 论 文 系 统 常 见 问 题 16 ( 二 ) 论 文 查 重 常 见 问 题 22 1 2 主

More information

I

I 机 电 一 级 注 册 建 造 师 继 续 教 育 培 训 广 东 培 训 点 网 上 报 名 操 作 使 用 手 册 (2013 年 1 月, 第 一 版 ) 第 一 章 个 人 注 册 与 个 人 信 息 管 理 1. 个 人 注 册 ( 请 每 人 只 申 请 一 个 注 册 号, 如 果 单 位 批 量 报 班 单 位 帮 申 请 注 册, 不 需 个 人 再 注 册 ) 首 次 报 班,

More information

何 秋 琳 张 立 春 视 觉 学 习 研 究 进 展 视 觉 注 意 视 觉 感 知

何 秋 琳 张 立 春 视 觉 学 习 研 究 进 展 视 觉 注 意 视 觉 感 知 第 卷 第 期 年 月 开 放 教 育 研 究 何 秋 琳 张 立 春 华 南 师 范 大 学 未 来 教 育 研 究 中 心 广 东 广 州 随 着 图 像 化 技 术 和 电 子 媒 体 的 发 展 视 觉 学 习 也 逐 步 发 展 为 学 习 科 学 的 一 个 研 究 分 支 得 到 研 究 人 员 和 教 育 工 作 者 的 广 泛 关 注 基 于 此 作 者 试 图 对 视 觉 学 习

More information

《C语言基础入门》课程教学大纲

《C语言基础入门》课程教学大纲 C 语 言 开 发 入 门 教 程 课 程 教 学 大 纲 课 程 编 号 :201409210011 学 分 :5 学 分 学 时 :58 学 时 ( 其 中 : 讲 课 学 时 :39 学 时 上 机 学 时 :19 学 时 ) 先 修 课 程 : 计 算 机 导 论 后 续 课 程 :C++ 程 序 设 计 适 用 专 业 : 信 息 及 其 计 算 机 相 关 专 业 开 课 部 门 : 计

More information

,,,,, :,, (.,, );, (, : ), (.., ;. &., ;.. &.., ;, ;, ),,,,,,, ( ) ( ),,,,.,,,,,, : ;, ;,.,,,,, (., : - ),,,, ( ),,,, (, : ),, :,

,,,,, :,, (.,, );, (, : ), (.., ;. &., ;.. &.., ;, ;, ),,,,,,, ( ) ( ),,,,.,,,,,, : ;, ;,.,,,,, (., : - ),,,, ( ),,,, (, : ),, :, : 周 晓 虹 : - -., - - - -. :( ), -,.( ),,, -. - ( ).( ) ', -,,,,, ( ).( ),,, -., '.,, :,,,, :,,,, ,,,,, :,, (.,, );, (, : ), (.., ;. &., ;.. &.., ;, ;, ),,,,,,, ( ) ( ),,,,.,,,,,, : ;, ;,.,,,,, (., : - ),,,,

More information

张 荣 芳 中 山 大 学 历 史 系 广 东 广 州 张 荣 芳 男 广 东 廉 江 人 中 山 大 学 历 史 系 教 授 博 士 生 导 师 我 们 要 打 破 以 前 学 术 界 上 的 一 切 偶 像 以 前 学 术 界 的 一 切 成 见 屏 除 我 们 要 实 地 搜 罗 材 料 到 民 众 中 寻 方 言 到 古 文 化 的 遗 址 去 发 掘 到 各 种 的 人 间 社 会 去

More information

Microsoft Word - 第7章 图表反转形态.doc

Microsoft Word - 第7章 图表反转形态.doc 第 七 章 图 表 反 转 形 态 我 们 知 道 市 场 趋 势 共 有 三 种 : 上 升 趋 势 下 降 趋 势 和 横 向 整 理 市 场 的 价 格 波 动 都 是 运 行 在 这 三 种 趋 势 中, 所 有 的 走 势 都 是 这 三 种 趋 势 的 排 列 组 合 如 图 市 场 趋 势 结 构 示 意 图 7-1 所 示 市 场 趋 势 结 构 示 意 图 7-1 图 市 场 趋

More information

深圳市新亚电子制程股份有限公司

深圳市新亚电子制程股份有限公司 证 券 代 码 :002388 证 券 简 称 : 新 亚 制 程 公 告 编 号 :2016-053 深 圳 市 新 亚 电 子 制 程 股 份 有 限 公 司 2016 年 第 二 次 临 时 股 东 大 会 决 议 公 告 本 公 司 及 董 事 会 全 体 成 员 保 证 公 告 内 容 真 实 准 确 和 完 整, 不 存 在 虚 假 记 载 误 导 性 陈 述 或 者 重 大 遗 漏 特

More information

0 年 上 半 年 评 价 与 考 核 细 则 序 号 部 门 要 素 值 考 核 内 容 考 核 方 式 考 核 标 准 考 核 ( 扣 原 因 ) 考 评 得 3 安 全 生 产 目 30 无 同 等 责 任 以 上 道 路 交 通 亡 人 事 故 无 轻 伤 责 任 事 故 无 重 大 质 量

0 年 上 半 年 评 价 与 考 核 细 则 序 号 部 门 要 素 值 考 核 内 容 考 核 方 式 考 核 标 准 考 核 ( 扣 原 因 ) 考 评 得 3 安 全 生 产 目 30 无 同 等 责 任 以 上 道 路 交 通 亡 人 事 故 无 轻 伤 责 任 事 故 无 重 大 质 量 0 年 上 半 年 评 价 与 考 核 细 则 序 号 部 门 要 素 值 考 核 内 容 考 核 方 式 考 核 标 准 无 同 等 责 任 以 上 道 路 交 通 亡 人 事 故 3 无 轻 伤 责 任 事 故 目 标 30 及 事 无 重 大 质 量 工 作 过 失 故 管 无 其 他 一 般 责 任 事 故 理 在 公 司 文 明 环 境 创 建 中, 无 工 作 过 失 及 被 追 究 的

More information

龚 亚 夫 在 重 新 思 考 基 础 教 育 英 语 教 学 的 理 念 一 文 中 援 引 的 观 点 认 为 当 跳 出 本 族 语 主 义 的 思 维 定 式 后 需 要 重 新 思 考 许 多 相 连 带 的 问 题 比 如 许 多 发 音 的 细 微 区 别 并 不 影 响 理 解 和

龚 亚 夫 在 重 新 思 考 基 础 教 育 英 语 教 学 的 理 念 一 文 中 援 引 的 观 点 认 为 当 跳 出 本 族 语 主 义 的 思 维 定 式 后 需 要 重 新 思 考 许 多 相 连 带 的 问 题 比 如 许 多 发 音 的 细 微 区 别 并 不 影 响 理 解 和 语 音 语 篇 语 感 语 域 林 大 津 毛 浩 然 改 革 开 放 以 来 的 英 语 热 引 发 了 大 中 小 学 英 语 教 育 整 体 规 划 问 题 在 充 分 考 虑 地 区 学 校 和 个 体 差 异 以 及 各 家 观 点 的 基 础 上 遵 循 实 事 求 是 逐 级 定 位 逐 层 分 流 因 材 施 教 的 原 则 本 研 究 所 倡 导 的 语 音 语 篇 语 感 语 域

More information

18 上 报 该 学 期 新 生 数 据 至 阳 光 平 台 第 一 学 期 第 四 周 至 第 六 周 19 督 促 学 习 中 心 提 交 新 增 专 业 申 请 第 一 学 期 第 四 周 至 第 八 周 20 编 制 全 国 网 络 统 考 十 二 月 批 次 考 前 模 拟 题 第 一 学

18 上 报 该 学 期 新 生 数 据 至 阳 光 平 台 第 一 学 期 第 四 周 至 第 六 周 19 督 促 学 习 中 心 提 交 新 增 专 业 申 请 第 一 学 期 第 四 周 至 第 八 周 20 编 制 全 国 网 络 统 考 十 二 月 批 次 考 前 模 拟 题 第 一 学 1 安 排 组 织 全 国 网 络 统 考 九 月 批 次 网 上 考 前 辅 导 第 一 学 期 第 一 周 统 考 考 前 半 个 月 2 下 发 全 国 网 络 统 考 九 月 批 次 准 考 证 第 一 学 期 第 一 周 导 出 下 半 年 成 人 本 科 学 士 学 位 英 语 统 一 考 试 报 考 3 信 息 第 一 学 期 第 一 周 4 教 学 计 划 和 考 试 计 划 上 网,

More information

目 录 关 于 图 标... 3 登 陆 主 界 面... 3 工 单 管 理... 5 工 单 列 表... 5 搜 索 工 单... 5 工 单 详 情... 6 创 建 工 单... 9 设 备 管 理 巡 检 计 划 查 询 详 情 销 售 管

目 录 关 于 图 标... 3 登 陆 主 界 面... 3 工 单 管 理... 5 工 单 列 表... 5 搜 索 工 单... 5 工 单 详 情... 6 创 建 工 单... 9 设 备 管 理 巡 检 计 划 查 询 详 情 销 售 管 宝 汇 德 Turbocare 微 服 务 系 统 客 户 操 作 手 册 Version 2.0 北 京 宝 汇 德 技 术 服 务 器 有 限 公 司 技 术 研 发 部 目 录 关 于 图 标... 3 登 陆 主 界 面... 3 工 单 管 理... 5 工 单 列 表... 5 搜 索 工 单... 5 工 单 详 情... 6 创 建 工 单... 9 设 备 管 理... 10 巡

More information

Microsoft Word - 文件汇编.doc

Microsoft Word - 文件汇编.doc 北 京 市 中 医 管 理 局 二 一 五 年 四 月 ... 1... 18 2015... 30 京 中 医 政 字 [2014]160 号 1 2 一 充 分 认 识 中 医 健 康 乡 村 建 设 工 作 的 重 要 意 义 二 建 立 健 全 工 作 保 障 机 制 2014 12 15 三 做 好 工 作 启 动 的 准 备 事 宜 1 2014 12 15 5-10 2014 12 15

More information

科 学 出 版 社 科 学 出 版 社 前 言 本 书 是 针 对 普 通 高 等 院 校 经 济 类 和 工 商 管 理 类 本 科 专 业 财 务 管 理 学 的 教 学 需 求, 结 合 教 育 部 经 济 管 理 类 本 科 财 务 管 理 学 课 程 教 学 大 纲 编 写 而 成 的 本 书 执 笔 者 都 是 长 期 工 作 在 财 务 管 理 教 学 一 线 的 专 业 教 师,

More information

国债回购交易业务指引

国债回购交易业务指引 附 件 1 上 海 证 券 交 易 所 新 质 押 式 国 债 回 购 交 易 业 务 指 引 一 总 述 根 据 上 海 证 券 交 易 所 债 券 交 易 实 施 细 则, 上 证 所 将 于 2006 年 5 月 8 日 起 推 出 新 质 押 式 国 债 回 购 新 质 押 式 回 购 与 现 行 质 押 式 回 购 相 比 区 别 主 要 在 以 下 几 个 方 面 :1 新 质 押 式

More information

评 委 : 李 炎 斌 - 个 人 技 术 标 资 信 标 初 步 审 查 明 细 表 序 号 投 标 单 位 投 标 函 未 按 招 标 文 件 规 定 填 写 漏 填 或 内 容 填 写 错 误 的 ; 不 同 投 标 人 的 投 标 文 件 由 同 一 台 电 脑 或 同 一 家 投 标 单

评 委 : 李 炎 斌 - 个 人 技 术 标 资 信 标 初 步 审 查 明 细 表 序 号 投 标 单 位 投 标 函 未 按 招 标 文 件 规 定 填 写 漏 填 或 内 容 填 写 错 误 的 ; 不 同 投 标 人 的 投 标 文 件 由 同 一 台 电 脑 或 同 一 家 投 标 单 评 委 : 李 炎 斌 - 个 人 清 标 评 审 明 细 表 评 审 因 素 序 号 投 标 单 位 清 标 评 审 1 深 圳 市 创 捷 科 技 有 限 合 格 2 四 川 川 大 智 胜 软 件 股 份 有 限 合 格 3 北 京 航 天 长 峰 科 技 工 业 集 团 有 限 公 司 合 格 4 深 圳 中 兴 力 维 技 术 有 限 合 格 5 深 圳 键 桥 通 讯 技 术 股 份 有

More information

黄 金 原 油 总 持 仓 增 长, 同 比 增 幅 分 别 为 4.2% 和 4.1% 而 铜 白 银 以 及 玉 米 则 出 现 减 持, 减 持 同 比 减 少 分 别 为 9.4%,9.4% 以 及 6.5% 大 豆, 豆 粕 结 束 连 续 4 周 总 持 仓 量 增 长, 出 现 小 幅

黄 金 原 油 总 持 仓 增 长, 同 比 增 幅 分 别 为 4.2% 和 4.1% 而 铜 白 银 以 及 玉 米 则 出 现 减 持, 减 持 同 比 减 少 分 别 为 9.4%,9.4% 以 及 6.5% 大 豆, 豆 粕 结 束 连 续 4 周 总 持 仓 量 增 长, 出 现 小 幅 小 麦 净 多 持 仓 增 加, 豆 油 豆 粕 净 多 持 仓 减 少 美 国 CFTC 持 仓 报 告 部 门 : 市 场 研 究 与 开 发 部 类 型 : 量 化 策 略 周 报 日 期 :212 年 5 月 7 日 电 话 :592-5678753 网 址 :www.jinyouqh.com 主 要 内 容 : 根 据 美 国 CFTC 公 布 的 数 据, 本 报 告 中 的 11 个

More information

Microsoft Word - 第3章.doc

Microsoft Word - 第3章.doc 52 5 天 通 过 职 称 计 算 机 考 试 ( 考 点 视 频 串 讲 + 全 真 模 拟 ) Word 2003 中 文 字 处 理 ( 第 2 版 ) 第 3 章 3 字 符 格 式 需 要 掌 握 的 考 点 字 体 字 形 和 字 号 的 设 置 ; 上 标 下 标 空 心 字 等 字 体 效 果 的 使 用 ; 字 符 间 距 的 调 整 ; 改 变 字 符 颜 色 底 纹 添 加

More information

(2015-2016-2)-0004186-04205-1 140242 信 号 与 系 统 Ⅰ 学 科 基 础 必 修 课 37 37 1 教 203 17 周 2016 年 06 月 13 日 (08:00-09:35) (2015-2016-2)-0004186-04205-1 141011

(2015-2016-2)-0004186-04205-1 140242 信 号 与 系 统 Ⅰ 学 科 基 础 必 修 课 37 37 1 教 203 17 周 2016 年 06 月 13 日 (08:00-09:35) (2015-2016-2)-0004186-04205-1 141011 关 于 2015-2016 学 年 第 二 学 期 期 末 周 内 考 试 时 间 地 点 安 排 选 课 课 号 班 级 名 称 课 程 名 称 课 程 性 质 合 考 人 数 实 际 人 数 考 试 教 室 考 试 段 考 试 时 间 (2015-2016-2)-0006178-04247-1 130101 测 试 技 术 基 础 学 科 基 础 必 修 课 35 35 1 教 401 17 周

More information

HSK( 一 级 ) 考 查 考 生 的 日 常 汉 语 应 用 能 力, 它 对 应 于 国 际 汉 语 能 力 标 准 一 级 欧 洲 语 言 共 同 参 考 框 架 (CEF) A1 级 通 过 HSK( 一 级 ) 的 考 生 可 以 理 解 并 使 用 一 些 非 常 简 单 的 汉 语

HSK( 一 级 ) 考 查 考 生 的 日 常 汉 语 应 用 能 力, 它 对 应 于 国 际 汉 语 能 力 标 准 一 级 欧 洲 语 言 共 同 参 考 框 架 (CEF) A1 级 通 过 HSK( 一 级 ) 的 考 生 可 以 理 解 并 使 用 一 些 非 常 简 单 的 汉 语 新 汉 语 水 平 考 试 HSK 为 使 汉 语 水 平 考 试 (HSK) 更 好 地 服 务 于 汉 语 学 习 者, 中 国 国 家 汉 办 组 织 中 外 汉 语 教 学 语 言 学 心 理 学 和 教 育 测 量 学 等 领 域 的 专 家, 在 充 分 调 查 了 解 海 外 实 际 汉 语 教 学 情 况 的 基 础 上, 吸 收 原 有 HSK 的 优 点, 借 鉴 近 年 来 国

More information

目 录 一 系 统 访 问... 1 二 门 户 首 页 申 报 用 户 审 核 用 户... 2 三 系 统 登 录 用 户 名 密 码 登 录 新 用 户 注 册 用 户 登 录 已 注 册 用

目 录 一 系 统 访 问... 1 二 门 户 首 页 申 报 用 户 审 核 用 户... 2 三 系 统 登 录 用 户 名 密 码 登 录 新 用 户 注 册 用 户 登 录 已 注 册 用 水 路 运 输 建 设 综 合 管 理 信 息 系 统 - 门 户 系 统 用 户 手 册 二 零 一 五 年 十 一 月 目 录 一 系 统 访 问... 1 二 门 户 首 页... 1 1. 申 报 用 户... 1 2. 审 核 用 户... 2 三 系 统 登 录... 4 1. 用 户 名 密 码 登 录... 4 1.1 新 用 户 注 册... 4 1.2 用 户 登 录... 7

More information

珠江钢琴股东大会

珠江钢琴股东大会 证 券 代 码 :002678 证 券 简 称 : 珠 江 钢 琴 公 告 编 号 :2015-038 广 州 珠 江 钢 琴 集 团 股 份 有 限 公 司 2015 年 年 度 股 东 大 会 决 议 公 告 本 公 司 及 董 事 会 全 体 成 员 保 证 信 息 披 露 的 内 容 真 实 准 确 完 整, 没 有 虚 假 记 载 误 导 性 陈 述 或 重 大 遗 漏 特 别 提 示 :

More information

抗 战 时 期 国 民 政 府 的 银 行 监 理 体 制 探 析 % # % % % ) % % # # + #, ) +, % % % % % % % %

抗 战 时 期 国 民 政 府 的 银 行 监 理 体 制 探 析 % # % % % ) % % # # + #, ) +, % % % % % % % % 抗 战 时 期 国 民 政 府 的 银 行 监 理 体 制 探 析 王 红 曼 抗 战 时 期 国 民 政 府 为 适 应 战 时 经 济 金 融 的 需 要 实 行 由 财 政 部 四 联 总 处 中 央 银 行 等 多 家 机 构 先 后 共 同 参 与 的 多 元 化 银 行 监 理 体 制 对 战 时 状 态 下 的 银 行 发 展 与 经 营 安 全 进 行 了 大 规 模 的 设 计 与

More information

云信Linux SSH认证代理用户手册

云信Linux SSH认证代理用户手册 Windows 主 机 登 录 保 护 (RDP) 管 理 员 配 置 手 册 V1.0 云 信 事 业 部 飞 天 诚 信 科 技 股 份 有 限 公 司 www.cloudentify.com 章 节 目 录 第 1 章 管 理 平 台 配 置 说 明... 1 1.1 注 册... 1 1.2 登 录... 3 1.3 添 加 应 用... 4 1.4 添 加 用 户... 7 1.5 激 活

More information

登录、注册功能的测试用例设计.doc

登录、注册功能的测试用例设计.doc 注 册 登 陆 测 试 用 例 和 修 改 密 码 测 试 用 例 完 整 版 摘 自 网 络, 狗 狗 整 理 zqh139@126.com 修 改 历 史 日 期 版 本 作 者 修 改 内 容 评 审 号 变 更 控 制 号 2010-11-25 1.0 初 稿 2011-09-17 2.0 整 理 一 注 册 测 试 用 例 序 号 : 1 控 件 名 称 : 功 能 描 述 : 注 册 编

More information

评 委 : 徐 岩 宇 - 个 人 技 术 标 资 信 标 初 步 审 查 明 细 表 序 号 投 标 单 位 投 标 函 未 按 招 标 文 件 规 定 填 写 漏 填 或 内 容 填 写 错 误 的 ; 不 同 投 标 人 的 投 标 文 件 由 同 一 台 电 脑 或 同 一 家 投 标 单

评 委 : 徐 岩 宇 - 个 人 技 术 标 资 信 标 初 步 审 查 明 细 表 序 号 投 标 单 位 投 标 函 未 按 招 标 文 件 规 定 填 写 漏 填 或 内 容 填 写 错 误 的 ; 不 同 投 标 人 的 投 标 文 件 由 同 一 台 电 脑 或 同 一 家 投 标 单 评 委 : 徐 岩 宇 - 个 人 清 标 评 审 明 细 表 评 审 因 素 序 号 投 标 单 位 清 标 评 审 1 深 圳 市 创 捷 科 技 有 限 合 格 2 四 川 川 大 智 胜 软 件 股 份 有 限 合 格 3 北 京 航 天 长 峰 科 技 工 业 集 团 有 限 公 司 合 格 4 深 圳 中 兴 力 维 技 术 有 限 合 格 5 深 圳 键 桥 通 讯 技 术 股 份 有

More information

 编号:

 编号: 编 号 : 企 业 内 高 技 能 人 才 培 养 评 价 实 施 方 案 ( 仅 适 用 于 企 业 特 有 行 业 特 有 工 种 ) 实 施 单 位 ( 公 章 ) 申 报 日 期 年 _ 月 日 1 企 业 内 高 技 能 人 才 培 养 评 价 项 目 实 施 方 案 申 报 表 项 目 名 称 等 级 项 目 性 质 课 时 申 报 单 位 联 系 人 通 讯 地 址 电 话 手 机 电

More information

<4D6963726F736F667420576F7264202D20B9D8D3DAB0BABBAAA3A8C9CFBAA3A3A9D7D4B6AFBBAFB9A4B3CCB9C9B7DDD3D0CFDEB9ABCBBE32303132C4EAC4EAB6C8B9C9B6ABB4F3BBE1B7A8C2C9D2E2BCFBCAE92E646F6378>

<4D6963726F736F667420576F7264202D20B9D8D3DAB0BABBAAA3A8C9CFBAA3A3A9D7D4B6AFBBAFB9A4B3CCB9C9B7DDD3D0CFDEB9ABCBBE32303132C4EAC4EAB6C8B9C9B6ABB4F3BBE1B7A8C2C9D2E2BCFBCAE92E646F6378> 上 海 德 载 中 怡 律 师 事 务 所 关 于 昂 华 ( 上 海 ) 自 动 化 工 程 股 份 有 限 公 司 二 〇 一 二 年 年 度 股 东 大 会 法 律 意 见 书 上 海 德 载 中 怡 律 师 事 务 所 上 海 市 银 城 中 路 168 号 上 海 银 行 大 厦 1705 室 (200120) 电 话 :8621-5012 2258 传 真 :8621-5012 2257

More information

自 服 务 按 钮 无 法 访 问 新 系 统 的 自 服 务 页 面 因 此 建 议 用 户 从 信 网 中 心 (http://nc.tju.edu.cn) 主 页, 右 下 角 位 置 的 常 用 下 载, 或 校 园 网 用 户 自 服 务 (http://g.tju.edu.cn) 首 页

自 服 务 按 钮 无 法 访 问 新 系 统 的 自 服 务 页 面 因 此 建 议 用 户 从 信 网 中 心 (http://nc.tju.edu.cn) 主 页, 右 下 角 位 置 的 常 用 下 载, 或 校 园 网 用 户 自 服 务 (http://g.tju.edu.cn) 首 页 校 园 网 认 证 计 费 系 统 变 更 说 明 及 使 用 帮 助 为 提 高 校 园 网 的 可 靠 性 和 可 用 性, 提 升 用 户 的 上 网 体 验, 同 时 也 为 解 决 近 期 校 园 网 无 法 认 证 或 登 录 页 面 弹 出 缓 慢 的 问 题, 信 网 中 心 于 近 期 对 校 园 网 认 证 计 费 系 统 进 行 升 级 切 换 现 将 升 级 后 新 系 统

More information

教师上报成绩流程图

教师上报成绩流程图 教 务 管 理 系 统 使 用 说 明 学 生 端 用 户 1 在 校 内 任 何 一 台 连 接 校 园 网 的 计 算 机 上 登 录 教 务 处 主 页 教 务 处 主 页 地 址 : http://jw.stdu.edu.cn/homepage 随 后 点 击 按 钮 ( 见 下 图 所 示 ), 即 可 进 入 综 合 教 务 管 理 系 统 2 在 综 合 教 务 管 理 区 域 内 键

More information

名 称 生 命 科 学 学 院 083001 环 境 科 学 1 生 物 学 仅 接 收 院 内 调 剂, 初 试 分 数 满 足 我 院 生 物 学 复 试 最 低 分 数 线 生 命 科 学 学 院 071300 生 态 学 5 生 态 学 或 生 物 学 生 命 科 学 学 院 040102

名 称 生 命 科 学 学 院 083001 环 境 科 学 1 生 物 学 仅 接 收 院 内 调 剂, 初 试 分 数 满 足 我 院 生 物 学 复 试 最 低 分 数 线 生 命 科 学 学 院 071300 生 态 学 5 生 态 学 或 生 物 学 生 命 科 学 学 院 040102 华 中 师 范 大 学 2016 年 接 收 校 内 外 优 秀 硕 士 研 究 生 调 剂 信 息 表 名 称 经 济 与 工 商 管 理 学 院 020101 政 治 经 济 学 1 经 济 学 类 毕 业 学 校 与 报 考 学 校 不 低 于 我 校 办 学 层 次 经 济 与 工 商 管 理 学 院 020105 世 界 经 济 学 1 经 济 学 类 毕 业 学 校 与 报 考 学 校

More information

马 克 思 主 义 公 正 观 的 基 本 向 度 及 方 法 论 原 则!! # #

马 克 思 主 义 公 正 观 的 基 本 向 度 及 方 法 论 原 则!! # # 马 克 思 主 义 公 正 观 的 基 本 向 度 及 方 法 论 原 则 马 俊 峰 在 社 会 公 正 问 题 的 大 讨 论 中 罗 尔 斯 诺 齐 克 哈 耶 克 麦 金 泰 尔 等 当 代 西 方 思 想 家 的 论 述 被 反 复 引 用 和 申 说 而 将 马 克 思 恩 格 斯 等 经 典 作 家 的 观 点 置 于 一 种 被 忽 视 甚 至 被 忘 却 的 状 态 形 成 这 种

More information

文 化 记 忆 传 统 创 新 与 节 日 遗 产 保 护 根 据 德 国 学 者 阿 斯 曼 的 文 化 记 忆 理 论 仪 式 与 文 本 是 承 载 文 化 记 忆 的 两 大 媒 体 在 各 种 仪 式 行 为 中 节 日 以 其 高 度 的 公 共 性 有 组 织 性 和 历 史 性 而 特 别 适 用 于 文 化 记 忆 的 储 存 和 交 流 节 日 的 文 化 功 能 不 仅 在 于

More information

( 二 ) 现 行 统 一 高 考 制 度 不 利 于 培 养 人 的 创 新 精 神,,,,,,,,,,,,, [ ],,,,,,,,,,, :, ;,,,,,,? ( 三 ) 现 行 统 一 高 考 制 度 不 利 于 全 体 学 生 都 获 得 全 面 发 展,, [ ],,,,,,,,,,,

( 二 ) 现 行 统 一 高 考 制 度 不 利 于 培 养 人 的 创 新 精 神,,,,,,,,,,,,, [ ],,,,,,,,,,, :, ;,,,,,,? ( 三 ) 现 行 统 一 高 考 制 度 不 利 于 全 体 学 生 都 获 得 全 面 发 展,, [ ],,,,,,,,,,, ( ) ( )... 李 雪 岩, 龙 耀 (. 广 西 民 族 大 学 商 学 院, 广 西 南 宁 ;. 中 山 大 学 教 育 学 院, 广 东 广 州 ) : 高 等 教 育 是 专 业 教 育 高 考 是 为 高 等 教 育 服 务 的, 是 为 高 等 专 业 教 育 选 拔 有 专 业 培 养 潜 质 的 人 才 现 行 高 考 制 度 忽 略 专 业 潜 质 的 因 素, 过 份 强

More information

一 公 共 卫 生 硕 士 专 业 学 位 论 文 的 概 述 学 位 论 文 是 对 研 究 生 进 行 科 学 研 究 或 承 担 专 门 技 术 工 作 的 全 面 训 练, 是 培 养 研 究 生 创 新 能 力, 综 合 运 用 所 学 知 识 发 现 问 题, 分 析 问 题 和 解 决

一 公 共 卫 生 硕 士 专 业 学 位 论 文 的 概 述 学 位 论 文 是 对 研 究 生 进 行 科 学 研 究 或 承 担 专 门 技 术 工 作 的 全 面 训 练, 是 培 养 研 究 生 创 新 能 力, 综 合 运 用 所 学 知 识 发 现 问 题, 分 析 问 题 和 解 决 上 海 市 公 共 卫 生 硕 士 专 业 学 位 论 文 基 本 要 求 和 评 价 指 标 体 系 ( 试 行 ) 上 海 市 学 位 委 员 会 办 公 室 二 O 一 二 年 三 月 一 公 共 卫 生 硕 士 专 业 学 位 论 文 的 概 述 学 位 论 文 是 对 研 究 生 进 行 科 学 研 究 或 承 担 专 门 技 术 工 作 的 全 面 训 练, 是 培 养 研 究 生 创

More information

上海证券交易所会议纪要

上海证券交易所会议纪要 附 件 上 海 市 场 首 次 公 开 发 行 股 票 网 下 发 行 实 施 细 则 第 一 章 总 则 第 一 条 为 规 范 拟 在 上 海 证 券 交 易 所 ( 以 下 简 称 上 交 所 ) 上 市 的 公 司 首 次 公 开 发 行 股 票 网 下 发 行 业 务, 提 高 首 次 公 开 发 行 股 票 网 下 申 购 及 资 金 结 算 效 率, 根 据 证 券 发 行 与 承 销

More information

类 似 地, 又 可 定 义 变 下 限 的 定 积 分 : ( ). 与 ψ 统 称 为 变 限 积 分. f ( ) d f ( t) dt,, 注 在 变 限 积 分 (1) 与 () 中, 不 可 再 把 积 分 变 量 写 成 的 形 式 ( 例 如 ) 以 免 与 积 分 上 下 限 的

类 似 地, 又 可 定 义 变 下 限 的 定 积 分 : ( ). 与 ψ 统 称 为 变 限 积 分. f ( ) d f ( t) dt,, 注 在 变 限 积 分 (1) 与 () 中, 不 可 再 把 积 分 变 量 写 成 的 形 式 ( 例 如 ) 以 免 与 积 分 上 下 限 的 5 ( 一 ) 微 积 分 学 基 本 定 理 当 函 数 的 可 积 性 问 题 告 一 段 落, 并 对 定 积 分 的 性 质 有 了 足 够 的 认 识 之 后, 接 着 要 来 解 决 一 个 以 前 多 次 提 到 过 的 问 题 在 定 积 分 形 式 下 证 明 连 续 函 数 必 定 存 在 原 函 数. 一 变 限 积 分 与 原 函 数 的 存 在 性 设 f 在 [,] 上

More information

3 月 30 日 在 中 国 证 券 报 上 海 证 券 报 证 券 时 报 证 券 日 报 和 上 海 证 券 交 易 所 网 站 上 发 出 召 开 本 次 股 东 大 会 公 告, 该 公 告 中 载 明 了 召 开 股 东 大 会 的 日 期 网 络 投 票 的 方 式 时 间 以 及 审

3 月 30 日 在 中 国 证 券 报 上 海 证 券 报 证 券 时 报 证 券 日 报 和 上 海 证 券 交 易 所 网 站 上 发 出 召 开 本 次 股 东 大 会 公 告, 该 公 告 中 载 明 了 召 开 股 东 大 会 的 日 期 网 络 投 票 的 方 式 时 间 以 及 审 北 京 市 君 致 律 师 事 务 所 关 于 浪 潮 软 件 股 份 有 限 公 司 2015 年 度 股 东 大 会 的 法 律 意 见 书 致 : 浪 潮 软 件 股 份 有 限 公 司 北 京 市 君 致 律 师 事 务 所 ( 以 下 简 称 本 所 ) 受 浪 潮 软 件 股 份 有 限 公 司 ( 以 下 简 称 公 司 ) 的 委 托, 指 派 律 师 出 席 2016 年 4 月

More information

第2章 数据类型、常量与变量

第2章  数据类型、常量与变量 第 2 章 数 据 类 型 常 量 与 变 量 在 计 算 机 程 序 中 都 是 通 过 值 (value) 来 进 行 运 算 的, 能 够 表 示 并 操 作 值 的 类 型 为 数 据 类 型 在 本 章 里 将 会 介 绍 JavaScript 中 的 常 量 (literal) 变 量 (variable) 和 数 据 类 型 (data type) 2.1 基 本 数 据 类 型 JavaScript

More information

Cybozu Garoon 3 管理员手册

Cybozu Garoon 3 管理员手册 附 录 D 数 据 的 输 入 样 式 数 据 的 种 类 和 输 入 字 符 的 值 等, 在 Garoon3 的 页 面 输 入 的 数 据 样 式 如 下 所 示 基 本 系 统 客 户 信 息 法 人 姓 名 字 符 串 ( 00 法 人 姓 名 ( 拼 音 ) 字 符 串 ( 00 图 标 (URL) 字 符 串 ( 255 应 用 程 序 新 名 称 字 符 串 ( 00 用 户 姓 名

More information

2014年中央财经大学研究生招生录取工作简报

2014年中央财经大学研究生招生录取工作简报 2015 年 中 央 财 经 大 学 研 究 生 招 生 录 取 工 作 简 报 一 硕 士 研 究 生 招 生 录 取 情 况 2015 年 共 有 8705 人 报 考 我 校 硕 士 研 究 生, 其 中 学 术 型 研 究 生 报 考 3657 人, 专 业 硕 士 研 究 生 报 考 5048 人 ; 总 报 考 人 数 较 2014 年 增 长 1.4%, 学 术 型 报 考 人 数 较

More information

金 不 少 于 800 万 元, 净 资 产 不 少 于 960 万 元 ; (3) 近 五 年 独 立 承 担 过 单 项 合 同 额 不 少 于 1000 万 元 的 智 能 化 工 程 ( 设 计 或 施 工 或 设 计 施 工 一 体 ) 不 少 于 2 项 ; (4) 近 三 年 每 年

金 不 少 于 800 万 元, 净 资 产 不 少 于 960 万 元 ; (3) 近 五 年 独 立 承 担 过 单 项 合 同 额 不 少 于 1000 万 元 的 智 能 化 工 程 ( 设 计 或 施 工 或 设 计 施 工 一 体 ) 不 少 于 2 项 ; (4) 近 三 年 每 年 工 程 设 计 与 施 工 资 质 标 准 一 总 则 建 筑 智 能 化 工 程 设 计 与 施 工 资 质 标 准 ( 一 ) 为 了 加 强 对 从 事 建 筑 智 能 化 工 程 设 计 与 施 工 企 业 的 管 理, 维 护 建 筑 市 场 秩 序, 保 证 工 程 质 量 和 安 全, 促 进 行 业 健 康 发 展, 结 合 建 筑 智 能 化 工 程 的 特 点, 制 定 本 标

More information

i 1) 系 统 运 作 前 设 定 *1. [2.1 网 页 主 机 名 称 设 定 ] -- 设 定 校 务 系 统 的 主 机 IP 地 址, 以 供 其 他 个 人 电 脑 连 接 及 使 用 该 系 统 *2. [2.3.1 输 入 / 修 改 学 校 资 料 ] -- 输 入 系 统 使

i 1) 系 统 运 作 前 设 定 *1. [2.1 网 页 主 机 名 称 设 定 ] -- 设 定 校 务 系 统 的 主 机 IP 地 址, 以 供 其 他 个 人 电 脑 连 接 及 使 用 该 系 统 *2. [2.3.1 输 入 / 修 改 学 校 资 料 ] -- 输 入 系 统 使 校 务 系 统 使 用 步 骤 说 明 手 册 简 介 此 手 册 是 配 合 校 务 系 统 使 用 手 册 编 辑 的, 因 此 必 须 同 时 参 考 校 务 系 统 使 用 手 册, 以 获 知 更 详 细 的 使 用 说 明 此 手 册 主 要 记 载 几 项 较 为 复 杂 事 务 的 处 理 步 骤 及 说 明, 以 让 使 用 者 更 容 易 掌 握 及 使 用 校 务 系 统 其

More information

一 开 放 性 的 政 策 与 法 规 二 两 岸 共 同 的 文 化 传 承 三 两 岸 高 校 各 自 具 有 专 业 优 势 远 见 杂 志 年 月 日

一 开 放 性 的 政 策 与 法 规 二 两 岸 共 同 的 文 化 传 承 三 两 岸 高 校 各 自 具 有 专 业 优 势 远 见 杂 志 年 月 日 河 北 师 范 大 学 学 报 新 时 期 海 峡 两 岸 高 校 开 放 招 生 问 题 探 讨 郑 若 玲 王 晓 勇 海 峡 两 岸 高 校 开 放 招 生 是 新 时 期 推 进 海 峡 两 岸 高 等 教 育 交 流 与 合 作 的 重 要 尝 试 系 统 梳 理 改 革 开 放 以 来 两 岸 招 生 政 策 与 就 学 人 数 发 展 变 化 的 历 史 进 程 可 发 现 促 进 两

More information

2. 本 次 修 改 后, 投 资 者 申 购 新 股 的 持 有 市 值 要 求 市 值 计 算 规 则 及 证 券 账 户 使 用 的 相 关 规 定 是 否 发 生 了 变 化? 答 : 未 发 生 变 化 投 资 者 申 购 新 股 的 持 有 市 值 是 指, 以 投 资 者 为 单 位

2. 本 次 修 改 后, 投 资 者 申 购 新 股 的 持 有 市 值 要 求 市 值 计 算 规 则 及 证 券 账 户 使 用 的 相 关 规 定 是 否 发 生 了 变 化? 答 : 未 发 生 变 化 投 资 者 申 购 新 股 的 持 有 市 值 是 指, 以 投 资 者 为 单 位 新 股 网 上 网 下 发 行 实 施 细 则 问 答 上 交 所 2016-01-05 一 网 上 发 行 业 务 问 答 1. 本 次 修 改 的 主 要 内 容 是 什 么? 答 : 本 次 修 改 的 主 要 内 容 包 括 : 一 是 取 消 了 投 资 者 在 申 购 委 托 时 应 全 额 缴 纳 申 购 资 金 的 规 定, 明 确 了 投 资 者 应 根 据 最 终 确 定 的 发

More information

2 熟 悉 Visual Basic 的 集 成 开 发 环 境 3 了 解 可 视 化 面 向 对 象 编 程 事 件 驱 动 交 互 式 开 发 等 基 本 概 念 4 了 解 Visual Basic 的 特 点 环 境 要 求 与 安 装 方 法 1 Visual Basic 开 发 应 用

2 熟 悉 Visual Basic 的 集 成 开 发 环 境 3 了 解 可 视 化 面 向 对 象 编 程 事 件 驱 动 交 互 式 开 发 等 基 本 概 念 4 了 解 Visual Basic 的 特 点 环 境 要 求 与 安 装 方 法 1 Visual Basic 开 发 应 用 Visual Basic 程 序 设 计 A 级 分 级 班 教 学 大 纲 ( 供 计 算 机 与 信 息 技 术 基 础 课 程 分 级 教 学 A 级 班 使 用 ) I 前 言 Visual Basic 程 序 设 计 课 程 是 一 门 计 算 机 语 言 基 础 课 程 通 过 对 该 课 程 的 学 习, 使 学 生 初 步 掌 握 Visual Basic 的 语 言 特 点, 掌

More information

课程类 别

课程类 别 美 声 演 唱 方 向 培 养 方 案 一 培 养 目 标 本 方 向 要 求 学 生 德 智 体 美 全 面 发 展, 培 养 能 在 文 艺 团 体 从 事 声 乐 演 唱 及 能 在 艺 术 院 校 从 事 本 方 向 教 学 的 高 级 门 人 才 二 培 养 规 格 本 方 向 学 生 应 系 统 掌 握 声 乐 演 唱 方 面 的 理 论 和 技 能, 具 备 较 高 的 声 乐 演 唱

More information

世华财讯模拟操作手册

世华财讯模拟操作手册 第 一 部 分 : 股 票 模 拟 操 作 部 分 1. 登 录 与 主 界 面 1.1 登 录 学 生 在 桌 面 上, 打 开 世 华 文 件 夹, 直 接 双 击 文 件 夹 中 的 快 捷 图 标, 系 统 弹 出 世 华 财 讯 模 拟 股 票 交 易 系 统 ( 客 户 端 ) 窗 口, 如 图 1.1 所 示 图 1.1 请 输 入 登 录 名 称 及 密 码, 单 击 确 认 登 录

More information

上证指数

上证指数 上 证 与 修 正 方 法 一 ( 一 ) 计 算 公 式 1. 上 证 指 数 系 列 均 采 用 派 许 加 权 综 合 价 格 指 数 公 式 计 算 2. 上 证 180 指 数 上 证 50 指 数 等 以 成 份 股 的 调 整 股 本 数 为 权 数 进 行 加 权 计 算, 计 算 公 式 为 : 报 告 期 指 数 =( 报 告 期 样 本 股 的 调 整 市 值 / 基 期 )

More information

全国建筑市场注册执业人员不良行为记录认定标准(试行).doc

全国建筑市场注册执业人员不良行为记录认定标准(试行).doc - 1 - - 2 - 附 件 全 国 建 筑 市 场 注 册 执 业 人 员 不 良 记 录 认 定 标 准 ( 试 行 ) 说 明 为 了 完 善 建 筑 市 场 注 册 执 业 人 员 诚 信 体 系 建 设, 规 范 执 业 和 市 场 秩 序, 依 据 相 关 法 律 法 规 和 部 门 规 章, 根 据 各 行 业 特 点, 我 部 制 订 了 全 国 建 筑 市 场 注 册 执 业 人

More information

Template BR_Rec_2005.dot

Template BR_Rec_2005.dot ITU-R BT.1789 建 议 书 1 ITU-R BT.1789 建 议 书 在 分 组 视 频 传 输 中 利 用 传 输 误 码 信 息 重 建 接 收 视 频 的 方 法 (ITU-R 44/6 和 ITU-R 109/6 课 题 ) (2007 年 ) 范 围 本 建 议 书 对 业 务 提 供 商 重 建 接 收 视 频 的 方 法 做 了 详 细 介 绍, 以 便 利 用 传 输

More information

·岗位设置管理流程

·岗位设置管理流程 实 施 岗 位 设 置 岗 位 设 置 编 码 受 控 状 态 执 行 心 门 行 政 人 力 资 控 制 门 总 经 办 源 各 职 能 门 行 政 人 力 资 源 总 经 办 总 经 理 根 据 公 司 发 展 战 略 进 行 职 能 分 解 和 机 构 设 置 工 作 分 析 根 据 人 力 资 源 规 划 确 定 编 制 意 见 职 责 划 分 与 岗 位 设 置 制 作 职 务 说 明 书

More information

正 规 培 训 达 规 定 标 准 学 时 数, 并 取 得 结 业 证 书 二 级 可 编 程 师 ( 具 备 以 下 条 件 之 一 者 ) (1) 连 续 从 事 本 职 业 工 作 13 年 以 上 (2) 取 得 本 职 业 三 级 职 业 资 格 证 书 后, 连 续 从 事 本 职 业

正 规 培 训 达 规 定 标 准 学 时 数, 并 取 得 结 业 证 书 二 级 可 编 程 师 ( 具 备 以 下 条 件 之 一 者 ) (1) 连 续 从 事 本 职 业 工 作 13 年 以 上 (2) 取 得 本 职 业 三 级 职 业 资 格 证 书 后, 连 续 从 事 本 职 业 1. 职 业 概 况 1.1 职 业 名 称 可 编 程 师 1.2 职 业 定 义 可 编 程 师 国 家 职 业 标 准 从 事 可 编 程 序 控 制 器 (PLC) 选 型 编 程, 并 对 应 用 进 行 集 成 和 运 行 管 理 的 人 员 1.3 职 业 等 级 本 职 业 共 设 四 个 等 级, 分 别 为 : 四 级 可 编 程 师 ( 国 家 职 业 资 格 四 级 ) 三

More information

收 入 支 出 项 目 2016 年 预 算 项 目 2016 年 预 算 预 算 01 表 单 位 : 万 元 ( 保 留 两 位 小 数 ) 一 公 共 财 政 预 算 拨 款 50.06 一 人 员 经 费 23.59 1 一 般 财 力 50.06 1 人 员 支 出 21.95 2 成 品

收 入 支 出 项 目 2016 年 预 算 项 目 2016 年 预 算 预 算 01 表 单 位 : 万 元 ( 保 留 两 位 小 数 ) 一 公 共 财 政 预 算 拨 款 50.06 一 人 员 经 费 23.59 1 一 般 财 力 50.06 1 人 员 支 出 21.95 2 成 品 100.12 2016 年 龙 岩 市 部 门 预 算 表 报 送 日 期 : 年 月 日 单 位 负 责 人 签 章 : 财 务 负 责 人 签 章 : 制 表 人 签 章 : 收 入 支 出 项 目 2016 年 预 算 项 目 2016 年 预 算 预 算 01 表 单 位 : 万 元 ( 保 留 两 位 小 数 ) 一 公 共 财 政 预 算 拨 款 50.06 一 人 员 经 费 23.59

More information

3 复 试 如 何 准 备 4 复 试 成 绩 计 算 5 复 试 比 例 6 复 试 类 型 7 怎 么 样 面 对 各 种 复 试 04 05

3 复 试 如 何 准 备 4 复 试 成 绩 计 算 5 复 试 比 例 6 复 试 类 型 7 怎 么 样 面 对 各 种 复 试 04 05 1 复 试 流 程 2 复 试 考 查 形 式 02 03 3 复 试 如 何 准 备 4 复 试 成 绩 计 算 5 复 试 比 例 6 复 试 类 型 7 怎 么 样 面 对 各 种 复 试 04 05 2 怎 样 给 导 师 留 下 良 好 的 第 一 印 象 把 握 进 门 时 机 1 面 试 中 穿 着 的 瞒 天 过 海 3 无 声 胜 有 声 的 肢 体 语 言 育 4 眼 睛 是 心

More information

<4D F736F F D D323630D6D0B9FAD3A6B6D4C6F8BAF2B1E4BBAFB5C4D5FEB2DFD3EBD0D0B6AF C4EAB6C8B1A8B8E6>

<4D F736F F D D323630D6D0B9FAD3A6B6D4C6F8BAF2B1E4BBAFB5C4D5FEB2DFD3EBD0D0B6AF C4EAB6C8B1A8B8E6> 中 国 应 对 气 候 变 化 的 政 策 与 行 动 2013 年 度 报 告 国 家 发 展 和 改 革 委 员 会 二 〇 一 三 年 十 一 月 100% 再 生 纸 资 源 目 录 前 言... 1 一 应 对 气 候 变 化 面 临 的 形 势... 3 二 完 善 顶 层 设 计 和 体 制 机 制... 4 三 减 缓 气 候 变 化... 8 四 适 应 气 候 变 化... 20

More information

4 进 入 交 互 区 设 置 的 组 件 管 理, 在 组 件 管 理 中, 教 师 可 以 选 择 课 程 空 间 中 的 所 有 组 件, 并 通 过 点 击 启 用 或 不 启 用 选 定 组 件 在 课 程 空 间 中 的 显 示 5 进 入 工 作 室 管 理 的 工 作 室 首 页,

4 进 入 交 互 区 设 置 的 组 件 管 理, 在 组 件 管 理 中, 教 师 可 以 选 择 课 程 空 间 中 的 所 有 组 件, 并 通 过 点 击 启 用 或 不 启 用 选 定 组 件 在 课 程 空 间 中 的 显 示 5 进 入 工 作 室 管 理 的 工 作 室 首 页, 网 络 教 育 新 平 台 教 师 使 用 简 易 手 册 一 登 录 教 师 工 作 室 1 打 开 西 南 科 技 大 学 网 络 教 育 教 学 教 务 新 平 台 主 页 面 :http://www.swust.net.cn/ 2 在 主 页 面 左 边 的 登 陆 区 中, 用 户 名 和 密 码 处 分 别 输 入 自 己 的 用 户 名 ( 教 师 ID 号 ) 和 密 码 ( 初 始

More information

目 录 第 一 章 博 星 卓 越 电 子 商 务 营 销 策 划 实 践 平 台 硬 件 使 用 介 绍... 3 第 二 章 博 星 卓 越 电 子 商 务 营 销 策 划 实 践 平 台 管 理 员 端 功 能 使 用 介 绍... 4 2.1 系 统 管 理 员 登 陆... 4 2.2 班

目 录 第 一 章 博 星 卓 越 电 子 商 务 营 销 策 划 实 践 平 台 硬 件 使 用 介 绍... 3 第 二 章 博 星 卓 越 电 子 商 务 营 销 策 划 实 践 平 台 管 理 员 端 功 能 使 用 介 绍... 4 2.1 系 统 管 理 员 登 陆... 4 2.2 班 博 星 卓 越 电 子 商 务 营 销 策 划 实 践 平 台 使 用 说 明 书 制 作 : 北 京 博 导 前 程 信 息 技 术 有 限 公 司 目 录 第 一 章 博 星 卓 越 电 子 商 务 营 销 策 划 实 践 平 台 硬 件 使 用 介 绍... 3 第 二 章 博 星 卓 越 电 子 商 务 营 销 策 划 实 践 平 台 管 理 员 端 功 能 使 用 介 绍... 4 2.1

More information

2006年顺德区高中阶段学校招生录取分数线

2006年顺德区高中阶段学校招生录取分数线 2014 年 顺 德 区 高 中 阶 段 学 校 考 试 提 前 批 第 一 批 第 二 批 学 校 录 取 根 据 佛 山 市 办 提 供 的 考 生 数 据, 现 将 我 区 2014 年 高 中 阶 段 学 校 考 试 提 前 批 第 一 批 第 二 批 学 校 的 录 取 公 布 如 下 : 一 顺 德 一 中 录 取 分 第 1 志 愿, 总 分 585, 综 合 表 现 评 价 A, 考

More information

第二讲 数列

第二讲   数列 Togisu XueD Persolized Eduio Developme Ceer 高 考 中 不 等 式 问 题 的 解 决 方 法 通 润 达 久 王 力 前 言 : 近 年 来 不 等 式 问 题 正 越 来 越 多 的 出 现 在 调 研 题 和 高 考 试 题 中 而 且 大 多 出 现 在 江 苏 高 考 的 填 空 压 轴 题 中 是 高 考 考 察 的 重 点 和 难 点 由 于

More information

朱 丽 明 柯 美 云 周 丽 雅 袁 耀 宗 罗 金 燕 候 晓 华 陈 旻 湖 滥 用 安 非 他 命 会 增 加 得 心 脏 病 的 风 险 据 美 国 科 技 新 闻 网 报 道 根 据 纽 约 路 透 社 报 道 一 份 新 的 研 究 显 示 青 年 及 成 年 人 若 滥 用 安 非 他 命 会 增 加 得 心 脏 病 的 风 险 美 国 德 州 大 学 西 南 医 学 中 心

More information

对 当 前 小 说 艺 术 倾 向 的 分 析 陈 晓 明 人 民 性 是 一 个 现 代 性 概 念 近 年 来 艺 术 上 趋 于 成 熟 的 一 批 作 家 倾 向 于 表 现 底 层 民 众 苦 难 的 生 活 这 使 他 们 的 作 品 具 有 现 实 主 义 的 显 著 特 征 在 对 苦 难 生 活 的 把 握 中 对 人 物 性 格 和 命 运 的 展 示 中 这 些 小 说 在 人

More information

<4D6963726F736F667420576F7264202D20BFC9B1E0B3CCD0F2BFD8D6C6CFB5CDB3C9E8BCC6CAA6B9FABCD2D6B0D2B5B1EAD7BC2E646F63>

<4D6963726F736F667420576F7264202D20BFC9B1E0B3CCD0F2BFD8D6C6CFB5CDB3C9E8BCC6CAA6B9FABCD2D6B0D2B5B1EAD7BC2E646F63> 国 家 职 业 标 准 1 可 编 程 序 控 制 系 统 设 计 师 国 家 职 业 标 准 1. 职 业 概 况 1.1 职 业 名 称 可 编 程 序 控 制 系 统 设 计 师 1.2 职 业 定 义 从 事 可 编 程 序 控 制 器 (PLC) 选 型 编 程, 并 对 应 用 系 统 进 行 设 计 集 成 和 运 行 管 理 的 人 员 1.3 职 业 等 级 本 职 业 共 设 四

More information

反 学 校 文 化 与 阶 级 再 生 产 小 子 与 子 弟 之 比 较 周 潇 作 者 通 过 对 北 京 某 打 工 子 弟 学 校 的 田 野 调 查 后 发 现 在 农 民 工 子 弟 中 间 盛 行 着 类 似 学 做 工 中 所 描 述 的 工 人 阶 级 小 子 的 反 学 校 文 化 但 是 由 于 制 度 安 排 与 社 会 条 件 的 差 异 子 弟 与 小 子 的 反 学 校

More information

微软用户

微软用户 学 生 空 间 用 户 操 作 手 册 目 录 前 言... 2 一 登 录 学 生 空 间... 2 二 学 生 空 间 页 面... 3 三 功 能 区... 3 3.1 课 程 学 习... 5 3.2 学 务 管 理... 7 3.3 学 习 档 案... 7 3.4 资 料 管 理... 8 3.7 课 程 班 级... 11 3.8 我 要 评 价... 11 四 交 流 互 动...

More information

西 南 大 学 硕 士 学 位 论 文 网 络 购 物 动 机 问 卷 的 编 制 及 实 测 姓 名 : 曹 建 英 申 请 学 位 级 别 : 硕 士 专 业 : 基 础 心 理 学 指 导 教 师 : 张 进 辅 20090401 网 络 购 物 动 机 问 卷 的

More information

光明乳业股份有限公司

光明乳业股份有限公司 光 明 乳 业 股 份 有 限 公 司 非 公 开 发 行 A 股 股 票 募 集 资 金 存 放 与 实 际 使 用 情 况 的 专 项 报 告 及 审 核 报 告 截 至 2012 年 12 月 31 日 止 审 核 报 告 光 明 乳 业 股 份 有 限 公 司 全 体 股 东 : 德 师 报 ( 核 ) 字 (13) 第 E0019 号 我 们 审 核 了 后 附 的 光 明 乳 业 股 份

More information

目 录 一 激 活 账 号... 2 二 忘 记 密 码 后 如 何 找 回 密 码?... 3 三 如 何 管 理 学 校 信 息 及 球 队 学 生 教 师 等 信 息... 6 四 如 何 发 布 本 校 校 园 文 化?... 11 五 如 何 向 教 师 发 送 通 知?... 13 六

目 录 一 激 活 账 号... 2 二 忘 记 密 码 后 如 何 找 回 密 码?... 3 三 如 何 管 理 学 校 信 息 及 球 队 学 生 教 师 等 信 息... 6 四 如 何 发 布 本 校 校 园 文 化?... 11 五 如 何 向 教 师 发 送 通 知?... 13 六 一 刻 校 园 足 球 管 理 平 台 使 用 说 明 ( 学 校 管 理 员 版 ) 一 刻 软 件 科 技 有 限 公 司 目 录 一 激 活 账 号... 2 二 忘 记 密 码 后 如 何 找 回 密 码?... 3 三 如 何 管 理 学 校 信 息 及 球 队 学 生 教 师 等 信 息... 6 四 如 何 发 布 本 校 校 园 文 化?... 11 五 如 何 向 教 师 发 送

More information

系统设计文档_样稿管理模块 V1.1_.doc

系统设计文档_样稿管理模块 V1.1_.doc 系 统 设 计 文 档 生 产 资 料 管 理 系 统 (Production Material Management System, PMMS) 样 稿 ( 样 件 ) 管 理 模 块 ( 第 1 期 ) 文 档 版 本 :1.1 文 档 日 期 : 2012-08-30 文 档 作 者 : 曾 勇 松 其 它 参 与 人 : 第 1 页 / 共 16 页 文 档 修 订 记 录 修 订 记 录

More information

ETF、分级基金规模、份额变化统计20130816

ETF、分级基金规模、份额变化统计20130816 ETF 分 级 基 金 规 模 份 额 变 化 统 计 截 至 上 周 末, 全 市 场 股 票 型 ETF 规 模 约 1451 亿, 份 额 约 1215 亿,ETF 总 份 额 及 规 模 的 周 变 动 值 分 别 为 -23-44 亿, 份 额 与 规 模 均 下 降 ; 分 级 基 金 规 模 约 438 亿, 份 额 572 亿, 总 份 额 及 规 模 的 周 变 动 值 分 别 为

More information

Pro Git

Pro Git Pro Git Table of Contents Pro Git.......................................................................................... 1 Scott Chacon 序..............................................................................

More information

徐天宏:《基因天堂》.doc

徐天宏:《基因天堂》.doc - 1 - 阅 读 说 明 您 现 在 所 阅 读 的 这 本 电 子 图 书 由 E 类 出 版 物 制 作 出 品 本 书 版 权 归 作 者 所 有, E 类 出 版 物 只 负 责 制 作 发 行 工 作 在 保 证 原 书 内 容 完 整 的 情 况 下, 您 可 以 对 本 书 进 行 转 载 如 果 您 愿 意, 我 们 很 乐 意 您 在 转 载 的 时 候 写 上 我 们 网 站

More information

<443A5C6D B5C30312EB9A4D7F7CEC4B5B55C30322EBACFCDACCEC4B5B55C C30342EC8CBC9E7CCFC5C31332ECFEEC4BFC5E0D1B55C E30385C322EB2D9D7F7CAD6B2E12E646F63>

<443A5C6D B5C30312EB9A4D7F7CEC4B5B55C30322EBACFCDACCEC4B5B55C C30342EC8CBC9E7CCFC5C31332ECFEEC4BFC5E0D1B55C E30385C322EB2D9D7F7CAD6B2E12E646F63> 浙 江 职 业 能 力 建 设 信 息 系 统 职 业 技 能 鉴 定 考 务 管 理 用 户 操 作 手 册 二 〇 一 五 年 八 月 目 录 0. 系 统 概 述...3 0.0. 简 要 概 述...3 0.1. 业 务 流 程 图... 3 1. 考 生 网 上 报 名... 3 1.0. 考 生 用 户 注 册 登 录... 5 1.1. 报 名 及 下 载 打 印 报 名 申 请 表...7

More information

1600 1000 40 50 2030 2000 采 取 行 动 的 机 会 90% 开 拓 成 功 的 道 路 2

1600 1000 40 50 2030 2000 采 取 行 动 的 机 会 90% 开 拓 成 功 的 道 路 2 简 略 版 本 :2015 3 10 2016 2021 全 球 卫 生 部 门 病 毒 性 肝 炎 战 略 2016 2021 2015 3 12 2012 2010 2014 2015 2016 2021 140 55% 35% 5 15% 5 20% 2.4 1.3 1.5 1 1600 1000 40 50 2030 2000 采 取 行 动 的 机 会 90% 开 拓 成 功 的 道 路

More information

四川省农村义务教育学生

四川省农村义务教育学生 四 川 省 农 村 义 务 教 育 学 生 营 养 改 善 计 划 协 调 小 组 办 公 室 川 学 生 营 养 办 函 2015 7 号 关 于 填 报 2015 年 农 村 义 务 教 育 学 生 营 养 改 善 计 划 受 益 学 生 数 据 的 通 知 各 相 关 市 ( 州 ) 学 生 营 养 办 : 为 了 贯 彻 落 实 国 务 院 办 公 厅 关 于 实 施 农 村 义 务 教 育

More information

抗 日 战 争 研 究 年 第 期

抗 日 战 争 研 究 年 第 期 田 子 渝 武 汉 抗 战 时 期 是 国 共 第 二 次 合 作 的 最 好 时 期 在 国 共 合 作 的 基 础 上 出 现 了 抗 日 救 亡 共 御 外 侮 的 局 面 这 个 大 好 局 面 的 出 现 与 中 共 长 江 局 的 丰 功 伟 绩 是 分 不 开 的 但 长 期 以 来 由 于 有 一 个 王 明 的 右 倾 错 误 直 接 影 响 了 对 它 的 全 面 科 学 准 确

More information

三武一宗灭佛研究

三武一宗灭佛研究 四 川 大 学 博 士 学 位 论 文 三 武 一 宗 灭 佛 研 究 姓 名 : 张 箭 申 请 学 位 级 别 : 博 士 专 业 : 中 国 古 代 史 指 导 教 师 : 杨 耀 坤 20020101 三

More information

一、资质申请

一、资质申请 二 工 程 监 理 企 业 资 质 有 关 问 答 111 什 么 样 的 企 业 可 以 在 本 省 申 请 工 程 监 理 企 业 资 质? 答 : 在 鄂 取 得 法 人 营 业 执 照 或 合 伙 企 业 营 业 执 照 的 企 业, 都 可 依 法 向 工 商 注 册 所 在 省 或 市 建 设 行 政 主 管 部 门 行 政 审 批 部 门 申 请 工 程 监 理 企 业 资 质 取 得

More information

关于修订《沪市股票上网发行资金申购

关于修订《沪市股票上网发行资金申购 关 于 修 订 沪 市 股 票 上 网 发 行 资 金 申 购 实 施 办 法 的 通 知 各 有 关 单 位 : 沪 市 股 票 上 网 发 行 资 金 申 购 实 施 办 法 ( 修 订 稿 ) ( 见 附 件 ) 已 经 中 国 证 券 监 督 管 理 委 员 会 批 准, 现 将 修 订 所 涉 主 要 内 容 公 布 如 下 一 第 二 条 ( 二 ) 申 购 单 位 及 上 限 修 改

More information

操作手册

操作手册 企 业 网 上 银 行 使 用 手 册 ( 智 信 版 ) 中 国 农 业 银 行 股 份 有 限 公 司 重 庆 市 分 行 目 录 使 用 网 上 银 行 前 务 必 了 解 的 信 息... 3 1. 基 本 功 能... 3 2. 网 银 操 作 员... 3 3. 登 录 方 式... 3 4. 特 别 提 醒... 3 快 速 使 用 指 南... 4 1. 注 册 用 户... 4 2.

More information

解 决 困 扰 事 业 单 位 高 效 运 行 的 人 员 编 制 难 题 应 摒 弃 既 有 经 验 化 判 断 的 思 维 限 囿 经 由 规 范 化 程 式 化 维 度 专 注 于 事 业 单 位 人 员 编 制 的 标 准 管 理 考 虑 到 事 业 单 位 人 员 编 制 的 复 杂 性 和 公 益 导 向 宜 在 编 制 标 准 定 位 上 确 定 整 体 性 发 展 性 公 益 性 取

More information

<4D6963726F736F667420576F7264202D20D0A3B7A2A1B232303135A1B3313135BAC5B9D8D3DAD7E9D6AFBFAAD5B9C8ABD0A3BDCCD6B0B9A4B8DACEBBC6B8D3C3B1E4B6AFB9A4D7F7B5C4CDA8D6AA2E646F63>

<4D6963726F736F667420576F7264202D20D0A3B7A2A1B232303135A1B3313135BAC5B9D8D3DAD7E9D6AFBFAAD5B9C8ABD0A3BDCCD6B0B9A4B8DACEBBC6B8D3C3B1E4B6AFB9A4D7F7B5C4CDA8D6AA2E646F63> 广 西 科 技 大 学 文 件 校 发 2015 115 号 关 于 组 织 开 展 全 校 教 职 工 岗 位 聘 用 变 动 工 作 的 通 知 校 属 各 单 位 : 我 校 首 次 岗 位 聘 用 聘 期 已 满, 根 据 上 级 有 关 岗 位 设 置 与 聘 用 管 理 文 件 精 神, 为 进 一 步 促 进 学 校 人 才 资 源 的 科 学 合 理 配 置, 深 化 岗 位 聘 用

More information

<433A5C55736572735C6B73625C4465736B746F705CB9FABCCAD6D0D2BDD2A9D7A8D2B5B8DFBCB6BCBCCAF5D6B0B3C6C6C0C9F3C9EAC7EBD6B8C4CFA3A832303136CDA8D3C3B0E6A3A92E646F63>

<433A5C55736572735C6B73625C4465736B746F705CB9FABCCAD6D0D2BDD2A9D7A8D2B5B8DFBCB6BCBCCAF5D6B0B3C6C6C0C9F3C9EAC7EBD6B8C4CFA3A832303136CDA8D3C3B0E6A3A92E646F63> 附 件 1 国 际 中 药 专 业 高 级 技 术 职 称 评 审 条 件 及 报 名 材 料 一 系 列 ( 一 ) 中 1 高 级 专 科 ( 副 ) 高 级 专 科 ( 副 ) 1 取 得 中 专 科 职 称 后, 独 立 从 事 中 临 床 实 践 5 年 以 上 2 取 得 中 博 士 学 位 后, 临 床 实 践 2 年 以 上 3 取 得 中 硕 士 学 位 后, 临 床 实 践 7

More information

全国艺术科学规划项目

全国艺术科学规划项目 全 国 艺 术 科 学 规 划 项 目 网 上 申 报 常 见 问 题 及 解 决 方 法 目 录 一 申 报 流 程...4 二 立 项 流 程...5 三 常 见 问 题 与 处 理...6 (1). 如 何 注 册...6 (2). 系 统 中 没 有 我 的 单 位 怎 么 办?...7 (3). 在 注 册 时 写 错 身 份 证 号 名 字 而 系 统 中 不 能 修 改 怎 么 办?...

More information

易 迪 拓 培 训 专 注 于 微 波 射 频 天 线 设 计 人 才 的 培 养 网 址 :http://www.edatop.com 射 频 和 天 线 设 计 培 训 课 程 推 荐 易 迪 拓 培 训 (www.edatop.com) 由 数 名 来 自 于 研 发 第 一 线 的 资 深 工 程 师 发 起 成 立, 致 力 并 专 注 于 微 波 射 频 天 线 设 计 研 发 人 才

More information

目 录 页 1. 欢 迎 使 用 网 上 预 约 面 谈 访 问 系 统... 3 2. 新 用 户... 4 2.1 新 用 户 登 入 帐 户 程 序... 4 2.2 启 动 网 上 预 约 面 谈 访 问 帐 户... 5 2.2.1 核 对 帐 户 的 地 址 资 料... 5 2.2.2

目 录 页 1. 欢 迎 使 用 网 上 预 约 面 谈 访 问 系 统... 3 2. 新 用 户... 4 2.1 新 用 户 登 入 帐 户 程 序... 4 2.2 启 动 网 上 预 约 面 谈 访 问 帐 户... 5 2.2.1 核 对 帐 户 的 地 址 资 料... 5 2.2.2 网 上 预 约 面 谈 访 问 使 用 指 南 香 港 特 别 行 政 区 政 府 统 计 处 目 录 页 1. 欢 迎 使 用 网 上 预 约 面 谈 访 问 系 统... 3 2. 新 用 户... 4 2.1 新 用 户 登 入 帐 户 程 序... 4 2.2 启 动 网 上 预 约 面 谈 访 问 帐 户... 5 2.2.1 核 对 帐 户 的 地 址 资 料... 5 2.2.2 阅

More information

中国石化油品销售企业CRM调研报告

中国石化油品销售企业CRM调研报告 中 国 石 化 化 工 销 售 CRM 电 子 超 市 客 户 操 作 手 册 编 写 : CRM 项 目 组 适 用 范 围 : 化 工 销 售 有 限 公 司 华 北 分 公 司 版 本 : 1.1 日 期 : 2015.04 目 录 1. 会 员 服 务... 3 1.1 会 员 注 册... 3 1.2 会 员 登 陆... 6 1.3 用 户 信 息... 6 1.4 销 售 中 心...

More information

2 2015 年 8 月 11 日, 公 司 召 开 2015 年 第 五 次 临 时 股 东 大 会, 审 议 通 过 了 关 于 公 司 <2015 年 股 票 期 权 激 励 计 划 ( 草 案 )> 及 其 摘 要 的 议 案 关 于 提 请 股 东 大 会 授 权 董 事 会 办 理 公

2 2015 年 8 月 11 日, 公 司 召 开 2015 年 第 五 次 临 时 股 东 大 会, 审 议 通 过 了 关 于 公 司 <2015 年 股 票 期 权 激 励 计 划 ( 草 案 )> 及 其 摘 要 的 议 案 关 于 提 请 股 东 大 会 授 权 董 事 会 办 理 公 证 券 代 码 :300017 证 券 简 称 : 网 宿 科 技 公 告 编 号 :2016-053 网 宿 科 技 股 份 有 限 公 司 关 于 调 整 公 司 2015 年 股 票 期 权 激 励 计 划 激 励 对 象 股 票 期 权 数 量 和 行 权 价 格 的 公 告 本 公 司 及 董 事 会 全 体 成 员 保 证 公 告 内 容 真 实 准 确 和 完 整, 没 有 虚 假 记

More information

2016年德州市机构编制委员会

2016年德州市机构编制委员会 2016 年 德 州 市 机 构 编 制 委 员 会 办 公 室 部 门 预 算 -1- 第 一 部 分 部 门 概 况 目 录 一 主 要 职 能 二 部 门 预 算 单 位 构 成 第 二 部 分 2016 年 部 门 预 算 表 表 1 2016 年 收 支 预 算 总 表 表 2 2016 2016 年 收 入 预 算 表 ( 科 目 ) 表 3 2016 年 收 入 预 算 表 ( 单 位

More information

2016年山东省民主党派办公大楼管理处

2016年山东省民主党派办公大楼管理处 2016 年 山 东 省 民 主 党 派 办 公 大 楼 管 理 处 部 门 预 算 目 录 第 一 部 分 部 门 概 况 一 主 要 职 能 二 部 门 预 算 单 位 构 成 第 二 部 分 2016 年 部 门 预 算 表 一 2016 年 收 支 预 算 总 表 ( 功 能 分 类 科 目 ) 二 2016 年 收 支 预 算 总 表 ( 经 济 分 类 科 目 ) 三 2016 年 收

More information

工 程 造 价 咨 询 企 业 管 理 系 统 操 作 手 册 目 录 1 造 价 企 业 登 录... 1 2 企 业 基 本 信 息 查 看... 3 3 企 业 人 员 信 息 查 看... 4 4 企 业 基 本 信 息 操 作... 5 4.1 企 业 简 介... 5 4.2 企 业 章

工 程 造 价 咨 询 企 业 管 理 系 统 操 作 手 册 目 录 1 造 价 企 业 登 录... 1 2 企 业 基 本 信 息 查 看... 3 3 企 业 人 员 信 息 查 看... 4 4 企 业 基 本 信 息 操 作... 5 4.1 企 业 简 介... 5 4.2 企 业 章 工 程 造 价 咨 询 企 业 管 理 系 统 操 作 手 册 工 程 造 价 咨 询 企 业 管 理 系 统 ( 造 价 企 业 ) 用 户 手 册 工 程 造 价 咨 询 企 业 管 理 系 统 操 作 手 册 目 录 1 造 价 企 业 登 录... 1 2 企 业 基 本 信 息 查 看... 3 3 企 业 人 员 信 息 查 看... 4 4 企 业 基 本 信 息 操 作... 5 4.1

More information

伊 犁 师 范 学 院 611 语 言 学 概 论 全 套 考 研 资 料 <2016 年 最 新 考 研 资 料 > 2-2 语 言 学 纲 要 笔 记, 由 考 取 本 校 本 专 业 高 分 研 究 生 总 结 而 来, 重 点 突 出, 借 助 此 笔 记 可 以 大 大 提 高 复 习 效

伊 犁 师 范 学 院 611 语 言 学 概 论 全 套 考 研 资 料 <2016 年 最 新 考 研 资 料 > 2-2 语 言 学 纲 要 笔 记, 由 考 取 本 校 本 专 业 高 分 研 究 生 总 结 而 来, 重 点 突 出, 借 助 此 笔 记 可 以 大 大 提 高 复 习 效 伊 犁 师 范 学 院 611 语 言 学 概 论 全 套 考 研 资 料 ......2 伊 犁 师 范 学 院 802 文 学 概 论 全 套 考 研 资 料 ......2 伊 犁 师 范 学 院 702 普 通 物 理 全 套 考 研 资 料 ......3 伊 犁

More information

目 录 办 公 OA 介 绍... 3 办 公 OA... 4 一. 收 件 箱 发 布 信 件 查 看 个 人 信 件... 7 二. 公 共 留 言 发 布 公 共 留 言 查 看 公 共 留 言... 9 三. 校 长 荐

目 录 办 公 OA 介 绍... 3 办 公 OA... 4 一. 收 件 箱 发 布 信 件 查 看 个 人 信 件... 7 二. 公 共 留 言 发 布 公 共 留 言 查 看 公 共 留 言... 9 三. 校 长 荐 思 库 网 Centricity2 目 录 办 公 OA 介 绍... 3 办 公 OA... 4 一. 收 件 箱... 4 1.1 发 布 信 件... 4 1.2 查 看 个 人 信 件... 7 二. 公 共 留 言... 8 2.1 发 布 公 共 留 言... 8 2.2 查 看 公 共 留 言... 9 三. 校 长 荐 读... 9 3.1 发 布 校 长 荐 读... 9 3.2

More information

合 并 计 算 配 售 对 象 持 有 多 个 证 券 账 户 的, 多 个 证 券 账 户 市 值 合 并 计 算 确 认 多 个 证 券 账 户 为 同 一 配 售 对 象 持 有 的 原 则 为 证 券 账 户 注 册 资 料 中 的 账 户 持 有 人 名 称 有 效 身 份 证 明 文 件

合 并 计 算 配 售 对 象 持 有 多 个 证 券 账 户 的, 多 个 证 券 账 户 市 值 合 并 计 算 确 认 多 个 证 券 账 户 为 同 一 配 售 对 象 持 有 的 原 则 为 证 券 账 户 注 册 资 料 中 的 账 户 持 有 人 名 称 有 效 身 份 证 明 文 件 深 圳 市 场 首 次 公 开 发 行 股 票 网 下 发 行 实 施 细 则 ( 征 求 意 见 稿 ) 第 一 章 总 则 第 一 条 为 规 范 深 圳 市 场 首 次 公 开 发 行 股 票 网 下 发 行 行 为, 根 据 证 券 发 行 与 承 销 管 理 办 法 及 相 关 规 定, 制 定 本 细 则 第 二 条 本 细 则 所 称 网 下 发 行 是 指 首 次 公 开 发 行 股

More information

第 一 部 分 MagiCAD for Revit 安 装 流 程

第 一 部 分 MagiCAD for Revit 安 装 流 程 MagiCAD 软 件 安 装 流 程 MagiCAD v2015.4 for Revit 广 联 达 软 件 股 份 有 限 公 司 BIM 中 心 编 写 2015 年 06 月 第 一 部 分 MagiCAD for Revit 安 装 流 程 一 安 装 前 需 要 确 认 的 内 容 安 装 MagiCAD 程 序 之 前, 请 您 先 确 定 以 下 事 宜 1. 当 前 用 户 账 户

More information

立 场 反 思 教 育 学 与 哲 学 和 科 学 的 对 话 杨 小 微 从 某 种 意 义 上 说 教 育 学 是 在 与 哲 学 等 相 关 学 科 的 对 话 中 成 长 起 来 的 它 先 后 经 历 了 亲 哲 学 和 亲 科 学 阶 段 而 今 正 在 走 向 事 理 知 识 时 期 对 话 使 教 育 学 从 马 克 思 主 义 哲 学 自 然 科 学 及 系 统 方 法 论 人 本

More information

untitled

untitled ( 一 ) 深 刻 认 识 学 习 教 育 的 重 大 意 义 : - 3 - ( 二 ) 明 确 学 习 教 育 的 任 务 目 标 ( 三 ) 把 握 特 点 方 法 - 4 - ( 四 ) 坚 持 六 项 原 则 在 - 5 - ( 五 ) 着 力 解 决 问 题 - 6 - - 7 - - 8 - ( 一 ) 学 党 章 党 规, 进 一 步 明 确 党 员 标 准 树 立 行 为 规 范

More information