Git 使用方法笔记 | 青训营笔记

136 阅读12分钟

这是我在青训营学习的第三篇笔记(主要是笔记整理在本地),今天跟一下进度,学的是git。

Git 使用方法笔记

为什么要学Git?

  • 入职要用
  • 开源社区学习

Git 是什么

版本控制和Git发展历史

Git 是自由切开源的分布式版本控制系统,无论是小项目还是对速度和效率有要求的巨型项目,Git 设计用于处理一切。

  • 版本控制是什么?

一种记录一个或若干文件内容编号,一边将来查阅特定版本修订情况的系统

Git 可以记录版本变换:比如对代码行的每一次修改

  • 为什么需要版本控制

  • 更好的关注变更,了解到每个版本的改动是什么,方便对改动的代码进行检查(检查一下bug,做一下review),来预防事故发生。

  • review没有发现代码有问题,已经把代码上线到线上去了,这个时候需要快速取回滚问题代码。也能够随时切换到不同的版本,回滚误删误该的问题代码

1.1 版本控制

版本控制类型代表性工具解决的问题
本地版本控制RCS本地代码的版本控制
集中式版本控制SVN提供一个远端服务器来维护代码版本,本地不保存代码版本,解决多人协作问题
分布式版本控制系统Git每个仓库都能记录版本历史,解决只有一个服务器保存版本的问题

1.1.1 本地版本控制

最初的方式

通过本地复制文件夹,来完成版本控制,一般可以通过不同的文件名来区分版本

注:最后肯定会变成:选课系统大作业1.1.1.1_beta_0531_更新2_最终版(不改了)(2).zip

解决方案

开发了一些本地的版本控制软件,其中最流行的是 RCS

基本原理

本地保存所有变更的补丁集,可以理解成就是所有的 Diff,通过这些补丁,我们可以计算出每个版本的实际的文件内容。

缺点

RCS 这种本地版本控制最致命的缺陷就是只能在本地使用,无法进行团队协作,因此使用的场景非常有限,因此衍生出了集中式版本控制

1.1.2 集中版本控制

代表性工具: SVN

基本原理:

  1. 提供一个远端服务来保存文件,所有用户的提交都提交到该服务器中

  2. 增量保存每次提交的 Diff,如果提交的增量中和远端现存的文件存在冲突,则需要本地提前解决冲突。

优点:

  1. 学习简单,更容易操作

  2. 支持二进制文件,对大文件支持更友好

现在有很多团队依然在使用 SVN,比如一些美术团队和游戏团队,美术素材是使用 SVN 进行保存的,SVN对大的二进制文件支持更友好,所以在游戏团队中用的更多一点。

缺点

  1. 本地不存储版本管理的概念,所有提交都只能连上服务器才能提交

  2. 分支上支持不友好,对大型项目团队合作比较困难

  3. 用户本地不保存所有版本的代码,如果服务器故障容易导致历史版本的丢失。

1.1.3 分布式版本控制

代表性工具: Git

基本原理:

1.每个库都有完整的提交历史,可以直接在本地进行代码提交

  1. 每次提交记录都是完整的文件快照,而不是增量记录

  2. 通过 Push 等操作来完成和远端代码的同步

优点:

  1. 分布式开发,每个库都是完整的提交历史,支持本地提交,强调个体(对于开源库友好)

  2. 分支管理功能强大,方便团队合作,多人协同开发

  3. 校验和机制保证完整性,一般只添加数据,很少执行删除操作,不容易导致到吗丢失

缺点:

  1. 相对 SVN 更复杂,学习成本更高

  2. 对于大文件的支持不是特别好(每一次提交都是一个完整的文件块表而不是记录增量,如果对大文件修改,仓库会膨胀的非常快,所以不推荐直接通过git在仓库存储大文件)(git-lfs工具可以弥补这个功能)

虽然现在 git 是使用最广的版本控制,但 git 也不是第一个分布式版本控制软件

1.2 Git 发展历史

作者

