不太熟悉Git? 不妨看看这篇文章

前言

Git是目前世界上最先进的分布式版本控制系统,由C语言进行开发

在2022年之前,Linux构建的方式是世界各地的志愿者把源代码文件通过diff的方式发送给Linus,然后由Linus本人通过手工方式合并代码

Linus痛恨的CVS和SVN都是集中式的版本控制系统,而Git是分布式的版本控制系统,这两者有何区别?

集中式:版本库是集中存放在中央服务器的,开发的时候用的都是开发机(本地机器),所以要先从中央服务器获取最新的版本,然后进行开发之后将代码推送给中央服务器。最大的问题是必须联网才能够进行工作,在网速慢的时候,提交大文件就会非常耗时。

分布式:分布式控制版本是没有"中央服务器"这个概念的,每个人的电脑上都是一个完整的版本库,这样在开发的时候就不需要联网,因为版本库在自己的电脑上。既然每个人的电脑上都有一个完整的版本库,那多个人如何协作呢?这时候就需要将各自对文件的修改推送给对方,就可以互相看到对方的修改了。和集中式版本控制系统相比,分布式版本控制的安全性会高很多。因为每个人的电脑里都有完整的版本库,某个人的版本库出问题了没有关系,而集中式版本控制只有一个版本库。通常分布式版本控制系统也有一台充当"中央服务器"的电脑,当然这个服务器的作用只是方便"交换修改",没有它也一样能够干活,只是交换修改不方便。

Git与svn的区别

  1. 历史记录:Git更加轻量级,每次提交只记录变化,而SVN每次提交都会存储完整的文件
  2. 版本管理:Git更加灵活,允许分支和分支合并,而SVN只有主干
  3. 安全性:Git分布式存储,一个服务器挂掉不会影响其他服务器,而SVN单一服务器容易出现安全问题
  4. 开发流程:Git的开发流程更加快捷,可以快速的实现拉取、提交,而SVN开发流程繁琐
  5. 部署:Git无需安全客户端,支持跨平台,而SVN必须安装客户端才能使用
  6. 使用:Git更加简单,学习成本更低,而SVN略显复杂

基本操作

创建版本库

版本库:仓库repository,可以简单理解为一个目录,这个目录中的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能够进行跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以"还原"。

创建一个版本库很简单

//1.创建目录

mkdir learngit

cd learngit

pwd

//2.通过git init将这个目录变成Git可以管理的仓库

执行完git init命令之后,当前目录会多出一个.git目录

这个目录是git用来跟踪版本库的,默认是一个隐藏文件

//3. 将文件放到Git仓库中

vim readme.txt //随便写点什么

git add readme.txt //将readme.txt文件添加到仓库

git commit -m "this is a readme.txt"

为什么add和commit要分成两步呢?因为一次commit可以提交很多文件,而可以多次add不同的文件

状态查看

现在我们将readme.txt文件修改为如下内容

Git is a distributed version control system.

Git is free software.

可以通过git status 来查看结果

git status 告诉我们readme.txt文件已经被修改了,但是还没有准备提交的修改

怎么查看到底该了什么内容呢?

可以通过git diff命令来查看difference,显示的格式正是Unix通用的Diff格式

通过git add readme.txt进行提交

再通过git status查看当前状态

这是告诉我们,被提交的修改有readme.txt文件

  • 如果想要查看工作区的状态,使用Git status命令
  • 如果git status告诉你有文件被修改过,用git diff可以查看修改内容

回退

我们再次修改readme文件

以下是新内容

Git is a distributed version control system. Git is free software distributed under the GPL.

之后尝试提交

像这样,不断对文件进行修改,然后不断提交修改到版本库里,就类似于把git的状态存盘,每当文件修改到一定程度的时候,就可以『保存一个快照』,这个快找在Git中被称为commit,一旦把文件弄乱了或者误删了文件,可以从最近的一个commmit中恢复
git log能够帮助我们看到git的历史记录

显示的是从最近到最远的提交日志,如果嫌输出信息太多,可以加上--pretty=online参数

在Git中,用HEAD来表示当前版本,上一个版本为HEAD^,上上一个版本为HEAD^^

我们可以通过git reset --hard HEAD^来将文件回退到上一个版本

在底层,Git在内部有一个指向当前版本的HEAD指针,当进行回退版本的时候,GIT仅仅是将HEAD的指向进行了改变

