Git
是一种分布式版本控制系统,它可以不受网络连接的限制,加上其它众多优点,目前已经成为程序开发人员做项目版本管理时的首选,非开发人员也可以用 Git
来做自己的文档版本管理工具。
一、Git 基础
1.1 Git 与 SVN 区别
Git
不仅仅是个版本控制系统,它也是个内容管理系统(CMS
),工作管理系统等。
如果你是一个具有使用 SVN
背景的人,你需要做一定的思想转换,来适应 Git
提供的一些概念和特征。
Git
与 SVN
区别点:
Git
是分布式的,SVN
不是:这是Git
和其它非分布式的版本控制系统,例如SVN
,CVS
等,最核心的区别。Git
把内容按元数据方式存储,而SVN
是按文件:所有的资源控制系统都是把文件的元信息隐藏在一个类似.svn
、.cvs
等的文件夹里。Git
分支和SVN
的分支不同:分支在SVN
中一点都不特别,其实它就是版本库中的另外一个目录。Git
没有一个全局的版本号,而SVN
有:目前为止这是跟SVN
相比Git
缺少的最大的一个特征。Git
的内容完整性要优于SVN
:Git
的内容存储使用的是SHA-1
哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏。
1.2 版本控制
版本控制(Revision control
)是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史,方便查看更改历史记录,备份以便恢复以前的版本的软件工程技术。一句话就是用于管理多人协同开发项目的技术。
- 优点
- 实现跨区域多人协同开发;
- 追踪和记载一个或者多个文件的历史记录;
- 组织和保护你的源代码和文档;
- 统计工作量;
- 并行开发、提高开发效率;
- 跟踪记录整个软件的开发过程;
- 减轻开发人员的负担,节省时间,同时降低人为错误。
1.3 Git 工作区、暂存区和版本库
工作区:就是你在电脑里能看到的目录。
版本库:工作区有一个隐藏目录
.git
,这个不算工作区,而是Git
的版本库。暂存区:本地版本库里存了很多东西,其中最重要的就是称为
stage
(或者叫index)的暂存区。
下面这个图展示了工作区、版本库中的暂存区和版本库之间的关系:
1.4 分支
分支是为了将修改记录的整个流程分开存储,让分开的分支不受其它分支的影响,所以在同一个数据库里可以同时进行多个不同的修改。
- 主分支(Master)
是 Git 为我们自动创建的第一个分支,也叫主分支,其它分支开发完成后都要合并到 master
- 分支合并(Merge)
将某分支上的更改联接到此主干或同为主干的另一个分支。
- 合并冲突(Conflict)
多人对同一文件的工作副本进行更改,并将这些更改提交到仓库。
1.5 标签
标签是用于标记特定的点或提交的历史,通常会用来标记发布版本的名称或版本号(如:publish/0.0.1
),虽然标签看起来有点像分支,但打上标签的提交是固定的,不能随意的改动。
1.6 头(HEAD)
头是一个象征性的参考,最常用以指向当前选择的分支。
二、四个区域
2.1 Workspace:工作区
- 程序员进行开发改动的地方,是你当前看到的,也是最新的;
- 平常我们开发就是拷贝远程仓库中的一个分支,基于该分支进行开发。在开发过程中就是对工作区的操作。
2.2 Index / Stage:暂存区
.git
目录下的index
文件, 暂存区会记录git add
添加文件的相关信息(文件名、大小、timestamp
...),不保存文件实体, 通过id
指向每个文件实体。可以使用git status
查看暂存区的状态。暂存区标记了你当前工作区中,哪些内容是被Git
管理的;- 当你完成某个需求或功能后需要提交到远程仓库,那么第一步就是通过
git add
先提交到暂存区,被Git
管理。
2.3 Repository:本地仓库
- 保存了对象被提交 过的各个版本,比起工作区和暂存区的内容,它要更旧一些;
git commit
后同步index
的目录树到本地仓库,方便从下一步通过git push
同步本地仓库与远程仓库的同步。
2.4 Remote:远程仓库
远程仓库的内容可能被分布在多个地点的处于协作关系的本地仓库修改,因此它可能与本地仓库同步,也可能不同步,但是它的内容是最旧的。
2.5 四个区域的关系
- 任何对象都是在工作区中诞生和被修改;
- 任何修改都是从进入 index 区才开始被版本控制;
- 只有把修改提交到本地仓库,该修改才能在仓库中留下痕迹;
- 与协作者分享本地的修改,可以把它们 push 到远程仓库来共享。
三、Git 文件操作
3.1 文件的四种状态
Untracked
: 未跟踪, 此文件在文件夹中, 但并没有加入到Git
库, 不参与版本控制.通过git add
状态变为Staged
;Unmodify
: 文件已经入库, 未修改, 即版本库中的文件快照内容与文件夹中完全一致. 这种类型的文件有两种去处, 如果它被修改, 而变为Modified.
如果使用git rm
移出版本库, 则成为Untracked
文件;Modified
: 文件已修改, 仅仅是修改, 并没有进行其他的操作. 这个文件也有两个去处,通过git add
可进入暂存staged
状态, 使用git checkout
则丢弃修改过, 返回到unmodify
状态, 这个git checkout
即从库中取出文件, 覆盖当前修改;Staged
: 暂存状态.执行git commit
则将修改同步到库中, 这时库中的文件和本地文件又变为一致, 文件为Unmodify
状态. 执行git reset HEAD filename
取消暂存, 文件状态为Modified
。
3.2 查看文件状态
git status # 添加指定文件到暂存区;
git add (dir) # 添加当前目录的所有文件到暂存区。
3.3 移除文件与目录(撤销 add)
git rm --cached # 直接从暂存区删除文件,工作区不做出改变
git checkout . # 用赞寻去全部或指定文件替换工作区的文件,这个操作很危险,会清楚工作区中未添加到暂存区的改动。
git clean # 一般会加上参数 -df,-d 表示包含目录,-f 表示强制清除;
3.4 查看文件修改后的差异
git status # 只能查看对哪些文件做了改动,如果要看改动了什么,可以用;
git diff (files) # 查看文件修改后的差异
此外,还有以下两个常用命令:
git diff --cached # 比较暂存区的文件与之前已经提交过的文件
git diff HEAD~n # 比较 repo 与工作空间中的文件差异
3.5 签出
- 使用情景
该文件已经存在仓库中,工作区已经对其进行修改过了,如果想撤销修改,可以使用 checkout
。
检出命令git checkout 是 git 最常用的命令之一,同时也是一个很危险的命令,因为折腾命令会重写工作区。
3.6 提交
git commit -m [message] # 提交暂存区到仓库区
git commit [file1] [file2] ... -m [message] # 提交暂存区的指定文件到仓库区
git commit -a # 提交工作区自上次 commit 之后的变化,直接到仓库区,跳过了 add,对新文件无效
git commit -v # 提交时显示所有 diff 信息
3.7 撤销更新
- 撤销暂存区更新
git reset HEAD [filename] # 将暂存区指定文件移出到工作区
- 撤销本地仓库更新
放弃工作区和暂存区的改动,同时 HEAD
指针指向前一个 commit
对象:
git log #查看提交日志,
git reset --hard HELD~1 #撤销了上一次的提交,这里的上一次提交日志就不存在了。
cat [filename] #查看文件内内容
git revert #把指定的提交修改回滚,并同时生成一个新的提交
3.8 日志与历史
git log #查看所有提交日志;
git log [filename] #查看某文件提交日志;
history #查看在 bash 下输入过的指令;
git reflog #查看仓库中所有分支的所有更新记录,包括已经撤销的更新。
四、Git 配置
4.1 分类介绍
4.2 Git 常用配置命令
- 对全局的
Git
用户名和邮箱进行配置
git config --global user.name "xxxx" :设置所有仓库提交的用户名
git config --global user.email "[email protected]":设置所有仓库提交的邮箱
- 查看所有配置或某一项配置:
git config --global --list;
git config --global --get user.name;
git config --global --get user.email;
五、本地仓库
5.1 创建全新仓库
- 使用当前目录作为
Git
仓库
只需使它初始化:git init
;执行后可以看到,仅仅在项目目录多出了一个.git
目录;
- 使用指定目录作为
Git
仓库
使用如下命令,可以把创建目录与仓库一起完成:
git init [project-name] # 新建一个目录,将其初始化为 Git 代码库
5.2 克隆远程仓库
- 将远程服务器上的仓库完全镜像一份至本地,而不是取某一个特定版本,所以用
clone
而不是checkout
,语法格式为:
git clone (url)
例如:我们要从克隆的远程仓库托管在 Github
上,首先,我们先前往 Github
上拷贝地址如:
https://github.com/vanDusty/Java-Note,然后执行:
git clone https://github.com/vanDusty/Java-Note```
克隆远程仓库到当前目录。
### 5.3 Git 仓库目录结构
- 初始化仓库使用 `git init` 命令,初始目录(`.git`)结构:
```xml
.git
├── branches
├── config
├── description
├── HEAD
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── prepare-commit-msg.sample
│ ├── pre-push.sample
│ ├── pre-rebase.sample
│ └── update.sample
├── info
│ └── exclude
├── objects
│ ├── info
│ └── pack
└── refs
├── heads
└── tags
9 directories, 13 files
- 这个初始化后的仓库,当创建完分支
dev-branch
,并push
后的目录结构:
.git/
├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── prepare-commit-msg.sample
│ ├── pre-push.sample
│ ├── pre-rebase.sample
│ └── update.sample
├── index
├── info
│ └── exclude
├── logs
│ ├── HEAD
│ └── refs
│ ├── heads
│ │ ├── dev-branch
│ │ └── master
│ └── remotes
│ └── origin
│ └── master
├── objects
│ ├── 2d
│ │ └── d2c71b69c837a7459f4bd9c9f7db6520731d06
│ ├── 5c
│ │ └── 7b8eda18a75e13d27c31e65a54b0abd7948510
│ ├── 77
│ │ └── cad3aecf7c2754231095598119979d62a1e1da
│ ├── info
│ └── pack
└── refs
├── heads
│ ├── dev-branch
│ └── master
├── remotes
│ └── origin
│ └── master
└── tags
19 directories, 25 files
5.4 目录结构说明
branches
目录
一种不常用的存储速记方式,用于指定 git fetch
,git pull
和 git push
的 URL
,目前已基本不用。
COMMIT_EDITMSG
文件
commit
编辑信息,仅记录最近一次提交的 commit
编辑信息。
config
文件
存储当前仓库的配置信息
description
文件
用于在 GitWeb
中展示项目的描述信息。
HEAD
文件
存储 HEAD 指针,指向当前分支,即:记录当前活动分支。
hooks
目录
目录下存储了许多钩子文件(一些脚本),这些文件是各种 Git
命令使用的自定义脚本,可以在 Git
特定阶段自动执行。
index
文件
暂存区(stage
),二进制文件。
info
目录
存储库的其他信息将记录在此目录中。
logs
目录
保存所有更新的引用记录。
objects
目录
简单理解就是:objects
目录是 Git
的数据库,存储所有数据内容。
refs
目录
(1)heads
目录
目录有以各个本地分支名命名的文件,保存对应分支最新提交的 ID
,是通过 SHA1
算法计算的一个字符串。
(2)remotes
目录
目录有以各个远程分支名命名的文件,保存对应分支最新提交的 ID
,和 heads
目录一个原理。
(3)tags
目录
存储在开发过程中打的标签,里面的文件以标签名命令,文件内容为对应的 ID
。
六、远程仓库
6.1 常见的托管平台
- GitHub
对,就是那个“全世界最大同性交友网站”。https://github.com
绝大多数好的开源项目都来自 GitHub,但是 GitHub 只能新建公开的 Git 仓库,私有仓库要收费,有时候访问比较卡,如果你做的是一个开源项目,可以首选 GitHub。
- Gitlab
提到 GitHub 就会自然的想到 Gitlab,Gitlab 支持无限的公有项目和私有项目。https://about.gitlab.com
- Bitbucket
Bitbucket免费支持 5 个开发成员的团队创建无限私有代码托管库。https://bitbucket.org
- Gitee 码云
前面说的都是国外的,下面来说几个国内的。码云,个人开发者可免费创建 1000 个项目(不限公有、私有),提供最多 5G 的免费代码存储空间。http://git.oschina.net,
- Coding.net
谈到 Coding.net,首先必须提的是速度快,同样一个账号最多可以创建 1000 个项目(5 个私有),也支持任务的创建等。https://coding.net
6.2 远程仓库操作
申请到了 Git
远程仓库的帐号并创建了一个空的远程仓库现在我们就可以结合本地的仓库与远程仓库一起协同工作了,多人协同开发,这也是我们工作时的情形,这里我们全部使用命令完成(实际开发中很多开发工具例如:IDEA
等,无需命令操作)。
- 克隆项目
远程操作的第一步,通常是从远程主机克隆一个版本库,这时就要用到 git clone
命令,例如:
git clone https://github.com/vanDusty/Java-Note
该命令会在本地主机生成一个目录,与远程主机的版本库同名。如果要指定不同的目录名,可以将目录名作为 git clone
命令的第二个参数。
git clone <版本库的网址> <本地目录名>
git remote
为了便于管理,Git
要求每个远程主机都必须指定一个主机名。git remote
命令就用于管理主机名。
不带选项的时候, git remote
命令列出所有远程主机名。
git remote -v #参看远程主机的网址。
结果表示,当前只有一台远程主机,叫做 origin
,以及它的网址。
origin [email protected]:jquery/jquery.git (fetch)
origin [email protected]:jquery/jquery.git (push)
克隆版本库的时候,所使用的远程主机自动被 Git
命名为 origin
。如果想用其他的主机名,需要用 git clone
命令的 -o
选项指定:
git clone -o dusty https://github.com/vanDusty/Java-Note
上面命令表示,克隆的时候,指定远程主机叫做 dusty
更多命令如下:
$ git remote show <主机名> #查看该主机的详细信息
$ git remote add <主机名> <网址> #添加远程主机
$ git remote rm <主机名> #删除远程主机
$ git remoterename<原主机名> <新主机名> #远程主机的改名
git fetch
一旦远程主机的版本库有了更新,需要将这些更新取回本地,这时就要用到该命令。
git fetch #将某个远程主机的更新,全部取回本地。
git fetch <远程主机名> <分支名> #取回指定主机名的指定分支
所取回的更新,在本地主机上要用"远程主机名/分支名"的形式读取。比如 origin
主机的 master
,就要用 origin/master
读取。
git checkout -b newBrach origin/master #在 origin/master 的基础上,创建一个新分支。
也可以使用 git merge
命令或者 git rebase
命令,在本地分支上合并远程分支。
git merge origin/master #或者$ git rebase origin/master
git pull
git pull
命令的作用是,取回远程主机某个分支的更新,再与本地的指定分支合并。它的完整格式稍稍有点复杂。
git pull <远程主机名> <远程分支名>:<本地分支名>
示例:取回 origin
主机的 master
分支,与本地的 develop
分支合并:
git pull origin master:develop
如果远程分支( master
)要与当前分支合并,则冒号后面的部分可以省略。可以简写为:
git pull origin master
上面命令表示,取回 origin/master
分支,再与当前分支合并。实质上,这等同于先做 git fetch
,再做 git merge
。
git fetch origin
git merge master/develop
git fetch
和git pull
的区别
git fetch:相当于是从远程获取最新版本到本地,不会自动合并。
git pull:相当于是从远程获取最新版本并merge到本地