22.11.2014 Aufrufe

用Git 进行版本控制

用Git 进行版本控制

用Git 进行版本控制

MEHR ANZEIGEN
WENIGER ANZEIGEN

Erfolgreiche ePaper selbst erstellen

Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.

用 Git 进 行 版 本 控 制<br />

李 博 杰 2012­05­20<br />

bojieli@gmail.com


版 本 控 制 的 史 前 时 代<br />

●<br />

用 存 储 介 质 拷 贝 代 码<br />

– 代 码 相 互 覆 盖 , 不 知 道 哪 个 版 本 是 正 确 的<br />

– 搞 错 了 无 法 恢 复 , 需 要 定 期 手 工 备 份<br />

●<br />

diff & patch<br />

– 1991~2002 , Linux 内 核<br />

– 能 看 到 文 件 之 间 的 差 异 , 知 道 哪 里 修 改 了<br />

– 更 改 历 史 需 要 手 工 维 护<br />

– GNU diff 不 支 持 二 进 制 文 件


版 本 控 制 的 诞 生<br />

●<br />

将 diff 和 patch 的 过 程 自 动 化<br />

– 单 一 文 件 版 本 管 理 工 具 RCS<br />

– 只 保 留 一 个 版 本 的 完 全 拷 贝 , 其 他 历 次 更 改 仅 保<br />

留 差 异 : V3=V1+△1+△2<br />

●<br />

CVS : 脚 本 实 现 的 RCS 文 件 容 器 (1985, 1986 publish,<br />

1989 rewriten in C)<br />

– 版 本 库 中 任 意 一 个 目 录 拿 出 来 是 一 个 新 的 版 本 库<br />

– Commit log, checkin, checkout, tag, branch<br />

– 文 件 的 版 本 号 相 互 独 立 , 全 局 版 本 号 只 能 不 停 地<br />

打 tag


CVS 原 理 示 意


SVN<br />

●<br />

●<br />

●<br />

索 引 的 是 二 维 信 息 : 文 件 、 版 本<br />

CVS : 工 作 区 中 的 每 个 文 件 对 应 版 本 库 中 的 一 个<br />

文 件<br />

SVN : 每 个 版 本 的 一 个 差 异 文 件 , 一 个 信 息 文 件<br />

– 以 顺 序 数 字 编 号 命 名<br />

– db/revs 下 的 : 与 上 一 提 交 的 差 异<br />

– db/revprogs 下 的 : 提 交 日 志 、 作 者 、 提 交 时 间<br />

●<br />

SVN 实 现 了 全 局 版 本 号 、 原 子 提 交 、 跟 踪 重 命 名


SVN 原 理 示 意


Linus 为 何 迟 迟 不 用 版 本 控 制<br />

●<br />

集 中 式 版 本 控 制 系 统<br />

– 版 本 库 存 储 在 服 务 器 端 , 每 次 提 交 、 查 看 日 志 能<br />

操 作 都 要 与 服 务 器 连 接<br />

– 代 码 的 稳 定 性 高 度 依 赖 中 心 服 务 器<br />

●<br />

– 历 史 不 容 篡 改 , 无 法 做 试 验 性 提 交 ,“ 一 失 足 成 千<br />

古 恨 ”<br />

Linus 认 为 这 不 符 合 开 源 项 目 的 精 神 , 因 此 直 到<br />

2002 年 才 使 用 商 业 版 本 控 制 系 统 BitKeeper 管 理<br />

Linux 代 码


分 布 式 版 本 控 制 系 统<br />

●<br />

●<br />

●<br />

●<br />

每 个 人 拥 有 一 个 完 整 的 版 本 库<br />

查 看 日 志 、 提 交 、 创 建 tag 和 分 支 等 操 作 可 以 在<br />

本 地 完 成 , 不 再 时 刻 需 要 网 络 连 接<br />

在 推 送 到 远 程 版 本 库 前 可 以 做 很 多 试 验 性 的 提<br />

交 , 反 复 悔 改 而 不 必 担 心 干 扰 其 他 人<br />

多 样 的 协 同 工 作 模 型 使 开 源 项 目 的 参 与 度 爆 发 式<br />

增 长


Git 是 逼 出 来 的<br />

●<br />

●<br />

2005 年 4 月 , Andrew Tridgell 试 图 对 BitKeeper 进 行 反<br />

向 工 程 , 以 开 发 一 个 能 与 之 交 互 的 开 源 工 具<br />

BitMover 公 司 要 求 收 回 对 Linux 社 区 的 免 费 授 权<br />

●<br />

Linus 只 好 “ 自 力 更 生 ”:<br />

– 2005­04­03 , 开 始 开 发<br />

– 2005­04­06 , 项 目 发 布<br />

– 2005­04­07 , Git 作 为 自 身 的 版 本 控 制 工 具<br />

– 此 时 git 代 码 只 有 1244 行 , 只 有 一 些 底 层 命 令


