文章目录
- GIT教程
GIT教程
Linux基础指令
cd空格路径
单纯的cd指令返回根目录
- cd 后加指定路径跳转至指定目录
- cd /盘符/文件夹名称/… 不区分大小写
- 盘符后需要加:号
- cd /盘符/文件夹名称/… 不区分大小写
cd空格… 返回上级目录
pwd 显示当前路径
ls 显示当前目录下的所有文件
ll 显示当前目录下的所有文件的详细信息
touch 新建一个新文件
rm 删除文件
mkdir 新建一个文件夹
rmdir 删除一个文件夹(只有文件夹为空时可删除)
rm -r 删除非空文件夹
mv
* 将文件或文件夹移动至另一文件夹下
* 将文件或文件夹重新命名
clear 清屏
reset 重启终端
history 显示命令历史
help 帮助
exit 退出
# 表示注释
vim
模式介绍
-
普通模式—光标移动 删除文本
- h—光标左移
- j—光标下移
- k—光标上移
- l—光标右移
- w—移到下一个单词
- b—移到上一个单词
-
插入模式—向文本缓冲中插入文本
命令 说明 i 在当前光标之前插入 I 在光标所在行行首进行插入 a 在光标后插入 A 在光标所在行行尾进行插入 o 在光标所在行下一行插入新行 O 在光标所在行上一行插入新行 -
命令行模式—可以输入会被解释成命令并执行的文本
三种模式互相切换
普通模式下按i(insert)或a(append)进入插入模式
插入模式或命令模式下按Esc进入普通模式
普通模式下输入:进入命令行模式
命令模式下输入wq退出vim
进入vim
通过vim空格文件名进入vim对文件进行编辑
退出vim
命令行模式下退出
命令 | 说明 |
---|---|
:q! | 强制退出不保存 |
:q | 退出 |
:wq! | 强制保存并退出 |
:wq | 保存并退出 |
:w 文件名 | 另存为 |
:saveas 文件路径 | 另存为 |
:x | 保存并退出 |
普通模式下退出vim
shiftzz或者切换成大写模式连按两下Z
普通模式下删除vim文本信息
命令 | 说明 |
---|---|
x | 删除光标所在字符 |
X | 删除光标所在前一个字符 |
dd | 删除整行 |
dw | 删除一个单词(不适用于中文) |
D | 从光标处删除至行尾 |
d^ | 从光标处删除至行首 |
dG | 删除到文档结尾处 |
d1G | 删除到文档首部 |
阿拉伯数字+dd | 包括光标所在行删除数字行 |
vim重复命令(普通模式下)
重复执行上次命令
在普通模式下.表示重复上一次的命令操作
执行指定次数相同的命令
阿拉伯数字+指令
光标快速跳转(普通模式下)
行间跳转
命令 | 说明 |
---|---|
阿拉伯数字+G | 跳转至指定行行首 |
gg | 跳转至首行行首 |
G | 跳转最后一行行首 |
行内跳转
命令 | 说明 |
---|---|
w | 光标跳转至下一个单词开头 |
e | 光标跳转至当前单词结尾 |
b | 光标跳转至前一个单词开头 |
ge | 光标跳转至前一个单词结尾 |
0或^ | 跳转至光标所在行行头 |
$ | 跳转至光标所在行行尾 |
f <字母> | 向后搜索<字母>并跳转至第一个匹配的位置 |
F<字母> | 向前搜索<字母>并跳转至第一个匹配的位置 |
t<字母> | 向后搜索<字母>并跳转至第一个匹配的位置之前的一个字母 |
T<字母> | 向前搜索<字母>并跳转至第一个匹配的位置之后的一个字母 |
vim大小写快速切换(普通模式下)
普通模式下通过~快速光标所在位置大小写
vim复制粘贴和剪切(普通模式下)
复制
命令 | 说明 |
---|---|
阿拉伯数字+yy | 从光标所在行向后复制数字行 |
y^ | 复制到行首,不包括光标所在处字符 |
y$ | 复制到行尾,不包括光标所在处字符 |
y数字w | 复制数字个字符包含光标所在处字符 |
yG | 复制至文本末 |
y1G | 复制至文本开头 |
粘贴
命令 | 说明 |
---|---|
p | 粘贴至光标后 |
P | 粘贴至光标前 |
剪切
dd就是剪切指令
替换和撤销(普通模式下)
替换
撤销(undo)
命令 | 说明 |
---|---|
阿拉伯数字+u | 撤销1次或n次操作 |
U | 撤销当前行的所有修改 |
Ctrl+r | redo,撤销undo的操作 |
快速缩进(普通模式下)
输入>>向右缩进
输入<<向左回退
命令行模式下输入:set shiftwidth=xx设定缩进字符数
文本位置调整
命令行模式下输入:ce 光标所在行文本居中
命令行模式下输入:ri 光标所在行文本右对齐
命令行模式下输入:le 光标所在行文本左对齐
查找(普通模式下)
快速查找
-
普通模式下输入/或?+字符或字符串进行查找/为向下查找?为向上查找
-
进入查找之后输入n或N继续查找,n查找下一个内容,N查找上一个内容
高级查找
- 普通模式下输入\*寻找光标所在处单词(向后)
- 普通模式下输入\#寻找光标所在处单词(向前)
- 上述两条指令中前加g即可实现部分匹配单词查找方向与上述方向相同
- 高级查找n及N指令依然适用
vim常用指令总结(git)
-
模式切换
- vim初始化界面为普通模式
- 普通模式下通过i(光标前进行插入)、I(光标所在行行首插入)、a(光标后插入)、A(光标所在行行末插入)进入插入模式
- 普通模式下输入:进入命令行模式
- 插入模式下需要通过Esc进入普通模式
- 普通模式下可以输入ZZ直接退出vim也可shift+zz退出vim
-
普通模式下光标移动
- h左移
- j下移
- k上移
- l右移
-
普通模式下删除文本信息
- 阿拉伯数字+dd—删除包括光标所在行在内向下的数字行
- dG—从光标所在行删除到文末,删除内容包括光标所在行
- d1G—从光标所在处删除到文首,删除内容包括光标所在行
-
文本格式
- 文本内容以#开头的是注释内容
-
光标快速跳转
- 显示行号指令—进入命令行模式输入 set nu
- G—跳转至最后一行行首
- gg—跳转至第一行行首
- 阿拉伯数字+G—跳转至第数字行行首
-
普通模式下命令重复
- 输入指令后按.进行指令的重复
-
撤销和重做(普通模式下)
- 撤销通过u
- 重做通过Ctrl+r
-
查找
- /+字符串—从光标处向后查找匹配字符串,输入n继续查找下一个匹配字符串,输入N继续查找上一个匹配字符串
- ?+字符串—从光标处向前查找匹配字符串,输入n继续查找下一个匹配字符串,输入N继续查找上一个匹配字符串
git
git中四种文件状态
git有三种状态,文件可能处于这四种状态其中之一
- 未跟踪(untracked)—文件未被git进行版本控制
- 已修改(modified)—修改了文件,还没保存到数据库中
- 已暂存(staged)—对一个已修改文件的当前版本做了标记,使之包含在下一次提交的快照中
- 已提交(committed)—数据已安全的存放在本地的数据库中
git的三个工作区
- git仓库—git用来保存项目的元数据及对象数据库的地方(存放项目的所有版本)
- 工作目录—对项目的某个版本独立提取出来的内容(存放项目的某个具体版本)
- 暂存区—一个文件,保存了下次将提交的文件信息列表
git工作流程
- 在工作目录中修改文件
- 暂存文件,将文件的快照放入暂存区
- 提交更新,找到暂存区域的文件,将快照永久的存入git仓库中
git的配置
gitconfig存放路径及作用
git有一个工具被称为git config,它允许你获得和设置配置变量;这些变量可以控制git的外观和操作,变量会被存放在三个位置。
- /c/users/用户名文件夹下的.gitconfig—包含系统上每一个用户及他们仓库的通用配置,使用带有–system选项的git config会从此文件读取配置变量
- git安装目录下etc文件夹下的gitconfig—只针对当前用户,使用电邮–global选项的git config会从此文件读取配置变量
- 使用仓库的git目录下的config—只针对该仓库
下一级别会覆盖上一级别的配置
git config配置用户信息
用户名配置:
git==空格config空格–global空格user.name空格==“自定义的用户名”
用户邮箱配置:
git==空格config空格–global空格user.email空格==“自定义邮箱地址”(邮箱地址引号可有可无)
修改邮箱或用户名:
用新邮箱或用户名重新配置一遍即可
删除已配用户信息:
git==空格config空格–global空格–unset空格==user.name
git config指令–system针对c盘下的config文件,–global针对安装目录下的gitconfig文件,不带任何后缀针对的是使用仓库的文件夹下的gitconfig文件
获取某一项配置:
git config <key> key为段(section).键(key)
例:git config user.name
git config user.email
etc.
获取帮助
格式:
1. git help \<verb>
2. git \<verb> --help
3. man git-\<verb>
4. git \<verb> -h ---命令窗口进行查看,内容要更直观
例:
git help config
git config --help
git基础
现有目录初始化仓库
指令:git init—使用git对现有项目进行管理,将在git bash的当前目录下创建.git的隐藏文件夹,该文件夹下包含你初始化的git仓库中的所有必须文件
git文件生命周期
检查当前文件状态
指令:git status—查看哪些文件处于什么状态
显示信息包括:
1. 当前所处分支
2. untracked files---未被跟踪的文件
3. changes not staged for commit---==红色==modified 已修改未暂存的文件
4. changed to be committed---==绿色==modified 已暂存未提交的文件
将文件添加到下次提交中
指令:git add 文件名+扩展名或者目录路径—开始跟踪一个文件或目录下的全部文件
该命令为多功能命令:
1. 开始跟踪新文件
2. 将已修改文件提交至暂存
3. 合并时将有冲突的文件标记为已解决的状态
将该命令理解为“添加内容到下一次提交中”而不是“将一个文件添加到项目中”更合适
状态简览
git status -s/-short—将文件状态进行简明扼要的显示
??—未跟踪文件
A—新添加的文件
左M—modified not staged
右M—staged not committed
忽略文件
通过创建.gitignore文件,列出要忽略的文件模式
.gitignore文件格式如下:
- 所有空行及#开头的内容将被忽略
- 以/开头防止递归
- 以/结尾指定目录
- 忽略指定模式以外的文件或目录,可在模式前加上!取反
- *号匹配零个或多个字符
- []匹配任意一个包含在括号内的字符,如果括号中使用-分割两个字符,表示所有在这两个字符范围内的都可匹配
- ?只匹配一个任意字符
- 使用**表示匹配任意中间目录
应用举例:
1. out---忽略out文件及out文件夹
2. *.c---忽略扩展名为.c的文件
3. !main.c---即使忽略扩展名为.c的文件仍然跟踪main.c文件
4. out/---忽略out文件夹及其子文件夹下的所有文件
5. /out---忽略out文件夹下的文件,out文件夹下子文件夹下的文件不在忽略范围内
6. out/*.txt---忽略out文件夹下的扩展名为.c的文件
7. out/\*\*/*.pdf---忽略out文件夹及其子文件夹下所有扩展名为.pdf的文件
8. *.[oa]---忽略扩展名为.o和.a的文件
9. *.[a-c]---忽略扩展名为.a .b .c的文件
查看已暂存和未暂存的修改
指令:git diff—对比当前项目中当前文件与暂存文件之间的区别,即修改后还未暂存的内容变化
git diff --staged—查看已暂存内容相较上次提交后的变化
git提交至仓库
指令:git commit—打开vim编辑器对提交内容进行注释编辑完成后退出vim进行提交
git commit -m 注释内容—不打开vim直接对提交内容进行注释并提交
提交显示信息:
1. 在哪个分支进行的提交
2. 本次提交的完整的SHA-1校验和是什么
3. 本次提交中有多少文件修改过,多少行添加和删改过
跳过暂存直接提交
指令:git commit -a—跳过git add步骤,把所有跟踪的文件进行暂存并提交,输入指令后会打开vim编辑器对提交内容进行注释
移除文件
指令:git rm 文件名—删除文件并停止对文件跟踪,执行该指令后需要进行一次提交
git rm -f 文件名—若文件在移除时已修改过并放进了暂存需要执行该指令进行文件移除
git rm --cached 文件名—将暂存区域的文件移除且不再跟踪但不删除文件
文件更名
指令:git mv 文件名 新文件名—将文件改为新文件名
该指令等效于下述三条指令:
- mv 文件名 新文件名
- git rm 文件名
- git add 新文件名
更名后需要重新提交来使变化保存进仓库中
查看提交历史
普通查看
指令:git log—查看提交历史
按提交时间列出所有更新,最近的更新在最上面
显示信息包括:
1. 每个提交的SHA-1校验和
2. 提交的作者及邮箱
3. 提交时间及提交说明
git log -p
指令:git log -p -提交次数opt—查看提交历史并列出提交次数的差异
例:git log -p -2—查看上两次提交历史并显示差异
git log --stat
指令:git log --stat—显示所有被修改的文件,有多少文件被修改了,以及被修改的文件哪些行被移除或是添加了
git log --pretty
指令:git log --pretty=oneline/short/full/fuller/format:“格式”—将git log显示的信息进行格式化显示
-
oneline—只显示SHA-1校验和及提交信息且放在一行显示
-
short—只显示SHA-1校验和、作者及提交信息
-
full—只显示SHA-1校验和、作者、提交者及提交信息
-
fuller—只显示SHA-1校验和、作者、作者提交时间、提交者、提交者提交时间及提交信息
-
format格式:
例:git log --pretty=format:“%h-%cn,%ce-%s”
作者与提交者之间的差异:作者指实际做出修改的人,提交者指最后将此工作成果提交到仓库的人
指令:git log --pretty=oneline/format:“格式”空格–graph—形象的显示出分支及合并的历史
限制git log输出长度
-
按时间进行限制
指令:git log --since/–until=“时间”
例:git log --since=2.weeks
git log --until=2021-04-30
-
按作者进行限制
指令:git log --author=作者
例:git log --author=Mr.G
-
按提交者进行限制
指令:git log --committer=作者
例:git log --committer=Mr.G
-
按提交信息的关键字进行限制
指令:git log --grep=关键字
例:git log --grep=移除
-
按添加或移除了某些字符串为限制进行限制
指令:git log -S字符串
例:git log -Sgit
撤销操作
撤销提交内容
指令:git commit --amend—若文件进行了修改并暂存了则提交新快照,若文件提交后未修改则保持原快照只修改提交信息
文件修改后不暂存进行撤销提交,文件快照不会更新
为上次提交追加提交文件
指令:git commit -m ‘initial commit’
git add forgotten_files
git commit --amend
取消暂存的文件
指令:git reset HEAD 文件名/git restore --staged 文件名—将已暂存文件变为未暂存状态
撤销对文件的修改
指令:git checkout空格–空格文件名—将已跟踪并且提交过的文件的本次修改撤销
远程仓库的使用
添加远程仓库
指令:git remote add <远程仓库简写> <远程仓库地址>
git remote add learn git@gitee.com:firstlove-l/learngit.git
查看远程仓库
指令:git remote—只显示远程仓库简写
git remote -v—显示远程仓库简写及其对应地址
远程仓库拉取与抓取
git clone
指令:git clone <远程仓库地址> <本地目录名>—将其他仓库克隆至本地(执行命令时当前目录下),包括被克隆仓库的版本变化,因此本地无需是一个仓库,且克隆将设置额外的远程跟踪分支。克隆操作是一个从无到有的过程。
git clone git@gitee.com:firstlove-l/learngit.git learngit
git pull
指令:git pull <远程仓库简写/远程仓库地址> <远程仓库分支>:<本地仓库分支>—拉取远程分支更新到本地仓库在于本地分支进行合并。git pull = git fetch + git merge
git pull origin master:test—将远程仓库origin的master分支拉取下来与本地test分支进行合并。
上条指令等价于 ==》 git fetch origin master:test + git merge test
git pull origin master—将远程仓库origin的master分支拉取下来与本地当前分支进行合并。
git fetch
指令:git fetch <远程仓库简写> <远程仓库分支>:<本地仓库分支>—拉取远程分支更新到本地仓库,若本地仓库分支不存在则新建分支
git diff <本地仓库分支>
git merge <本地仓库分支>
git branch -d <本地仓库分支>
git fetch lg master:test
git diff test
git merge test
git branch -d test
注意:若在合并时提示fatal: refusing to merge unrelated histories,则是因为两个分支没有取得任何联系
解决办法:执行git merge指令时增加–allow-unrelated-histories
git merge test --allow-unrelated-histories
三者区别
-
是否需要初始化本地仓库
git clone不需要而另两条指令需要初始化本地仓库
-
是否可以指定分支推送到远程
git clone下来的项目可以直接推送至远程
git pull 和git fetch 需要先执行git remote add 添加远程仓库后才能push
推送至远程仓库
指令:git push <远程仓库简写> <本地分支>
git push lg master
查看某个远程仓库
指令:git remote show <远程仓库简写>
git remote show lg
远程仓库的移除与重命名
重命名指令:git remote rename <旧名字> <新名字>—将本地中远程仓库名的简写由旧名字改为新名字
git remote rename lg learngit
移除指令:git remote rm <远程仓库简写>
git remote rm learngit
打标签
列出标签
指令:git tag—列出所有标签
git tag -l ‘标签名’—列出特定标签
创建标签
git使用两种主要类型的标签:轻量标签(lightweight)和附注标签(annotated)
- 轻量标签:很像一个不会改变的分支,是某个特定提交的引用
- 附注标签:存储在git仓库中的一个完整的对象,它是可以被校验的其中包括–打标签者的名字、邮箱、日期时间以及标签信息
通常建议创建附注标签
创建轻量标签指令:git tag 标签名
创建标注标签指令:git tag -a 标签名 -m “标签信息”— -a(append)追加标签名 -m(message)标签信息
历史版本追加标签
指令:git tag -a 标签名 (-m “标签信息”)SHA-1校验和简写(最少前四位推荐7位数)
标签信息为可选项,若指令中不包含标签信息,则执行完指令后打开vim添加标签信息
共享标签
默认情况下,git push指令并不会传送标签内容到远程仓库,在创建完标签后需要显示的将标签推送至远程仓库中
推送单一标签
指令:git push 远程仓库网址 标签名
例:git push my(远程仓库简写) v1.0
推送多个标签
指令:git push 远程仓库网址 --tags
删除标签
删除本地标签
指令:git tag -d 标签名
删远程仓库中的标签
指令:git push 远程仓库网址空格:refs/tags/标签名
git别名
作用:简化指令输入
配置别名指令:git config --global alias.指令简写 真实指令(真实指令可用‘’进行包裹来描述复杂指令)
例:git config --global alias.ci commit
git config --global alias.last ‘log -1 HEAD’
git分支
分支简介
git保存的不是文件的变化或差异,而是一系列文件不同时刻的快照
*在进行提交操作时,git会保存一个提交对象(commit object)**该提交对象保存一个指向暂存快照的指针、作者的姓名和邮箱、提交时输入的信息以及它的父对象的指针。*首次提交产生的提交对象没有父对象,普通提交产生的提交对象有一个父对象,而由多个分支合并产生的提交对象有多个父对象
暂存git完成的具体操作:为每一个文件计算校验和(使用SHA-1哈希算法),然后把当前版本的文件快照保存到git仓库中(git使用blob对象(保存着文件快照)来保存它们),最终将校验和加入到暂存区域等待提交
提交git完成的具体操作:git会先计算每一个子目录的校验和,然后在git仓库中这些校验和保存为树对象(记录着目录结构及blob对象指针)。随后,git便会创建一个提交对象(包含着指向前述树对象的指针和所有提交信息),它除了保存有暂存所提到的内容外,还包含指向这个树对象的指针
git的分支本质上仅仅是指向提交对象的可变指针,git的默认分支名字是master
创建分支
指令:git branch 分支名—创建一个可以移动的新的指针,但并不会切换到新分支去
例:git branch testing
git存在一个名为HEAD的特殊指针,指向当前所在的本地分支(可将HEAD想象为当前分支的别名)
查看各个分支所指向的对象
指令:git log --oneline --decorate
分支切换
指令:git checkout 分支名—切换到分支名上,HEAD指向分支名
例:git checkout testing
随着新分支的提交HEAD自动向前移动
查看项目分叉历史
指令:git log --oneline --decorate --graph --all
分支的新建与合并
新建分支
新建分支指令:git branch 分支名
创建分支并切换至新分支的指令:git checkout -b 分支名
git checkout -b testing ==> git branch testing
git checkout testing
删除分支
指令:git branch -d 分支名
合并分支
-
若被合并分支是合并分支的直接上游,则git只是简单的将指针向前移动。换句话说,当你试图合并两个分支时,如果顺着一个分支能直接走到另一个分支,那么git在合并两者时,只是将指针向前推进,此时合并操作并没有需要解决的分歧。合并信息中会显示“fast-forward”
只能在前的分支向在后的分支合并,在后的分支合并在前的分支提示ALREADY UO TO DATE(已经是最新的)
例:git checkout -b hotfix
在hotfix分支上修改并提交两次
git checkout master
git merge hotfix
git branch -d hotfix
-
被合并分支不是合并分支的直接祖先时,git会使用分支末端所指的快照以及两个分支的祖先,做一个简单的三方合并
git将此次三方合并的结果做了一个新的快照并自动创建一个新的提交指向它
解决合并冲突
-
打开冲突文件然后手动解决冲突***(个人推荐使用,因为使用mergetool即使未改动需合并内容使用git status指令git会显示要求提交)***
打开存在冲突的文件后会看到 >>>>>> ====== <<<<<<<符号
>>>>及<<<<用以表示属于哪个分支
====将冲突内容分开展示
合并的内容会有==(merge)==标识
内容修改完成后需要将符号删除
-
使用mergetool
-
使用mergetool首先需要进行配置
- git config --global merge.tool vimdiff
- git config --global merge.confilictsyle diff3
- git config --global mergetool.prompt false
-
输入指令:git mergetool
-
mergetool显示四个窗口,如下所示
╔═══════╦══════╦════════╗
║ ║ ║ ║
║ LOCAL ║ BASE ║ REMOTE ║
║ ║ ║ ║
╠═══════╩══════╩════════╣
║ ║
║ MERGED ║
║ ║
╚═══════════════════════╝LOCAL:被合并分支内容
BASE:共同祖先的内容
REMOTE:合并分支内容
MERGED:合并结果,这就是需要改动的内容,按照需要进行改动
窗口跳转指令:ctrlw方向键(方向键与VIM普通模式下光标移动一致 H-左 J-下 K-上 L-右)
-
编辑MERGED窗口内容
- MERGED窗口内容使用LOCAL内容指令: :diffg LO
- MERGED窗口内容使用BASE内容指令: :diffg BA
- MERGED窗口内容使用REMOTE内容指令: :diffg RE
-
保存并退出
进入命令行模式输入wqa–即:wqa 必须带a否则只能关闭光标所在窗口 a==all
-
-
解决完冲突后对每个文件进行git add指令来标记冲突已解决(实际使用未提示该步骤即可提交)
-
使用git commit指令进行提交
放弃合并
- 指令: git merge --abort
- 使用mergetool进行合并未提交仍可放弃合并
- 手动修改文件进行合并何时未执行git add指令仍可放弃合并
分支管理
常用指令:
- git branch—得到当前所有分支的列表,列表名称前有*代表现在检出的分支(即当前HEAD所指向的分支),此时提交时有*的分支会随着工作向前移动
- git branch -v—查看每个分支的最后一次提交
- git branch --merged—过滤git branch列表中已合并的分支
- git branch --no-merged—过滤git branch列表中未合并的分支
分支开发工作流
长期分支
较长一段时间内反复把一个分支合并入另一个分支
特性分支
特性分支是一种短期分支,用来实现单一特性或某些相关工作
远程分支
获取远程仓库分支及标签指令:git ls-remote (remote)
git remote show (remote)remote为URL的别名
例:git remote add origin URL(假设远程仓库别名为origin)
git ls-remote origin
git clone指令会将克隆的仓库自动命名为origin,拉取它的所有数据,创建一个指向它master分支的指针,并在本地将它命名为origin/master。git也会给你一个与origin的master分支指向同一个地方的本地master分支
远程仓库名字origin与分支名master一样,并没有什么特殊含义,可以理解为一种约定俗成
添加多个远程仓库
添加多个远程仓库,使用git fetch指令后本地分支如下所示
运行git fetch teamone
推送本地分支内容
推送本地分支时,本地分支并不会显示的与远程仓库同步,必须显示地推送想要分享的分支
指令:git remote (remote)(branch)—推送本地(branch)分支,来更新远程仓库的(branch)分支
例:git remote origin develop—推送本地develop分支来更新远程仓库的develop分支
推送本地分支到远程命名不同的分支指令:git remote (remote)(branch):(branch)—前(branch)为本地分支名,后(branch)为远程仓库分支名
例:git remote origin Dev:aswsomeDev
你推送完本地develop分支后,其他协作者从远程仓库抓取数据时,只会在本地生成一个远程分支origin/develop,指向服务器的develop分支引用,并不会在本地生成一个develop分支。
当抓取到新的远程跟踪分支时,并不会在本地自动生成一份可编辑的副本。换一句话,这种情况下不会有一个新的develop分支,只有一个不可修改的origin/develop指针。
需要运行 git merge origin/develop将这些工作合并进当前分支,如果想要在自己的develop分支上工作,可以将其建立在远程跟踪分支之上。指令:git checkout -b develop origin/develop,这会给你一个用于本地工作的分支,并且起点位于origin/develop
跟踪分支
从一个远程跟踪分支检出一个本地分支会自动创建所谓的“跟踪分支”,跟踪分支是与远程分支有直接关系的本地分支。如果在一个跟踪分支上执行git pull,git能自动识别去哪个服务器上抓取、合并到哪个分支
指令:git checkout --track (remote)/(branch)等价于git checkout -b (branch) (remote)/(branch)
例:git checkout --track origin/develop == git checkout -b develop origin/develop …
查看所有跟踪分支指令:git branch -vv—将所有的本地分支列出且包含更多信息,如每个分支在跟踪哪个远程分支,与远程分支相比,本地分支时领先还是落后
-
本地iss53分支有两个提交未推送到远程分支
-
本地master分支与远程master分支相同都是最新的
-
本地serverfix分支有三次提交未推送至远程,远程有一次提交未合并入本地
-
testing分支未跟踪任何远程分支
拉取
指令:git fetch (remote)—从服务器拉取本地没有的数据,但并不会修改本地工作目录中的内容,需要自行合并
git pull == git fetch + git merge
通常使用git fetch + gitmerge而非git pull指令
删除远程分支
指令:git push (remote) --delete (branch)
变基
变基
概念解释:筛选出当前分支指向的提交对象与被合并分支指向的提交对象以及两个分支共同祖先的差异并生成新的提交对象,然后当前分支指向新的提交对象。分支合并的一种方式,但提交历史更加整洁。
变基的实质:丢弃一些现有提交,然后相应的建立一些内容一样但实际不同的提交。
指令:git checkout branch_合并分支
git rebase branch_被合并分支
例:git checkout experiment
git rebase master
上两条指令等价于 git rebase master experiment
git checkout master
git merge experiment
变基与合并的区别:变基是将一系列提交按照原有次序依次应用到另一分支上,合并是把最终结果合并在一起
特性分支上的特性分支变基到另一分支
指令:git rebase --onto 被合并分支 特性分支 特性分支的特性分支
例:git rebase --onto master server client
git checkout master
git merge client
变基的风险
不要对在你的仓库外有副本的分支进行变基
用变基解决变基
Git 除了对整个提交计算 SHA-1 校验和以外,也对本次提交所引入的修改计算了校验和–即patch-id
Figure46情况下我们不应该进行合并而是应该执行:git rebase teamone/master,git将会:
- 检查哪些提交是我们分支独有的(C2,C3,C4,C6,C7)
- 检查其中哪些提交不是合并操作的结果(C2,C3,C4)
- 检查哪些提交在对方覆盖更新时没有被纳入目标分支(只有C2和C3,因为C4就是C4’)
- 把查到的这些提交应用在teamone/master上
从而我们将得到与Figure47中不同的结果,如下图所示
上述方案要想有效还需要对方在变基时确保C4及C4’是几乎一样的,否则变基操作将无法识别,并新建另一个类似C4的补丁(而这个补丁可能无法整洁的整合入历史,因为补丁中的修改已经存在于某个地方了)
本例中另一个简单的方法是使用git pull --rebase而不是直接git pull,或者手动完成这个过程,先git fetch再git rebase teamone/master
只要你把变基命令当做是在推送前清理提交历史的整洁工具,并且只在未推送至共用仓库的提交上执行变基操作就不会有事*
变基 VS 合并
观点一:提交历史是记录实际发生过什么,他是针对历史的文档本身就有价值
观点二:提交历史是项目过程中发生的事,重要的只是项目结果而非过程
原则:只对尚未推送或分享给别人的本地修改执行变基操作清理历史,从不对已推送至别处的提交执行变基操作
服务器上的git
生成SSH公钥
用户的SSH公钥存储在~/.ssh目录下(~/.ssh目录路径如下图所示),如果没有该文件夹需要手动建立并增加秘钥文件id_dsa以及id_dsa.pub(.pub后缀的文件为公钥,无后缀的为私钥)也可通过运行ssh-keygen指令来创建。
运行ssh-keygen指令时首先会让你确认秘钥的存储位置,然后会要求你输入两次秘钥口令,如果不想在使用秘钥时输入口令直接点两次回车让秘钥口令为空即可。
服务器添加SSH公钥
- 打开服务器
- 打开设置
- SSH公钥
- 添加SSH公钥
git工具
引用日志 reflog
指令:git reflog—查看引用日志
引用日志记录了最近几个月你的HEAD和分支引用所指向的历史。
注意:引用日志只存在于本地仓库,一个记录你在你自己仓库里做过什么的日志。其他人拷贝的仓库里的引用日志不会与你的相同。而你新克隆一个仓库的时候,引用日志是空的,因为你在仓库里还没有进行任何操作。
交互式暂存–将文件的特定部分组合成提交
简介
指令:git add -i/–interactive—进入交互终端模式,显示内容如下所示
git add -i指令显示内容说明:
-
staged与unstaged用来显示文件是否进行了暂存
- staged内容为+4/-2说明文件已暂存且说明了文件增加了几行删除了几行,此时文件unstaged一列显示nothing
- staged内容为unchanged则说明文件未暂存,此时文件unstaged一列显示+4/-2说明文件的修改,但修改内容未暂存
-
path用来显示文件为那个文件
-
使用git add -i指令后需要在What now> 后输入数字或首字母来选择操作
Command指令说明:
1. status---用来显示文件状态
2. update---用来选择将某文件变更为暂存状态
3. revert---用来将已暂存的文件取消暂存
4. add untracked---追加未跟踪文件并暂存
5. patch---暂存补丁
6. diff---查看已暂存内容相较上次提交后的变化
7. quit---退出交互终端模式,但在终端模式下进行的任何操作会被保留
8. help---查看帮助
暂存与取消暂存
步骤:
1. 进入交互模式后在what now\>后选择操作
2. 选择2进入暂存模式
3. 根据文件前的序号选择文件
4. 回车后文件序号前有<kbd>*</kbd>的说明文件将会被暂存
5. 此时可以在update>>后继续输入文件序号来暂存更多文件,若输入空格则退出暂存模式
6. 取消暂存则选择功能码3步骤与暂存一样
暂存补丁–暂存文件的特定部分
-
步骤与暂存类似
-
选择5进入暂存补丁
-
选择文件
-
回车进行补丁暂存选项
-
输入?可以显示各指令含义
-
可以不必在交互式暂存模式下进行暂存,也可通过 git add -p或git add --patch来启动同样的脚本
储藏与清理–混乱状态下的文件无需提交却可保存
储藏
指令:git stash或git stash save—将修改过且已保存的在暂存或未暂存状态下的文件储藏,然后将储藏推送到栈上。
git stash list—查看栈上储藏的东西
git stash apply stash@{2}—将储藏的工作重新应用
git stash drop stash@{2}—将储藏在栈上的暂存扔掉
上两条语句可以合并为 git stash pop stash@{2}
创造性的储藏–跳过已暂存文件
指令:git stash --keep-index—做了一些改动但改动只有一部分是完成的,执行此命令可以将未暂存的文件进行储藏,改动 完成且已暂存的可以进行提交但却不会放入储藏。
储藏未跟踪文件
指令:git stash --include-untracked—储藏任何创建的未跟踪的文件
交互式储藏
指令:git stash --patch—git不会储藏修改过的任何东西,但是会交互式地提示哪些改动想要储藏,哪些改动需要保存在工作目录中
储藏后修改后再应用
说明:文件在修改后进行了储藏,后又对原文件进行了修改,在应用储藏时需要按照如下指令进行
指令:git stash–>修改文件–>保存文件–>暂存文件–>应用储藏–>合并冲突–>完成储藏后修改后再应用储藏
搜索
普通搜索
指令:git grep <文本>—从提交历史或者工作目录中查找一个字符串或者正则表达式
git grep -n <文本>—输出git所找到的匹配行行号
git grep --count <文本>—仅仅显示哪些文件包含匹配以及包含了多少个匹配
git grep -p <文本> <匹配文件类型>—查看匹配的行属于哪个方法或函数
date.c 文件中有 match_multi_number 和 match_digit 两个函数调用了gmtime_r。
日志搜索–什么时候存在或引入的
指令:git log -S<文本> --oneline
行日志搜索
指令:git log -L :<文本>:<文件名>—展示代码中一行或者一个函数的历史
重写历史(本节针对未推送至远程仓库的本地仓)
修改最后一次提交
修改最近一次提交信息
-
修改最近一次提交的提交信息
指令:git commit --amend—打开文本编辑器,编辑器内有你最近一条提交信息,当修改完成并关闭后编辑器会将新的 提交内容替换最近一条提交信息
-
添加、修改和移除文件快照
指令:git add <文件名>—添加新文件或者将提交后已修改文件放入暂存
git rm <文件名>—将上次提交中的某文件移除
git commit --amend—修改提交信息
注意:git commit --amend修改的信息为日志信息而不是引用日志的信息。
引用日志包含了你在本地仓库进行的全部操作,因此就算使用重写也会被记录,只不过在日志中并不会体现出来
修改多个提交信息
指令:git rebase -i HEAD~<需要修改的HEAD往前多少个提交的数(包括当前HEAD)>
git rebase -i HEAD~2
根据需求将提交对象前的pick改为对应指令。修改提交将pick改为edit
修改完成后退出后,执行git add <文件>替换那次提交的文件,然后git commit --amend可对提交信息进行修改并提交
提交完成后执行 git rebase --continue指令修改下一个需要修改的提交
重新排序提交
可以使用交互式变基来重新排序或完全移除提交。
git rebase -i HEAD~4
删除不想要的提交即可实现提交的移除
将提交变换顺序即可实现提交的重新排序(越靠下的提交越新,最下一条提交为最新提交)
修改完后的提交历史
压缩提交
使用交互变基来将多个提交进行压缩
例:git rebase -i HEAD~3
pick–>squash 第一个squash底色为红色原因是,因为不能将旧提交压入新提交,只能将新提交压入旧提交。即第一行绝对不能squash(因为只有最旧的提交有父对象)
:wq保存退出
编辑提交信息然后 :wq
提交信息如下图所示
拆分提交–针对某次提交中有多个文件改动
使用交互变基来拆分提交,但某次提交必须有多个文件改动,想将改动的文件拆分开提交时使用此功能。
将需要拆分的提交对象前的pick改为–>edit,保存退出
示例:将README及blame拆分提交
指令:git rebase -i HEAD~3
git reset HEAD^
git add README
git commit -m"updated README formatting"
git add blame
git commit -m “added blame”
git rebase --continue
拆分完后日志如下图所示
filter-branch–改写历史中的大量提交
从每一个提交移除一个文件
指令:git filter-branch --tree-filter ‘rm -f 1.txt’ HEAD—从整个提交历史中移除1.txt文件(SHA-1值会发生变化)
使一个子目录作为新的根目录
指令:git filter-branch --subdirectory-filter <文件夹名称> HEAD
全局修改邮箱地址
重置揭密
三棵树
“树”在此的含义为“文件的集合”,而不是特定的数据结构
HEAD
HEAD是当前分支引用的指针,它总是指向该分支的最后一次提交。这表示HEAD将是下次提交的父结点。理解HEAD的最简单方式,就是将它看做你的上一次提交的快照
Index–索引
索引是你的预期的下一次提交,这个概念会引用为git的“暂存区域”。
运行git commit时git看起来的样子:
git将上一次检出到工作目录中的所有文件填充到索引区,他们看起来就像最初被检出时的样子。之后你会将其中一些文件替换为新版本,接着通过git commit将他们转换为树来用作新的提交。
Working Directory–工作目录
另外两棵树以一种高效但不直观的方式,将他们的内容存储在==.git文件夹中。工作目录会将它们解包为实际的文件以便编辑。你可以把工作目录当做沙盒==。在你将修改提交到暂存区并记录到历史之前,可以随意更改。
工作流程
git的主要目的是通过操纵这三棵树来以更加连续的状态记录项目的快照
工作流程可视化过程如下所示:
-
进入一个新目录,新建一个文件file.txt,称之为文件的V1版本。运行git init,这会创建一个git仓库,其中HEAD引用指向未创建的分支(master还不存在)。此时,只有工作目录中有内容。
-
运行git add来获取工作目录中的内容,并将其复制到索引中
-
接着运行git commit,它会取得索引中的内容并将其保存为一个永久的文件快照,然后创建一个指向该快照的提交啊对象,最后更新master来指向本次提交。
-
运行git status,会发现没有任何改动,因为现在三棵树完全相同。(此步骤可有可无)
-
假设修改的文件并想提交。首先在工作目录将文件进行修改,此时文件版本为V2。
-
此时运行git status会发现文件显示在“changes not staged for commit”,因为该条目在索引与工作目录之间存在不同。
-
运行git add来将它暂存到索引中
-
运行git status此时由于索引和HEAD不同,文件出现在“changes to be committed”。也就是说,现在预期的下一次提交与上一次提交不同。
-
运行git commit来完成提交
**切换分支或克隆的过程也类似。当检出一个分支时,它会修改HEAD指向新的分支引用,将索引填充为该次提交的快照,然后将索引的内容复制到工作目录中。**相当于提交过程的反过程。
重置的作用–reset
假设当前历史如下图所示
reset指令可视化过程如下:
-
移动HEAD
指令:git reset --soft HEAD~—reset做的第一件事就是移动HEAD指向。这与改变HEAD自身不同(checkout所做的);reset移动HEAD指向的分支。这意味着如果HEAD设置为master分支(假设你正在master分支上工作),运行git reset 9e5e6a4将会使master指向9e5e6a4
通过上图可知,该命令本质上是撤销了上一次git commit命令。此时你运行git commit时,git会创建一个新的提交,并移动HEAD所指向的分支来使其指向该提交。当你将它reset回HEAD~(HEAD的父节点)时,其实就是把该分支移动回原来的位置,而不会改变索引和工作目录。然后你就可以更新索引并在此运行git commit来完成git commit --amend所要做的事了。
git reset --soft HEAD~只修改HEAD指向并不会将索引的内容改变,但执行此命令后在此提交后SHA-1会变化
-
更新索引(–mixed)
指令:git reset --mixed HEAD~
-
更新工作目录(–hard)
指令:git reset --hard HEAD~
注意:–hard标记是reset命令唯一危险的用法,它也是git会真正地销毁数据的几个操作之一。其他任何形式的reset调用都可以轻易的撤销,但是==–hard选项不能,因为它强制覆盖了工作目录的文件。在这种情况下,git数据库中的一个提交还留有该文件的V3版本,我们可以通过reflog==来找回它。但是若该文件还未提交,git仍会覆盖它,导致无法恢复。
小结:
reset命令会以特定的顺序重写三棵树,在你指定以下选项时停止:
1. 移动HEAD分支的指向(若指定了==--soft==,则到此停止) 2. 使索引看起来像HEAD(若未指定==--hard==,则到此停止) 3. 使工作目录看起来像索引
通过路径来重置
若指定了一个路径,reset将会跳过移动HEAD分支的指向,并且将它的作用范围限定为指定的文件或文件集合。
指令:git reset <文件名>—跳过移动HEAD分支的指向,让索引看起来像HEAD本质是将文件从HEAD复制到索引中,其实相当于取消暂存的效果
下图说明使用 git reset <branch/SHA-1> <file>HEAD并不会移动
该指令与git add 正好相反
指令:git reset <某一提交的SHA-1> <文件名>—不从HEAD拉取数据,而是指定某次具体提交来拉取该次提交文件对应的版本
例:git reset eb43 file.txt
压缩
假设当前HEAD、索引及工作目录如下图所示
执行:git reset --soft HEAD~2
运行:git commit
**结果:**查看历史,发现V2版本已不在历史当中。
检出–checkout与reset区别
**相同点:**二者都是对三棵树进行操纵
不同点:取决于你是否传给该命令一个文件路径
-
不带路径—即运行**git checkout <分支名/SHA-1>和git reset --hard <分支名/SHA-1>**指令
-
checkout对工作目录是安全的,它会通过检查来确保不会将已修改的文件弄丢。而reset --hard不会做检查并会全面替换所有东西。checkout指令会先试着简单合并一下,将所有未修改过的文件全部更新。
-
reset会移动HEAD分支的指向,而checkout只会移动HEAD自身来指向另一个分支。
-
-
带路径
指令:git checkout <branch/SHA-1> <file>
该指令会像git reset <branch/SHA-1> <file>那样用该次提交的那个文件更新索引,但也会像git reset --hard <branch/SHA-1> <file>覆盖工作目录中对应的文件
总结
HEAD一列中的REF表示该命令移动了HEAD指向的分支引用,HEAD则表示只移动了HEAD本身
WD Safe?一列中如果标记为NO,那么运行该命令前应该谨慎考虑
高级合并
合并冲突
注意:在做一次可能有冲突的合并前尽可能的保证工作目录是干净的,要么把正在做的工作提交到一个临时分支,要么储藏它。
中断一次合并
指令:git merge --abort—尝试恢复到你运行合并前的状态
忽略空白
指令:git merge -Xignore-all-space <合并分支>
例:git merge -Xignore-all-space test
手动文件再合并
步骤:
-
首先进入合并冲突状态
-
获取三个版本的文件(分别为:被合并分支版本文件,合并分支版本文件,共同的祖先版本的文件)
指令:git show :1:<文件名> > <文件名1>
git show :2:<文件名> > <文件名2>
git show :3:<文件名> > <文件名3>
注意:后方的文件名1/2/3可随意起,但是不能重复。1代表的是共同的祖先版本,2代表的是被合并分支版本,3代表的是合并分支版本。
-
使用git merge-file指令来重新合并那个文件
指令:git merge-file -p <文件名1> <文件名2> <文件名3> ><文件名>
-
使用git diff指令查看两个分支的合并文件与各个版本文件之间的差别
指令:git diff --ours/theirs/base
-
解决合并冲突并使用git add将文件暂存来标记解决冲突
-
提交完成合并
-
使用git clean指令来清除临时文件
指令:git clean -f
检出冲突
有用的工具为–conflict
指令:git checkout --conflict=diff3 <文件名>
撤销合并
意外的将topic合并入master
解决办法1:修复引用
使用git reset --hard HEAD~
解决方法2:还原提交
指令:git revert -m 1 HEAD— -m 1标记指出“mainline”需要被保留下的父节点,当你引入一个合并到HEAD(git merge topic),新提交有两个父节点:第一个是HEAD(C6),第二个是将要合并入分支的最新提交(C4)。
新的提交==^M==与C6有完全一样的内容,从这儿开始就像合并从未发生过一样,除了“现在还没合并“的提交依然在HEAD历史中。如果再次尝试合并topic到master git会提示
因为topic中并没有东西不能从master中追踪到达,更糟糕的是,如果你在topic中增加工作然后再次合并,git只会引入被还原的合并之后的修改。
解决上述问题最好的办法是撤销还原原始的合并,因为现在你想要引入被还原出去的修改,然后创建一个新的合并提交
在本例中M与^M抵消了,^^M事实上合并入了C3和C4的修改,C8合并了C7的修改,所以现在topic已完全被合并了。
使用git调试
文件标注
指令:git blame -L 起始行,结束行 文件名—查看起始行到结束行中每一行的最后修改时间以及被谁所修改。
显示信息为:最后一次修改该行的提交部分的哈希值,作者的名字,提交的时间,行号,修改后的内容
二分查找
指令:git bisect start —启动二分查找
git bisect bad —告诉系统你当前所在的提交是有问题的
git bisect good 哈希值或版本号—告诉系统最后一次正常状态是在哪次提交
然后执行git bisect good/bad来告诉系统是向后查找还是向前进行二分查找
git bisect reset—重置HEAD指针到最开始的位置
打包
功能:将git push命令所传输的所有内容打包成一个二进制文件。类(无权限推送或者懒得新建一个仓库,就可以使用git bundle create命令来打包,类似于压缩,传送,解压)
指令:git bundle create 文件名.bundle master HEAD (.bundle为通用后缀名)(master HEAD为打包范围)
将.bundle文件赋值到某一文件下然后运行git,执行下列指令
git clone 文件名.bundle 目录名(在目录名下将打包的文件进行解压)
查看待解压打包是否合法命令如下
git bundle verify …/打包文件
git内部原理
.git目录
description—仅供gitweb使用,无需关心
config—含有项目特有的配置信息
info目录—包含一个全局排除性文件,用以放置那些不希望被记录在.gitignore文件中的忽略模式
hooks目录—包含客户端或服务器端的钩子脚本
objects目录—存储所有数据内容
refs目录—储藏指向数据(分支)的提交对象的指针
HEAD—指示目前被检出的分支
index—文件保存暂存区信息