前言
当前的项目开发中,使用的版本管理工具大多数是SVN或者Git,而Git高效灵活、分布式开发的特性也使得其越来越受开发者的青睐,对于开发人员来说,掌握svn之外,学会使用Git也是相当有必要的,本篇文章将对Git的常用命令和场景进行介绍,希望能够给各位读者一个参考。
一、Git概述
(一)Git产生的背景
在讲Git的使用前,我们不妨先了解一下Git是在什么背景下产生的。
Git 诞生于一个极富纷争大举创新的年代。Linux 内核开源项目有着为数众多的参与者。 绝大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务上(1991-2002年间)。 到 2002 年,整个项目组开始启用一个专有的分布式版本控制系统 BitKeeper
来管理和维护代码。
到了 2005 年,开发 BitKeeper
的商业公司同 Linux
内核开源社区的合作关系结束,他们收回了 Linux 内核社区免费使用 BitKeeper 的权力。 这就迫使 Linux 开源社区(特别是 Linux 的缔造者 Linus Torvalds)基于使用 BitKeeper 时的经验教训,开发出自己的版本系统。 简单来说的话,就是以Linus Torvalds
为首的Linux内核开发大佬因为原有的代码版本管理工具不能继续免费使用了,就自己开发了Git出来...
他们对新的系统制订了若干目标:
- 速度
- 简单的设计
- 对非线性开发模式的强力支持(允许成千上万个并行开发的分支)
- 完全分布式
- 有能力高效管理类似 Linux 内核一样的超大规模项目(速度和数据量)
(二)Git和Svn的对比
SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而开发人员工作的时候,用的都是自己的电脑,所以首先要从中央服务器下载最新的版本,然后开发,开发完后,需要把自己开发的代码提交到中央服务器。
但集中式版本控制工具存在着:服务器单点故障、容错性差的缺点,一旦中央服务器挂掉了,那么开发人员也就无法进行代码提交和版本更新(也就是需要向svn服务器发起请求的操作都做不了),势必会对项目进度造成影响。当然了,搭建SVN高可用集群在一定程度上也可以避免单点故障的问题。
Git是分布式版本控制系统(Distributed Version Control System,简称 DVCS) ,分为两种类型的仓库:本地仓库和远程仓库。
- 本地仓库:是在开发人员自己电脑上的Git仓库
- 远程仓库:是在远程服务器上的Git仓库
我们可以看到,在使用Git进行版本管理的过程中,并不是只有唯一一个仓库,事实上,远程仓库创建后,开发者每次在新的工作区执行clone
或者pull
动作时,都会新建一个本地仓库。可以这样说,如果你被困在一个不能连接网络的地方时,你仍然能够提交文件,查看历史版本记录,创建项目分支等。
流程图中的三个动作解释:
- Clone:克隆,就是将远程仓库复制到本地
- Push:推送,就是将本地仓库代码上传到远程仓库
- Pull:拉取,就是将远程仓库代码下载到本地仓库
(三)Git的工作流程
1.从远程仓库中克隆代码到本地仓库
2.从本地仓库中checkout代码然后进行代码修改
3.在提交前先将代码提交到暂存区
4.提交到本地仓库。本地仓库中保存修改的各个历史版本
5.修改完成后,需要和团队成员共享代码时,将代码push到远程仓库
(四)Git的下载和安装
下载地址: https://git-scm.com/download
我们根据自己的系统选择想要下载的版本进行下载安装即可。
安装完毕后,我们点击鼠标右键可以看到
Git Gui Here
,Git Bash Here
的话,就说明是安装成功了。二、Git的托管服务
我们从上面的介绍中可以得知,Git的仓库分为远程仓库和本地仓库。那么我们如何搭建Git远程仓库呢?我们可以借助互联网上提供的一些代码托管服务来实现,其中比较常用的有GitHub、码云、GitLab等。
GitHub( 地址:https://github.com/ )
GitHub 是一个面向开源及私有软件项目的托管平台,因为只支持Git 作为唯一的版本库格式进行托管,故名gitHub
码云(地址: https://gitee.com/ )
码云是国内的一个代码托管平台,由于服务器在国内,所以相比于GitHub,码云速度会更快
GitLab (地址: https://about.gitlab.com/ )
GitLab 是一个用于仓库管理系统的开源项目,使用Git作为代码管理工具,并在此基础上搭建起来的web服务
需要注意的是,由于GitHub的服务器在国外,所以如果我们直接访问的话可以相对来说速度会比较慢,所以下面我们将使用码云作为我们的远程仓库,进行远程仓库创建的演示。
1、 在码云官网上登录我们的账号,然后在我的页面中选择创建仓库
2、定义远程仓库初始参数后点击创建
这里有一个小细节,因为我们勾选了使用ReadMe文件初始化仓库,所以仓库
init
完成后,我们会看到项目根路径下会有两个ReadMe的中英文文本文件。
3. 查看远程仓库的地址
三、Git的常用命令
在使用Git命令之前,我们先创建一个空的文件夹,然后右键点击Git Bash Here
,进入Git命令窗口
1. 环境配置
当安装Git后首先要做的事情是设置用户名称和email地址。这是非常重要的,因为每次Git提交都会使用该用户信息
#设置用户信息
git config --global user.name “itcast”
git config --global user.email “hello@itcast.cn”
#查看配置信息
git config --list
git config user.name
通过上面的命令设置的信息会保存在~/.gitconfig文件中( ~
表示C盘具体的用户目录下)
2. 获取Git仓库
要使用Git对我们的代码进行版本控制,首先需要获得Git仓库
获取Git仓库一般有两种方式:
2.1从本地直接初始化
git init
2.2从远程仓库克隆
我们只要在指定的文件夹中执行git
的克隆命令后,就可以把我们远程的仓库文件拉取下来。
git clone 远程Git仓库地址
3.操作本地仓库
在讲解本地仓库前,我们先来理清一下一些概念
版本库:前面看到的.git隐藏文件夹就是版本库,版本库中存储了很多配置信息、日志信息和文件版本信息等
工作目录(工作区):包含.git文件夹的目录就是工作目录,主要用于存放开发的代码
暂存区:.git文件夹中有很多文件,其中有一个index文件就是暂存区,也可以叫做stage。暂存区是一个临时保存修改文件的地方
和Svn不同,我们新增、修改、删除的代码并不是直接提交到我们的本地仓库上的,而是需要先执行add
命令提交到暂存区,再通过commit
命令提交到我们的本地仓库中。
说到这里,有些人可能会比较好奇,为什么Git要有暂存区这个概念呢?为什么不选择直接就把文件提交到本地仓库呢?
查看了网上一些大佬的理解,说到最多的点是保障了提交的原子性: 我们可以分批,按一个个的小功能提交到暂存区,之后再统一提交。这样的话,当你的工作区有多个正在开发的功能,你可以有选择性的选择要提交的文件,保障代码的原子性。不过个人对这种看法还是持保留意见,一开始我还以为执行commit会把所有文件都提交上去,那么这种解释就说得通,但实际上我们还是可以通过commit来选择需要提交的文件。
这样的话,保障提交原子性的观点就站不住脚。只能说姑且把暂存区当做一种Git的特性来使用吧,毕竟这个操作本身也不会浪费什么时间,只是多了一步而已。
$ git commit [file1] [file2] ... -m [message]
接着说Git工作目录下的两种状态:
- untracked 未跟踪(未被纳入版本控制)
- tracked 已跟踪(被纳入版本控制)
- Unmodified 未修改状态
- Modified 已修改状态
- Staged 已暂存状态
untracked
一般出现在我们新创建的文件上,Unmodified
则是仓库中没有变动过的文件,Modified
出现在仓库中被修改过的文件, Staged
一般是加入暂存区的文件。
3.1查看文件状态
git status
3.2将未跟踪的文件加入到暂存区
git add filename
3.3将暂存区的文件取消暂存
# 方法1
git reset filename
#方法2
git restore --staged filename
3.4提交文件至本地仓库
git commit -m [message]
3.5删除文件
git rm filename
我们可以看到,使用git rm命令后,删除的动作是会直接保存到暂存区的,我们只需要再执行一下
commit
命令就可以把删除的动作提交到本地仓库。需要注意的是,如果是直接鼠标右键删除的话,那么删除的动作是不在暂存区的,需要我们自己再手动添加到暂存区中。
4.文件忽略列表
一般我们总会有些文件无需纳入Git 的管理,也不希望它们总出现在未跟踪文件列表。 通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。 在这种情况下,我们可以在工作目录中创建一个名为 .gitignore 的文件(文件名称固定),列出要忽略的文件模式。下面是一个示例:
# 表示所有以 .a 为后缀(包括子目录)的文件都进行忽略
*.a
# 表示 lib.a 作为特例,当做特殊情况处理
!lib.a
# 忽略当前目录中的TODO文件,但其他目录下的TODO文件不进行忽略
/TODO
# 忽略 build/ 下的所有文件
build/
# 忽略doc目录下的所有.txt文件,但是不包含子目录中的 .txt文件
doc/*.txt
# 忽略 doc/ 下所有 .pdf 后缀的文件
doc/**/*.pdf
注意,在window系统中一般不允许直接创建.
开头的文件名,所以这里的话我们可以直接在git窗口下使用touch
或者vi
进行文件的创建。
5. 查看日志
git log
6. 操作远程仓库
6.1查看远程仓库
如果想查看已经配置的远程仓库服务器,可以运行 git remote 命令。 它会列出指定的每一个远程服务器的简写。 如果已经克隆了远程仓库,那么至少应该能看到 origin ,这是 Git 克隆的仓库服务器的默认名字
# 查看远程仓库
git remote -v
# 查看指定远程仓库的所有信息
git remote show 远程仓库缩写
6.2添加远程仓库
git remote add <shortname> <url>
这里可能有人会有疑问,本地关联多个远程仓库的意义是什么呢?
我们可以举这么一个场景:如果本地的 git 库,既想 push 到 gitlab ,又想推到 github 上,那么可以使用 remote 相关的命令处理。
6.3从远程仓库克隆
如果你想获得一份已经存在了的 Git 仓库的拷贝,这时就要用到 git clone 命令。 Git 克隆的是该 Git 仓库服务器上的几乎所有数据(包括日志信息、历史记录等),而不仅仅是复制工作所需要的文件。 当你执行 git clone 命令的时候,默认配置下远程 Git 仓库中的每一个文件的每一个版本都将被拉取下来。
git clone [url]
6.4移除无效的远程仓库
git remote rm 远程仓库缩写
注意:此命令只是从本地移除远程仓库的记录,并不会真正影响到远程仓库
6.5从远程仓库获取最新代码
通常的话我们有两种方式可以拉取远程仓库的代码,分别是pull
和fetch
,区别在于前者会自动merge代码,而后者不会。
我们可以看到,使用
fetch
命令拉取代码的话,我们需要再执行merge命令,这样远程仓库的数据才会到我们本地仓库。
注意:如果当前本地仓库不是从远程仓库克隆,而是本地创建的仓库,并且仓库中存在文件,此时再从远程仓库拉取文件的时候会报错(fatal: refusing to merge unrelated histories ),解决此问题可以在git pull命令后加入参数--allow-unrelated-histories
6.5推送到远程仓库
我们在推送代码时,需要指明推送的远程仓库还有具体的分支
git push -u [remote-name] [branch-name]
这里如果是第一次推送的话,会弹出弹框要求你填写账号密码,只要写上你码云上面的账号即可。
7. Git分支
几乎所有的版本控制系统都以某种形式支持分支。 使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线。Git 的master
分支并不是一个特殊分支。 它跟其它分支没有区别。 之所以几乎每一个仓库都有 master
分支,是因为git init
命令默认创建它,并且大多数人都懒得去改动它。
7.1查看分支
# 列出所有本地分支
$ git branch
# 列出所有远程分支
$ git branch -r
# 列出所有本地分支和远程分支
$ git branch -a
7.2创建分支
git branch branchName
7.3切换分支
git checkout branchName
7.4推送至远程仓库分支
git push -u [remote repository name] [branch name]
7.5合并分支
git merge [branch name]
有时候合并操作不会如此顺利。 如果你在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,Git 就没办法合并它们,同时会提示文件冲突。此时需要我们打开冲突的文件并修复冲突内容,最后执行git add命令来标识冲突已解决
7.6删除分支
$ git branch -d branch2
如果要删除的分支中进行了一些开发动作,此时执行上面的删除命令并不会删除分支,如果坚持要删除此分支,可以将命令中的-d参数改为-D。如果要删除远程仓库中的分支,可以使用命令
git push origin –d branchName
8. Git标签
像其他版本控制系统(VCS)一样,Git 可以给历史中的某一个提交打上标签,以示重要。 比较有代表性的是人们会使用这个功能来标记发布结点(v1.0 、v1.2等)。标签指的是某个分支某个特定时间点的状态。通过标签,可以很方便的切换到标记时的状态。
这里或许有读者会有点混淆,Git标签和分支都可以表示某个时间上代码的状态,那么这两者的区别在哪呢?
要想解答这个问题,我们首先要对分支和标签的应用场景做一下区分。在实际应用中标签的作用是记录过去特定重要点的书签,也可以理解为是快照。例如版本发布。而分支是动态变化的,我们为了某个开发任务而创建了一条分支,完成后再进行合并以及删除分支(即标记)。
当然了,你也可以说光创建一条分支然后不再做修改,把它当做某个特定时期的版本来记录。这自然是可以的,但和这个功能的本意不符,也浪费了branch资源。
8.1列出已有的标签
# 列出所有tag
$ git tag
# 查看tag信息
$ git show [tag]
8.2新建一个标签
git tag [tagName]
8.3将标签推送至远程仓库
# 提交指定tag
$ git push -u [remote] [tag]
8.4检出标签
git checkout -b [branch] [tag]
8.5删除标签
git tag -d [tag]
8.6删除远程标签
git push origin :refs/tags/[tag]
至此,有关Git的常用命令和概念就介绍到这里了。对于GIt的使用,重点在于理解它的作用流程和重点概念的思想,比如知道标签、分支、远程仓库等具体的含义和使用场景,这样才能在工作中真正做到游刃有余。
参考资料:
为什么要先 git add 才能 git commit ?:
https://www.zhihu.com/question/19946553
标签与Git中的分支有何不同?我应该在哪里使用?:
https://www.imooc.com/wenda/detail/584818