【Git】Git的配置与使用(非常详细)
1、Git是什么
Git 是一个版本控制工具,用来记录代码的修改历史。它可以保存每次更改的内容,让开发者能随时回退到之前的版本,也方便多人协作开发代码。
1.1 Git核心概念
仓库(Repository):包含项目所有文件和版本历史的目录
工作目录(Working Directory):本地文件系统中的项目文件
暂存区(Staging Area/Index):准备下一次提交的文件集合
提交(Commit):保存到仓库中的项目快照
分支(Branch):指向特定提交的轻量级可移动指针
HEAD:指向当前所在分支或提交的指针
远程(Remote):托管在服务器上的仓库副本
1.2 Git工作流程

-
clone(克隆):从远程仓库中克隆代码到本地仓库
-
checkout (检出):从本地仓库中检出一个仓库分支然后进行修订
-
add(添加):在提交前先将代码提交到暂存区
-
commit(提交):提交到本地仓库。本地仓库中保存修改的各个历史版本
-
fetch(抓取):从远程库,抓取到本地仓库,不进行任何的合并动作,一般操作比较少。
-
pull(拉取):从远程库拉到本地库,自动进行合并(merge),然后放到到工作区,相当于 fetch+merge
-
push(推送):修改完成后,需要和团队成员共享代码时,将代码推送到远程仓库
2、vi编辑器
由于本篇文章基本都是在Git Bash中示例,会用到一些基本的linux命令,列举一些常用命令:
Bash
ls / ll:查看当前目录
cat:查看文件内容
touch:创建文件
cd:切换目录
pwd:显示当前路径
cp:复制文件
mv:移动/重命名
rm:删除文件
mkdir:创建目录
同时也会直接在命令行中编辑文件,需要用到vi编辑器,所以看看vi编辑器的用法:
vi是一个命令,也是一个命令行下的编辑器,它有如下功能:
- 打开文件,新建文件,保存文件
- 光标移动
- 文本编辑
- (多行间、多列间)复制、粘贴、删除
- 查找和替换
很多人不习惯在命令行下编辑文件,在实际开发中也不会经常在命令行下编辑文件,但是在Linux系统中对文件做些简单修改时,使用vi命令的效率非常高,并且在很多时候,比如现场调试,并没有GUI形式的编辑工具,vi是唯一选择。
常用命令:
vi编辑器由三种模式,各个模式的侧重点不一样:
- 一般模式:光标移动,复制,粘贴,删除
- 编辑模式:编辑文本
- 命令行模式:查找和替换
以下图示可表示vi编辑器三种模式下的切换:

说明:在终端输入 vi 文件名打开文件,这时进入"一般模式"。在一般模式下,按 i或 a键进入"编辑模式",就可以打字修改内容了。在一般模式下,按 :键进入"命令行模式",可以执行保存、退出等命令。
注:不论在哪个模式下,按Esc都能退回一般模式。
Bash
文件操作
vi 文件名 启动vi,如果文件存则打开文件,否则新建文件并打开
:q! 强制退出,不保存
:wq / :x 保存并退出
:q 退出
一般模式(光标移动)
h/j/k/l 左/上/右/下
0 行首
$ 行尾
gg 文件开头
G 文件末尾
文本编辑
x 删除当前字符
dd 删除当前行
u 撤销
yy 复制当前行
p 粘贴
3、Git的安装与常用命令
3.1 Git下载安装及环境配置
下载地址:git-scm.com/install/win...

下载完成后定义好安装目录后无脑点击下一步安装即可。
随后在桌面右键,显示更多选项,如果看到下面两个选项说明安装成功:

其中Git GUI是Git提供的图形界面工具,Git Bash是Git提供的命令行工具
接下来就是环境变量的配置,找到Git安装的目录,复制bin和cmd文件夹的目录。
如图所示步骤:



打开的界面选择新建,把刚才复制的两个目录粘贴过来,点击确定:

随后,桌面按下win+R,输入cmd回车,打开cmd终端,输入git --version,若出现版本号,则说明Git配置成功:

