耿雨飞的个人博客
首页
文章搜索
人工智能
项目相关
首页
文章搜索
人工智能
项目相关
登录
注册
[[ slide_text ]]
[[ item.c ]]
0
0
Git 使用教程
发布时间:
2023-03-20
作者:
gengyufei
来源:
gengyufei个人博客
Git
# 工作区、暂存区与版本库 我们先来理解下 Git 工作区、暂存区和版本库概念: * **工作区**:就是你在电脑里能看到的目录 * **暂存区**:英文叫 stage 或 index。一般存放在 .git 目录下的 index 文件(.git/index)中,所以我们把暂存区有时也叫作索引(index) * **版本库**:工作区有一个隐藏目录 .git,这个不算工作区,而是 Git 的版本库 下面这个图展示了工作区、版本库中的暂存区和版本库之间的关系:  ------------ - 图中**左侧为工作区,右侧为版本库**。在版本库中标记为 **"index" 的区域是暂存区(stage/index)**,标记为 **"master" 的是 master 分支所代表的目录树**。 - 图中我们可以看出此时 "HEAD" 实际是指向 master 分支的一个"游标"。所以图示的命令中出现 HEAD 的地方可以用 master 来替换。 - 图中的 **objects 标识的区域为 Git 的对象库,实际位于 ".git/objects" 目录下,里面包含了创建的各种对象及内容**。 - **当对工作区修改(或新增)的文件执行 git add 命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中。** - **当执行提交操作(git commit)时,暂存区的目录树写到版本库(对象库)中,master 分支会做相应的更新。即 master 指向的目录树就是提交时暂存区的目录树**。 - 当执行 **git reset HEAD 命令时,暂存区的目录树会被重写**,被 master 分支指向的目录树所替换,但是工作区不受影响。 - 当执行 git rm --cached <file> 命令时,会直接从暂存区删除文件,工作区则不做出改变。 - 当执行 **git checkout . 或者 git checkout -- <file> 命令时,会用暂存区全部或指定的文件替换工作区的文件**。这个操作很**危险**,会清除工作区中未添加到暂存区中的改动。 - 当执行 **git checkout HEAD . 或者 git checkout HEAD <file> 命令时,会用 HEAD 指向的 master 分支中的全部或者部分文件<font color="#660000">替换暂存区和以及工作区中的文件</font><br />**。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。 # git config 配置 ## config文件分类 Git中有三层config文件:**系统、全局、本地** ``` #查看系统config git config --system --list #查看当前用户(global)配置 git config --global --list #查看当前仓库配置信息 git config --local --list ``` **注意:对于同一配置项,三个配置文件的优先级是:local > global > system。** ## 设置用户名与邮箱(用户标识,必要) ```bash $ git config --global user.name "***" #名称 $ git config --global user.email ****@qq.com #邮箱 ``` ## 增删配置 ``` # 增加配置 git config [--local|--global|--system] section.key value # 删除配置 git config [--local|--global|--system] --unset section.key [--local|--global|--system] #可选的,对应本地,全局,系统不同级别的设置 section.key #区域下的键 value #对应的值 --local 项目级 --global 当前用户级 --system 系统级 ``` # git 基本命令 ## 创建仓库 ```bash # 方式1 git init # 方式2 git clone [url] # [url] 是你要拷贝的项目 git clone https://github.com/tianqixin/runoob-git-test ``` ## 提交与修改 ### git add ```bash #git add 命令可将该文件添加到暂存区。 #添加一个或多个文件到暂存区: git add [file1] [file2] ... #添加指定目录到暂存区,包括子目录: git add [dir] #添加当前目录下的所有文件到暂存区: git add . ``` ### git status ```bash 查看仓库当前的状态,显示有变更的文件 ``` ### git diff ``` # 1. 比较「暂存区」与「工作区」之间的差异 git diff # 2. 比较「给定提交 ID」与「工作区」的差异 git diff [commit_id] git diff HEAD #目前工作区的内容和当前分支的最新一次的提交进行比较 # 3. 比较「暂存区」与「给定提交 ID」的差异 git diff --cached [commit_id] # 如果省略 commit_id,则表示「暂存区」与「最新提交」的差异 # 4. 比较指定的两次提交 「commit1」与 「commit2」的差异 git diff commit1 commit2 ``` **注意:比较对象的先后顺序决定了 diff 的差异结果的「增删」。上面几个命令的的描述,就是「后者」-「前者」的差异。** ### git commit ``` git commit -m [message] ``` ### git reset ``` # 1. 语法 git reset [--soft | --mixed | --hard] [HEAD] # 2. --mixed 为默认,可以不用带该参数,用于重置暂存区的文件与上一次的提交(commit)保持一致,工作区文件内容保持不变。 # 简单讲:--mixed 重置[暂存区]与[版本库], $ git reset HEAD # 取消已缓存的内容 $ git reset HEAD hello.php # 取消指定文件的已缓存的内容 $ git reset HEAD^ # 回退所有内容到上一个版本 $ git reset HEAD^ hello.php # 回退 hello.php 文件的版本到上一个版本 $ git reset 052e # 回退到指定版本 # 3. --soft 参数用于回退到某个版本 # 简单讲: --soft 只重置[版本库] $ git reset --soft HEAD~3 # 回退上上上一个版本 # 4. --hard 撤销工作区中所有未提交的修改内容,将暂存区与工作区都回到上一次版本 # 简单讲:--hard 重置 [工作区][暂存区][版本库] # 注意:慎用!!! git reset --hard [commit_id] ``` ## 日志查看 ``` # 查看完整log $ git log # 查看简洁版log $ git log --oneline # 查看历史中什么时候出现了分支、合并 git log --graph # 逆向显示所有日志 $ git log --reverse --oneline # 查找指定用户的提交日志 $ git log --author=Linus --oneline -5 # 指定日期,可以执行几个选项:--since 和 --before,但是你也可以用 --until 和 --after。 #例如,如果我要看 Git 项目中三周前且在四月十八日之后的所有提交,我可以执行这个(我还用了 --no-merges 选项以隐藏合并提交): $ git log --oneline --before={3.weeks.ago} --after={2010-04-18} --no-merges ``` ## 远程操作 ### git fetch 用于从远程获取代码库 假设你配置好了一个远程仓库,并且你想要提取更新的数据,你可以首先执行: ``` git fetch [alias] ``` 以上命令告诉 Git 去获取远程仓库的最新数据,然后你可以执行: ``` git merge [alias]/[branch] ``` 以上命令将服务器上的任何更新(假设有人这时候推送到服务器了)合并到你的当前分支。 ### git pull 用于从远程获取代码并合并本地的版本, **其实就是 git fetch 和 git merge FETCH_HEAD 的简写**。 ``` # 语法: git pull <远程主机名> <远程分支名>:<本地分支名> # 更新操作(更新当前分支): $ git pull $ git pull origin # 指定远程分支 与 本地分支 git pull origin [branch_remote]:[branch_local] ``` ### git push 用于从将本地的分支版本上传到远程并合并 ``` # 语法 git push <远程主机名> <本地分支名>:<远程分支名> git push origin master:master # 如果本地分支名与远程分支名相同,则可以省略冒号: git push <远程主机名> <本地分支名> # 以下命令将本地的 master 分支推送到 origin 主机的 master 分支 $ git push origin master ``` # 分支管理 ## 创建分支: ``` git branch (branchname) git checkout -b (branchname) # 创建新分支并立即切换到该分支下 ``` ## 切换分支命令: ``` git checkout (branchname) ``` ## 列出分支基本命令: ``` git branch ``` ## 删除分支命令: ``` git branch -d (branchname) ``` ## 分支合并 ``` git merge [目标分支] # 注意:该命令将[目标分支]合并至[当前分支] ``` # 标签管理 ## 查看标签 ``` $ git tag or $ git tag -l [标签名称筛选字符串*] 或者 git tag --list [标签名称筛选字符串*] git tag -l "v4.0" ``` ## 查看标签的提交信息 ``` $ git show 标签名 ``` ## 在提交历史中,查看标签 ``` git log --oneline --graph ``` ## 创建轻量标签 ``` $ git tag 标签名 or $ git tag 标签名 提交版本 说明 : git tag 标签名 : 直接给当前的提交版本创建一个【轻量标签】 git tag 标签名 提交版本号 :给指定的提交版本创建一个 【轻量标签】 ``` ## 创建附注标签 ``` $ git tag -a 标签名称 -m 附注信息 or $ git tag -a 标签名称 提交版本号 -m 附注信息 说明: -a : 理解为 annotated 的首字符,表示 附注标签 -m : 指定附注信息 git tag -a 标签名称 -m 附注信息 :直接给当前的提交版本创建一个 【附注标签】 git tag -a 标签名称 提交版本号 -m 附注信息 :给指定的提交版本创建一个【附注标签】 ``` ## 删除标签 ``` $ git tag -d 标签名称 ``` ## 推送至远程仓库 默认情况下,git push 命令并不会把标签推送到远程仓库中。因此,我们必须 手动地将 本地的标签 推送到远程仓库中。 ``` $ git push origin 标签名称 or $ git push origin --tags ``` ## 删除远程仓库标签 ``` $ git push origin :regs/tags/标签名称 or $ git push origin --delete 标签名称 ``` # git merge 与 git rebase ## 相同点 merge和rebase都是用来合并分支的 ## 不同点 > 前提:假设有2条分支(master 与 dev),下述讨论均为:当前分支为master,合并的目标分支为dev,即:git merge dev 或者 git rebase dev。 ### git log 显示 merge命令不会保留merge的目标分支dev的commit,rebase会保留所有的commit ### 处理冲突的方式 (一股脑)使用merge命令合并分支,解决完冲突,执行git add .和git commit -m'fix conflict'。这个时候会产生一个commit。 (交互式)使用rebase命令合并分支,解决完冲突,执行git add .和git rebase --continue,不会产生额外的commit。这样的好处是,‘干净’,分支上不会有无意义的解决分支的commit;坏处,如果合并的分支中存在多个commit,需要重复处理多次冲突。 ### git pull git pull和git pull --rebase区别: git pull做了两个操作分别是‘获取’和合并。所以加了rebase就是以rebase的方式进行合并分支得到一条干净的分支流。 ## 场景 ### 不要在master分支 使用rebase #### 目的 目的是保持主干【干净】 #### 原因 若在主干分支上使用 git rebase,则会将其他分支的n个commit变基至主干分支上,这带来2个坏处 ##### git pull 主干每次被 git rebase,其他团队成员均需 git pull 拉取最新数据; ##### 污染主干 主干被 git rebase,会将开发分支中一些开发commit加入到主干中,从而污染主干,这带来一个实际开发中会遇到的问题: > 主干:master,开发:dev; 现在master 上假设有3个commit节点:A B C,然后dev 上有3个commit节点:D E F。其中,A B C是大功能上线的节点,D E F是一个新功能开发过程中的节点,如果现在 git rebase dev,那么master上的历史提交节点应该是: ```A -> B -> C -> D -> E -> F``` 那么,现在问题来了,如果新功能上线发现有问题,需要master紧急回滚至上一版本(即:节点C,我们现在是知道上一版本对应的节点是C,但实际开发中,多人协作开发,提交节点会远远比上述例子复杂),常规的操作应该是这样: 1. 切换至 master:git checkout master 2. 查看log:git log (用于获取主干master上的提交信息,即:获取上一版本的 commit_id,用于git reset 操作) 3. 重置(或者叫回滚): git reset commit_id(问题就在这里:在不知情的情况下,如何区分 C -> D -> E 这3个commit节点,哪一个是上一版本的commit,答案是:没有办法区分) # git merge 与 git merge --no-ff ## git merge 快进合并 当前工作分支到合并目标分支之间的提交历史是线性路径时,可以进行快进合并。在这种情况下,不需要真实的合并两个分支,Git只需要把当前分支的顶端指针移动到目标分支的顶端就可以了(也就是快进的意思)。 ## git merge --no-ff 禁止快进式合并 快进合并在两个分支出现分叉的情况下是不允许执行的。当目标分支相对于当前分支的提交历史不是线性的,Git只能通过三路合并算法来决定如何对两个分支进行合并。三路合并算法需要使用一个专用commit来整合两边的提交历史。这个名词源于Git要想生成合并commit,需要用到三个commits:两个分支的顶端commit,以及它们的共同祖先commit。 ## 小结  从合并后的代码来看,结果其实是一样的,区别就在于 --no-ff 会让 Git 生成一个新的提交对象。为什么要这样? 通常我们把 master 作为主分支,上面存放的都是比较稳定的代码,提交频率也很低,而 feature 是用来开发特性的,上面会存在许多零碎的提交,快进式合并会把 feature 的提交历史混入到 master 中,搅乱 master 的提交历史。 所以如果你根本不在意提交历史,也不爱管 master 干不干净,那么 --no-ff 其实没什么用。不过,如果某一次 master 出现了问题,你需要回退到上个版本的时候,比如上例,你就会发现退一个版本到了 B,而不是想要的 F,因为 feature 的历史合并进了 master 里。
0
0
已经是第一篇啦
下一篇:Docker 架构原理
你觉得文章怎么样
发布评论
162 人参与,0 条评论