git学习笔记(二)

一、创建Git仓库

在使用管理我们的项目文件之前我们需要有一个本地仓库才行。所以我们要在需要进行Git管理的项目中创建一个仓库。

你也可以仓库和项目目录一起创建:

在实际工作中我们更多的是需要克隆团队搭建好的框架然后在本地进行开发,所以要将远程服务器上的仓库完全镜像一份到本地:

其中url是远程服务器上的仓库地址。

创建好仓库之后我们进入项目目录,会发现一个隐藏的.git目录。关于版本的所有信息都存放在这个目录里。所以如果你想删除你某个项目中的git只需要删除这个.git目录即可。

config:这个文件夹里是你的保存设置,这里将写入远程URL,比如你的邮箱、用户名等。没一次在控制端使用“git config”,它都会在这里结束。

description:用来显示对仓库的描述

hooks:这里有一个有趣的特性。Git有一套可以自动运行在任何一个有意义的git阶段下的脚本,叫做hooks。hooks可以运行在commit/rebase/pull等状态之前或之后。脚本的名称决定了它什么时候执行。一个有用的pre-push脚本的例子将会被运行以测试控制器(远程控制)中的所有样式规则保持一致。

info-exclude:可以将你不想被git处理的文件放到到.gitnore文件夹里。

下面我们在之前创建的项目的根目录下新建一个index.html文件,然后在当目录下执行命令:

上面的命令可能你看不懂,可以直接看注释。我们发现.git文件夹的结构发生了变化:

可以看到多了一些内容。这些都是git执行commit操作之后自动创建的,git都会将其压缩并存储到自己的数据结构中。这个压缩对象有一个独特的名字、一个哈希码,存储在对象目录下。

简单的说你可以将commit看做是工作目录的一种快照,但不仅仅是快照。实际上当你commit时,git只做两件事情来创造你的工作目录的快照:

如果文件没有修改,git仅仅增加压缩文件的名字(hash)到快照中

如果文件被修改过,git就将其压缩,再将压缩后的文件存储到对象文件夹中。然后再添加这个压缩文件的文件名(hash)到快照中去。

这些压缩文件都会被存放在对象文件夹下。比如刚刚我们创建了一个index.html文件commit后,对象目录中多出了三个文件夹,其中第一个是我的index.html压缩文件,另一个对应的是我commit时所创建的快照文件,第三个是commit压缩,因为commit本身也是一个对象,它也会被压缩并存储在对象文件夹中。

其实一个commit命令实际上包含4件事情:

工作目录的快照文件的名称(哈希码)

注释

提交者信息

Parent commit的哈希码

其实git中任何事情都可以用一个正确的hash来得到,分支,标签,HEAD它们都一样。下面我们来看看HEAD

HEAD

打开refs/header/master发现:

10ea238c57c595e6a2bfa1619e52f2b25321ac6c

发现这和我么第一次commit时的哈希码是完全一样的,这说明head或者master无非是指向commit的一个指针。

好了,这一块讲的可能比较抽象,如果你看懂了最好,看不懂的话也不影响下面的学习。这里仅仅是让我们更加深刻的理解git内部是如何工作的。

二、查看文件状态

在git版本控制系统中,文件总共有四种状态,分别是:

未跟踪(Untracked):此文件在工作区中,但是还没有加入到git仓库,还未参与版本控制。

未修改(Unmodified):已经提交到仓库中了,但是在工作区中还未进行任何修改的文件,即版本库中的文件快照内容与工作区中的完全一致。

已经修改(Modified):自上次提交之后,文件进行修改了,还没有进行其他任何操作

暂存状态(Staged):文件已经加入到暂存区,还没有提交到仓库中

可以通过git命令来查看指定文件的当前状态:

不能不说git就是强大,我们在查看文件状态时它还会提示你可以对文件进行怎样的操作。

不同的git操作文件会进入不同的阶段,同时会改变当前的状态。

