简介
- 大神 Linus 除了 Linux 内核之外又产出了一个神作——Git分布式版本控制系统
- 2008 年,专门提供 Git 存储的 GitHub 网站上线
基操
- 大致流程是这样的
- 分为远程和本地的仓库
- 本地 Repo 如上图中间存储库Repository,实际上都存在项目根目录下的
.git
文件夹中,内部可能有多个 branch,但至少有一个叫 master的branch - 完成add和commit两步操作才能将新增或修改的文件纳入本地Repo的存储库中进行版本管理。中间Index索引数据应该也是和本地 Repo一样存在项目根目录下.git目录中
- 到这里Git与 CVS、SVN操作逻辑大致一致,只是实现了本地的版本控制。Git的特点是分布式
- 本地 Repo 如上图中间存储库Repository,实际上都存在项目根目录下的
- 本地 Repo 中的 branch 与一个或多个远程 Repo(上图中的Remote) 中的 branch 存在跟踪关系,这就构成了Git分布式版本控制的网状结构
$ git --help # Git命令用法及常用的Git命令列表
- git在不同操作系统的安装就不介绍了,Mac使用brew
- 安装
VS Code
时已经附带安装了Git(并没有),貌似当下这款工具比较流行,也推荐使用
本地版本库
- Git 本地版本库的基本用法
- 就基于vscode说明
- VS Code的工作区概念与Git的工作区概念基本一致,打开文件夹
Ctrl/⌘+O
- 打开源代码管理
Ctrl+Shift+G
,可以点击初始化存储库,如果在远程有仓库,可以直接:
git clone https://DOMAIN_NAME/YOUR_NAME/REPO_NAME.git # 然后再打开clone的文件,就有了.git
- 不管使用何种方式初始化一个Git本地版本库,都是在项目根目录下创建.git文件夹
- 现在安装的Git自带git bash和git cmd操作环境
- VS Code的工作区概念与Git的工作区概念基本一致,打开文件夹
- 感觉命令行更简便,看习惯哪个吧
- 注意,如果在vscode中打开层级目录,上层初始化了git,下层clone了仓库(也初始化),进入文件夹时源代码管理不管用
- 要保证打开的文件夹只有一份.git(直接打开当前工程)
- ctrl+s了才会在这里出现:
- 跟踪文件(绿色U标记表示未跟踪文件),点击那个
+
即可,-
可取消,这对应命令行的add
# 添加到暂存区(Index) git add FILES # 指定文件或文件列表 git add . # 用“.”表示当前目录 # 取消将特定文件(FILES)或者所有文件添加到暂存区 git reset HEAD FILES # 指定文件或文件列表 git reset HEAD # 放弃特定文件(FILES)或者所有文件的修改(检出,相当于撤回) git checkout -- FILES # 不要忘记“--” ,不写就把FILES当分支名了 git checkout . # 对应撤回那个箭头 # 如果已经add 到 index,就不会被 checkout
- 可以直接点击对号“√”(Ctrl+Enter)将暂存的文件提交到仓库中
-
会强制让你输入comment,在Enter
# 对应commit命令 git commit -m "wrote a commit log infro" # 可以查看提交日志 git log # 让HEAD回退到任意一个版本 git reset --hard HEAD^ # HEAD的前一个版本 git reset--hard HEAD^^ # HEAD的前两个版本 git reset --hard HEAD~100 # HEAD的前100个版本 git reset --hard 128个字符的commit-id # 也可以用版本号字符串来指定任意一个版本 git reset --hard 简写为commit-id的头几个字符 # HEAD只是一个指向特定版本的指针
-
需要配置用户名和邮箱,可以在全局/局部或者项目文件中配置
[user] name = roy email = szsplyr@163.com
-
一旦提交到仓库,尽管也可以撤销上次提交,但是依然会在仓库里留下记录
-
git log
只能查看HEAD及其之前(时间更早)的提交记录,所以改了指向之后最新的记录查不到# 这其实就有个问题:可以通过git reset —hard回到过去,那怎么回到未来? git reflog # 可以查看当前 HEAD 之后(时间更晚)的 提交记录 # 从而可以通过git reset —hard回到未来
-
- 小结
- 在本地开发,可以add暂存修改,或者checkout撤销修改
- commit之后,可以reset,通过回退版本撤回
远程版本库
- 如果未打开文件夹,vscode的源代码管理中会有两个选项,其中克隆存储库可以输入远程仓库地址,直接clone并初始化,并与远程仓库建立连接
- 此时直接创建了本地版本库,操作和上面相同
- 远程的场景:先以单人开发远程备份或者公开源代码为目的(循序渐进!)
- git clone之后默认的分支
- 远程为 origin/master
- 本地为 master
git remote # 输出远程存储库名称 git remote -v # origin https://gitee.com/roykun/ArticleSpiders.git (fetch) # origin https://gitee.com/roykun/ArticleSpiders.git (push) # git fetch、git push加上git clone是三个对远程存储库的基本操作
- 不管是在本地仓库还是远程仓库,对代码修改之前都首先进行代码同步操作,防止产生分叉和冲突
- vscode中这样操作,提交并拉取,同步的两大步骤!
- 在这里的单人开发场景中,只会推送或只会拉取,不会同时(因为没别人)
- 在命令行中,和远程交互,只需两句:
git pull # 实际上是 git fetch + git merge(合并)的组合 # Fetch from and integrate with another repository or a local branch git push # 将修改的代码推上去
- vscode中这样操作,提交并拉取,同步的两大步骤!
- 实际操作中难免会产生无法同步的情况,这时候需要在本地解决冲突
- 一般是由于本地commit了修改,还没push,然后去pull了,本地领先于远程会冲突
- 此时push一下就同步了(也不能叫冲突吧)
- 单人项目工作时间线上是串行的,只要及时将本地与远程同步就不会出现分叉
多人开发
- 实际项目往往多人团队,时间线常常是并行的,团队中的每个人都向远程版本库 push
- 理解了 Git 背后的设计理念,才能比较准确地把握分叉合并的处理方法
git add
添加到暂存区里的每一个文件都会由line diff
得到该文件的增量补丁(按行对比)- 所有文件的增量补丁合并起来存入仓库就是一个
commit
,会生成一个SHA-1 Hash值作为 commit ID(40个十六进制数字,保证数据完整性) - 按时间线依次排列的一组提交记录形成一个
branch
- 合并分支:
- 如图,项目在A版本处开始分叉,形成了两个分支,要合成新版本
- 其实两条时间线最终版本是F和G,只需将F与A的差异部分、G和A的差异部分都放入工作区,解决冲突合并即可
- 根据上图,为了轻松跟踪代码的成长轨迹,需要让一段连续的工作在commit日志的时间线上呈现为一段独立的分支线段,只在关键节点处进行分支合并,流程基本如下:
- 克隆或同步最新的代码到本地存储库
- 为自己的工作创建一个分支,该分支应该只负责单一功能模块或代码模块的版本控制
- 在该分支上完成某单一功能模块或代码模块的开发工作
- 最后,将该分支合并到主分支
分支
- 创建分支,都是将当前分支分叉出一个分支,并签出(checkout)到工作区
# 命令创建分支 git checkout -b royspace # -b表示切换过去
- 切换分支
- 合并分支
- 假如要将
royspace
合并到master,那么首先确保当期工作区处于master
分支,git pull
一下 - 合并royspace分支之前master分支别人更新过,合并过程会因为有冲突(都修改了同一位置)而失败
- 这时royspace分支的代码已经合并到了当前工作区,只需在当前工作区里先解决冲突,然后提交到仓库即可(add&commit)
- 对应命令:
git merge mybranch
- 假如要将
- 快进式合并
- 会合并到一条时间线中
- 但是希望royspace分支为一段独立的分支线段
- 实现很简单:
git merge --no-ff mybranch
,在vscode中没有对应的菜单选项,终端(ctrl+`)执行
- 会合并到一条时间线中
- 小结,遵循如下基本流程
git clone https://DOMAIN_NAME/YOUR_NAME/REPO_NAME.git git pull # 克隆或同步 git checkout -b mybranch git branch git add FILES git commit -m "commit log" git checkout master git pull git merge --no-ff mybranch # 可能需要解决冲突 git push
- 这样就会形成围绕master主线的时间线
rebase
- 开发者的提交要尽量干净简单
- 开发者要把自己的代码修改按照功能拆分成一个个相对独立的提交,一个提交对应一个功能点
- 而且要在对应的 commit log message 里面描述清楚
- 在上一个场景的基础上,增加一步Git Rebase
- 即在 royspace 分支上完成自己的工作之后(本地),为了让 log 记录将来更容易回顾参考,用
git rebase
重新整理一下提交记录 - 不要通过rebase对任何已经提交到远程仓库中的commit进行修改
- 即在 royspace 分支上完成自己的工作之后(本地),为了让 log 记录将来更容易回顾参考,用
- 命令格式
git rebase -i [startpoint] [endpoint] # 一般只指定[startpoint], [endpoint]默认是当前分支的HEAD # 还是使用reset一样的参数指定 HEAD^ commit id # 重新整理HEAD之前的三个commit节点 git rebase -i HEAD^^ # 包含当前commit # 此时会打开一个文件
- 文件内容如下
pick 76e9ec9 测试rebase1 pick c93f5b8 测试rebase2 # Rebase 0bb4ec6..c93f5b8 onto 0bb4ec6 (2 commands) # # Commands: # p, pick <commit> = use commit # r, reword <commit> = use commit, but edit the commit message # e, edit <commit> = use commit, but stop for amending # s, squash <commit> = use commit, but meld into previous commit # f, fixup [-C | -c] <commit> = like "squash" but keep only the previous # commit's log message, unless -C is used, in which case # keep only this commit's message; -c is same as -C but # opens the editor # 这是个内置的小的vi编辑器
- 修改commit
- 删除某个版本
- 修改提交日志消息
- 退出后想撤销rebase
git rebase --abort # 注释掉pick c93f5b8 测试rebase2
--abort
参数报错:fatal: No rebase in progress?,无法撤销删除的commit,why?- 同时,至少要
HEAD^^
,因为如果HEAD^,删除commit之后无法定位 - 如果发生冲突,解决冲突后需要将修改后的文件存入暂存区(git add),再执行如下命令
# 和pull远程代码产生的冲突不一样 # 这的冲突是因为你分支的代码上存在相互依赖,rebase掉的那部分别的commit正好需要 git rebase --continue
- 一些其他场景可以使用
git rebase --help
查看官方文档,在本地!
- 小结
- rebase操作是为了让本地的log更清晰,对commit的修改操作
Fork / Pull request
- 上面的场景是在一个企业或者团队中,有良好的信任关系和开发规范
- 为了应对类似开源社区的鱼龙混杂的环境,需要Fork+ Pull request的协作开发工作流程
- 当更正别人仓库里的Bug或者向别人仓库里贡献代码时
- 先 fork(分叉) 别人的仓库,相当于拷贝一份;
- 做一些 bug fix或其他的代码贡献;
- 发起 Pull request 给原仓库;
- 原仓库的所有者 review Pull request,如果没有问题的话,就会 merge Pull request 到原仓库中
- 可以两人一组互相Fork对方的仓库,演练一下
声明
- 内容转载自公众号,感谢作者
- 以上场景是git基本用法,遇到特殊情况可能需要更深入了解