使用git进行版本管理
常识
- 文件的几个状态:Untracked,Unmodified(已修改,此时文件在工作目录),Modified(已暂存,此时文件在暂存区),Staged(已提交,此时文件在版本仓库)
- 在终端使用git 命令,需要帮助可以直接在命令后面加
-h
,便可以查看命令的使用说明
文档状态:基本内容完成,处于细节调整中。
<a name="MljPM"></a>
一、git基础操作
<a name="zurBZ"></a>
开始使用
<a name="EhFLr"></a>
git的基础配置
git的基础配置分为三个作用域
- local:只对当前的长裤有效
- global:对登录用户的所有仓库有效
- system:对系统的所有用户有效
config的优先级:local>global>system
git config --global user.name 'name'
git config --global user.email 'email@email'
# 显示git config的配置
git config --list --local
git config --list --global
git config --list --system
git config --local user.name # 仅查看user.name
# 设置,缺省等同于local
git config --local
git config --global
git config --system
# 清除
git config --unset --local user.name
git config --unset --global user.name
git config --unset --system user.name
# git编辑器默认使用shell环境变量$EDITOR所指定的软件。一般vim/emacs,可以使用下面命令将终端下的默认编辑器更改为vim,
git config --global core.editor vim
<a name="67Zk9"></a>
初始化git仓库
# 已有项目代码,进入到项目的根目录,在当前目录下创建git仓库
git init
# 无项目代码
cd 某个文件夹
git init project # 在当前目录下创建和项目名称同名的文件夹
cd project
<a name="2daxB"></a>
忽略某些文件
在项目内,有些文件,比如常见的第三方依赖,或者是构建后生成的文件,他们可以通过安装或者构建得到,对于这些文件,我们通常不将其列入我们的版本仓库,这可以通过在项目的根目录下添加.gitignore
文件来实现。文件内容如下。
doc 将不会管理doc文件夹内的所有内容,也不管doc文件
doc/ 只不管doc文件夹内的文件
# 对于前端项目通常会添加如下的内容
.DS_Store
node_modules/
/dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
/test/unit/coverage/
/test/e2e/reports/
selenium-debug.log
我们在使用某些IDE时通常会有插件可以帮助我们更加快捷的添加gitignore。比如Jetbrains就可以通过插件很方便的添加gitignore
<a name="llpMN"></a>
git别名
git 为了方便我们的使用,可以对常用的命令起别名,从而提升终端的使用效率
git config --global alias.co checkout
git config --global alias.unstage 'reset HEAD --'
git config --global alias.visual '!gitk'
执行外部命令 + !
<a name="TEqOr"></a>
Git的工作流程
- 在目录中添加、修改文件
- 将需要进行版本管理的文件放入暂存区域
- 将暂存区域的文件提交到Git仓库
<a name="2AW2s"></a>
<a name="feqvU"></a>
将文件添加到暂存区
git add . # 跟踪当前目录下的所有文件
git add fileA fileB #跟踪fileA和fileB文件
git add -u # 将所有已跟踪文件的变更添加到暂存区
<a name="S73WU"></a>
将暂存区内容提交至git仓库
提交得commit message要尽可能得有意义,便于我们了解此次变更做了什么。否则这会其他人造成困扰,也会给我们得某些操作造成困扰,比如无法通过git reflog,知道我们得每个git操作具体做了什么
git commit
git commit -m 'message'
git commit -a -m 'message' 或 git commit -am 'message' 对于在工作区内新增加还未追踪的文件不生效。
git commit --amend //追加提交,在不增加新的commit-id的情况下,将新修改的代码追加到前一次commit-id中
git commit --help
<a name="oE4FV"></a>
打标签
git 打标签,针对的是一个commit。打标签一般是伴随着发版进行的,通过git标签,我们可以更加方便的进行版本查看和回退等操作,也是非常重要的一个环节。
git tag //列出已有标签
git tag -l 'v1.8.5' //使用特定模板进行查找
标签分为两类:轻量标签和附注标签
git tag -a v1.4 -m 'message'
git show v1.2 可以看到标签信息与对应的提交信息
git show [hash]
git tag v1.4 //轻量标签
git tag -a v1.2 9fceb02 //针对于某个commit打标签
****************** 共享, ********************
git push不会将tag推送到远程仓库
git push origin v1.5 // 标签必须显示推送
git psuh origin --tags // 推送所有
******************* 检出 ********************
git checkout [tag] // 检出某个tag
// 会处于分离头指针状态,,此时的提交不属于任何的分支,如需要修改需要新建分支
git checkout -b version2 v2.0.0 // 在特定标签上创建一个新分支
****************** 删除tag **********************
git tag -d v1.4
git push origin :refs/tags/v.14
<a name="7vMKv"></a>
分支
分支对于git来说是非常重要的一个概念,我们通常会在主分支上新建新的分支进行自己的开发或者是bug修改。然后等待开发完成并验证后,在通过合并分支,将我们的新功能或者是bug修改合并到主版本库
git branch branchName
git branch -v #看本地的分支
git log --oneline --decorate //查看各个分支所指的对象。
git checkout branchName
git checkout -b branchName
git log --oneline --decorate --graph --all //输出提交历史,各个分支的指向及项目分支匹配情况。
git分支实质上是包含所指对象校验和(长度为40的SHA-1值字符串)的文件。
git branch -d branchName
git branch -D branchName
git merge branchName
分支合并的结果做新的快照,自动创建一个提交指向这个新的快照。分支合并自动选取一个提交作为共同祖先,以此作为合并的基础
分支冲突:
1.遇到冲突,Git会暂停下来,合并时可以使用git status 查看因为冲突未合并的文件,使用git merge --abort强制合并,使用git add fileName标记已解决的冲突。
git branch --merged 或 git branch --no-merged 查看已经合并或者尚未合并的分支。
没有合并过的分支使用git branch -d branchName 删除会失败。
//修改分支名字,远程的要删除重新推
git branch -m oldName newName
<a name="jtXI6"></a>
分支开发工作流
<a name="lBQ1l"></a>
远程分支
远程引用是对远程仓库的引用(指针),包含分支标签等等。git ls-remote
显示地获取远程引用的完整列表,通过git remote show(remote) 获得远程分支的更多信息
git branch --set-upstream branch-name origin/branch-name
将本地分支与远程分支关联
<a name="7VDOn"></a>
<a name="duyEN"></a>
查看版本仓库
<a name="JoKHo"></a>
查看当前状态
查看当前git仓库的状态,比如说查看我们对工作区和贮存区做了什么更改,或者是我们当前处于哪一个分支都可以使用此命令。
git status
git status --short 或 git status -s //获得紧凑的格式输出
// ?? 新添加未跟踪的文件。
//A 新添加到暂存区的文件
//M右边,修改了没有放到暂存
//M左边,文件被修改了并放入暂存区。
<a name="bNuET"></a>
比较版本间差异
通过git的git diff命令,我们可以比较任意文件在两个commit之间的差异。
比较暂存区域和工作目录:git diff j f b d u 键控制翻页 g G第一行,最后一行 3g 跳到第三行 /或者?+关键词 上到下,下到上搜索,高亮的是匹配的 n下一个 N 上一个 输入h帮助文档 q退出
git diff # 工作区和暂存区。
git diff --cached / git diff staged # 暂存区和版本库
git diff -- 文件名 # 具体的文件差别 可以多个文件
git diff masterA materB # 比较任意两个分支
git diff [old-commit] [new-commit-id] -- fileName # git比较任意两个commit之间的差异
<a name="XOv3r"></a>
查看提交记录 git log
我们可以使用git log命令来查看我们对版本仓库的历史操作信息。
git log # 按照时间列出所有更新,最新的在上面,列出SHA-1校验和名字邮箱,提交时间,说明,只展示当前分支
git log -p -2 # 用来显示每次提交的内容差异,2表示仅显示最近两次提交。
git log -n2 # 仅显示最近两次提交。
git log --stat # 附带总结信息,列出每次被修改的文件
git log --pretty=oneline # 在同一行显示
git log --oneline # 同一行显示
git log --all # 展示所有分支的版本历史
git log --graph # 图形化的方式展示
git log branchName # 之查看某个分支,有all的话这个不生效
git log --pretty=format:"%h - %an, %ar : %s"
git log --since=2.weeks //最近两周
--author指定作者 --grep搜索提交说明中的关键字 如果要的到同时满足这两个选项的搜索条件的提交,就要用--all-match
-s 列出添加或移除了某些字符串的提交。例如
git log -Sfunction_name
--path指定路径
git log --pretty="%h - %s" --author=gitster --since="2008-10-01" \
--before="2008-11-01" --no-merges -- t/
git log --oneline --decorate 查看各个分支当前所指向的对象
git log --pretty=oneline [fileName]
git log --abbrev-commit '显示最简短的唯一值'
git help --web log # 通过浏览器查看所有
gitk # 打开图形化界面
<a name="ANAxu"></a>
文件操作
<a name="2XWcZ"></a>
对文件重命名
将readme重命名为readme.md
# 方法1
mv readme readme.md # 直接重命名
git add readme.md # 添加重命名后的文件
git rm readme # 删除原来文件
# 方法2 git命令
git mv readme readme.md #使用git mv变更文件名
<a name="wuwRS"></a>
<a name="uUDYh"></a>
重命名文件
git mv file_from file_to
等同于
mv file_from file_to
git rm file_from
git add file_to
<a name="XLRBw"></a>
删除文件
git rm 文件名。删除文件并提交,使文件不再被追踪。
git rm 文件名 等同于 rm 文件名+git add .
如果删除之前修改过并放入暂存区。需要-f强行删除
git rm --cached 文件名从暂存区删除,但保留在工作区
<a name="dE9Tl"></a>
贮藏代码
当我们对工作区和贮存区的代码进行了更改之后,有额外的任务插入,我们需要将现在工作区和贮存区的内容暂时的存储起来,此时便用到了 git stash
<a name="BoxJJ"></a>
git stash
git stash
git stash list
git stash apply // 已暂存的文件不会被重新暂存
git stash apply --index // 重新暂存已暂存的文件
git stash apply stash@{2}
git stash drop stash@{2} //删除储藏
git stash pop // 重新应用并删除
git stash branch branchName //从最新储藏新建分支,成功后会删掉该储藏
git stash save noteContent // 等同于git stash,但是可以增加注释
git stash clear //清理所有
git stash show stash@{2} //查看stash和当前的差异
<a name="3fa4e3ad"></a>
回退与撤销
<a name="Glw6W"></a>
撤销操作
git commit --amend //第二次提交代替第一次提交的结果
git reset HEAD <file>... // 取消暂存。(不加选项只会修改暂存区内容,所以并不危险)
git checkout -- <file>
<a name="Zgtx7"></a>
暂存区恢复到头指针的状态
不保留暂存区的所有内容,将其恢复到跟head一样。
- 首先要将工作区内容 git stash
- git reset HEAD 将暂存区的恢复到了工作区
<a name="4ilOn"></a>
将暂存区的恢复到工作区
取消工作区的变更<br />git checkout -- fileName
<a name="jvy9s"></a>
删除历史提交中近几个commit
git reset --hard hash
<a name="sGHxV"></a>
代码回退
- 将暂存区域恢复到之前状态:git reset HEAD 文件名,不指定为所有
- 将暂存区的旧版本呢覆盖回:
git checkout -- 文件名
- 回到过去:
- 仓库Repository和暂存区Stage之间,commit reset
- 工作空间Working和暂存区Stage之间,add checkout
- git reset HEAD~ ~指上一个
- git rest --mixed(默认的不用写) HEAD~
- 移动HEAD指向,将其指向上一个快照
- 将HEAD移动后指向的快照回滚到暂存区
- git rest --soft HEAD~
- 移动HEAD指向,将其指向上一个快照(撤销一次错误的提交)
- git rest --hard HEAD~ (存在危险性)
- 移动HEAD指向,将其指向上一个快照
- 将HEAD移动后指向的快照回滚到暂存区
- 将暂存区的文件还原到工作目录
- 移动HEAD的指向(--soft)
- 将快照混滚到暂存区域([--mixed], 默认)
- 将暂存区域还原到工作目录(--hard)
- 回滚指定快照,指定前几个:git reset id号
- 回滚快照里个别文件
git reset 快照版本 文件名/路径
git reset 快照版本的ID号
<a name="44a6ae3f"></a>
远程仓库
git remote // 列出远程仓库简写
git remote -v //展示简写和对应的URL
git remote add <shortname> <url>
git fetch [remote-name] //从远程仓库获取数据,执行完成,会拥有远程仓库中所有分支的引用,可以随时合并查看。只是将数据拉取到本地仓库,不会合并或者修改。
git pull 会自动甚至本地的master跟踪克隆的远程仓库的master
git push origin master //写入权限,之前别人没有推送过。
git remote show origin 查看某个远程仓库的信息
git remote rename oldname newname //修改远程仓库名字,也会修改你的远程分支的名字
git remote rm 仓库名
git clone -b [branchName] [branchName]
<a name="Ews5p"></a>
多人协作
多人协作,不能强制提交,不能变基。
<a name="uNUd8"></a>
多人修改同一个文件的文件名
会报冲突,手动解决<br />git rm 源文件名<br />git add 需要的文件名<br />git rm 分期的文件名
<a name="tZdqC"></a>
有人把文件名变更,其他人基于原来的文件名变更了文件内容。
git会自动合并。git可以很好的处理这种情况,其他的版本控制不一定能良好处理。
<a name="WdO9g"></a>
高级使用
<a name="HaVOb"></a>
git chery-pick
git chery-pick可以将某个commit复制到当前的分支。chery-pick后,处于对记录历史的考虑,Auther是这个commit原来的提交人,但commiter是自己。
举例子:将branch1 的 commit1 提交 应用到 branch2 分支上来
实现步骤:
- 使用git log 查看需要被应用到新分支的commit的 hash是多少。
- 切换到branch2
git chery-pick [commit1hash]
即可自动提交
chery-pick的参数:
- 在git chery-pick后加上
-n
可以避免自动提交。 - 在git chery-pick后加上
-e
可以编辑commit信息。 - 与git rebase类似,当遇到冲突时有以下处理办法,
git cherry-pick --continue
,git cherry-pick --abort
,git cherry-pick --quit
。
<a name="yaelu"></a>
将commit之间得差异差异应用到新的提交
场景:branchA和branchB的差异应用到branchC<br />实现步骤:
- 在
git diff
后增加> patch
- 切换到branchC
git apply patch
- 注意会在当前目录下生成patch文件,需要手动删除等操作
<a name="UMeBQ"></a>
git钩子
通过git钩子,可以使得我们在执行git得某些操作时,触发一些脚本,比如在提交时校验我们的commit message是否符合要求,才我们推送代码到远程前,运行我们代码中的单元测试确保代码质量等。
<a name="7fyKz"></a>
git忽略commit钩子
-
git commit --no-verify
,这可以使我们得提交不触发commit得执行。
<a name="z9FRo"></a>
使用git钩子校验commit message
<a name="hI1qP"></a>
git reflog
git reflog 主要记录我们对于git得操作,通过它可以查找到所有分支的所有操作记录,包括删除的以及reset的内容!当我们因为不恰当得git操作,不小心将我们得代码删除了,我们就可以通过git reflog找回我们得代码。git log存储在本地。
<a name="ERvGO"></a>
不小心git reset --hard HEAD^找回
不小心将我们得代码回退到了上个版本,找回丢失得代码。
实现步骤:
- 使用git reflog找到我们不小心删除得commit得commit hash。
- 使用chery-pick 找回即可
<a name="ElcKL"></a>
在分离头指针状态下进行了提交切换分支之后丢失代码得找回
实现步骤与 不小心git reset --hard HEAD^找回 方法相同
<a name="rfo4w"></a>
更改git提交历史
通过git rebase,我们可以很容易的对我们的提交历史做更改。对提交的历史进行修改,可能会造成代码的冲突,我们需要解决冲突。
更改git的提交历史是非常****危险****的操作,更改后的分支无法直接推送到远程仓库,****必须强制推送****,如果分支涉及到****多人协作,****需要特别注意,避免产生不必要的麻烦****。
<a name="YnqTY"></a>
通过变基来整合不同分支的修改
我们已master分支为基准开发新的功能,首先我们为了开发新的功能切出了新的分支feature1,经过一周时间,我们开发完毕。在这一周之内,其他同事也在进行正常的开发并且上线完成,合入到了master分支。此时我们需要将这一周之内其他同事对于master的更改应用到自己的feature1分支上,此时便可以通过合并分支和变基来实现,通过变基可以使我们的git提交历史趋与一条直线。他与合并分支的明显区别就是,合并分支,会使得我们的多次commit与这周内master变更的commit依照时间混合在一起,使用变基则会使得我们得commit顺序位于master上得这些提交之后。变基操作可能会产生冲突。
# 使用合并分支
git merge master
# 使用变基
git rebase master
<a name="80kqP"></a>
修改commit的顺序
git rebase -i [commit-hash]
- 此时会出现一个编辑窗口,我们直接在编辑窗口内移动coomit的顺序之后保存即可。
常用的vim快捷键:复制:yy, 粘贴p,回退 u ,保存wq
<a name="GsqR4"></a>
借助于git rebase,拆分commit
https://blog.csdn.net/weixin_33736832/article/details/87960663
实现步骤:
- git rebase -i [commit-hash]
- 此时会出现一个编辑器窗口,在编辑器内出现的是我们所有的commit,将要拆分的commit 前面状态改为e,保存编辑的内容。
- git rebse会停留在需要拆分的commit。
- 此时使用 git reset HEAD^ 撤销需要拆分的commit。
- 查看当前的status,此时可以按照我们的需要,添加多次提交。
- 我们更改玩提交后,git rebase --continue。此时我们已经成功修改了提交记录
<a name="l8Z2a"></a>
二、Git工具
<a name="00cIU"></a>
选择修订版本
<a name="iDKjz"></a>
分支引用
git show [branchName]
<br />
git show SHA-1
git rev-parse [branchName]
查看git当前处于什么状态。
<a name="IqnJZ"></a>
引用日志
git reflog
查看引用日志,每当HEAD所指向的位置发生了变化,git就会将这个信息存储到引用日志。
git show HEAD@{5}
git show master@{yesterday}
查看昨天这个分支指向了哪次提交。只对还在引用日志里的数据有用。
git log -g
查看类似于 git log 输出格式的引用日志信息
分支引用只存在于本地仓库,新克隆则引用日志是空的
<a name="N3X4n"></a>
祖先引用
使用HEAD^来查看上一次提交<br />
HEAD^2 D当前提交的第二次父
HEAD~ 指向第一次父提交<br />
HEAD~2 第一父提交的第一父提交
这两种可以组合
<a name="0u7UT"></a>
提交区间
git log master..experiment 查看在experiment分支中存在而不在master分支中存在的提交。
git log origin/master..HEAD 查看即将推送到远端的内容。
留空一边git默认其为HEAD
<a name="N5tS2"></a>
多点
git log refA..refB
git log ^refA refB
git log refB --not refA
与双点语法区别。
git log refA refB ^refC
git log refA refB --not refC
<a name="Tnphp"></a>
三点
选出两个引用中只有一个包含的提交<br />
git log master...experiment
<br />
增加参数 git log --left-right master...experiment
<a name="e889f8bb"></a>
交互式暂存
交互式暂存,将文件的特定部分组合成提交,修改一组文件后,希望改动可以放到若干个提交而不要混杂在一起。这可以确保提交是逻辑上独立的变更集<br />
git add -i
<br />
git add --interactive
在命令区域,可以暂存文件,取消暂存文件,暂存文件的一部分,添加未被追踪的文件,查看暂存区内容区别。
<a name="MJa3W"></a>
暂存与取消暂存文件
<a name="ba590b6b"></a>
<a name="Nntl6"></a>
三、git基础知识
<a name="fvGob"></a>
.git里面都有什么
- HEAD文件存储当前的分支,直接编辑与切换分支是同样的效果
- config文件存储,针对于本文件的config
- refs文件夹 heads对应分支 tags head内的文件存储的是hash值, tags内的hash,hash值指的是 commit
- objects 内的文件夹与文件夹内的文件组成hash值,使用git cat-file 查看是tree。 使用命令查看数的内容,是一个文件对象,通过git cat-file -p hsdh查看就是文件的内容
git cat-file -t hash #命令显示版本库对象的内容、类型及大小信息。 看内容用-p
commit tree blob是三个核心的对象。
只要任何文件的文件内容相同,就是唯一的blob
<a name="Mr8Rz"></a>
commit tree blob 三者关系
[图片上传失败...(image-acb2c9-1583609442636)]<br />一个commit对应一颗树,树代表了commit对应的视图,视图里面存储快照,快照里放了当前commit对应的本项目仓库的所有文件夹以及文件的快照。blob与文件名无关系。
举例:<br />空仓库,下有doc/readme
git add 将文件加入暂存区,那么git就会在.git/objects下创建对应文件的blob。比如 .git/objects/2d/832d90044c。使用git cat-file -t 2d832d90044c 查看可以发现类型是blob,通过git cat-file - p t2d832d90044c,可以查看到,文件的具体内容
git commit 提交后,那么git就会在.git/objects下会有4个文件.
- 一个对象:tree 内容是doc
- 一个对象:blob readme的文件内容
- 一个对象:tree 里面readme文件的blob
- 一个对象: commit
<a name="QZGt3"></a>
分离头指针(detached HEAD)
变更没有基于某个branch去做,所以进行分支切换时,在分离头指针之上产生的commit,可能会被git当作垃圾清理掉,如果认为变更时重要的那么,一定要将变更与分支绑到一起。<br />git checkout [commitHash]<br />本质上是工作在没有分支的状态下,此时可以做开发,提交(通过git log可以明显的看到HEAD没有与分支关联)。如果切换到新的分支,那些没有与分支关联的变更可能会被git当作垃圾清理掉。要对分支做变更。git checkout -b new-branch-name。
在分离头指针时切换分支git会提示是否要为已经提交的commit建branch, git branch <new-branch-name> <commitHash>
可以利用这一特性做尝试,当丢弃时,直接切换到别的分支即可。
<a name="9ip7C"></a>
特殊标识符HEAD
HEAD不仅可以指代新分支最后一次提交,同时HEAD还可以不跟分支挂钩,处于分离头指针状态(指导某个commit上),如果新建/切换branch,HEAD指针指向新的分支。无论处于什么状态,HEAD最终会落脚在commit。
HEAD可以指代commit。<br />git diff HEAD HEAD^ 比较这个commit和其父commit的提交。
HEAD^^ 等同于 HEAD~2
<a name="hUEe6"></a>
git的备份
可以备份到本地的其他地方。<br />协议和智能协议<br />直观区别:哑协议传输进度不不可⻅见;智能协议传输可⻅见。<br />传输速度:智能协议⽐比哑协议传输速度快。
git clone --bare /User//.git ya.git 哑协议 <br />git clone --bare file:///User//.git zhineng.git 智能协议
<a name="7EArh"></a>
git remote add zhineg file:///User/**/.git<br />git push 进行远端备份
<a name="YtZfa"></a>
<a name="tQku9"></a>
<a name="lkj6S"></a>
四、使用问题归纳
<a name="0Cv7z"></a>
本地初始化git仓库并添加远程仓库
git init
git remote add origin <远程仓库地址>
git add .
git commit -m "注释"
git pull --rebase origin master
git push origin master
<a name="VTDTW"></a>
<a name="pBtBm"></a>
删除远程分支后本地还可以通过 git branch -a看到
解决<br />https://www.cnblogs.com/taohuaya/p/10912245.html
git branch -a #查看所有本地分支和远程分支
git remote show origin #查看remote地址,远程分支,还有本地分支与之相对应的关系等
git remote prune origin [branch] # 根据提示进行此操作,删除本地分支的branch,这样就可以在本地删除远程不存在的分支
<a name="SrcWP"></a>
git忽略已经提交的文件(.gitignore文件无效)
解决链接:https://www.jianshu.com/p/e5b13480479b
方案一
git rm [ignoreFileName]
git commit -am [commit-message] #删除不需要的文件
在.gitignore
文件中添加忽略规则,提交.gitignore
文件
git push [remote]
方案二
git rm -r --cachced build/* # 都不需要可以不跟文件名
git add .
git commit -m 'commit '
git push
针对已经commit过且有改动的文件 (因为rm的是cached列表中的文件, cached列表即修改列表)
最好在创建 git仓库的同时就创建.gitignore
文件
<a name="nBAyQ"></a>
merge两个不相干的分支
git merge -allow-unrelated-histories master orogin/master
<a name="qvuQH"></a>
五、Git可视化软件推荐
<a name="Y9axO"></a>