一、文件,指令讲解
首先讲一下远程仓库和本地仓库在文件上面的区别,首先我们来看下对比图(当然这里说的区别是在于.git文件下面的文件内容,至于里面内容我们不会关注)這裡我们进行了相同的操作就是本地仓库里面新建了version.txt内容也是一样的v1.0:
图一
图二
图一为本地仓库,图二为克隆的远程仓库,首先我们应该看一下config里面的区别:
图三
图四
很容易看到了区别就是图四为远程仓库内容。
从这个文件中我们可以了解到:
1,本地库的当前分支为master,其关联的远程库名称为origin(不同的名称可以指向同一个远程库,参见git remote命令)
2,远程库origin所在的位置为(URL):https://github.com/dwlsxj/BattleHeart.git
然后进行了version.txt的编辑和提交。这是我们刚才新建version.txt(远程)以及再添加内容为“v1.0”,在添加到暂存区,提交到本地提交中,最后提交到远程服务器,下面是我们生成的commit对象和git对象还有就是tree对象内容:
$ find .git/objects/ -type f .git/objects/18/6c4629f6de8c956548570a113fa181d9b22692 commit对象 .git/objects/90/e34626d7993cf8d6c5e3e458744df4a9a31594 tree对象 .git/objects/c6/94c68edd99aeefa053f25308cd71ecca922036 blob对象
接下来我们就来看一下这个commit对象和tree对象下面的树分支和commit的指向内容。
$ git cat-file -t 186c46 commit $ git cat-file -t 90e346 tree $ git cat-file -t c694c6 blob $ git cat-file -p 186c46 tree 90e34626d7993cf8d6c5e3e458744df4a9a31594 author BattleHeart <[email protected]126.com> 1452303321 +0800 committer BattleHeart <[email protected]126.com> 1452303321 +0800 inital commit $ git cat-file -p 90e346 100644 blob c694c68edd99aeefa053f25308cd71ecca922036 version.txt $ git cat-file -p c694c6 V1.0
当我们git push origin master(命令格式:git push [remote-name] [branch-name])时候会生成一个我们会发现.git/refs/文件夹下多了一个remote文件夹
我们跟进去看一下会有一个默认origin服务器名称,下面会有一个master分支,也就是这个代表的是远程服务器的master分支并不代表我们本地的master分支。看一下这个master分支内容和本地分支的内容是否一致,因为我们刚才将提交的内容提交到origin服务器上,应该两个分支引用指向的commit记录是一样的。
根据上面图中,标号1代表的是本地master分支指向的内容,而标号2代表的是远程服务器上面master分支只想的内容。(186c46正是我们的提交记录commit对象)
接下来我们的操作就是在进行添加一个本地内容修改下version.txt,修改内容为添加版本为v1.1。然后我们不提交到远程服务器上面看一下这两个master指向。当然不用猜就已经知道结果了,肯定指向的内容是不一样的。
修改文件后生成的blob对象
$ find .git/objects/ -type f .git/objects/18/6c4629f6de8c956548570a113fa181d9b22692 .git/objects/90/e34626d7993cf8d6c5e3e458744df4a9a31594 .git/objects/9f/aadaec6f7c8657372544e6f414f9b0b6910279 .git/objects/c6/94c68edd99aeefa053f25308cd71ecca922036
提交到本地版本控制中。
$ git commit -m ‘second commit version.txt‘ [master 5777c82] second commit version.txt 1 file changed, 1 insertion(+)
提交之后产生的commit对象和新的tree对象内容
$ find .git/objects/ -type f .git/objects/18/6c4629f6de8c956548570a113fa181d9b22692 .git/objects/57/77c8270e24ff7417fa6f8d27f73c068acd42b0 .git/objects/90/e34626d7993cf8d6c5e3e458744df4a9a31594 .git/objects/9f/aadaec6f7c8657372544e6f414f9b0b6910279 .git/objects/a4/3a49679a6ecd3cbcdd889aeb9906b8620e7976 .git/objects/c6/94c68edd99aeefa053f25308cd71ecca922036
看一下了两个master分支内容:
$ cat .git/refs/heads/master 5777c8270e24ff7417fa6f8d27f73c068acd42b0 $ cat .git/refs/remotes/origin/master 186c4629f6de8c956548570a113fa181d9b22692
啊哈,果然不出我们所料,这就是说明本地提交的记录还没有提交的远程服务器上面所以说这个远程服务器的master分支还是指向了原来的commit对象。
当我们使用的了git fetch [remote-name]从服务器里面拉去数据的时候会产生一个FETCH_HEAD指针。这个命令会访问远程仓库,从中拉取所有你还没有的数据。 执行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。这时候我们会发现有一个FETCH_HEAD指针是一个指向远程最后一次获取的指针位置。首先我们看一下这个FETCH_HEAD指针的主要作用:
也就是说我们每次获取的本地没有的文件后都会有一个fetch_HEAD,这个指针和本地的HEAD指针有着区别,这样我们现在还看不出来因为本地和服务器上的记录都是一样的。这时候我们来在服务器上面添加一个readme文件提交上去,再在本地获取。
添加readme文件这里是在服务器上直接提交的记录,所以跟本地数据没有任何关系。
当我们使用git fetch origin命令的之后,我们来看一下.git/objects在服务器上产生的新的commit记录和tree对象还有git对象内容。
$ find .git/objects/ -type f .git/objects/18/6c4629f6de8c956548570a113fa181d9b22692 .git/objects/50/204399d2b99a020ac03dbca151570a959b7523 .git/objects/56/f565ecbaa66b236c42914bfd7e93a5eb0844f7 .git/objects/57/77c8270e24ff7417fa6f8d27f73c068acd42b0 .git/objects/90/e34626d7993cf8d6c5e3e458744df4a9a31594 .git/objects/9f/aadaec6f7c8657372544e6f414f9b0b6910279 .git/objects/a4/3a49679a6ecd3cbcdd889aeb9906b8620e7976 .git/objects/c6/94c68edd99aeefa053f25308cd71ecca922036 .git/objects/e0/26393257f8b7519ac7c378c6ff470ca737fe10
这里面有三个对象是git fetch后新增加的就是如下几个,然后我们通过查看其类型以及其中的内容就能看到确实是从服务器上拉取出来的数据。
$ git cat-file -t 502043 commit $ git cat-file -p 502043 tree e026393257f8b7519ac7c378c6ff470ca737fe10 parent 186c4629f6de8c956548570a113fa181d9b22692 author BattleHeart <[email protected]> 1452324317 +0800 committer BattleHeart <[email protected]> 1452324317 +0800 Create commit Readme Create commit Readme $ git cat-file -t 56f565 blob $ git cat-file -p 56f565 # BattleHeart README $ git cat-file -t e02639 tree $ git cat-file -p e02639 100644 blob 56f565ecbaa66b236c42914bfd7e93a5eb0844f7 README.md 100644 blob c694c68edd99aeefa053f25308cd71ecca922036 version.txt
其实这时候我们来看一下git的历史记录图。
这里的lol命令是我取的别名log –oneline –decorate –graph –all。可以看到这个命令分了两个,就是说origin/master是服务器的master分支。我们来看一下master分支。
$ cat .git/refs/heads/master 本地分支引用 5777c8270e24ff7417fa6f8d27f73c068acd42b0 $ cat .git/refs/remotes/origin/master 服务器master分支引用 50204399d2b99a020ac03dbca151570a959b7523
也就是说本地分支引用是没有变化的,变化的是服务器master分支引用的地址从186c46变化到了最新的502043这个commit提交记录上面了。其实这之后FETCH_HEAD也会将最新的commit记录在里面(这里不是指向master分支上而是指向了commit记录上面),也就是说FETCH_HEAD指的是: 某个branch在服务器上的最新状态。其中每一行对应于远程服务器的一个分支.当前分支指向FETCH_HEAD, 就是这个文件第一行对应的那个分支。这里服务器里面只有一个master分支是默认的分支。
虽然说拉去出来的数据但是并不是我们想要的,拉去的数据并没有放在本地仓库指向的内容里面而是服务器分支指向的内容里面。这时候我们是用git merge来进行合并。也就是说这个FETCH_HEAD里面存在的是服务器上每一个分支上面最后一个提交记录内容,当然我们也可以使用git merge FETCH_HEAD这个命令来合并分支,但是多条分支记录的时候他只会取出第一条merge记录。这时候我们就需要使用服务器的分支来进行合并了,比如origin/test指针就可以git merge origin/test。这与后面要讲的git pull的区别就是从服务器上面获取下来的内容分支之类的不会直接合并到本地,而是自己去合并但是git pull是git pull = git fetch + git merge的操作。
git pull命令:
接下来我们就来演示下git pull内容,首先我们在服务上面新建了一个空的仓库,把这个空的仓库拉到本地来,新建一个文件a,在文件a里面添加一条记录this is file a on local master,把他提交到本地仓库里面,我们看一下如下操作:
新建一个文件a并赋值。
$ echo ‘this is file a on local master‘ > a
看一下文件内容
$ cat a this is file a on local master
添加到暂存区内将添加的文件
$ git add .
将文件添加版本记录中去。
$ git commit -m ‘local first commit‘ [master (root-commit) 7358cc0] local first commit warning: LF will be replaced by CRLF in a. The file will have its original line endings in your working directory. 1 file changed, 1 insertion(+) create mode 100644 a
这时候看一下产生的对象
$ find .git/objects/ -type f .git/objects/34/da50374fa7eaa9d2ea609198237ed8af97c7e9 .git/objects/73/58cc0797e6bf72393ab54c543c6261784c27dc .git/objects/7e/8f2d0c90cdd5dffd9bfb5f32604f74199725e4
分别查看一下这些对象的类型以及对象的内容:
$ git cat-file -t 34da50 tree $ git cat-file -p 34da50 100644 blob 7e8f2d0c90cdd5dffd9bfb5f32604f74199725e4 a $ git cat-file -p 7e8f2d this is file a on local master $ git cat-file -p 7358cc tree 34da50374fa7eaa9d2ea609198237ed8af97c7e9 author BattleHeart <[email protected]126.com> 1452330539 +0800 committer BattleHeart <[email protected]126.com> 1452330539 +0800 local first commit
这时候我们将新的文件提交到服务器上面。
$ git push origin master Counting objects: 3, done. Writing objects: 100% (3/3), 235 bytes | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To https://github.com/dwlsxj/test.git * [new branch] master -> master
OK提交成功了,这时候.git/refs就会生成一个remote文件文件里面有默认服务器名称origin文件夹,该文件夹下面保存了服务器上最新的master分支指向的内容。让我们来看一下:
$ cat .git/refs/remotes/origin/master 服务器master分支 7358cc0797e6bf72393ab54c543c6261784c27dc $ cat .git/refs/heads/master 本地master分支 7358cc0797e6bf72393ab54c543c6261784c27dc
这时候是一样的,因为服务器分支和本地分支是一样的。接下来我们在服务器分支上面修改下a文件如下图所示:
这时候我们会想到服务器上面会产生commit记录这时候服务器的master分支的指向就不再是本地这个指向了。
这时候我们也来修改下本地的a文件。添加文本内容为this is file a on local master second edit。提交到版本控制中。看一下所有的操作内容:
编辑文件a
$ vim a
将a文件添加到暂存区内容
$ git add . warning: LF will be replaced by CRLF in a. The file will have its original line endings in your working directory.
看一下新生成的git对象内容
$ find .git/objects/ -type f .git/objects/34/da50374fa7eaa9d2ea609198237ed8af97c7e9 .git/objects/73/58cc0797e6bf72393ab54c543c6261784c27dc .git/objects/7e/8f2d0c90cdd5dffd9bfb5f32604f74199725e4 .git/objects/af/cc87af98402de78840dc0737700a62345abff4
将修改的a文件提交到记录里面。
$ git commit -m ‘local second commit on master‘ [master warning: LF will be replaced by CRLF in a. The file will have its original line endings in your working directory. de7c4cd] local second commit on master warning: LF will be replaced by CRLF in a. The file will have its original line endings in your working directory. 1 file changed, 1 insertion(+)
这时候我们来看一下提交记录的图,这时候HEAD指针是指向最新的master分支的,但是服务器的master分支则是指向原有的commit记录。
$ git lol * de7c4cd (HEAD -> master) local second commit on master * 7358cc0 (origin/master) local first commit
看一下产生的tree对象以及tree对象内容,还有commit对象。
$ find .git/objects/ -type f .git/objects/34/da50374fa7eaa9d2ea609198237ed8af97c7e9 .git/objects/66/e33f7b7a44ad93bcb3a049075e666b9041f63c .git/objects/73/58cc0797e6bf72393ab54c543c6261784c27dc .git/objects/7e/8f2d0c90cdd5dffd9bfb5f32604f74199725e4 .git/objects/af/cc87af98402de78840dc0737700a62345abff4 .git/objects/de/7c4cd49e32f197afc6161d0d6f5e6d413212c8 $ git cat-file -t 66e33f tree $ git cat-file -p 66e33f 100644 blob afcc87af98402de78840dc0737700a62345abff4 a $ git cat-file -p afcc87 this is file a on local master this is file a on local master second edit $ git cat-file -p de7c4c tree 66e33f7b7a44ad93bcb3a049075e666b9041f63c parent 7358cc0797e6bf72393ab54c543c6261784c27dc author BattleHeart <[email protected]126.com> 1452331483 +0800 committer BattleHeart <[email protected]126.com> 1452331483 +0800 local second commit on master
OK,到了这里我们就要开始见证奇迹的时候到了,因为我们同时修改了a文件都是从第一次提交的commit记录里面衍生出来的(服务器上和本地提交),接下来就是见证奇迹了。
$ git pull origin remote: Counting objects: 3, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 Unpacking objects: 100% (3/3), done. From https://github.com/dwlsxj/test 7358cc0..67f70c5 master -> origin/master Auto-merging a CONFLICT (content): Merge conflict in a Automatic merge failed; fix conflicts and then commit the result.
Pull之后我们先不着急看a文件的内容,我们先来看一下生成的对象内容。
$ find .git/objects/ -type f .git/objects/34/da50374fa7eaa9d2ea609198237ed8af97c7e9 .git/objects/59/7f7359a768d3e98b65cd8b1558e09489d920b3 .git/objects/66/e33f7b7a44ad93bcb3a049075e666b9041f63c .git/objects/67/f70c522e9e5b748445abbb41ea0cfa0fd171f9 .git/objects/73/58cc0797e6bf72393ab54c543c6261784c27dc .git/objects/7e/8f2d0c90cdd5dffd9bfb5f32604f74199725e4 .git/objects/af/cc87af98402de78840dc0737700a62345abff4 .git/objects/de/7c4cd49e32f197afc6161d0d6f5e6d413212c8 .git/objects/de/fb97746b25d3963454d4ff69fc903956b12075 .git/objects/f5/537ea43f681442d2656105a2f348284181ef95
接下来就要看一下新增加的对象内容,其实我们猜想就能才想到肯定有三个对象是git对象和commit对象、tree对象因为这是在服务器上提交记录产生的。那么还多一个对象是什么呢?我们来看一下吧。
[email protected] MINGW64 ~/Desktop/rrr/test (master|MERGING) $ git cat-file -t 597f73 该对象是服务器上提交内容对象 blob
对象的内容如下
[email protected] MINGW64 ~/Desktop/rrr/test (master|MERGING) $ git cat-file -p 597f73 this is file a on local master this is file a on server master
接下来67f70c是提交记录,是和上面blob一起的
[email protected] MINGW64 ~/Desktop/rrr/test (master|MERGING) $ git cat-file -t 67f70c commit [email protected]-20150201IC MINGW64 ~/Desktop/rrr/test (master|MERGING) $ git cat-file -p 67f70c tree f5537ea43f681442d2656105a2f348284181ef95 parent 7358cc0797e6bf72393ab54c543c6261784c27dc author BattleHeart <[email protected]> 1452330567 +0800 committer BattleHeart <[email protected]> 1452330567 +0800 this is file a on server master this is file a on server master
接下来是tree对象内容,都是服务器上第二次提交产生的。
[email protected] MINGW64 ~/Desktop/rrr/test (master|MERGING) $ git cat-file -t f5537ea tree $ git cat-file -p f5537ea 100644 blob 597f7359a768d3e98b65cd8b1558e09489d920b3 a $ git cat-file -t defb97 blob
哎呦,这个对象叼了,这个对象是在本地产生的,产生的冲突文件,我们会发现我没有将Administrator这一行去掉是因为我们现在文件是由冲突的,我们可以看到master|MERGING
[email protected] MINGW64 ~/Desktop/rrr/test (master|MERGING) $ git cat-file -p defb97 this is file a on local master <<<<<<< HEAD this is file a on local master second edit ======= this is file a on server master >>>>>>> 67f70c522e9e5b748445abbb41ea0cfa0fd171f9
那么下来我们看一下commit的图是如何的:
我们发现origin/master分支指向也更新为了最新的记录。并且现在还是两个分支,因为还有冲突没有解决。这时候我们需要解决冲突在提交就好了。
并将其提交到记录里面这时候我们就会发现最后的commit图就合并到了一起。
[email protected] MINGW64 ~/Desktop/rrr/test (master|MERGING) $ git add . $ git commit -m ‘merge‘ [master 0dd66df] merge [email protected]-20150201IC MINGW64 ~/Desktop/rrr/test (master) $ git lol * 0dd66df (HEAD -> master) merge | | * 67f70c5 (origin/master) this is file a on server master * | de7c4cd local second commit on master |/ * 7358cc0 local first commit
最后来一张图清晰地告诉大家git fetch和git push的区别。
二、结束语
本篇文章分析完毕,希望各位能够喜欢并且指正里面的错误,分析的有不正确的就指出来。谢谢啦。