飞道的博客

Git和GitHub学习笔记(更新中...)

298人阅读  评论(0)

1. 什么是Git?

  Git是什么?它是一款工具,什么工具?讲的专业一点,叫版本控制工具,或者叫源代码管理工具,对于源代码管理也可以约等于叫文件的管理(既然是文件的管理,那么文件的生死你版本工具需不需要负责),如果要给它起一个术语的话,叫scm,当然,这不是重点,重点的是你前面提到的版本控制是什么东西?这个问题适用于那些没有接触过任何版本控制工具的人,但我觉得还是留到下一节再讲,这里只是大概的说一说什么是git?GIT,全称是分布式版本控制系统(Distributed Version Control System),是一款可靠的,高效的,免费的工具,你可以在任何地方,甚至无网的情况下管理你的项目代码,实现备份,不怕文件丢失的烦恼。

2. 为什么要使用Git?

  版本控制的解释:版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。那么如果从个人开发看,是给了我们一个后悔药吃,当我们在写一个文档的时候,一直写啊写,写到第四天,发现写的不行,已经严重的偏离了主题,需要倒退,倒退到什么程度?倒退到第二天那种程度,如果没有像Git这款工具,我们只能手动的去删,因为每一天写完文档会关掉一次,所以,Ctrl+Z是不能撤销到第二天的程度的,这样就会对我们造成麻烦,严重的话,还得放弃,重新写。其实像这种思想在我们学习linux也深得体会,linux有些系统文件,在学习的过程中,如果要对其文件进行操作,我们必须copy一份,这样做的目的很明显,是为了防止出错,易于还原,否则一旦出错,只会造成我们的麻烦,因为人是有疏忽的时候,即使你很谨慎。除了linux这样,当我们面对比较重要的文档也是如此,我们不会在原有的基础上进行改造,而是在改造之前copy一份,做个备份。有句话叫:凡事给自己留条路。
  但问题来了,我们不是可以自己去备份吗?干嘛还要学习Git,如下图:

你看,当我们写的差不多的时候,先备份一下,在下一个版本继续写,如果写到第5版的时候发现不对劲,不能那样写,简直是一塌糊涂,必须退回,诶,退回到第三版刚刚合适,应该从哪里继续写,这样做不也行?那么Git的意义在哪?
  像上面那种方式,属于文件拷贝方式,当我们做大型项目的时候,如果像上面那种方式,一直拷贝,那么电脑上将会有N多个文件,而且还不利于管理,随着时代的进步,从这种原始的方法,过渡到一种叫文件服务器,这个时候,在你的电脑就不会看到太多的文件了,你的文件存在另外一个地方,也就是本地管理过渡到集中式管理,在第3节会说什么是集中式管理。但是在开发过程中,因为是团队开发,用这种方式去做的话,可想而知,很多人都在共用着这一台服务器,如果是一款普通的文件服务器,你还要担心你写的文档会不会被别人给覆盖掉等等,它没有这种能力去帮你管理文件里的内容,就单单这个团队开发的问题,就已经证明了这种管理方式根本行不通,我需要另一款工具来解决我在团队协作中的问题,那么这个工具可以说有两个,分别是svn和git,在第3节会讲它们的区别,但是这里的主角是Git。
  通过Git,以后在面对需求的时候,比如开发一个功能,那么在开发这个功能之前,我们是不是可以记录一次,然后在自己的分支上进行开发该功能,如果有一天,人家说公司的该功能呀,不想要了,就不要这个功能了,那有了版本控制,你是不是可以回退到我们刚刚开发该功能的哪初始阶段,你想要该功能,没问题,进一个版本,不要,我就退,轻轻松松,一个命令的事,这样,如果我们单纯的手动去搞的话,还可能搞砸了,越搞就越乱,越陷就越深,而且还有垃圾残留。总结一句话:如果该功能在线上出问题了,还有回滚的机会,正印证了那句话,给自己留条后路。当然了,不单单开发,像UI啊,在设计方面,为了迎合老板的审美观,会设计多套方案,这多套方案其实就约等于多套版本,这多套版本我们可以把它存在U盘里,保存着,或者让Git来帮你保存着,也是可以的。
  在第三段我们不是说明了Git可以解决团队协作的一些问题吗?有一个,就是覆盖问题。这样一来,团队的多个人就可以共同的修改某一个文件,不会出现我刚刚上传的文件,被你后面一上传就覆盖掉了。同时,为后面的纠错,项目整合提供了方便,否则,没有像Git这一款版本控制工具,那么开发的效率将会降低,增加我们的负担。我们还有什么理由不学好Git。

3. Git和Svn的区别

  上面在说Git的时候,讲到了版本控制,其实版本控制是一个思想,由这个思想衍生出了版本控制工具,Git就是其中之一。版本控制这四个字,已经不难理解了,版本,就是一种历史状态,控制,就是Git所表现出来的管理
  除了Git,还有一款版本控制工具,Svn,那么它跟Git有何区别呢?

  SVN属于集中式版本控制工具,重点在集中式上,除了Svn,还有CVS,VSS,Perforce也属于集中式,这两个了解即可。而Git属于分布式的版本控制工具,也就是集中式和分布式的区别。
集中式管理(CVCS):


如果服务器宕机了,你在本地就不能开发了,为什么呢?开发倒是可以,但是如果写到一半,发现写的思路是错的,有很多BUG,这时候你想退回到宕机前的那个版本,还能退回吗?是不是退不了,那你说,你要怎么工作。而且它还会让你的努力白费,这又怎么说,你想,你做到一半,做到这,你的代码有很多的闪光点,非常不错,这时候,你想,我得保存一下记录吧,让它成为一个新的版本,但是,哦豁,你刚想到服务器宕机了,无法提交,还能怎么办,但是却在这个时候你偏偏突然有了一个想法,想继续写代码,写啊写,发现坏了,不对劲,我得撤回我上一次写的那个所谓的闪光点,但是,服务器宕机了呀,我怎么撤回,就算服务器满血复活又回来了,因为我并没有去给它保存一个历史记录点,我又要怎么撤回,只能撤回到服务器宕机前的那个状态,这是不是意味着,我所做的闪光点白费了,写的代码没有保障,除非你运气好,可以CTRL+Z到原来的位置,还是刚刚恰好的那个位置。

分布式管理(DVCS):
  这时候,因为每台电脑都是一个服务器,都有历史版本,所以从存储空间看git要比svn大,但是git它肯定会有一种压缩算法来尽可能的压缩到最小,所以,跟svn比起来,也就大了那一丢丢。还有一个可以跟svn比较的,也就是分支,分支可以说是git优秀特性,对于svn,它的分支是copy一个完整的目录,而git它的实现方式就是一个指针,那么这两种方式一目了然,最后,在大型项目面前,优先使用git,你可以在你的项目做任何操作,只要你有上一次的保存点,你可以在你项目改来改去都没问题,改乱了还可以倒回去,但是svn这种集中化的就不一样了,前提是远程服务器不能宕机,如果宕了,你就不能在你的项目随意修改了,不然是无法撤回的。

4. Git的历史

  Git它的作者是Linus(林纳斯·本纳第克特·托瓦兹),也是linux的作者,因为他发明了linux,并且是开源的,所以会有更多的程序员投身到此工作中来,那么代码优化也会越来越好,这里涉及到改代码,一定需要一种版本控制工具的管理,Git是在2005年由linus花两周的时间用C语言写出来的,然后在这一个月之内,Linux系统的源码已经由Git管理了,在Git之前,Linux的内核源码是由BitKeeper来进行管理的,它也是分布式的,是一个商业版本,收费的,只不过对方授权linux社区可以免费使用,而在05年由于Linux社区牛人用反向工程试图去破解BitKeeper的协议,造成对方的不愉快,所以到05年才终止合作, 在这之后,Linus都在寻找可以替代BitKeeper的代码管理系统,有一种选择是恢复他以前用tarball和补丁的方法,但是已经没人喜欢这么做了,还有一些像集中式的,凡是不支持分布式的,性能上的等等不好的原因,都会被linux的作者linus给否定掉,折腾了一圈之后,就是自己写一款产品,用两周的时间写出来,git就此诞生。

5. 版本控制工具应该具备哪些功能

  1. 协同修改:多人并行不悖的修改服务器端的一个文件。
  2. 数据备份:不仅保存目录和文件的当前状态,还能够保存每一个提交过的历史状态。
  3. 版本管理:在保存每一个版本的文件信息的时候要做到不保存重复数据,以节约存储空间,提高运行效率。这方面SVN采用的是增量式管理的方式,而Git采用了文件系统快照的方式。
  4. 权限控制:对团队中参与开发的人员进行权限控制,那些该碰,哪些不该碰。同时对团队外的开发者贡献的代码进行审核----Git独有。
  5. 历史记录:查看修改人,修改时间,修改内容,日志信息。并且可以恢复到某一个历史状态。
  6. 分支管理:允许开发团队在工作过程中多条生产线同时推进任务,进一步提高效率。

注意版本控制工具一般包含客户端和服务端,git的服务端就是后面要学的GitHUB。然后像客户端,svn的客户端是存有某个版本,而git的客户端的存有所有版本。

