Git 第九章:README、Gist 和 Pages,背后都是一个 Git 仓库
前两章讲的都是本地仓库和 GitHub 上的仓库之间怎样传递提交push 把提交送上去fetch 和 pull 再把提交拿下来。这一章回到 GitHub 网站本身看看它在保存仓库之外还提供了哪些功能。这些功能可以分成两组。第一组管的是人也就是谁能看到这个仓库以及谁能往里推送。第二组管的是怎样把内容展示给别人包括仓库首页的 README、用来分享代码片段的 Gist以及把仓库发布成网站的 GitHub Pages。这些功能在网页上点几下就能用但是它们背后都是一个普通的 Git 仓库。README 是仓库里的一个文件Gist 本身就是一个仓库而 Pages 发布的是某条分支上的文件。如果记住了这一点很多看起来零散的规则就都说得通了。比如为什么把仓库改回私有也收不回已经公开过的内容又比如为什么在网页上改了 README 之后本地还要再 pull 一次。一、可见性公开的是整个仓库在 GitHub 上新建仓库时要在 Public 和 Private 之间选一个这就是仓库的可见性visibility。公开仓库对所有人可见不登录也能浏览和 clone第七章克隆的 octocat/Hello-World 就是一个公开仓库。私有仓库则只有拥有者和被邀请的合作者才能看到。仓库建好之后可见性也可以修改。打开仓库的 Settings在 General 页最下面的 Danger Zone 里找到 Change repository visibility点击 Change visibility再选择新的可见性。这时 GitHub 会先列出这次修改带来的后果确认之后才会生效。如果从公开改成私有那么仓库的 star 和 watcher 会被清空。别人已经 fork 出去的副本仍然是公开的只是和这个仓库断开了联系。如果用的是免费账号已经发布的 GitHub Pages 网站也会被撤下。如果从私有改成公开那么所有人都能看到代码任何人都可以 forkActions 的运行记录和日志也会一起公开。把私有仓库改成公开时有一点特别需要注意。公开的不只是最新的那些文件而是整个仓库其中也包括全部的历史。下面这个仓库在第一个提交里放进了一个写着令牌的 deploy.env第二个提交又把它删掉了gitcommit-m第一篇笔记# [main (root-commit) 7d73345] 第一篇笔记# 2 files changed, 2 insertions()# create mode 100644 README.md# create mode 100644 deploy.envgitrmdeploy.env# rm deploy.envgitcommit-m删掉 deploy.env# [main 9c0f49f] 删掉 deploy.env# 1 file changed, 1 deletion(-)# delete mode 100644 deploy.envls# README.mdgitshow HEAD~1:deploy.env# TOKEN这里是一串令牌代码 1git rm删除文件并且把这次删除放进暂存区。HEAD~1:deploy.env指的是上一个提交里的 deploy.env。工作区里已经没有 deploy.env 了但是第一个提交 7d73345 仍然保存着它。第四章讲过Git 保存的是一份一份完整的快照后面的提交删掉一个文件并不会改动前面的提交。所以仓库一旦公开任何人 clone 下来之后都能用同样的命令看到这个令牌。公开之前最好先用git log --all --stat把历史里出现过的文件都看一遍。如果令牌已经进过历史那么最稳妥的做法是把它当作已经泄露到发放它的地方把它作废再重新生成一个。git log --all --stat 只看文件名不显示内容确认令牌得靠 git log -p 或 gi反过来从公开改回私有也收不回已经公开过的内容。别人 fork 出去的副本仍然是公开的而 clone 到别人电脑上的仓库GitHub 更是管不到。二、合作者把推送的权限交给别人一个属于个人账号的仓库默认只有拥有者能往里推送。别人即使能看到这个仓库推送也会被拒绝。如果想和同学一起维护一个仓库就要把对方加为合作者collaborator。做法是打开仓库的 Settings点击左侧栏 Access 一组里的 Collaborators早些时候的界面上这一项叫 Manage access。接着点击 Add people输入对方的用户名或者邮箱从搜索结果里选中这个人并确认添加。对方会收到一封邀请邮件接受邀请之后才真正成为合作者。个人仓库里只有拥有者和合作者这两种身份。合作者可以 pull也可以 push但是和仓库设置有关的操作都只属于拥有者操作拥有者合作者pull 和 push能能邀请合作者能不能修改可见性能不能删除仓库能不能表中只列了几项完整的清单在 GitHub 文档的 Permission levels for a personal account repository 一页。也就是说合作者拿到的就是读和写这两种权限。私有仓库也没有办法只给某个人只读的权限文档里写的是Collaborators cant have read-only access to repositories owned by a personal account。如果需要更细的分工比如有的人只能看、有的人能推送就要把仓库建在组织organization下面。另外免费账号给公开仓库和私有仓库添加合作者都没有人数上的限制。把一个人从合作者里移除之后对方就不能再访问这个仓库了。但是文档里也写明了对方已经 clone 到本地的仓库仍然留在对方手里。这和上一节是同一个道理因为 Git 仓库复制出去的每一份都是完整的。有了合作者之后上一章模拟的就是每天都会遇到的情况。几个人往同一个仓库推送所以每个人在推送之前都要先用 fetch 或者 pull 把别人的提交拿下来。三、README仓库首页显示的那个文件打开 GitHub 上的一个仓库文件列表下面通常会显示一段说明。这段说明来自仓库里一个叫README的文件一般写成 README.md。GitHub 会把它渲染成网页显示在首页上所以它往往是访客看到的第一样东西。GitHub 的文档建议 README 讲清楚这几件事这个项目是做什么的它为什么有用怎样开始使用遇到问题去哪里求助谁在维护这个项目README 不一定要放在根目录。GitHub 会在三个地方找它分别是隐藏的.github目录、根目录和docs目录。如果几个地方都有 README那么按照.github、根目录、docs的顺序先找到哪个就显示哪个。优先级顺序是 .github 根目录 docs容易记反原文这句建议加粗。新建仓库的页面上有一个添加 README 的选项现在的界面上是一个叫 Add README 的开关。打开它之后GitHub 会在新仓库里生成一个 README.md作为仓库的第一个提交。第七章讲过如果本地已经有了提交就不要打开这个选项否则第一次推送会被拒绝。README 只是仓库里一个普通的文件所以修改它的办法和修改其他文件一样在本地修改、提交再 push 上去首页就会跟着更新。也可以直接在 GitHub 的网页上编辑保存的时候 GitHub 会让人填写提交信息然后在远程仓库里生成一个提交。这个提交本地还没有所以下一次在本地推送之前要先像上一章那样把它 pull 下来。四、Markdown 的基本写法README.md 的扩展名 .md 表示它是用Markdown写的。Markdown 是一种用普通符号来标记格式的纯文本写法比如在一行的开头加上#表示标题在文字的前后各加两个星号表示粗体。GitHub 显示 .md 文件时会先把它转换成 HTML再套上自己的样式显示出来。因为标记用的都是普通符号所以即使不经过转换源文件本身也很容易读懂。图 1 的左边是一份 README.md 的源文件右边是它在 GitHub 上显示出来的样子图 1右边按照 GitHub 的样式画出一级和二级标题下面各有一条细线第二层列表项前面是空心的圆点。图里用到的写法有下面这些# 标题是一级标题##是二级标题最多可以到六级#的后面要有一个空格。**粗体**是粗体*斜体*是斜体。~~删除线~~是删除线这是 GitHub 在标准 Markdown 之外加上的写法。 引用是引用块左边有一条竖线文字显示成灰色。- 项目是无序列表-换成*或也可以缩进两个空格就是下一层1. 项目是有序列表。用一对反引号把代码围起来是行内代码显示成等宽字体背景是浅灰色。[文字](地址)是链接是图片。图片方括号里的说明不会显示在图片旁边但是图片加载不出来时会显示它。代码块用三个反引号开头再用三个反引号结尾。开头那一行的反引号后面可以写上语言的名字bash git log --oneline 代码 2GitHub 根据 bash 这个名字决定怎样高亮。写成 py、js、c 这样的常见扩展名也能识别如果写了一个不认识的名字代码块照样显示只是没有高亮。表格也是 GitHub 加上的写法表头下面必须有一行分隔行| 命令 | 作用 | | :--- | :---: | | git fetch | 只更新 origin/* |代码 3如果省掉第二行这几行就只是普通的文字。冒号写在左边表示左对齐两边都写表示居中只写在右边表示右对齐。几处容易写错的地方第一处是标题后面的空格。#和文字之间如果没有空格比如写成#没有空格那么 GitHub 不会把它当成标题而是把这几个字符原样显示出来。第二处是图片前面的感叹号。感叹号必须紧挨着方括号如果写成! [logo](logo.png)中间多了一个空格那么 GitHub 会把它理解成一个普通的感叹号加上一个链接p!ahreflogo.pnglogo/a/p代码 4GitHub 转换出来的 HTML。页面上显示的是一个感叹号和一个叫 logo 的链接图片并没有出现。第三处是换行。在 .md 文件里只按一次回车并不会换行第一行 第二行 第三行 第四行代码 5「第三行」的后面还有两个空格在这里看不出来。GitHub 把它转换成下面的 HTMLp第一行 第二行/pp第三行br第四行/p代码 6p是一个段落br是一次换行。第一行和第二行之间只隔着一个换行符所以它们被放进了同一个段落。对浏览器来说段落里的换行符和空格一样所以这两行会接在一起显示在 Chrome 里看到的是「第一行 第二行」。空一行才会开始一个新的段落如果只想换行、不想分段就要在行尾加上两个空格或者加一个反斜杠这两种写法都会变成br。需要注意的是这条规则只对 .md 文件成立。在 issue 和 pull request 的评论框里写 Markdown 时GitHub 会把每一次回车都显示成换行。行尾两空格在多数编辑器里会被自动删掉靠它换行不稳反斜杠更可靠。第四处和中文的标点有关。粗体的星号如果紧挨着中文标点有时会失效。比如这是**加粗。**后面在 GitHub 上显示出来的仍然是原样的星号。这是因为结尾的**前面是标点、后面紧跟着汉字按照 Markdown 的规则它不能当作粗体的结束标记。如果把句号挪到星号外面写成这是**加粗**。后面那么就能正常显示成粗体了。五、Gist只装着几个文件的仓库Gist是 GitHub 用来分享代码片段的地方地址是 gist.github.com。新建一个 Gist 时要填三样东西分别是一段描述、一个带扩展名的文件名和文件的内容。GitHub 根据扩展名来决定怎样高亮比如文件名写成 hello.py内容就会按照 Python 来高亮。Gist 看起来只是一个贴代码的网页但是文档里写着Every gist is a Git repository, which means that it can be forked and cloned也就是说每一个 Gist 都是一个 Git 仓库。下面克隆 octocat 的一个公开 Gistgitclone https://gist.github.com/6cad326836d38bd3a7ae.git hello# Cloning into hello...# remote: Enumerating objects: 3, done.# remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 3 (from 1)# Receiving objects: 100% (3/3), done.cdhellols# hello_world.rbgitlog# commit de5b9b59d1f28206e8d646c7c8025e9809d0ed73 (HEAD - master, origin/master, origin/HEAD)# Author: The Octocat octocatnowhere.com# Date: Wed Oct 1 09:19:34 2014 -0700代码 7克隆地址是 gist.github.com/ 加上这个 Gist 的编号再加上 .git。3 个对象是一个提交、一个目录树和 hello_world.rb 的内容。和克隆普通的仓库一样本地有了 master 分支和 origin/master 这样的远程跟踪分支也有了完整的历史。这个 Gist 只有一个提交而且提交信息是空的因为在网页上创建和编辑 Gist 时并没有填写提交信息的地方。之后每在网页上编辑一次历史里就会多一个提交Gist 页面上的 Revisions 标签列出的就是这些提交以及每一次的改动。反过来在本地修改并提交之后也可以把提交 push 回自己的 Gist。创建 Gist 时默认的按钮是 Create secret gist点击旁边的下拉箭头才能换成 Create public gist。公开的 Gist 会出现在 Gist 的发现页里也能被搜索到而 secret Gist 不会出现在这些地方。但是 secret 并不等于私有文档里写的是Secret gists arent private任何人只要拿到它的网址就能看到里面的内容。另外公开的 Gist 不能再改成 secret而 secret Gist 可以改成公开。所以如果一段代码真的不能给别人看就应该放进私有仓库而不是放进 secret Gist。六、GitHub Pages把仓库发布成网站GitHub Pages是 GitHub 提供的静态网站托管服务。它从仓库里取出 HTML、CSS 和 JavaScript 文件需要的话先构建一遍然后发布成一个网站。所谓静态是指服务器只负责把这些文件原样发给浏览器并不会在服务器上运行程序。所以需要后端程序和数据库的功能Pages 本身是做不了的。配置的地方在仓库 Settings 的 Pages 页。在 Build and deployment 下面Source 选择 Deploy from a branch然后选择一条分支和一个目录。目录只有两种选择一种是根目录/另一种是/docs。保存之后这条分支上的这个目录就会被发布出去以后每次往这条分支推送网站都会重新发布一次。图 2发布源选的是 main 分支的 /docs所以根目录下的 README.md 和 ssh.md 不会出现在网站上。网站需要一个入口文件也就是打开网址时首先显示的那个页面。Pages 会在发布的目录里找 index.html、index.md 或者 README.md 当作入口文件。后两个是 Markdown 文件它们之所以也能用是因为从分支发布时Pages 默认会先用 Jekyll 构建一遍。Jekyll 是一个静态网站生成器它会把 Markdown 转换成 HTML。所以即使仓库里一个 .html 文件都没有只有一个 README.md也能发布成一个网站。如果不想让 Jekyll 处理这些文件可以在发布的目录里放一个名叫.nojekyll的空文件。Source 的另一个选项是 GitHub Actions也就是用自己写的工作流来构建和发布这样就可以换成 Jekyll 以外的生成器。这个博客就是先用 Hexo 生成静态文件再由 Actions 发布到 Pages 上的。网站的地址分两种。普通仓库发布的是项目网站每个仓库可以有一个如果仓库的名字正好是用户名.github.io那么它发布的就是用户网站每个账号只能有一个项目网站 https://用户名.github.io/仓库名/ 用户网站 https://用户名.github.io/代码 8两种网站都可以换成自己的域名。最后Pages 和第一节的可见性也有关系。免费账号只能给公开仓库开启 Pages所以把仓库改成私有之后已经发布的网站会被撤下。付费账号可以从私有仓库发布网站但是发布出来的网站仍然是公开的。文档里专门提醒了这一点如果仓库里有敏感的内容要先删掉再发布。小结写法或位置作用和要点Settings 的 Danger Zone修改可见性公开的是包括全部历史的整个仓库git show HEAD~1:文件查看上一个提交里的文件删掉的文件在旧提交里仍然存在Settings 的 Collaborators邀请合作者对方接受之后可以 pull 和 push但是不能改设置README.md显示在仓库首页依次在.github、根目录、docs里找# 标题#后面要有空格否则就不是标题图片感叹号要紧挨着方括号行尾两个空格或反斜杠.md 文件里的换行只按一次回车不会换行空一行是分段gist.github.com分享代码片段每个 Gist 都是一个可以 clone 的仓库secret 不等于私有Settings 的 Pages选一条分支和一个目录发布静态网站入口是 index.html、index.md 或 README.md这一章的几样功能都可以落回到 Git 仓库上来理解。可见性决定的是谁能读这个仓库合作者拿到的是往仓库里写的权限。README 是仓库里的一个文件Gist 是一个只装着几个文件的仓库Pages 发布的则是某条分支上的一个目录。因为仓库里保存着完整的历史而且每一份 clone 也都是完整的所以改可见性、移除合作者都只能管住以后已经复制出去的那些副本是收不回来的。