从根上学习 Git

用户头像
书旅
关注
发布于: 2020 年 08 月 18 日
从根上学习Git

什么是git?

Git是目前世界上最先进的分布式版本控制系统

什么是版本库?

版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”

SVN是集中式版本控制系统,Git是分布式版本控制系统,集中式和分布式区别?

集中式版本控制系统,版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。中央服务器就好比是一个图书馆,你要改一本书,必须先从图书馆借出来,然后回到家自己改,改完了,再放回图书馆

集中式版本控制系统最大的毛病就是必须联网才能工作,如果在局域网内还好,带宽够大,速度够快,可如果在互联网上,遇到网速慢的话,可能提交一个10M的文件就需要5分钟,这还不得把人给憋死啊

分布式版本控制系统根本没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,你工作的时候,就不需要联网了,因为版本库就在你自己的电脑上

既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?比方说你在自己电脑上改了文件A,你的同事也在他的电脑上改了文件A,这时,你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。和集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了

如何创建一个版本库?

很简单,创建一个空的文件夹,进入到文件夹中执行git init,即,将该文件夹变成Git可管理的仓库

Git基本操作

添加一个文件

提示:添加的文件必须放在你初始化的这个目录或其子目录下

将一个文件添加到仓库需要两步:

git add filename
git commit -m '备注'
查看当前工作区状态
git status
查看修改内容
git diff filename
查看提交日志(是按时间的由近到远的顺序)
git log



如果觉得显示的结果太长,可以在后边添加--pretty=oneline参数,最前边的一长串是git的版本号

git中的版本回退

首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD表示当前版本,也就是最新的提交,上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100

比如要回退到上一个版本

git reset --hard 版本号
查看每一次执行过的命令,即查看历史命令
git reflog
工作区和暂存区的概念

工作区:就是你在电脑上能看见的目录

版本库:在我们的工作区中会有一个隐藏的目录.git,这个就是git的版本库

Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD

前面提到我们把文件往Git版本库里添加的时候,是分两步执行的

  • 第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区

  • 第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支

因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改

你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改

假设现在我修改了readme文件,并添加了一个LICENSE文件,也就是执行完了git add,但是还没执行git commit

现在暂存区就是这个状态

所以,git add命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行git commit就可以一次性把暂存区的所有修改提交到分支

执行完git commit之后,版本库就变成了下边这样

撤销修改

场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file(这里的--一定要加上,不加上就表示切换分支了)

场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD file,就回到了场景1,第二步按场景1操作

场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,可以使用上边说到的版本回退,不过前提是没有推送到远程库

删除一个文件

删除一个文件也分两步:

git rm filename
git commit -m

分支管理

创建与合并分支

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

一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点

每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长

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

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

不过,从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变

假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并

所以Git合并分支也很快!就改改指针,工作区内容也不变!

合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支

命令总结

创建dev分支并切换
git checkout -b dev
查看当前分支
git branch
git branch命令会列出所有分支,当前分支前面会标一个*号
分支合并
git merge 分支名
git merge命令用于合并指定分支到当前分支
创建分支:git branch 分支名
切换分支:git checkout 分支名
创建并切换分支:git checkout -b 分支名
合并某分支到当前分支:git merge 分支名
删除分支:git branch -d 分支名
分支管理策略

在实际开发中,我们应该按照几个基本原则进行分支管理

  • 首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活

  • 那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本

  • 你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了

所以,团队合作的分支看起来就像这样

多人协作

当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin

要查看远程库的信息,用git remote或者查看更详细的信息,使用git remote -v,会显示出可以抓取和推送的origin地址,如果没有推送权限,就看不到push地址

推送分支

推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上

git push origin master

如果要推送其他分支,比如dev,就改成

git push origin dev

抓取分支

多人协作时,大家都会往master和dev分支上推送各自的修改

现在,模拟一个你的小伙伴,可以在另一台电脑(注意要把SSH Key添加到GitHub)或者同一台电脑的另一个目录下克隆

当你的小伙伴从远程库clone时,默认情况下,你的小伙伴只能看到本地的master分支

现在,你的小伙伴要在dev分支上开发,就必须创建远程origin的dev分支到本地,于是他用这个命令创建本地dev分支

git checkout -b dev origin/dev

现在,他就可以在dev上继续修改,然后,时不时地把dev分支push到远程

你的小伙伴已经向origin/dev分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送

推送失败,因为你的小伙伴的最新提交和你试图推送的提交有冲突,解决办法也很简单,Git已经提示我们,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送

git pull也失败了,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置dev和origin/dev的链接

这回git pull成功,但是合并有冲突,需要手动解决,解决的方法和分支管理中的解决冲突完全一样。解决后,提交,再push

因此,多人合作的工作模式通常是这样

  1. 首先,可以试图用git push origin branch-name 推送自己的修改;

  2. 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;

  3. 如果合并有冲突,则解决冲突,并在本地提交;

  4. 没有冲突或者解决掉冲突后,再用git push origin branch-name推送就能成功!

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

小结

  • 查看远程库信息,使用git remote -v

  • 本地新建的分支如果不推送到远程,对其他人就是不可见的;

  • 从本地推送分支,使用git push origin branch-name,如果推送失败,先用git pull抓取远程的新提交;

  • 在本地创建和远程分支对应的分支,使用git checkout -b branch-name origin/branch-name,本地和远程分支的名称最好一致;

  • 建立本地分支和远程分支的关联,使用git branch --set-upstream branch-name origin/branch-name

  • 从远程抓取分支,使用git pull,如果有冲突,要先处理冲突

标签管理

发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照

Git的标签虽然是版本库的快照,但其实它就是指向某个commit的指针(跟分支很像对不对?但是分支可以移动,标签不能移动),所以,创建和删除标签都是瞬间完成的

创建标签

首先切换到要打标签的分支上,然后使用git tag tagName命令

查看分支上有哪些标签:git tag

查看标签信息:git show 标签名

默认标签是打在最新提交的commit上的。有时候,如果忘了打标签,比如,现在已经是周五了,但应该在周一打的标签没有打,怎么办?

方法是找到历史提交的commit id,然后打上就可以了

比方说要对add merge这次提交打标签,它对应的commit id是f52c633,敲入命令

git tag v0.9 f52c633

注意:标签总是和某个commit挂钩。如果这个commit既出现在master分支,又出现在dev分支,那么在这两个分支上都可以看到这个标签

git tag -a tagname -m "blablabla..."可以指定标签信息

管理标签
  • 命令git push origin tagname可以推送一个本地标签;

  • 命令git push origin --tags可以推送全部未推送过的本地标签;

  • 命令git tag -d tagname可以删除一个本地标签;

  • 命令git push origin :refs/tags/tagname可以删除一个远程标签

参考:

https://www.liaoxuefeng.com/wiki/896043488029600



发布于: 2020 年 08 月 18 日阅读数: 45
用户头像

书旅

关注

公众号:IT猿圈 2019.04.11 加入

还未添加个人简介

评论

发布
暂无评论
从根上学习Git