git使用笔记

1 git简介

  Git 是分布式版本控制和源代码管理系统,重点使用和管理代码的速度。 Git 最初是由Linus Torvalds设计开发的,用于管理Linux内核开发。Git 是根据GNU通用公共许可证版本2的条款分发的自由/免费软件。
  git官网:git-website,git使用相关文档:git-doc

2 git基本使用

2.1 git-config

  配置git可以使用git config配置后的内容会写入/etc/gitconfig~/.gitconfig,另外git config可以接参数--global进行全局配置,配置会写入~/.gitconfig,使用参数--system配置会写入/etc/gitconfig。Windows中默认目录为C:\Documents and Settings\$USER

  git配置示例:

git config --global user.name "xxxx"        #配置用户名xxxx
git config --global user.email  xxx@xxx.com #配置用户邮箱xxx@xxx.com
git config --global core.editor vim         #配置git默认编辑器可选vim和emacs
git config --global merge.tool vimdiff      #配置git差异比较工具vimdiff
git config --list                           #显示当前配置

#$ git config --list
#user.name=xxxxx
#user.email=xxxxx@outlook.com
#http.postbuffer=524288000
#core.editor=vim
#merge.tool=vimdiff

  修改后可以看到~/.gitconfig文件内的内容:

[user]
        name = xxxx
        email = xxxx@outlook.com
[http]
        postBuffer = 524288000
[core]
        editor = vim
[merge]
        tool = vimdiff

  更多git config相关参数可以查看git config --help

2.2 git工作流程

  git使用流程一般为clone远程仓库–>修改–>add 和 commit–>push远程仓库。具体过程如下图所示:

在这里插入图片描述

  git使用分为三个区:工作区,暂存区和版本库:

  • 工作区:当前工作目录;
  • 暂存区:stage或者index,文件路径为.git/index,一般也把暂存区称为索引;
  • 版本库:即工作区中的隐藏目录.git

  如下图为三者之间切换的关系:

在这里插入图片描述

  其中wrokshop是工作区;index是暂存区;repository是暂存区master。

2.3 创建仓库

git init <name>

  演示如下:

$ git init git
初始化空的 Git 仓库于 /home/altas/Documents/git/.git/

  也可以直接创建目录然后,执行git init

# altas @ altas in ~/Documents [17:47:04]
$ mkdir git

# altas @ altas in ~/Documents [17:47:07]
$ cd git

# altas @ altas in ~/Documents/git [17:47:10]
$ git init
初始化空的 Git 仓库于 /home/altas/Documents/git/.git/

  也可以从远程仓库clone版本库到本地,其中url必须有,dir是可选的,如果不提供dir则自动创建目录:

git clone <url> <dir>

  其中版本库便是.git,该目录下的结构如下:

$ tree -a
.
└── .git
    ├── branches
    ├── config
    ├── description
    ├── HEAD
    ├── hooks
    │   ├── applypatch-msg.sample
    │   ├── commit-msg.sample
    │   ├── post-update.sample
    │   ├── pre-applypatch.sample
    │   ├── pre-commit.sample
    │   ├── prepare-commit-msg.sample
    │   ├── pre-push.sample
    │   ├── pre-rebase.sample
    │   └── update.sample
    ├── info
    │   └── exclude
    ├── objects
    │   ├── info
    │   └── pack
    └── refs
        ├── heads
        └── tags

10 directories, 13 files

2.4 git编辑和提交

  git使用涉及到的命令:

  • git status,查看当前仓库的文件状态;
  • git diff,查看修改了那些文件的哪些内容;
  • git add <file>,将文件添加到暂存区;
  • git commit -m <msg>,提交文件,从暂存区转到版本库;
  • git reset HEAD,撤销修改;
  • git rm,移除暂存区中的文件
  • git mv,对暂存区中的文件进行重命名。

2.4.1 [git add|git status|git commit]

  由于三个命令相关性比较高,因此放到一块儿。
  git add <file>将文件file添加到暂存区,可以使用git add .当前为暂存的所有文件添加到暂存区。
  git status查看文件状态,比如文件是否修改,文件是否在暂存区之类,配合git add,git commit使用。

# git status常用参数
-s, --short     #显示简短的输出
    --long      #显示详细的输出
-b  --branch    #显示指定分支的输出

  git status输出通过特殊的字母展示文件状态,不同状态之间可以相互组合,但是又不是随意组合:

' ' = unmodified
M = modified
A = added
D = deleted
R = renamed
C = copied
U = updated but unmerged

  git commit -m <msg>提交文件到版本库,当文件经过git add进行暂存之后使用,<msg>为当前提交的log信息,为日后索引提供方便,可以使用git commit -am <msg>代替git add+git commit两条指令,如果需要对已经提交的<msg>进行修改可以使用git --amend
  详细文档可以查看

  演示如下,git init创建空的版本库,只包含main.cadd.h,文件内容如下:

//main.c
#include <stdio.h>

int main()
{
        return 0;
}

  通过git status查看状态:

# altas @ altas in ~/Documents/git on git:master x [10:53:01]
$ git status
位于分支 master

初始提交

未跟踪的文件:
  (使用 "git add <文件>..." 以包含要提交的内容)

	add.h
	main.c

提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)

# altas @ altas in ~/Documents/git on git:master x [10:53:05]
$ git status -s
?? add.h
?? main.c

# altas @ altas in ~/Documents/git on git:master x [10:53:08]
$ git status --long
位于分支 master

初始提交

未跟踪的文件:
  (使用 "git add <文件>..." 以包含要提交的内容)

	add.h
	main.c

提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)

  可以看到git status默认是git status --long

# altas @ altas in ~/Documents/git on git:master x [10:54:36]
$ git add main.c

# altas @ altas in ~/Documents/git on git:master x [10:54:41]
$ git status -s
A  main.c
?? add.h

# altas @ altas in ~/Documents/git on git:master x [10:54:47]
$ git status
位于分支 master

初始提交

要提交的变更:
  (使用 "git rm --cached <文件>..." 以取消暂存)

	新文件:   main.c

未跟踪的文件:
  (使用 "git add <文件>..." 以包含要提交的内容)

	add.h

# altas @ altas in ~/Documents/git on git:master x [10:55:27]
$ git add .

# altas @ altas in ~/Documents/git on git:master x [10:55:45]
$ git status
位于分支 master

初始提交

要提交的变更:
  (使用 "git rm --cached <文件>..." 以取消暂存)

	新文件:   add.h
	新文件:   main.c

  之后便可以通过git commit提交当前改动到版本库。

# altas @ altas in ~/Documents/git on git:master o [11:00:25]
$ vim main.c

# altas @ altas in ~/Documents/git on git:master x [11:00:46]
$ git status -s
A  add.h
AM main.c

# altas @ altas in ~/Documents/git on git:master x [11:01:25] C:1
$ git commit -m "main.c and add.h"
[master (根提交) 7e09730] main.c and add.h
 2 files changed, 2 insertions(+)
 create mode 100644 add.h
 create mode 100644 main.c

# altas @ altas in ~/Documents/git on git:master x [11:01:30] C:1
 $ git status -s
 M main.c

# altas @ altas in ~/Documents/git on git:master x [11:03:30]
$ touch add.c

# altas @ altas in ~/Documents/git on git:master x [11:03:39]
$ git add add.c

# altas @ altas in ~/Documents/git on git:master x [11:03:47]
$ vim add.c

# altas @ altas in ~/Documents/git on git:master x [11:03:58]
$ git status -s
AM add.c
 M add.h

2.4.2 git diff

  git diff用于查看修改了哪些内容:

  • git diff,查看尚未缓存的改动;
  • git diff --cached,查看已经缓存的改动;
  • git diff HEAD,查看已经缓存的和未缓存的所有改动;
  • git diff --stat,显示摘要。

  可以使用--exit-code让输出直接输出到屏幕而不是less。
  详细文档git diff
  main.c文件内容如下:

//main.c修改前
#include <stdio.h>
#include "add.h"

int main()
{
        return 0;
}

//main.c修改后
#define BUBF_SIZE 20
int main()
{
        return 0;
}

  添加一条注释并且使用git add缓存文件:

#define BUBF_SIZE 20
//main function of the program
int main()
{
        return 0;
}

  使用git diff查看输出的区别:

# altas @ altas in ~/Documents/git on git:master x [13:32:09]
$ git status -s
MM main.c

# altas @ altas in ~/Documents/git on git:master x [13:32:13]
$ git diff --exit-code
diff --git a/main.c b/main.c
index df16112..21ef283 100644
--- a/main.c
+++ b/main.c
@@ -1,4 +1,5 @@
 #define BUBF_SIZE 20
