git学习--廖雪峰老师git学习教程

廖雪峰老师的教程很好!推荐学习链接

1、git的前世今生:

《git学习--廖雪峰老师git学习教程》

Git是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。
Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。
Git 与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,不必服务器端软件支持。

2、初始化仓库

使用git init命令初始化

3、工作区和暂存区

工作区:就是你在电脑里能看到的目录,比如我的HelloWorld文件夹就是一个工作区。

版本库:工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。
Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。(在Windows系统下git clone下来的项目文件夹根目录下就有一个.git文件夹,我想就是所谓的版本库吧!mac上是没有见到的是因为这个目录默认是隐藏的,用ls -ah命令就可以看见。见下图)

git init——>工作区(git add)——>暂存区(git commit)——>版本库

暂存区很重要,必须将每一次的修改放入暂存区,之后才能提交commit到版本库。否则你的修改将不会被跟踪,不能提交到版本库。
《git学习--廖雪峰老师git学习教程》

《git学习--廖雪峰老师git学习教程》(关于图上的关系详见文章结尾处的说明,必看很重要哦!)这张关系图很重要关系到对后面命令的理解。

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

第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区(stage);(还有git add也是用来为文件增加跟踪的)

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

因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。
你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。

一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的:

$ git status
# On branch master
nothing to commit (working directory clean)

《git学习--廖雪峰老师git学习教程》

4、git 相关命令

git init

git add <file> //把文件添加到仓库,

git add . //把所有的文件添加到暂存区(他会监控工作区的状态树,使用它会把工作时的所有变化提交到暂存区,包括文件内容修改(modified)以及新文件(new),但不包括被删除的文件。)

git commit -m “” //提交文件,-m后面跟的是本次提交说明

git status //可以掌握仓库的状态,看是否进行了更改(包括修改未提交的,它会告诉你哪些文件被修改过,但是具体修改了什么地方并没有告诉你。)

git diff <file> //可以看到文章具体修改的地方和内容,当知道这些之后在进行git add 提交到仓库就放心多了,最好在commit之前再status一下看仓库的状态,都是绿色的modified再提交。

小结:

要随时掌握工作区的状态,使用git status命令。
如果git status告诉你有文件被修改过,用git diff可以查看修改内容。

5、版本回退

Git帮助我们管理项目版本,每当你觉得文件修改到一定程度的时候,就可以“保存一个快照”,这个快照在Git中被称为commit。一旦你把文件改乱了,或者误删了文件,还可以从最近的一个commit恢复,然后继续工作,而不是把几个月的工作成果全部丢失。

git log 命令来实现版本的信息查看,输出日志

git log –pretty=oneline 输出以版本号信息为主要内容的日志

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

git reset 命令可用于回退版本

例如:git reset –hard HEAD^ //表示回退到上一个版本
此时在使用git log 可以看到输出的是从开始到上一个版本的日志信息

当然git reset也可以从回退版本回到最新的版本,只要上面的命令行窗口还没有被关掉,你就可以顺着往上找啊找啊,找到那个最新版本的commit id是xxxx…,于是就可以指定回到未来的某个版本:

例如:$ git reset –hard 3628164 //版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。

《git学习--廖雪峰老师git学习教程》

Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD从指向append GPL改为指向add distributed

git reflog用来记录你的每一次命令,包括每一次提交每一次回退,这是一个完整的记录

小结

HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset –hard commit_id。
穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本。
要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本。

6、管理修改

Git比其他版本控制系统设计得优秀,因为Git跟踪并管理的是修改,而非文件。

什么是修改?比如你新增了一行,这就是一个修改,删除了一行,也是一个修改,更改了某些字符,也是一个修改,删了一些又加了一些,也是一个修改,甚至创建一个新文件,也算一个修改。

也就是说git追踪的是修改而不是文件

因此要让自己的修改都被保存就需要将所有有用的修改都git add放入暂存区,然后进行commit提交,否则修改不会生效,也不会合并。
例如:第一次修改 -> git add -> 第二次修改 -> git commit 那么暂存区只会将第一次的修改提交,第二次的不会

第一次修改 -> git add -> 第二次修改 -> git add -> git commit
好,现在,把第二次修改提交了.

git diff HEAD — <file>命令可以查看工作区和版本库里面最新版本的区别

小结

理解Git是如何跟踪修改的,每次修改,如果不add到暂存区,那就不会加入到commit中。

7、 撤销修改

git checkout — file(注意–很重要)

可以丢弃工作区的修改(也就是说丢弃的是工作区修改的内容,还未保存的。命令将会把文件新修改的地方删除。所以这个命令最好使用在没有git add之前,而使用这个命令是可以将内容回退到上次提交的版本的。):

  • 一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;

  • 一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。

