git 学习总结

108 阅读8分钟

1. git基本介绍

认识 .git 文件

  1. 创建一个新的git仓库(git-demo),观察 .git 文件的基本组成
  • HEAD
    向当前正在工作的分支,是通过ref指向的一个引用
cat .git/HEAD

ref: refs/heads/master

表示当前分支为master

  • config
    保存了本地仓库的配置信息,输入cat .git/config查看config里面的内容 image.png

  • 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
    表示一系列的引用,包含headsremotetags等目录;在刚初始化的项目里面只包含headstags。如果是一个全新的空仓库,这两个目录都将是空的

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查看当前索引区的内容 image.png 查看.git/objects里面的内容 image.png 我们会发现 .git/objects 多了一个文件,我们可以通过下面的命令查看该文件的一些信息

git cat-file -p 72943a # 查看文件的具体内容
git cat-file -t 72943a # 查看文件的类型
git cat-file -s 72943a # 查看文件在磁盘上的大小

image.png 可以看出这个文件的类型时一个blob,里面的内容是aaa,在磁盘上的大小是4kb。其实这个blob对象就是 1.txt 内容对应的二进制压缩文件。这个文件的文件名就是一个sha1

sha1 值

通过git hash-object查看一个内容的对应的sha1

echo aaa | git hash-object -w --stdin

生成hash值以后,写入到标准输入

image.png 这个sha1其实是将内容按照格式 blob 字符长度\0内容 最终转换而成的

echo -e "blob 4\0aaa" | shasum

image.png

3. git commit

执行 git commit -m "first commit"产生一次提交。然后查看.git/objects image.png 会发现里面多了2个文件。分别查看这两个文件的类型和内容 image.png image.png

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 的内容

image.png 可以发现有一个parent属性指向上一次提交的commitId。此时形成的关系是 image.png commit通过parent就能形成一条链表。文件夹对应的就是一个tree对象。通过树结构,就能还原每次commit对应的所有的文件。

此时,查看HEAD相关的内容 image.png 可以发现HEAD指向master452cd1

4. 分支和tag

执行下面的命令创建一个dev分支和tag

git checkout -b dev
git tag -a v1.0.0 -m "test tag"

可以发现 refs里面添加了tagbranch信息,查看tagv1.0.0 的相关信息 image.png tag的类型就是tag
tag对应的是一个固定的commitIdbranch对应的是变化的commitId。tag和branch都可以理解为一个指针。

当删除tagbranch时,并不会删除该指针特有的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 分出来的第一次commitparent会指向master的最终的指向。注意mastercommit不会改变,但是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

  1. 执行完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 可以回滚到合并前的状态

  1. 注意只有master默认与远程的master进行过关联,其他分支都必须与远程仓库进行关联,比如:
  • 在推送的时候使用git push origin test -u
  • 使用git branch --set-upstream-to=origin/dev dev

然后就可以在test分支上面直接运行git pull或者git push,。

  1. 我们可以在.git/config中查看与远程产生关联的分支。

9. git worktree

  1. 目的:在本地进行多个分支工作
  2. git worktree list 查看工作树列表
  3. 使用: git worktree add ../wrapper master 在上一级目录下创建wapper目录,检出master
  4. 处理完毕以后删除多余的工作树
git worktree remove wrapper # 删除wrapper这个工作树

10. git 切换分支,将当前分支的修改带到另一个分支的情况

如果有被重写的内容会被提示
提示产生的条件: dev修改一个master也有的文件,并且devmaster必须没有处于同一条提交链上。

image.png