一.Git核心基础

1.1.Git指南

设置你的用户名和邮件地址

  • 每一个 Git 提交都会使用这些信息,它们会写入到你的每一次提交中,不可更改

  • 如果使用了 --global 选项,那么该命令只需要运行一次,因为之后无论你在该系统上做任何事情, Git 都会使用这些信息

1
2
git config --global user.name "coderxst"
git config --global user.email "coderxst@qq.com"

查看配置信息

1
git config --list

Git别名

1
2
git config --global alias.co checkout
git config --global alias.br branch

获取远程仓库

  • 方式一:初始化一个Git仓库,并且可以将当前项目的文件都添加到Git仓库中(目前很多的脚手架在创建项目时都会默认创建一个Git仓库)
1
git init
  • 方式二:从Git远程仓库,从其它服务器克隆(clone) 一个已存在的 Git 仓库(第一天到公司通常我们需要做这个操作)
1
git clone 远程仓库地址

1.2.Git工作流程

1.2.1文件状态划分

文件来划分不同的状态,以确定这个文件是否已经归于Git仓库的管理

  • 未跟踪(untracked):默认情况下,Git仓库下的文件也没有添加到Git仓库管理中,我们需要通过add命令来操作
  • 已跟踪(tracked):添加到Git仓库管理的文件处于已跟踪状态,Git可以对其进行各种跟踪管理

已跟踪的文件可以进行细分状态划分

  • staged:暂缓区中的文件状态
  • Unmodified:commit命令,可以将staged中文件提交到Git仓库
  • Modified:修改了某个文件后,会处于Modified状态

实际操作中,可以选择性地将这些修改过的文件放入暂存区,然后提交所有已暂存的修改,如此反复

image-20230225174613711

1.2.2.Git操作流程

QQ截图20230225182100

1.3.Git基本操作

1.3.1.检测文件状态

基本命令

1
git status

Untracked files:未跟踪的文件

  • 未跟踪的文件意味着 Git 在之前的提交中没有这些文件

  • Git 不会自动将之纳入跟踪范围,除非告诉它“需要跟踪该文件”

1.3.2.文件添加暂存区

跟踪文件命令

1
git add .

通过git add . 将所有的文件添加到暂存区中

如果已经跟踪了某一个文件,此时又修改了文件也需要重新添加到暂存区中

1.3.3.文件更新提交

现在的暂存区已经准备就绪,可以提交了

  • 每次准备提交前,先用 git status 看下,你所需要的文件是不是都已暂存起来了
  • 再运行提交命令 git commit
  • 可以在 commit 命令后添加 -m 选项,将提交信息与命令放在同一行
1
git commit -m "submit message"

如果我们修改文件的add操作,加上commit的操作有点繁琐,那么可以将两个命令结合来使用

1
git commit -a –m "submit message"

1.3.4.Git忽略文件

一般会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表

  • 一般是一些自动生成的文件,比如日志文件,或者编译过程中创建 的临时文件等
  • 可以创建一个名为 .gitignore 的文件,列出要忽略的文件的模式
  • 在实际开发中,这个文件通常不需要手动创建,在必须的时候添加自己的忽略内容即可

1.3.5.查看提交的历史

在提交了若干更新,又或者克隆了某个项目之后,查看一下所有的历史提交记录

  • 不传入任何参数的默认情况下,git log 会按时间先后顺序列出所有的提交,最近的更新排在最上面
  • 这个命令会列出每个提交的 SHA-1 校验和、作者的名字和电子邮件地址、提交时间以及提交说明
1
2
3
git log
git log --pretty=oneline
git log --pretty=oneline --graph

QQ截图20230225193907

1.3.6.版本回退

如果想要进行版本回退,我们需要先知道目前处于哪一个版本:Git通过HEAD指针记录当前版本

  • HEAD 是当前分支引用的指针,它总是指向该分支上的最后一次提交
  • 理解 HEAD 的最简方式,就是将它看做 该分支上的最后一次提交的快照