+//main function of the program
 int main()
 {
        return 0;

# altas @ altas in ~/Documents/git on git:master x [13:32:19] C:1
$ git diff --cached --exit-code
diff --git a/main.c b/main.c
index 31cd46b..df16112 100644
--- a/main.c
+++ b/main.c
@@ -1,6 +1,4 @@
-#include <stdio.h>
-#include "add.h"
-
+#define BUBF_SIZE 20
 int main()
 {
        return 0;

# altas @ altas in ~/Documents/git on git:master x [13:32:50] C:1
$ git diff HEAD --exit-code
diff --git a/main.c b/main.c
index 31cd46b..21ef283 100644
--- a/main.c
+++ b/main.c
@@ -1,6 +1,5 @@
-#include <stdio.h>
-#include "add.h"
-
+#define BUBF_SIZE 20
+//main function of the program
 int main()
 {
        return 0;

# altas @ altas in ~/Documents/git on git:master x [13:33:07] C:1
$ git diff --stat --exit-code
 main.c | 1 +
 1 file changed, 1 insertion(+)

2.4.3 git rm

  linux可以使用rm删除文件然后提交rm <file> && git add . && git commit -m <msg>,git提供了rm参数进行更方便的操作:

  • git rm <file>,移除已跟踪的文件清单中移除文件;
  • git rm -f <file>,如果修改过的文件存在到暂存区了,就需要强制删除;
  • git rm --cached <file>,仅仅删除暂存区中的该文件,而不删除工作区中的文件;
  • git rm -r <dir>,递归删除目录。

  详细文档:git rm
  演示操作如下:

$ ls
add.c  add.h  main.c

# altas @ altas in ~/Documents/git on git:master x [13:51:58]
$ git status -s
MM main.c

# altas @ altas in ~/Documents/git on git:master x [13:52:05]
$ git add .

# altas @ altas in ~/Documents/git on git:master x [13:52:10]
$ git rm add.h
rm 'add.h'

# altas @ altas in ~/Documents/git on git:master x [13:52:20]
$ ls
add.c  main.c

# altas @ altas in ~/Documents/git on git:master x [13:52:22]
$ git status -s
D  add.h
M  main.c

# altas @ altas in ~/Documents/git on git:master x [13:52:28]
$ git rm main.c
error: 如下文件有本地修改
    main.c
(使用 --cached 保留本地文件,或用 -f 强制删除)

# altas @ altas in ~/Documents/git on git:master x [13:52:39] C:1
$ git rm -f main.c
rm 'main.c'

# altas @ altas in ~/Documents/git on git:master x [13:52:49]
$ ls
add.c

# altas @ altas in ~/Documents/git on git:master x [13:52:51]
$ git status -s
D  add.h
D  main.c

# altas @ altas in ~/Documents/git on git:master x [13:52:54]
$ git rm --cached add.c
rm 'add.c'

# altas @ altas in ~/Documents/git on git:master x [13:53:19]
$ git status -s
D  add.c
D  add.h
D  main.c
?? add.c

2.4.4 git mv

  git mv用于移动或重命名一个文件、目录、软连接。
  详细文档:git mv
  演示内容如下:

$ git status -s
A  1
?? 2

# altas @ altas in ~/Documents/git on git:master x [14:03:21]
$ git mv 1 3

# altas @ altas in ~/Documents/git on git:master x [14:03:30]
$ git mv 2 4
fatal: 不在版本控制之下,源=2,目标=4

# altas @ altas in ~/Documents/git on git:master x [14:03:35] C:128
$ git status -s
A  3
?? 2

2.5 日志

2.5.1 git log

  git log查看提交历史日志:

  • git log,显示所有提交日志的详细信息,包括每次提交的hashcode,作者,作者邮箱,日期,提交日志等信息;
  • git log --oneline,单行简洁的显示日志;
  • git log --graph,以图形化的方式显示日志;
  • git log --reverse,逆序显示日志;
  • git log --author=xx,显示所有作者为xx的日志;
  • git log --before={xx} --after={yy}显示区间在[xx,yy]的日志,也可以换成--since?--before--until --after
  • git log -p <file>,显示某个文件的提交日志。

  详细文档:git log
  可以通过git config --global --replace-all core.pager "less -iXFR"配置日志直接显示在屏幕上。

$ git log
commit 9b3881a944143b19593bb77b362464f0a80a933e
Author: xxxx <xxxx@outlook.com>
Date:   Sun Jun 7 14:11:22 2020 +0800

    测试git log

commit 4f2caf0e7b734fffe6161b4cbc881e4dbfcaade7
Author: xxxx <xxxx@outlook.com>
Date:   Sun Jun 7 14:04:30 2020 +0800

    git mv 测试

# altas @ altas in ~/Documents/git on git:master o [14:19:48]
$ git log --oneline
9b3881a 测试git log
4f2caf0 git mv 测试

# altas @ altas in ~/Documents/git on git:master o [14:19:51]
$ git log --oneline --author=xxxx
9b3881a 测试git log
4f2caf0 git mv 测试

# altas @ altas in ~/Documents/git on git:master o [14:20:19]
$ git log --oneline --graph --no-merges --before={2020-06-08} --after={2020-4-20}
* 9b3881a 测试git log
* 4f2caf0 git mv 测试

# altas @ altas in ~/Documents/git on git:master o [14:23:20] C:128
$ git log --oneline -p 1
9b3881a 测试git log
diff --git a/1 b/1
index e69de29..44e9f1f 100644
--- a/1
+++ b/1
@@ -0,0 +1 @@
+i am the first file
4f2caf0 git mv 测试
diff --git a/1 b/1
new file mode 100644
index 0000000..e69de29

2.5.2 git blame

  git blame可以查看具体文件的日志的详细信息,以及修改的内容:

  • git blame <file>

  详细文档:git blame

$ git log --oneline -p 1
9b3881a 测试git log
diff --git a/1 b/1
index e69de29..44e9f1f 100644
--- a/1
+++ b/1
@@ -0,0 +1 @@
+i am the first file
4f2caf0 git mv 测试
diff --git a/1 b/1
new file mode 100644
index 0000000..e69de29

# altas @ altas in ~/Documents/git on git:master o [14:26:54]
$ git blame 1
9b3881a9 (xxxx 2020-06-07 14:11:22 +0800 1) i am the first file

2.6 撤销

2.6.2 git commit --amend

  如果本次提交有遗漏的文件可以使用git add <file> && git commit --amend

2.6.1 git reset

  git reset HEAD可以撤销通过add暂存到暂存区的内容。git reset可以撤销之前的操作git add, git commit等。

  • git reset --hard,reset暂存区和工作目录,从指定commit以来的工作目录中的任何改变都被丢弃,HEAD指向目标commit;
  • git reset --soft,撤销操作,但是不修改工作目录中的内容;

  详细文档:git reset](https://git-scm.com/docs/git-reset)

$ cat main.c
#include <stdio.h>
#include "add.h"

#define BUFF_SIZE 10
int main()
{
        return 0;
}

# altas @ altas in ~/Documents/git on git:master x [11:24:15]
$ git status -s
 M main.c

# altas @ altas in ~/Documents/git on git:master x [11:24:21]
$ git add main.c

# altas @ altas in ~/Documents/git on git:master x [11:24:28]
$ git status -s
M  main.c

# altas @ altas in ~/Documents/git on git:master x [11:24:30]
$ git reset HEAD
???????????
M       main.c

# altas @ altas in ~/Documents/git on git:master x [11:24:36]
$ git status -s
 M main.c

# altas @ altas in ~/Documents/git on git:master x [11:24:41]
$ cat main.c
#include <stdio.h>
#include "add.h"

#define BUFF_SIZE 10
int main()
{
        return 0;
}

2.6.2 git checkout

  详细文档:git checkout
  git checkout命令用于切换分支或恢复工作树文件,一般会重写工作区。
  git checkout -- <file>,撤销文件的修改。

# altas @ altas in ~/Documents/git on git:test x [17:17:53]
$ cat 1
i am the tom's file
the new line

# altas @ altas in ~/Documents/git on git:test x [17:17:55]
$ git checkout -- 1

# altas @ altas in ~/Documents/git on git:test o [17:18:01]
$ cat 1
i am the tom's file

2.6.3 git revert

  详细文档:git revert
  撤销 某次操作,此次操作之前和之后的commit和history都会保留,并且把这次撤销
作为一次最新的提交。

  • git revert HEAD:撤销前一次commit;
  • git revert HEAD^:撤销前前一次commit;
  • git revert <commit_hashcode>:撤销指定版本。
# altas @ altas in ~/Documents/git on git:test o [17:18:03]
$ ls
1  2  forgetten_file  jerry  test  tom

# altas @ altas in ~/Documents/git on git:test o [17:22:18]
$ vim revert

# altas @ altas in ~/Documents/git on git:test x [17:22:25]
$ git add .

# altas @ altas in ~/Documents/git on git:test x [17:22:27]
$ git commit -m "revert 测试"
[test 9e153ad] revert 测试
 1 file changed, 1 insertion(+)
 create mode 100644 revert

# altas @ altas in ~/Documents/git on git:test o [17:22:34]
$ git revert HEAD
[test 471a917] Revert "revert 测试"
 1 file changed, 1 deletion(-)
 delete mode 100644 revert

# altas @ altas in ~/Documents/git on git:test o [17:22:56] C:128
$ git log --oneline --graph
* 471a917 Revert "revert 测试"
* 9e153ad revert 测试
* befb88b ???????
* 957f61a test file for mastr
* 4a76f40 rebase变基操作
*   c5005c8 master merge test
|\
| * 0232f3f test 创建test
* | 7099010 master 创建test
|/
*   d5ac393 tom合并到master
|\
| * d7f28f0 tom创建1
| * 78c03b3 jerry 修改文件
| * 031b91a jerry 创建jerry
| * dfa1596 tom 创建tom
* | 2cc27a2 master 创建1
|/
* 9b3881a 测试git log
* 4f2caf0 git mv 测试

# altas @ altas in ~/Documents/git on git:test o [17:22:57]
$ ls
1  2  forgetten_file  jerry  test  tom

2.7 分支管理

  几乎每一种版本控制系统都以某种形式支持分支。使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作。

2.7.1 git branch

  详细文档:git branch

  • git branch,显示当前仓库的所有分支;
  • git branch <name>,创建分支name;
  • git branch -d <name>,删除分支;
  • git branch -a,显示所有分支,包含远程仓库分支;
  • git branch -m <old_name> <new_name>,重命名分支;
  • git branch r,显示远程分支;
  • git checkout <name>,切换分支;
  • git checkout -b <name>,创建并且切换分支;

  创建分支tom和jerry,tom创建文件tom,在tom的基础上创建分支jerry创建文件jerry

# altas @ altas in ~/Documents/git on git:master o [15:14:52]
$ git branch tom

# altas @ altas in ~/Documents/git on git:master o [15:15:06]
$ git checkout tom
切换到分支 'tom'

# altas @ altas in ~/Documents/git on git:tom o [15:15:19]
$ vim tom

# altas @ altas in ~/Documents/git on git:tom x [15:15:32]
$ git add . && git commit -m "tom 创建tom"
[tom dfa1596] tom 创建tom
 1 file changed, 1 insertion(+)
 create mode 100644 tom

# altas @ altas in ~/Documents/git on git:tom o [15:15:49]
$ git log --oneline --graph
* dfa1596 tom 创建tom
* 9b3881a 测试git log
* 4f2caf0 git mv 测试

# altas @ altas in ~/Documents/git on git:tom o [15:16:28]
$ git checkout -b jerry
切换到分支 'jerry'

# altas @ altas in ~/Documents/git on git:jerry o [15:16:32]
$ vim jerry

# altas @ altas in ~/Documents/git on git:jerry x [15:16:41]
$ git add . && git commit -m "jerry 创建jerry"
[jerry 031b91a] jerry 创建jerry
 1 file changed, 1 insertion(+)
 create mode 100644 jerry

# altas @ altas in ~/Documents/git on git:jerry o [15:16:51]
$ git log --oneline --graph
* 031b91a jerry 创建jerry
* dfa1596 tom 创建tom
* 9b3881a 测试git log
* 4f2caf0 git mv 测试

# altas @ altas in ~/Documents/git on git:jerry o [15:16:58]
$ git checkout master && git log --oneline --graph
切换到分支 'master'
* 9b3881a 测试git log
* 4f2caf0 git mv 测试

# altas @ altas in ~/Documents/git on git:master o [15:17:23]
$ git branch
  jerry
* master
  tom

  上面创建的分支关系如下:
在这里插入图片描述

2.7.2 git tag

  详细文档:git tag
  git可以通过git tag打标签,该命令接受一个参数——目标commit的hashcode(一般取前7位即可):

  • git tag,显示当前仓库的tag;
  • git tag -l <pattern>,使用pattern过滤部分tag;
  • git tag -a <tag> -m <msg>,给指定的commit打上tag;
  • git show <tag>,显示某个tag的详细信息;
  • git tag <tag> -lw,创建轻量标签;
  • git tag -a <tag> <hashcode>,给某一个已经提交的commit追加tag不附带信息;
  • git tag -d <tag>,删除tag;
  • git checkout <tag>,切换到指定tag的提交处,会导致HEAD处于分离状态;
  • git push origin <tag>,向远程仓库推送指定tag;
  • git push origin --tags,向远程仓库推送所有tag。

  为当前分支创建v1.0 tag,为tom创建v1.1 tag,为jerry创建v1.1.1。

# altas @ altas in ~/Documents/git on git:master o [15:40:03]
$ git log --oneline
9b3881a 测试git log
4f2caf0 git mv 测试

# altas @ altas in ~/Documents/git on git:master o [15:40:08]
$ git tag -a v1.0 4f2caf0 -m "v1.0"

# altas @ altas in ~/Documents/git on git:master o [15:40:25]
$ git show v1.0
tag v1.0
Tagger: xxxx <xxxx@outlook.com>
Date:   Sun Jun 7 15:40:25 2020 +0800

v1.0

commit 4f2caf0e7b734fffe6161b4cbc881e4dbfcaade7
Author: xxxx <xxxx@outlook.com>
Date:   Sun Jun 7 14:04:30 2020 +0800

    git mv 测试

diff --git a/1 b/1
new file mode 100644
index 0000000..e69de29
diff --git a/2 b/2
new file mode 100644
index 0000000..e69de29

# altas @ altas in ~/Documents/git on git:jerry o [15:43:57]
$ git checkout tom
切换到分支 'tom'

# altas @ altas in ~/Documents/git on git:tom o [15:44:02]
$ git tag -a v1.1 dfa1596 -m "tom的tag"

# altas @ altas in ~/Documents/git on git:tom o [15:44:23]
$ git checkout jerry
切换到分支 'jerry'

# altas @ altas in ~/Documents/git on git:jerry o [15:44:31]
$ git tag -a v1.1.1 031b91a -m "jerry的tag"

# altas @ altas in ~/Documents/git on git:jerry o [15:44:52] C:128
$ git tag
v1.0
v1.1
v1.1.1

# altas @ altas in ~/Documents/git on git:jerry o [15:45:28]
$ git tag -d v1.1.1
已删除标签 'v1.1.1' (曾为 48745c6)

# altas @ altas in ~/Documents/git on git:jerry o [15:46:12]
$ git tag -l "v1.*"
v1.0
v1.1

2.7.3 git merge

  详细文档:git merge
  git merge将两个不同的分支合并到一起。

  • git checkout <merge_branch> && git merge <branch>,先切换到merge_branch,然后再将merge_branch合并到branch。

  将jerry合并到tom,tom合并到master。

$ ls
1  2  jerry  tom

# altas @ altas in ~/Documents/git on git:jerry o [15:56:04]
$ cat tom
this is a file of tom, jerry modifies this file!

# altas @ altas in ~/Documents/git on git:jerry o [15:56:09]
$ git log --oneline
78c03b3 jerry 修改文件
031b91a jerry 创建jerry
dfa1596 tom 创建tom
9b3881a 测试git log
4f2caf0 git mv 测试

  关系图如下:
在这里插入图片描述

# altas @ altas in ~/Documents/git on git:jerry o [15:59:23]
$ git checkout tom
切换到分支 'tom'

# altas @ altas in ~/Documents/git on git:tom o [15:59:30]
$ git merge jerry
更新 dfa1596..78c03b3
Fast-forward
 jerry | 1 +
 tom   | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)
 create mode 100644 jerry

  修改文件1制造冲突,需要保证不同两个分支1文件内容不同,都进行过修改。

# altas @ altas in ~/Documents/git on git:tom o [16:06:18]
$ git checkout master
切换到分支 'master'

# altas @ altas in ~/Documents/git on git:master o [16:16:32]
$ git merge tom
自动合并 1
冲突(添加/添加):合并冲突于 1
自动合并失败,修正冲突然后提交修正的结果。

# altas @ altas in ~/Documents/git on git:master x [16:16:45] C:1
$ vim 1

# altas @ altas in ~/Documents/git on git:master x [16:16:52]
$ cat 1
<<<<<<< HEAD
i am the master's file
=======
i am the tom's file
>>>>>>> tom

# altas @ altas in ~/Documents/git on git:master x [16:16:54]
$ vim 1

# altas @ altas in ~/Documents/git on git:master x [16:17:03]
$ cat 1
i am the tom's file

# altas @ altas in ~/Documents/git on git:master x [16:17:05]
$ git commit -a -m "tom合并到master"
[master d5ac393] tom合并到master

# altas @ altas in ~/Documents/git on git:master o [16:18:08]
$ git log --oneline --graph
*   d5ac393 tom合并到master
|\
| * d7f28f0 tom修改了1
| * 78c03b3 jerry 修改文件
| * 031b91a jerry 创建jerry
| * dfa1596 tom 创建tom
* | 2cc27a2 master 修改了1
|/
* 9b3881a 测试git log
* 4f2caf0 git mv 测试

  过程图如下:
在这里插入图片描述

2.7.4 git rebase

  详细文档git rebase
  git rebasegit merge都是对分支进行合并,merge会保留所有的合并日志信息,而rebase只会留下一条干净的提交日志,rebase之后两个分支完全相同。

  基本步骤如下:

git checkout test
git rebase master
git checkout master
git merge test

  如果存在冲突:

git checkout test
git rebase master
#vim <file>
git add <file>
git rebase --continue
git checkout master
git merge test

  演示如下:

# altas @ altas in ~/Documents/git on git:master o [16:57:16]
$ git checkout test
切换到 'test'

# altas @ altas in ~/Documents/git on git:test o [16:57:32]
$ git rebase master
首先,回退分支以便在上面重放您的工作...
应用:test file for test
使用索引来重建一个(三方合并的)基础目录树...
回落到基础版本上打补丁及进行三方合并...
自动合并 test
冲突(添加/添加):合并冲突于 test
error: 无法合并变更。
打补丁失败于 0001 test file for test
失败的补丁文件副本位于:.git/rebase-apply/patch

当您解决了此问题后,执行 "git rebase --continue"。
如果您想跳过此补丁,则执行 "git rebase --skip"。
要恢复原分支并停止变基,执行 "git rebase --abort"# altas @ altas in ~/Documents/git on git:957f61a x [16:58:14]
$ cat test
<<<<<<< 957f61a8ca9326ef2aa00d20bd4b9a708966aabe
test file for master
=======
test file
>>>>>>> test file for test

# altas @ altas in ~/Documents/git on git:957f61a x [16:58:39]
$ git add .

# altas @ altas in ~/Documents/git on git:957f61a x [16:58:46]
$ git rebase --continue
应用:test file for test

# altas @ altas in ~/Documents/git on git:test o [16:58:54]
$ git checkout master
切换到分支 'master'

# altas @ altas in ~/Documents/git on git:master o [16:59:21]
$ git merge test
?? 957f61a..1e9fa4d
Fast-forward
 test | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

# altas @ altas in ~/Documents/git on git:master o [16:59:34] C:128
$ git log --oneline --graph
* 1e9fa4d test file for test
* 957f61a test file for mastr
* 4a76f40 rebase变基操作
*   c5005c8 master merge test
|\
| * 0232f3f test 创建test
* | 7099010 master 创建test
|/
*   d5ac393 tom合并到master
|\
| * d7f28f0 tom创建1
| * 78c03b3 jerry 修改文件
| * 031b91a jerry 创建jerry
| * dfa1596 tom 创建tom
* | 2cc27a2 master 创建1
|/
* 9b3881a 测试git log
* 4f2caf0 git mv 测试

# altas @ altas in ~/Documents/git on git:master o [16:59:49]
$ git branch
  jerry
* master
  test
  tom

# altas @ altas in ~/Documents/git on git:master o [17:02:41]
$ git checkout test
切换到分支 'test'

# altas @ altas in ~/Documents/git on git:test o [17:02:51]
$ git log --oneline --graph
* 1e9fa4d test file for test
* 957f61a test file for mastr
* 4a76f40 rebase变基操作
*   c5005c8 master merge test
|\
| * 0232f3f test 创建test
* | 7099010 master 创建test
|/
*   d5ac393 tom合并到master
|\
| * d7f28f0 tom创建1
| * 78c03b3 jerry 修改文件
| * 031b91a jerry 创建jerry
| * dfa1596 tom 创建tom
* | 2cc27a2 master 创建1
|/
* 9b3881a 测试git log
* 4f2caf0 git mv 测试

2.8 远程控制

  远程仓库可以使用github,流程为注册–>点击右上角的±->new repository–>输入基本参数–>创建。
  可以使用git remote add <shortname> <url>添加远程源。

# altas @ altas in ~/Documents/git on git:master o [18:00:16]
$ git remote add origin https://github.com/xxxx/git-learning.git

# altas @ altas in ~/Documents/git on git:master o [18:00:28]
$ git show origin
fatal: ambiguous argument 'origin': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

# altas @ altas in ~/Documents/git on git:master o [18:00:39] C:128
$ git remote show origin
* 远程 origin
  获取地址https://github.com/xxxx/git-learning.git
  推送地址https://github.com/xxxx/git-learning.git
  HEAD 分支(unknown)

2.8.1 git push

  详细文档:git push

  • git push origin <branch>:远程origin推送branch分支;
  • git push origin :<branch>等同于git push origin --delete <branch>删除远程的branch;
  • git push origin将当前branch推送到远程origin;
  • git push --all origin,将本地的所有branch推送到远程origin;
  • git push origin --tags,将本地的所有tag推送到远程;
  • git push origin :<tag>,删除远程tag;
  • git push origin <tag>,向远程推送指定tag;
  • git push -f origin <branch2>:<branch2>,将本地的branch2强行推送到远程的branch1。
# altas @ altas in ~/Documents/git on git:master o [18:00:50]
$ git push origin master
Username for 'https://github.com': xxxx
Password for 'https://xxxx@github.com':
对象计数中: 3, 完成.
压缩对象中: 100% (3/3), 206 bytes | 0 bytes/s, 完成.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/xxxx/git-learning.git
 * [new branch]      master -> master
# altas @ altas in ~/Documents/git on git:test o [18:03:58] C:1
$ git push origin test:master
Username for 'https://github.com': xxxx
Password for 'https://xxxx@github.com':
对象计数中: 3, 完成.
Delta compression using up to 4 threads.
压缩对象中: 100% (2/2), 完成.
写入对象中: 100% (3/3), 273 bytes | 0 bytes/s, 完成.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/xxxx/git-learning.git
   da17a8e..2d0e818  test -> master

2.8.2 git pull

  详细文档:git pull
  git pull从远程下拉文件到本地会自动合并:

  • git pull <origin> <branch1>:<branch2>远程branch1合并到本地的branch2;
  • git pull <origin> <branch1>:pull远程的branch1;
  • git fetch <origin> <branch>:从远程下拉文件不会自动合并;
# altas @ altas in ~/Documents/git on git:test o [18:07:21] C:1
$ git pull origin master test
?? https://github.com/xxxx/git-learning
 * branch            master     -> FETCH_HEAD
 * branch            test       -> FETCH_HEAD
?? 2d0e818..900a7d6
Fast-forward
 test3 | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 test3

2.8.3 git remote

  详细文档:git remote

  • git remote -v:查看远程版本库版本;
  • git remote show <remote>,查看远程版本库;
  • git remote add <remote> <url>,添加<remote>为远程的名字,<url>为链接

2.9 git submodule

  文档:git submodul
  git submodule是一个很好的多项目使用共同类库的工具,它允许类库项目做为repository,子项目做为一个单独的git项目存在父项目中,子项目可以有自己的独立的commit,push,pull。而父项目以Submodule的形式包含子项目,父项目可以指定子项目header,父项目中会的提交信息包含Submodule的信息,再clone父项目的时候可以把Submodule初始化。

  • git submodule add <url> <dir>,创建子模块;

  clone完带有子模块的项目后还需要执行git submodule init && git submodule update才能获取子模块的文件。

  演示:

# altas @ altas in ~/Documents/git-learning on git:master o [18:13:24] C:1
$ git submodule add https://github.com/xxxx/child child
正克隆到 'child'...
warning: 您似乎克隆了一个空仓库。
检查连接... 完成。
fatal: 您位于一个尚未初始化的分支
不能检出子模组 'child'

# altas @ altas in ~/Documents/git-learning on git:master x [18:13:36]
$ tree
.
├── child
│   └── child
├── test
└── test.test

1 directory, 3 files

# altas @ altas in ~/Documents/git-learning/child on git:master o [18:15:48]
$ vim newfile

# altas @ altas in ~/Documents/git-learning/child on git:master x [18:15:56]
$ git status
位于分支 master
您的分支领先 'origin/master' 共 3 个提交。
  (使用 "git push" 来发布您的本地提交)
未跟踪的文件:
  (使用 "git add <文件>..." 以包含要提交的内容)

	newfile

提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)

# altas @ altas in ~/Documents/git-learning/child on git:master x [18:15:57]
$ git add .

# altas @ altas in ~/Documents/git-learning/child on git:master x [18:16:01]
$ git commit -m "child new file"
[master d139bf1] child new file
 1 file changed, 1 insertion(+)
 create mode 100644 newfile

# altas @ altas in ~/Documents/git-learning/child on git:master o [18:16:06]
$ cd ..

# altas @ altas in ~/Documents/git-learning on git:master x [18:16:08]
$ git status
?位于分支 master
您的分支领先 'origin/master' 共 3 个提交。
  (使用 "git push" 来发布您的本地提交)
未跟踪的文件:
  (使用 "git add <文件>..." 以包含要提交的内容)

	newfile

提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)