Linus Torvalds (就是 Linux 这个项目的作者,同时也是 Git 的作者)

开发原因

怀疑 Linux 团队对 BitKeeper (另一种分布式版本控制系统,专有软件)进行了逆向工程,BitKeeper 不允许 Linux 团队继续无偿使用。因此决定自己开发一个分布式版本控制系统

其实最开始的时候 Linux 团队就很排斥使用 BitKeeper(因为 BitKeeper 不开源),有一天有一个 Linux 开发人员写了个工具,让人们能更好使用 BitKeeper,然后 BitKeeper 认为他们进行了逆向工程,不允许 Linux 团队继续无偿使用。

开发时间

大概花了两周时间,就完成了 Git 的代码第一个版本,后续 Linux 项目就开始使用 Git 进行维护

Github

全球最大的代码托管平台,大部分的开源项目都放在这个平台上

Gitlab

全球最大的开源代码托管平台,项目的所有代码都是开源的,便于在自己的服务器上完成 Gitlab 的搭建

相比于 github 的优势是自己的定制化更高的一个系统。

国内很多公司是基于gitlab去做代码托管平台,而不是直接托管在github上,因为gitlab可以自己去做很多定制化的地方

Gerrit

由 Google 开发的一个代码托管平台(谷歌内部有另一个代码托管平台,这个是对外开源的,主要用来托管Android,对 Android 多仓库支持较好)Android这个项目就托管在 Gerrit 之上

02 Git 的基本使用方式

Git 基本命令

  • 配置:config remote

  • 提交代码 add commit

  • 远端同步

  • 拉取代码 clone pull fetch

  • 推送代码 push

常见问题

  1. 为什么我明明配置了 Git 配置,但是依然没有办法拉取代码?

没有配置密钥,就去拉取代码,没有权限

  1. 为什么我 fetch 了 远端分支,但是我本地当前的分支历史还是没有变换?

2.1 Git 目录介绍,项目初始化

创建 demo 目录


mkdir demo

进入 demo 目录


cd demo

查看目录下的文件


ls demo

git 初始化


git init

其他参数

--initial-branch 初始化的分支(比如指定 main 分支而不是 master 分支)

--bare 创建一个裸仓库(纯 Git 目录,没有工作目录,只有 .git 目录的仓库,不允许添加文件,一般存储在服务器上的仓库就是通过 --bare 创建的,而本地的仓库就是通过 init 创建的)

--template 可以通过模板来创建预先构建好的自定义 git 目录(可以指定一些 git 目录的文件)

以树形结构打印 .git 目录


tree .git

这是我自己在 WSL 上(git 版本为 2.35.3,系统为OpenSUSE Leap 15.4)获得的输出结果,和视频上的略有出入


.git

├── branches
├── config
├── description
├── HEAD
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── fsmonitor-watchman.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-merge-commit.sample
│   ├── prepare-commit-msg.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   ├── push-to-checkout.sample
│   └── update.sample
├── info
│   └── exclude
├── objects
│   ├── info
│   └── pack
└── refs
├── heads
└── tags


9 directories, 17 files

HEAD:存储当前指向的分支,可以使用 cat 命令直接查看


$ cat .git/HEAD

ref: refs/heads/master

config: 存储 git 当前仓库的一些配置

hooks:存储一些hook,暂时不讲解

objects:存储的是一些文件信息

refs:存储一些分支信息

git 会生成一个仓库,这个仓库有工作区和暂存区的区别,工作区可以理解为你自己修改代码,新建文件的地方,然后通过 git add 命令将文件添加到暂存区,最后通过 commit 命令把它提交的 git 目录,其实暂存区和 commit 的提交其实都存在 ,git 目录里面

2.1.1 Git Config

不同级别的 Git 配置

Git 配置分成三部分

  • 全局配置(--global)

  • 系统级别的配置(--system)

  • 本地级别的配置(--local)

每个级别的配置可能重复,但是低级别的配置会覆盖更高级别的配置