3.2 Git基本配置
桌面右键选择Git Bash,输入以下内容,设置用户信息,包括用户名和邮箱,这里推荐使用Github的用户名和注册Github时的邮箱,方便后续多人协作开发。
Bash
git config --global user.name "你的用户名(注意不能出现中文)"
git config --global user.email "你自己的邮箱@example.com"
设置好后可以用以下指令查看配置信息:
Bash
git config --global user.name // 查看用户名
git config --global user.email // 查看邮箱
另外,有些常用指令参数非常多,每次都要输好多参数,我们可以使用别名,下面为常用的log指令配置别名,这样我们后面如果想要查看日志信息就不用每次输一大堆指令。
在Git Bash中分别输入以下指令:
Bash
echo 'alias git-log="git log --pretty=oneline --all --graph --abbrev-commit"' >> ~/.bashrc
source ~/.bashrc
git config --global alias.ll "ls -al"
这样后面要想查看提交日志,只用输入git-log指令。另外,若想查看当前目录只需输入ll指令
3.3 初始化本地仓库
在电脑中任意一个位置(推荐D盘)创建一个空文件夹作为本地仓库(存储所有Git提交记录和文件),随后进入该文件夹,右键打开Git Bash,输入git init初始化本地库:
Bash
git init
如果该文件夹中出现一个隐藏的.git文件,则说明本地库创建成功:

如果没有看到任何文件,看看是不是没有打开查看隐藏的项目:

3.4 基本操作指令
Git工作目录下对于文件的修改(包括新建、删除、更新)会存在几个状态,这些修改的状态会随着我们执行Git的命令发生变化。

如果新建一个文件那么这个文件就处于未跟踪状态(untracked),修改现有文件就是处于未暂存(unstaged)状态,这时文件都处于工作区,如果执行指令git add ,那么文件就会提交到暂存区,处于已暂存状态(staged),这个暂存区可以理解为提交到本地库之前的一个缓存区。如果我们真的要把暂存区的文件提交到本地库作为一个随时可以查看的版本,只需执行git commit指令。提交成功后就形成一次提交记录,并作为一个版本,每次提交都会形成一个版本。
状态之间的转换:
- git add:工作区 --> 暂存区
- git commit:暂存区 --> 本地仓库
3.4.1 常用指令
Bash
git status 查看修改的状态(暂存区、工作区)
git add 单个文件名|通配符 将工作区一个或多个文件添加到暂存区
git add . 将所有修改加入暂存区
git commit -m "提交说明" 提交暂存区内容到本地仓库的当前分支
git log 查看提交日志
git reset --hard commitID 版本切换,其中commitId可以使用git -log或git log指令查看
注:git log指令的一般形式是:
git log[option]
其中options:
- --all 显示所有分支
- --pretty=oneline 将提交信息显示为一行
- --abbrev-commit 使得输出的commitId更简短
- --graph 以图的形式显示
如何查看已经删除的记录?
使用git reflog 指令可以看到已经删除的提交记录
添加文件至忽略列表
一般总有一些文件无需纳入Git管理,也不希望它们出现在未跟踪的文件列表。通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。在这种情况下,可以在工作目录中创建一个名为.gitignore的文件(文件名固定),列出要忽略的文件模式,比如:
Markdown
# 忽略 .a 文件
*.a
# 不忽略 lib.a文件,即使上面忽略了所有 .a 文件
!lib.a
# 只忽略当前目录下的 TODO 文件,不忽略子目录中的 TODO(只忽略根目录下的 TODO 文件)
/TODO
# 忽略整个 build 目录
build/
# 忽略 doc 目录下的 .txt文件,但不包括子目录
doc/*.txt
# 忽略 doc 目录及其所有子目录中的 .pdf文件
doc/**/*.pdf
接下来举一个例子帮助大家更好地理解整个过程:
在自己本地仓库中右键打开Git Bash,输入vi file01.txt创建一个新的文本文件:

