Github和GitBook使用

1. GitHub

1.1. 创建账户

https://github.com/ 在上面注册账户: gitexample

1.2. 生成本地公钥/私钥对

在本地Ubuntu上,ssh-keygen命令创建公钥/私钥对:

$ ssh-keygen

然后根据提示在用户主目录下的.ssh目录中创建默认的公钥/私钥对文件,其中/.ssh/id_rsa是私钥文件,/.ssh/id_rsa.pub是公钥文件。注意私钥文件要严加保护,不能泄露给任何人。如果在执行ssh-keygen命令时选择了使用口令保护私钥,私钥文件是经过加密的。至于公钥文件~/.ssh/id_rsa.pub则可以放心地公开给他人。
也可以用ssh-keygen命令以不同的名称创建多个公钥,当拥有多个GitHub账号时,非常重要。这是因为虽然一个GitHub账号允许使用多个不同的SSH公钥,但反过来,一个SSH公钥只能对应于一个GitHub账号。下面的命令在~/.ssh目录下创建名为gitexample的私钥和名为gitexample.pub的公钥文件。

$ ssh-keygen -C "gitexample@gmail.com" -f ~/.ssh/gitexample

$ ls ~/.ssh/
gitexample  gitexample.pub

当生成的公钥/私钥对不在缺省位置(~/.ssh/id_rsa等)时,使用ssh命令连接远程主机时需要使用参数-i <filename>指定公钥/私钥对。或者在配置文件/.ssh/config中针对相应主机进行设定。例如对于上例创建了非缺省公钥/私钥对/.ssh/gitexample,可以在~/.ssh/config配置文件中写入如下配置。

Host github.com
    User git 
    Hostname github.com
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/gitexample               

好了,有了上面的准备,就将~/.ssh/gitexample.pub文件内容拷贝到剪切板。然后将公钥文件中的内容粘贴到GitHub的SSH公钥管理的对话框中。
设置成功后,再用ssh命令访问GitHub,会显示一条认证成功信息并退出。在认证成功的信息中还会显示该公钥对应的用户名。

$ ssh -T git@github.com
Hi gitexample! You have successfully authenticated, but GitHub does not provide shell access.

1.3. 项目托管

本章介绍如何在GitHub上创建一个新项目,包括创建版本库及为项目设计主页等。

1.3.1. 创建新项目

Your repositories --> Create a new repository --> set Repository name --> Create Reopsitory Button

1.3.2. 版本库初始化

如果是从头创建版本库,可以采用先克隆,建立提交数据,最后再通过推送完成GitHub版本库的初始化。步骤如下:

$ git clone git@github.com:gitexample/helloworld.git helloworld
Cloning into 'helloworld'...
Warning: Permanently added the RSA host key for IP address '13.250.177.223' to the list of known hosts.
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.
Checking connectivity... done.

1.3.3. 创建并修改文件README.md

My first GitHub project
This is a example repository.

Support markdown syntax format.

1.3.4. 添加README.md文件并提交。

$ git add README.md
$ git commit -m "README for this project."

1.3.5. 向GitHub推送,完成版本库初始化。

$ git push origin master

1.3.6. 从已有版本库创建

如果在GitHub项目初始化之前,数据已经存在于本地版本库中,显然像上面那样先克隆、再提交、后推送的方法就不适宜了。应该采用下面的方法。
为试验新的版本库初始化方法,先把刚刚新建的测试项目“helloworld”删除,同时也将本地工作区中克隆的“helloworld”删除。警告:删除项目的操作非常危险,不可恢复,慎用。
点击项目首页中"Settings"选项卡,在"Danger Zone"区域有"Delete this repository"
然后再重建版本库“helloworld”,见3.1节。
本地建立一个Git版本库。

$ mkdir helloworld
$ cd helloworld
$ git init

然后在版本库中添加示例文件,如README.md文件,内容同前。

$ git add README.md
$ git commit -m "README for this project."

为版本库添加名为origin的远程版本库。

$ git remote add origin git@github.com:gitexample/helloworld.git

执行推送命令,完成GitHub版本库的初始化。注意命令行中的-u参数,在推送成功后自动建立本地分支与远程版本库分支的追踪。

$ git push -u origin master

1.4. 操作版本库

1.4.1. 强制推送

