├── _static └── .gitignore ├── _build └── .gitignore ├── _templates └── .gitignore ├── images └── .gitignore ├── .gitignore ├── .gitmodules ├── _theme └── gotgithub │ ├── theme.conf │ ├── static │ ├── contents.png │ ├── navigation.png │ ├── worldhello.css │ └── sphinxdoc.css │ └── layout.html ├── _version.inc.in ├── 06-side-projects ├── others.rst ├── tools.rst ├── other-scm.rst ├── index.rst ├── ios.rst ├── shop.rst ├── jobs.rst ├── github-mac.rst ├── short-url.rst ├── hg-git.rst ├── svn.rst ├── opensource.rst ├── hub.rst └── gist.rst ├── 02-join-github ├── index.rst ├── 030-be-social.rst ├── 020-browse-repo.rst └── 010-account-setup.rst ├── 03-project-hosting ├── index.rst ├── 040-repo-hooks.rst ├── 030-repo-authz.rst ├── 010-new-project.rst ├── 020-repo-operation.rst └── 050-homepage.rst ├── 04-work-with-others ├── index.rst ├── 040-code-review.rst ├── 030-organization.rst ├── 020-shared-repo.rst ├── 050-issue.rst ├── 060-wiki.rst └── 010-fork-and-pull.rst ├── 05-commercial-github ├── index.rst ├── github-enterprise.rst └── non-free-plans.rst ├── contributors.inc ├── contrib ├── post-commit └── sphinx │ └── 0001-No-prefix-_-for-sphinx-build-output-directories.patch ├── index.rst ├── 01-explore-github ├── index.rst ├── 020-github-hightlights.rst ├── 030-explore-github.rst └── 010-what-is-github.rst ├── preface.inc ├── README.md ├── make.bat ├── Makefile └── conf.py /_static/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_build/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /_templates/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _version.inc 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "graphics"] 2 | path = graphics 3 | url = git://github.com/gotgit/gotgithub-graphics.git 4 | -------------------------------------------------------------------------------- /_theme/gotgithub/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = sphinxdoc.css 4 | pygments_style = friendly 5 | -------------------------------------------------------------------------------- /_theme/gotgithub/static/contents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/gotgithub/master/_theme/gotgithub/static/contents.png -------------------------------------------------------------------------------- /_theme/gotgithub/static/navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/gotgithub/master/_theme/gotgithub/static/navigation.png -------------------------------------------------------------------------------- /_version.inc.in: -------------------------------------------------------------------------------- 1 | :Author: Jiang Xin 2 | :Version: 3 | :Copyright: `Creative Commons BY-NC-SA `_ 4 | -------------------------------------------------------------------------------- /06-side-projects/others.rst: -------------------------------------------------------------------------------- 1 | .. _others: 2 | 3 | 其他 4 | ====== 5 | 6 | 7 | .. toctree:: 8 | :maxdepth: 1 9 | 10 | jobs 11 | shop 12 | short-url 13 | opensource 14 | -------------------------------------------------------------------------------- /02-join-github/index.rst: -------------------------------------------------------------------------------- 1 | .. _join-github: 2 | 3 | 加入GitHub 4 | ******************** 5 | 本章介绍如何在GitHub上注册账号,并以现有项目为例介绍GitHub的主要功能。 6 | 7 | .. toctree:: 8 | :maxdepth: 1 9 | 10 | 010-account-setup 11 | 020-browse-repo 12 | 030-be-social 13 | -------------------------------------------------------------------------------- /06-side-projects/tools.rst: -------------------------------------------------------------------------------- 1 | .. _client-tools: 2 | 3 | 客户端工具 4 | =============== 5 | 6 | GitHub提供的Web服务,在客户端通常只需要浏览器及Git命令行工具就可以满足需要了。\ 7 | 而GitHub还开发了一些客户端工具,以便用户有更好的客户端体验。 8 | 9 | .. toctree:: 10 | :maxdepth: 1 11 | 12 | github-mac 13 | hub 14 | ios 15 | -------------------------------------------------------------------------------- /06-side-projects/other-scm.rst: -------------------------------------------------------------------------------- 1 | .. _other-scm-support: 2 | 3 | 其他版本控制工具支持 4 | ======================= 5 | 6 | 如果曾经有人对 GitHub 只提供唯一的版本库格式(Git)托管表示过怀疑的话,那么\ 7 | 今天看到 GitHub 对其他版本控制工具提供的愈发完善的支持,争议应该烟消云散了吧。 8 | 9 | .. toctree:: 10 | :maxdepth: 1 11 | 12 | svn 13 | hg-git 14 | -------------------------------------------------------------------------------- /03-project-hosting/index.rst: -------------------------------------------------------------------------------- 1 | .. _hosting: 2 | 3 | 项目托管 4 | ******************** 5 | 本章介绍如何在GitHub上创建一个新项目,包括创建版本库及为项目设计主页等。 6 | 7 | .. toctree:: 8 | :maxdepth: 1 9 | 10 | 010-new-project 11 | 020-repo-operation 12 | 030-repo-authz 13 | 040-repo-hooks 14 | 050-homepage 15 | -------------------------------------------------------------------------------- /_theme/gotgithub/static/worldhello.css: -------------------------------------------------------------------------------- 1 | body { 2 | min-width: 740px; 3 | width: 960px; 4 | margin: 0 auto; 5 | -moz-border-radius: 1.5em; 6 | -webkit-border-radius: 1.5em; 7 | border-radius: 1.5em; 8 | } 9 | 10 | div.related { 11 | margin-top: 5px; 12 | margin-bottom: 0; 13 | } 14 | -------------------------------------------------------------------------------- /04-work-with-others/index.rst: -------------------------------------------------------------------------------- 1 | .. _cooperate: 2 | 3 | 工作协同 4 | ******************** 5 | 项目落户GitHub后,一定希望有越来越多的人能参与其中。GitHub提供了包括传统的\ 6 | 问题追踪系统、维基,还包括了分布式版本控制系统特有的协同工具。 7 | 8 | .. toctree:: 9 | :maxdepth: 1 10 | 11 | 010-fork-and-pull 12 | 020-shared-repo 13 | 030-organization 14 | 040-code-review 15 | 050-issue 16 | 060-wiki 17 | -------------------------------------------------------------------------------- /06-side-projects/index.rst: -------------------------------------------------------------------------------- 1 | .. _by-products: 2 | 3 | GitHub副产品 4 | ******************** 5 | GitHub最核心的产品是Git版本库(即项目)托管,此外GitHub还提供一些副产品\ 6 | (Side Project),通过附加的服务或技术提供了更多有趣的功能。例如提供数据粘贴的\ 7 | Gist网站,对其他版本控制工具如SVN和Hg的支持,各具特色的客户端工具,求职网站,\ 8 | 销售纪念品的GitHub商店等等。 9 | 10 | .. toctree:: 11 | :maxdepth: 1 12 | 13 | gist 14 | other-scm 15 | tools 16 | others 17 | -------------------------------------------------------------------------------- /05-commercial-github/index.rst: -------------------------------------------------------------------------------- 1 | .. _non-free: 2 | 3 | 付费服务 4 | ******************** 5 | 6 | GitHub神奇的协同工具使得开源项目的创建和协同更加简单、高效。有些人可能会提出\ 7 | 疑问:能否把GitHub用于私有项目呢?即能否只允许指定的用户访问项目和版本库,\ 8 | 而其他人不能访问呢?能否在企业内部架设一个一模一样的GitHub服务呢?GitHub\ 9 | 针对这些需求提供了解决方案,这些解决方案需要或多或少地付出一定费用。 10 | 11 | .. toctree:: 12 | :maxdepth: 1 13 | 14 | non-free-plans.rst 15 | github-enterprise.rst 16 | -------------------------------------------------------------------------------- /06-side-projects/ios.rst: -------------------------------------------------------------------------------- 1 | .. _ios: 2 | 3 | iOS应用 4 | ------------ 5 | 6 | GitHub还为iOS平台开发了应用,这样就可以在 iPhone、iPad 等苹果设备上实时跟踪\ 7 | GitHub上的项目了。在苹果AppStore上搜索GitHub公司的应用,可以找到GitHub Issues\ 8 | 和GitHub Jobs等应用,如图6-14所示。 9 | 10 | .. figure:: /images/side-projects/ios-apps.png 11 | :scale: 100 12 | 13 | 图6-14:iPhone上的issues应用 14 | 15 | 在iPhone中安装GitHub Issues应用,就可以随时查看所关注的GitHub项目的问题报告\ 16 | 和Pull Request等,如图6-15所示。 17 | 18 | .. figure:: /images/side-projects/ios-issues-iphone.png 19 | :width: 300 20 | 21 | 图6-15:iPhone上的GitHub Issues应用 22 | 23 | 而GitHub Jobs应用则和即将要介绍的GitHub招聘网站有关,用于浏览招聘信息。 24 | -------------------------------------------------------------------------------- /contributors.inc: -------------------------------------------------------------------------------- 1 | Git和GitHub促进了开源软件的发展是因为消除了核心开发者和贡献者的隔阂——你若能看到代码,你就能改进代码。开放的电子书亦是如此,下面的贡献者让本书变得更好。 2 | 3 | 以贡献时间为序,感谢: 4 | 5 | 1. `Zhang Hailong `_ 报告文字错误。问题: `#2 `_ 。 6 | 2. `Riku `_ 纠正文字错误。提交: `455d0db`_ , `f244e3d`_ 。 7 | 3. `windwiny `_ 纠正文字错误。提交: `1ed1a51`_ 8 | 9 | .. _455d0db: https://github.com/gotgit/gotgithub/commit/455d0db 10 | .. _f244e3d: https://github.com/gotgit/gotgithub/commit/f244e3d 11 | .. _1ed1a51: https://github.com/gotgit/gotgithub/commit/1ed1a51 12 | -------------------------------------------------------------------------------- /contrib/post-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script that is called after a successful 4 | # commit is made. 5 | # 6 | # To enable this hook, rename this file to "post-commit". 7 | 8 | gh_pages_repo=$(dirname $(pwd))/gotgithub.gh-pages 9 | 10 | if [ -d $gh_pages_repo ]; then 11 | cd $gh_pages_repo 12 | git fetch 13 | if [ $(git rev-list origin/gh-pages...HEAD | wc -l) -ne 0 ]; then 14 | echo "remote: repo $(basename $gh_pages_repo) switched to the updated gh-pages branch" >&2 15 | git reset --hard origin/gh-pages 16 | fi 17 | else 18 | echo "not found gh-pages repo!" >&2 19 | fi 20 | -------------------------------------------------------------------------------- /index.rst: -------------------------------------------------------------------------------- 1 | ========== 2 | GotGitHub 3 | ========== 4 | 5 | .. include:: _version.inc 6 | 7 | 前言 8 | ---- 9 | 10 | .. include:: preface.inc 11 | 12 | 目录 13 | ---- 14 | 15 | .. toctree:: 16 | :maxdepth: 3 17 | :numbered: 3 18 | :glob: 19 | 20 | 01-explore-github/index 21 | 02-join-github/index 22 | 03-project-hosting/index 23 | 04-work-with-others/index 24 | 05-commercial-github/index 25 | 06-side-projects/index 26 | appendix/markups 27 | 28 | 贡献者列表 29 | ---------- 30 | .. include:: contributors.inc 31 | 32 | 33 | .. 索引和表格 34 | .. ================== 35 | .. 36 | .. * :ref:`genindex` 37 | .. * :ref:`modindex` 38 | .. * :ref:`search` 39 | -------------------------------------------------------------------------------- /01-explore-github/index.rst: -------------------------------------------------------------------------------- 1 | .. _github-overview: 2 | 3 | 探索GitHub 4 | ******************** 5 | 熟悉Git\ [#]_\ 的人几乎都知道并喜欢GitHub\ [#]_\ ,反过来GitHub也吸引更多的\ 6 | 人来使用Git。GitHub正在成为开源项目托管的主要平台,是什么成就了GitHub? 7 | 8 | .. toctree:: 9 | :maxdepth: 1 10 | 11 | 010-what-is-github 12 | 020-github-hightlights 13 | 030-explore-github 14 | 15 | .. seealso:: 16 | 17 | 本书并非一本介绍Git的书,并且假设读者已经掌握了Git的相关操作。如果读者对\ 18 | Git尚不了解,可以参考我写的 《Git权威指南》\ [#]_\ 一书。此外还可以从网上\ 19 | 找到很多免费的、很好的Git资料,如:Git社区书\ [#]_\ 、Pro Git\ [#]_\ 等。 20 | 21 | ---- 22 | 23 | .. [#] http://git-scm.com/ 24 | .. [#] https://github.com/ 25 | .. [#] ISBN:9787111349679, 由机械工业出版社华章公司于2011年7月出版。 26 | .. [#] http://book.git-scm.com/ 27 | .. [#] http://progit.org/book/ 28 | -------------------------------------------------------------------------------- /06-side-projects/shop.rst: -------------------------------------------------------------------------------- 1 | .. _shop: 2 | 3 | GitHub:Shop 4 | =============== 5 | GitHub商店\ [#]_\ 销售着一些你在其他地方买不到的小东西——如印着GitHub吉祥物\ 6 | Octocat的纪念品,图6-18展示的就是一款印着Octocat的杯子。 7 | 8 | .. figure:: /images/side-projects/shop-octocat-mug.jpg 9 | :scale: 100 10 | 11 | 图6-18:印着Octocat吉祥物的杯子 12 | 13 | 图6-19则是GitHub热卖的一款体恤的前后两面的图案设计\ [#]_\ 。体恤前面印着GitHub\ 14 | 社区编程最核心的理念(fork you),体恤后面则可以用记号笔写下你在GitHub上的\ 15 | 主页地址。 16 | 17 | .. figure:: /images/side-projects/shop-fork-you-shirt.png 18 | :scale: 100 19 | 20 | 图6-19:超酷的GitHub体恤 21 | 22 | GitHub商店实际上是架设于Shopify\ [#]_\ 电子商务网站上的网店。GitHub商店并非\ 23 | GitHub主业,销售纪念品可以增强GitHub用户的认同感,而GitHub粉丝可以购买一项\ 24 | “装备”让自己看起来更酷。 25 | 26 | ---- 27 | 28 | .. [#] http://shop.github.com/ 29 | .. [#] https://github.com/blog/676-fork-you 30 | .. [#] http://www.shopify.com/ 31 | -------------------------------------------------------------------------------- /preface.inc: -------------------------------------------------------------------------------- 1 | 动笔写GitHub不是因为我对其了解,恰恰是对其太不了解。 2 | 3 | 在我的《Git权威指南》 [#]_ 一书中,涉及到GitHub的只有区区三页纸,这显然回答不了读者对于GitHub的诸多疑问。 4 | 记得在《Git权威指南》刚刚完稿之际,机械工业出版社华章公司的杨福川编辑就鼓动我写一本关于GitHub的书,我用了好多理由推辞了。 5 | 头条理由就是我真的累着了。在每一章节开始动笔之时,都好像是坐在了中学语文考试的考堂上写作文,时间快到了可仍然动不了笔, 6 | 再写一本书无疑要重复这一痛苦的经历。 7 | 第二个理由是我更喜欢编程,而不是写文档,尤其写GitHub会有大量截图、图像处理的琐碎工作。 8 | 第三个理由彻底让编辑投降,那就是GitHub是一个国外网站,也许书一出,【此句已被原作者删除】。 9 | 10 | 让我最终决定动笔,是源于CSDN蒋总在美国拜访GitHub总部后告诉我的一些见闻,我对GitHub如此成功运作产生了兴趣,于是开始研究GitHub的博客,愈发发现GitHub是一群有趣的人在做的有趣的事,如果只把GitHub当作一个Git服务器,实在是暴殄天物。GitHub已经并将继续获得成功,若真能凭借此书把GitHub尽量全面地展现,让每一个Git使用者用好GitHub也是一件幸事。 11 | 12 | 这本书将采用GitHub的方式进行撰写和发布 [#]_ ,任何人都可以看到本书(包括源码),更可以用GitHub的方法参与本书的撰写和纠错。网络出版对于我和杨福川编辑都是一个全新的体验。感谢Git,让我在一年内拥有了两种不同的出版体验。 13 | 14 | -- 蒋鑫, 2011.12 15 | 16 | ---- 17 | 18 | .. [#] http://www.worldhello.net/gotgit/ 19 | .. [#] https://github.com/gotgit/gotgithub 20 | -------------------------------------------------------------------------------- /06-side-projects/jobs.rst: -------------------------------------------------------------------------------- 1 | .. _jobs: 2 | 3 | GitHub:Jobs 4 | =============== 5 | GitHub求职网站\ [#]_\ ,于2010年8月开通,提供求职招聘服务\ [#]_\ 。还记得在\ 6 | “第2.1节 创建GitHub账号”介绍的相关内容么?当用户在个人设置中对简历和求职状态\ 7 | 进行设置和启用后,GitHub就会帮助用户寻找合适的工作机会,而工作机会就来自于\ 8 | GitHub的求职网站(如图6-16所示)。 9 | 10 | .. figure:: /images/side-projects/jobs-homepage.png 11 | :scale: 100 12 | 13 | 图6-16:GitHub求职网站 14 | 15 | 个人用户除了开启求职状态坐等通知外,还可以主动出击,直接到GitHub求职网站上\ 16 | 寻找合适的工作机会,整个求职过程是免费的。而作为企业主发布招聘启示则是收费\ 17 | 服务,发布招聘启示的流程如图6-17所示。 18 | 19 | .. figure:: /images/side-projects/jobs-post-process.png 20 | :scale: 100 21 | 22 | 图6-17:企业主发布招聘启示流程 23 | 24 | 企业发布招聘启示,首先要按照模版填写职位说明及留下供求职者投递简历的邮件地址,\ 25 | 然后用信用卡付费,每一个招聘启示的付费标准为350美元/月。一旦付费完成招聘马上\ 26 | 生效。GitHub作为程序员的聚集地,无疑是招聘和应聘的理想之地。 27 | 28 | ---- 29 | 30 | .. [#] http://jobs.github.com/ 31 | .. [#] https://github.com/blog/694-github-jobs-launches-tomorrow 32 | -------------------------------------------------------------------------------- /05-commercial-github/github-enterprise.rst: -------------------------------------------------------------------------------- 1 | .. _github-enterprise: 2 | 3 | GitHub企业版 4 | =========================== 5 | 出于隐私或法律原因而不能将代码托管到第三方平台的企业,可能希望在企业内部架设\ 6 | 专有的GitHub服务,能做到么?答案就是GitHub企业版(GitHub Enterprise)。 7 | 8 | 网址:\ https://enterprise.github.com/\ 。 9 | 10 | GitHub企业版搭建在企业本地网络中,因此企业拥有对版本库和项目完整的控制权限。\ 11 | GitHub企业版包含了GitHub上所有的好东西:提交历史、代码浏览、比较视图、\ 12 | Pull Requests、问题追踪、维基、Gists、组织和团队管理、强大的API,和漂亮的界面,\ 13 | 因此会使用GitHub就会使用GitHub企业版。此外企业版还增加了对LDAP和CAS支持功能,\ 14 | 以便和企业现有的认证系统整合等等\ [#]_\ 。 15 | 16 | GitHub企业版不像它的前身GitHub:FI\ [#]_\ 那样通过下载软件包进行安装和部署,\ 17 | 而是提供基于虚拟机的解决方案。即GitHub企业版以OVF虚拟机文件格式发布,可以运行\ 18 | 在多种虚拟机平台,如:VMWare、VirtualBox、Oracle VM、\ 19 | Red Hat Enterprise Virtualization和IBM POWER。\ 20 | 使用OVF格式让GitHub企业版的部署更加轻松。 21 | 22 | GitHub企业版根据用户数量收取年费,入门级的价格为20用户每年5,000美金。如果用户数\ 23 | 少,建议采用付费的GitHub托管账号。购买更多用户许可,访问GitHub企业版网站,\ 24 | 那儿有一个报价生成器,如图5-10所示。 25 | 26 | .. figure:: /images/commercial-products/github-enterprise-pricing.png 27 | :scale: 100 28 | 29 | 图5-10:GitHub企业版报价生成器 30 | 31 | ---- 32 | 33 | .. [#] https://github.com/blog/978-introducing-github-enterprise 34 | .. [#] GitHub:FI是GitHub Firewall Install的缩写,含义为在企业的防火墙内部架设\ 35 | GitHub服务,现已升级为GitHub Enterprise。 36 | -------------------------------------------------------------------------------- /02-join-github/030-be-social.rst: -------------------------------------------------------------------------------- 1 | .. _soc: 2 | 3 | 社交网络 4 | =============== 5 | 6 | 社交网络的一大特征就是用户间的相互关注,从而形成朋友圈或媒体圈,实现便捷的\ 7 | 信息分享和传播。GitHub支持项目级别及用户级别的关注。 8 | 9 | 关注一个项目很简单,只需点击项目名称右侧的“Watch”按钮。 10 | 11 | .. figure:: /images/join-github/watch-project.png 12 | :scale: 100 13 | 14 | 图2-27:项目的关注按钮 15 | 16 | 添加对项目的关注后,点击页面左上角的“github”文字图标进入仪表板(Dashboard)\ 17 | 页面,如图2-28所示。 18 | 19 | .. figure:: /images/join-github/dashboard-with-watched-prj.png 20 | :scale: 100 21 | 22 | 图2-28:关注项目在仪表板页的显示 23 | 24 | 仪表板页面的左侧显示所关注项目的最新动态,右侧会列表显示关注的项目列表。 25 | 26 | GitHub还可以关注用户。访问 https://github.com/mojombo 可以看到mojombo(GitHub\ 27 | 创始者之一)的用户页,关注他只需点击图2-29中的“Follow”按钮。从mojombo的用户页\ 28 | 还可以看到majombo关注的开发者,可以以此扩大GitHub朋友圈。 29 | 30 | .. figure:: /images/join-github/github-mojombo.png 31 | :scale: 100 32 | 33 | 图2-29:mojombo的用户页 34 | 35 | GitHub仪表板页面,有一个“RSS Feed”链接,如图2-30所示。点击该链接可以使用RSS客户端\ 36 | (如Google Reader)订阅,实现无需登录GitHub即可访问所关注的项目和人的动态。 37 | 38 | .. figure:: /images/join-github/rss-feed.png 39 | :scale: 100 40 | 41 | 图2-30:RSS Feed 42 | 43 | RSS中可能包括隐私信息,如私有版本库的更新信息,那么RSS订阅是如何保护个人隐私\ 44 | 呢?难道需要口令认证么?查看一下RSS订阅的URL,你会看到类似如下格式的URL地址: 45 | 46 | :: 47 | 48 | https://github.com/gotgithub.private.atom?token=681a8a5a38419ecfb80f8633d4cb4e16 49 | 50 | 原来RSS订阅用到了API Token进行身份认证,即保障了个人RSS的私密性,又避免直接\ 51 | 使用明文口令导致的密码泄露。关于API Token,参见本章\ :ref:`第2.1节中相关介绍 `\ 。 52 | -------------------------------------------------------------------------------- /06-side-projects/github-mac.rst: -------------------------------------------------------------------------------- 1 | .. _mac: 2 | 3 | github:mac 4 | ----------------------- 5 | 6 | GitHub专为Mac用户开发了一款图形化客户端应用\ ``github:mac``\ ,在Mac下操作\ 7 | GitHub更简单。软件下载地址: http://mac.github.com/ 。 8 | 9 | ``github:mac`` 可以实现版本库克隆、查看历史、提交、分支管理、与GitHub同步等\ 10 | 功能。图6-12展示的是提交界面,在提交界面中同时显示了变更的差异比较,用户可以\ 11 | 挑选文件中的部分变更进行提交,显然这个操作要比在命令行中执行\ 12 | :command:`git add --patch`\ 或\ :command:`git commit --patch`\ 要更加直观。 13 | 14 | .. figure:: /images/side-projects/mac-partial-commit.png 15 | :scale: 100 16 | 17 | 图6-12:筛选文件中的部分更改进行提交 18 | 19 | ``github:mac``\ 和GitHub深度集成,当配置好关联的GitHub账号后,会自动在本地\ 20 | 创建专用的SSH公钥/私钥对文件 :file:`~/.ssh/github_rsa` (如果该文件不存在的\ 21 | 话),然后将公钥文件传递到GitHub网站并自动完成配置。新增的SSH公钥文件显示在\ 22 | GitHub网站的账号设置中,如图6-13所示: 23 | 24 | .. figure:: /images/side-projects/mac-new-ssh-pubkey.png 25 | :scale: 100 26 | 27 | 图6-13:在GitHub上自动添加的SSH公钥 28 | 29 | 同时\ ``github:mac``\ 还在本地将新生成的私钥文件添加到\ ``ssh-agent``\ 30 | 认证代理中,这样一旦通过 SSH 协议连接GitHub,首先采用该公钥/私钥对进行身份\ 31 | 认证。用下面的命令可以查看添加到\ ``ssh-agent``\ 中的私钥文件。 32 | 33 | :: 34 | 35 | $ ssh-add -l 36 | 2048 aa:01:4f:d2:14:ba:5f:9f:8c:dc:b5:9d:44:cd:8e:18 /Users/jiangxin/.ssh/github_rsa (RSA) 37 | 38 | 这种透明的公钥认证管理非常酷,对于大多数只使用唯一一个GitHub账号的用户来说\ 39 | 是非常方便的。但如果用户拥有多个GitHub账号并需要不时切换账号,这种实现却很\ 40 | 糟糕,会导致认证错误。因为当\ ``ssh-agent``\ 认证代理缓存了私钥后,连接由\ 41 | 文件 :file:`~/.ssh/config` 设置的 SSH 别名主机无法使用指定的公钥/私钥对进行\ 42 | 认证,导致认证失败。 43 | 44 | 遇到 GitHub 账户 SSH 认证问题,可以运行下面命令清空\ ``ssh-agent``\ 缓存的私钥。 45 | 46 | :: 47 | 48 | $ ssh-add -d ~/.ssh/github_rsa 49 | Identity removed: /Users/jiangxin/.ssh/github_rsa (/Users/jiangxin/.ssh/github_rsa.pub) 50 | -------------------------------------------------------------------------------- /06-side-projects/short-url.rst: -------------------------------------------------------------------------------- 1 | .. _short-url: 2 | 3 | GitHub短网址服务 4 | =================== 5 | 6 | 在“第2.2节 浏览托管项目”一节介绍图形文件差异比较时,需要给出一个网址,但\ 7 | 这个网址很长。如下: 8 | 9 | :: 10 | 11 | https://github.com/cameronmcefee/Image-Diff-View-Modes/commit/8e95f70c9c47168305970e91021072673d7cdad8 12 | 13 | 很自然地想到了Google短网址服务,于是由上面的长网址生成出一个短小精干的网址:\ 14 | http://goo.gl/Gy85b\ ,访问该短网址会自动重定向到对应的长网址。 15 | 16 | 2011年11月,GitHub也推出了自己的短网址服务\ [#]_\ ,为GitHub自身网址提供\ 17 | 短网址转换服务。GitHub短网址服务没有像Google短网址服务那样提供基于Web的图形\ 18 | 化转换界面,而是需要用命令行进行网址转换。 19 | 20 | 例如对于网址 https://github.com/blog/985-git-io-github-url-shortener 的转换,\ 21 | 使用\ ``curl``\ 命令如下操作。 22 | 23 | * 将长网址转换为短网址。 24 | 25 | 命令\ ``curl``\ 输出中的\ ``Location:``\ 语句即是转换后的短网址。 26 | 27 | :: 28 | 29 | $ curl -i http://git.io -F 'url=https://github.com/blog/985-git-io-github-url-shortener' 30 | ... 31 | HTTP/1.1 201 Created 32 | ... 33 | Location: http://git.io/help 34 | 35 | * 查看短网址对应的原网址,同样使用\ ``curl``\ 命令。 36 | 37 | 命令\ ``curl``\ 输出302重定向地址即为原始网址。 38 | 39 | :: 40 | 41 | $ curl -i http://git.io/help 42 | HTTP/1.1 302 Found 43 | ... 44 | Location: https://github.com/blog/985-git-io-github-url-shortener 45 | 46 | 为使转换的短网址更易于记忆和识别,可在\ ``curl``\ 命令中用 code 参数设定期望的\ 47 | 短网址。例如下面命令将本节一开始提到的长网址转换为短网址:\ 48 | http://git.io/image-diff\ 。 49 | 50 | :: 51 | 52 | $ curl -i http://git.io -F \ 53 | 'url=https://github.com/cameronmcefee/Image-Diff-View-Modes/commit/8e95f70c9c47168305970e91021072673d7cdad8' \ 54 | -F 'code=image-diff' 55 | ... 56 | HTTP/1.1 201 Created 57 | ... 58 | Location: http://git.io/image-diff 59 | 60 | 61 | .. [#] http://git.io/help 62 | -------------------------------------------------------------------------------- /01-explore-github/020-github-hightlights.rst: -------------------------------------------------------------------------------- 1 | .. _github-hightlights: 2 | 3 | GitHub亮点 4 | =============== 5 | 6 | 是什么让GitHub如此成功?GitHub有什么魔力? 7 | 8 | 1. 只用Git。 9 | 10 | GitHub只支持Git格式的版本库托管,而不像其他开源项目托管平台还对CVS、SVN、\ 11 | Hg等格式的版本库进行托管。GitHub的哲学很简单,既然Git是最好的版本控制系统\ 12 | 之一(对于很多喜欢Git和GitHub的人没有之一),没有必要为兼顾其他版本控制\ 13 | 系统而牺牲Git某些独有特性。因此没有支持其他版本控制系统的历史负担,\ 14 | 是GitHub成功的要素之一。 15 | 16 | 只用Git并不是说GitHub完全无视其他版本控制系统的使用者,相反,GitHub面向\ 17 | SVN(Subversion)用户和Hg(Mercurial)用户开发了接口,让这些用户可以使用\ 18 | SVN或Hg的客户端工具访问Git版本库。 19 | 20 | 2. 对Git的完整支持。 21 | 22 | 相比其他开源项目托管平台,GitHub对Git版本库提供了完整的协议支持,支持HTTP\ 23 | 智能协议、Git-daemon、SSH协议。相比只支持HTTP协议的GoogleCode,GitHub\ 24 | 通过SSH协议可以实现版本库访问的无口令认证\ [#]_\ 。 25 | 26 | 3. 无处不在的Git。 27 | 28 | 除了在版本库托管上使用Git,Git还被GitHub应用到更多领域。维基使用Git,\ 29 | 可以通过克隆维基所在的版本库,离线修改维基;在线粘贴数据的Gist网站\ [#]_\ 30 | 使用Git,记录变更历史;以及在Jekyll应用的帮助下,用Git版本库维护个人网站\ 31 | 和博客等。 32 | 33 | 4. 在线编辑文件。 34 | 35 | GitHub提供了在线编辑文件的功能,不熟悉Git的用户也可以直接通过浏览器修改\ 36 | 版本库里的文件。 37 | 38 | 5. 社交编程。 39 | 40 | 将社交网络引入项目托管平台是GitHub的创举。用户可以关注项目、关注其他用户\ 41 | 进而了解项目和开发者动态。项目的派生(Fork)和拉拽请求(Pull Request)\ 42 | 构成GitHub最独具一格的工作模式。对提交代码的逐行评注及Pull Request构成了\ 43 | GitHub特色的代码审核。 44 | 45 | 6. 商业上的成功。 46 | 47 | GitHub通过私有版本库托管、面向企业的版本库托管和项目管理平台、人员招聘等\ 48 | 付费服务获得了商业上的成功,这种成功使得GitHub不必以页面中嵌入广告的方式\ 49 | 维持运营,最大的受益者还是用户。 50 | 51 | 7. 关注细节。 52 | 53 | GitHub网站采用了Ruby on Rails架构,在Web设计中运用了大量的JavaScript、\ 54 | AJAX、HTML5等技术,支持对使用Markdown等标记语言的内容进行渲染和显示等。\ 55 | 关注细节使得GitHub成为了项目托管领域的后起之秀。 56 | 57 | ---- 58 | 59 | .. [#] 实际上使用HTTP协议也可以免口令输入。即通过文件\ ``~/.netrc``\ 写入\ 60 | HTTP认证的明文口令。具体文件格式参见\ ``ftp``\ 命令MAN手册中相关介绍。 61 | .. [#] https://gist.github.com/ 62 | -------------------------------------------------------------------------------- /03-project-hosting/040-repo-hooks.rst: -------------------------------------------------------------------------------- 1 | .. _hooks: 2 | 3 | 版本库钩子扩展 4 | ================== 5 | 6 | 通过钩子扩展,GitHub托管的版本库可以和外部应用实现整合。整合的接口完全开放,\ 7 | 开发者可以访问GitHub的开源项目 `github/github-services`_ 开发新的应用整合脚本。\ 8 | 目前GitHub已经支持超过50个外部应用的整合,在这里恕不一一列举,仅以\ 9 | ``helloworld``\ 项目为例,介绍几个常见应用的整合。 10 | 11 | .. _github/github-services: https://github.com/github/github-services 12 | 13 | .. _mail-notify-hook: 14 | 15 | 邮件通知功能 16 | -------------- 17 | 18 | 配置邮件通知,可以实现新提交推送至版本库时,发送通知邮件。在版本库的管理界面,\ 19 | 选择“Service Hooks”中的Email进入邮件通知配置界面,如图3-15所示。配置界面很简单,\ 20 | 写上邮件地址,选择激活即可。为了便于整个团队都能收到通知邮件,可以将收件地址\ 21 | 设置为一个邮件列表。如果选择“Send From Author”,邮件的发件者显示为提交者的\ 22 | 邮件地址,否则发件者为\ ``noreply@github.com``\ 。 23 | 24 | .. figure:: /images/project-hosting/hooks-email.png 25 | :scale: 100 26 | 27 | 图3-15:邮件通知功能配置 28 | 29 | 邮件通知配置生效后,当有新提交推送到版本库时,会发出通知邮件,如图3-16所示。 30 | 31 | .. figure:: /images/project-hosting/mail-in-gg.png 32 | :scale: 100 33 | 34 | 图3-16:提交触发邮件通知 35 | 36 | .. _redmine-hook: 37 | 38 | 和Redmine整合 39 | --------------- 40 | 41 | Redmine是一个开源的项目管理平台,用于项目的需求管理和缺陷跟踪。Redmine可以和\ 42 | 多种版本库(包括Git)整合,可以直接通过Web界面浏览Git提交,还实现了提交和问题\ 43 | 的关联。 44 | 45 | Redmine需要周期性地扫描版本库,以便更新内置数据库及建立提交和问题的关联。\ 46 | 通常是以计划任务(crontab)的方式实现版本库的周期性扫描,这导致Redmine中\ 47 | 版本库更新会存在一定的延迟。GitHub提供的Redmine整合的钩子脚本能够在GitHub\ 48 | 版本库更新后,通过WebService触发Redmine主动扫描Git版本库获取更新。 49 | 50 | GitHub提供的Redmine整合的配置界面如图3-17所示。 51 | 52 | .. figure:: /images/project-hosting/hooks-redmine.png 53 | :scale: 100 54 | 55 | 图3-17:与Redmine整合 56 | 57 | 图3-17中的地址是Redmine部署的URL地址,项目ID是Redmine中的相关项目(如果不填写\ 58 | 则更新所有项目),而“Api Key”并非GitHub项目中配置的Api Key,而是Redmine中为\ 59 | 版本库更新配置的全局Api Key。相应的Redmine配置界面如图3-18所示。 60 | 61 | .. figure:: /images/project-hosting/redmine-api-key.png 62 | :scale: 100 63 | 64 | 图3-18:Redmine中的API Key配置 65 | 66 | -------------------------------------------------------------------------------- /01-explore-github/030-explore-github.rst: -------------------------------------------------------------------------------- 1 | .. _explore-github: 2 | 3 | 探索GitHub 4 | =============== 5 | 6 | 打开浏览器,访问网址 https://github.com/\ ,来探索GitHub吧。GitHub的首页\ 7 | (图1-5所示)特意给出了Git和GitHub的音标,可能不少国人需要据此校准一下\ 8 | Git的读音\ [#]_\ 。 9 | 10 | .. figure:: /images/explore-github/github-homepage.png 11 | :scale: 100 12 | 13 | 图1-5:GitHub的首页 14 | 15 | 16 | 在首页的右上角是导航条,从左至右分别是:注册和收费方案、探索GitHub、功能、\ 17 | 博客和登录。还醒目地显示出不断增长着的注册用户数和托管的版本库数目。 18 | 19 | 20 | 如果想要了解GitHub上哪些项目最热门,进而寻找到好的开源产品,那么可以从导航条\ 21 | 中的“Explore GitHub”开始。图1-6显示通过对社交数据的分析得到的托管版本库动态趋势。 22 | 23 | .. figure:: /images/explore-github/explore-trends.png 24 | :scale: 100 25 | 26 | 图1-6:版本库动态趋势 27 | 28 | 还可以根据感兴趣的人数、建立分支的数量、关注程度等寻找热门项目。图1-7显示\ 29 | 分支最多的项目是Homebrew —— 一款用ruby开发的苹果Mac OS X通用的非官方包管理\ 30 | 软件。考虑到不断攀升的苹果用户数量以及易于上手的ruby语言,这并不奇怪。 31 | 32 | .. figure:: /images/explore-github/explore-repositories.png 33 | :scale: 100 34 | 35 | 图1-7:热门版本库排行 36 | 37 | 图1-8显示了托管版本库所用编程语言的动态分布,程序员多掌握几个热门编程语言\ 38 | 一定会对找工作有帮助。;-) 39 | 40 | .. figure:: /images/explore-github/explore-languages.png 41 | :scale: 100 42 | 43 | 图1-8:托管项目的编程语言统计 44 | 45 | 46 | GitHub通过屏幕截图等方式介绍了GitHub的常见功能,可以通过点击导航条中的\ 47 | “Features”访问到。如图1-9可以看到在项目管理中,如何利用GitHub提供的团队管理\ 48 | 功能、维基、缺陷追踪以及代码审核。 49 | 50 | .. figure:: /images/explore-github/features-pm.png 51 | :scale: 100 52 | 53 | 图1-9:GitHub功能介绍 54 | 55 | .. _blog: 56 | 57 | 博客也是了解GitHub的一个重要的途径,可以获知GitHub的最新动态,如最新改进等。\ 58 | 图1-10显示的是GitHub在感恩节推出的促销活动:收费服务免费试用一个月!\ [#]_\ 59 | 如果及时关注博客就不会错过噢。 60 | 61 | .. _octocat: 62 | 63 | .. figure:: /images/explore-github/blog.png 64 | :scale: 100 65 | 66 | 图1-10:GitHub博客 67 | 68 | 图1-10的博客中一个由小章鱼和小猫组合而成的吉祥物,名字叫做Octocat。这个可爱的\ 69 | GitHub吉祥物时不时会出来带给你惊喜。 70 | 71 | 马上到GitHub上注册,开始GitHub之旅。 72 | 73 | ---- 74 | 75 | .. [#] 《Git权威指南》第1页就提到了两种常见的对Git的读音错误。 76 | .. [#] http://git.io/N9WJ5w 77 | -------------------------------------------------------------------------------- /03-project-hosting/030-repo-authz.rst: -------------------------------------------------------------------------------- 1 | .. _pubkey-mgmt: 2 | 3 | 公钥认证管理 4 | ================ 5 | 6 | 开发者向GitHub版本库写入最常用到的协议是SSH协议,因为SSH协议使用公钥认证,\ 7 | 可以实现无口令访问,而若使用HTTPS协议每次身份认证时都需要提供口令\ [#]_\ 。\ 8 | 使用SSH公钥认证,就涉及到公钥的管理。 9 | 10 | .. _pubkeys: 11 | 12 | 用户级公钥管理 13 | --------------- 14 | 15 | 开发者可能会从不止一台电脑访问GitHub中的版本库(用SSH协议),因不同的电脑有\ 16 | 不同的公钥/私钥对,这就需要为GitHub账号添加多个公钥。点击账号设置中的\ 17 | “SSH Public Keys”进入SSH公钥管理界面,如图3-11所示。 18 | 19 | .. figure:: /images/project-hosting/ssh-public-keys.png 20 | :scale: 100 21 | 22 | 图3-11:SSH多公钥管理 23 | 24 | 如图3-11,在创建gotgithub账号一开始,就手工添加了名为“My Mac OS X”的公钥,\ 25 | 显然这是为苹果电脑准备的。图中正在添加的名为“Key on Windows”是为Windows环境\ 26 | 下使用SSH协议访问GitHub准备的公钥。 27 | 28 | 当添加了新的公钥后,无论是从哪台电脑(苹果或PC)用SSH协议访问版本库时都拥有\ 29 | 相同授权,即都是以gotgithub账号身份来访问。例如用户级公钥访问GitHub的SSH服务,\ 30 | 在提示信息中会显示用户ID,如下: 31 | 32 | :: 33 | 34 | $ ssh -T git@github.com 35 | Hi gotgithub! You've successfully authenticated, but GitHub does not provide shell access. 36 | 37 | .. _deploy-keys: 38 | 39 | 项目级公钥管理 40 | ------------------ 41 | 42 | 多增加一个用户级别的公钥,就意味着可以从另外一台电脑访问该用户所有版本库。\ 43 | 但有时只希望从某台电脑上向某一个版本库“写入”,其他版本库则不可写,这可以通过\ 44 | 设置版本库级别的公钥认证实现。 45 | 46 | 以项目管理者(创建者)身份登录GitHub,例如以gotgithub用户身份访问\ 47 | ``gotgithub/helloworld``\ 版本库,进入到项目的管理页面,选择菜单中的\ 48 | “Deploy Keys”,即可设置项目级别公钥。如图3-12所示。 49 | 50 | .. figure:: /images/project-hosting/deploy-keys.png 51 | :scale: 100 52 | 53 | 图3-12:项目级公钥管理 54 | 55 | 就像一个用户可以设置多个用户级公钥一样,也可以为一个项目设置多个项目级公钥。\ 56 | 无论是项目级公钥还是用户级公钥都有同样的限制:一个公钥只能使用一次。 57 | 58 | 当使用项目级公钥访问GitHub的SSH服务,会在提示信息中显示版本库ID而非用户ID。\ 59 | 如下的命令输出中显示了版本库ID\ ``gotgithub/helloworld``\ 。 60 | 61 | :: 62 | 63 | $ ssh -i ~/.ssh/deploy-key -T git@github.com 64 | Hi gotgithub/helloworld! You've successfully authenticated, but GitHub does not provide shell access. 65 | 66 | ---- 67 | 68 | .. [#] 可以通过在文件\ ``~/.netrc``\ 中写入明文口令实现使用 HTTPS 协议时也能\ 69 | 自动完成认证。具体格式参见\ ``ftp``\ 命令的MAN手册中相关介绍。 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 这是一本关于GitHub的书,在线阅读请访问: 。 2 | 3 | 动笔写GitHub不是因为我对其了解,恰恰是对其太不了解。在我写的 [《Git权威指南》][gotgit] 一书中,涉及到GitHub的只有区区三页纸,这显然回答不了读者对于GitHub的诸多疑问。 4 | 5 | 这本书采用Creative Commons协议发布,并托管在GitHub上,意味着您可以免费阅读并可以用GitHub特有的方式参与本书的维护。 6 | 7 | ## 如何离线阅读 8 | 9 | 项目 [gotgit/gotgithub](https://github.com/gotgit/gotgithub) 的版本库中的 ``gh-pages`` 分支保存着本书编译后的页面,意味着您只要下载版本库并检出 ``gh-pages`` 分支即可在本地浏览。 10 | 11 | * 克隆版本库。 12 | 13 | $ git clone git://github.com/gotgit/gotgithub.git 14 | 15 | * 检出 ``gh-pages`` 分支。 16 | 17 | $ cd gotgithub 18 | $ git checkout gh-pages 19 | 20 | * 用浏览器打开 ``index.html`` 即可离线阅读。 21 | 22 | 因分支 ``gh-pages`` 的提交历史可能会周期性删除或压缩合并,为避免执行 ``git pull`` 更新分支时造成困惑,请对本地版本库进行如下设置。 23 | 24 | $ git config --add remote.origin.fetch +refs/heads/gh-pages:refs/heads/gh-pages 25 | 26 | ## 如何编译 27 | 28 | ### 预备 29 | 30 | * Python, docutils 31 | 32 | 本书使用 [reStructuredText](http://docutils.sourceforge.net/rst.html) 格式撰写,格式解析依赖 Python 和 docutils 包。 33 | 34 | * Sphinx 35 | 36 | 用 [Sphinx](http://sphinx.pocoo.org/) 工具进行编译。编译前先确认已经安装 Python、docutils 及 sphinx。 37 | 38 | * ImageMagick 及 Inkscape 39 | 40 | 本书图片矢量图采用 [Inkscape](http://inkscape.org/) 绘制,位图处理采用 [GIMP](http://www.gimp.org/) 。上述格式图片在网页显示需要格式转换,格式转换需用到 ImageMagick 和 Inkscape。 41 | 42 | * Git 43 | 44 | 不解释。 45 | 46 | ### 克隆版本库(本书书稿及图片) 47 | 48 | 本书用两个版本库维护: 49 | 50 | * 书稿版本库: 51 | 52 | https://github.com/gotgit/gotgithub/ 53 | 54 | * 图片版本库: 55 | 56 | https://github.com/gotgit/gotgithub-graphics/ 57 | 58 | 59 | 本书的图片版本库以子模组形式关联到书稿版本库,运行下面命令执行克隆: 60 | 61 | * 若尚未克隆书稿版本库,先克隆书稿版本库。 62 | 63 | $ git clone git://github.com/gotgit/gotgithub.git 64 | $ cd gotgithub 65 | 66 | * 默认检出 ``master`` 分支。如果当前非 ``master`` 分支,执行下面命令检出分支。 67 | 68 | $ git checkout master 69 | 70 | * 通过子模组更新命令克隆子模组版本库(即保存图片的版本库)并检出。 71 | 72 | $ git submodule init 73 | $ git submodule update 74 | 75 | ### 编译书稿 76 | 77 | 确保安装了 Sphinx、ImageMagick、Inkscape。编译本书使用命令: 78 | 79 | $ make html 80 | 81 | 编译后的网页位于 ``_build/html`` 目录下。 82 | 83 | 更多的格式输出参见下面的命令输出: 84 | 85 | $ make 86 | 87 | ## 如何贡献 88 | 89 | 请采用GitHub方式贡献。 90 | 91 | * 创建派生项目。即 Fork。 92 | * 修改您觉得不满意的地方。修改后推送到您创建的分支版本库中。 93 | * 通过 GitHub 向我发送 Pull Request。 94 | 95 | [gotgit]: http://www.worldhello.net/gotgit/ "Got Git" 96 | 97 | -- 蒋鑫, 98 | -------------------------------------------------------------------------------- /_theme/gotgithub/layout.html: -------------------------------------------------------------------------------- 1 | {# 2 | sphinxdoc/layout.html 3 | ~~~~~~~~~~~~~~~~~~~~~ 4 | 5 | Sphinx layout template for the sphinxdoc theme. 6 | 7 | :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | :license: BSD, see LICENSE for details. 9 | #} 10 | {% extends "basic/layout.html" %} 11 | 12 | {# put the sidebar before the body #} 13 | {% block sidebar1 %}{{ sidebar() }}{% endblock %} 14 | {% block sidebar2 %}{% endblock %} 15 | 16 | {% set css_files = css_files + ["/stylesheets/master.css", "/stylesheets/syntax.css", "static/worldhello.css"] %} 17 | 18 | {% block header %} 19 | 33 | {% endblock %} 34 | 35 | {%- block footer %} 36 | 57 | {%- endblock %} 58 | -------------------------------------------------------------------------------- /05-commercial-github/non-free-plans.rst: -------------------------------------------------------------------------------- 1 | .. _github-plans: 2 | 3 | GitHub收费方案 4 | =============== 5 | 6 | 访问网址 https://github.com/plans 可以看到GitHub提供的不同的服务方案列表。 7 | 8 | .. figure:: /images/commercial-products/available-plans.png 9 | :scale: 100 10 | 11 | 图5-1:GitHub服务方案列表 12 | 13 | 图5-1中显示了GitHub的三类(8种)服务方案: 14 | 15 | * 第一类是免费方案。免费用户账号可以创建任意数量的开放式项目(版本库),并且\ 16 | 可以为开放式项目设置任意数量的协同者。 17 | * 第二类是需要付费的个人账号方案。付费的个人账号允许托管私有版本库,即可以\ 18 | 创建只有自己及指定的私有协同者才能够访问的版本库,而其他人不能访问。根据允许\ 19 | 创建的私有版本库数量及私有版本库协同者数量,提供了三种收费标准(7美元/月、\ 20 | 12美元/月 和 22美元/月)。 21 | * 第三类是需要付费的组织账号方案。使用付费的组织账号,可以突破私有项目的\ 22 | 协同者数量限制,并使用更易管理的团队(Team)对项目进行授权。关于如何通过\ 23 | 团队配置授权参见“4.3. 组织和团队”。组织账号的付费标准较个人账号更高\ 24 | (有25美元/月、50美元/月、100美元/月 和 200美元/月),但同时也可以创建更多\ 25 | 的私有版本库和拥有更大的托管空间。 26 | 27 | 用户可以随时升级或降级自己在GitHub上的服务方案。点击菜单中的“Account Settings”\ 28 | 可以看到当前所选方案,如图5-2所示。 29 | 30 | .. figure:: /images/commercial-products/plan-status.png 31 | :scale: 100 32 | 33 | 图5-2:用户所选方案及状态 34 | 35 | 点击图5-2中的“Change plan”按钮,进入到更换GitHub服务方案页面,如图5-3所示。 36 | 37 | .. figure:: /images/commercial-products/change-plan.png 38 | :scale: 100 39 | 40 | 图5-3:更换方案 41 | 42 | 选择适合的收费方案并付款后,即可完成服务方案的升级。 43 | 44 | 当 gotgithub 用户升级为付费账号后,创建新版本库时就可以通过新的选项创建\ 45 | 私有版本库了。即在创建版本库时,如果不选择默认的“Anyone”,而是选择\ 46 | “Only the people I specify”可以创建私有版本库,如图5-4所示。 47 | 48 | .. figure:: /images/commercial-products/new-private-repository.png 49 | :scale: 100 50 | 51 | 图5-4:创建私有版本库 52 | 53 | 通过版本库的管理界面,可以随时将版本库的状态在公开和私有之间切换,如图5-5所示。 54 | 55 | .. figure:: /images/commercial-products/private-repos-settings.png 56 | :scale: 100 57 | 58 | 图5-5:私有版本库管理界面 59 | 60 | 付费账号的公开版本库没有协同者数量上的限制,但是私有版本库却存在协同者数量上\ 61 | 的限制。如图5-6所示,当私有版本库的协同者数量超出所选GitHub付费方案的限额后,\ 62 | 会显示“OVERLIMIT”的警告,不过超出限额的协同者依然可以操作私有版本库。 63 | 64 | .. figure:: /images/commercial-products/add-private-collaborators.png 65 | :scale: 100 66 | 67 | 图5-6:添加私有协同者 68 | 69 | 组织是一类特殊的不能登录的用户账号。如果要对组织账号进行配置,需要先以组织\ 70 | 所有者的用户账号登录,再通过切换上下文的方式访问组织账号。图5-7就是以\ 71 | gotgithub用户账号登录后,切换到 GotGitOrg 组织账号的管理界面。 72 | 73 | .. figure:: /images/commercial-products/org-plan-status.png 74 | :scale: 100 75 | 76 | 图5-7:团队账号的所选方案及状态 77 | 78 | 在图5-7所示的组织账号管理界面中显示了组织账号当前的GitHub方案,点击其中的\ 79 | “Change plan”按钮,显示如图5-8所示界面,可对组织账号的GitHub方案进行升级或降级。 80 | 81 | .. figure:: /images/commercial-products/org-change-plan.png 82 | :scale: 100 83 | 84 | 图5-8:团队账号更换方案 85 | 86 | 为组织账号选择一个付费方案后,就可以在组织的账号下创建私有版本库,并以团队\ 87 | 方式管理该私有版本库的授权。图5-9就是一个私有版本库\ ``GotGitOrg/NonPublicRepo``\ 88 | 的设置界面。 89 | 90 | .. figure:: /images/commercial-products/org-private-repo-settings.png 91 | :scale: 100 92 | 93 | 图5-9:团队的私有版本库设置 94 | -------------------------------------------------------------------------------- /04-work-with-others/040-code-review.rst: -------------------------------------------------------------------------------- 1 | .. _code-comments: 2 | 3 | 代码评注 4 | ============ 5 | 6 | 针对项目的每一次Pull Request就相当于一次代码评审,评审以讨论的形式显示在\ 7 | Pull Request中。 8 | 9 | 在Pull Request中还能够看到对应的提交(一个或多个),并可以直接针对提交进行\ 10 | 代码评注。对于采用集中式协同的项目,即使较少使用 Pull Request,也同样可以\ 11 | 使用代码评注。代码评注会触发通知邮件给项目的开发者。 12 | 13 | 代码评注有两种形式,一种是针对整个提交的评注,另外一种是对代码进行逐行评注。 14 | 15 | .. _commit-comments: 16 | 17 | 提交评注 18 | ------------ 19 | 20 | 查看项目的提交历史,从中选择一个提交,如图4-37所示。 21 | 22 | .. figure:: /images/work-with-others/commit-history.png 23 | :scale: 100 24 | 25 | 图4-37:helloworld项目提交历史 26 | 27 | 如图4-38是查看提交的界面。除了提交说明、提交者信息之外,还显示提交所修改的\ 28 | 文件和改动差异。在查看提交页面的最下方显示一个提交评注对话框,可以在其中写下\ 29 | 评注。评注可以使用 Markdown 语法。 30 | 31 | .. figure:: /images/work-with-others/commit-note.png 32 | :scale: 100 33 | 34 | 图4-38:添加提交评注 35 | 36 | 添加评注后,所评注的提交的作者会收到通知邮件,提醒针对自己的提交有了新的评论。\ 37 | 通知邮件如图4-39所示。 38 | 39 | .. figure:: /images/work-with-others/commit-note-email-notify.png 40 | :scale: 100 41 | 42 | 图4-39:提交评注的通知邮件 43 | 44 | 45 | 通过Web界面可以看到添加在提交下方的评注,并可以撰写新的评注展开讨论。评注者\ 46 | 本人或提交的作者还可以编辑甚至删除评注。如图4-40所示。 47 | 48 | .. figure:: /images/work-with-others/commit-note-admin.png 49 | :scale: 100 50 | 51 | 图4-40:提交评注 52 | 53 | GitHub还支持Git自身提供的评注功能\ [#]_\ ,如图4-41所示的是提交\ 54 | http://git.io/git-notes\ [#]_\ 的评注,这个评注并非通过GitHub添加的,而是由\ 55 | ``git-note``\ 命令提交的评注。这种评注针对一个特定提交只能有一个,GitHub只能\ 56 | 显示不能编辑和删除。关于如何通过命令行查看\ ``git-note``\ 格式的评注,参见\ 57 | 《Git权威指南》第570页“41.5 Git评注”。 58 | 59 | .. figure:: /images/work-with-others/commit-note-by-git-note.png 60 | :scale: 100 61 | 62 | 图4-41:git-note评注 63 | 64 | .. _line-comments: 65 | 66 | 逐行评注 67 | ------------ 68 | 69 | 还是以\ ``gotgithub/helloworld``\ 版本库中的提交为例,看一下GitHub支持的逐行\ 70 | 评注功能,即针对提交中的任意一行添加评注。浏览提交,如图4-42所示,当鼠标置于\ 71 | 任意一行代码时,在该行代码的左侧会显示一个添加注释的图标。 72 | 73 | .. figure:: /images/work-with-others/commit-line-note-btn.png 74 | :scale: 100 75 | 76 | 图4-42:添加逐行评注按钮 77 | 78 | 点击该图标(用于添加逐行评注的图标),会显示如图4-43所示的添加逐行评注对话框。\ 79 | 该评注对话框出现在两行代码之间,在其中写下评注。 80 | 81 | .. figure:: /images/work-with-others/commit-line-note-form.png 82 | :scale: 100 83 | 84 | 图4-43:添加逐行评注 85 | 86 | 添加评注后,项目的开发人员同样会收到邮件通知。针对同一行代码的多次评论按时间\ 87 | 顺序依次显示,图4-44展示了多个行间评注,其中一个评注还使用 Markdown 语法嵌入了\ 88 | 一个图片。 89 | 90 | .. figure:: /images/work-with-others/commit-line-note.png 91 | :scale: 100 92 | 93 | 图4-44:逐行评注和提交评注 94 | 95 | 更有意思的评注可以围观\ ``MrMEEE/bumblebee``\ 项目的一个bug修正提交(被戏称\ 96 | 一个空格引发的惨案)。地址: http://git.io/giant-bug\ [#]_\ 。 97 | 98 | ---- 99 | 100 | .. [#] http://www.kernel.org/pub/software/scm/git/docs/git-notes.html 101 | .. [#] 即网址 https://github.com/ossxp-com/gitdemo-commit-tree/commit/e80aa74 102 | .. [#] 即网址 https://github.com/MrMEEE/bumblebee/commit/a047be85247755cdbe0acce6 103 | -------------------------------------------------------------------------------- /01-explore-github/010-what-is-github.rst: -------------------------------------------------------------------------------- 1 | .. _what-is-github: 2 | 3 | ============== 4 | 什么是GitHub 5 | ============== 6 | GitHub(网址 https://github.com/\ )是一个面向开源及私有软件项目的托管平台,\ 7 | 因为只支持Git作为唯一的版本库格式进行托管,故名GitHub。 8 | 9 | 10 | GitHub的注册用户已经超过百万\ [#]_\ ,托管的版本库数量已超三百万,其中不乏知\ 11 | 名的开源项目,如:Ruby on Rails\ [#]_\ 、Hibernate\ [#]_\ 、phpBB\ [#]_\ 、\ 12 | jQuery\ [#]_\ 、Prototype\ [#]_\ 、Homebrew\ [#]_\ 等。 13 | 14 | GitHub于2008年4月10日正式发布\ [#]_\ ,相比始于1999年的SourceForge\ [#]_\ 和\ 15 | 2005年的GoogleCode\ [#]_\ ,GitHub后来者居上。以2011年的数据从代码提交数量上\ 16 | 看,GitHub已经超越其前辈\ [#]_\ ,如图1-1所示。 17 | 18 | .. figure:: /images/explore-github/survival-of-the-forges.png 19 | :scale: 100 20 | 21 | 图1-1:开源项目托管平台提交数量对照 22 | 23 | 对于一个开源项目,从开发角度讲大体上分为两类人群,一类称为核心开发团队,他们\ 24 | 可以向保存源代码的版本库提交,即对源代码的修改具有最终的决定权。另外一类称为\ 25 | 贡献者,他们不属于核心开发团队,虽然也能看到源代码,但无权向版本库提交修改。 26 | 27 | .. _cvcs-model: 28 | 29 | 采用传统的集中式版本控制系统(如SVN)的开源项目,这两个群体的用户体验都不是\ 30 | 太好。如图1-2所示,项目的贡献者(非核心成员)很不“高兴”,因为他们即便有修改\ 31 | 源代码的能力和渴望,也不能直接向版本库提交,要想成为提交者需要一个很长的建立\ 32 | 信任的过程。然而即便是核心开发团队的成员,体验也不是太好,因为凡是涉及到版本\ 33 | 库的操作(检入、检出、查看日志等)都需要在联网的状态下进行,网络带宽对用户\ 34 | 体验影响相当大。 35 | 36 | .. figure:: /images/explore-github/cvcs-model.png 37 | :scale: 100 38 | 39 | 图1-2:使用集中式版本控制系统 40 | 41 | .. _dvcs-model: 42 | 43 | Git等分布式版本控制系统的出现,彻底颠覆了原有代码管理的组织模式。使用Git,\ 44 | 不再依赖唯一的、集中式的版本库,而是每个开发者本地都拥有一份完整的版本库。\ 45 | Git并不排斥集中式的使用模式,但更倾向于将集中式版本库称为共享版本库。核心\ 46 | 开发团队的成员和贡献者(非核心成员)都可以从共享版本库克隆一份本地版本库,\ 47 | 但只有核心团队成员才可以将自己本地版本库的提交推送到共享版本库上。\ 48 | 如图1-3所示。 49 | 50 | .. figure:: /images/explore-github/dvcs-model.png 51 | :scale: 100 52 | 53 | 图1-3:使用分布式版本控制系统 54 | 55 | 使用Git做版本控制(如图1-3所示),核心开发团队非常“高兴”,因为他们和共享\ 56 | 版本库之间不必一直保持连接状态,诸如查看日志、提交、创建分支等几乎全部操作\ 57 | 都(脱离网络)在本地的版本库中完成。项目贡献者(非核心成员)也不再那么沮丧,\ 58 | 因为版本库人人皆可更改(当然是对本地版本库而言)。稍微让贡献者感到困难的就是\ 59 | 如何将自己对项目的改进被核心开发团队所了解并接纳。Git提供了多种途径,一个方法\ 60 | 是先用\ ``git format-patch``\ 命令将本地提交转换为补丁文件或补丁文件序列,再\ 61 | 通过邮件发送给核心开发团队。另外一个办法就是搭建一个自己专有的共享版本库,\ 62 | 通过邮件创建一个拉拽请求(Pull Request),让核心团队的开发者到自己的版本库来\ 63 | 抓取(Pull)。 64 | 65 | .. _github-model: 66 | 67 | GitHub的出现进一步推动了Git的普及,简化了版本控制的管理和操作流程,为开发者\ 68 | 提供了更好的交流平台。如图1-4所示。 69 | 70 | .. figure:: /images/explore-github/github-model.png 71 | :scale: 100 72 | 73 | 图1-4:GitHub的协同模式 74 | 75 | 使用GitHub,无论是项目的核心开发团队,还是普通的项目贡献者都工作得非常“愉快”。\ 76 | 创建项目变得非常轻松,创建者只需在GitHub上点击一下鼠标即可创建一个新版本库,\ 77 | 通过简单的Web操作即可完成项目授权进而组建项目核心团队。在GitHub中,\ 78 | 非核心团队成员参与项目也很容易。先找到自己希望参与的项目,然后只需在Web\ 79 | 上点击一下鼠标即可在自己的托管空间下创建一个派生(fork)的项目,\ 80 | 并对派生项目的版本库具有读写的完全权限,就好像这个项目原本就是由自己创立的\ 81 | 那样。当贡献者完成开发并向自己派生的版本库推送后,可以通过GitHub的Web界面向\ 82 | 项目的核心开发团队发送一个Pull Request,请求审核。项目的核心团队收到\ 83 | Pull Request后审核代码,审核通过后可以直接通过Web界面执行合并操作接纳贡献者的提交。 84 | 85 | ---- 86 | 87 | .. [#] https://github.com/blog/936-one-million 88 | .. [#] https://github.com/rails/rails 89 | .. [#] https://github.com/hibernate 90 | .. [#] https://github.com/phpbb/phpbb3 91 | .. [#] https://github.com/jquery/jquery 92 | .. [#] https://github.com/sstephenson/prototype 93 | .. [#] https://github.com/mxcl/homebrew 94 | .. [#] https://github.com/blog/40-we-launched 95 | .. [#] http://sourceforge.net/ 96 | .. [#] http://code.google.com/ 97 | .. [#] http://www.slideshare.net/sogrady/survival-of-the-forges 98 | -------------------------------------------------------------------------------- /06-side-projects/hg-git.rst: -------------------------------------------------------------------------------- 1 | .. _hg-git: 2 | 3 | 用Hg操作GitHub 4 | =============== 5 | 6 | Hg(又称Mercurial)和 Git 一样也是一个被广泛使用的分布式版本库控制工具。如果\ 7 | 一个熟悉 Hg 的开发者参与托管在 GitHub 上的项目,大可不必为更换版本控制工具而\ 8 | 苦恼,GitHub 上的一个名为 hg-git\ [#]_\ 的开源项目可以帮上忙。 9 | 10 | 得益于 Hg 的强大的插件扩展机制,安装 hg-git 并将其注册为Hg 插件后可提供Hg\ 11 | 操作 Git 版本库的能力。安装 hg-git 可以直接使用 :program:`easy_install` 命令: 12 | 13 | :: 14 | 15 | $ sudo easy_install hg-git 16 | 17 | 18 | 还可以直接从GitHub上下载hg-git最新代码进行安装: 19 | 20 | :: 21 | 22 | $ curl -L -k -o hg-git.zip https://github.com/schacon/hg-git/zipball/master 23 | $ unzip hg-git.zip 24 | $ cd schacon-hg-git-* 25 | $ sudo easy_install . 26 | 27 | 插件 hg-git 依赖于 Dulwich 项目,如果在安装过程遇到 Dulwich 无法编译,可能\ 28 | 是因为缺乏 C 编译器,或者尚未安装 python-dev 软件包。Dulwich 是一个Python\ 29 | 语言的 Git 实现,因此 hg-git 在运行过程中无需 Git 命令行。 30 | 31 | 和其他 Hg 插件类似,安装完毕后需要修改Hg配置文件(如文件 :file:`~/.hgrc` )\ 32 | 如下,以启用 hg-git 插件以及另外一个必须的 Hg 内置插件 —— bookmarks 插件。 33 | 34 | :: 35 | 36 | [extensions] 37 | bookmarks = 38 | hggit = 39 | 40 | 对 hg-git 安装配置完毕,就可以使用 Hg 操作 Git 版本库了。 41 | 42 | * 克隆 Git 版本库。 43 | 44 | :: 45 | 46 | $ hg clone git://github.com/ossxp-com/hello-world.git 47 | $ cd hello-world 48 | 49 | * Git 版本库的分支转换为 Hg 版本库中的 bookmarks。 50 | 51 | 新克隆的 Hg 版本库默认会更新到最新提交(即 tip 版本),未必处于所需的分支上。\ 52 | 用命令\ :command:`hg bookmarks`\ 显示分支列表,命令\ :command:`hg parents`\ 53 | 显示工作区对应的版本。 54 | 55 | :: 56 | 57 | $ hg bookmarks 58 | helper/master 10:2767ad9d7008 59 | helper/v1.x 8:994c2f0adc0b 60 | master 1:dcd365e3175c 61 | 62 | $ hg parents 63 | 修改集: 12:928384ca1e87 64 | 标签: jx/v1.0-i18n 65 | 标签: tip 66 | 用户: Jiang Xin 67 | 日期: Fri Dec 31 12:12:42 2010 +0800 68 | 摘要: Translate for Chinese. 69 | 70 | * 切换到所需的工作分支(如\ ``master``\ 分支)。 71 | 72 | 用\ `hg update -r`\ 命令切换分支。之后执行\ :command:`hg bookmarks`\ 命令会\ 73 | 看到当前工作分支用星号标识出来。 74 | 75 | :: 76 | 77 | $ hg update -r master 78 | $ hg book 79 | helper/master 10:2767ad9d7008 80 | helper/v1.x 8:994c2f0adc0b 81 | * master 1:dcd365e3175c 82 | 83 | * Git 的里程碑也被记录,并可被 :command:`hg tags` 命令显示。 84 | 85 | :: 86 | 87 | $ hg tags 88 | tip 12:928384ca1e87 89 | jx/v1.0-i18n 12:928384ca1e87 90 | jx/v2.3 10:2767ad9d7008 91 | ... 92 | 93 | * 使用\ :command:`hg pull`\ 命令和\ :command:`hg push`\ 命令可以实现和\ 94 | Git版本库的同步。 95 | 96 | * 有的命令如\ :command:`hg outgoing`\ 可在 1.7 版本的Hg中运行正常,\ 97 | 但对于高版本库的 Hg 存在兼容性问题。 98 | 99 | 实际上 hg-git 插件并非只针对 GitHub 的版本库,而是可以支持任意 Git 版本库\ 100 | 包括本地 Git 版本库。为了提供对 Git 版本库的透明支持,对 Git 版本库的 URL\ 101 | 的写法有特殊要求,即要能够从协议名称区分开 Git 版本库和默认的 Hg 版本库。 102 | 103 | * Git协议: 104 | 105 | `git://example.com[:port]/path/to/repo.git` 106 | 107 | * SSH协议: 108 | 109 | `git+ssh://[user@]example.com[:port]/path/to/repo.git` 110 | 111 | * HTTP协议: 112 | 113 | `git+http://[user@]example.com[:port]/path/to/repo.git` 114 | 115 | * HTTPS协议: 116 | 117 | `git+https://[user@]example.com[:port]/path/to/repo.git` 118 | 119 | * 本地协议: 120 | 121 | `/path/to/repo.git` 122 | 123 | 124 | ---- 125 | 126 | .. [#] https://github.com/schacon/hg-git 127 | -------------------------------------------------------------------------------- /06-side-projects/svn.rst: -------------------------------------------------------------------------------- 1 | .. _svn-github: 2 | 3 | 用SVN操作GitHub 4 | ================= 5 | 2008年4月1日,GitHub宣布推出基于SVN的SVNHub网站,后证实这是一个愚人节玩笑\ [#]_\ 。\ 6 | 2010年愚人节,类似消息再起,可这一次不再是玩笑\ [#]_\ 。即对于GitHub上的\ 7 | 每一个Git版本库,现在都可以用SVN命令进行操作。更酷的是 SVN 版本库使用的是\ 8 | 和 Git 版本库同样的地址\ [#]_\ 。 9 | 10 | 例如用下面的 Git 命令访问本书的 Git 版本库,显示版本库包含的引用。其中分支\ 11 | ``master``\ 用于维护书稿,分支\ ``gh-pages``\ 保存书稿编译后的 HTML 网页用于\ 12 | 在 GitHub 上显示。 13 | 14 | :: 15 | 16 | $ git ls-remote --heads https://github.com/gotgit/gotgithub 17 | ce5d3dda9b9ce8ec90def1da10181a094bea152f refs/heads/gh-pages 18 | c4d370b1b0bafb103de14e104ca18b8c31d80add refs/heads/master 19 | 20 | 如果使用 SVN 命令访问相同的版本库地址,Git 服务器变身为一个 SVN 服务器,\ 21 | 将Git的引用对应为 SVN 风格的分支。如下: 22 | 23 | :: 24 | 25 | $ svn ls https://github.com/gotgit/gotgithub 26 | branches/ 27 | trunk/ 28 | $ svn ls https://github.com/gotgit/gotgithub/branches 29 | gh-pages/ 30 | 31 | SVN 支持部分检出,下面命令将整个主线\ ``trunk``\ (相当于 Git 版本库的\ 32 | ``master``\ 分支)检出。 33 | 34 | :: 35 | 36 | $ svn checkout https://github.com/gotgit/gotgithub/trunk gotgithub 37 | A gotgithub/Makefile 38 | A gotgithub/README.rst 39 | ... 40 | Checked out revision 30. 41 | 42 | 还可以使用 SVN 命令创建分支,即相当于在 Git 版本库中创建新的引用。测试发现\ 43 | GitHub 尚不支持 SVN 远程拷贝创建分支,需要通过本地拷贝再提交的方式创建新分支。\ 44 | 操作如下: 45 | 46 | 1. 为避免检出版本库所有分支过于耗时,在检出时使用\ ``--depth=empty``\ 参数。 47 | 48 | :: 49 | 50 | $ svn checkout --depth=empty \ 51 | https://github.com/gotgit/gotgithub gotgithub-branches 52 | Checked out revision 30. 53 | 54 | 2. 进入到检出目录中,更新出\ ``trunk``\ 和\ ``branches``\ 两个顶级目录。 55 | 56 | :: 57 | 58 | $ cd gotgithub-branches 59 | $ svn up --depth=empty trunk branches 60 | A trunk 61 | Updated to revision 30. 62 | A branches 63 | Updated to revision 30. 64 | 65 | 3. 通过拷贝从主线\ ``trunk``\ 创建分支\ ``branches/svn-github``\ 。 66 | 67 | :: 68 | 69 | $ svn cp trunk branches/svn-github 70 | A branches/svn-github 71 | $ svn st 72 | A + branches/svn-github 73 | 74 | 4. 提交完成分支创建。 75 | 76 | :: 77 | 78 | $ svn ci -m "create branch svn-github from trunk" 79 | Authentication realm: GitHub 80 | Username: gotgithub 81 | Password for 'gotgithub': 82 | Adding branches/svn-github 83 | 84 | Committed revision 31. 85 | 86 | 5. 用 Git 命令可以看到服务器上创建了一个新的同名引用,并且指向和\ ``master``\ 一致。 87 | 88 | :: 89 | 90 | $ git ls-remote --heads https://github.com/gotgit/gotgithub 91 | ce5d3dda9b9ce8ec90def1da10181a094bea152f refs/heads/gh-pages 92 | c4d370b1b0bafb103de14e104ca18b8c31d80add refs/heads/master 93 | c4d370b1b0bafb103de14e104ca18b8c31d80add refs/heads/svn-github 94 | 95 | 下面尝试一下用 SVN 命令在新创建的分支\ ``svn-github``\ 中提交。 96 | 97 | 1. 进入到之前检出完整主线\ ``trunk``\ 的\ ``gotgithub``\ 目录,并将工作区切换\ 98 | 为分支\ ``branches/svn-github``\ 。 99 | 100 | :: 101 | 102 | $ cd ../gotgithub 103 | $ svn switch https://github.com/gotgit/gotgithub/branches/svn-github 104 | At revision 31. 105 | 106 | 2. 修改文件,查看工作区状态。 107 | 108 | :: 109 | 110 | $ svn st 111 | M 06-side-projects/040-svn.rst 112 | 113 | 3. 用 SVN 提交。 114 | 115 | :: 116 | 117 | $ svn ci -m "GitHub svn client support improved. Refs: http://git.io/svn" 118 | Sending 06-side-projects/040-svn.rst 119 | Transmitting file data . 120 | Committed revision 32. 121 | 122 | 4. 同样查看 Git 版本库的更新,会发现\ ``svn-github``\ 分支的指向已和\ 123 | ``master``\ 不同。 124 | 125 | :: 126 | 127 | $ git ls-remote --heads https://github.com/gotgit/gotgithub 128 | ce5d3dda9b9ce8ec90def1da10181a094bea152f refs/heads/gh-pages 129 | c4d370b1b0bafb103de14e104ca18b8c31d80add refs/heads/master 130 | 64b80cb5331e28fdfb896e2ab3085779bf6ca019 refs/heads/svn-github 131 | 132 | ---- 133 | 134 | .. [#] https://github.com/blog/31-back-to-subversion 135 | .. [#] https://github.com/blog/626-announcing-svn-support 136 | .. [#] https://github.com/blog/966-improved-subversion-client-support 137 | -------------------------------------------------------------------------------- /06-side-projects/opensource.rst: -------------------------------------------------------------------------------- 1 | _github-opensource: 2 | 3 | GitHub Open Source 4 | =================== 5 | 6 | GitHub已成为新的开源项目大本营,而且GitHub也将其API开放,并将部分模块开源,\ 7 | 借助社区的力量让GitHub变得更好。 8 | 9 | GitHub大部分的开源项目托管在其官方账号下: https://github.com/github 。 10 | 11 | .. _api: 12 | 13 | API接口 14 | ------- 15 | GitHub通过域名\ ``api.github.com``\ 提供API接口,数据以JSON格式传递。 16 | 17 | 详细的API参考手册参见网址: http://developer.github.com/ 。API手册的版本库\ 18 | 地址: https://github.com/github/developer.github.com 。 19 | 20 | .. _help-project: 21 | 22 | 官方手册 23 | -------- 24 | GitHub官方手册参见 http://help.github.com/ ,使用 Jekyll 维护。 25 | 26 | 项目地址: https://github.com/github/help.github.com 。 27 | 28 | .. _grit: 29 | 30 | Grit 31 | ---- 32 | Grit是Git的Ruby封装和实现,是GitHub调用Git的接口。部分是通过封装对\ ``git``\ 33 | 命令的调用实现的,部分则是纯Ruby实现。 34 | 35 | 项目地址: https://github.com/mojombo/grit 。 36 | 37 | .. _services-project: 38 | 39 | GitHub Services 40 | --------------- 41 | Git版本库推送会触发服务器端\ ``post-receive``\ 钩子脚本。此项目将GitHub的\ 42 | 服务器端钩子脚本开源,用户可以开发针对特定应用的钩子。GitHub还为其他GitHub\ 43 | 应用提供了事件接口,如问题变更、Pull Request、维基页面修改等\ [#]_\ 。 44 | 45 | 项目地址: https://github.com/github/github-services 。 46 | 47 | .. _hobot: 48 | 49 | Hubot 和 Hubot Scripts 50 | ---------------------- 51 | 可以把 hubot\ [#]_\ 看做是GitHub的Siri(最早出现于iPhone 4S 的智能语音助理)\ 52 | 或是新浪微博上的饮水姬\ [#]_\ 。GitHub将hobot和Campfire聊天室整合,\ 53 | hobot被聊天室会话触发可以实现诸如:打开办公室的门、根据wifi使用情况列出公司中的人、\ 54 | 通过公司喇叭读一段信息等等许多好玩的事情\ [#]_\ ,而实现GitHub自动化部署\ 55 | 则证明 hubot 可以完成更严肃的事情,在公司工作流中扮演举足轻重的地位\ [#]_\ 。 56 | 57 | Hobot已经开源,项目库地址:\ https://github.com/github/hubot\ 和\ 58 | https://github.com/github/hubot-scripts\ (脚本)。 59 | 60 | .. _gollum: 61 | 62 | Gollum 63 | ------ 64 | GitHub以Git为后端的维基系统就是由Gollum实现的。每一个维基网页对应于一个文件,\ 65 | 文件格式可以是 Markdown、textile、rdoc、org、creole、mediawiki、\ 66 | reStructuredText、asciidoc、pod 等。Gollum 调用名为github-markup的Ruby gem包\ 67 | (来自于下面要介绍的 Markup 项目)完成文件到网页的格式转换。 68 | 69 | 项目地址: https://github.com/github/gollum 。 70 | 71 | 关于GitHub维基参见本书“第4.6节维基”。 72 | 73 | .. _jekyll-project: 74 | 75 | Jekyll 76 | ------ 77 | Jekyll 是一个简单的、支持博客的静态网站编译器。可以使用Markdown和Textile两种\ 78 | 标记语言或者HTML撰写网页,并支持Liquid模版。实际上GitHub为托管项目生成静态\ 79 | 网页使用的就是Jekyll。 80 | 81 | 项目地址: https://github.com/mojombo/jekyll 。 82 | 83 | .. _linguist: 84 | 85 | Linguist 86 | -------- 87 | Linguist 是一个Ruby模块,GitHub使用该模块对数据文件进行语义分析,检测文件的\ 88 | 语言种类,代码加亮,对二进制文件进行忽略,限制非必须的差异显示,以及生成语言\ 89 | 分类图等。 90 | 91 | 项目地址: https://github.com/github/linguist 。 92 | 93 | .. _markup: 94 | 95 | Markup 96 | ------ 97 | GitHub通过这个ruby包对项目版本库根目录下的\ ``README``\ 文件,以及维基页面\ 98 | 等文件进行解析、转换为网页显示。支持 Markdown、textile、rdoc、org、creole、\ 99 | mediawiki、reStructuredText、asciidoc、pod 等标记语言。实际上在对上述标记语言\ 100 | 的解析和转换中,还依赖其他软件包,例如对于 Markdown 格式首选 Redcarpet\ [#]_\ ,\ 101 | 对 textile 格式使用 RedCloth,对 reStructuredText 格式调用外部命令\ 102 | ``rst2html``\ ,对 asciidoc 格式调用外部命令\ ``asciidoc``\ 等。 103 | 104 | 项目地址: https://github.com/github/markup 。 105 | 106 | 关于Markup软件包以及其他GitHub扩展的Markdown语法,参见:\ 107 | http://github.github.com/github-flavored-markdown\ 。 108 | 109 | .. _resque: 110 | 111 | Resque 112 | ------ 113 | Resque(发音类似 "rescue")是一个以Redis为后端的Ruby包,用于创建和管理后台\ 114 | 任务。可创建任务,将任务分配到多个队列,并在后台执行任务。 115 | 116 | 项目地址: https://github.com/defunkt/resque 。 117 | 118 | .. _gitpad: 119 | 120 | GitPad 121 | ------ 122 | 这是一个运行于Windows下类似\ ``Notepad.exe``\ 的应用程序,安装此应用后在\ 123 | Windows下做Git提交操作会调用类似记事本(Notepad)的应用撰写提交说明。 124 | 125 | 项目地址: https://github.com/github/GitPad 。 126 | 127 | .. _maven-plugins: 128 | 129 | Maven Plugins 130 | ------------- 131 | GitHub的Maven插件。 132 | 133 | 项目地址: https://github.com/github/maven-plugins 。 134 | 135 | .. _gitignore: 136 | 137 | Gitignore 138 | --------- 139 | 集合了针对各种语言环境的\ ``.gitignore``\ (忽略文件)模版。例如其中针对\ 140 | VisualStudio的忽略文件模版\ ``Global/VisualStudio.gitignore``\ 部分内容如下: 141 | 142 | :: 143 | 144 | # User-specific files 145 | *.suo 146 | *.user 147 | *.sln.docstates 148 | 149 | # Build results 150 | [Dd]ebug/ 151 | [Rr]elease/ 152 | 153 | 项目地址: https://github.com/github/gitignore 。 154 | 155 | .. _media-project: 156 | 157 | Media 158 | ------- 159 | 提供GitHub网站Logo和吉祥物 Octocat 的图片,只能在授权范围内使用。 160 | 161 | 项目地址: https://github.com/github/media 。 162 | 163 | 164 | ---- 165 | 166 | .. [#] https://github.com/blog/964-all-of-the-hooks 167 | .. [#] http://hubot.github.com/ 168 | .. [#] http://weibo.com/u/2625288792 169 | .. [#] http://zachholman.com/posts/why-github-hacks-on-side-projects/ 170 | .. [#] http://scottchacon.com/2011/08/31/github-flow.html#6__deploy_immediately_after_review 171 | .. [#] Redcarpet 是对一个高效的Markdown解析器,通过对C语言的 Sundown 库封装实现。\ 172 | 项目地址:\ https://github.com/tanoku/redcarpet\ 。 173 | -------------------------------------------------------------------------------- /03-project-hosting/010-new-project.rst: -------------------------------------------------------------------------------- 1 | .. _new-project: 2 | 3 | 创建新项目 4 | =============== 5 | 6 | .. _new-repo: 7 | 8 | 新版本库即是新项目 9 | ---------------------- 10 | 11 | 在GitHub,一个项目对应唯一的Git版本库,创建一个新的版本库就是创建一个新的项目。\ 12 | 访问仪表板(Dashboard)页面,如图3-1,可以看到关注的版本库中已经有一个,\ 13 | 但自己的版本库为零。在显示为零的版本库列表面板中有一个按钮“New Repository”,\ 14 | 点击该按钮开始创建新版本库。 15 | 16 | .. figure:: /images/project-hosting/new-repo-btn.png 17 | :scale: 100 18 | 19 | 图3-1:版本库列表面板 20 | 21 | 新建版本库的界面如图3-2所示。 22 | 23 | .. figure:: /images/project-hosting/new-project.png 24 | :scale: 100 25 | 26 | 图3-2:创建新项目 27 | 28 | 我们为新建立的版本库命名为“helloworld”,\ 29 | 相应的项目名亦为“helloworld”,创建完毕后访问项目页,提示版本库\ 30 | 尚未初始化,并给出如何初始化版本库的帮助,如图3-3所示。 31 | 32 | .. figure:: /images/project-hosting/project-uninitial.png 33 | :scale: 100 34 | 35 | 图3-3:项目尚未初始化 36 | 37 | 在图3-3中可以看到访问协议增加了一个支持读写的SSH协议,访问地址为:\ 38 | ``git@github.com:gotgithub/helloworld.git``\ 。注意任何GitHub用户均可使用该\ 39 | URL访问此公开版本库,但只有版本库建立者gotgithub具有读写权限,其他人\ 40 | 只有只读权限。在初始化版本库之前,最好先确认是否是用正确的公钥进行认证,如下: 41 | 42 | :: 43 | 44 | $ ssh -T git@github.com 45 | Hi gotgithub! You've successfully authenticated, but GitHub does not provide shell access. 46 | 47 | .. _init-by-clone: 48 | 49 | 版本库初始化 50 | -------------- 51 | 52 | 如果是从头创建版本库,可以采用先克隆,建立提交数据,最后再通过推送完成GitHub\ 53 | 版本库的初始化。步骤如下: 54 | 55 | * 克隆版本库。 56 | 57 | 克隆过程会显示警告,不过这个警告可以忽略,因为GitHub创建的版本库本来就是一个\ 58 | 空白的版本库。 59 | 60 | :: 61 | 62 | $ git clone git@github.com:gotgithub/helloworld.git 63 | Cloning into 'helloworld'... 64 | warning: You appear to have cloned an empty repository. 65 | 66 | * 创建文件\ ``README.md``\ [#]_\ 。 67 | 68 | 下面是一段示例文字,把这段文字保存为文件\ ``README.md``\ ,该文件的内容将会\ 69 | 直接显示在项目首页中(显示效果参见后面的图3-5)。 70 | 71 | :: 72 | 73 | # 我的第一个GitHub项目 74 | 75 | 这是项目 [helloworld](https://github.com/gotgithub/helloworld) , 76 | 欢迎访问。 77 | 78 | 这个项目的版本库是 **Git格式** ,在 Windows、Linux、Mac OS X 79 | 平台都有客户端工具可以访问。虽然版本库只提供Git一种格式, 80 | 但是你还是可以用其他用其他工具访问,如 ``svn`` 和 ``hg`` 。 81 | 82 | ## 版本库地址 83 | 84 | 支持三种访问协议: 85 | 86 | * HTTP协议: `https://github.com/gotgithub/helloworld.git` 。 87 | * Git协议: `git://github.com/gotgithub/helloworld.git` 。 88 | * SSH协议: `ssh://git@github.com/gotgithub/helloworld.git` 。 89 | 90 | ## 克隆版本库 91 | 92 | 操作示例: 93 | 94 | $ git clone git://github.com/gotgithub/helloworld.git 95 | 96 | 上面这段文字采用Markdown格式,您也可以使用其他支持的格式,只要确保\ ``README``\ 97 | 文件使用正确的扩展名。本书附录部分介绍了Markdown及其他GitHub支持的标记语言。\ 98 | 关于Markdown,目前我们只需知道这一个易于识别和理解的纯文本格式,可以方便的\ 99 | 转换为HTML。Markdown语法非常像我们在写邮件(纯文本)时用空行来分隔段落、\ 100 | 用星号开启列表、用缩进表示引用内容等等。 101 | 102 | * 添加\ ``README.md``\ 文件并提交。 103 | 104 | :: 105 | 106 | $ git add README.md 107 | $ git commit -m "README for this project." 108 | 109 | * 向GitHub推送,完成版本库初始化。 110 | 111 | :: 112 | 113 | $ git push origin master 114 | 115 | 然后查看GitHub上新建项目的首页。项目首页的上半部分可见版本库包含了一个新的\ 116 | 提交,以及版本库目录树中包含的文件,如图3-4所示。 117 | 118 | .. figure:: /images/project-hosting/project-pushed-head.png 119 | :scale: 100 120 | 121 | 图3-4:完成推送后的项目首页上半部分 122 | 123 | 在项目首页的下半部分,会看到\ ``README.md``\ 文件被转换为HTML显示,如图3-5所示。 124 | 125 | .. figure:: /images/project-hosting/project-pushed-tail.png 126 | :scale: 100 127 | 128 | 图3-5:完成推送后的项目首页下半部分 129 | 130 | .. _init-by-push: 131 | 132 | 从已有版本库创建 133 | ----------------- 134 | 135 | 如果在GitHub项目初始化之前,数据已经存在于本地版本库中,显然像上面那样先克隆、\ 136 | 再提交、后推送的方法就不适宜了。应该采用下面的方法。 137 | 138 | 为试验新的版本库初始化方法,先把刚刚新建的测试项目“helloworld”删除,\ 139 | 同时也将本地工作区中克隆的“helloworld”删除。\ 140 | 警告:删除项目的操作非常危险,不可恢复,慎用。 141 | 142 | * 点击项目首页中项目名称旁边的“Admin”按钮进入项目管理页,再点击页面最下方的\ 143 | 删除版本按钮,如图3-6所示。 144 | 145 | .. figure:: /images/project-hosting/project-delete.png 146 | :scale: 100 147 | 148 | 图3-6:删除项目 149 | 150 | * 然后再重建版本库“helloworld”,如本章一开始图3-2所示。 151 | 152 | 接下来使用下面的步骤完成“helloworld”版本库的初始化。 153 | 154 | * 本地建立一个Git版本库。 155 | 156 | :: 157 | 158 | $ mkdir helloworld 159 | $ cd helloworld 160 | $ git init 161 | 162 | * 然后在版本库中添加示例文件,如\ ``README.md``\ 文件,内容同前。 163 | 164 | :: 165 | 166 | $ git add README.md 167 | $ git commit -m "README for this project." 168 | 169 | * 为版本库添加名为\ ``origin``\ 的远程版本库。 170 | 171 | :: 172 | 173 | $ git remote add origin git@github.com:gotgithub/helloworld.git 174 | 175 | * 执行推送命令,完成GitHub版本库的初始化。注意命令行中的\ ``-u``\ 参数,在推送\ 176 | 成功后自动建立本地分支与远程版本库分支的追踪。 177 | 178 | :: 179 | 180 | $ git push -u origin master 181 | 182 | ---- 183 | 184 | .. [#] 以扩展名\ ``.md``\ ,\ ``.mkd``\ ,\ ``.mkdn``\ ,\ ``.mdown``\ ,\ ``.markdown``\ 185 | 等为结尾的文件,均以Markdown标记语言语法进行解析并显示。 186 | -------------------------------------------------------------------------------- /04-work-with-others/030-organization.rst: -------------------------------------------------------------------------------- 1 | .. _organizations: 2 | 3 | 组织和团队 4 | =============== 5 | 6 | GitHub 在早期没有专门为组织提供账号,很多企业用户或大型开源组织只好使用普通用户\ 7 | 账号作为组织的共享账号来使用。后来,GitHub推出了组织这一新的账号管理模式,满足大型\ 8 | 开发团队的需要。 9 | 10 | * 组织账号是不能用来登录的,它包含一个Owner(拥有者)用户组,只有属于这个组的\ 11 | 用户在登录后,才能切换为组织的管理者。 12 | * 可以创建任意多的团队(Team)即角色,对属于组织的用户进行管理。Owner Team\ 13 | 就是组织中权限最高的角色。 14 | * 组织和用户一样可以创建项目,但是组织没有SSH公钥配置,也不能以组织的身份操作\ 15 | 版本库。 16 | * 组织没有工作描述之类的个人账号才拥有的属性。 17 | 18 | .. _new-org: 19 | 20 | 创建新组织 21 | -------------- 22 | 23 | 组织是非登录账号,不能像创建普通登录账号那样直接创建,而是需要以GitHub用户\ 24 | 身份登录,然后再创建自己的组织,创建者成为组织天然的管理者。 25 | 26 | 图4-26就是用户gotgithub登录后,通过点击右上角的账号设置图标进入账号设置界面,\ 27 | 然后再点击菜单中的“Organizations”进入组织管理界面。 28 | 29 | .. figure:: /images/organization/new-org-1.png 30 | :scale: 100 31 | 32 | 图4-26:账号设置中的组织管理 33 | 34 | 在初始的组织管理界面中组织列表为空,即尚不属于任何组织。可以选择把当前用户\ 35 | gotgithub的账号转换为一个组织账号(前提是gotgithub的账号不属于任何组织)。\ 36 | 提供这一账号迁移功能是因为在GitHub提供组织这一新功能之前,很多公司或团队以\ 37 | 个人身份创建GitHub账号,但是以组织的形象出现,对于这类账号,GitHub提供了由\ 38 | 个人账号向组织账号迁移的途径。 39 | 40 | 在这里我们不进行这一迁移,而是以用户gotgithub的身份创建一个新的组织。点击\ 41 | “Create New Organization”按钮,显示创建组织表单,如图4-27所示。 42 | 43 | .. figure:: /images/organization/new-org-2.png 44 | :scale: 100 45 | 46 | 图4-27:创建新组织 47 | 48 | 这里填写组织名为 GotGitOrg。创建组织还要选择一个付费方案,默认会选择免费的\ 49 | 没有私有版本库的开源方案。 50 | 51 | 接下来为新建组织设定拥有者(Owner),如图4-28所示。当前用户,即正在创建组织\ 52 | 的用户,理所当然成为组织拥有者之一。还可以为组织指派更多的组织拥有者,多个组织\ 53 | 拥有者的权限并无差别,都可以管理组织,甚至可以将其他用户从拥有者团队中删除。 54 | 55 | .. figure:: /images/organization/new-org-3.png 56 | :scale: 100 57 | 58 | 图4-28:指派组织拥有者 59 | 60 | 完成创建后,访问用户账号设置界面中的组织面板,如图4-29所示,列出当前用户所属\ 61 | 的组织(GotGitOrg)。可以重新对组织进行设定,或者退出组织。注意因为当前用户\ 62 | 已经属于一个以上的组织,所以右侧将当前用户转换为组织的按钮被置灰。 63 | 64 | .. figure:: /images/organization/new-org-4.png 65 | :scale: 100 66 | 67 | 图4-29:加入组织后的组织管理界面 68 | 69 | .. _org-settings: 70 | 71 | 组织管理 72 | ------------------ 73 | 74 | 当用户gotgithub成为新建组织GotGitOrg的一员后,就可以在用户和组织的界面之间切换。\ 75 | 点击页面左上角“github”文字图标进入仪表板界面。 76 | 77 | .. figure:: /images/organization/dashboard-header.png 78 | :scale: 100 79 | 80 | 图4-30:用户仪表板界面 81 | 82 | 仪表板页面左上角“github”文字图标的下面就是用户上下文列表框。点击用户上下文\ 83 | 下拉列表,如图4-31所示。 84 | 85 | .. figure:: /images/organization/dashborad-context.png 86 | :scale: 100 87 | 88 | 图4-31:用户上下文切换列表 89 | 90 | 在用户上下文列表中选择组织GotGitOrg作为用户上下文后,则仪表板中显示的菜单和\ 91 | 个人账号仪表板菜单略有不同,如图4-32所示。 92 | 93 | .. figure:: /images/organization/org-dashboard.png 94 | :scale: 100 95 | 96 | 图4-32:组织GotGitOrg的仪表板界面 97 | 98 | 组织的仪表板界面与用户仪表板的不同之处在于增加了团队管理(Team)和组织管理\ 99 | (Organization Settings)。选择菜单中的“Team”进入团队管理界面,可以在组织中\ 100 | 添加任意数量的团队。添加新团队的界面如图4-33所示。 101 | 102 | .. figure:: /images/organization/new-team.png 103 | :scale: 100 104 | 105 | 图4-33:添加新团队iOS-Team 106 | 107 | 创建一个团队需要提供四个选项(如图4-33): 108 | 109 | 1. 团队名称。如:iOS-Team。 110 | 2. 团队成员。如:supergirl和omnidroid两个用户作为团队成员。 111 | 3. 团队权限。有三个选择:只读(Pull Only)、读写(Push & Pull)、读写并管理\ 112 | (Push, Pull & Administrative)。 113 | 4. 授权版本库。可以添加一个或多个版本库,只有对授权的版本库才拥有指定权限。 114 | 115 | 其中团队授权中的只读授权对于免费组织账号创建的开源项目没有实际意义,因为开源\ 116 | 项目人人可读,只有对于付费的组织账号创建的私密版本库才体现出价值。关于付费账号\ 117 | 和私密版本库将在后面的章节介绍。接下来介绍如何在组织账号下创建版本库。 118 | 119 | .. _org-repo-mgmt: 120 | 121 | 版本库管理 122 | -------------------- 123 | 124 | 组织拥有独立的项目托管空间,点击页面左上角的“github”文字图标进入组织账号的\ 125 | 仪表板界面。刚刚建立的组织账号的版本库尚未创建,点击图4-32所示的“New Repository”\ 126 | 按钮,创建版本库(即项目)。 127 | 128 | 新建版本库的界面如图4-34所示。 129 | 130 | .. figure:: /images/organization/new-prj-for-org.png 131 | :scale: 100 132 | 133 | 图4-34:新建项目界面 134 | 135 | 在组织的托管空间创建项目与在普通用户的空间下创建稍有不同,增加了团队设置\ 136 | 下拉框。图4-34显示在创建名为MyiPad项目时,只能为项目指派一个已定义团队,\ 137 | 要想为项目指派更多团队可以在项目创建完毕通过项目管理界面添加。 138 | 139 | 下面来看一看如何为已建立项目指派更多的团队。进入项目管理页面,点击左侧菜单项\ 140 | “Team”显示项目的团队管理界面,可以通过该界面,为项目添加和移除团队,如图4-35所示。 141 | 142 | .. figure:: /images/organization/org-prj-admin-teams.png 143 | :scale: 100 144 | 145 | 图4-35:项目的团队管理 146 | 147 | 属于团队的项目(版本库)可以转移给个人,反之亦然。图4-36展示了如何通过项目\ 148 | 管理界面在用户和组织之间转移项目(版本库)。 149 | 150 | .. figure:: /images/organization/org-prj-admin-transfer.png 151 | :scale: 100 152 | 153 | 图4-36:项目转移 154 | 155 | .. _pros-of-org: 156 | 157 | 个人还是组织 158 | ---------------------- 159 | 160 | 若使用“Fork + Pull”的工作模式,通过个人账号还是组织账号托管版本库,几乎没有\ 161 | 什么差别。如果一定要找出点不同,那就是在向托管版本库提交Pull Request时,邮件\ 162 | 通知的用户范围有所不同。 163 | 164 | * 对于个人账号,对其托管空间内的版本库发出Pull Request,通知邮件会发送给该\ 165 | 个人账号及该版本库设置的所有协作者(如果有的话)的邮箱。 166 | * 对于组织,对其托管空间内的版本库发出Pull Request,不会向组织的邮箱发送\ 167 | Pull Request,也不会向组织的所有者(Owner团队)发送通知邮件,而是向在版本库中\ 168 | 拥有Push权限的团队(非Owner团队)成员发送通知邮件。 169 | 170 | 因此,如果在组织的托管空间创建版本库,一定要要为版本库指派一个拥有Push权限的\ 171 | 团队,以免以“Fork + Pull”模式工作时,Pull Request没有人响应。 172 | 173 | 若是以共享版本库方式(即集中式协同模式)工作的话,使用组织来托管版本库会比\ 174 | 使用个人账号托管有效率得多。 175 | 176 | * 以个人账号托管,需要逐一为版本库设置协作者(Collaborators),如果版本库较多\ 177 | 且授权相同,配置过程繁琐且易出错。 178 | * 以组织方式托管,将用户分组,划分为一个一个的团队(Team),以团队为单位授权\ 179 | 则方便得多。 180 | * 如果是以付费账号创建的私密版本库,使用组织方式管理,会有包括只读、读写等\ 181 | 更丰富的授权类型,更符合项目管理的实际。 182 | -------------------------------------------------------------------------------- /02-join-github/020-browse-repo.rst: -------------------------------------------------------------------------------- 1 | .. _browse-repo: 2 | 3 | 浏览托管项目 4 | =============== 5 | 6 | 在上一节学习了如何建立GitHub的账户,接下来在建立自己的项目托管之前,\ 7 | 先来看看别人是怎么做的。 8 | 9 | 在GitHub中搜索字符串“GotGit”,可以搜索到我建立的一个项目,项目地址是:\ 10 | https://github.com/gotgit/gotgit/\ 。\ 11 | 由上至下,GotGit项目首页可以分为如下几个区域。 12 | 13 | * 区域一:项目概要介绍及版本库URL等。 14 | 15 | 项目GotGit托管在组织账号 gotgit 之下\ [#]_\ ,并且已经有若干关注用户和派生\ 16 | 项目。最下面一行显示版本库的访问地址,只显示了HTTP和Git-daemon两个协议的\ 17 | URL地址,这是因为当前用户对该版本库只具有只读权限,因此没有显示SSH协议的URL\ 18 | 地址。 19 | 20 | .. figure:: /images/join-github/gotgit-repo-tree-1.png 21 | :scale: 100 22 | 23 | 图2-10:版本库概要信息 24 | 25 | 使用任意一种协议均可克隆该Git版本库,但要注意只有Git 1.6.6及以上版本才支持\ 26 | 智能HTTP协议,低版本Git则无法用HTTP协议克隆GitHub上的版本库\ [#]_\ 。 27 | 28 | :: 29 | 30 | $ git clone https://github.com/gotgit/gotgit.git 31 | 32 | 或者使用Git-daemon协议。 33 | 34 | :: 35 | 36 | $ git clone git://github.com/gotgit/gotgit.git 37 | 38 | * 区域二:代码浏览子菜单及分支切换对话框。 39 | 40 | 默认项目代码页(即项目首页)显示项目文件列表(即\ ``Files``\ 子菜单),如\ 41 | 图2-11所示。右侧还显示项目\ ``gotgit/gotgit``\ 默认的分支为\ ``gh-pages``\ 42 | 而非常见的\ ``master``\ 分支。关于\ ``gh-pages``\ 分支,在“第3.5.2节 :ref:`project-homepage`\ ”\ 43 | 会介绍该分支的神奇用途。 44 | 45 | .. figure:: /images/join-github/gotgit-repo-tree-1-submenu.png 46 | :scale: 100 47 | 48 | 图2-11:代码浏览子菜单及分支 49 | 50 | * 区域三:显示最新提交的提交说明、提交用户头像、提交时间等提交信息。\ 51 | 右侧还显示此次提交对应的提交ID。 52 | 53 | .. figure:: /images/join-github/gotgit-repo-tree-2.png 54 | :scale: 100 55 | 56 | 图2-12:提交信息 57 | 58 | * 区域四:目录树。每个目录和文件后面还显示最后一次变更的提交说明。 59 | 60 | .. figure:: /images/join-github/gotgit-repo-tree-3.png 61 | :scale: 100 62 | 63 | 图2-13:目录树 64 | 65 | * 区域五:根目录下的文件\ ``README.md``\ 格式化为HTML输出。 66 | 67 | GitHub内置了多种文本标记语言的支持,如Markdown、Textile、reStructuredText、\ 68 | asciidoc、Wiki等。当发现根目录下的\ ``README``\ 文件后,会根据其扩展名判断\ 69 | 所用的标记语言类型,自动转换为HTML格式显示。 70 | 71 | .. figure:: /images/join-github/gotgit-repo-tree-4.png 72 | :scale: 100 73 | 74 | 图2-14:README文件 75 | 76 | 在GitHub的页面中可以使用键盘快捷键,按下问号(?)会在弹出窗口显示当前页面可用的快捷键。 77 | 78 | 在项目的代码浏览页按下字母“w”,弹出分支切换菜单,如图2-15所示。 79 | 80 | .. figure:: /images/join-github/gotgit-repo-switch-branch.png 81 | :scale: 100 82 | 83 | 图2-15:快捷键“w”切换分支 84 | 85 | 按下字母“t”,开启目录树中文件查找和过滤。图2-16就是在按下字母“t”后,当逐一输入\ 86 | 单词“download”时的过滤效果。 87 | 88 | .. figure:: /images/join-github/gotgit-repo-tree-filter.png 89 | :scale: 100 90 | 91 | 图2-16:快捷键“t”开启过滤器在目录树中搜索 92 | 93 | 点击代码浏览子菜单中的“Commits”(提交)显示版本库GotGit的提交历史,如图2-17所示。 94 | 95 | .. figure:: /images/join-github/gotgit-repo-commit-history.png 96 | :scale: 100 97 | 98 | 图2-17:提交历史 99 | 100 | 提交历史页面也支持快捷键,按下问号(?)或者点击页面中的键盘标志会显示快捷键\ 101 | 帮助。其中快捷键"j"和"k"用于在提交列表中向上和向下选择提交,在选中的提交按下\ 102 | 回车键,会显示该提交包含的文件改动差异,如图2-18所示。 103 | 104 | .. figure:: /images/join-github/gotgit-repo-commit-diff.png 105 | :scale: 100 106 | 107 | 图2-18:文本文件改动差异 108 | 109 | 在文本文件的差异比较中,不但将有差异的行标识出来,还将行内具体改动的字词用\ 110 | 特殊颜色进行了标识,不由得感叹GitHub的细致入微。 111 | 112 | .. _image-diff: 113 | 114 | GitHub还支持对图形文件的差异比较,并提供四种比较方式。在如下地址:\ 115 | http://git.io/image-diff\ [#]_\ 提供了一个示例提交。您可以去尝试一下不同的图形\ 116 | 文件比较方式,以便更直观地观察图形文件前后的改动。 117 | 118 | * 默认修改前后的两幅图片左右并排显示,如图2-19所示。 119 | 120 | .. figure:: /images/join-github/image-diff-2-up.png 121 | :scale: 100 122 | 123 | 图2-19:左右并排比较图形文件差异 124 | 125 | * 选择交换显示比较修改前后的图片,用鼠标左右拖动进度条,可以非常直观地看到\ 126 | 图片的差异。如图2-20所示。 127 | 128 | .. figure:: /images/join-github/image-diff-swipe.png 129 | :scale: 100 130 | 131 | 图2-20:交换显示图形文件比较差异 132 | 133 | * 还提供洋葱皮和色差比较,自己动手试试吧。 134 | 135 | 网络图是GitHub的一大特色,显示一个项目的版本库被不同用户派生(Fork)后,\ 136 | 各个版本库的派生关系。这个网络图最早使用Flash实现的,目前已经改为HTML5\ 137 | 实现\ [#]_\ 。图2-21的示例网络图来自于Gitosis项目\ [#]_\ 。 138 | 139 | .. figure:: /images/join-github/network-graph.png 140 | :scale: 100 141 | 142 | 图2-21:Gitosis项目网络图 143 | 144 | Pull Requests(拉拽请求)是派生(Fork)版本库的开发者向项目贡献提交的方法。\ 145 | 如图2-22所示,GotGit项目目前没有未被处理的Pull Request,但是可以看到有一个\ 146 | 已经关闭的Pull Request请求。 147 | 148 | .. figure:: /images/join-github/gotgit-repo-pull-request.png 149 | :scale: 100 150 | 151 | 图2-22:Pull Requests界面 152 | 153 | 这个Pull Request是GitHub用户agate发现了GotGit脚本中一个和ruby1.9不兼容的Bug,\ 154 | 当我把agate派生版本库中的提交合并到GotGit版本库后,该Pull Request自动关闭。\ 155 | 整个Pull Request的变更记录如图2-23所示。 156 | 157 | .. figure:: /images/join-github/gotgit-repo-pull-request-detail.png 158 | :scale: 100 159 | 160 | 图2-23:Pull Request的变更历史 161 | 162 | 缺陷追踪(Issue)也是GitHub工作流中一个重要的组件。GotGit项目用缺陷跟踪系统\ 163 | 帮助维护《Git权威指南》一书的勘误,图2-24可以看到当前有2个打开的问题和9个\ 164 | 已关闭的问题。 165 | 166 | .. figure:: /images/join-github/gotgit-repo-issue.png 167 | :scale: 100 168 | 169 | 图2-24:缺陷追踪 170 | 171 | GitHub还为项目提供报表分析。图2-25是GotGit项目中用到的开发语言分布图。 172 | 173 | .. figure:: /images/join-github/gotgit-repo-graph-lang.png 174 | :scale: 100 175 | 176 | 图2-25:GotGit项目开发语言分布图 177 | 178 | 图2-26是开发者对GotGit项目贡献分布图。 179 | 180 | .. figure:: /images/join-github/gotgit-repo-graph-impact.png 181 | :scale: 100 182 | 183 | 图2-26:GotGit项目贡献分布图 184 | 185 | ---- 186 | 187 | .. [#] 项目 gotgit 最早由用户 ossxp-com 创建,现已转移到组织 gotgit 账号之下。 188 | .. [#] https://github.com/blog/809-git-dumb-http-transport-to-be-turned-off-in-90-days 189 | .. [#] 短格式URL,实际对应于: https://github.com/cameronmcefee/Image-Diff-View-Modes/commit/8e95f7 190 | .. [#] https://github.com/blog/621-bye-bye-flash-network-graph-is-now-canvas 191 | .. [#] https://github.com/res0nat0r/gitosis 192 | -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | if NOT "%PAPER%" == "" ( 11 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 12 | ) 13 | 14 | if "%1" == "" goto help 15 | 16 | if "%1" == "help" ( 17 | :help 18 | echo.Please use `make ^` where ^ is one of 19 | echo. html to make standalone HTML files 20 | echo. dirhtml to make HTML files named index.html in directories 21 | echo. singlehtml to make a single large HTML file 22 | echo. pickle to make pickle files 23 | echo. json to make JSON files 24 | echo. htmlhelp to make HTML files and a HTML help project 25 | echo. qthelp to make HTML files and a qthelp project 26 | echo. devhelp to make HTML files and a Devhelp project 27 | echo. epub to make an epub 28 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 29 | echo. text to make text files 30 | echo. man to make manual pages 31 | echo. changes to make an overview over all changed/added/deprecated items 32 | echo. linkcheck to check all external links for integrity 33 | echo. doctest to run all doctests embedded in the documentation if enabled 34 | goto end 35 | ) 36 | 37 | if "%1" == "clean" ( 38 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 39 | del /q /s %BUILDDIR%\* 40 | goto end 41 | ) 42 | 43 | if "%1" == "html" ( 44 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 45 | if errorlevel 1 exit /b 1 46 | echo. 47 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 48 | goto end 49 | ) 50 | 51 | if "%1" == "dirhtml" ( 52 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 53 | if errorlevel 1 exit /b 1 54 | echo. 55 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 56 | goto end 57 | ) 58 | 59 | if "%1" == "singlehtml" ( 60 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 61 | if errorlevel 1 exit /b 1 62 | echo. 63 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 64 | goto end 65 | ) 66 | 67 | if "%1" == "pickle" ( 68 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 69 | if errorlevel 1 exit /b 1 70 | echo. 71 | echo.Build finished; now you can process the pickle files. 72 | goto end 73 | ) 74 | 75 | if "%1" == "json" ( 76 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished; now you can process the JSON files. 80 | goto end 81 | ) 82 | 83 | if "%1" == "htmlhelp" ( 84 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished; now you can run HTML Help Workshop with the ^ 88 | .hhp project file in %BUILDDIR%/htmlhelp. 89 | goto end 90 | ) 91 | 92 | if "%1" == "qthelp" ( 93 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 94 | if errorlevel 1 exit /b 1 95 | echo. 96 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 97 | .qhcp project file in %BUILDDIR%/qthelp, like this: 98 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\GotGitHub.qhcp 99 | echo.To view the help file: 100 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\GotGitHub.ghc 101 | goto end 102 | ) 103 | 104 | if "%1" == "devhelp" ( 105 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 106 | if errorlevel 1 exit /b 1 107 | echo. 108 | echo.Build finished. 109 | goto end 110 | ) 111 | 112 | if "%1" == "epub" ( 113 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 117 | goto end 118 | ) 119 | 120 | if "%1" == "latex" ( 121 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 122 | if errorlevel 1 exit /b 1 123 | echo. 124 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 125 | goto end 126 | ) 127 | 128 | if "%1" == "text" ( 129 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 130 | if errorlevel 1 exit /b 1 131 | echo. 132 | echo.Build finished. The text files are in %BUILDDIR%/text. 133 | goto end 134 | ) 135 | 136 | if "%1" == "man" ( 137 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 141 | goto end 142 | ) 143 | 144 | if "%1" == "changes" ( 145 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.The overview file is in %BUILDDIR%/changes. 149 | goto end 150 | ) 151 | 152 | if "%1" == "linkcheck" ( 153 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Link check complete; look for any errors in the above output ^ 157 | or in %BUILDDIR%/linkcheck/output.txt. 158 | goto end 159 | ) 160 | 161 | if "%1" == "doctest" ( 162 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 163 | if errorlevel 1 exit /b 1 164 | echo. 165 | echo.Testing of doctests in the sources finished, look at the ^ 166 | results in %BUILDDIR%/doctest/output.txt. 167 | goto end 168 | ) 169 | 170 | :end 171 | -------------------------------------------------------------------------------- /06-side-projects/hub.rst: -------------------------------------------------------------------------------- 1 | .. _hub: 2 | 3 | hub 4 | ------------ 5 | 6 | 对于命令行用户,GitHub提供了名为\ ``hub``\ 的命令行工具,对Git进行了简单的\ 7 | 封装。该项目在GitHub上的地址为: https://github.com/defunkt/hub 。 8 | 9 | 使用\ ``hub``\ 可以在命令行中简化对GitHub的操作。例如克隆本电子书的版本库,\ 10 | 若用\ ``hub``\ 命令,地址可大大简化: 11 | 12 | :: 13 | 14 | $ hub clone gotgit/gotgithub 15 | 16 | 若要在自己账号下创建派生项目,无需登录GitHub网站,直接通过命令行即可实现: 17 | 18 | :: 19 | 20 | $ cd gotgithub 21 | $ hub fork 22 | 23 | 安装\ ``hub``\ 很简单,可使用如下方法任意一种方法。 24 | 25 | * 克隆\ ``hub``\ 的版本库,从源码安装。安装步骤如下: 26 | 27 | :: 28 | 29 | $ git clone git://github.com/defunkt/hub.git 30 | $ cd hub 31 | $ rake install prefix=/usr/local 32 | 33 | * 用 RubyGems 包方式安装。 34 | 35 | ``hub``\ 用 Ruby 开发,也可用 RubyGems 包方式安装。需要注意,在安装完毕后\ 36 | 最好将\ ``hub``\ 打包为一独立运行脚本,以便运行时不再靠 RubyGems 加载,\ 37 | 提高加载速度。安装步骤如下: 38 | 39 | :: 40 | 41 | $ gem install hub 42 | $ hub hub standalone > ~/bin/hub && chmod 755 ~/bin/hub 43 | 44 | 安装完毕后,还需要对\ ``hub``\ 进行设置。定义两个Git风格的配置变量,以便\ 45 | ``hub``\ 命令能确定当前GitHub用户账号,并能够完成所需的 GitHub API 认证。 46 | 47 | :: 48 | 49 | $ git config --global github.user "your-github-username" 50 | $ git config --global github.token "your-github-token" 51 | 52 | 其中\ ``github.token``\ 中保存的是用户的API TOKEN,这在“2.1 创建GitHub账号”\ 53 | 一节有过介绍。 54 | 55 | 在使用\ ``hub``\ 过程中,如果要为区分哪些命令是\ ``git``\ 的,哪些是\ ``hub``\ 56 | 的,而不断在两个命令间切换显然太不方便了。``hub`` 命令支持以系统别名\ ``git``\ 57 | 的方式运行,即设置\ ``hub``\ 的系统别名为\ ``git``\ ,然后只需执行\ ``git``\ 58 | 命令,这样无论是\ ``git``\ 本身的命令还是\ ``hub``\ 扩展的命令都可正常运行。\ 59 | 但要注意要用系统提供的别名方式,而不能把\ ``hub``\ 脚本改名为\ ``git``\ ,\ 60 | 因为\ ``hub``\ 只是简单地对Git进行封装,运行时仍依赖\ ``git``\ 命令。在 bash \ 61 | 环境下建立别名可运行如下命令: 62 | 63 | :: 64 | 65 | $ alias git=hub 66 | 67 | 其他 shell 环境下如何建立系统别名呢?运行\ ``hub alias ``\ 命令查看\ 68 | 相关 shell 环境下建立别名的方法。例如对于 csh: 69 | 70 | :: 71 | 72 | $ hub alias csh 73 | Run this in your shell to start using `hub` as `git`: 74 | alias git hub 75 | 76 | 下面介绍\ ``hub``\ 的常用命令,节选自\ ``hub``\ 的项目页\ [#]_\ 。示例使用\ 77 | 了别名命令\ ``git``\ 调用,并把对应的原始的\ ``git``\ 命令写在命令的下面\ 78 | (用提示符\ ``>``\ 表示,方括号中是说明)。 79 | 80 | * git create 81 | 82 | 在GitHub上创建项目。 83 | 84 | :: 85 | 86 | $ git create -d '项目表述' 87 | > [ 在GitHub上创建版本库 ] 88 | > git remote add origin git@github.com:YOUR_USER/CURRENT_REPO.git 89 | 90 | $ git create recipes 91 | > [ 在GitHub上创建版本库 ] 92 | > git remote add origin git@github.com:YOUR_USER/recipes.git 93 | 94 | $ git create sinatra/recipes 95 | > [ 在组织账号 sinatra 下创建版本库 ] 96 | > git remote add origin git@github.com:sinatra/recipes.git 97 | 98 | * git clone 99 | 100 | 克隆版本库可使用URL简写,即“用户名/版本库”格式地址会自动扩展为Git协议\ 101 | (只读)地址或SSH协议(可写)地址。 102 | 103 | :: 104 | 105 | $ git clone schacon/ticgit 106 | > git clone git://github.com/schacon/ticgit.git 107 | 108 | $ git clone -p schacon/ticgit 109 | > git clone git@github.com:schacon/ticgit.git 110 | 111 | $ git clone resque 112 | > git clone git@github.com/YOUR_USER/resque.git 113 | 114 | * git fork 115 | 116 | 在GitHub自己账号下建立派生项目。 117 | 118 | :: 119 | 120 | $ git fork 121 | > [ 先在GitHub 上建立派生项目 ] 122 | > git remote add -f YOUR_USER git@github.com:YOUR_USER/CURRENT_REPO.git 123 | 124 | * git pull-request 125 | 126 | 打开编辑器输入标题和内容,然后在 GitHub 上创建 Pull Request。 127 | 128 | 129 | * git remote add 130 | 131 | 设置远程版本库。和\ ``git clone``\ 命令一样支持URL简写。 132 | 133 | :: 134 | 135 | $ git remote add rtomayko 136 | > git remote add rtomayko git://github.com/rtomayko/CURRENT_REPO.git 137 | 138 | $ git remote add -p rtomayko 139 | > git remote add rtomayko git@github.com:rtomayko/CURRENT_REPO.git 140 | 141 | $ git remote add origin 142 | > git remote add origin git://github.com/YOUR_USER/CURRENT_REPO.git 143 | 144 | * git fetch 145 | 146 | 获取他人同名版本库。自动建立远程版本库并获取提交。 147 | 148 | :: 149 | 150 | $ git fetch mislav 151 | > git remote add mislav git://github.com/mislav/REPO.git 152 | > git fetch mislav 153 | 154 | $ git fetch mislav,xoebus 155 | > git remote add mislav ... 156 | > git remote add xoebus ... 157 | > git fetch --multiple mislav xoebus 158 | 159 | * git cherry-pick 160 | 161 | 获取远程提交,并拣选至本地版本库。 162 | 163 | :: 164 | 165 | $ git cherry-pick http://github.com/mislav/REPO/commit/SHA 166 | > git remote add -f mislav git://github.com/mislav/REPO.git 167 | > git cherry-pick SHA 168 | 169 | * git am, git apply 170 | 171 | 获取 Pull Request,并应用于本地版本库。 172 | 173 | :: 174 | 175 | $ git am https://github.com/defunkt/hub/pull/55 176 | > curl https://github.com/defunkt/hub/pull/55.patch -o /tmp/55.patch 177 | > git am /tmp/55.patch 178 | 179 | * git browse 180 | 181 | 打开浏览器访问相应的URL地址。 182 | 183 | :: 184 | 185 | $ git browse 186 | > open https://github.com/YOUR_USER/CURRENT_REPO 187 | 188 | $ git browse -- commit/SHA 189 | > open https://github.com/YOUR_USER/CURRENT_REPO/commit/SHA 190 | 191 | $ git browse -- issues 192 | > open https://github.com/YOUR_USER/CURRENT_REPO/issues 193 | 194 | $ git browse resque 195 | > open https://github.com/YOUR_USER/resque 196 | 197 | $ git browse schacon/ticgit 198 | > open https://github.com/schacon/ticgit 199 | 200 | $ git browse schacon/ticgit commit/SHA 201 | > open https://github.com/schacon/ticgit/commit/SHA 202 | 203 | * git help hub 204 | 205 | 查看\ ``hub``\ 命令的帮助。 206 | 207 | .. [#] https://github.com/defunkt/hub#readme 208 | -------------------------------------------------------------------------------- /02-join-github/010-account-setup.rst: -------------------------------------------------------------------------------- 1 | .. _github-signup: 2 | 3 | 创建GitHub账号 4 | =============== 5 | 6 | 注册GitHub账号,只要点击导航条中的“Signup and Pricing”,或者点击首页中那个\ 7 | 大大的“Plans, Pricing and Signup”按钮,即进入收费方案介绍及注册页面。 8 | 9 | 收费?不必担心,开源软件托管是GitHub的基石,对于开源项目的版本库(即非私有\ 10 | 版本库)的托管,GitHub是免费的。在收费方案及注册页面中,最上面的就是针对于\ 11 | 开源的免费托管方案,如图2-1所示。 12 | 13 | .. figure:: /images/join-github/free-plan.png 14 | :scale: 100 15 | 16 | 图2-1:针对开源项目(公开版本库)的免费方案 17 | 18 | 至于本页其他付费方案,将在后面的章节介绍。点击免费方案右侧的\ 19 | “Create a free account”按钮,就进入到注册页面,如图2-2所示。 20 | 21 | .. figure:: /images/join-github/signup.png 22 | :scale: 100 23 | 24 | 图2-2:账号注册 25 | 26 | GitHub的注册页面非常简洁,只有登录ID,邮件地址和口令需要输入。要注意的是\ 27 | 每个邮件地址只能注册一次。 28 | 29 | 注册完毕即以新注册的账号自动登录。登录后即进入用户的仪表板(Dashborad)\ 30 | 页面。首次进入的仪表板页面还会在其中显示GitHub BootCamp(GitHub 新手训练营)\ 31 | 的链接,以帮助新用户快速入门。如果图2-3所示。 32 | 33 | .. figure:: /images/join-github/loggedin.png 34 | :scale: 100 35 | 36 | 图2-3:登录后的GitHub首页 37 | 38 | 仪表板页面是用户最重要的页面,因为创建新项目(新版本库)的链接就位于该页面。\ 39 | 重新设计的GitHub用户界面\ [#]_\ 中跳转到仪表板页面的链接不像之前那么直观,\ 40 | 鼠标移动到页面左上角的“github”文字图标会发现此图标可以点击,该文字图标即是\ 41 | 进入仪表板页面的快捷。 42 | 43 | 在页面右上方显示当前登录用户的名称和头像。图2-3中显示登录用户为 gotgithub,\ 44 | 而用户头像因为尚未设置所以显示为缺省图片——GitHub吉祥物Octocat的剪影。在页面\ 45 | 右上方还有三个图标,从左至右分别是:通知、账号设置和退出。点击账号设置图标\ 46 | 对账号进行进一步设置,如图2-4所示。 47 | 48 | .. figure:: /images/join-github/setting-profile.png 49 | :scale: 100 50 | 51 | 图2-4:账户设置页 52 | 53 | 账号设置的第一个页面是对用户公开身份信息进行设置,所有内容均为可选项,如果\ 54 | 填写将显示在个人页面中,并能被所有人访问。注意修改用户头像需要访问第三方\ 55 | 头像设置网站:\ `gravatar.com`_\ ,Gravatar网站提供的头像服务是一个通用服务,\ 56 | 可为大部分Web应用所使用。 57 | 58 | 图2-4中还显示了当前用户使用的GitHub托管方案(Free)和使用统计。因为当前注册\ 59 | 用户选择的是免费方案,所以可用的私有版本库数量和私有空间的协同者数目都是零。\ 60 | 免费方案拥有300MB托管空间,因当前尚未创建版本库托管,所以空间占用为零。\ 61 | GitHub对开源软件的300MB托管空间限制并非硬性限制,可以申请扩增托管空间,如果\ 62 | 不存在滥用情况的话。 63 | 64 | 点击菜单中的“Account Admin”,可以更改口令、查看API Token、修改用户名,以及\ 65 | 删除自身账号,如图2-5所示。 66 | 67 | .. figure:: /images/join-github/setting-admin.png 68 | :scale: 100 69 | 70 | 图2-5:账户管理 71 | 72 | .. _api-token: 73 | 74 | 其中API Token是和用户口令相关的密钥,当用户口令更改时API Token也随之更改。\ 75 | GitHub的某些应用会使用API Token进行身份认证,从而避免直接使用用户口令造成\ 76 | 泄露的风险。API Token若泄露的危害要远远小于口令泄露,这因为API Token不能用于\ 77 | 登录GitHub网站等,而且一旦API Token泄露可以很容易通过更改口令的方式更换\ 78 | API Token。 79 | 80 | 点击菜单中的“Email Addresses”,可以添加和删除邮件地址,如图2-6所示。GitHub\ 81 | 允许为一个账号绑定多个邮件地址,以便能够将Git版本库中的提交\ 82 | (提交者以 "用户名 <邮件地址>" 的格式给出)正确对应到GitHub账户。 83 | 84 | .. figure:: /images/join-github/setting-email.png 85 | :scale: 100 86 | 87 | 图2-6:邮件地址管理 88 | 89 | .. _ssh: 90 | 91 | GitHub为托管的Git版本库提供SSH协议支持,即用户可以用公钥认证的方式连接到\ 92 | GitHub的SSH服务器。下面的示例用ssh命令连接github.com的SSH服务,登录用户名为\ 93 | git(所有GitHub用户共享此SSH用户名,不要写成其他)。 94 | 95 | :: 96 | 97 | $ ssh -T git@github.com 98 | Permission denied (publickey). 99 | 100 | 上面的示例显示登录失败,这是因为我们尚未在GitHub账户中正确设置公钥认证。\ 101 | 图2-7显示的是GitHub的SSH公钥设置界面。 102 | 103 | .. figure:: /images/join-github/setting-ssh.png 104 | :scale: 100 105 | 106 | 图2-7:SSH公钥管理 107 | 108 | 要想向GitHub添加SSH公钥,首先要确保正确生成了对应的公钥/私钥对。关于SSH公钥\ 109 | 认证,在我的《Git权威指南》一书的“第29章使用SSH协议”中有详细介绍,这里仅做\ 110 | 简要的介绍。 111 | 112 | GitHub的SSH服务支持OpenSSH格式的公钥认证,可以通过Linux、Mac OS X、或Cygwin\ 113 | 下的\ ``ssh-keygen``\ 命令创建公钥/私钥对。命令如下: 114 | 115 | :: 116 | 117 | $ ssh-keygen 118 | 119 | 然后根据提示在用户主目录下的\ ``.ssh``\ 目录中创建默认的公钥/私钥对文件,\ 120 | 其中\ ``~/.ssh/id_rsa``\ 是私钥文件,\ ``~/.ssh/id_rsa.pub``\ 是公钥文件。\ 121 | 注意私钥文件要严加保护,不能泄露给任何人。如果在执行\ ``ssh-keygen``\ 命令时\ 122 | 选择了使用口令保护私钥,私钥文件是经过加密的。至于公钥文件\ 123 | ``~/.ssh/id_rsa.pub``\ 则可以放心地公开给他人。 124 | 125 | 也可以用\ ``ssh-keygen``\ 命令以不同的名称创建多个公钥,当拥有多个GitHub\ 126 | 账号时,非常重要。这是因为虽然一个GitHub账号允许使用多个不同的SSH公钥,\ 127 | 但反过来,一个SSH公钥只能对应于一个GitHub账号。下面的命令在\ ``~/.ssh``\ 目录\ 128 | 下创建名为\ ``gotgithub``\ 的私钥和名为\ ``gotgithub.pub``\ 的公钥文件。 129 | 130 | :: 131 | 132 | $ ssh-keygen -C "gotgithub@gmail.com" -f ~/.ssh/gotgithub 133 | 134 | 当生成的公钥/私钥对不在缺省位置(\ ``~/.ssh/id_rsa``\ 等)时,使用\ ``ssh``\ 命令\ 135 | 连接远程主机时需要使用参数\ ``-i ``\ 指定公钥/私钥对。或者在配置\ 136 | 文件\ ``~/.ssh/config``\ 中针对相应主机进行设定。例如对于上例创建了非缺省\ 137 | 公钥/私钥对\ ``~/.ssh/gotgithub``\ ,可以在\ ``~/.ssh/config``\ 配置文件中\ 138 | 写入如下配置。 139 | 140 | :: 141 | 142 | Host github.com 143 | User git 144 | Hostname github.com 145 | PreferredAuthentications publickey 146 | IdentityFile ~/.ssh/gotgithub 147 | 148 | 好了,有了上面的准备,就将\ :file:`~/.ssh/gotgithub.pub`\ 文件内容拷贝到剪切板。\ 149 | 公钥是一行长长的字符串,若用编辑器打开公钥文件会折行显示,注意拷贝时切莫在其中\ 150 | 插入多余的换行符、空格等,否则在公钥认证过程因为服务器端和客户端公钥不匹配而\ 151 | 导致认证失败。命令行下可直接用\ ``pbcopy``\ 命令\ [#]_\ 将文件内容拷贝到剪切板\ 152 | 以避免拷贝错误: 153 | 154 | :: 155 | 156 | $ cat ~/.ssh/gotgithub.pub | pbcopy 157 | 158 | 159 | 然后将公钥文件中的内容粘贴到GitHub的SSH公钥管理的对话框中,如图2-8所示。\ 160 | 161 | .. figure:: /images/join-github/setting-ssh-gotgithub.png 162 | :scale: 100 163 | 164 | 图2-8:添加SSH公钥认证 165 | 166 | 设置成功后,再用\ ``ssh``\ 命令访问GitHub,会显示一条认证成功信息并退出。\ 167 | 在认证成功的信息中还会显示该公钥对应的用户名。 168 | 169 | :: 170 | 171 | $ ssh -T git@github.com 172 | Hi gotgithub! You've successfully authenticated, but GitHub does not provide shell access. 173 | 174 | 如果您未能看到类似的成功信息,可以通过在\ ``ssh``\ 命令后面添加\ ``-v``\ 参数\ 175 | 加以诊断,会在冗长的会话中看到认证所使用的公钥文件等信息。然后比对所使用的公钥\ 176 | 内容是否和GitHub账号中设置的相一致。 177 | 178 | :: 179 | 180 | $ ssh -Tv git@github.com 181 | ... 182 | debug1: Authentications that can continue: publickey 183 | debug1: Next authentication method: publickey 184 | debug1: Offering RSA public key: /Users/jiangxin/.ssh/gotgithub 185 | ... 186 | debug1: Entering interactive session. 187 | Hi gotgithub! You've successfully authenticated, but GitHub does not provide shell access. 188 | ... 189 | 190 | 账号设置的最后一项是向GitHub提供你的求职信息。GitHub作为一个优秀程序员的聚集地,\ 191 | 已成为一个IT人才招聘的途径,如果你需要找工作的话,提供简历并打开“Available for hire”\ 192 | 选项,GitHub会向你推荐合适的工作机会。如图2-9所示。 193 | 194 | .. figure:: /images/join-github/setting-job.png 195 | :scale: 100 196 | 197 | 图2-9:求职信息管理 198 | 199 | .. _gravatar.com: http://gravatar.com/ 200 | 201 | ---- 202 | 203 | .. [#] https://github.com/blog/1007-skinny-header 204 | .. [#] Mac下的命令行工具\ ``pbcopy``\ 和\ ``pbpaste``\ 可以在命令行下操作剪贴板,\ 205 | Linux下的命令行工具\ ``xsel``\ 亦可实现类似功能。在Linux下可以创建别名用\ 206 | ``xsel``\ 命令来模拟\ ``pbcopy``\ 和\ ``pbpaste`` \。 207 | 208 | :: 209 | 210 | alias pbcopy='xsel --input' 211 | alias pbpaste='xsel --output' 212 | -------------------------------------------------------------------------------- /04-work-with-others/020-shared-repo.rst: -------------------------------------------------------------------------------- 1 | .. _shared_repo: 2 | 3 | 共享版本库 4 | =============== 5 | 6 | 除了独具特色的“Fork + Pull”的分布式工作模式,GitHub同样支持传统的集中式协同\ 7 | 工作模式。 8 | 9 | .. _collaborators: 10 | 11 | 版本库授权 12 | ----------------- 13 | 14 | GitHub可以通过多种途径为版本库授权,让版本库成为多人共享的版本库,从而让项目\ 15 | 管理者围绕项目创建一个核心开发团队。下面以gotgithub账号下的helloworld版本库\ 16 | 为例,介绍如何设置版本库的多人共享。 17 | 18 | 进入\ ``gotgithub/helloworld``\ 项目的管理界面,点击左侧导航条中的\ 19 | “Collaborators”,可以查看及添加项目的合作者,如图4-17所示。 20 | 21 | .. figure:: /images/work-with-others/collaborators.png 22 | :scale: 100 23 | 24 | 图4-17:添加项目合作者 25 | 26 | 图4-17为项目添加了两个合作者:supergirl和incredible。添加到项目当中的合作者\ 27 | 会收到通知邮件,告知已经被加入到相应的项目当中。当新加入的项目合作者,如\ 28 | incredible,登录GitHub,在仪表板的版本库列表中会看到源自gotgithub用户的版本库,\ 29 | 如图4-18所示。 30 | 31 | .. figure:: /images/work-with-others/prj-list-of-collab.png 32 | :scale: 100 33 | 34 | 图4-18:合作者项目列表 35 | 36 | 从图4-18可以看出GitHub用户incredible自己创建的项目托管在自己的空间下,而作为\ 37 | 合作者参与的项目仍然托管在原创建者(gotgithub)的空间下,这一点明显和派生\ 38 | (Fork)而来的项目不同。图4-19是以incredible登录GitHub访问\ 39 | ``gotgithub/helloworld``\ 的界面。 40 | 41 | .. figure:: /images/work-with-others/collab-view-helloworld.png 42 | :scale: 100 43 | 44 | 图4-19:以合作者身份访问项目 45 | 46 | 用户incredible对版本库\ ``gotgithub/helloworld``\ 拥有写入权限。和\ ``gotgithub``\ 47 | 用户稍有区别的是没有管理员权限。 48 | 49 | .. _central-model: 50 | 51 | 与传统集中式工作模式的异同 52 | -------------------------------- 53 | 54 | 传统的集中式版本控制系统,如CVS、SVN,所有用户都访问同一个版本库。采用集中式\ 55 | 工作模式的GitHub用户也同样是访问同一版本库。 56 | 57 | 对于由用户gotgithub创建的\ ``helloworld``\ 版本库,添加了合作者supergirl和\ 58 | incredible,三个人克隆版本库使用如下命令。 59 | 60 | * 用户 gotgithub 克隆版本库。 61 | 62 | :: 63 | 64 | gotgithub$ git clone https://gotgithub@github.com/gotgithub/helloworld.git 65 | 66 | * 用户 supergirl 克隆版本库。 67 | 68 | :: 69 | 70 | supergirl$ git clone https://supergirl@github.com/gotgithub/helloworld.git 71 | 72 | * 用户 incredible 克隆版本库。 73 | 74 | :: 75 | 76 | incredible$ git clone https://incredible@github.com/gotgithub/helloworld.git 77 | 78 | 传统集中式版本控制系统,所有的提交历史数据都在唯一的版本库中,各个提交是\ 79 | 顺序进行的。为了防止多用户在提交时相互覆盖,集中式版本控制系统发明了很多方法 。\ 80 | 有的采用编辑锁的形式(如VSS),更改文件前对文件锁定,其他人禁止对文件进行\ 81 | 访问(甚至无法读取),完成修改并提交后,文件解锁。有的如SVN,允许多人同时\ 82 | 编辑同一文件,但只有先进行提交的才能成功,后提交的会遇到“过时”错误,必须先\ 83 | 获取版本库中的新增提交并和本地修改进行合并。即传统集中式版本控制系统,\ 84 | 提交时必须和唯一的版本库所在的服务器保持连接,而且提交有可能会失败。 85 | 86 | 对于像Git这样的分布式版本控制系统,提交总是会成功,这是因为提交并不涉及和\ 87 | 共享服务器的交互,是针对本地克隆版本库进行的本地操作。采用集中式的工作模式,\ 88 | 共享版本库作为各个用户各自本地版本库数据交换、沟通的中介,只有在本地克隆\ 89 | 版本库需要和共享版本库同步的时候才要和服务器建立连接。例如将本地所做的一个\ 90 | 或多个提交推送到共享服务器,或者将服务器上新的提交获取到本地克隆版本库。 91 | 92 | 实际上无论采用分布式还是集中式的工作模式,Git都好像工作在一个独立的分支上\ 93 | (克隆即分支),即使共享版本库和本地克隆版本库的分支名都叫做\ ``master``\ 。\ 94 | 如图4-20,三个用户克隆\ ``gitgithub/helloworld``\ 版本库后,各自在本地执行\ 95 | 了一次或多次提交。 96 | 97 | 98 | .. figure:: /images/work-with-others/workflow-commit.png 99 | :scale: 100 100 | 101 | 图4-20:在本地版本库中的提交 102 | 103 | 三个用户各自的提交都会非常顺利,但是一旦决定将本地提交推送到共享服务器时就\ 104 | 可能遇到麻烦。用户 gotgithub 先执行推送,会非常顺利。如图4-21所示。 105 | 106 | :: 107 | 108 | gotgithub$ git push 109 | 110 | .. figure:: /images/work-with-others/workflow-commit-push.png 111 | :scale: 100 112 | 113 | 图4-21:用户gotgithub完成推送 114 | 115 | 而其他人就没有这么幸运了,会报告错误:遇到非快进式推送。Git的推送操作就像SVN\ 116 | 等集中式版本控制系统的提交操作那样,先执行者成功,后执行者糟糕(要解决冲突,\ 117 | 自动或手动)。 118 | 119 | .. _merge-and-push: 120 | 121 | 合并后推送 122 | --------------- 123 | 124 | 当用户gotgithub完成推送后,共享版本库以及三个用户的本地版本库如图4-21所示。\ 125 | 其中共享版本库变得和gotgithub用户的本地版本库相一致。此时如果用户supergirl\ 126 | 执行推送,会遇到错误:非快进式推送。 127 | 128 | :: 129 | 130 | supergirl$ git push 131 | To https://supergirl@github.com/gotgithub/helloworld.git 132 | ! [rejected] master -> master (non-fast-forward) 133 | error: failed to push some refs to 'https://supergirl@github.com/gotgithub/helloworld.git' 134 | To prevent you from losing history, non-fast-forward updates were rejected 135 | Merge the remote changes (e.g. 'git pull') before pushing again. See the 136 | 'Note about fast-forwards' section of 'git push --help' for details. 137 | 138 | GitHub并不对强制推送进行限制,但是用户supergirl不要用\ ``git push -f``\ 命令\ 139 | 强制推送,因为那样会覆盖掉共享版本库中用户gotgithub的推送,正确的做法是获取\ 140 | 共享版本库中新提交,并在本地版本库中和本地提交合并。即执行: 141 | 142 | :: 143 | 144 | supergirl$ git fetch 145 | supergirl$ git merge 146 | 147 | 获取和合并操作过程如图4-22所示。 148 | 149 | .. figure:: /images/work-with-others/workflow-fetch-merge.png 150 | :scale: 100 151 | 152 | 图4-22:合并操作示意图 153 | 154 | 实际上用户supergirl只需执行一条命令便可完成所有的操作: 155 | 156 | :: 157 | 158 | supergirl$ git pull 159 | 160 | 即:\ ``git pull = git fetch + git merge``\ 。 161 | 162 | 但是合并操作并不总是会成功,如果自动合并失败,会在暂存区对合并前后文件进行\ 163 | 标识,工作区进入冲突解决状态,在冲突解决完成之前不能提交。Git支持多种图形工具\ 164 | 帮助完成冲突解决,执行如下命令,即可自动调用已安装的冲突解决工具。 165 | 166 | :: 167 | 168 | supergirl$ git mergetool 169 | 170 | 冲突解决完毕,执行提交即完成冲突解决。如果在冲突解决过程把本地文件搞得一团糟,\ 171 | 随时可以取消合并操作。执行命令\ ``git reset --hard``\ 会取消冲突的合并让\ 172 | 本地版本库回到合并之前的状态。 173 | 174 | 成功完成合并后将本地版本库中的提交推送到共享版本库: 175 | 176 | :: 177 | 178 | supergirl$ git push 179 | 180 | 完成推送后的版本库示意图如图4-23所示。 181 | 182 | .. figure:: /images/work-with-others/workflow-merge-push.png 183 | :scale: 100 184 | 185 | 图4-23:完成合并后推送 186 | 187 | .. _rebase-and-push: 188 | 189 | 合并还是变基 190 | --------------- 191 | 192 | 合并并非多个开发者的工作成果融合的唯一选择,有时甚至并非最佳选择。一方面合并\ 193 | 会产生除了合并双方(或多方)所有提交外的一个新提交,增加了代码审核的负担,\ 194 | 另一方面本地多个提交混杂一起与远程分支合并会更困难。在特定情况下,变基是合并\ 195 | 之外的另一个选择。 196 | 197 | 图4-24展示用户incredible采用合并和变基两种不同解决方案的操作结果。图中右上\ 198 | 是合并操作后的结果,右下是变基操作后的结果。 199 | 200 | .. figure:: /images/work-with-others/workflow-merge-or-rebase.png 201 | :scale: 100 202 | 203 | 图4-24:合并和变基结果比较 204 | 205 | 若用户 incredible 选择变基操作,执行命令如下: 206 | 207 | * 获取远程版本库的提交到本地的远程分支。 208 | 209 | :: 210 | 211 | incredible$ git fetch origin 212 | 213 | * 执行变基操作,将本地\ ``master``\ 分支的提交变基到新的远程分支中。 214 | 215 | :: 216 | 217 | incredible$ git rebase origin/master 218 | 219 | 如果一切顺利,变基后推送到共享版本库。 220 | 221 | :: 222 | 223 | incredible$ git push 224 | 225 | 推送后的版本库状态如图4-25所示。 226 | 227 | .. figure:: /images/work-with-others/workflow-rebase-push.png 228 | :scale: 100 229 | 230 | 图4-25:变基后推送 231 | 232 | 如果希望在执行\ ``git pull``\ 时自动使用\ ``git rebase``\ 取代默认的\ 233 | ``git merge``\ 操作,可以在\ ``git pull``\ 命令行添加参数\ ``--rebase``\ 如下: 234 | 235 | :: 236 | 237 | $ git pull --rebase 238 | 239 | 或者通过配置变量设置当前分支使用变基策略,即每次执行\ ``git pull``\ 命令时\ 240 | 对于\ ``master``\ 分支,采用变基操作取代默认的合并操作。 241 | 242 | :: 243 | 244 | $ git config branch.master.rebase true 245 | 246 | 如果希望本地所有克隆版本库在执行\ ``git pull``\ 时都改变默认行为,将变基作为\ 247 | 首选,则如下设置全局变量。 248 | 249 | :: 250 | 251 | $ git config --global branch.autosetuprebase true 252 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | 15 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo " html to make standalone HTML files" 20 | @echo " dirhtml to make HTML files named index.html in directories" 21 | @echo " singlehtml to make a single large HTML file" 22 | @echo " pickle to make pickle files" 23 | @echo " json to make JSON files" 24 | @echo " htmlhelp to make HTML files and a HTML help project" 25 | @echo " qthelp to make HTML files and a qthelp project" 26 | @echo " devhelp to make HTML files and a Devhelp project" 27 | @echo " epub to make an epub" 28 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 29 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 30 | @echo " text to make text files" 31 | @echo " man to make manual pages" 32 | @echo " changes to make an overview of all changed/added/deprecated items" 33 | @echo " linkcheck to check all external links for integrity" 34 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 35 | @echo " gh-pages to publish htmls on gh-pages branch for GitHub" 36 | 37 | clean: 38 | -rm -rf $(BUILDDIR)/* 39 | 40 | html: pre-build 41 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 42 | @echo 43 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 44 | 45 | dirhtml: pre-build 46 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 47 | @echo 48 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 49 | 50 | singlehtml: pre-build 51 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 52 | @echo 53 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 54 | 55 | pickle: pre-build 56 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 57 | @echo 58 | @echo "Build finished; now you can process the pickle files." 59 | 60 | json: pre-build 61 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 62 | @echo 63 | @echo "Build finished; now you can process the JSON files." 64 | 65 | htmlhelp: pre-build 66 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 67 | @echo 68 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 69 | ".hhp project file in $(BUILDDIR)/htmlhelp." 70 | 71 | qthelp: pre-build 72 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 73 | @echo 74 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 75 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 76 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/GotGitHub.qhcp" 77 | @echo "To view the help file:" 78 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/GotGitHub.qhc" 79 | 80 | devhelp: pre-build 81 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 82 | @echo 83 | @echo "Build finished." 84 | @echo "To view the help file:" 85 | @echo "# mkdir -p $$HOME/.local/share/devhelp/GotGitHub" 86 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/GotGitHub" 87 | @echo "# devhelp" 88 | 89 | epub: pre-build 90 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 91 | @echo 92 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 93 | 94 | latex: pre-build 95 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 96 | @echo 97 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 98 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 99 | "(use \`make latexpdf' here to do that automatically)." 100 | 101 | latexpdf: pre-build 102 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 103 | @echo "Running LaTeX files through pdflatex..." 104 | make -C $(BUILDDIR)/latex all-pdf 105 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 106 | 107 | text: pre-build 108 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 109 | @echo 110 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 111 | 112 | man: pre-build 113 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 114 | @echo 115 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 116 | 117 | changes: pre-build 118 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 119 | @echo 120 | @echo "The overview file is in $(BUILDDIR)/changes." 121 | 122 | linkcheck: pre-build 123 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 124 | @echo 125 | @echo "Link check complete; look for any errors in the above output " \ 126 | "or in $(BUILDDIR)/linkcheck/output.txt." 127 | 128 | gh-pages: clean html 129 | @if ! git rev-parse refs/heads/gh-pages >/dev/null 2>&1 ; then \ 130 | echo "Branch gh-pages not exists, forgot check it out?" >&2; \ 131 | exit 1; \ 132 | fi 133 | @cp README.md _build/html/ 134 | @echo '*' > _build/html/images/.gitignore 135 | @git add -f _build/html; \ 136 | tree=$$(git write-tree); \ 137 | newhtml=$$(git ls-tree $$tree:_build | grep "html$$" | awk '{print $$3;}') ; \ 138 | oldhtml=$$(git rev-parse refs/heads/gh-pages^{tree}); \ 139 | if [ "$$oldhtml" = "$$newhtml" ]; then \ 140 | echo "HTML is uptodate, branch gh-pages not changed." ; \ 141 | else \ 142 | commit=$$(git log --format=%B -1 | git commit-tree $$newhtml -p refs/heads/gh-pages) ; \ 143 | git update-ref -m "HTML compiled from $$(git rev-parse HEAD)" refs/heads/gh-pages $$commit ; \ 144 | echo "Branch gh-pages changed." ; \ 145 | [ -x .git/hooks/post-commit ] && .git/hooks/post-commit; \ 146 | fi; \ 147 | git rm --cached -r -q _build/html 148 | 149 | gh-pages-shift: 150 | @if ! git rev-parse refs/heads/gh-pages^ >/dev/null 2>&1 ; then \ 151 | echo "Branch gh-pages not exists or only one commit, forgot check it out?" >&2; \ 152 | exit 1; \ 153 | fi 154 | if git update-ref -m "shift from $$(git rev-parse refs/heads/gh-pages)" refs/heads/gh-pages $$(git rev-parse refs/heads/gh-pages^); then \ 155 | echo "gh-pages branch shift to last commit."; \ 156 | fi 157 | 158 | 159 | doctest: 160 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 161 | @echo "Testing of doctests in the sources finished, look at the " \ 162 | "results in $(BUILDDIR)/doctest/output.txt." 163 | 164 | pre-build: images update-version 165 | 166 | images: 167 | @(cd graphics; \ 168 | if test -f Rakefile; then \ 169 | rake convert[../images,600]; \ 170 | else \ 171 | echo "ERROR: Submodule 'graphics' not found. Please init submodule using: " >&2; \ 172 | echo "ERROR: git submodule init && git submodule update" >&2; \ 173 | exit 1; \ 174 | fi) 175 | 176 | update-version: 177 | @sed -e "s##$$(git describe --dirty --always)#g" < _version.inc.in > _version.inc.tmp 178 | @if diff -q _version.inc.tmp _version.inc >/dev/null 2>&1; then \ 179 | rm _version.inc.tmp; \ 180 | else \ 181 | echo "_version.inc.in => _version.inc" ; \ 182 | mv _version.inc.tmp _version.inc; \ 183 | fi 184 | 185 | .PHONY : clean pre-build images gh-pages 186 | -------------------------------------------------------------------------------- /conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # GotGitHub documentation build configuration file, created by 4 | # sphinx-quickstart on Wed Aug 24 20:52:42 2011. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | #sys.path.insert(0, os.path.abspath('.')) 20 | 21 | # -- General configuration ----------------------------------------------------- 22 | 23 | # If your documentation needs a minimal Sphinx version, state it here. 24 | #needs_sphinx = '1.0' 25 | 26 | # Add any Sphinx extension module names here, as strings. They can be extensions 27 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 28 | extensions = ['sphinx.ext.todo', 'sphinx.ext.ifconfig'] 29 | 30 | # Add any paths that contain templates here, relative to this directory. 31 | templates_path = ['_templates'] 32 | 33 | # The suffix of source filenames. 34 | source_suffix = '.rst' 35 | 36 | # The encoding of source files. 37 | #source_encoding = 'utf-8-sig' 38 | 39 | # The master toctree document. 40 | master_doc = 'index' 41 | 42 | # General information about the project. 43 | project = u'GotGitHub' 44 | copyright = u'2011, 蒋鑫' 45 | 46 | # The version info for the project you're documenting, acts as replacement for 47 | # |version| and |release|, also used in various other places throughout the 48 | # built documents. 49 | # 50 | # The short X.Y version. 51 | version = '0.9' 52 | # The full version, including alpha/beta/rc tags. 53 | release = '0.9' 54 | 55 | # The language for content autogenerated by Sphinx. Refer to documentation 56 | # for a list of supported languages. 57 | #language = None 58 | 59 | # There are two options for replacing |today|: either, you set today to some 60 | # non-false value, then it is used: 61 | #today = '' 62 | # Else, today_fmt is used as the format for a strftime call. 63 | #today_fmt = '%B %d, %Y' 64 | 65 | # List of patterns, relative to source directory, that match files and 66 | # directories to ignore when looking for source files. 67 | exclude_patterns = ['_build'] 68 | 69 | # The reST default role (used for this markup: `text`) to use for all documents. 70 | #default_role = None 71 | 72 | # If true, '()' will be appended to :func: etc. cross-reference text. 73 | #add_function_parentheses = True 74 | 75 | # If true, the current module name will be prepended to all description 76 | # unit titles (such as .. function::). 77 | #add_module_names = True 78 | 79 | # If true, sectionauthor and moduleauthor directives will be shown in the 80 | # output. They are ignored by default. 81 | #show_authors = False 82 | 83 | # The name of the Pygments (syntax highlighting) style to use. 84 | pygments_style = 'sphinx' 85 | 86 | # A list of ignored prefixes for module index sorting. 87 | #modindex_common_prefix = [] 88 | 89 | 90 | # -- Options for HTML output --------------------------------------------------- 91 | 92 | # The theme to use for HTML and HTML Help pages. See the documentation for 93 | # a list of builtin themes. 94 | #html_theme = 'sphinxdoc' 95 | html_theme = 'gotgithub' 96 | 97 | # Theme options are theme-specific and customize the look and feel of a theme 98 | # further. For a list of options available for each theme, see the 99 | # documentation. 100 | #html_theme_options = {} 101 | 102 | # Add any paths that contain custom themes here, relative to this directory. 103 | html_theme_path = [ '_theme' ] 104 | 105 | # The name for this set of Sphinx documents. If None, it defaults to 106 | # " v documentation". 107 | html_title = u'GotGitHub' 108 | 109 | # A shorter title for the navigation bar. Default is the same as html_title. 110 | #html_short_title = None 111 | 112 | # The name of an image file (relative to this directory) to place at the top 113 | # of the sidebar. 114 | #html_logo = None 115 | 116 | # The name of an image file (within the static path) to use as favicon of the 117 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 118 | # pixels large. 119 | #html_favicon = None 120 | 121 | # Add any paths that contain custom static files (such as style sheets) here, 122 | # relative to this directory. They are copied after the builtin static files, 123 | # so a file named "default.css" will overwrite the builtin "default.css". 124 | html_static_path = ['_static'] 125 | 126 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 127 | # using the given strftime format. 128 | html_last_updated_fmt = '%Y/%m/%d' 129 | 130 | # If true, SmartyPants will be used to convert quotes and dashes to 131 | # typographically correct entities. 132 | #html_use_smartypants = True 133 | 134 | # Custom sidebar templates, maps document names to template names. 135 | #html_sidebars = {} 136 | 137 | # Additional templates that should be rendered to pages, maps page names to 138 | # template names. 139 | #html_additional_pages = {} 140 | 141 | # If false, no module index is generated. 142 | #html_domain_indices = True 143 | 144 | # If false, no index is generated. 145 | #html_use_index = True 146 | 147 | # If true, the index is split into individual pages for each letter. 148 | #html_split_index = False 149 | 150 | # If true, links to the reST sources are added to the pages. 151 | #html_show_sourcelink = True 152 | 153 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 154 | #html_show_sphinx = True 155 | 156 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 157 | #html_show_copyright = True 158 | 159 | # If true, an OpenSearch description file will be output, and all pages will 160 | # contain a tag referring to it. The value of this option must be the 161 | # base URL from which the finished HTML is served. 162 | #html_use_opensearch = '' 163 | 164 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 165 | #html_file_suffix = None 166 | 167 | # Output file base name for HTML help builder. 168 | htmlhelp_basename = 'GotGitHubdoc' 169 | 170 | 171 | # -- Options for LaTeX output -------------------------------------------------- 172 | 173 | # The paper size ('letter' or 'a4'). 174 | #latex_paper_size = 'letter' 175 | 176 | # The font size ('10pt', '11pt' or '12pt'). 177 | #latex_font_size = '10pt' 178 | 179 | # Grouping the document tree into LaTeX files. List of tuples 180 | # (source start file, target name, title, author, documentclass [howto/manual]). 181 | latex_documents = [ 182 | ('index', 'GotGitHub.tex', u'GotGitHub Documentation', 183 | u'Jiang Xin', 'manual'), 184 | ] 185 | 186 | # The name of an image file (relative to this directory) to place at the top of 187 | # the title page. 188 | #latex_logo = None 189 | 190 | # For "manual" documents, if this is true, then toplevel headings are parts, 191 | # not chapters. 192 | #latex_use_parts = False 193 | 194 | # If true, show page references after internal links. 195 | #latex_show_pagerefs = False 196 | 197 | # If true, show URL addresses after external links. 198 | #latex_show_urls = False 199 | 200 | # Additional stuff for the LaTeX preamble. 201 | #latex_preamble = '' 202 | 203 | # Documents to append as an appendix to all manuals. 204 | #latex_appendices = [] 205 | 206 | # If false, no module index is generated. 207 | #latex_domain_indices = True 208 | 209 | 210 | # -- Options for manual page output -------------------------------------------- 211 | 212 | # One entry per manual page. List of tuples 213 | # (source start file, name, description, authors, manual section). 214 | man_pages = [ 215 | ('index', 'gotgithub', u'GotGitHub Documentation', 216 | [u'Jiang Xin'], 1) 217 | ] 218 | 219 | language = 'zh_CN'; 220 | -------------------------------------------------------------------------------- /06-side-projects/gist.rst: -------------------------------------------------------------------------------- 1 | .. _gist: 2 | 3 | GitHub:Gist 4 | =============== 5 | 在GitHub网站的导航条上就有Gist子网站的链接: https://gist.github.com/ ,\ 6 | 在本节我们就揭开其面纱。 7 | 8 | .. figure:: /images/side-projects/gist-in-github-navbar.png 9 | :scale: 100 10 | 11 | 图6-1:GitHub上的Gist链接 12 | 13 | Gist作为一个粘贴数据的工具,就像 Pastie 网站\ [#]_\ 一样,可以很容易地将数据\ 14 | 粘贴在Gist网站中,并在其他网页中引用Gist中粘贴的数据。作为GitHub的一个子网站,\ 15 | 很自然地,Gist使用Git版本库对粘贴数据进行维护,这非常酷。 16 | 17 | .. _paste: 18 | 19 | 数据的粘贴和引用 20 | ----------------- 21 | 进入Gist网站的首页,就会看到一个大大的数据粘贴对话框,如图6-2所示。 22 | 23 | .. figure:: /images/side-projects/gist-homepage.png 24 | :scale: 100 25 | 26 | 图6-2:Gist网站首页 27 | 28 | 只要提供一行简单的描述、文件名,并粘贴文件内容,即可创建一个新的粘贴。创建\ 29 | 新的粘贴时,如果不指定文件名,将由系统自动指派。如图6-3所示。 30 | 31 | .. figure:: /images/side-projects/gist-create-new.png 32 | :scale: 100 33 | 34 | 图6-3:创建新的Gist 35 | 36 | 每一个新的粘贴称为一个Gist,并拥有唯一的URL。如果选择创建公开的Gist,URL中将\ 37 | 使用顺序递增的ID号,如本例创建的Gist的URL地址为:\ 38 | https://gist.github.com/1202870\ 。 39 | 40 | 若选择创建私有Gist,URL中则采用20位十六进制数字的ID,例如私密Gist:\ 41 | https://gist.github.com/78d67164131ec9e08dfe\ 。需要指出的是,私有Gist的\ 42 | 私密性并不像GitHub私有版本库的私密性那么强,只是其URL没有在用户Gist列表中列出,\ 43 | 也不能通过Gist网站搜索到而已。如果用户知道某私密Gist的URL地址,同样可以访问、\ 44 | 克隆该私密Gist,甚至创建基于该Gist创建分支(fork)。 45 | 46 | 当一个粘贴创建完毕后,会显示新建立的Gist页面,如图6-4所示。 47 | 48 | .. figure:: /images/side-projects/gist-created.png 49 | :scale: 100 50 | 51 | 图6-4:新创建的Gist 52 | 53 | 点击其中的“embed”(嵌入)按钮,就会显示一段用于嵌入其他网页的JavaScript代码,\ 54 | 如图6-5所示。 55 | 56 | .. figure:: /images/side-projects/gist-embed.png 57 | :scale: 100 58 | 59 | 图6-5:显示嵌入Java 60 | 61 | 62 | 对应的嵌入JavaScript代码如下: 63 | 64 | :: 65 | 66 | 67 | 68 | 69 | 将上面的JavaScript代码嵌入到网页(如博客\ [#]_\ )中,即可在相应的网页中嵌入\ 70 | 来自Gist的数据,并保持语法加亮等功能,如图6-6所示。 71 | 72 | .. figure:: /images/side-projects/gist-embed-in-blog.png 73 | :scale: 100 74 | 75 | 图6-6:博客中引用Gist数据 76 | 77 | .. _git-backend-gist: 78 | 79 | Gist背后的Git库 80 | ----------------- 81 | 创建的每一个Gist的背后都对应着一个Git版本库。例如之前创建的ID为1202870的Gist\ 82 | 对应的Git版本库,可以使用两种协议进行访问: 83 | 84 | * Git协议:\ ``git://gist.github.com/1202870.git`` 85 | * SSH协议:\ ``git@gist.github.com:1202870.git`` 86 | 87 | 可以通过Git命令克隆和操作该版本库。 88 | 89 | * 克隆该Gist对应的版本库。 90 | 91 | :: 92 | 93 | $ git clone git@gist.github.com:1202870.git 94 | $ cd 1202870 95 | 96 | * 查看修改日志。每一次对Gist中文件的修改对应于一次提交。 97 | 98 | :: 99 | 100 | $ git log 101 | commit 993d28a1319eca314ab2e3f4c46882cf328e5ff9 102 | Author: GotGitHub 103 | Date: Thu Sep 15 15:41:10 2011 +0800 104 | 105 | commit 4dd9cfd54e1522d0b62d92dd5f705a61e3fe8778 106 | Author: GotGitHub 107 | Date: Thu Sep 8 00:46:50 2011 -0700 108 | 109 | 110 | * 查看最近一次更改。 111 | 112 | :: 113 | 114 | $ git show HEAD 115 | commit 993d28a1319eca314ab2e3f4c46882cf328e5ff9 116 | Author: GotGitHub 117 | Date: Thu Sep 15 15:41:10 2011 +0800 118 | 119 | diff --git a/countdown.rb b/countdown.rb 120 | index a9d747b..9045738 100644 121 | --- a/countdown.rb 122 | +++ b/countdown.rb 123 | @@ -4,4 +4,8 @@ 124 | require 'Date' 125 | 126 | days=(DateTime.new(2012,10,15)-DateTime.now).ceil 127 | -puts "Maybe #{days} days left." 128 | \ No newline at end of file 129 | +if days >= 0 130 | + puts "Maybe #{days} days left." 131 | +else 132 | + puts "Passed for #{days.abs} days." 133 | +end 134 | \ No newline at end of file 135 | 136 | Gist网站并没有像GitHub网站那样对于Git版本库提供完整的、近乎复杂的操作界面和\ 137 | 工作流支持,而只提供了最基本的操作界面。如图6-7所示。 138 | 139 | .. figure:: /images/side-projects/gist-git-repo.png 140 | :scale: 100 141 | 142 | 图6-7:Gist版本库简易操作界面 143 | 144 | 在这个简易的Git版本库操作界面中,左侧是版本库的简介、文件预览以及在线编辑、\ 145 | 下载、加注星标\ [#]_\ 、版本库分支\ [#]_\ 等相关操作按钮。若以Gist创建者登录,\ 146 | 会在右侧看到他人基于该Gist创建分支的情况,但是并不提供GitHub才有的Pull Request\ 147 | 等功能。在界面的右侧还显示了Gist修订历史,和之前通过\ ``git log``\ 命令\ 148 | 从Git版本库看到的一样。 149 | 150 | .. _greasemonkey: 151 | 152 | Greasemonkey 153 | ----------------- 154 | Gist除了被用于粘贴数据(如代码块)并在网页中引用之外,还被用户挖掘出了新的\ 155 | 应用模式,例如用作Greasemonkey脚本的维护\ [#]_\ 。 156 | 157 | Greasemonkey\ [#]_\ 或类似插件为浏览器提供用户端JavaScript扩展功能,最早\ 158 | 出现于FireFox浏览器中。其他浏览器也陆续增加了对用户端JavaScript的支持,\ 159 | 如Safari的 NinjaKit\ [#]_\ 插件,IE的Trixie\ [#]_\ 插件,以及Chrome的\ 160 | Greasemetal插件\ [#]_\ 。关于如何在浏览器中安装并启用相应的插件,参照相关\ 161 | 插件网站的介绍,在此不做过多叙述。 162 | 163 | 当浏览器安装了 Greasemonkey 或类似插件之后,当访问扩展名为\ ``.user.js``\ 164 | 的URL时,会将该URL指向的JavaScript脚本安装在浏览器中,当访问指定的网址时会\ 165 | 自动调用相应的JavaScript脚本,修改相关网页内容或添加特效等等。 166 | 167 | 我针对《Git权威指南》官网的测试网页写了一个Greasemonkey示例脚本,可以展示\ 168 | 用户端JavaScript的魔法,这个用户端JavaScript脚本保存在Gist中:\ 169 | https://gist.github.com/1084591\ ,如图6-8所示。 170 | 171 | .. figure:: /images/side-projects/gist-greasemonkey.png 172 | :scale: 100 173 | 174 | 图6-8:保存Greasemonkey用户端脚本的Gist 175 | 176 | 该Greasemonkey脚本的文件名为\ ``click_more.user.js``\ ,该文件的文件头使用\ 177 | 特殊的注释语句为Greasemonkey提供相关的安装和注册信息,内容如下(为方便描述\ 178 | 添加了行号): 179 | 180 | :: 181 | 182 | 1 // ==UserScript== 183 | 2 // @name Click more for toggle 184 | 3 // @namespace gotgit 185 | 4 // @description Add a toogle effect at the location where anchor with a click-more css. 186 | 5 // @include http://www.worldhello.net/gotgit/demo* 187 | 6 // @include http://gotgit.github.com/gotgit/demo* 188 | 7 // @include http://www.ossxp.com/doc/gotgit/demo* 189 | 8 // @require http://code.jquery.com/jquery-1.6.2.min.js 190 | 9 // ==/UserScript== 191 | 192 | 其中第5、6、7行三条\ `include`\ 语句限定了此用户端JavaScript脚本的应用范围,\ 193 | 即只针对指定的URL(使用通配符)执行该脚本。第8行设定脚本依赖,即该脚本依赖\ 194 | jQuery,会在运行前到指定的URL地址加载jQuery脚本。 195 | 196 | 在安装该脚本前,先用浏览器访问网址\ http://www.worldhello.net/gotgit/demo.html\ ,\ 197 | 看看不加载用户端JavaScript脚本时网页的模样。该网页中包含一个长长的网上书店\ 198 | 列表,如图6-9所示。 199 | 200 | .. figure:: /images/side-projects/gist-user-js-apply-before.png 201 | :scale: 100 202 | 203 | 图6-9:应用用户端JavaScript脚本前的网页内容 204 | 205 | 接下来开始安装该用户端JavaScript脚本。安装非常简单,只要点击图6-8的Gist当中\ 206 | 的脚本文件对应的“raw”链接,即点击脚本文件原始内容链接\ [#]_\ 即可开启安装。\ 207 | 这是因为该URL以\ ``.user.js``\ 结尾,会被Greasemonkey(或类似插件)识别并安装,\ 208 | 如图6-10是Greasemonkey弹出的用户端脚本安装界面。 209 | 210 | .. figure:: /images/side-projects/gist-user-js-install.png 211 | :scale: 100 212 | 213 | 图6-10:安装用户端JavaScript脚本 214 | 215 | 用户端脚本安装完毕后,再访问同样的测试网页\ 216 | http://www.worldhello.net/gotgit/demo.html\ ,会发现网页中出现了\ 217 | 一个名为“更多”的可点击链接,长长的网上书店列表不见了。如图6-11所示。 218 | 219 | .. figure:: /images/side-projects/gist-user-js-apply-after.png 220 | :scale: 100 221 | 222 | 图6-11:应用用户端JavaScript脚本后的网页内容 223 | 224 | 如果查看网页源码,会发现该网页中根本没有包含和调用任何JavaScript脚本,只是\ 225 | 在页面源码中包含着一个没有任何实质输出的标签: 226 | 227 | :: 228 | 229 |