6. 安装Git

  网址:https://git-scm.com/download

  点击它,就会进行下载,默认下载的是64位的。下完就是下面这样子的。

  打开,大部分都是默认的,next即可,但有一个界面要注意下,如下

  然后在桌面或其它目录都行,点击右键,如果发现像下面这种:

就说明安装成功了。GUI表示图形界面操作,Bash表示命令行操作,在下面的学习中我们是以命令行进行操作的。命令行界面有个专业术语叫cli,因为全称是Command line interfac。

7. Git的三区和三种状态

三区

首先,要明白,git里面分为三个区,哪三个区呢?

  1. 工作区(Working Directory/Working Tree/Workspace):工作区就是我们能看到的,并且用来写代码的区域,简单的理解为在电脑能看到的目录或文件,比如下图中的index.html和a.php,但注意.git目录不包括,如图所示:

    在工作区当中,我们可以添加,编辑,修改文件等动作。
  2. 暂存区(Index Stage):临时存储用的,类似一个缓冲区,存放的是打算要提交但还没提交的东西,所谓的提交,就是提交到本地库哪里,就是第3点说的那个本地库。然后为什么暂存区的英文名有个index,索引呢?是因为它一般存放在".git目录下"下的index文件中,一开始是没有index文件的,只要你有东西添加到暂存区,它就会生成index文件。
  3. 本地库(Repository)或者说是对象库,版本库(这个版本库往大的看,就是本地库加上暂存区才为版本库,也是可以的,只不过是一个概念而已),历史区,生成历史版本,这些历史版本就是从暂存区提交过来而生成的,也就是说这才是真正做版本控制的,图上.git的目录就是本地库。

注意上面的三个区是先在本地的工作区创建一个文件,然后添加到暂存区,最后提交到本地库的这样一个流程。那么这句话中我标上黄色文字的那个对应着两条命令,一个是添加,add。还有一个是提交,commit。所以,当我们把工作区的文件添加到暂存区时,命令是git add,那么对应的提交就是git commit

理解:
  如果我们负责做某个项目的模块,那么我们会在我们工作区里写着一系列的文件代码,但是这个模块的开发可能需要两天的时间,在第一天模块还没开发完,就下班了,要回家了,那么我们在工作区写的代码就可以添加到暂存区来临时存储,因为在工作区里写的文件一旦丢失,就不可再找回,它不在版本控制的管理范围之内,所以,我们才会把它放到暂存区,但因为模块还没开发完,所以没必要把它放到本地库来生成历史版本,到第二天早上,我们继续开发,如果发现做着做着发现问题,一团糟,想撤回,刚好昨天在暂存区有存了一份,那么我们就可以把它从暂存区撤下来,还原到昨天的出厂设置,重新开发,有个后悔药可走。需要注意的是svn没有暂存区,因为它是直接提交到中央服务器的。

三种状态(只要你被git管理,就会存在下面三种状态的任意一个)

下面的三种状态就对应着上面的三个区:

  1. 已修改(modified):已修改表示在工作区修改了文件,但还没保存到暂存区中。注意在git中,不管你是对文件的修改,还是删除,或者是新建了一个新文件,都属于修改
  2. 已暂存(staged):已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
  3. 已提交(committed):已提交表示数据已经安全的保存在本地库中。

8. Git的简易工作流程(针对个人开发)

上图是针对个人电脑上的版本控制,不涉及远程库,因为还没讲到GitHub。上图展示了三个区,我们发现,在工作区写的代码首先先到暂存区,再到本地库,它不能绕开暂存区,为什么这样,如果你直接提交到本地库,那么就会生成一个版本,提交多次,就生成多次版本,没必要,那么暂存区就发挥作用了。而在本地库的代码检出到工作区是直接到,不用经过暂存区。

9. 思路与步骤

  在上面说到的什么三区这些,都跟git的内部有关,而且很重要,在后面学命令的过程中,都会用到,在第一节的时候我曾提到过git它是一个文件管理工具,顾名思义,就是管理文件的,好比老师管理学生,如果真要做个类比,老师就相当于学生管理工具,那么git是怎么管理文件的?

  1. 首先,我们项目在磁盘上的存在是不是一个文件夹?文件夹里面才是放我们的项目代码的,那好,git要管理我们的项目,是不是得进去你的项目里,说白了,就是cd 项目名,你不进去,怎么管理里面的源代码,好比,你一个老师不进教室,怎么管理学生?
  2. 你进去了教室,是不是得表明身份,你是一位老师,不然谁听你的,没错,那么我们进去项目文件夹里了,就要对这个文件夹做一个初始化(初始化的作用就是达到个人计算机与版本服务器同步,你可以理解初始化就是开辟一块空间,这块空间就叫版本库,里面存放版本信息),初始化成功会生成一个.git目录,而这个.git目录扮演的角色就相当于老师,那么该项目就变成了一个有老师管的班级。
  3. 接下来就是老师对学生的管理了,老师会实时的监测每个学生的变化或者状态(status),谁删除了文件,谁修改了文件…
  4. 老师最终的任务是什么?对比,git的最终任务是什么,是不是要为项目生成一个个版本?

第3点和第4点跟命令有关,是4个步骤中分量比较重要的部分,要注意。如果明白了上面的4个步骤,相信我们已经知道怎么做了。

10. Git的第一个命令之创建版本库和查看状态并设置签名

  版本库又名仓库,可以理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改,删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来时刻可以还原,在理解git的三区和状态后,我们将通过例子来加深印象。

  1. 初始化Git的本地仓库(相当于创建git的本地仓库):通过执行git init命令在本地初始化一个本地仓库,执行该命令后会在本地初始化一个没有任何文件的仓库。

    上图是一个空目录,这个目录叫gittest,你可以理解它就是项目名,gittest目录就是一个项目,因为刚刚创建,所以里面还没有东西,在这个目录里面,我们是要写许多的代码文件的,而这些代码文件在我们的电脑里是专门有一个仓库来管理我们的代码的,而这个仓库我们是不是还没有,不然我怎么来管理gittest目录下的所有文件,我得先给它创建一个仓库,也就是本地库,本地库才是真正来做版本控制的,有了这个本地库,那么我们在gittest里面创建的所有文件都可以做到版本控制(因为它里面存放了许多配置信息和版本信息),可以被git给追踪。那么继续往下看:

    点击它,进入命令行黑窗口,类似cmd,在这个黑窗口,你不仅可以写git命令,还可以写linux命令。

  执行了git init(1,创建本地库. 2,创建默认分支,master主分支)命令就可以看到在当前目录中多了一个.git目录,这就说明gittest的这个目录被git给初始化了,那么在gittest里的所有文件都可以交由git来管理,最后注意一点,.git目录是隐藏文件,隐藏文件是看不到的,要想看到得以下操作:
  然后用git status(通过查看状态可以清楚的知道文件所处的是三区的哪个区,以此可以有针对性的操作文件)来查看当前状态:

  这条命令可以检测到你在程序文件中的任何操作,比如,增删改,那么这时候我在gittest目录中新建一个a.html文件,一开始的创建,它并没有被git管理,或者叫追踪,在此处git学习中追踪就是管理

  这时候工作区多了一个a.html,那么它的状态也应该有所变化:

  图中present翻译过来是存在的意思。此时a.html呈现红色表示a.html文件还在工作区放着,并没有被git所管理,丢失了,是不能找回的
  补充:项目文件夹我们可以不用手动创建,在初始化的时候也可以一并把项目创建出来,比如:git init 项目名

  1. 设置签名:首先,准备好我们的用户名和email地址,比如用户名为cht,email地址为330689xxxx@qq.com,随便写,只是为了标识开发人员,提交的时候知道是谁提交的。设置签名分为项目级别签名和系统用户级别,项目级别的签名就表示我们设置的签名只在当前项目有用,比如上面的gittest目录,而系统用户级别就是登陆当前操作系统的用户范围,比项目级别要广。没有设置项目级别的签名,默认是系统用户级别的。
      接下来我们可以用条命令来设置一下签名:git config [--global] user.name xxx [--global],以及git config [--global] user.email xxx,不带global是项目级别的,带global是系统用户级别的。
    好,那么我们设置的用户名和email放在哪个地方保存着呢?如下命令:
    因为设置的是系统用户的签名,所以存放在家目录下。
    因为这个不带global,所以设置的是项目级别的,既然是项目级别的,那么设置的签名就应该在项目里面存放着,那么是项目里面的哪个文件?如下:

    在项目里直接通过git config --list可以也可以看到你的用户名邮箱之类的。如果要删除,命令:git config --unset user.name,忘了的话,直接git config回车即可,它就会有相应的参数介绍。

