文章目录
Git最小配置
添加配置
git config [--local | --global | --system] user.name 'Your name'
git config [--local | --global | --system] user.email 'Your email'
- local:作用区域为本仓库,配置文件在.git/config
- global: 作用为当前用户的所有仓库,配置文件在个人home目录下的.gitconfig里面
- system: 本系统的所有用户(这个基本不用),配置文件应该在git安装目录下
优先级:local > global > system
查看配置
git config #出现这个命令下可以添加的参数
git config --list #出现local、global、system三个范围的配置
git config [--local | --global | --system] --list #指定范围内的配置信息 这个指令添加local参数时候需要进入某个仓库里面才能使用,不然会提示错误
清除配置
git config --unset [--local | --global | --system] user.name 'Your name'
建立Git仓库(两种情景)
1、把已有的项目代码纳入Git管理
$ cd 项目代码所在的文件夹
$ git init #初始化仓库,当前文件夹下会创建一个.git的隐藏文件
2、新建的项目直接用Git管理
$ cd 某个文件夹
$ git init your_project #会在当前路径下创建和项目名称同名的文件夹
$ cd your_project
往仓库添加文件
添加流程
graph LR
A(工作目录)-->|git add files|B(暂存区)
B(暂存区)-->|git commit|C(版本历史)
添加到暂存区
进入到项目目录使用git add指令,其中
- git add . ++添加当前目录的所有文件到暂存区++
- git add ‘文件名字’ ++添加指定的文件到暂存区++
- git add -u ++工作区中所有被跟踪后(即之前有add这个文件)修改过的文件一起提交到暂存区++
在暂存区的文件可保留一套与目录中不一样的方案(但是又还没有成为一个版本历史),
如果当前目录的文件出现错误或者比之前一套还差,可以从暂存区把之前的文件回退到当前目录。
提交到版本库
当一次或者几次执行git add命令后,暂存区会有响应的文件生成,但是还没有提交,接下来就要执行git commit命令来进行提交到本地仓库。
- git commit ++直接提交++
- git commit -m’提交说明’ ++带有提交说明的一次提交,可从提交日志中看到这些信息++
Git文件重命名
文件状态:文件已经被Git管理,如果未被管理改名字和Git不是很大关联。
1、普通方式重命名
我们在命令框中对某个文件重命名一般是如下的步骤:
mv 'file_name' 'new_file_name' #这个时候使用git status会提示之前的文件被删除了,同时新增了一个文件
git add 'new_file_name' #然后使用add指令提交新的文件
git rm ''file_name #再使用rm 指令删除已被替换的文件
2、Git提供的文件重命名方式
经Git管理的文件修改名字只需要一行Git命令
git mv 'file_name' 'new_file_name' #效果和上面的方式是相同的,省去了add和rm可以直接进行commit
查看版本库(git log)
–all,–oneline,–graph可以自由组合
git log #查看版本历史
git log -all #查看所有包括分支在内的版本历史
git log --all --graph #图形化分支版本历史
git log --oneline #查看版本历史(简洁,顾名思义每个版本一行信息)
git log --oneline --all #查看所有版本历史包括所有的分支
git log master #查看master这一支下所有,也可以写其他分支的名字
git log -n #查看最近的n个版本历史
git log -n --oneline #查看最近的n个版本历史(每个版本单独一行显示)
查看.git目录
HEAD
存储当前工作所在的分支,即指向一个引用,refs下的分支。代表整个仓库当前的工作在哪一个分支上。
config
存储和本地仓库有关的信息,包括了用户名和邮箱(最初配置的user.name和user.email)
refs
包含了heads(分支)和tags。
-
heads对应的是分支,分支相当于一个独立的开发空间,例如前后端开发可以事先建立两个分支,互不影响。需要集成时,两个分支又可以一起集成到一个公共的分支上去。其中会有master或者更多的分支,例如master存放的是一个对象,使用git cat-file -t '版本号’可以查看这个对象的类型为commit类型。
-
tags有时候也叫做“里程碑”,项目开发到一个程度,有可观的成果了就打上一个标签,例如v1.0。
objects
在objects文件夹下面存放了许多文件夹(有数字和字母组成,例如4b、4d、da),还有一种类型是pack类型,git会做一个自我梳理的过程,如果松散的文件比较多,会进行打包。
此处使用git cat-file 命令查看文件需要输入hash值时,需要把文件名和在文件夹下的字符串放一起形成一个hash值。(举个例子,如果文件名是e8,cd e8后执行 ls -al,出现a424871993a81bf460564d5eb8d294c5253b6f,合并后形成一个hash值就是e8a424871993a81bf460564d5eb8d294c5253b6f)
objects文件夹下通常包含两种类型的对象tree和blob。
Git核心对象(commit、tree、blob)
对象之间的关系
commit
每一次执行commit命令都会生成这样的一个对象,使用git log命令可以查看这类对象,一个commit会对应一棵树(不会对应两棵树)。这棵树代表了commit对应的视图,视图存放了快照,快照的集合里面放了当前commit对应的本项目仓库的所有的文件夹以及文件的快照(即某次执行commit时候的文件目录结构)
tree
类似于文件夹
blob
tree的叶子节点,对应一个文件,blob和文件名没有关系,只要文件内容相同,在整个git仓库中只有一个blob(唯一),不会因为文件名不同而存在两份相同内容的blob。
分离头指针情况下的注意事项
git checkout 一般用来切换分支或者创建分支,如果不小心执行了某个指令可能分离了头指针
例如
git checkout 'commit的hash值'
分离头指针状态本质上是指正工作在一个没有分支的状态下。为什么说这个状态比较危险呢?因为这个状态下提交的commit并没有和其他分支有关联,如果切换了分支,分离头指针状态下的commit最后很可能被git当做垃圾清除掉。
但是分离头指针也有好处,当自己需要对程序进行尝试性的更新或者其他不确定性操作,变更后发现并没有达到自己预期那就可以随时把这个变更丢弃(切换到新的分支就可以了)
Git修改已提交commit的message信息
修改最近一次commit的message
执行
git commit --amend
进入编辑模式,这个commit是所在分支的最新的commit
修改之前commit的message
执行
git rebase -i '要修改的commit的父级commit的hash值'
进入交互模式。
页面会罗列出基于这个commit的所有子commit,根据编辑框提示,自主选择pick、reword、edit等,也可以使用简写pick->p、reword->r…,保存退出进入新的交互页面,输入新的message后保存退出即可查看。
git rebase会涉及到变基操作,分离头指针。rebase意味着基于新的base的commit来变更部分commits。它处理的时候,把HEAD指向base的commit,此时如果该commit没有对应branch,就会处于分离头指针状态,然后重新一个一个生成新的commit,当rebase创建完最后一个commit后,结束分离头指针,Git让变完基的分支指向HEAD。
合并commit
合并连续的commit
执行
git rebase -i '要合并的commit的父级commit的hash值'
进入交互模式,要合并几个commit,必须要选择一个commit为合并项,即必须有一个commit为pick,其他需要合并的commit设置为squash,也可以简写s。保存退出之后会进入一个新交互页面显示要合并的commit,也可以为合并后的commit添加message(在第一行说明后添加)。
合并间隔的commit
仍然使用
git rebase -i '要合并的commit的父级commit的hash值'
要合并的commit是最上级的情况
这个时候第一个commit已经是最上级了,它没有父级commit了,如果要合并它,那就在git指令后跟上它的hash值。这样进入交互页面,我们能看到它的子级commit,但是没有它自身。
举个例子比如git log显示的commit是这样的
commit 464746a(HEAD -> master)
commit 4611891
commit 43c6742
那么执行 git rebase -i 43c6742 交互页面是这样的:
pick 464746a commit的message #这个不是需要合并的commit
pick 4611891 commit的message #这个是需要合并的commit
那就需要最上面手动加上hash值为43c6742的commit,并且把需要合并的commit放在一起并把pick改为squash。类似这样:
pick 43c6742 # message不写也无所谓
pick 4611891 commit的message #这个是需要合并的commit
pick 464746a commit的message #这个不是需要合并的commit
这个情况会有很多提示信息,根据提示一步步完成就可以。(合并后可能会出现两个没有父级的commit,即两棵树,如果有多次合并可能就会有多个)
工作区、暂存区、HEAD所存文件之间的比较和恢复
比较暂存区和HEAD所含文件的差异(git diff)
HEAD所含的文件即最新的一次commit。出现的减号和加号表示变更。
git diff --cached # 比较暂存区和HEAD文件
git diff --staged # staged和cached一样,都是指暂存区
比较工作区和暂存区所含文件的差异
git diff # 默认比较工作区和暂存区的区别(比较所有文件)
git diff -- '文件名' # 比较工作区和暂存区指定文件的差异
把工作区、暂存区恢复成和HEAD一样
git reset --hard HEAD # 此命令执行后工作区、暂存区和HEAD文件一样
把暂存区还原成和HEAD所存文件一样的状态
恢复所有文件
git reset HEAD # 恢复暂存区和HEAD一样
选择暂存区中的某些文件恢复
git reset HEAD -- '文件名' #恢复指定文件和HEAD一样,后面可以跟多个,用空格分开
把工作区的文件恢复为和暂存区一样的状态
git checkout # 显示缺少的文件
git checkout -- '需要恢复的文件名' # 恢复指定文件工作区和暂存区一样
消除最近的几次commit
这条命令慎用
git reset --hard 'commit版本号(hash值)' # 工作区、暂存区和HEAD都恢复成了这个commit的文件情况
消除最近的提交
git reset --hard HEAD~1 #版本回退一级并清除暂存区,写2即回退两级,依次类推
git reset --hard HEAD^ # 回退一个版本,作用和HEAD~1是一样的
比较不同commit之间同个文件的差异
直接比较分支也是可以的,使用
git diff '分支名1' '分支名2' # 比较两分支所有的文件
这样会显示很多的不同,比较杂乱。可以直接选择commit进行比较
git diff 'commit版本号' 'commit版本号' -- '文件名' # 比较两个commit之间同个文件的差异,可以用空格分开比较多个文件
或者是两个分支之间同个文件的比较
git diff '分支名1' '分支名2' -- '文件名' # 空格分开增加多个文件进行比较
Git正确删除文件的方法
普通的删除方式即
rm '文件名'
但是这样还需要使用add指令提交到暂存区,这里可以使用和 git mv 指令类似的 git rm
git rm '文件名' # Git提供的快捷删除指定文件,直接提交到暂存区,不需要在工作目录下先删除
开发中加塞了紧急任务需要存储当前工作区(stash)
比如,当前正在开发A.java文件,此时因为其他情况需要修改这个A.java变成其他的模样,可以使用
git stash
命令来存储当前的文件,使用
git stash list
可以查看临时储存的文件(类似于堆栈),当临时工作做完后,可以使用
git stash apply
让存储的文件从堆栈中恢复到工作区,但是堆栈里还会保留,使用git stash list还是可以看到,但是如果使用
git stash pop
堆栈中就不再保留。
配置公私钥
众所周知ssh key是加密传输。加密传输的算法有好多,git使用rsa,rsa要解决的一个核心问题是,如何使用一对特定的数字,使其中一个数字可以用来加密,而另外一个数字可以用来解密。这两个数字就是你在使用git和github的时候所遇到的public key也就是公钥以及private key私钥。其中,公钥就是那个用来加密的数字,这也就是为什么你在本机生成了公钥之后,要上传到github的原因。从github发回来的,用那公钥加密过的数据,可以用你本地的私钥来还原。如果你的key丢失了,不管是公钥还是私钥,丢失一个都不能用了,解决方法也很简单,重新再生成一次,然后在github.com里再设置一次就行
检测是否已经存在公私钥
cd ~/.ssh
进入.ssh文件后使用ls -al,如果存在三个文件就说明已经存在公私钥
生成公私钥
ssh-keygen -t -rsa -b 4096 -C "邮箱地址"
同步本地仓库到GitHub
git remote add '站点名字' 'ssh协议' # 站点名字可以自己取,一般为origin
但是如果远程仓库带有类似README.md这样预先创建的文件,本地却还没有就会报错,就需要先使用pull命令或者fetch命令先把远端的文件同步到本地
比如使用:
git fetch '站点名' '分支名'
就可以把远端的指定分支拉取到本地,这个时候使用gitk指令可以看到又出现一棵独立的树(non-fast-forwards),这个情况下本地做push,远端是会报错的(同个分支情况下)
non-fast-forwards错误
这个错误经常出现在本地分支和远程分支不同步的情况,可以使用rebase或merge指令来解决
merge
比如远端的master分支和本地的master分支不一样,先切换到master分支
git checkout master
然后
git merge --allow-unrelated-histories '设定的远程站点名'/master # 因为是合并不想管的两棵树,所以需要加--allow-unrelated-histories
从远端仓库clone到本地
git clone 'ssh协议' # 克隆一份仓库到本地
如果没有指定本地仓库名字,会默认是ssh协议最后.git前面的那个文件名
团队合作中避免使用的命令
git push -f # 会突破Git本身的安全机制,即使不是fast-forwards也能push到远端
团队工作流
主干开发
适用于组件开发的团队,成员能力强,人员少,沟通顺畅
Git Flow
适用于不具备主干开发能力。有预定的发布周期,需要执行严格的发布流程。
GitHub Flow
适用于不具备主干开发能力。随时集成随时发布:分支集成时经过代码评审和自动化测试就可以立即发布的应用。
GitLab Flow(带生产分支)
适用于不具备主干开发能力。无法控制准确的发布时间,但又要求不停地集成。
GitLab Flow(带环境分支)
适用于不具备主干开发能力。需要逐个通过各个测试环境的验证才能发布。
GitLab Flow(带发布分支)
适用于不具备主干开发能力。需要对外发布和维护不同版本。
选择合适的分支集成策略
在Repository的Settings页面可以勾选Merge Button,
- Allow merge commits
- Allow squash merging
- Allow rebase merging
都勾选了就可以在使用的时候进行选择。
在远程仓库的选项中选择Pull requests,然后选择New pull request,然后选择要合并的分支和要被合并的分支,点击Create pull request,页面就可以选择三种分支集成方式。
Create a merge commit
比如有master分支和a分支,a合并到master上去,选择这种合并方式,生成一个新的commit,并且master和a的指针都指向这个commit
Squash and merge
这种合并方式,不会把a的指针指向新的commit(相当于a不改变),在master上会新生成一个新的集成了a分支上所有commit的一个完整的commit。可以理解为把a分支的所有commit合并然后添加在master上。
Rebase and merge
这种方式合并,也不会把a的指针指向新的commit(相当于a不改变),在master上新生成n个commit(n=a分支上不同的commit个数)。
Squash和Rebase这两种方式都不动a分支。
举个例子,在没有进行分支合并集成之前是这样:
使用第一种方式
使用第二种方式
第三种方式
Git中常用指令总结(非全部,git-bash)
git add 命令
git add #添加到暂存区
git branch 命令
git branch #查看分支(不包含详细信息)
git branch -v #查看本地的分支
git branch '分支名字' #创建分支
git branch -D '分支名字' #强制删除分支
git cat-file 命令
git cat-file -t '对象hash值' #显示对象类型
git cat-file -p '对象hash值' #显示对象内容
git cat-file -s '对象hash值' #显示对象大小
git checkout 命令
git checkout '分支名字' # 切换分支
git checkout -b '分支名字' # 基于当前的commit创建并切换分支
git checkout 'commit的hash值' -b '分支名字' # 基于某个commit创建分支
git checkout -b '本地要创建的分支' '远端存在的分支' # 本地创建和远端一样的一个分支
git check -- '需要恢复的文件名' # 恢复工作区和暂存区一样
git check # 显示工作区和暂存区不一样的文件
git commit 命令
git commit -m'提交说明' # 添加到版本历史 -m是可选的,用来提交说明
git commit -am'提交说明' # 相当于是add和commit的组合
git commit --amend #修改所在分支最近一次commit的message
git diff 命令
命令用于显示提交和工作树等之间的更改。此命令比较的是工作目录中当前文件和暂存区域快照之间的差异,也就是修改之后还没有暂存起来的变化内容。
git diff 'hash值' 'hash值' #比较两个对象之间的不同
git diff HEAD HEAD~1 #比较HEAD指代的commit和HEAD的父级,~也可以用^代替,HEAD^1^1代表HEAD的父级的父级,等同于head^^,也等同于head~2
git diff --cached # 比较暂存区和HEAD文件
git diff --staged # staged和cached一样,都是指暂存区
git diff # 默认比较工作区和暂存区的区别(比较所有文件)
git diff -- '文件名' # 比较工作区和暂存区指定文件的差异
git diff 'commit版本号' 'commit版本号' -- '文件名' # 比较两个commit之间同个文件的差异
git fetch
git fetch '远端站点名' # 站点是连接远端时候自己设定的,默认是origin,fetch远端到本地
git fetch '远端站点名' '分支名' # 指定拉取远端的某个分支
git help 命令
git help --web log #查看log所有参数
git log 命令
git log #查看所有版本历史
git log -all #查看所有分支版本历史
git log --all --graph #图形化分支版本历史
git log --oneline #查看所有版本历史(简单,顾名思义每个版本一行信息)
git log -n #查看最近的n个版本历史
git log -n --oneline #查看最近的n个版本历史(每个版本单独一行显示)
git pull 命令
git pull # 相当于执行了fetch和merge两个指令
git push 命令
git push '站点名' --all # 把本地仓库所有文件传输要远端
git rebase 命令
git rebase -i '要修改的commit的父commit的hash值' #可用来修改message和合并commit
git reset 命令
git reset --hard #清除暂存区和工作区所有的变更(谨慎操作)
git reset --hard HEAD~1 #版本回退一级并清除暂存区
git reset HEAD # 恢复暂存区和HEAD一样
git reset HEAD -- '文件名' #恢复指定文件和HEAD一样
git reset --hard HEAD # 此命令执行后工作区、暂存区和HEAD文件一样
git status 命令
git status #查看当前git管理区状态
git cat-file -t
ls -al #查看当前目录下所有文件包括隐藏的,去掉 -al 即查看非隐藏文件
cd 文件夹名字 #进入文件夹
cd / #退到根目录
cd .. #退到上一级目录
vi 文件名字 #进入文件的编辑状态
cp '文件路径+文件名字' '重置的文件名.格式' #复制文件到当前目录并重新指定名字及格式
cp '文件路径+文件名字' . # 复制文件到当前目录(注意是.不是句号)
cp -r '文件夹路径+文件夹名字' '重置的文件夹名字' #复制文件夹到当前目录并更改文件夹名字
cp -r '文件夹路径+文件夹名字' . # 复制文件夹到当前目录
mkdir 文件夹名字 #创建文件夹
mv '文件名+格式' '文件名+格式' #文件重命名(方式不止一种)
echo "hello,world" >readme 创建一个文件名是readme包含内容是"hello,world"的readme文件