《Pro Git》

《Pro Git》


(一)Git & 其他分布式版本控制系统 的区别

  1. 其他分布式版本控制系统比较关注文件内容的具体差异(如每次记录有哪些文件作了更新,以及更新了什么内容),有点类似Redis的AOF备份;而Git关注的是文件数据的整体是否发生改变,不保存前后变化的差异数据,类似Redis的RDB备份 —— 事实上,Git会把变化的文件作快照(snapshot)之后,记录在一个微型的文件系统中,每次提交更新后,它会纵览所有文件的指纹信息并对文件作一快照,然后保存一个指向该快照的索引,如果文件没有变化则会沿用上次的索引。

    快照(snapshot):
    Snapshot是关于制定数据集合的一个完全可用拷贝,该拷贝包括相应数据在某个时间节点的映像(一般会维系一个映射表和源数据进行对应)。
    指纹信息(fingerprint):
    Git的指纹信息是一个对文件快照进行SHA-1哈希算法校验和计算得到的40位十六进制字符串,以此来作为Git的文件索引。

详情参考《快照(Snapshot)》

  1. 其他分布式版本控制系统中,基本上所有操作都需要连接网络;而Git中绝大多数操作都可以在本地完成,不需要网络 —— Git的所有文件历史记录都会保存在本地,而不是只保存在远程仓库中。

(二)Git的 工作区域 & 文件状态

Git的 工作区域 & 文件状态

工作区域

  1. 工作目录(working directory)
  2. 暂存区域(staging area):git add
  3. 本地仓库(git repository):git commit

文件状态

  1. 已修改(modified):在工作目录(working directory)做出了修改、但还未通过git add暂存到暂存区域(staging area)的文件。
  2. 已暂存(staged):通过git add暂存到暂存区域(staging area)的文件。
  3. 已提交(commited):在暂存区域(staging area)通过git commit提交到本地仓库(git repository)的文件。

(三)Git命令

1.git help/--help/man:获取帮助

1
2
3
4
5
6
# 使用git command格式
git help <command>
git <command> --help

# 使用git-command格式
man git-command

2.git config --global:配置

一般来说,只有初次使用Git时才需要配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 配置用户名和邮箱
git config --global user.name "DragonBaby308"
git config --global user.email dragonbabay308.codeman@gmail.com

# 查看配置信息
git config --list

# 设置别名
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.unstage 'reset HEAD --'
git config --global alias.last 'log -1 HEAD'

如果修改了账号密码,那么重新对远程库进行操作时就会报错:

remote: HTTP Basic: Access denied fatal:
Authentication failed for xxx

这时候需要更改账号密码:

1
2
# 删除原有的账号密码,这样重新git clone时就会要求你重新输入账号密码
git config --system --unset credential.helper

3.git init:初始化新仓库

1
2
# 初始化新仓库
git init

4.git add:将已修改文件添加到暂存区

1
2
3
4
# 将已修改(modified)文件提交到staging area暂存区
git add .
git add *.c
git add README

5.git commit:将已暂存文件提交到本地仓库

1
2
3
4
5
6
7
8
# 将已暂存(staged)文件提交到git repository本地仓库
git commit -m "MESSAGE"

# 跳过git-add直接提交
git commit -a

# 修改上一次提交
git commit --amend

6.git clone:从现有仓库复制

Git 支持许多数据传输协议: git://http(s):// 或者 user@server:/path.git 表示的 SSH 传输协议。

1
2
3
4
# 复制到当前目录
git clone git://github.com/schacon/grit.git
# 复制到指定目录
git clone git://github.com/schacon/grit.git mygrit

7.git status:查看当前工作区状态

1
2
# 查看当前目录所有文件状态
git status

8.git diff:查看具体修改

1
2
3
4
5
6
7
# git diff不带参数:比较的是工作目录working directory和暂存区域staged area之间的差异
# —— 也就是修改(modifed)后还没有被暂存(staged)的变化内容
git diff

