git版本控制系统小白教程(下)

前言:本文主要介绍git版本控制系统的一些基础使用,适合小白入门,因为内容较多,会分为两部分进行分享,查看上部请点传送门

删除文件

​ git删除文件一般有三种情况,第一种是在工作区修改了文件,但是还没有添加到暂存区;第二种是已经添加到暂存区但是还没有提交到版本库中;第三种就是已经提交到版本库中。

撤销工作区的修改

​ 在工作区修改了文件,但还未添加到暂存区,即撤销在工作区对文件的修改动作,在前面查看版本状态的章节,我们已经介绍了一个命令,就是git restore <file>,新版本都会提示使用这个命令,旧版本则是提示使用git checkout -- <file>这个命令,它与新版命令的用法其实没什么区别,一样可以撤销工作区的修改,但这个命令有两种使用情况:

1.如果我们在文件修改后,文件还未添加到暂存区,想要把修改的全部撤回,那么使用该命令可以撤销修改的部分,回到这个文件的上一个版本。

2.如果文件已经添加到暂存区后,再一次修改了文件,使用该命令就可以撤销修改,回到添加到暂存区时的那一个版本。

​ 需要注意的是,git checkout -- <file>命令中有两个--,如果没有了--,它就不是撤销命令了,变成了切换分支命令git checkout,后面的章节中会提到这个命令。

从暂存区移除

​ 我们提交文件到版本库,一般需要两步,先添加到暂存区再提交到版本库,文件添加到暂存区时,我们可能不想提交了,那么就需要把他从暂存区中移除。

​ 前面查看版本状态的章节,我们已经介绍了一个命令git restore --staged <file>,同样的,这是新版的命令,我们安装了新版就使用新版命令就好了,仍使用旧版的同学,就可以使用git reset HEAD <file>这个命令,用法是一样的。

删除版本库的文件

1.git rm 删除文件

​ 一般我们在操作系统上删除文件,就是右键删除,但在git中,你在工作区删除了文件,实际上只是删除了你本地,版本库中该文件仍然是存在的,比如tets_login.py文件,在操作系统右键删除后我们查看一下git状态。

​ git知道你删除了哪些文件,并且工作区和版本库已经不一致了。这时你有两个选择,一是执行git rm <file>,并且执行git commit命令去删除版本库对应的文件,保持工作区和版本库一致。二是使用git restore <file>撤销对工作区的修改,把删除的文件还原,这样也可以保持工作区和版本库一致。

​ 我们也可以不在工作区操作(即右键删除),直接从版本库中删除文件,使用git rm <file>把删除动作添加到暂存区,然后git commit真正地从版本库中删除。我们想要真正地影响到版本库,都需要执行commit,否则它就只是对工作区产生影响。

​ 这里要注意的是,如果我们直接执行了git rm <file>,但还未提交到版本库,想要还原到工作区时,就需要先执行git restore --staged <file>移除暂存区,再执行git restore <file>撤销对工作区的修改。

2.git rm --cached 只删除版本库的文件

​ 使用git rmgit commit之后文件会从本地中删除,且会从版本库中删除。如果我们只想删除版本库的文件保留工作区的,我们可以在删除命令加上一个--cached参数,同样的执行时需要commit一下,如果想撤销,方法同上。我们不需要死记硬背这些命令,查看版本状态时它会提示你可以使用哪些命令。

3.git rm *.txt 删除符合规则的文件

​ 我们在忽略特殊文件的章节有提到过glob模式匹配,这里删除文件也支持模式匹配,比如git rm *.txt意思就是说删除所有的txt文件。

移动文件

​ git可以使用git mv <old_file> <new_file>来移动文件,需要输入旧文件名,新文件位置,新文件位置还可以重命名,如把test.txt移动到doc目录下,并且重命名为readme.txt:

​ 我们第一步执行的时候,提示No such file or directory意思是说没有text.txt文件或者目录不存在,因为我们目录没有创建,因此需要手动创建一个目录或者直接使用linux命令创建一个目录,就可以移动成功了,移动后记得要执行commit才会对版本库生效。

​ git这一个移动文件的命令实现原理很简单,其实就是执行了下面三个动作:

  • mv <old_file> <new_file>:linux移动命令
  • git rm <old_file>:删除旧文件
  • git add <new_file>:添加新文件到暂存区

版本补录

​ 什么是版本补录,就是我们在修改文件提交版本时遗漏了某些文件的提交,或者有了新的修改但又不想重新提交一个版本,希望补录在上一个版本中。

​ 我们只需要在执行commit时增加一个--amend参数,即git commit --amend,如提交了一个版本后,再次修改了readme.txt,然后执行该命令进行补录。