QQ截图20230225194449

我们可以通过HEAD来改变Git目前的版本指向

  • 上一个版本就是HEAD^,上上一个版本就是HEAD^^
  • 如果是上1000个版本,我们可以使用HEAD~1000
  • 可以指定某一个commit id
1
2
3
git reset --hard HEAD^ 
git reset --hard HEAD~100
git reset --hard 2d44982

1.3.7.Git的校验和

Git 中所有的数据在存储前都计算校验和,然后以校验和来引用

  • Git 用以计算校验和的机制叫做 SHA-1 散列(hash,哈希)
  • 由 40 个十六进制字符(0-9 和 a-f)组成的字符串,基于 Git 中文件的内容或目录结构计算出来

QQ截图20230225194856

1.4.远程仓库

1.4.1.入门指南

远程仓库(Remote Repository):在实际开发中通常是多人开发的,所以会将管理的代码共享到远程仓库中

  • 远程仓库通常是搭建在某一个服务器上的(本地也可以,但是本地很难共享)
  • 需要在Git服务器上搭建一个远程仓库
  • 目前有如下方式可以使用Git服务器
    • 使用第三方的Git服务器:比如GitHub、Gitee、Gitlab等等
    • 在自己服务器搭建一个Git服务

QQ截图20230225195653

1.4.2.远程仓库的验证

常见的远程仓库

服务器验证手段

  • 方式一:基于HTTP的凭证存储(Credential Storage)
  • 方式二:基于SSH的密钥

1.4.2.1.HTTP凭证存储

因为本身HTTP协议是无状态的连接,所以每一个连接都需要用户名和密码

  • 如果每次都这样操作,那么会非常麻烦
  • Git 拥有一个凭证系统来处理这个事情

一些Git Crediential 的选项

  • 选项一:默认所有都不缓存。 每一次连接都会询问你的用户名和密码
  • 选项二:“cache” 模式会将凭证存放在内存中一段时间。 密码永远不会被存储在磁盘中,并且在15分钟后从内存中清除
  • 选项三:“store” 模式会将凭证用明文的形式存放在磁盘中,并且永不过期
  • 选项四:如果你使用的是 Mac,Git 还有一种 “osxkeychain” 模式,它会将凭证缓存到你系统用户的钥匙串中(加密的)
  • 选项五:如果你使用的是 Windows,你可以安装一个叫做 “Git Credential Manager for Windows” 的辅助工具, 可以在 https://github.com/Microsoft/Git-Credential-Manager-for-Windows 下载(安装Git时默认以安装)
  • window查看凭据位置:控制面板 -> 用户账户 ->凭证管理器

1.4.2.2.SSH密钥

Secure Shell(安全外壳协议,简称SSH)是一种加密的网络传输协议,可在不安全的网络中为网络服务提供安全的传输环境

SSH以非对称加密实现身份验证

  • 例如其中一种方法是使用自动生成的公钥-私钥对来简单地加密网络连接,随后使用密码认证进行登录
  • 另一种方法是人工生成一对公钥和私钥,通过生成的密钥进行认证,这样就可以在不输入密码的情况下登录
  • 公钥需要放在待访问的电脑之中,而对应的私钥需要由用户自行保管
1
2
ssh-keygen -t ed25519 -C “your email"
ssh-keygen -t rsa -b 2048 -C “your email"

QQ截图20230225211433 拷贝

1.4.3.管理远程仓库

查看远程地址:比如从GitHub上clone下来的代码,它就是有自己的远程仓库的

1
2
git remote
git remote –v # -v是—verbose的缩写(冗长的)

QQ截图20230225212619

添加远程地址:也可以继续添加远程服务器(让本地的仓库和远程服务器仓库建立连接)重命名远程地址

1
2
git remote add <shortname> <url>
git remote add gitlab http://152.136.185.210:7888/coderwhy/gitremotedemo.git

