写在开头:平时工作主要是用git来管理代码,小乌龟也用的,但是用git还是最多。自然也看了很多关于git的教程,其中廖雪峰老师的应该是最好的,当然还有官网的。虽然看了很多,但是感觉学以致用,得自己总结一遍然后讲述出来才算是真正的学到。所以就开始下面的环节。
一、分布式版本控制系统的对比其他系统的优势
既然要介绍git,总得拿出一些产品的差异化来是吧,不然没什么特色和优点的产品,观众怎么会买账了是吧。git的最大的特色就是版本控制系统,对比以前的本地版本控制系统和集中化的版本控制系统,分布式的控制系统好处不言自喻,本地版本的控制系统简单理解就是每次提交前都要把之前的目录都抄袭一遍,然后记下这次抄袭的时间,下次增加新功能,然后提交前又把上次的又抄袭一遍,可想而知,每次都重复抄袭,难道不会眼晕么。而集中化的版本控制系统出现解决了在不同系统上的开发者协同的工作。诸如CVS和Subversion,这种都会有一个集中管理的服务器,用来保存所有文件的修订版本。那么问题来了,一个服务器,要是这个服务器冒泡了那可怎么办,要是冒泡不听话了,那么所有人都会痛哭流涕,因为有可能你之前的工作都会毁于一旦。这么有风险的事情,怎么才能避免了,于是分布式版本控制系统就横空出世了。像Git、Mercurial等工具就是这样的系统,客户端不是简单提取最新的版本文件快照,而是把整个代码仓库完整的镜像下来,做个对比。用过SVN的都知道版本库是集中放在中央服务器的,想当于班级上只有一个老师,所有学生做完作业后都交到这个老师哪里,老师把所有的学生作业整合在一起,第二天所有学生再把作业领回来,要是老师突然有事没来,那学生就很高兴了,因为作业提交不了。而git就不会让你有这种妄想啦,用git这个班级人人都是老师,你们所有的作业每个人哪里都有,所以作业弄丢了没关系,从你邻桌哪里copy一份接着做,是不是觉得很痛苦,再也不会不交作业那一天了。
二、Git基础
1、安装 Mac、Windows
下载后全局安装next到底。详细配置教程看这里。一些基本的配置这里就不做阐述了,大家点击链接看详情教程跟着操作就行。
2、初始化仓库
第一种方法就是直接在创建的新文件夹中打开git命令行工具,然后输入git init命令。
$ git init
Initialized empty Git repository in F:/learn-git/.git/
就会看到本地上增加了一个名为.git的子目录,这个子目录包含你初始化Git仓库中所有的必须文件,这些文件是Git的骨干。
第二种方法就使用git clone 直接克隆已经存在的项目,一般存在两种种协议:http://和git://。要是你的公司自己搭建了git服务器,那就使用SSH传输协议,这种就是快狠准,但是如果回到家,没有vpn的话远程没有没作用啦。比如下面我从github上克隆一个项目:
$ git clone https://github.com/FANZHETWO/git-learn.git
Cloning into 'git-learn'...
remote: Counting objects: 51, done.
remote: Compressing objects: 100% (27/27), done.
remote: Total 51 (delta 12), reused 48 (delta 12), pack-reused 0
Unpacking objects: 100% (51/51), done.
就会看到本地有一个叫git-learn的项目存在了,是不是非常的简单。
3、文件的状态
在弄懂一些命令前,我们先来了解git的四个状态的生命周期,搞懂了这个对于命令的了解将会更加的透彻。
- Untracked状态
当你初始化一个项目后,在这个项目中新增随意一个文件都是这样的状态,专业点的说就是未被跟踪的文件,注意区别clone一个项目的方式,clone一个项目下的文件都是已经被跟踪的,也就是 - Unmodified状态 未修改状态,也就是当你clone一个项目,你还没有修改或者增加任何代码,那么这个状态就是未修改状态。上面两个状态联合在一起就叫做++工作区++。
- Modified状态 看单词就知道这是修改了的状态,当你增加了一个方法或者修改了其中一些文件,哪些被修改的地方就处于这个状态。
- Staged状态 这个扎状态理所当然就是完成任务后已经提交后的代码状态,这个也叫做暂存区,暂存区这个概念就是分布式控制系统独有的。
4、最常用的一些命令
- git status
此命令就是检查当前文件的状态,从上面的的状态中我们可以知道文件有两种状态,跟踪和未跟踪的。我们分别中初始化和clone两个不同项目中看看这个命令的效果。
$ git status
On branch master
No commits yet
nothing to commit (create/copy files and use "git add" to track)
$ git status
On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
可以看出两个条件下并没有多大的差异,都是很干净没有修改和提交过。
接下来我在初始化的项目中新增一个README文件,并写下一句话。同样在clone下的项目修改README文件,增加一句话。看看,在使用git status有什么差别。
$ git status
On branch master
No commits yet
Untracked files:
(use "git add ..." to include in what will be committed)
README.md
nothing added to commit but untracked files present (use "git add" to track)
$ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: README.md
no changes added to commit (use "git add" and/or "git commit -a")
第一个是初始化的项目,第二个是clone项目,细心的小伙伴就看到了两个得到的结果是不同的。初始化的项目显示的结果是++Untracked files++,意思就是这个文件未被跟踪过,是新建的一个文件,而clone的项目显示的结果,changes not staged 是修改没有提交的。
- git add
上面讲到一个文件未跟踪的状态,那么add这个命令就是发起跟踪的命令。同时将新增或者修改的代码放到暂存区。我们来看下早clone下项目的效果
$ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD ..." to unstage)
modified: README.md
此时已新增的代码就已经放到暂存区了,等待下一步就是提交。这里注意,git add .和git add *效果是一样的,都是全部提交已经修改或者新增的文件。当然你也可以有针对的提交 比如git add README 就只把这一个文件放大暂存区了。
- .gitignore
我们总有一些文件不像纳入Git的管理,比如一些压缩的文件或者一些打包的工具。这时候我们就可以创建一个.gitignore文件,下面对常用的一些方法做出说明。
*.[oa]
*~
第一行告诉 Git 忽略所有以 .o 或 .a 结尾的文件。一般这类对象文件和存档文件都是编译过程中出现的。 第二行告诉 Git 忽略所有以波浪符(~)结尾的文件,许多文本编辑软件(比如 Emacs)都用这样的文件名保存副本
//即使忽略了已.a结束的文件,也不能忽略lib.a
!lib.a
//只在当前目录中忽略TODO文件
/TODO
//忽略build下所有的文件
build/
//忽略doc下第一级以.text结尾的文件
doc/*.txt
//忽略doc下的所有pdf文件
doc/**/*.pdf
- git diff
查看已暂存和未暂存的修改,这个命令平时我用到很少,要注意的地方就是它只是查看已暂存和未暂存的区别。
- git commit
我们在初始化的项目中使用这个方法看看会有什么效果
$ git commit -m "创建新文件"
[master (root-commit) e3eb612] 创建新文件
1 file changed, 1 insertion(+)
create mode 100644 README.md
请记住一点,这个命令提交是放在暂存区的快照,意思就是只有git add 后的才会提交,在git add后修改和创建的文件是提交不了的。从结果信息可以看到,我们目前处于master分支,提交的id是e3eb612,这其实一串哈希值,具体有什么作用后面会讲解到。然后注释是“创建新文件”,一处文件的修订过,增加了一行代码。主要就是这些信息。
惊喜小技巧,程序员最大的敌人就是低效率的工作,我们是不是每次commit都要add一下,这里可以把这两个方法整合在一起,git commit -a -m "注释" ,在commit后面增加个-a,git就会自动把所有已经跟踪过的文件暂存起来,切记,是跟踪过的项目。如果是新建的文件,那还是老师git add一遍把。
- git log
前面我们已经创建并提交了一个README文件,这时候需要查看下当初提交的信息。那么结果如下
$ git log
commit e3eb612cc2faa7c5d5440466ebf997525fadf512 (HEAD -> master)
Author: panqiang <[email protected]>
Date: Mon Jan 1 16:21:25 2018 +0800
创建新文件
默认没有参数的话,git log会按时间提交的先后出现,最近提交的最前面。上面提交的结果参数分别为提交的id。然后当前的分支为master,作者当然是我,还有提交的日期。
git log -p
常用的一个选项就是-p,用来显示每次提交的差异性。当审核代码或者快速浏览某个搭档提交所带来的变化时,这个功能就显示它的强大之处啦。git log --stat
--stat 选项在每次提交的下面列出所有被修改过的文件、有多少文件被修改了以及被修改过的文件的哪些行被移除或是添加了。
3.git log --pretty=oneline
--pretty也是一个常用的选项,主要是用于唤起一些别的钩子,比如跟着=oneline后就是将提交的信息简化成只有commit id和提交说明。
4.git log --pretty=format:"%h - %an,%ar : %s"
最有趣的就是format啦,你想怎样的格式记录下来,那么就能满足你的定制化。
这里的意思反馈的结果为简单的哈希字符串、作者名字、修订的时间以及提交的说明。
- git remote
我们大部分工作肯定要和别人共同完成的,所以远程仓库就显得尤为重要了,那里有你们团队的思想精华的集成。如果你是git init初始化一个项目,而没有配置远程服务器时,你会发现执行git remote会没有任何反应,而clone的项目,则会出现一个origin的单词,这个单词是什么意思,没错,就是你的远程地址,git会默认起名为origin,如果你想看到全名,只需后面添加git remote -v,效果如下:
$ git remote -v
origin https://github.com/FANZHETWO/git-learn.git (fetch)
origin https://github.com/FANZHETWO/git-learn.git (push)
- git remote add
当你写代码非常牛逼了,同时想提交几个远程仓库时,那么添加远程仓库这个方法就非常实用啦,例如你要添加一个名为multi-picker的仓库时,你就可以这样$ git remote add mp https://github.com/FANZHETWO/multi-picker.git,mp就是你为这个远程仓库所取的名字。这时你再查看就会有下面的结果。
$ git remote -v
mp https://github.com/FANZHETWO/multi-picker.git (fetch)
mp https://github.com/FANZHETWO/multi-picker.git (push)
origin https://github.com/FANZHETWO/git-learn.git (fetch)
origin https://github.com/FANZHETWO/git-learn.git (push)
- git fetch [remote-name]/git pull [remote-name]
这两个命令都是从远程拉取数据,但是却有很大的不同,一不小心操作错那就比较麻烦了。git fetch会将那个远程仓库中所有的分支拉取下来,但是不会自动合并或修改你当前的工作,你只能手动了,这会多么痛苦。而git pull则舒服多了,它会抓取远程分支到当前分支,并自动合并。
- git push [remote-name] [branch-name]
作业拿下来了,肯定还得交上去,拿下来的作业修后在提交前一定要先再拉取一遍,因为你不知道是否老师又批改了别的地方,要是批改了你不知道,没有修复,那你就会在同一个地方再次跌倒了。你也可以简写git push 这样默认的远程就是origin.
- git tag
这个命令是查询已有的标签,那么问题来了,标签有什么作用,如果你不经常有,而且敲代码一帆顺风,从来不出现什么问题,那你一定是个大牛,我们平凡人是偶尔会出现不记得自己敲了哪些代码了,发过的包可能也忘记了,这时候之前打过的标签就显得十分重要了,你想想,你可以麻烦找到有问题的那个包,是不是bug很快就会定位到。所以我们平凡人要养成打标签的习惯,也就是给自己多留些退路。哈哈
创建标签主要有两种类型:轻量标签(lightweight)和附注标签(annotated).
他们之间有什么区别:轻量标签,意味着信息量很少,它只是一个特定提交的引用。 而附注则更加详细,它们是可以被校验的;其中包含打标签者的名字、电子邮件地址、日期时间;还有一个标签信息;
下面分别展示轻和重的方法
轻: git tag v1.0
附:git tag -a v1.0 -m 'my first version'
-a 就是附注标签的标志, -m是不是很似曾相识,没错,就是要你写注释。这样你才能更加容易回想起来。
查看:首先可以通过git tag查看你都打了哪些版本的标签,然后再通过git show [版本号]来详细查看信息。例如
$ git show v1.4
tag v1.4
Tagger: panqiang <[email protected]>
Date: Mon Jan 1 21:05:09 2018 +0800
my first version
commit a93493964caf86e849deddf66762f8d222aa7402 (HEAD -> master, tag: v1.4, tag: show, origin/master, origin/HEAD)
Author: panqiang <[email protected]>
Date: Mon Jan 1 17:04:48 2018 +0800
第一次修改
diff --git a/README.md b/README.md
index c2223c2..0744374 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,4 @@
# git-learn
git 学习笔记
+
+增加一个新方法
\ No newline at end of file
上面这个附注标签是不是很详细。
- Git 别名
有没有发现git需要输入的命令很长一串,每次都要打那么多字,完全不符合程序员的工作精神,还好git能让写繁长的命令变得更简短,你想给哪些命令起什么外号,你就可以随便起,但是有一条,你得记住。
例如:
$ git config --global alias.co checkout
$ git config --global alias.br branch
$ git config --global alias.ci commit
$ git config --global alias.st status
以上方法就把常用的几个命令全局设置为间断的缩写。以后你查看跟踪的状态就只需要写git st 就和git status一样。是不是觉得很爽。
三、Git分支
这个章节我们来学习git必杀技特性,也是工作中使用频率最高的几个命令,我们会发现分支不仅仅只是平时开发新功能时才会用到,熟悉分支的管理你会发现这会极大的提高你的工作效率。接下来我们逐步了解。
- 区分master和分支
我们要知道master在git上其实不属于一个特殊分支,master在仓库初始化的时候就会默认存在,所以我们HEAD指针默认是指向master的。
- 创建dev分支
$ git branch testing
$ git branch
dev
* master
创建分支后我们查看git上当前存在master和dev两个分支,那个*就代表HEAD指向。咦,创建dev分之后是不是要在上面工作了,那么怎么切换到dev分支上了。
$ git checkout dev
$ git checkout -b dev
上面第二个命令将git branch 和git checkout在一起,是不是很简单高效。
- 分支的合并
好了,我们在dev分支已经把任务完成了,这时候想把成果合并到master上。仅需两步就可以完成。
$ git checkout master
$ git merge dev
Updating f42c576..3a0874c
Fast-forward
index.html | 2 ++
1 file changed, 2 insertions(+)
在合并的时候,你应该注意到了"快进(fast-forward)"这个词。 由于当前 master 分支所指向的提交是你当前提交(有关 dev 的提交)的直接上游,所以 Git 只是简单的将指针向前移动。 换句话说,当你试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候,只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧——这就叫做 “快进(fast-forward)”。
其实当遇到开发的时候需要修复master上的bug时是最有突出效果的。这时候你就可以切到master上再见一个issue分支用来解决bug,解决完后再合并到master上。
总结:以上三部分对于一个对git不是很熟悉的新手来说完全可以入门啦,掌握上面哪些基础的命令也完全可以应付平时的任务开发啦。