# 比较已暂存(staged)和上次提交(commited)的快照之间的差异
git diff --cached
git diff --staged

9.git rm:移除文件

1
2
3
4
5
# 从暂存区移除
git rm filename

# 从本地仓库移除
git rm --cached filename

10.git mv:改名

1
2
# 将file1改名为file2
git mv file1 file2

11.git log:查看提交历史

1
2
3
4
5
6
7
8
9
# 不带参数:按提交时间列出,最近的更新在最上面
git log

# -p 展开显示每次提交内容的差异
# -数字 只显示最近的对应次数的更新
git log -p -number

# --word-diff 显示单词层面上的对比
git log -p -2 --word-diff

12.撤销操作

有些撤销操作是不可逆的,所以一定要小心,一旦失误则有可能丢失部分工作成果。

(1)git commit --amend:修改上一次提交

1
2
3
4
5
git commmit -m "initial commit"
# 将忘记提交的文件加入暂存区
git add forgotten_file
# 覆盖上一次提交,两次commit只产生一个结果
git commit --amend

(2)git reset:删除上一次添加到暂存区的文件

1
2
# 取消文件暂存
git reset HEAD filename

(3)git checkout:取消对文件的修改

这个命令很危险,Git将不会保留你对文件做的所有修改。

1
2
# 取消对文件的修改
git checkout -- filename

13.远程仓库

(1)git remote:查看当前远程库

1
2
3
4
5
# 查看当前远程库的简短名字shortname
git remote

# 查看当前远程库的详细信息(克隆地址) --verbose
git remote -v

(2)git remote add:添加远程库

1
2
# 远程库名origin,URL为git://github.com/paulboone/ticgit.git
git remote add origin git://github.com/paulboone/ticgit.git

(3)git fetch/git pull:从远程仓库抓取数据

1
2
3
4
5
# 从远程仓库中抓取本地仓库没有的数据,运行后可在本地访问远程库所有分支
git fetch origin

# 抓取远程仓库的特定分支
git pull

(4)git push:推送数据到远程库

1
2
# 将本地的master分支,推送到远程库origin
git push -u origin master

(5)git remote show:查看某个特定远程库的信息

1
2
# 查看某个特定远程库的信息
git remote show origin

(6)git remote rename:重命名远程库

1
2
# 修改远程库名称
git remote rename origin neworigin

(7)git remote rm:删除远程库

1
2
# 删除远程库
git remote rm neworigin

14.标签

Git可以对某一个时间节点上的版本打上标签,人们在发布某个软件版本(比如v1.0)时经常这么做。

(1)git tag:显示已有标签

1
git tag

(2)git tag (-a -m):新建标签

1
2
3
4
5
# 新建一个轻量级标签,不含附注信息
git tag lightweight

# 新建一个含附注类型(annotated)的标签
git tag -a v1.0 -m "这个版本是1.0"

(3)git show [tagName]:查看对应标签详情

1
git show lightweight

(四)Git使用技巧

首次生成SSH密钥

1
2
3
4
5
6
7
# 生成RSA密钥,在.ssh目录下会生成id_rsa私钥、id_rsa.pub公钥
ssh-keygen -t rsa -C "ouremail@example.com"

cd ~/.ssh

# 复制公钥,加入Git远程仓库中
cat id_rsa.pub

命令补全:tab

Git bash中,输入命令的前几个单词,然后按tab键则可补全。

命令别名:git config –global alias.[aliasName] [commandName]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# git co = git checkout
git config --global alias.co checkout

# git br = git branch
git config --global alias.br branch

# git ci = git commit
git config --global alias.ci commit

# git st = git status
git config --global alias.st status

# 将文件从暂存区删除
# git unstage filename = git reset HEAD filename
git config --global alias.unstage 'reset HEAD --'

# 最后一次提交记录
# git last = git log -1 HEAD
git config --global alias.last 'log -1 HEAD'

