Git 详解

Git 核心概念及

Posted by leone on 2017-05-11

Git

Git核心概念讲解

什么是 Git

Git是一个分布式版本控制软件,最初由林纳斯·托瓦兹创作,于2005年以GPL发布。最初目的是为更好地管理Linux内核开发而设计。应注意的是,这与GNU Interactive Tools(一个类似Norton Commander界面的文件管理器)有所不同。git最初的开发动力来自于BitKeeper和Monotone。git最初只是作为一个可以被其他前端(比如Cogito或Stgit)包装的后端而开发的,但后来git内核已经成熟到可以独立地用作版本控制。很多著名的软件都使用git进行版本控制,其中包括Linux内核、X.Org服务器和OLPC内核等项目的开发流程。

版本控制系统

Git 是目前世界上最优秀的分布式版本控制系统。版本控制系统是能够随着时间的推进记录一系列文件的变化以便于你以后想要的退回到某个版本的系统。版本控制系统分为三大类:本地版本控制系统,集中式版本控制系统和分布式版本控制系统

本地版本控制(Local Version Control Systems)是将文件的各个版本以一定的数据格式存储在本地的磁盘(有的VCS 是保存文件的变化补丁,即在文件内容变化时计算出差量保存起来),这种方式在一定程度上解决了手动复制粘贴的问题,但无法解决多人协作的问题。

集中式版本控制(Centralized Version Control Systems)相比本地版本控制没有什么本质的变化,只是多了个一个中央服务器,各个版本的数据库存储在中央服务器,管理员可以控制开发人员的权限,而开发人员也可以从中央服务器拉取数据。集中式版本控制虽然解决了团队协作问题,但缺点也很明显:所有数据存储在中央服务器,服务器一旦宕机或者磁盘损坏,会造成不可估量的损失。

分布式版本控制( Distributed Version Control System)与前两者均不同。首先,在分布式版本控制系统中,像 Git,Mercurial,Bazaar 以及 Darcs 等,系统保存的的不是文件变化的差量,而是文件的快照,即把文件的整体复制下来保存,而不关心具体的变化内容。其次,最重要的是分布式版本控制系统是分布式的,当你从中央服务器拷贝下来代码时,你拷贝的是一个完整的版本库,包括历史纪录,提交记录等,这样即使某一台机器宕机也能找到文件的完整备份。

Git 工作区、暂存区、版本库概念

工作区(WorkingDirectory)

工作目录是对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘 上供你使用或修改。

暂存区(StagingArea)

暂存区域是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中。 有时候也被称作‘索引’,不过一般说法还是叫暂存区域。

版本库(Repository)

Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆 仓库时,拷贝的就是这里的数据。

Git 工作流程

1.在工作目录中修改文件。

2.暂存文件,将文件的快照放入暂存区域。

3.提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。

如果 Git 目录中保存着的特定版本文件,就属于已提交状态。 如果作了修改并已放入暂存区域,就属于已暂存状 态。 如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。 在Git 基础一章,你会进一步了解 这些状态的细节,并学会如何根据文件状态实施后续操作,以及怎样跳过暂存直接提交。

Git 安装