这就进入到vi编辑器中,注意这一步先不要编辑这个文件,按Esc输入:wq退出vi编辑器,随后输入git status查看修改状态:

在git中,红色 代表已做修改(新建,更新)但还没有添加到暂存区的文件,绿色 表示已添加到暂存区的变更,准备提交到本地库。所以这里就是表示file01文件未跟踪。输入git add .将工作区所有修改添加到暂存区,再次输入git status查看状态:

可以看到,文件变成绿色,说明已将修改添加到暂存区,这时输入git commit -m "add file01" 将修改提交到本地库作为一个版本,并输入git status再次查看状态:

这时git提示了"nothing to commit,working tree clean"表明当前暂存区没有任何修改,暂存区是"干净的"。我们可以使用git log指令查看提交记录:

红框中就是我们提交时所备注的提交说明,当前只有一个提交,就是创建file01文件。再次输入vi file01.txt,编辑文件内容为update count = 1,保存并退出vi编辑器:

在以上操作中,更新完file01文件后进行了保存和提交操作,提交说明为update file01表示这次提交是修改了file01文件,最后输入git log查看提交记录,提交记录显示当前有两次提交,一次是刚才我们创建文件的提交,另一次就是更新文件的提交。
接下来重点说说reset版本回退的用法。一般命令形式是git reset --hard commitID,这里这个commitID就是在提交记录中黄色部分:

特别长对吧,这时前面我们配置的log别名就可以解决这个问题,输入git-log指令:

这样commitID就变得非常简单了,取原ID的前七位,这样我们就能很方便地跳转到任意一个版本。输入git reset --hard 74c07c3(你自己的commitId)回退到新建文件的版本,这时打开文件看看,应该什么都没有:


关于Git Bash中的复制粘贴:
要注意的是,在Git Bash中,Ctrl+C和Ctrl+V是不管用的,要是想要复制commitID可以双击它,如果选中就已经复制成功,粘贴只需按一下鼠标滚轮即可。
如果这时我们想要回到刚才那个版本呢?还是一样的操作:


这样就能在已经提交的各个版本之间跳转,非常方便。不过这里还有一个问题,刚刚我们是处于add版本,这时使用git-log指令是看不到update版本的记录的:

当然你可能觉得不能理解,上面的记录不是还有update版本的id吗?有是没错,但是在实际使用中不一定会一直开着这个界面,有可能是关闭之后第二次打开,这时就不会有任何记录。那我们如何跳转到已经"消失"的那个版本呢?还记得上面介绍的git reflog指令吗?这个指令可以看到所有的git操作记录,分析操作记录就可以跳转到所谓"消失"的版本:

3.4.2 分支
几乎所有的版本控制系统都以某种形式支持分支。 使用分支意味着你可以把你的工作从开发主线上分离开来进行重大的Bug修改、开发新的功能,以免影响开发主线。
就像写文章时的草稿副本:
- 主分支是正式文档
- 功能分支是草稿纸
- 可以在草稿上随意修改,确认无误后再合并到正式文档
常用指令:
Bash
git branch 查看本地分支
git branch 分支名 创建本地分支
git checkout 分支名 切换分支
git checkout -b 分支名 创建并切换到分支
git merge 分支名 合并分支到当前分支
git branch -d 分支名 删除分支(需要做各种检查)
git branch -D 分支名 删除分支(不做任何检查,强制删除)
看下面合并分支的记录:

注:
- 一个星号就是一个版本
- HEAD指向哪个分支就表示当前处在该分支,HEAD只是一个标记而已
比如,在上面的例子中:

这里先创建了一个新分支dev01,随后在master分支上新建.gitignore文件并提交。使用git-log查看提交记录,这里而一看到各个分支的操作记录,当前只是master分支上多了一个新建.gitignore的提交。接着切换到dev01分支:

在dev01分支上新建file02并提交,提交记录显示dev01分支上多了一个新建file02的记录,两条分支位于两个分叉上。
分别看看不同分支上目录中的文件:


