## 第 28 章 Git 版本控制系统
**目录**
[](ch28.html#id3132645)
[版本控制之道](ch28s02.html)
[时间机器](ch28s02.html#id3132023)
[分支控制](ch28s02.html#id3132682)
[协同工作](ch28s02.html#id3132696)
[冲突合并](ch28s02.html#id3132707)
[常见版本控制系统](ch28s02.html#id3132719)
[git 如何工作](ch28s03.html)
[补丁](ch28s03.html#id3132799)
[git 对象](ch28s03.html#id3132818)
[操作级别](ch28s03.html#id3132919)
[初始化](ch28s04.html)
[创建版本库](ch28s04.html#id3133060)
[版本库状态](ch28s04.html#id3133181)
[配置](ch28s04.html#id3133399)
[版本更新](ch28s05.html)
[版本标签](ch28s05.html#id3133858)
[时间机器](ch28s06.html)
[分支管理](ch28s07.html)
[创建分支](ch28s07.html#id3134364)
[合并分支](ch28s07.html#id3134545)
[处理冲突](ch28s07.html#id3134650)
[通过文件协作](ch28s08.html)
[通过网络协作](ch28s09.html)
[gitweb](ch28s10.html)
## 版本控制之道
几乎所有项目[[61](ch28s02.html#ftn.id3132658)],都要使用版本控制,它究竟有什么优势呢?
### 时间机器
假设你使用的编辑器,不支持删除,那你就得特别的谨小慎微,甚至是如履薄冰:因为你打错了字没法删除
放松下来,目前我所接触的所有编译器中,还没有变态到这种程度的。
如果编译器提供了删除功能,却没有 undo,那可能会更可怕:如果你不小心选中了全部文字,手一抖……因为不能 undo,你知道,如果此时不小心按下 delete,你就得从头来过……你会为可能产生的后果而发抖
然而,命运总在你不想被打扰的时候来敲你可爱的门,在你手抖的刹那,你真的鬼使神差的按了下那个可怕的键……你的选择只能是重新来过或者放弃……
你会比任何时候都希望时间倒流一秒钟
还好,几乎所有的编译器,都会帮你回到一秒种、一分钟、一小时……之前
假设你想回到一天之前?一觉醒来,你突然想起,有一部分内容其实应该保留下来……但是编辑器在重启之后,就不能够再帮你回到以前的任何时间……
这种情况下,版本控制才是你的救命稻草
### 分支控制
如果项目只能朝一个方向发展,你会时常在确定方向的问题上犹豫不决。而使用版本控制,创建一个分支各自发展,在适当的时候合并分支,是最好的解决办法
### 协同工作
很多项目需要协同工作,版本控制能够提供协同工作需要的环境,解决协同工作可能产生的问题
### 冲突合并
项目成员可能会对一处内容进行不同的修改,版本控制能够反馈这些冲突,以便解决它
### 常见版本控制系统
传统的版本控制系统,需要在服务器上搭建一个中央仓库,所有成员都是客户端,具有不同的权限。这种方式适应大教堂开发模式,但比较依赖网络,且不够灵活,使用比较广泛的有cvs、svn等
而分布式版本控制系统,则不需要中央仓库,所有的成员都可以完全掌控己方的版本控制系统,通过多种方式灵活的协作。这种方式适应集市开发模式,典型的代表是git
* * *
> [[61](ch28s02.html#id3132658)] 项目并不总是意味着开发一个大型软件;你帮领导打一份文件,其实也是项目
## git 如何工作
尽管 `Linus Torvalds` 将 git 定位为:“傻瓜式的内容跟踪工具”,但它对不熟悉版本控制的朋友来说,还是过于复杂
所以我们需要先在概念上大概了解,git 是如何工作的
### 补丁
多数版本控制系统,使用补丁来纪录内容的改动。
当你修改了文件内容,版本控制系统会比较修改后的内容和原来的内容,并使用补丁纪录下来。 无论是查看版本之间的变化,或者需要回溯原来内容,都需要使用补丁中的内容
### git 对象
工作树
git将工作目录称为:工作树
索引
工作树的快照,无论是添加、删除文件,或者对文件内容进行修改,都需要提交到索引。git只跟踪被索引的内容
将改动提交到索引,意味着建立一个快照
版本库
存储工作树的各种版本
工作树中只保存当前内容,各种版本通过补丁的形式存储在版本库中
版本名称
git可以使用“版本ID”和“版本标签”作为版本名称
版本ID自动生成,版本标签用**git tag**命令指定
### 操作级别
git可以在四种级别上实现版本控制:
改动纪录
改动了文件内容,提交到索引,但未提交到版本库
该级别的常见操作有:**add diff**
版本纪录
改动被提交到版本库后,就成为一个新的版本
该级别的常见操作有:**commit log tag show reset**
其中**reset**、以及分支操作,需要在**commit**之后,**add**之前,没有待提交改动纪录的情况下进行
分支
分支为该主线上的系列版本
版本库
协同工作时,需要合并项目成员的版本库
该级别常见的操作有:**clone pull push**
## 初始化
### 创建版本库
git 基于文件夹(工作树)进行版本控制,在一个文件夹中创建 git版本库:
```
$ cd project/
$ git init
Initialized empty Git repository in .git/
```
> [![1](https://box.kancloud.cn/2015-10-12_561bcb76795ae.png)](ch28s04.html#git-init-1) 输出信息:在当前文件夹的 `.git/` 目录下创建版本库
将文件提交到 git索引:
```
git add file1 file2 file3 ……
```
更方便的作法是将当前文件夹中的所有文件全部加入到索引中
```
git add .
```
* 可以在 `.gitignore` 文件中设置排除的文件(通常把临时文件排除)
> 注意:git 只负责管理被索引的文件
此时,文件还没有被提交到版本库。向版本库提交第一个版本:
```
git commit
git commit -m "备注"
```
> [![1](https://box.kancloud.cn/2015-10-12_561bcb76795ae.png)](ch28s04.html#git-init-2) 调用系统默认编辑器编辑备注内容
### 版本库状态
使用 **git status** 命令查看版本库状态。先创建一个演示版本库:
```
mkdir sandbox #新建一个文件夹
cd sandbox/ #进入该文件夹
git init #初始化版本库
touch a b #新建 a b 两个文件
git add . #将这两个文件提交到索引
git commit -m "创建git版本库" #将第一个版本提交到版本库
```
这时使用 `git status` 查看版本库状态:
```
# On branch master
nothing to commit (working directory clean)
```
对文件进行一些操作:
```
vi a #编辑 a
rm b #删除 b
touch c #新建 c
```
再用 `git status` 查看:
```
# On branch master #在 master 分支上
# Changes to be committed: #已提交到索引,等待提交到版本库(其实本例中没有这一段)
# (use "git reset HEAD <file>..." to unstage)
#
# new file: e
# modified: f
#
# Changed but not updated: #改动未提交到索引
# (use "git add/rm <file>..." to update what will be committed)
#
# **modified: a**
# **deleted: b**
#
# Untracked files: #文件未提交到索引
# (use "git add <file>..." to include in what will be committed)
#
# **c**
no changes added to commit (use "git add" and/or "git commit -a")
```
> 注意:如果只是想删除该文件夹中的版本库,只要删除 `.git/` 目录即可
```
rm -rf .git
```
### 配置
git 初始化后,会在`.git/`目录下创建一个版本库,其中`.git/config`为配置文件。
#### 用户信息
为当前版本库添加用户信息[[62](ch28s04.html#ftn.id3133428)]:
```
[user]
name = kardinal
email = 2999am@gmail.com
```
也使用全局用户信息,在`~/.gitconfig`中写入上述内容,或者使用命令:
```
git config --global user.name "kardinal"
git config --global user.email 2999am@gmail.com
```
#### 语法高亮
在`~/.gitconfig`文件中添加如下语句,使用容易阅读的彩色来输出信息:
```
[color]
branch = auto
diff = auto
status = auto
```
或者自己定义:
```
branch.current # color of the current branch
branch.local # color of a local branch
branch.plain # color of other branches
branch.remote # color of a remote branch
diff # when to color diff output
diff.commit # color of commit headers
diff.frag # color of hunk headers
diff.meta # color of metainformation
diff.new # color of added lines
diff.old # color of removed lines
diff.plain # color of context text
diff.whitespace # color of dubious whitespace
status # when to color output of git-status
status.added # color of added, but not yet committed, files
status.changed # color of changed, but not yet added in the index, files
status.header # color of header text
status.untracked # color of files not currently being tracked
status.updated # color of updated, but not yet committed, files
```
* * *
> [[62](ch28s04.html#id3133428)] 这是必需的,请不要忽略
## 版本更新
现在创建一个 git版本库:(参见[“初始化”一节](ch28s04.html "初始化"))
```
mkdir sandbox
cd sandbox/
git init
touch test
git add .
git commit -m "创建git版本库"
```
**git log**查看版本纪录:
```
commit d63e709f565dcd60ab749f0eca27a947b02b8c26
Author: kardinal <2999am@gmail.com>
Date: Wed Nov 5 14:08:50 2008 +0800
创建 git版本库
```
> [![1](https://box.kancloud.cn/2015-10-12_561bcb76795ae.png)](ch28s05.html#git-use-1) 版本ID(默认自动生成)
> [![2](https://box.kancloud.cn/2015-10-12_561bcb768596e.png)](ch28s05.html#git-use-2) 提交者
> [![3](https://box.kancloud.cn/2015-10-12_561bcb94ee26f.png)](ch28s05.html#git-use-3) 提交日期
> [![4](https://box.kancloud.cn/2015-10-12_561bcb9508b82.png)](ch28s05.html#git-use-4) 备注
现在对`test`文件作一些修改:
```
增加一行内容
```
**git diff**查看自上次提交以来发生什么改动:
```
diff --git a/test b/test
index e69de29..bae0882 100644
--- a/test
+++ b/test
@@ -0,0 +1 @@
+增加一行内容
```
> [![1](https://box.kancloud.cn/2015-10-12_561bcb76795ae.png)](ch28s05.html#git-use-5) 典型的diff输出,如果你设置了彩色输出,这些内容会非常直观的显示
接下来,把这次的更新作为新的版本提交
```
git add test
git commit -m "增加了一行内容"
```
> [![1](https://box.kancloud.cn/2015-10-12_561bcb76795ae.png)](ch28s05.html#git-use-6) 将本次更新提交到索引(生成快照)。此时使用**git diff**查看改动纪录,看不到任何内容;但是仍可以使用**git diff --cached**查看缓存的改动纪录
> [![2](https://box.kancloud.cn/2015-10-12_561bcb768596e.png)](ch28s05.html#git-use-7) 提交为新版本后,便不能使用**git diff**查看改动纪录
> 提示:`git add`提交改动到索引,但并不提交到版本库。如果不想频繁的提交新版本,可以使用该命令提交改动到索引,比较和上一次提交的变化。只要不使用`git commit`提交,版本库中不会有新的版本
使用**git log**查看版本库纪录
```
commit 13aa16309db3693ea8a6b93b8a818e731194824c
Author: kardinal <2999am@gmail.com>
Date: Wed Nov 5 14:28:04 2008 +0800
增加了一行内容
commit d63e709f565dcd60ab749f0eca27a947b02b8c26
Author: kardinal <2999am@gmail.com>
Date: Wed Nov 5 14:08:50 2008 +0800
创建git版本库
```
如果想查看每个版本的改动纪录,使用**git log -p**
```
commit 13aa16309db3693ea8a6b93b8a818e731194824c
Author: kardinal <2999am@gmail.com>
Date: Wed Nov 5 14:28:04 2008 +0800
增加了一行内容
diff --git a/test b/test
index e69de29..bae0882 100644
--- a/test
+++ b/test
@@ -0,0 +1 @@
+增加一行内容
commit d63e709f565dcd60ab749f0eca27a947b02b8c26
Author: kardinal <2999am@gmail.com>
Date: Wed Nov 5 14:08:50 2008 +0800
创建git版本库
diff --git a/test b/test
new file mode 100644
index 0000000..e69de29
```
每次使用**git add**和**git commit**两个命令提交版本更新很繁琐,可以使用**git commit -a**提交(已索引文件的改动)
```
git commit -a -m "一次新的提交"
```
### 版本标签
使用**git tag**为某一版本创建版本标签:
```
git tag 1.0 d63e70
git tag 1.1 13aa16
git tag newest HEAD
```
* 版本标签存储在`.git/refs/tags/`目录
使用容易记忆的版本标签进行操作:
```
git diff 1.0 1.1
git diff 1.0 13aa16
git log 1.0
```
> [![1](https://box.kancloud.cn/2015-10-12_561bcb76795ae.png)](ch28s05.html#git-use-21) 查看1.0和1.1之间的变化
> [![2](https://box.kancloud.cn/2015-10-12_561bcb768596e.png)](ch28s05.html#git-use-22) 查看1.0纪录
## 时间机器
在`test`文件中随意改动,然后提交
```
git commit -a -m "意外改动"
```
`git log`,增加了一条纪录:
```
commit d9b03125921d20482937f43ea0bdbfbfb7fe1745
Author: kardinal <2999am@gmail.com>
Date: Wed Nov 5 15:18:49 2008 +0800
意外改动
```
使用**git reset**命令回溯到历史版本:
```
git reset HEAD^
git log
git diff
```
> [![1](https://box.kancloud.cn/2015-10-12_561bcb76795ae.png)](ch28s06.html#git-reset-0) `git reset`默认使用**--mixed**选项
> [![2](https://box.kancloud.cn/2015-10-12_561bcb768596e.png)](ch28s06.html#git-reset-1) `HEAD`表示当前版本,`HEAD^`表示前一个版本,`HEAD^^`表示前两个版本,`HEAD~4`表示前四个版本;也可以使用“版本标签”或“版本ID”来指定版本(只要前几位就可以了)
> [![3](https://box.kancloud.cn/2015-10-12_561bcb94ee26f.png)](ch28s06.html#git-reset-01) 可以看到版本纪录中最后一次提交已经取消
> [![4](https://box.kancloud.cn/2015-10-12_561bcb9508b82.png)](ch28s06.html#git-reset-02) 可以看到,**--mixed**选项回溯到提交到索引之前的状态
**git reset --soft**回溯到已提交到索引但未提交到版本库的状态
```
git commit -a -m "意外改动"
git reset --soft HEAD^
git log
git diff
git diff --cached
```
> [![1](https://box.kancloud.cn/2015-10-12_561bcb76795ae.png)](ch28s06.html#git-reset-2) 再一次将这些改变提交
> [![2](https://box.kancloud.cn/2015-10-12_561bcb768596e.png)](ch28s06.html#git-reset-3) 使用**--soft**选项回溯到上一版本
> [![3](https://box.kancloud.cn/2015-10-12_561bcb94ee26f.png)](ch28s06.html#git-reset-4) 版本纪录中已取消该版本
> [![4](https://box.kancloud.cn/2015-10-12_561bcb9508b82.png)](ch28s06.html#git-reset-5) 改动纪录中没有任何内容
> [![5](https://box.kancloud.cn/2015-10-12_561bcb95133e7.png)](ch28s06.html#git-reset-6) 改动已被提交到索引,但是未提交到版本库,所以缓存的改动纪录还可以查看
> 注意:`git reset` **回溯到**`git add`之前的状态;`git reset --soft`回溯到`git add`之后的状态
以上方法回溯到历史版本,只是回溯版本库和索引的纪录,而文件的内容并不会回溯到之前的状态,使用**git reset --hard**命令,将文件内容也一同回溯
```
git commit -a -m "意外改动"
git reset --hard HEAD^
git log
git diff --cached
cat test
```
> [![1](https://box.kancloud.cn/2015-10-12_561bcb76795ae.png)](ch28s06.html#git-reset-11) ……还得提交一次,谁让它是“意外改动”
> [![2](https://box.kancloud.cn/2015-10-12_561bcb768596e.png)](ch28s06.html#git-reset-12) 使用**--hard**选项回溯到上一版本
> [![3](https://box.kancloud.cn/2015-10-12_561bcb94ee26f.png)](ch28s06.html#git-reset-13) 版本纪录中已取消该版本
> [![4](https://box.kancloud.cn/2015-10-12_561bcb9508b82.png)](ch28s06.html#git-reset-14) 没有任何改动纪录待提交
> [![5](https://box.kancloud.cn/2015-10-12_561bcb95133e7.png)](ch28s06.html#git-reset-15) 文件内容回溯到上一版本的状态
**--hard**选项存在一定风险,因为很多情况下,你不能确定内容算不算“意外改动”。这时,可以新建一个分支,在这个分支中进行回溯,处理完成后合并两个分支,参见[“分支管理”一节](ch28s07.html "分支管理")
## 分支管理
### 创建分支
**git branch**命令查看分支:
```
git branch
* master
```
> [![1](https://box.kancloud.cn/2015-10-12_561bcb76795ae.png)](ch28s07.html#git-branch-1) 不带选项,默认为查看分支
> [![2](https://box.kancloud.cn/2015-10-12_561bcb768596e.png)](ch28s07.html#git-branch-2) `*`表示当前分支
> [![3](https://box.kancloud.cn/2015-10-12_561bcb94ee26f.png)](ch28s07.html#git-branch-02) `master`为默认分支
新建分支:
```
$ git branch slave
$ git checkout slave
M slave
Switched to branch "slave"
$ git branch
master
* slave
```
> [![1](https://box.kancloud.cn/2015-10-12_561bcb76795ae.png)](ch28s07.html#git-branch-3) `git branch`使用分支名称作参数,新建分支
> [![2](https://box.kancloud.cn/2015-10-12_561bcb768596e.png)](ch28s07.html#git-branch-4) `git checkout`,切换到指定分支
> [![3](https://box.kancloud.cn/2015-10-12_561bcb94ee26f.png)](ch28s07.html#git-branch-5) 查看分支
> [![4](https://box.kancloud.cn/2015-10-12_561bcb9508b82.png)](ch28s07.html#git-branch-6) 当前分支已变为`slave`
使用如下命令删除分支:(先不要删除,后面会用到)
```
git branch -D 分支名称
```
### 合并分支
使用**git merge**合并分支:
```
编辑 test
git commit -a -m "slave分支"
git checkout master
git diff master slave
git merge slave
```
> [![1](https://box.kancloud.cn/2015-10-12_561bcb76795ae.png)](ch28s07.html#git-branch-10) 增加一点内容
> [![2](https://box.kancloud.cn/2015-10-12_561bcb768596e.png)](ch28s07.html#git-branch-11) 在当前分支提交此版本
> [![3](https://box.kancloud.cn/2015-10-12_561bcb94ee26f.png)](ch28s07.html#git-branch-12) 切换到 master分支
> [![4](https://box.kancloud.cn/2015-10-12_561bcb9508b82.png)](ch28s07.html#git-branch-91) 比较两个分支
> [![5](https://box.kancloud.cn/2015-10-12_561bcb95133e7.png)](ch28s07.html#git-branch-13) 合并 slave分支 的内容
### 处理冲突
如果没有冲突的内容,git 会自动处理合并。如果产生冲突(同一行的内容不一致),git 会输出如下信息:
```
Auto-merged test
CONFLICT (content): Merge conflict in test
Automatic merge failed; fix conflicts and then commit the result.
```
* `test`文件在合并时发生冲突,需要手动处理冲突,然后后再次提交
现在处理冲突,打开`test`文件,有如下内容:
```
<<<<<<< HEAD:test
这是master分支中的一行
=======
这是slave分支中的一行
>>>>>>> slave:test
```
> [![1](https://box.kancloud.cn/2015-10-12_561bcb76795ae.png)](ch28s07.html#git-branch-21) 当前内容信息
> [![2](https://box.kancloud.cn/2015-10-12_561bcb768596e.png)](ch28s07.html#git-branch-22) 当前内容
> [![3](https://box.kancloud.cn/2015-10-12_561bcb94ee26f.png)](ch28s07.html#git-branch-23) 分隔线,分隔冲突的内容
> [![4](https://box.kancloud.cn/2015-10-12_561bcb9508b82.png)](ch28s07.html#git-branch-24) slave分支内容
> [![5](https://box.kancloud.cn/2015-10-12_561bcb95133e7.png)](ch28s07.html#git-branch-25) slave分支:test文件
修改这部分内容,保留正确的,然后提交
> 提示:冲突不只在合并分支时产生。无论何种冲突,处理的方法是一样的
合并后可以删除该分支:
```
git brancd -d slave
```
> [![1](https://box.kancloud.cn/2015-10-12_561bcb76795ae.png)](ch28s07.html#git-merge-22) **-D**强行删除分支;**-d**只有分支内容被合并后才能删除
## 通过文件协作
git 可以通过补丁文件进行协作(使用 email 传送补丁文件)
首先通过 **git clone** 创建一个镜像版本库,使用 `git branch -a`命令查看所有分支
```
$ git clone http://linuxtoy.org/path [local]
$ cd [local]
$ git branch -a
* master
origin/HEAD
origin/master
```
> [![1](https://box.kancloud.cn/2015-10-12_561bcb76795ae.png)](ch28s08.html#git-net-1) 原始版本库路径
> [![2](https://box.kancloud.cn/2015-10-12_561bcb768596e.png)](ch28s08.html#git-net-2) 镜像版本库路径。它是可选的,如果没有指定,则使用和发起者同样的路径(文件夹名称)
其中`origin` 为原始版本库镜像,在 master 分支上的工作,要生成对于 origin 的补丁,origin 必须与原始版本库保持一致,不要试图修改它
```
git fetch origin #更新 origin 分支。如果 origin 分支不是最新的原始版本库,会产生错误的补丁文件
git rebase origin #将工作迁移到最新原始版本库基础上
git format-patch origin #生成补丁文件
```
* 使用 **git rebase** 后可能会产生冲突,手动处理
生成的补丁文件为 `0001-[备注].patch`,发起者得到补丁后,使用 **git am** 命令将这个补丁应用到版本库
```
git checkout -b patched
git am 0001-[备注].patch
git checkout master
git diff master patched
git merge patched
```
> [![1](https://box.kancloud.cn/2015-10-12_561bcb76795ae.png)](ch28s08.html#git-am-11) 为谨慎起见,创建一个名为 “patched” 的分支,切换到此分支
> [![2](https://box.kancloud.cn/2015-10-12_561bcb768596e.png)](ch28s08.html#git-am-12) 在 “patched” 分支中应用补丁
## 通过网络协作
git 提供相当灵活的协作方式,最常见的方式为:协作者获得原始版本库的镜像,并在上面工作;发起者从协作者那里获取更新
协作者通过**git clone**创建一个镜像版本库:
```
git clone user@url:~/path [local]
```
网络对于 git 来说是透明的,凡是可以访问的位置,如 http、ftp、ssh……,甚至本地路径,对于 git 来说没有什么区别。
通过以下命令,创建一个本机原始版本库`sandbox`的镜像`project`,是允许的:
```
git clone ~/sandbox project
```
对于没有指定协议的远程路径,git 默认使用 ssh
```
(ssh://)user`@`127.0.0.1`:`~/sandbox
```
使用**git pull**获取协作者版本库中的内容:
```
git pull user@127.0.0.1:~/sanbox master[:newest]
```
> [![1](https://box.kancloud.cn/2015-10-12_561bcb76795ae.png)](ch28s09.html#git-pull-1) 版本库
> [![2](https://box.kancloud.cn/2015-10-12_561bcb768596e.png)](ch28s09.html#git-pull-2) 分支名称
> [![3](https://box.kancloud.cn/2015-10-12_561bcb94ee26f.png)](ch28s09.html#git-pull-3) 版本名称(可选。使用版本ID、版本标签,请不要使用“HEAD”)
> 提示:**git pull** 基于“版本”操作,也就是说,只有提交后才可以进行;这个命令会比较两个版本的时间戳,只获取更新的版本
当发起者进行了更新后,协作者应从发起者那里获取最新的原始版本库,并将当前工作迁移到最新的原始版本库基础上
```
git fetch origin #获取最新原始版本库
git rebase origin/master #将工作迁移到最新原始版本库
```
这时发起者再次使用 **git pull** 从协作者那里获取更新……
## gitweb
首先配置 web 服务器,使其支持 cgi,参见[“CGI”一节](ch23s03.html#server-cgi "CGI")
将 git 工作树拷贝到 web 服务器目录下:
```
cp -r sandbox /home/lighttpd/html/
```
gitweb 通常随 git 一同安装,拷贝文件到 git 工作树
```
cp /usr/share/gitweb/* /home/lighttpd/html/sandbox
```
检查 `/home/lighttpd/html/sandbox/gitweb.cgi` 文件中的如下语句
```
our $GIT = "/usr/bin/git";
our $projectroot = ".";
```
> [![1](https://box.kancloud.cn/2015-10-12_561bcb76795ae.png)](ch28s10.html#gitweb-0) git 执行文件位置
> [![2](https://box.kancloud.cn/2015-10-12_561bcb768596e.png)](ch28s10.html#gitweb-1) 项目根目录,也可以使用绝对路径,如 `/home/lighttpd/html/sandbox`
修改项目描述,编辑项目根目录下的 `.git/description` 文件
这样就建立了一个 gitweb 站点,通过以下地址访问:
```
http://linuxtoy.org/sandbox/gitweb.cgi
```
如果想通过 http 协议使用,例如:
```
git clone http://linuxtoy.org/sandbox/.git
```
则需要在项目根目录下执行 **git update-server-info**
- 开源世界旅行手册
- 授权
- 致谢
- 序言
- 更新纪录
- 导读
- 如何写作科技文档
- 部分 I. 气候
- 第 1 章 GUI? CLI?
- 第 2 章 UNIX 缩写风格
- 第 3 章 版本号的迷雾
- 第 4 章 Vim 还是 Emacs
- 第 5 章 DocBook 还是 TeX
- 第 6 章 完全用 Gnu/Linux 工作
- 第 7 章 病毒
- 第 8 章 磁盘 分区
- 第 9 章 文件系统
- 第 10 章 发行版介绍
- 第 11 章 编程语言
- 第 12 章 无根的根:无名师的 Unix 心传
- 部分 II. 地理
- 第 13 章 基础知识
- 第 14 章 命令系统
- 第 15 章 基本系统
- 第 16 章 软件管理
- 第 17 章 核心工具集
- 第 18 章 编译工具链
- 第 19 章 图形界面
- 第 20 章 国际化
- 第 21 章 内核
- 第 22 章 Grub
- 第 23 章 服务器
- 第 24 章 Vim 编辑器
- 第 25 章 Emacs 入门
- 第 26 章 正则表达式
- 第 27 章 docbook 指南
- 第 28 章 Git 版本控制系统
- 第 29 章 ConTeXt 入门指南
- 部分 III. 景观
- 第 30 章 终极 Shell -- ZSH
- 第 31 章 完美工作站 Archlinux
- 第 32 章 组织你的意念:Emacs org mode
- 第 33 章 Zsh+screen
- 第 34 章 gentoo stage3
- 第 35 章 硬件问题
- 第 36 章 网络设置
- 第 37 章 自制 LiveCD
- 第 38 章 awesome
- 第 39 章 openbox 工作环境
- 第 40 章 Emacs muse
- 第 41 章 写作工具链
- 第 42 章 使用 lftp
- 第 43 章 Firefox 使用技巧
- 第 44 章 FVWM
- 部分 IV. 地质
- 第 45 章 Unix
- 第 46 章 Gnu
- 第 47 章 软件业自由之神——Richard Stallman
- 第 48 章 Linux
- 第 49 章 GNOME与KDE的战争
- 第 50 章 Vim Emacs
- 第 51 章 年代纪
- 第 52 章 我的选择
- 第 53 章 补遗