什么是版本库呢?
版本库又名仓库,英文名repository,简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。由于git是分布式版本管理工具,所以git在不需要联网的情况下也具有完整的版本管理能力。
创建一个版本:
1)首先,选择一个合适的地方,创建一个空目录.
2)使用 git init
命令把这个目录变成Git可以管理的仓库:
命令输入后,会提示你,已经创建了一个空的Git仓库。目录下可以发现一个隐藏目录.git
目录默认是隐藏的,用ls -ah
命令就可以看见。
目录就是我们的:工作区,存放所有当前文档。此目录下的文件才会被Git管理
.git目录就是我们的:本地仓库,管理并保存所有的文档变化及历史状态。
总结:创建版本库的步骤:
1) 进入需要管理的目录
2) 执行 git init 命令
版本控制系统,其目的就是跟踪文本文件的改动,例如我们开发时编写的.java、.xml、.properties本质都是文本文件。文件中每一个字符的变化都会被跟踪并且管理。
将一个文件添加到本地仓库,分两步:
1) 使用 git add <file>
命令,添加文件。可以一次添加多个文件。
2) 使用git commit
命令,提交,一次即可。
note:首次使用需要告诉git,当前用户的基本信息
理解Git 工作区、暂存区和版本库概念
工作区:
工作区就是你在电脑里能看到的目录。比如新创建的目录:
其中包含了一个隐藏目录 .git ,其它就是我们需要被管理的文件。
版本库及暂存区:
工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。
Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。
把文件往Git版本库里添加的时候,是分两步执行的:
第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;
第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。
因为创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。
可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。
被版本库管理的文件不可避免的要发生修改,此时只需要直接对文件修改即可。修改完毕后需要将文件的修改提交到版本库。
差异比较:
用git diff -- readme.txt
命令可以查看工作区和版本库里面最新版本的区别
查看状态,提交修改:
我们如果不确定自己的哪些文件被修改了,可以使用git status 命令,查看当前工作区的状态:
可以清楚的看到:changes not staged for commit(修改没有被缓存,需要使用git add来进行添加操作)
我们使用git add 命令,添加到暂存区:
这次提示: changes to be commited (修改需要被提交),我们使用git commit 进行提交
提示说:工作区很干净,没有任何需要提交,搞定
日志查看:
我们通过 git log 命令,可以查看历史的每次提交信息:
如果嫌输出信息太多,看得眼花缭乱的,可以试试加上--pretty=oneline
参数:
可以发现,目前为止,我们已经在本地仓库中提交了3次,也就是说有3个不同版本。其中,最近的这个版本有一个标示:HEAD ,这就是标记当前分支的当前版本所在位置。本例当中,当前版本即 test version control这次提交。
另外,在log中,每一个版本的前面,都有一长串随机字符串:5bc7781319b…192728 ,这是每次提交的commit id ,这是通过SHA1算法得到的值,Git通过这个唯一的id来区分每次提交。
版本回退:
首先,Git通过HEAD来判断当前所在的版本位置。写成HEAD~100。
查看所有关联日志:
可以通过git reflog命令,看到以前的每次执行动作:
通过指定commit id 的方式,来指定HEAD的位置:
指令:git reset --hard {commit id}
总结:
如果要进行版本回退或前进,一般分两步:
1) 通过git log 或 git reflog 查看操作日志吗,查找版本的commit id
2) 通过 git reset --hard 设置HEAD到指定版本
其实版本的回退,仅仅是修改HEAD指针的位置而已,因此Git进行版本的切换,比svn要快的多!
撤销工作区修改
查看状态,Git提示我们,现在文件已经修改,等待被staged(暂存)。我们有两个选择:
1) 可以使用git add 来添加到暂存区,接着去提交文件
2) 可以使git checkout -- 来撤销修改
这里我们选择第二种方案,即可看到修改已经被撤销了!
撤销staged(暂存区)修改
查看状态,有修改等待被提交,并且有一行提示,可以使用 git reset HEAD 来撤销缓存修改。
前面说过,git reset 命令可以进行版本回退,此处reset 指定的是HEAD ,而不是其他版本,因此就有撤销缓存修改的作用:
查看状态,发现文件的修改被撤回到了工作区,尚未添加到staged(暂存区),我们再次执行git checkout -- 即可撤销工作区修改
总结:
撤销修改分两种情况:
1) 撤销工作区修改,使用git checkout --
2) 撤销暂存区修改,分两步:
a) 使用git reset HEAD 来撤销暂存区修改。
b) 使用git checkout -- 来撤销工作区修改
上述基本使用是在本机利用git进行文件版本管理,但是如果要想进行多人协作,我们就必须使用远程仓库。将本地仓库的数据同步到远程仓库,实现多人协作开发。
目前比较热门的代码托管社区:GitHub,码云
当然,也可以自己来搭建远程仓库。
填写项目信息,仓库创建完毕,可以看到,如果要与远程仓库同步,这里支持两种不同的通信协议,ssh和http。当我们选中一种协议后,后面会出现对应的远程仓库地址。
推荐使用ssh协议。因为http速度较慢,并且每次都需要验证用户名和密码。
但是要使用SSH协议进行同步,你就必须生成SSH密钥,并添加到当前仓库的许可列表中!
什么是ssh?
SSH是英文Secure Shell的简写形式。通过使用SSH,你可以把所有传输的数据进行加密,这样"中间人"这种攻击方式就不可能实现了,而且也能够防止DNS欺骗和IP欺骗。
使用SSH,还有一个额外的好处就是传输的数据是经过压缩的,所以可以加快传输的速度。SSH有很多功能,它既可以代替Telnet,又可以为FTP、Pop、甚至为PPP提供一个安全的"通道"。
如果一个用户从本地计算机,使用SSH协议登录另一台远程计算机,我们就可以认为,这种登录是安全的,即使被中途截获,密码也不会泄露。
最早的时候,互联网通信都是明文通信,一旦被截获,内容就暴露无疑。1995年,芬兰学者Tatu Ylonen设计了SSH协议,将登录信息全部加密,成为互联网安全的一个基本解决方案,迅速在全世界获得推广,目前已经成为Linux系统的标准配置。
Git-bash中已经集成了ssh功能,所以我们只需要简单的命令,即可生成密钥:ssh-keygen -t rsa
一路回车向下走,不要输入任何内容即可.
执行命令完成后,在window本地用户.ssh目录C:\Users\用户名.ssh下面生成如下名称的公钥和私钥
添加SSH密钥到git私服:
选择用户,个人设置,点击SSH密钥设置,并添加密钥,密钥的内容,就是刚刚生成的两个密钥中的公钥:id_rsa.pub
将这段公钥添加到用户,这就说明,这台公钥的机器具备了访问这个git账号中的远程仓库的权限
关联远程仓库:
git remote add origin [email protected]:taft31/payou.git
推送本地仓库到远程仓库的master分支:
git push -u origin master
需要注意的是:git remote add origin 后面紧跟的,其实是上面的仓库地址信息
执行命令,发现有提示信息,推送成功!此时登录远程仓库,刷新页面,可以看到数据已经推送,并且与本地仓库完全一致
从现在起,只要本地作了提交,就可以通过命令:
git push origin master
把本地master分支的最新修改推送至GitEE,现在就拥有了真正的分布式版本库
现在,假设一个新的成员要加入我们的开发队伍,那他首先要做的第一件事情,一定是从远程仓库获取所有代码。此时就可以使用克隆动作。克隆远程仓库:就是从远程把仓库复制一份到本地,克隆后会创建一个新的本地仓库。
找到一个目录 ,打开控制台,输入命令:git clone 远程仓库地址
现在如果有人也向远程仓库推送了代码,那么当我们就需要拉取 远程仓库的最新代码到本地:
在本地拉取最新代码,使用git pull 命令
总结:
1) 如果要使用SSH协议与远程仓库同步,就必须先在本地生成公钥和私钥,然后将公钥添加到远程的SSH列表
2) 如果已经有本地仓库,并且想要与远程仓库关联,一般需要两步:
a) 通过git remote add origin + 远程仓库地址 进行关联
b) 通过git push -u origin master推送本地仓库修改到远程仓库
c) 通过git pull 命令拉取远程库数据
3) 如果没有本地仓库,先创建远程仓库,然后通过git clone + 远程仓库地址 进行克隆并创建本地仓库
假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。
现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
Git的分支是与众不同的,无论创建、切换和删除分支,Git在1秒钟之内就能完成!无论你的版本库是1个文件还是1万个文件。
我们的每次提交,都对应一个具体的时间点,git会把这许多的时间点串起来,就形成了一条时间线,这条时间线就是一个分支。Git中默认的分支就是主分支,叫master。
我们查看当前的提交日志,发现总共有3次提交,这3次提交可以串起来成一条时间线,就是master分支:
每次提交,master分支都会新增一个时间点,分支线也不断变长。
当我们创建新的分支,例如dev分支,原来指向master的指针,就要指向新的dev分支,相同的时间点(提交点),这样分支就创建好了,你的工作区无需任何改变,创建分支的速度非常的快。
而要切换分支,只需要把HEAD指向dev即可,所以你的分支实现了光速切换!
不过,从现在开始,对工作区的修改和提交就是针对dev
分支了,比如新提交一次后,dev
指针往前移动一步,而master
指针不变:
假如我们在dev
上的工作完成了,就可以把dev
合并到master
上。Git怎么合并呢?最简单的方法,就是直接把master
指向dev
的当前提交,就完成了合并:
所以Git合并分支也很快!就改改指针,工作区内容也不变!
合并完分支后,甚至可以删除dev
分支。删除dev
分支就是把dev
指针给删掉,删掉后,我们就剩下了一条master
分支:
你会发现Git的分支管理,基本就是创建新的指针,改变HEAD指向,删除指针等操作,几乎没有文件的增删。所以速度非常快!
创建分支:
可以使用 git checkout -b 分支名 来创建并切换到新的分支:
注意到我们已经切换到了dev分支。 git checkout 加上 -b 参数,就等同于创建分支,并切换分支。相当于以下两条命令:
git branch dev # 创建分支
git checkout dev # 切换到具体分支
使用git branch 查看所有分支,当前分支前面会有一个*表示:
合并分支:
使用git checkout master切换回master分支,查看内容:
发现readme并没有改变,因为刚才修改的是dev分支。此时的HEAD已经指向了master了:
我们使用git merge dev命令将 dev分支的修改合并到master分支:
git merge命令用于合并指定分支到当前分支。合并后,再查看readme.txt的内容,就可以看到,和dev分支的最新提交是完全一样的。
删除分支:
合并完成后,就可以放心地删除dev分支了,可以使用git branch -d dev 命令删除dev分支,dev就是具体的分支名
总结:
1) 使用git branch 分支名 创建分支
2) 使用git checkout 分支名 来切换分支
3) 也可以使用 git checkout -b 分支名 来完成 创建并切换分支的操作
4) 使用git merge 分支名 来合并分支到当前分支
5) 使用git branch -d 分支名 来删除指定分支,注意:要删除一个未合并的分支。需要使用-D参数进行强制删除
制造冲突
现在我们新建一个分支dev,然后修改readme.txt,将dev的修改提交;
切换到master分支,并且在readme.txt最后添加内容,提交数据:
master和dev都有了各自新的提交,就出现冲突
解决冲突
这种情况下,是无法进行快速合并的。我们试一下,自动合并失败,必须先解决文件冲突,才能提交。
此时查看readme.txt文件,我们可以根据实际情况进行冲突解决,比如两者都保留,然后再次提交:
工作区就干净了。此时master和dev分支线就变成了这样:
可以用git log --graph --pretty=oneline --abbrev-commit命令来查看:
接下来就可以删除dev分支了。
现在假设这样一个场景,我们正在dev上进行开发,代码进行了一半,突然线上出现了一个紧急BUG需要你进行修复。我们可以创建一个新的hotFix分支来进行修复,但是等等。我当前的dev分支还有未提交的修改,怎么办?如果直接切换到新的分支,那么当前dev 的工作就白费了!有同学可能会说,我们先把代码提交啊,回头再接着写。但是这样的提交是不合理的,也没有任何实际意义的,因为你的代码并没有完成,你提交的是不完整的功能!
git给我们提供了stash功能,可以把当前工作区的内容进行“快照”,等待以后恢复使用。
实际操作,首先,我们创建并切换到dev分支:
修改readme.txt,查看状态,显然,readme有修改待提交。但是现在我们需要去进行紧急的BUG修复,没办法继续开发dev。
我们使用stash命令:
发现工作区已经被保存起来了,并且工作区再次变成了clean,可以放心的去新的分支修复BUG了。
我们切换回master,现在假设你修复完成了BUG,bug修复完成,然后我们再次回到dev分支去查看:
工作区是干净的,我们之前的修改去哪里了呢?通过git stash list命令来查看
工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下,有两个办法:
1) 方式1:用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除;
2) 方式2:用git stash pop,恢复的同时把stash内容也删了:
3) 注意,以上指令都可以在后面紧跟 stash的序号,来操作指定stash,例如: git stash apply stash@{0}
现在Git的客户端工具非常多,比较流行的例如:TortoiseGit(在svn中俗称小乌龟)、SourceTree。
安装和使用的原理和git命令方式是类似的,不再赘述.需要做一些配置,具体参考源文档.
l 空行或是以#开头的行即注释行将被忽略。
l 可以在前面添加正斜杠/来避免递归,下面的例子中可以很明白的看出来与下一条的区别。
l 可以在后面添加正斜杠/来忽略文件夹,例如build/即忽略build文件夹。
l 可以使用!来否定忽略,即比如在前面用了*.apk,然后使用!a.apk,则这个a.apk不会被忽略。
l *用来匹配零个或多个字符,如*.[oa]忽略所有以".o"或".a"结尾,*~忽略所有以~结尾的文件(这种文件通常被许多编辑器标记为临时文件);[]用来匹配括号内的任一字符,如[abc],也可以在括号内加连接符,如[0-9]匹配0至9的数;?用来匹配单个字符。