# altas @ altas in ~/Documents/git-learning on git:master x [18:16:09]
$ git add .

# altas @ altas in ~/Documents/git-learning on git:master x [18:16:21]
$ git commit -m "parent commit child's commit"
[master f93885f] parent commit child's commit
 1 file changed, 1 insertion(+), 1 deletion(-)

3 git原理

  Git有两种数据结构:可变的索引(index或stage或cache)用于缓冲工作目录信息与下一次提交的版本信息;不变的、仅追加的对象数据库。

3.1 git 目录结构

  git的版本库在.git目录下:

# altas @ altas in ~/Documents/git on git:master o [18:28:57]
$ tree -a
.
└── .git
    ├── branches
    ├── config
    ├── description
    ├── HEAD
    ├── hooks
    │   ├── applypatch-msg.sample
    │   ├── commit-msg.sample
    │   ├── post-update.sample
    │   ├── pre-applypatch.sample
    │   ├── pre-commit.sample
    │   ├── prepare-commit-msg.sample
    │   ├── pre-push.sample
    │   ├── pre-rebase.sample
    │   └── update.sample
    ├── info
    │   └── exclude
    ├── objects
    │   ├── info
    │   └── pack
    └── refs
        ├── heads
        └── tags

10 directories, 13 files

  该目录下的文件的含义如下:

  • description:仅供gitweb使用个;
  • config:项目特有的配置;
  • info/exclude:用以放置那些不希望被记录在 .gitignore 文件中的忽略模式(ignored patterns)
  • hooks:包含客户端或服务端的钩子脚本(hook scripts);
  • HEAD:指向目前被检出的分支;
  • objects:存储所有数据内容;
  • refs:存储指向数据(分支、远程仓库和标签等)的提交对象的指针;
  • index:保存暂存区信息;
  • logs:日志。