可以看到,master分支上有.gitignore、file01文件,dev01分支上有file01、file02文件。也就是说,不同的分支就相当于不同的箱子,装着不同的内容。我们可以在不同的分支上开发不同的功能,各个分支之间是独立的。两个分支上的功能开发完毕,最后要合并到一起,可以使用git merge 分支名,表示将分支合并到当前分支上,一般是合并到master分支上,所以合并之前要确保当前处在master分支上:

从合并完成之后的提交记录可以很清楚的看到各个分支之间的关系以及各个分支上的提交记录,展示的是master分支上的提交记录,在第三个星号处(从下往上数)master分支新建.gitignore,然后在分出去的分支上,第四个星号处,新建file02,接着合并到master上,形成最终版本。这时看看master分支中的文件:

既有.gitignore,也有file02,说明合并成功。
3.4.1 解决冲突
当两个开发者开发同一个项目,并且处于两个不同的分支上时,这时若两个人同时修改了同一个文件的同一行,那么在合并的时候就会产生冲突,Git也不知道采用哪个人的修改,Git就会把这个问题抛给我们,让我们来决定采用哪个版本的修改。我们解决这个问题的过程就叫做解决冲突。解决冲突的过程如下:
- 处理文件中冲突的地方
- 将解决完冲突的文件加入暂存区(add)
- 提交到仓库(commit)
现在,在上面的例子中来制造一些冲突来帮助我们更好地理解解决冲突的流程:

首先输入git-log查看当前分支,日志显示当前存在master分支和dev01分支,由于dev01分支和master分支不是完全相同的,这时我们先删掉dev01分支,随后创建并切换到dev分支上:

然后分别在dev、master分支上修改feile01文件的count为不同的值,这时就产生了冲突,如果这时我们想要把dev分支合并到master分支上:

Git提示自动合并失败,需要我们来手动处理冲突。这时输入cat file01查看file01当前内容:

可以看到Git已经帮我们列出冲突产生的位置,并把不同分支上的内容都展示出来,以便我们处理冲突。输入vi file01来修改这个冲突:


这里我们修改count为5,随后将这个修改提交:

最后输入git-log查看提交记录,发现已经成功处理了冲突,dev分支也已经合并到master分支上。
3.4.2 开发中分支的使用原则和流程
在开发中,一般有如下分支使用原则和流程:
- master(生产)分支
线上分支,主分支,中小规模项目作为线上运行的应用对应的分支。
- develop(开发)分支
是从master创建的分支,一般作为开发部门的主要开发分支,如果没有其他并行开发不同期上线要求,都可以在此版本进行开发,阶段开发完成后,需要合并到master分支,准备上线。
- feature/xxxx分支
从develop创建的分支,一般是同期并行开发,但不同期上线时创建的分支,分支上的开发任务完成后合并到devdelop分支。
- hotfix/xxxx分支
从master派生的分支,一般作为线上bug修复时使用,修复完成需要合并到master、test(用于代码测试)、devdelop分支。

master分支是稳定运行的版本,一般是已经部署上线的版本,是跑在服务器(或其他设备)上的最终版本。开发任务主要是在develop上进行的,但并不是直接在develop分支上开发新功能,而是所有已经开发的代码都应该提交到develop分支上面。若想开发一个新功能,那么就在devdelop分支上最新的版本上创建一个feature分支,命名为feature/功能,在这个feature分支上开发新的功能,开发完成后将代码合并到develop分支上。如果并行开发多个新功能,那么做法也是一样的,由其他开发者在devdelop上创建新的feature分支,最后将开发完的代码都合并到devdelop分支上。如果当前功能都已开发完毕,那么就可以将devdelop分支合并到master分支上形成一个release版本,这个relsase版本就是最终发布的稳定版。
需要注意的是,feature分支是为了多个新功能的开发互不影响而临时创建的分支,当把feature分支合并到devdelop分支以后,这个临时的feature分支就没用了,所以要把这个分支删除掉。那么当develop分支开发完成后,develop分支是不能删除的,master分支和develop分支是固定的两个分支,都不能删除。
另外,在图中还有hotfix分支,这个分支是用来修复已经发布的版本中产生的bug。比如图中release2版本中用户反馈了一个bug,我们现在要去修复这个bug,可肯定不能在master分支上直接修改,这会影响到所有的用户。这时我们西欧iiu可以在当前版本上创建一个hotfix分支,在这个分支上进行一系列的修改、测试,最终确定bug已经修复后,再合并到master分支上形成下一个ralease版本。随后还要将hotfix分支合并到devdelop分支上,因为如果不合并到develop分支上,后续develop分支新功能开发完毕合并到master分支上时,就会丢失先前修复的提交,所以这里还要合并到develop分支上。
3.4.3 快进模式
Git快进模式(Fast-forward)是一种分支合并方式,当要合并的分支是当前分支的直接后继时,Git会简单地将当前分支指针向前移动到目标分支的最新提交而不创建新的合并提交。
这么说太抽象了,还是看一个例子:

