git
git init: 当执行git init的时候,会初始化代码仓库,在当前目录下面出现一个.git目录 .git ├── HEAD ├── config ├── description ├── hooks ├── info │?? └── exclude ├── objects │?? ├── info │?? └── pack └── refs ├── heads └── tags 8 directories, 17 files
1. config:
是仓库的本地配置文件,对应的还有一个全局的配置文件~/.gitconfig
同时存在,本地的优先生效,如果没有配置本地,则使用全局配置
全局配置:git config --global -l 会打印出global的配置
本地配置:git config -l 打印出local的配置
配置:git config user.name ”shijiapeng“
git config user.email "shijiapeng@baidu.com"
git add: 可以创建一个文件,写点东西,这里创建了一个hello.txt, 内容是hello world git add之前目录不会发生变化: 是因为在代码仓库中进行修改,默认是在工作区中的,工作区中的内容并不归git管理,但是git能够识别出来文件是在工作区中(Untracked file) 如果想让git管理,通过git add添加到缓存区中 git add 之后目录发生的变化: .git ├── HEAD ├── config ├── description ├── hooks ├── index ├── info │?? └── exclude ├── objects │?? ├── 3b │?? │?? └── 18e512dba79e4c8300dd08aeb37f8e728b8dad │?? ├── info │?? └── pack └── refs ├── heads └── tags
9 directories, 19 files
通过上述对比:发现,多了一个目录和两个文件:index, 3b(目录) 18e512dba79e4c8300dd08aeb37f8e728b8dad
objects:
git objects用来干什么?
git objects最主要的有三种
先看第一种:blob
git cat-file :
查看objects文件(这个文件有一连串的字符,比如3d, 18e,由sha1哈希算法生成,可以把文件的内容或者字符串变成一串加密的字符)
1. 查看这个objects类型:git cat-file -t 3b18e5 -> blob
blob:在git对象中是用来存储文件内容的
2. 看这个objects的内容是什么:git cat file -p 3b1825 -> hello world
3. 看这个对象的大小是多少:git cat file -s 3b1825 -> 12 文件字符个数 + 1个换行符
通过git add把一个文件添加到缓存区中,实际上会生成git的对象objects,它存储的是文件的内容,文件的大小,对象的类型,不存储文件名, 将文件的内容、文件的大小和文件的类型通过sha1哈希算法,来生成字符串3b18e5.....当做是文件名,文件的内容是:”blob 12\0 hello world“通过压缩之后的内容
那么文件名的信息去哪里了?
工作区和缓存区(索引区): 文件名的信息存储在index文件中 查看index文件内容: git ls-files:把当前在缓存区中的文件给列出来 git ls-files -s:将文件的其他信息也列出来:文件的权限,文件所对应的blob对象, ?, 文件名 索引区:连接工作区和代码仓库,为了代码仓库去做准备,index保存了待提交的当前的状态 Untracked files: 表示没有提交到缓存区中的新创建的文件 Changes to be commited: 表示缓存区中的文件 Changes not staged for commit: 表示修改了索引区中的文件,还没有提交到索引区
当我们先创建了一个文件并且提交了索引区,此时发生的变化:
1. 生成一个index文件,通过git ls-files -s 查看文件信息,里面存储的是待提交的文件当前的状态
2. 在.git/objects下生成了一个以文件内容 + 文件大小 + 文件类型通过hash算法生成一连串字符串为命名的文件
当我们修改了一个已经提交到索引区的文件,此时发生的变化:
1, 更新index待提交的文件的状态,会更新这个文件所对应的blog对象,也就是那一串hash值
2, 在.git/objects下重新生成了一个以文件内容 + 文件大小 + 文件类型通过hash算法生成一连串字符串为命名的文件,之前的保留
.git
├── HEAD
├── config
├── description
├── hooks
├── index
├── info
│?? └── exclude
├── objects
│?? ├── 3b
│?? │?? └── 18e512dba79e4c8300dd08aeb37f8e728b8dad(hello.txt)
│?? ├── a2
│?? │?? └── 34dcc620326f6fbd01dc04abdcd70b454a6af3(原来hello2.txt)
│?? ├── d0
│?? │?? └── 2b296954690b63febc7f8869cedcf72e6ab16f(新的hello2.txt)
│?? ├── info
│?? └── pack
└── refs
├── heads
└── tags
11 directories, 21 files
git commit: git commit 之后的结构发生的变化
├── COMMIT_EDITMSG
├── HEAD(是一个指针,永远指向当前工作的分支)
├── config
├── description
├── hooks
├── index
├── info
│?? └── exclude
├── logs
│?? ├── HEAD
│?? └── refs
│?? └── heads
│?? └── master
├── objects
│?? ├── 19
│?? │?? └── d20615f940b4ca6c24ed262c7aee5a5ca5e13b(新的, tree对象)
│?? ├── 3b
│?? │?? └── 18e512dba79e4c8300dd08aeb37f8e728b8dad
│?? ├── a2
│?? │?? └── 34dcc620326f6fbd01dc04abdcd70b454a6af3
│?? ├── d0
│?? │?? └── 2b296954690b63febc7f8869cedcf72e6ab16f
│?? ├── f0
│?? │?? └── c347606fdaa6a9c4c0302b9d9e8664c6410f7b(新的, commit对象)
│?? ├── info
│?? └── pack
└── refs
├── heads
│?? └── master(当前master分支最新的commit是这个master内容f0c347606fdaa6a9c4c0302b9d9e8664c6410f7b,commit对象)
└── tags
16 directories, 27 files
通过 git cat-file -t foc347:来查看这个对象的类型,发现他是一个commit
这是git objects的第二种类型
通过-p 来查看这个commit对象的内容:
tree 19d20615f940b4ca6c24ed262c7aee5a5ca5e13b
author shijiapeng <shijiapeng@baidu.com> 1625923968 +0800
committer shijiapeng <shijiapeng@baidu.com> 1625923968 +0800
1st commit
git objects的第三种类型:tree,后面是它的文件的名字
作者的信息以及邮件的地址,时间戳
以上就是一个commit对象包含的内容
通过-p观察一下tree对象包含的内容:
100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad hello.txt
100644 blob d02b296954690b63febc7f8869cedcf72e6ab16f hello2.txt
类似于一个目录的作用,包括这次提交的文件的状态
总结:一次commit,产生了两个对象,一个commit对象,一个tree对象
这里将hello2.txt再次改变
├── COMMIT_EDITMSG
├── HEAD
├── config
├── description
├── hooks
├── index
├── info
│?? └── exclude
├── logs
│?? ├── HEAD
│?? └── refs
│?? └── heads
│?? └── master
├── objects
│?? ├── 19
│?? │?? └── d20615f940b4ca6c24ed262c7aee5a5ca5e13b
│?? ├── 3b
│?? │?? └── 18e512dba79e4c8300dd08aeb37f8e728b8dad
│?? ├── 9f
│?? │?? └── 345dcadd7545224978de6e244d1d486f74e672(新的hello2.txt)
│?? ├── a2
│?? │?? └── 34dcc620326f6fbd01dc04abdcd70b454a6af3
│?? ├── d0
│?? │?? └── 2b296954690b63febc7f8869cedcf72e6ab16f
│?? ├── f0
│?? │?? └── c347606fdaa6a9c4c0302b9d9e8664c6410f7b
│?? ├── info
│?? └── pack
└── refs
├── heads
│?? └── master
└── tags
第二次commit之后,tree的变化:
├── objects
│?? ├── 02
│?? │?? └── cc618005ead9df31a5474ae72dedc289b31adf(新的commit对象)
│?? ├── 19
│?? │?? └── d20615f940b4ca6c24ed262c7aee5a5ca5e13b
│?? ├── 3b
│?? │?? └── 18e512dba79e4c8300dd08aeb37f8e728b8dad
│?? ├── 49
│?? │?? └── c1294c2a600622b6c5cb9969b93053227e3cb7(新的tree对象)
│?? ├── 9f
│?? │?? └── 345dcadd7545224978de6e244d1d486f74e672
│?? ├── a2
│?? │?? └── 34dcc620326f6fbd01dc04abdcd70b454a6af3
│?? ├── d0
│?? │?? └── 2b296954690b63febc7f8869cedcf72e6ab16f
│?? ├── f0
│?? │?? └── c347606fdaa6a9c4c0302b9d9e8664c6410f7b
│?? ├── info
│?? └── pack
新的commit对象内容:
tree 49c1294c2a600622b6c5cb9969b93053227e3cb7
parent f0c347606fdaa6a9c4c0302b9d9e8664c6410f7b
author shijiapeng <shijiapeng@baidu.com> 1625925573 +0800
committer shijiapeng <shijiapeng@baidu.com> 1625925573 +0800
2nd commit
添加文件对于git objects的测试
在工作区中,一个空文件,git认为是不算是改变的,必须在空文件里面添加内容, git add之后
├── objects
│?? ├── 02
│?? │?? └── cc618005ead9df31a5474ae72dedc289b31adf
│?? ├── 19
│?? │?? └── d20615f940b4ca6c24ed262c7aee5a5ca5e13b
│?? ├── 3b
│?? │?? └── 18e512dba79e4c8300dd08aeb37f8e728b8dad
│?? ├── 49
│?? │?? └── c1294c2a600622b6c5cb9969b93053227e3cb7
│?? ├── 9f
│?? │?? └── 345dcadd7545224978de6e244d1d486f74e672
│?? ├── a2
│?? │?? └── 34dcc620326f6fbd01dc04abdcd70b454a6af3
│?? ├── d0
│?? │?? └── 2b296954690b63febc7f8869cedcf72e6ab16f
│?? ├── e3
│?? │?? └── 69d65e71acc22096ffa85dede56d74147e0295(新的)
│?? ├── f0
│?? │?? └── c347606fdaa6a9c4c0302b9d9e8664c6410f7b
│?? ├── info
│?? └── pack
也就是说目录不算一个对象
git commit之后
├── objects
│?? ├── 02
│?? │?? └── cc618005ead9df31a5474ae72dedc289b31adf
│?? ├── 19
│?? │?? └── d20615f940b4ca6c24ed262c7aee5a5ca5e13b
│?? ├── 3b
│?? │?? └── 18e512dba79e4c8300dd08aeb37f8e728b8dad
│?? ├── 49
│?? │?? └── c1294c2a600622b6c5cb9969b93053227e3cb7
│?? ├── 9f
│?? │?? └── 345dcadd7545224978de6e244d1d486f74e672
│?? ├── a2
│?? │?? └── 34dcc620326f6fbd01dc04abdcd70b454a6af3
│?? ├── bc
│?? │?? └── ff05e62dd58edbd7395e12a8812f66eda63d00(tree:里面包含一个tree)
│?? ├── d0
│?? │?? └── 2b296954690b63febc7f8869cedcf72e6ab16f
│?? ├── e3
│?? │?? └── 69d65e71acc22096ffa85dede56d74147e0295
│?? ├── f0
│?? │?? ├── 5bff8e42b68e6afb67bd0c1a340490864b0b54(commit对象)
│?? │?? └── c347606fdaa6a9c4c0302b9d9e8664c6410f7b
│?? ├── f2
│?? │?? └── 5733bf4595c604f4096012e095c8ae7f02cf8c(tree对象)
│?? ├── info
│?? └── pack
一个comiit包含一个tree,这个tree里面又包含一个tree和两个文件
git文件状态: Untracked:在工作区新创建一个文件的时候,Untracked -> Staged:git add Modified:文件已经在Staged中,又对这个文件做了一个修改 Modified -> Staged:git add Staged:在暂存区中的文件 Unmodified:在仓库中的文件:Staged -> Unmodified:git commit
反向切换:
总结:每次git add 的时候 会在.git/objects文件中生成一个blob对象,如果对这个文件修改,会重新生成一个新的blob对象,文件原来的对象保留
其实这个对象以文件的形式存在,可以通过 git cat-file来查看这个文件,-s:查看这个文件的内容,-s:查看这个文件大小,-t:查看这个文件的类型
在git add之后,index文件中保存的是暂存区中文件状态信息,包括文件名字,文件的权限,文件对应的对象名字,可以通过git ls-files来查看index
-s:可以把上述信息都给显示出来
git commit的时候,会在.git/objects文件中生成两个对象,commit对象和tree对象,commit对象的内容包括,它的父commit对象(也就是上次提交的commit对象),它还包含一个tree对象,其他的就是提交者的信息,tree对象的内容包括:这次提交的文件的状态(和index文件中的内容是一致的)
对于文件夹的操作:
在git中空文件夹是不算改变的,文件夹中必须有内容
example:新创建一个文件夹folder,git status是看不到任何改变,接着在folder下创建一个a.txt,再次git status会发现发生了改变,git add之后发现objcts下只生成了一个对象,这说明文件夹是不算对象的,但是会在index中记录这个a.txt所在的路径
git commit的时候:会生成三个对象,一个commit, 两个tree,一个tree里面包含另一个tree,另一个tree的内容是a.txt,也就是当创建目录,在目录下对文件进行修改的时候,会通过生成不同的tree对象来记录文件的层次关系,也就是通过tree来表示文件夹层次关系
Branch: 分支是一个有名字的指针,指向一个commit 分支指向的commit存储在哪里? 怎么知道当前在那个分支?.git/HEAD 它是一个指针,总是指向当前工作的分支,并且指向当前工作分支最新的一次commit 实现是这样的:HEAD里面存储的是当前指向那个分支(存储的是这个分支的路径),然后可以查看这个分支的文件,这个分支文件里面存储的sha1计算出来的字符串那,是一个commit对象,就表示这个分支指向的最新一次的commit
git log 可以看到提交的历史
分支操作:
git branch:当前有哪些分支
git branch <branch_name> 创建分支,基于当前分支
git branch -D <branch_name> 强制删除分支
git checkout 切换分支
当我们删除分支后,这个分支产生的特有的对象依旧可以访问到(也就是当前分支上没有文件),这个对象算是垃圾对象,就像是多次git add 已经提交的文件,之前的产生的对象就变成垃圾对象
git branch 创建一个分支,创建分支的背后发生了什么呢?
objects/下没有任何变化,因为只是创建了一个指针,这个指针的信息保存在:.git/refs/head/下,创建一个分支名为名字的文件
git checkout 切换分支,切换分支实质:就是HEAD内容发生了改变
git branch -D 在当前分支上,无法删除当前分支,-D:是强制删除,--delete:不会强制删除,会提示错误信息,只是删除某个指向特定commit的指针而已,但是这个commit本身并没有别删除
git checkout 某一个指定的commit: git checkout -b 分支名:创建并切换到这个新创建的分支上 git switch -C 分支名: 前面说到,git中,是HEAD指向分支名的名字,分支指向commit对象,”git checkout 某一个指定的commit“ 这个意思是说让我们的HEAD直接指向一个commit对象,这个状态叫做”分离头指针“,它的一个使用场景是什么呢? 场景:比如我删除了某个分支,(注意:删除的本质只不过是删除了这个分支的名字,并没有删除这个分支commit 对象),但是我可能是误删除,想要找到这个分支并恢复这个分支,那么应该怎么做呢? 首先通过 git reflog找到这个commit对象的sha1字符串, 然后git checkout 这个sha1字符串,此时会出现上述的分离头指针的状态,但是当切换一个已有的分支的时候会丢弃这个状态,想要保存,可以通过新创建并切换到这个分支上的操作,就相当于现在是一个匿名的分支,需要为它起一个名字,可以通过:最开始的两种方法
git diff: shijiapeng@B000000346147t git-demo % git diff diff --git a/test.txt b/test.txt index 9daeafb…9276bb4 100644 — a/test.txt +++ b/test.txt @@ -1 +1,3 @@ test + +haha
分析上述信息:
a:代表索引区工作内容,b:代表工作区的工作内容
diff --git a/test.txt b/test.txt
index 9daeafb..9276bb4 100644 (9daeafb:索引区中有效的blob对象,9276bb4:工作区中的blob对象,无效 )
--- a/test.txt(--:代表索引区)
+++ b/test.txt (++:代表工作区)
@@ -1 +1,3 @@ (通过@@作为块的划分,来分割一块一块的不同)(-1:在索引区文件的第四行,+1,3表示:会显示工作区第一行,往后数3行的内容)
test
+
git diff 默认比较的是索引区和工作区的不同
git diff --cached 比较的是索引区和代码仓库的不同
远程仓库: 将本地仓库导入到远程仓库: 1. 添加远程库:git remote add origin 远程仓库地址url 发生的变化: 修改了.git/config文件,配置了一个 remote origin,内容是url 和 fetch 2. 将本地仓库内容添加到远程仓库中去:git push -u origin master: 把本地仓库push到远程origin仓库的master分支 做的事情:将原来的objects对象进行了压缩,并写到远程仓库上,其次就是在远程仓库上创建了一个master分支 发生的变化:多了4个目录,2个文件 res/remotes/origin/master log/remotes/origin/master master文件的内容:sha1哈希值,类型是一个commit对象,和当前的本地master指向同一个对象
远程仓库存储:git remote add origin
git对象压缩: 场景:比如我本地有一个文件10MB, 当我进行一次git add的时候,会在.git/objects下生成一个经过压缩的git对象,这个git对象的内容就是本地文件的内容经过压缩之后的内容,比如是7MB,如果对这个文件进行轻微的删除,第二次进行 git add,那么在objects下还会生成一个7MB的git对象,第三次,第四次git add都会产生同样的效果,长远来看,我本地才占10MB,如果我要有很多次提交,那么占用的空间会非常的大
git gc:
效果,.git/objects/pack
git merge: fast forward: 场景:在当前master分支上,chekout出了一个bugfix分支,并且进行了commit操作,master分支一直没动,还是当初checkout出bugfix时的分支,然后在master分支上,执行了git merge bugfix的操作,实际上是将master分支的HEAD指针,移动到了bugfix(前天条件是:master没有移动) 在merge之后,在.git下会出现一个ORIG_HEAD文件,这个文件存储是merge之前的当前分支的commit,方便回滚 3 way merge: 场景:基于master分支,checkout出了一个bugfix分支,并且这个bugfix分支进行了commit操作,然后master分支也进行了commit操作,在master执行git merge bugfix,会生成一个新的commit对象,这个commit对象的parent有两个,分别是生成此对象master和bugfix的最新的commit(前提是master移动了) 3 way mrege带冲突: 场景,基于3 way merge的场景下,bugfix和commit都修改了同一个文件,然后在master执行git merge bugfix会产生冲突,此时如果git ls-files -s 会发现在索引区会出现三个文件,我们解决完冲突之后 直接git add, 然后查看git ls-files -s ,就会只有一个,然后直接git commit 就可以了,不用加-m, 也会生成一个新的commit对象,有两个parent,同上 git rebase: 场景:比如master分支和bugfix分支,已经出现分叉了,不想3 way merge,想要fast forwoard合并,那么可以进行rebase 首先,fast forward的前提是:master基于bugfix分支没有动过,换句话说,bugfix分支上含有master分支的最新一次提交 rebase的目的是就是想办法让没有master的最新一次commit的bugfix分支(出现分叉了),重新拥有master的最新一次提交 在bugfix上,执行git rebase master, 存在的问题,会重写commit,如果已经push过commit了,在rebase之后,再次push就会出错 如果别人拉了你的代码,然后你进行了rebase,并且强制push了,那么会对别人产生影响,所以如果分支有多人共同使用,尽量不要rebase 不要在master上rebase,在自己分支上rebase
tag:
本地分支和远程分支: git branch -r 查看远程分支 git fetch 检查本地分支对应的的远程仓库的分支的情况,并且同步远程仓库,也可以拉去本地没有的远程分支,如果远程分支被删除,那么不会删除本地的远程分支 git fetch --prune: 会对远程仓库没有,但是本地有的分支,进行删除 git remote: 显示远程仓库的名字 git remote -v 显示push或fetch的url,通过这个url来进行fetch或push git remote show 远程仓库的名字 去检查远程仓库分支和本地分支的关联的情况
可以通过git fetch来更新本地的远程分支,但是如果想要更新本地分支,需要git merge origin/远程分支
git branch -vv 查看本地分支是否和本地的远程分支关联,以及如果关联,版本之间差异
如果和关联本地分支和本地的远程分支?
首先通过git fetch,然后git checkout 本地分支名
git fetch & git pull:
FETCH HEAD: 当我们执行git fetch的时候,.git,目录下会出现FETCH_HEAD文件,这个文件中记录了所有远程分支的最新一次的commit的对象,他们的顺序会发生变化 在本地分支和远程分支关联的情况下,如果切换分支后,并且执行git fetch,再次查看.git/FETCH_HEAD会发现远程分支的顺序会发生变化,第一行就是我们当前分支所关联的远程分支,只要执行git fetch就会发生这样的变化 git pull = git fetch + git merge: 会首先将进行git fetch, 更新所有的远程分支到本地,也会更新FETCH_HEAD的最新commit,以及不同分支的位置,然后会git merge FETCH_HEAD中第一个位置的commit
git push: 将本地分支推送到远程仓库,如果是第一次推送到远程仓库,使用:git push origin dev,第一次推送,远程仓库中没有这个分支,那么会在远程仓库中新建一个分支,加上git push -u参数,可以让建立本地分支和新建的远程分支的连接,这样再下次推送远程分支的时候,直接git push就可以了 git push origin -d dev,可以删除远程分支
git branch -vv: 可以查看本地分支和远程分支的追踪情况
.git/hook:
git work tree: 本地有两个分支master 和 bugfix,我在bugfix分支上进行开发,此时需要切换到master上进行操作,但是bugfix上有一些更改的文件还没保存,如果直接checkout master,会把对文件的新的修改,带到master,但是我们不想带到master,有三种办法: 1. 在bugfix分支上进行一次commit 2. 在bugfix分支上进行一次git stash操作,然后master分支上完事之后,切换到bugfix分支上,执行git stash pop操作 3. 在bugfix分支上执行git worktree add ./branch-master master, 这条命令会将master分支保存到当前目录的branch-master目录里面,我们进行cd branch-master就切换到了master分支,执行完操作之后,可以进入到我们bugfix分支对应的目录,通过git worktree list查看,然后通过git worktree remove branch-master 删除这个目录,可以看到我们不用对本地修改的文件做任何的更改,就可以完成其他分支的开发
|