Skip to content

Latest commit

 

History

History
306 lines (268 loc) · 11.8 KB

Git-Study_1.md

File metadata and controls

306 lines (268 loc) · 11.8 KB

内容概要

  1. Git是什么以及起源
  2. Git中的文件状态,快照的含义
  3. 仓库的创建、忽略文件的设置;暂存和取消暂存;文件的删除和恢复;文件移动和重命名;查看修改和提交记录;最基础的推送操作

参考文献

  1. Pro Git
  2. git diff命令输出解读-阿里云社区
  3. git rm --cached 与git reset HEAD -- 的区别

1. Git起步

Git是什么呢?简单的说,Git是一个分布式版本控制系统(Distributed Version Control System,DVCS),用于跟踪软件开发过程中源码的变化。“分布式”表明在写代码时多个程序员可以互相协作,“版本控制”表明它并不是仅仅保存了仓库最新的版本哦,仓库的整个“前世今生”,也就是变更历史,都记在了它的小本本上(Git版本库)。

Git是怎么诞生的呢?2002年,Linux内核开源项目组开始免费使用一个叫BitKeeper的DVCS来管理代码,2005年呢不给免费用了,Linux开源社区就开发出了Git.

2. Git的工作原理

Git的工作区域

我们称.git文件夹为Git版本库,称开发的项目和.git文件夹共同所在的父目录为工作目录。

2.1 Git中文件的状态

基本的Git工作流程如下:

  1. 在工作目录中修改文件。
  2. 暂存文件(git add),将文件的快照放入暂存区域。(其实就是Git版本库下的一个文件,保存了下次将提交的文件列表信息)
  3. 提交更新(git commit),找到暂存区域的文件,将快照永久存储到Git版本库。

我们先将Git中的文件分为两大状态:已跟踪的(tracked)和未跟踪的(untracked)。已跟踪的文件是指在暂存区域或者Git版本库中的文件,这些文件已被纳入了版本控制。两个地方都不在的文件就是未跟踪的文件。

根据Git的工作流程的三个步骤,可以将已跟踪的文件再划分为3个状态:未修改(unmodified)、已修改(modified)、已暂存(staged)。三个状态的切换周期如下。

Git文件的生命周期

2.2 Git中快照的含义

Git中的快照是什么意思呢?在你每次暂存文件时,Git对已修改的文件直接拷贝,对未修改的文件保存一个之前存储文件的链接(可以更高效),这就是快照的概念。

项目随时间改变的快照

上图给出了项目随时间改变的快照,当文件发生了修改时(相对于上次暂存文件),我们在文件名后添加/修改整数后缀表示文件被修改了(文件名实际并没有变),如Version1中的A在Version2中变成了A1;我们用虚线框出没有被修改的文件,如Version2中的B。

值得注意的是,上述过程都是本地执行,所有暂存、提交造成的版本更新都记录在本地的Git版本库。而git push、get fetch等需要跟Github在线仓库打交道的命令才需要连接服务器。

3. Git实战

我们为编程之路专栏创建一个工作目录,以后所有的文章的离线版(md文件)包括相关的代码和资源文件都会放在该目录下。

3.1 创建工作目录和初始化仓库

$ mkdir 编程之路;cd 编程之路
$ git init

3.2 忽略文件

列出工作目录下的文件

$ ls
doc/  Git学习(一).md  README.md  src/  Ubuntu-18.04安装与配置.md

其中,doc是存放文档的文件夹,src是存放代码的文件夹,Git学习(一).md是本文的离线版。在本次写作过程中,doc下存放的图片会不断增加,.md也会不断被修改,在写作完成之前,我们不希望其纳入Git的管理。在这种情况下,我们可以创建一个名为.gitignore的文件,列出要忽略的文件。

$ cat .gitignore
/doc
*.md

3.3 暂存和取消暂存文件

将所有文件放入暂存区(不包括要忽略的文件)

$ git add .

检查一下文件状态