查看当前分支,日志显示当前有两个一模一样的分支:master分支和dev分支。随后我们在dev分支上创建file03文件并提交:

再次查看提交日志,发现dev分支上只比master分支上多了一次提交,如果这时我们将dev分支合并到master分支上:

Git会自动识别到dev分支上master分支的直接后继,即master分支上有的dev分支也都有,这时合并就不会产生冲突,就会触发Git的快进模式,即Fast-forward模式,Git会简单地把当前分支指针指向dev分支的最新提交,这时master分支也就处于这个地方,而且不会产生以往合并分支时类似于小桥一样的结构。
4、Git远程仓库的使用
前面我们所有的提交都是在本地仓库中进行的,如果想把代码备份到互联网中,那么就需要使用远程仓库来管理。远程仓库是指存储在网络上的Git仓库,主要用于团队协作和代码备份。
常用的远程仓库平台有:
本篇文章将采用GitHub平台作为示例,介绍远程仓库的配置以及代码管理。
4.1 GitHub
Git与GitHub的关系:Git是一个版本控制系统,而GitHub是一个使用Git的工具和服务提供者。开发者可以在本地进行版本控制,然后将代码推送到GitHub上进行共享和协作。

- 登录GitHub官网,注册账号


- 注册好后返回登录

- 登录成功后创建仓库


- 提供HTTP或SSH链接远程仓库

4.2 操作远程仓库
4.2.1 添加远程仓库
此操作是先初始化本地库,然后与已创建的远程库进行对接。
- 命令:
git remote add <远端名称> <仓库路径>- 远端名称,默认是origin,取决于远端服务器设置
- 仓库路径:在GitHub创建仓库时提供的HTTP
比如:

回车后若没有提示错误就说明远程仓库连接成功
4.2.2 查看远程仓库
- 命令:
git remote

