1. git基本介绍
认识 .git 文件
- 创建一个新的git仓库(git-demo),观察 .git 文件的基本组成
- HEAD
向当前正在工作的分支,是通过ref
指向的一个引用
cat .git/HEAD
ref: refs/heads/master
表示当前分支为master
-
config
保存了本地仓库的配置信息,输入cat .git/config
查看config里面的内容 -
description
仓库的描述信息 -
hooks
执行的一些钩子脚本,.sample
默认不生效,去掉.sample
以后才会生效。
exit 1: 表明没有通过校验
exit 0: 通过校验
-
info/exclude
exclude
可以与.gitignore
实现相同的功能。区别是.gitignore
是团队共享,.git/info/exclude
是共个人专用 -
objects
包含一系列重要的对象:tree、blob、commit、tag。
objects/pack:压缩文件,通过git gc
进行压缩 或者 通过git push
推送到远程仓库都会形成压缩文件。 -
refs
表示一系列的引用,包含heads
、remote
、tags
等目录;在刚初始化的项目里面只包含heads
、tags
。如果是一个全新的空仓库,这两个目录都将是空的
heads
:表示一系列的本地分支
remote
:远程仓库分支
tags
:tag标签
git的三个区
- 工作目录 ( working directory ):操作系统上的文件,所有代码开发编辑都在这上面完成。
- 索引( index or staging area ):可以理解为一个暂存区域,这里面的代码会在下一次commit被提交到Git仓库。(
git add
以后) - Git仓库( git repository ):由Git object记录着每一次提交的快照,以及链式结构记录的提交变更历史。(
git commit
以后)
配置相关
git config -l
查看当前配置
git config -l --global
查看全局配置
当不想使用global里面的user信息时,就可以添加项目用户
git config user.name "demo"
git config user.email "demo@demo.com"
通过cat .git/config
查看新的config信息
2. git add
blob对象
首先在命令行输入echo aaa > 1.txt
产生一个文件
通过 git add 1.txt
将 1.txt 添加到索引区。可以通过git ls-files
查看当前索引区的内容
查看
.git/objects
里面的内容
我们会发现
.git/objects
多了一个文件,我们可以通过下面的命令查看该文件的一些信息
git cat-file -p 72943a # 查看文件的具体内容
git cat-file -t 72943a # 查看文件的类型
git cat-file -s 72943a # 查看文件在磁盘上的大小
可以看出这个文件的类型时一个
blob
,里面的内容是aaa
,在磁盘上的大小是4kb
。其实这个blob
对象就是 1.txt
内容对应的二进制压缩文件。这个文件的文件名就是一个sha1
值
sha1 值
通过git hash-object
查看一个内容的对应的sha1
值
echo aaa | git hash-object -w --stdin
生成hash值以后,写入到标准输入
这个sha1其实是将内容按照格式
blob 字符长度\0内容
最终转换而成的
echo -e "blob 4\0aaa" | shasum
3. git commit
执行 git commit -m "first commit"
产生一次提交。然后查看.git/objects
会发现里面多了2个文件。分别查看这两个文件的类型和内容
de73fa6a
类型:commit
详情:设置的提交者信息以及提交时的备注信息
dff4011
类型:tree
详情:包含文件名称与blob
对象的映射
此时形成的关系为
接下来添加几个特殊的文件
- 一个空文件夹
- 一个文件夹包含一个文件
- 一个文件夹包含多个文件
依次执行下面的命令
mkdir file1 file2 file3
echo bbb > file2/2-1.txt
echo ccc > file3/3-1.txt
echo ddd > file3/3-2.txt
此时的目录结构是
此时完成完成git add
,git commit
空文件夹并不会形成记录,查看 452cd1a
的内容
可以发现有一个
parent
属性指向上一次提交的commitId。此时形成的关系是
commit
通过parent
就能形成一条链表。文件夹对应的就是一个tree
对象。通过树结构,就能还原每次commit
对应的所有的文件。
此时,查看HEAD
相关的内容
可以发现
HEAD
指向master
的452cd1
4. 分支和tag
执行下面的命令创建一个dev
分支和tag
git checkout -b dev
git tag -a v1.0.0 -m "test tag"
可以发现 refs
里面添加了tag
和branch
信息,查看tag
号 v1.0.0
的相关信息
tag
的类型就是tag
tag
对应的是一个固定的commitId
,branch
对应的是变化的commitId
。tag和branch都可以理解为一个指针。
当删除
tag
和branch
时,并不会删除该指针特有的blob
对象。
5. 文件解压缩、垃圾文件
文件压缩
当版本库中有太多的松散对象,或者你手动执行 git gc
命令,或者你向远程服务器执行推送时,Git 都会进行压缩操作。
- 查看压缩文件情况
git verify-pack -v .git/objects/pack/pack-xxx.idx
文件解压
- 解压文件
当执行解压操作的时候要把pack文件移动到其他地方
mv .git/objects/pack/pack-xxx.pack .git/
解压命令(注意:这儿是<)
git unpack-objects < .git/pack-xxx.pack
解压的文件会被放到 objects
里面
git add 产生的垃圾文件
大多数情况下,执行git gc
通常会去执行git prune
git prune
删除没有被引用的对象
git prune -n
查看执行git prune将被删除的对象
git fsck --unreachable
查看没有被引用的对象
在master上面进行如下操作
echo bbb > 2.txt
git add -A
rm -rf 2.txt
echo ccc 3.txt
git add -A
rm -rf 3.txt
这样就会产生2条blob
类型的垃圾对象
可以发现通过执行git gc
并没有帮我们移除垃圾对象。此时就需要手动执行git prune
删除分支产生的垃圾文件
当我们在新分支创建了新内容,并产生了很多该分支单独拥有的对象。然后发现该分支没有用时,删了了该分支,此时就会产生很多垃圾对象。
我们进行如下操作
git checkout -b temp
echo "temp">temp.txt
git add temp.txt
git commit -m "添加temp.txt"
git checkout master
git branch -D temp
此时我们如果需要删除temp
分支所产生的一些提交信息,就可以执行下面的命令
git prune
无效,使用git gc
时,会将这些文件正常进行压缩(这些文件并不会被识别成unreachable)
git -c gc.reflogExpire=0 -c gc.reflogExpireUnreachable=0 \
-c gc.rerereresolved=0 -c gc.rerereunresolved=0 \
-c gc.pruneExpire=now gc "$@"
6. fast-forward、git merge、git rebase
fast-forward
当分支master
是分支dev
的祖先节点时,切换到分支master
,执行 git merge dev
,此时就会发生fast-forward
,分支master
会直接指向dev
所在的位置
fast forward
以后会产生一个OARIG_HEAD
文件,里面的内容是原分支以前指向的commit
。(可以通过git reset OARIG_HEAD
回滚到合并前的状态 )
git rebase
在dev
上运行 git rebase master
,其实就是把dev
的操作在master
上面执行一次,最终 dev
分出来的第一次commit
的parent
会指向master
的最终的指向。注意master
的commit
不会改变,但是dev
上面新的commitId
都会相应发生改变。
此时就满足了fast forward
的条件,切换到master
,执行 git merge dev
,此时就是 fast forward merge
了。此时如果还想找到F对应的 commitId
,可以使用git reflog
查看操作日志.
git rebase
会隐藏掉合并信息,所以在多人协作的时候尽量不要使用,在单人开发时为了分支的干净可以进行使用。
7. 远程仓库
远程仓库就是一个.git文件
创建一个远程仓库
git init --bare .
至此,我们生成了一个远程仓库地址,它的 SSH 地址是:
// 前面的git表示用户是git
git@x.xxx.xxx.xxx:/home/www/website/myBlog.git
8. git fetch、git pull
git fetch
通过git fetch
同步远程仓库信息以后,远程信息会保存到 refs/remotes/origin
下面。注意如果是通过git clone
的项目,默认的远程分支会以packed-refs
的形式存在,通过删除该文件,然后再执行 git fetch
,这样远程分支信息就会同步进refs/remotes/origin
- 执行完
git fetch
以后会增加.git/FETCH_HEAD
文件,同时当前分支对应的远程分支的commit
对象对应的sha1
值会排在FETCH_HEAD
文件的第一行
cat .git/FETCH_HEAD
fd2a2d7dae0176f08507623dbae2e661d1e39d9d branch 'master' of gitee.com:feiying-tf/git-test
fd2a2d7dae0176f08507623dbae2e661d1e39d9d not-for-merge branch 'dev' of gitee.com:feiying-tf/git-test
当使用git pull
的时候,首先会执行 git fetch
,然后使用 git merge
合并第一行对应的分支。
git pull
会产生 .git/ORIG_HEAD
,通过 ORIG_HEAD
可以回滚到合并前的状态
- 注意只有
master
默认与远程的master
进行过关联,其他分支都必须与远程仓库进行关联,比如:
- 在推送的时候使用
git push origin test -u
- 使用
git branch --set-upstream-to=origin/dev dev
然后就可以在test
分支上面直接运行git pull
或者git push
,。
- 我们可以在
.git/config
中查看与远程产生关联的分支。
9. git worktree
- 目的:在本地进行多个分支工作
git worktree list
查看工作树列表- 使用:
git worktree add ../wrapper master
在上一级目录下创建wapper
目录,检出master
- 处理完毕以后删除多余的工作树
git worktree remove wrapper # 删除wrapper这个工作树
10. git 切换分支,将当前分支的修改带到另一个分支的情况
如果有被重写的内容会被提示
提示产生的条件:
dev
修改一个master
也有的文件,并且dev
与master
必须没有处于同一条提交链上。