# altas @ altas in ~/Documents/git on git:master o [20:06:05] C:1
$ cat .git/HEAD
ref: refs/heads/master

# altas @ altas in ~/Documents/git on git:master o [20:06:13]
$ ls .git/objects
6b  info  pack

# altas @ altas in ~/Documents/git on git:master o [20:06:26]
$ ls .git/refs
heads  tags

3.2 git对象

  git有四个基本对象:blob对象,tree对象,commit对象,tag对象。

3.2.1 blob对象

  blob (二进制大对象)是使用zlib压缩算法对一个文件的内容压缩后的结果。Blobs没有保存文件名、时间戳或其他元数据。Git将其存储在位于隐藏的.git/objects文件夹中。文件的名称为使用SHA-1哈希函数对原文件内容生成的哈希值。这些对象文件称为Blob,每次将新文件添加到存储库时会创建Blob对象。

  • git hash-object:创建blob对象;
  • git cat-file -p <hash-code>:读取对象的内容。

  git针对目标文件生成的40位hash值的前2位会作为目录名,后38位作为文件名。

# altas @ altas in ~/Documents/git-learning [8:37:51] C:1
$ git init
初始化空的 Git 仓库于 /home/altas/Documents/git-learning/.git/

# altas @ altas in ~/Documents/git-learning on git:master o [8:37:57]
$ tree .git/objects
.git/objects
├── info
└── pack

2 directories, 0 files

# altas @ altas in ~/Documents/git-learning on git:master o [8:38:09]
$ echo "test content for test file" | git hash-object -w --stdin
0ce127a4b31e6bf1ed3285b8292e222d29812fed

# altas @ altas in ~/Documents/git-learning on git:master o [8:38:40]
$ tree .git/objects
.git/objects
├── 0c
│   └── e127a4b31e6bf1ed3285b8292e222d29812fed
├── info
└── pack

3 directories, 1 file

# altas @ altas in ~/Documents/git-learning on git:master o [8:38:52]
$ git cat-file -p 0ce127a4b31e6bf1ed3285b8292e222d29812fed
test content for test file

# altas @ altas in ~/Documents/git-learning on git:master o [8:38:59]
$ echo "version 1" > test.txt

# altas @ altas in ~/Documents/git-learning on git:master x [8:39:22]
$ git hash-object -w test.txt
83baae61804e65cc73a7201a7252750c76066a30

# altas @ altas in ~/Documents/git-learning on git:master x [8:39:28]
$ echo 'version 2' > test.txt

# altas @ altas in ~/Documents/git-learning on git:master x [8:39:46]
$ git hash-object -w test.txt
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a

# altas @ altas in ~/Documents/git-learning on git:master x [8:39:52]
$ find .git/objects -type f
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
.git/objects/0c/e127a4b31e6bf1ed3285b8292e222d29812fed

# altas @ altas in ~/Documents/git-learning on git:master x [8:40:08]
$ git cat-file -p 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
version 2

# altas @ altas in ~/Documents/git-learning on git:master x [8:40:35]
$ git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30
version 1

# altas @ altas in ~/Documents/git-learning on git:master x [8:40:48]
$ git cat-file -p 0ce127a4b31e6bf1ed3285b8292e222d29812fed
test content for test file

# altas @ altas in ~/Documents/git-learning on git:master x [8:41:00]
$ git cat-file -t 0ce127a4b31e6bf1ed3285b8292e222d

# altas @ altas in ~/Documents/git-learning on git:master x [8:41:20] C:1
$ git cat-file -t 0ce127a4b31e6bf1ed3285b8292e222d29812fed
blob

3.2.2 tree对象