local 就存在刚才的 .git/config 目录下

系统配置会存在 /etc/gitconfig 里面

全局配置存在当前用户的 ~/.gitconfig 里面

最高级别的是系统级别的,然后是 global,然后是 local

2.1.2 常见的 Git 配置

只有知道了自己电脑里面到底配置了怎样的内容,才能在使用的过程中更好的使用git工具。

比如哪些内容没有配,然后 git 报错了,我们也可以通过这个配置的内容取发现蛛丝马迹

用户名配置:

全新开始使用git的时候就需要做的一个配置


git config --global user.name "example"

git config --global user.email "example@exmaple.com"

instead of 配置

可以做一些url替换,比如http协议和SSL协议的替换


git config --global url.git@github.com:.insteadOf https://github.com

Git 命令别名配置

可以简化一些命令


git config --global alias.cin "commit --amend --no-edit"

这样设置以后,输入 git cin 就相当于输入 git commit --amend --no-edit

2.2 Git Remote

表示本地和远端仓库的关联信息

分为 HTTP 和 SSH 两种

在刚才新建的仓库执行 git remote -v 是没有输出的,因为还没有添加 Remote

查看 Remote


git remote -v

添加 Remote


git remote add origin_ssh git@github.com:git/git.git

git remote add origin_http https://github.com/git/git.git

此时再去查看 git remote -v 可以看到有内容了


origin_http https://github.com/git/git.git (fetch)

origin_http https://github.com/git/git.git (push)

origin_ssh git@github.com:git/git.git (fetch)

origin_ssh git@github.com:git/git.git (push)

再去查看 .git/config,可以看到新增了内容


[core]

repositoryformatversion = 0

filemode = true

bare = false

logallrefupdates = true

[remote "origin_ssh"]

url = git@github.com:git/git.git