上文提交时,提交用户设置的邮件地址并非gitexample用户设置的邮件地址。补救办法就是对此提交进行修改,然后强制推送到GitHub。
重新设置core.editor,user.name和user.email配置变量。

$ git config --global core.editor vim
$ git config user.name "gitexample"
$ git config user.email "gitexample@gmail.com"

执行Git修补提交命令。
注意使用参数--reset-author会将提交信息中的属性Author连同AuthorDate一并修改,否则只修改Commit和CommitDate。参数-C HEAD维持提交说明不变。

$ git commit --amend --reset-author -C HEAD

查看提交日志,发现提交者信息和作者信息都已经更改。

$ git log --pretty=fuller
commit 959af4fe61bbdc984dcc26acd139ea3e164096f4
Author:     gitexample <gitexample@gmail.com>
AuthorDate: Tue Sep 4 15:58:25 2018 +0800
Commit:     gitexample <gitexample@gmail.com>
CommitDate: Tue Sep 4 15:58:25 2018 +0800

    README for this project.

使用强制推送。

$ git push -f origin master

1.4.2. 新建分支

Git的分支就是保存在.git/refs/heads/命名空间下的引用。引用文件的内容是该分支对应的提交ID。当前版本库中的默认分支master就对应于文件.git/refs/heads/master。
若在GitHub版本库中创建分支,首先要在本地版本库中创建新的分支(即引用),然后用推送命令将本地创建的新的引用连同所指向的提交推送到GitHub版本库中完成GitHub上分支的创建。操作如下:
本地版本库中建立新分支mybranch1。
创建分支有多种方法,如使用git branch命令,但最为便捷的就是git checkout -b命令,同时完成新分支的创建和分支切换。

$ git checkout -b mybranch1
Switched to a new branch 'mybranch1'

为了易于识别,添加一个新文件hello1,并提交。

$ touch hello1
$ echo "How are you!" > hello1 
$ git add hello1 
$ git commit -m "add hello1."
[mybranch1 f65bdb4] add hello1.
 1 file changed, 1 insertion(+)
 create mode 100644 hello1

通过推送命令,将本地分支mybranch1推送到GitHub远程版本库,完成在GitHub上的新分支创建。

$ git push -u origin mybranch1

在GitHub上查看版本库,会看到新增了一个分支mybranch1,不过默认分支仍为master。

1.4.3. 设置默认分支

在"Settings"选项卡可以设置Default branch。
修改了GitHub默认分支后,如果再从GitHub克隆版本库,本地克隆后版本库的默认分支也将改变。

$ git clone git@github.com:gitexample/helloworld.git helloworld-branch1
Cloning into 'helloworld-branch1'...
remote: Counting objects: 6, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 6 (delta 0), reused 6 (delta 0), pack-reused 0
Receiving objects: 100% (6/6), done.
Checking connectivity... done.
$ cd helloworld-branch1/
$ git branch

实际上修改GitHub上版本库的默认分支,就是将GitHub版本库的头指针HEAD指向了其他分支,如mybranch1分支。这可以从下面命令看出。

$ cd helloworld-branch1/
$ git branch -r
  origin/HEAD -> origin/mybranch1
  origin/master
  origin/mybranch1
$ cd helloworld-master/
$ git branch -r
  origin/HEAD -> origin/master
  origin/master
  origin/mybranch1

1.4.4. 删除分支

不能删除当前工作分支。因此先切换到其他分支,例如从GitHub版本库中取出master分支并切换。

$ cd helloworld-branch1/
$ git branch
  master
* mybranch1
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git branch
* master
  mybranch1

现在可以删除mybanch1分支。下面的命令之所以使用-D参数,而非-d参数,是因为Git在删除分支时为避免数据丢失,默认禁止删除尚未合并的分支。参数-D则可强制删除尚未合并的分支。

$ git branch -D mybranch1
Deleted branch mybranch1 (was f46a284).

现在只是本地分支被删除了,远程GitHub服务器上的mybranch1分支尚在。删除远程GitHub版本库中的分支就不能使用git branch命令,而是要使用git push命令,而且要先在GitHub上把默认分支切换到mater。在使用推送分支命令时要使用一个特殊的引用表达式(冒号前为空)。

$ git push origin :mybranch1

1.4.5. 里程碑管理