1
2
3
4
5
6
7
Windows: 在[Git官网](https://git-scm.com)下载Git GUI安装软件并安装

MacOS: 使用HomeBrew安装`brew install git`

CentOS: 使用CentOS默认的包管理器安装`yum install git`

Ubuntu: 使用apt-get安装`apt-get install git`

Git 配置

用户配置

当安装完 Git 应该做的第一件事就是设置用户名称与邮件地址。这样做很重要,因为每一个 Git 的提交都会使用这些信息,并且它会写入到每一次提交中,不可更改:

1
2
$ git config --global user.name "user"
$ git config --global user.email "user@mail.com"

再次强调,如果使用了 --global 选项,那么该命令只需要运行一次,因为之后无论你在该系统上做任何事情, Git 都会使用这些信息。 当你想针对特定项目使用不同的用户名称与邮件地址时,可以在那个项目目录下运行不使用 --global 选项的命令来配置。很多 GUI 工具都会在第一次运行时帮助你配置这些信息

文本编辑器

既然用户信息已经设置完毕,你可以配置默认文本编辑器了,当 Git 需要您输入信息时会调用它。 如果未配置,Git 会使用操作系统默认的文本编辑器,通常是 Vim。 如果你想使用不同的文本编辑器,例如 Emacs,可以这样做:
$ git config --global core.editor emacs

注:Vim 和 Emacs 是像 Linux 与 Mac 等基于 Unix 的系统上开发者经常使用的流行的文本编辑器。 如果你对这些编辑器都不是很了解或者你使用的是 Windows 系统,那么可能需要搜索如何在 Git 中配置你最常用的编辑器。 如果你不设置编辑器并且不知道 Vim 或 Emacs 是什么,当它们运行起来后你可能会被弄糊涂、不知所措

检查配置信息

如果想要检查你的配置,可以使用 git config --list 命令来列出所有 Git 当时能找到的配置。

1
2
3
4
5
6
7
8
9
[root@localhost ~]# git config --list
user.name=leone
user.email=exklin@gmail.com
[root@localhost ~]#
color.status=auto
color.branch=auto
color.interactive=auto
color.diff=auto
...

Git 常用命令详解

1.Git文件操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
$ git help [command]                  # 显示command的help
$ git show [commit sha id] # 显示某次提交的内容
$ git checkout [file] # 抛弃工作区修改
$ git checkout . # 抛弃工作区修改
$ git add [file] # 将工作文件修改提交到本地暂存区
$ git add . # 将所有修改过的工作文件提交暂存区
$ git reset [file] # 从暂存区恢复到工作文件
$ git reset -- . # 从暂存区恢复到工作文件
$ git reset --mixed HEAD~1 # 修改版本库,修改暂存区,保留工作区
$ git reset --soft HEAD~2 # 修改版本库,保留暂存区,保留工作区
$ git reset --hard HEAD~3 # 修改版本库,修改暂存区,修改工作区
$ git commit --amend # 修改最后一次提交记录
$ git revert [$id] # 恢复某次提交的状态,恢复动作本身也创建次提交对象
$ git revert HEAD # 恢复最后一次提交的状态
$ git status # 查看当前工作区状态
$ git config --list # 看所有用户
$ git ls-files # 查看已经被提交的文件
$ git commit -a # 提交当前repos的所有的改变
$ git commit -v # 参数-v可以查看看commit的差异
$ git commit -m "message" # 提交到本地并添加提交信息
$ git commit -am "init" # 添加并提交

$ git log 的常用选项

-p 按补丁格式显示每个更新之间的差异。
--stat 显示每次更新的文件修改统计信息。
--shortstat 只显示 --stat 中最后的行数修改添加移除统计。
--name-only 仅在提交信息后显示已修改的文件清单。
--name-status 显示新增、修改、删除的文件清单。
--abbrev-commit 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符。
--relative-date 使用较短的相对时间显示(比如,“2 weeks ago”)。
--graph 显示 ASCII 图形表示的分支合并历史。
--pretty 使用其他格式显示历史提交

$ git rm --cached [file] # 从暂存区中删除文件
$ git rm -f [file] # 强行移除版本控制的文件
$ git rm -r --cached [file] # 递归删除-r,是文件夹的时候有用

$ git diff [file] # 比较当前文件和暂存区文件差异
$ git diff [$id] [$id] # 比较两次提交之间的差异
$ git diff [branch1]..[branch2] # 在两个分支之间比较
$ git diff --staged # 比较暂存区和版本库差异
$ git diff --cached # 比较暂存区和工作区差异
$ git diff --stat # 仅仅比较统计信息

$ git stash # 暂存当前正在进行的工作
$ git stash push # 将暂存给push到一个临时空间中
$ git stash list # 查看所有缓存的代码
$ git stash clear # 清空缓存区内容
$ git stash drop # 移除某个贮藏
$ git stash save # 指定message
$ git stash show # 查看指定stash的diff
$ git stash apply stash@{1} # 取出指定的缓存代码
$ git stash pop # 将文件从临时空间pop下来
$ git fetch origin dev:dev # 获取最新版本到本地不会自动merge
$ git pull origin master:master # 获取最新版本到本地会自动merge

Git分支操作相关命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ git branch                          # 查看分支
$ git branch [dev] [master] # 在master创建dev分支
$ git branch -v # 查看各个分支最后提交信息
$ git branch -r # 查看远程分支信息
$ git branch -a # 查看所有分支信息
$ git branch -m [aaa] [bbb] # 将aaa 重命名为bbb
$ git branch [name] # 从当前分支创建分支
$ git branch -d [branch] # 删除某个分支
$ git branch -D [branch] # 强制删除某个分支
$ git branch --set-upstream-to origin/test master # 本地分支和远程分支关联
$ git branch --set-upstream-to=origin/master help #
$ git branch --merged # 查看已经被合并到当前分支的分支
$ git branch --no-merged # 查看尚未被合并到当前分支的分支

$ git commit [$id] -b [new_branch] # 从历史提交记录创建分支
$ git commit [$id] # 从历史提交记录checkout出来,但无分支信息,切换到其他分支会自动删除
$ git checkout [name] # 切换分支
$ git checkout --track origin/dev # 切换到远程dev分支
$ git checkout -b [name] # 从当前分支新建并切换到name
$ git checkout -b [new_br] [br] # 基于branch创建新的new_branch
$ git merge origin/dev # 将分支dev与当前分支进行合并
$ git merge [branch] # 将branch分支合并到当前分支
$ git merge --no-ff [branch] # 使用 squash 方式合并,把多次分支commit历史压缩为一次
$ git merge origin/master --no-ff # 不使用 fast-forward 方式合并,保留分支的 commit 历史
$ git rebase master [branch] # 将master rebase到branch,相当于: git clone [branch] && git rebase master && git clone master && git merge [branch]

Git远程分支管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ git pull                              # 抓取远程仓库所有分支更新并合并到本地
$ git pull --no-ff # 抓取远程仓库所有分支更新并合并到本地,不要快进合并
$ git fetch origin # 抓取远程仓库更新
$ git merge origin/master # 将远程主分支合并到本地当前分支
$ git clone --track origin/branch # 跟踪某个远程分支创建相应的本地分支
$ git clone -b [l_b] origin/[r_b] # 基于远程分支创建本地分支,功能同上
$ git push # push所有分支
$ git push origin master # 将本地指定分支推到远程主分支
$ git push -u origin master # 将本地主分支推到远程(如无远程主分支则创建,用于初始化远程仓库)
$ git clone [git@xxx.git] # 克隆远程仓库
$ git remote -v # 查看远程服务器地址和仓库名称
$ git remote show [name] # 查看远程服务器仓库状态
$ git remote add [name] [url] # 添加远程仓库地址
$ git remote rm [repository] # 删除远程仓库
$ git remote set-url --push [name] [newUrl]# 修改远程仓库
$ git pull [rName] [lName] # 拉取远程分支
$ git push [rName] [lName] # 推送远程分支
$ git push origin [lname]:[rname] # 本地分支push到远程
$ git push origin -d [name] # 删除远程分支-d也可以用--delete
$ git remote set-head origin master # 设置远程仓库的HEAD指向master分支
$ git branch --set-upstream master origin/master

Git版本回退操作相关命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
HEAD :当前版本
HEAD^ :上一个版本
$ git log # 查看commit的信息
$ git log --pretty=oneline # 单行展示历史记录
$ git log --oneline # 单行简单展示
$ git reflog # 查看命令历史记录
$ git checkout . # 撤销所有本地改动代码
$ git checkout [file] # 撤销所有本地改动代码
$ git reset HEAD . # 撤销所有add文件
$ git reset HEAD [file] # 撤销单个add文件
$ git reset --soft HEAD # 只回退commit的信息,保留修改代码
$ git reset --hard HEAD^ # 彻底回退到上次commit版本,不保留修改代码
$ git revert # 以前commit的id
$ git reset --hard [branch] # 本地代码回退到与git远程仓库保持一致
--hard 参数会抛弃当前工作区的修改
--soft 参数的话会回退到之前的版本,但是保留当前工作区的修改,可以重新提交

Git标签操作相关命令

1
2
3
4
5
6
7
8
9
$ git tag                               # 查看标签
$ git tag [name] # 创建版本
$ git tag -d [name] # 删除版本
$ git tag -r # 查看远程版本
$ git tag -a [name] -m [message] # 创建带注释的tag
$ git push origin [name] # 创建远程版本(本地版本push到远程)
$ git push origin :refs/tags/[name] # 删除远程版本
$ git pull origin --tags # 合并远程仓库的tag到本地
$ git push origin --tags # 上传本地tag到远程仓库

Git子模块(submodule)相关操作命令

当我们在开发一个项目的时候需要引用另一个库(一个独立版本控制的git仓库)这个时候我们就可以使用git 的submodule

1
2
3
4
5
6
7
8
9
10
$ git submodule add [url] [path]        # 添加子模块
$ git submodule init # 初始化子模块,只在首次检出仓库时运行一次就行
$ git submodule update # 更新子模块 每次更新或切换分支后都需要运行一下

# 删除子模块:分4步

$ git rm --cached [path]
编辑“.gitmodules”文件,将子模块的相关配置节点删除掉
编辑“ .git/config”文件,将子模块的相关配置节点删除掉
手动删除子模块残留的目录

Git flow

1
2
3
4
5
6
7
8
9
$ git flow init     			# 初始化
$ git flow feature # 开始新Feature
$ git flow bugfix # Manage your bugfix branches.
$ git flow release # Manage your release branches.
$ git flow hotfix # Manage your hotfix branches.
$ git flow support # Manage your support branches.
$ git flow version # Shows version information.
$ git flow config # Manage your git-flow configuration.
$ git flow log # Show log deviating from base branch.

Git补丁管理

Git 提供了两种补丁方案,一是用git diff生成的UNIX标准补丁.diff文件,二是git format-patch生成的Git专用.patch 文件。

  • .diff文件只是记录文件改变的内容,不带有commit记录信息,多个commit可以合并成一个diff文件。
  • .patch文件带有记录文件改变的内容,也带有commit记录信息,每个commit对应一个patch文件。

git apply [options] […]

1
2
3
4
5
6
# 某次提交(含)之前的几次提交n指从sha1 id对应的commit开始算起n个提交。
$ git format-patch [commit sha1 id] -[n] # 生成补丁
$ git format-patch [commit sha1 id]..[commit sha1 id] # 生成补丁
$ git diff [commit sha1 id] [commit sha1 id] > [name] # 生成补丁
$ git apply --check 00001-commit-message.patch # 测试补丁能否成功
$ git apply 00001-commit-message.patch # 应用补丁

Git忽略一些文件、文件夹不提交

在仓库根目录下创建名称为“.gitignore”的文件,写入不需要的文件夹名或文件,每个元素占一行即可,如

1
2
3
4
5
6
target/
*.zip
*.tar
*.tar.gz
.DS_Store
Thumbs.db

在工作中总结的一些常见的命令,今后还会继续补充,如有遗漏欢迎补充。