3.2.2.1 tree读取

  tree对象对应于文件目录。包含文件名列表以及文件的类型比特(包含许可权)、到blob(对应于文件)或tree对象的引用。tree对象是源树(source tree)的快照。用默克树(英语:Merkle Tree)实现。
  可以通过git cat-file -p <branch>^{tree}查看,master^{tree}语法表示 master 分支上最新的提交所指向的树对象。

  在 Windows 的 CMD 中,字符 ^ 被用于转义,因此必须双写它以避免出现问题:git cat-file -p master^^{tree}。 在 PowerShell 中使用字符 {} 时则必须用引号引起来,以此来避免参数解析错误:git cat-file -p 'master^{tree}'。在 ZSH 中,字符 ^ 被用在通配模式(globbing)中,因此必须将整个表达式用引号引起来:git cat-file -p "master^{tree}"

# altas @ altas in ~/Documents/git-learning on git:master x [8:46:57]
$ git update-index --add --cacheinfo 100644 83baae61804e65cc73a7201a7252750c76066a30 test.txt

# altas @ altas in ~/Documents/git-learning on git:master x [8:47:26]
$ git write-tree
d8329fc1cc938780ffdd9f94e0d364e0ea74f579

# altas @ altas in ~/Documents/git-learning on git:master x [8:47:49] C:129
$ git cat-file -t d8329fc1cc938780ffdd9f94e0d364e0ea74f579
tree

# altas @ altas in ~/Documents/git-learning on git:master x [8:47:57] C:1
$ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579
100644 blob 83baae61804e65cc73a7201a7252750c76066a30	test.txt

在这里插入图片描述

3.3.2.2 tree创建

  Git 根据某一时刻暂存区(即 index 区域,下同)所表示的状态创建并记录一个对应的树对象, 如此重复便可依次记录(某个时间段内)一系列的树对象。 因此,为创建一个树对象,首先需要通过暂存一些文件来创建一个暂存区。 可以通过底层命令 git update-index为一个单独文件—test.txt文件的首个版本——创建一个暂存区。 利用该命令,可以把 test.txt文件的首个版本人为地加入一个新的暂存区。 必须为上述命令指定 --add选项,因为此前该文件并不在暂存区中(我们甚至都还没来得及创建一个暂存区呢); 同样必需的还有--cacheinfo选项,因为将要添加的文件位于 Git 数据库中,而不是位于当前目录下。 同时,需要指定文件模式、SHA-1 与文件名。
  文件模式为 100644,表明这是一个普通文件。 其他选择包括:100755,表示一个可执行文件;120000,表示一个符号链接。

  • git update-index:为文件创建暂存区;
  • git write-tree:写树;
  • git read-tree:读树,--prefix将一个已有的树对象作为子树读入暂存区。
# altas @ altas in ~/Documents/git-learning on git:master x [8:49:46] C:127
$ echo "new file" > new.txt

# altas @ altas in ~/Documents/git-learning on git:master x [8:50:18] C:129
$ git update-index --add --cacheinfo 100644 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt

# altas @ altas in ~/Documents/git-learning on git:master x [8:50:22]
$ git update-index --add new.txt

# altas @ altas in ~/Documents/git-learning on git:master x [8:50:41]
$ git write-tree
0155eb4229851634a0f03eb265b69f5a2d56f341

# altas @ altas in ~/Documents/git-learning on git:master x [8:50:44]
$ git cat-file -p 0155eb4229851634a0f03eb265b69f5a2d56f341
100644 blob fa49b077972391ad58037050f2a75f74e3671e92	new.txt
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a	test.txt

# altas @ altas in ~/Documents/git-learning on git:master x [8:51:15] C:128
$ git read-tree --prefix=backup d8329fc1cc938780ffdd9f94e0d364e0ea74f579

# altas @ altas in ~/Documents/git-learning on git:master x [8:52:46]
$ git write-tree
aac4207d2dbd60b80651aa5ca182a09145c781e4

# altas @ altas in ~/Documents/git-learning on git:master x [8:52:51]
$ git cat-file -p aac4207d2dbd60b80651aa5ca182a09145c781e4
040000 tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579	backup
100644 blob fa49b077972391ad58037050f2a75f74e3671e92	new.txt
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a	test.txt

# altas @ altas in ~/Documents/git-learning on git:master x [8:53:01]
$ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579
100644 blob 83baae61804e65cc73a7201a7252750c76066a30	test.txt

在这里插入图片描述

3.2.3 commit对象

  commit对象链接tree对象在一起而成为history,包含顶层源目录的tree对象名字、一个时间戳、log信息、0个或多个父commit对象的名字。用于保存特定版本的树型文件夹结构以及提交作者,电子邮件地址,日期和描述性提交消息。

  git可以通过git commit-tree创建commit对象,git commit -tree <child_hash_code> -p <parent_hash_code>可以为创建的提交对象指定父对象。

# altas @ altas in ~/Documents/git-learning on git:master x [9:06:22] C:128
$ echo "first commit" | git commit-tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
4cada8f8e2f4ae1c716a0334aef316e79f9e7173

# altas @ altas in ~/Documents/git-learning on git:master x [9:08:10]
$ git cat-file -p 4cada8f8e2f4ae1c716a0334aef316e79f9e7173
tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author xxxx <xxxx@outlook.com> 1591578490 +0800
committer xxxx <xxxx@outlook.com> 1591578490 +0800

first commit

# altas @ altas in ~/Documents/git-learning on git:master x [9:08:33]
$ find .git/objects -type f
.git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579
.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
.git/objects/01/55eb4229851634a0f03eb265b69f5a2d56f341
.git/objects/aa/c4207d2dbd60b80651aa5ca182a09145c781e4
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a
.git/objects/4c/ada8f8e2f4ae1c716a0334aef316e79f9e7173
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
.git/objects/0c/e127a4b31e6bf1ed3285b8292e222d29812fed

# altas @ altas in ~/Documents/git-learning on git:master x [9:08:37]
$ echo 'second commit' | git commit-tree 0155eb4229851634a0f03eb265b69f5a2d56f341 -p 4cada8f8e2f4ae1c716a0334aef316e79f9e7173
56a15db894d139806e1cd67a59e73b4f39dd44fa

# altas @ altas in ~/Documents/git-learning on git:master x [9:12:05]
$ echo 'third commit' | git commit-tree aac4207d2dbd60b80651aa5ca182a09145c781e4  -p 56a15db894d139806e1cd67a59e73b4f39dd44fa
f15f398c878a0cd355627dea48d6052793864361

# altas @ altas in ~/Documents/git-learning on git:master x [9:14:18]
$ git log --stat f15f398c878a0cd355627dea48d6052793864361
commit f15f398c878a0cd355627dea48d6052793864361
Author: xxxx <xxxx@outlook.com>
Date:   Mon Jun 8 09:14:18 2020 +0800

    third commit

 backup/test.txt | 1 +
 1 file changed, 1 insertion(+)

commit 56a15db894d139806e1cd67a59e73b4f39dd44fa
Author: xxxx <xxxx@outlook.com>
Date:   Mon Jun 8 09:12:05 2020 +0800
    second commit

 new.txt  | 1 +
 test.txt | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

commit 4cada8f8e2f4ae1c716a0334aef316e79f9e7173
Author: xxxx <xxxx@outlook.com>
Date:   Mon Jun 8 09:08:10 2020 +0800

    first commit
:
Author: xxxx <xxxx@outlook.com>
Date:   Mon Jun 8 09:14:18 2020 +0800

    third commit

 backup/test.txt | 1 +
 1 file changed, 1 insertion(+)
# altas @ altas in ~/Documents/git-learning on git:master x [9:15:24] C:127
$ find .git/objects -type f
.git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579
.git/objects/f1/5f398c878a0cd355627dea48d6052793864361
.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
.git/objects/01/55eb4229851634a0f03eb265b69f5a2d56f341
.git/objects/aa/c4207d2dbd60b80651aa5ca182a09145c781e4
.git/objects/56/a15db894d139806e1cd67a59e73b4f39dd44fa
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a
.git/objects/4c/ada8f8e2f4ae1c716a0334aef316e79f9e7173
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
.git/objects/0c/e127a4b31e6bf1ed3285b8292e222d29812fed

在这里插入图片描述

3.2.4 tag对象

  tag对象是一个容器,包含了到另一个对象的引用,也可以增加关于另外对象的元数据。通常它保存需要追溯的特定版本数据的一个commit对象的数字签名。

3.2.5 git对象存储

  git数据存储格式为head+content,之后经过SHA-1-hash得到哈希值,在经过zip压缩写入.git/objects

在这里插入图片描述

  ruby脚本演示过程如下:

# altas @ altas in ~/Documents/git-learning on git:master x [10:07:07]
$ irb
irb(main):001:0> content = "what is up, doc?"
=> "what is up, doc?"
irb(main):002:0> header = "blob #{content.length}\0"
=> "blob 16\u0000"
irb(main):003:0> store = header + content
=> "blob 16\u0000what is up, doc?"
irb(main):004:0> require 'digest/sha1'
=> true
irb(main):005:0> sha1 = Digest::SHA1.hexdigest(store)
=> "bd9dbf5aae1a3862dd1526723246b20206e5fc37"
irb(main):006:0> require 'zlib'
=> true
irb(main):007:0> zlib_content = Zlib::Deflate.deflate(store)
=> "x\x9CK\xCA\xC9OR04c(\xCFH,Q\xC8,V(-\xD0QH\xC9O\xB6\a\x00_\x1C\a\x9D"
irb(main):008:0> path = '.git/objects/' + sha1[0,2] + '/' + sha1[2,38]
=> ".git/objects/bd/9dbf5aae1a3862dd1526723246b20206e5fc37"
irb(main):009:0> path = '.git/objects/' + sha1[0,2] + '/' + sha1[2,38]
=> ".git/objects/bd/9dbf5aae1a3862dd1526723246b20206e5fc37"
irb(main):011:0> require 'fileutils'
=> true
irb(main):012:0> FileUtils.mkdir_p(File.dirname(path))
=> [".git/objects/bd"]
irb(main):013:0> File.open(path, 'w') { |f| f.write zlib_content }
=> 32
irb(main):014:0>

# altas @ altas in ~/Documents/git-learning on git:master x [10:06:11]
$ echo -n "what is up, doc?" | git hash-object --stdin
bd9dbf5aae1a3862dd1526723246b20206e5fc37

