1. Git 初始化配置
查看git版本:git --version
配置信息:
git config --global user.name "ice"
git config --global user.email "ice@example.com"
这样你提交代码有bug时,测试人员可以查到是谁提交,并向你发邮件
这里可以设置几个级别的配置:
--system
:针对当前系统的配置--global
:针对当前用户的配置- 不写参数:默认只针对此项目配置
要查看已有的配置信息,可以使用命令:
git config --list
2. Git 底层命令
2.1 基础的 Linux 命令
-
clear
:清屏 -
echo
:往控制台打印信息,如:
-
ls
系列命令(简略):ls -1
每行仅显示一个文件或目录名称
ls -a
或ls --all
列出当前目录下下所有文件和目录
ls -A
或ls --almost-all
类似ls -a
,但是不列出当前目录和父目录
ll
或ls -l
显示当前目录下文件详细信息
-
find 目录名
将对应目录下的所有文件&子孙目录显示到控制台
-
find 目录名 -type f
将对应目录下的所有文件显示到控制台
-
rm 文件名
删除文件 -
mv src dest
移动文件,可以用此命令重命名文件 -
cat 文件url
查看对应文件内容 -
vim 文件url
按i进入插入模式,进行文件得编辑
编辑完成后,先按ESC,然后输入:
进行命令的执行q!
:强制退出,不保存wq
:保存退出set nu
:设置行号
2.2 初始化新仓库
命令:
git init
在你工作区文件夹内执行,将其纳入Git管理
创建完成后,该文件夹里会创建一个.git
文件夹,里面包括:
其中每个文件夹含义为:
hooks
:该目录包含客户端或服务端的钩子脚本,类似面向切面编程info
:该目录包含一个全局性排除文件logs
:该目录保存日志信息objects
:该目录存储所有的数据内容refs
:该目录存储指向数据(分支)的提交对象的指针config
:该文件包含项目特有的配置选项description
:该文件用来显示对仓库的描述信息HEAD
:该文件指向目前被检出的分支index
:该文件保存暂存区信息
2.3 git 对象
Git 的核心部分是一个简单的键值对数据库。你可以向数据库插入任意类型的内容,它会返回一个键值,通过该键值可以在任意时刻再次检索该内容。
-
向数据库写入内容,并返回对应键值
例如:echo 'hello world' | git hash-object -w --stdin
-w
选项指示hash-object命令存储数据对象;若不指定此选项,则该命令仅反回对应的键值--stdin
选项则指示该命令从标准输入读取内容;若不指定此选项,则须在该命令尾部给出待存储文件得路径 -
根据键值拉取数据
git cat-file -p 哈希值
-p
选项指示该命令自动判断内容的类型,并为我们显示格式友好的内容返回对应文件得内容
-
对一个文件进行简单的版本控制
-
将一个文件纳入Git数据库
git hash-object -w ./text.txt
-
查看存储的内容
git cat-file -p 04be3f68d453d5754167c0e0cbcd91c92d61bf39
修改文件后阻碍次提交,会生成2个hash,并不是增量提交
-
-
问题
- 记住文件的每一个版本所对应的 SHA-1 值并不现实
- 在 Git 中,文件名并没有被保存,我们仅保存了文件得内容
解决方案:树对象
2.4 树对象
树对象,它能解决文件名保存的问题,也允许我们将多个文件组织到一起。Git 以一种类似于UNIX 文件系统的方式存储内容。所有内容均以树对象和数据对象(git对象)的形式存储,其中树对象对应了 UNIX 中的目录项,数据对象(git对象)则大致上对应文件内容。一个树对象包含了一条或多条记录(每条记录含有一个指向git对象或者子树对象的 SHA-1 指针,以及相应的模式、类型、文件名信息)。一个树对象也可以包含另一个树对象。
-
查看暂存区
git ls-files -s
-
构建树对象
我们可以通过update-index
;write-tree
;read-tree
等命令来构建树对象并塞入到暂存区-
利用
update-index
命令为 text.txt 文件首个版本创建一个暂存区,并通过write-tree
命令生成树对象git update-index --add --cacheinfo 100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad test.txt
文件模式:
- 100644:表示一个普通文件
- 100755,:表示一个可执行文件
- 120000:表示一个符号链接
--add
选项:因为此前改该文件并不在暂存区中,首次需要使用此选项添加--cacheinfo
选项:因为要添加的文件位于 git 数据库中,而不是位于当前目录下,所以需要此命令此时暂存区就有内容了
- 从上面结果可以看出,git对象和树对象都是存储在objects文件夹内的
update-index
命令是创建缓存区,write-tree
是将暂存区生成树对象,存入版本库(数据区)write-tree
写入版本库时不会删除暂存区内容
git对象代表文件的一次次版本,树对象代表项目的一次次版本
-
新增 new.txt 将 new.txt 和 test.txt 文件得第二个版本塞入暂存区,并通过
write-tree
命令生成树对象存入版本库
这里可以看出,修改过的文件和修改前的文件存的是2个git对象,hash值是不同的
git update-index --add new.txt
这个命令会执行2个操作,先是创建git对象存储到版本库,紧接着创建树对象到暂存区
-
将第一个树对象加入第二个树对象,使其成为新的树对象
此时项目的第三个版本不仅包括修改过的 test.txt 和 new.txt,还包括最初的 test.txt 版本
-
-
问题
现在有的三个树对象,分别代表了我们想要跟踪的不同项目快照。然而问题依旧:若想重用这些快照,你必须记住所有三个 SHA-1 哈希值;并且,你也完全不知道是谁保存了这些快照,在什么时候保存的,以及为什么保存这些快照。而以上这些,正是提交对象能为你保存的基本信息。
2.5 提交对象
我们可以通过调用 commit-tree
命令创建一个提交对象,为此需要指定一个树对象的 SHA-1 值,以及该提交的父提交对象(如果有的话,第一次将暂存区做快照就没有父对象)
- 样例一
- 样例二
-p
后面跟的是前一个的提交对象的 SHA-1 值
真正代表一个项目版本的就是提交对象,它是对树对象的封装。
3. Git 操作最基本流程
- 创建工作目录,初始化新仓库,
git init
- 将修改的文件提交到暂存区,
git add ./
- 将暂存区内容提交,
git commit -m "注释"
git add
命令是先把文件生成为git对象存储到版本库,再将其快照添加到暂存区
git commit
命令是先根据暂存区内容生成一个树对象,存储到版本库,然后再将其拿出来包裹成提交对象,再出存储到版本库
commit 后暂存区内容仍然存在
4. Git 其他常用高级命令
4.1 检查当前文件状态
git status
一般来说,所有文件不外乎两种状态:已跟踪或未跟踪
已跟踪的状态可能是已修改、已提交或已暂存
4.2 查看已暂存和未暂存的更新
实际上git status
显示的比较简单,仅仅是列出了修改过的文件,如果要查看具体修改了什么地方,可以用git diff
命令。这个命令它已经能解决我们两个问题了:当前做的哪些更新没有被暂存?有哪些更新已经暂存起来准备下次提交?
-
当前做的哪些更新还没有暂存
git diff
-
有哪些更新已经暂存起来准备下次提交
git diff --cached git diff --staged // 1.6.1及以上
4.3 提交
之前提到的提交命令git commit -m "注释"
适合注释短的适合,要是注释很长可以直接执行命令
git commit
然后进入vim编辑模式,写大段注释。
4.4 跳过使用暂存区提交
尽管使用暂存区的方式可以精心准备要提交的细节,但有时候这么做略显繁琐。Git提供了一个跳过使用暂存区的方式,只要在提交的时候,给git commit
加上-a
选项,Git就会自动把所有已经跟踪过的文件暂存起来一起提交,从而跳过git add
步骤,即git commit -a
4.5 移除文件
删除文件在Git中被视为修改操作,可以使用git rm
命令完成此操作,并连带从工作区删除实体文件,此操作不会生成新的git对象,但是提交同样会产生树对象和提交对象
4.6 文件改名
如果直接用mv
命令改名,其实是先删除一个文件,在新增一个文件,git中有命令git mv
可以直接完成此操作。
注意本质是删除和新增文件
4.7 查看历史记录
-
git log
在提交了若干更新,又或者克隆了某个项目之后,你也许想回顾下提交历史。完成这个任务最简单有效的工具是git log
命令
默认不用任何参数的话,git log
会按提交时间列出所有的更新,最近的提交在最上面。该命令会列出每个提交的 SHA-1 值、作者的名字和邮箱、提交时间以及提交说明
-
git log 参数
git log --pretty=oneline
git log --oneline
4.8 Rebase
一般只在本地分支用!不要轻易用于远程仓库!
4.8.1 修改老旧的commit message
提交历史如下所示:
例如将v2
的commit message 修改成v2.1
执行命令git rebase -i <commithash>
-i
表示交互式
commithash
应该是要修改的提交的前一次提交(parent),这里是v1
的commithash
执行git rebase -i 516bbaaca100da099ecd4ff27528822db5896d10
后界面如图:
作如下修改:
其实这里面是编辑命令,编辑完git会自动执行
这里保存退出会进入另一个交互式的vim编辑窗口:
修改commit message保存退出即可:
此时界面会显示修改成功,现在看看提交历史:
已经修改完成
4.8.2 合并多个连续commit
这个现象很常见,一开始开发很容易完成一部分就像提交一次保存,但是对于整个项目来说,这种太密集了,没有意义,只需对一个功能保留一个提交即可,所以需要合并多个commit,一般情况下这些commit都是连续的。
初始log记录如上面图片所示,想要合并提交还是需要使用Rebase命令,比如合并v2
到v4
,命令中的commithash应为v1
的:
git rebase -i d8081efefc598caeb05f00b13e08d52bdef3a112
这里选择squash
命令,保存退出后,进入下一个vim编辑界面,这个就是合成后的commit message编辑界面:
保存退出:
此时在查看提交日志:
4.8.3 合并几个间隔的commit
下面介绍如果一个功能的commit不是连续的如何解决合并问题,初始提交记录如下所示:
现在想把create a.txt
和create c.txt
合并起来,由于create a.txt
已经是最早的提交了,没有parent,故只能执行:
git rebase -i 64231128d6df0b6
进入该commit 然后vim编辑器中手动添加它,才进入是这样的:
你我们需要修改成这样:
保存退出后,返回git bash并现实一些信息,继续执行命令:
git rebase --continue
进入如下vim编辑界面:
此时可以修改合并后的提交message,如改成这样:
保存退出后,显示成功修改:
此时查看提交历史:
5. Git 分支操作(杀手功能)
几乎所有的版本控制系统都以某种形式支持分支。使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线。在很多版本控制系统中,这是一个略微低效的过程——常常需要完全创建一个源代码目录的副本。对于大项目来说,这样的过程会耗费许多时间。而 Git 的分支模型是极其搞高效轻量的,是 Git的必杀技特性,也正是因为这一特性,使得 Git 从众多版本控制系统中脱颖而出。
分支就是指向一个提交对象的活动的指针
5.1 创建分支
git branch 分支名
创建一个新分支,但是并不会自动切换到新分支中去
如果你想直接切换到新建的分支,可以执行下面命令
git checkout -b 分支名
5.2 查看当前所有分支的列表
git branch
5.3 切换分支
git checkout 分支名
切换分支会动三个地方:
- HEAD
- 暂存区
- 工作目录
【最佳实践】每次切换分支前,当前分支一定是干净的(已提交状态)
5.4 查看项目分支历史
git log --oneline --decorate --graph --all
可以看到类似下面的结果:
5.5 合并分支
git merge 分支名
执行此命令是将其它分支合并到当前执行命令时所在的分支
有时候合并分支会有冲突,只要解决文件的冲突提交即可
5.6 删除分支
【注】不能删除当前所在分支,需要先切出去再删除
git branch -d 分支名
git branch -D 分支名
区别在于
-d
会检查merge状态,分支没有合并会报错,-D
则会强制删除
5.7 查看每个分支的最新提交
git branch -v
5.8 创建一个指向特定提交对象的分支
git branch 分支名 提交对象的hash值
想回哪里回哪里,就是版本回退
5.9 查看哪些分支已合并到当前分支
git branch --merged
在这个列表中分支名前没有
*
号的分支通常可以使用git branch -d
删除掉
5.10 查看哪些分支未合并到当前分支
git branch --no-merged
尝试使用
git branch -d
命令删除在这个列表中的分支时会失败
5.11 分支模式
一般项目都有个主分支,一般不直接在上面进行开发,每个程序员都需要自己在此基础上拉一个自己的分支来开发,一般以自己花名来为开发分支命名。
具体开发每一个功能时,其实最好还是在自己的开发分支上再拉一个分支,开发完了再合并到自己的分支上,所以功能开发测试完毕再合并到主分支中。
5.12 分支原理
- .git/refs 目录
这个目录中有两个文件夹,heads 和 tags,分别存储分支和标签及其所对应的提交对象的hash值。
- HEAD 引用
当运行类似于git branch branchname
这样的命令时,Git 会取得当前所在分支最新提交对应的 SHA-1 值,并将其加入到你想要创建的任何新分支中。那么,如何知道 SHA-1 值呢,答案是通过 HEAD 文件。
HEAD 文件是一个符号引用,指向目前所在分支。所谓符号引用,意味着它并不像普通引用那样包含一个 SHA-1 值,他是一个指向其他引用的指针。
6. 别名
6.1 新建别名
Git 不会在你输入部分命令时自动推断出你想要的命令。如果不想每次输入完整的 Git 命令,可以给这些命令设置别名。
git config --global alias.别名 原命令
一般用
--global
够了,因为主要就是为用户自定义快捷命令设置别名的
原命令不包过第一个单词
git
,如果超出一个单词需要给命令整体加引号
6.2 删除指定别名
git config --global --unset alias.别名
6.3 删除所有别名
git config --global --remove-section alias
7. Git 存储
有时,当你在开发分支上工作一段时间临时要切换到另一个分支做点事,可你又不想就此提交,此时你需要用到git stash
命令。
-
git stash
将未完成的修改保存到一个栈上,而你可以在任何时候重新应用这些改动。 -
git stash list
查看存储
-
git stash apply stash@{0}
注意stash@{0}
是根据存储列表得到的标识符,如果不指定一个存储,调用命令git stash apply
,则默认是最近的一个存储 -
git stash pop
应用存储,然后立即从栈上扔掉它 -
git stash drop 存储名
移除指定存储
8. 撤销 & 重置
8.1 撤回在工作区的修改
git restore <filename>
此命令针对的是已经被追踪的文件,之后有过修改,但是还没有执行
git add
之前,要是想反悔,可以使用这个命令回退到修改前。
8.2 撤回暂存区的快照
git restore --staged <filename>
此命令针对的是已经执行
git add
命令,但是还没有git commit
,要是想反悔,可以使用这个命令回退到git add
前。
8.3 撤回自己的提交
-
提交注释写错了
比如执行
git commit -m "提交注释"
,提交完发现有问题,需要重写,可以先修改发现错误的文件,然后执行git commit --amend
进入VIM模式创新编辑注释,之后 Git 会自动提交。 -
已经提交了不合适的修改到版本库时,想要撤销本次提交
版本回退,前提是你没有推送到远程版本库
9. 版本回退
9.1 soft
git reset --soft HEAD~
这与改变 HEAD 自身不同,reset
移动 HEAD 指向的分支,意思就是,不简单是 HEAD 指向前面某个版本,而是 HEAD 还是指向当前分支,但是当前分支指向的版本变了。
HEAD后面几个
~
,就回退几个版本
它本质上是撤销了上一次
git commit
命令。当你运行git commit
时,Git 会创建一个新的提交,并移动 HEAD 所指向的分支来使其指向该提交。
暂存区并没有变化
git reset --soft HEAD~
=git commit --amend
9.2 mixed
git reset [--mixed] HEAD~
不仅移动 HEAD(带着分支一起移动),还改变了暂存区
9.3 hard
git reset --hard HEAD~
在mixed基础上,还改变了工作区
这是一个危险用法,它是 Git 会真正销毁数据的仅有的几个操作之一
git checkout 分支名
和git reset --ghard XXX
区别:
git checkout
只动 HEAD,--hard
动HEAD而且带着分支一起走git checkout
对工作目录是安全的,--hard
强制覆盖工作目录
10. 标签
Git 可以给历史中的某一个提价打上标签,以示重要,比较有代表性的是人们会使用这个功能来标记发布结点(v1.0 等等)
10.1 列出标签
git tag
10.2 创建标签
Git 使用两种主要类型的标签:轻量标签和附注标签
-
轻量标签很像一个不会改变的分支——它只是一个特定提交的引用
git tag v1.0
或者git tag v1.0 commithash
指定某个版本打标签 -
附注标签是存储在 Git 数据库中的一个完整对象。它们是可以被校验的,其中包含打标签者的名字、email地址、日期时间,还有一个标签信息。通常建议创建附注标签,这样你可以拥有以上所有信息。但如果你只是想用一个临时的标签,或者因为某些原因不想保存那些信息,轻量标签也是可用的。
git tag -a v1.1 git tag -a v1.2 commithash git tag -a v1.3 commithash -m "my version v1.3"
10.3 查看特定标签
git show tagname
10.4 删除标签
git tag -d tagname
10.5 检出标签
如果你想查看某个标签所指向的文件版本,,可以使用git checkout tagname
命令,但是会使仓库处于分离头指针(detached HEAD)状态。如果你此时做了某些修改想要提交,标签不会发生变化,但是新提交不属于任何分支,并且将无法访问,除非访问确切的提交hash值。因此,如果你需要进行更改——比如说你正在修复旧版本的错误,这通常需要创建一个新分支:
git checkout -b tagname
11. 远程协作流程
- 项目经理创建远程仓库
- 项目经理创建本地仓库
- 项目经理为远程仓库配置别名
git remote add <别名> <url>
:添加远程仓库并设置别名git remote -v
:显示远程仓库使用的 Git 别名预期对应的urlgit remote show <远程仓库别名>
:查看某一个远程仓库更多信息git remote rename <别名1> <别名2>
:重命名git remote rm <远程仓库别名>
:删除远程仓库
- 项目经理推送本地项目到远程仓库
git push <别名> <分支名>
- 成员克隆远程仓库到本地
git clone <url>
- 项目成员邀请成员加入团队
- 成员推送提交到远程仓库
- 项目经理更新成员提交内容
git pull
12. 远程分支
12.1 跟踪远程分支
本地创建新分支,推送到远程仓库时会自动添加新分支,但是此时之前克隆过仓库的成员再次通过git fetch
命令下拉代码就需要可能先要创建一个分支来接受它(merge),然后可以执行下面命令来建立跟踪关系。
git branch -u <remotename>/<branch>
git checkout -b 本地分支名 <remotename>/<branch>
git checkout --track <remotename>/<branch>
都是新建分支,同时跟踪远程仓库的分支
命令git pull
可以一次性将远程分支拉到本地分支上,省略了fetch后的merge过程,但是和上面一样,还是要先建立分支跟踪关系,这样pull的时候才会自动拉下来到对应分支上。
说白了,要想pull一步搞定,要确保每个分支已经和远程分支有跟踪关系了
12.2 删除远程分支
git push origin --delete <BranchName> // 删除远程分支
git remote prune origin --dry-run // 列出仍在远程跟踪,但是远程已被删除的无用分支
git remote prune origin // 清除上面列出的远程分支
13. 远程冲突
每次写代码和提交代码前最好先git pull
更新一下,有冲突先解决再提交,
14. 忽略文件
- 格式规范
- 所以空行或者以注释符号
#
开头的行都会被 Git 忽略 - 可以使用标准的 glob 模式匹配
*
代表匹配任意个字符?
代表匹配任意一个字符**
代表匹配多级目录
- 匹配模式前有
/
,表示根目录,比如/a
表示仅忽略根目录下的a
文件夹,其他路径下的a
文件夹仍然保留 - 匹配模式最后有斜杠,说明要忽略的是目录,比如
b/
,它表示忽略所有b
文件夹,无论它在哪个层级 - 前置
!
表示取反,不排除
- 所以空行或者以注释符号