文章目录
- 前言
- git和svn
- git最重要的是什么
- 总结
前言
工作中使用git从两年前开始,一直以前add -> commit ->push
常规操作,真正在工作中使用后才逐渐理解git强大,这种理解是建立在不断解决问题的基础上,不断处理遇到的问题,就像升级怪物一样,是的git理解也越来越全面。因为在使用git之前一直用svn作为版本控制工具,对git和svn差异也有自己的理解。网上搜索了很多关于两者区别的文章,我就不重复了。我只是从自己的理解来描述两者的区别。
git和svn
关于git和svn网上有很多不同的文章,大部分都会提到分布式、存储方式、版本号、完整性等。,而我今天要写的区别是两者提交记录的结构。
由于是版本控制工具,那么每一次历史提交都必须能够追溯和回归svn提交记录时线性的,以时间轴为参考基准,所有提交按时间顺序排列,因为svn必须将记录提交给服务器才能生效,所有服务器相当于每个服务器svn每个客户端的总控制svn提交到服务器时线性排列,在修改和提交之前,必须将本地文件状态更新为与服务器相同的状态。
正因为在svn服务器负责总控操作,所以最新的提交记录可以保证时间是整个svn在最新状态下,提交记录不依赖端时间,完全由服务器时间排序。
在git没有这样的总控服务器。虽然每个代码库通常都有统一的托管服务器,但它的作用是任何一个git可以替换客户端,因为git它可以离线提交。托管服务器只是我们存储代码的地方svn按时间排序服务器的做法大不相同。
git提交记录通常是树形结构,有时会变成起点和终点的网状结构git中间时间只有参考意义,不能决定提交记录的顺序。如果你仍然怀疑这一点,你可能是一个svn重度用户还没有理解git操作原理。
对于这个问题,可以举个例子,操作相同的文件svn中2月13日修改一次,2月14日修改一次,那么2月15日看这个文件一定是2月14日修改后的状态;而在git同样的文件在2月13日和2月14日修改一次,文件的状态取决于两次修改是否在同一分支,以及合并时如何处理,随着时间的延长和多分支的结合,这种错位往往对时间的依赖最小,此时提交的顺序无法用时间来衡量。
假如一开始就是git,上述问题并不明显,但已经习惯了svn再使用git,在处理历史可追溯性问题时,往往很容易找到错误的方向。经常通过时间过滤的内容不是你想要的,这在实践中需要注意。
git最重要的是什么
我相信每个人都有自己的答案。有些人认为它是分布式的,有些人认为切换分支非常方便,我的答案是 commit 的设计哲学
,我觉得这是git中的精髓,git中的commit就像链表中的元素一样,用来自己和其他元素commit串联到一起,形成branch
、tag
、HEAD
等等。
我们可以通过 git log
看一个命令 commit:
$ git log -1 commit 7bf665f125a4771db095c83a7ad6ed46692cd314 (HEAD -> 6.0, tag: 6.0.6, origin/6.0) Author: Oran Agra <oran@redislabs.com> Date: Sun Jul 19 14:00:20 2020 0300 Redis 6.0.6.
这条commit id 为 7bf665f125a4771db095c83a7ad6ed46692cd314
,这是整个库中唯一的,通过 git log
可以看到提交时间、作者、简要说明等信息,那么提交和库有什么关系呢?
通过括号中的内容,我们可以知道当前的提交是这个库6.0
同时是标签的分支6.0.6
,也与远端的6.0
分支同步。
使用 git cat-file
命令可以进一步查询commit组织形式:
$ git cat-file -p HEAD tree c3d4b2bcd934be7e4ed98edac5aa7e9c054503c3 parent a5696bdf4f2687ab45f633ccb7cdc4ee9c2f957d author Oran Agra <oran@redislabs.com> 1595156420 0300 committer Oran Agra <oran@redislabs.com> 1595268506 0300 Redis 6.0.6.
可以发现这次提交包含了 tree c3d4b2bcd934be7e4ed98edac5aa7e9c054503c3
,同时,它的父亲提交 parent a5696bdf4f2687ab45f633ccb7cdc4ee9c2f957d
,有了这两个id可以推出当前版本的内容和历史记录。
通过 tree c3d4b2bcd934be7e4ed98edac5aa7e9c054503c3
当前版本中的所有文件都可以递归找到:
$ git cat-file -p c3d4b2bcd934be7e4ed98edac5aa7e9c054503c3 040000 tree 6608d88fe6a7a25b137b869040103ab261310da4 .github 100644 blob e445fd2017bb0c13af2f40cd7f24afefdb603ade .gitignore 100644 blob 484aeb62186033d32e9a4bdf12434cb6b8c56fb5 00-RELEASENOTES 100644 blob 7af2593407805c308cc25739ac9c6520031de60f BUGS 100644 blob 000edbeaf0270bf3b9e457274ab092b02b176b84 CONTRIBUTING 100644 blob a381681a1c2524ed586c6a87dfeb9ccdf1e86ded COPYING 100644 blob 3083f1afd50c34e1139ab1577510a17e968b0ed4 INSTALL 100644 blob 3727894624fdabf72995e6f94998a2cad359f760 MANIFESTO 100644 blob e614ede891f2dd183a3ae41ea1ac3b63fe2e7634 Makefile 100644 blob 55537e01fe862dd200ebe1078033122facfc854e README.md 100644/span> blob 2d020d0ceb0ddc7fd0bb2a6185e57a9afd5aef79 TLS.md 040000 tree 43ccdd93a80b35e03160d9db34f1e844a62a74b4 deps 100644 blob 8c53f015a20934bdb41c77152fd32a557d719fae redis.conf 100755 blob ade1bd09a539ecd8dcdd09e59a658539dab9bce6 runtest 100755 blob 27829a5fe8afacf893fe9bafc4245971ce375d6c runtest-cluster 100755 blob f6cc0a2589dea0f95b77b226e54200a29b8237ae runtest-moduleapi 100755 blob 3fb1ef61561289b2bf8622e49645f66dab83eeea runtest-sentinel 100644 blob 4ca5e5f8fc5abe2938c66a6851bba0c90058620f sentinel.conf 040000 tree e3b3338a7c60eafb3d9c19d3784e2482beea1d4b src 040000 tree af5de133fa0a0da30fe487be40783ef9644fba6d tests 040000 tree 5a82556097d23f0c16a8e5432d464f2ab434fd2a utils
通过 parent a5696bdf4f2687ab45f633ccb7cdc4ee9c2f957d
可以找出上一次提交,进而递归找出所有的提交,要注意有些commit的parent不止一个:
$ git cat-file -p a5696bdf4f2687ab45f633ccb7cdc4ee9c2f957d
tree 1adcf548620c6134f7d5fd072c05b981d0f36118
parent e15528bf1da1f1232fd08801ad382c915be94662
author Oran Agra <oran@redislabs.com> 1595162001 +0300
committer Oran Agra <oran@redislabs.com> 1595268506 +0300
Run daily CI on PRs to release a branch
这个commit的设计真的很神奇,一个个commit串起来就是一个branch,本质来讲branch
只是commit的一个别名,包括HEAD
也是,而 tag
也是对commit的一个描述,在不加描述信息时和commit也是一样的。
$ git cat-file -p 6.0.6
tree c3d4b2bcd934be7e4ed98edac5aa7e9c054503c3
parent a5696bdf4f2687ab45f633ccb7cdc4ee9c2f957d
author Oran Agra <oran@redislabs.com> 1595156420 +0300
committer Oran Agra <oran@redislabs.com> 1595268506 +0300
Redis 6.0.6.
$ git cat-file -p HEAD
tree c3d4b2bcd934be7e4ed98edac5aa7e9c054503c3
parent a5696bdf4f2687ab45f633ccb7cdc4ee9c2f957d
author Oran Agra <oran@redislabs.com> 1595156420 +0300
committer Oran Agra <oran@redislabs.com> 1595268506 +0300
Redis 6.0.6.
所以理解了commit的定位以后,所有切换分支、切换tag、操作HEAD,本质上都是在对commit进行操作,这些操作的参数完全可以用commit id来替换HEAD、branch name、tag name等等。
总结
- svn的提交记录是一个按时间排序的线性结构,git的提交记录是一个参考时间的树状结构
- git记录中时间先后不能代表commit修改的先后,回溯查找时要注意这一点才能解释很多疑惑
- git中的commit我认为是它的精髓,通过commit的串联和别名,形成分支、标签、HEAD等多种元素,隐藏了细节,方便了操作
什么才是精彩的人生?扬在脸上的自信、长在心底的善良、融进血里的骨气、刻进生命里的坚强~