里程碑即tag,其管理和分支管理非常类似。里程碑和分支一样也是以引用的形式存在的,保存在.git/refs/tags/路径下。引用可能指向一个提交,但也可能是其他类型(Tag对象)。

  1. 轻量级里程碑:用git tag <tagname> [<commit>] 命令创建,引用直接指向一个提交对象<commit>。
  2. 带说明的里程碑:用git tag -a <tagname> [<commit>] 命令创建,并且在创建时需要提供创建里程碑的说明。Git会创建一个tag对象保存里程碑说明、里程碑的指向、创建里程碑的用户等信息,里程碑引用指向该Tag对象。
  3. 带签名的里程碑:用git tag -s <tagname> [<commit>] 命令创建。是在带说明的里程碑的基础上引入了PGP签名,保证了所创建的里程碑的完整性和不可拒绝性。

下面演示一下里程碑的创建和管理。

  1. 先在本地创建一个新提交。
$ touch hello1
$ git add hello1
$ git commit -m "add hello1 for mark."
  1. 本地创建里程碑mytag1、mytag2和mytag3。
$ git tag -m "Tag on initial commit" mytag1 HEAD^
$ git tag -m "Tag on new commit"     mytag2
$ git tag mytag3
  1. 查看新建立的里程碑。
$ git tag -l -n1
mytag1          Tag on initial commit
mytag2          Tag on new commit
mytag3          add hello1 for mark.
  1. 将本地里程碑推送到GitHub远程版本库。