fetch = +refs/heads/*:refs/remotes/origin_ssh/*

[remote "origin_http"]

url = https://github.com/git/git.git

fetch = +refs/heads/*:refs/remotes/origin_http/*

打错了怎么办? 通过 git remote -h 可以获取帮助命令


usage: git remote [-v | --verbose]

or: git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--mirror=<fetch|push>] <name> <url>

or: git remote rename <old> <new>

or: git remote remove <name>

or: git remote set-head <name> (-a | --auto | -d | --delete | <branch>)

or: git remote [-v | --verbose] show [-n] <name>

or: git remote prune [-n | --dry-run] <name>

or: git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]

or: git remote set-branches [--add] <name> <branch>...

or: git remote get-url [--push] [--all] <name>

or: git remote set-url [--push] <name> <newurl> [<oldurl>]

or: git remote set-url --add <name> <newurl>

or: git remote set-url --delete <name> <url>

  


-v, --verbose be verbose; must be placed before a subcommand

可以看到有 git remote rename 命令,以及 git remote remove 等等,可以通过这些命令进行修改

同一个 Origin 设置不同 Push 和 Fetch URL

比如,要从一个Github的一个开源库去拉代码,再部署到自己的仓库上

但是我又不想设置两个不同的 origin,想在同一个源里面实现这个功能

可以通过 --set-url 命令,把它单独设置一个 Push 的源


$ git remote add origin git@github.com/git.git

  


$ git remote set-url --add --push origin git@github,com:my_repo/git.git

  


$ git remote -v

origin git@github.com/git.git (fetch)

origin git@github,com:my_repo/git.git (push)

origin_http https://github.com/git/git.git (fetch)

origin_http https://github.com/git/git.git (push)

origin_ssh git@github.com:git/git.git (fetch)

origin_ssh git@github.com:git/git.git (push)

可以看到 origin 下面 fetch 和 push 不是同一个,这样就可以把 fetch 和 push 达到不同的仓库上去


$ cat .git/config

[core]

repositoryformatversion = 0

filemode = true

bare = false

logallrefupdates = true

[remote "origin_ssh"]

url = git@github.com:git/git.git

fetch = +refs/heads/*:refs/remotes/origin_ssh/*

[remote "origin_http"]

url = https://github.com/git/git.git

fetch = +refs/heads/*:refs/remotes/origin_http/*

[remote "origin"]

url = git@github.com/git.git

fetch = +refs/heads/*:refs/remotes/origin/*

pushurl = git@github,com:my_repo/git.git

.git/config 也发生了变换,其实每一个 remote 命令都会在 config 里面加配置,如果你熟悉配置文件,不使用命令,直接修改配置文件也是可以的。


$ vim .git/config

2.2.1 HTTP Remote

HTTP Remote 走的是 HTTP 协议去做身份认证,SSH 走的是 SSH 协议去做身份认证,

现在接触到的是 Go 开发 Go的依赖会很多。

每一次克隆或者编译的时候都要拉取代码,每一次操作都要求做认证会非常麻烦。所以需要进行免密配置

URL:

github.com/git/git.git

免密配置:

保存在内存:git config --global credential.helper 'cache --timeout=3600'

保存在硬盘:git config --global credential.helper "store --file /path/to/credential-file"

不指定目录的情况默认是 ~/.git-credentials

将密钥信息保存在指定文件中

具体格式: ${scheme}://${user}:${password}@github.com

正常来讲不推荐用 HTTP 方式取访问 Git 的,因为他可能相对来说没那么安全,这种存在内存或硬盘的方式也没那么方便。可能我们一般还是会使用 SSH 的方式去连接远端的仓库

2.2.2 SSH Remote

URL:

git@github.com:git/git.git

URL一般是 git@ 开头 或者 ssh: 开头

免密配置:

SSH 可以通过公私钥的机制,将生成的公钥存放在服务端,从而实现免密访问

目前 Key 的类型有四种,分别是 dsa, rsa, ecdsa, ed25519

默认使用的是 rsa,由于一些安全问题,现在已经不推荐使用 dsa 和 rsa 了,推荐优先使用 ed25519

为什么配置了公私钥还是无法访问?

一些新版本的 windows 代码不允许使用 dsa 和 rsa 类型的 key,在 ssh 层面拒绝了这两种 key 的访问。

生成公私钥:ssh-keygen -t ed25519 -C "your_email@example.com" 密钥默认存在 ~/.ssh/id_ed25519.pub

另外,在 SSH 访问时,其实需要指定你使用的 key 是哪一个,要修改 ssh 配置。可以自行查一下

2.3 Git Add

先创建一个 readme.md 文件


$ touch readme.md

查看 .git 目录的变化


$ tree .git


.git

├── branches

├── config

├── description

├── HEAD

├── hooks

│   ├── applypatch-msg.sample

│   ├── commit-msg.sample

│   ├── fsmonitor-watchman.sample

│   ├── post-update.sample

│   ├── pre-applypatch.sample

│   ├── pre-commit.sample

│   ├── pre-merge-commit.sample

│   ├── prepare-commit-msg.sample

│   ├── pre-push.sample

│   ├── pre-rebase.sample

│   ├── pre-receive.sample

│   ├── push-to-checkout.sample

│   └── update.sample

├── info

│   └── exclude

├── objects

│   ├── info

│   └── pack

└── refs

├── heads

└── tags

  


9 directories, 17 files

没有任何变化,因为只是修改了工作区文件。


$ git status


On branch master

  


No commits yet

  


Untracked files:

(use "git add <file>..." to include in what will be committed)

readme.md

  


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

使用 git add 命令 添加文件,此时再用 git status 查看,有待提交的新文件


$ git add .

  


$ git status

On branch master

  


No commits yet

  


Changes to be committed:

(use "git rm --cached <file>..." to unstage)

new file: readme.md

  


再查看 .git 目录,会发现 object 目录下出现了新文件内容

55 和 7d 连起来就是 object id


$ tree .git

.git

├── branches

├── config

├── description

├── HEAD

├── hooks

│   ├── applypatch-msg.sample

│   ├── commit-msg.sample

│   ├── fsmonitor-watchman.sample

│   ├── post-update.sample

│   ├── pre-applypatch.sample

│   ├── pre-commit.sample

│   ├── pre-merge-commit.sample

│   ├── prepare-commit-msg.sample

│   ├── pre-push.sample

│   ├── pre-rebase.sample

│   ├── pre-receive.sample

│   ├── push-to-checkout.sample

│   └── update.sample

├── index

├── info

│   └── exclude

├── objects

│   ├── 55

│   │   └── 7db03de997c86a4a028e1ebd3a1ceb225be238

│   ├── info

│   └── pack

└── refs

├── heads

└── tags

  


10 directories, 19 files

存储的时候实际上是加密的,但可以通过 git cat-file 来查看


$ git cat-file -p 557db03de997c86a4a028e1ebd3a1ceb225be238

它存的内容就是刚才创建的 readme.md 里面的内容

git add 之后,文件加入到了暂存区,需要通过 commit 命令才能真正提交到目录里面


$ git commit -m "add readme"

$ tree .git

.git

├── branches

├── COMMIT_EDITMSG

├── config

├── description

├── HEAD

├── hooks

│   ├── applypatch-msg.sample

│   ├── commit-msg.sample

│   ├── fsmonitor-watchman.sample

│   ├── post-update.sample

│   ├── pre-applypatch.sample

│   ├── pre-commit.sample

│   ├── pre-merge-commit.sample

│   ├── prepare-commit-msg.sample

│   ├── pre-push.sample

│   ├── pre-rebase.sample

│   ├── pre-receive.sample

│   ├── push-to-checkout.sample

│   └── update.sample

├── index

├── info

│   └── exclude

├── logs

│   ├── HEAD

│   └── refs

│   └── heads

│   └── master

├── objects

│   ├── 3a

│   │   └── 3aff7fa9639da674465c43fac565c1291f952b

│   ├── 55

│   │   └── 7db03de997c86a4a028e1ebd3a1ceb225be238

│   ├── ba

│   │   └── b66a6e458f1bdb58250bc6dc463f9b04f292db

│   ├── info

│   └── pack

└── refs

├── heads

│   └── master

└── tags

  


15 directories, 25 files

objects 目录出现了新内容,从一个 55 开头的文件变成了 3 个文件 3a 55 ba


$ git cat-file -p 3a3aff7fa9639da674465c43fac565c1291f952b

$ 100644 blob 557db03de997c86a4a028e1ebd3a1ceb225be238 readme.md

里面存储了 blob 类型 ID 是 557db03de997c86a4a028e1ebd3a1ceb225be238 文件名是 readme.md

它是一个目录树类型的 object,可以在里面存储刚才的目录信息,因为我们这一份代码除了代码本身内容,还会有目录树

再来看 ba 开头的文件


$ git cat-file -p bab66a6e458f1bdb58250bc6dc463f9b04f292db  22:32:05

tree 3a3aff7fa9639da674465c43fac565c1291f952b

author nya <nya@Administrator> 1685543499 +0800

committer nya <nya@Administrator> 1685543499 +0800

  


add readme

它是一个 commit 记录,会存 tree (目录树)是什么,作者 commiter 以及 message 信息

通过 git log 命令可以看到最新的 commit 就是这个


$ git log

commit bab66a6e458f1bdb58250bc6dc463f9b04f292db (HEAD -> master)

Author: nya <nya@Administrator>

Date: Wed May 31 22:31:39 2023 +0800

  


add readme

2.5 Objects

commit /tree/ blob 在 git 里面都统一称为 Object,除此之外还有个 tag 的 object

Blob:存储文件的内容

Tree:存储文件的目录信息

Commit:存储提交信息,一个 Commit 可以对应唯一版本的代码

如何把这三个信息串联到一起?

  1. 通过 Commit 寻找到 Tree 信息,每个 Commit 都会存储对应的 Tree ID

  2. 通过 Tree 存储的信息,获取到对应的目录树信息

  3. 从 tree 中获得 blob 的ID,通过 Blob ID 获取对应的文件内容

2.6 Refs

$ cat .git/refs/heads/master

bab66a6e458f1bdb58250bc6dc463f9b04f292db

里面存了最新的commit ID

新建一个分支


git checkout

查看 .git 目录的变化


.git

├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── fsmonitor-watchman.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-merge-commit.sample
│   ├── prepare-commit-msg.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   ├── push-to-checkout.sample
│   └── update.sample
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│   └── heads
│   ├── master
│   └── test
├── objects
│   ├── 3a
│   │   └── 3aff7fa9639da674465c43fac565c1291f952b
│   ├── 55
│   │   └── 7db03de997c86a4a028e1ebd3a1ceb225be238
│   ├── ba
│   │   └── b66a6e458f1bdb58250bc6dc463f9b04f292db
│   ├── info
│   └── pack
└── refs
├── heads
│   ├── master
│   └── test
└── tags

15 directories, 27 files

查看 test 的内容:


$ cat .git/refs/heads/test

bab66a6e458f1bdb58250bc6dc463f9b04f292db

和 master 是一样的

refs 的内容就是对应的 commit ID

因此把 ref 当作指针,只想对于的 Commit 来表示当前 Ref 对应的版本

不同种类的 ref

refs/heads 前缀表示的是分支,除此之外还有其他种类的 ref, 比如 refs/tags 前缀表示的是标签

Branch

git checkout -b 可以创建一个新分支

分支一般用于开发阶段,是可以不断添加 Commit 进行迭代的

Tag

标签一般表示的是一个稳定版本,指向的 Commit 一般不会变更

通过 git tag 命令生成 tag


git tag v0.0.1

.git 目录下的变化(重复部分省略)


...

├── heads
│   ├── master
│   └── test
└── tags
└── v0.0.1

  


15 directories, 28 files

tags 里面依然是 Commit ID


$ cat .git/refs/tags/v0.0.1

bab66a6e458f1bdb58250bc6dc463f9b04f292db

2.7 Annotation Tag (附注标签)

什么是附注标签

一种特殊的 Tag,可以给 Tag 提供一些额外信息(比如这次发布新增了什么 Featue,可以在附注标签里完成)

如何创建附注标签

使用 git tag -a 命令


.git

├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── fsmonitor-watchman.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-merge-commit.sample
│   ├── prepare-commit-msg.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   ├── push-to-checkout.sample
│   └── update.sample
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│   └── heads
│   ├── master
│   └── test
├── objects
│   ├── 3a
│   │   └── 3aff7fa9639da674465c43fac565c1291f952b
│   ├── 55
│   │   └── 7db03de997c86a4a028e1ebd3a1ceb225be238
│   ├── 7d
│   │   └── 032bba013d8ca8ec44a16f850b8d4c8f3aa3db
│   ├── ba
│   │   └── b66a6e458f1bdb58250bc6dc463f9b04f292db
│   ├── info
│   └── pack
└── refs
├── heads
│   ├── master
│   └── test
└── tags
├── v0.0.1
└── v0.0.2

16 directories, 30 files

查看新增的 tag,会发现它和之前的 v0.0.1 并不是同一个 commit ID


$ cat .git/refs/tags/v0.0.2  23:17:39

7d032bba013d8ca8ec44a16f850b8d4c8f3aa3db

使用 git log 查看日志


commit bab66a6e458f1bdb58250bc6dc463f9b04f292db (HEAD -> test, tag: v0.0.2, tag: v0.0.1, master)

Author: nya <nya@Administrator>

Date: Wed May 31 22:31:39 2023 +0800

同时 object 下面有新增的 3aff7fa9639da674465c43fac565c1291f952b


$ git cat-file -p 7d032bba013d8ca8ec44a16f850b8d4c8f3aa3db

object bab66a6e458f1bdb58250bc6dc463f9b04f292db

type commit

tag v0.0.2

tagger nya <nya@Administrator> 1685546118 +0800

  


add feature 1

这就是要介绍的第四种 object: tag object

包含了:

真正指向的 object

tag 标签名

tagger 谁打的标签

标签信息

2.8 追溯历史版本

获取当前版本代码

通过 ref 指向的 Commit 可以获取唯一的代码版本

获取历史版本代码

commit object 会有 parent commit 字段,通过 commit 的串联获取历史版本代码

  1. 修改文件,并提交,创建新的 object

.git

├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── fsmonitor-watchman.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-merge-commit.sample
│   ├── prepare-commit-msg.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   ├── push-to-checkout.sample
│   └── update.sample
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│   └── heads
│   ├── master
│   └── test
├── objects
│   ├── 0c
│   │   └── 44662d1910f56d8457a1a90c9472600734558d
│   ├── 3a
│   │   └── 3aff7fa9639da674465c43fac565c1291f952b
│   ├── 55
│   │   └── 7db03de997c86a4a028e1ebd3a1ceb225be238
│   ├── 75
│   │   └── 2dd80785917dd0175ce244b6e6e193d807ddeb
│   ├── 77
│   │   └── e5b1f9c44b629da267980e24a07f4f04bd7acc
│   ├── 7d
│   │   └── 032bba013d8ca8ec44a16f850b8d4c8f3aa3db
│   ├── ba
│   │   └── b66a6e458f1bdb58250bc6dc463f9b04f292db
│   ├── info
│   └── pack
└── refs
├── heads
│   ├── master
│   └── test
└── tags
├── v0.0.1
└── v0.0.2

  


19 directories, 33 files

objects 目录下从 4 个文件变成了 7 个文件


git cat-file -p 0c44662d1910f56d8457a1a90c9472600734558d 
tree 77e5b1f9c44b629da267980e24a07f4f04bd7acc
parent bab66a6e458f1bdb58250bc6dc463f9b04f292db

author nya <nya@Administrator> 1685546681 +0800
committer nya <nya@Administrator> 1685546681 +0800



update readme

新增的commit包含了 parent,从而可以让不同的commit串联

新的提交包含的tree是一个全新的tree,因为里面的blob id不一样了。

2.9 修改历史版本

1. commit --amend

通过这个命令可以修改最近的一次 commit 信息,修改之后 commit id 会变

2. rebase

通过 git rebase -i HEAD~3 可以实现对最近 3 个 commit 的修改

  • 合并commit

  • 修改具体的commit message

  • 删除某个commit

3. filter --branch

该命令可以指定删除所有提交种的某个文件或者全局修改邮箱地址等操作。(比如提交了一个大文件,需要删除)

2.10 Objects

新增的 Object

修改 Commit 之后新增了一个 Commit Object,但之前的 Commit Object 并没有删除

悬空的 Object

顾名思义就是没有 ref 指向的 object,可以通过 git fsck --lost-found 来查找


$ git fsck --lost-found  23:34:26

Checking object directories: 100% (256/256), done.

dangling commit 0c44662d1910f56d8457a1a90c9472600734558d

2.11 Git GC(垃圾回收)

GC

git gc 命令可以删除不需要的object,会对object进行打包压缩来减少仓库体积。

改一次文件(哪怕只改一行)就是一个独立的object,会造成很多冗余,git底层做了优化,可以通过打包压缩减少体积

Reflog

用于记录操作日志,防止如操作后数据丢失,通过reflog来找到丢失的数据,手动将日志设置为过期(不设置为过期还可以看到老commit的引用,无法通过 git gc 删除)。

指定时间

git gc pure=now 指定的是修建多久之前的对象,默认是两周前

2.13 Git Clone Pull Fetch

  • clone:拉取完整代码仓库

  • fetch:拉去某些分支的最新代码,但不会合并

  • pull:拉去远程分支并于本地代码合并,相当于git fetch + git merge

2.14 Git Push

将本地代码同步至远端,一般使用 git push origin master