git
Git是目前世界上最先进的分布式版本控制系统,版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统
文件存储机制
Git存储使用的是一个内容寻址的文件系统,其核心部分是一个简单的键值对(key-value)数据库,当向数据库中插入任意类型的内容,它会返回一个40位十六进制的SHA-1哈希值用作索引。
在版本库中,Git维护的数据结构有:以下4种对象及索引,并通过保存commitID有向无环图的log日志来维护与管理项目的修订版本和历史信息。
blob -- 1个blob保存1个文件的1个版本的数据
tree -- 表示1个目录,记录着目录里所有文件blob哈希值、文件名子目录名及其他元数据。通过递归引用其他目录树,从而建立一个包含文件和子目录的完整层次结构
commit -- 1个提交对象保存版本库中每一次变化的元数据,每个提交对象指向一个版本的git目录树对象
tag -- 分为轻量标签和附注标签。轻量标签实际上是一个特定提交的引用,附注标签是存储在git中的一个完整可被校验的对象(保存在.git/refs/tags中),还包含打标签者的名字、e-mail、日志、注释等信息
Git是怎么储存信息的
这里会用一个简单的例子让大家直观感受一下Git是怎么储存信息的。 首先我们先创建两个文件。
$ git init
$ echo '111' > a.txt
$ echo '222' > b.txt
$ git add *.txt
Git会将整个数据库储存在.git/目录下,如果你此时去查看.git/objects目录,你会发现仓库里面多了两个object。
$ tree .git/objects
.git/objects
├── 58
│ └── c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c
├── c2
│ └── 00906efd24ec5e783bee7f23b5d7c941b0c12c
├── info
└── pack
好奇的我们来看一下里面存的是什么东西。
$ cat .git/objects/58/c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c
xKOR0a044K%
怎么是一串乱码?这是因为Git将信息压缩成二进制文件。但是不用担心,因为Git也提供了一个能够帮助你探索它的api git cat-file [-t] [-p], -t可以查看object的类型,-p可以查看object储存的具体内容。
$ git cat-file -t 58c9
blob
$ git cat-file -p 58c9
111
可以发现这个object是一个blob类型的节点,他的内容是111,也就是说这个object储存着a.txt文件的内容。
这里我们遇到第一种Git object,blob类型,它只储存的是一个文件的内容,不包括文件名等其他信息。然后将这些信息经过SHA1哈希算法得到对应的哈希值 58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c,作为这个object在Git仓库中的唯一身份证。
也就是说,我们此时的Git仓库是这样子的:
我们继续探索,我们创建一个commit。
$ git commit -am '[+] init'
$ tree .git/objects
.git/objects
├── 0c
│ └── 96bfc59d0f02317d002ebbf8318f46c7e47ab2
├── 4c
│ └── aaa1a9ae0b274fba9e3675f9ef071616e5b209
...
我们会发现当我们commit完成之后,Git仓库里面多出来两个object。同样使用cat-file命令,我们看看它们分别是什么类型以及具体的内容是什么。
$ git cat-file -t 4caaa1
tree
$ git cat-file -p 4caaa1
100644 blob 58c9bdf9d017fcd178dc8c0... a.txt
100644 blob c200906efd24ec5e783bee7... b.txt
这里我们遇到了第二种Git object类型——tree,它将当前的目录结构打了一个快照。从它储存的内容来看可以发现它储存了一个目录结构(类似于文件夹),以及每一个文件(或者子文件夹)的权限、类型、对应的身份证(SHA1值)、以及文件名。
此时的Git仓库是这样的:
$ git cat-file -t 0c96bf
commit
$ git cat-file -p 0c96bf
tree 4caaa1a9ae0b274fba9e3675f9ef071616e5b209
author lzane 李泽帆 1573302343 +0800
committer lzane 李泽帆 1573302343 +0800
[+] init
接着我们发现了第三种Git object类型——commit,它储存的是一个提交的信息,包括对应目录结构的快照tree的哈希值,上一个提交的哈希值(这里由于是第一个提交,所以没有父节点。在一个merge提交中还会出现多个父节点),提交的作者以及提交的具体时间,最后是该提交的信息。
此时我们去看Git仓库是这样的:
到这里我们就知道Git是怎么储存一个提交的信息的了,那有同学就会问,我们平常接触的分支信息储存在哪里呢?
$ cat .git/HEAD
ref: refs/heads/master
$ cat .git/refs/heads/master
0c96bfc59d0f02317d002ebbf8318f46c7e47ab2
在Git仓库里面,HEAD、分支、普通的Tag可以简单的理解成是一个指针,指向对应commit的SHA1值。
其实还有第四种Git object,类型是tag,在添加含附注的tag(git tag -a)的时候会新建,这里不详细介绍,有兴趣的朋友按照上文中的方法可以深入探究。
至此我们知道了Git是什么储存一个文件的内容、目录结构、commit信息和分支的。其本质上是一个key-value的数据库加上默克尔树形成的有向无环图(DAG)。这里可以蹭一下区块链的热度,区块链的数据结构也使用了默克尔树。\
Git的三个分区
接下来我们来看一下Git的三个分区(工作目录、Index 索引区域、Git仓库),以及Git变更记录是怎么形成的。了解这三个分区和Git链的内部原理之后可以对Git的众多指令有一个“可视化”的理解,不会再经常搞混。 接着上面的例子,目前的仓库状态如下:
这里有三个区域,他们所储存的信息分别是:
- 工作目录 ( working directory ):操作系统上的文件,所有代码开发编辑都在这上面完成。
- 索引( index or staging area ):可以理解为一个暂存区域,这里面的代码会在下一次commit被提交到Git仓库。
- Git仓库( git repository ):由Git object记录着每一次提交的快照,以及链式结构记录的提交变更历史。
我们来看一下更新一个文件的内容这个过程会发生什么事。
运行echo "333" > a.txt将a.txt的内容从111修改成333,此时如上图可以看到,此时索引区域和Git仓库没有任何变化。
运行git add a.txt将a.txt加入到索引区域,此时如上图所示,git在仓库里面新建了一个blob object,储存了新的文件内容。并且更新了索引将a.txt指向了新建的blob object。
运行git commit -m 'update'提交这次修改。如上图所示。
- Git首先根据当前的索引生产一个tree object,充当新提交的一个快照。
- 创建一个新的commit object,将这次commit的信息储存起来,并且parent指向上一个commit,组成一条链记录变更历史。
- 将master分支的指针移到新的commit结点。
至此我们知道了Git的三个分区分别是什么以及他们的作用,以及历史链是怎么被建立起来的。基本上Git的大部分指令就是在操作这三个分区以及这条链。可以尝试的思考一下git的各种命令,试一下你能不能够在上图将它们“可视化”出来,这个很重要,建议尝试一下。 如果不能很好的将日常使用的指令“可视化”出来,推荐阅读《图解Git》。
一些有趣的问题
有兴趣的同学可以继续阅读,这部分不是文章的主要内容。
问题1:为什么要把文件的权限和文件名储存在tree object里面而不是blob object呢?
想象一下修改一个文件的命名。
如果将文件名保存在blob里面,那么Git只能多复制一份原始内容形成一个新的blob object。而Git的实现方法只需要创建一个新的tree object将对应的文件名更改成新的即可,原本的blob object可以复用,节约了空间。
问题2:每次commit,Git储存的是全新的文件快照还是储存文件的变更部分?
由上面的例子我们可以看到,Git储存的是全新的文件快照,而不是文件的变更记录。也就是说,就算你只是在文件中添加一行,Git也会新建一个全新的blob object。那这样子是不是很浪费空间呢?
这其实是Git在空间和时间上的一个取舍,思考一下你要checkout一个commit,或对比两个commit之间的差异。如果Git储存的是问卷的变更部分,那么为了拿到一个commit的内容,Git都只能从第一个commit开始,然后一直计算变更,直到目标commit,这会花费很长时间。而相反,Git采用的储存全新文件快照的方法能使这个操作变得很快,直接从快照里面拿取内容就行了。
当然,在涉及网络传输或者Git仓库真的体积很大的时候,Git会有垃圾回收机制gc,不仅会清除无用的object,还会把已有的相似object打包压缩。
问题3:Git怎么保证历史记录不可篡改?
通过SHA1哈希算法和哈系树来保证。假设你偷偷修改了历史变更记录上一个文件的内容,那么这个问卷的blob object的SHA1哈希值就变了,与之相关的tree object的SHA1也需要改变,commit的SHA1也要变,这个commit之后的所有commit SHA1值也要跟着改变。又由于Git是分布式系统,即所有人都有一份完整历史的Git仓库,所以所有人都能很轻松的发现存在问题。
原文链接:www.lzane.com/tech/git-in…
git flow
就像代码需要代码规范一样,代码管理同样需要一个清晰的流程和规范
-
master 主分支 , 产品的功能全部实现后 , 最终在master分支对外发布
该分支为只读唯一分支 , 只能从其他分支(release/hotfix)合并 , 不能在此分支修改
另外所有在master分支的推送应该打标签做记录,方便追溯
例如release合并到master , 或hotfix合并到master\ -
develop 主开发分支 , 基于master分支克隆
包含所有要发布到下一个release的代码
该分支为只读唯一分支 , 只能从其他分支合并
feature功能分支完成 , 合并到develop(不推送)
develop拉取release分支 , 提测
release/hotfix 分支上线完毕 , 合并到develop并推送\ -
feature 功能开发分支 , 基于develop分支克隆 , 主要用于新需求新功能的开发
功能开发完毕后合到develop分支(未正式上线之前不推送到远程中央仓库!!!)
feature分支可同时存在多个 , 用于团队中多个功能同时开发 , 属于临时分支 , 功能完成后可选删除\ -
release 测试分支 , 基于feature分支合并到develop之后 , 从develop分支克隆
主要用于提交给测试人员进行功能测试 , 测试过程中发现的BUG在本分支进行修复 , 修复完成上线后合并到\develop/master分支并推送(完成功能) , 打Tag
属于临时分支 , 功能上线后可选删除\ -
hotfix 补丁分支 , 基于master分支克隆 , 主要用于对线上的版本进行BUG修复
修复完毕后合并到develop/master分支并推送 , 打Tag
属于临时分支 , 补丁修复上线后可选删除
所有hotfix分支的修改会进入到下一个release
创建 Devlop
git branch develop
git push -u origin develop
开始 Feature
# 通过develop新建feaeure分支
git checkout -b feature develop
# 或者, 推送至远程服务器:
git push -u origin feature
# 修改md文件
git status
git add .
git commit
完成 Feature
git pull origin develop
git checkout develop
#--no-ff:不使用fast-forward方式合并,保留分支的commit历史
#--squash:使用squash方式合并,把多次分支commit历史压缩为一次
git merge --no-ff feature
git push origin develop
git branch -d some-feature
# 如果需要删除远程feature分支:
git push origin --delete feature
开始 Release
git checkout -b release-0.1.0 develop
完成 Release
git checkout master
git merge --no-ff release-0.1.0
git push
git checkout develop
git merge --no-ff release-0.1.0
git push
git branch -d release-0.1.0
git push origin --delete release-0.1.0
# 合并master/devlop分支之后,打上tag
git tag -a v0.1.0 master
git push --tags
开始 Hotfix
git checkout -b hotfix-0.1.1 master
完成 Hotfix
git checkout master
git merge --no-ff hotfix-0.1.1
git push
git checkout develop
git merge --no-ff hotfix-0.1.1
git push
git branch -d hotfix-0.1.1
git push origin --delete hotfix-0.1.1
git tag -a v0.1.1 master
git push --tags
git 常用命令
文件状态跟踪记录
状态查看:git status
三种状态
1.untraked file(工作区的文件未被跟踪的文件)
2.changes to be commited(暂存区的文件,将要被提交的修改)
3.changes not staged for commit(没有准备提交的修改或文件之前提交过,然后进行了修改但还未add)
查看命令帮助
git config --help // 查看git config命令详细用法
git help config // 功能同上
配置
git config --global user.name "kekec" // 配置提交用户名
git config --global user.email "kekec@qq.com" // 配置e-mail信息
git config --global core.editor vim // 配置默认文本编辑器,当Git 需要你输入信息时会调用它
git config --global alias.st status // 为status配置别名st,这样git status就可以写成git st
git config --list // 查看当前仓库的所有配置信息(包括分支相关的信息)
git config user.name // 查看当前仓库的用户名信息
git config -e --global // 编辑全局配置文件(用户名和e-mail信息就记录在其中) 所在目录:c:/users/<用户名>/.gitconfig
git config -e // 编辑当前仓库的配置文件 所在目录:.git\config
git config --global credential.helper wincred // windows下删除git凭据,后面执行git命令会弹框要求输入用户名和密码
工作区文件操作
cat file //查看当前工作区文件具体内容
git checkout -- file//其实是用版本库里的最新版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
rm file //从工作区删除文件
git版本库暂存区操作
git add . 将修改文件提交到暂存区
git add file //添加文件到暂存区
git add -u//只会处理已修改或者已删除的文件,但是不会处理新建的文件
git reset HEAD file //将文件的版本库的最新版本重新放回工作区(已add)
git版本库操作
git log查看以后退出按Q
git log --pretty=oneline //简化信息,方便查看
git log --pretty=oneline --abbrev-commit
git reflog //查看每一次commit以及reset,以便查看commit-id
git reset --hard HEAD^ //回退到当前工作区版本的上一个版本
HEAD^:上一个版本 HEAD^^:上上一个版本 HEAD~100:上100个版本 commit-id(对应版本id)
git rm file //从版本库删除文件 git commit -a -m "A file was deleted" //从远程仓库删除文件再commit
文件修改查看
git diff file//(add提交前) //查看具体修改内容
git diff HEAD -- readme.txt //查看版本库最新版本与工作区最新版本的区别
储藏区
git stash // 将工作区中所有文件的修改备份压栈到储藏区,然后丢弃工作区与暂存区的所有文件的修改
git stash pop // 使用储藏区的栈顶处备份(stash@{0})来恢复当前分支的工作区,并将栈顶备份移除
git stash apply stash@{1} // 使用储藏区的栈顶下面一个备份(stash@{1})来恢复当前分支的工作区,但不移除储藏区中任何备份
git stash list // 查看储藏区栈列表
git stash show -p stash@{0} // 查看储藏区的栈顶处备份中各个文件的内容
git stash drop // 直接移除储藏区的栈顶处备份(不用于恢复当前分支的工作区)
git stash clear // 清除储藏区栈列表
查看远程版本库
git remote -v // 显示远程仓库的URL 注:由于git是分布式的,所有远程仓库可能有很多个
git remote-ls // 查看远程仓库URL和分支信息
git remote // 查看远程仓库名称 一般为origin
git remote rename origin test // 将远程仓库名称从origin修改为test
git remote show origin // 显示远程仓库的信息
git remote rm origin // 删除.git/config文件中添加remote origin相关的信息
git remote add origin github.com/kekec/Test.… // 在.git/config文件中添加remote origin指向的远程仓库URL(若已存在,则命令执行失败)
分支(查看/新建/切换/删除)
git branch // 列出所有本地分支
git branch -r // 列出所有远程分支cache
git branch -a // 列出所有本地分支和远程分支cache
git branch -av // 列出所有本地分支和远程分支cache(含简单说明)
git branch v1.0 // 在当前分支的HAED指针下创建名为v1.0的分支(创建完不会切到v1.0分支上)
git branch --track v1.0 origin/v1.0 // 若v1.0分支不存在则先新建,然后将其与远程分支origin/v1.0建立
git branch -m v1.0 x1.0 // 将分支v1.0重命名为x1.0
git branch -d v2.0 // 删除名为v2.0的分支(必须先切到其他分支上才能执行删除操作)
git branch -D v2.0 // 强制删除名为v2.0的分支(必须先切到其他分支上才能执行删除操作)
git branch -dr origin/v2.0 // 删除远程分支origin/v2.0 cache
git checkout v1.0 // 切换到v1.0分支上(v1.0分支不存在则命令执行失败)
git checkout -b v1.0 // 创建并切换到v1.0分支上(v1.0分支存在则命令执行失败)
git checkout -B v1.0 // 不存在则创建,并切换到v1.0分支上
git checkout -f v1.0 // 强制切换到v1.0分支上,丢弃暂存区和工作区中的所有文件的修改(工作区中未追踪的文件不受影响)
git checkout -f -B v1.0 origin/v1.0 // 不存在则创建,强制切换到v1.0分支上,丢弃暂存区和工作区中的所有文件的修改,并将HEAD指向origin/v1.0处(工作区中未追踪的文件不受影响)
git checkout - // 切换到上一次分支
分支合并
git checkout master git merge Feature // 将Feature分支merge合并到当前分支Master(无冲突时会直接提交)
git rebase Feature // 将Feature分支rebase合并到当前分支Master
**注1:git rebase会先找出共同的祖先节点,从祖先节点把Feature分支的提交记录全都剪切下来,然后合到Master 分支(合并前后commitID会不一样)
**注2:相对来说,git merge处理冲突更直接,但会增加一些冗余的提交记录;而git rebase能够保证清晰线性的提交记录,但这也将合并的操作没有被记录下来
**注3:最好是用git rebase合并远程分支到本地,git merge合并Feature分支到Master分支
**注4:在合并Feature分支到Master分支前,务必先执行git pull -r origin Feature来进行远程分支与本地分支的rebase合并\
**注5:处于冲突状态(conflict)的文件为UU(可通过git status -s --ignored来查找),手动处理完冲突后,然后使用git add该文件,最后继续执行git merge/rebase --continue来完成合并的提交工作
**注6:README.md文件冲突内容如下
<<<<<<< HEAD new code ======= old code >>>>>>>
head 到 ======里面的内容是自己的commit的内容=========到 >>>>>>里面的内容是下拉的内容
**注7:可以使用git mergetool来使用外部merge工具(可以在c:/users/<用户名>/.gitconfig文件配置beyond compare作为默认的mergetool)来处理冲突。
git merge/rebase --abort // 撤销当前merge或rebase操作
git merge/rebase --skip // 强制使用Feature分支的内容
git merge/rebase --continue // 手动处理完冲突后使用git add该文件,最后继续执行git merge/rebase --continue来完成合并的提交工作
git merge origin/master // fetch完之后,可以将远程分支cache master分支merge合并到当前分支上
git rebase origin/master // fetch完之后,可以将远程分支cache master分支rebase合并到当前分支上
git rebase --onto master 76cada~ // 将当前分支从[76cada, HEAD]区间段的提交ebase合并到master上
远程操作
git clone github.com/kekec/Test.… // 将github.com/kekec/Test.…
git clone github.com/kekec/Test.… MyProject // 将github.com/kekec/Test.…
git fetch origin master // 从远程仓库拉取master分支状态的变化信息(工作区文件不会更新)
git fetch // 从远程仓库拉取所有分支和tag状态的变化信息(工作区文件不会更新)
git fetch -p // 从远程仓库拉取所有分支和tag状态的变化信息,并清除已被删除的远程分支和tag在本地的缓存(工作区文件不会更新)
git pull <远程仓库名> <远程分支名>:<本地分支名>
git pull origin master // 先执行fetch,然后将远程origin/master分支merge合并到当前分支(最后会更新origin/master, origin/HEAD指针到最新提交)
git pull origin v1.0:master // 先执行fetch,然后将远程origin/v1.0分支merge合并到本地master分支
git pull -p // 先执行fetch,然后将对应的远程分支merge合并到当前分支,并清除已被删除的远程分支和tag在本地的缓存
git pull -r origin master // 先执行fetch,然后将远程origin/master分支rebase合并到master分支
git push <远程仓库名> <本地分支名>:<远程分支名>
git push -u origin master // 将本地仓库的修改push到origin所指向的远程仓库URL的master分支上,并在.git/config文件中记录当前分支与远程分支master的对应关系
git push origin // 将当前分支更新推送给对应的远端分支
git push origin -f // 使用当前分支更新强行覆盖对应的远端分支(合入远端分支有冲突时,也使用当前分支更新)
git push --set-upstream origin v1.0 // 将本地分支v1.0更新推送给对应的远端分支remotes/origin/v1.0,并将建立与远程分支origin/v1.0的追踪关系
git push origin -d v1.0 // 删除远端分支v1.0 功能同上
git 工具
- vscode里面的提交
-
githubDesktop,SourceTree
-
gitBash
创建仓库
- 本地创建git仓库 git init // 在当前目录创建一个空的git代码库
git init MyGame // 在当前目录创建一个名为MyGame的文件夹,然后在其中创建一个空的git代码库
.git目录结构如下:
hooks:不同操作时执行的hook脚本
info/exclude:与.gitignore文件(该文件需放在.git文件夹的同级目录中,windows下可通过命令行type nul > .gitignore来创建)一样,用作文件过滤。不同的是:该文件不会提交到版本库,因此过滤只对本地生效,不影响其他人
# 忽略所有.so 结尾的文件
*.so
# 但 game.so 除外
!game.so
# 仅仅忽略项目根目录下的 README.md 文件,不包括 subdir/README.md
/README.md
# 忽略 .svn/ 目录下的所有文件
.svn/
# 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt
doc/*.txt
# 忽略 doc/ 目录下所有扩展名为 txt 的文件
doc/**/*.txt
文件说明
logs/refs/heads:各个本地分支的版本log记录
logs/refs/remotes:各个远程分支cache的log记录
logs/refs/stash:储藏区数据
logs/HEAD:git操作记录
objects:2级文件索引(把SHA-1哈希值拆成了:2位+38位),存储commit数据、blob文件数据和tree目录数据
objects/pack:pack文件为存储commit、tree目录及blob文件的压缩数据;idx文件为pack文件中各数据对象的索引
objects/info/packs:该文件记录所有git库的pack文件列表
refs/heads:各个本地分支HEAD
refs/remotes:各个远程分支cache的HEAD
refs/tags:各个附注标签的信息
COMMIT_EDITMSG:上一次提交的注释
config:版本库相关的配置信息
description:仓库描述信息,供gitweb程序使用
index:暂存区相关的信息
HEAD:指向当前分支的最近提交(如:ref: refs/heads/master) 注:可通过git rev-parse HEAD命令打印当前HEAD的commit id
ORIG_HEAD:执行git merge/git pull/git reset操作时,会把调整为新值之前的先前版本的HEAD记录到OERG_HEAD中,用于恢复或回滚之前的状态
FETCH_HEAD:git fech将所有抓取分支的HEAD记录到.git/FETCH_HEAD中
MERGEHEAD:正在合并进HEAD的commit id
packed-refs:远程版本库cache和远程标签cache
配置
- 配置用户名和邮箱
git config --global user.name "feilefeile"
git config --global user.email "1045255891@qq.com"
提交
- 编写代码提交代码 和远程建立连接
touch README.md
git add README.md
git commit -m "first commit"
git remote add origin https://gitee.com/feilefeile/git1.git
git push -u origin master
图解过程
远程仓库
远程仓库是指托管在网络上的项目仓库,可能会有好多个,其中有些你只能读,另外有些可以写
查看当前远程库列表:
git remote//它会列出每个远程库的简短名字
git remote -v //-v 选项,显示对应的克隆地址
查看远程仓库信息
git remote show [remote-name]//查看某个远程仓库的详细信息
添加远程仓库
git remote add [shortname] [url]
删除远程仓库
git remote rm [remote-name]
远程仓库重命名
git remote rename [old] [new]
远程操作
远程引用是对远程仓库的引用(指针),包括分支、标签等等。\
远程分支
git ls-remote//显示获得远程引用的完整列表
本地仓库推送至远程库(推送分支)\
git push [remote-name] [branch-name]
推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上
1.git remote add origin git@github.com:murphyuwu/task0001.git//添加远程仓库origin
git push origin master//实际上是把当前分支master推送到远程库origin。
2.git push -u origin master//推送当前分支master
(加上了-u参数,Git不但会把`本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令)
从远程库克隆仓库到本地
从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了
分支管理
Git分支
每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支,所以分支就是一条commi时间线。
一个项目可以对应多条分支,也可以对应一条分支
HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。
创建分支并切换
一步完成:git checkout -b <name>(-b参数表示创建并切换,)
两步完成:
git branch <name>//创建分支
git checkout <name>//切换分支到当前分支
查看分支:git branch
合并分支:
git merge <name>//合并指定分支到当前分支,fast forward模式
git merge --no-ff <name>//可以看出曾经合并过
合并分支时,加上--no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。--no-ff参数,表示禁用Fast forward
删除分支:
git branch -d <name>
git branch -D <name>//强行删除
查看分支合并图
git log --graph
git log --graph --pretty=oneline --abbrev-commit//简化信息
本地分支与远程分支同步
git checkout -b branch-name origin/branch-name//在本地创建和远程分支对应的分支
git branch --set-upstream branch-name origin/branch-name//建立本地分支和远程分支的关联
分支创建发生的几件事
1.创建新分支时,新分支会指向现有分支最新的commit对象,如下图。
2.现有分支在工作区以及暂存区的状态,新分支也拥有同样的状态。
3.切换到新分支工作时,会改变之前的状态,并与新分支同步。
正因为切换到新分支工作时,现有分支的状态会与新分支的状态保持同步,因此为了避免这一状况,可以保住现有工作区的状态,再切换到新分支工作,此时新分支的状态就是干净的
git stash //保存当前工作现场
git stash drop //删除保存
git stash apply //恢复现场,但并不删除保存
git stash pop //恢复现场,并删除保存
git stash list //查看保存工作情况
创建分支并切换
$ git checkout -b dev//创建并切换分支到dev分支
$ git add readme.txt
$ git commit -m "branch test"//dev分支向前移一步
$ git checkout maste//切换到master分支
$ git merge dev//master向前一步与dev分支合并
$ git branch -d dev//删除dev分支
解决冲突
- merge 分支造成的冲突 由于在develop分支上面开发了feature_1.1分支和feature_1.1分支,在两个不同的分支修改了同一个文件导致的更改.此时把feature_1.1 和feature_1.2进行代码合并的话会导致冲突
- 直接在vscode或者记录里面更新最新的,共同地方比较更改,看问题
- 在我们feature分支,先把develop分支代码合并到feature_1.1,然后在自己分支上面直接使用rebase合并代码,这样你提交的代码commit就在之前人提交的后面,重新设置了基准线,然后git add . =>git rebase --continue
在实际开发过程中我们要分析冲突的来源,一般尽可能减少冲突的操作,一般开发分支有多人参与的时候建议每天先pull一次,修改代码以后再进行提交,这样可以尽可能避免冲突的来源
命令:
git reset // 丢弃暂存区中的所有文件的修改(工作区不受影响)
git reset --hard // 丢弃暂存区和工作区中的所有文件的修改(工作区中未追踪的文件不受影响)
git reset --soft HEAD~ // 仅将当前分支的HEAD指向上一次提交(暂存区和工作区中的所有文件的修改都不丢弃)
git reset --soft HEAD~2 // 仅将当前分支的HEAD指向上两次提交(暂存区和工作区中的所有文件的修改都不丢弃)
git reset --merge // 在被污染的工作区中回滚merge或者pull
(1) 即便你已经在本地更改了一些你的工作区,你也可安全的git pull,前提是你知道将要pull的内容不会覆盖你的工作区中的内容。
(2) git pull完后,你发现这次pull下来的修改不满意,想要回滚到pull之前的状态,我们可以执行git reset --hard ORIG_HEAD,但是这个命令有个副作用就是清空你的工作区,即丢弃你的本地未add的那些改变。 为了避免丢弃工作区中的内容,可以使用git reset --merge ORIG_HEAD,注意其中的--hard 换成了 --merge,这样就可以避免在回滚时清除工作区。
git reset --keep // 保留工作区并丢弃一些之前的提交
假设你正在编辑一些文件,并且已经提交,接着继续工作,但是现在你发现当前在工作区中的内容应该属于另一个分支,与之前的提交没有什么关系。此时,可以开启一个新的分支,并且保留着工作区中的内容。
(1) 这次是把在branch1中的改变提交了。
(2) 此时发现,之前的提交不属于这个分支,此时新建了branch2分支,并切换到了branch2上。
(3) 此时可以用reset --keep把在start之后的提交清除掉,但是保持工作区不变。
git revert --no-edit 3a6c702376168aa15a2f3d7bc98000d07a70d023 // 回滚3a6c702376168aa15a2f3d7bc98000d07a70d023提交,然后提交到本地仓库
git revert HEAD~ // 回滚HEAD的上一次提交,然后会弹出vim环境编辑注释(输入:q直接使用默认注释内容、输入:q!放弃修改使用默认注释内容、输入:x或:wq保存当前修改的注释内容),然后提交到本地仓库
git revert -n HEAD3 // 回滚掉HEAD3处的提交,不自动提交到本地仓库
git revert -n HEAD2..HEAD // 回滚掉(HEAD2, HEAD]之间的2次提交,不自动提交到本地仓库
注:git reset是把HEAD向后移动来删除提交,而git revert是用一次新的提交来回滚之前的提交(HEAD会继续前进)