QQ截图20230225212907

重命名远程地址

1
git remote rename gitlab glab

移除远程地址

1
git remote remove gitlab

1.4.4.远程仓库的交互

从远程仓库clone代码:将存储库克隆到新创建的目录中

1
git clone http://152.136.185.210:7888/coderwhy/gitremotedemo.git 

将代码push到远程仓库:将本地仓库的代码推送到远程仓库中

  • 默认情况下是将当前分支(比如master)push到origin远程仓库的
1
2
git push
git push origin master

从远程仓库fetch代码:从远程仓库获取最新的代码

  • 默认情况下是从origin中获取代码
1
2
git fetch
git fetch origin
  • 获取到代码后默认并没有合并到本地仓库,我们需要通过merge来合并
1
git merge

从远程仓库pull代码:上面的两次操作有点繁琐,我们可以通过一个命令来操作远程仓库的交互

1
2
3
# 这两个等价
git pull
git fetch + git merge(rebase)

二.Git进阶

2.1.Git标签(tag)

对于重大的版本常常会打上一个标签,以表示它的重要性

  • Git 可以给仓库历史中的某一个提交打上标签
  • 比较有代表性的是会使用这个功能来标记发布结点(v1.0、v2.0 等等)

创建标签

  • Git 支持两种标签:轻量标签(lightweight)与附注标签(annotated)
  • 附注标签:通过-a选项,并且通过-m添加额外信息
1
2
git tag v1.0
git tag -a v1.1 -m "附注标签"

推送标签

  • 默认情况下,git push 命令并不会传送标签到远程仓库服务器上
  • 在创建完标签后必须显式地推送标签到共享服务器上,当其他人从仓库中克隆或拉取,也能得到那些标签
1
2
git push origin v1.0	# 推送指定标签
git push origin --tags # 推送所有标签

删除本地标签

1
2
git tag -d <tagname>
git tag -d v1.0

删除远程标签

1
2
git push <remote> –delete <tagname>
git push origin -d v1.1

检出标签

  • 如果想查看某个标签所指向的文件版本,可以使用 git checkout 命令
  • 通常在检出tag的时候还会创建一个对应的分支(分支后续了解)
1
git checkout v1.0

2.2.Git提交对象

在进行提交操作时,Git 会保存一个提交对象(commit object)

  • 该提交对象会包含一个指向暂存内容快照的指针
  • 该提交对象还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针
    • 首次提交产生的提交对象没有父对象,普通提交操作产生的提交对象有一个父对象
    • 而由多个分支合并产生的提交对象有多个父对象

QQ截图20230226101911

2.3.Git分支

2.3.1.master分支

Git 的分支,其实本质上仅仅是指向提交对象的可变指针

  • Git 的默认分支名字是 master,在多次提交操作之后,其实已经有一个指向最后那个提交对象的 master 分支
  • master 分支会在每次提交时自动移动

Git 的 master 分支并不是一个特殊分支

  • 它就跟其它分支完全没有区别
  • 之所以几乎每一个仓库都有 master 分支,是因为 git init 命令默认创建它,并且大多数人没有去改动它

QQ截图20230226102840

2.3.2.分支的创建和切换

创建分支:本质就是它为你创建了一个可以移动的新的指针

1
git branch testing

QQ截图20230226103348

切换分支:本质就是把HEAD指针指向所切换的分支

1
git checkout testing

QQ截图20230226103812

创建分支并同时切换

  • 通常我们会在创建一个新分支后立即切换过去
  • 这可以用 一条命令搞定
1
git checkout -b <newbranchname>

2.3.3.使用分支的目的

列举一个简单的分支新建与分支合并的例子,实际工作中可能会用到类似的工作流

  • 开发某个项目,在默认分支master上进行开发
  • 实现项目的功能需求,不断提交
  • 并且在一个大的版本完成时,发布版本,打上一个tag v1.0.0

