一篇入门 -- Git

一. Git 介绍

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

了解更多,可点击 维基百科 - Git

廖雪峰的Git博客

Git虽然是文件的版本控制工具,但它所管理的并不是文件,比较贴切的说,它控制的应该是文件的修改.

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

1.1 Git和SVN的对比

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

二. Git入门

2.1 基础概念介绍

git共有四个

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

同时还有五种状态

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

image

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

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

注意,add可以一次提交多个文件,如git add a.scala b.scala,也可以多次提交,如git add a.scala git add b.scala.

如果使用git add --all,提交当前工作区的全部更改到暂存区
如果使用git add .,提交当前目录下的全部更改到暂存区

git status 状态查看器

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

$ git status

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

git checkout 放弃修改

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

$ git checkout -- helloWorld.scala

注意,这个--是必须要有的,不然它就会切换分支了,分支概念后面说.

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

git reset HEAD helloWorld.scala

关于 reset 和 HEAD 的概念,之后会讲到.

git commit => 本地仓库

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

$ git commit -m "wrote a hello file"

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

-m后面的String,是对于本次commit的备注,可以没有,也可以使任何内容,但建议是一段可描述本次提交的语言,方便日后自己追溯,也方便其他人可以了解本次提交,方便他大胆的更新或合并你本次的代码.关于更新和合并下文讲.

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

git push

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

$ git push origin master

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

git pull

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

$ git pull

如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>

三. 版本穿越

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

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

第一个空格前面的一串数字,就是那次commit的commit id(提交版本号),和SVN不一样,Git的commit id不是1,2,3……递增的数字,而是一个SHA1计算出来的一个非常大的数字,用十六进制表示,而且你看到的commit id和我的肯定不一样,以你自己的为准.为什么commit id需要用这么一大串数字表示呢?因为Git是分布式的版本控制系统,后面我们还要研究多人在同一个版本库里工作,如果大家都用1,2,3……作为版本号,那肯定就冲突了.

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

$ git reset --hard 4f0a047023f8a4902f40f03fb5f6040775a9e1ee

我们就穿越成功了.

reset 的 --hard参数后面讲解

最后跟的commit id不用写全,git会自动查询,但也不要写的太少,推荐写七位左右

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

$ 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

没错,我又回来了.

这里要说一下,git切换分支的速度非常快,因为git把你的每次commit编程了链表的一个点.内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD换个指向而已.

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指向的就是当前分支。

就像这样:
image

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

image

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

git checkout

首先,我们创建dev分支

$ git branch dev

查看现在都有哪些分支:

$ git branch
* master
  dev

有*号的表示是当前分支

然后切换到dev分支:

$ git checkout dev

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

$ git checkout -b dev

参数-b就是分支不存在,则创建分支

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

$ git branch master
$ git merge dev

此时,用

$ git log --graph

可以查看分支合并过程

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

$ git branch -d dev

五. 番外

5.1 stash 暂存

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

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

如何提取:

$ git apply
$ git pop

这两句的功能一样,都是拿出刚才stash的文件修改,区别在于,apply只是提取,不会删除刚才的stash,而pop就像弹栈一样,在提取的同时删除了stash.

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

5.2 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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值