# altas @ altas in ~/Documents/git-learning on git:master x [10:07:49]
$ git cat-file -p bd9dbf5aae1a3862dd1526723246b20206e5fc37
what is up, doc?%

3.2.6 git 引用

  引用本身就是一个指针,只不过在git中以文件的形式存在,引用的目录在.git/refsgit show-ref显示当前版本库中的所有引用。

# altas @ altas in ~/Documents/git-learning on git:master x [10:26:06]
$ tree .git/refs
.git/refs
├── heads
└── tags
2 directories, 0 files

  可以通过写入refs中的文件,修改引用,但不建议。

# altas @ altas in ~/Documents/git-learning on git:master x [10:27:35]
$ echo f15f398c878a0cd355627dea48d6052793864361 > .git/refs/heads/master

# altas @ altas in ~/Documents/git-learning on git:master x [10:28:53]
$ git log --oneline master
f15f398 third commit
56a15db second commit
4cada8f first commit

  git可以git update-ref用来修改引用,git update-ref <file> <hashcode><hashcode>也可以用来创建分支,分支本身就是一个指向分支的引用或者指针。

# altas @ altas in ~/Documents/git-learning on git:master x [10:29:04]
$ git update-ref refs/heads/master f15f398c878a0cd355627dea48d6052793864361

# altas @ altas in ~/Documents/git-learning on git:master x [10:30:53] C:128
$ git update-ref refs/heads/test 56a15db894d139806e1cd67a59e73b4f39dd44fa

# altas @ altas in ~/Documents/git-learning on git:master x [10:30:59]
$ git log --oneline test
56a15db second commit
4cada8f first commit

# altas @ altas in ~/Documents/git-learning on git:master x [10:31:12]
$ git branch
* master
  test

在这里插入图片描述

3.2.6.1 HEAD引用

  HEAD 文件通常是一个符号引用(symbolic reference),指向目前所在的分支。 所谓符号引用,表示它是一个指向其他引用的指针。
  修改HEAD可以手动编辑.git/HEAD文件,也可以使用git symbolic-ref命令修改。

# altas @ altas in ~/Documents/git-learning on git:master x [10:33:22]
$ cat .git/HEAD
ref: refs/heads/master

# altas @ altas in ~/Documents/git-learning on git:master x [10:36:49]
$ git checkout test
切换分支到 'test'

# altas @ altas in ~/Documents/git-learning on git:test o [10:36:52]
$ cat .git/HEAD
ref: refs/heads/test

# altas @ altas in ~/Documents/git-learning on git:test o [10:36:54]
$ git symbolic-ref HEAD
refs/heads/test

# altas @ altas in ~/Documents/git-learning on git:test o [10:41:24]
$ git symbolic-ref HEAD refs/heads/master

# altas @ altas in ~/Documents/git-learning on git:master x [10:41:37]
$ git branch
* master
  test
3.2.6.2 标签引用

  标签对象(tag object) 非常类似于一个提交对象——它包含一个标签创建者信息、一个日期、一段注释信息,以及一个指针。 主要的区别在于,标签对象通常指向一个提交对象,而不是一个树对象。 它像是一个永不移动的分支引用——永远指向同一个提交对象,只不过给这个提交对象加上一个更友好的名字罢了。
  可以通过git update-ref refs/tags/<tag-name> <hash-code>创建标签,标签的文件在.git/refs/tags

# altas @ altas in ~/Documents/git-learning on git:master x [10:50:09]
$ git update-ref refs/tags/v1.0 56a15db894d139806e1cd67a59e73b4f39dd44fa

# altas @ altas in ~/Documents/git-learning on git:master x [10:50:25]
$ git tag -a v1.1 aac4207d2dbd60b80651aa5ca182a09145c781e4 -m "test tag"

# altas @ altas in ~/Documents/git-learning on git:master x [10:51:02]
$ tree .git/refs/tags
.git/refs/tags
| v1.0
└── v1.1

0 directories, 2 files

# altas @ altas in ~/Documents/git-learning on git:master x [10:51:15]
$ cat .git/refs/tags/v1.1
72a567ddc56d42f1b00f7a3489ac2bc54dd75e45

# altas @ altas in ~/Documents/git-learning on git:master x [10:51:24]
$ cat .git/refs/tags/v1.0
56a15db894d139806e1cd67a59e73b4f39dd44fa

# altas @ altas in ~/Documents/git-learning on git:master x [10:51:26]
$ git cat-file -p 72a567ddc56d42f1b00f7a3489ac2bc54dd75e45
object aac4207d2dbd60b80651aa5ca182a09145c781e4
type tree
tag v1.1
tagger xxxx <xxxx@outlook.com> 1591584662 +0800

test tag

# altas @ altas in ~/Documents/git-learning on git:master x [10:51:39]
$ git cat-file -p 56a15db894d139806e1cd67a59e73b4f39dd44fa
tree 0155eb4229851634a0f03eb265b69f5a2d56f341
parent 4cada8f8e2f4ae1c716a0334aef316e79f9e7173
author xxxx <xxxx@outlook.com> 1591578725 +0800
committer xxxx <xxxx@outlook.com> 1591578725 +0800

second commit
3.2.6.3 远程引用

   如果添加了一个远程版本库并对其执行过推送操作,Git 会记录下最近一次推送操作时每一个分支所对应的值,并保存在refs/remotes目录下。 例如,你可以添加一个叫做 origin 的远程版本库,然后把 master 分支推送上去。

# altas @ altas in ~/Documents/git-learning on git:master x [10:54:22]
$ git remote add origin https://github.com/xxxx/git-learning.git

# altas @ altas in ~/Documents/git-learning on git:master x [10:54:25]
$ git push -u origin master
Username for 'https://github.com': xxxx
Password for 'https://xxxx@github.com':
对象计数中: 9, 完成.
Delta compression using up to 4 threads.
压缩对象中 100% (5/5), 完成.
写入对象中 100% (9/9), 739 bytes | 0 bytes/s, 完成.
Total 9 (delta 0), reused 0 (delta 0)
To https://github.com/xxxx/git-learning.git
 * [new branch]      master -> master

# altas @ altas in ~/Documents/git-learning on git:master x [10:54:44]
$ ls .git/refs/remotes/origin/master
.git/refs/remotes/origin/master

# altas @ altas in ~/Documents/git-learning on git:master x [10:54:54]
$ cat .git/refs/remotes/origin/master
f15f398c878a0cd355627dea48d6052793864361

# altas @ altas in ~/Documents/git-learning on git:master x [10:54:58]
$ git cat-file -p f15f398c878a0cd355627dea48d6052793864361
tree aac4207d2dbd60b80651aa5ca182a09145c781e4
parent 56a15db894d139806e1cd67a59e73b4f39dd44fa
author xxxx <xxxx@outlook.com> 1591578858 +0800
committer xxxx <xxxx@outlook.com> 1591578858 +0800

third commit

3.3 包文件

  git并不是简单的存储每个版本的文件的blob数据,提供了一种针对不同版本的数据文件之间的差异的存储文件——包文件。
  包文件包含.idx.pack,文件.idx存储不同版本之间的数据偏移,.pack存储数据,可以通过git verify-pack查看。

# altas @ altas in ~/Documents/git-learning on git:master x [11:04:15]
$ find .git/objects -type f
.git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579
.git/objects/f1/5f398c878a0cd355627dea48d6052793864361
.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
.git/objects/01/55eb4229851634a0f03eb265b69f5a2d56f341
.git/objects/72/a567ddc56d42f1b00f7a3489ac2bc54dd75e45
.git/objects/aa/c4207d2dbd60b80651aa5ca182a09145c781e4
.git/objects/56/a15db894d139806e1cd67a59e73b4f39dd44fa
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a
.git/objects/4c/ada8f8e2f4ae1c716a0334aef316e79f9e7173
.git/objects/bd/9dbf5aae1a3862dd1526723246b20206e5fc37
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
.git/objects/0c/e127a4b31e6bf1ed3285b8292e222d29812fed

# altas @ altas in ~/Documents/git-learning on git:master x [11:04:37]
$ ls
1.png  new.txt  repo.rb  test.txt

# altas @ altas in ~/Documents/git-learning on git:master x [11:04:42]
$ git add .

# altas @ altas in ~/Documents/git-learning on git:master x [11:04:46]
$ git commit -m "add 1.png file"
[master 489b88d] add 1.png file
 3 files changed, 1 deletion(-)
 create mode 100644 1.png
 delete mode 100644 backup/test.txt
 create mode 100644 repo.rb

# altas @ altas in ~/Documents/git-learning on git:master o [11:05:00]
$ git cat-file -p master^{tree}
100644 blob 8d1deebb8a78157ddaaa7236c878dc21dd932dda	1.png
100644 blob fa49b077972391ad58037050f2a75f74e3671e92	new.txt
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391	repo.rb
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a	test.txt

# altas @ altas in ~/Documents/git-learning on git:master o [11:05:08]
$ git cat-file -s 8d1deebb8a78157ddaaa7236c878dc21dd932dda
125634

# altas @ altas in ~/Documents/git-learning on git:master o [11:05:20]
$ echo "new line" >> 1.png

# altas @ altas in ~/Documents/git-learning on git:master x [11:05:47] C:1
$ git commit -am "1.png add new line"
[master fe0f637] 1.png add new line
 1 file changed, 0 insertions(+), 0 deletions(-)

# altas @ altas in ~/Documents/git-learning on git:master o [11:05:53]
$ git cat-file -p master^{tree}
100644 blob 68a5b4519b726591ae19d58a2cf39b30049ceedc	1.png
100644 blob fa49b077972391ad58037050f2a75f74e3671e92	new.txt
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391	repo.rb
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a	test.txt

# altas @ altas in ~/Documents/git-learning on git:master o [11:06:23]
$ git cat-file -s 68a5b4519b726591ae19d58a2cf39b30049ceedc
125643

# altas @ altas in ~/Documents/git-learning on git:master o [11:06:57]
$ git gc
对象计数中: 17, 完成.
Delta compression using up to 4 threads.
压缩对象中: 100% (12/12), 完成.
写入对象中: 100% (17/17), 完成.
Total 17 (delta 1), reused 0 (delta 0)