$ git status
On branch master
No commits yet
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   .gitignore

可以看到此次只暂存了*.gitignore*。那可以手动暂存被忽略的文件吗?我们试一下,

$ git add *.md -f
$ git status
On branch master
No commits yet
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   .gitignore
	new file:   "Git学习(一).md"
	new file:   README.md
	new file:   "Ubuntu-18.04安装与配置.md"

是可以的,但是我们现在并不想用Git管理这些文件,还记得吗,在暂存区域的文件属于已跟踪的文件,所以我们要把它们从暂存区删掉

$ git rm --cahed *.md
error: the following file has staged content different from both the
file and the HEAD:
    Git学习(一).md
(use -f to force removal)

好像出了问题?这是因为我在暂存Git学习(一).md之后,又修改Git学习(一).md(往里面添加了”是可以的,但是我们现在并不想...从暂存区删掉“),导致暂存区域和工作目录下的Git学习(一).md文件产生了差异,现在再删除暂存区的Git学习(一).md会导致修改前的版本丢失,所以Git不让我们删除,这是一种保护机制。但我们自己知道这操作没问题,来吧,暴力点

$ git rm --cahed *.md -f
rm 'Git学习(一).md'
rm 'README.md'
rm 'Ubuntu-18.04安装与配置.md'
$ git status
On branch master
No commits yet
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   .gitignore

有一个命令git reset HEAD -- < file > 功能与 git rm --cached < file > 功能类似,具体请见git rm --cached < file >与git reset HEAD -- < file >的区别

3.4 查看修改

写一点项目代码

//hello.h
#include<iostream>

class Hello{
    Hello(const Hello &)=delete;
    Hello &operator(const Hello &)=delete;
    ~Hello()=delete;

    static void Greeting(){
        std::cout<<"Hello World!\n";
    }
}
//hello.cpp
#include"hello.h"
//main.cpp
#include"hello.h"

int main(){
    Hello::Greeting();
}

暂存一下

$ git add .

导师说cout有多态,嫌慢不给用,改一下

//hello.h
#include<cstdio>

class Hello{
    Hello(const Hello &)=delete;
    Hello &operator(const Hello &)=delete;
    ~Hello()=delete;

    static void Greeting(){
        std::printf<<"Hello World!\n";
    }
}

让Git帮我们看看改了哪儿

$ git diff

git diff的输出

第一行:diff --git a/src/hello/hello.h b/src/hello/hello.hgit diff实质上用的是diff的文本比较工具,其中a/src/hello/hello.h代表源文件,也就是修改前的文件,b/src/hello/hello.h代表目标文件,也就是修改后的文件;

第二行:index 98963f7..25dc952 100644, index后面两个16进制值表示两个文件的hash值,最后的数字是文件的权限和属性。

第三四行:---代表源文件,+++代表目标文件;

第五行和第十一行:差异范围,表明紧接着的具体差异在源文件和和目标文件的位置,以@@ -1,4 +1,4 @@为例,其分为两个部分,第一个是-1,4,表示紧接着的具体差异在源文件从第1行开始的4行,第二个是+1,4,则在目标文件的第1行开始的4行。

以-开头 的行代表在源文件的基础上删除,以+开头代表在源文件基础上添加;

3.5 提交更新

提交一下更新,把快照永久放到版本库里

$ git commit -m 'first commit'
[master (root-commit) 00b9fbf] first commit
 4 files changed, 19 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 src/hello/hello.cpp
 create mode 100644 src/hello/hello.h
 create mode 100644 src/hello/main.cpp

3.6 文件的删除和恢复

导师说”Hello类又没静态域,把hello.cpp删了“。

$ git rm src/hello/hello.cpp
rm hello.cpp

提交到Git版本库,想少打几个字(-a是add的缩写,作为这条命令的参数表示"你帮我自动add一下吧")

# 所有未被忽略的文件必须处于已跟踪状态才行
$ git commit -a -m "delete one source file"