11. 介绍.git目录

  .git目录是一个隐藏文件,其实就是告诉你没事的时候不要去乱动它,否则乱动了就不能做版本控制了,然后我们点进去看看里面的文件和目录吧!大概的看一看,看不懂,可以先略过。

  1. hooks:翻译过来是"钩子"的意思,所谓的钩子就类似回调函数,在我提交代码前我可以做一些东西(可以做一些控制,绝对你可不可以提交),提交后又做一些东西,大概就是这样的。
  2. info:包含一个全局性排除文件。就是哪些文件是git不需要管理的就可以放里面去。
  3. logs:保存日志信息。
  4. objects:相当于数据库,它是目录存储所有数据内容的。
  5. refs:目录存储指向数据(分支)的提交对象的指针。比如master指针。
  6. config:配置文件,如果你设置了项目级别的签名,那么打开config文件里面就会有你的用户名和邮箱(往后看就知道了,什么是项目级别的签名,这里先提一下)。
  7. description:用来显示对仓库的描述信息。
  8. HEAD:文件指示目前被检出的分支。里面的内容是ref: refs/heads/master,表示当前所处的分支是master分支,你切换成其它分支,它里面的内容也会实时变化。
  9. index:文件保存暂存区信息。

  .git目录包含了一个对象数据库和构成我们存储库的元数据,如果我们将.git目录发送给其他人,那么其他人便拥有我们完整的git项目,和完整的项目历史记录

12. 查看命令的帮助文档

  既然都已经开始学命令了,那么命令它是有文档的,就是介绍这个命令是什么意思,比如init,它是初始化的意思,那么它对应的文档我们是可以通过一条命令找出来的,如下:  一回车,就会有浏览器弹出来,如下:  类似的还有:git config --help

13. Git添加文件和提交文件

  接着第10节继续说,现在就是把工作区里的a.html添加到暂存区,让其git去管理这个文件,添加就是add,命令就是git add 文件名它的作用就是将一个未追踪的文件变成已追踪的文件
  然后再看其状态:  是不是发现a.html变绿了,对比上面的红色。说明该文件已经存在暂存区中了,被git给管理了,还要注意括号里的命令,那是一个撤回命令,就是从暂存区里撤回,恢复成上一个状态,不用记,看提示敲即可,除了它,还有一个命令也是,只不过它是没有带文件名的,git reset Head,也是可以把一个文件从暂存区撤到工作区的。最后,注意一下,如果要把项目里面的所以文件都添加到暂存区,就不要每次add后面加个文件名了,那么就是git add .,这个命令就表示当前项目里全部添加,都交给git管理。
  既然已经添加到暂存区了,现在就是要提交到本地库(如果文件代码开发的差不多了,需要建立起一个项目版本,就要commit),提交就是commit,如下:
  注意,如果不指定文件名,会把暂存区里的所有文件都提交。敲完命令回车,将会进入下面的界面:  所以,输入我们的提交信息吧!否则会终止提交。  :wq保存退出即可,这是linux的知识。  提交完后再看一下它的状态:  (注意上图nothing to commit表示暂存区里的文件都已提交,working tree clean表示工作区是干净的,没有新内容)。然后我们对a.html文件进行修改,可以给文件里的内容添点东西,然后再查看状态:  这个时候,工作区里的a.html修改了,而提交到本地库的那个a.html并没有修改,就造成不一致,所以在图上会有红色来标注工作区存在修改文件modified,对比第一次的new file,表示这一次添加的上来的文件不是新文件。图上的第一个括号表示如果你想要跟本地库保持一致,那么就要重新添加,再提交,或者也可以直接提交,就是图上的最后一句括号里的git commit -a。第二个括号表示撤销修改,返回上一次状态。
  然后我们重新添加到暂存区,再观察它的状态,如下:
  然后继续提交,不过,这次提交不一样,如果像上次提交那样,就要进一次编辑器,麻烦,如果不想进,往下看:
  总结:
  注意上图第3点的两条命令可以整合,即-am,命令:git commit -am "说明信息",它对新建文件是没有效的哦,因为要先add,再commit。说到这,我们要明白,我们对每一个文件的操作,都有可能生成一个版本(注意这个版本是整个项目的版本,而不是单独的文件版本),所以git必须实时的检测当前的文件状态。检测就是git status,它检测的是工作区和暂存区的状态,非本地库。对于工作区我们还可以细分为两部分,一部分就是无颜色的,一部分是红颜色的,而暂存区全绿,红色表示新增的文件或者已修改的文件。绿色表示该文件已经添加到暂存区了,被git管理起来了,如果是无色的(看不到的)表示已经被提交了。这样相信你对git status已经有好的理解了,在后面有个画图总结你就可以看到。

14. Git的显示日志

  日志英文log,所以如下:
  注意它们的历史信息都是最新提交的在上面。通过查看日志可以知道我们当前所处于哪个版本,到时候穿越就可以移动HEAD指针,也就是上图的(HEAD->master),它的位置表示现在所处的版本。
  对于git log这个命令它显示出来的格式,还有一个是在一行的显示,如下:

  pretty翻译过来是漂亮的,oneline翻译过来是单线,就是一行的意思,也就是说打印出来的日志要以一种漂亮的,一行的格式展现出来。对比简简单单的git log而言,它更简单了,发现签名和日期都没有了,只留下版本号和信息。当然了,你也可以不加漂亮的,如下:
  哈希值只显示一部分。下一个是git reflog
  这时候发现有个东西是HEAD@{数字},花括号里面的数字其实表示的是指针要移动的次数,比如它要回到上一次新建的那个状态,就是first commit,那么它就要移动1步,那里不是写着HEAD@{1}吗。
  最后,因为提交的东西多了,那么生成的版本也多了,那么查看日志的信息也多了,这样的话,一屏就显示不下,会出现第一页,第二页,第三页这些,那么要怎么翻页呢?记住了,空格表示向下翻页,字母b表示向上翻页,q退出。如果是查看最近或者是最新两条记录,命令:git log -2
  总结及扩展:
少了一个,是以图形化方式展示的,这个可以很明显的看出分支的信息:git log --graph

15. commit引发的分支走向

  这个也是要说一下的,什么意思,其实就是你的每一次commit,你所处的分支走向,这对于我们去理解命令是很有帮助的,并且对上节学习的查看历史记录也很有帮助,首先,有个网址:http://onlywei.github.io/explain-git-with-d3/#zen,如下:
  选择git commit,点击,如下:
  通过上图的图形,再结合上节学的查看历史记录对比,我们可以知道,当前所处的是master分支上的e137e9b这个位置,它是一个提交记录,也就是说,每个圆点就是一个提交。这时候,按照上图指引,在输入git命令框的那里输入一个命令:git commit并回车即可。看效果,如下:
  我们可以再输入几个git commit命令,比如再输入3个,如下:
  可以看出,指针(HEAD->master)所指的圆圈是发光的,表示现在我们所处的版本,如果想退回上一个版本,很容易,移动指针即可,因为每个版本之间都有一条无形的线将它们连接起来,也就是如图上的箭头,共同的形成了一条路。而查看历史记录的命令就是从第一个开始,一直到发光的那个圈圈为止。现在很容易理解,但是往后学习更难的命令,没有这样的演示是很难懂的。

16. Git删除文件和提交修正

  删除文件有两种方式,一种是linux命令删除,rm -rf 文件名。一种是git命令删除,git rm 文件名
  现在我们新建两个文件(c.txt和d.txt),提交后来试一下,如下:

  也就是说,用git命令去删除一个文件的话,它会多做一步,就是把你的删除操作添加到暂存区,你直接提交即可。
  类似的,还有重命名,命令是git mv 旧文件名 新文件名,执行这条命令,注意,对应git来说,重命名就是把原来的删掉,重新新建一个新文件,再提交到暂存区,以b.html为例,如下:

  接下来如果是提交信息写错了,也就是-m后面引号里写的信息写错了,那么怎么办?git也帮我们想到了,如下:
记住上面这幅图,下面开始命令演示,如下:实质上跟上一次提交是一次提交。

17. 永久删除文件后找回

  好,让我们把工作区里的a.html给删除吧,如下:
  查看状态时,如上图,红色意味着什么?是不是意味着工作区和暂存区不一致?没错,你在工作区删除了文件,并没有对暂存区和本地库进行操作,所以在依然还在,其实它跟modified是一样的,一样要添加,提交,这样一来的话,在工作区删除文件后的提交就是真正的删除了,那么这次的提交也一样会生成一个版本,那是不是意味着我往后退一个版本,工作区里的a.html就会重新回来呢?答案是,对的。
  给大家梳理梳理一下,加深对删除的理解,首先,加深,你在本地库就已经有a.html了,那么对应的,暂存区也应该有一份a.html吧,好,这时候,你把工作区的a.html给删除了,但是暂存区的a.html是没有被删除的,它依然还在,那怎么删除它呢,git add不就删除了吗,正常操作嘛,那么对应的,本地库里的a.html还在,怎么删除,git commit嘛,这样三区就一致了,就是这种删除法,比如,我在工作区里新建一个b.html,然后添加到暂存区,但是我不提交,我直接就把工作区里的b.html给删除了,但是暂存区还在,那么这时候查看一下状态,如下:
因为暂存区有一份,所以是new file,但是删除了,就是deleted,但是不要以为暂存区有一份,就可以直接提交了,必须要add,因为是新建状态,那么此时的add,如下:这时候,删了就是删了,本地库因为你并没有提交,所以并没有记录,也不能找回了,所以,如果一个文件要想找回,前提是你的这个文件有提交到本地库过。
  学到这里,我们发现只要对文件进行新建啊,修改啊,删除啊,都会要你提交记录一个新的版本,这样的好处,就是让删除的也能够起死回生。