总之,就是让这个文件回到最近一次git commit或git add时的状态。

git reset HEAD file 可以把暂存区的修改撤销掉(unstage),重新放回工作区。(这个命令是说可以将你git add的文件从暂存区撤销掉,重新放回工作区。但是文件内容不做修改,如果想丢弃文件内容则用上面的命令。)

小结

场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout — file。
场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD file,就回到了场景1,第二步按场景1操作。
场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。

8、删除文件

  • rm <file>
    它的作用是将文件从工作区删除,但是暂存区和版本库的文件还在,除非之后使用git add .或者<file> 告诉暂存区已经删除文件。但是只要没有commit都可以将文件还原回来,使用git reset –hard 版本号 或者 git reset –hard HEAD

  • git rm <file>
    这个命令和rm <file>的区别在于,它不仅将工作区的文件删除了,并且将删除告诉了暂存库,暂存库也没有该文件。但是和前面一样,只要不提交还能使用命令进行恢复。

上面是如何删除文件,但是如果想将删除的文件找回来,使用以下命令:

    • git checkout — <file>

    1. checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。

    但是要注意的是,这个命令在对使用rm <file>删除的文件有效,能够还原删除的文件,但是使用git rm <file>删除的文件再使用这个命令恢复文件时就会报错。
    

    《git学习--廖雪峰老师git学习教程》

    • git reset –hard 版本号 或者 git reset –hard HEAD
      一键还原

    • git reset HEAD
      git checkout <file>

    两步还原

    原因是:大家可以回到本文的第一张关系图,可以看出蛛丝马迹。图中我们可以看出checkout — <file>是将文件从暂存区恢复到工作区,而git rm <file>删除了工作区和暂存区的文件,所以使用checkout没有效果。

    而git reset –hard 版本号 或者 git reset –hard HEAD 直接是版本还原,同时还原暂存区和工作区。
    而git reset HEAD只是将暂存区的还原,因此之后还要使用git checkout <file>,来将文件从暂存区还原到工作区。

    小结

    命令git rm用于删除一个文件。如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。

    9、远程仓库(先创建本地仓库再创建远程仓库(其中远程仓库不改变任何信息的方法,readme也不创建))

    现在一般使用github或者gitlab做远程仓库,关于ssh这篇文章写的很好(特别是如何查看mac的ssh)ssh

    • 首先和远程仓库建立链接:
      $ git remote add origin git@github.com:weiweismile/仓库名.git

    远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。

    • 把本地库的所有内容推送到远程库上:

    $ git push -u origin master

    把本地库的内容推送到远程,用git push命令,实际上是把当前分支master推送到远程。

    由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。

    • 再次推送
      从现在起,只要本地作了提交,就可以通过命令:

    $ git push origin master
    

    把本地master分支的最新修改推送至GitHub,现在,你就拥有了真正的分布式版本库!

    注意:在第一次远程提交时会产生SSH警告

    小结

    要关联一个远程库,使用命令git remote add origin git@server-name:path/repo-name.git;
    关联后,使用命令git push -u origin master第一次推送master分支的所有内容;
    此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改;
    分布式版本系统的最大好处之一是在本地工作完全不需要考虑远程库的存在,也就是有没有联网都可以正常工作,而SVN在没有联网的时候是拒绝干活的!当有网络的时候,再把本地提交推送一下就完成了同步,真是太方便了!

    10、远程仓库(先创建远程仓库,再创建本地仓库。(这时远程仓库会同时创建readme文件))

    • 先clone一个本地仓库

    git clone git@github.com:michaelliao/gitskills.git
    • 接着进入这个本地仓库文件夹,使用git push origin master进行内容的push。

    这里要注意两点:一个是必须要进入到相应的文件夹进行操作。一个是必须要将本地要提交的文件commit放在版本库才能提交到远程。

    GitHub给出的地址不止一个,还可以用https://github.com/michaellia…这样的地址。实际上,Git支持多种协议,默认的git://使用ssh,但也可以使用https等其他协议。

    使用https除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放http端口的公司内部就无法使用ssh协议而只能用https。

    小结

    要克隆一个仓库,首先必须知道仓库的地址,然后使用git clone命令克隆。

    Git支持多种协议,包括https,但通过ssh支持的原生git协议速度最快。

    11、分支管理

    1. 创建与合并分支

    • 合并原理
      在版本回退里,你已经知道,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master分支。HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。

    一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点:
    《git学习--廖雪峰老师git学习教程》

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

    当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:
    《git学习--廖雪峰老师git学习教程》

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

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

    《git学习--廖雪峰老师git学习教程》

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

    《git学习--廖雪峰老师git学习教程》

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

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

    《git学习--廖雪峰老师git学习教程》

    1. 实战演练

    • 创建分支

    $ git checkout -b dev
    Switched to a new branch 'dev'

    git checkout命令加上-b参数表示创建并切换,相当于以下两条命令:

    $ git branch dev
    $ git checkout dev
    Switched to branch 'dev'
    
    • 用git branch命令查看当前分支:

      $ git branch

      • dev
        master

    git branch命令会列出所有分支,当前分支前面会标一个*号。
    然后,我们就可以在dev分支上正常提交,比如对readme.txt做个修改,加上一行:

    Creating a new branch is quick.
    

    然后提交:

    $ git add readme.txt 
    $ git commit -m "branch test"
    [dev fec145a] branch test
     1 file changed, 1 insertion(+)
    
    • dev分支的工作完成,我们就可以切换回master分支:

      $ git checkout master
      Switched to branch 'master'
      

    切换回master分支后,再查看一个readme.txt文件,刚才添加的内容不见了!因为那个提交是在dev分支上,而master分支此刻的提交点并没有变:
    《git学习--廖雪峰老师git学习教程》

    • 将分支合并到master分支上

     $ git merge dev
    
     Updating d17efd8..fec145a
     Fast-forward
     readme.txt |    1 +
     1 file changed, 1 insertion(+)

    git merge命令用于合并指定分支到当前分支。合并后,再查看readme.txt的内容,就可以看到,和dev分支的最新提交是完全一样的。

    注意到上面的Fast-forward信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。

    当然,也不是每次合并都能Fast-forward,我们后面会讲其他方式的合并。

    • 删除dev分支了:

    $ git branch -d dev
    Deleted branch dev (was fec145a).

    • 删除后,查看branch,就只剩下master分支了:

      $ git branch

      • master

    小结

    Git鼓励大量使用分支:

    查看分支:git branch

    创建分支:git branch <name>

    切换分支:git checkout <name>

    创建+切换分支:git checkout -b <name>

    合并某分支到当前分支:git merge <name>

    删除分支:git branch -d <name>

    1. 解决冲突

    产生冲突的原因是一般当分支和master都修改了同一个文件,那么合并时会发生冲突,这需要人工手动解决。
    Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容。
    当大家商量好如何修改以后,在进行merge。(注意还是要先add和commit)

    用git log –graph命令可以看到分支合并图:

    $ git log --graph --pretty=oneline --abbrev-commit
    *   59bc1cb conflict fixed
    |\
    | * 75a857c AND simple
    * | 400b400 & simple
    |/
    * fec145a branch test
    ...
    

    《git学习--廖雪峰老师git学习教程》

    小结

    当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。

    用git log –graph命令可以看到分支合并图。

    1. 分支合并策略

    合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。

    如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。因此我们采用–no-ff策略。

    准备合并dev分支,请注意–no-ff参数,表示禁用Fast forward:

    $ git merge --no-ff -m "merge with no-ff" dev
    

    合并后,我们用git log看看分支历史:

    $ git log –graph –pretty=oneline –abbrev-commit

    • 7825a50 merge with no-ff
      |\

    | * 6224937 add merge
    |/

    • 59bc1cb conflict fixed

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

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

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

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

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

    《git学习--廖雪峰老师git学习教程》

    小结

    Git分支十分强大,在团队开发中应该充分应用。

    合并分支时,加上–no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。

    番外

    图4中左侧为工作区,右侧为版本库。在版本库中标记为 “index” 的区域是暂存区(stage, index),标记为 “master” 的是 master 分支所代表的目录树。
    图中我们可以看出此时 “HEAD” 实际是指向 master 分支的一个”游标”。所以图示的命令中出现 HEAD 的地方可以用 master 来替换。
    图中的 objects 标识的区域为 Git 的对象库,实际位于 “.git/objects” 目录下,里面包含了创建的各种对象及内容。
    当对工作区修改(或新增)的文件执行 “git add” 命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中。
    当执行提交操作(git commit)时,暂存区的目录树写到版本库(对象库)中,master 分支会做相应的更新。即 master 指向的目录树就是提交时暂存区的目录树。
    当执行 “git reset HEAD” 命令时,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响。
    当执行 “git rm –cached <file>” 命令时,会直接从暂存区删除文件,工作区则不做出改变。
    当执行 “git checkout .” 或者 “git checkout — <file>” 命令时,会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险,会清除工作区中未添加到暂存区的改动。
    当执行 “git checkout HEAD .” 或者 “git checkout HEAD <file>” 命令时,会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。
    《git学习--廖雪峰老师git学习教程》

        原文作者:weiweismile
        原文地址: https://segmentfault.com/a/1190000009829228
        本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
    点赞