产品战略专家梁宁确认出席AICon北京站,分享AI时代下的商业逻辑与产品需求 了解详情
写点什么

Git 历险记(三)——创建一个自己的本地仓库

  • 2011-02-22
  • 本文字数:4233 字

    阅读完需:约 14 分钟

如果我们要把一个项目加入到 Git 的版本管理中,可以在项目所在的目录用 git init 命令建立一个空的本地仓库,然后再用 git add 命令把它们都加入到 Git 本地仓库的暂存区(stage or index)中,最后再用 git commit 命令提交到本地仓库里。

创建一个新的项目目录,并生成一些简单的文件内容:

复制代码
<b>$ mkdir test_proj</b>
<b>$ cd test_proj</b>
<b>$ echo “hello,world” > readme.txt</b>

在项目目录创建新的本地仓库,并把项目里的所有文件全部添加、提交到本地仓库中去:

复制代码
$ git init #在当前的目录下创建一个新的空的本地仓库
Initialized empty Git repository in /home/user/test_proj/.git/
$ git add . #把前目录下的所有文件全部添加到暂存区
$ git commit -m 'project init' #创建提交
[master (root-commit) b36a785] project init
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 readme.txt

Git 目录的结构

git init 命令在项目的顶层目录中建了一个名为:“.git”的目录,它的别名是 “Git 目录”(Git directory)。这时”Git 目录”中虽然有一些文件,但是没有任何提交(commit)在里面,所以我们叫它是空仓库(empty Git repository)。

和 SVN 不同,一个 Git 项目一般只在项目的根目录下建一个“.git”目录,而 SVN 则会在项目的每一个目录下建一个”.svn”目录;这也我喜欢 Git 的原因之一:)

Git 把所有的历史提交信息全部存储在“Git 目录”里,它就是一个 Git 项目的仓库;你对本地的源代码进行编辑修改后创建的提交也都会先保存在这里面,然后再推送到远端的服务器。当我们我把项目目录和“Git 目录”一起拷到其它电脑里,它能马上正常的工作(所有的提交信息全都保存在 Git 目录里);甚至可以只把“Git 目录”拷走也行,但是要再签出(checkout)一次。

Git 为了 调试的方便,它可以指定项目的 Git 目录的位置。有两种办法:一是设置“GIT_DIR”环境变量,二是在命令行里设定“–git-dir–git-dir”参数指定它的位置,大家可以看一下这里 ( git(1) Manual Page )。

庖丁解牛

前面的这些东东我在第一篇里也大概的讲过一些,但是今天我们想不但要开动这辆叫“Git”的跑车,还想看看它里面有些什么样的零件,是怎么构成的。

OK,我们来看看“test_proj”项目里的“Git 目录”的结构:

复制代码
$cd test_proj/.git
$ ls | more
branches/ # 新版的 Git 已经不再使用这个目录,所以大家看到它 #一般会是空的
COMMIT_EDITMSG # 保存着上一次提交时的注释信息
config # 项目的配置信息
description # 项目的描述信息
HEAD # 项目当前在哪个分支的信息
hooks/ # 默认的“hooks” 脚本文件
index # 索引文件,git add 后把要添加的项暂存到这里
info/ # 里面有一个 exclude 文件,指定本项目要忽略的文件 #,看一下这里
logs/ # 各个 refs 的历史信息
objects/ # 这个目录非常重要,里面存储都是 Git 的数据对象
# 包括:提交 (commits), 树对象 (trees),二进制对象 #(blobs), 标签对象(tags)。
#不明白没有关系,后面会讲的。
refs/ # 标识着你的每个分支指向哪个提交(commit)。

我先用 git log 命令来看一下这个 Git 项目里有哪些提交:

复制代码
$ git log
commit 58b53cfe12a9625865159b6fcf2738b2f6774844
Author: liuhui998 <liuhui998@nospam.com>
Date: Sat Feb 19 18:10:08 2011 +0800
project init

大家可以看到目前只有一个提交(commit)对象,而它的名字就是:”58b53cfe12a9625865159b6fcf2738b2f6774844”。这个名字就是对象内容的一个 SHA 签名串值,只要对象里面的内容不同,那么我们就可以认为对象的名字不会相同,反之也成立。我在使用时一般不用把这个 40 个字符输全,只要把前面的 5~8 个字符输完就可以(前提是和其它的对象名不冲突)。为了方便表示,在不影响表达的情况下,我会只写 SHA 串值的前 6 个字符。

我们可以用 git cat-file 来看一下这个提交里的内容是什么:

复制代码
$ git cat-file -p 58b53c
<b>tree 2bb9f0c9dc5caa1fb10f9e0ccbb3a7003c8a0e13</b>
author liuhui998 <liuhui998@nospam.com> 1298110208 +0800
committer liuhui998 <liuhui998@nospam.com> 1298110208 +0800
project init

