一. Git 介绍

Git作为一款分布式的版本控制工具,作为一名程序员,是必须要掌握的.

最初由林纳斯·托瓦兹(Linus Torvalds)创作,于2005年以GPL发布。最初目的是为更好地管理Linux内核开发而设计.后来git内核已经成熟到可以独立地用作版本控制,使的很多著名的软件都开始使用git进行版本控制.

了解更多,可点击

维基百科 - Git廖雪峰的Git博客

原文地址

目前Git有很多桌面应用,可以方便实现各种功能,比如我推荐的GitKraken,但是如果要更好使用桌面应用,对于下文中的概念好命令还是很有必要的.

1.1 Git和SVN的对比

分布式的集中式的
把内容按元数据(记录改动)方式存储按文件
下载后,脱网也可查看全部log需要联网需要联网查看log
没有一个全局的版本号
内容存储使用的是SHA-1哈希算法,内容完整性要优于SVN差于Git

二. Git入门

2.1 基础概念介绍

git共有四个

  • 工作区(Working Area)
  • 暂存区(Stage)
  • 本地仓库(Local Repository),仅自己可见
  • 远程仓库(Remote Repository),全组成员可见

同时还有五种状态

  • 未修改(Origin)
  • 已修改(Modified)&未追踪(Untracked)
  • 已暂存(Staged)
  • 已提交(Committed)
  • 已推送(Pushed)

一篇入门 -- Git-LMLPHP

对于图上的所有命令,之后都会有详细的介绍,各位不要捉急.

2.2 Git的安装和GitHub的使用

Git的安装就不介绍了,网上很多.

GitHub是通过Git进行版本控制的软件源代码托管服务,并且它免费,在小组开发时,我们可以将代码托管到GitHub上,非常方便.官网https://github.com/

2.3 创建版本库

版本库,说白了就是我们要交给Git管理的文件夹.

$ cd ~/workspace/git/
$ git init

命令行进入到目标目录,然后输入git init,就会在当前目录下生成一个.git的文件夹,此时,这个版本库就创建好了.所有在此目录下和子目录下的文件改动,都会被git发现..git文件夹就是这个工程的本地版本库.

我们也可以直接在GitHub clone代码到本地,方法如下:

$ git clone git@github.com:michaelliao/bootstrap.git

2.4 基本使用

此时在git目录下,新建文件helloWorld.scala.然后想继续修改,但是怕最近的修改出错,想可以随时回到当前状态,那要怎么做呢?如果我想让这个开发的文件,在小组内的其他成员也见,又要怎么做呢?

git add => 暂存区

第一步就是先将要保存的文件加入暂存区:

$ git add helloWorld.scala

git status 状态查看器

使用下面命令,可以查看当前工作区的修改情况:

$ git status

Git非常清楚地告诉我们,helloWorld.scala还从来没有被添加过,所以它的状态是Untracked。如果我们已经添加过helloWorld.scala文件,在对其进行修改,那么它就是modified状态

git checkout 放弃修改

如果想放弃现在没有add,可以使用下面命令,将工作区的改动还原

$ git checkout -- helloWorld.scala

可是,如果add之后,又想放弃目前暂存区的更改呢?,就要使用下面的语句,然后重复上面的步骤.

git reset HEAD helloWorld.scala

git commit => 本地仓库

第二步,将缓存区的内容提交到本地仓库中:

$ git commit -m "wrote a hello file"

此时,我们已经将本次更改保存到本地仓库,现在就可以放心大胆的继续开发hello了.因为我们已经有了一个现在时间的代码快照,随时可以回到快照.

现在可以使用git status查看一下当前什么状态.

git push

但是当前的hello文件只有你自己可见,要想小组的其他人也能看到并且编辑,需要将它同步到远程.方法如下:

$ git push origin master

这样就讲本地仓库中提交的代码push到远程的master分支上了.此时,其他小组成员就可以看到你之前编写的文件并且可以同步和修改了.

git pull

那么其他成员如何下载你的最新代码呢?使用如下命令即可:

$ git pull

三. 版本穿越

如果想要回到之前的某个版本,应该如何做呢?

3.1 git历史书

首先应该查询我之前的所有操作,通过命令:

$ git log

查看最近的操作,但是内容有些多,可以加上参数--pretty=oneline简化输出信息.

输出信息类似下面:

f078079284c567571286ef7a168f095af9acdd03 (HEAD -> Clock-0704, tag: v1.0, origin/Clock-0704) Merge remote-tracking branch 'origin/alex-0704' into Clock-0704
bbb87262b8d8d61d85fc6579ab46527e431ee176 (origin/alex-0704) 修复无限bug
4f0a047023f8a4902f40f03fb5f6040775a9e1ee Merge remote-tracking branch 'origin/alex-0704' into Clock-0704

知道了历史书,我们就可以定位具体的穿越位置,使用

$ git reset --hard 4f0a047023f8a4902f40f03fb5f6040775a9e1ee

我们就穿越成功了.

此时有个语法糖, 如果我们只是想返回最近的上一次提交,可以使用

$ git reset --hard HEAD^

返回上上次呢git reset --hard HEAD^^,上三次呢?

没错,是你想的那样,但是有别的方法git reset --hard HEAD~3,不然100次不是很累.

