简介
Git是一个开源的分布式版本控制系统,可以有效、高速的处理从很小到非常大的项目版本管理。Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。
Git是用于Linux内核开发的版本控制工具。与CVS、Subversion一类的集中式版本控制工具不同,它采用了分布式版本库的作法,不需要服务器端软件,就可以运作版本控制,使得源代码的发布和交流极其方便。Git的速度很快,这对于诸如Linux内核这样的大项目来说自然很重要。Git最为出色的是它的合并追踪(merge tracing)能力。
安装Git
yum install git -y
Git对象模型
Git 对象模型是整个 Git 设计思想中最核心的部分。理解 Git 对象模型是理解整个 Git 的关键。简单来说,每个 Git 对象包含三部分:类型,大小和内容。其中,对象的类型又分为 commits, trees, blobs, tags。
-
blob 对象:一块二进制数据,用来存储文件数据,通常是一个文件。
一个"blob对象"就是一块二进制数据,它没有指向任何东西或有任何其它属性,甚至连文件名都没有.
因为blob对象内容全部都是数据,如两个文件在一个目录树(或是一个版本仓库)中有同样的数据内容,那么它们将会共享同一个blob对象。Blob对象和其所对应的文件所在路径、文件名是否改被更改都完全没有关系。
-
tree 对象:有点类似目录的概念,它指向 blob 对象或是其它 tree 对象的指针,一般用来表示内容之间的目录层次关系。
tree对象、blob对象和其它所有的对象一样,都用其内容的SHA1哈希值来命名的;只有当两个tree对象的内容完全相同(包括其所指向所有子对象)时,它的名字才会一样,反之亦然。这样就能让Git仅仅通过比较两个相关的tree对象的名字是否相同,来快速的判断其内容是否不同
-
commit 对象:一个 commit 对象只指向一个 tree 对象,用来标记项目某一个特定时间点的状态,如时间戳、父对象、作者、提交者等。
从图可以看到一个提交有以下几个部分组成-
一个 tree 对象: tree对象的SHA1签名, 代表着目录在某一时间点的内容.
-
父对象 (parent(s)): 提交(commit)的SHA1签名代表着当前提交前一步的项目历史. 上面的那个例子就只有一个父对象; 合并的提交(merge commits)可能会有不只一个父对象. 如果一个提交没有父对象, 那么我们就叫它“根提交"(root commit), 它就代表着项目最初的一个版本(revision). 每个项目必须有至少有一个“根提交"(root commit). 一个项目可能有多个"根提交“,虽然这并不常见(这不是好的作法).
-
作者 : 做了此次修改的人的名字, 还有修改日期.
-
提交者(committer): 实际创建提交(commit)的人的名字, 同时也带有提交日期. TA可能会和作者不是同一个人; 例如作者写一个补丁(patch)并把它用邮件发给提交者, 由他来创建提交(commit).
-
注释 用来描述此次提交.
-
-
tag 对象:标记某一个提交(commit) ,一个标签对象包括一个对象名(译者注:就是SHA1签名), 对象类型, 标签名, 标签创建人的名字(“tagger”), 还有一条可能包含有签名(signature)的消息
如何组合到一起?
假设有如下目录结构
$>tree
.
|-- README
`-- lib
|-- inc
| `-- tricks.rb
`-- mylib.rb
2 directories, 3 files
把它提交到Git中
可以看到: 每个目录都创建了 tree对象 (包括根目录), 每个文件都创建了一个对应的 blob对象 . 最后有一个 commit对象 来指向根tree对象(root of trees), 这样我们就可以追踪项目每一项提交内容.
Git的三个工作区域
Git 仓库模型大致分为三个工作区域,分别为工作目录,暂存区域,以及本地仓库
Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,拷贝的就是这里的数据。
工作目录是对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。
暂存区域是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中。 有时候也被称作`‘索引’’,不过一般说法还是叫暂存区域。
基本的 Git 工作流程如下:
- 在工作目录中修改文件。
- 暂存文件,将文件的快照放入暂存区域。暂存文件,将文件的快照放入暂存区域。
- 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。
三个工作区域的转换
- git add files:把当前工作文件拷贝到暂存区域。
- git commit:在暂存区域生成文件快照并提交到本地仓库。
- git reset :用来撤销最后一次 git add files,也可以用 git reset 撤销所有暂存区域文件。
- --soft:将HEAD引用指向给定的提交,但不影响索引和工作目录;
- --mixed:将HEAD引用指向给定的提交,并将索引内容改变为指定提交的快照;但不改变工作目录;
- --hard:将HEAD引用指向给定的提交、将索引内容改变为指定提交的快照,并改变工作目录中的内容反映指定提交的内容;
- git checkout -- files:把文件从暂存区域覆盖到工作目录,用来丢弃-本地修改。
下面具体看看怎么操作
添加到暂存区
git add README
查看状态
git status
可以看到已经有个新文件了
提交
git commit -m "first commit"
现在显示提交成功了
查看提交历史
git log
从暂存区撤销修改
现在修改README文件
echo "change" > README
从暂存区撤销
git checkout README
三个工作区域文件的比较
git diff:查看尚未暂存的文件更新了哪些部分。
git diff --cached:查看已暂存文件和上次提交时的快照之间的差异。
git diff HEAD:查看未暂存文件与最新提交文件快照的区别。
git diff <index1> <index2>:查看不同快照之间的区别。
先修改一下README文件
echo "2">> README
查看工作区与暂存区文件不同
[root@localhost testpro]# git diff
diff --git a/README b/README
index d00491f..1191247 100644
--- a/README
+++ b/README
@@ -1 +1,2 @@
1
+2
查看工作区与最新提交文件快照的不同
[root@localhost testpro]# git diff HEAD
diff --git a/README b/README
index d00491f..1191247 100644
--- a/README
+++ b/README
@@ -1 +1,2 @@
1
+2
查看暂存区与上次提交时的快照之间的不同
因为这时候没有什么不同的,先把刚才修改的文件添加到暂存区
git add README
查看不同
[root@localhost testpro]# git diff --cached
diff --git a/README b/README
index d00491f..1191247 100644
--- a/README
+++ b/README
@@ -1 +1,2 @@
1
+2
文件状态
工作目录下的每一个文件都不外乎这两种状态:已跟踪或未跟踪。 已跟踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录,在工作一段时间后,它们的状态可能处于未修改,已修改或已放入暂存区。 工作目录中除已跟踪文件以外的所有其它文件都属于未跟踪文件,它们既不存在于上次快照的记录中,也没有放入暂存区。 初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态。
编辑过某些文件之后,由于自上次提交后你对它们做了修改,Git 将它们标记为已修改文件。 我们逐步将这些修改过的文件放入暂存区,然后提交所有暂存了的修改,如此反复。所以使用 Git 时文件的生命周期如下:
查看当前文件状态
git status
表示已跟踪文件在上次提交后都未被更改过。 此外,上面的信息还表明,当前目录下没有出现任何处于未跟踪状态的新文件。还显示了当前所在的分支为master
这时候我们修改README这个文件,并创建一个文件test
[root@localhost testpro]# echo "change" > README
[root@localhost testpro]# echo "1" > test
[root@localhost testpro]# git status
# On branch master
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: README
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# test
no changes added to commit (use "git add" and/or "git commit -a")
这时候可以看到README这个文件的状态是修改,表示这个文件被修改了,而test这个文件状态是还没被追踪,表示是个新建的文件
也可以用
git status -s
基本操作
配置用户名和email
git config --global user.name hal
git config --global user.email hal@ice.com
查看配置
[root@localhost testpro]# git config -l
user.name=hal
user.email=hal@ice.com
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
初始化仓库
git init
可以看到初始化成功一个空目录
这个目录下有个隐藏目录.git
[root@localhost testpro]# tree ./.git -L 1
./.git
├── branches #Git 项目分支信息
├── config #Git 项目配置信息
├── description #Git 项目描述信息
├── HEAD #指向 Git 项目当前分支的头指针
├── hooks #默认的"hooks"脚本,被特定事件发生前后触发。
├── info #里面含一个 exclude 文件,指 Git 项目要忽略的文件。
├── objects #Git 的数据对象,包括:commits, trees, blobs, tags。
└── refs #指向所有 Git 项目分支的指针。
对文件的处理常用命令
git add:暂存文件;
git rm:删除工作目录中的文件,及索引中的映射;
git rm --cached:只删除索引中的映射;
git mv:改变工作目录中的文件名,及索引中的映射;
git ls-files -s:列出暂存区(stage area)中的文件对象
git cat-file -p:美观排版显示文件内容;
git hash-object:计算文件的hash码;
git write-tree:根据当前索引中的内容创建树对象;
重命名文件
如果直接在工作区mv的话是不可以的。git还是会识别成2个不同的文件,我们可以测试看看
它认为你删了README文件,增加了README1文件所以如果要重命名或者移动应该用
mv README1 README #先还原一下
git mv README README2 #移动文件
显示文件内容
git cat-file -p 119124
远程仓库的使用
克隆仓库
[root@localhost ~]# git clone https://github.com/835311324/ansible_keepalived-nginx.git
Cloning into 'ansible_keepalived-nginx'...
remote: Enumerating objects: 59, done.
remote: Counting objects: 100% (59/59), done.
remote: Compressing objects: 100% (39/39), done.
remote: Total 59 (delta 6), reused 49 (delta 3), pack-reused 0
Unpacking objects: 100% (59/59), done.
添加远程仓库
git remote add K_N https://github.com/835311324/ansible_keepalived-nginx.git
查看远程仓库
[root@localhost ansible_keepalived-nginx]# git remote -v
K_N https://github.com/835311324/ansible_keepalived-nginx.git (fetch)
K_N https://github.com/835311324/ansible_keepalived-nginx.git (push)
origin https://github.com/835311324/ansible_keepalived-nginx.git (fetch)
origin https://github.com/835311324/ansible_keepalived-nginx.git (push)
查看某个远程仓库具体信息
[root@localhost ansible_keepalived-nginx]# git remote show K_N
* remote K_N
Fetch URL: https://github.com/835311324/ansible_keepalived-nginx.git
Push URL: https://github.com/835311324/ansible_keepalived-nginx.git
HEAD branch: master
Remote branch:
master new (next fetch will store in remotes/K_N)
Local ref configured for 'git push':
master pushes to master (up to date)
这时候就可以用K_N代替整个URL了
从远程仓库抓取
git fetch [remote-name]
git pull [remote-name] ##等价于 git fetch + git merge 的功能
推送到远程仓库
git push [remote-name] [branch-name]
远程仓库的移除与重命名
[root@localhost ansible_keepalived-nginx]# git remote rename K_N KN
[root@localhost ansible_keepalived-nginx]# git remote
KN
origin
标签
Git 可以给历史中的某一个提交打上标签,以示重要。 比较有代表性的是人们会使用这个功能来标记发布结点(v1.0 等等)。
创建标签
git tag -a v2.0 -m 'my tag one'
-m 选项指定了一条将会存储在标签中的信息。 如果没有为附注标签指定一条信息,Git 会运行编辑器要求你输入信息
列出标签
[root@localhost ansible_keepalived-nginx]# git tag -l
v2.0
分支
Git 中的分支本质上是一个指向 commit 对象的可变指针。Git 会维护一个默认分支——master。每一次提交之后,master 指针都会自动向前移动。Git保存着一个名为 HEAD 的特别指针,它是一个指向当前工作分支的指针。我们可以将 HEAD 想象为当前分支的别名。
创建一个新的分支
git branch bugFix
切换到其他分支
git checkout bugFix
从这个图可以形象的看到HEAD指针指向bugFix这个分支,表示分支切换了。
如果吧创建分支与切换分支这两步合并可以执行
git checkout -b bugFix
这时候再进行一次提交就会变成
然后切换分支
git checkout master
这条命令做了两件事。第一,它把 HEAD 指针移回到 master 分支;第二,把工作目录中的文件替换成了 master 分支所指向的快照内容。也就是说,从现在开始,基于该文件的一系列提交都将始于一个较老的版本。它的主要作用在于,可以将 bugFix 分支里作出的修改暂时取消,隔离 bugFix 分支对 master 分支的影响。在实际项目中,我们经常有这样的需求,即采用 developer 分支开发主要版本,bugFix 分支负责修复 bug,彼此互相隔离,最后合并。
合并分支
git merge bugFix
git rebase bugFix
可以看到,git merge 命令把两个父分支合并进行一次提交,但提交历史不是线性的。相比之下,分支衍合命令 git rebase 在当前分支上重演另一个分支的历史,从而保证提交历史是线性的
冲突
合并有时候并不是顺利的,大部分时候都会有冲突。这时候Git仅作合并,但是不提交,它会停下来等人为的解决冲突
查看哪些文件发生冲突,可以用git status查看
删除标签
git branch -d bugFix
参考文章
https://www.ibm.com/developerworks/cn/opensource/os-cn-tourofgit/index.html?mhq=git&mhsrc=ibmsearch_a
https://git-scm.com/book/zh/v2