Git教程
- [一、安装 Git](#一、安装 Git)
-
- [1. 用 AI 安装Git](#1. 用 AI 安装Git)
- [2. Windows 手动安装](#2. Windows 手动安装)
- [3. macOs 手动安装](#3. macOs 手动安装)
- 二、存档与回滚
-
- [1. 如何存档](#1. 如何存档)
- [2. 回滚的 3 种场景](#2. 回滚的 3 种场景)
-
- [2.1 回滚(改动只在工作区)](#2.1 回滚(改动只在工作区))
- [2.2 回滚(改动已进暂存区)](#2.2 回滚(改动已进暂存区))
- [2.3 回滚(改动已提交为 commit)](#2.3 回滚(改动已提交为 commit))
- [2.4 git reset 的三种模式](#2.4 git reset 的三种模式)
- [2.3 另外的回滚方式(改动已提交为 commit)](#2.3 另外的回滚方式(改动已提交为 commit))
- [3. 使用.gitignore忽略文件](#3. 使用.gitignore忽略文件)
- 三、分支与合并
-
- [1. 什么是分支](#1. 什么是分支)
- [2. 创建分支](#2. 创建分支)
- [3. 合并分支](#3. 合并分支)
- [4. 处理合并冲突](#4. 处理合并冲突)
- [5. 两次合并的区别](#5. 两次合并的区别)
- [6. Rebase](#6. Rebase)
-
- [6.1 什么是 Rebase](#6.1 什么是 Rebase)
- [6.2 Rebase 在项目中如何应用](#6.2 Rebase 在项目中如何应用)
-
- [6.2.1 倒回合并前](#6.2.1 倒回合并前)
- [6.2.2 git rebase](#6.2.2 git rebase)
- [6.2.3 合并 master](#6.2.3 合并 master)
- [6.3 总结](#6.3 总结)
- [7. Stash](#7. Stash)
-
- [7.1 Stash 的使用场景](#7.1 Stash 的使用场景)
- [7.2 模拟 git stash 的使用过程](#7.2 模拟 git stash 的使用过程)
- [8. Worktree](#8. Worktree)
-
- [8.1 Worktree 的使用场景](#8.1 Worktree 的使用场景)
- [8.2 模拟 Worktree 的使用过程](#8.2 模拟 Worktree 的使用过程)
- [8.3 总结](#8.3 总结)
- [9. GitHub 的使用场景](#9. GitHub 的使用场景)
-
- [9.1 Git 和 GitHub 的关系](#9.1 Git 和 GitHub 的关系)
- [9.2 配置SSH Key](#9.2 配置SSH Key)
- [9.3 创建 GitHub 仓库](#9.3 创建 GitHub 仓库)
- [9.4 上传代码到 GitHub](#9.4 上传代码到 GitHub)
- [9.5 从 GitHub 下载代码](#9.5 从 GitHub 下载代码)
一、安装 Git
1. 用 AI 安装Git

2. Windows 手动安装
打开 Git 下载网站,点击【Install for Windows 】 按钮,按照指示就可以下载了。


安装 Git 的方法和安装普通软件的方法差不多,我们一路点击 Next 就可以了。

3. macOs 手动安装
在 macOs 上安装 Git 有多种方式。 最简单的方法是安装Xcode Command Line Tools。 Mavericks (10.9) 或更高版本的系统中,在Terminal 里尝试首次运行 git 命令即可。
二、存档与回滚
1. 如何存档
我们以【马克代办 】文件夹为例,首先进入到【马克代办】文件夹:

- git init:把当前的目录初始化成一个 Git 仓库。
执行这个命令之后,当前项目就具备了用 Git 进行版本管理的能力。之后每完成一个功能,就可以把它保存成一个对应的版本,这样即使改坏了,我们也可以随时回滚到之前能够正常工作的状态。

- git status:查看当前 Git 仓库的状态。

我们先把index.html、index.js 和 style.css 保存成一个版本,在保存之前我们来看一下 Git 的原理:

- git add:添加一个或多个文件到暂存区。

执行成功,我们来看一下 Git 当前的状态:

在做 git commit 之前,我们还需要做一个准备工作,那就是设置一下自己的名字和邮箱:
bash
git config --global user.name "v_hluhe"
git config --global user.email "v_hluhe@tencent.com"
这两行命令只需要设置一次,以后在这台电脑上无论是继续操作这个项目,还是新建其他的项目,都不用重新设置。
- git commit -m '提交说明':提交暂存区到本地仓库。

- git log:用来查看 commit 历史。

在这里我们就完成了一次提交,简单来说就是先用git add命令把工作区 里面准备保存的内容记录到暂存区 ,再用git commit命令根据暂存区 里的内容在提交历史里面创建一个commit。
2. 回滚的 3 种场景

2.1 回滚(改动只在工作区)
- git diff:比较文件的不同,即工作区和暂存区的差异。

输入q,即可退出。
- git restore style.css:恢复或撤销工作区文件的更改。

- git restore .:恢复或撤销工作区当前目录下的所有更改。
2.2 回滚(改动已进暂存区)
分两步:
- git restore --staged style.css:恢复或撤销暂存区文件的更改。
- git restore style.css:恢复或撤销工作区文件的更改。


2.3 回滚(改动已提交为 commit)
我们先来布置一下环境,先把这个深色主题的改动提交一个commit,这样后面才好显示回滚:

- git reset --hard HEAD~1:回退到上一个版本(倒数第二个提交)。

HEAD 代表提交历史里面最新的 commit:

--hard :代表工作区 、暂存区 和提交历史 三个区域一起回滚,除了 --hard 还有一些其他的选项。
2.4 git reset 的三种模式
这三种模式的区别是在于回滚时所影响的区域不一样。

默认为 --mixed 模式,也就是说你如果什么都不写:git reset HEAD~1 相当于 git reset --mixed HEAD~1。
其实这并不是唯一的回滚方式,还有另外一种回滚方式,是通过增加一个回滚的 commit 来达到回滚的效果。
2.3 另外的回滚方式(改动已提交为 commit)
git revert 的核心作用是通过创建一个全新的提交,来撤销(抵消)某一次或某几次历史提交所引入的更改。

- git revert HEAD:撤销最后一次提交;

按:wq再回车就可以退出了。
我们看这里面就多了一个 commit,这个 commit 与前面那个 commit 的改动就是完全相反了:

而且这个 commit 不会把原来的 commit 从历史里面删掉,只是会新增一个新的 commit 把它给抵消掉,所以整个过程依然是完整保留的。
3. 使用.gitignore忽略文件
比如说,我的项目里有一个 note.txt 文件是用来记录我学习的进度的,这个文件和项目本身并没有其他的关联,所以我希望 Git 能够忽略它。


这个时候需要先创建一个.gitignore文件,添加规则是每一行写一个要忽略的文件名或路径:

可以看出来,我们的 vscode 文件夹和 notes.txt 已经被 Git 忽略了。

最后再把.gitignore文件提交上去就可以了。

三、分支与合并
1. 什么是分支
现在假设出现了一个新的需求,比如:我们要给【马克代办 】增加一个新的功能,支持用户给每一条代办设置截止时间。
然后再设想一下,如果我们需要分多次提交我们开发的新功能代码,直接在 master 分支上提交这些 commit ,会导致 master 分支上的代码出现半可用状态,这并不是我们想要的。

所以,对于这种需要分成好几个阶段才能够完成的新功能,我们需要先基于当前稳定的 master 分支创建一个新的分支,专门用来开发【截止时间】这个功能;接下来,与这个功能相关的几个 commit 都先提交到这个新的分支上。这样在我们开发新功能的整个过程中,master 分支都可以继续保持在原来稳定可用的状态。等到这个新功能全部开发完成,也测试通过了之后,再把这个新分支合回 master 。


2. 创建分支
- git branch feature/due-date:创建新分支xxx;
- git branch :列出所有分支;

注意:* 表示当前所在分支。 - git switch feature/due-date :切换到feature/due-date分支;

实际开发中,我们采用下面这个更方便的写法: - git switch -c feature/due-date :先创建分支,然后立刻切换过去(
-c代表create的缩写);

注意:git checkout -b feature/due-date 等同于 git switch -c feature/due-date,只不过是之前更老的写法。
3. 合并分支
在 feature/due-date 上提交好代码,我们就可以把【截止时间】这部分的代码合并到 master上了。
-
git switch master:切换到 master 分支;
-
git branch:确认是否切换成功;
-
git merge feature/due-date :将代码合并到 master 分支上;

-
git log --oneline :查看提交记录(并且让每个 commit 只占一行);

-
git branch -d feature/due-date :删除分支(
-d代表delete的缩写);
4. 处理合并冲突
现在我们手上有两个新功能要做,一个是【排序 】,一个是【全部完成 】,按照之前的规矩是一个功能一个分支,那我们就从当前这个 master 上面拉出两个分支,分别是:负责【排序 】的分支------feature/sort 和 负责【全部完成 】的分支------feature/complete-all。

分别在两个分支上进行开发和代码提交后,我们现在需要把这两个分支的代码依次合并回 master 上。
先切回 master,然后合并 feature/sort 分支,【排序】功能已经顺利地合并进来了。

下面我们再合并【全部完成】功能,这次出问题了,我们的代码有了合并冲突:

解决冲突的方法:
- 手动修改冲突代码内容;
- 将这次合并提交 (执行
git add和git commit);
冲突代码修改前:

冲突代码修改后:

将合并提交:

5. 两次合并的区别
第一次合并是 Fast-forward (快进),第二次合并 Git 需要新造一个 commit。


6. Rebase
6.1 什么是 Rebase
在真实的开发过程中,一个功能往往要做1~2周,总共可能要提交几十个 commit ,在开发新功能的过程中,原来的 master 也在不停地更新,如果等到最后一刻才合并的话,冲突可能会有很多处。更糟糕的是,你可能会发现自己代码的实现方式和 master 上根本就不兼容,还得返工重写。

解决方法就是在开发的过程中,时不时地把自己的分支挪到 master 最新的位置后面。比如说,我们假设一个功能要做一两个周,里面要提交很多的 commit ,在一开始的时候,我们从 master 上拉出一条 feature 分支在上面写代码,过了一阵,master 上合进了别人的新代码,这个时候我们的功能才做了一半,还不能合进 master ,但是我们可以把 feature 分支挪到 master 最新的位置后面。之后我们在 feature 分支上接着写,master 也在继续合进新的代码,又过了一阵,我们再把 feature 分支挪到 master 最新的位置后面。。。等功能全部做完,最后要合并的时候,因为我们的分支一直跟在 master 最新的位置后面,这次合并就是一次 Fast-forward,冲突也早在前面解决掉了。

所以在开发的过程中,时不时地把自己的分支挪到最新的 master 上去,就是更为稳妥的做法,这个过程就叫做 Rebase,简单地来形容,就是给分支换个地基。
6.2 Rebase 在项目中如何应用
6.2.1 倒回合并前
我们还是以上面的案例为例,先把时间倒回合并feature/complete-all分支之前:

- git rebase --hard HEAD~1:回滚倒之前的分支;
- git switch feature/complete-all:切换到全部完成的分支上;
6.2.2 git rebase
-
git rebase master :把地基换到master上;

-
手动修复代码冲突:


-
git add index.js index.html:将修改后的冲突文件添加到暂存区;
-
git rebase --continue :在解决完代码合并冲突后,继续执行被暂停的Rebase操作;

6.2.3 合并 master
- git switch master:把分支挪到 master;
- git merge feature/complete-all:合并 feature/complete-all 分支代码;

现在,feature/complete-all 分支上的代码就已经成功的合并到 master 分支上了。
6.3 总结
rebase 的四个步骤:
- git rebase master;
- 手动解决冲突代码;
- git add+冲突文件;
- git rebase --continue;
7. Stash
7.1 Stash 的使用场景
我们在开发中,经常会遇到这种场景,比如:我现在正在 feature/clear-completed 分支上开发【清空已完成功能 】,当代码开发到一半时,忽然线上出现一个关于【过期日期 】的 bug 需要紧急修复,这个时候如果直接切换到 master 分支拉取代码进行修复,就会失去 feature/clear-completed 分支上工作区中尚未提交的代码,此时我们一般有两种解决方案:
方法一:将当前分支代码提交;
bash
git add index.js
git commit -m '清空全部完成函数'
方法二:将当前分支代码使用 git stash 保存起来,等修复完线上的 bug 后,再使用 git stash pop 恢复代码到工作区继续修改就可以了。
bash
git stash
git stash pop

如果我们代码基本已经写完,那么这两种方案都可以使用,然而若我们的代码只写到一半,那就只能使用 git stash 的方法了。
7.2 模拟 git stash 的使用过程
我们来把整个过程模拟一遍:
- 从 master 上拉取 feature/clear-completed 分支,用来开发【清空已完成功能】;
bash
git switch -c feature/clear-completed
- 代码开发中...;
- 使用 git stash 将尚未提交的代码保存起来;
bash
git stash
- 切回到 master 分支去修;
bash
git switch master
- 从 master 上拉取一条分支修 bug;
bash
git switch -c hotfix/due-date
- 修复完成,提交代码;
bash
git add index.js
git commit -m '修复过期日期'
- 提交完成后,把这部分改动合回 master;
bash
git switch master
git merge hotfix/due-date
- 合并完成,删除 hotfix/due-date 分支;
bash
git branch -d hotfix/due-date
- 切回 feature/clear-completed 分支继续开发;
bash
git switch feature/clear-completed
- 把保存在stash里面的代码取回到工作区;
bash
git stash pop
- 继续修改代码;
- 将开发好的代码提交;
bash
git add index.js
git commit -m '清空已完成功能'
- 将这部分改动合回 master ;
bash
git switch master
git merge feature/clear-completed
- 合并顺利完成!
8. Worktree
8.1 Worktree 的使用场景
Stash 可以将没写完的代码收起来,让工作区变干净,然后我们去干别的,干完了再把代码取回来,所以 Stash 很适合临时打断一下的情况。但有些情况就不太适合了,比如说是同时开发多个分支。那对于频繁更换新分支的场景,我们能不能再开启一个目录呢?


像上面这样,一个工作区加上一个暂存区就是一个 worktree 了。每一个 worktree 都有自己独立的工作区和暂存区,但是所有的 worktree 都共用一份提交历史。
8.2 模拟 Worktree 的使用过程
- 新建分支开发【导出 】功能
现在假设我们要做一个【导出 】功能,点击导出按钮之后就可以导出当前的待办列表了。
按照前面的规矩,我们需要从 master 上拉取一条新分支,接下来的开发内容就在新分支上面干了。
bash
git switch -c feature/export
git add .
git commit -m '添加导出按钮'
- 新建 worktree
产品那边想新增一个【统计 】功能,用于在页面上显示有几项代办还剩下几项代办未完成之类的信息,而且这个功能比较急,下周就要上线,但是我们的【导出 】功能才写了一半,按照之前学过的方法我们可以使用 Stash 来保存这部分的代码,但这次我们换一个做法,使用 Worktree 来实现。
- git worktree add -b feature/stas .../马克代办(统计) master :创建一个 worktree,基于 【master 】分支创建一个叫做【feature/stas 】的分支,然后放在【.../马克代办(统计)】 目录下;

然后我们去父级目录下面确认一下,发现【马克代办(统计)】 的文件夹已经创建好。

- git worktree list:查看当前所有的 worktree;

- 打开新目录【马克代办(统计)】完成统计功能;
bash
git add .
git commit -m '完成统计功能'
git branch

*代表当前分支;
+代表这个分支在其他某一个 worktree(子工作目录)里被检出了;
- 将统计功能代码合并到 master 上;
bash
git branch master
git merge feature/stas
git branch -d feature/stas
- 删掉统计功能对应的 worktree;
bash
cd ../mark-todolist
git worktree remove ../马克代办(统计)
git worktree list
可以看出,删除成功,现在只剩下原来的那一个 worktree 了。

- 继续完成【导出】功能;
bash
git add .
git commit -m '完成导出功能'
- 把【导出】功能合并到 master 上;
bash
git branch master
git merge feature/export
git branch -d feature/export
8.3 总结
- git worktree add -b feature/stas .../马克代办(统计) master:添加 worktree
- git worktree list:查看所有的 worktree
- git worktree remove .../马克代办(统计):删除 worktree
9. GitHub 的使用场景
9.1 Git 和 GitHub 的关系
GitHub 和 Git,它们是两个东西,但是却经常被混在一起说。
- Git 是一个工具,装在你自己的电脑上,负责记录你对代码做的每一次改动。
- GitHub 是一个网站,把你用 Git 记录的这些改动,存到云端,让别人也能看到、下载、一起改。
简单地说,Git 是本地的,GitHub 是云端的。

接下来我们要做的就是,把电脑上的项目传一份到 GitHub 上面去。
9.2 配置SSH Key

下面我们要生成两个文件,分别是私钥 和公钥 ,私钥 留在自己的电脑上面,公钥发给GitHub。

- 首先,我们来到终端生成这两个文件,我们需要执行的命令是:
bash
ssh-keygen -t ed25519 -C "2420841402@qq.com"
- 我们依次回车,文件已经生成好了,主要还是看红框里面的这两行,上面的一行是 私钥 ,下面的一行是 公钥:

- 然后,我们再来看一下公钥 的内容,以便把它上传到 GitHub 上面去:
git bash命令窗口执行:
bash
cd ~/.ssh
cat id ed25519.pub

- 后面的输出结果就是公钥的内容了,我们把这个内容复制下来,然后再回到 GitHub 这边,点击右上角的头像,再点击 Settings:

- 在左边找到 SSH and GPG keys ,再点击 New SSH key:

- 我们直接把公钥 的内容粘贴在这个文本框里边,然后点击 ADD SSH Key:

- 现在公钥就算是发给GitHub了:

- 我们回到终端,测试一下有没有生效:
我们打开git bash窗口,输入以下命令,这个命令就是尝试去连接一下 GitHub。
bash
ssh -T git@github.com

- 到这里这台电脑的 SSH key 就已经配置好了。
9.3 创建 GitHub 仓库
身份验证相关的工作做完了之后,下面我们就在 GitHub 上面创建一个仓库,后面我们就会把本地代码推送到这个仓库里面。
- 我们回到 GitHub 页面这里,然后再点击我们的头像,选择里面的【Repositories】。

- 再点击右上角的【New 】,这样我们就进入到了创建仓库的页面。


- 我们填写好对应的内容,点击【Create repository】,仓库就创建好了。

- 目前这还是一个空的仓库,GitHub 在页面上面给了我们一段提示,下一小节就会演示如何使用这三条命令把我们本地的项目代码推送到新创建的 GitHub 仓库中。

9.4 上传代码到 GitHub
- 首先,打开项目文件夹,复制第一条要在
git bash终端执行的命令,这行命令就是让 Git 把我们刚才创建的 GitHub 仓库地址记下来,并且给它个名称,就叫做 origin ,以后再说 origin,Git 就知道指的是这个 GitHub 仓库了;
bash
git remote add origin https://github.com/NunuOfficial/MarkTodoList.git
-
第二条命令我们不需要执行,因为第二条命令是把主分支名从 master 改成 main ,我们就用 master 就行了;
-
下面我们复制第三条命令,这行代码就是用来把本地的项目代码和 Git 记录推送到 GitHub 上面的,但是要把 main 改成 master。
bash
git push -u origin master
绑定了之后,以后在 master 上推代码的时候直接执行git push就行了。


- 以后提交代码的三个指令:
bash
git add
git commit -m 'xxx'
git push

9.5 从 GitHub 下载代码
最后我们再来验证一下,这个 GitHub 上面的备份到底管不管用,假设有一天项目文件夹不小心被删了,我们能不能把这些代码给找回来,我们来模拟一下这个情况。
- 首先,把马克代办的文夹件删掉;

- 点击 【Code 】,选择 SSH,然后再点击复制按钮;

- 执行
git clone,把 GitHub 上面的仓库拉到我们本地上来的,并且我们希望拉取后的项目的名字叫做【马克代办】;
bash
git clone git@github.com:NunuOfficial/MarkTodo.git 马克代办

执行成功后,我们可以看到这个项目又回来了。
