Git操作
摘要
- git 是常用的代码版本工具,另外一种为 svn
- 常见的 git 平台有 Github,GitLab,Coding.net,码云等
# Git 指令
# 基本的 Git 指令
git status # 查看状态
git diff # 查看详细变更
git add . # 把所有修改的东西增加,'.'表示全部文件
git checkout xxx # 还原到上个版本的状态,xxx为文件名
git commit -m "xxx" # 提交到本地服务器,xxx为记录文本
git push origin master # 提交到远程服务器
git clone # 克隆文件到本地
git pull origin xxx # 从远程服务器拉取代码
git branch # 查询分支
git checkout -b xxx # 新增分支/修改当前分支,xxx为分支名字
git merge xxx # 合并分支,xxx为分支名字
git fetch origin master# 将某个远程主机的更新,全部取回本地
git pull 和 git fetch 的区别
与 git pull 相比 git fetch 相当于是从远程获取最新版本到本地,但不会自动 merge。如果需要有选择的合并 git fetch 是更好的选择。效果相同时 git pull 将更为快捷。
# 实际常用的 git 命令
git clone
git branch -av #查看远程分支
git checkout -b [本地不存在的分支] [远程分支] #拉取远程分支
git status
git branch -D [本地分支] #删除本地分支
git branch -m #本地修改分支名
git pull
git merge [需要合并的分支]
git checkout -b [本地分支] #创建本地分支
git push origin [本地分支]:[远程不存在的分支]
然后配套 VSCode
来可视化使用 commit,stash,push
等指令更简便,另外 VSCode
中的暂存修改其实就是 git add
更多 Git 命令相关文章
# 使用 .gitinore 和 .gitkeep
在使用 git 进行代码版本管理的时候,会出现这么两种情况。
- 我希望默认不去上传某个文件夹及其文件
- 我希望要保留这个文件夹的存在,但不上传其中的文件
# 对于第一种情况,就可以使用 .gitinore
.gitinore 中写入需要忽略的文件夹和文件,以下是常用的示例。
.DS_Store
node_modules/
/dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
/test/unit/coverage/
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
# 对于第二种情况,就需要同时使用 .gitinore 和 .gitkeep
首先现在对应的文件夹,以 test
为例,创建一个 .gitkeep
文件
然后在 .gitinore 中进行说明
/test/*
!.gitkeep
如果还想在这个目录中上传其他文件,就直接接着写下去 !xxx
之类的就可以了
# 个人使用 git 开发项目流程
首先,你需要注册一个 git 云平台的账户(例如 github,coding.net),然后创建一个仓库,勾选创建 README.md,获取仓库($project) 的 http 链接($url)
在本地,你需要已经下载使用 git 需要的工具,配置好 git bash
git config --global user.name "yourName"
git config --global user.email "yourEmail@.com"
# 创建本地的仓库并上传
git clone $url
cd $project
touch test.txt
git add .
git commit -m "first commit"
git remote add origin $url
git push -u origin master
如果是已经存在的项目,比如说是使用 vue-cli 创建了一个完整的项目,然后想与远程仓库关联,则需要进行以下操作
git remote add origin $url
git pull origin master --allow-unrelated-histories
# 以下操作会直接覆盖远程的数据。
git push -u origin master -f
注意:
--allow-unrelated-histories
是为了强制将本地仓库与远程仓库进行关联- 另外,这里的远程仓库最好是一个空仓库
# 更新远程代码到本地仓库
git pull origin master
## git fetch origin master
## git merge origin/master
# 快速清理分支
在同一个仓库不断地进行需求开发后,会发现本地的分支和远程库的分支越来越多,这个时候就希望能快速清理掉这些“过期”分支了
- 清理本地仓库对应的远程分支比较容易,git 本身就提供了命令,能清理掉本地远程库中那些"实际远程分支已经不存在"的分支了
git remote prune origin
- 清理本地仓库对应的本地分支就比较麻烦一点,这里以删除 "feature" 作为开头的分支为例
git checkout master ## 切回 master 来执行以下的删除命令
git branch |grep 'feature' |xargs git branch -d ## 通过 linux 的 grep 和 xargs 命令来完成
# 多人协作使用 git 开发项目流程
多人协作的情况下,是已经有版本库了,这里依旧以 $url
代替。假设分配给你的分支为 dev
# 获取远程的代码
git clone $url
git fetch origin dev
git merge origin/dev
# 提交代码到远程的分支
git add .
git commit -m "myTest"
git push origin dev
另外,可以使用git branch
来查询分支,使用git pull
来更新本地分支。
注意
git checkout 是个很危险的命令,因为既可以用来回滚文件,也可以用来切换分支,谨慎使用
# 操作 git commit
- git log
- 只能查看各个 commit 的记录
- git rebase
- 在尚在开发的功能分支需要重新同步主分支时,可以使用
git merge --no-off
命令,但这样做会出现一个merge commit
,从而使commit
的路径节点出现分叉,后续也很难去理顺该仓库的发展变更。 - 此时使用
git rebase
就能够更好地去控制 commit 路径图。 - 以同步主分支为例,此时需要在当前的分支下,在命令行输入
git rebase master
即可。git rebase
此时的功能是- 将从原本主分支
checkout
出来的HEAD
指针移动到最新主分支的HEAD
上 - 再将本分支的
commit
再次回放,生成有同样commit
信息,但是 hash 不一样的commit
- 最后由开发者去处理冲突。这一串流程,被称为
变基
- 将从原本主分支
git rebase
也可以将多个 commit 合成一个 commit,需要使用git rebase -i [commit起始hash] [commit终点hash(默认是最新的commit hash)]
进入 vim 编辑模式来进行变更
- 在尚在开发的功能分支需要重新同步主分支时,可以使用
- git cherry-pick
- 能够直接将某个分支上的某个 commit 直接作为补丁打到目标分支中
- git reset
- 能够直接回滚到某个 commit,一般需要
git reset --hard [cmmitHash]
来强制回滚,其实际原理是将HEAD
指针往后移了
- 能够直接回滚到某个 commit,一般需要
- git revert
- 能够撤销某个 commit 并生成一个
revert commit
的commit
。其实际上是将HEAD
指针往前移,只是新的 commit 的内容和要 revert 的内容正好相反,能够抵消要被 revert 的内容 - 比如:我们 commit 了三个版本(版本一、版本二、 版本三),突然发现版本二不行(如:有 bug),想要撤销版本二,但又不想影响撤销版本三的提交,就可以用 git revert 命令来反做版本二,生成新的版本四,这个版本四里会保留版本三的内容,但撤销了版本二的内容。
- 能够撤销某个 commit 并生成一个
注意
这一小节只是简单的记录对 git commit 的篡改,更多的细节需要再手动实践才行,同时这类操作要求开发者能基本使用 vim 来编辑文件
特别注意
只能在自己使用的 feature 分支上进行 rebase 操作,不允许在集成分支上进行 rebase,因为这种操作会修改集成分支的历史记录。
# 使用 git stash
假设现在有一个需要 hotfix
的 bug
,但是处理的时候太着急,没有切出分支就直接在 master
上改了代码,此时我不能直接 push master
,但又不想再重写一遍刚刚的 hotfix
,于是 git stash
命令就派上用场了
git stash ## 临时存储到堆栈中
git checkout -b hotfix/xxx ## 切换出 hotfix 的分支
git stash pop ## 将刚刚存储的内容重写回来
这样我就得到了我想要的 hotfix
分支,于是可以愉快地提 merge request
合并代码了。git stash
命令的作用就是将目前还不想提交的但是已经修改的内容进行保存至堆栈中,后续可以在某个分支上恢复出堆栈中的内容
# 使用 git tag
为什么需要使用 tag?其实是为了给某些具有代表性的功能来标记发布节点。
git tag -l # 查看当前仓库的 tag 记录
git tag -a v1.0 -m "第一个版本" # 创建一个 v1.0 的 tag 标签,使用 -m 来附加记录
git push origin v1.0 # 推送该标签到远程仓库上,注意,直接使用 git push 不会传送标签
git push origin --tags # 一次性把本地的标签全推送到远程仓库
git tag -d [tagName] # 删除标签
如果以后有某个定制的修改是基于某个版本的话,就可以在对应的版本 tag 上 git checkout -b [branchName]
一个新的分支来继续开发
tag 和 branch 的不同点
- tag 对应某次 commit, 是一个点,是不可移动的。
- branch 对应一系列 commit,是很多点连成的一根线,有一个 HEAD 指针,是可以依靠 HEAD 指针移动的。
# 基本的上线和回滚流程
即:将自己写好的项目上线和回滚的基本流程。
# 上线流程要点
- 将测试完成的代码提交到
git
版本库的master
分支 - 将当前服务器的代码全部打包并记录版本号,备份
- 将
master
分支的代码提交覆盖到线上服务器,生成新版本号
# 回滚流程要点
- 将当前服务器的代码打包并记录版本号(tag 或者 release),备份
- 将备份的上一个版本号解压,覆盖到线上服务器,并生成新的版本号
附加 linux 基本命令
ssh # 登录
mkdir # 创建文件夹
ls # 查看目录下的文件
ll # 以详细信息的方式来查看目录下的文件
pwd # 查看当前目录
cd .. # 返回上层目录
rm a.js # 删除 a.js
rm -rf a # 删除文件夹名为 a 的文件夹
vi # 使用 vim 编辑器,可以用于创建文件,修改和查看文件内容
cat # 查看文件内容
head -n 2 a.js # 查看文件的头部内容,即文件的前两行
tail -n 2 a.js # 查看文件的尾部内容,,即文件的最后两行
cp # 拷贝
mv # 移动文件夹
# 生产线流程
# git-flow
git-flow,或者说 github-flow
,这是我在工作后认识的一种 git 的流程方式,流程虽然复杂,但确实是最佳实践,适合多人协作敏捷开发模式
主要将每个环节细化,以 branch 为主,分为
- master
- develop
- release
- feature
- bugfix
- hotfix
按照 git-flow
流程可以更好地把控每一个功能点和版本管理。另外配合 standard-version 和 commitlint 使用后会让团队之间的开发更加规范
注意
使用 git-flow 并不是必须的。当积攒了一定的使用经验后,很多团队会不再需要它了。当你能正确地理解工作流程的基本组成部分和目标的之后,你完全可以定义一个属于你自己的工作流程。
# gitlab-flow
gitlab-flow 和 git-flow
不同,流程上会更加简便一些,简单来说,所以分支会留下一个 master 分支和无数个 feature 分支,然后 feature 分支测试通过后直接 merge 到 master 分支中
# 个人认为合适的流程
在 git-flow
和 gitlab-flow
的基础上,我整理出适合个人的流程习惯
首先将分支整理成四大分支,然后分系统的稳定程度进行处理
分支名 | 功能说明 |
---|---|
master | Prod 专用分支 |
uat | UAT 专用分支 |
sit | SIT 专用分支 |
develop | DEV 专用分支,该分支不能合并到其他分支中 |
系统稳定前
- 除了 develop 上现有功能外,之后的新功能统一从 master 上拉取,以单独(feature)功能分支存在,bugfix 也在当前功能分支,保持该功能分支不受影响,保证可以提前上线
- 原先混在在一起的功能,优先级较高的进行 bugfix,尽快上线,优先级较低的可以暂时注释掉对应模块的代码,先将功能进行冻结,等稳定后再开发
- 不允许进行重构,一切以版本稳定为优先
- 这一阶段一定要快,2 周内使版本稳定
当 master = uat = sit = develop
时表示版本稳定
系统稳定后
- 新功能分支全部 pull master 分支
- 不允许直接在 uat,sit 等分支直接修改,一旦有修改操作就进行 reset
- 新功能统一从 master 上拉取,以单独功能分支存在,bugfix 也在当前功能分支,保持该功能分支不受影响,将之前冻结的功能分别拉出对应的功能分支
- develop 分支可以进行多个功能分支合并再 dev 环境上测试
- 发布 sit 将功能分支合并到 sit,发布 uat 则合并到 uat,发布 prod 则合并到 master
- 重构部分的修改,或者公共部分的修改,需要单独拉出一个 refactor 分支,该分支不直接合并到三大分支中,而是从 master 分支拉出一条功能分支中来处理合并逻辑,再由功能分支合并到三大分支
- 对应的功能分支一旦合并到 master 发布后,一周内无异常,线上删除该功能分支,分支负责人本地保留
- hotfix 分支也是直接从 master 拉出,根据情况紧急程度进行分支合并
另外关于回滚,建议不直接进行代码回顾,而是搭建 jenkins + docker
直接重新回退到上个镜像的包即可,或者可以使用其他 CI/CD
的流程。
关于功能混杂的问题,必须与产品,测试沟通好,以系统稳定为优先,不然会一直在不稳定的循环中度过,并且如果功能互相依赖,可以将多个互相依赖的分支再整合成一个完整的功能分支。另外如果因为某些问题的存在,可以打出一个单独的 release 分支来进行特殊处理。每一次进行大版本的升级需要打 tag
弊端
注意!这套 Git 流程是有一个弊端的,特此声明!
- 会出现
master,uat,sit,dev
四条环境主分支,但是以master
为主 - 从
master
checkout 出来的分支,在其他主分支的默认 merge 处理中,会出现Merge xxx into master
的commit
- 合并不同分支到环境分支时出现的合并冲突,其解决方案可能不一致,所以需要单独再 checkout 一条分支进行处理,严重时会出现每上一个环境就需要解决一次冲突的情况