继续开发后续的新功能,正在此时,突然接到反馈说有个很严重的问题需要紧急修补, 将按照如下方式来处理

  • 切换到tag v1.0.0的版本,并且创建一个分支hotfix

想要新建一个分支并同时切换到那个分支上,可以运行一个带有 -b 参数的 git checkout 命令

  • git checkout –b hotfix

QQ截图20230226104754

2.3.4.分支开发和合并

分支上开发、修复bug

  • 我们可以在创建的hotfix分支上继续开发工作或者修复bug
  • 当完成要做的工作后,重新打上一个新的tag v1.0.1

切换回master分支,但是这个时候master分支也需要修复刚刚的bug

  • 所以我们需要将master分支和hotfix分支进行合并
1
2
git checkout master
git merge hotfix

QQ截图20230226105642

2.3.5.查看和删除分支

查看当前所有的分支,可以通过以下命令

1
2
3
4
git branch	# 查看当前所有的分支 
git branch –v # 同时查看最后一次提交
git branch --merged # 查看所有合并到当前分支的分支
git branch --no-merged # 查看所有没有合并到当前分支的分支

如果某些已经合并的分支我们不再需要了,那么可以将其移除掉

1
2
git branch –d hotfix	# 删除当前分支 
git branch –D hotfix # 强制删除某一个分支

2.3.6.Git工作流

由于Git上分支的使用的便捷性,产生了很多Git的工作流(Git flow)

  • 在整个项目开发周期的不同阶段,可以同时拥有多个开放的分支
  • 可以定期地把某些主题分支合并入其他分支中

比如以下的工作流

  • master作为主分支
  • develop作为开发分支,并且有稳定版本时,合并到master分支中
  • topic作为某一个主题或者功能或者特性的分支进行开发,开发完成后合并到develop分支中

QQ截图20230226110657

比较常见的git flow

QQ截图20230226110744

2.3.7.Git远程分支

2.3.7.1.入门指南

远程分支是也是一种分支结构

  • /的形式命名的

  • 把远程仓库clone下来代码,分支的结构如下

  • 其他人修改了代码,那么远程分支结构如下

    • 需要通过fetch来获取最新的远程分支提交信息

QQ截图20230226111243

2.3.7.2.远程分支的管理

操作一:推送分支到远程

  • 当你想要公开分享一个分支时,需要将其推送到有写入权限的远程仓库

  • 运行 git push <remote> <branch>

1
git push origin master

操作二:跟踪远程分支

  • 当克隆一个仓库时,它通常会自动地创建一个跟踪 origin/master 的 master 分支
  • 也可以设置其他的跟踪分支,可以通过运行 git checkout --track <remote>/<branch>
  • 如果你尝试检出的分支(a) 不存在且 (b) 刚好只有一个名字与之匹配的远程分支,那么 Git 就会为你创建一个跟踪分支
1
2
git checkout --track <remote>/<branch>
git checkout <branch>

操作三:删除远程分支

  • 如果某一个远程分支不再使用,我们想要删除掉,可以运行带有 --delete 选项的 git push 命令来删除一个远程分支
1
git push origin --delete <branch>

2.3.8.rebase

2.3.8.1.入门指南

在 Git 中整合来自不同分支的修改主要有两种方法:merge 以及 rebase

QQ截图20230226112910 拷贝

  • 在上面的图例中,可以提取在 C4 中引入的补丁和修改,然后在 C3 的基础上应用一次
  • 在 Git 中,这种操作就叫做 变基(rebase)
  • 可以使用 rebase 命令将提交到某一分支上的所有修改都移至另一分支上,就好像“重新播放”一样
  • 对于rebase的理解
    • 可以将其理解成改变当前分支的base
    • 比如在分支experiment上执行rebase master,那么可以改变experiment的base为maste
1
2
git checkout experiment
git rebase master

2.3.8.2.rebase原理