230 | 231 | 实际上正是这个特殊的标签被Greasemonkey所加载的用户端脚本识别,为HTML网页\ 232 | 添加了特效。 233 | 234 | .. _gist-cli: 235 | 236 | 命令行操作Gist 237 | ----------------- 238 | GitHub开发者还写了一个名为gist的命令行工具对Gist进行操作,地址见\ 239 | https://github.com/defunkt/gist\ 。 240 | 241 | 该工具使用Ruby开发,对两个特定的Git风格的配置变量进行如下设置后,即可实现在\ 242 | 命令行中自动以特定用户身份登录操作Gist。 243 | 244 | :: 245 | 246 | $ git config --global github.user "your-github-username" 247 | $ git config --global github.token "your-github-token" 248 | 249 | 其中\ ``github.token``\ 中保存的是用户的API TOKEN,这在“2.1 创建GitHub账号”\ 250 | 一节有过介绍。 251 | 252 | 使用gist命令行工具创建新的Gist非常简单。 253 | 254 | * 创建包含一个文件(如\ ``script.py``\ )的Gist,使用如下命令。 255 | 256 | :: 257 | 258 | $ gist script.py 259 | 260 | * 创建包含多个文件的Gist,使用类似如下的命令。 261 | 262 | :: 263 | 264 | $ gist script.js notes.txt 265 | 266 | 如果对命令行操作方式感兴趣,参考gist工具网站的\ `README`\ 文件。 267 | 268 | 269 | ---- 270 | 271 | .. [#] http://pastie.org/ 272 | .. [#] http://www.worldhello.net/2011/09/14/2521.html 273 | .. [#] 对感兴趣的Gist进行收藏,参见博客 https://github.com/blog/673-starring-gists 。 274 | .. [#] 访问他人创建的Gist时,提供分支功能按钮。 275 | .. [#] https://github.com/blog/302-gist-for-greasemonkey 276 | .. [#] https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/ 277 | .. [#] http://ss-o.net/safari/extension/NinjaKit.safariextz 278 | .. [#] http://www.bhelpuri.net/Trixie/ 279 | .. [#] 版本4之后的Chrome内置了Greasemonkey类似功能,无需额外插件。 280 | .. [#] https://gist.github.com/raw/1084591/73c3e4dfc827732241ca753fe7bb985c14c9d7ab/click_more.user.js 281 | -------------------------------------------------------------------------------- /04-work-with-others/050-issue.rst: -------------------------------------------------------------------------------- 1 | .. _issues: 2 | 3 | 缺陷跟踪 4 | =============== 5 | 6 | 缺陷跟踪(Bug Tracking)是软件研发流程中重要的一环,集项目需求管理和缺陷管理\ 7 | 于一身,通过对研发工作流的控制帮助团队建立规范的研发体系。GitHub提供轻量级的\ 8 | 缺陷跟踪模块,称为Issues。小巧、易用的Issues模块能与Pull Request紧密整合,\ 9 | 是Pull Request工作流的有益补充。 10 | 11 | 一个小型、管理文档和网页的项目,使用Pull Request往往就足够了。试想如果贡献者\ 12 | 能够直接修改代码(Fork and edit this file)并通过Pull Request贡献给项目核心\ 13 | 开发者,那么为什么还要通过Issues模块报告错误并由他人来更改呢?但是对于大型\ 14 | 项目需要做需求管理,或者参与代码开发有难度,则非常有必要通过Issues模块启用\ 15 | 缺陷跟踪系统,提供更多途径让贡献者参与到项目中来。 16 | 17 | 缺陷跟踪可以通过项目的管理页面开启或关闭,如图4-45所示。 18 | 19 | .. figure:: /images/issues/project-admin-features-issue.png 20 | :scale: 100 21 | 22 | 图4-45:开启或关闭Issues模块 23 | 24 | .. _labels: 25 | 26 | 标签 27 | -------- 28 | 29 | 缺陷跟踪系统通常可用于管理多种不同类型的问题:需求、缺陷或其它,也可以通过\ 30 | 项目不同模块、组件来为问题分类。GitHub在问题分类的实现上非常简单,通过标签\ 31 | (label)来为问题建立分类。 32 | 33 | 开启Issues模块后,项目的菜单中多出一个“Issues”项,点击则进入问题浏览界面,\ 34 | 如图4-46所示。左侧的边栏是问题过滤器,由上至下分为三个部分。最上面的过滤器\ 35 | 根据问题的所有者对问题进行筛选,默认选择所有人的问题。中间的过滤器是根据\ 36 | 里程碑对问题进行筛选,默认未选定任何里程碑(初始尚未创建任何里程碑),不对\ 37 | 问题进行里程碑过滤。最下面的过滤器依据问题标签对问题进行筛选,初始标签尚未\ 38 | 创建。 39 | 40 | .. figure:: /images/issues/issue-no-label.png 41 | :scale: 100 42 | 43 | 图4-46:尚未定义标签 44 | 45 | 鼠标点击左侧边栏标签过滤器中的新建标签的文本框,显示如图4-47所示的新建标签\ 46 | 界面。输入新的标签名,并为标签选择一个颜色,创建新的标签。 47 | 48 | .. figure:: /images/issues/issue-new-label.png 49 | :scale: 100 50 | 51 | 图4-47:创建新标签 52 | 53 | 创建的标签显示在过滤器中,如图4-48所示。点击过滤器中的标签则对问题进行筛选,\ 54 | 取消过滤器可以点击图中标记的“Clear active milestone and label filters”链接。 55 | 56 | .. figure:: /images/issues/issue-labels.png 57 | :scale: 100 58 | 59 | 图4-48:过滤器中的标签选项 60 | 61 | 标签过滤器下方的“Manage Labels”按钮用于管理标签,下面的输入框用于创建新的标签。 62 | 63 | .. _milestone: 64 | 65 | 里程碑 66 | ----------- 67 | 68 | 里程碑(Milestones)是项目进度管理的重要工具。在传统项目管理中,里程碑对应\ 69 | 于一个项目开发计划、一个软件版本;在敏捷项目管理中,里程碑对应于一个Sprint\ 70 | (冲刺);在软件代码的版本库中则对应于一个标签(tag)或分支(branch)。 71 | 72 | 在Issues模块中的“Milestones”页面用于里程碑管理。创建新的里程碑需要输入里程碑\ 73 | 名称和里程碑的截止时间,如图4-49所示。 74 | 75 | .. figure:: /images/issues/issue-new-milestone.png 76 | :scale: 100 77 | 78 | 图4-49:创建新里程碑 79 | 80 | 创建的里程碑以进度条形式显示在里程碑页面中,如图4-50所示定义了两个里程碑。\ 81 | 这两个里程碑的时间跨度定义的太长,敏捷的项目管理从来不这么定义。 82 | 83 | .. figure:: /images/issues/issue-milestones.png 84 | :scale: 100 85 | 86 | 图4-50:里程碑列表 87 | 88 | .. _lifecycle: 89 | 90 | Issue的生命周期 91 | ----------------- 92 | 93 | GitHub的Issues模块非常简单,对标签和里程碑进行简单的设置后,基本上就完成了\ 94 | Issues模块的配置工作,接下来就是如何创建和修改Issue,完成项目的缺陷跟踪和\ 95 | 需求管理等,这才是Issues模块的主要工作。 96 | 97 | 每个Issue都有自己的生命周期,从问题的创建,到问题的指派,再到问题的解决,\ 98 | 直至问题的关闭。图4-51就是以普通贡献者身份为项目创建Issue。 99 | 100 | .. figure:: /images/issues/issue-new-by-non-member.png 101 | :scale: 100 102 | 103 | 图4-51:以普通贡献者身份创建问题 104 | 105 | 录入问题标题和描述后,点击“Submit new issue”按钮,完成问题创建。图4-52显示了\ 106 | 新建立的问题,可以看出新建问题尚未设置标签。 107 | 108 | .. figure:: /images/issues/issue-created.png 109 | :scale: 100 110 | 111 | 图4-52:新创建的问题尚未添加标签等 112 | 113 | 普通贡献者创建问题时只能录入问题的标题和描述,而不能设置问题的指派\ 114 | (谁来负责)、添加标签和设置里程碑。如果希望问题通知到特定的开发者,\ 115 | 可以在问题描述中以“@用户名”的方式通知到该用户\ [#]_\ ,这也是众多社交\ 116 | 软件通行的做法。 117 | 118 | 项目成员创建问题时,拥有更大权限,也有更多的可选项。如图4-53所示。 119 | 120 | .. figure:: /images/issues/issue-new-by-member.png 121 | :scale: 100 122 | 123 | 图4-53:以项目成员身份创建问题 124 | 125 | 完成上述两个问题的创建后,问题浏览界面显示新创建的两个问题,一个以项目成员\ 126 | 身份创建的问题已经被设置了“缺陷”的标签,而另外一个问题则没有设置任何标签。\ 127 | 如图4-54所示。 128 | 129 | .. figure:: /images/issues/issue-list.png 130 | :scale: 100 131 | 132 | 图4-54:所有问题列表 133 | 134 | 以项目成员身份登录,在问题浏览界面即可为问题重新设定标签,指派负责人,设置\ 135 | 里程碑,以及关闭问题等。如图4-55所示。 136 | 137 | .. figure:: /images/issues/issue-update.png 138 | :scale: 100 139 | 140 | 图4-55:为问题添加指派、里程碑和标签 141 | 142 | 在问题浏览页面的过滤器中选择里程碑”Version 4.0“,可以看到两条问题都显示出来,\ 143 | 这是因为这两条问题都属于该里程碑。里程碑的进度条显示进度为零,这是因为里程碑\ 144 | 所包含的全部(两个)问题都处于打开状态,尚未解决。如图4-56所示。 145 | 146 | .. figure:: /images/issues/issue-list-with-milestone.png 147 | :scale: 100 148 | 149 | 图4-56:通过里程碑筛选问题 150 | 151 | 邮件通知功能是缺陷跟踪系统推动工作流的重要工具,GitHub的Issues模块也具有邮件\ 152 | 通知功能。除了像其他缺陷跟踪系统在收到邮件通知后,访问Web界面参与问题的讨论外,\ 153 | 还可以直接以邮件回复的功能参与到工作流中\ [#]_\ 。 154 | 155 | GitHub还支持版本库提交和问题建立关联,只要提交说明中出现“#xxx”(Issue编号)\ 156 | 字样。如果在提交说明中的问题编号前出现特定关键字,还可以关闭问题。支持的关键字有: 157 | 158 | * fixes #xxx 159 | * fixed #xxx 160 | * fix #xxx 161 | * closes #xxx 162 | * close #xxx 163 | * closed #xxx 164 | 165 | 下面就以\ ``gotgithub/helloworld``\ 版本库为例,关闭编号为“#1”的问题。 166 | 167 | * 克隆版本库,若本地工作区尚不存在。 168 | 169 | :: 170 | 171 | $ git clone git@github.com:gotgithub/helloworld.git 172 | $ cd helloworld 173 | 174 | * 编辑文件\ `src/main.c`\ ,改正“问题#1”发现的文字错误。 175 | 176 | :: 177 | 178 | $ vi src/main.c 179 | $ git diff 180 | diff --git a/src/main.c b/src/main.c 181 | index 3daf9fe..f974b49 100644 182 | --- a/src/main.c 183 | +++ b/src/main.c 184 | @@ -19,7 +19,7 @@ int usage(int code) 185 | " say hello to the world.\n\n" 186 | " hello -v, --version\n" 187 | " show version.\n\n" 188 | - " hello -h, -help\n" 189 | + " hello -h, --help\n" 190 | " this help screen.\n\n"), _VERSION); 191 | return code; 192 | } 193 | 194 | 195 | * 将修改添加至暂存区。 196 | 197 | :: 198 | 199 | $ git add -u 200 | 201 | * 提交,并在提交说明中用\ ``fixed #xxx``\ 关键字关闭相关问题。 202 | 203 | :: 204 | 205 | $ git commit -m "Fixed #1: -help should be --help." 206 | 207 | * 向GitHub版本库推送。 208 | 209 | :: 210 | 211 | $ git push 212 | 213 | 推送完毕后,在问题浏览界面可以看到里程碑“Version 4.0”的进度已经完成了一半,\ 214 | 即其中一个问题(#1)已经完成并关闭。如图4-57所示。 215 | 216 | .. figure:: /images/issues/issue-milestone-half-closed.png 217 | :scale: 100 218 | 219 | 图4-57:关闭一个问题,里程碑完成50% 220 | 221 | 查看已经完成的问题(#1),可以看到其中关联到一个提交,该提交正是我们刚刚创建的。\ 222 | 如图4-58所示。 223 | 224 | .. figure:: /images/issues/issue-closed-by-commit.png 225 | :scale: 100 226 | 227 | 图4-58:已关闭问题中的提交链接 228 | 229 | 点击关联的提交,显示如图4-59的提交界面,出现在提交说明中的问题编号也可点击,\ 230 | 指向缺陷追踪系统中该问题的链接。 231 | 232 | .. figure:: /images/issues/commit-link-to-issue.png 233 | :scale: 100 234 | 235 | 图4-59:提交中的问题链接 236 | 237 | .. _pull-request-as-issue: 238 | 239 | Pull Requst也是Issue 240 | -------------------------- 241 | 242 | Pull Request和Issue一样,也是一种对项目的反馈,而且是更为主动的反馈。GitHub的\ 243 | Issues模块将Pull Request也纳入到问题的管理之中,完美地将Pull Request整合到问题\ 244 | 追踪的框架之中。 245 | 246 | 为了弄清二者之间的关联,首先创建一个Pull Request。 247 | 248 | 以非项目成员(如用户 omnidroid)的账号访问\ ``gotgithub/helloworld``\ 项目,\ 249 | 查看文件\ ``src/Makefile``\ ,点击“Fork and edit this file”按钮快速创建派生\ 250 | 项目,如图4-60所示。 251 | 252 | .. figure:: /images/issues/fork-and-edit-btn-for-issue.png 253 | :scale: 100 254 | 255 | 图4-60:在线编辑并创建派生项目 256 | 257 | 通过GitHub提供的在线编辑功能修改\ ``src/Makefile``\ 文件,修改完毕后撰写提交\ 258 | 说明,点击“Propose File Change”按钮提交。如图4-61所示。 259 | 260 | .. figure:: /images/issues/fork-and-edit-form-for-issue.png 261 | :scale: 100 262 | 263 | 图4-61:在线编辑并提交 264 | 265 | 在提交说明中特意使用了“Fixed #2”关键字,以便该提交被上游版本库接纳后能够关闭关联的问题。 266 | 267 | 当完成提交后,GitHub会自动开启创建新的Pull Request对话框,如图4-62所示。 268 | 269 | .. figure:: /images/issues/new-pull-request-for-issue.png 270 | :scale: 100 271 | 272 | 图4-62:创建Pull Request 273 | 274 | Pull Request创建完毕后,除了在菜单项“Pull Requests”中有显示外,在“Issues”的\ 275 | 问题浏览页面中也会显示。如图4-63所示,新建立的Pull Request的编号不是从壹开始\ 276 | 创建,而是接着问题的编号顺序创建,所以当Pull Request出现在问题列表中时,\ 277 | 如果不注意后面的山型的分支图标,根本意识不到这不是一个普通的问题(Issue),\ 278 | 而是一个Pull Request。 279 | 280 | .. figure:: /images/issues/issue-list-with-pull-request.png 281 | :scale: 100 282 | 283 | 图4-63:Pull Request也显示在Issues中 284 | 285 | 显示在问题浏览界面中的Pull Request和问题一样,可以为其设置标签、指派负责人、\ 286 | 设置里程碑。如图4-64所示。 287 | 288 | .. figure:: /images/issues/pull-request-update-as-issue.png 289 | :scale: 100 290 | 291 | 图4-64:可以像更新其他Issue那样更新Pull Request 292 | 293 | 当Pull Request归类到里程碑“Version 4.0”中时,在过滤器打开里程碑“Version 4.0”,\ 294 | 可以看到本来已经完成50%的进度,由于新增了一个“问题”(Pull Request),导致\ 295 | 进度降低了。如图4-65所示。 296 | 297 | .. figure:: /images/issues/milestone-progress-with-pull-request.png 298 | :scale: 100 299 | 300 | 图4-65:里程碑进度调整 301 | 302 | 点击编号为“#3”的问题(Pull Request),会进入到Pull Request页面。点击页面中的\ 303 | “Merge pull request”按钮实现Pull Request的合并。如图4-66所示。 304 | 305 | .. figure:: /images/issues/merge-pull-request-for-issue.png 306 | :scale: 100 307 | 308 | 图4-66:在线合并Pull Request 309 | 310 | 点击“Confirm Merge”确认合并,如图4-67所示。 311 | 312 | .. figure:: /images/issues/merge-pull-request-for-issue-confirm.png 313 | :scale: 100 314 | 315 | 图4-67:确认合并Pull Request 316 | 317 | 完成合并后,查看该Pull Request,可以看到该Pull Request已经关闭。如图4-68所示。 318 | 319 | .. figure:: /images/issues/pull-request-close-for-issue.png 320 | :scale: 100 321 | 322 | 图4-68:Pull Request自动关闭 323 | 324 | 如果再回到问题浏览界面,能够猜到现在里程碑“Version 4.0”的进度是多少么? 325 | 326 | 由于关闭了编号为“#3”的Pull Request,以及所合并的Pull Request中对应提交的提交\ 327 | 说明的指令同时关闭了编号为“#2”的问题,所以现在里程碑“Version 4.0”关联的所有\ 328 | 问题均已关闭。里程碑也显示已关闭,即里程碑完成度为100%。 329 | 330 | .. figure:: /images/issues/milestone-closed.png 331 | :scale: 100 332 | 333 | 图4-69:里程碑关闭 334 | 335 | ---- 336 | 337 | .. [#] https://github.com/blog/821-mention-somebody-they-re-notified 338 | .. [#] https://github.com/blog/811-reply-to-comments-from-email 339 | -------------------------------------------------------------------------------- /_theme/gotgithub/static/sphinxdoc.css: -------------------------------------------------------------------------------- 1 | /* 2 | * sphinxdoc.css_t 3 | * ~~~~~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- sphinxdoc theme. Originally created by 6 | * Armin Ronacher for Werkzeug. 7 | * 8 | * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 9 | * :license: BSD, see LICENSE for details. 10 | * 11 | */ 12 | 13 | @import url("basic.css"); 14 | 15 | /* -- page layout ----------------------------------------------------------- */ 16 | 17 | body { 18 | font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 19 | 'Verdana', sans-serif; 20 | font-size: 14px; 21 | letter-spacing: -0.01em; 22 | line-height: 150%; 23 | text-align: center; 24 | background-color: #BFD1D4; 25 | color: black; 26 | padding: 0; 27 | border: 1px solid #aaa; 28 | 29 | margin: 0px 80px 0px 80px; 30 | min-width: 740px; 31 | } 32 | 33 | div.document { 34 | background-color: white; 35 | text-align: left; 36 | background-image: url(contents.png); 37 | background-repeat: repeat-x; 38 | } 39 | 40 | div.bodywrapper { 41 | margin: 0 240px 0 0; 42 | border-right: 1px solid #ccc; 43 | } 44 | 45 | div.body { 46 | margin: 0; 47 | padding: 0.5em 20px 20px 20px; 48 | } 49 | 50 | div.related { 51 | font-size: 1em; 52 | } 53 | 54 | div.related ul { 55 | background-image: url(navigation.png); 56 | height: 2em; 57 | border-top: 1px solid #ddd; 58 | border-bottom: 1px solid #ddd; 59 | } 60 | 61 | div.related ul li { 62 | margin: 0; 63 | padding: 0; 64 | height: 2em; 65 | float: left; 66 | } 67 | 68 | div.related ul li.right { 69 | float: right; 70 | margin-right: 5px; 71 | } 72 | 73 | div.related ul li a { 74 | margin: 0; 75 | padding: 0 5px 0 5px; 76 | line-height: 1.75em; 77 | color: #EE9816; 78 | } 79 | 80 | div.related ul li a:hover { 81 | color: #3CA8E7; 82 | } 83 | 84 | div.sphinxsidebarwrapper { 85 | padding: 0; 86 | } 87 | 88 | div.sphinxsidebar { 89 | margin: 0; 90 | padding: 0.5em 15px 15px 0; 91 | width: 210px; 92 | float: right; 93 | font-size: 1em; 94 | text-align: left; 95 | } 96 | 97 | div.sphinxsidebar h3, div.sphinxsidebar h4 { 98 | margin: 1em 0 0.5em 0; 99 | font-size: 1em; 100 | padding: 0.1em 0 0.1em 0.5em; 101 | color: white; 102 | border: 1px solid #86989B; 103 | background-color: #AFC1C4; 104 | } 105 | 106 | div.sphinxsidebar h3 a { 107 | color: white; 108 | } 109 | 110 | div.sphinxsidebar ul { 111 | padding-left: 1.5em; 112 | margin-top: 7px; 113 | padding: 0; 114 | line-height: 130%; 115 | } 116 | 117 | div.sphinxsidebar ul ul { 118 | margin-left: 20px; 119 | } 120 | 121 | div.footer { 122 | background-color: #E3EFF1; 123 | color: #86989B; 124 | padding: 3px 8px 3px 0; 125 | clear: both; 126 | font-size: 0.8em; 127 | text-align: right; 128 | } 129 | 130 | div.footer a { 131 | color: #86989B; 132 | text-decoration: underline; 133 | } 134 | 135 | /* -- body styles ----------------------------------------------------------- */ 136 | 137 | p { 138 | margin: 0.8em 0 0.5em 0; 139 | } 140 | 141 | a { 142 | color: #CA7900; 143 | text-decoration: none; 144 | } 145 | 146 | a:hover { 147 | color: #2491CF; 148 | } 149 | 150 | div.body a { 151 | text-decoration: none; 152 | font-weight: normal; 153 | } 154 | 155 | h1 { 156 | margin: 0; 157 | padding: 0.7em 0 0.3em 0; 158 | font-size: 1.5em; 159 | color: #11557C; 160 | } 161 | 162 | h2 { 163 | margin: 1.3em 0 0.2em 0; 164 | font-size: 1.35em; 165 | padding: 0; 166 | } 167 | 168 | h3 { 169 | margin: 1em 0 -0.3em 0; 170 | font-size: 1.2em; 171 | } 172 | 173 | div.body h1 a, div.body h2 a, div.body h3 a, div.body h4 a, div.body h5 a, div.body h6 a { 174 | color: black!important; 175 | } 176 | 177 | h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor { 178 | display: none; 179 | margin: 0 0 0 0.3em; 180 | padding: 0 0.2em 0 0.2em; 181 | color: #aaa!important; 182 | } 183 | 184 | h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, 185 | h5:hover a.anchor, h6:hover a.anchor { 186 | display: inline; 187 | } 188 | 189 | h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover, 190 | h5 a.anchor:hover, h6 a.anchor:hover { 191 | color: #777; 192 | background-color: #eee; 193 | } 194 | 195 | a.headerlink { 196 | color: #c60f0f!important; 197 | font-size: 1em; 198 | margin-left: 6px; 199 | padding: 0 4px 0 4px; 200 | text-decoration: none!important; 201 | } 202 | 203 | a.headerlink:hover { 204 | background-color: #ccc; 205 | color: white!important; 206 | } 207 | 208 | cite, code, tt { 209 | font-family: 'Consolas', 'Deja Vu Sans Mono', 210 | 'Bitstream Vera Sans Mono', monospace; 211 | font-size: 0.95em; 212 | letter-spacing: 0.01em; 213 | } 214 | 215 | tt { 216 | background-color: #f2f2f2; 217 | border-bottom: 1px solid #ddd; 218 | color: #333; 219 | } 220 | 221 | tt.descname, tt.descclassname, tt.xref { 222 | border: 0; 223 | } 224 | 225 | hr { 226 | border: 1px solid #abc; 227 | margin: 2em; 228 | } 229 | 230 | a tt { 231 | border: 0; 232 | color: #CA7900; 233 | } 234 | 235 | a tt:hover { 236 | color: #2491CF; 237 | } 238 | 239 | pre { 240 | font-family: 'Consolas', 'Deja Vu Sans Mono', 241 | 'Bitstream Vera Sans Mono', monospace; 242 | font-size: 0.95em; 243 | letter-spacing: 0.015em; 244 | line-height: 120%; 245 | padding: 0.5em; 246 | border: 1px solid #ccc; 247 | background-color: #f8f8f8; 248 | } 249 | 250 | td pre { 251 | font-family: 'Consolas', 'Deja Vu Sans Mono', 252 | 'Bitstream Vera Sans Mono', monospace; 253 | font-size: 1em; 254 | letter-spacing: 0.015em; 255 | line-height: 120%; 256 | padding: 0em; 257 | border: 0; 258 | background-color: #ffffff; 259 | } 260 | 261 | pre a { 262 | color: inherit; 263 | text-decoration: underline; 264 | } 265 | 266 | td.linenos pre { 267 | padding: 0.5em 0; 268 | } 269 | 270 | div.quotebar { 271 | background-color: #f8f8f8; 272 | max-width: 250px; 273 | float: right; 274 | padding: 2px 7px; 275 | border: 1px solid #ccc; 276 | } 277 | 278 | div.topic { 279 | background-color: #f8f8f8; 280 | } 281 | 282 | table { 283 | border-collapse: collapse; 284 | margin: 0 -0.5em 0 -0.5em; 285 | } 286 | 287 | table td, table th { 288 | padding: 0.2em 0.5em 0.2em 0.5em; 289 | } 290 | 291 | div.admonition, div.warning { 292 | font-size: 0.9em; 293 | margin: 1em 0 1em 0; 294 | border: 1px solid #86989B; 295 | background-color: #f7f7f7; 296 | padding: 0; 297 | } 298 | 299 | div.admonition p, div.warning p { 300 | margin: 0.5em 1em 0.5em 1em; 301 | padding: 0; 302 | } 303 | 304 | div.admonition pre, div.warning pre { 305 | margin: 0.4em 1em 0.4em 1em; 306 | } 307 | 308 | div.admonition p.admonition-title, 309 | div.warning p.admonition-title { 310 | margin: 0; 311 | padding: 0.1em 0 0.1em 0.5em; 312 | color: white; 313 | border-bottom: 1px solid #86989B; 314 | font-weight: bold; 315 | background-color: #AFC1C4; 316 | } 317 | 318 | div.warning { 319 | border: 1px solid #940000; 320 | } 321 | 322 | div.warning p.admonition-title { 323 | background-color: #CF0000; 324 | border-bottom-color: #940000; 325 | } 326 | 327 | div.admonition ul, div.admonition ol, 328 | div.warning ul, div.warning ol { 329 | margin: 0.1em 0.5em 0.5em 3em; 330 | padding: 0; 331 | } 332 | 333 | div.versioninfo { 334 | margin: 1em 0 0 0; 335 | border: 1px solid #ccc; 336 | background-color: #DDEAF0; 337 | padding: 8px; 338 | line-height: 1.3em; 339 | font-size: 0.9em; 340 | } 341 | 342 | .viewcode-back { 343 | font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 344 | 'Verdana', sans-serif; 345 | } 346 | 347 | div.viewcode-block:target { 348 | background-color: #f4debf; 349 | border-top: 1px solid #ac9; 350 | border-bottom: 1px solid #ac9; 351 | } 352 | 353 | div.figure { 354 | text-align: center; 355 | margin: 1.5em 0 1.5em 0; 356 | } 357 | 358 | div.figure p.caption { 359 | font-size: 0.9em; 360 | } 361 | 362 | div.figure img { 363 | border: 1px solid #ccc; 364 | padding: 5px; 365 | } 366 | 367 | .footnote-reference { 368 | font-size: smaller; 369 | line-height: 0; 370 | vertical-align: super; 371 | margin: 0; 372 | font-weight: normal; 373 | } 374 | 375 | div.body a.footnote-reference { 376 | text-decoration: none; 377 | } 378 | 379 | table.classic { 380 | border: 2px solid black; 381 | border-collapse: collapse; 382 | background: white; 383 | } 384 | 385 | table.classic td, table.docutils th { 386 | padding: 1px 8px 1px 5px; 387 | border: 1px solid black; 388 | } 389 | 390 | table.classic th { 391 | background: #aaa; 392 | color: white; 393 | border: 1px solid black; 394 | text-align: center; 395 | } 396 | 397 | .strike { 398 | text-decoration: line-through; 399 | } 400 | 401 | .underline { 402 | text-decoration: underline; 403 | } 404 | -------------------------------------------------------------------------------- /03-project-hosting/020-repo-operation.rst: -------------------------------------------------------------------------------- 1 | .. _git-operations: 2 | 3 | 操作版本库 4 | =============== 5 | 6 | .. _noff-push: 7 | 8 | 强制推送 9 | ---------- 10 | 11 | 细心的读者可能从图3-4已经看出,显示的提交者并非gotgithub用户,而是一个名为\ 12 | ossxp-com的用户,这是因为GitHub是通过提交中的邮件地址来对应到GitHub用户的。\ 13 | 看看提交说明: 14 | 15 | :: 16 | 17 | $ git log --pretty=fuller 18 | commit 92dee9b8125afc9a606394ed463f9f264f2d3d58 19 | Author: Jiang Xin 20 | AuthorDate: Wed Dec 14 14:52:40 2011 +0800 21 | Commit: Jiang Xin 22 | CommitDate: Wed Dec 14 14:52:40 2011 +0800 23 | 24 | README for this project. 25 | 26 | 原来提交用户设置的邮件地址并非gotgithub用户设置的邮件地址。补救办法就是对此\ 27 | 提交进行修改,然后强制推送到GitHub。 28 | 29 | * 重新设置\ ``user.name``\ 和\ ``user.email``\ 配置变量。 30 | 31 | 因为gotgithub是一个仅在本书使用的示例账号,我可不想影响本地其他项目的提交,\ 32 | 因此下面的设置命令没有使用\ ``--global``\ 参数,只对本地\ ``helloworld``\ 33 | 版本库进行设置。 34 | 35 | :: 36 | 37 | $ git config user.name "Jiang Xin" 38 | $ git config user.email "gotgithub@gmail.com" 39 | 40 | * 执行Git修补提交命令。 41 | 42 | 注意使用参数\ ``--reset-author``\ 会将提交信息中的属性\ ``Author``\ 连同\ 43 | ``AuthorDate``\ 一并修改,否则只修改\ ``Commit``\ 和\ ``CommitDate``\ 。\ 44 | 参数\ ``-C HEAD``\ 维持提交说明不变。 45 | 46 | :: 47 | 48 | $ git commit --amend --reset-author -C HEAD 49 | 50 | * 查看提交日志,发现提交者信息和作者信息都已经更改。 51 | 52 | :: 53 | 54 | $ git log --pretty=fuller 55 | commit e1e52d99fa71fd6f606903efa9da04fd0055fca9 56 | Author: Jiang Xin 57 | AuthorDate: Wed Dec 14 15:05:47 2011 +0800 58 | Commit: Jiang Xin 59 | CommitDate: Wed Dec 14 15:05:47 2011 +0800 60 | 61 | README for this project. 62 | 63 | * 直接推送会报错。 64 | 65 | 错误信息中出现\ ``non-fast-forword``\ (非快进式推送),含义为要推送的提交并非\ 66 | 继远程版本库最新提交之后的提交,推送会造成覆盖导致服务器端有数据(提交)会丢失。 67 | 68 | :: 69 | 70 | $ git push 71 | To git@github.com:gotgithub/helloworld.git 72 | ! [rejected] master -> master (non-fast-forward) 73 | error: failed to push some refs to 'git@github.com:gotgithub/helloworld.git' 74 | To prevent you from losing history, non-fast-forward updates were rejected 75 | Merge the remote changes (e.g. 'git pull') before pushing again. See the 76 | 'Note about fast-forwards' section of 'git push --help' for details. 77 | 78 | * 使用强制推送。 79 | 80 | 对于此例,考虑到还没有其他人关注\ ``helloworld``\ 这个刚刚建立的示例项目,\ 81 | 显然不需要向上面命令的错误信息所提示的那样先执行\ ``git pull``\ 合并上游版本库\ 82 | 再推送,而是选择强制推送,以新的修补提交覆盖包含错误提交者ID的提交。 83 | 84 | :: 85 | 86 | $ git push -f 87 | Counting objects: 3, done. 88 | Delta compression using up to 2 threads. 89 | Compressing objects: 100% (2/2), done. 90 | Writing objects: 100% (3/3), 629 bytes, done. 91 | Total 3 (delta 0), reused 0 (delta 0) 92 | To git@github.com:gotgithub/helloworld.git 93 | + 92dee9b...e1e52d9 master -> master (forced update) 94 | 95 | 完成强制推送后,再查看GitHub项目页面,会发现提交者已经显示为gotgithub用户。\ 96 | 如图3-7所示。 97 | 98 | .. figure:: /images/project-hosting/force-push.png 99 | :scale: 100 100 | 101 | 图3-7:强制更新后,提交者已更改 102 | 103 | .. _new-branch: 104 | 105 | 新建分支 106 | --------- 107 | 108 | Git的分支就是保存在\ ``.git/refs/heads/``\ 命名空间下的引用。引用文件的内容是\ 109 | 该分支对应的提交ID。当前版本库中的默认分支\ ``master``\ 就对应于文件\ 110 | ``.git/refs/heads/master``\ 。 111 | 112 | 若在GitHub版本库中创建分支,首先要在本地版本库中创建新的分支(即引用),\ 113 | 然后用推送命令将本地创建的新的引用连同所指向的提交推送到GitHub版本库中\ 114 | 完成GitHub上分支的创建。操作如下: 115 | 116 | * 本地版本库中建立新分支\ ``mybranch1``\ 。 117 | 118 | 创建分支有多种方法,如使用\ ``git branch``\ 命令,但最为便捷的就是\ 119 | ``git checkout -b``\ 命令,同时完成新分支的创建和分支切换。 120 | 121 | :: 122 | 123 | $ git checkout -b mybranch1 124 | Switched to a new branch 'mybranch1' 125 | 126 | * 为了易于识别,添加一个新文件\ ``hello1``\ ,并提交。 127 | 128 | :: 129 | 130 | $ touch hello1 131 | $ git add hello1 132 | $ git commit -m "add hello1 for mark." 133 | [mybranch1 f46a284] add hello1 for mark. 134 | 0 files changed, 0 insertions(+), 0 deletions(-) 135 | create mode 100644 hello1 136 | 137 | * 通过推送命令,将本地分支\ ``mybranch1``\ 推送到GitHub远程版本库,完成在GitHub\ 138 | 上的新分支创建。 139 | 140 | :: 141 | 142 | $ git push -u origin mybranch1 143 | Counting objects: 4, done. 144 | Delta compression using up to 2 threads. 145 | Compressing objects: 100% (2/2), done. 146 | Writing objects: 100% (3/3), 281 bytes, done. 147 | Total 3 (delta 0), reused 0 (delta 0) 148 | To git@github.com:gotgithub/helloworld.git 149 | * [new branch] mybranch1 -> mybranch1 150 | Branch mybranch1 set up to track remote branch mybranch1 from origin. 151 | 152 | 在GitHub上查看版本库,会看到新增了一个分支\ ``mybranch1``\ ,不过默认分支仍为\ 153 | ``master``\ ,如图3-8所示。 154 | 155 | .. figure:: /images/project-hosting/new-branch.png 156 | :scale: 100 157 | 158 | 图3-8:版本库新增了一个分支 159 | 160 | .. _default-branch: 161 | 162 | 设置默认分支 163 | --------------- 164 | 165 | 可以改变GitHub上版本库显示的默认分支,如果版本库包含多个分支的话。\ 166 | 例如修改版本库的默认分支为\ ``mybranch1``\ ,点击项目名称旁边的“Admin”按钮,\ 167 | 修改项目的默认分支。如图3-9所示。 168 | 169 | .. figure:: /images/project-hosting/set-default-branch.png 170 | :scale: 100 171 | 172 | 图3-9:设置缺省分支 173 | 174 | 设置了GitHub默认分支后,如果再从GitHub克隆版本库,本地克隆后版本库的默认分支\ 175 | 也将改变。 176 | 177 | :: 178 | 179 | $ git clone git@github.com:gotgithub/helloworld.git helloworld-nb 180 | Cloning into 'helloworld-nb'... 181 | remote: Counting objects: 6, done. 182 | remote: Compressing objects: 100% (4/4), done. 183 | remote: Total 6 (delta 0), reused 6 (delta 0) 184 | Receiving objects: 100% (6/6), done. 185 | $ cd helloworld-nb 186 | $ git branch 187 | * mybranch1 188 | 189 | 实际上修改GitHub上版本库的默认分支,就是将GitHub版本库的头指针\ ``HEAD``\ 190 | 指向了其他分支,如\ ``mybranch1``\ 分支。这可以从下面命令看出。 191 | 192 | :: 193 | 194 | $ git branch -r 195 | origin/HEAD -> origin/mybranch1 196 | origin/master 197 | origin/mybranch1 198 | 199 | 也可以从\ ``git ls-remote``\ 命令看出头指针\ ``HEAD``\ 和引用\ ``refs/heads/mybranch1``\ 200 | 指向同一个对象的哈希值。 201 | 202 | :: 203 | 204 | $ git ls-remote 205 | From git@github.com:gotgithub/helloworld.git 206 | f46a28484adb6c1b4830eb4df582325c740e9d6c HEAD 207 | e1e52d99fa71fd6f606903efa9da04fd0055fca9 refs/heads/master 208 | f46a28484adb6c1b4830eb4df582325c740e9d6c refs/heads/mybranch1 209 | 210 | .. _del-branch: 211 | 212 | 删除分支 213 | --------------- 214 | 215 | 删除当前工作分支会报错。例如下面的命令试图分支\ ``mybranch1``\ ,但没有成功: 216 | 217 | :: 218 | 219 | $ git branch -d mybranch1 220 | error: Cannot delete the branch 'mybranch1' which you are currently on. 221 | 222 | 错误信息显示不能删除当前工作分支。因此先切换到其他分支,例如从GitHub版本库\ 223 | 中取出\ ``master``\ 分支并切换。 224 | 225 | :: 226 | 227 | $ git checkout master 228 | 229 | 可以看出新的工作分支为\ ``master``\ 分支。 230 | 231 | :: 232 | 233 | $ git branch 234 | * master 235 | mybranch1 236 | 237 | 现在可以删除\ ``mybanch1``\ 分支。下面的命令之所以使用\ ``-D``\ 参数,\ 238 | 而非\ ``-d``\ 参数,是因为Git在删除分支时为避免数据丢失,默认禁止删除\ 239 | 尚未合并的分支。参数\ ``-D``\ 则可强制删除尚未合并的分支。 240 | 241 | :: 242 | 243 | $ git branch -D mybranch1 244 | Deleted branch mybranch1 (was f46a284). 245 | 246 | 现在只是本地分支被删除了,远程GitHub服务器上的\ ``mybranch1``\ 分支尚在。\ 247 | 删除远程GitHub版本库中的分支就不能使用\ ``git branch``\ 命令,而是要使用\ 248 | ``git push``\ 命令,不过在使用推送分支命令时要使用一个特殊的引用表达式\ 249 | (冒号前为空)。如下: 250 | 251 | :: 252 | 253 | $ git push origin :mybranch1 254 | remote: error: refusing to delete the current branch: refs/heads/mybranch1 255 | To git@github.com:gotgithub/helloworld.git 256 | ! [remote rejected] mybranch1 (deletion of the current branch prohibited) 257 | error: failed to push some refs to 'git@github.com:gotgithub/helloworld.git' 258 | 259 | 为什么删除远程分支出错了呢?是因为没有使用强制推送么? 260 | 261 | 实际上即使使用强制推送也会遇到上面的错误。GitHub发现要删除的\ ``mybranch1``\ 262 | 分支是远程版本库的缺省分支,因而禁止删除。重新访问GitHub的项目管理页面,\ 263 | 将缺省分支设置回\ ``master``\ 分支,参照图3-9。然后再次执行如下命令,即可成功\ 264 | 删除分支。 265 | 266 | :: 267 | 268 | $ git push origin :mybranch1 269 | To git@github.com:gotgithub/helloworld.git 270 | - [deleted] mybranch1 271 | 272 | 执行\ ``git ls-remote``\ 命令可以看到GitHub远程版本库已经不存在分支\ ``mybranch1``\ 。 273 | 274 | :: 275 | 276 | $ git ls-remote git@github.com:gotgithub/helloworld.git 277 | From git@github.com:gotgithub/helloworld.git 278 | e1e52d99fa71fd6f606903efa9da04fd0055fca9 HEAD 279 | e1e52d99fa71fd6f606903efa9da04fd0055fca9 refs/heads/master 280 | 281 | .. _git-tags: 282 | 283 | 里程碑管理 284 | ------------ 285 | 286 | 里程碑即tag,其管理和分支管理非常类似。里程碑和分支一样也是以引用的形式存在的,\ 287 | 保存在\ ``.git/refs/tags/``\ 路径下。引用可能指向一个提交,但也可能是其他类型(Tag对象)。 288 | 289 | - 轻量级里程碑:用\ ``git tag []`` 命令创建,引用直接指向\ 290 | 一个提交对象\ ````\ 。 291 | 292 | - 带说明的里程碑:用\ ``git tag -a []`` 命令创建,并且在创建时\ 293 | 需要提供创建里程碑的说明。Git会创建一个tag对象保存里程碑说明、里程碑的指向、\ 294 | 创建里程碑的用户等信息,里程碑引用指向该Tag对象。 295 | 296 | - 带签名的里程碑:用\ ``git tag -s []`` 命令创建。是在带说明的\ 297 | 里程碑的基础上引入了PGP签名,保证了所创建的里程碑的完整性和不可拒绝性。 298 | 299 | 下面演示一下里程碑的创建和管理。 300 | 301 | * 先在本地创建一个新提交。 302 | 303 | :: 304 | 305 | $ touch hello1 306 | $ git add hello1 307 | $ git commit -m "add hello1 for mark." 308 | 309 | * 本地创建里程碑\ ``mytag1``\ 、\ ``mytag2``\ 和\ ``mytag3``\ 。 310 | 311 | :: 312 | 313 | $ git tag -m "Tag on initial commit" mytag1 HEAD^ 314 | $ git tag -m "Tag on new commit" mytag2 315 | $ git tag mytag3 316 | 317 | * 查看新建立的里程碑。 318 | 319 | :: 320 | 321 | $ git tag -l -n1 322 | mytag1 Tag on initial commit 323 | mytag2 Tag on new commit 324 | mytag3 add hello1 for mark. 325 | 326 | * 将本地里程碑推送到GitHub远程版本库。 327 | 328 | :: 329 | 330 | $ git push origin refs/tags/* 331 | Counting objects: 6, done. 332 | Delta compression using up to 2 threads. 333 | Compressing objects: 100% (4/4), done. 334 | Writing objects: 100% (5/5), 548 bytes, done. 335 | Total 5 (delta 0), reused 0 (delta 0) 336 | To git@github.com:gotgithub/helloworld.git 337 | * [new tag] mytag1 -> mytag1 338 | * [new tag] mytag2 -> mytag2 339 | * [new tag] mytag3 -> mytag3 340 | 341 | * 删除本地里程碑。 342 | 343 | :: 344 | 345 | $ git tag -d mytag3 346 | Deleted tag 'mytag3' (was c71231c) 347 | 348 | * 删除GitHub远程版本库中的里程碑。 349 | 350 | :: 351 | 352 | $ git push origin :mytag3 353 | To git@github.com:gotgithub/helloworld.git 354 | [deleted] mytag3 355 | 356 | 此时查看GitHub上的项目页,会看到已有两个里程碑,如图3-10所示。 357 | 358 | .. figure:: /images/project-hosting/tags-list.png 359 | :scale: 100 360 | 361 | 图3-10:里程碑列表 362 | -------------------------------------------------------------------------------- /04-work-with-others/060-wiki.rst: -------------------------------------------------------------------------------- 1 | .. _wiki: 2 | 3 | 维基 4 | =============== 5 | 6 | 维基是Web协同著作平台,可以让任何浏览网页的人都能够方便地参与网页的编辑和创建。\ 7 | 这源自于维基如下魔力: 8 | 9 | * 快速更改。修改网页无需复杂的后台修改和网页部署流程,浏览的网页直接提供编辑\ 10 | 按钮,任何查看网页的用户均可在线编辑网页。 11 | * 简洁语法。编写网页不需要学习复杂的HTML,取而代之的是易学易用的格式化文本\ 12 | (维基语法),有的维基还提供图形化编辑界面。 13 | * 版本控制。熟悉Git的人,可以把维基看作是Web的版本控制。历次修改都记录在案,\ 14 | 历史修订可进行比较,可恢复到历史版本等。 15 | * 维基链接。页面链接使用\ ``[[页面名称]]``\ 语法,可以非常方便地创建新页面,\ 16 | 并实现页面间的互联。 17 | 18 | GitHub提供了维基模块,方便项目团队创建社区驱动和维护的项目文档。 19 | 20 | .. _wiki-init: 21 | 22 | 维基初始化 23 | ------------- 24 | 25 | GitHub的维基模块可以通过项目管理页面控制开启或关闭,默认开启,如图4-70所示。\ 26 | 因为GitHub提供了项目展示的多种途径,一些小项目如果觉得用\ ``README``\ 文件构建\ 27 | 项目说明,或者用\ ``gh-pages``\ 分支维护项目主页就足够了,大可关闭维基模块。 28 | 29 | .. figure:: /images/wiki/project-admin-features-wiki.png 30 | :scale: 100 31 | 32 | 图4-70:开启或关闭Wiki模块 33 | 34 | 项目启用维基后,进入维基页面,如图4-71所示,会发现维基页面并没有自动创建,\ 35 | 还需要进行初始化。 36 | 37 | .. figure:: /images/wiki/wiki-not-exist.png 38 | :scale: 100 39 | 40 | 图4-71:尚未初始化的维基界面 41 | 42 | 点击“Create Wiki Now”按钮,自动创建维基首页,如图4-72所示。 43 | 44 | .. figure:: /images/wiki/wiki-home-init.png 45 | :scale: 100 46 | 47 | 图4-72:自动创建的维基首页 48 | 49 | .. _use-wiki: 50 | 51 | 使用维基 52 | ----------- 53 | 54 | 自动创建的维基首页只有非常简单的信息,点击编辑按钮,修改维基首页。编辑界面\ 55 | 如图4-73所示。 56 | 57 | .. figure:: /images/wiki/wiki-edit-init.png 58 | :scale: 100 59 | 60 | 图4-73:编辑维基首页 61 | 62 | 编辑界面有6个部分需要重点说明: 63 | 64 | 1. 页面名称。首页的页面名称为“Home”,不能随意更改,否则无法找到首页,或者页面\ 65 | 之间的跳转会失效。 66 | 2. 工具条。从左至右分别是设置一级标题、二级标题、三级标题、插入链接、图片、\ 67 | 字体加粗、斜体、代码块、列表、编号列表、引用、水平分割线等。 68 | 3. 页面语法格式。默认采用Markdown语法,还可以选用AsciiDoc、Creole等语法。\ 69 | 注意如果改变语法格式,该维基页面的内容需要手工进行调整,而且页面的实际存储\ 70 | 文件的文件扩展名会改变。 71 | 4. 语法帮助。当按下工具条中的帮助按钮会显示本语法帮助表格。 72 | 5. 维基内容编辑框。整个维基页面的内容都在这个编辑框中。鼠标拖动该编辑框右下角\ 73 | 可以对编辑框大小进行缩放。 74 | 6. 可选的修改说明。修改页面时提供说明便于跟踪对页面的历史修订。 75 | 76 | 对初始创建的首页进行更改,如图4-74所示。 77 | 78 | .. figure:: /images/wiki/wiki-edit-format.png 79 | :scale: 100 80 | 81 | 图4-74:修改选中文本样式 82 | 83 | 图4-74中,在维基内容编辑框中选择两行文本,然后点击工具栏中的列表按钮,为选中\ 84 | 内容应用列表样式。应用新样式后的效果如图4-75所示。然后填写提交说明,点击“Save”\ 85 | 按钮保存更改。 86 | 87 | .. figure:: /images/wiki/wiki-edit-save.png 88 | :scale: 100 89 | 90 | 图4-75:填写提交说明保存更改 91 | 92 | 注意图4-75的维基内容中有\ ``[[页面名称]]``\ 样式的语法,这个语法是维基特色的\ 93 | 页面链接语法,指向另外的维基页面。实际页面输出如图4-76所示。 94 | 95 | .. figure:: /images/wiki/wiki-home-simple-wiki-links.png 96 | :scale: 100 97 | 98 | 图4-76:保存更改后的维基首页 99 | 100 | 如果对维基语法\ ``[[页面名称]]``\ 生成的链接标题不满意,还可以用\ 101 | ``[[链接标题|页面名称]]``\ 格式创建维基链接。对首页重新做一次修改,修改如下: 102 | 103 | * 将\ ``[[HowtoClone]]``\ 改为\ ``[[how to clone|HowtoClone]]``\ 。 104 | * 将\ ``[[HowtoContribute]]``\ 改为\ ``[[how to contribute|HowtoContribute]]``\ 。 105 | 106 | 修改后的首页效果如图4-77所示。 107 | 108 | .. figure:: /images/wiki/wiki-home-update-wiki-links.png 109 | :scale: 100 110 | 111 | 图4-77:修改维基链接标题后的首页 112 | 113 | 无论怎样更改维基页面都不怕内容丢失,因为维基记录了每一次修订历史,并可以回退\ 114 | 任意一次修改。点击维基页面中的“Page History”按钮,查看页面修订历史,如图4-78所示。 115 | 116 | 117 | .. figure:: /images/wiki/wiki-page-history.png 118 | :scale: 100 119 | 120 | 图4-78:页面修订历史 121 | 122 | 首页的修订历史记录着维基初始化以来所有的修改,包括修改者、修改时间、提交说明,\ 123 | 以及一个可点击的对象ID。点击对象ID查看对应版本的页面。还可以对不同版本的页面进行\ 124 | 比较,选中两个版本点击“Compare Revisions”按钮,如图4-79所示。 125 | 126 | .. figure:: /images/wiki/wiki-page-compare.png 127 | :scale: 100 128 | 129 | 图4-79:页面版本间比较 130 | 131 | 在页面版本间的比较界面中,提供回退此次修改的按钮。点击“Revert Changes”按钮\ 132 | (图4-79所示),可以回退对首页的修改。查看首页的修订历史,会看到回退记录也\ 133 | 显示其中,如图4-80所示。 134 | 135 | .. figure:: /images/wiki/wiki-reverted.png 136 | :scale: 100 137 | 138 | 图4-80:包含回退记录的页面修订历史 139 | 140 | 在维基中创建新页面有多种方法,可以点击页面中的“New Page”按钮,也可以像我们\ 141 | 之前做的那样先在页面中用\ ``[[页面名称]]``\ 格式嵌入维基链接,然后在生成的\ 142 | 页面中可以看到指向新页面的链接,当然这些链接所指向的页面并不存在。 143 | 144 | .. figure:: /images/wiki/wiki-newpage.png 145 | :scale: 100 146 | 147 | 图4-81:页面中的维基链接 148 | 149 | 如图4-81所示,点击页面中指向不存在维基页面的链接,会自动开启创建新页面的\ 150 | 对话框,如图4-82所示。 151 | 152 | .. figure:: /images/wiki/wiki-newpage-edit.png 153 | :scale: 100 154 | 155 | 图4-82:创建新维基页面 156 | 157 | 输入维基页面的内容,然后填写提交说明,点击“Save”按钮,保存新页面。生成的\ 158 | 新页面如图4-83所示。 159 | 160 | .. figure:: /images/wiki/wiki-newpage-created.png 161 | :scale: 100 162 | 163 | 图4-83:生成的新页面 164 | 165 | 166 | 如果当前用户对页面具有写权限,则在页面左下角会看到一个删除本页面的链接。点击\ 167 | “Delete this page”链接并经确认后会删除页面。然后继续在维基中操作,如创建另外\ 168 | 一个新页面\ ``HowtoContribute``\ 。 169 | 170 | 如果对之前删除页面\ ``Howtoclone``\ 的操作后悔,可以通过下面方法找回。 171 | 172 | * 访问菜单中的“Wiki History”项,显示整个维基的修订记录(不是某个页面的修订\ 173 | 记录)。如图4-84所示。 174 | 175 | .. figure:: /images/wiki/wiki-history.png 176 | :scale: 100 177 | 178 | 图4-84:维基修订记录 179 | 180 | * 从图4-84可见上面第二条记录就是删除\ ``HowtoClone``\ 页面的操作,选择该记录\ 181 | 及前一次记录,执行版本比较,如图4-85所示。 182 | 183 | .. figure:: /images/wiki/wiki-compare-drop-page.png 184 | :scale: 100 185 | 186 | 图4-85:版本比较 187 | 188 | * 点击“Revert Changes”按钮,可以取消页面删除动作。更新后的维基修订历史如图4-86所示。 189 | 190 | .. figure:: /images/wiki/wiki-page-restored.png 191 | :scale: 100 192 | 193 | 图4-86:还原修订后的维基修订记录 194 | 195 | * 查看维基页面列表,可以看到页面\ ``Howtoclone``\ 已经被找回。 196 | 197 | .. figure:: /images/wiki/wiki-pages.png 198 | :scale: 100 199 | 200 | 图4-87:维基页面列表 201 | 202 | .. _git-backend-wiki: 203 | 204 | 维基与Git 205 | ------------ 206 | 207 | 随着对GitHub维基的深入使用,可能会遇到下面的问题:如何嵌入图片?多人编辑时\ 208 | 如何避免冲突?解决这几个问题的办法就是用Git操作维基。在之前查看维基修订历史,\ 209 | 以及进行版本间比较时可能已经看出和Git是如何的相似,实际上GitHub的维基页面\ 210 | 就是用Git版本库实现的。 211 | 212 | 在维基页面访问菜单中的“Git Access”项,会看到用Git访问维基页面的方法。如图4-88所示。 213 | 214 | .. figure:: /images/wiki/wiki-git-access.png 215 | :scale: 100 216 | 217 | 图4-88:用Git访问维基 218 | 219 | 对于项目\ ``gotgithub/helloworld``\ 来说,用Git克隆其维基,用如下命令: 220 | 221 | :: 222 | 223 | $ git clone git@github.com:gotgithub/helloworld.wiki.git 224 | 225 | 进入到刚刚克隆的\ ``helloworld.wiki``\ 工作区中,查看包含的文件,会看到有三个文件。 226 | 227 | :: 228 | 229 | $ cd helloworld.wiki 230 | $ ls 231 | Home.md Howtoclone.creole Howtocontribute.md 232 | 233 | 三个文件对应于三个维基页面,文件名就是维基的页面名称,而扩展名对应于采用的\ 234 | 维基语法。以\ ``.md``\ 扩展名结尾的页面采用Markdown语法,而以\ ``.creole``\ 235 | 结尾的文件采用Creole标准维基语法。 236 | 237 | 下面就通过Git在维基版本库中添加一个图片。添加图片的操作只通过GitHub维基的Web\ 238 | 界面是很难实现的,而使用Git则易如反掌。 239 | 240 | * 创建一个名为\ ``images``\ 目录。这个目录并非必须,只是为了易于管理。 241 | 242 | :: 243 | 244 | $ mkdir images 245 | $ cd images 246 | 247 | * 在\ ``images``\ 目录中添加图片。 248 | 249 | 下面的操作从GitHub官方版本库中下载图片\ ``octocat.png``\ 并进行适当缩放。 250 | 251 | :: 252 | 253 | $ wget https://github.com/github/media/raw/master/octocats/octocat.png 254 | $ mogrify -resize '200' octocat.png 255 | 256 | * 将图片添加到暂存区并提交。 257 | 258 | :: 259 | 260 | $ git add octocat.png 261 | $ git commit -m "add sample image." 262 | 263 | * 将本地提交推送到GitHub远程版本库。 264 | 265 | :: 266 | 267 | $ git push 268 | 269 | 完成推送后,访问下面的网址可以看到刚刚上传的图片: 270 | 271 | :: 272 | 273 | https://github.com/gotgithub/helloworld/wiki/images/octocat.png 274 | 275 | 接下来在维基页面中引用图片。嵌入图片的Markdown语法是:\ 276 | ``![Alt text here](Image URL here)``\ 。当然可以通过编辑本地版本库\ 277 | ``gotgithub/helloworld.wiki.git``\ 中的文件,但通过GitHub维基编辑界面嵌入\ 278 | 图片无需记忆复杂的语法。如图4-89所示。 279 | 280 | .. figure:: /images/wiki/wiki-insert-image.png 281 | :scale: 100 282 | 283 | 图4-89:在维基页面中嵌入图片 284 | 285 | 点击“Preview”按钮,可以在保存前查看效果。在图4-90所示的预览界面中可以看到\ 286 | 修改后的效果。 287 | 288 | .. figure:: /images/wiki/wiki-insert-image-preview.png 289 | :scale: 100 290 | 291 | 图4-90:预览效果 292 | 293 | 多人同时编辑一个维基页面会引起冲突,先提交的用户会成功,其他用户的编辑界面\ 294 | 马上会显示冲突警告,并且保存按钮也被置灰,如图4-91所示。 295 | 296 | .. figure:: /images/wiki/wiki-edit-conflict.png 297 | :scale: 100 298 | 299 | 图4-91:编辑冲突 300 | 301 | GitHub的维基编辑界面没有提供冲突解决的工具,而利用Git本身强大的冲突解决功能\ 302 | 可以很容易地解决这一难题。 303 | 304 | 例如用户\ ``gotgithub``\ 编辑维基首页\ ``Home``\ 遇到编辑冲突,为防止数据丢失,\ 305 | 先将编辑框中的维基文本拷贝并粘贴到一个临时文件中,如文件中\ 306 | ``/path/to/draft.md``\ 。然后进行如下操作将\ ``draft.md``\ 中内容合并到维基页面中。 307 | 308 | * 如果本地已经克隆维基版本库,则执行下面命令更新。 309 | 310 | :: 311 | 312 | $ cd helloworld.wiki 313 | $ git pull 314 | 315 | * 如果没有,则克隆维基版本库。 316 | 317 | :: 318 | 319 | $ git clone git@github.com:gotgithub/helloworld.wiki.git 320 | $ cd helloworld.wiki 321 | 322 | * 用Git命令查看版本库的历史,以便找出发生冲突的原始版本。 323 | 324 | 从下面的输出可以看出我们编辑的版本是基于提交\ ``fbb4bb4``\ ,由于用户\ 325 | ``incredible``\ 先于我们完成了对维基页面的修改以致发生了冲突。 326 | 327 | :: 328 | 329 | $ git log -3 --pretty=short 330 | commit 5ff5d998bb6cf99337813915282df94701d17ea0 331 | Author: incredible 332 | 333 | Add a note as image link broken if url without a end slash. 334 | 335 | commit fbb4bb4f330bacf765d51736359b0a3e81ed945b 336 | Author: gotgithub 337 | 338 | Insert image in page. 339 | 340 | commit 94182c2b57ebce1f1bf8a310f78df87ae8e8219a 341 | Author: gotgithub 342 | 343 | add sample image. 344 | 345 | * 基于提交\ ``fbb4bb4``\ 建立分支,如分支\ ``mywiki``\ 。 346 | 347 | :: 348 | 349 | $ git checkout -b mywiki fbb4bb4 350 | 351 | * 将保存的\ ``draft.md``\ 覆盖欲修改的文件,如\ ``Home.md``\ 。 352 | 353 | :: 354 | 355 | $ cp /path/to/draft.md Home.md 356 | 357 | * 提交修改。 358 | 359 | :: 360 | 361 | $ git add -u 362 | $ git commit -m "Use absolute image link." 363 | 364 | * 切换到\ ``master``\ 分支。 365 | 366 | :: 367 | 368 | $ git checkout master 369 | 370 | * 合并我们在\ ``mywiki``\ 分支的修改。 371 | 372 | :: 373 | 374 | $ git merge mywiki 375 | Auto-merging Home.md 376 | CONFLICT (content): Merge conflict in Home.md 377 | Automatic merge failed; fix conflicts and then commit the result. 378 | 379 | * 调用图形工具解决冲突。 380 | 381 | :: 382 | 383 | $ git mergetool 384 | 385 | * 提交并查看合并后的提交关系图。 386 | 387 | :: 388 | 389 | $ git commit -m "merge with incredible's edit." 390 | $ git log --oneline --graph -4 391 | * d33b55a merge with incredible's edit. 392 | |\ 393 | | * 121c3b2 Use absolute image link. 394 | * | 5ff5d99 Add a note as image link broken if url without a end slash. 395 | |/ 396 | * fbb4bb4 Insert image in page. 397 | 398 | * 查看用户\ ``incredible``\ 的修改。 399 | 400 | :: 401 | 402 | $ git show --oneline HEAD^1 403 | 5ff5d99 Add a note as image link broken if url without a end slash. 404 | diff --git a/Home.md b/Home.md 405 | index 6ada8e8..0bca3ec 100644 406 | --- a/Home.md 407 | +++ b/Home.md 408 | @@ -1,5 +1,7 @@ 409 | This is a sample project for the book "GotGitHub". 410 | 411 | +**Note**: if can not see the following image, add a slash('/') at the end of the URL. 412 | + 413 | ![GitHub Octocat](images/octocat.png) 414 | 415 | ## HOWTOs 416 | 417 | * 查看用户\ ``gotgithub``\ 的修改。 418 | 419 | :: 420 | 421 | $ git show --oneline HEAD^2 422 | 121c3b2 Use absolute image link. 423 | diff --git a/Home.md b/Home.md 424 | index 6ada8e8..cdb9167 100644 425 | --- a/Home.md 426 | +++ b/Home.md 427 | @@ -1,6 +1,6 @@ 428 | This is a sample project for the book "GotGitHub". 429 | 430 | -![GitHub Octocat](images/octocat.png) 431 | +![GitHub Octocat](https://github.com/gotgithub/helloworld/wiki/images/octocat.png) 432 | 433 | ## HOWTOs 434 | * To access the repository of this project see: [[HowtoClone]] 435 | 436 | * 将本地合并后的版本库推送到GitHub。 437 | 438 | :: 439 | 440 | $ git push 441 | 442 | 再来看看推送后GitHub的维基修订历史,和本地版本库看到的历史是一致的,如图4-92所示。 443 | 444 | .. figure:: /images/wiki/wiki-pushed-history.png 445 | :scale: 100 446 | 447 | 图4-92:推送后的维基修订历史 448 | 449 | GitHub维基背后的引擎名为Gollum,GitHub已将其开源,项目网址:\ 450 | https://github.com/github/gollum 。安装Gollum,在克隆的维基版本库中运行\ ``gollum``\ 451 | 就可以在本地启动维基服务。 452 | -------------------------------------------------------------------------------- /04-work-with-others/010-fork-and-pull.rst: -------------------------------------------------------------------------------- 1 | .. _fork-and-pull-request: 2 | 3 | Fork + Pull模式 4 | ==================== 5 | 6 | 参与GitHub中的项目开发,最常用和推荐的首选方式是“Fork + Pull”模式。在\ 7 | “Fork + Pull”模式下,项目参与者不必向项目创建者申请提交权限,而是在自己的托管\ 8 | 空间下建立项目的派生(Fork)。 9 | 10 | 如果一个开源项目派生出另外的项目,通常意味着项目的分裂和开发团队的削弱,\ 11 | 而GitHub中的项目派生则不会,而且正好相反,GitHub中的项目派生是项目壮大的体现。\ 12 | 所有的派生项目都会有链接指向原始项目,派生项目没有独立的缺陷追踪系统(ISSUE),\ 13 | 而是必须利用创建者本人的项目中的缺陷追踪系统。至于在派生项目中创建的提交,\ 14 | 可以非常方便地利用GitHub的Pull Request工具向原始项目的维护者发送Pull Request。 15 | 16 | 下面以GotGit版本库为例,介绍如何利用GitHub提供的Fork和Pull Request工具实现工作协同。 17 | 18 | .. _fork: 19 | 20 | 版本库派生 21 | ------------------ 22 | 23 | GotGit版本库\ [#]_\ 用于维护《Git权威指南》一书的官网和勘误,下面演示的勘误表\ 24 | 修改是由王胜\ [#]_\ 通过GitHub之外的一个缺陷追踪平台报告的\ [#]_\ 。\ 25 | 他在报告中,甚至直接用GNU diff格式告诉我该如何修改。 26 | 27 | 下面就以用户gotgithub身份,访问版本库 https://github.com/gotgit/gotgit/ ,\ 28 | 添加新的勘误。如图4-1所示,gotgit项目在之前的示例中已经被我们关注但尚未Fork。 29 | 30 | .. figure:: /images/work-with-others/gotgit-repo-before-fork.png 31 | :scale: 100 32 | 33 | 图4-1:原gotgit项目 34 | 35 | 点击项目名称右侧的Fork按钮,便在gotgithub用户自己的托管空间下创建项目派生,\ 36 | 派生项目版本库出现在版本库列表中,如图4-2。 37 | 38 | .. figure:: /images/work-with-others/gotgit-in-repo-list.png 39 | :scale: 100 40 | 41 | 图4-2:gotgithub用户的项目列表 42 | 43 | 访问派生后的版本库,会发现和派生前的几乎相同,除了没有缺陷跟踪(ISSUE),\ 44 | 以及标识了该项目派生之前的原路径等。如图4-3所示。 45 | 46 | .. figure:: /images/work-with-others/gotgit-repo-forked.png 47 | :scale: 100 48 | 49 | 图4-3:派生的gotgit项目 50 | 51 | 现在gotgithub用户就在本地派生的版本库中提交。 52 | 53 | * 克隆 gotgithub/gotgit 版本库。 54 | 55 | :: 56 | 57 | $ git clone git@github.com:gotgithub/gotgit.git 58 | $ cd gotgit 59 | 60 | * 为了向问题的发现者致敬,并经王胜同意,以他的身份进行提交。 61 | 62 | :: 63 | 64 | $ git config user.name "Wang Sheng" 65 | $ git config user.email wangsheng@ossxp.com 66 | 67 | * 编辑\ ``errata.mkd``\ 文件\ [#]_\ ,录入新发现的书中的文字错误。 68 | 69 | :: 70 | 71 | $ vi errata.mkd 72 | 73 | * 对\ ``error.mkd``\ 的改动如下: 74 | 75 | :: 76 | 77 | $ git diff 78 | diff --git a/errata.mkd b/errata.mkd 79 | index b0b68fb..29e40cf 100644 80 | --- a/errata.mkd 81 | +++ b/errata.mkd 82 | @@ -14,5 +14,6 @@ 83 | | 66 | 倒数第11行 | Author(提交者) | Author(作者) | [Github#2](http://github.com/gotgit/gotgit/issues/2) | 84 | | 144 | 第1行 | \`$ **git rev-parse A^{tree} A:** | $ **git rev-parse A^{tree} A:** | [#153](http://redmine.ossxp.com/redmine/issues/153) | 85 | | 218 | 第8行 | 况下,Gits标识出合并冲突, | 况下,Git标识出合并冲突, | [#159](http://redmine.ossxp.com/redmine/issues/159) | 86 | +| 369 | 第21行 | 但 `-i` 参数仅当对一个项执行时才有效。 | 但 `-i` 参数仅当对一个项目执行时才有效。 | [Github#3](http://github.com/gotgit/gotgit/issues/3) | 87 | | 516 | 倒数第15行 | **oldtag="cat"** | **oldtag=\`cat\`** | [#151](http://redmine.ossxp.com/redmine/issues/151) | 88 | 89 | * 提交修改。至于提交说明中出现的编号,是为了和缺陷跟踪系统关联,会在后面章节介绍。 90 | 91 | :: 92 | 93 | $ git add -u 94 | $ git commit -m "Fixed #3: should be 项目, not 项." 95 | 96 | * 推送提交到GitHub。 97 | 98 | :: 99 | 100 | $ git push 101 | 102 | 访问GitHub上的派生项目页面,会看到以用户wangsheng在\ ``master``\ 分支\ [#]_\ 103 | 创建的提交。如图4-4所示。 104 | 105 | .. figure:: /images/work-with-others/gotgit-new-commit.png 106 | :scale: 100 107 | 108 | 图4-4:派生版本库中的新提交 109 | 110 | .. _pull-request: 111 | 112 | Pull Request 113 | ------------------ 114 | 115 | 那么如何能够让gotgit原始项目的创建者知道这个派生项目及新的提交呢?GitHub提供的\ 116 | 工具就是“Pull Request”。注意到图4-3右上方“Pull Request”按钮了么?点击该按钮进入\ 117 | Pull Request创建界面。 118 | 119 | 在弹出的Pull Request创建界面中,点击菜单中的“Commits”,查看所包含的提交。如图4-5所示。 120 | 121 | .. figure:: /images/work-with-others/pull-request-form-commit.png 122 | :scale: 100 123 | 124 | 图4-5:Pull Request包含的提交 125 | 126 | 点击菜单中的“Files Changed”,查看所包含的提交。如图4-6所示。 127 | 128 | .. figure:: /images/work-with-others/pull-request-form-file.png 129 | :scale: 100 130 | 131 | 图4-6:Pull Request包含的改动差异 132 | 133 | 点击菜单中的“Preview Discussion”,填写Pull Request的标题和内容,完成Pull Request\ 134 | 的创建。如图4-7所示。 135 | 136 | .. figure:: /images/work-with-others/pull-request-form-discuss.png 137 | :scale: 100 138 | 139 | 图4-7:Pull Request的提交界面 140 | 141 | 当Pull Request发出后,项目gotgit的开发者会收到通知邮件,如图4-8所示。 142 | 143 | .. figure:: /images/work-with-others/pull-request-email.png 144 | :scale: 100 145 | 146 | 图4-8:Pull Request的通知邮件 147 | 148 | 点击邮件中的URL链接,以项目gotgit的开发者(如ossxp-com)身份登录,看到如图4-9\ 149 | 的视图。之所以看到有两个用户参与到此Pull Request,是因为Pull Request创建者和\ 150 | 提交的作者是不同的用户。图4-9下方的表单可以向Pull Request追加评论,或者关闭此\ 151 | Pull Request。 152 | 153 | .. figure:: /images/work-with-others/pull-request-owner-view.png 154 | :scale: 100 155 | 156 | 图4-9:Pull Request接收者视图 157 | 158 | GitHub如果检测到Pull Request中包含的提交可以直接合并,会显示自动合并的提示信息,\ 159 | 点击图4-9中提示信息中的自动合并按钮,显示图4-10的自动合并对话框。 160 | 161 | .. figure:: /images/work-with-others/pull-request-auto-merge.png 162 | :scale: 100 163 | 164 | 图4-10:Pull Request的通知邮件 165 | 166 | 点击“Confirm Merge”按钮即完成Pull Request中所含提交的自动合并。自动合并完成后,\ 167 | Pull Request页面下方会以评论的形式出现相关提示,并自动关闭Pull Request。\ 168 | 如图4-11所示。 169 | 170 | .. figure:: /images/work-with-others/pull-request-closed.png 171 | :scale: 100 172 | 173 | 图4-11:Pull Request关闭 174 | 175 | .. _merge-by-hands: 176 | 177 | 手工合并 178 | ------------ 179 | 180 | Pull Request提供的自动合并显示在提交日志中是什么样子的呢?以用户ossxp-com\ 181 | 身份检出版本库,会看到用户wangsheng的提交已经合并到版本库中。 182 | 183 | :: 184 | 185 | $ git clone git@github.com:gotgit/gotgit.git 186 | $ cd gotgit 187 | $ git log --graph -3 188 | * commit 6c1f1ee152629fd2f8d00ebe92c27a32d068d756 189 | |\ Merge: 00c6c4b 7ecdfe7 190 | | | Author: OpenSourceXpress 191 | | | Date: Tue Aug 16 01:23:47 2011 -0700 192 | | | 193 | | | Merge pull request #4 from gotgithub/master 194 | | | 195 | | | Find a typo in the book 196 | | | 197 | | * commit 7ecdfe7451412cfb2e65bb47c12cf2162e21c841 198 | |/ Author: Wang Sheng 199 | | Date: Tue Aug 16 10:17:53 2011 +0800 200 | | 201 | | Fixed #3: should be 项目, not 项. 202 | | 203 | * commit 00c6c4bfab9824bd967440902ce87440f9e87852 204 | | Author: Jiang Xin 205 | | Date: Wed Aug 3 11:50:31 2011 +0800 206 | | 207 | | Change font color for stronger text from red to brown. 208 | 209 | 可以看出GitHub的自动合并产生了一个合并提交,类似执行\ ``git merge --no-ff``\ 210 | 命令。也就是说即使用户wangsheng的提交是一个“快进式提交”(基于gotgit/gotgit\ 211 | 版本库最新提交所做的提交),也要产生一个合并提交。 212 | 213 | 可能有人并不喜欢这种用\ ``--no-ff``\ 参数的非标准的合并方式,因为这种合并产生\ 214 | 了一个多余的提交,可能增加代码评审的负担。若要取消GitHub的自动合并也很简单,\ 215 | 因为Git无所不能: 216 | 217 | :: 218 | 219 | $ git reset --hard HEAD^ # 回退一个提交,即回退到当前提交的第一个父提交 220 | $ git rev-parse HEAD # 检查是否正确的回退 221 | 00c6c4bfab9824bd967440902ce87440f9e87852 222 | $ git push -f # 强制推送回退的 master 分支 223 | 224 | 下面就演示一下当收到他人的Pull Request后,该如何手动合并。实际上在很多情况下,\ 225 | Pull Request所含提交有可能造成合并冲突,那样的话GitHub不再、也不能提供自动合并\ 226 | 功能,就必须采用手工合并的方式。 227 | 228 | * 将Pull Request发出者的派生版本库添加为一个新的源。 229 | 230 | 例如收到来自gotgithub用户的Pull Request,不妨以gotgithub为名添加新的源。 231 | 232 | :: 233 | 234 | $ git remote add gotgithub https://github.com/gotgithub/gotgit.git 235 | 236 | * 此时版本库中有两个源,一个克隆时自动建立的origin,另外一个就是新增加的gotgithub。 237 | 238 | :: 239 | 240 | $ git remote -v 241 | gotgithub https://github.com/gotgithub/gotgit.git (fetch) 242 | gotgithub https://github.com/gotgithub/gotgit.git (push) 243 | origin git@github.com:gotgit/gotgit.git (fetch) 244 | origin git@github.com:gotgit/gotgit.git (push) 245 | 246 | * 获取远程版本库gotgithub的分支和提交。 247 | 248 | :: 249 | 250 | $ git fetch gotgithub 251 | From https://github.com/gotgithub/gotgit 252 | * [new branch] gh-pages -> gotgithub/gh-pages 253 | * [new branch] master -> gotgithub/master 254 | 255 | * 现在除了本地分支\ ``master``\ 外,还有若干远程分支,如下: 256 | 257 | :: 258 | 259 | $ git branch -a 260 | * master 261 | remotes/gotgithub/gh-pages 262 | remotes/gotgithub/master 263 | remotes/origin/HEAD -> origin/master 264 | remotes/origin/gh-pages 265 | remotes/origin/master 266 | 267 | 268 | * 将远程分支\ ``remotes/gotgithub/master``\ (可简写为\ ``gotgithub/master``\ )\ 269 | 合并到当前分支中。 270 | 271 | :: 272 | 273 | $ git merge gotgithub/master 274 | Updating 00c6c4b..7ecdfe7 275 | Fast-forward 276 | errata.mkd | 1 + 277 | 1 files changed, 1 insertions(+), 0 deletions(-) 278 | 279 | * 查看提交说明,看到此次合并没有产生不必要的合并提交。 280 | 281 | :: 282 | 283 | $ git log --graph -2 284 | * commit 7ecdfe7451412cfb2e65bb47c12cf2162e21c841 285 | | Author: Wang Sheng 286 | | Date: Tue Aug 16 10:17:53 2011 +0800 287 | | 288 | | Fixed #3: should be 项目, not 项. 289 | | 290 | * commit 00c6c4bfab9824bd967440902ce87440f9e87852 291 | | Author: Jiang Xin 292 | | Date: Wed Aug 3 11:50:31 2011 +0800 293 | | 294 | | Change font color for stronger text from red to brown. 295 | 296 | * 将合并推送到GitHub版本库中。 297 | 298 | :: 299 | 300 | $ git push 301 | 302 | .. _online-edit: 303 | 304 | 在线编辑 305 | --------- 306 | 307 | GitHub提供了在线编辑功能,这样可以无需克隆版本库、无需使用Git即可完成对版本库\ 308 | 中文件的修改,甚至可以在你的iPad甚至iPhone上完成对文件的修改。 309 | 310 | 以gotgithub账户身份登录GitHub,访问之前派生而来的版本库gotgithub/gotgit\ 311 | 中的文件,例如文件\ ``errata.md``\ [#]_\ ,会看到其中一个“Edit this file”\ 312 | 的按钮,如图4-12所示。 313 | 314 | 315 | .. figure:: /images/work-with-others/edit-this-file-btn.png 316 | :scale: 100 317 | 318 | 图4-12:浏览自己版本库中文件 319 | 320 | 点击图4-12中的“Edit this file”按钮,开始在线编辑文件\ ``errata.md``\ ,\ 321 | 编辑器还支持语法加亮,如图4-13所示。 322 | 323 | .. figure:: /images/work-with-others/edit-this-file-form.png 324 | :scale: 100 325 | 326 | 图4-13:编辑文件 327 | 328 | .. _quick-fork-edit-pull-request: 329 | 330 | 简化的 Fork + Pull Request 331 | -------------------------------- 332 | 333 | 到目前,我们已经了解了GitHub的三大武器:Fork、Pull Request和在线编辑。对于\ 334 | 最常用的“Fork + Pull Request”操作,GitHub还提供了一个快捷模式。即GitHub对于\ 335 | 无权更改的他人版本库中的文件,提供了一个类似在线编辑的按钮,名为\ 336 | “Fork and edit this file”按钮,自动完成版本库派生和在线编辑,即将三大武器一勺烩。 337 | 338 | 访问他人版本库(尚未在自己空间派生)中的文件,例如访问下面地址:\ 339 | http://git.io/hello-world-makefile\ [#]_\ 。显示他人(ossxp-com)版本库\ 340 | ``hello-world``\ 中的\ ``src/Makefile``\ 文件,如图4-14所示。 341 | 342 | .. figure:: /images/work-with-others/fork-and-edit-btn.png 343 | :scale: 100 344 | 345 | 图4-14:浏览他人版本库中文件 346 | 347 | 点击图4-14中的“Fork and edit this file”按钮,会自动在自己托管空间创建派生\ 348 | 版本库,并开始在线编辑文件\ ``src/Makefile``\ ,如图4-15所示。 349 | 350 | .. figure:: /images/work-with-others/fork-and-edit-form.png 351 | :scale: 100 352 | 353 | 图4-15:派生并编辑文件 354 | 355 | 文件修改完毕,点击“Propose File Change”按钮,会将改动作提交到派生的版本库中,\ 356 | 并马上开启一个新的Pull Request。如图4-16所示。 357 | 358 | .. figure:: /images/work-with-others/fork-and-edit-pull-request.png 359 | :scale: 100 360 | 361 | 图4-16:编辑完毕自动开启Pull Request 362 | 363 | 点击“Send pull request”按钮完成Pull Request的创建。如果仔细查看图4-16,会发现\ 364 | Pull Request所包含的修改发生在\ ``gotgithub/hello-world``\ 派生版本库中的\ 365 | ``patch-1``\ 分支中,并非通常的\ ``master``\ 分支。 366 | 367 | 原版本库\ ``ossxp-com/hello-world``\ 的开发者会收到一封邮件,通知有新的\ 368 | Pull Request,如下所示(前四行为信头): 369 | 370 | :: 371 | 372 | From: GotGitHub 373 | Date: 2011/12/17 374 | Subject: [hello-world] Bugfix: build target when version.h changed. (#1) 375 | To: Jiang Xin 376 | 377 | 378 | Without this fix, when version changed only version.h update, target rebuild needs a second `make`. 379 | 380 | You can merge this Pull Request by running: 381 | 382 | git pull https://github.com/gotgithub/hello-world patch-1 383 | 384 | Or you can view, comment on it, or merge it online at: 385 | 386 | https://github.com/ossxp-com/hello-world/pull/1 387 | 388 | -- Commit Summary -- 389 | 390 | * Bugfix: build target when version.h changed. 391 | 392 | -- File Changes -- 393 | 394 | M src/Makefile (3) 395 | 396 | -- Patch Links -- 397 | 398 | https://github.com/ossxp-com/hello-world/pull/1.patch 399 | https://github.com/ossxp-com/hello-world/pull/1.diff 400 | 401 | --- 402 | Reply to this email directly or view it on GitHub: 403 | https://github.com/ossxp-com/hello-world/pull/1 404 | 405 | 版本库\ ``ossxp-com/hello-world``\ 的管理员既可以通过GitHub提供的图形化界面\ 406 | 完成对 Pull Request 的审核和合并,也可以在命令行下完成。正如邮件中所述若使用\ 407 | 命令行,操作如下: 408 | 409 | :: 410 | 411 | $ git pull https://github.com/gotgithub/hello-world patch-1 412 | 413 | 414 | ---- 415 | 416 | .. [#] https://github.com/gotgit/gotgit/ 417 | .. [#] https://github.com/wangsheng/ 418 | .. [#] http://redmine.ossxp.com/redmine/issues/161 419 | .. [#] 版本库 gotgit/gotgit 已将勘误文件重命名为\ ``errata.md``\ 。 420 | .. [#] 版本库 gotgit/gotgit 原\ ``master``\ 分支内容已转移至\ ``gh-pages``\ 421 | 分支,通过GitHub提供的网站部署机制完成网页的编译和部署。 422 | .. [#] 版本库 gotgit/gotgit 已重构。分支\ ``gh-pages``\ 中文件\ ``errata.md``\ 423 | 文件来自于原\ ``master``\ 分支的\ ``errata.mkd``\ 文件,地址:\ 424 | https://github.com/gotgithub/gotgit/blob/gh-pages/errata.md 。 425 | .. [#] 即地址 https://github.com/ossxp-com/hello-world/blob/master/src/Makefile 。 426 | -------------------------------------------------------------------------------- /03-project-hosting/050-homepage.rst: -------------------------------------------------------------------------------- 1 | .. _homepage: 2 | 3 | 建立主页 4 | ================= 5 | 6 | 很多开源项目托管平台都支持为托管的项目建立主页,但主页的维护方式都没有GitHub\ 7 | 这么酷。大多数托管平台无非是开放一个FTP或类似服务,用户把制作好的网页或脚本\ 8 | 上传了事,而在GitHub用户通过创建特殊名称的Git版本库或在Git库中建立特别的分支\ 9 | 实现对主页的维护。 10 | 11 | .. _user-homepage: 12 | 13 | 创建个人主页 14 | -------------- 15 | 16 | GitHub 为每一个用户分配了一个二级域名\ ``.github.io``\ ,用户为自己\ 17 | 的二级域名创建主页很容易,只要在托管空间下创建一个名为\ ``.github.io``\ 18 | 的版本库,向其\ ``master``\ 分支提交网站静态页面即可,其中网站首页为\ 19 | ``index.html``\ 。下面以\ ``gotgithub``\ 用户为例介绍如何创建个人主页。 20 | 21 | * 用户\ ``gotgithub``\ 创建一个名为\ ``gotgithub.github.io``\ 的Git版本库。 22 | 23 | 在GitHub上创建版本库的操作,参见“第3.1节 :ref:`new-project`\ ”。 24 | 25 | * 在本地克隆新建立的版本库。 26 | 27 | :: 28 | 29 | $ git clone git@github.com:gotgithub/gotgithub.github.io.git 30 | $ cd gotgithub.github.io/ 31 | 32 | * 在版本库根目录中创建文件\ ``index.html``\ 作为首页。 33 | 34 | :: 35 | 36 | $ printf "