18. Git的版本穿越

  这里要注意一下指针的概念,版本切换就是靠这个指针来切的,指向谁,那么当前所处的版本就是谁,如下:

基于索引值操作

  索引值,不用说,就是前面说的版本号,唯一标识,哈希值这些,就是图上第一列的黄色字体。
  格式:git reset --hard 索引值,如下:
  这时候,再查看其状态:  是不是就变了,可以看看a.html里面的内容是否有变化,有变化,就是变回你第一次提交的那次内容,就说明版本切换成功了。

使用^符号或者~符号

  使用这个要注意,它只能往后退,不能往前。所以,在演示之前,先让指针往前走一步,如下:  因为在操作是执行了两次git reset --hard 8f3a6d5,所以在上图的查看日志时前两条会出现一样的信息,就是reset:moving to 8f3a6d5。好,看日志的时候,不用看带有reset:字眼的,要看就看有commit:字眼的,图上就是后两条,发现指针已经向前了,到了第二次提交哪里,这个时候我们进行往后退,到第一次提交那里,那么这时候就不是用基于索引的方式去弄了,而是基于^来移动,如下:  这条命令就是说以HEAD为准,向后退,那么HEAD就是指针指向的版本位置,以这个版本为基准,移动到这个版本的后面,移几位?1位。然后看日志的时候一样要看带commit:字眼的,图上由红色框框出来的部分,这个。但是注意查看日志的第一个命令,在前面讲过,就是git log,这个要注意,如下:
  我们发现,它就只有一个版本,不是总共有两个commit的版本吗?其实,这个命令它查看日志的时候它只会看当前版本以及当前版本的后一个版本,很明显,当前版本,也就是那个我们第一次提交,在它的身后是没有其它人的,我们可以测试一下,我们把指针往前移,到第二次提交的那个,再进行相同的测试,发现,如下:  正如我所说,它只往后看,不往前看,那么像git log --oneline也是如此。
  现在,如果我想让它倒退两步,当然了,两条历史版本是不够的,再多加一条,就可测试,这里就明说,倒退两步,就是两个^,倒退几步,就是几个^。但是这种方式不行,如果要倒退很多步呢?那岂不是要写很多个^?所以,又引入了一个,就是波浪线~,它怎么用?如下:
  像这样,就表示倒退3步,倒退几步,就写几步。上面不管是^还是~,都是往后退,不能往前退,要记住,同样,用http://onlywei.github.io/explain-git-with-d3/#reset进行演示。

19. hard和soft以及mixed参数

  来说说git reset --hard 索引值里的hard参数,这个hard翻译过来就是坚硬的意思,就是硬的,那么有硬就有软,的确,是有软的,那么硬的和软的有啥子区别?
  我们可以用帮助命令来看下,命令:git help reset,回车查看,如下:  先看一下soft,把英文解释复制到翻译软件上看看什么意思,如下:  这句话的意思就表示它仅仅在本地库移动指针。那么就会造成本地库跟暂存区以及工作区不一致,现在用命令试一下,如下:这时候我们用软指针移到第二次提交哪里,那么我在第二次提交的时候增加了一行是bbbbb,因为它只是更新了本地库,不触摸暂存区和工作区,那么是不是即使移动了指针,工作区里的a.html还是原来的状态,里面的内容并没有变,还是第一次提交的那个aaaaa,好,如下:
然后打开a.html,看看有没有bbbbb这一行,如下:
发现根本就没有,退出,输入查看状态命令,看看暂存区的状态,如下:
发现它是绿色的modified,在前面,是不是只有我们修改了工作区里的文件,再添加到暂存区才是绿色的modified吗,是不是表示了暂存区的文件和工作区的文件一致,而soft这个前面也看到了,它是不触摸暂存区和工作区的,相反,也是达到了文件一致的效果,所以它是绿色的。只有本地库向前进了一步,也就是如下:

这也是为什么它是绿色的modified的原因,那么这个理解了下面的mixed,hard也应该理解了,往下看。
  下一个是mixed,如下:  按照翻译过来的文字,看,它只是重置了本地库和暂存区,暂存区就是索引,index嘛,它不重置工作数,就是工作区,那么,图下所示:

进行证明,如下:
在证明之前先让三区保持一致,三区保持一致不用说了,就是hard,但是我们也对它进行翻译吧,如下:

20. 画图总结

  下面这幅图是我在网上学习看过不错的一幅图,当然下面的图是自己对照的画的,通过这幅图可以对上面几节有个总结,如下:

21. 比较文件

  比较文件有三种,一种是工作区的文件和暂存区的文件比较,一种是工作区的文件和本地库的文件比较,另一种是暂存区的文件和本地库的文件比较,先看看工作区的文件和暂存区的文件比较,我们先打开a.html(已经提交过了),看看里面的内容是什么,如下:
然后我们在里面增加些内容,如下:
增加了ccc这些字母,因为还没有add,所以工作区和暂存区是不一致的,那么现在就可以用命令来看看它们的区别,如下:
绿色的就是新增的,红色的就是删除的,我们发现没有,它不是直接增加ccc这些内容的,而是先删掉要增加的那一行,再增加最新的一行。注意,目标文件是工作区的文件,上图演示的结果就是暂存区的文件如何演变为工作区的文件的过程。
  接下来是看工作区和本地库的差异,那么命令有所改变,格式:git diff [历史版本号] a.html,如下:
HEAD就是说跟当前版本进行比较,图上就表名了本地库和工作区存在差异。
  接下来看暂存区与本地库的差异,命令如下:
没结果说明没差异,没错,我们只是修改了文件,还没有去add呢,所以暂存区的状态跟本地库的状态是一致的,上面的命令说一下,如果是跟别的版本对比,命令:git diff --cached 版本号。好,那么现在我们就add一下吧,这样的话,工作区和暂存区一致,跟本地库就不一致了,再看看工作区和暂存区的差异,如下:也就是说,如果一致,看其差异是没有任何信息的。既然add了,再看看暂存区和本地库的差异吧,如下:
这样是不是从刚才的没有差异信息,变成了有差异信息了呀,哪怎么让暂存区的内容跟本地库的内容一致,是不是commit一下就可以了?再查看差异,如下:一样,没有任何信息就是信息一致了,最后,说一下,如果不带文件名,就是git diff,就表示所有文件的对比。
  有一种情况是a.txt和b.txt进行比较,命令diff a.txt b.txt。注意第二行的内容是一样的,都是8个b,如下:

注意b.txt有5行的,也就是a.txt第3行空一行,b.txt最后一行空一行,如下:

  1. '<‘和’>'分别用于表示diff命令后面的第一个文件(变动前的文件)和第二个文件(目标文件,或者是变动后的文件)的内容。
  2. “1c1”表示第一个文件的第1行有修改(a 添加、d 删除、c 修改),修改为第二个文件中的第1行。对应的过渡文件如下:
    “2a3,4”表示第一个文件的第2行后有添加,添加第二个文件的第3行和第4行。对应的过渡文件如下:
    “4d5”表示第一个文件的第4行有删除,删除的结果是第二个文件的第5行,很明显为空。所以对应的过渡文件如下:
    结论就是告诉你a.txt怎么演变为b.txt,中间需要有一个过渡文件演示。
上图是加了-u参数的,那么怎么看最终结果?
  1. — a.txt 前的三个-号,表示改动前的文件,三个加号就表示改动后的文件。
  2. @@为固定格式,里面的-1,4表示第一个文件的第1行到第4行,+1,5表示第二个文件的第1行到第5行。
  3. 下面的-就代表减轻,删除,+就代表增加,没有符号就表示两个文件共用。说白了就是演示a.txt怎么演变成b.txt的过程。

总结:

22. 查看文件内容经历过的变化

命令:git blame 文件名,可查看谁提交,时间,注意上面的括号最右边的数字1和2,代表第几行,1就是第1行,2就是第2行,如果你重新编辑l.html,把第二行修改了,然后重新添加提交,最后blame查看一下,还是两条信息,最后一条信息的结果就会有所变化(最后一个修改者会把前一个修改者覆盖掉),也就是说,通过git blame命令,我们可以查出某个文件的每一行内容到底是由哪位大神写的。如图上文件的第一行就是cht这个家伙在2021年1月21号晚上11点08分的时候提交的代码。

扩展

如果要查文件中某某行由谁所写:
命令:git blame 文件名 -L a,b
-L 参数表示后面接的是行号(Line), a,b代表文件的第a行到第b行之间的文件内容情况。a,则代表从第a行到文件结尾, 比如:git blame 文件名 -L a,,b则代表从文件开头到第b行。

23. 分支介绍

  什么是分支呢?就是在版本控制过程中,使用多条线同时推进多个任务。在上面学习中我们是在主分支master上的,默认的。
  它在团队协作中很有用,几乎团队里面的每个成员都开一个分支,在前面几节都是我们一个人在玩,但是如果是团队开发,分支是必不可少的,为什么呢?你想,一个项目肯定会有多个功能,那么每个功能我们是不是可以开一个分支去做,是不是为了防止出错,因为分支和分支间是独立的,所以一个功能的错误并不会导致其它功能的错误。如果团队里的每个人都去主干上master去开发,那么如果出现错误了,要删除分支,必定会影响进度,那么你开一个分支的好处,就是你的那个分支出现问题,删掉,没问题,并不会对其它分支产生影响,包括主分支。所以,总结,好处如下:

  1. 一个分支一个功能,那么多个功能可以齐头并进的开发。
  2. 如果一个分支出现问题,删除掉,并不会对其它分支产生影响,也可以理解成是保护主分支。
  3. 各个分支是独立,互不干扰的。

  继续往下看:

创建分支

  命令:git branch。作用:为你创建一个可以移动的新的指针,比如,创建一个test分支:git branch test。这会在当前所在的提交对象上创建一个指针。注意,这个命令是创建一个分支,并不会自动切换到新分支上去。
  我们发现HEAD指向master,说明当前是master指针指向我们当前的版本号,而test并没有HEAD指针指向它,说明HEAD走的同时不带test指针一起走,那么如果我们要切换到新分支,也就是test分支上去的话,那么HEAD指针就得指向test指针,指向成功,说明切换分支成功。上图只能说明创建成功。

切换分支

  命令:git checkout 分支名(会更新我们的工作树和索引)。如下:
是不是指向成功!说明切换成功,当前正在test分支上。注意,此时test分支的状态跟master分支的状态是一致的,相当于copy了一份。往后继续开发就不关master的事了,除非最后合并了。这样的话,你在test分支上开发,如果你的代码写的很烂,可以把test分支给删掉,如果写的很牛逼,那么可以跟master分支进行合并。
  这时候我们可以修改一下a.html,注意打开是只有前两行的,现在我们给它加上新的两行,就是fff~,zzz~这些,如下:
然后add那些就不说了,提交完之后,查看状态,如下:上面是不是HEAD指针带着test指针一起走的,而master却留在原地,说明我们在test分支上。还是我前面说的,看日志要看就看commit,就是我上面用红色框框住的。为了不影响视觉,用如下命令:

  最后注意注意,在切换回其它分支前,比如test分支要切换回master分支,那么必须保证test分支里面的所有文件都已经添加和提交了,否则是切换不回去的,这要注意,它会报错。为什么呢,因为如果切换回master分支,我在前面已经说了,它会更新工作树和索引,那么你在test的工作区所做的修改就会被丢失,因为它是红色的,并没有去add,意味着并没有交给git去管理,那么git在更新的过程中就不会管你这个文件,而是丢到垃圾桶去,你要么马上add,要么就用我后面要说的第24节存储点的stash去解决

查看分支列表

  就是查看有哪些分支存在,如下:

  说白了,就是不加分支名,否则就创建了。图上打了个星星,并且是绿色的表示当前分支。

删除分支

  删除分支必须要切回到主分支去删除,不能自己删自己,那么我要删掉test分支,就得切换回主分支,然后再删除test分支,如下:
  大家有看到图上红色字的说明吧,为什么-d不能删除,因为test所在的版本比我们master所在的版本还要新,它就是告诉我们难道不把test的最新代码拿过来再删除吗?其实我们在删除之前可以执行一个命令:git branch --merged,这个命令会列出当前分支,也就是master,它会以绿色的形式展现出来,同时还会列出比它版本还旧的分支名,那么就可以放心的把它删除,没出现名字的分支名说明比当前分支的版本还新,要合并才行,具体自己操作一下。比如下面这个:

查看分支所指向的版本

新建一个分支并且使分支指向对应的提交对象

  命令:git branch 分支名 哈希值

分支改名

  命令:git branch -m 旧分支名 新分支名

创建分支的分支走向图

  先提交一下,如下:  ok,开始切换分支,如下:
  再次commit,如下:

24. 切换分支之存储点








  在dev这个分支上,写完一半,可用git stash保存起来,最好用git stash save "说明信息"。到时候,要继续开发,可用git stash list进行查看,有哪些保存点,释放,根据需要用pop或者apply,即git stash popgit stash apply
  如果有遇到下面这情况:

我用pop或者apply是从上到下一个一个释放的,如果我想直接释放中间那个,命令:git stash apply stash@{1}即可。

25. 合并分支

  合并分支一样要切换到主分支上,也就是接受修改的,允许被合并的那个分支。然后就是一个命令了,那么这时候,就先新建一个分支,还是test分支,上面被删除了,现在重新建回来,跟上面的操作一样,打开a.html,增加最后两行,现在打开a.html是只有前两行的,也就是aaa~,bbb~这些,那么就按照上一节,先把test分支建好,并切换过去,再修改文件,然后add,commit,再切换回主分支,最后就是合并了,默认前6步已经好了,现在开始合并,如下:
  这样就合并好了,图上Fast-forward表示快进,意思是master前进了几个版本,赶上了test分支所对应的版本。但是更准确的解释应该是,test分支拥有master的最新的那次提交,也就是说,当我们创建并切换回新分支后,在分支上的修改提交并没有影响到master,我们并没有在master上修改提交,是不是意味着test分支就拥有master分支的最新的那次提交吗?像这样子的合并就是Fast-forward,不知道你们能不能理解。如图:
好,注意,它生成出来的commit id(通过git log可看)跟test分支上最新的commit id是一致的。如果把Fast-forward给禁掉了,那么就会生成一个新的commit id,什么意思呢?如下演示:
  以上并没有禁快进,下图演示禁快进,如下:

  重点在一个命令上,就是合并的命令git merge --no-ff dev,说白了,就是多做了一步,提交的操作。注意,在这个命令回车之后,会进入一个界面,这个界面就是提交信息的界面,跟git commit没有加-m是一样的。

分支走向图

  好,现在我们来看看禁掉Fast-forward的结果,如下,先看第一步:
  接下来就是正片了,如下:
  怎么理解这幅图呢?上面我说过,Fast-forward的存在,意味着master的分身也就是test或者bugFix都行,这两个分支拥有master分支的最新修改,或者说最新提交,那么禁掉,是不是说明了master的分身并没有master分支的最新修改,就好像我们切换回master分支上进行修改并提交,所以master分支有了最新的提交(你是可以演示的,比如你可以切换回主分支上,改个文件,注意不要跟你要合并的那个分支的某个文件的某一行冲突,然后add,最后commit,再合并:git merge 分支名。反正要想读懂我说的话,得自己动手操作,你就会明白),而分身因为是独立的嘛,所以并没有拥有master的最新提交,那么这次的合并就不是Fast-forward合并。无论如何记住,Fast-forward产生的前提条件是主分支没有生成新的commit对象
  在上图中,虽然有两条路,但注意,查看历史git log它走的是有bugFix的那条路,版本穿越git reset --hard HEAD^走的就是下面那条路,那么命令的执行结果就是528de6a的位置。总结:查看历史它会把所有的分支情况都列举出来,至于版本回退它是根据你当前所在哪个分支决定的

正常分支走向图

26. 解决合并分支后产生的冲突

  比如,现在的a.html里面有两行,如下:现在,我在test分支上,去打开a.html,在b的后面增加下面句话:
然后保存退出,最后add,commit到本地库哪里,再切换回master主分支,打开a.html,一样,只有前两行,就是上上面第一幅图的那个,现在,我同样在b的后面增加些内容,如下:还是保存退出,然后add,commit,这些做完后,就是合并了,但是这次的合并不一样,因为他们合并的内容冲突了,如上的两幅图,一样都是在b后面的内容,一个是3个test,一个是3个master,都是在同一个位置上,那么这样子去合并,git怎么知道要选谁,毕竟它们处于同一个位置。那么合并后的效果如下:
意思是说自动合并失败了,自动失败了,那就手动呗,怎么手动啊,我们可以打开a.html文件,如下:
发现了什么,是不是test,master这些字眼都出现了,因为git不知道要留下谁,只能你来决定。现在关心的是,上图那些特殊的标记是什么?先看那个HEAD到等号之间的内容,HEAD就是指针指向的当前版本,因为我们是在主分支上打开的a.html,所以在HEAD和等号之间是主分支里的内容,此处理解为就是自己的内容。那么等号到>test里面的内容就test分支里的内容,这就是别人的内容。现在我们可以去编辑里面的内容,先把特殊符号去掉,然后编辑到你满意为止,比如下图:
然后保存退出,查看下状态:也就是说用git add添加到暂存区来表名冲突已解决,因为我们刚刚只是在工作区解决了冲突。那么如下:哪开始commit吧,不过要注意,commit不要带文件名,不然出错,如下:

27. 标签tag

  tag是什么?翻译过来就是标签的意思。标签标签,就是给它打上一个标签,给谁打,给当前所处的版本打,那么这个标签就跟当前版本绑定起来了。它的本质跟branch一样,它也是一个指针,所以,你创建一个标签,就是创建一个指针,只不过这个指针是静止不动的
  打标签的作用是什么呢?可以这么说,它跟commit id,也就是那个哈希值一样,以此来回退到此版本,只不过大家都记不住那冗长的哈希值,所以才用tag来记录,它一般都是用来发布版本的时候才用的,比如项目的1.0版本,上线了,往后还有2.0版本,也可以打上一个标签记录一下,到时候2.0出现什么问题回退到1.0也方便。