Git 是 逼 出 来 的 (cont'd)<br />

– 2005­04­18 , 第 一 次 分 支 合 并<br />

– 2005­04­29 , Git 的 性 能 达 到 Linus 的 预 期<br />

– 2005­06­16 , Linux 2.6.12 开 始 采 用 Git<br />

●<br />

●<br />

– 2005­07­26 , Linus 功 成 身 退 , 将 Git 的 维 护 交<br />

给 另 一 位 主 要 贡 献 者 Junio C Hamano<br />

最 初 的 Git 除 了 一 些 核 心 命 令 , 都 用 脚 本 语 言 写<br />

成 ( 现 在 为 了 效 率 大 多 用 C 语 言 重 写 )<br />

Android, Debian, Eclipse, Git, Gnome, KDE, Linux kernel, Perl,<br />

PHP, PostgreSQL, Qt, Ruby on Rails, X.org...


Git config<br />

●<br />

git 是 分 布 式 版 本 控 制 系 统 , 不 存 在 “ 验 证 ” 用 户 名 、<br />

●<br />

密 码 的 中 央 版 本 库<br />

git 根 据 提 交 者 设 置 的 name 和 email 记 录 更 改 是 谁 做<br />

出 的 , 因 此 安 装 git 后 的 第 一 件 事 就 是 设 置 个 人 信<br />

息 :<br />

– git config ­­global user.email “bojieli@gmail.com”<br />

– git config ­­global user.name “boj”<br />

– 不 要 逐 字 照 抄 , 不 然 功 劳 就 算 到 我 头 上 了 ~


Git 初 始 化<br />

●<br />

●<br />

初 始 化 版 本 库 : git init<br />

得 到 隐 藏 的 .git , 版 本 库 信 息 都 存 储 在 .git 中<br />

●<br />

版 本 库 在 服 务 器 端 ?<br />

●<br />

版 本 库 在 本 地 ?<br />

– 集 中 存 储 在 全 局 的 “ 数 据 库 ” 中 ?<br />

– 在 工 作 区 每 个 目 录 中 ?( CVS , Subversion )<br />

– 在 工 作 区 根 目 录 下 ?( git )


首 次 提 交<br />

●<br />

●<br />

●<br />

git init 只 是 建 立 了 一 个 空 的 版 本 库<br />

要 将 已 有 的 内 容 纳 入 版 本 控 制 , 需 要<br />

将 当 前 目 录 中 的 所 有 文 件 添 加 到 索 引<br />

– git add .<br />

●<br />

然 后 提 交 到 版 本 库 。<br />

– git commit ­m “initial commit”<br />

●<br />

查 看 提 交 历 史 是 否 正 常<br />

– git log


git commit<br />

●<br />

git commit 的 输 出 信 息<br />

– 此 次 提 交 是 在 master ( 主 分 支 ) 上 的 , 是 该 分<br />

支 上 的 根 提 交 ( 首 个 提 交 ), 提 交 ID 为 ……<br />

– 此 次 提 交 修 改 了 11 个 文 件 , 增 加 了 1244 行<br />

– 创 建 了 若 干 个 文 件 , 其 权 限 均 为 644<br />

●<br />

Git 规 定 提 交 必 须 输 入 提 交 说 明<br />

– git commit ­m “commit message”<br />

– 或 git commit , 进 入 编 辑 器 编 辑 提 交 说 明 ( 至 少<br />

学 会 一 种 编 辑 器 吧 , 不 然 保 存 退 出 都 成 问 题 )


Git config 的 配 置 哪 里 去 了<br />

git config ­­system /etc/gitconfig 系 统<br />

git config ­­global ~/.gitconfig 当 前 用 户<br />

git config .git/config 当 前 版 本 库<br />

●<br />

git config 默 认 只 针 对 当 前 版 本 库<br />

– 当 前 版 本 库 是 如 何 确 定 的 ?<br />

– 没 有 全 局 “ 数 据 库 ”, 只 能 找 .git 哦<br />

●<br />

Git 配 置 文 件 使 用 ini 文 件 格 式<br />

– 读 : git config .<br />

– 写 : git config .


Git config 试 验<br />

●<br />

如 果 不 设 置 user, email 会 怎 么 样 ?<br />

– git config ­­unset ­­global user.name<br />

– git config ­­unset ­­global user.email<br />

– git commit ­­allow­empty ­m “anonymous<br />

commit” ( 加 参 数 以 允 许 空 提 交 )<br />

●<br />

如 果 设 置 了 当 前 版 本 库 的 user, email 会 怎 么 样 ?<br />

– git config user.email “bojieli@gmail.com”<br />

– git commit ­­amend ­­allow­empty ­­reset­author<br />

●<br />

●<br />

amend : 重 新 进 行 最 近 一 次 提 交<br />

reset­author : 默 认 不 更 新 提 交 日 志 中 的 Author


git add 与 暂 存 区<br />

●<br />

●<br />

首 先 对 版 本 库 做 一 些 修 改<br />

用 git diff 查 看 有 哪 些 改 动<br />

– 输 出 就 是 GNU diff 的 样 式<br />

– 支 持 二 进 制 文 件 哦<br />

●<br />

git commit ­m “unstaged commit” , 成 功 了 吗 ?<br />

– git log 没 有 增 加 新 的 提 交 日 志<br />

– git status 有 点 似 曾 相 识 的 感 觉<br />

– git diff 与 commit 前 相 比 没 有 变 化


git add 与 暂 存 区 (cont'd)<br />

●<br />

Changes not staged for commit?<br />

●<br />

只 有 暂 存 的 修 改 才 会 被 提 交 。<br />

●<br />

●<br />

– 如 果 同 时 在 修 改 两 个 不 同 功 能 模 块 , 一 起<br />

commit 不 如 把 两 个 模 块 的 修 改 分 开 commit<br />

– 如 果 正 在 开 发 的 两 个 模 块 一 个 已 经 编 好 , 另 一 个<br />

尚 未 完 成 , 如 何 提 交 阶 段 性 成 果 ?<br />

git 的 “ 暂 存 区 ” 设 计 赋 予 用 户 对 提 交 内 容 进 行 控 制<br />

的 能 力 , 使 得 “ 按 需 提 交 ” 成 为 可 能 。<br />

工 作 区 (working dir) => 暂 存 区 (index) => 版 本 库 (repo)


git add 与 暂 存 区 (cont'd)


查 看 状 态 : git status<br />

●<br />

工 作 目 录 中 的 每 个 文 件 可 能 处 于 四 种 状 态 之 一 :<br />

– untracked : 未 被 跟 踪 , 新 添 加 的 文 件 不 会 被 自<br />

动 跟 踪 哦 ~<br />

– unmodified : 已 被 跟 踪 , 但 未 被 修 改<br />

– modified : 已 被 修 改 , 但 尚 未 被 暂 存<br />

– staged : 修 改 已 被 暂 存<br />

●<br />

git add 就 是 把 文 件 或 目 录 加 入 暂 存 区 的 方 法


文 件 的 四 种 状 态 及 转 化


查 看 状 态 : git status (cont'd)<br />

●<br />

git add 的 几 种 偷 懒 方 法<br />

– git add . 将 当 前 目 录 下 的 所 有 文 件 变 更 ( 包 括 新<br />

文 件 ) 放 入 暂 存 区<br />

– git add ­u 将 当 前 目 录 下 被 版 本 库 跟 踪 的 所 有 文<br />

件 变 更 ( 不 包 括 新 文 件 ) 放 入 暂 存 区<br />

– git add ­A 将 当 前 目 录 下 所 有 文 件 变 更 ( 包 括 新<br />

文 件 ) 放 入 暂 存 区 , 并 查 找 重 命 名 情 况<br />

– git add ­i 使 用 交 互 式 界 面 选 择 需 要 添 加 的 文 件


查 看 状 态 : git status (cont'd)<br />

●<br />

每 次 提 交 前 add 太 麻 烦 ?<br />

– git commit ­a = git add ­u + git commit<br />

●<br />

Git 可 以 跟 踪 文 件 的 移 动<br />

– git mv , 直 接 将 文 件 移 动 写 入 暂 存 区<br />

●<br />

– 使 用 mv 移 动 , 只 要 使 用 git add ­A 添 加 , git 也<br />

能 检 测 到 文 件 移 动<br />

git rm 可 以 将 文 件 的 删 除 写 入 暂 存 区


查 看 状 态 : git status (cont'd)<br />

●<br />

git status 扫 描 工 作 区 改 动 时<br />

– 根 据 .git/index 记 录 的 时 间 戳 、 长 度 等 信 息 判 断<br />

工 作 区 文 件 是 否 被 改 变<br />

– 如 果 时 间 戳 改 变 了 , 需 要 读 取 文 件 内 容 , 计 算 其<br />

SHA1 hash 值 , 与 版 本 库 中 的 相 比 较 ; 如 果 文<br />

件 内 容 没 有 改 变 , 则 更 新 .git/index 时 间 戳<br />

●<br />

.git/index 是 包 含 文 件 索 引 的 目 录 树 , 记 录 文 件 名 、<br />

时 间 戳 、 文 件 长 度 等 。 文 件 内 容 存 储 于 git 对 象<br />

库 .git/objects 中 , 文 件 索 引 建 立 了 文 件 与 对 象 库 中<br />

文 件 内 容 间 的 对 应 。


查 看 状 态 : git status (cont'd)<br />

●<br />

git status<br />

– 扫 描 暂 存 区 相 对 工 作 区 的 改 动 ( 只 看 索 引 )<br />

– 扫 描 暂 存 区 相 对 版 本 库 的 改 动<br />

– 扫 描 未 被 版 本 库 跟 踪 的 文 件 ( 扫 描 目 录 )<br />

●<br />

修 改 一 个 文 件 , 添 加 到 暂 存 区 ; 再 修 改 它 一 下 ,<br />

执 行 git status 。 根 据 前 面 的 原 理 , 你 能 想 到 会 发<br />

生 什 么 吗 ?<br />

●<br />

暂 存 区 在 .git/index , 版 本 库 在 哪 里 ?


走 进 版 本 库 的 老 巢<br />

●<br />

●<br />

●<br />

.git/HEAD: “ref: refs/heads/master”<br />

.git/refs/heads/master: 一 个 提 交 ID<br />

对 象 存 储 在 .git/objects/ID 的 前 两 位 /ID 的 后 38 位<br />

●<br />

强 大 的 git cat­file :<br />

– git cat­file ­t 3eb72cf (commit)<br />

– git cat­file ­p 3eb72cf (tree, parent)<br />

●<br />

tree 对 象 : 本 次 提 交 的 文 件 列 表 ( 文 件 名 、 属<br />

性 、 内 容 所 在 对 象 )


走 进 版 本 库 的 老 巢 (cont'd)<br />

●<br />

●<br />

blob 对 象 存 储 的 就 是 文 件 内 容<br />

– 由 于 对 象 是 按 照 hash 值 存 储 的 , 相 同 内 容 的 文<br />

件 只 会 出 现 一 个 副 本 。<br />

commit 对 象 中 的 parent 指 向 上 一 提 交<br />

– Git 中 每 个 提 交 的 tree 是 对 于 上 一 提 交 的 增 量<br />

– 通 过 commit 对 象 间 的 parent 关 联 , 可 以 识 别 出<br />

一 条 跟 踪 链 : git log ­­pretty=raw ­­graph


Git object hash<br />

●<br />

Commit, tree, blob 对 象 的 40 位 ID 是 如 何 生 成 的 ?<br />

●<br />

首 先 看 commit 的 ID 生 成 规 则<br />

●<br />

●<br />

●<br />

git cat­file ­p HEAD 等 于 git cat­file commit HEAD<br />

git rev­parse HEAD<br />

(echo ­n "commit "; echo ­n `git cat­file ­p HEAD | wc ­c`; printf<br />

"\000"; git cat­file ­p HEAD) | sha1sum


Git object hash (cont'd)<br />

●<br />

●<br />

●<br />

●<br />

文 件 内 容 blob 对 象<br />

git rev­parse HEAD:Makefile<br />

(echo ­n "blob "; echo ­n `git cat­file ­p HEAD:Makefile | wc ­c`;<br />

printf "\000"; git cat­file ­p HEAD:Makefile) | sha1sum<br />

tree 对 象<br />

●<br />

●<br />

●<br />

git cat­file ­p HEAD^{tree} 不 等 于 git cat­file tree HEAD^{tree}<br />

git rev­parse HEAD^{tree}<br />

(echo ­n "tree "; echo ­n `git cat­file tree HEAD^{tree} | wc ­c`;<br />

printf "\000"; git cat­file tree HEAD^{tree}) | sha1sum


Git object hash (cont'd)<br />

●<br />

●<br />

●<br />

●<br />

如 果 提 交 采 用 顺 序 编 号 , 在 分 布 式 版 本 控 制 系 统<br />

中 无 法 做 到 不 同 人 的 提 交 编 号 不 同<br />

Git 通 过 SHA1 密 码 学 意 义 上 的 无 重 复 特 性 使 得 不<br />

同 内 容 、 不 同 时 间 、 不 同 作 者 的 提 交 “ 全 球 唯 一 ”<br />

每 个 对 象 的 ID 与 对 象 类 型 、 长 度 、 内 容 关 联<br />

一 个 提 交 的 ID 与 其 父 提 交 关 联 , 使 得 修 改 历 史 会<br />

产 生 连 锁 效 应 , 谁 也 不 能 篡 改 历 史


Hash collision<br />

●<br />

160 位 的 SHA1 hash 冲 突 的 概 率 微 乎 其 微 , 因 而 Git<br />

并 没 有 考 虑 hash 冲 突 的 问 题<br />

●<br />

Linus 在 Git 邮 件 列 表 中 回 应 此 问 题 时 说 :<br />

●<br />

在 commit 时 , git 如 果 发 现 待 添 加 的 新 对 象 SHA1<br />

值 已 经 存 在 , 则 会 认 为 这 个 对 象 已 经 存 在 , 因 而<br />

引 起 冲 突 的 新 文 件 不 会 被 保 存 。 用 户 执 行<br />

checkout 时 才 会 发 现 得 到 的 文 件 与 所 期 望 的 不 同 。


Hash collision<br />

●<br />

●<br />

●<br />

因 此 , 即 使 恶 意 者 提 交 了 引 起 冲 突 的 文 件 , 也 不<br />

会 改 变 历 史 。( 构 造 SHA1 冲 突 是 很 难 的 !)<br />

如 果 真 的 在 很 偶 然 的 情 况 下 发 生 了 冲 突 , 则 只 要<br />

修 改 待 添 加 的 文 件 ( 如 加 个 空 行 ) 即 可 避 开 。<br />

如 果 提 前 获 知 一 个 patch , 并 构 造 冲 突 包 抢 先 发<br />

给 Linus , 则 能 让 真 正 的 patch 被 “ 虚 假 ” 提 交 进 去<br />

– 例 如 commit message 在 显 示 时 被 认 为 是 \0 结 尾<br />

的 , 因 此 可 以 用 hash­object 手 工 制 作 commit<br />

对 象 , 在 \0 后 面 藏 一 些 好 玩 的 东 西


访 问 对 象 的 方 法<br />

● “ 基 址 ”<br />

● “ 偏 移 ”<br />

– 在 不 引 起 冲 突 的 情 况 下 , 只 要 把 SHA1 hash 值<br />

的 前 几 位 写 出 即 可<br />

– master = refs/heads/master = heads/master<br />

– HEAD<br />

– HEAD^, HEAD^^, HEAD^ : 父 提 交<br />

– HEAD^2 : 第 二 个 父 提 交 ( 有 多 个 父 提 交 时 用 )<br />

– HEAD~5 : 祖 先 提 交


访 问 对 象 的 方 法<br />

●<br />

内 容<br />

– 树 对 象 : HEAD^{tree}<br />

– 文 件 对 象 : HEAD:Makefile<br />

– 暂 存 区 中 的 文 件 对 象 : :Makefile<br />

●<br />

命 令<br />

– git show : 查 看 提 交 或 对 象 的 详 细 信 息<br />

– git rev­parse : 查 询 对 象 ID<br />

– git cat­file : 查 看 对 象 内 容 和 类 型


研 究 Git 常 用 命 令<br />

– Git cat­file<br />

– Git rev­parse<br />

– Git show<br />

– Git ls­tree<br />

– Git ls­files<br />

– Git hash­object<br />

– Git rev­list


分 支 游 标<br />

●<br />

●<br />

●<br />

●<br />

●<br />

cat .git/refs/heads/master<br />

Echo “Hello World” >> README<br />

Git commit ­a ­m “Does master follow this new commit?”<br />

cat .git/refs/heads/master<br />

refs/heads/master 就 像 一 个 游 标 , 总 是 指 向 master<br />

( 主 分 支 ) 上 “ 最 新 ” 的 提 交 。<br />

– refs/tags/tagxxx 指 向 tag xxx ( 里 程 碑 )<br />

– refs/heads/branchxxx 指 向 branch xxx ( 分 支 )


git reset<br />

●<br />

有 了 游 标 , 我 们 就 可 以 在 git 历 史 中 任 意 穿 梭 了<br />

– 1. 将 HEAD 游 标 指 向 新 的 提 交 ID<br />

– 2. 用 游 标 所 指 向 的 提 交 替 换 暂 存 区<br />

– 3. 用 暂 存 区 替 换 工 作 区<br />

●<br />

git reset<br />

– git reset ­­soft (1)<br />

– git reset ­­hard (1,2,3) 危 险 !<br />

– git reset (1,2)<br />

– git reset ­­mixed (1,2)


git reset (cont'd)<br />

●<br />

●<br />

不 指 定 commit , 默 认 为 HEAD<br />

git reset : 丢 弃 已 暂 存 的 更 改<br />

●<br />

git reset ­­hard 丢 弃 工 作 区 和 已 暂 存 的 更 改 ( 危 险 )<br />

●<br />

git reset ­­ filename : git add 的 反 向 操 作<br />

● git commit ­­amend = git reset ­­soft HEAD^ + git commit ­e ­F<br />

– .git/COMMIT_EDITMSG 保 存 了 上 次 的 提 交 说 明<br />

●<br />

git reset ­­hard HEAD^ : 丢 弃 最 近 一 次 提 交 及 此 后 的<br />

更 改 ( 危 险 )


最 后 一 道 防 线 : git reflog<br />

●<br />

●<br />

●<br />

上 张 slides 中 带 ­­hard 的 都 被 标 为 “ 危 险 ”, 因 为 工<br />

作 区 没 有 备 份 , 一 旦 覆 盖 永 久 丢 失 ; 那 么 版 本 库<br />

中 被 reset 丢 弃 的 提 交 还 能 找 回 来 吗 ?<br />

.git/logs/HEAD, .git/logs/refs/heads/master<br />

– 游 标 的 更 改 是 有 历 史 记 录 的 , 因 此 这 些 被 “ 丢 弃 ”<br />

的 提 交 事 实 上 记 录 在 册<br />

– 不 过 不 能 高 枕 无 忧 , 默 认 90 天 过 期 , 过 期 后 的<br />

更 改 记 录 会 被 清 理 掉<br />

git reflog : 查 看 游 标 历 史


最 后 一 道 防 线 : git reflog (cont'd)<br />

●<br />

访 问 对 象 的 方 法 又 多 了 一 种 :<br />

– HEAD@{n} : HEAD 游 标 的 第 n 次 变 化<br />

– master@{n} : master 分 支 游 标 的 第 n 次 变 化<br />

– HEAD@{0} 就 是 刚 刚 进 行 的 操 作<br />

●<br />

– git reset HEAD@{1} 就 能 把 刚 刚 被 reset 的 提 交<br />

找 回 来 啦<br />

利 用 git reset 对 分 支 游 标 和 工 作 区 的 强 大 控 制 能<br />

力 , 我 们 可 以 在 历 史 中 自 由 穿 梭 , 甚 至 改 变 历 史


破 坏 git 的 防 线<br />

●<br />

不 小 心 commit 了 一 个 大 文 件 , 无 论 如 何 reset ­­hard<br />

版 本 库 也 不 会 变 小<br />

– 只 要 被 引 用 , 对 象 就 会 一 直 保 留<br />

– 直 接 删 除 对 应 的 object , 版 本 库 的 一 致 性 被 破 坏<br />

– git fsck ­­no­reflog : dangling 的 对 象 就 是 没 有<br />

被 引 用 的 对 象 , 随 时 可 能 被 清 理 掉<br />

– git reflog expire ­­expire=now ­­all<br />

– git fsck<br />

– git prune


Git checkout<br />

●<br />

●<br />

git reset 虽 然 强 大 , 但 针 对 的 都 是 HEAD , 而 HEAD<br />

指 向 的 是 refs/heads/master , 这 就 意 味 着 只 能 修 改<br />

当 前 分 支 。 那 么 HEAD 本 身 该 如 何 修 改 呢 ?<br />

git checkout <br />

– 将 某 个 特 定 的 提 交 检 出 : 更 新 HEAD 指 向<br />

, 用 此 提 交 更 新 暂 存 区 和 工 作 区<br />

– 危 险 , 暂 存 区 和 工 作 区 的 未 提 交 改 动 会 被 覆 盖<br />

●<br />

git checkout [­­] <br />

– 不 更 新 HEAD , 只 覆 盖 指 定 路 径 的 文 件


git checkout (cont'd)<br />

●<br />

●<br />

You are in 'detached HEAD' state?<br />

分 离 头 指 针 , 就 是 HEAD 头 指 针 指 向 了 一 个 具 体<br />

的 提 交 , 如 果 再 次 执 行 checkout , 就 会 覆 盖 掉 这<br />

个 HEAD , 从 而 丢 失 这 一 串 提 交 。<br />

– 事 实 上 可 以 通 过 reflog 这 个 神 器 找 回 来<br />

●<br />

推 荐 的 方 式 是 创 建 新 的 分 支 :<br />

– git checkout ­b new_branch_name <br />

– 省 略 commit , 则 默 认 为 HEAD


Git stash<br />

●<br />

●<br />

如 果 有 一 些 未 提 交 的 改 动 , 现 在 希 望 参 考 一 下 原<br />

来 的 版 本 , 怎 么 办 ?<br />

git stash 可 以 保 存 当 前 进 度 , 把 工 作 区 和 暂 存 区 尚<br />

未 提 交 的 改 动 照 个 快 照 保 存 起 来 , 然 后 reset ­­hard<br />

●<br />

git stash list 查 看 保 存 的 进 度 列 表 ( 栈 )<br />

– 又 一 种 访 问 对 象 的 方 法 : stash@{n}<br />

●<br />

git stash pop 把 栈 顶 的 进 度 “ 出 栈 ”<br />

●<br />

git stash apply 应 用 栈 顶 的 进 度 , 但 不 出 栈


git stash 原 理<br />

●<br />

stash 之 后 , 发 现 多 了 个 refs/stash , 内 有 提 交 ID<br />

– 一 个 提 交 如 何 同 时 表 示 工 作 区 和 暂 存 区 ?<br />

●<br />

顺 藤 摸 瓜 , 发 现 一 个 commit , 它 有 两 个 parent<br />

– git cat­file 不 要 这 么 快 就 忘 了 哦 ~<br />

– 这 是 一 个 合 并 提 交 , 内 容 为 工 作 区 进 度 ( Work<br />

In Progress, WIP )<br />

– 一 个 parent 是 原 来 的 HEAD<br />

– 另 一 个 parent 是 暂 存 区 进 度 ( 它 的 parent 是 原<br />

来 的 HEAD )


git checkout (cont'd)<br />

●<br />

git checkout [­­] <br />

– 用 暂 存 区 覆 盖 工 作 区<br />

●<br />

●<br />

git checkout <br />

– 切 换 到 已 有 分 支 : 更 新 HEAD 指 向<br />

refs/heads/branch , 用 分 支 的 最 新 提 交 覆 盖 暂<br />

存 区 和 工 作 区<br />

git checkout ­b []<br />

– 从 创 建 新 分 支 , 覆 盖 暂 存 区 和 工 作 区<br />

●<br />

git checkout 之 前 忘 记 git stash , 欲 哭 无 泪


git 中 的 时 光 穿 梭 机<br />

●<br />

如 果 说 git log 是 版 本 库 历 史 的 一 张 平 面 图 , 那 么<br />

git 图 形 工 具 就 是 一 张 全 息 图 , 能 更 直 观 地 展 示 各<br />

提 交 间 的 相 互 关 系 。<br />

– gitk ( 原 生 )<br />

– qgit ( QT )<br />

– gitg ( GTK+ )<br />

●<br />

在 命 令 行 下 , git log ­­graph 也 可 以 显 示 提 交 关 系


查 看 指 定 范 围 的 历 史<br />

●<br />

git log 中 不 仅 可 以 指 定 一 个 提 交 , 还 可 以 指 定 提 交<br />

范 围 。<br />

●<br />

git log ­­oneline A : A 的 所 有 历 史 提 交 ( 一 棵 树 )<br />

●<br />

git log ­­oneline D F : 两 棵 树 的 并 集<br />

●<br />

●<br />

●<br />

git log ­­oneline ^G D : ^ 是 取 反 , 即 不 包 括 树 G<br />

git log ­­oneline G..D : 与 上 面 相 同<br />

git log ­­oneline D..G : 与 上 面 不 同


查 看 指 定 范 围 的 历 史 (cont'd)<br />

●<br />

●<br />

●<br />

git log ­­oneline B...C : 两 个 版 本 能 够 共 同 访 问 到 的 除<br />

外 = B C ­­not $(git merge­base ­­all B C)<br />

git log ­­oneline B^@ : 不 包 括 自 身 的 历 史 提 交<br />

git log ­­oneline B^! : 只 包 括 自 身 , 不 包 括 历 史 提 交<br />

●<br />

使 用 git rev­list 可 列 出 匹 配 的 提 交 ID


定 制 git log 的 输 出<br />

●<br />

●<br />

git log ­3 : 显 示 最 近 的 3 条 日 志<br />

git log ­p : 显 示 日 志 时 同 时 显 示 GNU diff 样 式 改 动<br />

– 如 : git log ­p ­1 head<br />

●<br />

●<br />

git log ­­stat : 显 示 日 志 时 同 时 显 示 diffstat 改 动 摘 要<br />

git log ­­pretty=raw : 显 示 commit 的 原 始 数 据<br />

●<br />

●<br />

git log ­­pretty=fuller : 同 时 显 示 Author 和 Committer<br />

git show : 显 示 单 个 提 交


git diff<br />

●<br />

git diff B A : 比 较 两 个 commit 或 tag<br />

●<br />

git diff A : 比 较 工 作 区 和 A<br />

●<br />

●<br />

git diff ­­cached A : 比 较 暂 存 区 和 A<br />

git diff : 比 较 工 作 区 和 暂 存 区<br />

●<br />

git diff ­­cached : 比 较 暂 存 区 和 HEAD<br />

●<br />

git diff HEAD : 比 较 工 作 区 和 HEAD


git blame<br />

●<br />

查 看 这 个 文 件 的 每 行 最 早 是 在 什 么 版 本 、 由 谁 引<br />

入 的 , 以 便 定 位 引 起 bug 的 版 本 和 开 发 者<br />

●<br />

git blame <br />

●<br />

只 查 看 某 几 行 :( 6,10 中 间 不 能 有 空 格 )<br />

– git blame ­L 6,10 README<br />

– git blame ­L 6,+5 README


多 步 悔 棋<br />

●<br />

●<br />

前 面 提 到 修 补 最 近 提 交 使 用 git commit ­­amend , 根<br />

据 其 原 理 ,“ 多 步 悔 棋 ” 也 不 难 实 现 。<br />

例 如 , 我 们 希 望 把 过 去 的 多 个 提 交 压 缩 成 一 个 提<br />

交 , 隐 藏 反 复 试 验 的 过 程 , 使 版 本 库 更 干 净 :<br />

– git reset ­­soft HEAD^^<br />

– git status ( 看 看 现 在 成 什 么 样 了 )<br />

– git commit ­m “finish the new feature”


git rebase<br />

●<br />

●<br />

如 果 开 发 进 行 了 一 段 时 间 才 想 到 要 整 理 之 前 的 提<br />

交 , 该 怎 么 办 ?<br />

git rebase ­­onto <br />

– git checkout D ( 要 把 C 和 D 融 为 一 体 )<br />

– git reset ­­soft HEAD^^<br />

– git commit ­C C ( 使 用 C 的 提 交 说 明 )<br />

– git tag newbase ( 新 提 交 打 上 标 签 多 方 便 )<br />

– git rebase ­­onto newbase E^ master ( 这 里 用<br />

master 取 代 F , 就 能 直 接 修 改 master 的 指 向<br />

而 无 须 再 对 其 进 行 reset HEAD@{1} )


git rebase ­i<br />

●<br />

●<br />

使 用 git rebase ­i , 可 以 通 过 编 辑 文 件 的 方 式 , 方<br />

便 地 “ 定 制 ” 提 交 历 史<br />

git rebase ­i <br />

– 可 以 省 略 , 默 认 为 HEAD<br />

●<br />

将 希 望 合 并 的 提 交 的 pick 修 改 为 squash ( 或<br />

fixup ), 保 存 退 出 即 可 完 成 rebase 操 作 。


git revert<br />

●<br />

●<br />

在 合 作 开 发 的 过 程 中 , 一 旦 推 送 到 了 远 程 版 本<br />

库 , 就 无 法 改 变 历 史 了 。 如 何 修 正 错 误 提 交 呢 ?<br />

重 新 做 一 次 新 的 提 交 , 即 错 误 的 历 史 提 交 的 反 向<br />

提 交 , 这 样 就 达 到 了 git reset HEAD^ 的 效 果 。<br />

– git revert HEAD


Git clone<br />

●<br />

●<br />

通 过 clone 的 方 式 实 现 版 本 库 的 备 份<br />

git clone<br />

– 生 成 一 个 “ 看 起 来 一 样 ” 的 版 本 库<br />

●<br />

– 向 有 工 作 区 的 版 本 库 中 推 送 ( push ) 是 不 允 许<br />

的 , 因 为 这 样 会 搞 乱 工 作 区 和 暂 存 区 ( 除 非 设<br />

置 receive.denyCurrentBranch=ignore )<br />

git clone ­­bare<br />

– 生 成 一 个 裸 版 本 库 , 即 不 包 含 工 作 区 的 版 本 库


git 协 议<br />

●<br />

不 同 git 版 本 库 间 进 行 数 据 交 换 的 方 式 :<br />

●<br />

智 能 协 议 ( 在 数 据 传 输 过 程 中 有 进 度 显 示 )<br />

– SSH<br />

– Git<br />

– 本 地 协 议 ( file://)<br />

– HTTP ( git­http­backend )


git 协 议 (cont'd)<br />

●<br />

哑 协 议 ( 远 程 版 本 库 方 没 有 运 行 程 序 , 全 靠 客 户<br />

端 主 动 发 现 )<br />

– FTP<br />

– rsync<br />

– HTTP ( 普 通 )<br />

– 哑 协 议 的 传 输 速 度 较 慢 , 因 为 客 户 端 需 要 通 过 网<br />

络 获 得 .git/info/refs 获 取 当 前 版 本 库 的 引 用 列<br />

表 , 再 根 据 提 交 ID 访 问 对 象 库 目 录 下 的 文 件 。


Git pull & push<br />

●<br />

从 远 程 服 务 器 获 取 版 本 库 的 更 新<br />

– git pull = git fetch + git merge<br />

●<br />

把 本 地 的 版 本 库 推 送 到 远 程 版 本 库<br />

– git push<br />

●<br />

pull 和 push 时 如 何 知 道 远 程 版 本 库 在 哪 里 ?<br />

– .git/config: remote “origin”<br />

●<br />

pull 时 如 何 知 道 该 和 本 地 的 哪 个 分 支 合 并 ?<br />

– .git/config: branch “master”


非 快 进 式 推 送<br />

●<br />

●<br />

如 果 当 前 分 支 的 每 一 个 提 交 都 已 经 存 在 于 另 一 个<br />

分 支 , git 执 行 “ fast­forward” 操 作 , 即 不 创 建 新 的<br />

提 交 , 只 是 将 当 前 分 支 指 向 合 并 进 来 的 分 支 。<br />

non­fast­forward 推 送 , 即 远 程 版 本 库 有 一 个<br />

commit , 而 自 己 没 有 这 个 commit ; 亦 即 在 上 次 git<br />

pull 之 后 有 人 推 送 了 代 码<br />

– git push ­f ( 危 险 , 会 覆 盖 他 人 的 修 改 )<br />

– git pull; merge and resolve conflict; git push;


git 分 支<br />

●<br />

●<br />

●<br />

●<br />

●<br />

git branch : 分 支 列 表<br />

git checkout : 切 换 到 分 支<br />

git checkout ­b : 新 建 分 支<br />

git branch ­d : 删 除 已 被 当 前 分 支 合 并 的 其<br />

他 分 支<br />

git branch ­D : 强 制 删 除 分 支


git 分 支 合 并<br />

●<br />

git merge <br />

– 将 分 支 合 并 到 当 前 分 支<br />

●<br />

如 果 发 生 冲 突 , 且 自 动 合 并 没 有 成 功 , 则 暂 存 区<br />

和 工 作 区 内 有 一 个 特 殊 的 状 态 , 必 须 手 动 解 决 冲<br />

突 并 提 交 它 到 暂 存 区 , 否 则 commit 会 失 败<br />

– 只 需 编 辑 发 生 冲 突 的 文 件 ( 像 diff 的 样 式 )<br />

– git add<br />

– git commit ­m “resolve conflict”


git 分 支 合 并 (cont'd)<br />

●<br />

如 果 发 现 不 小 心 合 并 错 了 怎 么 办 ?<br />

– git reset ­­hard HEAD<br />

– HEAD 指 向 当 前 commit<br />

●<br />

如 果 已 经 把 合 并 后 的 代 码 commit , 但 还 想 撤 销 :<br />

– git reset ­­hard ORIG_HEAD ( 危 险 )<br />

– ORIG_HEAD 指 向 “ 危 险 操 作 ” 前 的 HEAD 。 执 行<br />

merge 后 , ORIG_HEAD 就 是 HEAD@{1}


Git tag<br />

●<br />

git tag : 列 出 版 本 库 中 的 现 有 标 签<br />

– git tag ­l : 模 糊 匹 配 某 些 标 签<br />

●<br />

git tag [] : 新 建 轻 量 级 标 签<br />

– git tag : 以 HEAD 建 立 标 签 , 最 常 用<br />

●<br />

●<br />

●<br />

git tag ­m : 为 新 建 的 标 签 添 加 消 息<br />

git tag ­a : 创 建 一 个 标 签 对 象 , 并 需<br />

要 标 签 消 息 。 此 时 标 签 引 用 指 向 一 个 标 签 对 象 ,<br />

而 不 是 一 个 commit 。<br />

git tag ­d : 删 除 标 签


tag 为 什 么 push 不 上 去<br />

●<br />

tag 要 push 到 远 程 版 本 库 , 与 分 支 操 作 没 有 任 何<br />

区 别 。<br />

– git push origin <br />

– git push origin : ( 删 除 标 签 )<br />

●<br />

如 果 tag 名 字 和 分 支 名 字 相 同 , 需 要 指 定 refs :<br />

– refs/tags/<br />

– refs/heads/branches/<br />

●<br />

把 所 有 的 tags 都 push 到 远 程 版 本 库 :<br />

– git push origin ­­tags


tag 为 什 么 pull 不 下 来<br />

●<br />

与 push 同 理 。<br />

– git pull origin <br />

– git pull origin ­­tags


Git hooks<br />

●<br />

●<br />

在 .git/hooks 目 录 下 有 一 些 脚 本 , 在 特 定 的 事 件 被<br />

触 发 后 调 用 。<br />

远 程 版 本 库 在 pre­receive 时 检 查 提 交 者 的 用 户 名 和<br />

E­mail 的 合 法 性 , gitosis 和 gitolite 就 是 这 样 做 的<br />

●<br />

利 用 git 部 署 Web 站 点 :<br />

– 建 立 一 个 bare 版 本 库 作 为 remote origin<br />

– Web 目 录 做 一 个 clone , 并 禁 止 Web 访 问 .git<br />

– 在 origin 的 post­update 脚 本 中 : 进 入 Web 目 录<br />

执 行 git checkout , 更 新 代 码


Git 基 本 工 作 流 程<br />

git config;<br />

git clone;<br />

while (project not finished) {<br />

cd 工 作 目 录 ;<br />

git pull;<br />

编 写 代 码 ;<br />

git add 修 改 的 文 件 ;<br />

git status;<br />

git commit ­m “message”;<br />

git pull;<br />

if (conflict) {<br />

手 动 修 改 发 生 冲 突<br />

的 文 件 ;<br />

git add 发 生 冲 突<br />

的 文 件 ;<br />

git commit ­m<br />

“resolve conflict”;<br />

}<br />

git push;<br />

sleep(); // 休 息 一 会 儿<br />

} //end while


Git 基 本 操 作<br />

● Git clone<br />

● Git config<br />

● Git log<br />

● Git init<br />

● Git pull<br />

● Git add<br />

● Git push<br />

● Git status<br />

● Git commit<br />

● Git checkout<br />

● Git reset


Git 常 用 操 作<br />

● Git fetch<br />

● Git tag<br />

● Git show<br />

● Git stash<br />

● Git merge<br />

● Git reflog<br />

● Git branch<br />

● Git diff<br />

● Git revert<br />

● Git grep<br />

● Git rebase<br />

● Git blame<br />

● Git mv<br />

● Git rm


推 荐 资 料<br />

●<br />

●<br />

●<br />

Git Community Book<br />

Pro Git (progit.org)<br />

《 Git 权 威 指 南 》, 蒋 鑫 著<br />

– 本 slides 中 的 大 部 分 内 容 是 从 此 书 中 摘 抄 重 组 的<br />

●<br />

Google

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!