git的基本使用
首发日期:2018-09-23
git的介绍
git是一个分布式版本控制系统,它可以帮助开发者管理开发项目的版本记录、回退、更新。
题外话:什么是版本控制系统?--以版本控制系统的功能开始
在你日常生活中,如果你对某个项目进行不断的版本增强的时候,你可能会得出多个版本(例如初定版,1.1版,3.0版,最终版),而由于你是不能百分百确定最新版本是否是符合开发思路的(可能走某条路反而让版本增加了更多bug,于是要从头开始),所以你会需要每个版本的记录文件,确保存有原来的版本来进行重新开发,你可能会对每个版本进行拷贝一份,这样就可以确保自己存有发生错误之前的版本了。但“拷贝”法有很严重的问题,一是单纯的拷贝会浪费较多空间、二是每个拷贝你都需要提供一个文件来描述这个拷贝是哪个版本的。而版本控制系统就可以很大程度地解决上面两个问题。
1.版本控制系统可以帮助开发者协同工作,节省项目管理时间:以Linux的版本控制为例,众所周知,Linux是开源的,所以它有很多“免费的员工”,如果没有一个版本控制系统的话,一个普通公司的多个程序员提交的代码主管要“手动拷贝+手动合并+手动清理优化”才能得到最终的代码,而使用一些市面上的版本控制软件如SVN,主管就可以定义一个“房间”,程序员给“这个房子装修的东西”,主管可以在版本控制软件上进行代码对比查看合并,这样就节省了项目管理的时间。
2.版本控制系统可以帮助开发者记录项目的开发记录、回退版本和版本对比:以给软件增加新功能为例,开发新功能,首先要保证旧系统运行,在没有版本控制系统的情况下,我们一般都要对原始代码进行拷贝,避免在原始代码的基础上修改导致出错时无法恢复到功能上线前(假设不是修复新功能而是先下线再修复)。而使用版本控制系统后,开发者可以将原始代码记录提交到版本控制系统上,然后每一次开发都提交一次记录到版本控制系统上,版本控制系统会将提交的所有的开发记录保存,这使得我们可以方便的“回退”到某一版本。而版本控制系统一般还提供版本对比功能,可以查看某版本与另一个版本的代码在哪里有区别。(有人说可以拷贝,但长期开发时,拷贝消耗的时间很多,而提交只需要几条命令。并且不同的版本控制系统采用不同的记录方法,所以会比拷贝要节省资源,同时拷贝不可以对比文件的细致内容。)
所以,总的来说,版本控制系统有以下几个好处:
辅助多人协同开发
提交项目管理效率,提高开发效率
跟踪记录项目中版本变化,可以查看不同版本之间的细致区别
帮助管理源代码,轻松回退历史版本
跟踪记录整个软件的开发过程
权限控制:管理开发者提交、修改的权限,
题外话:集中式与分布式版本控制系统的区别?
git与集中式版本控制系统(如svn等)不同,集中式版本控制系统由主服务器存储更新记录,git可以在多台主机中保存更新记录。
集中式的版本控制系统,都有一个单一的集中管理的服务器,保存所有文件的修订版本,开发者通过客户端连到这台服务器,取出最新的文件或者提交更新。 完成工作后,开发者都必须提交到这台服务器。由于服务器的限制,集中式版本控制系统最大的毛病就是必须联网才能工作。
分布式控制系统,每一个客户机都相当于一台服务器,每台主机都是一个版本库,当需要协作开发时,客户端并不只是从某台主机中提取最新版本的文件快照,而是把代码仓库完整地镜像下来。 这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。而开发者完成工作后,开发者可以将修改推送给别人,别人就可以看到开发者的修改。
补充:
本文不涉及深层次运行原理,只会讲到涉及操作的一些基础知识。想了解更多可以参考 Pro Git 第二版 简体中文 · GitBook (Legacy),或者参考官网文档:https://git-scm.com/book/zh/v1/起步
# git的基础使用相关知识
区域:
要把代码提交到本地服务器,那么就要认识下面几个区域。考虑到对项目更新的追踪情况,git将项目的所处区域分为以下几种。
工作区:工作区就是你最开始的工作目录。你开发了一个项目,并且这个项目位于仓库目录(仓库后面说)中,但还没有做任何版本跟踪或版本提交操作,那么此时这个项目就位于工作区。
暂存区:你可以把暂存区理解成提交到本地库前的一个中间者,因为直接提交到本地库的话,有时候可能会发生错误,为了避免错误,所以有了个暂存区。当你执行git add XXX【XXX是一个文件或目录】的时候,git就会把XXX添加到暂存区,这样就会开始进行版本跟踪。你可以把暂存区理解成幼儿园的校车,老师(开发者)把一个个孩子(项目文件)带到校车(暂存区)上,等到装满了并且检查没少人(验错,以免漏了文件)之后再把小孩子送到家(提交到本地库)。暂存区中会对将要提交的文件进行追踪,如果这时候工作区中进行了修改,那么可以使用
git status
查看出来。本地库:本地库就是本地的版本仓库,在本地的仓库中存有版本更新记录。你的每一次提交都会在本地库中记录下来。当项目提交到本地库,即代表此次提交成为了一个历史版本。对于多个版本,你可以选择前进到某个版本或者回退到某个版本。
区域转换命令:工作区 --git add-->暂存区--git commit -->本地库
文件状态:
未跟踪状态Untracked:一个文件没有被git追踪时【说明不在仓库中也不再暂存区中】,是Untracked状态,需要
git add
来让它被git追踪。已暂存staged:一个文件刚刚被
git add
后就会变成staged状态。已提交commited:一个文件被
git commit
后,数据已经提交并保存在本地仓库中,就会变成已提交状态。【这个状态也是未修改状态,刚提交的文件就是说明是未修改的】已修改modified:一个文件被追踪后,如果在工作区中进行了修改,这个工作区的文件就会变成已修改状态,它需要重新
git add
或git commit
来将修改后的文件提交到本地库。
本地库与远程库:
由于git是分布式的,所以它可以在本地中创建版本仓库,但在多人协作开发的时候,由于处在不同的主机,所以需要某个主机作为“远程库”来管理核心的版本信息。远程库与本地库性质相同,不同它由于处在远端区域,所以称为远程库,远程库用于管理多人协作时的版本更新,开发者可以克隆远程库到本地中作为本地库,然后提交修改后的信息到远程库。
windows中git的安装
这里以windows安装为例,如果想了解其他系统的安装方式,可以自查。
1.下载安装
2.开始安装
之后就是纯粹的安装了。
git的使用
命令的使用位置:
先提一下命令的输入位置,然后下面再介绍有什么命令。在windows下,右键可以看到(如果你安装过程勾选了的话)
,第一个就是在本目录中以可视化方式打开git,第二个是以命令行方式打开git。下面介绍的使用将只介绍命令行方式。学会了命令行方式你就很容易去学会在可视化窗口下操作git。
命令行窗口是这样子的:
稍微提一下,git是linux之父创造的,git也支持很多Linux命令。所以你可以使用linux命令的方式来显示下一页、显示下一行等操作【这些命令由于是Linux的内容,这里不会讲】
初始化仓库:
git用于版本控制更新,要使用它,必须要有一个用于记录版本信息的仓库。
git的版本控制命令要在仓库中才能使用,所以要在需要进行版本控制的项目目录进行初始化仓库(也就是追踪一个项目的也必须要在这个项目文件夹进行初始化仓库。)。这样就可以对项目的文件进行管理了。
命令:
在当前目录初始化一个仓库:
git init
在当前目录初始化一个指定名字的仓库:
git init 目录名
《git官方文档》:初始化后,在当前目录下会出现一个名为 .git 的目录,所有 Git 需要的数据和资源都存放在这个目录中。不过目前,仅仅是按照既有的结构框架初始化好了里边所有的文件和目录,但我们还没有开始跟踪管理项目中的任何一个文件。
设置签名:
git的其他命令使用需要先设置用户签名,这是为了区分不同开发人员的身份。如果没有任何能够标识提交者的东西的话,就没办法知道这份工作是谁做的了,这是不允许的。
用户签名包含用户名和邮箱,仅用于本地库中区分开发者(这不是远程库的账户密码)。远程库需要的别的用户名和密码,这会在后面github协作中再讲。【用户签名中用户名和邮箱仅仅涉及用户标识,所以它不必是真实的,邮箱和用户名是可以随机的】
用户签名是必须的,不允许匿名用户提交版本操作。
用户签名也是有作用范围的,采用两种不同的设置方式。
仓库级别:只在这个仓库范围内生效
全局级别:在当前系统用户范围内生效
仓库级别的用户签名的生效优先级高于全局级别的【就近原则】
语法:
//仓库级别git config user.name 用户名 git config user.email 邮箱 //全局级别git config --global user.name 用户名 git config --global user.email 邮箱
示例:
$ git config user.name neo$ git config user.email xxx@163.com$ git config --global user.name tom$ git config --global user.email tom@163.com
仓库级别的用户签名信息保存在仓库目录下.git/config文件中;全局级别的用户签名信息保存在系统用户目录下的.gitconfig 文件中【使用
cd ~
可以快速切换到当前用户目录】查看这些配置信息可以使用
git config --list
来查看
查看状态:
命令:
git status
作用:查看当前工作区、暂存区的状态。查看工作区的文件追踪情况。
有什么状态和状态转换可以参考运行原理中的文件状态。
查看状态可以帮助我们查看文件追踪情况,避免遗漏。
提交到暂存区:
命令:
提交指定文件:
git add 文件名
提交所有文件到暂存区:
git add .
作用:目标文件会被提交到暂存区,并被git进行追踪,被追踪之后,可以使用git status查看文件的变化。
文件名可以使用通配符
*
,例如git add *.java
代表提交所有以.java为后缀的文件名。提交到暂存区,相当于给文件拍了一个快照,然后存储到暂存区中。
提交到本地库:
命令:
提交暂存区中的指定文件:
git commit 文件名
【执行这个命令后会以vim方式打开一个文件,需要在这个文件的非#
起头的行中输入版本更新描述】带描述提交:
git commit -m "版本更新描述" 文件名
【由于版本更新描述都会比较简洁明了,通常使用这个方式】把已经跟踪过的文件都提交:
git commit -a -m "版本更新描述"
作用:将暂存区的文件提交到本地库。【注意是暂存区的文件,所以如果提交到暂存之后再修改工作区的文件是不会提交后来的修改的】
对于之前已经被跟踪过了的文件(已经被仓库记录过的),当你提交到仓库过后,暂存区中是没有东西的了(但这些文件还是被追踪着的),如果你想略过把文件提交到暂存区的步骤,可以选择直接把已经追踪过的文件提交到本地库,可以使用命令:
git commit -a -m "版本更新描述"
。
从暂存区移除文件:
如果你想把某个文件删掉,但又想把这个删除操作提交到仓库中(git add只能添加文件到暂存区,git add +git commit也只能做到修改或增加文件),可以使用以下命令。
命令:
仅仅删除暂存区:git rm --cached xxx
既删除暂存区中的,也删除工作区文件:git rm xxx
要把这个删除操作提交到本地库,那么还需要
git commit
文件重命名:
如果你想对某个追踪过的文件进行文件重命名,可以使用以下命令。
命令:
git mv file_from file_to
如果要把这个文件重命名操作提交到本地库,那么还需要
git commit
查看历史记录:
每一次git commit
都相当于提交一次版本更新操作,这会留下一个版本记录。
版本记录会使用哈希值来唯一标识每一个版本。
查看版本记录可以帮助我们了解版本更新情况,以及可以帮助我们进行版本回退等操作。
命令:
选项 说明 %H
提交对象(commit)的完整哈希字串 %h
提交对象的简短哈希字串 %T
树对象(tree)的完整哈希字串 %t
树对象的简短哈希字串 %P
父对象(parent)的完整哈希字串 %p
父对象的简短哈希字串 %an
作者(author)的名字 %ae
作者的电子邮件地址 %ad
作者修订日期(可以用 -date= 选项定制格式) %ar
作者修订日期,按多久以前的方式显示 %cn
提交者(committer)的名字 %ce
提交者的电子邮件地址 %cd
提交日期 %cr
提交日期,按多久以前的方式显示 %s
提交说明 默认不用任何参数的话,
git log
会按提交时间列出所有的更新,最近的更新排在最上面。查看所有版本更新记录:
git log
【注意翻页(space)、下一行(enter)这些操作与linux的相同,q为退出】显示每次提交的内容差异:
git log -p
查看最近num次的提交:
git log -num
单行显示版本更新记录(只包含哈希值和版本描述信息):
git log --pretty=oneline
或git log --oneline
以格式化的方式显示版本更新记录:
git log --pretty=format:"%h - %an, %ar : %s"
显示版本更新中分支变化情况(用于查看分支合并情况):
git log --graph
完整显示版本更新记录:git reflog【git永远只会增加版本,不会因为版本回退而删除某个版本(完整地记录版本更新情况,连回退操作也当成一个版本更新操作),版本回退之后,git log是看不到比回退版本更高的版本的,而git reflog可以看到完整的版本更新情况】
版本回退与前进:
当发生某个重大bug时,我们可能希望回到某个版本重新开始,那么可以使用版本回退。【前进就是回退之后又突然想去新版本看看】
命令:
git reset --hard HEAD^
【一个^表示回退一步,n个表示回退 n 步】git reset --hard HEAD~n
【表示后退n步】基于索引值:
git reset --hard 哈希值
【哈希值可以使用头七位】基于当前版本与目标版本更新次数:
上面的命令仅仅介绍了使用--hard的,其实还可以使用--soft和--mixed。
hard,soft,mixed区别:
hard:在本地库进行回退,并将暂存区和工作区回退到目标版本。
mixed:在本地库进行回退,并将暂存区的追踪情况回退到目标版本,工作区不变。
soft:仅仅在本地库进行回退。暂存区的追踪情况和工作区文件不变。
前进的时候需要借助
git reflog
命令,因为需要借助这个命令来查看哈希值。git reflog中记录了所有的版本更新情况,即使是回退,它也会进行记录,不会因为回退而删除某些版本更新纪录。有了哈希值之后,就可以利用git reset --hard 哈希值
来前进了
文件对比:
有时候可能需要查看文件修改情况,可以使用文件对比来查看修改情况。
命令:
将工作区中的文件和暂存区的所有文件进行比较:
git diff
将工作区中的文件和暂存区的指定文件进行比较:
git diff 文件
将暂存区中的文件与本地库中上次提交的同名文件进行比较:
git diff --cached
将本地库中的某个版本的同名文件与工作区中的文件进行对比:
git diff 哈希值 文件名
分支操作:
什么是分支?
当多人协作开发一个项目时,可能多人开发的并不是同一种功能(例如战斗功能、交易功能),一般来说都会分别开发功能,最后再进行整合。
而这些功能可能都会基于某个前置功能,某些额外功能的开发都需要前置功能的代码,对于不使用版本控制的开发来说,可能会使用拷贝基础的代码,再进行开发。
而使用了git之后,你应该知道你可以直接使用命令来获取某个版本的代码文件的。这就省去了繁杂的拷贝了。
但git提供的分支(默认我们开发的分支是master分支)可以形象地显示分功能开发问题,而分支合并可以处理整合问题。
"分支就是不同流向的分支历史"
基于当前版本来创建分支:
git branch 分支名
查看分支:
git branch -v
或git branch
切换分支:
git checkout 分支名
创建并切换分支:
git checkout -b 分支名
合并分支:
git merge 分支名
【在某个分支执行这个命令,就是把当前分支与目标分支合并】删除分支:
git branch -d 分支名
其他话:
切换分支也会更改工作区的文件。
分支合并冲突:
上面提到了分支合并,但有时候合并会发生冲突的。例如,某个开发者基于master分支的某个版本创建了issue分支来进行了开发,(要注意的是这时候issue分支基于某一份共同的代码),但创建了issue分支之后,master分支上继续进行了版本更新,于是原本issue基于的代码发生了变化。开发完了之后,要进行分支合并,git会检查文件是否相同,它就会发现两份代码有了不一样的地方,于是就有了冲突。【就好比A从B的冰箱拿了四个苹果,B自己偷偷拿了1个苹果,后来A要还给B苹果的时候,B说原本有五个苹果,但A说只拿了四个,这就是一种冲突】
查看哪个文件发生冲突:git status
解决方案:
可以合并的文件会进行合并,当无法合并后,git会在目标文件中进行标注:
手动选择保留哪些内容:
使用
git add
来告诉git冲突解决。然后使用git commit
进行提交即可。
git与github的搭配使用:
git通常需要与远程库进行配合使用,因为通常来说git用于同步协作。这个远程库可以是自己公司架设的代码库(内部搭建的 Gitlab之类的)、也可以是常见的代码托管库(github、码云等等)
创建github仓库:
1.创建新仓库:
2.填写仓库描述:
## 给git添加远程仓库:
命令:
git remote add [远程库简写名] [url]
【远程库简写名是用于简便使用远程库的,可以随意】url地址的获取:
## 查看远程仓库:
要查看当前配置有哪些远程仓库,可以用 git remote
命令,它会列出每个远程库的简写名字及url。
命令:
git remote -v
:显示对应的克隆地址git remote show [远程库简写名]
:查看远程仓库信息
克隆远程库:
克隆远程库,就是从远程库拉取完整的版本更新记录和项目数据下来,会在本地创建一个远程仓库名的文件夹。
命令:
git clone [url]
克隆下来的仓库包含了历史更新信息,默认文件夹名字就是远程仓库名。如果不想使用使用远程仓库名,可以使用
git clone url 文件夹名称
来更改仓库的名称。url这个东西上面已经讲过是什么了。
拉取远程库分支到本地:
克隆远程库是完整地拷贝一个仓库。如果你之前已经有过这个仓库了,你可以直接拉取分支,拉取某个分支需要的流量会比克隆远程库要少得多。**在拉取的时候也会获取到版本更新记录 **
命令:
git pull 远程库简写名 分支名
:拉取某个分支的代码到本地,要求本地已经初始化了仓库,会与本地的分支进行合并。【由于会合并,所以要考虑本地库和远程库版本差距太大的时候的情况。】git fetch 远程库简写名 分支名
:拉取某个分支的代码到本地,要求本地已经初始化了仓库,但注意pull会尝试合并分支,而fetch不会尝试合并分支。
推送本地代码到远程库:
命令:
git push 远程库简写 分支名
:把本地仓库的代码提交到远程库的指定分支上。注意:推送到github的时候会提示输入用户名和密码。
标签
标签用于给版本增加标注。版本历史记录中的描述主要用于记录版本信息。但例如你想把这个版本标注成可上线的正式版,那么你可以使用标签来标注。版本记录只有哈希值跟描述文字来描述版本,而标签可以使用自定义的如v1.2之类的数字标注来标注版本。
不是每个版本都需要标签。
查看标签:
命令:
git tag
创建标签:
给当前提交打一个标签。
命令:
创建一个标签:
git tag 标签名
给某个版本提交标签:
git tag -a 标签名 版本哈希值
查看标签信息:
查看相应标签的版本信息,并连同显示打标签时的提交对象
命令:
git show 标签名
推送标签:
默认情况下,标签不会推送到远程库中,需要使用命令来推送标签。
命令:
推送指定标签到远程库:
git push 远程库简写名 标签名
推送所有标签到远程库:
git push 远程库简写名 --tags
文件提交过滤
一个项目在开发的过程中,可能会需要运行测试,所以可能会把一些运行日志文件留在项目目录中,而这些日志文件对于版本更新是没有作用的,所以不应该把他提交到远程库中。要知道每一份空间都是珍贵的。
而除了日志文件,可能还有其他一些文件,这些文件可能是比较有用的,所以我们不能去“净化工作区”----删除它们。通常我们都会定义一个.gitignore
文件,这个文件通常用于过滤上传到远程库的项目文件,在里面定义的文件不会被提交到仓库中。
.gitignore文件要位于仓库的根目录
文件 .gitignore
的格式规范如下:
所有空行或者以注释符号
#
开头的行都会被 Git 忽略。可以使用标准的 glob 模式匹配。
匹配模式最后跟反斜杠(
/
)说明要忽略的是目录。要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(
!
)取反。
所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。星号(
*
)匹配零个或多个任意字符;[abc]
匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);问号(?
)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如[0-9]
表示匹配所有 0 到 9 的数字)。
通常来说,网上都会有大量的针对不同项目的gitignore文件。普通使用的时候去网上抄一份就行,如果有特殊文件,那么就要自己手写了。
下面以在github中创建仓库时选择自动生成的针对java项目的.gitignore文件为例:
# Compiled class file 过滤.class文件*.class# Log file 过滤日志文件*.log# BlueJ files*.ctxt# Mobile Tools for Java (J2ME).mtj.tmp/# Package Files # 过滤jar包,war包*.jar*.war*.nar*.ear*.zip*.tar.gz*.rar# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xmlhs_err_pid*
写在最后
基本来说,上面这些命令足够日常使用了。
希望这篇博文能帮助到你