Git学习(1)

参考廖雪峰老师教程的git学习笔记

Posted by 许大仙 on January 14, 2019

Git学习篇

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

CVS/SVN:集中式版本控制系统,版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。

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

既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。

分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了。

在实际使用分布式版本控制系统的时候,其实很少在两人之间的电脑上推送版本库的修改,因为可能你们俩不在一个局域网内,两台电脑互相访问不了,也可能今天你的同事病了,他的电脑压根没有开机。因此,分布式版本控制系统通常也有一台充当“中央服务器”的电脑,但这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已

Git的优势不单是不必联网这么简单,后面我们还会看到Git极其强大的分支管理,把SVN等远远抛在了后面。

版本库/仓库【repository】

简单理解为目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,或者在将来某个时刻可以“还原”。

一、创建版本库

  1. 在本地创建一个空目录,并进入该目录,此处为GitLearning

    git1

  2. 使用git init命令把该目录变成git可管理的目录【通过ls -ah可以看到该目录下出现了一个隐藏文件.git】

    git2

  3. git init命令以后,已经建好了git仓库,并且提示这是一个empty Git repository

  4. 最好在空目录下创建git仓库【选择一个已经有东西的目录也是可以的,但是不建议如此】

二、把文件添加到版本库

①跟踪文件情况
  1. 一切的版本控制系统,只能跟踪纯文本编写文件的改动【txt,doc,代码等文本文件】

    **注意!!!!**

    • Microsoft的Word格式是二进制格式,因此,版本控制系统是没法跟踪Word文件的改动的。
    • 强烈建议使用标准的UTF-8编码,所有语言使用同一种编码,既没有冲突,又被所有平台所支持。
    • 千万不要使用Windows自带的**记事本**编辑任何文本文件
      • 原因是Microsoft开发记事本的团队使用了一个非常弱智的行为来保存UTF-8编码的文件,他们自作聪明地在每个文件开头添加了0xefbbbf(十六进制)的字符。
      • 会遇到很多不可思议的问题,比如,网页第一行可能会显示一个“?”,明明正确的程序一编译就报语法错误,等等,都是由记事本的弱智行为带来的。
      • 建议下载Notepad++代替记事本,把Notepad++的默认编码设置为UTF-8 without BOM
  2. 而图片、视频这些二进制文件,虽然也能由版本控制系统管理,但没法跟踪文件的变化。只知道图片从100KB改成了120KB,但到底改了啥,版本控制系统不知道,也没法知道。

②添加某文件到版本库
  1. 放到GitLearning目录下【即放到你初始化好为仓库的目录下】

  2. git add <文件名>

    若文件名处为. 时表示添加当前目录下所有文件

  3. git commit -m”<本题提交的说明>"

    -m后面输入的是本次提交的说明,方便从历史记录里方便地找到改动记录

    git成功会提示,n file changed,m insertions:n个文件被改动,插入了m行内容

  4. 多次add,一次commit

    $ git add file1.txt
    $ git add file2.txt file3.txt
    $ git commit -m "add 3 files."
    
③获取工作区动态
  1. git status:让我们时刻掌握仓库当前的状态【文件修改,文件是否最新,文件删除等】

    git3

    我们删除了README.md文件,添加了text.md文件,但是在上次git add+commit以后没有再添加提交过,因此当前版本库的状态status是:

    Changes not staged for commit列出了还没有准备提交的修改

    deleted: README.md 文件被删除了

    untracked file:从来没有被添加过的文件【text.md】

  2. git diff <文件名>:查看文件变更/修改的具体内容【查看工作区和版本库里面最新版本的区别】

  3. 重新git add以后,通过git status 查看状态

    git4

    提示了当前仓库的状态和变更情况

  4. git commit,提交,得到提交后的变更情况

    git5

  5. 此时再git status

    $ git status
    On branch master
    nothing to commit, working tree clean
    

    即Git告诉我们当前没有需要提交的修改,而且,工作目录是干净(working tree clean)

④小结
  • 要随时掌握工作区的状态,使用git status命令。
  • 如果git status告诉你有文件被修改过,用git diff可以查看修改内容。
  • 在Git中被称为commit 认为是一个快照。一旦把文件改乱了,或者误删了文件,还可以从最近的快照中恢复——即从一个commit中恢复

版本回退

一、历史记录总览

  1. git log命令显示从最近到最远的提交日志
    • commit id(版本号)【SHA-1,用十六进制表示】
      • 和SVN不一样,Git的commit id不是1,2,3……递增的数字
      • commit id需要用这么一大串数字表示呢?因为Git是分布式的版本控制系统,若多人在同一个版本库里工作,如果大家都用1,2,3……作为版本号,那肯定就冲突了。
    • 提交者author
    • 提交日期date
    • commit命令 -m给出的message信息
  2. git log –pretty=oneline:历史记录简述信息
    • commit id(版本号)
    • -m给出的message信息

git6

二、回退

  1. Git版本:

    • 在Git中,用HEAD表示当前版本【即此时的5130a45....
    • 上一个版本就是HEAD^,上上一个版本就是HEAD^^
    • 往上100个版本可写成HEAD~100
  2. git reset –hard HEAD^:回退到上一个版本

    • 原来的text.md文件

    git71

    • 回滚到该Git仓库的上一个版本【HEAD^——这里指的是add text.md】

    git73

    • text.md变成之前的状态

    git72

  3. 此时再看git log!!!!之前那个add some的版本没了!

    git74

  4. 怎么回去?

    • 前提:能知道要退回版本的commit id
    • 只要上面的命令行窗口还没有被关掉,或者你还记得那个commit id
    • 你就可以顺着往上找啊找啊,找到那个add somecommit id5130a4...,于是就可以指定回到未来的某个版本:
    $ git reset --hard <回到commit id>
    HEAD is now at 83b0afe append GPL
    
    • 版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。

      git75

    • 确实回来了

    • 如果命令行被你关了,又不记得commit id了,怎么办?

    • Git提供了一个命令git reflog用来记录你的每一次命令:

      git76