导师不在,偷偷写个弱智小游戏

//game.h
#include<string>            
#include<cstdio>
using std::string;
using std::printf;

void Game(const string &question){
    if(question.size()<6) printf("我不回答这种弱智问题。");
    else if(*question.rbegin()!='?' && *question.rbegin()!='~') printf("请端正你
的态度。");
    else printf("随缘。");
}

顺手提交一下

# 此时game.h还处于未跟踪状态,不能使用git commit -a
$ git add src/hello/game.h
$ git commit -m "unspeakable"

被抓到了,”实验室电脑磁盘不够用了,删了“。假装删掉(在Git版本库中还存在快照)

$ rm src/hello/game.h

好像看不到人影了,从Git版本库恢复出来

$ git checkout -- src/hello/game.h

真的不想玩了

$ git rm src/hello/game.h # 等效于 rm src/hello/game.h;git add .
$ git commit -m "not play game agagin"

3.7 查看提交历史

$ git log
commit a4f664509c95115b4cc5659d9365cdd8a93ff1a8 (HEAD -> master)
Author: LYC-BF <540536130@qq.com>
Date:   Sat Mar 2 16:33:43 2019 +0800

    not play game agagin

commit ca391642fe89c1f3fd758e9ee587ff70df3b47a9
Author: LYC-BF <540536130@qq.com>
Date:   Sat Mar 2 16:08:17 2019 +0800

    unspeakable

commit 8c630070224b9326bbfcdbffda103c81a58452fa
Author: LYC-BF <540536130@qq.com>
Date:   Sat Mar 2 15:28:18 2019 +0800

    delete one source file

commit 00b9fbf9561776a0640335f060e337112daf0105
Author: LYC-BF <540536130@qq.com>
Date:   Sat Mar 2 14:21:38 2019 +0800

    first commit

一个常用的选项是 -p,用来显示每次提交的内容差异。你也可以加上 -<n> 来仅显示最近n次提交。git log参数超多,毫无疑问有各种筛选功能,具体请看man git log。

3.8 文件(夹)的移动和改名

与Linux的mv命令相似,文件(夹)的移动和改名共用同一个命令git mv,修改名含有路径参数即是移动。将* .md文件放入其专属的文件夹内

$ git mv Ubuntu-18.04安装与配置.md doc/Ubuntu-18.04安装与配置.md
fatal: not under version control, source=Ubuntu-18.04安装与配置.md, destination=doc/Ubuntu-18.04安装与配置.md

显然git mv只能操作已跟踪的文件,因此我们先暂存一下,移动文件完文件后再取消暂存

$ git add *.md -f
$ git mv Ubuntu-18.04安装与配置.md doc/Ubuntu-18.04安装与配置.md
$ git mv Git学习(一).md doc/Git学习(一).md
$ git reset HEAD -- doc/ *.md
$ git reset HEAD -- README.md
$ git status
On branch master
nothing to commit, working tree clean

其实对于未跟踪的文件,移动和改名只需要使用Linux的mv命令即可,这里使用git mv只是为了介绍git mv移动文件的前提和git reset的用法。

3.9 分支、远程仓库代称、推送简述

看一下目前的版本库有哪些分支

$ git branch
* master

前面的*号指明了当前所在分支。在这里也就是唯一的分支master,该分支是调用git init命令时默认创建的。

我们添加一个远程仓库,用来将本地的版本库托管在服务器上, 运行git remote add <shortname> <url>

$ git remote add origin git@github.com:Mr-Awakened/Programming-Road.git

这里origin就是远程仓库代称,用来代指其后的url,代称可以是其他名字。如果是自己的仓库,一般起名为origin。我们可以调用git remote命令看一下目前有哪些远程仓库

$ git remote
origin

取消忽视文件,并快照存到版本库

$ cat .gitignore
$ git add .
$ git commit -m "add git basics"

最后我们将版本库推送到服务器

$ git push origin master