前言:
本文记录学习使用 Git 版本管理工具的学习笔记,通过阅读参考链接中的博文和实际操作,快速的上手使用 Git 工具。
本文参考了引用链接博文里的内容。
引用:
Git使用教程-配置管理
git reset详解-CSDN博客
3、Git使用不完全指南:git/github/gitlab/gitee的区别,原理和常用命令总结_gitlab github gitee-CSDN博客
正文
1. Git的3棵树
在本地仓库中Git维护三棵树,这是Git的核心构架。这三棵树分别是:工作区域,暂存区域,和Git仓库。
- 工作区域(Working Directory),就是你平时存放项目代码的地方
- 暂存区域(Stage),用于临时存放你的改动,事实上它只是一个文件,保存即将提交的文件列表信息。
- Git仓库(Repository),就是安全村行数据的位置,这里有你提交的所有版本的数据。其中,HEAD指向最新放入仓库的版本(这第3棵树,确切的说,应该是Git仓库中的HEAD指向的版本)。
Git的一般工作流程是:
- 在工作目录中添加,修改文件
- 将需要进行版本管理的文件放入暂存区
- 将暂存区的文件提交到Git仓库
因此,Git管理的文件有三种状态:已修改(modified),已暂存(staged),和已提交(commited),依次对应上面的每一个流程。
2. Git初始化一个仓库
###创建一个目录,这个目录将会作为git repository 仓库
dimon@dimon-VirtualBox:~$ mkdir gittest
###进入这个目录
dimon@dimon-VirtualBox:~$ cd gittest/###执行'git init'命令初始化git项目,初始化成功之后会有一个隐藏的'.git'文件
dimon@dimon-VirtualBox:~/gittest$ git init
已初始化空的 Git 仓库于 /home/dimon/gittest/.git/###
dimon@dimon-VirtualBox:~/gittest$ git add README.md
fatal: 路径规格 'README.md' 未匹配任何文件###在当前目录里创建一个文件'README.md',md表示Markdown文件
dimon@dimon-VirtualBox:~/gittest$ touch README.md
###'git add'命令把文件加入暂存区
dimon@dimon-VirtualBox:~/gittest$ git add README.md###'git commit'命令把文件提交到git仓库repository,'-m'后面是提交日志
dimon@dimon-VirtualBox:~/gittest$ git commit -m "add a readme file"
[master (根提交) d7c707d] add a readme file1 file changed, 0 insertions(+), 0 deletions(-)create mode 100644 README.md
dimon@dimon-VirtualBox:~/gittest$
3. Git使用教程--状态
怎么知道哪些文件是新添加的,哪些文件已经加入到暂存区?总不能自己用文本记录下来吧?
当然不,作为通用的版本管理系统,我们遇到的问题,Git都已经有了解决方案,可以用 'gti status‘ 命令查看当前状态。
dimon@dimon-VirtualBox:~/gittest$ git status
On branch master
nothing to commit, working tree clean
dimon@dimon-VirtualBox:~/gittest$
on branch master:我们位于一个叫做'master’的分支里,这个是默认的分支。
nothing to commit, working directory clean:说明你的工作目录当前是“干净的”,没有需要提交的文件(意思就是自上次提交之后工作目录中的内容压根就没有改动过)。
在工作目录里添加 LICNESE 文件,并用Notepad++修改文件'LICENSE'的内容。输入 'git status'命令,提示如下:
###在工作目录下创建了新文件'LICENSE'
dimon@dimon-VirtualBox:~/gittest$ touch LICENSE
dimon@dimon-VirtualBox:~/gittest$ ls
LICENSE README.md###编辑文件的内容
dimon@dimon-VirtualBox:~/gittest$ vi LICENSE
dimon@dimon-VirtualBox:~/gittest$ ###'git status'命令查看文件的状态
dimon@dimon-VirtualBox:~/gittest$ git status
On branch master
Untracked files:(use "git add <file>..." to include in what will be committed)LICENSEnothing added to commit but untracked files present (use "git add" to track)
dimon@dimon-VirtualBox:~/gittest$
Untracked files 说明存在未追踪文件(下面红色的那个)
所谓的“未追踪”文件,是指那些新添加的并且未被加入到暂存区域或者提交的文件。它们处于一个逍遥法外的状态,但你一旦将它们假如暂存区或提交到Git仓库,他们就开始受到Git的“跟踪”。
圆括号是的提示是 git 给用户的提示:使用 git add <file> 命令将待提交的文件添加到暂存区域。
dimon@dimon-VirtualBox:~/gittest$ git add LICENSE
dimon@dimon-VirtualBox:~/gittest$ git status
On branch master
Changes to be committed:(use "git reset HEAD <file>..." to unstage)new file: LICENSEdimon@dimon-VirtualBox:~/gittest$
‘use git reset HEAD <file> to unstage’ 的意思是,如果你反悔了,你可以使用 git reset HEAD 命令恢复暂存区域。如果后面接文件名,表示恢复该文件;如果不接文件名,则表示上一次添加的文件。
再次添加到“暂存区域”,然后执行 'git commit -m "add a license file"'命令:
###把文件'LICENSE'加到暂存区
dimon@dimon-VirtualBox:~/gittest$ git add LICENSE###查看当前文件状态,暂存区有文件未提交
dimon@dimon-VirtualBox:~/gittest$ git status
On branch master
Changes to be committed:(use "git reset HEAD <file>..." to unstage)new file: LICENSE###把暂存区提交到Git 仓库
dimon@dimon-VirtualBox:~/gittest$ git commit -m "add a license file"
[master a588543] add a license file1 file changed, 6 insertions(+)create mode 100644 LICENSE###查看当前文件状态
dimon@dimon-VirtualBox:~/gittest$ git status
On branch master
nothing to commit, working tree clean
dimon@dimon-VirtualBox:~/gittest$
关于修改
突然发现版权那块忘记写上自己名字了...
打卡LINCESE文件,修改"Copyrith (c) 2024 dimon",保存。
执行 git status 命令:
dimon@dimon-VirtualBox:~/gittest$ vi LICENSE
dimon@dimon-VirtualBox:~/gittest$ git status
On branch master
Changes not staged for commit:(use "git add <file>..." to update what will be committed)(use "git checkout -- <file>..." to discard changes in working directory)modified: LICENSEno changes added to commit (use "git add" and/or "git commit -a")
dimon@dimon-VirtualBox:~/gittest$
由于对工作目录中的文件进行了修改,导致这个文件和“暂存区域”的文件不匹配了,所以Git提了两条建议
- 使用 git add 将工作目录的新版本覆盖暂存区域的几版本,然后提交
- 使用 git checkout将暂存区域的旧版本覆盖工作目录的新版本(危险操作:相当于丢弃工作目录的修改)
还有一种情况没有分析,大家先把新版本文件覆盖掉暂存区域的旧版本:
dimon@dimon-VirtualBox:~/gittest$ git add LICENSE
dimon@dimon-VirtualBox:~/gittest$ git status
On branch master
Changes to be committed:(use "git reset HEAD <file>..." to unstage)modified: LICENSEdimon@dimon-VirtualBox:~/gittest$
然后我们打开LINCENSE文件,将 Dimon 修改为 Dimon.chen
dimon@dimon-VirtualBox:~/gittest$ vi LICENSE
dimon@dimon-VirtualBox:~/gittest$ git status
On branch master
Changes to be committed:(use "git reset HEAD <file>..." to unstage)modified: LICENSE (绿色)Changes not staged for commit:(use "git add <file>..." to update what will be committed)(use "git checkout -- <file>..." to discard changes in working directory)modified: LICENSE (红色)dimon@dimon-VirtualBox:~/gittest$
这次的情况:被绿的LINCENSE文件存放在暂存区(待提交),同时红色的LICENSE说明文件还在工作目录等待添加到暂存区域。
这种情况你应该意识到这里存在两个不同版本的 LICENSE 文件,如果此时你直接执行了 commit 命令,那么提交的是暂存区域的版本,如果你希望提交工作目录的新版本,那么你先执行 add 米宁覆盖暂存区域,然后在提交。
一步到位
从工作目录一步添加到Git仓库: git commit -am "说明"
dimon@dimon-VirtualBox:~/gittest$ git commit -am "change the license file"
[master 1657372] change the license file1 file changed, 1 insertion(+), 1 deletion(-)
dimon@dimon-VirtualBox:~/gittest$
-a的已是是add。
git log 查看历史操作记录
3. Git使用教程5
有关回退的命令有两个: reset 和 checkout
先执行Git log命令,将此事的Git仓库可视化
三棵树的情况
回滚快照
注:快照即提交的版本,每一个版本我们称之为一个快照。
现在我们利用 reset 命令回滚快照,帮看看Git仓库和三棵树分别发生了什么。
执行: git reset HEAD ~ 命令:
主: HEAD 表示最新提交的快照,而 HEAD ~表示HEAD的上一个快照,HEAD~~表示上上个快照。如果表示10个快照可以使用 HEAD~10
###现在'git log'能看到3个版本,HEAD指向第3次提交
dimon@dimon-VirtualBox:~/gittest$ git log
commit 1657372baaca2c3c797d3e2f1676b087810e655b (HEAD -> master)
Author: dimon <1181302388@qq.com>
Date: Fri Mar 15 10:48:34 2024 +0800change the license filecommit a588543db607cffd7ec6d2a6504175639d8ed61b
Author: dimon <1181302388@qq.com>
Date: Fri Mar 15 10:13:20 2024 +0800add a license filecommit d7c707d2c4b2dc4c332e252c419f47c4eabc4846
Author: dimon <1181302388@qq.com>
Date: Thu Mar 14 18:21:33 2024 +0800add a readme file
dimon@dimon-VirtualBox:~/gittest$ git reset HEAD~
Unstaged changes after reset:
M LICENSE
dimon@dimon-VirtualBox:~/gittest$ git status
On branch master
Changes not staged for commit:(use "git add <file>..." to update what will be committed)(use "git checkout -- <file>..." to discard changes in working directory)modified: LICENSEno changes added to commit (use "git add" and/or "git commit -a")###现在'git log'能看到2个版本,HEAD指向第2次提交
dimon@dimon-VirtualBox:~/gittest$ git log
commit a588543db607cffd7ec6d2a6504175639d8ed61b (HEAD -> master)
Author: dimon <1181302388@qq.com>
Date: Fri Mar 15 10:13:20 2024 +0800add a license filecommit d7c707d2c4b2dc4c332e252c419f47c4eabc4846
Author: dimon <1181302388@qq.com>
Date: Thu Mar 14 18:21:33 2024 +0800add a readme file
dimon@dimon-VirtualBox:~/gittest$
此时我们回滚到第二课树(暂存区域)
第一次执行reset后的Git仓库
git reset HEAD~ 命令其实是 git rest --mixed HEAD~ 的缩写,--mixed选型是默认的。
git reset HEAD~ 命令其实影响了两棵树:首先是移动 HEAD 的指向,将其指向上一个快照(HEAD~);然后再讲该位置的快照回滚到暂存区域。
--soft 选项
git reset --soft HEAD~ 命令就相当于只移动 HEAD 的指向,但并不会将快照回滚到暂存区。相当于撤销了上一次的提交(commit)。以不小心提交了,后悔了,那么你就执行 git reset --soft HEAD~ 命令(此时执行命令 git log 命令,也不会再看到已经撤销的那个提交)。
--hard选项
reset不仅移动HEAD的指向,将快照回滚到暂存区,它还将暂存区的文件还原到工作目录。
回滚指定快照
reset 不仅可以回滚指定快照,可以可以回滚个别文件
命令格式: git reset 快照 文件名/路径
这样,它就会忽略移动HEAD指向这一步(因为你知识回滚快照的部分内容,并不是整个快照,所以HEAD的指向不应发生改变),直接将指定快照的指定文件回滚到暂存区域。
不仅可以往回滚,还可以往前滚。
这里需要强调的是: reset 不仅是一个“复古”的命令,它不仅可以回到过去,还可以到“未来”。
唯一的前提条件是:你需要指导指定快照的ID号。
哪如果不小心把命令窗口关了不记得ID号怎么办?
命令: git reflog
Git揭露的每一次操作的版本号ID。
4. Git使用教程6--版本对比
目的:对比版本之间有哪些不同。
准备工作
创建一个叫做MyProject2的新文件作为此次演示的目录,初始化Git。
创建一个game.py的文件,将下面代码拷贝进去。
再新建一个README文件,写清楚这是第一个课后作业。
执行 git add README game.py 命令将文件添加到暂存区,紧着执行 git commit -m '猜数字游戏' 提交一个项目快照。
dimon@dimon-VirtualBox:~/MyProject2$ git init
Initialized empty Git repository in /home/dimon/MyProject2/.git/
dimon@dimon-VirtualBox:~/MyProject2$ touch game.py
dimon@dimon-VirtualBox:~/MyProject2$ vi game.py
dimon@dimon-VirtualBox:~/MyProject2$ touch README
dimon@dimon-VirtualBox:~/MyProject2$ vi README
dimon@dimon-VirtualBox:~/MyProject2$ git add README game.py
dimon@dimon-VirtualBox:~/MyProject2$ git commit -m "猜数字游戏"
[master (root-commit) c1df484] 猜数字游戏2 files changed, 22 insertions(+)create mode 100644 READMEcreate mode 100644 game.py
dimon@dimon-VirtualBox:~/MyProject2$ vi game.py
dimon@dimon-VirtualBox:~/MyProject2$ vi README
dimon@dimon-VirtualBox:~/MyProject2$ git diff
然后修改 game.py 文件内容化和 README文件内容。
比较暂存区域域工作目录的差异:
直接执行 git diff 命令是比较暂存区域与工作目录的文件内容。
现在来解释一下上面每一行的含义:
第一行: diff --git a/README b/README
表示对比的存放在暂存区域的README和工作目录的README
第二行: index 2146df9..7d37b6e 100644
表示对应的文件ID分别是2146df9 和7d37b6e,左边暂存区域与,右边当前目录。最后的100644是指定文件的类型和权限。
第三行:--- a/README
---表示的是就文件(存放在暂存区域)
第四行:+++ b/README
+++表示该文件是新文件(存放在工作区域)
第五行:@@ -1 +1,2 @@
以@@开头和结束,总监的-表示旧文件,+表示新文件,后面的数字表示开始行号,显示行数。
第6,7行:
home work 2024-03-15 11:37
+《零基础入门学习Git》
这是将两个文件合并显示的结果,前面有+的绿色一行说明是新文件所独有的,浅灰色的说明是两个文件共享的。所有,+1,2 表示线文件合并显示中从第一行开始,显示2行内容。那为啥-1后面没有显示行数?因为在合并显示结果中,旧文件已经完全包含在新文件中了(也就是旧文件没有自己的“独有”内容)。
最后的(:)是什么?
意思是窗口太小,没有办法完全显示,正在等待命令(Vim编程知识)
移动命令:
- j,k:向下/向上一行
- f,b:向下翻页/向上翻页
- d,u:向下翻半页/向上翻半页
跳转命令
g, G:跳转到第一行/跳转到最后一行
搜索命令
输入反斜杠(/)或问号(?),后面搜索关键字
区别:斜杠(/)表示从当前位置向下搜索,问号(?)表示从当前位置向上搜索
接着输入n表示顺着当前的搜索方向快速调到下一个匹配位置,大写的N则是与当前搜索方向相反。
退出和帮助
在点点(:)后面输入q,表示退出diff;输入h表示进入帮助页面,你会看看到很多命令和功能,输入q可以退出帮助页面。
比较两个历史快照
我们执行命令 git commit -am "添加功能:玩家只有三次机会”命令,添加并提交目录中的所有文件。执行git log命令,可以看到Git仓库中已经有连个快照了。
###查看当前'git status'状态
dimon@dimon-VirtualBox:~/MyProject2$ git status
On branch master
Changes not staged for commit:(use "git add <file>..." to update what will be committed)(use "git checkout -- <file>..." to discard changes in working directory)modified: READMEmodified: game.pyno changes added to commit (use "git add" and/or "git commit -a")###‘git commit -am’ 一次性提交工作目录到暂存目录和Git仓库
dimon@dimon-VirtualBox:~/MyProject2$ git commit -am "添加功能:玩家只有三次机会"
[master 7e3a972] 添加功能:玩家只有三次机会2 files changed, 9 insertions(+), 4 deletions(-)
dimon@dimon-VirtualBox:~/MyProject2$###‘git log’查看当前Git仓库快照,当前有两个快照
dimon@dimon-VirtualBox:~/MyProject2$ git log
commit 7e3a9725b0e348a938153d37b6450eb74eb5febb (HEAD -> master)
Author: dimon <1181302388@qq.com>
Date: Fri Mar 15 14:37:26 2024 +0800添加功能:玩家只有三次机会commit c1df484caeee134d604a08e6957b28fb836c1ea3
Author: dimon <1181302388@qq.com>
Date: Fri Mar 15 11:38:14 2024 +0800猜数字游戏
dimon@dimon-VirtualBox:~/MyProject2$
执行命令 git diff 7e3a972 c1df484 命令,既可以比较Git仓库中两个快照的差异
git diff 7e3a9725b0e348a938153d37b6450eb74eb5febb c1df484caeee134d604a08e6957b28fb836c1ea3
比较当前工作目录和Git仓库中的快照
我们稍微改动一下README的内容
目前我们的Git仓库应该是
三棵树应该是
比较之前版本和快照与当前工作目录的内容
输入: git diff ed3708c 命令即可
比较当前版本库快照与当前工作目录的内容,
输入: git diff HEAD 命令即可
比较Git仓库与暂存区域
执行 git add README.md命令,将第三版的README文件添加到暂存区域。
然后三棵树就是这样的了:
如果希望比较最新提交的快照和暂存区域的文件,只需要执行命令 diff --cached 命令;当前艳也可以指定其他快照,就是需要多协商一个ID值,即 git diff --cached ID号。
哈哈,感觉有点乱,实际上就是这三棵树之间相互比较,最后附上一个总结图片。
5. Git使用教程7--修改最后一次提交,删除文件和重命名文件
问题出现
Situation one: 版本一提交(commit)到仓库,突然想起漏掉了两个文件还没有添加(add)
Situation two: 版本一提交(commit)到仓库,突然兴起版本说明写的不够全面,无法彰显你本次修改的重大意义....
由于使用 reset 命令过于繁琐,需要提交一个新的版本,这里可以使用带 --amend 选项的 commit 命令(即 git commit -amend) Git 会“更正”最近一次的提交。由于这里没有-m说明,会进入一下页面。
这个编辑界面知识让你编辑提交说明而已。如果不需要修改,可以连续按两次大写Z来退出,或者先按下(:),再输入q!退出,Git会保留旧的提交说明。如果需要提交说明又不想出现这么繁琐的方式,输入 git commit --amend -m "新的提交说明"就可以。
删除文件
问题1:不小心删除文件怎么办?
现在从工作目录总手动删除 README 文件,然后执行 git status命令:
提醒使用 checkout 命令可以将暂存区域的文件恢复到工作目录:
dimon@dimon-VirtualBox:~/MyProject2$ git checkout -- README
dimon@dimon-VirtualBox:~/MyProject2$
注意 "--"两端各有一个空格。
问题2:如何彻底删除一个文件哪?
假如不小心把小黄图下载到工作目录,然后又不小心提交到了Git仓库。
dimon@dimon-VirtualBox:~/MyProject2$ touch yellow.jpg
dimon@dimon-VirtualBox:~/MyProject2$ git add yellow.jpg
dimon@dimon-VirtualBox:~/MyProject2$ git commit -m "有一次伟大的改进"
[master eeba2c4] 有一次伟大的改进1 file changed, 0 insertions(+), 0 deletions(-)create mode 100644 yellow.jpg
dimon@dimon-VirtualBox:~/MyProject2$
执行 git rm yellow.jpg 命令
dimon@dimon-VirtualBox:~/MyProject2$ git rm yellow.jpg
rm 'yellow.jpg'
dimon@dimon-VirtualBox:~/MyProject2$
此时工作目录中的小黄图(yellow.jpg)已经被删除了...
但是执行 git status 命令,你仍然发现Git还不肯松手
dimon@dimon-VirtualBox:~/MyProject2$ git status
On branch master
Changes to be committed:(use "git reset HEAD <file>..." to unstage)deleted: yellow.jpgdimon@dimon-VirtualBox:~/MyProject2$
意思说它在仓库快照里发现有一个 'yellow.jpg'的文件,但似乎在暂存区域和当前工作目录不见了。
此时可以执行 git reset -- soft HEAD~ 命令将快照回滚到上一个位置,然后重新提交,Git就不会再提小黄图的事了。
dimon@dimon-VirtualBox:~/MyProject2$ git reset --soft HEAD~
dimon@dimon-VirtualBox:~/MyProject2$ git status
On branch master
nothing to commit, working tree clean
dimon@dimon-VirtualBox:~/MyProject2$
注意:rm命令删除的只是工作目录和暂存区的文件(即取消跟踪,下一次提交是不纳入版本管理)
问题3:我在工作目录里中增加了一个 test.py 文件,然后执行 git add test.py 命令将其加入到暂存区,此时我修改了test.py文件的内容,那么暂存区和工作目录就是两个不同的test.py文件了,此时如果我执行 git rm test.py 命令,Git会下意识的阻止我,这怎么办?
dimon@dimon-VirtualBox:~/MyProject2$ git status
On branch master
Changes to be committed:(use "git reset HEAD <file>..." to unstage)new file: test.pyChanges not staged for commit:(use "git add <file>..." to update what will be committed)(use "git checkout -- <file>..." to discard changes in working directory)modified: test.pydimon@dimon-VirtualBox:~/MyProject2$
dimon@dimon-VirtualBox:~/MyProject2$ git rm test.py
error: the following file has staged content different from both the
file and the HEAD:test.py
(use -f to force removal)
dimon@dimon-VirtualBox:~/MyProject2$
因为两个不同内容的同名文件,谁知道你是不是搞清楚了都要删除?还是提醒一下好,别等一下出错又要赖机器。。。
根据提示,执行 git rm -rf test.py 命令就可以把两个都是删除
问题4:我只想删除暂存区域的文件,保留工作目录,应该怎样操作?
执行 git rm --cached “文件名” 命令。
重命名文件
直接在工作目录重命名文件,执行 git status 会出错
正确的姿势应该是:
git mv 旧文件名 新文件名
dimon@dimon-VirtualBox:~/MyProject2$ git mv test.py TEST.py
dimon@dimon-VirtualBox:~/MyProject2$ git status
On branch master
Changes to be committed:(use "git reset HEAD <file>..." to unstage)renamed: test.py -> TEST.pydimon@dimon-VirtualBox:~/MyProject2$ git commit -m 'rename test.py to TEST.py'
教程彩蛋6:
如何让Git识别某些格式的文件,然后自动不跟踪他们?
比如工作目录里有三个文件 1.temp, 2.temp, 3.temp ,我们不希望后缀名为 .temp 的文件被跟踪,可是每次 git status 都会出现
6. Git使用教程7--创建和切换分支
分支是什么?
假设你的大项目已经上线了(有上百万人在使用),过了一段时间及突然觉得应该添加一些新功能,但是为了保险起见,你肯定不能再当前项目上直接开发,这时候你就有创建分支的必要了。
对比其他版本控制系统而言,创建分支常常需要创建一个源代码目录的副本,项目越大,耗费的时间就越多;而Git由于每一个节点都已经是一个完整的项目,所以只需要创建多一个“指针”(像master)指向分支开始的位置即可。
创建分支
来到之前创建的项目MyProject2,
当前项目状态如下
执行Git status查看状态
可以看到README文件被修改并添加到暂存区域(还没有提交),所以当前三棵树应该是这样:
创建分支,使用 “git branch 分支名” 命令:
dimon@dimon-VirtualBox:~/MyProject2$ git branch feature
dimon@dimon-VirtualBox:~/MyProject2$
没有任何提示说明分支创建成功(一般也不会失败,除非创建了同名的分支会提醒你一下),此时执行 git log --decorate 命令查看:
如果希望以“精简版”的方式显示,可以加上一个 --oneline 选项(即 git log --decorate --oneline),这样就只用一行来显示快照记录。
可以看到最新的快照后面多了一个(HEAD->master,fature)
它的意思是:目前有两个分支,一个是主分支(master),一个是我们刚才创建的新分支(feature),然后HEAD指针仍然指向默认的master分支。
目前仓库中的快照应该是这样的。
切换分支
现在我们需要将工作目录环境切换到新创建的分支(feature)上,使用的是我们之前语言又止的checkout命令。执行 git checkout feature 命令:
dimon@dimon-VirtualBox:~/MyProject2$ git checkout feature
M README
Switched to branch 'feature'
dimon@dimon-VirtualBox:~/MyProject2$
这样HEAD指针就指向了 feature 分支了:
dimon@dimon-VirtualBox:~/MyProject2$ git log --decorate --oneline
d97db41 (HEAD -> feature, master) rename test.py to TEST.py
4bd1779 add faile test.py
67d158e 添加功能:玩家只有三次机会
c1df484 猜数字游戏
dimon@dimon-VirtualBox:~/MyProject2$
现在我们再进行一次提交(暂存缓冲区还有一个更改的文件没有提交呢):
dimon@dimon-VirtualBox:~/MyProject2$ git commit -m "修改了说明文件"
[feature d158dfb] 修改了说明文件1 file changed, 1 insertion(+)
dimon@dimon-VirtualBox:~/MyProject2$
现在仓库中的快照应该是这个样子(提交的快照由当前HEAD指针指向的分支来管理)
然后我们再讲 HEAD 指针切回 master 分支
dimon@dimon-VirtualBox:~/MyProject2$ git checkout master
Switched to branch 'master'
dimon@dimon-VirtualBox:~/MyProject2$
细心的朋友会发现上一次对 README 文件的修改已经当然无存了,这是因为我们工作目录已经回到了 master 分支的状态。
现在对 README 文件做修改(随意修改),然后执行 git commit -m "再次修改说明文件":
dimon@dimon-VirtualBox:~/MyProject2$ git commit -am "再次修改说明文件"
[master 756c48b] 再次修改说明文件1 file changed, 1 insertion(+)
dimon@dimon-VirtualBox:~/MyProject2$
目前仓库快照变成了这样
执行 git log --oneline --decorate --graph --all 命令
--graph 选项表示让 Git 绘制分支图,--all 表示显示所有分支
7. Git使用教程9-合并和删除分支
合并分支
当一个子分支的使命完成后,它就应该回归到主分支中去。
合并分支我们使用 merge 命令,执行 git merge feature 命令,将 feature 分支合并到 HEAD 所在的分支(master)上:
dimon@dimon-VirtualBox:~/MyProject2$ git merge feature
Auto-merging README
CONFLICT (content): Merge conflict in README
Automatic merge failed; fix conflicts and then commit the result.
dimon@dimon-VirtualBox:~/MyProject2$
从Git提示的内容来看,我们知道这次合并并没有成功,Git提示:
合并 README 时出现了冲突。
所以自动合并失败;请修改冲突的内容请从新提交快照。
意思是说现在你需要先解决冲突的问题,Git才能进行合并操作。所谓冲突,无非就是像两个分支中存在同名但内容却不相同的问价,Git不知道你要舍弃哪一个或者保留哪一个,所以需要你自己来决定。
此时执行 git status 命令也会显示需要你解决冲突:
然后Git会在有冲突的文件中加一些标记,可以打开 README 文件查看一下:
以 ‘=====’为界,上到"<<< HEAD"表示当前分支,下到">>> fature"表示 待合并的 feature 分支,之间的内容就是冲突的地方。
现在我们将README 同意修改掉(去掉 <<<< HEAD 等内容)
保存文件,然后提交快照
dimon@dimon-VirtualBox:~/MyProject2$ git add README
dimon@dimon-VirtualBox:~/MyProject2$ git commit -m '解决冲突'
[master 90086a5] 解决冲突
dimon@dimon-VirtualBox:~/MyProject2$
执行 git log --decorate --all --graph --oneline 命令,可以看到此时分支已经自动合并了
当然,如果不存在冲突,就不用搞这么多了... ,举个例子:
执行: git checkout -b feature2 命令,(相当于 git branch feature2 和 git checkout feature2 两个命令的合体)
dimon@dimon-VirtualBox:~/MyProject2$ git branch feature2
dimon@dimon-VirtualBox:~/MyProject2$ git checkout feature2
Switched to branch 'feature2'
dimon@dimon-VirtualBox:~/MyProject2$
在工作目录随便创建一个文件 feature2.txt 并提交快照。
dimon@dimon-VirtualBox:~/MyProject2$ git add feature2.txt
dimon@dimon-VirtualBox:~/MyProject2$ git commit -m "我就是试试"
[feature2 11c5b05] 我就是试试1 file changed, 1 insertion(+)create mode 100644 feature2.txt
dimon@dimon-VirtualBox:~/MyProject2$
执行 git log --decorate --oneline --graph --all 命令:
可以看到, feature 分支比master分支快了一步。现在我们切换回 master 分支,并将 feature2 分支合并进来:
###当前工作目录切换到 master 分支
dimon@dimon-VirtualBox:~/MyProject2$ git checkout master
Switched to branch 'master'
dimon@dimon-VirtualBox:~/MyProject2$ ###'git merge'合并 feature2 分支到 master 分支
dimon@dimon-VirtualBox:~/MyProject2$ git merge fature2
merge: fature2 - not something we can merge
dimon@dimon-VirtualBox:~/MyProject2$ git merge feature2
Updating 90086a5..11c5b05
Fast-forwardfeature2.txt | 1 +1 file changed, 1 insertion(+)create mode 100644 feature2.txt
dimon@dimon-VirtualBox:~/MyProject2$
这次 git 只显示了 fast-forward (快进)这个词,这是因为 feature2 这个分支的父节点是 master 分支,所以 git 只需要简单的而移动 master 的指向就可以。
执行 git log --decorate --all --graph --oneline 命令:
删除分支,使用 git branch -d "分支名" 命令:
dimon@dimon-VirtualBox:~/MyProject2$ git branch -d feature
Deleted branch feature (was d158dfb).
dimon@dimon-VirtualBox:~/MyProject2$
执行 git log --decorate --all --graph --oneline 命令:
用于Git的分支原理实际上只是通过一个指针记载,所以创建和删除分支都几乎是瞬间完成。
注意:如果试图删除为合并的分支,Git会提示你“该分支未完全合并,如果你确定要删除,请使用 git branch -D 分支名”命令。
彩蛋:Git的两种合并方式--Fast-forward和Three-way-merge
Fast-forward
所谓的 Fast-forward 就是当待合并的分支位于目标分支的直接上游时,Git只需要把目标分支的指针直接移动既可以实现合并。
如下图 master 分支就是位于 feature2 分支的直接下游:
将 fearture2 分支合并到 master 分支,只需要移动 master 分支即可。
Three way Merge
如果待合并的两个分支不在同一条线上,那么进行合并就需要解决一个根本的问题--冲突!
为何两个分支在同一条线上就不会出现冲突?
因为Git的快照是按时间顺序提交的,所以在同一条时间线上的两个快照,他们是有先后顺序,尽管两者可能出现同名文件不同内容,Git会认贼这是“改变”而不是“冲突”。
举个例子:
合并 C3 和 C4 得到C5,但C5应该如何处理冲突哪?
SVN会把问题抛给用户,让用户自行解决;Git则显得更为高明,它会找到第三个快照,然后综合三者特点自动解决冲突。
那第三个亏按照应该如何决定哪?
没错,应该找两者的共同“祖先”作为参照物,一比较就知道两个分支都干了什么?
图片中C3, C4的共同祖先是C1,可以看到C3和C4分别增加了 test2.py 和 test1.py 两个文件。因对比之后发现三者并没有冲突,所以C5应该是三者的合体,集同事拥有 comm.py test1.py 和 test2.py 三个文件。
另外,值得一提的是,Git的这种合并方式也适用于同名文件的不同更改。
举个例子:
这里C3和C4都只有一个文件 test.txt ,但是内容却不一样。如果这样合并,你猜Git会不会报“冲突”?
答案是不会的!
因为 Git 找到了它们的共同祖先 C1,可以看到C3 和 C4 都是在 C1 的基础上机型添加(C4在第二行添加了"I",C3在第四行添加了"FishC",C1的第三行“Love”是他们共同拥有的),同时在每一行并没有产生冲突的地方,所以Git会自动合并的C5是这样的。
#test.txt
I
Love
FishC
注意:如果Git检测到同一行有不同的内容,还会报冲突并让你自行决定去谁留谁的。
8. Git使用教程10--匿名分支和chekout命令
再论checkout
事实上,checkout 命令有两种功能。
- 从历史快照(或暂存区域)中拷贝文件到工作目录
- 切换分支
从历史快照(或暂存区)中拷贝文件到工作目录
当给定某个文件名时,Git会从指定的提交中拷贝文件到暂存区域和工作目录。比如执行 git checkout HEAD~ README 命令会将上一个快照中的 README 文件复制到工作目录和暂存区域中。
如果命令中没有指定具体的快照ID,将从暂存区域恢复指定文件到工作目录(git checkout README)
有些朋友会问:“上一次看你在文件前面有加两个横杠 -- ,这次怎么就没有了呢?”
问题的好?Git提醒你写成 git checkout -- README 的形式,那是为了预防你恰好有一个分支叫做 REDME,那么它就搞不懂你要恢复文件还是要切换分支了,所以约定两个横杠(--)后面跟的是文件名。
反过来说,如果你确保你没有一个叫做 README 的分支,你直接写 git checkout README 也是妥妥没有问题的。
切换分支
首先我们知道 Git 的分支其实就是添加一个指向快照的指针,其次我们知道切换分支除了修改 HEAD 指针的的指向,还会改变暂存区域和工作目录的内容。
所以执行 git chekout 373c0 命令,Git 主要做了下面两件事(当然事实上Git还做了更多)
那回过头来 ,如果我们只想恢复指定的文件/路径,那么我们只需要指定具体的文件,Git就会忽略第一步修改HEAD指向的操作,这不正是根之前讲的reset命令一致的么?
checkout 命令和 reset 命令的区别
恢复文件
下面开始划重点:
它们的区别是 reset 命令只将指定文件恢复到暂存区域(--mixed),而 checkout 命令同时覆盖暂存区域和工作目录。
注意:也许你师徒使用 git rest --hard HEAD~ README 命令让 reset 同时覆盖工作目录,但Git会告诉你这是徒劳的(此时 reset 不允许使用 --sort 或 hard 选项)
这样看来,在恢复文件方面, reset 命令要比 chekout 命令更安全一些。
恢复快照
reset 命令是用来“回到过去”的,根据选项的不同,reset命令将移动 HEAD 指针(--soft)-> 覆盖暂存区域(--mixed,默认)->覆盖工作目录(--hard)。
checkout 命令虽说是用于切换分支,但前面你也看到了,它事实上也是通过移动 HEAD 指针和覆盖暂存区域,工作目录来实现的。
哪问题来了:他们有什么区别哪?
下面开始haul重点了:
- 第一个区别是,对于 reset --hard 命令来说,checkout命令更安全。因为 Chekout命令在切换分支前会先检查一下当前工作状态,如果不是'clean'的话,Git不会允许你这样做;而 'reset --hard'命令则是直接覆盖掉所有数据。
- 另一区别是如何更新 HEAD 的指向,reset 命令会移动 HEAD 所在的分支指向,而checkout命令只会移动HEAD自身来指向另外一个分支。
看到文字你肯定懵,我们举例说明: