创建储存库

本节部分内容参考自 git-recipes,在原文基础上有一定修改。

git init

git init 命令创建一个新的 Git 储存库。它可以在一个已经有文件的储存库中使用,用来将已存在但还没有版本控制的项目转换成一个 Git 储存库,或者在一个空文件夹里使用,创建一个空的新储存库。

运行 git init 命令会在你项目的根目录下创建一个新的 .git 目录,其中包含了你项目必需的所有元数据。除了 .git 目录之外,已经存在的文件不会被改变。

详细用法

点击展开
git init

将当前的目录转换成一个 Git 储存库。它在当前的目录下增加了一个 .git 目录,于是就可以开始记录项目版本了。

git init <directory>

在指定目录创建一个空的 Git 储存库。运行这个命令会在当前目录下创建一个名为 directory,只包含 .git 子目录的空目录。

git init --bare <directory>.git

初始化一个裸的 Git 储存库(无工作树)。这个目录会创建一个名为 <directory>.git 的目录,其中包含了一个储存库的元信息。裸的储存库一般用作服务器上的共享储存库。关于裸的储存库,后文会详细解释。

git clone

git clone 命令拷贝整个 Git 仓库。这个命令获取到的副本是一个完备的Git仓库——它包含自己的历史,管理自己的文件,并且环境和原仓库完全隔离。

为了方便起见,Git 在 clone 时会自动创建一个名为 origin 的远程连接,指向原有仓库。这让和中央仓库之间的交互更加简单。

如果项目在远程仓库已经设置完毕,git clone 是用户获取开发副本最常见的方式。和 git init 相似,clone 通常也是一次性的操作——只要开发者获得了一份工作副本,所有版本控制操作和协作管理都是在本地仓库中完成的。

详细用法

点击展开
git clone <repo>

将位于 repo 的仓库克隆到本地机器。原仓库可以在本地文件系统中,或是通过 HTTP 或 SSH 连接的远程机器。

git clone <repo> <directory>

将位于 repo 的仓库克隆到本地机器上的 directory 目录。

谁是本体?

当我们用 git init 创建储存库的时候,Git 只是在当前目录下创建了一个 .git 目录。而如果我们手动把这个目录删掉,它就又从储存库变回原本平平无奇的文件夹了。整个过程中,Git 没有对原本的文件做出任何破坏性的改变。

看来所有的秘密都藏在这个 .git 目录中了。Git 到底对这个目录用了什么魔法?

当我们在开发时,99.999% 的时间都是在和我们自己的代码打交道,.git 文件夹对我们而言是一个黑箱。所以,有的时候,我们可能会产生一种错觉,认为我们能看到的代码是本体,而 .git 只是 Git 提供的附属品而已。

但事实并非如此。从我们敲下 git init 的那一刻开始,世界在 Git 的眼中就发生了改变。在 Git 看来,那个不起眼的 .git 才是储存库的本体,其他的一切都是它的附属品。

没错,就和一些漫画中的人物一样,「眼镜才是本体」。

储存库和工作树

什么是工作树?

在 Git 眼中,真正的储存库只是那一个 .git 文件夹。而在外面天天被我们盯着看的东西,在 Git 的世界里叫做「工作树 (worktree)」。

一个 Git 储存库可以有一个或多个工作树,甚至可以没有工作树。我们用 git initgit clone 建立的,都是一个储存库和一个工作树的组合,这样建立的工作树称为「主工作树」。

git worktree list 命令,可以查看当前 Git 储存库中的所有工作树。

$ git worktree list
E:/git-remake-guide  5c64c4d [创建储存库]

Git 的文件系统

在 Git 中,文件以两种形式存放:在储存库中,按照 Git 的格式将每一个版本储存为一个个 object,并按照更改历史、提交信息等等建立索引;在工作树中,按照一般的操作系统可识别的文件系统的方式存放。由于在储存库中,文件存在多个版本,而且并不是按照目录结构存放的,所以,对储存库中的文件不能直接编辑,而是要通过 Git 命令提取到工作树之后再编辑。

从储存库中取出文件,将其置入工作树中的操作,称为「签出(checkout)」。没错,就是熟悉的 git checkout 命令。

而与之对应的,将工作树中的文件写入储存库的操作,称为「签入」。签入操作没有一个单独的命令对应,而是通过 git add git commit 等一系列命令来完成。

一般情况下,开发的流程是这样的:先从储存库中签出文件,然后在工作树中修改文件,最后进行签入,把工作树中的文件写入储存库。

所以,Git 的储存库不仅包含着工作树中全部的内容,也包含着工作树全部的历史。就算把工作树删掉,依然可以从储存库中签出一个工作树的版本出来。

裸仓库

工作树的存在,是为了方便对储存库内容进行编辑。有的时候,我们只需要 Git 储存库,而不需要工作树。比如,像 GitHub 这样的托管服务上,只需要知道用户的储存库,而不用管理一个工作树。

像这样没有工作树,只有储存库的情况,称为「裸仓库」。

git init --bare <directory>.git 命令,可以创建一个裸仓库。这个命令会在当前文件夹下建立一个名为 directory.git 的文件夹,其中的文件结构和一般的 .git 如出一辙。

Git 约定裸仓库的名称以 .git 结尾。这就是为什么我们看到的 GitHub 上的储存库链接的结尾都是 XXX.git;实际上,这就是一个裸仓库的文件夹名。

既然我们可以克隆一个 GitHub 上的裸仓库,我们当然也能克隆一般的储存库。你可以找一个本地的 Git 储存库,然后克隆它试试:

$ git clone E:\git-remake-guide\.git
Cloning into 'git-remake-guide'...
done.
$ lsd --tree --classic .\git-remake-guide\
git-remake-guide
├── assets
│   ├── custom.css
│   ├── sidetoc.css
│   └── sidetoc.js
├── book.toml
├── README.md
├── src
│   ├── Git-重学指南.md
│   ├── SUMMARY.md
└── theme
    ├── favicon.png
    ├── favicon.svg
    └── index.hbs

完美。