Git版本控制工具--基础命令和分支管理
1.Git仓库的基本概念和流程
版本库的概念:版本库又名仓库,英文名repository,你可以简单的理解一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改,删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻还可以将文件”还原”。
1.1 git仓库的基本概念
1. 远程仓库(Remote):
也叫作资源库,是远程机器上的代码库,用于做不同版本库文件交换更新。如Gitlab,GitHub,gitee。
2. 本地库(Repository):
是用户在本地创建的目录,拥有远程库的一个快照,由工作区和版本库构成。
工作区(Workspace):
本地库的根目录中除.git目录以外的内容,存储内容的实际文件。
暂存区(stage/Index):
也叫做缓存区,暂存信息存放在.git目录"下的index文件(.git/index)中,用于临时保存内容的修改;
版本库(.git目录)
是本地库的根目录中的一个隐藏目录.git,用于记录版本信息,Git进行版本控制所需要的文件,则都放在.git文件夹中;
3. 分支(Branch):
本地库中默认创建一个主(master)分支,分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作。
本地库和远程库的关系
开发人员通过Git命令来管理代码,最常用的6个命令如下图所示:
1.2 git仓库的工作流程
从一般开发者的角度来看,使用Git的工作流程是:
1. 克隆远程库:从远程库上克隆完整的Git仓库(包括代码和版本信息)到本地;
2. 在本地库上修改代码:在本地库上根据不同的开发目的,创建分支,修改代码;
3. 提交到分支:在本地分支上提交代码;
4. 把修改合并到本地主分支:在本地库上提交更新,也就是说,把修改合并到本地主分支;
5. 把远程库合并到本地主分支:把远程库上的最新代码fetch下来,跟本地主分支合并,如果存在冲突,那么解决冲突。
6. 把本地主分支提交到远程库:生成补丁(patch),把补丁发送给远程库。
2.Git的命令
2.1 创建版本库
创建一个版本库也非常简单,如下D盘下 目录下新建一个testGit版本库。
右键通过(Open Git Bash here)命令行的方式打开窗口
pwd 命令是用于显示当前的目录。
通过命令 git init 把这个目录变成git可以管理的仓库,如下
Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。
这时候你当前testgit目录下会多了一个.git的目录,这个目录是Git来跟踪管理版本的,没事千万不要手动乱改这个目录里面的文件,否则,会把git仓库给破坏了。.git里面内容如下:
2.2 添加文件和修改提交文件
首先要明确下,所有的版本控制系统,只能跟踪文本文件的改动,比如txt文件,网页,所有程序的代码等,Git也不例外,版本控制系统可以告诉你每次的改动,
但是图片,视频这些二进制文件,虽能也能由版本控制系统管理,但没法跟踪文件的变化,只能把二进制文件每次改动串起来,也就是知道图片从1kb变成2kb,但是版本控制不知道具体改动的内容。
1. 创建文件readme.txt, 此刻文件在工作区(WorkSpace)
编辑时注意:点击大写I,进入编辑模式
点击ESC,切换模式,可以切换到命令行格式
切入命令行格式之后,输入 :wq 保存退出
通过git status可以查看文件追踪的情况
2. 使用命令 git add readme.txt添加到暂存区里面去。
我们发现添加到暂存区的时候会有警告出现。不过没关系,是换行符的警告。
我们可以看到readme.txt目前处于暂存区。
如果要提交多个文件
我们可以在add后面指定文件的列表
git add test2.txt test3.txt
如果想要添加工作区所有文件到暂存区
git add .
我们可以通过提示的话来撤销回工作区 暂存区-->工作区
git rm --cached readme.txt
3. 提交文件到主分支 暂存区-->版本库
git commit -m 'first commit'
现在我们已经提交了一个readme.txt文件了,我们下面可以通过命令git status来查看是否还有文件未提交
注意:注释是必须要写的。在小组协作中,注释十分关键
4. 修改文件,在文件中加入一行。查看git的状态
修改之后,查看文件状态,我们发现文件进入到工作区 版本库-->工作区
修改之后,可以进行两种操作
git add 将工作区的文件添加到暂存区
git restore 回滚,撤销修改 将工作区的文件返回版本库
文件回滚:
还可以通过checkout上一个版本的文件来覆盖修改后的文件
git checkout -- readme.txt 工作区退回版本库
注意 :--后面要有空格
2.3 版本回退
2.3.1 日志查看
查看下历史记录
git log
git log命令显示从最近到最远的显示日志,我们可以看到最近三次提交
如果嫌上面显示的信息太多的话,我们可以使用命令 git log --pretty=oneline 演示如下:
2.3.2 版本回退和撤销
1. 版本回退
直接在版本库回退版本
现在我想使用版本回退操作,我想把当前的版本回退到上一个版本,要使用什么命令呢?可以使用如下2种命令,第一种是:git reset --hard HEAD^ 那么如果要回退到上上个版本只需把HEAD^ 改成 HEAD^^ 以此类推。那如果要回退到前100个版本的话,使用上面的方法肯定不方便,我们可以使用下面的简便命令操作:git reset --hard HEAD~100 即可。未回退之前的readme.txt内容如下:
HEAD 最近一次 commit
HEAD^ 上次提交
HEAD~100 上100次提交
查看提交日志
同时我们也可以通过sha1的前四位来做回退
git reset –heard sha1
2. 回退撤销
通过版本号回退,使用命令方法如下:
git reset --hard 版本号
可以通过如下命令即可获取到版本号:git reflog
查看撤销后的日志结果。
2.4 删除文件
添加并且提交了多个文件
一般情况下,可以直接在文件目录中把文件删了,使用下面的命令,将文件从版本库中删除,移到暂存区。 版本库-->暂存区
git rm test.txt
我们发现删除的文件直接进入暂存区(此刻需要注意,如果使用rm删除不在暂存区,需要git add才会进入暂存区。如果进入暂存区可以退回工作区,使用下面命令
git reset HEAD test.txt 暂存区-->工作区
此时文件不在暂存区,而在工作区,通过add命令可以将文件放到暂存区
如果我想彻底从版本库中删掉了此文件的话,可以再执行commit命令 提交掉。暂存区-->版本库
彻底删除
2.5 Git配置信息Config
2.5.1 config 概述
在git中,我们使用git config 命令用来配置git的配置文件,git配置级别主要有以下3类:
1、仓库级别 local 【优先级最高】
2、用户级别 global【优先级次之】
3、系统级别 system【优先级最低】
git 仓库级别对应的配置文件是当前仓库下的.git/config
git 用户级别对应的配置文件是用户宿主目录下的~/.gitconfig
git 系统级别对应的配置文件是git安装目录下的 /etc/gitconfig
当然我们可以在cmd命令提示符中输入以下查看仓库配置信息
仓库级别
git config --local -l
用户级别
git config --global -l
系统级别:
system-系统级: 在git安装以后,git的默认配置项都在这里;
global-全局级:登录用户全局级别的git配置;
local -仓库级: 对不同的仓库进行自定义配置。
这三个级别配置的内容都是相同的,不同的是优先级;同样的配置信息的优先级是:local > global > system; 也就是说,针对同样的某个配置,如果同时存在在local和global中时,local中的配置将会覆盖global中的配置项的值,当然也会同样覆盖system中的配置项;这样做的好处是:既方便共用,又可以个性化自定义。
遵循的理念是:对于所有用户都通用的配置项放在system中;对于每个独立的用户相对的共用项放在global;对于某个仓库特殊的配置放在local
2.5.2 config 修改
演示修改用户名和邮箱:
git config --global user.name "renliang"
git config --global user.email "renliang@126.com"
注意不要手动修改 每个级别的配置文件,要用命令。
对于git来说,配置文件的权重是仓库>全局>系统。Git会使用这一系列的配置文件来存储你定义的偏好,它首先会查找/etc/gitconfig文件(系统级),该文件含有对系统上所有用户及他们所拥有的仓库都生效的配置值。接下来Git会查找每个用户的~/.gitconfig文件(全局级)。最后Git会查找由用户定义的各个库中Git目录下的配置文件.git/config(仓库级),该文件中的值只对当前所属仓库有效。
3. .gitignore文件
在项目中,我们可能一起提交多个文件
git add -A 提交所有变化
git add -u 提交被修改(modified)和被删除(deleted)文件,不包括新文件(new)
git add . 提交新文件(new)和被修改(modified)文件,不包括被删除(deleted)文件
在使用git的过程中,一般我们总会有些文件无需纳入git的管理,也不希望它们总出现在未跟踪文件列表,这些文件通常是日志文件、临时文件、编译产生的中间文件、工具自动生成的文件等等。此时我们可以创建一个名为 .gitignore 的文件,列出要忽略的文件模式,Git会根据这些模式规则来判断是否将文件添加到版本控制中。注意:在windows下可以创建文件名为.gitignore.,保存之后系统会自动重命名为 .gitignore
3.1 格式规范
(1)所有空行或者以注释符号 # 开头的行都会被 Git 忽略
(2)可以使用标准的 glob 模式匹配
(3)匹配模式最后跟斜杠(/)说明要忽略的是目录
(4)要忽略指定模式以外的文件或目录,可以在模式前加上感叹号(!)进行取反
3.2 glob 模式
所谓的 glob 模式是指 shell 所使用的简化了的正则表达式,匹配规则如下:
"*":星号匹配零个或多个任意字符
[]:匹配任何一个列在方括号中的字符,如[ab]匹配a或者匹配b
"?":问号匹配一个任意字符
[n-m]:匹配所有在这两个字符范围内的字符,如[0-9]表示匹配所有0到9的数字
匹配示例
logs/: 忽略当前路径下的logs目录,包含logs下的所有子目录和文件
/logs.txt: 忽略根目录下的logs.txt文件
*.class: 忽略所有后缀为.class的文件
!/classes/a.class:不忽略classes目录下的a.class文件
tmp/*.txt: 只忽略tmp目录下的.txt文件
**/foo: 可以忽略/foo, a/foo, a/b/foo等
3.3 .gitignore的使用
1. 在需要创建 .gitignore 文件的文件夹, 右键选择Git Bash 进入命令行,进入项目所在目录。
2. 输入 touch .gitignore 在文件夹就生成了一个“.gitignore”文件
3.然后用编辑器打开这个文件进行编辑就行了。
4.然后就写规则来操作要忽略的文件了。
创建两个文件
查看状态我们发现testclass.class不在工作区
提交也不会进入暂存区
3.4 定义全局的.gitignore
除了可以在项目中定义.gitignore文件外,还可以设置全局的.gitignore文件来管理所有Git项目的行为。
这种方式在不同的项目开发者之间是不共享的,是属于项目之上Git应用级别的行为。
可以在任意目录下创建相应的.gitignore文件,然后再使用以下命令配置Git
git config --global core.excludesfile ~/.gitignore
3.5 gitignore规则不生效
.gitignore只能忽略那些原来没有被track的文件,如果某些文件已经被纳入了版本管理中,则修改.gitignore是无效的。所以一定要养成在项目开始就创建.gitignore文件的习惯。
3.6 java开发通用模板
## .gitignore for Grails 1.2 and 1.3
# .gitignore for maven
target/
*.releaseBackup
# web application files
#/web-app/WEB-INF
# IDE support files
/.classpath
/.launch
/.project
/.settings
/*.launch
/*.tmproj
/ivy*
/eclipse
# default HSQL database files for production mode
/prodDb.*
# general HSQL database files
*Db.properties
*Db.script
# logs
/stacktrace.log
/test/reports
/logs
*.log
*.log.*
# project release file
/*.war
# plugin release file
/*.zip
/*.zip.sha1
# older plugin install locations
/plugins
/web-app/plugins
/web-app/WEB-INF/classes
# "temporary" build files
target/
out/
build/
# other
*.iws
#.gitignore for java
*.class
# Package Files #
*.jar
*.war
*.ear
## .gitignore for eclipse
*.pydevproject
.project
.metadata
bin/**
tmp/**
tmp/**/*
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.settings/
.loadpath
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
## .gitignore for intellij
*.iml
*.ipr
*.iws
.idea/
## .gitignore for linux
.*
!.gitignore
!.gitattributes
!.editorconfig
!.eslintrc
!.travis.yml
*~
## .gitignore for windows
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
## .gitignore for mac os x
.DS_Store
.AppleDouble
.LSOverride
Icon
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
## hack for graddle wrapper
!wrapper/*.jar
!**/wrapper/*.jar
gitignore语法说明:
- 注释:注释使用#,换行没有实际作用,只是为方便阅读。
- 正则匹配(不可以用来匹配/):
- 星号(*):匹配零个或多个任意字符。
- 问号(?):匹配一个任意字符。
- 目录匹配:
- 当/在字符串起始位置时,表示从.gitignore文件所在目录开始匹配,否则下级目录也都将匹配。
- 当/在文件名末尾时,只匹配目录,否则同名的目录和文件都将被匹配。
- 取反(不忽略):之前已经被忽略了的文件,使用!后,该文件将会被重新包含到跟踪列表中(即不忽略),注意:如果该文件的父级目录被忽略了,使用!无效。
- 匹配字符列表:使用[],可匹配中括号里的任意一个字符,比如:ab[sd]gk,即可以匹配:absgk、abdgk。
- 匹配多级目录:使用两个星号**,比如:a/**/b 可以用来匹配:a/b、a/x/b、a/x/y/b等等。
由于.gitignore需要在push之前创建,在创建.gitignore之前push的,.gitignore并不会生效。可以通过下面的方法解决:
git rm -rf --cached
git add .
git status
git commit -m ".gitignore"
更新.gitignore文件后,也如上操作。
通过git的命令来检查已有的.gitignore规则是否有效:
git check-ignore [文件或文件名]
git check-ignore -v [文件或文件名]
当文件会被gitignore规则有效时,会输出,反之,则没有输出。带-v的命令,会列出是那条规则产生的效果。
3.7 diff命令
git diff readme.txt
diff里面a表示前面那个变量,b表示第二个变量
HEAD commit版本
Index staged版本
3.7.1. 工作目录 vs 暂存区
git diff
意义:查看文件在工作目录与暂存区的差别。如果还没 add 进暂存区,则查看文件自身修改前后的差别。也可查看和另一分支的区别。
3.7.2. 暂存区 vs Git仓库
git diff --cached
意义:表示查看已经 add 进暂存区但是尚未 commit 的内容同最新一次 commit 时的内容的差异。 也可以指定仓库版本:
git diff --cached
3.7.3. 工作目录 vs Git仓库
git diff
意义:查看工作目录同Git仓库指定 commit 的内容的差异。
=HEAD 时:查看工作目录同最近一次 commit 的内容的差异。
3.7.4. Git仓库 vs Git仓库
git diff
意义:Git仓库任意两次 commit 之间的差别。
以上命令可以不指定 ,则对全部文件操作。以上命令涉及和 Git仓库 对比的,均可指定 commit 的版本。
4 Git分支管理
Git 的默认分支就是 master。你所作的commit会在master分支上自动移动。 在多次提交操作之后,master分支指向最后那个commit object(提交对象链)。
Git 的 “master” 分支并特殊,跟其它分支没有区别。 之所以几乎每一个仓库都有 master 分支,是因为 git init 命令默认创建它。
但很多时候听别人说master分支,往往有一种 这个分支是稳定、无bug的分支。而develop往往预示这新功能,不稳定的分支。这和分支策略有关,但本质上这两个分支没区别。
4.1 分支创建
通过git branch来查看和创建分支。
git branch dev
分支切换到dev
git checkout dev
创建分支与切换分支同时完成
git checkout -b dev2
这是我们在dev2分支创建一个文件A.txt并且提交。我们发现在dev2分支可以看到这个文件,当我们切换会master时候无法看到这个文件。
所在分支为dev2时
所在分支为master时
4.2 分支删除
1.不能删除自己所在的分支
我们可以切换到master删除一个合并后的或者没有发生变化的分支
2.如果一个分支发生了变化不能删除
4.3 分支合并
我们继续基于上面的例子,切换到master上做dev2 的合并
我们发现master分支上也添加了A.txt这个文件
如果修改的是同一个文件也可以做同样的合并,让我们切换到dev2分支修改A.txt中的内容。在A.txt中添加了一行World
我们切换会master分支的时候发现提示了A.txt的变动。通过合并发现master分支上也合并了dev2修改的内容,合并之后dev2就删除就被允许了。
4.4 分支的本质
master指向的是提交
HEAD是指向当前的分支,当前在哪个分支就指向哪个分支
第二张图上我们可以看到创建了dev的分支,当我们切换到dev分支的时候HEAD就会指向dev
当我们进入.git文件夹查看HEAD的内容的时候可以看到,所处分支不同内部的文件指向就不同。
master分支
dev2分支
git的分支与svn不同,svn是整体拷贝一份分支,git用的是指针。
如果dev发生修改提交,dev的版本就会向后移动。
在master分支上如果合并就会出现下面的图
4.5 分支的冲突
我们在dev2分支里面修改A.txt文件添加一行 update by dev2后提交
我们在master分支里面修改A.txt文件同时添加一行 update by master后提交
合并时候我们发现出现冲突
>>>>>>>>>>dev2是dev2分支修改
我们需要手工合并。修改后报了master的内容
此时代码冲突,位于工作区
我们可以通过图形来查看冲突的提交日志。
git log --graph
5. Git stash
1 当正在dev分支上开发某个项目,这时项目中出现一个bug,需要紧急修复,但是正在开发的内容只是完成一半,还不想提交,这时可以用git stash命令将修改的内容保存至堆栈区,然后顺利切换到hotfix分支进行bug修复,修复完成后,再次切回到dev分支,从堆栈中恢复刚刚保存的内容。
2 由于疏忽,本应该在dev分支开发的内容,却在master上进行了开发,需要重新切回到dev分支上进行开发,可以用git stash将内容保存至堆栈中,切回到dev分支后,再次恢复内容即可。
总的来说,git stash命令的作用就是将目前还不想提交的但是已经修改的内容进行保存至堆栈中,后续可以在某个分支上恢复出堆栈中的内容。这也就是说,stash中的内容不仅仅可以恢复到原先开发的分支,也可以恢复到其他任意指定的分支上。git stash作用的范围包括工作区和暂存区中的内容,也就是说没有提交的内容都会保存至堆栈中。
5.1 git stash
我们在master分支修改A.txt添加一行
这时我们要切换dev2分支
git stash 会先把修改的内容做保存然后我们就可以切换到其他的分支
注意:git stash 保存的位置,不属于工作区,是独立于工作区之外的一个位置,因此直接再分支上查看,是看不到保存内容的
列出stash保存的所有修改
git stash list
我们可以将stash过的修改恢复出来。通过pop取出最近的恢复并且删除stash中的修改
git stash pop
如果两次pop由于提一次没有做提交则会报错,所以我们应该把第一次pop的提交,在pop第二次的。
6.分支管理策略
git 的分支整体预览图如下:
从上图可以看到主要包含下面几个分支:
master:git默认主分支(这里不作操作)。
stable:稳定分支,替代master,主要用来版本发布。
develop:日常开发分支,该分支正常保存了开发的最新代码。
feature:具体的功能开发分支,只与 develop 分支交互。
release:release 分支可以认为是 stable分支的未测试版。比如说某一期的功能全部开发完成,那么就将 develop 分支合并到 release分支,测试没有问题并且到了发布日期就合并到 stable分支,进行发布。
bugfix:线上 bug 修复分支。
面试题:项目开发主要需要几个分支?
master/ stable, develop,bugfix
6.1 主分支
因为master分支我们不作操作,所以针对stable和develop这两个主分支来讲解。
stable分支:用来发布,管理着多个稳定的版本。
develop分支:就是我们日常开发的分支。
使用这两个分支就具有了最简单的开发模式:develop 分支用来开发功能,开发完成并且测试没有问题后,则将 develop 分支的代码合并到 stable分支并发布。
6.2 辅助分支
通过这些分支,我们可以做到:团队成员之间并行开发,增加新功能更加容易,可以同时进行开发和版本发布、线上bug修复等。
6.2.1 Feature分支
feature 分支用来开发具体的功能,一般基于develop分支,最后完成功能后再合并到develop分支。
比如,目前我们针对develop分支来做功能开发,在开发的过程中会有紧急需求需要开发,且在本次版本发布时间之前要能测试完成。我们可以基于之前稳定版本另开一个feature分支来做紧急需求的开发,发布并进行测试,完成之后再合并到develop分支上。
6.2.2 release分支
release分支作为预发布分支,release 分支从 develop 分支 fork 出来,最终会合并到 develop 分支和 stable 分支,合并到 stable分支上就是可以发布的代码了。
为什么我从develop分支fork出来,还要合并到develop分支中呢?因为我们在release分支上难免会有bug产生,修复bug也是在release分支上,所以必须要合并到develop分支。
6.2.3 bugfix分支
bugfix 分支用来修复线上bug。当线上代码出现 bug 时,我们基于 stable 分支开一个bugfix分支,修复 bug之后再将 bugfix分支合并到stable分支并进行发布,同时develop 分支作为最新最全的代码分支,bugfix分支也需要合并到 develop 分支上去。