文章目录
- 一、预备知识
- 1.Linux常用指令
- 2.vim编辑器基本使用
- 二、Git基础
- 1.工作区、暂存区、本地仓库和远程仓库
- 2.git init
- 3.git add
- 4.git status
- 5.git commit
- 6.git push
- 7.git pull
- 8.git 分支管理(branch、checkout、merge)
- 9.git clone和log
- 10.git diff
- 11.git fetch
- 12.git rm
- 13.git mv
- 三、Git进阶
- 1.git reset
- 2.git cherry-pick
- 3.git remote
- 4.git stash
- 5.忽略提交文件.gitignore
- 四、工作中常遇见的情况及解决
- 1.冲突合并
- 2.推代码前未拉取最新代码导致冲突
- 3.待补充
在近期的实习中,学习到不少先前在学校不会的git指令,于是决定在周末重学一遍git,并将笔记记录在此博客。
本文内容偏基础,个人认为讲得足够细致,请耐心读取,欢迎读者底部留言评论。
GIt相关内容光看不练是无法掌握的,多进行实操才可熟记于心。
一、预备知识
1.Linux常用指令
这里介绍一下本文用到的指令:
cd
:更改当前工作目录
ls
:列出指定目录中的文件和子目录
mkdir
:创建一个新目录
rmdir
:删除指定目录
touch
:创建新文件
echo
:本文使用$ echo "文本内容" > 文件名
实现将文本输入到指定文件内。
cat
:输出文件内容
rm
:删除文件或目录
2.vim编辑器基本使用
vim是一种文本编辑器,它通常在命令行界面下使用。它具有非常强大的编辑能力和快捷键操作,可以用来编辑各种文本文件,如代码、配置文件等。
vim编辑器有两种模式:命令模式、编辑模式。
命令模式里有一种子模式叫末行模式。
- 命令模式:可以进行删除、复制、粘贴等快捷操作。
- 编辑模式:可以编辑文本内容。
- 末行模式:可以通过命令操作文件,比如搜索、保存、退出等。
按i
进入编辑模式,编辑模式左下角会显示插入或insert字符,编辑模式下可以输入文本内容。
按esc
退出可以编辑模式,进入命令模式。
在命令模式下,输入英文的:
进入末行模式,在末行模式下,可以根据输入的不同指令按回车执行不同的操作。
常用指令如下:
指令 | 作用 |
---|---|
:q! | 不保存,强制退出 |
:wq | 保存后退出 |
:q | 退出 |
:w | 保存 |
二、Git基础
Git 是一个非常流行的分布式版本控制系统,用于管理代码、文本文件等任何形式的文档。它让一个或多个开发者合作编写代码变得容易而舒适,同时提供了远程存储库和与不同工具的整合。
在基础部分,我们介绍一些最常用的指令,以及最基本的操作。
1.工作区、暂存区、本地仓库和远程仓库
学习要点:四个概念分别表示更改在哪个位置。
在学习Git操作之前,请务必分清楚这四个概念,它们在代码管理的过程中起到不同的作用。
- 工作区:是指开发者本地的工作目录,可以理解为保存源代码的目录,比如我们正在使用VSCode开发,VSCode所操作的文件目录即是工作区。在工作区内修改或添加文件后需要通过Git将其提交到版本控制系统中,这时候文件的修改还不存在于
Git repository
中。 - 暂存区:也称为
Git Index
(索引),是在进行commit操作之前使用的一个缓存区域,它存放着即将被提交的代码更改,可以简单理解为在提交代码前的一个临时存储区域。通过使用git add
命令可以将修改的文件添加到暂存区中,而使用git reset
命令则可以将文件从暂存区移除。 - 本地仓库:也称为
Git Repository
,是存储代码更改历史记录的地方,以及比保存在工作区中更加安全和稳定的版本库。当从暂存区提交代码后,会生成一个新的提交对象,并被保存在本地Git代码库中。可以通过使用git commit
将暂存区的文件添加到本地仓库。本地仓库往往可以使用git init
生成。 - 远程仓库:是指代码托管服务提供的服务器端存储库,一般使用
GitHub
、GitLab
等在线平台扮演着远程仓库的角色。当本地仓库需要与其他人协作或备份时,可以将本地仓库中的改动通过git push
推送到远程仓库进行分享或备份,也可以通过git pull
从远程仓库拉取最新代码到本地进行更新。
对于一个Git项目,一般情况下,工作区是开发者进行开发和修改代码的地方,暂存区是用来暂存即将提交的文件更改,本地仓库则是存储代码版本历史记录和进行分支管理的主要地方。而远程仓库则是为了协同开发、备份和分享代码而产生的一个重要概念。
总结一下代码提交的过程所经过的位置:工作区—>暂存区—>本地仓库—>远程仓库。
2.git init
git init
命令用于在目录中创建新的Git仓库(也就是本地仓库)。
$ git init
例如,我们在桌面创建一个名为demo
的文件夹,并创建新的Git仓库。
$ mkdir demo
$ cd demo/
$ git init
Initialized empty Git repository in C:/Users/22706/Desktop/demo/.git/
# 初始化空Git仓库完毕
现在你可以在你的文件夹中会有一个隐藏的.git
子目录(文件夹内要设置展示隐藏文件夹才能看到),这就是你的GIt仓库(此文件夹的本地仓库)了,所有有关你的此项目的快照数据都存放在这里。使用ls -a
命令可以在bash面板中查看隐藏文件。
3.git add
git add
命令用于将工作区的文件添加到暂存区。
学习要点:如何将一次提交一个或几个更改以及如何一次提交所有文件到暂存区。
添加一个或多个文件到暂存区:
$ git add [file1] [file2] ...
添加指定目录到暂存区,包括子目录:
$ git add [dir]
添加当前目录下的所有文件到暂存区:
$ git add .
现在我们实现demo
目录下创建两个文件,并将它们添加至暂存区
$ touch README.md # 创建文件
$ touch hello.js # 创建文件
$ ls # 列出此目录下所有文件
README.md hello.js
$ git add README.md hello.js # git add
$ git status # 用于显示当前git仓库的状态,即哪些文件被修改了、哪些文件已经准备好提交到仓库,以及当前所在分支等信息,后续会详细介绍
On branch master # 在master分支
No commits yet # 还没有提交
Changes to be committed: # 要提交的更改:
(use "git rm --cached <file>..." to unstage)
new file: README.md
new file: hello.js
在项目开发的过程中,添加所有文件使用的很普遍,所以务必记住git add .
该命令。
4.git status
git status
命令**用于显示当前GIt仓库的状态,即哪些文件被修改了、哪些文件已经准备好提交到仓库,以及当前所在的分支等信息。**需要注意的是:git status
查看的状态显示的是commit
之前的状态,看不了commit的状态?????有待考证。
学习要点:git status 输出的状态分别是哪个阶段的状态、输出状态英文含义、简短输出参数的使用、简短输出的不同颜色的英文所表示更改所在阶段以及何种状态。
$ git status
执行git status
命令后,Git会返回一个列表,列出已修改但未添加至暂存区(索引区)的文件列表、已添加至暂存区(索引区)但未提交的文件列表和尚未追踪(untracked)的文件列表。同时还会显示当前所在的分支名称、是否是干净工作区(没有任何被修改的文件)等信息。
另外使用-s
(short)参数可以获取简短的仓库状态输出结果
$ git status -s
我们在上一小节的基础上修改README.md
文件,并查看仓库状态
$ echo "Hello World" > README.md # 写入内容到指定文件
$ git status
On branch master # 在master上
No commits yet # 还没有任何提交
Changes to be committed: # 要提交的更改:(指的就是已经在暂存区的更改)
(use "git rm --cached <file>..." to unstage)
new file: README.md
new file: hello.js
Changes not staged for commit: # 未暂存以提交的更改:(指的就是在工作区修改后还没add到暂存区的更改)
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
$ git status -s # 简短输出
AM README.md # A表示新增文件,M表示被修改过,绿色表示已add到暂存区,红色表示还在工作区
A hello.js
如上图:A表示已在暂存区的新增文件(add),M表示被修改过(modified),绿色表示已add到暂存区,红色表示还在工作区未add到暂存区。
现在我们提交暂存区的更改再查看状态试试:
$ git commit -m "首次提交" # 提交指令,后续会详细介绍
[master (root-commit) 33b65a6] 首次提交
2 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 README.md
create mode 100644 hello.js
$ git status # 看状态
On branch master
Changes not staged for commit: # 未暂存以提交的更改:(指的就是在工作区修改后还没add到暂存区的更改)
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
no changes added to commit (use "git add" and/or "git commit -a") # 没有被add(在暂存区)的更改以提交
$ git status -s # 简短输出状态
M README.md # 红色的M
如上图,通过简短输出状态,看到只剩下红色的M的README.md
,表示此更改还在工作区,已经提交的更改是没有状态可看的。
还需要提醒的是,在工作区新增的文件,如未add至暂存区前,使用git status -s
查看到的状态是??
,其表示的意思也是新增(add),只不过还在工作区中,并未作出修改。如下,新增文件html.js
后简短输出状态:
$ touch html.js # 新增文件
$ git status -s # 简短输出状态
M README.md
?? html.js
$ git add . # add暂存
warning: in the working copy of 'README.md', LF will be replaced by CRLF the next time Git touches it # 这个警告提示是由于在不同的操作系统中换行符的表示方式不同所导致的,CRLF表示windows的换行,LF表示linux的换行,这里不细说。
$ git status -s # add暂存
M README.md
A html.js
可以看到提交前的新建并不修改的文件状态是红色问号,add暂存后就变为绿色A。
通过执行git status
,我们可以了解项目中代码的变更情况,帮助我们更好地掌握代码版本的演进。
在分支切换时,会常用此指令查看当前所处分支,确保分支无误后再进行代码提交等操作。
5.git commit
git commit
命令用于将暂存区内容添加到本地仓库。
学习要点:如何提交、提交成功的输出信息的含义、提交成功后状态变化、查看提交日志
提交暂存区到本地仓库中:
$ git commit -m [message]
[message]
是一些备注信息。如果未输入备注信息会自动进入vim编辑器,要求你在vim编辑器内输入备注信息,否则提交失败。有关vim编辑器的基本使用可以查看我往期文章:vim编辑器基本使用。
提交暂存区指定文件到仓库区:
$ git commit [file1] [file2] ... -m [message]
-a
参数设置修改文件后不需要执行git add
命令,直接来提交
$ git commit -a
另外在第一次提交前要设置用户信息,包括用户名和邮箱,此用户名与邮箱与远程仓库无任何关联,不产生影响,只是用于区分提交用户。不是真实邮箱也问题不大。
$ git config --global user.name 'WuJianRong'
$ git config --global user.email 2270645424@qq.com
如果去掉--gobal
参数则只对当前仓库有效。
下面我们在上一小节的基础上提交代码,再查看状态以及提交日志(后续会详细介绍):
$ git commit -m "二次提交"
[master 3dec064] 二次提交
2 files changed, 1 insertion(+) # 2个文件有更改,新插入1行代码
create mode 100644 html.js # 表示一个新文件被添加到Git仓库中,六位数字表示文件权限,100644:可读写,100755:可执行文件
$ git status
On branch master
nothing to commit, working tree clean # 没有可提交的东西,工作树是干净的
$ git status -s
# 简短输出是空 表无可提交的东西
$ git log # 查看提交日志
commit 3dec0643778646c59200135deb39d1e4506ee124 (HEAD -> master) # 中间那串表示key,用于区分提交记录,可以使用此key回退提交版本,后续会详细介绍
Author: WuJianRong <2270645424@qq.com> # 提交用户,多人协作一个项目时用于区分哪个程序猿提交的
Date: Sat Jun 10 22:35:21 2023 +0800 # 提交时间
二次提交
commit 33b65a64b587bb20d6d289b0eeb3b11d4b730e1f
Author: WuJianRong <2270645424@qq.com>
Date: Sat Jun 10 21:53:03 2023 +0800
首次提交
详细请看代码备注。
6.git push
git push
命令用于从本地的分支版本上传到远程并合并。
学习要点:如何推送、本地仓库如何连接远程仓库、如何配置代理
命令格式如下:
$ git push <远程主机名> <本地分支名>:<远程分支名>
如果本地分支名与远程分支名相同,则可以省略冒号:
$ git push <远程主机名> <本地分支名>
在将本地仓库的更改推送到远程仓库前,要先配置默认推送的目标远程仓库,可以使用git remote add
命令将远程仓库添加到本地仓库,并为它命名一个别名,从而在以后使用该别名来引用该远程仓库。
这里以我在GitHub上新建的一个仓库为例:
$ git remote add origin https://github.com/WuJianR/Git-Study.git
# 输出空 表示配置成功?也许失败也是空?有待考证 反正我是成功了(苦笑脸)
$ git push origin master
fatal: unable to access 'https://github.com/WuJianR/Git-Study.git/': OpenSSL SSL_read: Connection was reset, errno 10054 # 没配置代理导致的
$ git config --global http.proxy # 查看是否配置http代理
# 输出空 表示没有配置任何http代理
$ git config --global http.proxy http://127.0.0.1:7890 # 配置http代理,代理IP地址可以在电脑的设置中查看(需要开VPN),配置https代理也是一个方法
$ git push origin master # 推送到远程查看的master分支
Enumerating objects: 6, done. # 一些推送信息,暂时不关注
Counting objects: 100% (6/6), done.
Delta compression using up to 16 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 514 bytes | 514.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/WuJianR/Git-Study.git
* [new branch] master -> master # 远端没有master分支会自动新建一个master
在GitHub上成功查看到push结果:
另外,我们经常使用后面不带参数的git push
命令,如果不带任何参数,默认情况下它会将当前分支推送到与其所跟踪的远程分支同名的远程分支上,如果该分支不存在,则会创建一个同名的远程分支并将当前分支推送到该分支上。
7.git pull
git pull
命令用于从远程拉取代码并合并本地的版本。
git pull
其实就是git fetch
和git merge FETCH_HEAD
的简写。命令格式如下:
git pull <远程主机名> <远程分支名>:<本地分支名>
下面我们在远端新建一个名为new-remote-file
文件,再尝试在本地拉取远程的更改(即远端仓库和本地工作区的差异):
$ git pull origin master # 拉取
remote: Enumerating objects: 4, done. # 一些拉取的描述信息
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (2/2), done.
Unpacking objects: 100% (3/3), 736 bytes | 147.00 KiB/s, done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
From https://github.com/WuJianR/Git-Study
* branch master -> FETCH_HEAD
3dec064..b8fbc74 master -> origin/master
Updating 3dec064..b8fbc74
Fast-forward
new-remote-file | 1 + # 文件名 | 一个新增文件
1 file changed, 1 insertion(+) # 一个变更了的文件,新增一行内容
create mode 100644 new-remote-file # 可读写文件
查看本地工作区的文件内容,可以看到本地工作区的内容和远程仓库的同步了。
再次拉取,则会显示当前工作区已是最新的了。
$ git pull origin master
From https://github.com/WuJianR/Git-Study
* branch master -> FETCH_HEAD
Already up to date.
工作中,我们经常使用后面不带参数的git pull
命令,它git pull
不带参数是一个道理,可看上一小节末尾。
8.git 分支管理(branch、checkout、merge)
几乎每一种版本控制系统都以某种形式支持分支。使用分支意味着你可以从开发主线上分离开来,然后再不影响主线的同时继续工作。有人把Git的分支模型称为必杀技特性,而正是因为它,将Git从版本控制系统家族里区分出来。
学习要点:如何分别实现本地分支增删合查、如何查看远程仓库分支、分支合并[核心]
查看本地仓库所有分支命令:
$ git branch
添加-r
参数可以查看远程仓库所有分支:r
即表示remote。
$ git branch -r
创建分支命令:
$ git branch [branchname]
切换分支命令:
$ git checkout [branchname]
合并分支命令:将其他分支合并到当前分支
$ git merge [branchname]
删除分支命令:不能删除当前所在分支
$ git branch -d [branchname]
新建分支并切换到新建的分支的快捷命令:相当于先新建分支,再切换分支,两条指令合二为一
$ git checkout -b [branchname]
下面我们接着上一节的案例,在本地新建分支,但不切换分支,在原分支上做出更改,再切换新建分支,对比差异:
$ git branch # 查看本地仓库所有分支
* master # * 表示当前所在分支
$ git branch -r # 查看远程仓库所有分支
origin/master
$ ls # 查看当前分支下工作区所有文件
README.md hello.js html.js new-remote-file
$ git branch newbranch1 # 新建名为newbranch1的分支
$ git branch # 查看本地仓库的所有分支及当前所处分支
* master
newbranch1
$ touch hi.js # 当前分支下新建文件hi.js
$ ls
README.md hello.js hi.js html.js new-remote-file
$ git add . # 添加所有文件至到暂存区
$ git commit -m "add hi.js" # 提交暂存区的文件至本地仓库
[master 2bf84e8] add hi.js
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 hi.js
$ git checkout newbranch1 # 切换到新建分支
Switched to branch 'newbranch1'
$ git branch # 查看本地仓库的所有分支及当前所处分支
master
* newbranch1
$ ls # 可以看到新建分支并没有在master分支下新建的hi.js文件
README.md hello.js html.js new-remote-file
$ git checkout master # 切换回master分支
Switched to branch 'master'
$ ls # 可以看到master是有hi.js文件的
README.md hello.js hi.js html.js new-remote-file
通过执行上述指令可以看到,在当前分支做出更新,并提交更新后,在没有和其他分支合并的前提下切换到其他分支,Git将还原你的工作目录到你创建分支时候的样子,如上述在master
分支新建并提交的hi.js
文件在newbranch1
分支下是没有的。
但是值得注意的是:**当你在Git中切换分支时,Git会自动将当前分支中的工作区文件和暂存区文件进行保存,然后Git会从目标分支读取它最新的commit的内容到工作区和暂存区。**也就是没提交到本地仓库的内容会被保存,切换到其他分支依旧可以看到这些内容,提交到本地仓库就代表不在工作区和暂存区了,切换其他分支上也就看不到这些内容了。上面说的都是在没有合并分支的前提下。
下面我们在master分支上新建一个文件nihao.js
,不提交至本地仓库,我们切换分支对比工作区文件列表可以验证我上面说的:
$ git checkout newbranch1
Switched to branch 'newbranch1'
$ touch 'nihao.js' # master分支下新建文件在工作区
$ git checkout newbranch1 # 没提交到本地仓库直接切换分支
Switched to branch 'newbranch1'
$ ls # newbranch1分支下可以看到在master分支未提交的nihao.js文件
README.md hello.js html.js new-remote-file nihao.js
下面我们再来讲讲如何分支合并,
GIt分支合并是将两个不同的分支合并成一个新的分支,其中一个分支被称为"目标分支"(或基础分支,也就是当前所在分支),另一个分支被称为"要被合并的分支"。在合并的过程中,Git会比较两个分支之间的差异并尝试将这些差异合并到目标分支上,从而创建一个新的包含两个分支的代码库。通常情况下,新版本的分支应该被合并到旧版本的分支上,而不是相反,因为大多数情况下我们希望保留最新的更改并将其合并入旧版本中以创建一个更新版本。
下面我们接着上面的案例,通过实践来体会分支合并,在上面的案例中,我们在master
分支中做出了更新(即新建并提交了一个hi.js
的文件),newbranch1
分支仍处于新建hi.js
之前的提交状态,也就是说master
分支相当于新版本,newbranch1
分支相当于旧版本,我们先在新版本分支上尝试合并旧版本分支,看看会有啥结果,再切换旧版本分支,合并新版本分支,再看看会有什么不同:
$ git checkout master # 切换到新版本分支
Switched to branch 'master'
$ git merge newbranch1 # 合并旧版本
Already up to date. # 可以看到输出显示 当前分支已经是最新的了,也就是合并方向反了
$ git checkout newbranch1 # 切换旧版本分支
Switched to branch 'newbranch1'
$ git merge master # 合并新版本分支
Updating b8fbc74..2bf84e8
Fast-forward
hi.js | 0
1 file changed, 0 insertions(+), 0 deletions(-) # 可以看到旧版本分支里新增了一个文件,也就是master分支的新建提交了的文件
create mode 100644 hi.js
$ git log # 再看一眼提交日志,可以看到在新版本分支master分支的最新的提交也同步到newbranch1分支上了
commit 2bf84e8c5df42fc42fe0cfb6c5be53026056427c (HEAD -> newbranch1, master)
Author: WuJianRong <2270645424@qq.com>
Date: Sun Jun 11 12:06:30 2023 +0800
add hi.js
commit b8fbc741ab8b5861f41052306c30e0effb8a3261 (origin/master)
Author: echo5 <97384745+WuJianR@users.noreply.github.com>
Date: Sat Jun 10 23:27:02 2023 +0800
Create new-remote-file
远端新建的一个文件
commit 3dec0643778646c59200135deb39d1e4506ee124
Author: WuJianRong <2270645424@qq.com>
Date: Sat Jun 10 22:35:21 2023 +0800
二次提交
commit 33b65a64b587bb20d6d289b0eeb3b11d4b730e1f
Author: WuJianRong <2270645424@qq.com>
Date: Sat Jun 10 21:53:03 2023 +0800
首次提交
$ git branch -d master # 删除被合并的分支
Deleted branch master (was 2bf84e8).
$ git branch # 查看分支列表,可以看到master分支不见了
* newbranch1
上述代码的对比可以很好的验证了新版本的分支应该被合并到旧版本的分支上。
但是值得注意的是,以上被合并和删除的master分支仅仅只是用于测试。
真实工作中,master分支往往作为主分支,不能用于被合并,往往都是新建一个分支,在新分支上开发新功能,再把新分支合并到主分支master上,最后删除新分支。
9.git clone和log
这两个命令一个是克隆远程仓库的项目,一个是查看提交日志。
git clone [仓库链接]
git log
...
...
查看提交日志常在处理冲突的过程中使用。
10.git diff
git diff
命令用于比较文件的不同,即比较文件在暂存区和工作区的差异。
学习要点:git diff
命令比较的是哪两个区域的差异、参数--cached
的使用,git status
和git diff
的差异
git diff
命令显示已写入暂存区和已经被修改但尚未写入暂存区文件的区别。
它有两个主要的应用场景:
- 尚未暂存的改动:
git diff
- 查看已暂存的改动:
git diff --cached
- 查看已暂存的与未暂存的所有改动:
git diff HEAD
- 显示摘要而非整个diff:
git diff --state
显示暂存区和工作区的差异:
$ git diff [file]
显示具体文件的暂存区和上一次提交之间的差异:
$ git diff --cached [file]
或
$ git diff --staged [file]
显示两次提交之间的差异:
$ git diff [commitId] [commitId]
下面我们接着上一节的案例,测试一下如何显示两次提交之间的差异:
$ git log # 查看提交记录,并复制最近两次提交的id,用于比较两次的差异
commit 939c25049f982e389725e5c25a59c47fb7f66a28 (HEAD -> master)
Author: echo5 <97384745+WuJianR@users.noreply.github.com>
Date: Mon Jun 12 19:01:14 2023 +0800
Update README.md
这是一次更新
commit b8fbc741ab8b5861f41052306c30e0effb8a3261
Author: echo5 <97384745+WuJianR@users.noreply.github.com>
Date: Sat Jun 10 23:27:02 2023 +0800
Create new-remote-file
远端新建的一个文件
commit 3dec0643778646c59200135deb39d1e4506ee124
Author: WuJianRong <2270645424@qq.com>
Date: Sat Jun 10 22:35:21 2023 +0800
二次提交
commit 33b65a64b587bb20d6d289b0eeb3b11d4b730e1f
$ git diff b8fbc741ab8b5861f41052306c30e0effb8a326 939c25049f982e389725e5c25a59c47fb7f66a28 # 比较近两次提交的差异,比较前面id的提交相比于后面的id的提交的差异
diff --git a/README.md b/README.md
index 557db03..1c8a839 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,2 @@ # -1表示从第一行开始,后续跟着的0行代码被删减了,如果删减了非0行的话会用逗号隔开,然后跟变化的行数
# +1,2 表示从1行开始,有连续两行代码出现新增
Hello World
+你好啊! # 新增了一行
可以看到最新一次提交记录较上次提交多增了一行内容,跟上一案例的结果完美对上。
下面我们再测试一下git diff
的使用,修改README.md
文件并添加至暂存区,再次修改README.md
,使用git diff
对比工作区和暂存区的差异:
$ vim README.md # 进入vim编辑器修改md文件
$ cat README.md # 查看修改结果
Hello World
$ git status # status 显示变化的状态 没有暂存 add/modified/deleted
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
no changes added to commit (use "git add" and/or "git commit -a") # 提示git add添加暂存
$ git diff # diff 显示工作区和暂存区的详细变化
diff --git a/README.md b/README.md
index df41c03..557db03 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1 @@
Hello World
-测试diff!
$ git add . # 添加暂存
$ git status # status 显示变化的状态 暂存for commit add/modified/deleted
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: README.md
$ git diff # 此时暂存和工作区没差异所以输出空
$ vim README.md # 再次进入vim编辑器修改md文件
$ cat README.md # 再次查看修改结果
Hello World
测试diff!
$ git diff # 查看工作区和暂存区的差异
diff --git a/README.md b/README.md
index 557db03..df41c03 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,2 @@
Hello World
+测试diff!
$ git status -s # 使用 git status查看状态
MM README.md # 一个红色的M和一个绿色的M分别表示工作区有更改和暂存区有更改
通过上面的git status
和git diff
比对,可以看到,git status
显示当前工作区和Git仓库之间的差异,包括未暂存、已暂存和已提交的文件,并显示change的状态,而git diff
则一行一行地显示暂存区和工作区之间的差异。
最后我们再测试一下git diff --cached
显示暂存区与上一次提交commit的差异:
$ git diff --cached
diff --git a/README.md b/README.md
index 1c8a839..557db03 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1 @@
Hello World
-你好啊! # 上一次提交是有你好啊这行的,暂存区的是删除了该行的
11.git fetch
git fetch
命令用于从远程获取代码库。
学习要点:git fetch
实现什么功能、它和git pull
的区别以及优势。
git fetch [alias]
alias
指的是远程仓库的别名
git pull
也实现从远程仓库拉取代码到本地仓库,并且它会自动合并代码以更新工作区,但是git fetch
只执行拉取代码到本地仓库,并不合并和更新工作区。
它相比git pull
的优势在于:
- 不会自动合并代码,如果你希望手动检查远程仓库的代码并决定是否进行合并操作,则可以使用
git fetch
代码。 - 更灵活,由于
git fetch
命令不会自动合并代码,所以允许你在执行合并前查看远程代码的状态,这是pull无法实现的。 - 减少错误,当你使用
git pull
命令时,Git 会自动合并代码,这可能会导致合并冲突或错误的代码合并。而使用git fetch
命令则可以避免这种风险,因为你可以在合并之前完全查看远程代码的状态。
因此,确认远程代码状态无误后,通常会再执行合并操作。
git merge [alias]/[branchname]
下面我使用我的GitHub上的一个远程仓库为例,测试fetch的使用
在本地初始化一个仓库,连接远程仓库后拉取代码:
$ git init # init
Initialized empty Git repository in C:/Users/22706/Desktop/demo2/.git/
$ git remote add origin https://github.com/WuJianR/Git-Study.git # 连接远程仓库
$ git pull origin master # 拉取远程仓库的master分支
From https://github.com/WuJianR/Git-Study
* branch master -> FETCH_HEAD
$ ls # 验证拉取结果,可以看到工作区的文件和远程仓库的完全对上。
README.md hello.js html.js new-remote-file
$ cat README.md # 查看README.md的内容,便于和后续的操作结果进行对比
Hello World
下面我们修改远程仓库的readme.md
文件,
然后我们在本地使用git fetch
命令更新修改
$ git fetch origin
remote: Enumerating objects: 8, done.
remote: Counting objects: 100% (8/8), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (6/6), 1.49 KiB | 152.00 KiB/s, done.
From https://github.com/WuJianR/Git-Study
b8fbc74..939c250 master -> origin/master
$ cat README.md
Hello World
以上信息b8fbc74..939c250 master -> origin/master
说明远程的master分支已被更新,并且可以看到工作区的README.md
文件并没有更新,接着我们使用git merge origin/master
命令将更新同步到本地。
$ git merge origin/master
Updating b8fbc74..939c250
Fast-forward
README.md | 1 +
1 file changed, 1 insertion(+)
$ cat README.md
Hello World
你好啊!
可以看到成功将更新同步至本地工作区。
12.git rm
git rm
命令用于删除文件
git rm
删除文件有以下几种形式:
将文件从暂存区和工作区删除:
git rm <file>
如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除参数-f
。
git rm -f runoob.txt
如果想把文件从暂存区域删除,但仍然希望保留在当前工作目录中,换句话说,仅是从跟踪清单中删除,使用--cached
选项即可:
git rm --cached <file>
13.git mv
git mv
命令用于移动或重命名一个文件、目录或软连接。
git mv [file] [newfile]
示例:
$ ls
README.md hello.md
$ git mv hello.md hi.md
$ ls
README.md hi.md
可以看到文件名由hello.md
的变为hi.md
。
三、Git进阶
1.git reset
git reset
命令用于回退版本,可以指定退回到某一次提交的版本。
学习要点:git reset
的默认参数是哪个?三个不同的参数实现的回退效果有什么区别?
git reset
命令的语法格式如下:
$ git reset [--soft | --mixed | --hard] [HEAD]
--mixed
为默认,可以不带该参数,用于重置暂存区的文件与上一次的commit保持一致,工作区文件内容保持不变。
$ git reset [HEAD]/commitID
--soft
参数不会改变暂存区和工作区的任何内容,只是将HEAD指针移动到指定提交的版本:
$ git reset --soft [HEAD]/commitID
--hard
参数撤销工作区中所有未提交的修改内容,将暂存区和工作区都回退到某个版本,并删除之前的所有提交信息。
下面我们来看案例,这里初始化了一个只有一次提交记录的Git仓库:
$ ls
readme.md
$ cat readme.md # 展示文件初始内容
hello world!
$ git log # 查看提交记录
commit e2d22d24f6dce634101d56e851dafbce80d0e423 (HEAD -> master)
Author: WuJianRong <2270645424@qq.com>
Date: Tue Jun 13 21:50:23 2023 +0800
init
$ vim readme.md # 使用vim编辑器修改文件
$ cat readme.md # 查看修改结果
hello world!
nihao!
$ git status -s # 查看状态
M readme.md # 前面一个空格,后跟一个红色M 表示被修改 并且还没到暂存区
$ git add . # 添加至暂存区
warning: in the working copy of 'readme.md', LF will be replaced by CRLF the next time Git touches it
$ git status -s # 查看状态
M readme.md # 第一个空格变成绿色的M 表示修改过的文件存到暂存区了
$ git reset # 重置暂存区到和上一次提交一致
Unstaged changes after reset: # 重置之后未暂存的变化
M readme.md # 和git add .之前是一样的状态
$ git status -s
M readme.md # 和git add .之前是一样的状态
$ cat readme.md # 查看工作区文件内容
hello world! # 工作区内容并没有被重置,保持不变。
nihao!
可以看到,我们修改了文件并暂存后再使用git reset --mixed
回退,只能将暂存区的内容回退到上一次commit的版本,工作区是不会回退的。
下面我们再测试一下git reset --hard
$ ls
readme.md
$ cat readme.md # 查看文件内容,用于后续比对
hello world!
新增内容...
$ git log # 可以看到有两条提交记录
commit 8cece25f4c8aabf8d78d2bd8862320e4bb41ea4e (HEAD -> master)
Author: WuJianRong <2270645424@qq.com>
Date: Tue Jun 13 22:10:57 2023 +0800
add
commit e2d22d24f6dce634101d56e851dafbce80d0e423 # 后续我们回退到此版本
Author: WuJianRong <2270645424@qq.com>
Date: Tue Jun 13 21:50:23 2023 +0800
init
$ vim readme.md # 修改文件内容
$ cat readme.md # 查看修改结果
hello world!
新增内容...
addddddddddd
$ git add . # 暂存
$ git commit -m 'adddddddd' # 提交
[master cf58686] adddddddd
1 file changed, 1 insertion(+)
$ git reset --hard e2d22d24f6dce634101d56e851dafbce80d0e423 # 回退到第一个版本
HEAD is now at e2d22d2 init
$ cat readme.md # 查看工作区结果 是Git仓库初始化的内容 版本彻底回退,工作区也没保留
hello world!
$ git log # 提交记录只剩初始化的那条
commit e2d22d24f6dce634101d56e851dafbce80d0e423 (HEAD -> master)
Author: WuJianRong <2270645424@qq.com>
Date: Tue Jun 13 21:50:23 2023 +0800
init
可以看到--hard
参数它删除了回退点之前的所有信息,连工作区都没有保留,因此谨慎使用–hard
参数。
2.git cherry-pick
git cherry-pick
命令可以让你在当前分支上使用其他的分支的commitID
来回退到此提交版本的代码状态。
也就是说,你可以在一个分支上应用另一个分支的某个提交,并将其更新为该提交的版本。
$ git cherry-pick [commitID]
下面我们初始化Git仓库
,并新建分支来测试此命令:
$ git branch
* master
$ ls
readme.md
$ cat readme.md # 查看初始化的文件内容
hello world!
$ git checkout -b newbranch # 新建并切换到新分支
Switched to a new branch 'newbranch'
$ git branch # 查看分支情况
master
* newbranch
$ vim readme.md # 使用vim编辑器修改文件内容
$ cat readme.md # 查看修改结果
hello world!
newbranch content!
$ git add . # 暂存修改
$ git commit -m "新分支内修改" # 提交修改
[newbranch 2d5c494] 新分支内修改
1 file changed, 1 insertion(+)
$ git log # 查看提交日志,获取此分支下的提交ID
commit 2d5c494504c14782c89f8fadb7cdef4e6feaee4d (HEAD -> newbranch)
Author: WuJianRong <2270645424@qq.com>
Date: Tue Jun 13 22:32:14 2023 +0800
新分支内修改
commit e2d22d24f6dce634101d56e851dafbce80d0e423 (master)
Author: WuJianRong <2270645424@qq.com>
Date: Tue Jun 13 21:50:23 2023 +0800
init
$ git checkout master # 切换到master分支
Switched to branch 'master'
$ git cherry-pick 2d5c494504c14782c89f8fadb7cdef4e6feaee4d # 回退newbranch分支的最新提交版本
[master b619412] 新分支内修改
Date: Tue Jun 13 22:32:14 2023 +0800
1 file changed, 1 insertion(+)
$ cat readme.md # 查看结果
hello world!
newbranch content! # 回退其他分支的版本成功
3.git remote
git remote
命令用于管理Git仓库中配置的远程仓库。
学习要点:如何查看Git仓库连接的远程仓库的信息和详细信息?为什么一个链接/仓库名分别有fetch
仓库和push
仓库?
查看当前Git仓库所有的远程仓库信息:
$ git remote -v
显示指定远程仓库的详细信息:
$ git remote show <远程仓库地址>或<远程仓库名>
向GIt仓库中添加一个新的远程仓库,并未其指定一个名称:
$ git remote add <远程仓库名称> <远程仓库地址>
其他命令:
$ git remote rm <远程仓库名> # 删除指定仓库
$ git remote rename <旧名称> <新名称> # 改仓库名
这里我们先测试一下查看远程仓库信息的两个命令:
$ git remote -v
origin https://github.com/WuJianR/Git-Study.git (fetch)
origin https://github.com/WuJianR/Git-Study.git (push)
$ git remote show https://github.com/WuJianR/Git-Study.git
* remote https://github.com/WuJianR/Git-Study.git # 远程仓库地址
Fetch URL: https://github.com/WuJianR/Git-Study.git # fetch命令操作的仓库
Push URL: https://github.com/WuJianR/Git-Study.git # push命令操作的仓库
HEAD branch: master # 主分支
Local ref configured for 'git push':
master pushes to master (local out of date)
这里为什么**一个链接会有两个不同的仓库(Fetch仓库和Push仓库)**呢?
这是便于多人协作开发项目。
Fetch 远程仓库和 Push 远程仓库的区别在于它们实际上完成的操作不同。
具体来说,Fetch 远程仓库是我们从其它仓库中获取代码更新的地方。当我们使用 git fetch
命令时,Git 将从 Fetch 远程仓库中获取最新的代码变更,并将它们保存到本地代码仓库中。这一过程仅涉及本地仓库和远程仓库之间的数据传输,不会改变本地代码仓库中的代码。
而 Push 远程仓库则是我们将本地修改推送到其它仓库的地方。当我们使用 git push
命令时,Git 将把当前本地分支的所有提交推送到指定的 Push 远程仓库中,从而将本地修改同步到远程仓库中。
这种方式使得 Git 可以比较灵活地进行协作开发,比如我们可以从多个源头获取更新(从多个 Fetch 远程仓库中),并将自己所做的修改推送到合适的目标(通过指定对应的 Push 远程仓库)。
下面我们再测试一下向Git仓库添加新的远程仓库命令:git remote add <远程仓库名称> <远程仓库地址>
:
$ git remote add minDemo https://github.com/WuJianR/minDemo.git
$ git remote -v
minDemo https://github.com/WuJianR/minDemo.git (fetch)
minDemo https://github.com/WuJianR/minDemo.git (push)
origin https://github.com/WuJianR/Git-Study.git (fetch)
origin https://github.com/WuJianR/Git-Study.git (push)
可以看到成功添加了一个取名为minDemo
的新仓库,并都具有fetch
仓库和push
仓库。
4.git stash
待补充
5.忽略提交文件.gitignore
待补充
四、工作中常遇见的情况及解决
1.冲突合并
当进行 Git 分支合并时,有时会出现合并冲突的情况。合并冲突是指两个分支上的同一个文件被两个分支中的不同修改所影响,从而无法自动合并。此时,Git 会给出提示,让用户手动解决冲突。
解决合并冲突通常包含以下步骤:
- 确定冲突的文件和位置。
- 手动编辑文件,通过解决冲突来合并两个分支中的修改。
- 将解决冲突后的文件添加到暂存区。
- 对修改进行提交,完成合并操作。
需要注意的是,解决合并冲突需要谨慎对待,因为错误的决策可能导致代码库的不稳定或者损失。如果不确定如何解决冲突,可以寻求其他开发者的帮助,或者查找相关的文档和教程以获取帮助。
下面我们初识化一个带有readme.md
文件的本地仓库,来测试解决冲突合并的问题:
$ git init # init
Initialized empty Git repository in C:/Users/22706/Desktop/demo2/.git/
$ touch readme.md # 新建文件
$ git branch newbranch # 新建分支,报错了,原因是没有commit对象,要有commit记录才能在commit最新版本基础上新建分支
fatal: not a valid object name: 'master'
$ git add . # add暂存
$ git commit -m "初始化一个带有readme.md文件的本地仓库" # commit本地仓库
[master (root-commit) 402da2d] 初始化一个带有readme.md文件的本地仓库
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 readme.md
$ git branch newbranch # 新建newbranch分支
$ git branch # 查看分支列表 验证结果
* master
newbranch
下面我们通过使用vim编辑器修改master分支上的readme.md
里面的内容,
$ cat readme.md # 输出readme.md的内容
# 里面是空的
$ vim readme.md # 进入vim编辑器修改readme.md的内容
$ cat readme.md # 输出readme.md的内容
this is a change! # 这是新增的内容
$ git add . # 暂存更改 警告主要由window和linux换行符表示方式不同导致,忽略
warning: in the working copy of 'readme.md', LF will be replaced by CRLF the next time Git touches it
$ git commit -m "在readme.md中新增一行内容:this is a change!" # 提交更改
[master b34e00e] 在readme.md中新增一行内容:this is a change!
1 file changed, 1 insertion(+)
再切换新的分支,修改readme.md
的同个位置的内容,
$ git checkout newbranch # 切换新分支
Switched to branch 'newbranch'
$ cat readme.md # 内容空
$ vim readme.md # vim编辑器写入内容
$ cat readme.md # 输出readme.md的内容
THIS IS A CHANGE!
$ git add . # 暂存
warning: in the working copy of 'readme.md', LF will be replaced by CRLF the next time Git touches it
$ git commit -m "在readme.md中新增一行内容:THIS IS A CHANGE!" # 提交
[newbranch f2eab5b] 在readme.md中新增一行内容:THIS IS A CHANGE!
1 file changed, 1 insertion(+)
上面我们实现了在两个不同分支的同一个文件的做出不同的修改,下面我们实现合并时会产生合并冲突:
$ git checkout master # 切换到主分支
Switched to branch 'master'
$ git merge newbranch # 合并新分支到主分支
Auto-merging readme.md
CONFLICT (content): Merge conflict in readme.md # 此处提示有冲突在readme.md文件中
Automatic merge failed; fix conflicts and then commit the result. # 提示自动合并失败
$ cat readme.md # 根据提示查看冲突文件,可以看到文件内容被更改了,里面添加了冲突提示
<<<<<<< HEAD
this is a change!
=======
THIS IS A CHANGE!
>>>>>>> newbranch
$ vim readme.md # 进入vim编辑器解决冲突
$ cat readme.md # 文件修改结果[示例]
this is a change!
THIS IS A CHANGE!
$ git diff # 查看差异
diff --cc readme.md
index 5a091c3,eccce91..0000000
--- a/readme.md
+++ b/readme.md
@@@ -1,1 -1,1 +1,2 @@@
+this is a change!
+ THIS IS A CHANGE!
下面我们可以用git add
告诉文件冲突已经解决。
$ git status -s
UU readme.md # 红色的UU表示还在工作区的一个要合并冲突的文件
$ git add . # 表示冲突已解决
$ git status -s
M readme.md # 由红色UU变成了绿色的M,表示在暂存区的被修改过的文件
$ git commit -m "解决冲突" # 提交冲突解决的结果
[master 8475cf1] 解决冲突
$ cat readme.md # 这就是冲突解决的最终结果!
this is a change!
THIS IS A CHANGE!
现在我们成功解决了合并中的冲突,并提交了结果。