sudo 无权限时命令前面加上它
git fetch 同步分支
git stash save "暂存的备注" 暂存代码
git stash list 查看暂存记录
git stash pop 恢复暂存的工作区内容
git stash clear 清空暂存
git reset --hard 取消【取回暂存代码】
版本管理
1.是什么?
是一种在开发过程中用于管理文件、目录或工程等内容的修改历史,方便查看更改历史记录、备份以便恢复以前的版本的软件工程技术,也就是可以回到任何一个版本、大家可以进行协作开发、可以详细记录代码的变更情况。 简单来说就是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统、用于管理多人协同开发项目的一项技术、也是开发中一个非常重要的工具。 主要提供三种能力:可逆、并行、注解。
2.有哪些?
根据类别可以分成:
分布式版本控制系统
和集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为分布式版本管理系统每个计算机都有一个完整的仓库,可本地提交,可以做到离线工作,则不用像集中管理那样因为断网情况而无法工作。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了。
代表工具为Git、HG:
优点:
- 适合多人团队协作开发
- 代码集中化管理
- 可以离线工作
- 每个计算机都是一个完整仓库
集中式版本控制系统
版本库集中存放在中央服务器。因此在本地工作时,要先拉取最新的版本,才能开始工作,结束工作之后再推到🀄️央服务器。
代表工具:SVN、CVS
优点:
- 适合多人团队协作开发
- 代码集中化管理
缺点:
- 单点故障
- 必须联网,无法单机工作
本地版本控制系统
优点:
- 简单,很多系统中都有内置
- 适合管理文本,如系统配置
缺点:
- 其不支持远程操作,因此并不适合多人版本开发
3.版本控制系统的优点
- 记录文件所有历史变化,这是版本控制系统的基本能力
- 支持多功能并行开发,通常版本控制系统都支持分支,保证了并行开发的可行
- 可追溯、随时恢复到任意时间点,历史记录功能使我们不怕改错代码了
- 历史记录包括了作者、日期以及关于每次更改目的的书面说明。
- 拥有完整的历史记录可以追溯到以前的版本,以帮助分析错误的根本原因,这在需要修复旧版本软件中的问题时 是非常重要的
- 多人协作并行开发,对于多人协作项目,支持多人协作开发的版本管理将事半功倍
- 保持独立工作的同时 还支持合并功能,开发人员能够验证每个分支的更改是否冲突,并处理冲突
Git
什么是git?
是一个分布式版本控制软件,最初目的是为更好地管理Linux内核开发而设计
分布式版本控制系统的客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复
git的工作原理
理解git原理需要先理解几个术语:
- 工作区: 相当于本地写代码的区域,如 git clone 一个项目到本地,相当于本地克隆了远程仓库项目的一个副本(也可以说是在电脑里能看到的目录);
- 缓存区: 只能通过git bash等窗口显示,它是提交代码、解决冲突的中转站(是一个文件,保存了下次将提交的文件列表信息,一般存放在 .git 目录下的 index 文件中)
- 版本库: 工作区有一个.git目录(被隐藏了),内含缓存区或暂存区;
- 本地仓库: 连接本地代码和远程代码的枢纽,仅能通过git bash显示。在没有联网状态下,本地代码可先提交至该处
- 远程仓库:托管远程代码的中央服务器,如开源网站github。
- HEAD: 是 .git/HEAD 文件,它存储着当前working directory所处的某次commit
(用git对项目进行版本管理的时候
用HEAD表示当前的版本
HEAD^ 表示上一个版本 同 HEAD~1
HEAD^^ 表示上上个版本 HEAD~2
HEAD~100 表示上100个版本 )
当我们通过git init创建或者git clone一个项目的时候,项目目录会隐藏一个.git子目录,其作用是用来跟踪管理版本库的
Git 中所有数据在存储前都计算校验和,然后以校验和来引用,所以在我们修改或者删除文件的时候,git能够知道
Git用以计算校验和的机制叫做 SHA-1 散列(hash,哈希), 这是一个由 40 个十六进制字符(0-9 和 a-f)组成字符串,基于 Git 中文件的内容或目录结构计算出来,
如:2e305fcd7172de63f3c4227fd941df6d4ab36cf7
当我们修改文件的时候,git就会修改文件的状态,可以通过git status进行查询,状态情况如下:
- 已修改(modified):表示修改了文件,但还没保存到数据库中。
- 已暂存(staged):表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
- 已提交(committed):表示数据已经安全的保存在本地数据库中。
文件状态对应的,不同状态的文件在Git中处于不同的工作区域,主要分成了四部分,就👆提到的工作区、缓存区、本地仓库、远程仓库。
找了几张图来更好的理解 👆讲到的关系:
git与svn的区别及优缺点
- Git是分布式的,而SVN是集中式的
- Git把内容按元数据方式存储,而SVN是按文件
- Git没有一个全局版本号,而SVN有(目前为止这是跟 SVN 相比 Git 缺少的最大的一个特征)
- Git的内容的完整性要优于SVN
git常用命令
日常基本操作
* git init 初始化仓库,默认为 master 分支
* git add . 提交全部文件修改到缓存区
* git add <具体某个文件路径+全名> 提交某些文件到缓存区
* git diff 查看当前代码 add后,会 add 哪些内容
* git diff --staged查看现在 commit 提交后,会提交哪些内容
* git status 查看当前分支状态
* git pull <远程仓库名> <远程分支名> 拉取远程仓库的分支与本地当前分支合并
* git pull <远程仓库名> <远程分支名>:<本地分支名> 拉取远程仓库的分支与本地某个分支合并
* git commit -m "<注释>" 提交代码到本地仓库,并写提交注释
* git commit -v 提交时显示所有diff信息
* git commit --amend \[file1] \[file2] 重做上一次commit,并包括指定文件的新变化
* git log 查看历史记录
* git log -patch -num 显示每次提交所引入的差异,num表示显示最近的num次提交
* git log -stat 显示每次提交的文件修改统计信息。
* git graph 在日志旁以 ASCII 图形显示分支与合并历史。
* git log --pretty=oneline/short/full/fuller/format 可以使用不同于默认格式的方式展示提交历史, format 可以定制记录的显示格式 [常用选项](https://git-scm.com/book/zh/v2/Git-%E5%9F%BA%E7%A1%80-%E6%9F%A5%E7%9C%8B%E6%8F%90%E4%BA%A4%E5%8E%86%E5%8F%B2#pretty_format),当 oneline 或 format 与另一个 log 选项 --graph 结合使用时尤其有用。 这个选项添加了一些 ASCII 字符串来形象地展示你的分支、合并历史
关于提交信息的格式,可以遵循以下的规则:
* feat: 新特性,添加功能
* fix: 修改 bug
* refactor: 代码重构
* docs: 文档修改
* style: 代码格式修改, 注意不是 css 修改
* test: 测试用例修改
* chore: 其他修改, 比如构建流程, 依赖管理
远程同步-远程操作常见的命令:
* git fetch \[remote] 下载远程仓库的所有变动
* git remote -v 显示所有远程仓库
* git pull \[remote] \[branch] 拉取远程仓库的分支与本地当前分支合并
* git fetch 获取线上最新版信息记录,不合并
* git push \[remote] \[branch] 上传本地指定分支到远程仓库
* git push \[remote] --force 强行推送当前分支到远程仓库,即使有冲突
* git push \[remote] --all 推送所有分支到远程仓库
* git remote rename old new 修改一个远程仓库的简写名 如:old 重命名为new
分支操作
* git branch 查看本地所有分支
* git branch -r 查看远程所有分支
* git branch -a 查看本地和远程所有分支
* git branch <新分支名> 基于当前分支,新建一个分支
* git branch -D <分支名> 删除本地某个分支
* git branch <新分支名称> <提交ID> 从提交历史恢复某个删掉的某个分支
* git branch -m <原分支名> <新分支名> 分支更名
* git branch --merged 查看哪些分支已经合并到当前分支
* git branch --no-merged 查看所有包含未合并工作的分支
* git checkout <分支名> 切换到本地某个分支
* git checkout <远程库名>/<分支名> 切换到线上某个分支
* git checkout -b <新分支名> 把基于当前分支新建分支,并切换为这个分支(创建并切换)
* git checkout --orphan <新分支名> 新建一个空分支(会保留之前分支的所有文件)
* git merge <分支名> 合并分支
* git merge --abort 合并分支出现冲突时,取消合并,一切回到合并前的状态
* git push <远程库名> :<分支名> 删除远程某个分支
撤销
* git checkout \[file] 恢复暂存区的指定文件到工作区 (⚠️:任何修改都会消失)
* git checkout \[commit] \[file] 恢复某个commit的指定文件到暂存区和工作区
* git checkout . 恢复暂存区的所有文件到工作区
* git reset \[commit] 重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变
* git reset --hard 重置暂存区与工作区,与上一次commit保持一致
* git reset \[file] 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变
* git revert \[commit] 后者的所有变化都将被前者抵消,并且应用到当前分支
reset:真实硬性回滚,目标版本后面的提交记录全部丢失了
revert:同样回滚,这个回滚操作相当于一个提价,目标版本后面的提交记录也全部都有
存储操作
你正在进行项目中某一部分的工作,里面的东西处于一个比较杂乱的状态,而你想转到其他分支上进行一些工作,但又不想提交这些杂乱的代码,这时候可以将代码进行存储
* git stash 暂时将未提交的变化移除
* git stash pop 取出储藏中最后存入的工作状态进行恢复,会删除储藏
* git stash list 查看所有储藏中的工作
* git stash apply <储藏的名称> 取出储藏中对应的工作状态进行恢复,不会删除储藏
* git stash clear 清空所有储藏中的工作
* git stash drop <储藏的名称> 删除对应的某个储藏
* git commit --amend 重新提交commit
git merge、git rebase、git reset 、git revert、git checkout 、git cherry pick
git merge 合并某个分支到当前分支下,并自动进行新的提交
* git merge --no-commit <next> # 合并某个分支到当前分支下,不进行新的提交
* git merge <master> <next> # 合并master分支和next分支到当前分支顶部
git rebase 把当前分支衍合到指定分支上:
* git rebase --continue # 如果有冲突需要先解决冲突,解决完冲突之后执行
* git rebase --abort # 放弃本次衍合操作
* git rebase --skip # 直接使用master分支取代此分支
git reset 回退版本,可以指定退回某一次提交的版本
* git reset HEAD^ #回退所有内容到上一个版本
* git reset HEAD^ hello.php #回退 hello.php 文件的版本到上一个版本
* git reset commit\_id #回退到指定版本
git revert 撤销某次操作,此次操作之前和之后的commit和history都会保留,并且把这次撤销,作为一次最新的提交
* git revert HEAD 撤销前一次 commit
* git revert HEAD^ 撤销前前一次 commit
* git revert commit\_id (比如:fa042ce57ebbe5bb9c8db709f719cec2c58ee7ff) 撤销某次commit
git checkout 切换到分支的head版本
* git checkout \<tag\_name> # 取出当前分支的tag\_name版本
* git checkout <master> \<file\_name.txt> # 放弃指定分支对file\_name的修改
* git checkout -b <next> # 在当前分支上创建新分支并将工作区设置为该分支上
git cherry pick 将指定的提交coomit应用于其他分支。
* git cherry-pick commit\_id #将指定的提交commit\_id ,应用于当前分支
* git cherry-pick <分支名> #将某分支的最近一次提交,转移到当前分支
* git cherry-pick commit\_ida commit\_idb #将 commit\_ida 和 commit\_idb 两个提交应用到当前分支
* git cherry-pick commit\_ida..commit\_idb #将 commit\_ida 到 commit\_idb 之间所有的提交应用到当前分支(commit\_ida必须早于commit\_idb,commit\_ida不会包含在Cherry pick中,需要包含a则使用git cherry-pick commit\_ida^..commit\_idb )
面试中遇到过的问题
1.git pull 和 git fetch 的理解?有什么区别?
可以概括为:从远程的分支获取最新的版本到本地的命令
举个🌰:
在多人协同工作时,我们每个人都是从远程仓库里拉了一份最新的代码到本地仓库,不可避免的情况是,同事小明在开发A功能,同事小涛在开发B功能。马上到6点了,打工的一天即将结束~~~这时小明已经把他的功能开发完并且通过git提交到了远程仓库里,这个时候小涛本地仓库里的代码就和远程长裤的不一样了,如果这个时候小涛的功能也开发完成,如果直接push到远程就会出现冲突的情况。所以我们在向远程推送代码之前,要先检查一下本地与远程的的差异,把远程最新的代码拉到本地,处理冲突之后再进行提交、推送操作。
此时就要用到git pull和git fetch。
git fetch:是从远程获取最新版本到本地,不会自动merge
在拉取代码过程中,git fetch会首先检查本地仓库和远程仓库的差异,检查哪些不存在于本地仓库,然后将这些变动的提交拉取到本地。
但是,这里请注意,它是把远程提交拉取到本地仓库,而不是本地工作目录,它不会自行将这些新数据合并到当前工作目录中,我们需要继续执行git merge才会把这些变动合并到当前工作目录。
git pull:从远程获取最新版本并merge到本地
git pull和git fetch刚好相反,它直接获取远程的最新提交,直接拉取并合并到本地工作目录,而且在合并过程中不会经过我们的审查,如果不仔细检查,这样很容易遇到冲突。
图解:
这样看起来git fetch会更加安全的选择,因为git fetch仅仅把远程的新代码拉下来,不会对本地做修改,这样我们就可以在合并之前检查文件的变化。
不过我自己在工作中用命令行工具比较多,也挺好用的。
用法:
git fetch
git fetch <远程主机名> <远程分支名>:<本地分支名>
举个🌰:
git fetch origin master:wt/test #从远程的origin仓库的master分支下载代码到本地并新建一个wt/test 分支\
git fetch origin master #将远程origin仓库的master分支拉取下来到本地当前分支
git fetch不会进行合并,执行后需要手动执行git merge合并
git merge wt/test
git pull
git pull <远程主机名> <远程分支名>:<本地分支名>
举个🌰:
git pull origin master:wt/test #将远程主机origin的master分支拉取过来,与本地的branchtest分支合并
git pull origin master #将远程origin仓库的master分支拉取下来到本地当前分支
2.git reset 和 git revert 的理解?区别?
可以概括为:对远程代码进行恢复操作
举个🌰:
小涛在工作中粗心大意,提交了错误代码,需要撤销错误代码
小涛在工作中收到pd的A、B需求,开发完成之后,pd说A需求暂时不发布,小涛马不停蹄的撤。。
小涛在工作中认真开发,并且经过测试没问题,就把代码push到了远程,此时远程居然出现了问题,赶紧撤。。
总而言之,由于自己或者其他人代码提交污染了远程分支,就需要对远程代码进行恢复回退,一般就会用到 reset 和 revert 两种命令
**⚠️:当我们进行了错误的提交,但是还没有push到远程分支,想要撤销本次提交,建议使用git reset 命令
**如果commit后push到远程分支上了,建议用git revert
git reset:用于回退版本,可以遗弃不再使用的提交
修改HEAD的位置,即将HEAD指向的位置改变为之前存在的某个版本,适用场景: 如果想恢复到之前某个提交的版本,且那个版本之后提交的版本我们都不要了,就可以用这种方法。
git revert:在当前提交后面,新增一次提交,抵消掉上一次提交导致的所有变化,不会改变过去的历史,主要是用于安全地取消过去发布的提交
图解:
用法:
git reset
* git reset -mixed(默认) #取消了commit ,取消了add
* git reset -soft #取消了commit
* git reset -hard #取消了commit ,取消了add,取消源文件修改
* git reset –hard HEAD\~3 #会将最新的3次提交全部重置,就像没有提交过一样。\
git reset –hard commit\_id #回退到 commit\_id版本\
git push origin HEAD --force #推送到远程仓库
* git reset HEAD <file> # 取消暂存文件
git revert
* git revert HEAD #撤销前一次 commit
* git revert HEAD^ #撤销前前一次 commit
* git revert commit\_id #撤销某次commit
* git revert -e commit\_id #用于在还原提交之前编辑提交信息(默认选项)。
* git revert -n commit\_id #不会打开文本编辑器,直接恢复上次的提交(不会自动提交,需要手动提交)
3.对git rebase 和 git merge的理解?区别?(git cherry pick)
可以概括为:整合来自不同分支的修改(合并代码)
git rebase(变基)
rebase 命令将提交到某一分支上的所有修改都移至另一分支上,就好像“重新播放”一样。
主要的好处是历史记录更加清晰,是在原有提交的基础上将差异内容反映进去,消除了 git merge所需的不必要的合并提交
官ba:当执行rebase操作时,git会从两个分支的共同祖先开始提取待变基分支上的修改,然后将待变基分支指向基分支的最新提交,最后将刚才提取的修改应用到基分支的最新提交的后面。
当我们在master (待变基分支)上执行git rebase test(基分支)时,git就会从两者的共同祖先B开始,提取master分支上的修改,也就是C,D两个commit,提取到之后git会先保存起来,然后将master分支指向test分支最新提交的节点,也就是F节点,然后把提取到的C,D接到F后面,在这个过程当中,会删除原来的C,Dcommit记录,生成新的C‘,D',虽然C',D'和原来的C,Dcoommit的内容是一样的,但是commit id是不同的。
rebase操作如果用一句话进行解释就是改变基底。master分支原来的基底是A,现在变成了以test分支最新的提交F做为新的基底了
git merge
通过merge合并分支会新增一个merge commit,然后将两个分支的历史联系起来
其实是一种非破坏性的操作,对现有分支不会以任何方式被更改,但是会导致历史记录相对复杂\
git cherry pick
将指定的提交coomit应用于其他分支。
🌰使用场景:
使用规则:
什么情况下适用于merge?
执行merge操作,主要是为了将我们当前的branch提交居前。你需要问一个问题:“这个待merge的其他分支为什么存在或者建立的?”
-
本地/临时的分支
如果这个分支只是为了master提交更简洁而单独拉出来的分支
1)假设现在master分支有新的commit,那这个临时分支为了保持更新,需要执行rebase使得提交在最新的master之上。假设
2)假设master分支没有产生新的commit,那么执行merge ff足以。 -
重要的分支
重要的分支一般是团队认定的分支,敏捷开发中一般依据的Sprint/Story/Feature来定义。而在bug追溯系统中一般用issue来定义。
如果此时master分支自从branch out这个新分支时,并未有新的提交,那么直接请使用merge而不是rebase,并且使用 --no-ff(该分支中间的commits在master不可见,从而保持master简洁)
什么情况下适用于rebase?
顾名思义,rebase=改变branch的base。
- 当准备push到远程分支时,如果已经有别的成员push了新的提交,系统会拒绝你的push请求。此时如果执行pull --> merge,将会导致提交历史不够简洁。这种情况就适合rebase来实现。
- 还有一种情况,当有一个新的idea,从而建立一个新分支,但是过了很久以后,才有时间继续进行。这时候,master分支已经有了长足进步,可能fix了一些bug,也可能有其他的改进,如果你想让你的idea基于最新的成果,那么rebase也是不二之选。
- 最后一种非常常用的,不过目的时为了让历史commit更加简洁,在开发过程中,可能会在分支中commit非常频繁,比如为了修改一个bug,可能会commit多次,这将会导致提交的历史不够简洁,因此在把上述push到远程分支的时候,就可以使用rebase来合并多次commit为1次。
git rebase -i HEAD~x #x代表合并之前的几个commit
4.开发中你是怎么处理冲突的?
1.进行 add、commit的操作
2.git pull origin 迭代分支名
2.1 有冲突-需要解决冲突(原则:自己没改的,保留传入的更改;自己更改过的,看是否有其他同事也更改过,沟通之后进行对应的保留)->开页面测试->确认无误继续提交
2.2 无冲突->开页面测试->确认无误继续提交
3.git push