工作原理

  • 首先找到这两个分支(即当前分支 experiment、变基操作的目标基底分支 master) 的最近共同祖先 C2
  • 然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件
  • 然后将当前分支指向目标基底 C3
  • 最后以此将之前另存为临时文件的修改依序应用

再次执行master上的合并操作

1
2
git checkout master
git merge experiment

QQ截图20230226113726

2.3.9.merge与rebase对比

rebase和merge是对Git历史的不同处理方法

  • merge用于记录git的所有历史,那么分支的历史错综复杂,也全部记录下来

  • rebase用于简化历史记录,将两个分支的历史简化,整个历史更加简洁

  • 依据rebase的底层原理,就可以根据自己的特定场景选择merge或者rebase

注意:rebase有一条黄金法则:永远不要在主分支上使用rebase

  • 如果在main上面使用rebase,会造成大量的提交历史在main分支中不同
  • 而多人开发时,其他人依然在原来的main中,对于提交历史来说会有很大的变化

QQ截图20230226114325

2.4.遇到的问题

2.4.1.本地分支的上游分支(跟踪分支)

问题:当前分支没有track的分支

QQ截图20230226114652

原因:当前分支没有和远程的origin/master分支进行跟踪

  • 在没有跟踪的情况下,我们直接执行pull操作的时候必须指定从哪一个远程仓库中的哪一个分支中获取内容

QQ截图20230226114807

如果想要直接执行git fetch是有一个前提的:必须给当前分支设置一个跟踪分支

QQ截图20230226114908

2.4.2.拒绝合并不相干的历史

问题:合并远程分支时,拒绝合并不相干的历史

QQ截图20230226115816

原因:我们将两个不相干的分支进行了合并

出处:Git refusing to merge unrelated histories on rebase - Stack Overflow

image-20230226120214270

简单来说就是:过去git merge允许将两个没有共同基础的分支进行合并,这导致了一个后果:新创建的项目可能被一个毫不 怀疑的维护者合并了很多没有必要的历史,到一个已经存在的项目中,目前这个命令已经被纠正,但是我们依然可以通过-- allow-unrelated-histories选项来逃逸这个限制,来合并两个独立的项目

image-20230226121010127

2.4.3.合并冲突(conflict)

上面我们通过pull从Git远程仓库获取到分支内容后会自动进行合并(merge)

但是并非所有的情况都可以正常的合并,某些情况下合并会出现冲突

image-20230226121208644

一般在vscode中点击上面的选项,选择最合适的;也可以根据需求保留所需求的内容

2.4.4.本地分支名与远程分支名不一致

  • git branch --set-upstream-to=origin/master 设置上游分支
  • git config push.default upstream 为解决本地的分支名与远程的分支名不一致的问题,设置push的默认对象为upstream

2.5.常见开源协议

image-20230226121342725

三.应用

3.1.指南

情况一:已经有项目,并且有远程仓库

  • git clone xxxxxx
  • 进行开发
    • git add .
    • git commit -m "提交信息"
    • git pull -> git fetch + git merge
    • git push

情况二:开发一个全新项目(由自己来搭建)

  • 1.创建一个远程仓库
  • 2.1.方案一
    • git clone xxxxxx
    • 在clone下来的文件夹中开始搭建整个项目
    • git add.
    • git commit -m “提交的信息”
    • git push
  • 2.2.方案二
    • 创建一个本地仓库和搭建本地项目
    • git remote add origin xxxxxx
    • git fetch
    • git branch --set-upstream-to=origin/master 设置上游分支
    • git merge --allow-unrelated-histories 强制对两个没有共同基础的分支进行合并
    • git config push.default upstream 为解决本地的分支名与远程的分支名不一致的问题,设置push的默认对象为upstream
    • git push

3.2.常见Git命令

image-20230226151028435

声明:以上内容根据coderwhy老师的上课内容编写,部分内容引用ppt笔记。