前言:
git是分布式的版本库控制系统,它能方便你将自己的代码寄存于远程服务器上,从而实现集体合作开发。git有GUI
图形界面,然而使用终端命令仍是主流。以下基于Ubuntu系统操作git(其方式也适用于windows等系统),实现git的基
本安装,关联github账户,本地创建及更改库,分支,连接远程版本库。
本文章讲解了一般需要用到的命令,以及更重要的——git管理代码的机制。由于本人知识有限,若有错误,望
指出和谅解。
第一节 git安装:
ubuntu下的安装:
ubuntu默认没有安装git,alt+ctrl+t打开终端,运行下面命令来安装:
检查是否安装成功:
未报错就代表成功安装。
第二节 git配置设置:
以下设置只要设置一遍,以后不用重新设置
用户名及邮箱:
其他配置:
启用git输出显示颜色
设置git编辑器(默认为vim)
第三节 关联github账户:
获取ssh密钥:
之后一路回车即可
密钥保存在home/.ssh/id_rsa.pub文件里(home目录下.ssh是隐藏的,按快捷键ctrl+h显示)
添加密钥至github:
复制密钥,打开浏览器登录你的github账户,在setting>SSH and GPG keys 里添加密钥即可。
第四节 本地版本库创建:
提示:使用git --help或者git 命令 --help获取帮助。
git三大工作区域:
1.工作目录:即本地目录,是可以看到的,供用户编辑更改代码文档。
2.暂存区:所在目录为.git/index,所有工作目录中的更改要先提交到暂存区,才能让版本库接受。
3.版本库:所在目录为.git/objects。是所有代码的集中区域,记录用户提交的更改。
创建空的本地版本库:
git init 命令:将普通文件夹mypro初始化为空的版本库。
执行以上命令后会发现mypro文件夹下出现一个隐藏文件夹.git,这就是git仓库储存文件的地方。
也可以删除库(读者可以不删除它,因为下面还需要一个空仓库)
实际上就是把mypro文件夹删掉即可
克隆远端版本库:
这是创建本地版本库的另一种方式,也是最常用的方式,用此方法我们可以克隆他人的版本库进行学习
和使用,我们这里还是以自己github上的版本库为例。
git clone +仓库地址就可以克隆了,当然你也可以为克隆的版本库重命名(如果没删除过mypro,可以重
名为成其他名称):
`
第五节 更新本地版本库:
创建与更改文件:
在工作目录mypro下新建一个名为hello的文档
终端下输入git status 查看工作目录的状态:
发现有红色的hello,说明虽然我们创建了hello文档,但属于未跟踪状态,但版本库并未记录下这次创建.
下面,我们进行同步(正如前面所说,要将更改提交到库,必须先提交到暂存区):
git add +文件名:提交更改到暂存区。
git commit -m "更改说明" :将暂存区的所有更改提交到库。
当提交到暂存区后,出现“新文件:hello”,表示更改已经被跟踪记录,但还未正式提交到库,git status比较的
永远是版本库与工作目录的不同。
让我们在正式提交更改后再看一下工作区状态:
`
因为此时版本库与工作目录是同步的,所以工作区状态为无。
接下来,我们来更改hello文件内容,添加"hello!":
请读者自行查看工作区状态(在大型开发中这是非常必要的),这次显示的是“更改:hello”,所以可以看出,git
对于更改是有明确分类的。
请读者将更改提交到库,注释为“fill the context”。
查看更改提交历史:
1.git show 可以查看最近一次更改的具体内容:
commit这一行是为更改分配的指针(专业名叫sha-1值,下文都以sha-1值称呼),它指向这次更改。
“fill the context “是用户添加的更改的说明。
---a/hello代表更改前的hello被代替了.
+++b/hello代表更改厚的hello,下面两行是更改的内容,第一行可以知道被更改的行数,最后一行表示增加了
"hello!"一行。
2.git log 可以查看历史记录(输入wq后即可退出查看):
这里显示我们一共更改了两次,commit 后面是指向本次更改的sha-1值。
回退操作:
有时候,我们想撤销提交,git有强大的回退操作:
1.从版本库回退到暂存区(相当于撤销git commit操作):
其中HEAD是一个指针,在我们回退前,它指向最后一次更改的指针(24ffa3c...读者可在上文看到这个指针),
DAED~1 表示指针回退一个历史点,指向上一次更改的指针(ce169e0...读者可以使用git log查看,可以发现ce169e0...
变成了第一个)(注意:读者的指针同我的是不同的)。
下面请读者输入git commit -m "fill the context"恢复到回退前(此时会分配一个新的指针),然后在hello文档结尾加一
句“how are you”,最后提交到库,提交说明为“change the context”,之后git log会发现有三次更改。
以上完成后,我们尝试让HEAD回退两个历史点(应该会认为结果同上面的回退是一样的,即hello文档里有“hello!”,hello文
档已提交到暂存区,但未提交到库)。
可以看到,确实回退到了创建文档之后的第一次更改,但是回退时我们的第二次更改却没有被删除。
git diff --cached +sha-1值:可以对比暂存区的文档与库中某一提交点的该文档之间的区别,这里的sha-1值
指向创建文档的更改,那时hello文档还没有内容。
关于如何回退时删除第二次更改,我将在下面讲到,现在让我们提交更改到库,操作如下:
2.回退到工作目录:
成功回退到工作目录,请读者执行git checkout hello命令,放弃更改的提交。
git checkout +文件 适用于工作目录下更改(只能是修改不能是创建)过的,未提交到暂存区的文件的回退。
那么提交到暂存区的文件如何回退到工作目录呢?
当然是用git reset --mixed HEAD了(git reset HEAD默认是--mixed),为什么呢?首先,--mix的作用就是回退到
工作目录,其次HEAD后面没跟参数,表示指向当前的sha-1值(下面实例中为68698...),下面是一个例子:
回退前:
回退后:
那么为什么没有git reset --soft HEAD命令呢?因为当从库回退到暂存状态时,对于库来说,放弃了这次更改,HEAD
就要指向上一次更改(e59b54...),所以这里应该指定HEAD~1。
3.回退至上次更改:
操作前请提交三次hello的更改(请不要嫌麻烦,这至少有利于熟悉git 命令):
第一次添加“hello!”,注释为“fill after mixedreset”;
第二次添加“how are you? ”,注释为“change1 after mixedreset”;
第三次添加“i'm fine.”,注释为“change2 after mixedreset”;
更改结果如下:
我们想彻底放弃第三次更改,而这次更改已经提交到库:
我们看到状态回退到我们第二次提交更改后的时候了。
git log --oneline:oneline参数可以简化信息。
现在可以回答上文提出的多历史点回退问题:
请读者输入下图中的代码:
上面代码中,我们先用hard放弃了第二次的更改,然后再用soft回退,这样去掉了第二次更改,保留了第一次更改。
4.小结:
至此,回退操作已经讲完了,需要注意的是,soft一般用于回退一个历史点(尽管上文举过回退两个的例子,但很容易发现
这样的回退是没有意义的),适用于提交到库的更改;--mixed 一般用于不回退历史点或一个历史点,分别适用于提交到库的
更改和暂存的更改;checkout不回退历史点,适用于未暂存的更改;--hard一般用于回退一个或多个历史点,具有强制性与普适性。
拓展:
1.使用git log -g --oneline(-g参数可以列出所有分配过的sha-1值所对应的更改):
通过上图对比(这些是我们上文操作的所有历史记录,读者可以根据这些记录回忆之前的操作,其中HEAD@{2}和HEAD@{3}是
由于本人操作失误撤销回退产生的,下文也会讲如何撤销回退),可以发现,不加g参数当回退的时候就会丢弃一些记录,而加
上g参数则会显示所有记录(-g代表全局,在之后的分支管理中是很重要的)。
2.使用sha-1值回退:在回退操作讲解中,我们之前使用的都是HEAD~,但也可以使用sha~1值
我们用sha-1值重新实现拓展1中操作,代码输入如下(这里选择了静默执行):
结果如下:
第六节 分支管理:
分支无论在本地库管理,还是大型项目集体开发,都是非常有效的管理方式。
本地创建删除与切换分支:
库在初始化后默认有一个master分支(主分支),事实上我们之前的所有操作都是在master上进行的,那么我们可以在master分支上
衍生出一个小分支(子支),而对于这个子支来说,master就是它的父支。
下面我们就来创建两个新的分支:
git branch 分支名:在当前分之下创建一个子支;
git checkout 分支名:切换到某分支;
git branch:查看所有分支;
上面我们在master下创建了分支dev,dev就是master的一个副本,然后又在dev分支下创建了dev-1分支(*标注标志当前所在分支);
请读者在dev分支下提交hello的修改(之前回退到了暂存区),注释为“fill at dev”:
通过上图可以发现,由于dev是master的一个副本,当在dev下提交修改时,master并不会被影响。
再比较dev与dev-1:
这次我们用上文学过的git diff比较,发现dev-1也没有被影响,因此可以知道,分支间在创建后就各自独立。
我们可以删除不想要的分支,删除前需要切换到另一支分支,否则无法删除(此时我们处于master,要删除Dev-1分支):
分支合并:
有分必有合,我们创建分支的目的有三种:
1.利用创建的分支做一些不确定的更改,而不影响到原来的分支。
2.利用某个分支实现某个功能模块,从而利于版本管理。
3.团队合作中,远程库作为根,并从根上为每个人员分配一个分支。
首先,我们将dev合并到master上(要求当前分支为master,了解两个分支的更改差异):
这样就使得master与dev同步了。
如果要取消合并,可以使用回退操作,如下:
这里的Orig_head指上一次head所指的位置。
可以通过下面命令查看未合并的分支(即存在更改差异的):
第七节 远程操作:
第二节中我们已经关联了github账户,一个github账户能够添加多个ssh秘钥,这样多个人就可以提交到同一个github仓库。
建立连接与推送文件:
首先,要建立连接,并提交文件到github:
git remote add origin 仓库地址:加入远程仓库地址(并没有连接,如果地址有错,将不会发现),将远程仓库定义为origin,注意,如果
执行此命令前已经有过一个origin了,要先执行git remote remove origin删除原来的origin。
下面就可以推送文件了:
git push origin master:表示将本地的master同步到远程origin的master主分支上(你可以看图片最后一行)。
拉取文件:
作为B员工,当有新的项目时,首先要做的是克隆远程库(git clone),而之后当远程库有新的文件提交后,每个员工在提交自己的代码前都
应该先拉取远程库的文件,使本地库得到更新,此时不能使用git clone,而是git pull,如图:
该比命令表示拉取远程库的master分支保存到本地master(由于当前分支为master,所以本地分支省略了),也可以拉取远程库的develop分支并保存到
本地develop分支:
但是,这种方法的拉取并不安全,他会直接覆本地同名文件,而并没有征求用户的同意,下面的方式相对是安全的:
git fetch的格式与pull类似。但是fetch后,你无论是用git status还是git log都看不到拉取的文件,这是因为,fecth建立的是引用,你可以通过
git log FECTCH_HEAD命令查看远程分支的更改,如果决定接受同步,可以输入git merge origin/master来合并分支。
第八节 分支策略与工作流:
本节我们将简单的模拟一个开发项目,以展现git在团队开发中的强大管理功能,在模拟中,读者应将该模拟想象成一个庞大的开发项目,关注
分支策略,分工模式等。
在这之前,我们先来了解最基本的分支策略规定,由于现实开发的需要,每个团队可能采用的分支方式都有差异,但基本上都基于下面的规则:
master:主分支,永远是最稳定的分支,不应该直接在该分支上进行开发,该分支在每一时刻都是作为某个版本的产品供用户使用。
develop:开发分支,从master中分离出来,用于存放开发者写的、相对稳定的、具有一定功能的代码。当该分支功能集全并测试后,合并到master分支。
以上两个分支在远程和本地都存在。
feature:功能分支,开发者的主要阵地,是开发者为开发某个功能模块,而在本地从develop分支分离出的分支,当功能写完后,合并到develop分支,并
删除feature分支。
hotfix:修复分支,当已发行的版本存在bug需要修复时,从master分支分离出,完成修复后,合并到master与develop分支,并删除hotfix分支。
release分支:预发布分支,从develop分离,新版本发布前的调整与测试分支,可以进行细小的bug修复,修复完后,合并到master和develop分支,版本发
布后,删除改分支。
现在,让我们通过一个简单的实例了解分支策略与工作流。
1.boss在github上创建了一个项目仓库,以提交readme.txt初始化仓库,此时已经有master分支了,boss还要给员工们(张三,李四)分配develop分支,这
些都可以在github上直接操作。
2.下面boss,提交了一份已经写好的index.html文件:
3.员工张三,李四分别克隆仓库(克隆只会克隆master分支),并拉取了develop分支(以张三为例):
4.张三,李四分别从本地develop分支上分离出feature分支,并开发自己的功能模块,开发完成后合并到本地和远程的develop分支(以张三为例):
远端状态:
5.假如李四写得较慢,当张三提交develop后,李四需要先将develop再拉取一下,更新自己的develop和feature,才能提交:
得知远程develop有更新后,先更新后commit:
远端状态:
6.此时,版本1的所有功能都开发完毕,boss在本地创建realease分支进行测试,而此时员工又可以进行版本2功能的开发,我们来看一下最终版本1的公布过程:
打上v1.0的标签,以便之后的跟踪:
查看远程状态:
7.然而,正当员工们兴致勃勃地开发下一个版本时,有用户抱怨v1.0的一个bug,而距v2.0成功开发完成还有很长时间。为了尽快解决这个情况,boss决定让张三
放下功能模块的开发,来解决这个bug:
如上图,如果直接先git add再切到其他分支来修复bug,会使得feature3.js也被跟踪,此时明智的方法是使用 git stash:
然后张三就可以修改bug了,在修改完后,合并master分支,并发布v1.1:
终于,bug修复完了,张三又可以高高兴兴地去开发他的功能模块了:
后记:
到此git的学习结束了,但是你只是入门而已,还需要不停地实践与学习去感受git与github的魅力,比如浏览学习github上优秀的开原项目,git高级合并等等。
下面我列出了一些可以参考学习的网站,希望能进一步帮助你:
- https://blog.csdn.net/ouyang_peng/article/details/54981671
- https://www.cnblogs.com/yaks/p/5666400.html
- https://gitee.com/progit/
- http://rogerdudler.github.io/git-guide/index.zh.html
最后,感谢读者的阅读,有不当之处,望请包涵!