git原理与基本命令

参考链接:https://medium.com/@shalithasuranga/how-does-git-work-internally-7c36dcb1f2cf

简介:

1.什么是git:

Git是目前世界上最先进的分布式版本控制系统。

2.SVN与git的最主要区别:

SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以首先要从中央服务器哪里得到最新的版本,然后干活,干完后,需要把自己做完的活推送到中央服务器。集中式版本控制系统是必须联网才能工作,如果在局域网还可以,带宽够大,速度够快,如果在互联网下,如果网速慢的话,根本不好用了。

Git是分布式版本控制系统,那么它就没有中央服务器的,每个人的电脑就是一个完整的版本库,这样,工作的时候就不需要联网了,因为版本都是在自己的电脑上。既然每个人的电脑都有一个完整的版本库,那多个人如何协作呢?比如说自己在电脑上改了文件A,其他人也在电脑上改了文件A,这时,你们两之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。

git基本命令

一、初始化

创建目录并进入目录:

 xqkang@xqkang:~$ mkdir git_demo
xqkang@xqkang:~$ ls 
baidunetdisk          examples.desktop  testubuntu.jar  视频  音乐
baidunetdiskdownload  git_demo          ubuntutt.tar    图片  桌面
docker                qj                公共的          文档
docker_demo           test.txt          模板            下载
xqkang@xqkang:~$ cd git_demo/

使用git init命令进行初始化:

xqkang@xqkang:~/git_demo$ git init
已初始化空的 Git 仓库于 /home/xqkang/git_demo/.git/

git init命令只做一件事,就是在项目根目录下创建一个.git子目录,用来保存版本信息。

xqkang@xqkang:~/git_demo$ ls .git/
branches  config  description  HEAD  hooks  info  objects  refs

二、保存对象
新建一个空文件:

xqkang@xqkang:~/git_demo$ touch test.txt
xqkang@xqkang:~/git_demo$ ls
test.txt

然后,把这个文件加入 Git 仓库,也就是为test.txt的当前内容创建一个副本。
x

qkang@xqkang:~/git_demo$ git hash-object -w test.txt
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391

(git hash-object -w :实际上将对象写入对象数据库)
上面代码中,git hash-object命令把test.txt的当前内容压缩成二进制文件,存入 Git。压缩后的二进制文件,称为一个 Git 对象,保存在.git/objects目录。

这个命令还会计算当前内容的 SHA1 哈希值(长度40的字符串),作为该对象的文件名。下面看一下这个新生成的 Git 对象文件。

xqkang@xqkang:~/git_demo$ ls -R .git/objects/
.git/objects/:
e6  info  pack

.git/objects/e6:
9de29bb2d1d6434b8b29ae775ad8c2e48c5391

.git/objects/info:

.git/objects/pack:

ls -R :
查看文件的内容:递归显示子目录

查看一下文件里的内容:

