git
常用命令
diff

git diff
:当工作区有改动,临时区为空,diff的对比是“工作区”与最后一次commit提交的仓库的共同文件”;当工作区有改动,临时区不为空,diff对比的是“工作区”与“暂存区”的共同文件”。
git diff --cached
:显示暂存区与最后一次commit的改动。
git diff
<分支1> <分支2>
显示两个分支最后一次commit的改动。
init
git init
初始化一个仓库
add
git add .
将除.gitignore
中声明的所有文件加入暂存区
也可以git add 特定文件,将文件加入暂存区。
commit

git commit -m "提交说明"
提交到工作区
git commit --amend
修改上次的提交记录
push
git push
提交到远程库。可以指定分支提交
比如:git push origin main
指定为main分支
pull
git pull
将改动拉倒本地库,并合并,默认为Merge合并
如果加上rebase
参数则合并方式变为rebase合并。
log
git log
查看提交历史
git log --oneline
简洁输出
branch
git branch xxx
建立分支xxx
然后用git checkout xxx
切换到分支xxx
或者用git checkout -b xxx
完成同样的操作
checkout

git checkout
回退版本即head分离 但master不变
checkout可以切换分支,也可以head分离,但是分支的位置不变,但后面的reset和revert都会改变分支
reset
git reset --hard HEAD^
回退一个版本(master也改变)
git reset --hard HEAD~n
回退n个版本
git reset --hard xxxxxx
xxxxxx为版本号 即为git
log显示的每一个版本号 一般为前六位
reset的参数有三种,其作用如下:
最危险但最常用的就是hard。soft也常用来修改某个提交,只修改提交,之后再进行提交即可达到修改的目的。
revert
git revert
命令用于撤消对仓库提交历史的更改。其他“撤消”命令,例如
git checkout 和 git reset,将HEAD和分支引用指针移动到指定的提交。git
revert也需要一个指定的提交,但是,它并不会将 ref
指针移动到这个提交。revert
操作将采用反转指定的提交的更改,并创建一个新的“还原提交”。然后更新 ref
指针以指向新的还原提交,使其成为分支的HEAD。(创建一个新的commit结点)
merge
git merge
比如在master分支里 执行git merge xxx
将xxx分支合并到master中,一般项目开发,一人一个分支,最后提交的时候合并再提交。不过更推荐用git
rebase方法,这样合并后的分支更加直观。一般是进行三方合并。
branch
最常用的就是创建和删除分支
git branch -f master~3
将分支强制回退三个版本,但head不动
cherry-pick
当需要另一个分支的所有改动时,用git merge
,但当需要部分改动时候,要用git cherry-pick xxx
xxx为哈希值或者分支名,指定为分支名时候,将分支的最新改动合并过来
rebase
当不知道提交的哈希值时,可以用git rebase -i HEAD~x
来可视化管理,可以调整提交的顺序,可以删除不想要的提交,或合并提交
这是线性化的自动的 cherry-pick
git rebase xx1 xx2
将xx2分支上的提交记录放到xx1后面
此外活用
git rebase -i
,进行可视化的操作。 ### fetch
git fetch
获取远程仓库的数据,不会改变你本地仓库的状态,不会更新你的master,也没有更改你的本地磁盘文件,可以理解为单纯的下载操作。而git pull
相当于git fetch + git merge
即抓取后合并
reflog
git reflog
查看操作记录,这个操作可以撤销不小心用git reset
回退版本的操作
stash
它会保存当前工作进度,会把暂存区和工作区的改动保存到一个未完结变更的堆栈中;执行完这个命令后,在运行 git status
命令,就会发现当前是一个干净的工作区,没有任何改动。
git stash
是本地的,不会上传到服务器上;- 可以通过使用
git stash save 'message...'
可以添加一些注释。

底层内容
这几天系统学习了一下git的底层内容,通透了很多,记录一些。贴一下原博客:https://www.lzane.com/tech/git-internal/ 首先看一个图
首先要明确有四种object,第一种是记录文件内容,第二种是记录目录结构,第三种是记录提交信息,第四种是记录tag信息,第四种无关紧要。
从下面开始看,最下面记录的文件内容,注意只记录文件内容,不包括文件名等其它内容。是一个blob类型的节点,将文件的内容信息经过SHA1哈希算法得到对应的哈希值作为这个object在Git仓库中的唯一身份证。
然后再往上的三角形记录的是仓库的目录结构,它将当前的目录结构打了一个快照。从它储存的内容来看可以发现它储存了一个目录结构(类似于文件夹),以及每一个文件(或者子文件夹)的权限、类型、对应的身份证(SHA1值)、以及文件名。
再往上就是记录的提交的信息,它储存的是一个提交的信息,包括对应目录结构的快照tree的哈希值,上一个提交的哈希值,提交的作者以及提交的具体时间,最后是该提交的信息。
还有分支的信息和Head。HEAD、分支和普通的Tag可以理解为一个指针,指向对应commit的sha1值。
仓库有三个分区:工作目录、index索引区域、Git仓库。
当文件被修改后,只是工作目录发生了改变,其余两个是没有任何变化的。当运行git add xxx
命令后,即将xxx文件加入了索引区域,此时新建了一个blob
object,并且将原来指向xxx指向了新建的blob
Object,记住索引索引的是add的所有文件,这时运行git commit
,会生成一个tree
object,然后创建commit
object,将分支等信息指向新的commit。注意每次commit都是储存的全新的文件快照而不是变更部分。
合并策略
Fast-forward(没有分叉)

Recursive(最常用最重要)
Recursive是Git分支合并策略中最重要也是最常用的策略,是Git在合并两个有分叉的分支时的默认行为。其算法可以简单描述为:递归寻找路径最短的唯一共同祖先节点,然后以其为base节点进行递归三向合并。
那怎么保证Git能够找到正确的合并base节点,尽可能的减少冲突呢?答案就是,Git在寻找路径最短的共同祖先节点时,如果满足条件的祖先节点不唯一,那么Git会继续递归往下寻找直至唯一。不唯一的多个祖先节点做一个临时的三向合并节点。
Recursive策略已经被大量的场景证明它是一个尽量减少冲突的合并策略,我们可以看到有趣的一点是,对于两个合并分支的中间节点(如上图节点4,5),只参与了base的计算,而最终真正被三向合并拿来做合并的节点,只包括末端以及base节点。
需要注意Git只是使用这些策略尽量的去帮你减少冲突,如果冲突不可避免,那Git就会提示冲突,需要手工解决。(也就是真正意义上的冲突)。 ### Ours & Theirs ### Octopus ## 参考