笔记:关于Git
需要了解的概念
- 每个
Git
的配置文件都放在.git/config
文件中 - 工作区:电脑里的资源管理器中的目录
- 暂存区:一个
git
本地库中暂时存储某些修改的地方,英文名stage
或者index
,一般存放在.git
目录下的index
文件 - 版本库:也叫本地库,工作区的一个隐藏文件
.git
,不算工作区范围内 - 远程仓库:一般使用
github
上创建的仓库,可把本地库中的内容传输给远程仓库 git
一般工作流:- 把工作区的文件加入暂存区跟踪管理名单
- 在工作区编辑文件
- 添加修改到暂存区
- 提交到版本库
- 传输到远程仓库
也就是说文件一般是这么流的:工作区⇒暂存区⇒版本库⇒远程仓库
- 分支:
Git
把版本库的每次提交串成一条时间线,这条时间线就是一个分支。分支(比如master
)上有个指针(master
指针),用来指向这个分支(或者说这条时间线)上的最新提交。而HEAD
严格来说,指向的并不直接是时间线上的最新提交,而是这条时间线上“指向时间线上的最新提交”的master
指针
每次提交,
master
分支就会变长一截,master
指针的指向随着最新提交向前
- 在主分支
master
基础上创建分支,比如dev
时- 会新建一个指针
dev
,指向的是master
指针指向的那个最新提交 - 还会把
HEAD
指针改为指向dev
指针
- 会新建一个指针
所以
Git
创建分支很快,因为除了增加一个dev
指针,改改HEAD
的指向,工作区的文件都没有任何变化
通常
master
分支仅用来发布新版本,不直接在该分支工作。工作都在dev
分支上,成员应该都有自己的分支,然后合并到dev
。等dev
中的新版本完成,再合并到master
分支。而bug
通常由一个临时分支来修复,用完就丢
-
而创建分支
dev
后,新提交会导致由dev
分支变长一截,dev
指针跟着向前,而master
指针是不动的 -
假如我们在
dev
分支的工作完成了,就应该把dev
分支的内容合并到master
。合并有3种情况:“快进(无冲突)”、“非快进,修改不同文件(无冲突)”以及“非快进,修改相同文件(有冲突)”。不清楚看这个博客- 快进(无冲突):在
master
分支没有任何新提交的情况下,合并是直接把master
指针指向dev
指针指向的当前提交 - 非快进,修改不同文件(无冲突):
master
和dev
分支都有提交,且提交中修改的不是同一个文件,合并是直接取修改过的不同文件的并集 - 非快进,修改相同文件(有冲突):
master
和dev
分支都有提交,而提交中修改的是同一个文件,且有冲突,合并后会产生冲突,必须手动解决冲突后提交
- 快进(无冲突):在
不管哪种情况,
Git
合并都快,因为也就改改master
指针和HEAD
指针,dev
指针是不改变的
若是在2个分支中都有新提交,那么就不属于
快进
- 把
dev
合并到master
后甚至可以删除dev
分支,也就是把dev
指针删除掉,删掉后就剩1条master
分支了
安装Git
在Git
官网安装Git
,默认选项安装即可
安装
Git
的同时会安装Git Bash
,这是一个便在windows
下使用git
命令的模拟终端
使用Git Bash
跟踪文件前
- 身份设置
$ git config --global user.name "<名字>"
$ git config --global user.email "<邮箱>"
其中的<名字>只是昵称
-
创建本地库(或者称版本库)
-
选择磁盘的任意一个地方,创建一个空目录。比如在
D
盘创建一个depot
目录(若window系统,路径中最好不要有中文)- 直接在资源管理器创建
- 或者在
Git Bash
中使用命令创建
$ cd d: $ mkdir depot
-
先切到这个创建的空目录,然后把这个创建的空目录变成
Git
可管理的本地库
$ cd depot $ git init
创建完可以发现在
depot
目录中有一个隐藏目录.git
,这个目录是Git
跟踪管理版本库的,千万别手动修改 -
正式使用Git Bash
- 查看现在本地库的情况:未跟踪文件、已跟踪文件的修改记录、暂存库文件以及合并后产生冲突的文件
$ git status
-
把文件加入
Git
的跟踪名单上- 先在资源管理器的本地库,即
D:\depot
中添加文件,即手动创建或者移动过来
注意:要使用
Git
跟踪管理的文件一定要放在创建的本地库中,不然Git
根本找不到这个文件注意:所有的版本控制系统都只能跟踪文本文件的改动,对于二进制文件是无法知晓其改动的。而
Microsoft
的Word
是二进制格式,所以版本控制系统是没法跟踪Word
文件的改动的- 把创建后未跟踪的文件添加进
stage
,即暂存区的跟踪管理名单
$ git add <文件名>
- 先在资源管理器的本地库,即
-
当工作区文件发生修改,将修改提交到暂存区
$ git add <文件名>
没错,也是使用
git add
命令
- 提交暂存区的所有内容给版本库当前分支,一般是一个小功能完成后才提交到版本库的
$ git commit -m "<提交说明>"
commit
时的<提交说明>最好有所规范,便于阅读
- 查看工作区和版本库最新版本的区别
$ git diff -- <文件名>
记得
--
和<文件名>
之间有个空格
- 撤回工作区的修改
$ git checkout -- <文件名>
记得
--
和<文件名>
之间有个空格
这种情况是“修改了工作区但是没有提交修改到暂存区”,即暂存区是干净的
该撤回可以让工作区回到修改前的状态,又叫做“丢弃工作区的修改”
- 撤回
git
对文件的跟踪
$ git reset HEAD <文件名>
- 撤回工作区的修改提交,即撤销
add
$ git reset --hard HEAD
这种情况是“修改了工作区而且提交修改到了暂存区”,即暂存区不干净了(有一个你想撤回的错误
git add
)
而
git reset
命令是撤回了git add
,即撤回了“提交修改到了暂存区”,那么暂存区又干净了。现在变成属于楼上那种情况了,即“修改了工作区但是没有提交修改到暂存区”
其实这种情况也可以归为楼下的范围的,只不过回退的版本是版本库的最新版本
- 撤回
commit
记录,也就是版本回退(其实我觉得应该说成版本重置的)
$ git reset --hard <版本>
没错,也是它
同时会导致暂存区的提交记录丢失,楼上就用了这个特性
HEAD
是指向“当前分支中指向最新提交的指针”的指针,如果想回退上一个版本就是HEAD^
,上几个版本就加几个^
,而上100个的话也写太多了,可以写成HEAD~100
<版本>可以用
HEAD
类的写法,或者是使用版本号(怎么查,楼下会说明)
版本回退会导致“想回退到的那个版本”之后的
commit
记录全部消失。若想撤销本次版本回退,那就是想回到“回退前的那个版本”,那不就是“版本重置”?没错,还是用git reset --hard <版本>
,只不过这一次的<版本>需要填的是“已经不存在commit
记录的版本的版本号”,这个使用楼下的git log
命令已经找不到了,那怎么办?楼下的楼下会说明
- 查看
commit
记录以及分支合并情况
$ git log
这个命令是查看
commit
记录的,会显示commit
的那次版本的版本号、日期和提交说明
后加参数
--pretty=oneline
:变成简略版本的一行输出,包括版本号和提交说明
后加参数
--graph
:commit
记录以图形形式展示,加上这个比较能看得出分支的合并情况
后加参数
--abbrev-commit
:版本号数据减少,剩前面几位数字
- 查看历史命令,可查看“未来版本号”
$ git reflog
是记录在该版本库中使用过
git commit
命令以及git reset
命令,因为其中会显示每次commit
和reset
的版本号,所以可以用其来查询“由于回退而导致消失的commit
记录的版本的版本号”,即所谓“未来的版本号”
-
删除已跟踪的文件
-
在资源管理器删除要删除的文件,或者用命令
rm <文件名>
,效果一致 -
确定是否删除
- 确定删除的话
$ git rm <文件名> $ git commit -m "提交说明"
注意:删除记录也要提交的,删除文件会导致工作区和版本库不一致
- 如果只是在资源管理器把文件误删了,想撤回这个删除。那么就属于上文提到的“撤回工作区的修改”这种情况
$ git checkout -- <文件名>
如果是从未添加到版本库中的文件,误删了是没法通过
Git
恢复的
-
-
创建分支
$ git branch <分支名>
$ git checkout -b <分支名>
第2个命令的含义是:创建并切换到分支
上文“撤回工作区修改”时也用过这个
git checkout
开头的命令,注意区分
由于
git checkout
开头的命令有相似性,所以在最新版的Git
中,创建并切换到分支变成使用
$ git switch -c <分支名>
- 切换分支
$ git checkout <分支名>
在最新版的
Git
中,切换分支变成使用
注意:在切换分支之前,要先确保状态“干净”(全部
commit
或者保存现场,如何保存?下下面有说明),否则会污染其它分支
$ git switch <分支名>
- 查询分支
$ git branch
前面标
*
的就是当前分支
- 合并指定分支到当前分支
$ git merge (--no-ff -m "<提交说明>") <分支名>
所以如果想合并到
master
分支,应该先切换到master
分支,使用楼上的楼上的切换命令
如果是“快进”情况的话,合并完就发现
master
的最新提交跟dev
分支的最新提交完全一致了,因为仅仅是将master
指针指向dev
指针指向的那次提交;如果不是“快进”情况,那么合并后会产生冲突,如何解决?请看楼下的楼下
参数
--no-ff -m "<提交说明>""
:禁用“快进”合并模式,Git
就会在merge
时生成一个新的commit
,这样,从分支历史上就可以看出分支信息
- 删除分支
$ git branch -d <分支名>
因为创建、合并和删除分支非常快,所以
Git
鼓励使用分支完成某个任务,合并后再删掉分支.这和直接在master
分支上工作效果是一样的,但过程更安全
参数
-d
换成-D
:如果有一个临时分支,它commit
了,现在应该合并到主分支,但你不想合并了,想直接删除这个临时分支,使用参数-d
是删除不了的,会弹出error
,但可以使用-D
参数强行删除
- 解决冲突
使用git status
命令查看产生冲突的文件,打开文件看看内容。比如下面这个就是有冲突的位置,在master
分支中这个位置的句子是“我是master”,而在dev
分支中这个位置的句子却是“我是dev”。把内容改成想要的(记得删掉冲突标识<<<<
、>>>>
和====
),然后再git add
以及git commit
<<<<<<< HEAD
我是master
=======
我是dev
>>>>>>> dev
提交后可用
git log --graph
看一下分支的合并情况,上文有说明过这个命令
如果是想合并到
master
分支的,那么冲突就产生在master
分支,被合并的分支是不会被影响的。所以解决冲突只需要在“想合并到”的分支上
- 保存工作区现场
$ git stash
bug
通常会创建一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。但如果现在在dev
分支,有个master
分支上的bug
需要立即修复,而在dev
中的工作区的工作还没完成(即:工作区有修改然后没add
的情况),直接切会污染分支,可以先把工作现场保存起来,等之后想恢复再恢复
注意:不保存暂存区的内容
后加参数
list
:是查看保存记录
后加参数
apply <stash记录的id>
:恢复保存的现场,但记录还存在
后加参数
pop
:恢复保存的现场,同时删除记录
后加参数
drop <stash记录的id>
:删除保存的记录,即apply
+drop
=pop
上面说到可以利用新开分支来处理
bug
,若是处理完在master
分支上的bug
,但dev
其实早期是从master
中切出来的,那这个bug
其实在dev
分支上也存在,若是不想在dev
上重复一次修复同一个bug
的操作,还有简单的方法,看楼下
- 复制特定提交
$ git cherry-pick <commit id>
只会复制该次提交的修改部分,而不是一整个提交
可用于在不同分支修复同一个
bug
-
连接远程仓库(推荐
ssh
)- 查看是否已经创建
ssh key
:
在用户主目录(如C:\Users\DELL)下,看看有没有.ssh
目录,如果有,再看看这个目录下有没有id_rsa
和id_rsa.pub
这两个文件,如果已经有了,说明已经创建好了
id_rsa
:私钥,不可泄漏;id_rsa.pub
:公钥,告诉别人没关系- 创建
ssh key
$ git ssh-keygen -t rsa -C "<邮箱>"
这里一路回车就ok。由于这个key也不是用于军事目的,所以也无需设置密码
-
在
github
创建ssh
链接:
登录github
在setting
里的ssh keys
里面添加一个ssh key
,title
任意,key
文本框输入id_rsa.pub
文件的内容 -
在
github
上创建一个远程仓库
github
的免费仓库是所有人可见的,不要放敏感信息。如果想要有私人仓库:花钱可以得到github
私有仓库,或者自己搭建git
服务器- 连接本地库和远程仓库
$ git remote add <名字> <ssh链接>
<名字>:
git
(没改变github
里的)中该远程仓库的名字,取什么名字都可以。通常是使用origin
,Git
默认的叫法,也可以改成别的,但是origin
这个名字一看就知道是远程库。或者使用项目的简略名字<
ssh
链接>:这个在github
中远程仓库里可以复制,比如git@github.com:7AAAAAAA/test.git
- 查看是否已经创建
-
查看已连接的远程仓库
$ git remote (-v)
不加
-v
显示的只有git
中该远程仓库的名字;加上参数-v
会显示push
的地址,但如果没有推送权限,就看不到push
的地址
- 推送指定分支到远程仓库
$ git push (-u) <远程库在git的名字> <本地库指定分支名>
<远程库在git的名字>指的是之前在“连接本地库和远程仓库”时自定义的名字,若忘记了,可使用
git remote
查询
参数
-u
的作用是:把本地库指定分支与远程仓库的同名分支关联了,下次推送就不用使用这个-u
参数了
通常只有
master
(主分支)和dev
(开发分支)需要与远程仓库同步,feature
(功能分支)的推送取决是否需要在此分支上进行开发,而bug
分支一般不推送,除非有要求
不同的人向远程仓库的某一分支都推送了自己本地的修改版本,可能产生冲突,解决方法是:每次推送前先把远程仓库中的最新版本拉取到本地,然后合并,解决冲突之后再推送。如何拉取远程仓库的最新版本到本地?看楼下
- 从远程仓库拉取
$ git pull
若是拉取失败,可能是没有指定本地分支和远程仓库分支的链接(会提示
no tracking information
),那么可以通过以下命令设置它们的链接
$ git branch --set-upstream-to=<远程仓库>/<远程仓库的分支> <本地分支名>
- 从远程仓库克隆一个本地库
$ git clone <ssh链接>
第1次使用
push
和clone
时会弹出ssh
警告,这是因为Git
使用ssh
连接,而ssh
连接在第一次验证GitHub
服务器的Key
时,需要你确认GitHub
的Key
的信息是否真的来自GitHub
的服务器,输入yes
回车即可。Git
会输出一个警告,告诉你已经把GitHub
的Key
添加到本机的一个信任列表里了
默认情况下,克隆的本地库默认会与“克隆来源”的远程仓库连接,且连接的名字为
origin
(即可通过git remote
命令查看到origin
),并且只能看到master
分支,可以使用以下命令创建本地库分支和与远程仓库分支关联
$ git checkout -b <分支名> <远程仓库>/<远程仓库的分支>
通过克隆的本地库中<远程仓库>默认为
origin
,本地库分支和远程仓库分支的名称最好一致
- 把本地未
push
的分叉提交历史整理成直线,也称“变基”(啊,看不太懂这个,有空看看这个)
$ git rebase
提交不是一条线(提交历史不清晰)(原因可能是
pull
后自动merge
了别人的提交,产生了分叉,最后自动产生了一个merge
后的新的提交版本)。解决办法:push
前使用rebase
(过程就是:先把刚才自己提交版本临时保存,再把版本更新到最新远程,再把临时保存的提交重新提交)
特点是把分叉的提交历史“整理”成一条直线,看上去更直观。缺点是本地的分叉提交已经被修改过了
该命令可以使得查看历史提交的变化变得更加容易,因为分叉提交需要三方对比
- 创建标签
$ git tag <标签名> <commit id>
标签
tag
就是一个让人容易记住的有意义的名字,它其实是指向某个commit
的指针,但特殊的是,这个指针是不移动的
注意:标签总是和某个
commit
挂钩。如果这个commit
既出现在master
分支,又出现在dev
分支,那么在这2个分支上都可以看到这个标签
注意:创建的标签都存储在本地,不会自动推送到远程,可也可以手动推送,看下下面
打标要先切换到需要打标签的分支上
如果不加参数<commit id,那就是默认情况,默认标签是打在最新提交的
commit
上的;否则就是为指定commit
打标签。可使用git log
命令查看commit id
还有自带说明的标签,然而其实就是加参数
$ git tag -a <标签名> -m "" <commit id>
- 查看标签
$ git tag
标签不是按时间顺序列出,而是按字母排序的
- 查看标签信息
$ git show <标签名>
会显示标签的对应
commit
信息以及标签说明等
- 删除本地标签
$ git tag -d <标签名>
- 推送标签到远程库
$ git push <远程仓库在git中的名字> <标签名>
若<标签名>为
--tags
,可以一次性推送全部未推送的标签到远程仓库
- 删除已推送到远程库的标签
先在本地删除要删除的标签,使用楼上的楼上的命令
然后再从远程删除,命令如下
$ git push <远程仓库在git中的名字> :refs/tags/<标签名>
- 自定义
Git Bash
$ git config --global color.ui tru
Git
会适当地显示不同的颜色
- 忽略文件
在Git
工作区的根目录下创建一个特殊的.gitignore
文件,然后把要忽略的文件名填进去,Git
就会自动忽略这些文件。可以使用*
和!
符号,比如*.class
表示忽略所有.class
文件,!test.txt
表示不忽略test.txt
文件
注意:
.gitignore
文件本身要放到版本库里,并且可以对.gitignore
文件做版本管理
如果想添加一个文件到Git
,但发现添加不了,原因肯是这个文件被.gitignore
忽略了,可以选择强制添加,使用-f
,如下
$ git add -f <文件名>
- 查看忽略文件
$ git check-ignore -v <文件名>
- 为命令配置别名
$ git config --global alias.<别名> <原本的命令>
比如
$ git config --global alias.st status
命令就是告诉Git
,以后st
就表示status
加参数
--global
表示全局,也就是该命令在电脑的所有Git
仓库下都有用,如果不加,即表示只对当前仓库起作用