​ 上面补录时加了-a参数直接一步到为提交到了版本库,提交到版本库后会进入版本信息的vim(vi)界面,vim是unix系统文本编辑器,学过linux的同学应该都有了解。这里它进入vim界面我们可以修改增加版本的一些备注信息,如果不需要修改,可以通过按下Esc退出编辑状态,然后连按两次大写字母Z,就可以退出vim了。

​ 退出vim模式后,我们再查看一下版本信息就会看到最近的一个版本有两次提交记录,其中一次是补录的。

版本标签

​ git标签(tag)的作用主要是快速地定位版本以及版本间的区分,毎次发布一个版本时,我们通常都会在版本库中打上一个标签,标签可以跟踪到版本。之后不管在什么时候,我们都可以通过一个标签,把对应的历史版本取出来。因此,标签实际上就是版本库的一个快照。

​ 但你可能回想,我们commit的时候不是生成了一个版本唯一id吗?版本id是一连串的字符,不容易记住且无法辨别是哪一个版本。总而言之,标签就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起。

附注标签

​ git使用的标签有两种类型:轻量标签(lightweight)和附注标签(annotated)。附注标签实际就是存储在版本库中的一个独立对象,它有自身的一些校验信息,包含标签名、作者的信息、标签日期以及标签说明。一般我们都会使用附注标签,方便查看更多的标签信息。

​ 关键命令:git tag -a <tagname> -m '标签说明'这里的-a指的是附注(annotated)

​ 打上标签之后,我们查看版本信息会发现最近的一个版本多了一个tag,因为git默认标签是打在最新提交的一个版本上的。

轻量标签

​ 如果我们只是临时加的标签,不需要额外的一些标签信息,我们就可以使用轻量版标签。

​ 关键命令:git tag <tagname>,默认是给最近的一个版本打上标签

查看指定标签

​ 使用git show <tagname>查看指定标签的信息

​ 从上图我们可以看出查询的标签是一个附注标签,它有很多一些附带的信息,如标签名、作者的信息、标签日期以及标签说明;那轻量标签又是如何呢?下面这个就是一个轻量标签了,它只是简单的打了一个标签,没有更多的信息了。

查看所有标签

​ 使用git tag可以查看版本库中有哪些标签,标签名按字母排序。

查看匹配标签

​ 这里查看标签同样适用glob模式匹配,关键命令:git tag -l 匹配规则,-l表示以列表形式查看标签,如查看v开头的标签:

​ 因为只打了一个v开头的标签,因此只显示一个。

补录标签

​ 默认标签是打在最新提交的commit上的。那么如果commit时忘了打标签怎么办呢?答案就是找到历史提交的commit的id,在标签命令的然后打上标签即可。关键命令:git tag <tagname> 版本号

​ 当然,补录的时候我们是可以选择补录附注标签还是轻量标签的,根据相关命令补充参数即可。

删除标签

​ 如果标签打错了,可以删除标签。关键命令:git tag -d <tagname>,-d表示delete

​ 因为我们创建的标签默认都只存储在本地,不会自动推送到远程,因此打错的标签可以在本地直接删除。

​ 这是为什么呢,因为git pull推送时并不会把标签也推送到远程服务器上,只有通过显示命令才能把标签推送出去,关键命令:git push origin <tagname>,或者一次性推送所有标签git push origin --tags,关于推送,会在后面的章节介绍。

​ 如果标签已经推送出去,要删除标签就需要先删除本地,再从远程删除,远程删除命令:git push origin :refs/tags/<tagname>

分支管理

分支的工作方式

​ 几乎每个版本控制系统都支持分支管理,使用分支意味着你可以从开发主线上分离出来,在不影响主线的同时继续工作,在前面多次commit和查看版本,可以知道每次commit,git都会把他们串成一条时间线,这条时间线就是一个分支,目前为止,我们的git里只有一条时间线,即主分支(master分支)。

​ 另外,我们查看版本信息的时候,会发现最新的commit上总会有一个HEAD的标识,如下图:

HEAD是什么意思呢,它可以理解为“头指针”,指向当前工作区的分支,当分支很多时,git如何知道你在哪条分支上工作呢,就是通过HEAD来标识。因为当前只有master主干,没有其他分支,因此HEAD是一直指向master的。

​ 这里我们要明白,HEAD是指向分支的,不是指向commit,master(分支)才是指向commit,开始的时候,master分支是一条时间线,git用master指向最新的commit,再用HEAD指向master,这样就能明确知道当前的分支,以及当前分支的提交点,如下图:

​ 之后每一次commit,master分支就会向前移动一步,随着不断的提交,master分支的时间线就会越来越长(在没有其他分支的情况下)。

​ 当我们创建分支的时候,比如创建一个dev分支,git就会新建一个dev指针,指向跟master一样的commit,再把HEAD指向dev,表示当前是在dev分支上,如下图:

​ git创建一个分支是很快的,因为它只是增加了一个dev指针,改变一下HEAD的指向,而工作区的文件是没有任何变化的,但从现在开始,之后对工作区的修改和commit都是针对dev分支了,每次commit,dev都会往前移一步,而master指针不变,如下图:

​ 假如我们在dev分支上的工作已经完成了,就可以把dev合并到master上。那git如何分支呢?显然,直接把master指向dev最新的commit就可以完成合并了,因此,git的合并分支也很快,改一下指针即可,工作区内容不变。

​ 合并完分支之后,甚至可以把dev分支删掉,这里删除分支其实就是把dev指针删掉,因为我们已经合并完成了,删掉后,这条时间线就只剩下一个master分支。

分支操作

1.创建分支

​ 关键命令:git branch <branchname>

2.查看分支

​ 关键命令:git branch,*号代表当前所属分支

3.切换分支

​ 关键命令:git checkout <branchname>

​ 也可以直接在创建分支时加上一个-b参数(branch-分支的缩写),表示创建并切换,完整命令:git checkout -b <branchname>,相当于执行了创建和切换两个命令。

​ 🎈知识点

​ 在删除文件的章节曾介绍过一个git checkout -- <file>命令,是用于撤销工作区的修改,同样是checkout命令,作用却相差很大,因此,新版的git提供了git switch用于切换分支,git restore <file>用于撤销工作区的修改,用于区分两者的功能,虽然提供了新的命令,但旧命令仍然是可用的。

​ switch切换分支命令如下:

  • git switch <branchname>:切换分支
  • git switch -c <branchname>:创建并切换分支

4.合并分支

​ 现在我们已经切换到dev分支了,我们在dev分支修改一下readme.txt,追加一行:this is a new branch!

​ dev的工作已经完成,切换到master分支,查看一下readme.txt,发现刚刚在readme.txt追加的一行不见了,因为那是在dev上commit的,不会影响到master。

​ 现在我们来合并分支,关键命令:git merge <branchname>,这个命令用于把当前分支合并到指定的分支。合并后,我们再查看readme.txt就会再次看到追加的那一行。

​ 如果你仔细看,会发现合并成功时,有一行这样的提示:Fast-forward,这是告诉我们,这个合并是“快进模式”,也就是直接把master的指针直接指向dev的最新commit,所以合并速度很快。当然,并不是每次合并能如此快速,后面还会讲其他方式的合并。

5.删除分支

​ 合并完成后,dev分支就没有用了,我们可以选择把它删除,只保留master一个主分支,删除命令:git branch -d <branchname>,d就是delete的意思。

合并冲突

1.解决冲突

​ 并不是每一次合并都是顺利完成的,我们先看一下下面一个例子。

​ 第一步,我们先重新创建一个dev分支,并在readme.txt上追加一行:this is dev!然后commit。

​ 第二步,切回master分支,此时readme.txt是没有上面追加的那一行的,但我们给它追加一行:this is master!然后commit。

​ 现在master分支和dev分支各自都有一个新的commit,时间线都往前移了一步,如下图:

​ 前面合并分支时我们也提到过,并不是每一次合并都是快进模式,像上面的情况,合并的时候就会引起冲突,因为两个commit工作区的文件内容不是一样的,我们先试着合并:

​ 果然,git告诉我们readme.txt文件存在冲突,使用git status查看工作区情况时也会告诉我们有文件冲突。

​ 这个时候我们查看readme.txt,会发现里面多了一些内容,git用<<<<<<<=======>>>>>>>标记了不同分支的内容。当文件存在冲突时,我们必须手动解决冲突后再提交,即手动修改文件。

2.查看合并情况

​ 手动解决冲突后,工作树会再次回到干净状态。此时master和dev分支就变成了下图的样子:

​ 查看合并情况:git log --graph,我们可以再加上--pretty==oneline(一个版本显示一行)和--abbrev-commit(仅显示版本号前几个字符)两个参数,更简洁的查看合并情况:

分支管理策略

1.禁用快进模式

​ 前面合并分支,我们就提到过Fast forward快进模式,但这种模式在删除分之后,就会丢掉分支信息,无法去查看合并的记录。

​ 因此我们可以强制禁用Fast forward模式,git会在合并时生成一个新的commit,这样在查看合并记录时就可以查看分支信息了。