打上一个新标签

  命令:git tag <tag name>,比如要发布1.0版本,命令就是git tag "v1.0"。还有一个就是要给标签名加一个注释,命令:git tag -a <tag name> -m <tag message>,比如git tag -a "v8.0.0" -m "更新了若干功能"

查看有哪些标签(类似 git branch)

  命令:git tag

查看历史记录的同时查看标签信息

  命令:git log --decorate。通过这个命令可以看出某某标签跟某某版本id进行绑定。但也可以不加--decorate

指定某个具体的commit id,打上tag标签

  命令:git tag -a <tag name> 版本id -m <tag message>

查看标签的详细信息

  命令:git show <tag name>

标签的删除

  命令:git tag -d <tag name>

根据tag回退

  一样,在创建一个tag,叫v2.0如下:
  然后回退到v1.0,命令如下:
  其实就是标签代替了哈希值。

检出标签

  命令:git checkout -b <branchName> <tagName>

28. 游离HEAD

  在前面说过HEAD(HEAD 是一个对当前检出记录的符号引用,也就是指向你正在其基础上进行工作的提交记录。)是一个指针,指的是一个具体的分支,但它不仅可以指一个具体的分支,还可以指向哈希值,变成游离状态,英文名叫detached,所以你一看到这个单词,就要注意你现在所处的是不是游离状态,一般我们切换到一个哈希值,不会去那里进行提交操作,你一提交,HEAD指向的哈希值就会变化,这很容易理解,因为HEAD会指向最新提交,你每一次提交就会对应一个哈希值。这次哈希值就充当起分支的角色,此时你git branch就会发现如下:
  当你切换回master时,你在游离状态所做的修改就会丢失,也不能说丢失,你要找回除非你记得那一大串的哈希值,然后切换过去那个哈希值的状态就可以了,你用git branch是无效的,所以我们不会在游离状态的时候对文件进行修改提交,那我们切换过去是干什么呢?无非就是查看那个时候的文件状态,或者就是新建一个分支,git branch 分支名。分支名总比那一大串的哈希值好记吧!这样你新建了一个分支,就不再处于游离状态了。

29. 简化历史记录rebase

  在上面讲merge合并的时候其实也表名了合并有两种情况,一种是Fast-forward,另一种是非Fast-forward(术语叫3-way merge,即三向合并)的。并且也告诉你Fast-forward产生的前提条件是怎么样的,接下来将要学习新的命令git rebase,并且会让它跟git merge做下对比。所以在学习rebase的时候也需要有一个分支,我们先做如下准备:
  注意,我是切换到bugFix进行rebase的(一般我们不会在master分支上进行rebase),好,开始进行rebase,如下:
  好,我们发现了它变成了一条直线,如果是merge,它不一样,而是分叉的,没错,这也是区别之一,那么以后,像rebase这种情况,我们就很难看出bugFix分支具体做了什么,因为它们都处于一条直线中,那么具体是什么意思呢?在上上幅图中不难看出,是不是已经出现了分叉阶段了,bugFix并不能获取到master的最新提交,它获取到的其实是b1174ec版本的提交信息,而master已经更新到了0c5d130版本,所以在这种情况下,合并并不是Fast-forward,但是我即想分叉,又想Fast-forward怎么办,那rebase就出场了,如上图rebase后的结果跟上上图还没rebase的结果一对比,是不是上图就让bugFix获取master的最新提交,最新提交是哪个,是不是0c5d130?没错,是不是移动指针,移到0c5d130的后面?这样不就满足Fast-forward的条件了吗?虽然移到后面了,你还有没有发现,后面两个圆圈的哈希值跟上面灰色的两个圆圈的哈希值不一样了,因为它对我们bugFix分支上的所有commit历史进行了重写。但本质上还是一样的。
  上面我们通过执行rebase命令后就已经满足了Fast-forward的条件了,接下来切换master,并merge一下bugFix分支,这些在合并那个章节就已经讲过了,不属于rebase的知识点,如下:

  注意rebase过程中是有可能发生冲突的,也就是图上上的0c5d130…和15a83cb…是有可能发生文件冲突的,有冲突就解决呗,因为我们是在bugFix分支上解决的冲突,所以在bugFix分支上所指向的版本就会有文件更新。我们来演示一下冲突问题,如下:

  打开b.txt,解决冲突,如下:   哪个要留,自己决定,比如两个都留,把特殊符号去掉,然后保存退出,如下:   示意图如下:   下面来演示一下rebase怎么把多个提交记录合并为一条提交记录,就是标题所说的简化历史记录,如下:   然后查看日志,如下:   开始正片,如下:   该命令的-i指的是--interactive,执行回车会出现如下界面:   所以,做如下修改:   在上图中,如果不是s,而是e,下面有解析,就是edit,编辑的意思,那它就是前面第16节的提交修正的第二种方式。好,回归正题,如下:   还是保存退出。再一次查看日志,你就会发现真的合并了,如下:

   注意点就是如果前三条记录已经push到远程仓库了,那么就最好不要去合并了
   再看看第三种场景,我们知道pull是fetch加上merge,所以如下:
  执行git pull命令,如下:
  相信我们肯定看的懂,你拉取下来,就相当创建了一个新分支叫origin/master,然后再merge到master,所以会出现分叉,如果不想出现分叉,哪是不是可以用rebase代替merge呀,所以我们不直接执行pull,而是先执行fetch,再执行rebase,这样就不会分叉了。

30. 反做revert

  revert它跟reset很相似,但也有不同之处,区别之一是revert会提交一个新的commit,具体我们先以演示为例:
  到第二次提交经过测试发现有bug,那么按照reset思想,就是git reset --hard 91852b1,但是不怎么做,我们用revert测试,如下:
  其实到这里我们就知道它跟reset的区别了,有个明显的区别是如果reset到第一次提交那虽然跟revert生成新的commit对象是一样的,但是reset并不能查看到第二次提交的历史记录,而revert如上图是不是可以看到第二次提交的历史记录。
  现在看第二种情况,下面的图接上图继续,第二次情况如下:

  但是这次并没有发现bug,而是在下一次提交发现bug,如下:  如上图在第4次提交有bug,而我在下一次提交才发现,如果以reset思想,是不是要reset到第3次提交,也就是哈希值为dbd84984这个,这样的话我们在第5次提交那所做的修改不就没了吗,也就是我插入的数字2不就没了?那怎么办,我只是想把中间的那个bug去掉,而不影响第3行的,那么revert的价值就出来了,如下:

  问题解决!

31. 针对团队与跨团队开发的Git流程图(与第8节拼接)

  在第8节的简易流程图中是个人在本地自己玩的流程图,而这节是团队之间与跨团队之间的交互流程(引出远程库)。也是为下一节讲GitHub做准备。如图:

  注意小外的远程库跟对方的远程库是独立存在的。

32. GitHub操作

目的

  借助github托管项目代码。

基本概念

  1. 仓库(Repository)仓库的意思,即你的项目,你想在GitHub上开源一个项目,那就必须要新建一个Repository,如果你开源的项目多了,你就拥有多个Repositories。
  2. 收藏(Star):收藏项目,点击星星图标,变成Unstar就是收藏成功。
  3. 复制克隆项目(Fork):fork翻译过来就是分叉的意思,但在此处也是复制的意思,也可以说是它有两层意思,分叉和复制,根据19节那副图可以知道,fork就是把对方的仓库复制一份到自己的仓库,而分叉就意味着它们俩是独立的,分道扬镳。结合起来就是,两个克隆人,虽然长的一样,身体构造啊什么什么的都一样,但是它们却在不同的地方生活着,谁也不影响谁。最后fork具体怎么用,下面会说。
  4. 发起请求(Pull Request):结合上图来看,pull request,就是拉取请求的意思,用于跨团队协作中,它是基于fork来的,是希望对方可以把自己写的合并到对方那里去。
  5. 关注(Watch):这个翻译过来不仅仅是看的意思,还是观察的意思,表示如果你watch了某个项目,那么以后只要这个项目有任何的更新,你都会第一时间收到关于这个项目通知的提醒。
  6. 事务卡片(Issue):举个例子,就是你开源了一个项目,别人发现了你的项目有bug,或者哪些地方做的不够好,他就可以给你提个issue,即问题,问题多了,就是issues,然后你看到这个问题就可以逐步去修复,修复好了就可以一个个的Close掉。
  7. Github主页:账号创建成功或点击网址导航栏Github图标都可以进入Github主页:该页左侧主要显示用户动态以及关注用户或关注仓库的动态,右侧显示所有的Git库。
  8. 仓库主页:仓库主页主要显示项目的信息,如:项目代码,版本,收藏/关注/fork情况等。
  9. 个人主页:个人信息:头像,个人简介,关注我的人,我关注的人,我关注的git库,我的开源项目,我贡献的开源项目等信息。