(五)Git分支

https://learngitbranching.js.org/——在线学习git分支

1.master分支 & HEAD指针

  1. master分支:每个Git项目都默认有一个master分支,在没有新建分支的情况下就是唯一分支。
  2. HEAD指针:指向当前工作分支的一个指针。

2.在提交树上移动

  1. ^:向上移动一个提交记录

    master^就是master的上一次提交记录
    HEAD^就是当前分支的上一次提交记录,可以使用HEAD^一直向前移动

  2. ~<num>:向上移动num个提交记录

    HEAD~4就是从当前分支向上移动4次提交记录

3.git branch:新建分支

1
2
# 新建一个分支,名为testing
git branch testing

4.git checkout:切换分支

通过git checkout [branchname]命令,将HEAD指针移动到指定分支。

1
2
# 切换到testing
git checkout testing

如图:
branch切换

5.git checkout -b:新建&切换到分支

1
2
# 创建并切换到newbranch分支
git checkout -b newbranch

6.合并分支

(1)git merge

git merge [branchname]命令将对应分支合并到当前分支,一般是在master分支中做的。
在master执行git merge bugFix

1
2
3
4
5
6
7
8
9
# 切换到master分支
git checkout master

# 先将newbranch1合并到master,再将newbrach2合并到master
git merge newbreach1 newbrach2

# 如果合并分支遇到冲突,可以通过git-status查看冲突代码;
# 在合并后的分支(此处为master)中将冲突代码解决,然后重新git-add && git-commit
git status

(2)git rebase

rebase实际上就是取出一系列的提交记录,“复制”它们,然后在另一个地方逐个地放下去。
rebase可以创造更线性的提交历史。

rebasemerge的不同:
1.git merge b是将分支b合并到当前所在分支(a),所以一般在master分支中执行
2.git rebase b是将当前所在分支(a)合并到分支b

在bugFix分支执行git rebase master
执行git rebase master后,bugFix分支上的工作在master的最顶端,同时我们也得到了一个更线性的提交序列。注意,提交记录C3仍然存在,而C3’是我们rebasemaster上的副本。接下来需要更新master
在master分支执行git rebase bugFix

7.git branch -d:删除分支

1
2
# 删除newbranch分支
git branch -d newbranch

8.查看当前所有分支

1
2
3
4
5
6
7
8
9
10
11
# 查看当前所有分支
git branch

# 查看各个分支最后一次提交对象的信息
git branch -v

# 查看已合并分支
git branch --merged

# 查看未合并分支
git branch --no-merged

9.撤销操作

(1)git reset:本地撤销

git reset HEAD^
缺点:对于多人一起开发的分支无效
因为git push的时候会报错——本地版本比远程库旧!!

当然你可以git push -f强推,但是如果是企业环境是不可能这么做的。

(2)git revert:共享(远程)撤销

git revert HEAD
原理:C2’的状态与C1相同,是用来撤销C2的修改的
优点:revert后可以将更改推送到远程仓库与他人共享
注意:git revert后带的版本,是你不想要的版本

所以,如果你的本地修改被远程分支覆盖掉了,你也可以通过git revert HEAD还原本地修改


(六)内部原理

.git目录结构

git init会创建一个.git目录,目录结构如下:

1
2
3
4
5
6
7
8
9
HEAD          # ! 指向当前分支
branches/ # 新版本中不再使用branches目录
config # 包含了项目特有的配置选项
description # 仅供GitWeb程序调用
hooks/ # 钩子函数
index # ! 保存暂存区域(staging area)信息
info/ # 保存了一份不希望在.gitignore文件中管理的忽略模式的全局可执行文件
objects/ # ! 存储所有数据内容
refs/ # ! 存储所有指向数据(分支)的提交对象的指针

Git的 本质 & 命令

本质

Git的本质:内容寻址(content addressable)文件系统 + VCS(Version Control System,版本控制系统)用户界面

