ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
## 第 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**