GotGitHub's HomePage

It works.\n" > index.html 37 | 38 | * 建立提交。 39 | 40 | :: 41 | 42 | $ git add index.html 43 | $ git commit -m "Homepage test version." 44 | 45 | * 推送到GitHub,完成远程版本库创建。 46 | 47 | :: 48 | 49 | $ git push origin master 50 | 51 | * 访问网址: http://gotgithub.github.io/ 。 52 | 53 | 最多等待10分钟,GitHub就可以完成新网站的部署。网站完成部署后版本库\ 54 | 的所有者会收到邮件通知。 55 | 56 | 还有要注意访问用户二级域名的主页要使用HTTP协议非HTTPS协议。 57 | 58 | .. _project-homepage: 59 | 60 | 创建项目主页 61 | --------------- 62 | 63 | 如前所述,GitHub会为每个账号分配一个二级域名\ ``.github.io``\ 64 | 作为用户的首页地址。实际上还可以为每个项目设置主页,项目主页也通过\ 65 | 此二级域名进行访问。 66 | 67 | 例如\ ``gotgithub``\ 用户创建的\ ``helloworld``\ 项目如果启用了项目主页,\ 68 | 则可通过网址\ ``http://gotgithub.github.io/helloworld/``\ 访问。 69 | 70 | 为项目启用项目主页很简单,只需要在项目版本库中创建一个名为\ ``gh-pages``\ 71 | 的分支,并向其中添加静态网页即可。也就是说如果项目的Git版本库中包含了名为\ 72 | ``gh-pages``\ 分支的话,则表明该项目提供静态网页构成的主页,可以通过网址\ 73 | ``http://.github.io/``\ 访问到。 74 | 75 | 下面以用户\ ``gotgithub``\ 的项目\ ``helloworld``\ 为例,介绍如何维护项目主页。 76 | 77 | 如果本地尚未从GitHub克隆\ ``helloworld``\ 版本库,执行如下命令。 78 | 79 | :: 80 | 81 | $ git clone git@github.com:gotgithub/helloworld.git 82 | $ cd helloworld 83 | 84 | 当前版本库只有一个名为\ ``master``\ 的分支,如果直接从\ ``master``\ 分支创建\ 85 | ``gh-pages``\ 分支操作非常简单,但是作为保存网页的\ ``gh-pages``\ 分支中的内容\ 86 | 和\ ``master``\ 分支中的可能完全不同。如果不希望\ ``gh-pages``\ 分支继承\ 87 | ``master``\ 分支的历史和文件,即想要创建一个干净的\ ``gh-pages``\ 分支,\ 88 | 需要一点小技巧。 89 | 90 | 若使用命令行创建干净的\ ``gh-pages``\ 分支,可以从下面三个方法任选一种。 91 | 92 | 第一种方法用到两个Git底层命令:\ ``git write-tree``\ 和\ ``git commit-tree``\ 。\ 93 | 步骤如下: 94 | 95 | * 基于\ ``master``\ 分支建立分支\ ``gh-pages``\ 。 96 | 97 | :: 98 | 99 | $ git checkout -b gh-pages 100 | 101 | * 删除暂存区文件,即相当于清空暂存区。 102 | 103 | :: 104 | 105 | $ rm .git/index 106 | 107 | * 创建项目首页\ ``index.html``\ 。 108 | 109 | :: 110 | 111 | $ printf "hello world.\n" > index.html 112 | 113 | * 添加文件\ ``index.html``\ 到暂存区。 114 | 115 | :: 116 | 117 | $ git add index.html 118 | 119 | * 用Git底层命令创建新的根提交,并将分支\ ``gh-pages``\ 重置。 120 | 121 | :: 122 | 123 | $ git reset --hard $(echo "branch gh-pages init." | git commit-tree $(git write-tree)) 124 | 125 | * 执行推送命令,在GitHub远程版本库创建分支\ ``gh-pages``\ 。 126 | 127 | :: 128 | 129 | $ git push -u origin gh-pages 130 | 131 | 第二种方法用到Git底层命令:\ ``git symbolic-ref``\ 。步骤如下: 132 | 133 | 134 | * 用\ ``git symbolic-ref``\ 命令将当前工作分支由\ ``master``\ 切换到一个尚不\ 135 | 存在的分支\ ``gh-pages``\ 。 136 | 137 | :: 138 | 139 | $ git symbolic-ref HEAD refs/heads/gh-pages 140 | 141 | * 删除暂存区文件,即相当于清空暂存区。 142 | 143 | :: 144 | 145 | $ rm .git/index 146 | 147 | * 创建项目首页\ ``index.html``\ 。 148 | 149 | :: 150 | 151 | $ printf "hello world.\n" > index.html 152 | 153 | * 添加文件\ ``index.html``\ 到暂存区。 154 | 155 | :: 156 | 157 | $ git add index.html 158 | 159 | * 执行提交。提交完毕分支\ ``gh-pages``\ 完成创建。 160 | 161 | :: 162 | 163 | $ git commit -m "branch gh-pages init." 164 | 165 | * 执行推送命令,在GitHub远程版本库创建分支\ ``gh-pages``\ 。 166 | 167 | :: 168 | 169 | $ git push -u origin gh-pages 170 | 171 | 第三种方法没有使用任何Git底层命令,是从另外的版本库获取提交建立分支。\ 172 | 操作如下: 173 | 174 | * 在\ ``helloworld``\ 版本库之外创建另外一个版本库,例如\ ``helloworld-web``\ 。 175 | 176 | :: 177 | 178 | $ git init ../helloworld-web 179 | $ cd ../helloworld-web 180 | 181 | * 在\ ``helloworld-web``\ 版本库中创建主页文件\ ``index.html``\ 。 182 | 183 | :: 184 | 185 | $ printf "hello world.\n" > index.html 186 | 187 | * 添加文件\ ``index.html``\ 到暂存区。 188 | 189 | :: 190 | 191 | $ git add index.html 192 | 193 | * 执行提交。 194 | 195 | 实际提交到\ ``master``\ 分支,虽然提交说明中出现的是\ ``gh-pages`` \。 196 | 197 | :: 198 | 199 | $ git commit -m "branch gh-pages init." 200 | 201 | * 切换到\ ``helloworld``\ 版本库目录。 202 | 203 | :: 204 | 205 | $ cd ../helloworld 206 | 207 | * 从\ ``helloworld-web``\ 版本库获取提交,并据此创建\ ``gh-pages``\ 分支。 208 | 209 | :: 210 | 211 | $ git fetch ../helloworld-web 212 | $ git checkout -b gh-pages FETCH_HEAD 213 | 214 | 215 | * 执行推送命令,在GitHub远程版本库创建分支\ ``gh-pages``\ 。 216 | 217 | :: 218 | 219 | $ git push -u origin gh-pages 220 | 221 | 222 | 无论哪种方法,一旦在GitHub远程版本库中创建分支\ ``gh-pages``\ ,项目的主页\ 223 | 就已经建立。稍后(不超过10分钟),用浏览器访问下面的地址即可看到刚刚提交的\ 224 | 项目首页: http://gotgithub.github.io/helloworld/ 。 225 | 226 | 除了以上通过命令行创建\ ``gh-pages``\ 分支为项目设定主页之外,GitHub还提供\ 227 | 了图形操作界面。如图3-19所示。 228 | 229 | .. figure:: /images/project-hosting/github-pages.png 230 | :scale: 100 231 | 232 | 图3-19:项目管理页面中的GitHub Pages选项 233 | 234 | 当在项目管理页面中勾选“GitHub Pages”选项,并按照提示操作,会自动在项目版本库\ 235 | 中创建\ ``gh-pages``\ 分支。然后执行下面命令从版本库检出\ ``gh-pages``\ 分支,\ 236 | 对项目主页进行相应定制。 237 | 238 | :: 239 | 240 | $ git fetch 241 | $ git checkout gh-pages 242 | 243 | .. _dedicate-domain: 244 | 245 | 使用专有域名 246 | --------------- 247 | 248 | 无论是用户主页还是项目主页,除了使用\ ``github.com``\ 下的二级域名访问之外,\ 249 | 还可以使用专有域名。实现起来也非常简单,只要在\ ``master``\ 分支(用户主页\ 250 | 所在版本库)或\ ``gh-pages``\ 分支(项目版本库)的根目录下检入一个名为\ 251 | ``CNAME``\ 的文件,内容为相应的专有域名。当然还要更改专有域名的域名解析,\ 252 | 使得该专有域名的IP地址指向相应的GitHub二级域名的IP地址。 253 | 254 | 例如\ ``worldhello.net``\ [#]_\ 是我的个人网站,若计划将网站改为由GitHub托管,\ 255 | 并由账号\ ``gotgit``\ 通过个人主页提供服务,可做如下操作。 256 | 257 | 首先按照前面章节介绍的步骤,为账号\ ``gotgit``\ 设置账户主页。 258 | 259 | 1. 在账户\ ``gotgit``\ 下创建版本库\ ``gotgit.github.io``\ 以维护该账号主页。 260 | 261 | 地址: https://github.com/gotgit/gotgit.github.io/ 262 | 263 | 2. 将网站内容提交并推送到该版本库\ ``master``\ 分支中。 264 | 265 | 即在\ ``gotgit.github.io``\ 版本库的根目录下至少包含一个首页文件,如\ 266 | ``index.html``\ 。还可以使用下节将要介绍到的 Jekyll 技术,让网页有统一的\ 267 | 显示风格,此时首页文件可能并非一个完整的HTML文档,而是套用了页面模版。 268 | 269 | 3. 至此当访问网址\ http://gotgit.github.io\ 时,会将账号\ ``gotgit``\ 270 | 的版本库\ ``gotgit.github.io``\ 中的内容作为网站内容显示出来。 271 | 272 | 接下来进行如下操作,使得该网站能够使用专有域名\ ``www.worldhello.net``\ 273 | 提供服务。 274 | 275 | 1. 在账号\ ``gotgit``\ 的版本库\ ``gotgit.github.io``\ 根目录下添加文件\ 276 | ``CNAME``\ ,文件内容为:\ ``www.worldhello.net``\ 。 277 | 278 | 参见: https://github.com/gotgit/gotgit.github.io/blob/master/CNAME 279 | 280 | 2. 然后更改域名\ ``www.worldhello.net``\ 的IP地址,指向域名\ ``gotgit.github.io``\ 281 | 对应的IP地址(注意不是\ ``github.com``\ 的IP地址)。 282 | 283 | 完成域名的DNS指向后,可试着用\ ``ping``\ 或\ ``dig``\ 命令确认域名\ 284 | ``www.worldhello.net``\ 和\ ``gotgit.github.io``\ 指向同一IP地址。 285 | 286 | :: 287 | 288 | $ dig @8.8.8.8 -t a www.worldhello.net 289 | ... 290 | ; ANSWER SECTION: 291 | www.worldhello.net. 81078 IN A 204.232.175.78 292 | 293 | $ dig @8.8.8.8 -t a gotgit.github.io 294 | ... 295 | ; ANSWER SECTION: 296 | gotgit.github.io. 43200 IN A 204.232.175.78 297 | 298 | 设置完成后用浏览器访问 http://www.worldhello.net/ 即可看到由账号\ ``gotgit``\ 299 | 的版本库\ ``gotgit.github.io``\ 维护的页面。若将域名\ ``worldhello.net``\ 300 | (不带www前缀)也指向IP地址\ ``204.232.175.78``\ ,则访问网址\ 301 | http://worldhello.net/\ 会发现GitHub体贴地将该网址重定向到正确的地址\ 302 | http://www.worldhello.net/\ 。 303 | 304 | 在账号\ ``gotgit``\ 下的其他版本库,若包含了\ ``gh-pages``\ 分支,亦可由域名\ 305 | ``www.worldhello.net``\ 访问到。 306 | 307 | * 网址 http://www.worldhello.net/doc 实际对应于版本库 `gotgit/doc `_ 。 308 | * 网址 http://www.worldhello.net/gotgit 实际对应于版本库 `gotgit/gotgit `_ 。 309 | * 网址 http://www.worldhello.net/gotgithub 实际对应于版本库 `gotgit/gotgithub `_ 。 310 | 311 | 312 | .. _jekyll: 313 | 314 | 使用Jekyll维护网站 315 | ------------------------- 316 | 317 | Jekyll是一个支持Textile、Markdown等标记语言的静态网站生成软件,还支持博客和\ 318 | 网页模版,由Tom Preston-Werner(GitHub创始人之一)开发。Jekyll用Ruby语言实现,\ 319 | 项目在GitHub的托管地址: http://github.com/mojombo/jekyll/ ,专有的URL地址为:\ 320 | http://jekyllrb.com/\ 。 321 | 322 | GitHub为用户账号或项目提供主页服务,会从相应版本库的\ ``master``\ 分支或\ 323 | ``gh-pages``\ 分支检出网页文件,然后执行 Jekyll 相应的命令对网页进行编译。\ 324 | 因此在设计GitHub的用户主页和项目主页时都可以利用Jekyll,实现用Markdown等标记\ 325 | 语言撰写网页及博客,并用页面模版实现网页风格的统一。 326 | 327 | 安装Jekyll最简单的方法是通过RubyGems安装,会自动将Jekyll依赖的\ 328 | directory_watcher、liquid、open4、maruku和classifier等Gem包一并安装。 329 | 330 | :: 331 | 332 | $ gem install jekyll 333 | 334 | 如果安装过程因编译扩展模组遇到错误,可能是因为尚未安装所需的头文件,需要进行如下操作: 335 | 336 | * 对于Debian Linux、Ubuntu等可以用如下方法安装所需软件包: 337 | 338 | :: 339 | 340 | $ sudo apt-get install ruby1.8-dev 341 | 342 | * 如果是Red Hat、CentOS或Fedora等系统,使用如下命令安装: 343 | 344 | :: 345 | 346 | $ sudo yum install ruby-devel 347 | 348 | * 对于Mac OSX,可能需要更新RubyGems,如下: 349 | 350 | :: 351 | 352 | $ sudo gem update --system 353 | 354 | Jekyll安装完毕,执行下面的命令显示软件版本: 355 | 356 | :: 357 | 358 | $ jekyll -v 359 | Jekyll 0.11.0 360 | 361 | 要学习如何用Jekyll设计网站,可以先看一下作者Tom Preston-Werner在GitHub上的\ 362 | 个人网站是如何用Jekyll制作出来的。 363 | 364 | 克隆版本库: 365 | 366 | :: 367 | 368 | $ git clone git://github.com/mojombo/mojombo.github.com.git 369 | 370 | 版本库包含的文件如下: 371 | 372 | :: 373 | 374 | $ cd mojombo.github.com 375 | $ ls -F 376 | CNAME _config.yml _posts/ css/ index.html 377 | README.textile _layouts/ atom.xml images/ random/ 378 | 379 | 版本库根目录下的\ ``index.html``\ 文件不是一个普通的HTML文件,而是使用Liquid\ 380 | 模版语言\ [#]_\ 定义的页面。 381 | 382 | :: 383 | 384 | 1 --- 385 | 2 layout: default 386 | 3 title: Tom Preston-Werner 387 | 4 --- 388 | 5 389 | 6
390 | 7