命令

  1. 底层(Plumbing)命令:UNIX风格的命令/脚本调用,比如hash-objectupdate-indexcommit-tree……

    Git的最初设计目的是一个为VCS服务的内容寻址文件系统,而非一个独立的VCS系统,所以才会有很多和文件操作有关的底层命令。

  2. 高层(Porcelain)命令:用户友好的命令,前文所有命令都是,如git-branchgit-checkoutgit-remote……


Git对象(类型 & 存储)

Git的核心是存储键值对(Key-Value),允许插入任何类型的内容,然后返回一个Key(即通过SHA-1哈希算法得到的值),通过该Key可以在任何时候取出对应内容。

类比HashMap的put和get(key)。

类型

(1)tree对象/树对象

树对象可以存储文件名(即一个子tree对象,类似于UNIX中的目录),也可以存储一组数据。
所有内容以子tree对象(类似UNIX中的目录)或blob对象(对应inodes或文件内容)存储。
一个单独的树对象包含一条或多条 tree 记录,每一条记录含有一个指向 blob 或子 tree 对象的 SHA-1 指针,并附有该对象的权限模式 (mode)、类型和文件名信息。
tree object

(2)commit对象/提交对象

指明了该时间点项目快照的顶层树对象(即顶层tree对象的SHA-1值)、作者/提交者信息(从 Git 设置的 user.name 和 user.email中获得)以及当前时间戳、一个空行,以及提交注释信息。
commit object

存储

Git往磁盘存储对象默认用的格式是松散对象(loose object)格式,即全量存储;另一种情况是packfile二进制文件存储,是增量存储

  1. Git 以对象类型为起始内容构造一个文件头,可以是blob/tree/commit,然后将文件头和原始内容拼接后计算SHA-1校验和

    类比MD5加盐存储。

  2. 然后Git会对数据进行zlib压缩,并写入磁盘。对象保存的路径为:SHA-1 值的头两个字符作为子目录名称,剩余 38 个字符作为文件名保存至该子目录中


Git引用(Reference)

Git采用的是K/V存储,其中Key值是对象的SHA-1值,然而SHA-1值难以记忆,所以我们用一个简单的名字来记录这些SHA-1值,这些指针被存储在.git/refs目录下,被称为引用


packfile二进制文件 & Git GC

  1. Git往磁盘存储对象默认用的格式是松散对象(loose object)格式

    所谓松散对象格式是说,如果Git中原本已有一个对象,对其进行少量修改后,Git会完全新建另一个对象进行存储(全量存储),而不是只保存两者的内容差异(增量存储),这样会导致重复的内容在磁盘中出现两次,严重占用存储空间。

  2. 当仓库中有太多的松散对象,或是手工调用git gc命令,或推送至远程服务器时,Git就会将松散对象打包至一个叫packfile的二进制文件,使用增量存储替代全量存储以节省空间并提高效率。

    从某种程度上来说,松散对象就是不在packfile中的对象。

  3. Git会不定时地自动运行auto gc命令,大部分情况下该命令什么也不做,只有存在7000个左右松散对象或50个以上的packfile时,才会调用git gc:收集所有松散对象并将它们存入packfile,合并packfile为一个大的packfile,然后将不被任何commit对象引用的、已存在超过一段时间的对象删除

    可以通过git gc –auto来手动执行GC。
    通过修改配置中的gc.auto和gc.autopacklimit可以调整GC时松散对象和packfile的阈值,默认分别是7000和50.

-------------本文结束感谢您的阅读-------------

本文标题:《Pro Git》

文章作者:DragonBaby308

发布时间:2019年06月12日 - 13:31

最后更新:2020年04月13日 - 23:48

原始链接:http://www.dragonbaby308.com/progit/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

急事可以使用右下角的DaoVoice,我绑定了微信会立即回复,否则还是推荐Valine留言喔( ఠൠఠ )ノ
0%