Git

Pro Git

Git笔记

Posted by HustDsy on November 9, 2021

1.Git 简史

​ Git有三种状态,分别是已提交(commited)、 已修改(modified)和已暂存(staged)。

  • 已提交:表示数据已经安全的保存在本地数据库中
  • 已修改:表示修改了文件,但还没保存到数据库中
  • 已暂存:表示数据对一个已修改的文件的当前版本做了标记,使之包含在下次提交的快照中

由此引入Git项目的三个工作区域的概念:Git仓库,工作目录以及暂存区域。

image-20211111162803588

Git 仓库目录

是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,拷贝的就是这里的数据。

工作目录

是对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。

暂存区域

是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中。 有时候也被称作`‘索引’’,不过一般说法还是叫暂存区域。

基本的 Git 工作流程如下:

  1. 在工作目录中修改文件。
  2. 暂存文件,将文件的快照放入暂存区域。
  3. 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。

2.Git信息配置

用户信息的配置有System级别 ,global(用户)级别和local(当前仓库)三个设置,底层配置会覆盖顶层配置。用户信息包括用户名称和邮件地址。

git config --global user.name "HustDsy"
git config --global user.email "379644606@qq.com"
#查看配置信息
git config --global --list

3.Git基础

  • 克隆现有的仓库, git clone git@github.com:HustDsy/progit.git

  • 工作目录下的每一个文件都不外乎这两种状态:已跟踪或未跟踪。 已跟踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录,在工作一段时间后,它们的状态可能处于未修改,已修改或已放入暂存区。 工作目录中除已跟踪文件以外的所有其它文件都属于未跟踪文件,它们既不存在于上次快照的记录中,也没有放入暂存区。 初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态。

    image-20211112173839410

  • 为了方便下面的测试,在github上新建了一个git仓库progit,用来接下来的测试。(测完就删了)

3.1检查当前文件状态

使用git status来查看文件的状态。现在最开始仓库里只有readme这样的一个文件。

image-20211112174913780

可以看出来,所有已跟踪的文件在上次提交后都未被修改过。此外,上面的信息还表明,当前目录下没有出现任何处于未跟踪状态的新文件,否则Git会列出来。现在分支名是main。

接下来再项目下创建一个新的txt文件。如果之前并不存在这个文件,使用 git status命令,会看到一个未跟踪的文件。

$ echo "dsy is a cool boy" > description.txt
$ git status
On branch main
Your branch is up to date with 'origin/main'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	description.txt

在状态报告中可以看到新建的description.txt文件出现在Untracked files下面。未跟踪的文件意味着Git之前的快照(提交)中没有这些文件;

3.2跟踪新文件

使用命令git add开始跟踪一个文件。所以要跟踪新文件,运行:

$ git add description.txt
$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   description.txt

只要在 Changes to be committed 这行下面的,就说明是已暂存状态。

3.4暂存已修改文件

现在来修改一个已被跟踪的文件。就修改description.txt,可以

$ echo "today is friday" > description.txt
$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   description.txt

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   description.txt

可以看到文件出现在Changes not staged for commit这一行,说明已跟踪文件的内容发生了变化,但还没有放到暂存区。接下来使用git add,这个命令理解成添加内容到下一次提交中,现在运行git status来看看结果。

$ git add description.txt
$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   description.txt

3.5状态简览

使用git status -s或者git status --short将会得到一种更为紧凑的格式输出。

$ git status -s
MM 2.txt
 M README.md
A  description.txt
M  test.txt
?? 1.txt

其中A代表新增的文件,??代表未跟踪的文件,M出现在右边代表修改了,但是没有添加到暂存区,M出现在左边代表修改了并且添加到了暂存区。出现两个MM代表添加到暂存区之后又被修改了。

3.5忽略文件

一般我们总有些文件无需纳入Git管理,也不希望它们总出现在未跟踪文件列表。通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等,在这种情况下,我们可以创建一个名为gitinore的文件,列出要忽略的文件模式。来看一个实际的例子:

*.[oa]
*~

第一行告诉Git忽略.o或.a结尾的文件。一般这类对象文件和存档文件都是编译过程中出现的。

GitHub有一个十分详细的针对数十种项目及语言的.gitnore文件列表,网址为https://github.com/github/gitignore 。

3.6查看已暂存和为暂存的修改

直接使用git diff用来比较工作目录中当前文件和暂存区域快照之间的差异.

使用git diff --cached或者git diff --staged用来表示已暂存的将要添加到下次提交里的内容。

前者可以理解成当前版本和修改后的已经暂存的版本的比较,后者可以理解成初始版本和修改后的已经暂存的版本的比较。

3.7提交更新

现在的暂存区域已经准备妥当已经可以提交了。在此之前,请一定要确认还有什么修改过的或新建的文件还没有git add过,否则提交的时候不会记录这些还没暂存起来的变化。

git commit命令后添加-m选项,将提交信息与命令放在同一行,如下所示:

$ git commit -m "add some test"
[main 77b3d30] add some test
 4 files changed, 3 insertions(+)
 create mode 100644 1.txt
 create mode 100644 description.txt

请记住,提交时记录的是放在暂存区域的快照。 任何还未暂存的仍然保持已修改状态,可以在下次提交时纳入版本管理。 每一次运行提交操作,都是对你项目作一次快照,以后可以回到这个状态,或者进行比较。

3.8跳过使用暂存区域

尽管使用暂存区域的方式可以精心准备要提交的细节,但有时候这么做略显繁琐。Git提供了一个跳过使用暂存区域。git commit -a

$ git status
On branch main
Your branch is ahead of 'origin/main' by 3 commits.
  (use "git push" to publish your local commits)

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   1.txt
	modified:   2.txt
	modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")
$ git commit -m "skip stage" -a
[main e8b2879] skip stage
 3 files changed, 3 insertions(+), 2 deletions(-)

3.9移除文件

首先假设第一种情况我们需要删除某个文件,正常的操作一般是三步,执行rm命令,再执行git add命令,最后在git commit。使用git rm的话相当于直接执行了前两步。

$ git rm 2.txt
$ git add 2.txt
$ git rm description.txt
$ git status
On branch main
Your branch is ahead of 'origin/main' by 5 commits.
  (use "git push" to publish your local commits)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	deleted:    2.txt
	deleted:    description.txt

第二种情况就是我们将某些文件加入到了暂存区,但是我们想将它从暂存区移除这时候使用git rm --cached

$ git status
On branch main
Your branch is ahead of 'origin/main' by 7 commits.
  (use "git push" to publish your local commits)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   test.txt
#现在我们将test.txt从暂存区移除
$ git rm --cached test.txt
#查看状态,已经移除了
$ git status
On branch main
Your branch is ahead of 'origin/main' by 7 commits.
  (use "git push" to publish your local commits)

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	test.txt

nothing added to commit but untracked files present (use "git add" to track)

3.10重命名文件

$ git mv file_from file_to
$ git mv README.md README
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README
#相当于接下来的三条命令
$ mv README.md README
$ git rm README.md
$ git add README

3.11查看提交历史

在提交了若干更新之后,或者克隆了某个项目之后,我们回顾历史。完成这个任务的命令是git log。默认不使用任何参数的话,显示的是如下信息。

$ git log
commit 8802df96599434fd5ee82a3704c6bf88187eeb5a (HEAD -> main)
Author: HustDsy <379644606@qq.com>
Date:   Mon Nov 15 15:41:30 2021 +0800

    dada

commit 21ebd4bb70e592e75f79be67252eb9df26d2e8cd
Author: HustDsy <379644606@qq.com>
Date:   Mon Nov 15 15:31:22 2021 +0800

    dada

commit 1106d9bcce983340778d4bdfdc2d0ba449e8e902
Author: HustDsy <379644606@qq.com>
Date:   Mon Nov 15 15:27:19 2021 +0800

    delete

commit f14b52c530ba8dd266bc4b2fb7746ad93dd38eea
Author: HustDsy <379644606@qq.com>
Date:   Mon Nov 15 15:16:53 2021 +0800

    git rm

加上-p是用来显示每次提交的内容差异,加上-2来仅显示最近两次提交。该选项除了显示基本信息之外,还附带了每次commit的变化。使用git --stat显示每次提交的简略信息。

$ git log -p -2
commit 8802df96599434fd5ee82a3704c6bf88187eeb5a (HEAD -> main)
Author: HustDsy <379644606@qq.com>
Date:   Mon Nov 15 15:41:30 2021 +0800

    dada

diff --git a/test.txt b/test.txt
new file mode 100644
index 0000000..be40189
--- /dev/null
+++ b/test.txt
@@ -0,0 +1,2 @@
+2222
+2222

commit 21ebd4bb70e592e75f79be67252eb9df26d2e8cd
Author: HustDsy <379644606@qq.com>
Date:   Mon Nov 15 15:31:22 2021 +0800

    dada

diff --git a/test.txt b/test.txt
deleted file mode 100644
index 49ca6c4..0000000
$git log --stat -2
commit 8802df96599434fd5ee82a3704c6bf88187eeb5a (HEAD -> main)
Author: HustDsy <379644606@qq.com>
Date:   Mon Nov 15 15:41:30 2021 +0800

    dada

 test.txt | 2 ++
 1 file changed, 2 insertions(+)

commit 21ebd4bb70e592e75f79be67252eb9df26d2e8cd
Author: HustDsy <379644606@qq.com>
Date:   Mon Nov 15 15:31:22 2021 +0800

    dada

 test.txt | 2 --
 1 file changed, 2 deletions(-)

另外一个常用的选项是--pretty。这个选项可以指定使用不同于默认格式的方式展示提交历史,这个选项有一些内建的子选项供你使用。git log --pretty=format"%h%s" --graph

$ git log --pretty=format:"%h %s" --graph
* 2d3acf9 ignore errors from SIGCHLD on trap
*  5e3ee11 Merge branch 'master' of git://github.com/dustin/grit
|\
| * 420eac9 Added a method for getting the current branch.
* | 30e367c timeout code and tests
* | 5a09431 add timeout protection to grit
* | e1193f8 support for heads with slashes in them
|/
* d6016bc require time for xmlschema
*  11d191e Merge branch 'defunkt' into local

image-20211115162956231

git log的常用选项如下所示:

image-20211115163039874

还可以限制git log的输出范围。

image-20211115164214496

3.12撤销操作

撤销操作以下几种情

  • 假设上一次的commit操作,你忘记添加文件或者commit信息写错了,使用–amend选项的提交命令尝试重新提交.git commit -amend

  • 假设你把某个文件放到了暂存区,但是想把它拿出来

    $ git status
    On branch main
    Your branch is ahead of 'origin/main' by 10 commits.
      (use "git push" to publish your local commits)
      
    Changes to be committed:
      (use "git restore --staged <file>..." to unstage)
    	new file:   dsy.txt
    	modified:   test.tst
    $ git reset HEAD dsy.txt
    #或者
    git restore --staged dsy.txt
    #这样子dsy.txt就从暂存区里拿了出来
    $ git status
    On branch main
    Your branch is ahead of 'origin/main' by 10 commits.
      (use "git push" to publish your local commits)
      
    Changes to be committed:
      (use "git restore --staged <file>..." to unstage)
    	modified:   test.tst
      
    Untracked files:
      (use "git add <file>..." to include in what will be committed)
    	dsy.txt
    
  • 撤销对文件的修改,加入你不保留对某个文件的修改,如何方便地撤销修改。有两种方式。将它还原成上次提交的样子或者刚放入工作目录时的样子。

$ git checkout -- dsy.txt
$ git restore dsy.txt

3.13远程仓库

  • 查看已配置完毕的远程仓库服务器可以使用git remote -v命令

    $ git remote -v
    dsy	https://github.com/HustDsy/CCEH.git (fetch)
    dsy	https://github.com/HustDsy/CCEH.git (push)
    dsyy	git@github.com:HustDsy/CCEH.git (fetch)
    dsyy	git@github.com:HustDsy/CCEH.git (push)
    origin	git@github.com:HustDsy/progit.git (fetch)
    origin	git@github.com:HustDsy/progit.git (push)
    pb	https://github.com/paulboone/ticgit (fetch)
    pb	https://github.com/paulboone/ticgit (push)
    #使用git fetch可以拉下来相关的仓库的代码
    
  • 添加远程仓库git remote add (仓库名) (仓库地址)

  • 从远程仓库中抓取与拉去数据git fetch [remote-name]

  • 推送到远程仓库git push [remote-name] [branch name]

    #推送到origin的master分支
    $git push origin master 
    
  • 远程仓库的移除git remote rm [name]与命名git remote rename

    $git remote rename pb paul
    $git remote rename paul
    

3.14打标签

  • 列出标签 git tag
  • 创建标签,主要有轻量标签和附注标签,一个轻量标签很像一个不会改变的分支 - 它只是一个特定提交的引用。然而,附注标签是存储在 Git 数据库中的一个完整对象。 它们是可以被校验的;其中包含打标签者的名字、电子邮件地址、日期时间;还有一个标签信息;并且可以使用 GNU Privacy Guard (GPG)签名与验证。 通常建议创建附注标签,这样你可以拥有以上所有信息;但是如果你只是想用一个临时的标签,或者因为某些原因不想要保存那些信息,轻量标签也是可用的。
  • 附注标签的创建运行tag命令式指定-a选项。git tag -a v1.1 -m "test tag----"
  • 轻量标签,轻量标签的本质上是将提交检验和存储到一个文件中,创建轻量级标签,不需要使用-a -s -m选项,只需要提供标签名字。git tag v1.1
  • 后期打标签 git tag -a v1.2 [校验和]
  • 共享标签/将标签显式的推送到共享服务器上.git push [remote-name] [tag-name]
  • 检出标签 git checkout -b [branchname] [tagname]

4.Git分支

Git保存的不是文件的变化或者差异,而是一系列不同时刻的文件快照,在进行提交操作时,Git会保存一个提交对象。每次提交会包含一个指向暂存内容快照的指针。但不仅仅是这样,该提交对象还包含了作者的姓名和邮箱,以及指向它的父对象的指针。普通提交操作产生的提交对象有一个父对象,而由多个分支合并产生的提交对象有多个父对象。

$ git add README test.rb LICENSE
$ git commit -m 'The initial commit of my project'

当使用 git commit 进行提交操作时,Git 会先计算每一个子目录(本例中只有项目根目录)的校验和,然后在 Git 仓库中这些校验和保存为树对象。 随后,Git 便会创建一个提交对象,它除了包含上面提到的那些信息外,还包含指向这个树对象(项目根目录)的指针。如此一来,Git 就可以在需要的时候重现此次保存的快照。

image-20211117151553113

做些修改后再次提交,那么这次产生的提交对象会包含一个指向上次提交对象(父对象)的指针

image-20211117151710277

Git的分支,其实本质上仅仅是指向提交对象的可变指针。Git的默认分支名字是master。在多次提交操作之后。其实已经有一个指向最后那个提交对象的master分支。它会在每次的提交操作中自动向前移动。

image-20211117152142387

4.1分支创建

git创建分支实际上是创建了一个可以移动的新的指针。创建一个testing分支,可以使用git branch命令:

$ git branch testing

这会在当前所在的提交对象上创建一个指针。

image-20211117155710451

在 Git 中,Head是一个指针,指向当前所在的本地分支(译注:将 HEAD 想象为当前分支的别名)

我们可以简单的使用git log命令查看各个分支当前所指的对象。提供这一功能的参数是--decorate

git log --oneline --decorate

4.2分支切换

要切换到一个已存在的分支,我们可以使用git checkout命令。我们现在切换到新创建的testing分支去:

git checkout testing

image-20211117160409740

在两个分支上分别提交之后分支的变化如下:

image-20211117160821009

4.3分支的新建与合并

image-20211117165122256

git merge [branchname] 将branchname 合并到当前分支

git checkout -b [branchname]创建对应的分支

git branch -d [branchname]删除某个分支

git branch -r -d [remotename/branch]在不删除远程分支的情况下停止跟踪远程分支

git checkout -t [remote-branch] -b [branchname]创建一个本地分支跟踪远端分支

4.4分支开发工作流

git push [服务器名字] [本地分支]:[远程分支]

git pull [服务器名字] [远程分支]:[本地分支]

git fetch [服务器名] [分支名]

git push origin --delete [branchname] 或者 git push origin :[branchname]

4.5变基

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
  • 可以这样理解,手先git会将experiment这个分支上的每个commit都取消掉。
  • 将上面的操作临时保存成patch文件,存在.git/rebase目录下;
  • 然后把experiment分支更新到最新的master分支上
  • 最最后把上面保存的patch文件应用到experiment分支上
  • image-20211119173227348

image-20211119173236019