Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
用 Git 进 行 版 本 控 制<br />
李 博 杰 20120520<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 />
– 20050403 , 开 始 开 发<br />
– 20050406 , 项 目 发 布<br />
– 20050407 , Git 作 为 自 身 的 版 本 控 制 工 具<br />
– 此 时 git 代 码 只 有 1244 行 , 只 有 一 些 底 层 命 令
Git 是 逼 出 来 的 (cont'd)<br />
– 20050418 , 第 一 次 分 支 合 并<br />
– 20050429 , Git 的 性 能 达 到 Linus 的 预 期<br />
– 20050616 , Linux 2.6.12 开 始 采 用 Git<br />
●<br />
●<br />
– 20050726 , 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 allowempty m “anonymous<br />
commit” ( 加 参 数 以 允 许 空 提 交 )<br />
●<br />
如 果 设 置 了 当 前 版 本 库 的 user, email 会 怎 么 样 ?<br />
– git config user.email “bojieli@gmail.com”<br />
– git commit amend allowempty resetauthor<br />
●<br />
●<br />
amend : 重 新 进 行 最 近 一 次 提 交<br />
resetauthor : 默 认 不 更 新 提 交 日 志 中 的 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 catfile :<br />
– git catfile t 3eb72cf (commit)<br />
– git catfile 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 catfile p HEAD 等 于 git catfile commit HEAD<br />
git revparse HEAD<br />
(echo n "commit "; echo n `git catfile p HEAD | wc c`; printf<br />
"\000"; git catfile p HEAD) | sha1sum
Git object hash (cont'd)<br />
●<br />
●<br />
●<br />
●<br />
文 件 内 容 blob 对 象<br />
git revparse HEAD:Makefile<br />
(echo n "blob "; echo n `git catfile p HEAD:Makefile | wc c`;<br />
printf "\000"; git catfile p HEAD:Makefile) | sha1sum<br />
tree 对 象<br />
●<br />
●<br />
●<br />
git catfile p HEAD^{tree} 不 等 于 git catfile tree HEAD^{tree}<br />
git revparse HEAD^{tree}<br />
(echo n "tree "; echo n `git catfile tree HEAD^{tree} | wc c`;<br />
printf "\000"; git catfile 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 />
的 , 因 此 可 以 用 hashobject 手 工 制 作 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 revparse : 查 询 对 象 ID<br />
– git catfile : 查 看 对 象 内 容 和 类 型
研 究 Git 常 用 命 令<br />
– Git catfile<br />
– Git revparse<br />
– Git show<br />
– Git lstree<br />
– Git lsfiles<br />
– Git hashobject<br />
– Git revlist
分 支 游 标<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 noreflog : 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 catfile 不 要 这 么 快 就 忘 了 哦 ~<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 mergebase all B C)<br />
git log oneline B^@ : 不 包 括 自 身 的 历 史 提 交<br />
git log oneline B^! : 只 包 括 自 身 , 不 包 括 历 史 提 交<br />
●<br />
使 用 git revlist 可 列 出 匹 配 的 提 交 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 ( githttpbackend )
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 执 行 “ fastforward” 操 作 , 即 不 创 建 新 的<br />
提 交 , 只 是 将 当 前 分 支 指 向 合 并 进 来 的 分 支 。<br />
nonfastforward 推 送 , 即 远 程 版 本 库 有 一 个<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 />
远 程 版 本 库 在 prereceive 时 检 查 提 交 者 的 用 户 名 和<br />
Email 的 合 法 性 , gitosis 和 gitolite 就 是 这 样 做 的<br />
●<br />
利 用 git 部 署 Web 站 点 :<br />
– 建 立 一 个 bare 版 本 库 作 为 remote origin<br />
– Web 目 录 做 一 个 clone , 并 禁 止 Web 访 问 .git<br />
– 在 origin 的 postupdate 脚 本 中 : 进 入 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 />