面经每日一总结 (九,git)
分布式和集中式版本控制工具的区别
集中式
svn, cvs
将代码集中到一台服务器管理控制。多人开发只需要连接到这台服务器,取出最新的文件或者提交更新。
但是如果这个服务器出现问题,就不能进行下载和更新了。
分布式
git
客户端并不只提取最新版本的文件快照, 而是把代码仓库完整的镜像下 来,包括完整的历史记录。
如果任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。
每一个用户的克隆都是对代码仓库的完整备份。 即每台克隆的电脑都有一个仓库存储着该项目所有的内容。
如何初始化一个仓库
git init
git clone
git管理下的两种状态
- 未跟踪:默认情况下,Git仓库下的文件也没有添加到Git仓库管理中,我们需要通过add命令来操作。
- 已跟踪:添加到Git仓库管理的文件处于已跟踪状态,Git可以对其进行各种跟踪管理。
已跟踪的文件又可以进行细分状态划分:
- staged:暂缓区中的文件状态。执行
git add
后的状态。 - Unmodified:commit命令,可以将staged中文件提交到Git仓库。
- Modified:修改了某个文件后,会处于Modified状态。
这三种状态是一个闭环。文件修改后执行git add
,状态将从Modified
变为staged
。
注意git commit -a -m ""
命令只能提交那些已经跟踪过一次的文件,新创建的文件不能被提交成功。
git校验和
Git 中所有的数据在存储前都计算校验和,然后以 校验和 来引用。
- Git 用以计算校验和的机制叫做 SHA-1 散列(hash,哈希)。
- 这是一个由 40 个十六进制字符组成的字符串,基于 Git 中文件的内容或目录结构计算出来。
git log 查看提交的历史
// 打印详细历史记录。空格查看下一个
git log
// 只包含校验和和描述信息的记录
git log --pretty=oneline
// 通过图来展示。(多分支)
git log --pretty=oneline --graph
git reset 版本回退
Git通过HEAD指针记录当前版本。
HEAD 是当前分支引用的指针,它总是指向该分支上的最后一次提交。就是将它看做 该分支上的最后一次提交 的快照。
我们可以通过HEAD来改变Git目前的版本指向:
- 上一个版本就是HEAD^,上上一个版本就是HEAD^^。
- 如果是上1000个版本,我们可以使用HEAD~1000。
- 我们可以可以指定某一个commit id(校验和)。
git reset --hard HEAD^
git reset --hard HEAD~1000
git reset --hard 2d44982
由于我们撤回了历史记录。那么git log
将不会保存该记录之前的历史提交记录。如果还想找回以前的提交记录,我们可以这样
git reflog
// 然后通过
git reset --hard commit id
远程仓库
查看远程地址:比如我们之前从GitHub上clone下来的代码,它就是有自己的远程仓库的:
git remote
git remote –v //-v是—verbose的缩写(冗长的)
添加远程地址:我们也可以继续添加远程服务器(让本地的仓库和远程服务器仓库建立连接):
// git remote add <shortname> <url>
git remote add origin(一般起名origin) 仓库地址
重命名远程地址:
// git remote rename 原仓库名称 现在仓库名称
git remote rename origin changeOrigin
移除远程地址:
// git remote remove 仓库名称
git remote remove origin
远程仓库的交互
从远程仓库clone代码:将存储库克隆到新创建的目录中。
git clone url
将代码push到远程仓库:将本地仓库的代码推送到远程仓库中
- 默认情况下是将当前分支push到origin远程仓库的。
git push
git push origin master
从远程仓库fetch代码:从远程仓库获取最新的代码
- 默认情况下是从origin中获取代码。
- 获取到代码后默认并没有合并到本地仓库,我们需要通过merge来合并。
git fetch
git fetch origin master
git merge
从远程仓库pull代码:上面的两次操作有点繁琐,我们可以通过一个命令来操作
git pull
git fetch + git merge(rebase)
本地分支的上游分支(跟踪分支)
在我们直接通过git pull
拉取并合并远程仓库代码时,如果没有跟踪两个分支,将会报错。
在没有跟踪的情况下,我们直接执行pull操作的时候必须指定从哪一个远程仓库中的哪一个分支中获取内容。
git pull origin master
如果我们想要直接执行git fetch是有一个前提的:必须给当前分支设置一个跟踪分支。可以通过上面那种方式解决,也可以通过下面这种方式解决
git fetch
报错。
设置完上游分支后,它将可以把代码获取到本地内存中(git fetch),但是不能进行合并(git merge),会出现
refusing to merge unrelated histories
拒绝合并不相干的历史
过去git merge允许将两个没有共同基础的分支进行合并,这导致了一个后果:新创建的项目可能被一个毫不 怀疑的维护者合并了很多没有必要的历史,到一个已经存在的项目中,目前这个命令已经被纠正,但是我们依然可以通过-- allow-unrelated-histories选项来逃逸这个限制,来合并两个独立的项目。
我们在合并两个不相干的分支(即没有共同祖先的分支)时,可以在后面跟上-- allow-unrelated-histories
来强制合并。
如果设置了上游分支,
git merge
不加任何分支,他表示和当前分支的上游分支进行合并。
上面这些问题只会在git init
初始化仓库提交的时候会遇到,通过git clone
克隆项目并提交时不会遇到。所以一般都是在远程仓库中常见一个新仓库,然后克隆下来进行开发。
git push完整写法
git push origin head:远程分支
修改git push的默认行为
git push报错就是因为git push的默认行为。
如果我们想要直接使用git push
推送代码到远程仓库,我们需要修改git push
的默认行为,因为git push
的默认行为是simple
。
- simple表示推到远程仓库和本地分支同名的分支上去,如果远程仓库没有的话,那么将会推送不成功。报错。
- nothing : 无默认操作,需要显示地指定远程分支。
- current表示提交到远程仓库中同名分支,如果没有则重新创建一个分支进行提交。
- upstream表示提交到设置的远程仓库上游分支中。
git branch --set-upstream-to=origin/master
- matching表示push所有本地和远程两端都存在的同名分支
在Git的2.0之前,push.default属性默认被设为'matching',2.0之后被改成为'simple'。
修改git push
默认行为
git config push.default <simple | current | upstream | nothing | matching>
如果我们没有设置当前分支的上游分支,则可以在首次push的时候,加上-u(或者是 --set-upstream)参数来push当前分支到远程仓库。
git push -u origin master
Git标签(一般不会使用)
对于重大的版本我们常常会打上一个标签,以表示它的重要性:
- Git 可以给仓库历史中的某一个提交打上标签。
- 比较有代表性的是人们会使用这个功能来标记发布结点( v1.0 、 v2.0 等等)。
创建标签
Git 支持两种标签。轻量标签(lightweight)与附注标签(annotated)。
附注标签:通过-a选项,并且通过-m添加额外信息。
git tag v1.0.0
git tag -a v1.1.1 -m "标签描述"
默认情况下,git push 命令并不会传送标签到远程仓库服务器上。 在创建完标签后你必须显式地推送标签到共享服务器上,当其他人从仓库中克隆或拉取,他们也能得到你的那些标签。
git push origin v1.1.1 // 推送指定标签
git push origin --tags // 推送本地全部标签
删除和检出tag
删除本地tag
git tag -d <tagname>
删除远程tag
git push <remote> –d <tagname>
// git push origin -d v1.1.1
检出tag
- 如果你想查看某个标签所指向的文件版本。
git checkout <tagname>
git checkout v.1.1.1
- 通常我们在检出tag的时候还会创建一个对应的分支。就是切换到该tag版本下,依据这个版本代码创建一个分支进行开发。
git 提交对象
当我们执行完git add
后,存储的文件对象。
以下命令默认会去.git/objects
文件夹下查找
// 查看存储文件对象的类型
git cat-file -t commtid(校验和)
// 查看存储文件的内容
git cat-file -p commtid(校验和)
当执行
git commit -m ""
命令后,.git/objects
文件夹下又多出了一些文件。
- 多出的文件是记录
git add
后校验和和具体文件的映射关系。 - 多出的文件是记录当前提交的用户信息和commit描述。
下面总结一下
在进行提交操作时,Git 会保存一个提交对象(commit object):
- 该提交对象会包含一个指向暂存内容快照的指针。
- 该提交对象还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针。
- 首次提交产生的提交对象没有父对象,普通提交操作产生的提交对象有一个父对象。
- 而由多个分支合并产生的提交对象有多个父对象。
当前分支就是指向最后一个的commit对象。即保存用户信息的文件。然后head指向当前分支。
git 分支
切换分支就是将head指向当前分支而已。
创建分支表示从当前分支包含的代码,克隆一个副本进行开发。
分支相关操作
查看本地和远程仓库的所有分支
git branch -a
查看远程所有分支
git branch -r
如果想要拉取其他分支,可以这样。
如果想要将远程分支与本地分支联系起来,则执行(以feature分支为例)
git checkout -b feature origin/feature
或者使用-t参数,它默认 在本地建立一个和远程分支名字一样的分支,并拉取该分支的所有代码。
git checkout -t origin/feature
// 或者
git checkout feature
查看所有合并到当前分支的分支
git branch --merged
查看所有没有合并到当前分支的分支
git branch --no-merged
删除当前分支,注意删除分支并不是删除提交历史记录,只是将该分支名称删除。
git branch –d 分支名称
强制删除某一个分支
git branch –D 分支名称
删除远程分支
git push origin -d 远程分支名称
git 工作流
常见仓库的两种情况分析
git rebase 和 git merge
git rebase, 就是重新设置当前分支的base。使用 rebase 命令将提交到某一分支上的所有修改都移至另一分支上, 让其提交记录成为线性结构,而非图结构。
事实上,rebase和merge是对Git历史的不同处理方法:
- merge用于记录git的所有历史,那么分支的历史错综复杂,也全部记录下来。
- rebase用于简化历史记录,将两个分支的历史简化,整个历史更加简洁。
注意:rebase有一条黄金法则:永远不要在主分支上使用rebase。
如果在main上面使用rebase,会造成大量的提交历史在main分支中不同(因为会在main中写大量的代码)。而多人开发时,其他人依然在原来的main中,对于提交历史来说会有很大的变化。
转载自:https://juejin.cn/post/7169950307848290341