如何查看文件的状态我们可以用git status [filename]。那我们想查看指定状态的文件都有哪些该怎么办呢,比如我想知道我的项目中有哪些文件是未跟踪(Untracked)的。

我们可以通过git ls-files指令来查看指定状态的文件列表,如下:

三、提交文件

将工作区中的代码提交到本地仓库中有两种途径,第一种就是一步一步的来,先添加到暂存区,然后再提交到本地仓库。第二种是直接从工作区提交到本地仓库。

我们先看一下第一种路径。

1、将文件添加到暂存区

添加未跟踪或未修改的文件到暂存区,git命令如下:

2、移除暂存区中的文件(撤销add操作)

有的时候可能发现要提交的文件中有错误,但是已经添加到暂存区了,先在我想把它从暂存区中移除,这种情况该怎么办。

有三种方法可以实现将文件从暂存区中移除。

(1)、方法一:git rm --cached filename1 filename2...

这种方法是将指定文件从暂存区中删除,并且不影响工作区的文件。

git rm的相关扩展

git rm filename,不但从暂存区中删除,同时从工作区中删除

git mv a.txt b.txt,把a.txt改名为b.txt

git clean[options],移除所有未跟踪文件,一般会加上参数-df,-d表示包含目录,-f表示强制清除。

(2)、方法二:git reset HEAD filename1,filename2...

将指定的文件从暂存区回退到工作区,并且不影响工作区中的文件。也可以使用git reset filename。

git reset的相关扩展

其实git reset还可以做很多事情,下面我们来说一下通过reset实现版本回退。

git reset HEAD^:回退版本,一个^表示一个版本,可以多个,另外也可以使用git reset HEAD~n这种形式。(它有3个参数,分别是--soft,--mixed,--hard)

带有--soft参数:git reset --soft HEAD~1表示将版本库软回退1个版本,所谓的软回退表示将本地版本库的头指针全部重置到指定版本,且将这次提交之后的所有变更都移动到暂存区。

默认的是mixed参数:git reset HEAD~1表示为版本库回退1个版本,将本地版本库的头指针全部重置到指定版本库,且重置暂存区,即这次提交之后的所有变更都移动到未暂存阶段。

hart参数:git reset --hard HEAD~1表示将版本库回退1个版本,但是不仅仅将本地版本库的头指针全部重置到指定版本,也会重置到暂存区,并且也会将工作区代码回退到这个版本。

==注意soft参数与默认参数都不会修改工作区代码,只有hard参数才会修改工作区代码,所以要谨慎使用hard参数==

好了,现在我们已经知道了文件如何添加到暂存区了,接下来我们需要将文件从暂存区提交到本地仓库。

(3)、方法三:git checkout --

git checkout --files把文件从暂存区域复制到工作目录,用来丢弃本地修改。当执行git checkout .或者git checkout --命令时,会用暂存区全部或指定的文件替换工作区中的文件。

3、commit命令提交文件到暂存区

commit命令有几种不同的用法:

git commit [filename1 filename2...] -m[message]:提交暂存区中的文件到仓库,其中m表示备注信息。

git commit -a:提交工作区自上次commit之后的变化,直接到仓库区,跳过了add,对新文件无效。(这就是我们前面所说的路径一)

git commit -v:提交时显示所有的diff信息(什么是diff信息,后面会详细介绍)

git commit --amend [filename1 filename2...]-m[message]:使用一次新的commit,代替上一次提交,如果代码没有任何新的变化,则用来改写上一次commit的提交信息。

(1)、关于diff

我们知道可以通过git status来查看文件的状态,但是我们也只能查看文件处于什么状态,不能了解每个文件的内容具体做了哪些改动。那么我们如何查看文件修改后的差异呢?

可以通过git diff[files]命令可以比较工作区中的文件与暂存区中的文件具体差异。