注册GitHub账号

  现在我们已经学完本地操作了,下一步将是团队操作,在多人开发中,需要有一个库,叫远程库,这样大家就可以一起管理同一个项目了,这节远程库的选择就是GitHub网站,它处于外网中,不管大家在什么地方都可以都可以操作。网址:https://github.com/。好,哪我们点击进去进行注册吧。
  如果还没注册,就点击Sign up进行注册,如下:

  点击Create account,进入验证界面,如下:
  验证成功进入下面问答界面,如下:
  可以选一下,进入下一界面:
  要进行验证邮箱,点击上图的红色框按钮,再登陆你的邮箱,这里我是163邮箱,如下:
  点击,进入如下界面:
  点击验证,后面没什么,不截图了。进入https://github.com/settings/emails可以看看你的邮箱是否验证成功了。验证成功后就可以登陆了。点Sign in,登录进入如下界面:

  上面这个界面就是Github主页。
  下一步:

Settings:用户设置,类似于个人中心那样,在这可以修改密码呀,昵称啊,设置背景主题呀这些,点进去,里面大概讲一讲吧:

  1. Public profile:公共信息 是谁都能看,写上你的真实姓名,邮箱,简介,地址,修改头像什么的,让更多的人认识你。
  2. Account:自己的账户,里有一个Change username 这是你登陆或者注册的那个用户名,不是Profile里的那个名。
  3. Account Security:安全,可以修改你的密码。
  4. Emails:对邮箱的操作,可以增加,修改。
  5. 。。。。

创建仓库

点进去,如下: 1. Public:公共仓库作为开源的项目。
2. Private:私有仓库作为内部团队协作管理的项目。现在不管是public还是private都是免费的。
3. README.md:为当前仓库的说明文件,比如这个仓库是干什么的,怎么用。

然后点击创建,如下:
以上就是仓库主页,点击左上角项目名也可进入,当然,如果你勾选了Add a README file,那么看到的界面会不一样,不过没关系,等会可以新建一个文件就行了,因为它也只是一个说明文件而已。好,在上面已经学过那些基本的概念了,那么像上图应该大部分可以看的懂。

仓库管理

  我们以如下界面为例:
来进行新建文件,很简单,在右上脚哪有个英文叫Add file,点击,选择Create new file,进入,如下:
点击提交,然后到仓库主页看看,如下:当我们点击红色框里的新建a.php文件的时候,进入如下界面:表示提交的详细信息,下面是写评论的。
如果点击的是红色框里的文件名,就是文件内容。点进去可以编辑文件和删除文件,每编辑一次或删除一次都算一次提交。学过命令的可以想的通,如果要查看所有提交的信息,如下:
点进去即可,它相当于git log。那么点进去,如下按钮,可以回退回退查看某个状态点,但注意它并不是版本回退:这时回退要注意,比如我回退到第一个,也就是Initial commit那个,如下:

因为这时候只要一条信息,如果要查看全部信息,要么点击仓库主页的那个项目名进行刷新一下仓库主页,就会回到最新的第二次提交了,再点进去,这也是为什么说它不是真正的版本回退。或者如下:
注意,它不是回退版本,只是让你查看那个时候的状态点而已,不能编辑。你一刷新仓库主页就没了。最后,如果要上传文件,点击Add file,它会出现下拉框,选upload那个就是上传,要查找文件,就点Go to file,或者按键盘t也可以
  其它的就不说了,主要看自己琢磨。

33. Git忽略文件

  忽略文件表示该文件不被git管理,不添加到暂存区,也不提交,因为没必要,哪怎么实现呢?以d.txt为例,如下:
  如果这样,哪我们添加的时候得特别小心,因为一不小心就会把d.txt添加进去,为了杜绝这种现象,有一个文件,它是隐藏文件,名字是固定的,叫.gitignore,如下:
  本身它也是普通文件,但意义不一样,现在我们是想把d.txt脱离git的管理,那么就得打开.gitignore文件,进行编辑,加上一行文件,把要脱离git管理的文件名写上去,如下:
保存退出,再查看状态,如下:
是不是发现红色字眼的d.txt没有了,这些你再用git add .是不是也不怕了,你再把.gitignore当成普通文件添加,并提交即可。.gitignore也支持通配符,比如我要过滤掉.d结尾的文件,就是*.d。但是a.d除外,那就是!a.d
最后,如果要注释,#号表示注释。

34. 团队开发实战

1.项目组长初始化项目

新建项目LibraryManage:

2.新建文件并编辑文件

然后保存退出。

3.把建好的文件提交到本地库

4.创建远程库

先在github上创建一个仓库,仓库名尽量跟本地的项目名一致。如下:

5.为远程库地址取别名

然后为我们复制下来的地址取一个别名,如下:
最后查看一下:

6.推送到远程库

然后刷新一下github的项目仓库,如下:

命令:git push 远程地址 远程对应的哪个分支

7.小程进行克隆

那么我们新建另一个目录叫小程,如下:

命令:git clone 远程地址。克隆这个效果不仅可以帮我们初始化好项目,还会创建origin远程库地址别名,用git remote -v查看。

8.小程对克隆下来的文件进行修改

小程要想push到远程库是不可能的,因为项目组长还没添加它为项目成员。注意,在测试是要有两个github账号来测试

9.加入成员

对方接受就好了。小程再去推送就可以了。

9.项目组长拉取操作

注意pull是由两个组成的,fetch和merge就等于pull。
上面是分开做的,可以直接用pull。像fetch它是把远程库的代码先拉取到本地库,生成一个分支叫origin/master,然后工作区再把origin/master分支的代码merge进来,就实现了跟pull一样的操作。

10,冲突解决

比如两个人都是修改同一个文件和同一行,那么在push上去就会出现问题,比如,我们看看远程库index.php修改后的内容,就相当于提交,如下:
然后我们也在第二行加东西,如下:
开始push,但是因为远程库已经更新了,所以不能push上去,而是pull,进行拉取到最新版本,再push,如下:
是不是跟讲分支是一样的?还是把特殊符号去掉,编辑,add,commit(不带文件名),再push即可。

11,小程求助外援小外

反正小外会去项目组长的仓库主页上,不是有个fork按钮吗?一点就行了,然后到自己的仓库就有了,进行clone到自己本地,就可以在自己的仓库上进行编辑开发了,最后push到自己fork下来的仓库,但是项目组长的那个并没有更新,所以我们要Pull request,注意是在自己,也就是小外的仓库主页进行pull request,如图:
等待对方项目组长通过就行了。对方看到也可以以一种聊天的方式跟你交谈。

35. SSH免密登陆

  如果我们在本地库去push到远程库是需要账号密码的,因为你选择的是https协议的,所以才每次push都要账号密码,除非你系统有凭据可以把账号密码保存下来,才不用输,接下来介绍的SSH免密登陆就可以让你不用账号密码也能push上去。首先,先创建一个远程仓库,如下:
  先别管,现在某个目录创建一个本地库,然后创建a.txt,add,commit即可,但别着急push。如下:
  开始做密钥操作,如下:
  keygen是Key Generator的缩写,译为密钥生成器。-t后面接的是要创建的密钥类型。通过这个命令可以生成一个密钥对,分别是私钥和公钥,会有专门的一个文件保存。上图还没执行完,我们一直回车即可,就会出现如下的界面:
  这样就生成了密钥对,它会有默认的生成位置。就在当前用户的家目录下,如下:
  那么cd进去,就会发现有两个文件,一个文件是私钥的,一个是公钥的,如下:
  不带.pub就是私钥文件。私钥文件就不要告诉其它人,而公钥我们是要告诉github服务器的。然后我们打开公钥文件,如下:
  全部复制,然后打开github官网,进入Settings,如下:


  点击Add后会进入一个确认密码解密,把github密码输入即可,如下:
  ok后,进入的界面如下:  这样,github就知道我们的公钥是什么了。这样就可以无需输入用户名和密码,就可以直接和github进行通讯。接下来我们开始推送,如下:
  再刷新一下远程仓库如下:  这样a.txt就出来了。

36. push出现的问题

  这节主要讲强制推送这个问题,它跟前面讲的reset有关,先做如下准备,如下:
  先建立一个远程库,接着进入cli进行操作,如下:

  然后推送远程库,如下:
  远程仓库刷新就来了,如下:
  点击上图的红色框进去,就会看到提交记录:
  打开cli,开始做reset操作,回退到第一次提交那里,如下:
  下面就开始正片了,如下:
  报错了,push失败,为什么失败?那是因为我们本地库HEAD指向的版本比远程库的版本要旧。所以,要想push成功,如下:
  再刷新一下远程库,如下:
  这样就行了。但是用revert就不会出现这种情况。

37. Eclipse操作git

  Eclipse中默认自带了git插件,通过点击Help->About Eclipe,如下:
  如果没有,就得去安装插件,如下:  Marketplace译为市场。搜索egit即可 。

设置签名

  下一步,设置好签名,如下:
  按照以上格式进行设置。

创建本地库

  先创建好maven工程,名叫demo,如下:
  开始初始化,如下:
  Team的意思就是团队的意思。

  然后看看项目有没有发生变化,如下:
  这就说明项目已被初始化了,看看pom.xml是不是有个问号,问号表示,没有被追踪(没添加到暂存区)

添加暂存区

  现在开始写几句代码,如下:
  开始添加,如下:
  然后项目中我们发现图标变了,变成了蓝色的加号,蓝色的加号表示刚刚添加到了暂存区(就是从问号形态添加到暂存区)