# altas @ altas in ~/Documents/git-learning on git:master o [11:07:10]
$ find .git/objects -type f
.git/objects/info/packs
.git/objects/pack/pack-2c68ae4945e385d83b164a51357f08bb7874fa96.idx
.git/objects/pack/pack-2c68ae4945e385d83b164a51357f08bb7874fa96.pack
.git/objects/bd/9dbf5aae1a3862dd1526723246b20206e5fc37
.git/objects/0c/e127a4b31e6bf1ed3285b8292e222d29812fed

# altas @ altas in ~/Documents/git-learning on git:master o [11:07:17]
$ git verify-pack .git/objects/pack/pack-2c68ae4945e385d83b164a51357f08bb7874fa96.idx

# altas @ altas in ~/Documents/git-learning on git:master o [11:07:37]
$ git verify-pack -v .git/objects/pack/pack-2c68ae4945e385d83b164a51357f08bb7874fa96.idx
fe0f6376ba21c3d4bffb1d60ec7e2b5a64605a38 commit 241 156 12
489b88d9803aca162254d003d66b871e1f730e63 commit 237 153 168
f15f398c878a0cd355627dea48d6052793864361 commit 235 150 321
56a15db894d139806e1cd67a59e73b4f39dd44fa commit 236 151 471
72a567ddc56d42f1b00f7a3489ac2bc54dd75e45 tag    139 123 622
4cada8f8e2f4ae1c716a0334aef316e79f9e7173 commit 187 120 745
aac4207d2dbd60b80651aa5ca182a09145c781e4 tree   104 108 865
d8329fc1cc938780ffdd9f94e0d364e0ea74f579 tree   36 46 973
d5bfe8ba19ff4f9e7ca057f3f92bb113bf8d564f tree   139 137 1019
427a3f940ce42c20e4e72baa7ebd8ae8e8520f1f tree   139 136 1156
0155eb4229851634a0f03eb265b69f5a2d56f341 tree   71 76 1292
83baae61804e65cc73a7201a7252750c76066a30 blob   10 19 1368
fa49b077972391ad58037050f2a75f74e3671e92 blob   9 18 1387
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a blob   10 19 1405
68a5b4519b726591ae19d58a2cf39b30049ceedc blob   125643 124533 1424
8d1deebb8a78157ddaaa7236c878dc21dd932dda blob   11 24 125957 1 68a5b4519b726591ae19d58a2cf39b30049ceedc
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 blob   0 9 125981
非 delta:16 个对象
链长 = 1: 1 对象
.git/objects/pack/pack-2c68ae4945e385d83b164a51357f08bb7874fa96.pack: ok

3.4 引用规范

  引用规范准确的说是远程和本地的交互规范,可以通过git remote add <name> <url>添加远程服务器,写入.git/config文件中。

$ cat .git/config
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
[remote "origin"]
	url = https://github.com/xxxx/git-learning.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
	remote = origin
	merge = refs/heads/master

  基本格式为:fetch=+<src>:<dst>其中+告诉 Git 即使在不能快进的情况下也要(强制)更新引用。<src><dst>分别为远程和本地引用的位置,支持通配符,但是rfs/heads/q*部分通配符是非法的。fetch可以替换为push
  下面三种参数是等价的:

  • git log origin/master;
  • git log remotes/origin/master;
  • git log refs/remotes/origin/master;

  针对团队的需求可以使用可以使用命名空间(或目录)来区分。

