写点什么

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:3247850

评论

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

Go 并发编程原语及并发设计模式

baiyutang

Go 编程 云原生 并发’

7thonline第七在线出席中服协时尚科技峰会 探讨AI商品管理落地

第七在线

优化数据处理效率,解读 EasyMR 大数据组件升级

袋鼠云数栈

大数据组件 大数据计算引擎 大数据引擎 数据组件 组件升级

淘宝API接口测试中的安全性保障

Noah

OmniOutliner 5 Pro for Mac(信息大纲记录工具) v5.13.1正式版

Mac相关知识分享

【YashanDB知识库】ycm托管数据库时,数据库非OM安装无法托管

YashanDB

yashandb 崖山数据库 崖山DB

生成式 AI 时代的数据库:Databend 与大模型的融合探索

Databend

利用人工智能ChatGPT自动生成基于PO的数据驱动测试框架

霍格沃兹测试开发学社

DaisyDisk for Mac(mac磁盘清理软件) 4.25中文激活版

Mac相关知识分享

Topaz Photo AI for Mac(人工智能降噪软件) v3.1.3激活版

Mac相关知识分享

闲鱼开发者必读:如何利用闲鱼商品列表数据接口进行数据分析

tbapi

闲鱼API 闲鱼商品列表接口 闲鱼商品列表数据接口 闲鱼商品列表数据采集

测试环境领域到测试环境产品

阿里技术

阿里巴巴 测试 淘宝 测试环境

iZotope RX 7 Advanced for Mac(降噪和音频修复软件) 免激活版

Mac相关知识分享

音频修复软件

比特币挖矿可能是一个 200 亿美元的市场,美国硬件推动:伯恩斯坦

dappweb

多人同时大批量导出数据到 Excel 干崩服务器,如何处理?

源字节1号

开源

穿越流程管理的丛林!Prothentic学院「流程管理培训」第1期圆满落幕

望繁信科技

业务流程管理 望繁信科技 流程管理培训 陈果George Prothentic学院

利用人工智能ChatGPT自动生成基于PO的数据驱动测试框架

测吧(北京)科技有限公司

测试

BricsCAD 24 for Mac(高效CAD建模软件) v24.2.06中文激活版

Mac相关知识分享

TunesKit Video Cutter for mac(视频分割编辑器) v3.0.0激活版

Mac相关知识分享

Charles for Mac(HTTP协议抓包工具) v5.0b16注册激活版

Mac相关知识分享

什么是云原生?

源字节1号

开源

全渠道AI数字化商品管理 零售品牌增长“超级引擎”

第七在线

百度智能云 ABC Stack 获可信云技术最佳实践,共建一云多算生态

Baidu AICLOUD

数据安全风险怎么理解?包括哪些方面?

行云管家

数字化 数据安全 企业数据

【Hbase】运维问题汇总

扬_帆_起_航

PVP2 ProVideoPlayer2 for Mac(PVP2多屏幕演示投放软件) 中文版

Mac相关知识分享

卡奥斯智能交互引擎:引领工业智能,共创数字化未来

Openlab_cosmoplat

人工智能 大模型

济宁有正规等保测评机构吗?大神求告知!

行云管家

等保 等级保护 济宁

Tenable Nessus 10.8.0 发布,新增功能概览

sysin

漏洞扫描 Nessus 漏洞管理

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