其中---a表示修改之前的文件,+++b表示修改之后的文件。

git diff --cached:比较暂存区的文件与之前已经提交过的文件

当然你也可以把工作区中的状态和本地仓库中的状态进行diff,命令如下:

git diff HEAD~n,比较本地仓库与工作空间中的文件差异

(2)、修正提交

如果我们提交过后发现有个文件改错了,或者只是想修改提交说明,这时可以对相应的文件做出修改,将修改过的文件通过“git add”添加到暂存区,然后执行git commit --amend -m[message]

四、撤销提交

有时候我们提价了文件之后,发现有错误,不想提交了,想撤回。那么该怎样撤回之前的提交呢?撤销提交有三种方式,分别是reset、checkout和revert,下面我们将分别介绍这三种方法。(其实上checkout本质上并没有实现撤销提交,因为它根本没有改变HEAD指针,只是在某些情况下实现了撤销提交的效果)。

1、方法一:git reset

reset的字面意思是“重置”,在介绍它之前我们先说一下几个概念。

(1)、三棵树

理解reset和checkout的最简单的方法,就是以Git的思维框架(将其作为内容管理器)来管理三棵不同的树。“树”在我们这里的实际意思是“文件的集合”,而不是指特定的数据结构。

Git作为一个系统,它以它的一般操作来管理并操纵这三棵树:

HEAD:HEAD是当前分支引用的指针,它总是指向该分支的最后一次提交。这表示HEAD将是下一次提交的父结点。通常,理解HEAD的最简单方式,就是将它看做你的上一次提交的快照。

Index:索引是你的“预期的下一次提交,暂存区域”,运行git add后,代码就进入“暂存区域”。

Working Directory:可以把工作目录当做“沙盒”。在将修改提交到暂存区并记录暂存区并记录到历史之前,可以随意更改。

(2)、重置的作用

将当前的分支重设(reset)到指定的或者HEAD(默认是HEAD,即最新的 一次提交),并且根据参数有可能更新index和working directory(默认是mixed)

有关--hard,--soft和--mixed参数之前我们已经提到了,这里我们简单的举例说一下。

--hard:重设index和working Directory,从

以来在working directory中的任何改变都被丢弃,并把HEAD指向。

说明:第二次提交的b.txt已经被丢弃!HEAD指针重新指向了第一次提交的commitID。彻底回退到某个版本,本地的源码也会变为上一个版本的内容。

--soft:index和working directory中的内容不做任何改变,仅仅把HEAD指向

。自从以来的所有改变都会显示在git status的“Changes to be commited”中。

说明:第二次提交的test2被重置到了“changes to be commited”中!HEAD指针重新指向了第一次提交的commitID。回退到某个版本,只回退了commit的信息。如果还要提交,直接commit即可。意思就是暂存区和工作区中的内容没有回退。

--mixed:仅仅重设index,但是不重设working directory。这个模式是默认模式,即当不显示告知git reset模式时,会使用mixed模式。这个模式的效果是,working directory中文件的修改都会被保留,不会丢失,但是也不会被标记成“Changes to be commited”,但是会打出什么还未被更新的报告。

说明:第二次提交的b.txt被重置到了初始状态(上述示例为“Untracked”)HEAD指针重新指向了第一次提交的commitID。回退到某个版本,只保留源码,回退commit和index信息。

当然reset还可以用来解决冲突,这时候就需要使用另外两个模式了merge和keep。关于这一点我们会在分支那一块讲解。

2、checkout实现撤销提交

当执行“git checkout HEAD .”或者“git checkout HEAD”命令时,会用HEAD指向的master分支中的全部或部分文件替换暂存区以及工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中为提交的改动,也会清除暂存区中未提交的改动。

这种做法就是我们经常说到了检出,它也有很大的作用,比如说我们提交文件之后,在工作区继续书写代码,修改了很多地方,但是发现我们修改错了,想重新回到修改之前的样子,我们除了撤销提交之外还可以用检出,将本地仓库中的文件复制到暂存区和工作区中。