[remote "origin"]
	url = https://github.com/xxxx/git-learning.git
	fetch = +refs/heads/master:refs/remotes/origin/master
	fetch = +refs/heads/<name>/*:refs/remotes/origin/<name>/*

3.5 Hook

  本节的内容基本来自git-book。
  Git 能在特定的重要动作发生时触发自定义脚本。 有两组这样的钩子:客户端的和服务器端的。 客户端钩子由诸如提交和合并这样的操作所调用,而服务器端钩子作用于诸如接收被推送的提交这样的联网操作。
  git的hook文件在.git/hooks目录下,这里面存储了一些shell脚本供使用者参考,git的hook支持任何可执行脚本——python,ruby等。git提供的示例都是以.sample结尾,如果需要使用需要更改名称去除.sample。即一个正确的不带扩展名的可执行文件放入到.git/hooks目录下便可以安装一个hook脚本。

3.5.1 客户端hook

  客户端钩子分为很多种。 下面把它们分为:提交工作流钩子、电子邮件工作流钩子和其它钩子。
  提交工作流钩子
  pre-commit 钩子在键入提交信息前运行。 它用于检查即将提交的快照,例如,检查是否有所遗漏,确保测试运行,以及核查代码。 如果该钩子以非零值退出,Git 将放弃此次提交,不过你可以用 git commit --no-verify 来绕过这个环节。 你可以利用该钩子,来检查代码风格是否一致(运行类似 lint 的程序)、尾随空白字符是否存在(自带的钩子就是这么做的),或新方法的文档是否适当。
  prepare-commit-msg 钩子在启动提交信息编辑器之前,默认信息被创建之后运行。 它允许你编辑提交者所看到的默认信息。 该钩子接收一些选项:存有当前提交信息的文件的路径、提交类型和修补提交的提交的 SHA-1 校验。 它对一般的提交来说并没有什么用;然而对那些会自动产生默认信息的提交,如提交信息模板、合并提交、压缩提交和修订提交等非常实用。 你可以结合提交模板来使用它,动态地插入信息。
  commit-msg 钩子接收一个参数,此参数即上文提到的,存有当前提交信息的临时文件的路径。 如果该钩子脚本以非零值退出,Git 将放弃提交,因此,可以用来在提交通过前验证项目状态或提交信息。 在本章的最后一节,我们将展示如何使用该钩子来核对提交信息是否遵循指定的模板。
  post-commit 钩子在整个提交过程完成后运行。 它不接收任何参数,但你可以很容易地通过运行 git log -1 HEAD 来获得最后一次的提交信息。 该钩子一般用于通知之类的事情。
  电子邮件工作流钩子
  你可以给电子邮件工作流设置三个客户端钩子。 它们都是由 git am 命令调用的,因此如果你没有在你的工作流中用到这个命令,可以跳到下一节。 如果你需要通过电子邮件接收由 git format-patch 产生的补丁,这些钩子也许用得上。

  第一个运行的钩子是 applypatch-msg 。 它接收单个参数:包含请求合并信息的临时文件的名字。 如果脚本返回非零值,Git 将放弃该补丁。 你可以用该脚本来确保提交信息符合格式,或直接用脚本修正格式错误。

  下一个在 git am 运行期间被调用的是 pre-applypatch 。 有些难以理解的是,它正好运行于应用补丁 之后,产生提交之前,所以你可以用它在提交前检查快照。 你可以用这个脚本运行测试或检查工作区。 如果有什么遗漏,或测试未能通过,脚本会以非零值退出,中断 git am 的运行,这样补丁就不会被提交。

  post-applypatch 运行于提交产生之后,是在 git am 运行期间最后被调用的钩子。 你可以用它把结果通知给一个小组或所拉取的补丁的作者。 但你没办法用它停止打补丁的过程。
  其他钩子
  pre-rebase 钩子运行于变基之前,以非零值退出可以中止变基的过程。 你可以使用这个钩子来禁止对已经推送的提交变基。 Git 自带的 pre-rebase 钩子示例就是这么做的,不过它所做的一些假设可能与你的工作流程不匹配。

  post-rewrite 钩子被那些会替换提交记录的命令调用,比如 git commit --amend 和 git rebase(不过不包括 git filter-branch)。 它唯一的参数是触发重写的命令名,同时从标准输入中接受一系列重写的提交记录。 这个钩子的用途很大程度上跟 post-checkout 和 post-merge 差不多。

  在 git checkout 成功运行后,post-checkout 钩子会被调用。你可以根据你的项目环境用它调整你的工作目录。 其中包括放入大的二进制文件、自动生成文档或进行其他类似这样的操作。

  在 git merge 成功运行后,post-merge 钩子会被调用。 你可以用它恢复 Git 无法跟踪的工作区数据,比如权限数据。 这个钩子也可以用来验证某些在 Git 控制之外的文件是否存在,这样你就能在工作区改变时,把这些文件复制进来。

  pre-push 钩子会在 git push 运行期间, 更新了远程引用但尚未传送对象时被调用。 它接受远程分支的名字和位置作为参数,同时从标准输入中读取一系列待更新的引用。 你可以在推送开始之前,用它验证对引用的更新操作(一个非零的退出码将终止推送过程)。

  Git 的一些日常操作在运行时,偶尔会调用 git gc --auto 进行垃圾回收。 pre-auto-gc 钩子会在垃圾回收开始之前被调用,可以用它来提醒你现在要回收垃圾了,或者依情形判断是否要中断回收。

3.5.2 服务端hook

  钩子脚本在推送到服务器之前和之后运行。 推送到服务器前运行的钩子可以在任何时候以非零值退出,拒绝推送并给客户端返回错误消息,还可以设置足够复杂的推送策略。
  pre-receive:
  处理来自客户端的推送操作时,最先被调用的脚本是 pre-receive。 它从标准输入获取一系列被推送的引用。如果它以非零值退出,所有的推送内容都不会被接受。 你可以用这个钩子阻止对引用进行非快进(non-fast-forward)的更新,或者对该推送所修改的所有引用和文件进行访问控制。

  update:
  update 脚本和 pre-receive 脚本十分类似,不同之处在于它会为每一个准备更新的分支各运行一次。 假如推送者同时向多个分支推送内容,pre-receive 只运行一次,相比之下 update 则会为每一个被推送的分支各运行一次。 它不会从标准输入读取内容,而是接受三个参数:引用的名字(分支),推送前的引用指向的内容的 SHA-1 值,以及用户准备推送的内容的 SHA-1 值。 如果 update 脚本以非零值退出,只有相应的那一个引用会被拒绝;其余的依然会被更新。

  post-receive:
  post-receive 挂钩在整个过程完结以后运行,可以用来更新其他系统服务或者通知用户。 它接受与 pre-receive 相同的标准输入数据。 它的用途包括给某个邮件列表发信,通知持续集成(continous integration)的服务器, 或者更新问题追踪系统(ticket-tracking system) —— 甚至可以通过分析提交信息来决定某个问题(ticket)是否应该被开启,修改或者关闭。 该脚本无法终止推送进程,不过客户端在它结束运行之前将保持连接状态, 所以如果你想做其他操作需谨慎使用它,因为它将耗费你很长的一段时间。

3.6 git传输协议

3.6.1 哑协议

  哑协议在传输过程中,服务端不需要有针对 Git 特有的代码;抓取过程是一系列 HTTP 的 GET 请求,这种情况下,客户端可以推断出服务端 Git 仓库的布局,很难保证版本库的安全性和私有化。
  当使用git cloneclone版本库时会发生:

  1. GET info/refs,获取远程引用和SHA-1值的列表;
  2. GET HEAD,获取HEAD引用,得到版本库最后一次提交的信息;
  3. GET objects/xx/xxxxxx...xxxx,根据HEAD信息得到一个松散格式的对象,可以得到提交的详细信息和历史信息,便可以获取下一个提交对象和当前提交的目录树,根据目录树可以得到当前的数据信息;
  4. 根据3得到的tree对象获取该对象,可能会发生404,代表在 HTTP 服务端没有找到该对象。 这有好几个可能的原因——这个对象可能在替代版本库里面,或者在包文件里面。 Git 会首先检查所有列出的替代版本库:
    1. GET objects/info/http-alternates,如果这返回了一个包含替代版本库 URL 的列表,那么 Git 就会去那些地址检查松散格式对象和文件——这是一种能让派生项目共享对象以节省磁盘的好方法。否则 要检查服务端有哪些可用的包文件,需要获取 objects/info/packs 文件,这里面有一个包文件列表;
    2. GET objects/info/packs,这样就能查找到你需要的对象在那个包文件里,然后检查索引,然后根据索引从包文件中获取数据;
  5. 然后根据3-4递归的获取数据。

3.6.2 智能协议

  哑协议虽然很简单但效率略低,且它不能从客户端向服务端发送数据。 智能协议是更常用的传送数据的方法,但它需要在服务端运行一个进程,而这也是 Git 的智能之处——它可以读取本地数据,理解客户端有什么和需要什么,并为它生成合适的包文件。 总共有两组进程用于传输数据,它们分别负责上传和下载数据。

  详细内容尽可能参考git传输协议

3.6.2.1 上传

  客户端运行send-pack,服务端运行receive-pack用于上传数据。
  上传过程中git会运行send-pack进程通过,ssh链接服务器,尝试通过ssh在服务端运行ssh -x git@server "git-receive-pack 'simplegit-progit.git'",git-receive-pack命令会立即为它所拥有的每一个引用发送一行响应,这个响应一般包含服务端仓库的一些信息。本地的send-pack进程便可以判断本地和服务端的区别从而告知服务端的进程push那些文件。在一次数据请求之后,客户端会发送POST 请求的内容是 send-pack 的输出和相应的包文件。 服务端在收到请求后相应地作出成功或失败的 HTTP 响应。

3.6.2.2 下载

  客户端运行fetch-pack,服务端运行upload-pack用于上传数据。
  首先两个进程和上传时类似通过ssh链接获得响应,然后fetch-back向服务端发送自己有什么,想要什么通知upload-back进程发送自己所需要的对象包。抓取操作的握手需要两个 HTTP 请求。 第一个是向和哑协议中相同的端点发送 GET 请求,第二个数据交换则是一个单独的请求。

3.7 数据维护和恢复

3.7.1 维护

  Git 会不定时地自动运行一个叫做 “auto gc” 的命令。 大多数时候,这个命令并不会产生效果。 然而,如果有太多松散对象(不在包文件中的对象)或者太多包文件,Git 会运行一个完整的 git gc 命令。 “gc” 代表垃圾回收,这个命令会做以下事情:收集所有松散对象并将它们放置到包文件中, 将多个包文件合并为一个大的包文件,移除与任何提交都不相关的陈旧对象。git gc进行垃圾处理。
  一般情况下只有松散对象大于等于7000或者超过50个包文件才会进行gc。相关的设置可以通过修改gc.auto,gc.autopacklimit
  如果当前仓库的引用过多,git也会将引用打包成一个包文件存储在.git/packed-refs/中。

$ cat .git/packed-refs
# pack-refs with: peeled fully-peeled
7adfc7f87f08caeebba6664204988f3ac7bda9ea refs/heads/master
985b82d329a131471b283e443c027cdfb942e5b9 refs/remotes/origin/master

3.7.2 数据恢复

  第一种情况,当错误的使用checkout时,可以使用git refloggit log -g来找到你想恢复的提交的hash值,git reflog会记录每一次提交的记录。然后在利用你想恢复的提交信息新建一个分支,便可以得到之前的master的信息只不过现在在另一个分支而已。
  第二种情况git reflog也无法获取相关信息,可以使用git fsck --full显示出没有被其他对象指向的对象,然后利用相同的方法恢复。

  如果仓库历史上存在大文件,即便在之后的版本删除了该文件,该文件的数据还是会存储在objects中,但是删除大文件的对象又会破坏整个版本库,因此需要一种方法移除这个大文件。

  如下这里添加了一个104m的文件然后删除(git count-objects -v可以查看空间占用大小,size-pack以kb为单位计算)。
  因为执行完gc之后数据都混合在一起,需要通过git verify-pack命令查看不同文件的偏移。然后通过git rev-list命令找到对应文件的信息,再通过git log --oneline --branches -- <file>查看该文件和哪些提交相关。然后通过命令git filter-branch --index-filter 'git rm --cached --ignore-unmatch <file>'移除该文件(该命令并不会让命令将修改在硬盘上检出的文件,而只是修改在暂存区或索引中的文件)。然后移除就提交的指针rm -Rf .git/refs/original,rm -Rf .git/logs/。最后使用git prune --expire now真正删除。

# altas @ altas in ~/Documents/git on git:master x [19:43:07]
$ ll -h
总用量 104M
-rwxrwxr-x 1 altas altas 104M 9月  17  2015 top_potsdam_2_10_label.tif

# altas @ altas in ~/Documents/git on git:master x [19:43:10]
$ git add .

# altas @ altas in ~/Documents/git on git:master x [19:43:13]
$ git commit -m "big file"
[master (根提交) b57e68c] big file
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100755 top_potsdam_2_10_label.tif

# altas @ altas in ~/Documents/git on git:master o [19:43:34]
$ echo 1 > 1

# altas @ altas in ~/Documents/git on git:master x [19:43:49]
$ ls
1  top_potsdam_2_10_label.tif

# altas @ altas in ~/Documents/git on git:master x [19:43:52]
$ rm top_potsdam_2_10_label.tif

# altas @ altas in ~/Documents/git on git:master x [19:43:54]
$ git add .

# altas @ altas in ~/Documents/git on git:master x [19:43:58]
$ git commit -m "1"
[master de08644] 1
 2 files changed, 1 insertion(+)
 create mode 100644 1
 delete mode 100755 top_potsdam_2_10_label.tif

# altas @ altas in ~/Documents/git on git:master o [19:44:02]
$ git count-objects -v
count: 6
size: 1360
in-pack: 0
packs: 0
size-pack: 0
prune-packable: 0
garbage: 0
size-garbage: 0

# altas @ altas in ~/Documents/git on git:master o [19:44:05]
$ git gc
对象计数中: 6, 完成.
Delta compression using up to 4 threads.
压缩对象中: 100% (4/4), 完成.
写入对象中: 100% (6/6), 完成.
Total 6 (delta 0), reused 0 (delta 0)

# altas @ altas in ~/Documents/git on git:master o [19:44:12]
$ git count-objects -v
count: 0
size: 0
in-pack: 6
packs: 1
size-pack: 699
prune-packable: 0
garbage: 0

# altas @ altas in ~/Documents/git on git:master o [19:46:43]
$ git verify-pack -v .git/objects/pack/pack-9a9e658b6b47e017199ab9b21a6147e6b6165b15.idx|sort -k 3 -n | tail -3
b57e68cb654157634733091d171259ab83a5225d commit 183 120 156
de08644ec018271956acdee3fd6caafdfd64bfa3 commit 224 144 12
d9f75353f5aaec2651afbd9133212b9efeb6829e blob   108035480 714833 392

# altas @ altas in ~/Documents/git on git:master o [19:49:47]
$ git log --oneline --branches -- top_potsdam_2_10_label.tif
de08644 1
b57e68c big file

# altas @ altas in ~/Documents/git on git:master o [20:04:16] C:1
$ git rev-list --objects --all | grep d9f7
d9f75353f5aaec2651afbd9133212b9efeb6829e top_potsdam_2_10_label.tif

# altas @ altas in ~/Documents/git on git:master o [20:05:02]
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch top_potsdam_2_10_label.tif'
Rewrite b57e68cb654157634733091d171259ab83a5225d (1/2) (0 seconds passed, remaining 0 predicted)    rm 'top_potsdam_2_10_label.tif'
Rewrite de08644ec018271956acdee3fd6caafdfd64bfa3 (2/2) (0 seconds passed, remaining 0 predicted)
Ref 'refs/heads/master' was rewritten

# altas @ altas in ~/Documents/git on git:master o [20:06:23]
$ rm -rf .git/refs/original

# altas @ altas in ~/Documents/git on git:master o [20:06:38]
$ rm -rf .git/logs

# altas @ altas in ~/Documents/git on git:master o [20:06:57]
$ git gc
对象计数中: 5, 完成.
Delta compression using up to 4 threads.
压缩对象中: 100% (2/2), 完成.
写入对象中: 100% (5/5), 完成.
Total 5 (delta 0), reused 2 (delta 0)

# altas @ altas in ~/Documents/git on git:master o [20:07:02]
$ git count-objects -v
count: 4
size: 1352
in-pack: 5
packs: 1
size-pack: 1
prune-packable: 0
garbage: 0
size-garbage: 0

# altas @ altas in ~/Documents/git on git:master o [20:07:19] C:129
$ git prune --expire now

# altas @ altas in ~/Documents/git on git:master o [20:07:22]
$ git count-objects -v
count: 0
size: 0
in-pack: 5
packs: 1
size-pack: 1
prune-packable: 0
garbage: 0
size-garbage: 0

4 参考

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值