[Git][基本原理与命令]
引言
Git是工作中最常用的版本控制工具,本文中将介绍其常用的命令。
根据作用的不同,可以分为基本命令、撤销命令、合并命令与远程仓库命令,下面将依次介绍这些命令。
基本原理
git 中提供了底层api供我们直接对底层数据结构进行调用,其中
git cat-file [-t] [-p] hashcode,其中-t可以查看hash值对应文件的类型,-p可以查看hash之对应的内容
基本对象
在最初的版本里实际上只有3种对象,分别是blob,tree和changeset(后续被改名为commit).本文主要对这三种对象进行介绍。基本的对象有tag域与binary data域。
Blob
在blob对象中,其tag的取值为blob,并在binary data域中存储了文件的内容,是最简单的object。但并不会保存文件名相关的信息。那么在一个工程项目中,我们有时候仅会对文件的名字进行改动,这时候则需要引出tree对象对这一变化进行追踪。
tree
tree的tag值为tree,binary data中维护了一个list。list的每一个item可以是一个tree对象,也可以是一个blob对象。 在每一个item中,有如下的属性:
- 权限和类型
- 路径
- 对象的sha1(类似于指针)
从这个角度来看,tree的list中既可以引用blob对象,也可以引用tree对象,循环往复,构成一颗文件结构树。
tree结构图[1]
利用tree与blob进行项目的追踪与维护
我们知道,若文件内容发生变化,则会生成新的blob对象与新的sha1值。对于tree对象,我们使用sha1值作为对blob对象的引用,那么该目录中的文件内容发生变化而引起对应的blob对象发生变化是,会导致tree维护的list中的内容发生变化,那么tree本身的sha1值也会发生变化。这就意味着,我们需要生成一个新的tree对象,来维护这一变化。
举个例子
对于如下结构的目录:
.
├── foo
│ └── a.txt
└── b.txt
如果我们将a.txt中的内容从"111"修改为"222", 那么整体的tree将会有如下结构:
可以看到新内容与旧内容分别维护了各自的根目录对应的tree,并复用没有修改内容的blob节点。
commit
仅靠tree对象与blob对象并不能记录与提交版本相关的信息。为了回溯任意一个版本,我们还需要一个对象对版本的提交信息进行维护——commit对象。
commit对象的tag是commit,binaray data域有以下内容:
- tree相关的信息
- parent
- author
- committer
其中,当我们提交一个新的commit时,会有一个对应的tree对象的与之关联。parent的个数可以是0个或者多个,维护着到上一个commit的sha1指针。可以通过该指针回溯到任意一个历史版本。从这个角度看,我们可以将git理解为基于一个key-value的数据库与默克尔树形成的有向无环图(DAG)的存储系统[3]。
tag
tag代表将分支永久化,生成tag后,这个tag对应的内容将永不可变,所以一般应用或者软件版本的发布一般用tag。
索引区
在我们的tree结构中,如果要访问一个具体的blob对象,需要使用深度优先遍历,通过路径上的信息来得到最终blob的完整路径。如果我们想要对比不同commit版本的文件之间的差异的话,就需要对两棵不同版本的tree进行深度遍历,当树很深时会涉及大量磁盘i/o。
为了提高i/o的速度,可以引入一个缓存,来提高文件读取的速度,这就是暂存区。通常相关的数据存储在./git/index。git使用mmap技术,访问文件内容。这里的文件内容指的是一系列entry对象,每一个entry对象中维护了对应的blob对象的映射与完整路径与其他相关的信息,entry对象在内存中按完整路径进行升序排列。这样,在后续的比较中就可以使用二分查找快速定位到相同文件名的不同版本完整地址,并进行比较。
命令
1. 基本命令
1.1 git add
当输入git add命令后,git会将文件生成对应的blob文件,并保存在git仓库中。但是并没有在tree上进行相应的结构映射,而只是在索引区建立了直接的索引。而tree结构的建立,需要等到commit命令才执行。
1.2 git commit
git commit后就会生成本次版本的对应树结构,涉及到一系列tree对象与commit对象的生成。(注意,blob对象在git add时就已经生成)
1.3 git checkout
该命令根据参数的不同,有许多效果。其主要的作用是在不同的分支之间切换。
查看代码[4]
git checkout # 列出本地所有分支
git checkout [<branch>] # 切换到该分支
git checkout -b [<branch>] # 创建并切换到该分支
git checkout -d [<branch>] # 删除该分支
-f # 强制执行
其余的一些命令参数如--merge,可以通过与其他命令的组合来实现,对结果把控更可靠。
2. 撤销命令
git reset
这条命令指定退回到某一个版本的提交。
查看代码
--mixed # 工作区不变,其余内容退回到指定版本
-hard # 所有内容退回到指定版本
-soft # 回退到指定版本
该命令后可以指定文件名,用于对指定文件进行版本回退
需要谨慎使用 --hard参数,该参数会删除回退版本后的commit版本。另外永远不要将已经push并merge到origin中的branch的内容进行回退,否则会导致本地仓库的不同步。如果非要使用,后续则需要git push -f进行强制推送
git revert
其用法与 git reset类似,但是与reset不同的是,其不会删除一些历史commit记录,而是使用创建一个新的commit的方式来达到撤销历史修改的记录。对于一些已经push或产生其他副作用的修改,最好使用revert方式进行撤销操作。而reset则多用于本地的撤销修改。注:revert的commitid选则回退版本后一个的commitid。
3. 合并命令
git merge
会在两个parent commit上创建一个新的分支,若不存在冲突,则直接fast-forward merge。
查看代码
git merge [<branch>] # 将指定的分支合并到当前分支上
若合并产生冲突,会在日志中提示,并且在合并操作产生的commit中,会标出冲突的部位。当我们以手动的方式修改完冲突后,使用git add 通知git已完成冲突合并,并用commit完成这次merge(此时会自动带上 merge的message)
git rebase
从字面上来理解,是为当前分支重新确定一个基。达到的效果与merge类似,但是其commit的拓扑结构是不一致的。当执行完rebase后,是将当前分支以“类似”嫁接的形式,嫁接到某个指定的分支上,而不是类似于merge,将两个分支合并起来。
举个例子
当前我们的分支结构如下:
我们正在dev分支上,想将dev的修改记录应用到master分支上,但又不想以merge的方式进行。此时我们可以使用rebase,为dev分支重新确定一个基。执行完git rebase master后,效果如下:
完成了类似嫁接的操作, 将dev的原始分叉点重新定位在master分支的最新的commit上,并生成了与dev分支长度相同的新的一系列commit。
4. 远程仓库命令
git fetch
该命令会在本地仓库中更新所有在远程端被追踪的分支,并用灰色的标记来标记这些属于remote的分支
举个例子
远程与本地分支的原始状态
执行完git fetch后本地仓库的状态:
git pull
该命令相当于一条组合命令,在默认的参数下相当于git fetch + git merge [<remote_branch>],当然我们可以指定参数git>],当然我们可以指定参数git pull --rebase,那么就会使用git rebase来替代第二条命令。注意,默认只会将当前分支对应的远程分支合并进来。
举个例子
原始状态
执行完git pull后
git push
该命令会将本地分支的内容推送到对应的远程分支上。从理论上来讲,所有的push操作都应该进行fast-forward merge,若仍存在一些不同则会发生reject错误。遇到这种情况时,通常需要先将远程的内容pull到本地进行合并后,再进行push。
参考
[2]Explain Git with D3 (onlywei.github.io)
[Git][基本原理与命令]的更多相关文章
- 【原理、命令】Git基本原理、与Svn的区别、命令
一.Git是什么? Git是目前世界上最先进的分布式版本控制系统.工作原理 / 流程:Workspace:工作区Index / Stage:暂存区Repository:仓库区(或本地仓库)Remote ...
- Git 基本原理与经常使用命令
平时使用过两种版本号控制软件 SVN 和 Git,平心而论,假设纯粹自己使用,那么绝对 Git 更加适合,本地库.远程库.离线工作.强大而灵活的分支.大名鼎鼎的Github, 这些都是选择 Git 的 ...
- git基本原理
git基本原理 一.总结 一句话总结:把原理那张图图背下来 1.git中的四大区,除了远程仓库和本地仓库,剩下两个是什么? 解答:工作区和暂存区. 2.git中的四大区(例如远程仓库和本地仓库),他们 ...
- GIT 版本控制常用命令学习汇总
GIT 版本控制常用命令汇总 git version 查看当前git版本信息 git help 获取全部命令帮助信息 git help <command> 获取指定命令帮助信息 git c ...
- git的一些命令行
以下代码均在命令行中执行:在目标文件夹目录下: 1.初始化一个Git仓库,使用git init命令. 2.添加文件到Git仓库,分两步: 第一步,使用命令git add <file>,注意 ...
- git log 常用命令及技巧
git log常用命令以及技巧 1.git log 如果不带任何参数,它会列出所有历史记录,最近的排在最上方,显示提交对象的哈希值,作者.提交日期.和提交说明.如果记录过多,则按Page Up.Pag ...
- Git基本常用命令
Git基本常用命令如下: mkdir: XX (创建一个空目录 XX指目录名) pwd: 显示当前目录的路径. git init 把当前的目录变成可以管理的git仓库,生成隐藏.git文件. git ...
- git workflow常用命令
git init git status git add readme.txt git add --all Adds all new or modified files git comm ...
- git的一些命令
因为项目的原因,大家把项目托管到git上,然后我不会,队友就传了一个廖雪峰的git教程,讲的很详细,不会用git的同学,可以在http://pan.baidu.com/s/1pKizolP上下载,这是 ...
- 关于Git的stash命令
add 添加新文件到 Git 代码仓库的索引中 $ git add filename mv 移动或重命名文件 $ git mv old-filename new-filename rm 从工作目录和 ...
随机推荐
- 当多核变单核:破解CPU核心神秘失踪的终极指南!
CPU 核心与线程识别问题解决文档 1. 背景 在一台物理主机上运行 lscpu 命令时,发现系统仅识别到 1 个核心和 1 个线程,尽管主机搭载的是 Intel Xeon E5-2686 v4 处理 ...
- 几张图带你了解.NET String
String 字符串作为一种特殊的引用类型,是迄今为止.NET程序中使用最多的类型.可以说是万物皆可string 因此在分析dump的时候,大量字符串对象是很常见的现象 string的不可变性 str ...
- 初探python栈帧逃逸
前言 以前在一些大型比赛就遇到这种题,一直没时间去研究,现在康复训练下:) 生成器介绍 生成器(Generator)是Python中一种特殊的迭代器,它可以在迭代过程中动态生成值,而不需要一次性将所有 ...
- Windows编译运行webrtc全过程,并实现屏幕共享
文章分为三部分,代码获取/编译/运行. 第一步获取代码,打开cmd执行以下指令即可 set WORKSPACE=E:\webrtc mkdir %WORKSPACE% cd /d %WORKSPACE ...
- register at least one qt version using“qt vs tools“->“qt options“问题描述及解决方法
问题描述:在安装了Qt 5.9.8,vs 2022, QT VS Tool 2022并配置好环境变量之后创建Qt项目时无法创建,提示至少需要注册一个Qt版本到Qt VS Tools的Qt Option ...
- 记录:coding持续集成之自动发布项目
把一个SpringBoot项目在DevOps一站式研发平台coding编译成jar远程部署到服务器分为几步?答:3步. 1.编译生成构建产物-jar包: 2.SCP 上传到远端服务器: 3.远程执行s ...
- Go中数组和切片
数组和切片 [1].数组 1.什么是数组 一组数 数组需要是相同类型的数据的集合 数组是需要定义大小的 数组一旦定义了大小是不可以改变的. package main import "fmt& ...
- 从零开始的Python世界生活——内置模块(Math)
从零开始的Python世界生活--内置模块(Math) Python的math模块提供了丰富的数学函数和常数,支持基本的数学运算.三角函数.对数.指数等,适用于科学计算和工程应用. 数学常量: 注意m ...
- Windows 杜比OEM授权
我们中高端的windows笔记本上都可以看到Dolby音效,TV电视上也有支持Dolby显示选项. 杜比主要有几类:Dolby全景声(也叫Atmos).Dolby视界(Vision).杜比影院(Dol ...
- Think in Java之构造器的真正调用顺序
构造器是OOP的重要组成部分,很多人认为它很容易.只不过是new了一个对象而已.而think in java的作者却告诉我们,其实这并不容易.先看下面这个例子.在你没看结果之前,你觉得你的答案是对的么 ...






