linus研发,分布式版本控制工具;
- Git is :
- 目录内容管理系统
- 树状的历史存储系统
- 傻瓜式的内容追踪器
- 一个工具箱:a toolkit
- git 命令有两种
- the “plumbing”:低级命令
- the “porcelain”:高级命令
- 版本控制系统的两种常见的逻辑:
- delta storage:增量存储系统
- DAG storage:有向无环图
- 版本控制系统的三种形式:
- local:本地版本控制系统
- centralized:有中心节点的集中版本控制系统
- distributed:分布式的版本控制系统
- 常见版本控制系统解决方案:
- delta storage:
- local:rcs
- centralized:cvs-->svn,perfirce
- distributed:darcs, mercurial(商业化)
- DAG storage:
- local:cp -r,timemachine
- centralized:bitkeeper
- distributed:git,bazaar
- delta storage:
git是通过记录快照的方式来实现文件内容保留的,它不是记录差异;但是,git在仓库中保留的每一个文件进行追踪时,采用独特的方式,没一个文件的名称在仓库中,这个仓库只是文件追踪系统,它只追踪内容,意味着仅保留文件内容的变化,至于文件叫什么名,文娱哪个目录下,仓库在文件内部是不加任何保留的;
git仓库是一个对象存储系统,所谓对象存储,每一个文件的所有数据,无论是数据还是元数据都是自包含的,往存储空间里存放的每一个文件,这个文件的所有属性都是自包含;
所以每一个文件都是由着自己对自我结构描述属性等信息的内容,所有称作是完成而独立的对象;
引用每一个对象时,只需要引用对象的一个符号链接或一个名称就行;git版本仓库里的内容就是对象存储系统;
例如,有一个文件需要保存到仓库中,将来必要时能在仓库中检索出来原来的某个版本;这个文件在工作区中看是文件,而且是保存在文件系统上的文件,但是一旦把它纳入到版本库中去,就以为这它要被版本库生成一个文件对象而保存在版本库中;
git对每一个文件对象的名称追踪时,就需要对文件另起一个名字,这个名字很独特是文件内容的哈希值;注意:文件名是文件内容的哈希值;可以使用MD5或sha取得文件的哈希码,只要文件内容变化,它的哈希一定会改变;本来git的仓库就是通过保存文件快照进行的,而它的快照保存机制就是只要文件内容变了就把这个文件复制一份新版本出来,重新创建个对象;
如果在工作区中有两个文件不在同一个目录下,但内容一摸一样,在对象库中起始只保存一个版本
在开发应用程序时,通常要创建个目录,里面存放程序的代码:
[root@node1 ~]# mkdir testproj
[root@node1 ~]# cd testproj/
为了能够使这个代码目录下,将来把所有版本内容的变化可以基于版本进行追偿,可以随时回到过去的某个版本,通常就需要在这个目录下创建一个隐藏目录,里面记录这个工作目录当中的每一个文件在需要保存状态时的状态变化;
所以,对应testproj就叫做工作目录,在工作目录下就会有版本库自动生成版本仓库;
-
.git 隐藏目录不需要手动创建,使用git init 命令自动生成:
[root@node1 testproj]# git init Initialized empty Git repository in /root/testproj/.git/ [root@node1 testproj]# ls -a . .. .git
-
git隐藏目录(版本库)内部有两个主要部分组成,第一个部分叫索引(暂存区),第二部分叫对象库
工作目录中有2个文件,当需要记录过去状态是要保存版本库,就把工作目录中的文件创建成文件对象,添加到对象库中;这个添加过程分2步:- 第一步叫暂存
暂存就是在索引中把当前工作目录中一共有多少文件、每个文件路径、和它对应的哈希码在仓库中记录一个快照;索引中记录的就是工作目录中的文件名和对象库中的文件名一一对应的表,而这个内容就是一个快照,酷照是工作目录中的目录结构和对象库中文件的对应关系,这就是暂存;还有个快照是在对象库中快照的文件内容; - 第二步叫提交
所谓提交就是把索引中的快照,也要保存在对象库中;保存在对象库中的索引叫版本树,因为它记录的是文件的目录树结构,所以被称作树对象;文件被称作文件对象;还有一个对象叫分支;一旦完成了提交,就意味着把索引中记录的当前的树快照,也给它保存在对象库当中,生成一个树对象,将来为了引用树序列,需要一个纸箱这个数最开始出的指针;这个就称作一个分支;
提交后,索引中的内容依然存在是指向对象库中的文件对象的,而对象库中的索引树也是指向文件对象的;
当对工作目录中的文件修改后,再做暂存时,对象库中就会又创建一个新对象;
此时,索引中的内容发生变化了,但对象库中的索引对象并没有变;所以,当修改文件再次暂存时,文件内容发生改变,文件对象也发生改变,由此索引当中的树对象,对于没改变的文件依然指向老版本,对于已发生改变的文件则指向新版本;但是,此前的提交就是为了追踪过去版本的,是不会改变的;
这只是暂存,当你再做提交时,就会在对象库中自动生成一个新的索引对象,就是把索引中的内容做个快照保存一个新的树(索引)对象;这个新的树对象则指向文件的新版本,没有变化的文件还是指向老版本;而这两个树对象是有先后关系的;
不断暂存、提交时间久了以后,会生成一个链式关系;使用head可以指向去过对象库中的老版本;如果当前工作目录中有文件指向老版本时,可能会丢失;
- 第一步叫暂存
git的安装基本使用
-
安装:git官方站点:https://git-scm.com/
[root@node1 ~]# yum info git #git已经收录在base仓库 [root@node1 ~]# yum list all git* Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile * epel: mirrors.tuna.tsinghua.edu.cn Installed Packages git.x86_64 1.8.3.1-14.el7_5 @updates Available Packages GitPython.noarch 1.0.1-7.el7 epel git.x86_64 1.8.3.1-20.el7 updates git-all.noarch 1.8.3.1-20.el7 updates git-annex.x86_64 5.20140221-1.2.el7 epel git-bzr.noarch 1.8.3.1-20.el7 updates git-cal.noarch 0.9.1-3.el7 epel git-cola.noarch 2.10-1.el7 epel git-cvs.noarch 1.8.3.1-20.el7 updates git-daemon.x86_64 1.8.3.1-20.el7 updates git-email.noarch 1.8.3.1-20.el7 updates git-extras.noarch 4.6.0-3.el7 epel git-gnome-keyring.x86_64 1.8.3.1-20.el7 updates git-gui.noarch 1.8.3.1-20.el7 updates git-hg.noarch 1.8.3.1-20.el7 updates git-instaweb.noarch 1.8.3.1-20.el7 updates git-lfs.x86_64 2.4.2-2.el7 epel git-merge-changelog.x86_64 0-14.20150325git.el7 epel git-p4.noarch 1.8.3.1-20.el7 updates git-publish.noarch 1.4.4-4.el7 epel git-review.noarch 1.24-5.el7 epel git-svn.x86_64 1.8.3.1-20.el7 updates git-tools.noarch 2017.10-1.el7 epel git-xcleaner.noarch 1.5-1.el7 epel git2cl.noarch 2.0-0.8.git8373c9f.el7 epel gitflow.noarch 0.4.2.20120723git53e9c76-4.el7 epel gitg.x86_64 3.26.0-1.el7 epel gitg-devel.x86_64 3.26.0-1.el7 epel gitg-libs.x86_64 3.26.0-1.el7 epel github2fedmsg.noarch 0.3.6-1.el7 epel gitk.noarch 1.8.3.1-20.el7 updates gitolite3.noarch 1:3.6.10-1.el7 epel gitstats.noarch 0-0.6.20130723gita923085.el7 epel gitweb.noarch 1.8.3.1-20.el7 updates
git在本地工作的模式
-
在本地如何实现版本控制:
[root@node1 ~]# yum -y install git [root@node1 ~]# git --help 用法: git [--version] [--help] [-c name=value] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path] [-p|--paginate|--no-pager] [--no-replace-objects] [--bare] [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>] <command> [<args>] 最常用的git命令: add Add file contents to the index #添加文件内容至索引 bisect Find by binary search the change that introduced a bug #通过二分查找丁文引入bug branch List, create, or delete branches #列出、创建或删除分支 checkout Checkout a branch or paths to the working tree #检出一个分支或路径到工作区 clone Clone a repository into a new directory #克隆一个版本库到一个新目录 commit Record changes to the repository #记录变更到版本库 diff Show changes between commits, commit and working tree, etc #显示提交之间、提交和工作区之间等的差异 fetch Download objects and refs from another repository #从另外一个版本库下载对象和引用 grep Print lines matching a pattern #输出和模式匹配的行 init Create an empty Git repository or reinitialize an existing one #创建一个空的git版本看或重新初始化一个已存在的版本库 log Show commit logs #显示提交日志 merge Join two or more development histories together #合并两个或更多开发历史 mv Move or rename a file, a directory, or a symlink #移动或重命名一个文件目录或符号链接 pull Fetch from and merge with another repository or a local branch #获取合并另外的版本库或一个本地分支 push Update remote refs along with associated objects #更新远程引用和相关的对象 rebase Forward-port local commits to the updated upstream head #本地提交转移至跟新后的上游分支中 reset Reset current HEAD to the specified state #重置当前HEAD到指定状态 rm Remove files from the working tree and from the index #从工作去和索引中删除文件 show Show various types of objects #显示各种类型的对象 status Show the working tree status #显示工作区状态 tag Create, list, delete or verify a tag object signed with GPG #创建、列出、删除或校验一个GPG签名的tag对象
示例:
如上文中创建的目录testproj[root@node1 ~]# cd testproj/ [root@node1 testproj]# tree .git .git ├── branches ├── config ├── description ├── HEAD ├── hooks │ ├── applypatch-msg.sample │ ├── commit-msg.sample │ ├── post-update.sample │ ├── pre-applypatch.sample │ ├── pre-commit.sample │ ├── prepare-commit-msg.sample │ ├── pre-push.sample │ ├── pre-rebase.sample │ └── update.sample ├── info │ └── exclude ├── objects │ ├── info │ └── pack └── refs ├── heads └── tags
初始化以后,创建了好多文件:
branches:分支
config:当前版本仓库的配置;对于git配置有三个等级:
- 系统等级:对所有工作在当前系统上的用户都有效的配置;
- 全局等级:对当前用户全局;一个用户可能维护有n个仓库,这n个仓库都共享的配置;
- 仓库等级:每个仓库自己独有的配置;
HEAD:指向最新的树对象
objects:对象库目录
演示:
此时没有index目录,因为还没有添加文件,创建文件后自动生成
[root@node1 testproj]# cp /etc/passwd ./
[root@node1 testproj]# vim README
Project: test project
[root@node1 testproj]# ls
passwd README
[root@node1 testproj]# git add . #暂存工作目录下所有文件,也可以使用git add -a
[root@node1 testproj]# tree .git
.git
├── branches
├── config
├── description
├── HEAD
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── prepare-commit-msg.sample
│ ├── pre-push.sample
│ ├── pre-rebase.sample
│ └── update.sample
├── index
├── info
│ └── exclude
├── objects
│ ├── 63
│ │ └── 452156647080f0f17e0d0cb14d7c1f1f90e22e
│ ├── 78
│ │ └── 270f5908030219758738a85d499b8bfe6baa2b
│ ├── info
│ └── pack
└── refs
├── heads
└── tags
11 directories, 16 files
此时自动创建了index目录,用来存放暂存区的数据
[root@node1 testproj]# file .git/index
.git/index: Git index, version 2, 2 entries
[root@node1 testproj]# vim README
[root@node1 testproj]# git add README
Project: test project
version1.1.0
[root@node1 testproj]# tree .git
.git
├── branches
├── config
├── description
├── HEAD
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── prepare-commit-msg.sample
│ ├── pre-push.sample
│ ├── pre-rebase.sample
│ └── update.sample
├── index
├── info
│ └── exclude
├── objects
│ ├── 63
│ │ └── 452156647080f0f17e0d0cb14d7c1f1f90e22e
│ ├── 78
│ │ └── 270f5908030219758738a85d499b8bfe6baa2b
│ ├── 7a
│ │ └── 7f3b22a21932aefe2b204fe528475ca358d422
│ ├── info
│ └── pack
└── refs
├── heads
└── tags
12 directories, 17 files
此时修改了README文件内容后重新暂存后objects目录下多了一个目录
[root@node1 testproj]# mkdir docs
[root@node1 testproj]# touch docs/changelog
[root@node1 testproj]# git add docs
[root@node1 testproj]# tree .git
.git
├── branches
├── config
├── description
├── HEAD
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── prepare-commit-msg.sample
│ ├── pre-push.sample
│ ├── pre-rebase.sample
│ └── update.sample
├── index
├── info
│ └── exclude
├── objects
│ ├── 63
│ │ └── 452156647080f0f17e0d0cb14d7c1f1f90e22e
│ ├── 78
│ │ └── 270f5908030219758738a85d499b8bfe6baa2b
│ ├── 7a
│ │ └── 7f3b22a21932aefe2b204fe528475ca358d422
│ ├── info
│ └── pack
└── refs
├── heads
└── tags
12 directories, 17 files
此时暂存区未发生改变,因为git add 不追踪空目录
[root@node1 testproj]# vim docs/changelog
auth: magedu
[root@node1 testproj]# git add docs/
[root@node1 testproj]# tree .git
.git
├── branches
├── config
├── description
├── HEAD
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── prepare-commit-msg.sample
│ ├── pre-push.sample
│ ├── pre-rebase.sample
│ └── update.sample
├── index
├── info
│ └── exclude
├── objects
│ ├── 63
│ │ └── 452156647080f0f17e0d0cb14d7c1f1f90e22e
│ ├── 78
│ │ └── 270f5908030219758738a85d499b8bfe6baa2b
│ ├── 7a
│ │ └── 7f3b22a21932aefe2b204fe528475ca358d422
│ ├── 7e
│ │ └── 184b2a7d9bd622e74df67fbc6a9f75345090db
│ ├── info
│ └── pack
└── refs
├── heads
└── tags
13 directories, 18 files
此时在objects目录下多了一个目录,有内容的东西才能作为对象存储
[root@node1 testproj]# git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: README
# new file: docs/changelog
# new file: passwd
#
[root@node1 testproj]# git rm --cached passwd
rm 'passwd'
[root@node1 testproj]# ls
docs passwd README
[root@node1 testproj]# git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: README
# new file: docs/changelog
#
--cached是指删除缓存内的文件,不删除工作目录下的文件
git配置文件分为三种:
- 系统级:/etc/git/gitconfig
- 全局:~/.gitconfig --global
- 仓库特有: REPO/.git/config --system
git 提交时必须要指明提交的用户名和email信息,因此,在提交前需要先手动配置
[root@node1 testproj]# git config -l #列出当前版本库的配置,此命令要在版本库下执行或直接查看.git/config
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
[root@node1 testproj]# cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
user.email:指明用户的邮箱;
user.name:指明用户的用户名
[root@node1 testproj]# git config --global user.name "MageEdu"
[root@node1 testproj]# git config --global user.email "magedu@magedu.com"
[root@node1 testproj]# git config -l
user.name=MageEdu
user.email=magedu@magedu.com
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
因为使用的是全局级别,在用户家目录下会生成.gitconfig文件;
[root@node1 testproj]# ls -a /root/.gitconfig
/root/.gitconfig
[root@node1 testproj]# cat /root/.gitconfig
[user]
name = MageEdu
email = magedu@magedu.com
如果没指明则是仓库级别的:
[root@node1 testproj]# cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
git提交
每一次提交要指明变化说明,会自动打一个文件名;
如果不想使用文本编辑的交互式进行说明变化信息,也可以是用-m <msg>
[root@node1 testproj]# git commit -m "v0.0.1"
[master (root-commit) 7adf5fc] v0.0.1
3 files changed, 28 insertions(+)
create mode 100644 README
create mode 100644 docs/changelog
create mode 100644 passwd
[root@node1 testproj]# cd .git/objects/
[root@node1 objects]# ls 其中的两数字的目录为分层管理众多的文件对象;
47 5d 63 78 7a 7e 89 be fc info pack
[root@node1 objects]# ll
total 0
drwxr-xr-x. 2 root root 52 Dec 19 23:10 47
drwxr-xr-x. 2 root root 52 Dec 19 23:11 5d
drwxr-xr-x. 2 root root 52 Dec 19 22:31 63
drwxr-xr-x. 2 root root 52 Dec 19 22:31 78
drwxr-xr-x. 2 root root 98 Dec 19 23:06 7a
drwxr-xr-x. 2 root root 52 Dec 19 22:43 7e
drwxr-xr-x. 2 root root 52 Dec 19 23:10 89
drwxr-xr-x. 2 root root 52 Dec 19 23:06 be
drwxr-xr-x. 2 root root 52 Dec 19 23:06 fc
drwxr-xr-x. 2 root root 6 Dec 19 20:35 info
drwxr-xr-x. 2 root root 6 Dec 19 20:35 pack
[root@node1 objects]# ll 89 #文件名就是文件内容的哈希码;
total 4
-r--r--r--. 1 root root 111 Dec 19 23:10 6049330e920c53a53017258b8334e75b41c779
git-clone
- 用法:
git clone <repository> [<directory>]
<repository>
:指明要克隆的仓库;-
<directory>
:克隆到哪个位置;[root@node1 ~]# ls anaconda-ks.cfg testproj [root@node1 ~]# git clone testproj/ new Cloning into 'new'... done. [root@node1 ~]# ls #自动生成new目录 anaconda-ks.cfg new testproj [root@node1 ~]# cat new/README Project: test project version1.1.0 version1.1.1
这就叫远程仓库,把远程仓库克隆到本地;这也是生成仓库的第二种办法;
所以,生成仓库有两种办法:第一,创建新目录,里面创建文件,添加、提交就生成仓库了;第二,把别人的仓库克隆到本地;
[root@node1 testproj]# vim INSTALL
[root@node1 testproj]# ls
docs INSTALL passwd README
[root@node1 testproj]# git add INSTALL
[root@node1 testproj]# git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: INSTALL
#
[root@node1 testproj]# git rm --cached INSTALL
rm 'INSTALL'
[root@node1 testproj]# git status
# On branch master
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# INSTALL
nothing added to commit but untracked files present (use "git add" to track)
[root@node1 testproj]# git ls-files
README
docs/changelog
passwd
[root@node1 testproj]# git add INSTALL
[root@node1 testproj]# git ls-files #显示目录树种的内容
INSTALL
README
docs/changelog
passwd
[root@node1 testproj]# tree .git
.git
├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── prepare-commit-msg.sample
│ ├── pre-push.sample
│ ├── pre-rebase.sample
│ └── update.sample
├── index
├── info
│ └── exclude
├── logs
│ ├── HEAD
│ └── refs
│ └── heads
│ └── master
├── objects
│ ├── 09
│ │ └── 5e68cb0e446afdccc74ca4f6e08add47106ab8
│ ├── 47
│ │ └── 9abf23993860872cded7d081e4a8622900dbca
│ ├── 5d
│ │ └── 6ed1a098f588ab6526fd3b53a1331d298d44af
│ ├── 63
│ │ └── 452156647080f0f17e0d0cb14d7c1f1f90e22e
│ ├── 78
│ │ └── 270f5908030219758738a85d499b8bfe6baa2b
│ ├── 7a
│ │ ├── 7f3b22a21932aefe2b204fe528475ca358d422
│ │ └── df5fc60c3e8829843257e47d22f30fdfb78d42
│ ├── 7e
│ │ └── 184b2a7d9bd622e74df67fbc6a9f75345090db
│ ├── 89
│ │ └── 6049330e920c53a53017258b8334e75b41c779
│ ├── be
│ │ └── 0e31953e749a2d94f7386c508c2b7900112b50
│ ├── fc
│ │ └── bc6dac21cf3af7e7acf87c602a7d96fa836a6a
│ ├── info
│ └── pack
└── refs
├── heads
│ └── master
└── tags
22 directories, 29 files
git ls-files:列出文件;
git cat-fles:查看文件;
git hash-object:计算文件的hash码;
git write-tree:根据当前索引中的内容创建树对象;
简单总结git版本库:
git的版本库就是存储了所有用来维护和管理当前项目的修订版本和历史信息数据的一个数据库;
所以版本库就是数据库里面记录的大量的数据,存储了版本库当中所有文件的完整副本,甚至包括版本库自身的副本都有;
除此之外,每个版本库还保存了跟版本库相关的一组元数据,就是版本库提交者的用户信息邮箱地址等;
克隆还可以复制仓库内容,但只复制数据不复制元数据;
版本库中有非常重要的两个区域:缓存区和对象库;
对象库中有四类对象:块、树、提交、标签;
而索引是个临时的动态的二进制文件,用于描述某个时刻,整个工作目录当中或整个版本库当中某一个版本的状态信息;
因为索引里面反应了要么是当前目录里add进去的文件,要么是某一版本库当中已经被提交的文件的映射信息;索引内容的随时能变化的;
如果有多个版本都提交了, 如果此时整个工作目录反应的是第一个版本,那么索引当中显示的信息都是第一个版本中所有文件的映射信息;
如果此时让工作目录反应了第二个版本,索引中的文件立即会改变成第二个版本中所有文件映射信息了;所以说索引里面是临时的、动态的信息;
如果当前没有提交任何版本,只是add了一些数据,那么索引里反应的都是add进去文件的映射路径;