xqkang@xqkang:~/git_demo$ cat .git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 
xK��OR0`	��

虽然是一个test.txt是空文件,但里面也会保存一些元数据。
如果想要看到文件里的内容,使用git cat-file命令。
$ git cat-file -p e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
现在里面还什么都没有。下面我们往里面添加一些内容:

xqkang@xqkang:~/git_demo$ echo 'hello world' < test.txt 
hello world

文件内容已经改变,需要再次把文件保存成git对象:

xqkang@xqkang:~/git_demo$ git hash-object -w test.txt
3b18e512dba79e4c8300dd08aeb37f8e728b8dad

这时文件在3b里:
kang@xqkang:~/git_demo$ ls .git/objects/
3b e6 info pack
查看文件:

xqkang@xqkang:~/git_demo$ git cat-file -p 3b18e512dba79e4c8300dd08aeb37f8e728b8dad
hello world

这时可以看出hello world 已经打印出来。

三、暂存区
文件保存成二进制对象以后,还需要通知 Git 哪些文件发生了变动。所有变动的文件,Git 都记录在一个区域,叫做"暂存区"(英文叫做 index 或者 stage)。等到变动告一段落,再统一把暂存区里面的文件写入正式的版本历史。
git update-index命令用于在暂存区记录一个发生变动的文件。

git update-index --add --cacheinfo <mode>,<sha1>,<path>

mode:模式
100644:普通文件
100755:可执行文件
120000:符号链接
sha1:hash-object创建出的hash值
path:文件路径

xqkang@xqkang:~/git_demo$ git update-index --add --cacheinfo 100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad test.txt

上面命令向暂存区写入文件名test.txt、二进制对象名(哈希值)和文件权限。

git ls-files命令可以显示暂存区当前的内容。

xqkang@xqkang:~/git_demo$ git ls-files --stage
100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad 0	test.txt

上面代码表示,暂存区现在只有一个文件test.txt,以及它的二进制对象名和权限。知道了二进制对象名,就可以在.git/objects子目录里面读出这个文件的内容。
git status命令会产生更可读的结果。

xqkang@xqkang:~/git_demo$ git status
位于分支 master

尚无提交

要提交的变更:
  (使用 "git rm --cached <文件>..." 以取消暂存)

	新文件:   test.txt

上面代码表示,暂存区里面只有一个新文件test.txt,等待写入历史。
四、git add 命令
上面两步(保存对象和更新暂存区),如果每个文件都做一遍,那是很麻烦的。Git 提供了git add命令简化操作

xqkang@xqkang:~/git_demo$ sudo vim test2.txt
[sudo] xqkang 的密码: 
xqkang@xqkang:~/git_demo$ git add --all
xqkang@xqkang:~/git_demo$ git status 
位于分支 master

尚无提交

要提交的变更:
  (使用 "git rm --cached <文件>..." 以取消暂存)

	新文件:   test.txt
	新文件:   test2.txt

上面命令相当于,对当前项目所有变动的文件,执行前面的两步操作。

五、commit概念

暂存区保留本次变动的文件信息,等到修改了差不多了,就要把这些信息写入历史,这就相当于生成了当前项目的一个快照(snapshot)。

项目的历史就是由不同时点的快照构成。Git 可以将项目恢复到任意一个快照。快照在 Git 里面有一个专门名词,叫做 commit,生成快照又称为完成一次提交。

下文所有提到"快照"的地方,指的就是 commit。

六、完成提交

首先,设置一下用户名和 Email,保存快照的时候,会记录是谁提交的。

$ git config user.name "用户名" 
$ git config user.email "Email 地址"

接下来,要保存当前的目录结构。前面保存对象的时候,只是保存单个文件,并没有记录文件之间的目录关系(哪个文件在哪里)。

git write-tree命令用来将当前的目录结构,生成一个 Git 对象。

xqkang@xqkang:~/git_demo$ git write-tree
8110dabcfaf2343ab6317309471f4e9d502ddd4e

查看一下这个文件中的内容:

xqkang@xqkang:~/git_demo$ git cat-file -p 8110dabcfaf2343ab6317309471f4e9d502ddd4e
100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad	test.txt
100644 blob 15055f30538e60bf7883dbb4fe0800cabb4ec20e	test2.txt

这时可以看出当前目录中包含两个文件test.txt 和test2.txt
所谓快照,就是保存当前的目录结构,以及每个文件对应的二进制对象。上一个操作,目录结构已经保存好了,现在需要将这个目录结构与一些元数据一起写入版本历史
git commit-tree命令用于将目录树对象写入版本历史。

xqkang@xqkang:~/git_demo$ echo "first commit" | git commit-tree 8110dabcfaf2343ab6317309471f4e9d502ddd
d06b3d28e7e600b81e59280635b55920c630be7f

上面代码中,提交的时候需要有提交说明,echo "first commit"就是给出提交说明。然后,git commit-tree命令将元数据和目录树,一起生成一个 Git 对象。现在,看一下这个对象的内容。

xqkang@xqkang:~/git_demo$ git cat-file -p d06b3d28e7e600b81e59280635b55920c630be7f
tree 8110dabcfaf2343ab6317309471f4e9d502ddd4e
author xqkang <1220647515@qq.com> 1575962979 +0800
committer xqkang <1220647515@qq.com> 1575962979 +0800

first commit

上面代码中,输出结果的第一行是本次快照对应的目录树对象(tree),第二行和第三行是作者和提交人信息,最后是提交说明。

git log命令也可以用来查看某个快照信息。

xqkang@xqkang:~/git_demo$ git log --stat d06b3d28e7e600b81e59280635b55920c630be7f
commit d06b3d28e7e600b81e59280635b55920c630be7f
Author: xqkang <1220647515@qq.com>
Date:   Tue Dec 10 15:29:39 2019 +0800

    first commit

 test.txt  | 1 +
 test2.txt | 1 +
 2 files changed, 2 insertions(+)

Git 提供了git commit命令,简化提交操作。保存进暂存区以后,只要git commit一个命令,就同时提交目录结构和说明,生成快照。

七、git commit 命令

Git 提供了git commit命令,简化提交操作。保存进暂存区以后,只要git commit一个命令,就同时提交目录结构和说明,生成快照。

xqkang@xqkang:~/git_demo$ git commit -m "first commit"
[master (根提交) 4c2d3e8] first commit
 2 files changed, 2 insertions(+)
 create mode 100644 test.txt
 create mode 100644 test2.txt

git chechout命令用于切换到某个快照

xqkang@xqkang:~/git_demo$ git checkout d06b3d28e7e600b81e59280635b55920c630be7f
注意:正在检出 'd06b3d28e7e600b81e59280635b55920c630be7f'。

您正处于分离头指针状态。您可以查看、做试验性的修改及提交,并且您可以通过另外
的检出分支操作丢弃在这个状态下所做的任何提交。

如果您想要通过创建分支来保留在此状态下所做的提交,您可以通过在检出命令添加
参数 -b 来实现(现在或稍后)。例如:

  git checkout -b <新分支名>

HEAD 目前位于 d06b3d2 first commit

git show命令用于展示某个快照的所有代码变动

xqkang@xqkang:~/git_demo$ git show
commit d06b3d28e7e600b81e59280635b55920c630be7f (HEAD)
Author: xqkang <1220647515@qq.com>
Date:   Tue Dec 10 15:29:39 2019 +0800

    first commit

diff --git a/test.txt b/test.txt
new file mode 100644
index 0000000..3b18e51
--- /dev/null
+++ b/test.txt
@@ -0,0 +1 @@
+hello world
diff --git a/test2.txt b/test2.txt
new file mode 100644
index 0000000..15055f3
--- /dev/null
+++ b/test2.txt
@@ -0,0 +1 @@
+hahhahahahha

八、branch的概念

到了这一步,还没完。如果这时用git log命令查看整个版本历史,你看不到新生成的快照。

$ git log

上面命令没有任何输出,这是为什么呢?快照明明已经写入历史了。
(我不知为什么我的就出来查询结果了)

xqkang@xqkang:~/git_demo$ git log
commit d06b3d28e7e600b81e59280635b55920c630be7f (HEAD)
Author: xqkang <1220647515@qq.com>
Date:   Tue Dec 10 15:29:39 2019 +0800

    first commit

原来git log命令只显示当前分支的变动,虽然我们前面已经提交了快照,但是还没有记录这个快照属于哪个分支。

所谓分支(branch)就是指向某个快照的指针,分支名就是指针名。哈希值是无法记忆的,分支使得用户可以为快照起别名。而且,分支会自动更新,如果当前分支有新的快照,指针就会自动指向它。比如,master 分支就是有一个叫做 master 指针,它指向的快照就是 master 分支的当前快照。

用户可以对任意快照新建指针。比如,新建一个 fix-typo 分支,就是创建一个叫做 fix-typo 的指针,指向某个快照。所以,Git 新建分支特别容易,成本极低。

Git 有一个特殊指针HEAD, 总是指向当前分支的最近一次快照。另外,Git 还提供简写方式,HEAD^指向 HEAD的前一个快照(父节点),HEAD~6则是HEAD之前的第6个快照。

每一个分支指针都是一个文本文件,保存在.git/refs/heads/目录,该文件的内容就是它所指向的快照的二进制对象

名(哈希值)。

九、更新分支

首先修改一下test.txt文件:
下面演示更新分支是怎么回事。首先,修改一下test.txt。

 $ echo "hello world again" > test.txt

然后,保存二进制对象。

xqkang@xqkang:~/git_demo$ git hash-object -w test.txt
c90c5155ccd6661aed956510f5bd57828eec9ddb

接着,将这个对象写入暂存区,并保存目录结构。

  $ git update-index test.txt
    $ git write-tree

    b3fb6a43bafc8e4deea5e8a9928a7efe3d675d63

最后,提交目录结构,生成一个快照。

xqkang@xqkang:~/git_demo$ echo "second commit" | git commit-tree b3fb6a43bafc8e4deea5e8a9928a7efe3d675d63 -p d06b3d28e7e600b81e59280635b55920c630be7f
b9304cccef0a745d25794b6fec587648fbd35706

上面代码中,git commit-tree的-p参数用来指定父节点,也就是本次快照所基于的快照。

现在,我们把本次快照的哈希值,写入.git/refs/heads/master文件,这样就使得master指针指向这个快照。

xqkang@xqkang:~/git_demo$ echo b9304cccef0a745d25794b6fec587648fbd35706 >.git/refs/heads/master 

现在,git log就可以看到两个快照了。

 xqkang@xqkang:~/git_demo$ git log
commit d06b3d28e7e600b81e59280635b55920c630be7f (HEAD)
Author: xqkang <1220647515@qq.com>
Date:   Tue Dec 10 15:29:39 2019 +0800

    first commit

。。。。。。。我的没有出现两个快照
反而是在git commit -m “second” 时出现了两个快照

git log的运行过程是这样的:

    查找HEAD指针对应的分支,本例是master
    找到master指针指向的快照,本例是785f188674ef3c6ddc5b516307884e1d551f53ca
    找到父节点(前一个快照)c9053865e9dff393fd2f7a92a18f9bd7f2caa7fa
    以此类推,显示当前分支的所有快照

最后,补充一点。前面说过,分支指针是动态的。原因在于,下面三个命令会自动改写分支指针。

    git commit:当前分支指针移向新创建的快照。
    git pull:当前分支与远程分支合并后,指针指向新创建的快照。
    git reset [commit_sha]:当前分支指针重置为指定快照。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值