​ 合并时禁用快进模式的关键命令: git merge --no-ff -m '版本信息' <brandname>

​ 我们重新建一个test分支来使用一下这个禁用命令:

​ 因为本次合并禁用了快进模式,git在合并时会生成一个新的commit,所以要加上-m参数,把commit描述写进去,现在已经合并完成,我们来查看一下合并情况:

​ 之前我们使用快进模式时,合并后,dev和master是指向同一个commit的,合并只是把master指针向前移了一步,而禁用快进之后,合并时创建了一个新的commit,master会指向新的commit,test仍然指向所在分支的最近的一次commit,如下对比图:

2.分支策略

​ 在实际的工作中,我们应按照以下几个基本原则进行分支管理:

  • ✔️ master要保持一个稳定的版本,即仅用来发布新版本,不在此分支进行日常的开发;
  • ✔️ 日常开发在dev分支上,也就是说,dev是不稳定的一个分支,在到达某个时间点后,需要发布版本时,再把dev合并到master上;
  • ✔️ 每个人分别在自己的分支上进行开发,根据需要再时不时地合并到dev分支上。

​ 这时候,团队的分支情况就像下图:

检出版本

检出版本

​ 通过分支管理,我们已经了解到分支的概念,也知道HEAD是指向分支的,比如下图中HEAD是指向master分支的,master分支又默认指向最新的commit,但是有时候我们可能并不想要分支指向最新的commit,由于某些原因最新的commit不可用了,想要在分支中的某一个旧版本进行继续工作时怎么办呢?

​ 我们可以使用checkout来检出版本,什么是检出版本,说白了其实就是修改HEAD的指向。

​ 检出版本关键代码:git checkout 版本号

​ 首先查看一下历史版本,记住当前master最新的commit是ef69bbf,想要检出版本的版本号:66b725a

​ 现在我们来检出这个版本:

​ 检出版本后,会出现一堆提示,它告诉我们已经检出到66b725a版本,并且当前处于detached HEAD(分离头指针)状态,这个状态意思是说,HEAD指针指向了一个具体的commit,而不是分支,我们说过HEAD是指向分支的,分支才是指向commit的。这里它处于分离状态是因为我们主动把HEAD指针修改了,使它指向了一个commit。

​ 检出版本后,再次查看版本记录,对比发现最近提交的两个版本已经看不到了,指针已经指向了回退的版本。

​ 如果要查看比回退高的版本,可以执行git reflog

​ 除了可以通过版本号检出版本,还可以通过标签名去检出,关键代码:git checkout <tagname>,这里不再做演示。

​ 现在我们已经检出一个版本了,我们在这个版本上对readme.txt追加一行:this is checkout!添加到暂存区后,查看一下文件的状态:

​ 它提示了HEAD detached at 66b725a,意思是说当前不属于任何分支,因为HEAD处于“分离头指针”模式,接下来我们提交这个版本,查看一下版本信息:

​ 此时HEAD指针指向新的commit,并且是在检出版本的前面,我们先记下这个最新的commit:99dc7db。然后切换到master分支:

​ 切换到master分之后,它发出一个警告:99dc7db这个commit将被遗留掉,因为这个commit不在任何一个分支上。此时,我们再次查看一下版本信息:

​ 发现已经看不到99dc7db这个commit的信息了,HEAD重新指向了master分支的最新commit:ef69bbf,而我们检出版本时修改的readme.txt文件也回到了之前的版本。由于99dc7db这个commit当时是处于分离头指针状态,不属于任何分支,因此也没有任何分支可以追踪到,但它并不是被删除了,它仍保留在版本库中,我们可以通过指定版本号去查看这个commit:

挽留分离头指针

​ 在“分离头指针”模式下提交的commit,在没有归到任何一个分支时,也就只能通过版本号(99dc7db)指定去访问了,如果这个commit是master分支所需要的,可以使用合并命令,把这个commit合并到master分支上,实现两者的兼容:

别名

设置别名

​ git并不会像一些编译软件,在你输入部分命令时就自动联想到你想要的命令,必须要手动敲出一个完整且正确的命令。还好git支持别名设置,可以通过 git config 文件来轻松地给一些常用、或者复杂的命令设置一个别名,比如git status命令我们经常使用,我们可以告诉git以后st就表示status

​ 关键命令:git config --global alias.别名 命令--global是全局参数,也就是说设置的命令在这台电脑的所有git仓库下都可用。

​ 当然,我们还可以设置其他各种各样的命令,比如:ck表示checkout,ci表示commit,br表示branch等,具体可以参考https://git-scm.com/book/zh/v2/Git-基础-Git-别名

