不太熟悉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的历史变动,各种基础以及高级操作和相关命令,以及他们的原理,相信大家能够有所收获。

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

相关推荐
Victor35615 分钟前
Netty(20)如何实现基于Netty的WebSocket服务器?
后端
缘不易15 分钟前
Springboot 整合JustAuth实现gitee授权登录
spring boot·后端·gitee
Kiri霧21 分钟前
Range循环和切片
前端·后端·学习·golang
WizLC24 分钟前
【Java】各种IO流知识详解
java·开发语言·后端·spring·intellij idea
Victor35631 分钟前
Netty(19)Netty的性能优化手段有哪些?
后端
爬山算法37 分钟前
Netty(15)Netty的线程模型是什么?它有哪些线程池类型?
java·后端
白宇横流学长1 小时前
基于SpringBoot实现的冬奥会科普平台设计与实现【源码+文档】
java·spring boot·后端
Python编程学习圈2 小时前
Asciinema - 终端日志记录神器,开发者的福音
后端
bing.shao2 小时前
Golang 高并发秒杀系统踩坑
开发语言·后端·golang
壹方秘境2 小时前
一款方便Java开发者在IDEA中抓包分析调试接口的插件
后端