Git教程
Git教程
- 1. Git安装
- 2. Git初次配置
- 3. Git理论基础
- 4. 实战
- 4.1 初始化Git`git init`
- 4.2 将文件放入暂存区域`git add`
- 4.3 将文件提交到Git仓库`git commit`
- 4.4 总结
- 4.5 Github克隆仓库`git clone http...`
- 5. 工作区域新增或修改文件
- 5.1 查看状态`git status`
- 5.2 使用`git add`提交到暂存区域:
- 5.3 用暂存区域旧一版的文件覆盖工作区域`git checkout --文件`
- 5.4 反悔提交到暂存区域`git reset HEAD 文件名`
- 5.5 一步到位`git commit -am`
- 6. 回到过去`git reset`
- 7. 版本对比
- 7.1 比较暂存区域与工作目录`git diff`
- 7.2 比较两个历史快照`git diff 6e2697 ed3708c`
- 7.3 比较当前工作目录和Git仓库的快照`git diff ed3808c`
- 7.4 比较暂存区域和Git仓库快照`git diff -cached`
- 7.5 总结
- 8. 开发中常见情况
- 8.1 重新提交最后次提交`git commit --amend`
- 8.2 删除文件`git rm 文件名`
- 8.3 重命名文件`git mv a b`
- 8.4 忽略跟踪一些指定文件`.gitignore`
- 9. 创建和切换分支
- 10. Git实际工作流程
- 10.1 开发分支(develop)
- 10.2 功能分支(feature)
- 10.3 预发布分支(release)
- 10.4 维护分支(hotfix)
- 10.5 常设分支与临时分支
- 10.6 正式开发GIt分支管理
- 11. 合并和删除分支
- 12. checkout注意事项
1. Git安装
sudo apt-get install git
2. Git初次配置
git config --global user.name "SHU-FLYMAN" # 用户名
git config --global user.email "[email protected]" # 邮箱
查看自己的信息:
git config --list
--->输出
user.name=SHU-FLYMAN
[email protected]
3. Git理论基础
3.1 Git记录的是什么?
3.1.1 SVN等版本控制系统工作原理
SVN等其他版本控制系统工作原理: 记录每一次改动
3.1.2 Git版本控制系统工作原理
而Linux采用另外一种方式:
如果每个版本中有任何文件发生变动,Git会将该文件整个复制并且保存起来.
这种设计虽然会消耗更多的空间,但在 分支管理 时却带来很多益处和便利。
位于分支 master
无文件要提交,干净的工作区3.2 三棵树-工作区域、暂存区域和Git仓库
1. 工作区域(Working Directory): 平时存放项目代码的地方。
2. 暂存区域(Stage): 用于临时存放改动,事实上它只是一个文件,保存即将提交的文件列表信息
3. Git仓库(Repository): 安全存放数据的位置,这里有你提交的 所有版本的数据,其中 HEAD指针 指向 最新放入仓库 的 版本。
3.2 工作流程
- 在工作目录中 添加、 修改 文件
- 将需要进行 版本管理 的文件 放入 暂存区域
- 将 暂存区域 文件 提交到 Git仓库
因此 Git仓库 文件有三种状态:
- 已 修改(modified)
- 已 暂存(staged)
- 已 提交(committed)
3.3 暂存区域作用
为何多增加一个暂存区域?
像某些厂家开发一个产品,一般他们都留有一手,不会把该产品的所有特性一次性发布。通过产品的迭代,每年秋季你就可以开开心心地买到又有一项新功能的“新产品”。
有些时候,你并不想把工作目录中所有的新功能都提交到最新版本,你可以添加一些本次需要提交的文件到暂存区域,然后从暂存区中提交它们。另外,用户在实际工作中,有点修改可以先暂存,不满意可以再修改,方便,如果直接提交到仓库,错了就比较麻烦了,造成提交历史比较混乱。
4. 实战
4.1 初始化Gitgit init
[email protected]:~/Desktop/Myproject$ git init
已初始化空的 Git 仓库于 /home/flyman/Desktop/Myproject/.git/
会新建一个.git
的隐藏文件,用来跟踪管理版本迭代。
4.2 将文件放入暂存区域git add
在新建完文件README.md
之后,
git add README.md
文件已经添加到暂存区域了。
4.3 将文件提交到Git仓库git commit
git commit -m "add a readme file"
[email protected]:~/Desktop/Myproject$ git init
已初始化空的 Git 仓库于 /home/flyman/Desktop/Myproject/.git/
Git 告诉你有 1 个文件被改动(README.md),插入了 1 行内容。
-
commit
是提交的意思,-m
选项后边跟着的是本次提交的说明,就是大概描述这一版本做了哪些改动,以便今后可以迅速查看。 - 对于这个提交的说明,Git 是强制要求你必须写的。如果没有使用
-m
选项,Git 会自动打开一个编辑器,要求你在其中输入提交说明,输入完毕后保存退出即可(操作命令与 vim 编辑器一致)
4.4 总结
总结一下,将工作目录的文件放到 Git 仓库只需要两步:
- Step One ->
git add 文件名
- Step Two ->
git commit -m "你干了啥"
4.5 Github克隆仓库git clone http...
除了在空文件夹中初始Git
,Git还允许你克隆一个现有的仓库,直接输入git clone 目标
命令即可:
git clone https://github.com/n0tr00t/Sreg
源码将会全部拷贝在当前路径的文件夹中。
5. 工作区域新增或修改文件
5.1 查看状态git status
一个项目有百把十个文件,怎么知道那些文件是新添加的,那些文件已经加入到了暂存区域呢?
git status
---
位于分支 master
无文件要提交,干净的工作区
因为在上次commit
之前,工作目录没有发生改动。
在工作区发生修改之后,例如添加了一个LICENCE.md
文件,再次执行命令
git status
---
位于分支 master
未跟踪的文件:
(使用 "git add <文件>..." 以包含要提交的内容)
LICENSE.md
提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)
5.2 使用git add
提交到暂存区域:
git add LICENCE.md
git status
---
位于分支 master
要提交的变更:
(使用 "git reset HEAD <文件>..." 以取消暂存)
新文件: LICENSE.md
5.3 用暂存区域旧一版的文件覆盖工作区域git checkout --文件
git checkout -- <文件>
git checkout -- <文件>
丢弃工作区改动,用 暂存区域 旧一版 的 文件 覆盖 工作区域。
5.4 反悔提交到暂存区域git reset HEAD 文件名
可以使用git reset HEAD <文件名>
恢复暂存区域,取消提交到暂存区域。
git reset HEAD <文件>
git reset HEAD 123456.md
位于分支 feature
未跟踪的文件:
(使用 "git add <文件>..." 以包含要提交的内容)
123456.md
提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)
5.5 一步到位git commit -am
git commit -am "change the license file"
-
-a
Git会自动讲工作目录中所有 已跟踪 的文件 先add
到暂存区域,然后执行commit
命令
6. 回到过去git reset
-
git add
将 工作目录 的文件放入 暂存区域 -
git commit
将 暂存区域 的文件提交到 Git仓库 -
git reset
将 Git仓库 的文件还原到 暂存区域 -
git checkout
将 暂存区域 的文件还原到工作目录
6.1 查看Git仓库版本
git log
---
commit 460adf3376ca914c770913316ce60b6c1e497b8e (HEAD -> master)
Author: SHU-FLYMAN <[email protected]>
Date: Fri Oct 19 09:18:15 2018 +0800
add a LICENCE
commit 2a121fabd13b317303b7ba88b0e844a3ca0dc487
Author: SHU-FLYMAN <[email protected]>
Date: Thu Oct 18 19:25:00 2018 +0800
add a readme file
假设根据Git
记录,将Git
仓库可视化:
三颗树
6.2 git reset
命令
6.2.1 回滚快照
快照即提交的版本,每个版本我们称之为 快照
执行git reset HEAD~
git reset HEAD~
Git仓库
HEAD
指针指向了前一版本快照
三颗树
三棵树变成了这样:
- 首先是移动
HEAD
的指向,将其指向上一个快照(HEAD~) - 然后再将该位置的快照回滚到暂存区域
-
--soft
选项——只移动HEAD
指向,但不会将快照回滚到 暂存区域,事实上 撤销了上一次的提交,使用git log
命令将无法看到已经撤销了的那个提交git reset --soft HEAD~
-
--hard
选项——还会将 暂存区域 的文件 还原到 工作目录
6.2.2 回滚指定快照git reset 00c2929
如果快照比较多,你又懒得去数有多少个“上”,那么你可以通过指定具体的快照 ID 来回滚该快照。
git reset 00c2929 # 没有必要将全部 ID 号都给输入进去,一般只要输入前几位
6.2.3 回滚个别文件git reset 快照号 文件名
reset
不仅可以回滚指定快照,还可以回滚个别文件:
git reset 快照 文件名/路径
- 它会忽略
HEAD
的指向这一步(因为你只是回滚快照的部分内容,并不是整个快照,索引HEAD
的指向不应该发生变化), - 直接将指定快照的 指定文件 回滚到 暂存区域
6.2.4 往前回滚git reset ID
往前回滚的唯一前提:知道指定快照的 ID 号
通过git reflog
查看每一次操作记录,前面即有id号
git reflog
---
2a121fa (HEAD -> master) [email protected]{0}: reset: moving to HEAD~
460adf3 [email protected]{1}: commit: add a LICENCE
2a121fa (HEAD -> master) [email protected]{2}: commit (initial): add a readme file
然后
git reset 00c2929
7. 版本对比
-
创建
README.md
和game.py
文件,并将其添加到暂存区域,接着执行git commit -m “猜数字游戏”
提交项目的第一个快照git add README.md game.py git commit -m "猜数字游戏"
-
修改代码
7.1 比较暂存区域与工作目录git diff
7.1.1 窗口内容解读
git diff
比较 暂存区域 和 工作目录 的文件内容。
git diff
diff 是Linux系统上非常重要的工具,用于比较文件的内容。特别是比较两个版本不同的文件内容以找到 改动 的地方。
-
第一行
diff --git a/README.md b/README.md
,表示存在 暂存区域 的README.md
文件和 工作目录 的README.md
文件 -
第二行:
index 79668377..142a180 100644
-
79668377
——第一个文件的ID
-
472a180
——第二个文件的ID
-
100644
——指定文件的类型和权限
-
-
第三行:
--- a/README.md
-
---
表示该文件是 旧文件 (存放在 暂存区域)
-
-
第四行:
+++ b/README.md
-
+++
表示该文件是 新文件 (存放在 工作区域)
-
-
第五行:
@@ -1 +1,2 @@
以
@@
开头和结尾,中间的-
代表旧文件,+
代表新文件,后边的数字表示 开始行号,显示行数 -
第6-7行:
这是将两个文件合并显示的结果:
- 有个
+
的 绿色 的那一行说明是新文件独有的 - 浅灰色 的 则是两个文件所共有的内容
- 有个
-
所以
+1,2
表示新文件在合并显示中以第 1 行开始,显示 2 行
为什么-1后面没有显示行数,因为在合并显示的结果中,旧文件已经完全包含在新文件中了,因此旧文件没有自己“独有的内容“
-
\Non newline at the end of file
:Git
提醒文件不是以换行符结束。最后空一行是编程的一个好习惯。
7.1.2 常用命令
-
移动命令
-
j
向下移动一行,k
向上移动一行 -
f
向下移动一页,b
向上移动一页
-
-
跳转命令
-
g
第一行,G
最后一行 - 先按下
3
,再按g
,则表示去第 3 行
-
-
搜索命令
输入
/
(从当前位置向下搜索)或者?
(从当前位置向下搜索),后面跟上搜索的关键字
,按下回车,所有关键字均高亮显示
7.2 比较两个历史快照git diff 6e2697 ed3708c
执行git log
看到Git仓库中已经有两个快照了
执行git diff 6e2697 ed3708c
即可比较Git
仓库中两个快照的差异
7.3 比较当前工作目录和Git仓库的快照git diff ed3808c
Git
仓库:
三棵树:
如果希望比较第一份 快照ed3808c
与 当前工作目录 的内容差异,输入git diff ed3708c
命令即可:
如果希望比较 最新提交的快照 和 当前工作目录 之间的差异,输入git diff HEAD
7.4 比较暂存区域和Git仓库快照git diff -cached
三棵树:
如果希望比较 最新快照 和 暂存区域 的文件,只需要执行git diff --cached
命令
当然也可以指定 其他快照,就需要多写一个ID
号,git diff --cached ed3708c
7.5 总结
8. 开发中常见情况
8.1 重新提交最后次提交git commit --amend
实际开发中,你可能遇到下面这两种情况:
- 版本刚一提交
commit
到Git仓库
,突然想到 漏掉 两个文件没有add
到 暂存区域,这就好比你是老司机,开车开到终点站发现乘客还没有上车。 - 版本刚一提交
commit
到Git仓库
,突然想起来,版本说明 写得不够全面,无法彰显你本次修改的重大意义
我们可以reset
回到过去,但这就好像我们打游戏打不过Boss,退回最新存档重练的感觉,会有记录。有没有一步到位的方法呢?
git commit --amend
,Git
会用 暂存区域的文件更正
最新的一次提交
上一次修改后,三棵树的样子是这样的:
这时,我们希望将暂存区域的REMEDE.md
文件进行提交,但没有必要 增加 一个 新的版本,怎么办?
- 如果需要 修改 提交说明 使用
git commit --amend filename -m “新的提交说明”
命令,Git仓库
不会新增加新版本 - 暂存区域 的内容也提交上去了
工作原理:其实相当于重新提交最后次修改,不在Git
上保留记录
8.2 删除文件git rm 文件名
-
git rm yellow.jpg
,rm
只删除工作目录和暂存区域的文件,即取消跟踪,下次提交不纳入版本管理 -
git reset --soft HEAD!
将快照回滚到上一个位置
当 工作目录 和 暂存目录文件不一致时,Git
会提醒,可以以下处理:
-
git rm -f test.py
将两个文件 同时删除 -
git rm –cached
只删除 暂存区域文件
8.3 重命名文件git mv a b
git mv game.py wordgame.py
,原理和Linux
系统一样,通过移动来重命名。
8.4 忽略跟踪一些指定文件.gitignore
现在工作目录有1.temp
、2.temp
、3.temp
,我们不希望后缀名为temp
的文件被跟踪:
可以新建一个.gitignore
文件,然后在里面添加忽略内容,.gitignore
语法如下:
- 所有 空行 或者以注释# 开头的行都会被
Git
忽略 - 星号
*
匹配零个或者多个任意字符 -
[abc]
匹配任何一个列在方括号中的字符 - 问号
?
只匹配一个任意字符 -
[a-z]
匹配所有在这两个范围内的字符 - 匹配模式最后跟反斜杠
/
说明 要忽略的是目录 - 匹配模式以反斜杠
/
开头,说明防止递归 - 要忽略指定模式以外的文件或目录,可以在模式前加上感叹号
!
取反
9. 创建和切换分支
9.1 分支是什么?
假如你的大项目已经上限了(有成千上百的人在用),过了一段时间你觉得应该 添加新功能,但为了 保险 起见,你肯定不能在当前项目上直接开发,这时候你就需要另起炉灶(创建分支)了。
9.2 创建分支git branche 分支名
假设Git
仓库现在是这个样子,可以看到 README.md
文件被 修改,并添加到 暂存区域
,但还没有被提交,三棵树:
我们开始 创建分支,git branch 分支名
:
git branch feature # 没有任何提示说明分支创建成功,除非创建同名分支,它才会提醒
使用git log --decorate
查看:
希望精简地看,可以使用git log --decorate --online
,它的意思是:
- 目前有两个分支,一个主分支
master
,另一个feature
分支 -
HEAD
指针指向默认的master
分支
目前仓库快照应该是这样的:
9.3 切换分支git checkout 分支名
现在我们需要将工作环境切换到新创建的分支feature
上来,使用checkout
命令:
git checkout feature
这样HEAD
指针就指向了feature
分支:
使用git log --dcorate –oneline
查看:
我们再进行一次提交,暂存区域还有个文件修改没有提交:
现在仓库中的快照应该是这样的:
然后我们将HEAD
指针切回master
分支:
发现 上一次对README.md
文件修改当然无存,仓库就变成这样了:
如果再对README.md
进行修改,然后再次执行git commit -m “再次修改说明文件”
:
仓库快照就会这样:
使用git log --oneline --decorate --graph --all
命令查看:
9.4 直接创建并切换分支
其实还可以用git cheackout -b feature2
直接创建并切换分支。
10. Git实际工作流程
Git 工作流使用一个 中间仓库 作为 所有开发者 的交流地点,开发程序者在本地工作,然后将各自的 分支 推送到 中间仓库。
10.1 开发分支(develop)
代替单一的master
分支,上图的工作流使用了两个分支来处理 项目发布 和 日常开发
-
master
主分支通常只用于对外发布项目的版本,日常开发应该在另外条分支上完成 - 我们把开发的分支称为
devolop
分支
10.2 功能分支(feature)
每个新功能都应该用单独的一个 功能分支 进行开发,功能分支 应该从 开发分支 中分离出来,功能开发完成后再合并到开发分支中。
- 功能分支不应该跟
master
分支有任何交流的机会 - 功能分支用
feature-*
的形式命名
10.3 预发布分支(release)
在项目正式发布之前,你可能需要一个 预发布 的版本进行 测试,于是你可以从 开发分支中分离出 预发布分支,用于内部或公开分支。
- 预发布分支 应该同时合并到主分支和开发分支中
-
预发布分支 可以用
release-*
的形式命名
10.4 维护分支(hotfix)
项目正式发布之后,难免出现bug,这时就需要创建一个分支,进行bug的修补,称为 维护分支。
-
维护分支 应该从 主分支 中分离出来,
bug
被修补后,再 合并 到 主分支 和 开发分支 -
维护分支 以
hotfix-*
的形式命名
10.5 常设分支与临时分支
常设分支:
-
主分支
master
-
开发分支
devolop
其余分支 属于 临时分支,用完之后应该及时删除:
- 功能分支
feature
- 预发布分支
release
- 维护分支
hotfix
10.6 正式开发GIt分支管理
11. 合并和删除分支
在Git
仓库里,所有快照事实上都是按照提交时间排放的,我们把串联这些快照的 **时间轴 ** 称为 分支,默认情况下,Git
只有一条 master
主分支。但在实际开发中,一条分支根本不够用,实际分支张这样:
一个项目根据需求会产生很多分支,但最终都会被合并回去。
11.1 有冲突的分支合并
当一个子分支的实名结束之后,它就应该回归到主分支中去。加入分支状态是这样的:
11.1.1 git merge 分支名
合并分支我们用git merge 分支名
:
git merge feature # 会将该分支合并到HEAD指针指向的分支
从Git
提示的内容来看,我们知道这次合并并没有成功,Git
说:
- 合并
REMEAD.md
文件的时候出现冲突 - 自动合并失败,请修改冲突的内容,并重新提交快照
意思是:
- 你需要先解决冲突的问题,
Git
才能够进行合并操作。 - 所谓冲突,就像两个分支中存在同名但内容却不同的文件,
Git
不知道你要舍弃哪个或者保留哪个,需要你自己来做决定。
11.1.2 解决冲突
这时执行git status
命令会显示你要解决的冲突:
Git
会在有冲突的文件中加入一些标记,打开README.md
文件:
- 以
=====
为界 - 上到
<<<<<<<<HEAD
的内容表示当前分支 - 下到
>>>>>>>>feature
的内容表示待合并的feature
分支 - 之间的内容即冲突的地方
我们将内容修改为:
《零基础入门学习Python》第004讲
课后作业:文字游戏
Power by www.FishC.com
11.1.3 提交快照,add
/commit
保存文件,然后提交快照:
git add README.md
git commit -m "解决冲突"
其实就是利用
git merge feature
比较两个分支差异,然后处理差异,之后将处理好的文件提交上去,形成新版本的快照。
自动合并
执行git log --decorate --all -graph --oneline
命令,显示此时的分支已经 自动合并。当然如果不存在冲突,则不需要搞那么多。
11.2 没有冲突的合并
执行git checkout -b feature2
,相当于执行git brance feature2
和 git checkout feature2
两步,然后新建一个文件feature2.txt
,并提交快照。执行gitlog --decorate --all --graph --online
命令:
我们看到,feature2
分支比master
分支快了一步,现在我们切换回master
分支,并且将feature2
分支合并进来:
git checkout maseter
git merge feature2
Git
只显示了快进Fast-forward
,因为;
-
feature2
这个分支的 父节点 是master
分支,所以Git
只要简单地移动master
指向即可
执行git log --decorate --all --graph --oneline
变成了:
11.3 删除分支git branch -d 分支名
对于不需要的分支,我们将它删除比较好,否则版本管理太乱了。使用git branch -d 分支名
来删除:
11.4 自动三方合并
11.4.1 Fast-forward
当 待合并的分支 位于 目标分支的直接上游,
Git
只需要把目标分支的指针直接移动即可实现合并
例如下面图片:master
分支位于feature2
分支的直接上游
将feature2
分支合并到master
分支,只需要移动master
分支指针即可:
11.4.2 三方合并
如果待合并的两个分支不再同一条线上,那么进行合并就需要解决一个根本的问题——冲突
为什么在同一条线上就不会冲突呢?
不同命文件冲突
因为Git
的快照是按时间顺序提交的,所以同一条线上两个快照,它们是有先后顺序的,尽管两者可能出现同名文件不同内容,Git
会认为这是 改变 而不是 冲突
合并C3
和C4
得到C5
,但C5
应该如何解决冲突呢?它会找到第三个快照,就是共同祖先,一比较,发现C3
增加了test1
,c4
增加了test2
,因此C5
是三种合体,因此不存在冲突。
同名文件不同更改
Git
找到共同祖先C1
,可以看到:
-
C4
在第二行添加了1
-
C3
在第4行添加了FishC
-
love
第三行共同拥有
因此在每一行并没有产生冲突,所以自动合并后C5
是这样的:
# test.txt
I
love
FishC
如果Git
检测到同一行有不同内容,还是会报冲突让你自行决定谁去谁留
12. checkout注意事项
checkout
命令有两个功能:
- 从历史快照(或者暂存区域)中拷贝文件到工作目录,
git checkout README.md
,没有指定具体的快照ID
则从暂存区域恢复指定文件到工作目录 - 切换分支
注意
-
恢复用
reset
更安全,将上一版本的快照恢复到暂存区域,再checkout
回来。 -
涉及工作区域的操作要注意,如果没有
commit
,Git
没有操作可以恢复。