1

什么是git

git 是一个版本控制软件
gitHub和gitLab都是用于管理版本的的服务端软件
gitHub提供免费服务(代码公开)及付费服务(代码为私有)
GitLab用于在企业内部管理Git版本库 功能上类似于GitHub

git的优点

**直接记录快照 而非差异比较
近乎所有的操作都在本地执行
时刻保持数据的完整性
多数操作仅添加数据
本地建立版本库
本地版本控制
多主机异地协同工作
重写提交说明
有后悔药可以吃
更好用的提交表单
更好的差异比较
更完善的分支系统
速度极快**

git 的开发模型

基于Git分支的开发模型
1 develop分支  (频繁变化的一个分支)
2 test分支 (供测试与产品等人员使用的一个分支,变化不是特别的频繁)
3  master分支  (生产发布分支 变化非常不频繁的一个分支)
bugfix(hotfix)分支(生产系统当中出现了紧急Bug,用于紧急修复的分支)
常见的三种文件状态

*文件的三种状态

  (Git文件  Git文件已被版本库管理的文件)

已修改(modified) 在工作目录修改Git文件
已暂存(staged)对已修改的文件执行GIt暂存操作 将文件存入暂存区
已提交(committed)将已暂存的文件执行git 提交操作 将文件存入版本库
*

git 的常用命令

删除文件

rm -rf /*  会将操作系统的所有文件都删除
rm -rf .git  会递归式的删除git文件下的所有文件
rm text2.txt 删除  这时 被删除的文件未被纳入暂存区当中
rm -rf  mr  强制删除空文件夹
rm  -rf  *  清空当前文件夹

git rm   的动作
1 删除了一个文件
2 将删除的文件纳入到暂存区

恢复被删的文件

a . git reset HEAD text2.txt  将文件从暂存区恢复到工作区
b.  git checkout --text2.txt 将工作区中修改丢弃掉

生成公私钥

which  ssh-keygen

创建文件

touch test.txt   touch创建文件

echo 'ggg' > text6.txt  写入内容并创建text6.txt

vi .gitignore    创建文件并进入vi编辑界面

编辑文件

vim text.txt    进入vim i编辑文件   esc :wq退出

echo 'child5' >>child.txt  //往文件末尾追加内容
>>是追加的意思
>是替换的意思

查看内容

cat test.txt   查看文件内容

按tab键会自动补全文件名

git add t

取消缓存

git rm cached text.txt   取消缓存
git reset HEAD text.txt  取消缓存

恢复成文件修改前的状态

git checkout --  text.txt

替换文件的内容

echo 'welcome' > text.txt   welcome会替换text.txt里面所有的内容

显示当前所在的目录

pwd

对于版本库已经存在的文件的修改的提交

git commit -a -m 'hh' 跳过添加缓存区  直接 添加版本库

git commit -am '提交'   直接提交修改省略存入暂存区

光标的控制

ctr + a  光标由最后端移动到最前面

ctr  + e   光标由最前端移动到最 后面

文件重命名

git mv text2.txt text3.txt   将text2.txt重命名为text3.txt

修改刚刚提交错误的commit message

git commit --amend -m '修改错误的commitmessage'

git log

git log  
-p 展开显示每次提交内容的差异
-n(number)  仅显示最近的n次更新
--stat  仅显示简要的增改行数统计
--pretty=oneline 以单行的显示
--pretty=format:"%h-%an,%ar:%s"   特定格式完整的信息

**查看log以图形化的方式**
git log --graph
git log --graph --abbrev-commit  简写的方式
git log --graph --pretty=oneline --abbrev-commit 更简写的方式

如果远端上有一个master分支  你可以在本地通过下面几种方式来访问他们的历史记录:
git log origin/master  会查看远程orgin/master分支上的记录

完整 git log remotes/origin/master
上面两种 最后 git 都会把他们转换成:git log refs/remotes/origin/master

获得所有关于config的参数

git help config

当前文件夹下的文件

ls 获取当前文件夹下的文件

ls -all 取当前文件夹下的所有文件

创建分支

git branch newbranch

切换到上一次所在的分支

git checkout -

删除分支

git branch -d branchname  删除分支
git branch -D branchname  强制删除

删除远程的分支

git push  origin  :branchName
git push origin --delete branchName

git remote rm origin   //删除远程仓库
再加回来  git remote add origin http(url)

创建分支并切换

git checkout -b branchname
创建完之后 与 之创建前所在的分支指向的是同一个commit_id

git checkout -b branchName origin/branchName
(新建一个新的分支和远程分支保持对应关系)
git checkout --track origin/test(同上 ,但是默认会起一个和远端相同的名字)

查看文件的修改记录

git blame test.txt   文件是由谁修改了哪几行

查看远程仓库

git remote show  会显示与本地对应的远程仓库的别名(比如origin)

git remote show origin 远程仓库的详细信息

查看远程分支

git branch -a   查看远程对应的远程分支

git branch -av  详细的远程分支信息 及最近一次的提交信息

git diff

原始文件与目标文件的区别
  版本库   暂存区
1 工作区与暂存区之间的差别
git diff
2 工作区与特定的commit_id 版本库之间的区别
git diff commit_id
git diff HEAD   工作区与版本库(最新提交)之间的比较
3 暂存区与版本库之间的差别
git diff --cached commit_id 暂存区与某次特定提交之间的区别
git diff --cached  暂存区与最新提交之间的区别

git pull

pull   拉取 同时执行合并merge
pull === fetch+merge
git fetch 仅拉取

git pull 的完整命令:git pull origin srcBranch:destBranch     //srcBranch远程分支  destBranch本地分支

从远端的master分支 拉取到本地与本地的mymaster与它对应
从端拉取master拉取到本地的远端分支mymaster
git fetch origin master:refs/remotes/origin/mymaster
全写的一种方式
然后 跟踪本地远端分支 : git checkout --tract origin/master
然后本地分支裁剪  git remote prune

添加远程仓库

git  remote add origin url    origin(默认的url名字)

git add

三个作用  1.将未追踪的文件加入到跟踪列表中  添加到 缓存区中

2 将已经追踪的文件纳入到缓存区当中

3 如果一个文件出现了冲突,手动解决冲突后  然后git add 表示冲突已经被解决了

你当前文件的修改与暂存区的内容保持一致

git checkout -- test.txt 丢弃你当前文件的修改与暂存区的内容保持一致(改的是工作区)

本地的内容还原与版本库一样

git checkout --

将添加到暂存区的内容从暂存区移除到工作区

git reset HEAD text.txt

fast-forward

一个点本地分支直接转到另一个点 不需要merge  就叫fast-forward

git merge

git merge branch4   merge branch4的代码合并到当前分支
git add <file>  解决冲突后要git add  表示冲突已解决
然后 git commit  提交到版本库 终止当前merge状态

如果可能,合并分支是Git会使用fast-forward模式
在这种模式下  删除分支时会丢掉分支信息
合并分支时加上 --no-ff参数会禁用fast-forward 这样会多出一个commit id

git merge --no-ff dev
git 的最小单位是行,它不会判断一行的哪个字符发生了变化

显示当前分支最近一次的提交

git branch -v

git图形化界面

在git版本库的文件夹下 gitk  可打开
 git gui 可打开

修复远端缺失的分支

git  branch --unset-upstream

克隆代码并重命名项目目录

git clone url   myject

vim  编译删除

set number
esc : 1,5d    删除1到5行的数据

假如远端的分支已经删除, 本地再pull 的话会报错

执行 git remote prune   裁剪本地分支
  
git refspec (本地分支与远程分支之间的映射关系)

在缺省情况下 refspec会被git remote add 命令所自动生成  git会获取远端上refs/heads下的所有引用 并将他们写到本地的refs/remotes/origin目录下

推当前分支到远程 并和远程建立关联

git push 的完整写法: git push origin src:dest      //src是本地的分支   dest是远程的分支

git push --set-upstream origin branchName
git push --set-upstream origin branchName:anther     本地的分支branchName推到远程并改名为anther

git push -u origin master  将代码推到远程并将本地的分支与远程的分支做关联

如果本地和远程的分支名字不统一,那么push就应该是 :git push origin HEAD:远程分支的名字
建议本地和远程一样,创建和本地分支名字一样的分支: git push origin branchName(全写 git push origin branchName:branchName)

分支

一个commit对象链  一条工作记录线

版本回退

回退到上一个版本

git reset -- hard HEAD^
git reset -- hard HEAD^^ 回退到上两次提交
git reset --hard HEAD~1  往前回退的次数
git reset -- hard commit_id
也可以往前走  git reset --hard commit_id(前面的id)
返回到某一个版本

git checkout <commit_id> 回退
本地分支回到最初的那个点
git checkout commit_id      
然后HEAD属于游离的状态

image.png
git branch -D  branchName
image.png

git stash

git stash

git  stash list

恢复现场
git stash apply(stash内容并不删除 需要通过git stash drop stash@{0}手动删除)

git stash pop (恢复的同时也将stash内容删除)

git stash apply stash@{0}

分支改名

git branch -m master master2  将分支master改名为master2

重命名远程分支

1 删除远程分支
2  本地分支重命名  提交到远程

git remote rename origin origin2 //将远程的origin仓库重命名未origin2

改文件的状态

git rm --cached hello.txt   //从暂存区删除  改文件的状态就变成了未跟踪的状态

更改暂存区文件的名字

git mv a.txt aa.txt   //将暂存区a.txt文件重命名aa.txt

等同于:
1 mv a.txt aa.txt
2 git rm a.txt
3 git add aa.txt

Git标签

标签本质上是一个标记而已,不能改变的静态的东西
标签本身也是一个完整的对象  它要记录自己的commit message 、commit_id、  标签本身的注释
与commit_id 相似  与SHA-1的点是一样的
创建标签的时候,指向了特定的commit_id,
新建标签 标签有两种:轻量级标签(lightweight)与带有附注标签(annotated)

创建一个轻量级标签
git tag v1.0.1

创建一个带有附注的标签
git tag -a v1.0.2 -m 'release version'

删除标签
git tag -d tag_name

git tag 查看本地所有标签

git tag -l 'v*'  查找标签

git tag -l  ‘?2*’  查找任意开头的带2的标签

git show v1.0    git 的提交信息

git push origin v1.0     就是将v1.0标签推到远程
git push origin v1.0  v2.0  批量推送
git push origin --tags  将本地的标签全部推送到远程
git pull   会将本地没有的标签拉下来
git push origin :refs/tags/v6.0 将远程的标签删除
git push origin --delete tag v5.0   将远程的标签删除
git tag -d v5.0  将本地的标签删除

将标签推送到远程的完整写法
git push origin refs/tags/v7.0:refs/tags/v7.0  

完整写法
拉取单个指定标签
git fetch origin tag v7.0

HEAD

HEAD 指向的分支  分支指向的是具体的提交点
HEAD指向的是当前分支
HEAD标记:HEAD文件是一个指向你当前所在分支的引用标识符,而该文件并不包含SHA-1值,而是一个指向另外一个引用的指针
当执行git commit命令时,git会创建一个commit对象,该对象包含(指向了这次包含的内容,指针指向区,又包含了一个parent指向,指向它的父的parent提交),并且将这个commit对象的prent指针设置为HEAD所指向的引用的SHA-1值
我们对于HEAD任务修改  都会被 git reflog  记录下来(不包括symbolic-ref和手动修改)
实际上 我们可以通过git 底层命令symbolic-ref来实现对HEAD文件内容的修改
git symbolic-ref HEAD  读取HEAD
git symbolic-ref HEAD refs/heads/develop(分支名)   设置HEAD

image.png

gitignore 文件的规则

*.b  忽略以b结尾的文件
!a.b除外    !取反
/TODO   仅仅忽略项目根目录下的TODO文件,不包括subdir/TODO

build/ #忽略huild目录下的所有文件

doc/*.txt  会忽略doc目录下的以.txt结尾的文件  但不包含子目录的

doc/*/*.txt 会忽略doc的子目录及子目录的以.txt结尾的文件

doc/**/*.txt   会忽略doc目录及子和子子目录的所有以.txt结尾的文件

本地远程分支

本地的远程分支 :远程分支在本地的一种镜像
origin/master  本地远程master  对应远程分支的关系
本地代码push 的时候不仅将本地的代码推到远程 还会将本地的远程分支改变提交点
origin/master ...远程分支 就是起到本地的git分支跟远端的分支做一个中转(媒介  桥梁)那么更新的时候 从远端拉取最新文件  然后git 会修改本地远端分支的指向  然后再把指向跟你本地的代码做一个合并

git合并的原则

git遵循三方提交的原则

Commit id

git的提交id(commit id)是一个摘要值 这个数值实际上是sha1计算出来的
**对于每个版本库的基础设置
user.name与user.email**

1  /etc/gitconfig(几乎不会使用) git config --system
2 ~/.gitconfig (很常用)  git config --global
3 针对于特定项目的  .git/config文件中  git config --local

git config --unset user.email   删除config的配置

创建文件夹并进入

mkdir git_bare && cd git_bare //创建文件夹并进入

git cherry-pick

将你对某一个分支的修改  commit信息应用到另外一个分支上面

image.png

Git rebase

变基
衍合
rebase 改变分支的根基
rebase  的功能类似于merge
merge 会产生一个新的提交  这个新的提交会指向之前要合并的两个分支

image.png
Rebase
git rebase origin
image.png
执行完之后c5和c6就没有用了
image.png
image.png

将一个分支的内容应用到另一个分支上面
会修改git的提交历史

rebase过程中也会出现冲突
解决冲突后 使用git add添加  然后执行
git rebase -- continue
接下来GIt会继续应用余下的补丁

任何时候都可以通过如下命令终止rebase  分支会恢复到rebase开始前的状态
git rebase -- abort
  

不要对master分支执行rebase  否则会引起很多问题
一般来说 执行rebase的分支都是自己本地的分支  没有推送到远程版本库
与其他人没有共享
因为会修改git的提交历史,如果同团队的其他成员拉代码,会引出很多问题

当我们在test分支  git reabse  origin 
实际上是将test与origin的祖先节点后的test节点以补丁的形式保存了起来
以dev节点作为基础节点(basePoint)将保存下来的第一个提交在dev节点后面做一个重放(应用补丁) 应用的过程可能会出现冲突  也可能不会出现冲突
解决完冲突  会将第二个补丁应用到dev的后面
那么dev还是没有变化的   而test则处在dev的后面位置了
如果将test merge到dev上   那么就执行fast-foward   dev就指向了2'的点

image.png
image.png

git rebase --skip  //就是说rebase dev  后 以dev的为准 那么就丢掉了test1的修改 如果再冲突继续执行skip 那么test2的修改也会丢失  然后test分支上保存的是dev的修改

rebase会将原来分叉的历史变成一条直线,但是会修改git的提交历史

test分支上 使用两种方式的不同
git merge develop
整个develop的修改会合并到test上、      优点完整保存git的提交历史

git rebase  develop        优点git的提交历史很整洁
就是将develop变成test的基点(basepoint)
不管test后面有多少提交
将这些提交以补丁的形式保存起来
将这些补丁再以回放的形式在develop分支以一个提交一个提交的回放
回放完毕之后  
在test上面后面提交就没有用了,会被git 的git gc 垃圾处理掉

git 别名

image.png

br是你起的别名  branch是完整的命令

git config --global alias.br branch

git config --global alias.st status

git config --global alias.co checkout

git config --global alias.unstage 'reset HEAD'

git config --global alias.ui '!gitk'   !指的是外部命令

实际上是记录在了这里 vi ~/.gitconfig

git 裸库

没有工作区域的git仓库  一般在服务器端使用
因为在服务器端 一般作为文件传输与文件承载的中介
托管我们所编写的程序代码   因此不需要存在工作区域
mkdir git_bare && cd git_bare //创建文件夹并进入
创建裸库 : git init --bare

image.png

submodule

*submodule  (子模块)  一个 大项目  依赖于其他的模块
ssh只能放到一个项目里面  可设置账户sskey  就能多个项目*

git submodule add git@github...(子地址) mymodule
  • 将远程的被依赖的库拉取过来之后放到parent项目的mymodule目录当中  这个目录 不能之前就存在  是之前就没有的目录*

image.png
拉取子目录最新的代码  进入子模块  git pull
image.png

如果你的一个工程使用到了submodule,对于克隆这个操作默认情况下是不会把submodule的代码克隆下来的,需要你手工的执行一次:
1 submodule的初始化 :git submodule init
2 执行一次更新   git submodule update --recursive

image.png
克隆的时候把所有的子模块一次性克隆下来

git clone git@....  gitparaent(文件名)  --recursive

image.png

移除子模块
git rm --cached mymodule  从暂存区移除
rm -rf mymodule   从工作区域移除
rm .gitmodules    
然后 git add .
git commit -m 'remove submodule'
git push

git subtree

同git submodule类似 但是可以双向修改   可以在父仓库修改子模块的代码

1 git subtree add --prefix subtree
 git remote add subtree-origin  git@...
 git subtree add -P subtree  subtree-origin master --squash

git subtree add --prefix=subtree
git subtree add --prefix=subtree subtree-origin master --squash
subtree是目录的名字(将远程被依赖的代码放到subtree目录下)
subtree-origin 是依赖模块的克隆地址
master是克隆的分支

squash是可选的参数
如果没有这个参数 它会把远程的subtree库
的提交一个一个的拉取到本地,然后再创建一个合并提交
会把子模块的提交信息完整的添加到主仓库的提交历史里面

如果加了这个参数,会将子仓库的提交合并压缩成生成一个提交,并merge到主仓库 生成一个merge提交,防止污染主仓库的提交历史

  

但是一旦项目一开始使用了squash,后面就要一直使用
如果一开始没有使用squash.,后面就不要使用
因为git遵循三方提交原则  当我们提交的时候会找不到三方合并共同的prent的那个点了,会存在找不到报错的情况

如果依赖的子模块代码更新了

git subtree pull --prefix=subtree subtree-origin master --squash
或
git subtree pull -P subtree subtree-origin master --squash
git push

image.png
如果加了 就会合并成一个提交历史  合并到当前分支上
在主仓库修改子模块中的内容

1 git push   先提交到远程主仓库
2 将对依赖的子模块的修改提交到远程及依赖子模块的远程

git subtree push --prefix=subtree subtree-origin master
或 
git subtree push -P subtree subtree-origin master

不加squash   就会把子模块的提交历史也merge过来
image.png

*当我们从主仓库拉子模块的代码时 会有一定的概率会有冲突
因为他们的初始父节点是无关连的*
image.png

**git log subtree-origin/master
subtree的远程分支所对应的提交历史**

Git文件下的.git文件

**git 的配置不建议手工改配置  提议利用命令改
因为命令可能一次性会改很多  而手动有可能只改了一个  那就乱套了**
image.png
ORIG_HEAD (远程的HEAD 处在什么位置)

FETCH_HEAD

你在远程拉取代码后HEAD在什么位置,记录的commit_id,和commit message

image.png

config目录
image.png

这句话表示   git 会获取到远端 refs/heads下的所有引用 并将他们这些引用写入到本地的refs/remotes/origin/下面
默认的情况下 是根据名字匹配的  比如远端是 master 那么本地也叫master
这个是git fetch 操作 对于远端缺省的refs/heads 值   有加号强制更新后
图上还有些 默认的merge 远端的地址  比如 master同远端的refs/heads/master  merge

refs 目录
image.png

heads 表示的是当前的信息

image.png

远端分支位于remotes目录下

tags 目录
image.png

标签本身就是一个commit _id  

轻量级的只有commit_id

注释标签有两个id  本身的id  和指向的commit_id

COMMIT_EDITMSG   文件记录的是上次提交的信息


HappyCodingTop
526 声望847 粉丝

Talk is cheap, show the code!!