一个新的问题:如果这个时候我又想回到GPL的那个版本该怎么办呢?

可以看到这时候我们通过Git log进行日志查看,已经没有那个版本的记录了

我们可以通过git reflog 看到之前提交的id

通过git reset --hard参数+ID 进行回退

工作区与暂存区

Git与其他版本控制系统的一个不同之处就是有暂存区的概念
工作区

就是电脑里能够看到的目录,比如learngit文件夹
版本库

工作区有一个隐藏目录.git,这个不算是工作区,而是Git的版本库。Git的版本库里面有很多东西,其中最重要的就是称为stage(index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫做HEAD

我们将文件往Git版本库添加的时候,是分两步执行的

  • git add把文件添加进去,实际上就是把文件添加到暂存区
  • git commit提交更改,实际上就是把文件去的所有内容提交到当前分支

因为在创建GIT版本库的时候,GIT自动为我们创建了唯一一个分支master,所以git commit就是往master分支上提交更改

git commit之后

管理修改

每一次的修改都必须使用git add添加之后,才会放到暂存区,在git commit的时候才会提交到分支。如果修改了之后没有使用git add命令,那么是不会提交到分支中的

撤销修改

当我们将文件修改成以下内容,但是未提交的时候,git会进行以下提示

Git is free software distributed under the GPL. Git has a mutable index called stage. Git tracks changes of files. My stupid boss still prefers SVN.

可以使用git checkout -- readme.txt 进行把readme.txt文件的工作区的修改全部撤销

切记,一定要添加--标签,不然这个命令会变成切换到另一个分支

这里的撤销有两种情况

  • readme.txt自修改之后还没有被放到缓存区中,现在,撤销修改就回到和版本库一模一样的状态
  • readme.txt已经添加到暂存区之后,又做了修改,撤回修改就回到添加到暂存区之后的状态

当然 现在推荐的命令是 git restore readme.txt

当已经把修改通过git add命令添加到暂存区之后,想要丢弃修改,分成两步

  • git reset HEAD
  • git checkout --file

删除文件

当我们在工作区下添加一个文件

vim test.txt

内容如下:

hello world.

然后通过git add test.txt 与 git commit -m "add test.txt"进行提交

这个时候我们把test.txt文件进行删除

再通过git status查看会发现

现在我们有两种选择

1.确定删除 使用git rm test.txt,然后git commit -m "remove test.txt"

2.删错了 使用版本库里的版本替换工作区的版本 git checkout -- test.txt

远程仓库

添加远程仓库
git remote add origin git@xxx

git push //将本地库的内容推送到远程库上

-u 参数不但能够实现远程推送 还能够将本地分支与远端分支关联起来 之后的推送或者拉取就能够简化命令
git remote -v

查看远程仓库信息
git remote rm origin

删除远程仓库(解除本地和远程的绑定关系)
git clone克隆远端代码 ssh协议的速度>https

分支管理

HEAD是一个指针,指向的分支就是当前分支,在一开始的时候,master分支是一条线,Git使用master指向最新的提交,再用HEAD指向master,就能确定当前分支以及提交点

每次提交,master分支都会向前移动一步,这样随着不断提交,master分支的线也会越来越长

当我们创建了一个新的分支,其实也就是一个新的指针dev,指向的是master相同的提交,再把HEAD指向dev,表示当前分支在dev上

因为git的新建分支是创建一个指针以及修改HEAD的指向,而文件本身内容不变,所以速度很快

从现在开始,对工作区的修改和提交就是针对dev分支了,新提交之后dev指针向前移动,但是master指针不变

分支管理

git branch dev 创建dev分支

git checkout dev 切换到dev分支

添加一个内容为hello world的test文件

git add + git commit 提交成果

git checkout master 切换回主分支

git merge dev 将dev分支合并到当前分支

然后再通过git branch -d dev将dev分支进行删除

git推荐现在的分支操作使用switch命令
git switch -c 创建并切换到新分支
git switch master 创建到已有分支

冲突

git使用<<<<<<<,=======,>>>>>>>标记出不同分支的内容标记出不同的分支

更改冲突文件为想要的内容后提交

分支管理

在合并分支的时候,如果可能,git会使用fast forward模式,在这种模式下,删除分支之后就会丢掉分支信息

合并分支的时候,加上--no-ff参数就可以使用普通模式合并,合并后的历史有分支,能够看出来曾经做过合并
git merge --no-ff "merge dev" dev

bug

一般每个bug都会新建一个分支来修改,修复之后合并分支,然后将临时分支删除

git stash 将当前工作区进行存储

一是用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除;

另一种方式是用git stash pop,恢复的同时把stash内容也删了:

因此,多人协作的工作模式通常是这样:

  1. 首先,可以试图用git push origin 推送自己的修改;
  2. 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
  3. 如果合并有冲突,则解决冲突,并在本地提交;
  4. 没有冲突或者解决掉冲突后,再用git push origin 推送就能成功!

如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to origin/。

这就是多人协作的工作模式,一旦熟悉了,就非常简单。

标签

git tag 给commit打标签

git show 查看标签信息

git tag -a 查看所有标签

git tag -d 删除标签

git push origin 推送本地标签

github

1.fork一个仓库 然后clone仓库 因为没权限clone原仓库

2.开发 往自己的仓库推送

3.推送pr给原仓库

Git进阶操作

git pull

拉取成功但不更更新

这个因为本地有更改,和仓库对应不上,解决方式如下

1.git stash将本地修改存储起来

2.git pull //建议使用完整的git pull origin branchname

git pull内部执行原理

pull包含两个操作,fetch和merge

fetch: 将远程仓库拉取到本地仓库

merge:将本地仓库和分支进行merge

git pull的时候会向远端发送git-upload-pack请求,携带的是本地仓库commit的记录,如果一致则不需要拉取,不一样就将远端仓库拉下来

push之前pull的原因

git commit的时候,仓库并不会将本地和远程仓库代码进行比较,不会识别出代码是否存在冲突,必须进行pull命令之后,才会将本地代码和远程仓库的代码进行比较,如果二者的代码存在冲突,必须要解决冲突后重新commit push,如果不存在冲突,则pull的时候直接合并代码,不会将本地代码覆盖掉

git status中的untracked file

在Git中,未被跟踪的文件(untracked file)是指存在与Git管理的目录中,但是尚未被添加到Git版本控制中的文件。这些文件没有被Git追踪,因此它们不受Git版本控制的管理。

当使用git status命令查看git存储库的状态,一般日志文件就会是untracked file,因为没必要对日志进行追踪,日志也不会提交到代码库中

如果想把未被追踪的文件添加到Git版本控制中,可以使用git add命令将它们添加到Git的暂存区中,然后使用git commit提交到存储库中

git add提交了多余的文件,并且已经git commit了,怎么撤销

如果只是git add了,但是还没有git commit ,也就是说这些文件只是添加到了暂存区,还没有进行提交,那么可以通过git reset + filename,将文件从暂存区中删除,或者git reset直接将所有文件都从暂存区中撤销

第一步得撤销git commit

git revert sha值 撤销你的某一个提交

或者直接 git revert HEAD 撤销最近一次提交,并创建一个新的提交来记录这个撤销操作,这个操作可以用来修复一个错误的提交或者撤销一个不必要的提交

如果报错error:commit sha is a merge but no -m option was given

这是因为正在尝试通过git revert命令撤销一个合并提交,但是没有指定用于撤销的父提交。要解决这个问题,需要使用-m选项来指定父提交,该选项后面需要指定一个数字,标识用于撤销提交的父提交的编号

假设想撤销最近的一次合并提交,可以使用
git revert -m 1 HEAD

使用合并提交的第一个父提交来撤销该提交

git commit错分支了怎么办

1.git log命令查找刚刚提交的SHA值

2.git branch + git checkout 切换到你想提交的分支

3.git cherry-pick + sha 讲提交应用到当前分支

git revert后工作区代码消失

git reset --hard HEAD 该命令会将工作区和暂存区都重置为最新的提交,并清除所有未提交的修改。需要注意的是,这个命令会清除本地未提交的更改,因此在使用前请确认这些更改已经备份或者提交到了其他分支上。如果仍然无法恢复更改,可以使用git reflog命令查找之前的提交记录,使用git reset --hard +sha指向指定的提交

修改git commit提交信息

当我们cr被打回来的时候,就不需要重新git commit,而是直接git commit --amend修改之前的提交信息,这会被git认为是一次新的提交

1.git commit --amend 打开编辑器

2.修改提交信息

3.git push

查看git信息

1.git log 查看所有的提交历史记录 例如提交ID 作者 日期 提交说明等等,常用于查看Git仓库的历史提交记录以及对比和合并分支

2.git reflog 记录了Git仓库的每一次操作,包括分支和标签的创建、删除、移动等操作,这个命令可以用来找回已经删除的分支或者标签

git diff

1.git diff --cached 查看暂存区和最后一次提交之间的差异 也就是git add离上一次的更改

2.git diff filename 查看文件的更改

3.git diff 查看已经修改但是未暂存(没有add)的更改

工作区、暂存区、本地仓库、远程仓库

1.工作区:实际写代码的地方,电脑上的文件夹,里面放着我们的代码文件

2.暂存区:git提供的一个临时的存储取余,可以暂时存储我们修改过的文件,等待提交到本地仓库,对应的是某一个分支

3.本地仓库:存储代码版本历史记录的地方,可以看作是Git维护的一个数据库,存储了项目的所有历史版本

4.远程仓库:一个在网络上的Git仓库,通常由代码托管服务商提供,可以把本地仓库的代码推送到远程仓库中,也可以从远程仓库中拉取代码到本地仓库进行使用

基本工作流:通过git add将文件添加到暂存区,然后通过git commit提交到本地仓库,最后通过git push提交到远程仓库

为什么需要本地仓库:

  • 安全性:本地仓库可以在本地保存代码的历史版本,即使在远程仓库数据丢失或者被破坏的情况下,本地仓库中的代码仍然是安全的
  • 离线操作:没有网络的情况下,本地仓库允许开发人员继续对代码进行修改和提交
  • 提高效率:由于本地仓库不需要每次从远程仓库拉取代码,可以大大减少代码拉取的时间和网络带宽

git工作区修改后切换到新分支,保存修改

如果在工作区有未提交的修改,并且切换到新分支,那么这些修改是不会被保存的,因为一个暂存区对应的是一个分支

这个时候我们就需要

1.git stash //将工作区的修改保存到一个临时区

2.git checkout

3.git stash pop 或者git stash apply

4.如果有冲突的话,处理对应的文件

markdown 复制代码
<<<<<<< HEAD
// 这里是当前分支的修改内容
=======
// 这里是合并分支的修改内容
>>>>>>> merge-branch

二者区别

apply从stash中恢复最近的一次修改,但不会将这些修改移出

pop从stash中恢复最近的一次修改,同时将修改弹出

git merge和git rebase的区别

merge 是合并的意思

rebase 是复位基底的意思

推荐是使用git rebase 因为rebase的代码历史非常清晰

比如有一个master分支,同时6个人进行开发,需要创建六个单独的个人分支,然后使用merge的话就会有六个branch和主分支交织在一起,也就是master的commit历史是网状的.master是创建一个新的结点,然后将两个分支的历史联系在一起

而rebase会把提交移动到master的最前面,形成一条线

结语

本文泛谈Git的历史变动,各种基础以及高级操作和相关命令,以及他们的原理,相信大家能够有所收获。

创作不易,如果有收获欢迎点赞、评论、收藏,您的支持就是我最大的动力。

相关推荐
Q_19284999062 分钟前
基于Springcloud的智能社区服务系统
后端·spring·spring cloud
xiaocaibao7774 分钟前
Java语言的网络编程
开发语言·后端·golang
会说法语的猪1 小时前
springboot实现图片上传、下载功能
java·spring boot·后端
凡人的AI工具箱2 小时前
每天40分玩转Django:实操多语言博客
人工智能·后端·python·django·sqlite
Cachel wood2 小时前
Django REST framework (DRF)中的api_view和APIView权限控制
javascript·vue.js·后端·python·ui·django·前端框架
m0_748234082 小时前
Spring Boot教程之三十一:入门 Web
前端·spring boot·后端
想成为高手4992 小时前
国产之光--仓颉编程语言的实战案例分析
后端
编码浪子3 小时前
构建一个rust生产应用读书笔记7-确认邮件2
开发语言·后端·rust
昙鱼3 小时前
springboot创建web项目
java·前端·spring boot·后端·spring·maven
白宇横流学长3 小时前
基于SpringBoot的停车场管理系统设计与实现【源码+文档+部署讲解】
java·spring boot·后端