git config --global alias.ck checkout
git config --global alias.br branch
git config --global alias.cf config
git config --global alias.fc fetch

​ 配置的命令还可以是多个,但需要加上引号来表示,比如我们可以配置一个git last,让其显示最后一次提交信息,即last是log -1的别名:

配置文件

​ 还有一些很长的命令我们都可以这样修改成简单易记的别名,而这些别名设置都保存在~/.gitconfig文件中:

​ 别名就在[alias]后面,要删除别名,直接把对应的行删掉即可;如果要修改别名,直接重新设置就行,git会在已有的别名进行覆盖,除了可查看配置文件,也可以通过git config --list | grep alias命令来查看这些别名:

GitHub的使用

​ 前面我们所讲的操作都是基于本地的,git作为一个分布式的版本控制系统,和svn最显著的区别就是可以离线使用,不需要创建一个中心的git服务,就可以在本地使用版本控制。

​ 同一个git仓库,可以分布到不同的机器上。在实际工作中,我们在多人协作时就必须要有一个集中管理的地方,因此就有了各种各样的代码托管服务,GitHub就是其中的一种,这里就不对其进行过多的介绍。

​ GitHub官网地址:https://github.com/,自行注册一个账号。

新建项目

​ 登录后,在首页点击Start a project

​ 然后根据页面提示,输入对应的信息:

​ 填好信息之后点击创建,就会跳转到该仓库,可以看到仓库的远程地址:

git远程地址

​ 查看本地关联的远程仓库地址,相关命令如下:

  • git remote:不带参数,列出已经存在的远程分支
  • git remote -v:列出详细信息,在每一个名字后面列出其远程url, -v 参数为 –verbose 的简写,表示显示对应的克隆地址
  • git remote add 仓库名 仓库远程url :添加一个远程仓库

​ 我们先查看一下本地是否有远程的仓库:

​ 没有内容返回说明当前没有远程的仓库,接下来我们把刚刚创建好的github仓库添加到本地:

​ 添加成功后再次查看远程地址就有对应的数据返回了。

git远程操作

1.查看远程项目信息

​ 关键代码:git remote show 远程仓库名

2.推送内容到远程仓库

​ 关键代码:git push 远程仓库名 <branchname>首次执行,会弹出github登录界面,授权登录即可,推送完毕查看github的文件,会发现分支的所有文件都已经传到github了

3.从远程仓库拉取代码

​ 关键代码:

  • git fetch 远程仓库名 <branchname>:从远程获取最新版本到本地,但不会自动merge
  • git pull 远程仓库名 <branchname> :从远程获取最新版本并merge到本地

​ 我们先在github模拟其他人修改分支内容,点击github分支中的Create new file新建一个txt文件,输入内容后点击左下角Commit new file提交;

​ 然后在本地的git进行拉取文件,我们先看一下使用git fetch来拉取的效果:

​ 拉取完成后,查看一下本地的工作目录,发现在github创建的test_fetch.txt并没有出现在本地,这是为什么呢?原来git fetch并没有真正地去影响到本地工作目录,只是把数据拉取下来放到了本地的远程仓库,我们可以通过git merge 远程仓库名/<branchname>来合并分支。

​ 这样的话,每次拉取都需要执行一遍合并实在太麻烦了,git还有另外一个拉取命令,就是git pull 远程仓库名 <branchname>,它会在拉取代码时自动合并到分支,拉取后再看一下工作目录,数据已经与远程仓库一致了:

4.远程修改

​ 关键代码:

  • git remote rename <old_name> <new_name>:重命名远程仓库名称,只是修改本地
  • git remote rm name:删除远程仓库地址(github地址如果更新了就可以删除重新添加)

5.克隆远程项目

​ 关键代码:git clone url

​ 当我们本地没有git项目且已知一个远程项目可用时,我们就可以通过克隆,把远程仓库的整个项目克隆下来进行工作。首先,需要手动创建一个文件夹,在文件里右键点击Git Bash Here进入git命令窗口(也可以通过cd命令切换到该目录),输入克隆命令回车即可,这里克隆的url就是github远程项目的地址,可以在github上查看。

​ 克隆完成后,就可以进入项目开始你的工作了,默认是进入到master分支,需要在其他分支工作时使用分支切换命令切换即可,也可以在克隆时加上-b参数切换分支,克隆完成后会自动切换到指定的分支,完整命令:git clone -b <branchname> url

posted @ 2020-05-15 10:37  miki_peng  阅读(1348)  评论(0编辑  收藏  举报