大家可以看到:提交“58b53c” 是引用一个名为“2bb9f0”的树对象(tree)。一个树对象(tree)可以引用一个或多个二进制对象(blob), 每个二进制对象都对应一个文件。 更进一步, 树对象也可以引用其他的树对象,从而构成一个目录层次结构。我们再看一下这个树对象(tree)里面有什么东东:

复制代码
$ git cat-file -p 2bb9f0
100644 <b>blob 2d832d9044c698081e59c322d5a2a459da546469 readme.txt</b>

不难看出,2bb9f0”这个树对象(tree)包括了了一个二进制对象(blob),对应于我们在前面创建的那个叫 ”readme.txt”的文件。现在我们来看看这个”blob”里的数据是不是和前面的提交的内容一致:

复制代码
$ git cat-file -p 2d832d
hello,world

哈哈,熟悉的“hello,world”又回来了。

想不想看看提交对象、树对象和二进制对象是怎么在”Git 目录“中存储的;没有问题,执行下面的命令,看看”.git/objects”目录里的内容:

复制代码
$ find .git/objects
.git/objects
.git/objects/2b
.git/objects/<b>2b/b9f0c9dc5caa1fb10f9e0ccbb3a7003c8a0e13</b>
.git/objects/2d
.git/objects<b>/2d/832d9044c698081e59c322d5a2a459da546469</b>
.git/objects/58
.git/objects/<b>58/b53cfe12a9625865159b6fcf2738b2f6774844</b>
.git/objects/info
.git/objects/pack

如果大家仔细看上面命令执行结果中的粗体字,所有的对象都使用 SHA 签名串值作为索引存储在”.git/objects”目录之下;SHA 串的前两个字符作为目录名,后面的 38 个字符作为文件名。

这些文件的内容其实是压缩的数据外加一个标注类型和长度的头。类型可以是提交对象(commit)、二进制对象(blob)、 树对象(tree)或者标签对象(tag)。

如何 clone 一个远程项目

我身边的很多朋友是因为要得到某个开源项目的代码,所以才开始学习使用 Git。而获取一个项目的代码的一般的做法就是用 git clone 命令进行直接复制。

例如,有些朋友可能想看一下最新的 linux 内核源代码,当我们打开它的网站时,发现有如下面的一段提示:

复制代码
URL
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
http://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git

URL 下面的三行字符串表示三个地址,我们可以通过这三个地址得到同样的一份 Linux 内核源代码。

也就是说下面这三条命令最终得到的是同一份源代码:

复制代码
git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
git clone http://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
git cone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git

我们先来看一下 URL,git://、http://、 https:// 这些代表是传输 git 仓库的协议形式,而“ git.kernel.org “则代表了 Git 仓库存储的服务器名字(域名),“ /pub/scm/linux/kernel/git/torvalds/linux-2.6.git” 则代表了 Git 仓库在服务器上位置。

Git 仓库除了可以通过上面的 git、http、https 协议传输外还可以通过 ssh、ftp(s)、rsync 等协议来传输。 git clone 的本质就是把“Git 目录”里面的内容拷贝过来,大家想想看,一般的“Git 目录”里有成千上万的各种对象(提交对象,树对象,二进制对象…),如果逐一复制的话,其效率就可想而知。

如果通过 git、ssh 协议传输,服务器端会在传输前把需要传输的各种对象先打好包再进行传输;而 http(s)协议则会反复请求要传输的不同对象。如果仓库里面的提交不多的话,前者和后者的效率相差不多;但是若仓库里有很多提交的话,git、ssh 协议进行传输则会更有效率。

不过现在 Git 对 http(s)协议传输 Git 仓库做了一定的优化,http(s)传输现在也能达到 ssh 协议的效率,有兴趣的朋友可以看一下这里( Smart HTTP Transport )。

好的,现在我们执行了下面这条命令,把 linux-2.6 的最新版源代码 clone 下来:

复制代码
<b>$cd ~/</b>
<b>$mkdir temp</b>
<b>$git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git</b>
Initialized empty Git repository in /home/liuhui/temp/linux-2.6/.git/
remote: Counting objects: 1889189, done.
remote: Compressing objects: 100% (303141/303141), done.
Receiving objects: 100% (1889189/1889189), 385.03 MiB | 1.64 MiB/s, done.
remote: Total 1889189 (delta 1570491), reused 1887756 (delta 1569178)
Resolving deltas: 100% (1570491/1570491), done.
Checking out files: 100% (35867/35867), done.

当我们执行了“git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git”这条命令后大家可以看到这条输出:

复制代码
Initialized empty Git repository in /home/user/temp/linux-2.6/.git/

这就是意味着我们在本地先建了一个“linux-2.6”目录,然后在这个目录建了一个空的 Git 本地仓库(Git 目录);里面将会存储从网上拉下来的历史提交。

下面两条输入代表服务器现在调用 git-pack-objects 对它的仓库进行打包和压缩:

复制代码
remote: Counting objects: 1888686, done.
remote: Compressing objects: 100% (302932/302932), done.

然后客户端接收服务器端发过送过来的数据:

复制代码
Receiving objects: 100% (1889189/1889189), 385.03 MiB | 1.64 MiB/s, done.

在我们执行完上面的 clone linux-2.6 代码的的操作后,Git 会从“Git 目录”里把最新的代码到签出(checkout)到“linux-2.6”这个目录里面。我们一般把本地的“linux-2.6”这个目录叫做”工作目录“(work directory),它里面保存着你从其它地方 clone(or checkout)过来的代码。当你在项目的不同分支间切换时,“工作目录”中的文件可能会被替换或者删除;“工作目录”只是保存着当前的工作,你可以修改里面文件的内容直到下次提交为止。

大家还记得前面的“庖丁解牛”吗,是不是觉得只杀一头叫“hello,world”的小牛太不过瘾了。没有问题,拿起前面的那把小刀,来剖析一下现在躺在你硬盘里这头叫“linux-2.6”大牛看看,我想一定很好玩。


在写篇文章的过程中,我要感谢在那些关心我并提出真诚意见的朋友,如果没有你们真诚的意见,我也许没有这么强烈的紧迫感,也不会深深的感到自己的不足。我是第一次写专栏,张凯锋同学给了我很大的帮助。最后还是要感谢我的家人,是他们让我有时间来进行写作:)

2011-02-22 21:3247758

评论

发布
暂无评论
发现更多内容

Remote Write:观测云数据存储的新篇章

可观测技术

Prometheus

构建业务可观测性:统一数据结构的重要性

可观测技术

数据结构

大模型加持,火山引擎数据飞轮转入消费行业

字节跳动数据平台

大数据 数字化转型 云服务 数据平台 火山引擎

好的测试用例应该具备哪些特质

老张

软件测试 质量保障 测试用例 研发管理工具

强大全景图拼接制作工具PTGui Pro for Mac

Mac相关知识分享

Mac软件 图像制作

超越传统:观测云的监控功能与 AI 集成

可观测技术

人工智能

科大讯飞学习机s30和p30区别对比

妙龙

科大讯飞 学习机

关键字搜索新视角:API返回值中的商品热度分析

技术冰糖葫芦

API Explorer平台 API Explorer API 接口 API 测试 pinduoduo API

京东秒送LBS场景下的容灾数据备份方案

京东零售技术

架构 数据 LBS 企业号2024年8月PK榜

陶建辉在第 41 届中国数据库学术会议上深度解析时序数据库

TDengine

没有它,你的Scrum无法实现!

敏捷开发

Scrum 敏捷开发

讯兔科技携手豆包大模型,勇闯AI投研“深水区”

新消费日报

大咖公开课 | AI自动化应用开发,让创意与效率并驾齐驱!

测试人

软件测试

大咖公开课 | AI自动化应用开发,让创意与效率并驾齐驱!

霍格沃兹测试开发学社

企业如何通过数据虚拟化,构建逻辑数据编织平台?

Aloudata

数据虚拟化 Data Fabric 逻辑数据编织

ETL数据集成丨PostgreSQL数据迁移至Hive数据库

RestCloud

数据库 postgresql hive ETL 数据集成

支付域——收单业务

庄小焱

支付系统 金融 收单 跨境收单

得物Flink内核探索实践

得物技术

flink 技术分享 企业号2024年8月PK榜

2024英特尔中国学术峰会:聚焦绿色计算,推动产学融合

E科讯

LeetCode题解:1250. 检查「好数组」,裴蜀定理,详细注释

Lee Chen

LED显示屏批发需要注意的关键事项

Dylan

LED显示屏 全彩LED显示屏 led显示屏厂家 市场 采购

谷歌访问助手插件 for Mac中文版

Mac相关知识分享

谷歌插件

电子阅读软件中文版MarginNote 3 for Mac

Mac相关知识分享

强大 SSH 工具Termius mac 版

Mac相关知识分享

持续更新与技术领先:保持监控系统的先进性

可观测技术

产品迭代

LeetCode题解:1797. 设计一个验证系统,哈希表,JavaScript,详细注释

Lee Chen

LeetCode题解:1234. 替换子串得到平衡字符串,滑动窗口,详细注释

Lee Chen

Visio文件编辑查看工具:Visio Viewer for Mac 激活版

你的猪会飞吗

mac软件下载

数据分析统计软件Minitab Express for Mac激活版

Mac相关知识分享

数据分析 Mac软件

QCN974/QCN6274 Chip multi-band network adapter configuration details - Innovation in the age of Wi-Fi 7

wifi6-yiyi

WiFi7

观测云的成本效益分析:开源工具在企业监控中的应用

可观测技术

开源

Git 历险记(三)——创建一个自己的本地仓库_Java_刘辉_InfoQ精选文章