git
源代码管理工具
- CVS
- SVN
- Git
- VSS
概述
主要功能
- 记录历史信息
- 团队协作
三个区域
版本库:已提交到git仓库中的数据
暂存区:下一次要提交的文件快照
工作目录:编写代码的目录
初始化仓库
在某个文件夹中,使用如下命令,将在当前目录下创建一个 gittest 文件夹,并在该文件夹中初始化git本地仓库
git init gittest
配置
git config --global user.name "pcbhyy"
git config --global user.email "pcbhyy@163.com"
--global :针对当前用户
--system:针对当前系统所有用户
不加选项:针对当前项目
创建并添加文件到暂存区中
//先创建 1.txt文件
git add 1.txt
查看git状态
显示 暂存区 与 工作区 状态不同的文件。这其中包含了已修改但未暂存, 或已经暂存但没有提交的文件。
git status
提交
将暂存区中内容提交到本地仓库中
git commit -m "提交说明"
//打开一个文件,在其中添加注释,适合于注释比较多和需要换行的情况
git commit
查看命令帮助
git --help 命令名
如:
git --help commit
查看提交历史
默认不用任何参数的话, git log 会按提交时间列出所有的更新, 最近的更新排在最上面。
git log
//n:显示的提交数
git log -n
//-p , 用来显示每次提交的内容差异
git log -p
//--stat:查看每次提交的简略的统计信息
git log --stat
撤销
舍弃工作目录中对一个文件的修改 (修改的文件未被暂存或提交),命令: checkout –filename
git checkout test.txt
注意:请务必记得
git checkout -- <file>
是一个危险的命令。 你对那个文件在本地的任何修改都会消失——Git 会用最近提交的版本覆盖掉它。 除非你确实清楚不想要对那个文件的本地修改了,否则请不要使用这个命令。舍弃工作目录中所有未保存的变更 (文件可能已暂存也可能没有,但未被提交),命令: reset –hard
注意:git reset 确实是个危险的命令,如果加上了 –hard 选项则更是如此。 然而在上述场景中,工作目录中的文件尚未修改,因此相对安全一些。
从工作目录中删除未跟踪(新添加未缓存或提交过)的文件
git clean -fd
-d:删除未跟踪文件夹
-f:强制删除
修补最后一次提交
git commit -m 'init commit' //修改最后一次提交的注释 git commit -m 'change common' --amend git commit -m 'init commit' //增加一个暂存文件 git add 3.txt //修改最后一次提交的内容 git commit --amend
当你在修补最后的提交时,并不是通过用改进后的提交 原位替换 掉旧有提交的方式来修复的, 理解这一点非常重要。从效果上来说,就像是旧有的提交从未存在过一样,它并不会出现在仓库的历史中。
修补提交最明显的价值是可以稍微改进你最后的提交,而不会让“啊,忘了添加一个文件”或者 “小修补,修正笔误”这种提交信息弄乱你的仓库历史。
恢复到某次提交
//e5c3f2ce79fc005c5208ceb95b170b614d0a63e3:为提交Id git reset --hard e5c3f2ce79fc005c5208ceb95b170b614d0a63e3
显示所有提交历史
//该命令分析你所有分支的头指针的日志来查找出你在重写历史上可能丢失的提交 git reflog
重要目录结构
HEAD 文件、 ( 尚待创建的) index 文件, 和 objects 目录、 refs 目录。 这些条目是 Git 的核心组成部分。
- objects 目录存储所有数据内容;
- refs 目录存储指向数据( 分支) 的提交对象的指针;
- HEAD 文件指示目前被检出的分支;
- index 文件保存暂存区信息。
提交过程
我们假设现在有一个工作目录, 里面包含了三个将要被暂存和提交的文件。 暂存操作
会为每一个文件计算校验和( 使用我们在 Chapter 1 中提到的 SHA-1 哈希算法) , 然后会把当前版本的
文件快照保存到 Git 仓库中( Git 使用 blob 对象来保存它们) , 最终将校验和加入到暂存区域等待提交:
$ git add README test.rb LICENSE
$ git commit -m 'The initial commit of my project'
当使用 git commit 进行提交操作时, Git 会先计算每一个子目录( 本例中只有项目根目录) 的校验
和, 然后在 Git 仓库中这些校验和保存为树对象。 随后, Git 便会创建一个提交对象, 它除了包含上面提
到的那些信息外, 还包含指向这个树对象( 项目根目录) 的指针。 如此一来, Git 就可以在需要的时候重
现此次保存的快照。
现在, Git 仓库中有五个对象: 三个 blob 对象( 保存着文件快照) 、 一个树对象( 记录着目录结构和
blob 对象索引) 以及一个提交对象( 包含着指向前述树对象的指针和所有提交信息) 。
做些修改后再次提交, 那么这次产生的提交对象会包含一个指向上次提交对象( 父对象) 的指针。
Git分支
Git 的分支, 其实本质上仅仅是指向提交对象的可变指针。 Git 的默认分支名字是 master 。 在多次提交
操作之后, 你其实已经有一个指向最后那个提交对象的 master 分支。 它会在每次的提交操作中自动向
前移动。
Git 的 “master” 分支并不是一个特殊分支。 它就跟其它分支完全没有区别。 之所以几乎每一个仓库都
有 master 分支, 是因为 git init 命令默认创建它, 并且大多数人都懒得去改动它。
分支创建
git branch testing
这会在当前所在的提交对象上创建一个指针。
当前工作分支
那么, Git 又是怎么知道当前在哪一个分支上呢? 也很简单, 它有一个名为 HEAD 的特殊指针。 请注意
它和许多其它版本控制系统( 如 Subversion 或 CVS) 里的 HEAD 概念完全不同。 在 Git 中, 它是一个
指针, 指向当前所在的本地分支( 译注: 将 HEAD 想象为当前分支的别名) 。 在本例中, 你仍然在
master 分支上。 因为 git branch 命令仅仅 创建 一个新分支, 并不会自动切换到新分支中去。
你可以简单地使用 git log 命令查看各个分支当前所指的对象。 提供这一功能的参数是–decorate 。
$ git log --oneline --decorate
f30ab (HEAD->master, testing) add feature #32 - ability to add new
34ac2 fixed bug #1328 - stack overflow under certain conditions
98ca9 initial commit of my project
分支切换
要切换到一个已存在的分支, 你需要使用 git checkout 命令。 我们现在切换到新创建的 testing 分支去:
git checkout testing
分支切换会改变你工作目录中的文件
在切换分支时, 一定要注意你工作目录里的文件会被改变。 如果是切换到一个较旧的分支, 你的工作
目录会恢复到该分支最后一次提交时的样子。 如果 Git 不能干净利落地完成这个任务, 它将禁止切换
分支。
切换分支后提交
如图所示, 你的 testing 分支向前移动了, 但是 master 分支却没有, 它仍然指向运行 git checkout 时所指的对象。 这就有意思了, 现在我们切换回 master 分支看看:
git checkout master
这条命令做了两件事。 一是使 HEAD 指回 master 分支, 二是将工作目录恢复成 master 分支所指向
的快照内容。 也就是说, 你现在做修改的话, 项目将始于一个较旧的版本。 本质上来讲, 这就是忽略
testing 分支所做的修改, 以便于向另一个方向进行开发。
我们不妨再稍微做些修改并提交:
$ vim test.rb
$ git commit -a -m 'made other changes'
现在, 这个项目的提交历史已经产生了分叉( 参见 Figure 3-9) 。 因为刚才你创建了一个新分支, 并切换
过去进行了一些工作, 随后又切换回 master 分支进行了另外一些工作。 上述两次改动针对的是不同分
支: 你可以在不同分支间不断地来回切换和工作, 并在时机成熟时将它们合并起来。 而所有这些工作, 你
需要的命令只有 branch 、 checkout 和 commit 。
你可以简单地使用 git log 命令查看分叉历史。 运行
git log –oneline –decorate –graph –all , 它会输出你的提交历史、 各个分支的指向以
及项目的分支分叉情况。
$ git log --oneline --decorate --graph --all
* c2b9e (HEAD, master) made other changes
| * 87ab2 (testing) made a change
|/
* f30ab add feature #32 - ability to add new formats to the
* 34ac2 fixed bug #1328 - stack overflow under certain conditions
* 98ca9 initial commit of my project
创建分支实质上就是向一个文件中写入一个对象校验和
由于 Git 的分支实质上仅是包含所指对象校验和( 长度为 40 的 SHA-1 值字符串) 的文件, 所以它的创建
和销毁都异常高效。 创建一个新分支就像是往一个文件中写入 41 个字节( 40 个字符和 1 个换行符) , 如
此的简单能不快吗?
合并分支
快进
在合并的时候, 你应该注意到了”快进( fast-forward) “这个词。 由于当前 master 分支所指向的提交
是你当前提交( 有关 hotfix 的提交) 的直接上游, 所以 Git 只是简单的将指针向前移动。 换句话说, 当你
试图合并两个分支时, 如果顺着一个分支走下去能够到达另一个分支, 那么 Git 在合并两者的时候, 只会
简单的将指针向前推进( 指针右移) , 因为这种情况下的合并操作没有需要解决的分歧——这就叫做
“快进( fast-forward) ” 。
git checkout master
git merge hotfix
删除分支
git branch -d hotfix
合并提交
这和你之前合并 hotfix 分支的时候看起来有一点不一样。 在这种情况下, 你的开发历史从一个更早的
地方开始分叉开来( diverged) 。 因为, master 分支所在提交并不是 iss53 分支所在提交的直接祖
先, Git 不得不做一些额外的工作。 出现这种情况的时候, Git 会使用两个分支的末端所指的快照( C4
和 C5 ) 以及这两个分支的工作祖先( C2 ) , 做一个简单的三方合并。
和之间将分支指针向前推进所不同的是, Git 将此次三方合并的结果做了一个新的快照并且自动创建一个
新的提交指向它。 这个被称作一次合并提交, 它的特别之处在于他有不止一个父提交。
遇到冲突时的分支合并
有时候合并操作不会如此顺利。 如果你在两个不同的分支中, 对同一个文件的同一个部分进行了不同的修
改, Git 就没法干净的合并它们。 如果你对 #53 问题的修改和有关 hotfix 的修改都涉及到同一个文件
的同一处, 在合并它们的时候就会产生合并冲突:
此时 Git 做了合并, 但是没有自动地创建一个新的合并提交。 Git 会暂停下来, 等待你去解决合并产生的
冲突。 你可以在合并冲突后的任意时刻使用 git status 命令来查看那些因包含合并冲突而处于未合并
( unmerged) 状态的文件:
任何因包含合并冲突而有待解决的文件, 都会以未合并状态标识出来。 Git 会在有冲突的文件中加入标准
的冲突解决标记, 这样你可以打开这些包含冲突的文件然后手动解决冲突。 出现冲突的文件会包含一些特
殊区段, 看起来像下面这个样子:
//解决冲突后,添加冲突文件到缓冲区中,标记冲突解决
git add index.html
//提交冲突文件到本地仓库
git commit -m '解决合并冲突'
远程连接
Git 通过储存一个称为远程(remote)的连接来联系远方的朋友。本地仓库可以有零个、一个或多个远程连接。通常情况下, Git 仓库只有一个远程连接,即 origin(源)。你可能已经见过了这个术语。分配给远程仓库的这个别名,从中下载或克隆了你的本地副本。你可以使用任何你喜欢的名称来命名你的远程连接。在你第一次启动一个新项目时,可能还没有现成的代码,也可能有了一些代码。(这显而易见,不是么?)如果你还没有代码,那么可以遵循代码托管系统中的指示,将这个空项目克隆到你本地的开发环境中。然而,如果你在本地已经有了一些代码,那么将会想要上传你已有的代码。要想这么做,你需要建立一个从你的本地仓库到项目托管服务的新连接。在项目仓库的本地副本中,检查你是否已经设置好一个远程连接,如下所示。$ git remote --verbose
如果你从本地启动项目,就不会看到任何远程连接,因此,如果现在什么都没有显示也没关系。如果你为这个仓库设置了一个远程连接,将会看到如下内容。
origin https://github.com:emmajane/gitforteams.git (fetch)
origin https://github.com:emmajane/gitforteams.git (push)
每行都以远程连接(origin)的别名开头,后跟远程仓库的原地址。这些行总是会成对出现:每一对的第一行表明你将从哪里获取新的工作(fetch),而第二行表明你会将新的工作上传到哪里(push)
添加一个远程仓库连接
git remote add nickname project-url
移除一个远程连接
git remote remove nickname
将仓库的本地副本上传到远程服务器
git push nickname branch_name
如果你希望与别人共享所有的本地分支,可以修改这个命令,如下所示
git push --all nickname
fatal: refusing to merge unrelated histories
允许不相关历史提,强制合并
git pull origin master --allow-unrelated-histories