A3.12 底层命令
最后更新于:2022-04-02 01:36:37
# 底层命令
在本书中我们也遇到了不少底层的命令。
我们遇到的第一个底层命令是在 [合并请求引用](http://git-scm.com/book/zh/v2/1-github/_pr_refs) 中的 `ls-remote` 命令。我们用通过它来查看服务端的原始引用。
我们在 [手动文件再合并](http://git-scm.com/book/zh/v2/1-git-tools/_manual_remerge)、 [Rerere](http://git-scm.com/book/zh/v2/1-git-tools/_rerere) 及 [索引](http://git-scm.com/book/zh/v2/1-git-tools/_the_index) 章节中使用 `ls-files` 来查看暂存区的更原始的样子。
我们同样在 [分支引用](http://git-scm.com/book/zh/v2/1-git-tools/_branch_references) 一节中提到了 `rev-parse` 命令,它可以接受任意字符串,并将其转成一个对象的 SHA-1 值。
我们在 [Git 内部原理](http://git-scm.com/book/zh/v2/1-git-internals/_git_internals) 一章中对大部分的底层命令进行了介绍,这差不多正是这一章的重点所在。 我们尽量避免了在本书的其他部分使用这些命令。
';
A3.11 管理
最后更新于:2022-04-02 01:36:35
# 管理
如果你正在管理一个 Git 仓库,或者需要通过一个复杂的方法来修复某些东西,Git 提供了一些管理命令来帮助你。
## git gc
`git gc` 命令在你的仓库中执行 “garbage collection” ,删除数据库中不需要的文件和将其他文件打包成一种更有效的格式。
此命令一般在背后为你工作,虽然你可以手动执行它-如果你想的话。 我们在[维护](http://git-scm.com/book/zh/v2/1-git-internals/_git_gc) 一节中研究此命令的几个示例。
## git fsck
`git fsck` 命令用来检查内部数据库的问题或者不一致性。
我们只在 [数据恢复](http://git-scm.com/book/zh/v2/1-git-internals/_data_recovery) 这一节中快速使用了一次此命令来搜索所有的漂流对象(dangling object)。
## git reflog
`git reflog` 命令分析你所有分支的头指针的日志来查找出你在重写历史上可能丢失的提交。
我们主要在 [引用日志](http://git-scm.com/book/zh/v2/1-git-tools/_git_reflog) 一节中提到了此命令,并在展示了一般用法,及如何使用 `git log -g` 来通过 `git log` 的输出来查看同样的信息。
我们同样在 [数据恢复](http://git-scm.com/book/zh/v2/1-git-internals/_data_recovery) 一节中研究了一个恢复丢失的分支的实例。
## git filter-branch
`git filter-branch` 命令用来根据某些规则来重写大量的提交记录,例如从任何地方删除文件,或者通过过滤一个仓库中的一个单独的子目录以提取出一个项目。
在 [从每一个提交移除一个文件](http://git-scm.com/book/zh/v2/1-git-tools/_removing_file_every_commit) 一节中,我们解释了此命令,并探究了其他几个选项,例如 `--commit-filter`,`--subdirectory-filter` 及 `--tree-filter` 。
在 [Git-p4](http://git-scm.com/book/zh/v2/1-git-and-other-scms/_git_p4) 和 [TFS](http://git-scm.com/book/zh/v2/1-git-and-other-scms/_git_tfs) 的章节中我们使用它来修复已经导入到外部仓库。
';
A3.10 外部系统
最后更新于:2022-04-02 01:36:33
# 外部系统
Git 有一些可以与其他的版本控制系统集成的命令。
## git svn
`git svn` 可以使 Git 作为一个客户端来与 Subversion 版本控制系统通信。 这意味着你可以使用 Git 来检出内容,或者提交到 Subversion 服务器。
[Git 与 Subversion](http://git-scm.com/book/zh/v2/1-git-and-other-scms/_git_svn) 一章深入讲解了此命令。
## git fast-import
对于其他版本控制系统或者从其他任何的格式导入,你可以使用 `git fast-import` 快速地将其他格式映射到 Git 可以轻松记录的格式。
在 [一个自定义的导入器](http://git-scm.com/book/zh/v2/1-git-and-other-scms/_custom_importer) 一节中深入讲解了此命令。
';
A3.9 邮件
最后更新于:2022-04-02 01:36:30
# 邮件
很多 Git 项目,包括 Git 本身,基本是通过邮件列表来维护的。 从方便地生成邮件补丁到从一个邮箱中应用这些补丁,Git 都有工具来让这些操作变得简单。
## git apply
`git apply` 命令应用一个通过 `git diff` 或者甚至使用 GNU diff 命令创建的补丁。 它跟补丁命令做了差不多的工作,但还是有一些小小的差别。
我们在 [应用来自邮件的补丁](http://git-scm.com/book/zh/v2/1-distributed-git/_patches_from_email) 一节中演示了它的使用及什么环境下你可能会用到它。
## git am
`git am` 命令用来应用来自邮箱的补丁。特别是那些被 mbox 格式化过的。 这对于通过邮件接受补丁并将他们轻松地应用到你的项目中很有用。
我们在 [使用 am 命令应用补丁](http://git-scm.com/book/zh/v2/1-distributed-git/_git_am) 命令中提到了它的用法及工作流,包括使用 `--resolved`、`-i` 及`-3` 选项。
我们在 [电子邮件工作流钩子](http://git-scm.com/book/zh/v2/1-customizing-git/_email_hooks) 也提到了几条 hooks,你可以用来辅助与 `git am` 相关工作流。
在 [邮件通知](http://git-scm.com/book/zh/v2/1-github/_email_notifications) 一节中我们也将用此命令来应用 格式化的 GitHub的推送请求的变更。
## git format-patch
`git format-patch` 命令用来以 mbox 的格式来生成一系列的补丁以便你可以发送到一个邮件列表中。
我们在 [通过邮件的公开项目](http://git-scm.com/book/zh/v2/1-distributed-git/_project_over_email) 一节中研究了一个使用 `git format-patch` 工具为一个项目做贡献的示例。
## git imap-send
`git imap-send` 将一个由 `git format-patch` 生成的邮箱上传至 IMAP 草稿文件夹。 我们在[通过邮件的公开项目](http://git-scm.com/book/zh/v2/1-distributed-git/_project_over_email) 一节中见过一个通过使用 `git imap-send` 工具向一个项目发送补丁进行贡献的例子。
## git send-email
`git send-mail` 命令用来通过邮件发送那些使用 `git format-patch` 生成的补丁。
我们在 [通过邮件的公开项目](http://git-scm.com/book/zh/v2/1-distributed-git/_project_over_email) 一节中研究了一个使用 `git send-email` 工具发送补丁来为一个项目做贡献的示例。
## git request-pull
`git request-pull` 命令只是简单的用来生成一个可通过邮件发送给某个人的示例信息体。 如果你在公共服务器上有一个分支,并且想让别人知道如何集成这些变更,而不用通过邮件发送补丁,你就可以执行此命令的输出发送给这个你想拉取变更的人。
我们在 [派生的公开项目](http://git-scm.com/book/zh/v2/1-distributed-git/_public_project) 一节中演示了如何使用 `git request-pull` 来生成一个推送消息。
';
A3.8 补丁
最后更新于:2022-04-02 01:36:28
# 补丁
Git 中的一些命令是以引入的变更即提交这样的概念为中心的,这样一系列的提交,就是一系列的补丁。 这些命令以这样的方式来管理你的分支。
## git cherry-pick
`git cherry-pick` 命令用来获得在单个提交中引入的变更,然后尝试将作为一个新的提交引入到你当前分支上。 从一个分支单独一个或者两个提交而不是合并整个分支的所有变更是非常有用的。
在 [变基与拣选工作流](http://git-scm.com/book/zh/v2/1-distributed-git/_rebase_cherry_pick) 一节中描述和演示了 `Cherry picking`
## git rebase
`git rebase` 命令基本是是一个自动化的 `cherry-pick` 命令。 它计算出一系列的提交,然后再以它们在其他地方以同样的顺序一个一个的 `cherry-picks` 出它们。
在 [变基](http://git-scm.com/book/zh/v2/1-git-branching/_rebasing) 一章中详细提到了此命令,包括与已经公开的分支的变基所涉及的协作问题。
在 [替换](http://git-scm.com/book/zh/v2/1-git-tools/_replace) 中我们在一个分离历史记录到两个单独的仓库的示例中实践了此命令,同时使用了 `--onto`选项。
在 [Rerere](http://git-scm.com/book/zh/v2/1-git-tools/_rerere) 一节中,我们研究了在变基时遇到的合并冲突的问题。
在 [修改多个提交信息](http://git-scm.com/book/zh/v2/1-git-tools/_changing_multiple) 一节中,我们也结合 `-i` 选项将其用于交互式的脚本模式。
## git revert
`git revert` 命令本质上就是一个逆向的 `git cherry-pick` 操作。 它将你提交中的变更的以完全相反的方式的应用到一个新创建的提交中,本质上就是撤销或者倒转。
我们在 [还原提交](http://git-scm.com/book/zh/v2/1-git-tools/_reverse_commit) 一节中使用此命令来撤销一个合并提交。
';
A3.7 调试
最后更新于:2022-04-02 01:36:26
# 调试
Git 有一些命令可以用来帮你调试你代码中的问题。 包括找出是什么时候,是谁引入的变更。
## git bisect
`git bisect` 工具是一个非常有用的调试工具,它通过自动进行一个二分查找来找到哪一个特定的提交是导致 bug 或者问题的第一个提交。
仅在 [二分查找](http://git-scm.com/book/zh/v2/1-git-tools/_binary_search) 一节中完整的介绍了此命令。
## git blame
`git blame` 命令标注任何文件的行,指出文件的每一行的最后的变更的提交及谁是那一个提交的作者。 当你要找那个人去询问关于这块特殊代码的信息时这会很有用。
只有 [文件标注](http://git-scm.com/book/zh/v2/1-git-tools/_file_annotation) 一节有中提到此命令。
## git grep
`git grep` 命令可以帮助在源代码中,甚至是你项目的老版本中的任意文件中查找任何字符串或者正则表达式。
只有 [Git Grep](http://git-scm.com/book/zh/v2/1-git-tools/_git_grep) 的章节中与提到此命令。
';
A3.6 检查与比较
最后更新于:2022-04-02 01:36:24
# 检查与比较
## git show
`git show` 命令可以以一种简单的人类可读的方式来显示一个 Git 对象。 你一般使用此命令来显示一个标签或一个提交的信息。
我们在 [附注标签](http://git-scm.com/book/zh/v2/1-git-basics/_annotated_tags) 一节中使用此命令来显示带注解标签的信息。
然后,我们在 [选择修订版本](http://git-scm.com/book/zh/v2/1-git-tools/_revision_selection) 一节中,用了很多次来显示不同的版本选择将解析出来的提交。
我们使用 `git show` 做的最有意思的事情是在 [手动文件再合并](http://git-scm.com/book/zh/v2/1-git-tools/_manual_remerge) 一节中用来在合并冲突的多个暂存区域中提取指定文件的内容。
## git shortlog
`git shortlog` 是一个用来归纳 `git log` 的输出的命令。 它可以接受很多与 `git log` 相同的选项,但是此命令并不会列出所有的提交,而是展示一个根据作者分组的提交记录的概括性信息
我们在 [制作提交简报](http://git-scm.com/book/zh/v2/1-distributed-git/_the_shortlog) 一节中展示了如何使用此命令来创建一个漂亮的 changelog 文件。
## git describe
`git describe` 命令用来接受任何可以解析成一个提交的东西,然后生成一个人类可读的字符串且不可变。 这是一种获得一个提交的描述的方式,它跟一个提交的 SHA-1 值一样是无歧义,但是更具可读性。
我们在 [生成一个构建号](http://git-scm.com/book/zh/v2/1-distributed-git/_build_number) 及 [准备一次发布](http://git-scm.com/book/zh/v2/1-distributed-git/_preparing_release) 章节中使用 `git describe` 命令来获得一个字符串来命名我们发布的文件。
';
A3.5 项目分享与更新
最后更新于:2022-04-02 01:36:21
# 项目分享与更新
在 Git 中没有多少访问网络的命令,几乎所以的命令都是在操作本地的数据库。 当你想要分享你的工作,或者从其他地方拉取变更时,这有几个处理远程仓库的命令。
## git fetch
`git fetch` 命令与一个远程的仓库交互,并且将远程仓库中有但是在当前仓库的没有的所有信息拉取下来然后存储在你本地数据库中。
我们开始在 [从远程仓库中抓取与拉取](http://git-scm.com/book/zh/v2/1-git-basics/_fetching_and_pulling) 一节中介绍了此命令,然后我们在 [远程分支](http://git-scm.com/book/zh/v2/1-git-branching/_remote_branches) 中看到了几个使用示例。
我们在 [向一个项目贡献](http://git-scm.com/book/zh/v2/1-distributed-git/_contributing_project) 一节中有几个示例中也都有使用此命令。
在 [合并请求引用](http://git-scm.com/book/zh/v2/1-github/_pr_refs) 我们用它来抓取一个在默认空间之外指定的引用,在 [打包](http://git-scm.com/book/zh/v2/1-git-tools/_bundling) 中,我们了解了怎么从一个包中获取内容。
在 [引用规格](http://git-scm.com/book/zh/v2/1-git-internals/_refspec) 章节中我们设置了高度自定义的 `refspec` 以便 `git fetch` 可以做一些跟默认不同的事情。
## git pull
`git pull` 命令基本上就是 `git fetch` 和 `git merge` 命令的组合体,Git 从你指定的远程仓库中抓取内容,然后马上尝试将其合并进你所在的分支中。
我们在 [从远程仓库中抓取与拉取](http://git-scm.com/book/zh/v2/1-git-basics/_fetching_and_pulling) 一节中快速介绍了此命令,然后在 [查看远程仓库](http://git-scm.com/book/zh/v2/1-git-basics/_inspecting_remote) 一节中了解了如果你运行此命令的话,什么将会合并。
我们也在 [用变基解决变基](http://git-scm.com/book/zh/v2/1-git-branching/_rebase_rebase) 一节中了解了如何使用此命令来来处理变基的难题。
在 [检出冲突](http://git-scm.com/book/zh/v2/1-git-tools/_checking_out_conflicts) 一节中我们展示了使用此命令如何通过一个 URL 来一次性的拉取变更。
最后,我们在 [签署提交](http://git-scm.com/book/zh/v2/1-git-tools/_signing_commits) 一节中我们快速的介绍了你可以使用 `--verify-signatures` 选项来验证你正在拉取下来的经过 GPG 签名的提交。
## git push
`git push` 命令用来与另一个仓库通信,计算你本地数据库与远程仓库的差异,然后将差异推送到另一个仓库中。 它需要有另一个仓库的写权限,因此这通常是需要验证的。
我们开始在 [推送到远程仓库](http://git-scm.com/book/zh/v2/1-git-basics/_pushing_remotes) 一节中介绍了 `git push` 命令。 在这一节中主要介绍了推送一个分支到远程仓库的基本用法。 在 [推送](http://git-scm.com/book/zh/v2/1-git-branching/_pushing_branches) 一节中,我们深入了解了如何推送指定分支,在 [跟踪分支](http://git-scm.com/book/zh/v2/1-git-branching/_tracking_branches) 一节中我们了解了如何设置一个默认的推送的跟踪分支。 在 [删除远程分支](http://git-scm.com/book/zh/v2/1-git-branching/_delete_branches) 一节中我们使用 `--delete` 标志和 `git push` 命令来在删除一个在服务器上的分支。
在 [向一个项目贡献](http://git-scm.com/book/zh/v2/1-distributed-git/_contributing_project) 一整节中,我们看到了几个使用 `git push` 在多个远程仓库分享分支中的工作的示例。
在 [共享标签](http://git-scm.com/book/zh/v2/1-git-basics/_sharing_tags) 一节中,我们知道了如何使用此命令加 `--tags` 选项来分享你打的标签。
在 [发布子模块改动](http://git-scm.com/book/zh/v2/1-git-tools/_publishing_submodules) 一节中,我们使用 `--recurse-submodules` 选项来检查是否我们所有的子模块的工作都已经在推送子项目之前已经推送出去了,当使用子模块时这真的很有帮助。
在 [其它客户端钩子](http://git-scm.com/book/zh/v2/1-customizing-git/_other_client_hooks) 中我们简单的提到了 `pre-push` 挂钩(hook),它是一个可以用来设置成在一个推送完成之前运行的脚本,以检查推送是否被允许。
最后,在 [引用规格推送](http://git-scm.com/book/zh/v2/1-git-internals/_pushing_refspecs) 一节中,我们知道了使用完整的 refspec 来推送,而不是通常使用的简写形式。 这对我们精确的指定要分享出去的工作很有帮助。
## git remote
`git remote` 命令是一个是你远程仓库记录的管理工具。 它允许你将一个长的 URL 保存成一个简写的句柄,例如 `origin` ,这样你就可以不用每次都输入他们了。 你可以有多个这样的句柄,`git remote` 可以用来添加,修改,及删除它们。
此命令在 [远程仓库的使用](http://git-scm.com/book/zh/v2/1-git-basics/_remote_repos) 一节中做了详细的介绍,包括列举、添加、移除、重命名功能。
几乎在此书的后续章节中都有使用此命令,但是一般是以 `git remote add ` 这样的标准格式。
## git archive
`git archive` 命令用来创建项目一个指定快照的归档文件。
我们在 [准备一次发布](http://git-scm.com/book/zh/v2/1-distributed-git/_preparing_release) 一节中,使用 `git archive` 命令来创建一个项目的归档文件用于分享。
## git submodule
`git submodule` 命令用来管理一个仓库的其他外部仓库。 它可以被用在库或者其他类型的共享资源上。 `submodule` 命令有几个子命令, 如(`add`、`update`、`sync` 等等)用来管理这些资源。
只在 [子模块](http://git-scm.com/book/zh/v2/1-git-tools/_git_submodules) 章节中提到和详细介绍了此命令。
';
A3.4 分支与合并
最后更新于:2022-04-02 01:36:19
# 分支与合并
Git 有几个实现大部的分支及合并功能的实用命令。
## git branch
`git branch` 命令实际上是某种程度上的分支管理工具。 它可以列出你所有的分支、创建新分支、删除分支及重命名分支。
[Git 分支](70182) 一节主要是为 `branch` 命令来设计的,它贯穿了整个章节。 首先,我们在 [分支创建](70183) 一节中介绍了它,然后我们在 [分支管理](70185) 一节中介绍了它的其它大部分特性(列举及删除)。
在 [跟踪分支](http://git-scm.com/book/zh/v2/1-git-branching/_tracking_branches) 一节中,我们使用 `git branch -u` 选项来设置一个跟踪分支。
最后,我们在 [Git 引用](70242) 一节中讲到了它在背后做一什么。
## git checkout
`git checkout` 命令用来切换分支,或者检出内容到工作目录。
我们是在 [分支切换](70183) 一节中第一次认识了命令及 `git branch` 命令。
在 [跟踪分支](http://git-scm.com/book/zh/v2/1-git-branching/_tracking_branches) 一节中我们了解了如何使用 `--track` 标志来开始跟踪分支。
在 [检出冲突](70221) 一节中,我们用此命令和 `--conflict=diff3` 来重新介绍文件冲突。
在 [重置揭密](70220) 一节中,我们进一步了解了其细节及与 `git reset` 的关系。
最后,我们在 [HEAD 引用](http://git-scm.com/book/zh/v2/1-git-internals/_the_head) 一节中介绍了此命令的一些实现细节。
## git merge
`git merge` 工具用来合并一个或者多个分支到你已经检出的分支中。 然后它将当前分支指针移动到合并结果上。
我们首先在 [新建分支](http://git-scm.com/book/zh/v2/1-git-branching/_basic_branching) 一节中介绍了 `git merge` 命令。 虽然它在本书的各种地方都有用到,但是`merge` 命令只有几个变种,一般只是 `git merge ` 带上一个你想合并进来的一个分支名称。
我们在 [派生的公开项目](http://git-scm.com/book/zh/v2/1-distributed-git/_public_project) 的后面介绍了如何做一个 `squashed merge` (指 Git 合并时将其当作一个新的提交而不是记录你合并时的分支的历史记录。)
在 [高级合并](http://git-scm.com/book/zh/v2/1-git-tools/_advanced_merging) 一节中,我们介绍了合并的过程及命令,包含 `-Xignore-space-change` 命令及 `--abort` 选项来中止一个有问题的提交。
在 [签署提交](http://git-scm.com/book/zh/v2/1-git-tools/_signing_commits) 一节中我们学习了如何在合并前验证签名,如果你项目正在使用 GPG 签名的话。
最后,我们在 [子树合并](http://git-scm.com/book/zh/v2/1-git-tools/_subtree_merge) 一节中学习了子树合并。
## git mergetool
当你在 Git 的合并中遇到问题时,可以使用 `git mergetool` 来启动一个外部的合并帮助工具。
我们在 [遇到冲突时的分支合并](http://git-scm.com/book/zh/v2/1-git-branching/_basic_merge_conflicts) 中快速介绍了一下它,然后在 [外部的合并与比较工具](http://git-scm.com/book/zh/v2/1-customizing-git/_external_merge_tools) 一节中介绍了如何实现你自己的外部合并工具的细节。
## git log
`git log` 命令用来展示一个项目的可达历史记录,从最近的提交快照起。 默认情况下,它只显示你当前所在分支的历史记录,但是可以显示不同的甚至多个头记录或分支以供遍历。 此命令通常也用来在提交记录级别显示两个或多个分支之间的差异。
在本书的每一章几乎都有用到此命令来描述一个项目的历史。
在 [查看提交历史](http://git-scm.com/book/zh/v2/1-git-basics/_viewing_history) 一节中我们介绍了此命令,并深入做了研究。 研究了包括 `-p` 和 `--stat` 选项来了解每一个提交引入的变更,及使用`--pretty` 和 `--online` 选项来查看简洁的历史记录。
在 [分支创建](http://git-scm.com/book/zh/v2/1-git-branching/_create_new_branch) 一节中我们使用它加 `--decorate` 选项来简单的可视化我们分支的指针所在,同时我们使用 `--graph` 选项来查看分叉的历史记录是怎么样的。
在 [私有小型团队](http://git-scm.com/book/zh/v2/1-distributed-git/_private_team) 和 [提交区间](http://git-scm.com/book/zh/v2/1-git-tools/_commit_ranges) 章节中,我们介绍了在使用 `git log` 命令时用`branchA..branchB` 的语法来查看一个分支相对于另一个分支, 哪一些提交是唯一的。 在 [提交区间](http://git-scm.com/book/zh/v2/1-git-tools/_commit_ranges) 一节中我们作了更多介绍。
在 > 和 [三点](http://git-scm.com/book/zh/v2/1-git-tools/_triple_dot) 章节中,我们介绍了 `branchA...branchB` 格式和 `--left-right` 语法来查看哪些仅其中一个分支。 在 [合并日志](http://git-scm.com/book/zh/v2/1-git-tools/_merge_log) 一节中我们还研究了如何使用 `--merge`选项来帮助合并冲突调试,同样也使用 `--cc` 选项来查看在你历史记录中的合并提交的冲突。
在 [引用日志](http://git-scm.com/book/zh/v2/1-git-tools/_git_reflog) 一节中我们使用此工具和 `-g` 选项 而不是遍历分支来查看 Git 的 `reflog`。
在 [搜索](http://git-scm.com/book/zh/v2/1-git-tools/_searching) 一节中我们研究了`-S` 及 `-L` 选项来进行来在代码的历史变更中进行相当优雅地搜索,如一个函数的历史。
在 [签署提交](http://git-scm.com/book/zh/v2/1-git-tools/_signing_commits) 一节中,我们了解了如何使用 `--show-signature` 来为每一个提交的 `git log`输出中,添加一个判断是否已经合法的签名的一个验证。
## git stash
`git stash` 命令用来临时地保存一些还没有提交的工作,以便在分支上不需要提交未完成工作就可以清理工作目录。
[储藏与清理](http://git-scm.com/book/zh/v2/1-git-tools/_git_stashing) 一整个章节基本就是在讲这个命令。
## git tag
`git tag` 命令用来为代码历史记录中的某一个点指定一个永久的书签。 一般来说它用于发布相关事项。
我们在 [打标签](http://git-scm.com/book/zh/v2/1-git-basics/_git_tagging) 一节中介绍了此命令及相关细节,并在 [为发布打标签](http://git-scm.com/book/zh/v2/1-distributed-git/_tagging_releases) 一节实践了此命令。
我也在 [签署工作](http://git-scm.com/book/zh/v2/1-git-tools/_signing) 一节中介绍了如何使用 `-s` 标志创建一个 GPG 签名的标签,然后使用 `-v` 选项来验证。
';
A3.3 快照基础
最后更新于:2022-04-02 01:36:17
# 快照基础
对于基本的暂存内容及提交到你的历史记录中的工作流,只有少数基本的命令。
## git add
`git add` 命令将内容从工作目录添加到暂存区(或称为索引(index)区),以备下次提交。 当`git commit` 命令执行时,默认情况下它只会检查暂存区域,因此 `git add` 是用来确定下一次提交时快照的样子的。
这个命令对于 Git 来说特别的重要,所以在本书中被无数次的提及和使用。 我们将快速的过一遍一些可以看到的独特的用法。
我们在 [跟踪新文件](70175) 一节中介绍并详细解释了 `git add` 命令。
然后,我们在 [遇到冲突时的分支合并](70184) 一节中提到了如何使用它来解决合并冲突。
接下来,我们在 [交互式暂存](70215) 一章中使用它来交互式的暂存一个已修改文件的特定部分。
最后,在 [树对象](70241) 一节中我们在一个低层次中模拟了它的用法,以便你可以了解在这背后发生了什么。
## git status
`git status` 命令将为你展示工作区及暂存区域中不同状态的文件。 这其中包含了已修改但未暂存,或已经暂存但没有提交的文件。 一般在它显示形式中,会给你展示一些关于如何在这些暂存区域之间移动文件的提示。
首先,我们在 [检查当前文件状态](70175) 一节中介绍了 `status` 的基本及简单的形式。 虽然我们在全书中都有用到它,但是绝大部分的你能用 `git status` 做的事情都在这一章讲到了。
## git diff
当需要查看任意两棵树的差异时你可以使用 `git diff` 命令。 此命令可以查看你工作环境与你的暂存区的差异(`git diff` 默认的做法),你暂存区域与你最后提交之间的差异(`git diff --staged`),或者比较两个提交记录的差异(`git diff master branchB`)
首先,我们在 [查看已暂存和未暂存的修改](http://git-scm.com/book/zh/v2/1-git-basics/_git_diff_staged) 一章中研究了 `git diff` 的基本用法,在此节中我们展示了如何查看哪些变化已经暂存了,哪些没有。
在 [提交准则](70203) 一节中,我们在提交前使用 `--check` 选项来检查可能存在的空白字符问题。
在 [确定引入了哪些东西](http://git-scm.com/book/zh/v2/1-distributed-git/_what_is_introduced) 一节中,了解了使用 `git diff A...B` 语法来更有效地比较不同分支之间的差异。
在 [高级合并](70221) 一节中我们使用 `-b` 选项来过滤掉空白字符的差异,及通过 `--theirs`、`--ours`和 `--base` 选项来比较不同暂存区冲突文件的差异。
最后,在 [开始使用子模块](70224) 一节中,我们使用此命令合 `--submodule` 选项来有效地比较子模块的变化。
## git difftool
当你不想使用内置的 `git diff` 命令时。`git difftool` 可以用来简单地启动一个外部工具来为你展示两棵树之间的差异。
我们只在 [查看已暂存和未暂存的修改](http://git-scm.com/book/zh/v2/1-git-basics/_git_diff_staged) 一节中简单的提到了此命令。
## git commit
`git commit` 命令将所有通过 `git add` 暂存的文件内容在数据库中创建一个持久的快照,然后将当前分支上的分支指针移到其之上。
首先,我们在 [提交更新](http://git-scm.com/book/zh/v2/1-git-basics/_committing_changes) 一节中涉及了此命令的基本用法。 我们演示了如何在日常的工作流程中通过使用 `-a` 标志来跳过 `git add` 这一步,及如何使用 `-m` 标志通过命令行而不启动一个编辑器来传递提交信息。
在 [撤消操作](http://git-scm.com/book/zh/v2/1-git-basics/_undoing) 一节中我们介绍了使用 `--amend` 选项来重做最后的提交。
在 [分支简介](70183),我们探讨了 `git commit` 的更多细节,及工作原理。
在 [签署提交](70217) 一节中我们探讨了如何使用 `-S` 标志来为提交签名加密。
最后,在 [提交对象](http://git-scm.com/book/zh/v2/1-git-internals/_git_commit_objects) 一节中,我们了解了 `git commit` 在背后做了什么,及它是如何实现的。
## git reset
`git reset` 命令主要用来根据你传递给动作的参数来执行撤销操作。 它可以移动 `HEAD` 指针并且可选的改变 `index` 或者暂存区,如果你使用 `--hard` 参数的话你甚至可以改变工作区。 如果错误地为这个命令附加后面的参数,你可能会丢失你的工作,所以在使用前你要确定你已经完全理解了它。
首先,我们在 [取消暂存的文件](70215) 一节中介绍了 `git reset` 简单高效的用法,用来对执行过 `git add` 命令的文件取消暂存。
在 [重置揭密](70220) 一节中我们详细介绍了此命令,几乎整节都在解释此命令。
在 [中断一次合并](70221) 一节中,我们使用 `git reset --hard` 来取消一个合并,同时我们也使用了`git merge --abort` 命令,它是 `git reset` 的一个简单的封装。
## git rm
`git rm` 是 Git 用来从工作区,或者暂存区移除文件的命令。 在为下一次提交暂存一个移除操作上,它与 `git add` 有一点类似。
我们在 [移除文件](http://git-scm.com/book/zh/v2/1-git-basics/_removing_files) 一节中提到了 `git rm` 的一些细节,包括递归地移除文件,和使用 `--cached`选项来只移除暂存区域的文件但是保留工作区的文件。
在本书的 [移除对象](70246) 一节中,介绍了 `git rm` 仅有的几种不同用法,如在执行 `git filter-branch` 中使用和解释了 `--ignore-unmatch` 选项。 这对脚本来说很有用。
## git mv
`git mv` 命令是一个便利命令,用于移到一个文件并且在新文件上执行`git add`命令及在老文件上执行`git rm`命令。
我们只是在 [移动文件](http://git-scm.com/book/zh/v2/1-git-basics/_git_mv) 一节中简单地提到了此命令。
## git clean
`git clean` 是一个用来从工作区中移除不想要的文件的命令。 可以是编译的临时文件或者合并冲突的文件。
在 [清理工作目录](70216) 一节中我们介绍了你可能会使用 `clean` 命令的大量选项及场景。
';
A3.2 获取与创建项目
最后更新于:2022-04-02 01:36:15
# 获取与创建项目
有几种方式获取一个 Git 仓库。 一种是从网络上或者其他地方拷贝一个现有的仓库,另一种就是在一个目录中创建一个新的仓库。
## git init
你只需要简单地运行 `git init` 就可以将一个目录转变成一个 Git 仓库,这样你就可以开始对它进行版本管理了。
我们一开始在 [获取 Git 仓库](70174) 一节中介绍了如何创建一个新的仓库来开始工作。
在 [远程分支](70187) 一节中我们简单的讨论了如何改变默认分支。
在 [把裸仓库放到服务器上](70192) 一节中我们使用此命令来为一个服务器创建一个空的祼仓库。
最后,我们在 [底层命令和高层命令](70240) 一节中介绍了此命令背后工作的原理的一些细节。
## git clone
`git clone` 实际上是一个封装了其他几个命令的命令。 它创建了一个新目录,切换到新的目录,然后 `git init` 来初始化一个空的 Git 仓库, 然后为你指定的 URL 添加一个(默认名称为`origin` 的)远程仓库(`git remote add`),再针对远程仓库执行 `git fetch`,最后通过`git checkout` 将远程仓库的最新提交检出到本地的工作目录。
`git clone` 命令在本书中多次用到,这里只列举几个有意思的地方。
在 [克隆现有的仓库](70174) 一节中我们通过几个示例详细介绍了此命令。
在 [在服务器上搭建 Git](70192) 一节中,我们使用了 `--bare` 选项来创建一个没有任何工作目录的 Git 仓库副本。
在 [打包](70225) 一节中我们使用它来解包一个打包好的 Git 仓库。
最后,在 [克隆含有子模块的项目](70224) 一节中我们学习了使用 `--recursive` 选项来让克隆一个带有子模块的仓库变得简单
虽然在本书的其他地方都有用到此命令,但是上面这些用法是特例,或者使用方式有点特别。
';
A3.1 设置与配置
最后更新于:2022-04-02 01:36:12
# 设置与配置
有两个命令使用得最多了,从第一次调用 Git 到每天的日常微调及参考,这个两个命令就是:`config` 和 `help` 命令
## git config
Git 做的很多工作都有一个默认方式。 对于绝大多数工作而言,你可以改变 Git 的默认方式,或者根据你的偏好来设置。 这些设置涵盖了所有的事,从告诉 Git 你的名字,到指定偏好的终端颜色,以及你使用的编辑器。 此命令会从几个特定的配置文件中读取和写入配置值,以便你可以从全局或者针对特定的仓库来进行设置。
本书的所有章节几乎都有用到 `git config` 命令。
在 [初次运行 Git 前的配置](70170) 一节中,在开始使用 Git 之前,我们用它来指定我们的名字,邮箱地址和编辑器偏好。
在 [Git 别名](70180) 一节中我们展示了如何创建可以展开为长选项序列的短命令,以便你不用每次都输入它们。
在 [变基](70188) 一节中,执行 `git pull` 命令时,使用此命令来将 `--rebase` 作为默认选项。
在 [凭证存储](70227) 一节中,我们使用它来为你的 HTTP 密码设置一个默认的存储区域。
在 [关键字展开](70231) 一节中我们展示了如何设置在 Git 的内容添加和减少时使用的 smudge 过滤器 和 clean 过滤器。
最后,基本上 [配置 Git](http://git-scm.com/book/zh/v2/1-customizing-git/_git_config) 整个章节都是针对此命令的。
## git help
`git help` 命令用来显示任何命令的 Git 自带文档。 但是我们仅会在此附录中提到大部分最常用的命令,对于每一个命令的完整的可选项及标志列表,你可以随时运行 `git help ` 命令来了解。
我们在 [获取帮助](70171) 一节中介绍了 `git help` 命令,同时在 [配置服务器](70194) 一节中给你展示了如何使用它来查找更多关于 `git shell` 的信息。
';
C. Git 命令
最后更新于:2022-04-02 01:36:10
# 附录C. Git 命令
在这一整本书里我们介绍了大量的 Git 命令,并尽可能的通过讲故事的的方式来介绍它们,慢慢的介绍了越来越多的命令。但是这导致这些命令的示例用法都散落在在全书的各处。
在此附录中,我们会将本书中所提到过的命令都过一遍,并根据其用途大致的分类。我们会大致地讨论每个命的作用,指出其在本书中哪些章节使用过。
';
A2.3 JGit
最后更新于:2022-04-02 01:36:08
# JGit
如果你想在一个 Java 程序中使用 Git ,有一个功能齐全的 Git 库,那就是 JGit 。 JGit 是一个用 Java 写成的功能相对健全的 Git 的实现,它在 Java 社区中被广泛使用。 JGit 项目由 Eclipse 维护,它的主页在 [*http://www.eclipse.org/jgit*](http://www.eclipse.org/jgit) 。
## 起步
有很多种方式可以让 JGit 连接你的项目,并依靠它去写代码。 最简单的方式也许就是使用 Maven 。你可以通过在你的 pom.xml 文件里的 `` 标签中增加像下面这样的片段来完成这个整合。
~~~
org.eclipse.jgit
org.eclipse.jgit
3.5.0.201409260305-r
~~~
在你读到这段文字时 `version` 很可能已经更新了,所以请浏览[*http://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit*](http://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit) 以获取最新的仓库信息。 当这一步完成之后, Maven 就会自动获取并使用你所需要的 JGit 库。
如果你想自己管理二进制的依赖包,那么你可以从 [*http://www.eclipse.org/jgit/download*](http://www.eclipse.org/jgit/download) 获得预构建的 JGit 二进制文件。 你可以像下面这样执行一个命令来将它们构建进你的项目。
~~~
javac -cp .:org.eclipse.jgit-3.5.0.201409260305-r.jar App.java
~~~
## 底层命令
JGit 的 API 有两种基本的层次:底层命令和高层命令。 这个两个术语都来自 Git ,并且 JGit 也被按照相同的方式粗略地划分:高层 API 是一个面向普通用户级别功能的友好的前端(一系列普通用户使用 Git 命令行工具时可能用到的东西),底层 API 则直接作用于低级的仓库对象。
大多数 JGit 会话会以 `Repository` 类作为起点,你首先要做的事就是创建一个它的实例。 对于一个基于文件系统的仓库来说(嗯, JGit 允许其它的存储模型),用 `FileRepositoryBuilder`完成它。
~~~
// 创建一个新仓库
Repository newlyCreatedRepo = FileRepositoryBuilder.create(
new File("/tmp/new_repo/.git"));
newlyCreatedRepo.create();
// 打开一个存在的仓库
Repository existingRepo = new FileRepositoryBuilder()
.setGitDir(new File("my_repo/.git"))
.build();
~~~
无论你的程序是否知道仓库的确切位置,builder 中的那个流畅的 API 都可以提供给它寻找仓库所需所有信息。 它可以使用环境变量 (`.readEnvironment()`) ,从工作目录的某处开始并搜索 (`.setWorkTree(…).findGitDir()`) , 或者仅仅只是像上面那样打开一个已知的 `.git` 目录。
当你拥有一个 `Repository` 实例后,你就能对它做各种各样的事。 下面是一个速览:
~~~
// 获取引用
Ref master = repo.getRef("master");
// 获取该引用所指向的对象
ObjectId masterTip = master.getObjectId();
// Rev-parse
ObjectId obj = repo.resolve("HEAD^{tree}");
// 装载对象原始内容
ObjectLoader loader = repo.open(masterTip);
loader.copyTo(System.out);
// 创建分支
RefUpdate createBranch1 = repo.updateRef("refs/heads/branch1");
createBranch1.setNewObjectId(masterTip);
createBranch1.update();
// 删除分支
RefUpdate deleteBranch1 = repo.updateRef("refs/heads/branch1");
deleteBranch1.setForceUpdate(true);
deleteBranch1.delete();
// 配置
Config cfg = repo.getConfig();
String name = cfg.getString("user", null, "name");
~~~
这里完成了一大堆事情,所以我们还是一次理解一段的好。
第一行获取一个指向 `master` 引用的指针。 JGit 自动抓取位于 `refs/heads/master` 的 *真正的* master 引用,并返回一个允许你获取该引用的信息的对象。 你可以获取它的名字 (`.getName()`) ,或者一个直接引用的目标对象 (`.getObjectId()`) ,或者一个指向该引用的符号指针 (`.getTarget()`) 。 引用对象也经常被用来表示标签的引用和对象,所以你可以询问某个标签是否被 “削除” 了,或者说它指向一个标签对象的(也许很长的)字符串的最终目标。
第二行获得以 `master` 引用的目标,它返回一个 ObjectId 实例。 不管是否存在于一个 Git 对象的数据库,ObjectId 都会代表一个对象的 SHA-1 哈希。 第三行与此相似,但是它展示了 JGit 如何处理 rev-parse 语法(要了解更多,请看 [分支引用](http://git-scm.com/book/zh/v2/1-git-tools/_branch_references) ),你可以传入任何 Git 了解的对象说明符,然后 JGit 会返回该对象的一个有效的 ObjectId ,或者 `null` 。
接下来两行展示了如何装载一个对象的原始内容。 在这个例子中,我们调用`ObjectLoader.copyTo()` 直接向标准输出流输出对象的内容,除此之外 ObjectLoader 还带有读取对象的类型和长度并将它以字节数组返回的方法。 对于一个( `.isLarge()` 返回 `true`的)大的对象,你可以调用 `.openStream()` 来获得一个类似 InputStream 的对象,它可以在没有一次性将所有数据拉到内存的前提下读取对象的原始数据。
接下来几行展现了如何创建一个新的分支。 我们创建一个 RefUpdate 实例,配置一些参数,然后调用 `.update()` 来确认这个更改。 删除相同分支的代码就在这行下面。 记住必须先`.setForceUpdate(true)` 才能让它工作,否则调用 `.delete()` 只会返回 `REJECTED` ,然后什么都没有发生。
最后一个例子展示了如何从 Git 配置文件中获取 `user.name` 的值。 这个 Config 实例使用我们先前打开的仓库做本地配置,但是它也会自动地检测并读取全局和系统的配置文件。
这只是底层 API 的冰山一角,另外还有许多可以使用的方法和类。 还有一个没有放在这里说明的,就是 JGit 是用异常机制来处理错误的。 JGit API 有时使用标准的 Java 异常(例如`IOException` ),但是它也提供了大量 JGit 自己定义的异常类型(例如`NoRemoteRepositoryException`、 `CorruptObjectException` 和`NoMergeBaseException`)。
## 高层命令
底层 API 更加完善,但是有时将它们串起来以实现普通的目的非常困难,例如将一个文件添加到索引,或者创建一个新的提交。 为了解决这个问题, JGit 提供了一系列高层 API ,使用这些 API 的入口点就是 `Git` 类:
~~~
Repository repo;
// 构建仓库。。。
Git git = new Git(repo);
~~~
Git 类有一系列非常好的 *构建器* 风格的高层方法,它可以用来构造一些复杂的行为。 我们来看一个例子——做一件类似 `git ls-remote` 的事。
~~~
CredentialsProvider cp = new UsernamePasswordCredentialsProvider("username", "p4ssw0rd");
Collection remoteRefs = git.lsRemote()
.setCredentialsProvider(cp)
.setRemote("origin")
.setTags(true)
.setHeads(false)
.call();
for (Ref ref : remoteRefs) {
System.out.println(ref.getName() + " -> " + ref.getObjectId().name());
~~~
这是一个 Git 类的公共样式,这个方法返回一个可以让你串连若干方法调用来设置参数的命令对象,当你调用 `.call()` 时它们就会被执行。 在这情况下,我们只是请求了 `origin` 远程的标签,而不是头部。 还要注意用于验证的 `CredentialsProvider` 对象的使用。
在 Git 类中还可以使用许多其它的命令,包括但不限于`add`、`blame`、`commit`、`clean`、`push`、`rebase`、`revert` 和 `reset`。
## 拓展阅读
这只是 JGit 的全部能力的冰山一角。 如果你对这有兴趣并且想深入学习,在下面可以找到一些信息和灵感。
* JGit API 在线官方文档: [*http://download.eclipse.org/jgit/docs/latest/apidocs*](http://download.eclipse.org/jgit/docs/latest/apidocs) 。 这是基本的 Javadoc ,所以你也可以在你最喜欢的 JVM IDE 上将它们安装它们到本地。
* JGit Cookbook : [*https://github.com/centic9/jgit-cookbook*](https://github.com/centic9/jgit-cookbook) 拥有许多如何利用 JGit 实现特定任务的例子。
* [*http://stackoverflow.com/questions/6861881*](http://stackoverflow.com/questions/6861881) 指出了几个好的资源。
';
A2.2 Libgit2
最后更新于:2022-04-02 01:36:06
## Libgit2
另外一种可以供你使用的是 Libgit2。 Libgit2 是一个 Git 的非依赖性的工具,它致力于为其他程序使用 Git 提供更好的 API。 你可以在 [*http://libgit2.github.com*](http://libgit2.github.com/) 找到它。
首先,让我们来看一下 C API 长啥样。 这是一个旋风式旅行。
~~~
// 打开一个版本库
git_repository *repo;
int error = git_repository_open(&repo, "/path/to/repository");
// 逆向引用 HEAD 到一个提交
git_object *head_commit;
error = git_revparse_single(&head_commit, repo, "HEAD^{commit}");
git_commit *commit = (git_commit*)head_commit;
// 显示这个提交的一些详情
printf("%s", git_commit_message(commit));
const git_signature *author = git_commit_author(commit);
printf("%s <%s>\n", author->name, author->email);
const git_oid *tree_id = git_commit_tree_id(commit);
// 清理现场
git_commit_free(commit);
git_repository_free(repo);
~~~
前两行打开一个 Git 版本库。 这个 `git_repository` 类型代表了一个在内存中带有缓存的指向一个版本库的句柄。 这是最简单的方法,只是你必须知道一个版本库的工作目录或者一个 `.git` 文件夹的精确路径。 另外还有 `git_repository_open_ext` ,它包括了带选项的搜索,`git_clone` 及其同类可以用来做远程版本库的本地克隆, `git_repository_init` 则可以创建一个全新的版本库。
第二段代码使用了一种 rev-parse 语法(要了解更多,请看 [分支引用](http://git-scm.com/book/zh/v2/1-git-tools/_branch_references) )来得到 HEAD 真正指向的提交。 返回类型是一个 `git_object` 指针,它指代位于版本库里的 Git 对象数据库中的某个东西。`git_object` 实际上是几种不同的对象的 “父” 类型,每个 “子” 类型的内存布局和`git_object` 是一样的,所以你能安全地把它们转换为正确的类型。 在上面的例子中,`git_object_type(commit)` 会返回 `GIT_OBJ_COMMIT` ,所以转换成 `git_commit` 指针是安全的。
下一段展示了如何访问一个提交的详情。 最后一行使用了 `git_oid` 类型,这是 Libgit2 用来表示一个 SHA-1 哈希的方法。
从这个例子中,我们可以看到一些模式:
* 如果你声明了一个指针,并在一个 Libgit2 调用中传递一个引用,那么这个调用可能返回一个 int 类型的错误码。 值 `0` 表示成功,比它小的则是一个错误。
* 如果 Libgit2 为你填入一个指针,那么你有责任释放它。
* 如果 Libgit2 在一个调用中返回一个 `const` 指针,你不需要释放它,但是当它所指向的对象被释放时它将不可用。
* 用 C 来写有点蛋疼。
最后一点意味着你应该不会在使用 Libgit2 时编写 C 语言程序。 但幸运的是,有许多可用的各种语言的绑定,能让你在特定的语言和环境中更加容易的操作 Git 版本库。 我们来看一下下面这个用 Libgit2 的 Ruby 绑定写成的例子,它叫 Rugged,你可以在[*https://github.com/libgit2/rugged*](https://github.com/libgit2/rugged) 找到它。
~~~
repo = Rugged::Repository.new('path/to/repository')
commit = repo.head.target
puts commit.message
puts "#{commit.author[:name]} <#{commit.author[:email]}>"
tree = commit.tree
~~~
你可以发现,代码看起来更加清晰了。 首先, Rugged 使用异常机制,它可以抛出类似于`ConfigError` 或者 `ObjectError` 之类的东西来告知错误的情况。 其次,不需要明确资源释放,因为 Ruby 是支持垃圾回收的。 我们来看一个稍微复杂一点的例子:从头开始制作一个提交。
~~~
blob_id = repo.write("Blob contents", :blob)
index = repo.index
index.read_tree(repo.head.target.tree)
index.add(:path => 'newfile.txt', :oid => blob_id)
sig = {
:email => "bob@example.com",
:name => "Bob User",
:time => Time.now,
}
commit_id = Rugged::Commit.create(repo,
:tree => index.write_tree(repo),
:author => sig,
:committer => sig,
:message => "Add newfile.txt",
:parents => repo.empty? ? [] : [ repo.head.target ].compact,
:update_ref => 'HEAD',
)
commit = repo.lookup(commit_id)
~~~
[![](image/561a25142a2cd.png)](http://git-scm.com/book/zh/v2/ch00/co___git________CO1-1)
创建一个新的 blob ,它包含了一个新文件的内容。
[![](image/561a250d39b5c.png)](http://git-scm.com/book/zh/v2/ch00/co___git________CO1-2)
将 HEAD 提交树填入索引,并在路径 `newfile.txt` 增加新文件。
[![](image/561a250a858f0.png)](http://git-scm.com/book/zh/v2/ch00/co___git________CO1-3)
这就在 ODB 中创建了一个新的树,并在一个新的提交中使用它。
[![](image/561a24fed1af7.png)](http://git-scm.com/book/zh/v2/ch00/co___git________CO1-4)
我们在 author 栏和 committer 栏使用相同的签名。
[![](image/561a24fdc1b9c.png)](http://git-scm.com/book/zh/v2/ch00/co___git________CO1-5)
提交的信息。
[![](image/561a24fcb8e13.png)](http://git-scm.com/book/zh/v2/ch00/co___git________CO1-6)
当创建一个提交时,你必须指定这个新提交的父提交。 这里使用了 HEAD 的末尾作为单一的父提交。
[![](image/561a24fb4af80.png)](http://git-scm.com/book/zh/v2/ch00/co___git________CO1-7)
在做一个提交的过程中, Rugged (和 Libgit2 )能在需要时更新引用。
[![](image/561a24f0071f6.png)](http://git-scm.com/book/zh/v2/ch00/co___git________CO1-8)
返回值是一个新提交对象的 SHA-1 哈希,你可以用它来获得一个 `Commit` 对象。
Ruby 的代码很好很简洁,另一方面因为 Libgit2 做了大量工作,所以代码运行起来其实速度也不赖。 如果你不是一个 Ruby 程序员,我们在 [其它绑定](http://git-scm.com/book/zh/v2/ch00/_libgit2_bindings) 有提到其它的一些绑定。
### [高级功能](http://git-scm.com/book/zh/v2/%E5%B0%86-Git-%E5%B5%8C%E5%85%A5%E4%BD%A0%E7%9A%84%E5%BA%94%E7%94%A8-Libgit2#高级功能)
Libgit2 有几个超过核心 Git 的能力。 例如它的可定制性:Libgit2 允许你为一些不同类型的操作自定义的“后端”,让你得以使用与原生 Git 不同的方式存储东西。 Libgit2 允许为自定义后端指定配置、引用的存储以及对象数据库,
我们来看一下它究竟是怎么工作的。 下面的例子借用自 Libgit2 团队提供的后端样本集 (可以在[*https://github.com/libgit2/libgit2-backends*](https://github.com/libgit2/libgit2-backends) 上找到)。 一个对象数据库的自定义后端是这样建立的:
~~~
git_odb *odb;
int error = git_odb_new(&odb);
git_odb_backend *my_backend;
error = git_odb_backend_mine(&my_backend, /*…*/);
error = git_odb_add_backend(odb, my_backend, 1);
git_repository *repo;
error = git_repository_open(&repo, "some-path");
error = git_repository_set_odb(odb);
~~~
*(注意:这个错误被捕获了,但是没有被处理。我们希望你的代码比我们的更好。)*
[![](image/561a24ee65d8b.png)](http://git-scm.com/book/zh/v2/ch00/co___git________CO2-1)
初始化一个空的对象数据库( ODB ) “前端”,它将被作为一个用来做真正的工作的 “后端” 的容器。
[![](image/561a24e791845.png)](http://git-scm.com/book/zh/v2/ch00/co___git________CO2-2)
初始化一个自定义 ODB 后端。
[![](image/561a24e59cad8.png)](http://git-scm.com/book/zh/v2/ch00/co___git________CO2-3)
为这个前端增加一个后端。
[![](image/561a24e425699.png)](http://git-scm.com/book/zh/v2/ch00/co___git________CO2-4)
打开一个版本库,并让它使用我们的 ODB 来寻找对象。
但是 `git_odb_backend_mine` 是个什么东西呢? 嗯,那是一个你自己的 ODB 实现的构造器,并且你能在那里做任何你想做的事,前提是你能正确地填写 `git_odb_backend` 结构。 它看起来*应该*是这样的:
~~~
typedef struct {
git_odb_backend parent;
// 其它的一些东西
void *custom_context;
} my_backend_struct;
int git_odb_backend_mine(git_odb_backend **backend_out, /*…*/)
{
my_backend_struct *backend;
backend = calloc(1, sizeof (my_backend_struct));
backend->custom_context = …;
backend->parent.read = &my_backend__read;
backend->parent.read_prefix = &my_backend__read_prefix;
backend->parent.read_header = &my_backend__read_header;
// ……
*backend_out = (git_odb_backend *) backend;
return GIT_SUCCESS;
}
~~~
`my_backend_struct` 的第一个成员必须是一个 `git_odb_backend` 结构,这是一个微妙的限制:这样就能确保内存布局是 Libgit2 的代码所期望的样子。 其余都是随意的,这个结构的大小可以随心所欲。
这个初始化函数为该结构分配内存,设置自定义的上下文,然后填写它支持的 `parent` 结构的成员。 阅读 Libgit2 的 `include/git2/sys/odb_backend.h` 源码以了解全部调用签名,你特定的使用环境会帮你决定使用哪一种调用签名。
### [其它绑定](http://git-scm.com/book/zh/v2/%E5%B0%86-Git-%E5%B5%8C%E5%85%A5%E4%BD%A0%E7%9A%84%E5%BA%94%E7%94%A8-Libgit2#其它绑定)
Libgit2 有很多种语言的绑定。 在这篇文章中,我们展现了一个使用了几个更加完整的绑定包的小例子,这些库存在于许多种语言中,包括 C++、Go、Node.js、Erlang 以及 JVM ,它们的成熟度各不相同。 官方的绑定集合可以通过浏览这个版本库得到:https://github.com/libgit2[] 。 我们写的代码将返回当前 HEAD 指向的提交的提交信息(就像 `git log -1` 那样)。
LibGit2Sharp
如果你在编写一个 .NET 或者 Mono 应用,那么 LibGit2Sharp ([*https://github.com/libgit2/libgit2sharp*](https://github.com/libgit2/libgit2sharp)) 就是你所需要的。 这个绑定是用 C# 写成的,并且已经采取许多措施来用令人感到自然的 CLR API 包装原始的 Libgit2 的调用。 我们的例子看起来就像这样:
~~~
new Repository(@"C:\path\to\repo").Head.Tip.Message;
~~~
对于 Windows 桌面应用,一个叫做 NuGet 的包会让你快速上手。
objective-git
如果你的应用运行在一个 Apple 平台上,你很有可能使用 Objective-C 作为实现语言。 Objective-Git ([*https://github.com/libgit2/objective-git*](https://github.com/libgit2/objective-git)) 是这个环境下的 Libgit2 绑定。 一个例子看起来类似这样:
~~~
GTRepository *repo =
[[GTRepository alloc] initWithURL:[NSURL fileURLWithPath: @"/path/to/repo"] error:NULL];
NSString *msg = [[[repo headReferenceWithError:NULL] resolvedTarget] message];
~~~
Objective-git 与 Swift 完美兼容,所以你把 Objective-C 落在一边的时候不用恐惧。
pygit2
Python 的 Libgit2 绑定叫做 Pygit2 ,你可以在 [*http://www.pygit2.org/*](http://www.pygit2.org/) 找到它。 我们的示例程序:
~~~
pygit2.Repository("/path/to/repo") # 打开版本库
.head # get the current branch
.peel(pygit2.Commit) # walk down to the commit
.message # read the message
~~~
### [扩展阅读](http://git-scm.com/book/zh/v2/%E5%B0%86-Git-%E5%B5%8C%E5%85%A5%E4%BD%A0%E7%9A%84%E5%BA%94%E7%94%A8-Libgit2#扩展阅读)
当然,完全阐述 Libgit2 的能力已超出本书范围。 如果你想了解更多关于 Libgit2 的信息,可以浏览它的 API 文档: [*https://libgit2.github.com/libgit2*](https://libgit2.github.com/libgit2), 以及一系列的指南:[*https://libgit2.github.com/docs*](https://libgit2.github.com/docs). 对于其它的绑定,检查附带的 README 和测试文件,那里通常有简易教程,以及指向拓展阅读的链接。
';
A2.1 命令行 Git 方式
最后更新于:2022-04-02 01:36:03
## 命令行 Git 方式
一种方式就是启动一个 shell 进程并在里面使用 Git 的命令行工具来完成任务。 这种方式看起来很循规蹈矩,但是它的优点也因此而来,就是支持所有的 Git 的特性。 它也碰巧相当简单,因为几乎所有运行时环境都有一个相对简单的方式来调用一个带有命令行参数的进程。 然而,这种方式也有一些固有的缺点。
一个就是所有的输出都是纯文本格式。 这意味着你将被迫解析 Git 的有时会改变的输出格式,以随时了解它工作的进度和结果。更糟糕的是,这可能是无效率并且容易出错的。
另外一个就是令人捉急的错误修复能力。 如果一个版本库被莫名其妙地损毁,或者用户使用了一个奇奇怪怪的配置, Git 只会简单地拒绝表现自己的强大能力。
还有一个就是进程的管理。 Git 会要求你在一个独立的进程中维护一个 shell 环境,这可能会无谓地增加复杂性。 试图协调许许多多的类似的进程(尤其是在某些情况下,当不同的进程在访问相同的版本库时)是对你的能力的极大的挑战。
';
B. 将 Git 嵌入你的应用
最后更新于:2022-04-02 01:36:01
# 附录B. 将 Git 嵌入你的应用
假设你的应用程序的目标人群是开发者,如果它能够被整合进一些源码控制的功能,那真真是极好的。甚至对于一个例如文档编辑器之类的不是为开发者而设计的应用程序,它们也可能从版本控制系统中受益,并且 Git 的实现方式在很多情况下都表现得非常出色。
如果你想将 Git 整合进你的应用程序的话,一般来说你有三种可能的选择:启动一个 shell 来使用 Git 的命令行工具;使用 Libgit2;或者使用 JGit。
';
A1.7 总结
最后更新于:2022-04-02 01:35:59
## 总结
你已经学会了如何从日常工具中发挥 Git 的强大力量,以及从自己的程序中访问 Git 仓库的方法。
';
A1.6 Powershell 中的 Git
最后更新于:2022-04-02 01:35:56
## Powershell 中的 Git
Windows 中的普通命令行终端 (`cmd.exe`) 无法自定义 Git 使用体验,但是如果你正在使用 Powershell,那么你就十分幸运了。 一个名为 Posh-Git ([*https://github.com/dahlbyk/posh-git*](https://github.com/dahlbyk/posh-git)) 的扩展包提供了强大的 tab 补全功能, 并针对提示符进行了增强,以帮助你聚焦于你的仓库状态。 它看起来像:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-12_561bcbbf9926f.png)
Figure 1-13. 附带了 Posh-Git 扩展包的 Powershell。
如果你已经在 Windows 上安装了 GitHub,Posh-Git 也会被安装,你只需要添加以下两行到你的`profile.ps1` 文件 (文件位于 `C:\Users\\Documents\WindowsPowerShell`):
~~~
. (Resolve-Path "$env:LOCALAPPDATA\GitHub\shell.ps1")
. $env:github_posh_git\profile.example.ps1
~~~
如果你没有在 Windows 上安装 GitHub,只需要从 ([*https://github.com/dahlbyk/posh-git*](https://github.com/dahlbyk/posh-git)) 下载一份 Posh-Git 发行版,并且解压至 `WindowsPowershell` 目录。 然后以管理员权限打开 Powershell 提示符,并且执行下面的命令:
~~~
> Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Confirm
> cd ~\Documents\WindowsPowerShell\posh-git
> .\install.ps1
~~~
它将会向你的 `profile.ps1` 文件添加适当的内容,Posh-Git 将会在下次打开提示符时被启用。
';
A1.5 Zsh 中的 Git
最后更新于:2022-04-02 01:35:54
## Zsh 中的 Git
Git 还为 Zsh 提供了一个 Tab 补全库。 复制 `contrib/completion/git-completion.zsh`到你的 home 目录,然后在 `.zshrc` 中 source 即可。 相对于 Bash,Zsh 的接口更加强大:
~~~
$ git che
check-attr -- 显示 gitattributes 信息
check-ref-format -- 检查引用名称是否符合规范
checkout -- 从工作区中检出分支或路径
checkout-index -- 从暂存区拷贝文件至工作目录
cherry -- 查找没有被合并至上游的提交
cherry-pick -- 从一些已存在的提交中应用更改
~~~
意义不明的 Tab 补全并不仅仅会被列出;它们还会有帮助性的描述,你可以通过不断敲击 Tab 以图形方式浏览补全列表。 该功能可用于 Git 命令、它们的参数和在仓库中内容的名称(例如 refs 和 remotes),还有文件名和其他所有 Zsh 知道如何去补全的项目。
在提示符自定义方面,Zsh 很好地兼容了Bash,并允许你同时使用一个右侧提示符。 把如下代码添加至你的 `~/.zshrc` 文件中,就可以在右侧显示分支名称:
~~~
setopt prompt_subst
~~~
当你的命令行位于一个 Git 仓库目录时,在任何时候,都可以在命令行窗口右侧显示当前分支。 它看起来像这样:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-12_561bcbbf4f7af.png)
Figure 1-11. 自定义 `zsh` 提示符.
Zsh 本身已足够强大,但还有一些专门为它打造的完整框架,使它更加完善。 其中之一名为 "oh-my-zsh",你可以在 [*https://github.com/robbyrussell/oh-my-zsh*](https://github.com/robbyrussell/oh-my-zsh) 找到它。 oh-my-zsh 的扩展系统包含强大的 Git Tab 补全功能,且许多提示符 "主题" 可以展示版本控制数据。 [Figure 1-12](http://git-scm.com/book/zh/v2/ch00/oh_my_zsh_git) 只是可以其中一个可以通过该系统实现的例子。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-12_561bcbbf6658e.png)
Figure 1-12. 一个 oh-my-zsh 主题的示例.
';