Blog Posts

391 | 8
    392 | 9 {% for post in site.posts %} 393 | 10
  • {{ post.date | date_to_string }} » {{ post.title }}
  • 394 | 11 {% endfor %} 395 | 12
396 | ... 397 | 63
398 | 399 | 为方便描述为内容添加了行号,说明如下: 400 | 401 | * 第1-4行是YAML格式的文件头,设定了该文件所使用的模版文件及模版中要用到的变量。 402 | 403 | 凡是设置有YAML文件头的文件(目录\ ``_layouts``\ 除外)无论文件扩展名是什么,\ 404 | 都会在Jekyll编译时进行转换。若源文件由Markdown等标记语言撰写(扩展名为\ ``.md``\ 、\ 405 | ``.textile``\ 等),Jekyll还会将编译后的文件还将以扩展名\ ``.html``\ 来保存。 406 | 407 | * 其中第2行含义为使用default模版。 408 | 409 | 对应的模版文件为\ ``_layouts/default.html``\ 。 410 | 411 | * 第3行设定本页面的标题。 412 | 413 | 在模版文件\ ``_layouts/default.html``\ 中用\ ``{{ page.title }}``\ 语法嵌入\ 414 | 所设置的标题。下面是模版文件中部分内容: 415 | 416 | :: 417 | 418 | 419 | 420 | {{ page.title }} 421 | 422 | * 第6行开始的内容绝大多数是标准的HTML语法,其中夹杂少量Liquid模版特有的语法。 423 | 424 | * 第9行和第11行,对于有着Liquid或其他模版编程经验的用户,不难理解其中出现的由\ 425 | “{%”和“%}”标识的指令是一个循环指令(for循环),用于逐条对博客进行相关操作。 426 | 427 | * 第10行中由“{{”和“}}”标识的表达式则用于显示博文的日期、链接和标题。 428 | 429 | 非下划线(_)开头的文件(包括子目录中文件),如果包含YAML文件头,就会使用Jekyll\ 430 | 进行编译,并将编译后的文件复制到目标文件夹(默认为\ ``_site``\ 目录)下。\ 431 | 对于包含YAML文件头并用标记语言Markdown等撰写的文件,还会将编译后的文件以\ 432 | ``.html``\ 扩展名保存。而以下划线开头的文件和目录有的直接忽略不予处理(如\ 433 | ``_layouts``\ 、\ ``_site``\ 目录等),有的则需要特殊处理(如\ ``_post``\ 目录)。 434 | 435 | 目录\ ``_post``\ 用于保存博客条目,每个博客条目都以\ ``--
-``\ 436 | 格式的文件名命名。扩展名为\ ``.md``\ 的为Markdown格式,扩展名为\ ``.textile``\ 437 | 的为Textile格式。这些文件都包含类似的文件头: 438 | 439 | :: 440 | 441 | --- 442 | layout: post 443 | title: How I Turned Down $300,000 from Microsoft to go Full-Time on GitHub 444 | --- 445 | 446 | 即博客使用文件\ ``_layouts/post.html``\ 作为页面模版,而不是\ ``index.html``\ 447 | 等文件所使用的\ ``_layouts/default.html``\ 模版。这些模版文件都采用Liquid模版\ 448 | 语法。保存于\ ``_post``\ 目录下的博客文件编译后会以\ 449 | ``//
/.html``\ 文件名保存在输出目录中。 450 | 451 | 在根目录下还有一个配置文件\ ``_config.yml``\ 用于覆盖Jekyll的默认设置,例如\ 452 | 本版本库中的设置。 453 | 454 | :: 455 | 456 | markdown: rdiscount 457 | pygments: true 458 | 459 | 第1行设置使用rdiscount软件包作为Markdown的解析引擎,而非默认的Maruku。第2行开启\ 460 | pygments支持。对于中文用户强烈建议通过配置文件\ ``_config.yml``\ 重设 markdown \ 461 | 解析引擎,默认的 Maruku 对中文支持不好,而使用 rdiscount 或 kramdown 均可。\ 462 | 关于该配置文件的更多参数详见Jekyll项目维基\ [#]_\ 。 463 | 464 | 编译Jekyll编辑网站只需在根目录执行\ ``jekyll``\ 命令,下面的命令是GitHub更新\ 465 | 网站所使用的默认指令。 466 | 467 | :: 468 | 469 | $ jekyll --pygments --safe 470 | 471 | 现在执行这条命令,就会将整个网站创建在目录\ ``_site``\ 下。 472 | 473 | 如果没有安装Apache等Web服务器,还可以使用Jekyll的内置Web服务器。 474 | 475 | :: 476 | 477 | $ jekyll --server 478 | 479 | 默认在端口4000开启Web服务器。 480 | 481 | 网址 http://gitready.com/ 是一个提供Git使用小窍门的网站,如图3-20所示。 482 | 483 | .. figure:: /images/project-hosting/gitready.png 484 | :scale: 100 485 | 486 | 图3-20:Git Ready 网站 487 | 488 | 你相信这是一个用Jekyll制作的网站么?看看该网站对应的IP,会发现其指向的正是GitHub。\ 489 | 研究GitHub上 `gitready`_ 用户托管的版本库,会发现\ ``en``\ 版本库的\ ``gh-pages``\ 490 | 分支负责生成\ ``gitready.com``\ 网站,\ ``de``\ 版本库的\ ``gh-pages``\ 分支负责\ 491 | 生成德文网站\ ``de.gitready.com``\ ,等等。而\ ``gitready``\ 版本库则是各种语种网站的汇总。 492 | 493 | .. _`gitready`: https://github.com/gitready 494 | 495 | 我的个人网站也使用Jekyll构建并托管在GitHub上,网址:\ http://www.worldhello.net/\ 。 496 | 497 | ---- 498 | 499 | .. [#] “Hello, world”最为程序员所熟知,2002年申请不到helloworld相关域名便\ 500 | 退而求其次,申请了 worldhello.net。 501 | .. [#] http://liquidmarkup.org/ 502 | .. [#] https://github.com/mojombo/jekyll/wiki/configuration 503 | -------------------------------------------------------------------------------- /contrib/sphinx/0001-No-prefix-_-for-sphinx-build-output-directories.patch: -------------------------------------------------------------------------------- 1 | From d83d125e3dadcd13b7cc95fba28e4036b4089b85 Mon Sep 17 00:00:00 2001 2 | Message-Id: 3 | From: Jiang Xin 4 | Date: Fri, 26 Aug 2011 10:39:23 +0800 5 | Subject: [PATCH] No prefix '_' for sphinx build output directories 6 | 7 | [This hack is based on sphinx 1.1.3.] 8 | 9 | Some phinx output dirs has a prefix such as '_static', '_sources', 10 | '_images', '_downloads'. Because GitHub website host does not like 11 | directories with a '_' prefix, and these paths are hard-coded (can 12 | not be changed through config files), so comes this workaround. 13 | 14 | This patch change the hard-coded dirs from '_static', '_images', 15 | '_sources', '_downloads' to 'static', 'images', 'sources', and 16 | 'downloads'. 17 | 18 | Signed-off-by: Jiang Xin 19 | --- 20 | doc/_templates/indexsidebar.html | 2 +- 21 | doc/_templates/layout.html | 2 +- 22 | doc/conf.py | 10 ++++---- 23 | doc/config.rst | 6 ++--- 24 | doc/templating.rst | 2 +- 25 | sphinx/application.py | 4 +-- 26 | sphinx/builders/html.py | 40 ++++++++++++++--------------- 27 | sphinx/builders/htmlhelp.py | 2 +- 28 | sphinx/builders/qthelp.py | 4 +-- 29 | sphinx/builders/websupport.py | 10 ++++---- 30 | sphinx/ext/graphviz.py | 2 +- 31 | sphinx/ext/pngmath.py | 2 +- 32 | sphinx/quickstart.py | 6 ++--- 33 | sphinx/themes/agogo/layout.html | 4 +-- 34 | sphinx/themes/basic/domainindex.html | 2 +- 35 | sphinx/themes/basic/layout.html | 10 ++++---- 36 | sphinx/themes/basic/search.html | 2 +- 37 | sphinx/themes/basic/sourcelink.html | 2 +- 38 | sphinx/themes/basic/static/searchtools.js_t | 2 +- 39 | sphinx/themes/default/layout.html | 2 +- 40 | sphinx/themes/haiku/layout.html | 8 +++--- 41 | sphinx/themes/pyramid/layout.html | 4 +-- 42 | sphinx/themes/pyramid/static/ie6.css | 2 +- 43 | sphinx/themes/scrolls/layout.html | 4 +-- 44 | sphinx/websupport/__init__.py | 2 +- 45 | tests/root/_static/README | 1 - 46 | tests/root/_static/excluded.css | 1 - 47 | tests/root/_static/subdir/foo.css | 1 - 48 | tests/root/conf.py | 2 +- 49 | tests/root/static/README | 1 + 50 | tests/root/static/excluded.css | 1 + 51 | tests/root/static/subdir/foo.css | 1 + 52 | tests/test_build_html.py | 24 ++++++++--------- 53 | tests/test_quickstart.py | 11 +++++--- 54 | 34 files changed, 91 insertions(+), 88 deletions(-) 55 | delete mode 100644 tests/root/_static/README 56 | delete mode 100644 tests/root/_static/excluded.css 57 | delete mode 100644 tests/root/_static/subdir/foo.css 58 | create mode 100644 tests/root/static/README 59 | create mode 100644 tests/root/static/excluded.css 60 | create mode 100644 tests/root/static/subdir/foo.css 61 | 62 | diff --git a/doc/_templates/indexsidebar.html b/doc/_templates/indexsidebar.html 63 | index feafd90..e3125f6 100644 64 | --- a/doc/_templates/indexsidebar.html 65 | +++ b/doc/_templates/indexsidebar.html 66 | @@ -1,5 +1,5 @@ 67 | 69 | +

70 | 71 |

Download

72 | {% if version.endswith('(hg)') %} 73 | diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html 74 | index 6e609e1..d748c8a 100644 75 | --- a/doc/_templates/layout.html 76 | +++ b/doc/_templates/layout.html 77 | @@ -18,6 +18,6 @@ 78 | 79 | {% block header %} 80 |
81 | -Sphinx logo 82 | +Sphinx logo 83 |
84 | {% endblock %} 85 | diff --git a/doc/conf.py b/doc/conf.py 86 | index 1b8ba3e..f5c49b1 100644 87 | --- a/doc/conf.py 88 | +++ b/doc/conf.py 89 | @@ -20,7 +20,7 @@ show_authors = True 90 | 91 | html_theme = 'sphinxdoc' 92 | modindex_common_prefix = ['sphinx.'] 93 | -html_static_path = ['_static'] 94 | +html_static_path = ['static'] 95 | html_sidebars = {'index': ['indexsidebar.html', 'searchbox.html']} 96 | html_additional_pages = {'index': 'index.html'} 97 | html_use_opensearch = 'http://sphinx.pocoo.org' 98 | @@ -34,13 +34,13 @@ epub_publisher = 'http://sphinx.pocoo.org/' 99 | epub_scheme = 'url' 100 | epub_identifier = epub_publisher 101 | epub_pre_files = [('index.html', 'Welcome')] 102 | -epub_exclude_files = ['_static/opensearch.xml', '_static/doctools.js', 103 | - '_static/jquery.js', '_static/searchtools.js', '_static/underscore.js', 104 | - '_static/basic.css', 'search.html'] 105 | +epub_exclude_files = ['static/opensearch.xml', 'static/doctools.js', 106 | + 'static/jquery.js', 'static/searchtools.js', 'static/underscore.js', 107 | + 'static/basic.css', 'search.html'] 108 | 109 | latex_documents = [('contents', 'sphinx.tex', 'Sphinx Documentation', 110 | 'Georg Brandl', 'manual', 1)] 111 | -latex_logo = '_static/sphinx.png' 112 | +latex_logo = 'static/sphinx.png' 113 | latex_elements = { 114 | 'fontpkg': '\\usepackage{palatino}', 115 | } 116 | diff --git a/doc/config.rst b/doc/config.rst 117 | index 5d188c6..0da3f3a 100644 118 | --- a/doc/config.rst 119 | +++ b/doc/config.rst 120 | @@ -491,7 +491,7 @@ that use Sphinx' HTMLWriter class. 121 | exceed 200 pixels. Default: ``None``. 122 | 123 | .. versionadded:: 0.4.1 124 | - The image file will be copied to the ``_static`` directory of the output 125 | + The image file will be copied to the ``static`` directory of the output 126 | HTML, so an already existing file with that name will be overwritten. 127 | 128 | .. confval:: html_favicon 129 | @@ -836,8 +836,8 @@ the `Dublin Core metadata `_. 130 | template filename is empty, no html cover page is created. No cover at all 131 | is created if the tuple is empty. Examples:: 132 | 133 | - epub_cover = ('_static/cover.png', 'epub-cover.html') 134 | - epub_cover = ('_static/cover.png', '') 135 | + epub_cover = ('static/cover.png', 'epub-cover.html') 136 | + epub_cover = ('static/cover.png', '') 137 | epub_cover = () 138 | 139 | The default value is ``()``. 140 | diff --git a/doc/templating.rst b/doc/templating.rst 141 | index 05a1346..1e24904 100644 142 | --- a/doc/templating.rst 143 | +++ b/doc/templating.rst 144 | @@ -198,7 +198,7 @@ Overriding works like this:: 145 | 146 | Add additional script files here, like this:: 147 | 148 | - {% set script_files = script_files + ["_static/myscript.js"] %} 149 | + {% set script_files = script_files + ["static/myscript.js"] %} 150 | 151 | .. data:: css_files 152 | 153 | diff --git a/sphinx/application.py b/sphinx/application.py 154 | index bfb39a7..9d336bf 100644 155 | --- a/sphinx/application.py 156 | +++ b/sphinx/application.py 157 | @@ -467,7 +467,7 @@ class Sphinx(object): 158 | StandaloneHTMLBuilder.script_files.append(filename) 159 | else: 160 | StandaloneHTMLBuilder.script_files.append( 161 | - posixpath.join('_static', filename)) 162 | + posixpath.join('static', filename)) 163 | 164 | def add_stylesheet(self, filename): 165 | from sphinx.builders.html import StandaloneHTMLBuilder 166 | @@ -475,7 +475,7 @@ class Sphinx(object): 167 | StandaloneHTMLBuilder.css_files.append(filename) 168 | else: 169 | StandaloneHTMLBuilder.css_files.append( 170 | - posixpath.join('_static', filename)) 171 | + posixpath.join('static', filename)) 172 | 173 | def add_lexer(self, alias, lexer): 174 | from sphinx.highlighting import lexers 175 | diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py 176 | index 8184037..1b38437 100644 177 | --- a/sphinx/builders/html.py 178 | +++ b/sphinx/builders/html.py 179 | @@ -71,8 +71,8 @@ class StandaloneHTMLBuilder(Builder): 180 | embedded = False # for things like HTML help or Qt help: suppresses sidebar 181 | 182 | # This is a class attribute because it is mutated by Sphinx.add_javascript. 183 | - script_files = ['_static/jquery.js', '_static/underscore.js', 184 | - '_static/doctools.js'] 185 | + script_files = ['static/jquery.js', 'static/underscore.js', 186 | + 'static/doctools.js'] 187 | # Dito for this one. 188 | css_files = [] 189 | 190 | @@ -104,7 +104,7 @@ class StandaloneHTMLBuilder(Builder): 191 | 192 | if self.config.language is not None: 193 | if self._get_translations_js(): 194 | - self.script_files.append('_static/translations.js') 195 | + self.script_files.append('static/translations.js') 196 | 197 | def _get_translations_js(self): 198 | candidates = [path.join(package_dir, 'locale', self.config.language, 199 | @@ -412,9 +412,9 @@ class StandaloneHTMLBuilder(Builder): 200 | doctree.settings = self.docsettings 201 | 202 | self.secnumbers = self.env.toc_secnumbers.get(docname, {}) 203 | - self.imgpath = relative_uri(self.get_target_uri(docname), '_images') 204 | + self.imgpath = relative_uri(self.get_target_uri(docname), 'images') 205 | self.post_process_images(doctree) 206 | - self.dlpath = relative_uri(self.get_target_uri(docname), '_downloads') 207 | + self.dlpath = relative_uri(self.get_target_uri(docname), 'downloads') 208 | self.current_docname = docname 209 | self.docwriter.write(doctree, destination) 210 | self.docwriter.assemble_parts() 211 | @@ -452,7 +452,7 @@ class StandaloneHTMLBuilder(Builder): 212 | 213 | if self.config.html_use_opensearch and self.name != 'htmlhelp': 214 | self.info(' opensearch', nonl=1) 215 | - fn = path.join(self.outdir, '_static', 'opensearch.xml') 216 | + fn = path.join(self.outdir, 'static', 'opensearch.xml') 217 | self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn) 218 | 219 | self.info() 220 | @@ -507,13 +507,13 @@ class StandaloneHTMLBuilder(Builder): 221 | def copy_image_files(self): 222 | # copy image files 223 | if self.images: 224 | - ensuredir(path.join(self.outdir, '_images')) 225 | + ensuredir(path.join(self.outdir, 'images')) 226 | for src in self.status_iterator(self.images, 'copying images... ', 227 | brown, len(self.images)): 228 | dest = self.images[src] 229 | try: 230 | copyfile(path.join(self.srcdir, src), 231 | - path.join(self.outdir, '_images', dest)) 232 | + path.join(self.outdir, 'images', dest)) 233 | except Exception, err: 234 | self.warn('cannot copy image file %r: %s' % 235 | (path.join(self.srcdir, src), err)) 236 | @@ -521,14 +521,14 @@ class StandaloneHTMLBuilder(Builder): 237 | def copy_download_files(self): 238 | # copy downloadable files 239 | if self.env.dlfiles: 240 | - ensuredir(path.join(self.outdir, '_downloads')) 241 | + ensuredir(path.join(self.outdir, 'downloads')) 242 | for src in self.status_iterator(self.env.dlfiles, 243 | 'copying downloadable files... ', 244 | brown, len(self.env.dlfiles)): 245 | dest = self.env.dlfiles[src][1] 246 | try: 247 | copyfile(path.join(self.srcdir, src), 248 | - path.join(self.outdir, '_downloads', dest)) 249 | + path.join(self.outdir, 'downloads', dest)) 250 | except Exception, err: 251 | self.warn('cannot copy downloadable file %r: %s' % 252 | (path.join(self.srcdir, src), err)) 253 | @@ -536,16 +536,16 @@ class StandaloneHTMLBuilder(Builder): 254 | def copy_static_files(self): 255 | # copy static files 256 | self.info(bold('copying static files... '), nonl=True) 257 | - ensuredir(path.join(self.outdir, '_static')) 258 | + ensuredir(path.join(self.outdir, 'static')) 259 | # first, create pygments style file 260 | - f = open(path.join(self.outdir, '_static', 'pygments.css'), 'w') 261 | + f = open(path.join(self.outdir, 'static', 'pygments.css'), 'w') 262 | f.write(self.highlighter.get_stylesheet()) 263 | f.close() 264 | # then, copy translations JavaScript file 265 | if self.config.language is not None: 266 | jsfile = self._get_translations_js() 267 | if jsfile: 268 | - copyfile(jsfile, path.join(self.outdir, '_static', 269 | + copyfile(jsfile, path.join(self.outdir, 'static', 270 | 'translations.js')) 271 | 272 | # add context items for search function used in searchtools.js_t 273 | @@ -557,7 +557,7 @@ class StandaloneHTMLBuilder(Builder): 274 | themeentries = [path.join(themepath, 'static') 275 | for themepath in self.theme.get_dirchain()[::-1]] 276 | for entry in themeentries: 277 | - copy_static_entry(entry, path.join(self.outdir, '_static'), 278 | + copy_static_entry(entry, path.join(self.outdir, 'static'), 279 | self, ctx) 280 | # then, copy over all user-supplied static files 281 | staticentries = [path.join(self.confdir, spath) 282 | @@ -570,18 +570,18 @@ class StandaloneHTMLBuilder(Builder): 283 | if not path.exists(entry): 284 | self.warn('html_static_path entry %r does not exist' % entry) 285 | continue 286 | - copy_static_entry(entry, path.join(self.outdir, '_static'), self, 287 | + copy_static_entry(entry, path.join(self.outdir, 'static'), self, 288 | ctx, exclude_matchers=matchers) 289 | # copy logo and favicon files if not already in static path 290 | if self.config.html_logo: 291 | logobase = path.basename(self.config.html_logo) 292 | - logotarget = path.join(self.outdir, '_static', logobase) 293 | + logotarget = path.join(self.outdir, 'static', logobase) 294 | if not path.isfile(logotarget): 295 | copyfile(path.join(self.confdir, self.config.html_logo), 296 | logotarget) 297 | if self.config.html_favicon: 298 | iconbase = path.basename(self.config.html_favicon) 299 | - icontarget = path.join(self.outdir, '_static', iconbase) 300 | + icontarget = path.join(self.outdir, 'static', iconbase) 301 | if not path.isfile(icontarget): 302 | copyfile(path.join(self.confdir, self.config.html_favicon), 303 | icontarget) 304 | @@ -746,7 +746,7 @@ class StandaloneHTMLBuilder(Builder): 305 | self.warn("error writing file %s: %s" % (outfilename, err)) 306 | if self.copysource and ctx.get('sourcename'): 307 | # copy the source file for the "show source" link 308 | - source_name = path.join(self.outdir, '_sources', 309 | + source_name = path.join(self.outdir, 'sources', 310 | os_path(ctx['sourcename'])) 311 | ensuredir(path.dirname(source_name)) 312 | copyfile(self.env.doc2path(pagename), source_name) 313 | @@ -930,7 +930,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder): 314 | 315 | if self.config.html_use_opensearch: 316 | self.info(' opensearch', nonl=1) 317 | - fn = path.join(self.outdir, '_static', 'opensearch.xml') 318 | + fn = path.join(self.outdir, 'static', 'opensearch.xml') 319 | self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn) 320 | 321 | self.info() 322 | @@ -1003,7 +1003,7 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder): 323 | # if there is a source file, copy the source file for the 324 | # "show source" link 325 | if ctx.get('sourcename'): 326 | - source_name = path.join(self.outdir, '_sources', 327 | + source_name = path.join(self.outdir, 'sources', 328 | os_path(ctx['sourcename'])) 329 | ensuredir(path.dirname(source_name)) 330 | copyfile(self.env.doc2path(pagename), source_name) 331 | diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py 332 | index fdf25cc..d122563 100644 333 | --- a/sphinx/builders/htmlhelp.py 334 | +++ b/sphinx/builders/htmlhelp.py 335 | @@ -212,7 +212,7 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): 336 | outdir += os.sep 337 | olen = len(outdir) 338 | for root, dirs, files in os.walk(outdir): 339 | - staticdir = root.startswith(path.join(outdir, '_static')) 340 | + staticdir = root.startswith(path.join(outdir, 'static')) 341 | for fn in files: 342 | if (staticdir and not fn.endswith('.js')) or \ 343 | fn.endswith('.html'): 344 | diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py 345 | index 5e5deaf..f2fa13a 100644 346 | --- a/sphinx/builders/qthelp.py 347 | +++ b/sphinx/builders/qthelp.py 348 | @@ -155,8 +155,8 @@ class QtHelpBuilder(StandaloneHTMLBuilder): 349 | outdir += os.sep 350 | olen = len(outdir) 351 | projectfiles = [] 352 | - staticdir = path.join(outdir, '_static') 353 | - imagesdir = path.join(outdir, '_images') 354 | + staticdir = path.join(outdir, 'static') 355 | + imagesdir = path.join(outdir, 'images') 356 | for root, dirs, files in os.walk(outdir): 357 | resourcedir = root.startswith(staticdir) or \ 358 | root.startswith(imagesdir) 359 | diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py 360 | index b775730..f0a1135 100644 361 | --- a/sphinx/builders/websupport.py 362 | +++ b/sphinx/builders/websupport.py 363 | @@ -37,7 +37,7 @@ class WebSupportBuilder(PickleHTMLBuilder): 364 | raise RuntimeError('websupport builder must be used with ' 365 | 'the builtin templates') 366 | # add our custom JS 367 | - self.script_files.append('_static/websupport.js') 368 | + self.script_files.append('static/websupport.js') 369 | 370 | def set_webinfo(self, staticdir, virtual_staticdir, search, storage): 371 | self.staticdir = staticdir 372 | @@ -58,9 +58,9 @@ class WebSupportBuilder(PickleHTMLBuilder): 373 | 374 | self.cur_docname = docname 375 | self.secnumbers = self.env.toc_secnumbers.get(docname, {}) 376 | - self.imgpath = '/' + posixpath.join(self.virtual_staticdir, '_images') 377 | + self.imgpath = '/' + posixpath.join(self.virtual_staticdir, 'images') 378 | self.post_process_images(doctree) 379 | - self.dlpath = '/' + posixpath.join(self.virtual_staticdir, '_downloads') 380 | + self.dlpath = '/' + posixpath.join(self.virtual_staticdir, 'downloads') 381 | self.docwriter.write(doctree, destination) 382 | self.docwriter.assemble_parts() 383 | body = self.docwriter.parts['fragment'] 384 | @@ -129,7 +129,7 @@ class WebSupportBuilder(PickleHTMLBuilder): 385 | # "show source" link 386 | if ctx.get('sourcename'): 387 | source_name = path.join(self.staticdir, 388 | - '_sources', os_path(ctx['sourcename'])) 389 | + 'sources', os_path(ctx['sourcename'])) 390 | ensuredir(path.dirname(source_name)) 391 | copyfile(self.env.doc2path(pagename), source_name) 392 | 393 | @@ -142,7 +142,7 @@ class WebSupportBuilder(PickleHTMLBuilder): 394 | PickleHTMLBuilder.handle_finish(self) 395 | 396 | # move static stuff over to separate directory 397 | - directories = ['_images', '_static'] 398 | + directories = ['images', 'static'] 399 | for directory in directories: 400 | src = path.join(self.outdir, directory) 401 | dst = path.join(self.staticdir, directory) 402 | diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py 403 | index ee93594..296cf0e 100644 404 | --- a/sphinx/ext/graphviz.py 405 | +++ b/sphinx/ext/graphviz.py 406 | @@ -128,7 +128,7 @@ def render_dot(self, code, options, format, prefix='graphviz'): 407 | if hasattr(self.builder, 'imgpath'): 408 | # HTML 409 | relfn = posixpath.join(self.builder.imgpath, fname) 410 | - outfn = path.join(self.builder.outdir, '_images', fname) 411 | + outfn = path.join(self.builder.outdir, 'images', fname) 412 | else: 413 | # LaTeX 414 | relfn = fname 415 | diff --git a/sphinx/ext/pngmath.py b/sphinx/ext/pngmath.py 416 | index 78c331a..e391e4c 100644 417 | --- a/sphinx/ext/pngmath.py 418 | +++ b/sphinx/ext/pngmath.py 419 | @@ -78,7 +78,7 @@ def render_math(self, math): 420 | 421 | shasum = "%s.png" % sha(math.encode('utf-8')).hexdigest() 422 | relfn = posixpath.join(self.builder.imgpath, 'math', shasum) 423 | - outfn = path.join(self.builder.outdir, '_images', 'math', shasum) 424 | + outfn = path.join(self.builder.outdir, 'images', 'math', shasum) 425 | if path.isfile(outfn): 426 | depth = read_png_depth(outfn) 427 | return relfn, depth 428 | diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py 429 | index 86dc047..4552e91 100644 430 | --- a/sphinx/quickstart.py 431 | +++ b/sphinx/quickstart.py 432 | @@ -160,7 +160,7 @@ html_theme = 'default' 433 | # Add any paths that contain custom static files (such as style sheets) here, 434 | # relative to this directory. They are copied after the builtin static files, 435 | # so a file named "default.css" will overwrite the builtin "default.css". 436 | -html_static_path = ['%(dot)sstatic'] 437 | +html_static_path = ['static'] 438 | 439 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 440 | # using the given strftime format. 441 | @@ -852,7 +852,7 @@ Either, you use a directory "_build" within the root path, or you separate 442 | if 'dot' not in d: 443 | print ''' 444 | Inside the root directory, two more directories will be created; "_templates" 445 | -for custom HTML templates and "_static" for custom stylesheets and other static 446 | +for custom HTML templates and "static" for custom stylesheets and other static 447 | files. You can enter another prefix (such as ".") to replace the underscore.''' 448 | do_prompt(d, 'dot', 'Name prefix for templates and static dir', '_', ok) 449 | 450 | @@ -997,7 +997,7 @@ def generate(d, overwrite=True, silent=False): 451 | d['exclude_patterns'] = repr(d['dot'] + 'build') 452 | mkdir_p(builddir) 453 | mkdir_p(path.join(srcdir, d['dot'] + 'templates')) 454 | - mkdir_p(path.join(srcdir, d['dot'] + 'static')) 455 | + mkdir_p(path.join(srcdir, 'static')) 456 | 457 | def write_file(fpath, mode, content): 458 | if overwrite or not path.isfile(fpath): 459 | diff --git a/sphinx/themes/agogo/layout.html b/sphinx/themes/agogo/layout.html 460 | index d063194..fd6d4a2 100644 461 | --- a/sphinx/themes/agogo/layout.html 462 | +++ b/sphinx/themes/agogo/layout.html 463 | @@ -15,7 +15,7 @@ 464 |
465 | {%- if logo %} 466 | 470 | {%- endif %} 471 | {%- block headertitle %} 472 | @@ -75,7 +75,7 @@ 473 | {%- endfor %} 474 | {%- if show_source and has_source and sourcename %} 475 |
476 | - {{ _('Show Source') }} 479 | {%- endif %} 480 |
481 | diff --git a/sphinx/themes/basic/domainindex.html b/sphinx/themes/basic/domainindex.html 482 | index 947a01e..776c034 100644 483 | --- a/sphinx/themes/basic/domainindex.html 484 | +++ b/sphinx/themes/basic/domainindex.html 485 | @@ -39,7 +39,7 @@ 486 | in entries %} 487 | 488 | {% if grouptype == 1 -%} 489 | - 492 | {%- endif %} 493 | {% if grouptype == 2 %}   {% endif %} 494 | diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html 495 | index 9fb989c..a00570e 100644 496 | --- a/sphinx/themes/basic/layout.html 497 | +++ b/sphinx/themes/basic/layout.html 498 | @@ -52,7 +52,7 @@ 499 | {%- block sidebarlogo %} 500 | {%- if logo %} 501 | 505 | {%- endif %} 506 | {%- endblock %} 507 | @@ -100,8 +100,8 @@ 508 | {%- endmacro %} 509 | 510 | {%- macro css() %} 511 | - 512 | - 513 | + 514 | + 515 | {%- for cssfile in css_files %} 516 | 517 | {%- endfor %} 518 | @@ -120,10 +120,10 @@ 519 | {%- if use_opensearch %} 520 | 523 | + href="{{ pathto('static/opensearch.xml', 1) }}"/> 524 | {%- endif %} 525 | {%- if favicon %} 526 | - 527 | + 528 | {%- endif %} 529 | {%- endif %} 530 | {%- block linktags %} 531 | diff --git a/sphinx/themes/basic/search.html b/sphinx/themes/basic/search.html 532 | index 4cdc693..69a5b6e 100644 533 | --- a/sphinx/themes/basic/search.html 534 | +++ b/sphinx/themes/basic/search.html 535 | @@ -9,7 +9,7 @@ 536 | #} 537 | {% extends "layout.html" %} 538 | {% set title = _('Search') %} 539 | -{% set script_files = script_files + ['_static/searchtools.js'] %} 540 | +{% set script_files = script_files + ['static/searchtools.js'] %} 541 | {% block extrahead %} 542 |