提交

  观察图标:

  有个黄色的圆柱,它表示已经提交到Git仓库。下面应该做忽略文件的处理,否则又是全部提交。如下:
  是不是又加入到暂存区了,观察图标,从黄色圆柱加>变为雪花,并且>不见了,先说说黄色圆柱加>,黄色圆柱表示已提交到仓库,>表示存在未提交的修改,连起来就是圆柱加>表示文件已提交到Git仓库,但是里面有个别修改还没提交。雪花表示添加到了暂存区,等待被提交。下面,开始配置.gitignore文件,可以先打开一个网址: https://github.com/github/gitignore,可以看看Java.gitignore,完整代码如下:

# Compiled class file
*.class #不要编译文件

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

.classpath
.project
.settings
.gitignore
target

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

  注意有些是补充的,像上面.classpath啊,.project啊,.settings啊这些,都是eclipse的特定文件,和开发也没多大关系,所以要忽略掉,这是为了防止团队开发时发生错误,因为有人的eclipse跟你的不一样,可能导致eclipse的特定文件里的内容也不一样,所以,如果都push到远程库,还得为这些文件解决冲突,就没这个必要了。要想看到这些文件,如下:
  回车就可看到项目结构的改变,如下:  这样就很清楚的看到.classpath等这些文件。好,复制上面代码内容到.gitignore文件即可,没有创建一下,然后可以把.gitignore放在任意位置,可以把它放在.gitconfig同目录下,再打开.gitconfig文件,加上这么一句话,如下:  这次再去add,看看是不是把不该添加的文件给忽略掉了呀!好,可以放心提交了。

切换版本

创建分支

  点击,输入dev,然后Finish,它就会自动的切换到dev分支上。

合并分支

  更新下代码,然后提交,如下:
  提交就不截图了,接下来就要切换回master分支上,如下:
  再看看我们在dev分支上更新的代码在master分支上是没有的,如下:
  好,开始合并,如下:

解决冲突

  在master分支上加上下面这句话:
  开始commit,然后切换回dev分支,也更新代码,如下:
  一样commit,接下来开始合并,因为master分支和dev分支都是改了第9行的代码,所以它们肯定会发生冲突,我们先切换回主干上,再合并:
  经过交谈,决定两个都留,如下:
  然后,就是提交了…

上传本地库到github

  点击next,进入下一个界面,如下:   点击next,再点击Finish,反正一路默认。最后刷新远程仓库,就有东西了。

更新本地库

  更新本地库就是pull,我们打开远程仓库进行在线编辑。如下:
  那么在eclipse那里,就得去pull,而不是push,否则会出错,我们也可以更新下代码,进行提交然后直接push,看看结果,如下:

  无法push,所以要pull,拉取最新代码,如下:
  开始解决冲突,如下:
  后面就不用说了,解决完提交即可。

从github克隆项目

  获取地址不说。如下:



  如果提示要输入那就输入呗。然后都是默认的,如果遇到下面界面注意选第三个,如下:
  克隆成功后项目结构如下:
  接下来,进行转换,如下:
  点击,再看项目结构就变了,如下:
  这时候,就有两个本地库了,我们设置下两个本地库的签名,不让他们一样,如下:
  这样,你用这个去提交,就是fenniaoniao了,用我上面的demo项目去提交,因为没设置局部的,而是全局的,就是cht,就近原则嘛,可以看看设置签名那节。

fenniaoniao更新远程库

  然后fenniaoniao更新了代码,如下:
  开始add和push,如下:
  点击next:
  点击ok,如下:
  为什么push不上去,因为你还不是fenxianxian的团队之一,远程仓库是fenxianxian创建的,外人是不能随意更改远程代码的,除非对方同意你是该团队之一,才会让你push,如下:

  在fenniaoniao的163邮箱就可以查收,如下:
  或者,是对方fenxianxian给你一个邀请链接给fenniaoniao,邀请链接怎么获得?如下:
  再把复制好的邀请链接发给fenniaoniao即可,fenniaoniao拿到后以网页的形式打开,如下:
  点击接受即可,这样fenniaoniao和fenxianxian就是合作伙伴了,一个团队的。再重新push一下,然后刷新下远程仓库,看看fenniaoniao的代码是不是更新上去了。

38. Idea操作git

  打开idea编辑器,新建一个springboot项目,项目名为boot,随便,只是测试,如下图:
  我们可以写几句代码,随便,如下:  我做为项目组长(fenxianxian),在本地测试没问题了,如下:
  接下来,就是要往远程push我这个项目了,但是,我idea中并没有去配置版本控制工具,其实它默认是有集成git的,哪怎么做?我们知道,一个文件夹要被git管理,那么这个文件夹里面得有.git隐藏文件,项目更是如此,如下:
  首先,按如下,先进行配置,需要指定.exe核心程序,这也是它跟eclipse的不同之处:
  然后,开始对项目进行初始化,生成.git隐藏目录,但是我们不用命令,而用idea的图形化界面操作,如下:

  确定即可,这样就会初始化项目,相当于执行了git init。如下:
  这样就成功了!然后看看你的项目文件是不是有变颜色,如下:  红色说明还没被git管理,要想被git管理,就要add,如下:
  因为我们是选择整个项目去add,所以那些红色的文件都会变绿,如下:
  接着进行commit,如下:
  点击进去,将会进入一个界面,说明界面,如下:
  点击下方的commit按钮即可。中途如果出现警告,没关系,继续提交。然后,你的项目文件就会恢复到原来的那种无色状态,就是正常状态。要想证明,可以用命令查看,git log,如下:
  也可以不用命令,如下:  点击,就可以看到。接下来,开始建立远程库,如下:  远程库创建好了,接下来就是要把本地的项目push到远程库上面去,达到本地库和远程库一致。但是push之前要登陆我们的github账号,如下:
  如果遇到invalid authentication data错误,哪就选择token登陆,怎么获得token?在Github【Settings】->【Deveployer settings】->【Personal access token】获取new token,如下:

  获取到了复制,粘贴即可登陆成功,别忘点击Apply,和ok。如下:
  开始push,但是,按以往学习命令的时候,push之前要设置下远程地址,git remote add origin 远程地址,说明我要往哪个地址推,字眼是remote,所以,如下:  点击ok即可。下一步开始push:
  然后刷新你的远程库,如下,是不是就来了:  接下来开发人员(fenniaoniao)进行clone,我们新开一个窗口,进行clone,那么要获取地址,如下:  点击然后把获取到的地址粘贴即可clone。可以把clone下来的项目进行测试,测试没问题开始修改代码,如下:  开始add和commit,我们可以单个文件commit,而不选择整个项目,如下:
  最后,开始push,但是这样直接push肯定不行,因为是同一个人,而不是两个人,所以我们要做一些设置,就是在上面我们不是设置了如下图吗:
  因为我要演示两个人的交互过程,而上图设置的是全局的用户名和密码,那么你每次push它就会拿上图的用户名和密码,所以在演示两个人交互的过程,先把它给删了,在每次push之前我们中重新输入即可。所以这次push就会让你输入用户名和密码了,注意push的对象是谁,不是项目组长fenxianxian,而是fenniaoniao,如下:
  然后提交,但是很明显,肯定不会push成功,因为还每让fenxianxian把fenniaoniao加入自己的团队中,所以要把fenniaoniao加入团队中才可以,否则会在你的右下角出现此提示框,如下:
  加入团队就不截图了,看上一节的eclipse操作git即可。然后再重新push即可。对方fenxianxian就会把最新的代码pull下来,pull的过程也不截图了。接下来看看冲突问题,比如fenniaoniao更新了下代码,如下:
  fenniaoniao在第24行开始增加了代码,并push到远程库上。对方fenxianxian也一样在第24行开始增加了代码,如下:
  然后提交push上去,就会遇到下面情况:  说白了就是远程库有最新代码,你并没pull下来,而是直接push,不过没关系,点击Merge,如下:
  三个按钮,你可以选择,要么你的,要么它的,如果都要,就得手动去修改,如果你点的是最后一个按钮,Merge…,又会进入下一个界面:
  很明显,左边有两个按钮,还是一样告诉你选你的,还是选他的,如果你直接点了右边的按钮Apply,又会弹出一个框,如下:
  注意,左边的按钮就是标记为已解决的不要点,否则就表示你写的代码和别人写的代码都不要,那么你下一次push就会把对方代码弄没,如果你都要就必须手动去编辑代码,靠编辑器自动去弄是不行的,所以,如下:
  说白了,都要就是要编辑中间的代码,然后点击Apply,最后在弹出的框点击左边的按钮。再看看自己的编辑器,别忘了重新push上去。在刷新一下远程库看看是不是都有了,然后fenniaoniao那里的代码就不是最新的代码了,就要pull,pull之前要保证你的本地没有修改,都已经提交到本地库了,否则会在右下角出现该框:
  意思就是你本地存在修改,好办法就是,添加存储点,把我们的修改存储起来,如下:
  把我们的修改Stash ,存储起来。操作完后,你会发现项目是修改前的了。然后下一步就是pull,可能会冲突,有冲突解决冲突,再下一步,取出我们修改,然后合并,如下:

  点击Pop stash,就可以了。别忘了最后的push。


转载:https://blog.csdn.net/weixin_45729764/article/details/113344655
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场