远程仓库的名称为origin
4.2.3 推送到远程仓库
- 命令:git push [-f] [--set-upstream] [<远端名称> <本地分支名><:远端分支名>
-
这里先省略掉
-f和--set-upstream两个参数,则这个指令可以写成:git push origin master:master,表示将本地的master分支推送到远程仓库的master分支上。 -

-
这样就推送成功了,刷新GitHub中的仓库:
-

-
可以看到我们的远程仓库已经有内容了,并且和本地的master分支的内容一模一样。
-
如果远端分支名和本地分支名相同,则可以只写本地分支
- 比如在上面的例子中,本地分支和远端分支名都为master,我们就可以省略为:
git push origin master,还是表示将本地的master分支推送到远程仓库的master分支上。 
- 比如在上面的例子中,本地分支和远端分支名都为master,我们就可以省略为:
-
可以看到和上面的方式没什么区别,简化了一点而已。
-
-f表示强制覆盖,就是说有冲突存在时,git不允许直接push,这时我们可以加上-f来强制覆盖远程仓库的代码。 -
--set-upstream表示将本地分支推送到远程的同时并且建立起本地的当前分支和远端分支的关联关系,这样后面再次推送时Git就可以根据这个关联关系来判断我们想推送到远端的哪个分支上,这时就可以只输git push来完成推送操作。- 比如:
git push --set-upstream origin master
- 比如:
-

-
这样本地的master分支已经和远程的master分支关联上了。下次如果在本地master分支上想要往远程的master分支推送时只用输入
git push指令。 -
如果当前分支已经和远端分支关联,则可以省略分支名和远端名。
git push将master分支推送到已关联的远端分支。
-
和正常的指令效果一样,可以正常推送。
-
4.2.4 查看本地分支与远程分支的关联关系
- 命令:
git branch -vv

4.2.5 从远程仓库克隆
如果已经有一个远端仓库,我们可以直接克隆到本地
- 命令:git clone <仓库路径> [本地目录]
- 本地目录可以省略,会自动生成一个目录
我们可以新建一个文件夹来克隆远程仓库:

在想要克隆的目录下右键打开Git Bash,输入git clone <仓库路径> <本地名称>,如果不指定目录就会自动新建一个目录,名称就是远程仓库的名称。等待一会就克隆成功了,它和我们本来的本地库和远程仓库的内容都是一模一样的。关掉刚刚克隆仓库的Git Bash,因为这是在hello-git外面打开的Git Bash是操作不了hello-git这个库的,进入hello-git右键打开Git Bash,输入git-log,在刚刚推送的Git Bash中也输入git-log:

可以看到基本一模一样,只不过我们的本地库多了一个dev分支而已。
4.2.6 从远程仓库中抓取和拉取
有这么一个场景:两个人协作开发同一个项目,其中一个人开发完一个功能后将代码提交,另一个人想要拿到这个更新,是不是可以直接克隆整个远程仓库呢?这当然是可以的,不过克隆操作一般只会用到一次,在创建项目的时候才会用到。每次要拿到新的更新都克隆太过麻烦了,因为有可能整个项目很大,非常费时间。这就要用到抓取和拉取操作了。
- 抓取 命令:
git fetch [remote name] [branch name]- 抓取指令即使将远端仓库里更新都抓取到本地,不会进行合并。
比如,我在原本的本地库中新建文件file04并提交:

看看新建之前的日志:

可以发现git将当前分支指针后移指向最新提交,这就是前面介绍的快进模式。我们将本地的修改推送到远端中去,就类似于一个快进模式的合并。
随后在克隆的仓库中通过fetch来拿到这个更新:

但是当前本地库还处于add file03,本地的master分支中并没有add file04这个提交,说明fetch指令只会拿到更新,并不会把这个更新合并到本地分支。这时我们可以通过merge指令来将远端master分支合并到本地master分支上:

和另一个Git Bash中的日志一样,接下来看看拉取,可以直接抓取并合并。
- 拉取 命令:
git pull [remote name] [branch name]- 拉取指令就是将远端仓库的修改拉到本地并自动进行合并,等同于fetch + merge
- 如果不指定远端名称和分支名,则拉取与当前分支相关联的远端分支的所有更新
在原始本地库中新建file05并推送:

在克隆库中拉取:

日志显示两个人的记录一模一样,比单纯的抓取方便多了。
4.2.7 远程解决冲突
在一段时间,A、B用户修改了同一个文件,且修改了同一行位置的代码,此时会发生合并冲突。
A用户在本地修改代码后优先推送到远程仓库,此时B用户在本地修订代码,提交到本地仓库后,也需要
推送到远程仓库,此时B用户晚于A用户,故需要先拉取远程仓库的提交,经过合并后才能推送到远端分
支,如下图所示。

在B用户拉取代码时,因为A、B用户同一段时间修改了同一个文件的相同位置代码,故会发生合并冲
突。
远程分支也是分支,所以合并时冲突的解决方式也和解决本地分支冲突相同相同,在此不再赘述。
附:几条铁律
- 切换分支前先提交本地的修改
- 代码及时提交,提交过了就不会丢
- 遇到任何问题都不要删除文件目录