三、版本回退原理

  • Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD指针从当前版本修改到回退的版本上。

  • 对于某个仓库有三个版本按照时间近到远有append GPL,add distributed以及wrote a readme file。当前版本为append GPL,现在回退到add distributed。

  • Git仅仅是把HEAD从指向append GPL

git-head

​ 改为指向add distributed

git-head-move

  • 然后顺便把工作区的文件更新了。所以你让HEAD指向哪个版本号,你就把当前版本定位在哪。

四、小结

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

工作区和暂存区

  • 工作区:在电脑里能看到的目录

  • 当git init以后,普通的电脑目录——工作区就变成了版本库【有一个隐藏目录.git】、

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

      git-repo

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

    • git add:把文件修改添加到暂存区【把要提交的所有修改放到暂存区(Stage)】

      • 可以认为暂存区就是暂存修改
    • git commit:提交更改,实际上就是把暂存区的所有内容提交到当前分支【可以一次性把暂存区的所有修改提交到分支】

      • 只提交add到暂存区的修改,没有add的修改不会更新到当前分支
      • 因为我们创建Git版本库时,Git自动为我们创建了唯一一个`master`分支,所以,现在,`git commit`就是往`master`分支上提交更改
    • 一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的

      $ git status
      On branch master
      nothing to commit, working tree clean
      
    • Git跟踪并管理的是修改,而非文件。

撤销修改

一、工作区回退

  1. 在工作区修改了 readme.txt但是还没有提交到暂存区时

  2. git status查看一下当前状态

    $ git status
    On branch master
    Changes not staged for commit: //changes还没有stage到暂存区
      (use "git add <file>..." to update what will be committed) //提示通过git add提交到暂存区
      (use "git checkout -- <file>..." to discard changes in working directory)//提示如果要抛弃工作区的修改,通过git checkout -- <file>
       
        modified:   readme.txt
       
    no changes added to commit (use "git add" and/or "git commit -a")
    
  3. 命令git checkout -- readme.txt:把readme.txt文件在工作区的修改全部撤销

    • 如果readme.txt只是在工作区修改,还没有提交到暂存区。那么这条命令会使得readme.txt撤销修改,回到和版本库一模一样的状态【也就是上一次add/commit以后的状态,即没有这个修改以前的状态】;——最近一次是git commit
    • 如果`readme.txt`已经添加到暂存区后,又作了修改现在,撤销修改就**回到添加到暂存区后的状态**。【也是刚刚这个工作区的修改被撤销,但是之前提交到暂存区的修改没有被撤销】——最近一次是git add
  4. 总之,就是让这个文件回到最近工作区修改以前的状态【在最近一次git add或git commit之前的那个状态】

    • git checkout -- file命令中的--很重要,没有--,就变成了“切换到另一个分支”的命令

二、暂存区回退

  1. 修改已经add到暂存区了,此时工作区没有别的修改时

    $ git status
    On branch master
    Changes to be committed://change已暂存,当还没commit
    (use "git reset HEAD <file>..." to unstage) //通过git reset HEAD <file>,可以撤销暂存区的修改
    modified:   readme.txt
    
  2. 用命令git reset HEAD <file>:可以把暂存区的修改撤销掉(unstage)重新**放回工作区**

  3. git reset命令既可以回退版本【加–hard 版本】,也可以把暂存区的修改回退到工作区【没有–hard,直接版本+file名称】

  4. git reset HEAD <file>以后,暂存区是干净的,但是修改放回工作区了,因此工作区是脏的,有修改的【参考一、工作区退回】

    $ git status
    On branch master
    Changes not staged for commit:
    (use "git add <file>..." to update what will be committed)
    (use "git checkout -- <file>..." to discard changes in working directory)
    modified:   readme.txt
    
  5. 再使用 git checkout -- readme.txt,那么将会把工作区的修改也撤销。

  6. 所以通过两部,可以撤销暂存区的修改:

    $ git status
    On branch master
    nothing to **commit**, working tree clean
    

三、小结

场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file【这个命令实际是:用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。但是只能恢复文件到最新版本HEAD,除非你版本回退了,HEAD变了】

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

场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本退回一节【git reset –hard 版本号/版本标识】,不过前提是没有推送到远程库。

文件删除

一、工作区删除

  • 在文件管理器中把没用的文件删了
  • rm命令删了
  • git status命令:会提示哪些文件被删除了【给出当前工作区状态】

二、版本库删除

  • 从版本库中删除该文件:用命令git rm删掉+git commit -m"<message>"

    • git rm:先删除工作区里的文件【相当于手动删除加git add,而rm只是工作区删除没有加add】
    • git commit:提交删除到版本库
    • 先手动删除文件,然后使用git rm <file>git add<file>效果是一样的。
    $ git rm test.txt //从版本库中删除文件
    rm 'test.txt'
      
    $ git commit -m "remove test.txt"
    [master d46f35e] remove test.txt
     1 file changed, 1 deletion(-)
     delete mode 100644 test.txt //提示已经删除
    

三、撤销删除

  • 工作区删错,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:

    $ git checkout -- test.txt
    

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