使用git log查询一下历史,已经看不到4f0a之后的提交了.我们穿越成功了,但是git log没有了之后的commit id,如果我们反悔了,岂不是回不去了.

3.2 git万年历

不要怕,在Git中,总是有后悔药可以吃的,Git提供了一个命令git reflog用来记录你的每一次命令:

$ git reflog

输出信息类似下面:

f078079 (HEAD -> Clock-0704, tag: v1.0, origin/Clock-0704) HEAD@{0}: commit (merge): Merge remote-tracking branch 'origin/alex-0704' into Clock-0704
4f0a047 HEAD@{1}: commit (merge): Merge remote-tracking branch 'origin/alex-0704' into Clock-0704
89529a2 HEAD@{2}: commit: report over

再次穿越:

$ git reset --hard f078079

没错,我又回来了.

3.3 代码回滚的讲解

代码回滚的方式分三种Reset、Checkout、Revert

那么应该如何使用和选择呢?

reset 详解

reset 是移动HEAD指针,如果HEAD指针指向了之前提交的commit id,就等于放弃了近期的代码更改,回到了当时,如上面的例子,但是它比想象的要强大,同时支持几个参数,如下:

  • --mixed – 默认选项。缓存区和你指定的提交同步,但工作目录不受影响,也就是缓存区移入工作区,然后 工作区 > 本地仓库
  • --soft – 缓存区和工作目录都不会被改变,也就是最近修改优先
  • --hard – 缓存区和工作目录都同步到你指定的提交,也就是本地仓库优先

checkout 详解

主要是处理工作区的修改

revert 详解

创建一个修改来修改之前的提交,不会影响历史,是最安全的回滚办法.好像Ctrl + Z

文件层面操作

git resetgit checkout 命令也接受文件路径作为参数。这时它的行为就大为不同了。它不会作用于整份提交,参数将它限制于特定文件。

如:

git reset HEAD~2 helloWorld.scala

会把最近的第二次提交中的helloWorld.scala文件提取出来放到暂存区.

而:

git checkout HEAD~2 helloWorld.scala

会把最近的第二次提交中的helloWorld.scala文件提取出来放到工作区.

总结

  • git revert当做Ctrl + Z,它不会修改历史记录,并且会生成记录.所以执行之前,要先stash.
  • git reset HEAD用来撤销没有提交的更改.
  • git checkout主要是处理工作区中没有add的修改
git reset提交层面在私有分支上舍弃一些没有提交的更改
git reset文件层面舍弃缓存区中的更改
git checkout提交层面切换分支或查看旧版本
git checkout文件层面舍弃工作目录中的更改
git revert提交层面在公共分支上回滚更改
git revert文件层面(然而并没有)

四. 分支概念

如果对于同一个项目,领导要张三去开发新功能的同时,要李四去修复当前版本的一个bug呢?

这时,我们就要做一个比穿越时间更神奇的事情了,就是创建平行宇宙 => 分支.

分支可以将某个时间节点的代码分别放到两个平行线上,这两条线上的开发互不影响,只有在两者需要合并的时候重合即可.

4.1 分支基础

在版本回退里,你已经知道,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master分支。HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。

就像这样:

一篇入门 -- Git-LMLPHP

当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:

一篇入门 -- Git-LMLPHP

你看,Git创建一个分支很快,因为除了增加一个dev指针,改改HEAD的指向,工作区的文件都没有任何变化!

git checkout

首先,我们创建dev分支

$ git branch dev

查看现在都有哪些分支:

$ git branch
* master
dev

然后切换到dev分支:

$ git checkout dev

也有语法糖,可以二步合一

$ git checkout -b dev

现在我们在dev分支已经开发完了,要把代码统一回master分支.要先切换回master

合并分支:

$ git checkout master
$ git merge dev

此时,用

$ git log --graph --pretty=oneline --abbrev-commit

可以查看分支合并过程

合并完成,删除之前的dev分支

$ git branch -d dev

五. 番外

5.1 stash 暂存

可以缓存工作区的修改到stash中,那在什么场景使用呢?

比如,你正在开发新模块,但之前分支中有改动,需要合并分支,可是你们项目中有一些配置文件,如数据库配置等你连接到了自己的数据库上,每次合并分支还要重新修改,很麻烦,这时就可以先stash,然后merge,在把stash中的文件修改提取出来.

如何提取:

$ git apply
$ git pop

可以使用git stash list查看全部stash列表.使用git stash apply stash@{0}恢复指定stash

5.2 Rebase 变基

不是很好理解,就当做是最后一次的 merge 吧。

因为rebase 后,两个分支的修改记录都会合并为一个分支的记录,好像另一个分支从来没有出现过一样。

5.3 tag 标签管理

可以理解为commit的收藏功能.

$ git tag v1.0

使用上面命令,就会在最近的commit上创建一个tag,也可以使用 git tag v1.0 commitId给指定commit打tag

删除标签

$ git tag -d v1.0

从远程删除。删除命令也是push,但是格式如下:

$ git push origin :refs/tags/v0.9

5.4 .gitignore 忽略文件

忽略文件的原则是:

  1. 忽略操作系统自动生成的文件,比如缩略图等;
  2. 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
  3. 忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。

5.5 设置别名

$ git config --global alias.co checkout
$ git config --global alias.ci commit
$ git config --global alias.br branch
04-18 21:15
查看更多