$ git push origin refs/tags/*
Counting objects: 6, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 548 bytes, done.
Total 5 (delta 0), reused 0 (delta 0)
To git@github.com:gotgithub/helloworld.git
 * [new tag]         mytag1 -> mytag1
 * [new tag]         mytag2 -> mytag2
 * [new tag]         mytag3 -> mytag3
  1. 删除本地里程碑。
$ git tag -d mytag3
Deleted tag 'mytag3' (was c71231c)
  1. 删除GitHub远程版本库中的里程碑。
$ git push origin :mytag3
To git@github.com:gotgithub/helloworld.git
  [deleted]         mytag3

另外,版本库还可以与一些扩展应用建立联系,比如跟Email 和Redmine整合,Email可以实现新提交推送至版本库时,发送通知邮件。Redmine可以和多种版本库(包括Git)整合,可以直接通过Web界面浏览Git提交,还实现了提交和问题的关联。

1.5. 建立主页

1.5.1. 创建个人主页

GitHub 为每一个用户分配了一个二级域名<user-id>.github.io,用户为自己的二级域名创建主页很容易,只要在托管空间下创建一个名为<user-id>.github.io的版本库,向其master分支提交网站静态页面即可,其中网站首页为index.html。下面以gitexample用户为例介绍如何创建个人主页。

  1. 用户gitexample创建一个名为gitexample.github.io的Git版本库。
  2. 在本地克隆新建立的版本库。
$ git clone git@github.com:gitexample/gitexample.github.io.git gitexample.github.io
Cloning into 'gitexample.github.io'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.
Checking connectivity... done.
$ cd gitexample.github.io/
  1. 在版本库根目录中创建文件index.html作为首页。
$ printf "<h1>gitexample's HomePage</h1>It works.\n" > index.html
  1. 提交
$ git add index.html
$ git commit -m "Homepage test version."
  1. 推送到GitHub,完成远程版本库创建。
$ git push origin master
  1. 访问网址: http://gitexample.github.io/

1.5.2. 创建项目主页

如前所述,GitHub会为每个账号分配一个二级域名<user-id>.github.io作为用户的首页地址。实际上还可以为每个项目设置主页,项目主页也通过此二级域名进行访问。

例如gitexample用户创建的helloworld项目如果启用了项目主页,则可通过网址http://gitexample.github.io/helloworld/访问。

为项目启用项目主页很简单,只需要在项目版本库中创建一个名为gh-pages的分支,并向其中添加静态网页即可。也就是说如果项目的Git版本库中包含了名为gh-pages分支的话,则表明该项目提供静态网页构成的主页,可以通过网址http://<user-id>.github.io/<project-name>访问到。

下面以用户gitexample的项目helloworld为例,介绍如何维护项目主页。

如果本地尚未从GitHub克隆helloworld版本库,执行如下命令。

$ git clone git@github.com:gitexample/helloworld.git
$ cd helloworld

当前版本库只有一个名为master的分支,如果直接从master分支创建gh-pages分支操作非常简单,但是作为保存网页的gh-pages分支中的内容和master分支中的可能完全不同。如果不希望gh-pages分支继承master分支的历史和文件,即想要创建一个干净的gh-pages分支,需要一点小技巧。

若使用命令行创建干净的gh-pages分支,可以从下面三个方法任选一种。

第一种方法用到两个Git底层命令:git write-tree和git commit-tree。步骤如下:

  1. 基于master分支建立分支gh-pages。
$ git checkout -b gh-pages
  1. 删除暂存区文件,即相当于清空暂存区。
$ rm .git/index
  1. 创建项目首页index.html。
$ printf "hello world.\n" > index.html
  1. 添加文件index.html到暂存区。
$ git add index.html
  1. 用Git底层命令创建新的根提交,并将分支gh-pages重置。
$ git reset --hard $(echo "branch gh-pages init." | git commit-tree $(git write-tree))
  1. 执行推送命令,在GitHub远程版本库创建分支gh-pages。
$ git push -u origin gh-pages

稍后,用浏览器访问下面的地址即可看到刚刚提交的项目首页: http://gitexample.github.io/helloworld/
当然,还有其他创建方法,详见:GotGitHub

2. GitBook

本文以Ubuntu 14.04.5 LTS为例安装。

2.1. 安装nodejs

首先需要安装 nodejs,以便能够使用 npm 来安装 gitbook。
从URL: https://nodejs.org/en/下载Linux版本的nodejs,我下载的是node-v8.11.4-linux-x64.tar.xz。

$ xz -d node-v8.11.4-linux-x64.tar.zx
$ tar xvf node-v8.11.4-linux-x64.tar
$ sudo cp node-v8.11.4-linux-x64 /opt/
$ sudo chmod 640 /etc/sudoers
$ sudo vi /etc/sudoers

Defaults    secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/node-v8.11.4-linux-x64/bin/"

$ sudo chmod 440 /etc/sudoers

重启系统。

2.2. 安装gitbook

$ sudo npm install -g gitbook-cli

2.3. 基本使用

gitbook 的基本用法非常简单,基本上就只有两步:

使用 gitbook init 初始化书籍目录
使用 gitbook serve 编译书籍
下面将结合一个非常简单的实例,来介绍 gitbook 的基本用法。

2.3.1. gitbook init

首先,创建如下目录结构:

$ tree book/
book/
├── README.md
└── SUMMARY.md

0 directories, 2 files

README.md 和 SUMMARY.md 是两个必须文件,README.md 是对书籍的简单介绍:

$ cat book/README.md 

This is a book powered by [GitBook](https://github.com/GitbookIO/gitbook).

SUMMARY.md 是书籍的目录结构。内容如下:

$ cat book/SUMMARY.md 

* [Chapter1](chapter1/README.md)
  * [Section1.1](chapter1/section1.1.md)
  * [Section1.2](chapter1/section1.2.md)
* [Chapter2](chapter2/README.md)

创建了这两个文件后,使用 gitbook init,它会为我们创建 SUMMARY.md 中的目录结构。

$ cd book
$ gitbook init
$ tree
.
├── chapter1
│   ├── README.md
│   ├── section1.1.md
│   └── section1.2.md
├── chapter2
│   └── README.md
├── README.md
└── SUMMARY.md

2 directories, 6 files

注意:在我的实验中,gitbook init 只支持两级目录!

2.3.2. gitbook serve

书籍目录结构创建完成以后,就可以使用 gitbook serve 来编译和预览书籍了:

$ gitbook serve
Live reload server started on port: 35729
Press CTRL+C to quit ...

info: 7 plugins are installed 
info: loading plugin "livereload"... OK 
info: loading plugin "highlight"... OK 
info: loading plugin "search"... OK 
info: loading plugin "lunr"... OK 
info: loading plugin "sharing"... OK 
info: loading plugin "fontsettings"... OK 
info: loading plugin "theme-default"... OK 
info: found 5 pages 
info: found 0 asset files 
info: >> generation finished with success in 1.1s ! 

Starting server ...
Serving book on http://localhost:4000

gitbook serve 命令实际上会首先调用 gitbook build 编译书籍,完成以后会打开一个 web 服务器,监听在本地的 4000 端口。
现在,可以用浏览器打开 http://127.0.0.1:4000 查看书籍的效果。
现在,gitbook 为我们创建了书籍目录结构后,就可以向其中添加真正的内容了,文件的编写使用 markdown 语法,在文件修改过程中,每一次保存文件,gitbook serve 都会自动重新编译,所以可以持续通过浏览器来查看最新的书籍效果!
另外,用户还可以下载 gitbook 编辑器,做到所见即所得的编辑

3. GitHub + GitBook管理图书

3.1. 书籍内容存储

使用github提供的git仓库管理书籍内容,增加、修改书籍内容,提交至git仓库即可。

  1. github上创建仓库arm-cm4-guide
  2. checkout arm-cm4-guide到本地
$ git clone git@github.com:gitexample/arm-cm4-guide.git arm-cm4-guide
  1. 编写书籍内容
$ cd arm-cm4-guide/
$ cat README.md 

ARM Cortex-M3与Cortex-M4权威指南(第三版)

$ cat SUMMARY.md 

* [ARM Cortex-M 处理器简介](chapter1/README.md)
    * [什么是ARM Cortex-M处理器](chapter1/section1.md)
        * [Cortex-M处理器家族](chapter1/section1.1.md)
        * [处理器和微控制器的区别](chapter1/section1.2.md)
    * [ARM Cortex-M处理器的优势](chapter1/section2.md)
        * [低功耗](chapter2/section2.1.md)
        * [代码密度](chapter2/section2.2.md)
$ tree
.
├── README.md
└── SUMMARY.md

0 directories, 2 files

$ gitbook init
info: create chapter1/README.md 
info: create chapter1/section1.md 
info: create chapter1/section1.1.md 
info: create chapter1/section1.2.md 
info: create chapter1/section2.md 
info: create chapter2/section2.1.md 
info: create chapter2/section2.2.md 
info: create SUMMARY.md 
info: initialization is finished

$ tree
.
├── chapter1
│   ├── README.md
│   ├── section1.1.md
│   ├── section1.2.md
│   ├── section1.md
│   └── section2.md
├── chapter2
│   ├── section2.1.md
│   └── section2.2.md
├── README.md
└── SUMMARY.md

2 directories, 9 files
  1. 提交到arm-cm4-guide仓库
$ git add README.md SUMMARY.md chapter1/ chapter2/
$ git commit -m "add book content."
$ git push origin master

3.2. 构建书籍

首先,使用 gitbook build 将书籍内容输出到默认目录,也就是当前目录下的 _book 目录。

$ gitbook build
info: 7 plugins are installed 
info: 6 explicitly listed 
info: loading plugin "highlight"... OK 
info: loading plugin "search"... OK 
info: loading plugin "lunr"... OK 
info: loading plugin "sharing"... OK 
info: loading plugin "fontsettings"... OK 
info: loading plugin "theme-default"... OK 
info: found 8 pages 
info: found 0 asset files 
info: >> generation finished with success in 1.5s ! 

$ ls _book/
chapter1  chapter2  gitbook  index.html  search_index.json

3.3. 创建 gh-pages 分支

将gitbook生成的html page发布github pages上(实际是提交内容到gh-pages 分支)。所以先创建arm-cm4-guide的 gh-pages 分支。

$ git checkout -b gh-pages
$ rm .git/index
$ git reset --hard $(echo "branch gh-pages init." | git commit-tree $(git write-tree))
$ git push -u origin gh-pages

现在,目录下应该只剩下 _book 目录了,首先,忽略一些文件:

$ echo "*~" > .gitignore
$ echo "_book" >> .gitignore
$ git add .gitignore
$ git commit -m "Ignore some files"

然后,加入 _book 下的内容到分支中:

$ cp -r _book/* .
$ git add .
$ git commit -m "Publish book"

3.4. 上传书籍内容到 GitHub

现在,可以将编译好的书籍内容上传到 GitHub 中 arm-cm4-guide项目的 gh-pages 分支了.

$ git push -u origin gh-pages

访问 https://gitexample.github.io/arm-cm4-guide/ 就可以阅读 arm-cm4-guide这本书了!

4. 利用 GitHub Pages 快速搭建个人博客

fork别人的模板(qiubaiying.github.io),rename为自己的仓库名(<user-id>.github.io)。
把里面的页面内容修改为自己的即可。