看起来checkout和git reset --hard HEAD~1有点像,但是它们本质是不一样的,前者只是从仓库中检出最新提交的当前所在分支的文件到暂存区和工作空间中,并没有改变HEAD指针,而后者则是将HEAD指向前一个commit,并且删除当前的commit。

所以checkout只是在某些情况下实现了撤销提交的效果,本质上是没有过撤销提交的。

3、revert实现撤销提交

git revert也是撤销命令,它和reset的区别在于reset是指向原地或者向前移动指针,git revert是创建一个commit来覆盖当前的commit,指针向后移动。

git是撤销某次操作,此次操作之前的commit都会被保留。举一个例子:假设有三个提交(commit1,commit2,commit3),使用git status:

当执行git revert HEAD~1时(撤销倒数第一个操作,指针向前移动一个),使用git log可以看出现在指针正在commit2上呢。

git revert的用法:

git revert HEAD:撤销前一次commit

git revert HEAD^:撤销前前一次commit

git revert commitID(比如:fa042ce57ebbe5bb9c8db709f719cec2c58ee7ff)撤销指定的版本,撤销也会作为一次提交进行报错。

当我们执行一次 git revert HEAD会发现先创建了一个commit并且工作区回退了一步。

git revert是用一次新的commit来回滚之前的commit,git reset是直接删除指定的commit。

在回滚这一操作上看,效果差不多。但是在日后继续merge以前的老版本时有区别的。因为git revert是用一次逆向的commit“中和”之前的commit,因此日后合并老的branch时,导致这部分改变不会再次出现,但是git reset是把某些commit在某个branch上删除,因而和老的branch再次merge时,这些被回滚的commit应该还会被引入。

git reset是把HEAD向前移动了一下,而git是HEAD继续往后走,只是新的commit的内容和要revert的内容正好相反,能够抵消要被revert的内容。

扩展:查看日志与历史

上面我们用到了git log --oneline命令,我们现在就说一下日志相关的命令。命令格式如下:

示例:

git log --graph:表示以图形化的方式显示提交历史的关系,这就可以方便地查看提交历史的分支信息,当然是控制台用字符画出来的图形。

"git log -1"则表示显示1行。

更多关于git log的用法这里不展开讲了,只简单提一下这几个常用到的。

五、创建忽略文件

这个就很简单了,有时候我们不想把某些文件纳入版本控制中,想在提交的时候把一些不想提交的文件跳过。

比如我在使用node开发一个小的应用,但是用到了很多第三方模块,当我提交到远程仓库的时候,如果我把整个项目都提交上去,如果这些第三方模块体积很大的话,再加上网很慢的情况,可能我出去喝杯咖啡回来都还没提交完呢。

所以我们可以把这些模块文件都忽略掉,在package文件中列一个模块清单就可以。

那怎样忽略这些文件呢?

我们需要创建一个名为".gitignore"的文件,这个文件的规则如下:

文中的空行或以井号(#)开始的行将会被忽略。

可以使用Linux通配符。例如:星号*代表任意多个字符,问号(?)代表一个字符,方括号([abc])代表可选字符范围,大括号()代表可选的字符串等。

如果名称的最前面有一个叹号(!),表示例外规则,将不被忽略。

如果名称的最前面是一个路分隔符(/),表示要忽略的文件在此目录下,而子目录中的文件不忽略。

如果名称的最后面是一个路径分隔符(/),表示要忽略的是此目录下该文件名称的子目录,而非文件(默认文件或目录都忽略)。

示例:

更多的规则请看这里

小结

本文介绍了文件的不同操作,包括添加到暂存区,提交文件,撤销提交等等,后面的文件我们将介绍git中比较重要的块: post_link git学习笔记(三) git中的分支

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180408G0660P00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券