重构谷粒商城07:Git一小时快速起飞指南
重构谷粒商城07:Git一小时快速起飞指南
前言:这个系列将使用最前沿的cursor作为辅助编程工具,来快速开发一些基础的编程项目。目的是为了在真实项目中,帮助初级程序员快速进阶,以最快的速度,效率,快速进阶到中高阶程序员。
本项目将基于谷粒商城项目,并且对谷粒商城项目进行二次重构,使其满足最新的主流技术栈要求。
上一篇文章我们快速、精准、全面的学习了maven。准备好项目所需要的maven环境。这篇文章,我们准备代码开发所需的代码托管工具Git,全面介绍Git的使用,Github等远程托管平台的使用。结合了生产中实战的许多技巧,经验。绝对干货满满,无论小白还是老手,都可以有所收获。
1、什么是git
Git 是一种分布式版本控制系统,它能够跟踪文件的变化、管理代码的历史,并允许多人协作开发。Git 最早由 Linus Torvalds(Linux 操作系统的创建者)于 2005 年开发,目的是为了更高效地管理 Linux 内核的开发。

没有git,要保留文件的多个版本,需要这么玩。

现在主流的版本系统有两类:集中式、分布式。

集中式版本控制系统是指所有的版本历史和版本数据都存储在一个中央服务器上,开发者从这个服务器获取代码并进行修改。由于所有版本信息都在一个中央服务器上,管理和维护相对简单。但如果中央服务器发生故障或不可用,所有的开发者都无法获取代码或进行提交。
分布式版本控制系统允许每个开发者的本地仓库都有完整的项目历史记录,并且可以独立于中央仓库进行开发。开发者之间通过推送和拉取操作进行协作。开发者可以在没有网络连接的情况下进行版本控制操作,所有历史记录都保存在本地。可以通过推送和拉取操作方便地与其他开发者进行协作,无需频繁访问中央服务器。
2、git的安装和配置
官网下载即可。https://git-scm.com/?hl=zh-cn

使用git第一步,配置用户名和邮箱,这样提交代码时,才可以知道是谁提交的。
**通过git bash(windows电脑)打开我们自己任意git工作目录。**执行

查看确认下用户名和邮箱。
C:\Users\半旧>git config --global --list
user.name=wangzhou
user.email=wangzhou@cstarway.com
user.password=i2345
credential.http://192.168.100.119:3080.provider=generic
credential.https://gitee.com.provider=generic
3、新建仓库
在 Git 中,仓库(Repository)是存储项目文件及其版本历史记录的地方。仓库可以包含项目的所有文件、目录结构、提交记录、分支、标签以及其他版本控制相关的数据。Git 仓库有两个主要类型:本地仓库和远程仓库。
仓库就可以理解成为一个文件夹。本地新建仓库有两种方式,方式1,在本地创建新仓库。方式2,从远程拉取一个仓库。

新建目录learngit。git init就可以将其初始化为一个仓库。
C:\Users\半旧\Desktop\wz\learngit>git init
Initialized empty Git repository in C:/Users/半旧/Desktop/wz/learngit/.git/
从远程仓库拉代码。
git clone https://gitcode.com/gh_mirrors/avatar/avatar.git
4、工作区域和工作状态

工作区是你直接编辑和修改文件的地方,这里是你实际操作代码的环境。当你对文件进行修改时,它们会出现在工作区,但这些修改还没有被 Git 跟踪。
暂存区是一个临时存放区,用来保存你希望在下次提交时包含的文件更改。你可以通过 git add 命令把工作区的修改添加到暂存区。暂存区的内容是 Git 在提交前的预备阶段。
本地仓库是 Git 用来存储所有版本历史的地方。当你执行 git commit 命令时,暂存区的更改会被保存到本地仓库,形成一个新的提交记录。所有的提交信息都会存储在本地仓库中。
思考:为何在工作区和本地仓库中要有一个暂存区?
暂存区提供了一个缓冲区,可以在提交之前选择性地组织修改。通过暂存区,你可以决定哪些修改要被提交,而哪些可以暂时忽略。这对于确保提交的代码干净且有条理非常重要,尤其是当你同时进行多个任务时。比如,你在一个文件中修复了一个 bug,但在另一个文件中做了一些实验性的修改,暂存区允许你只提交 bug 修复部分的文件A,而不提交实验性代码文件B。
不知道你懂了没有,没懂后面自己工作中慢慢体会吧。
5、添加和提交文件
其实就是这几个命令。

这里补充下git中文件的状态有哪些?
未跟踪(Untracked):指的是新添加到工作区的文件,Git 并没有开始跟踪它们,也就是这些文件从未通过 git add 命令被添加到暂存区。
已修改(Modified):指的是文件在工作区中已经被修改,但这些修改还没有被添加到暂存区。文件内容已经改变,但 Git 还没有准备好将这些修改提交到版本历史中。
已暂存(Staged):指的是文件已经通过 git add 命令被添加到暂存区,准备提交。它的变化已经标记为将要包含在下次提交中的内容。
已提交(Committed):指的是文件的修改已经通过 git commit 命令被提交到本地仓库,成为版本历史的一部分。
已删除(Deleted):指的是文件已经从工作区中删除,但删除操作还没有被提交。如果删除的文件已经添加到暂存区,它就变成了已暂存删除状态。
已合并(Merged):指的是在进行 Git 合并操作时,文件已经成功合并并且没有冲突。如果在合并过程中出现冲突,Git 会要求你手动解决
来一个demo。git仓库最开始是空空的。新建一个文件,可以看到,它是未跟踪状态。
半旧@banjiu MINGW64 ~/Desktop/wz/learngit (master)
$ git status
On branch masterNo commits yetnothing to commit (create/copy files and use "git add" to track)半旧@banjiu MINGW64 ~/Desktop/wz/learngit (master)
$ echo "这是第一个文件" > file1.txt半旧@banjiu MINGW64 ~/Desktop/wz/learngit (master)
$ git status
On branch masterNo commits yetUntracked files:(use "git add <file>..." to include in what will be committed)file1.txtnothing added to commit but untracked files present (use "git add" to track)
添加到暂存区。
半旧@banjiu MINGW64 ~/Desktop/wz/learngit (master)
$ git add file1.txt
warning: in the working copy of 'file1.txt', LF will be replaced by CRLF the next time Git touches it半旧@banjiu MINGW64 ~/Desktop/wz/learngit (master)
$ git status
On branch masterNo commits yetChanges to be committed:(use "git rm --cached <file>..." to unstage)new file: file1.txt
接下来演示下,git commit,将暂存区的文件提交到本地仓库。
半旧@banjiu MINGW64 ~/Desktop/wz/learngit (master)
$ echo "这是第二个文件" > file2.txt半旧@banjiu MINGW64 ~/Desktop/wz/learngit (master)
$ git status
On branch masterNo commits yetChanges to be committed:(use "git rm --cached <file>..." to unstage)new file: file1.txtUntracked files:(use "git add <file>..." to include in what will be committed)file2.txt半旧@banjiu MINGW64 ~/Desktop/wz/learngit (master)
$ git commit -m "第一次提交"、
[master (root-commit) 59ee545] 第一次提交1 file changed, 1 insertion(+)create mode 100644 file1.txt半旧@banjiu MINGW64 ~/Desktop/wz/learngit (master)
$ git status
On branch master
Untracked files:(use "git add <file>..." to include in what will be committed)file2.txtnothing added to commit but untracked files present (use "git add" to track)
git add可以配合通配符使用,批量添加文件到暂存区。git add .把所有修改提交到暂存区。
git log可以查看提交记录。

可以使用下面命令,来将文件添加到暂存区,并提交
git commit -am "xxx"
6、git reset回退版本
git reset 是 Git 中一个非常强大的命令,用于重置当前的 HEAD 到指定的状态。根据不同的用途,git reset 提供了三种模式,每种模式的效果不同:
–soft: 使用 git reset --soft 会将 HEAD 指向指定的提交,但保留暂存区(Staging Area)和工作区的修改。这意味着你重置了提交历史,但暂存区的文件仍然是那些提交中的内容,可以继续修改或者进行新的提交。
-mixed(默认模式): 使用 git reset --mixed 或者直接使用 git reset(如果没有指定模式,默认就是 --mixed)时,会将 HEAD 指向指定的提交,并且重置暂存区的内容,但保留工作区的修改。这意味着你撤销了提交和暂存的内容,但修改仍然存在于工作区,你可以决定是否将其再次添加到暂存区。
–hard: 使用 git reset --hard 会将 HEAD、暂存区和工作区全部重置到指定的提交状态。所有的修改,包括暂存区和工作区的修改都会丢失,因此这个命令要小心使用。

来具体操作下。
初始化一个新仓库。

新建三个文件分别加到暂存区,分三次提交。

将仓库复制三个副本,用于三个不同模式操作的demo。

第一种模式,soft,工作区和暂存区的文件都还存在。

第二种模式,mixed,暂存区的内容不保留,工作区的内容还保留。

第三种模式,hard。彻底把所有的内容在工作区和暂存区清空。

前两个模式一般用于,本地有多次提交,你认为没有必要分成多次,可以先回退,重新合并成为一次提交!
一般很少建议使用第三种模式,除非你真的打算放弃所有的代码。重头来过。如果误操作,使用了第三种模式。可以使用git reflog查看操作的历史记录。然后进行回滚。


7.git diff查看差异

举个栗子,比较工作区和暂存区。

比较工作区和版本库的内容。

比较暂存区和版本库的内容。可以看到没有差别。

执行一次提交后,会怎么样?答案是没有不同的地方了。

还可以用来比较两个提交版本之间的差异。
git diff加上两个版本的commit id就可以。

还可以使用HEAD表示最新一次提交,HEAD^或者HEAD~表示HEAD前一次提交。下面的玩法都ok。

可以指定只查看某个文件的差异。

还可以加上分支名,比较两个分支的差异。
8、使用git rm删除文件
如何从版本库中删除文件?

可以用git rm来一步到位。

最后,无论哪种方式,都还要记得git commit。这样才能更新版本库。否则,删除的文件还存在于版本库中。

9、.gitignore文件
.gitignore 文件是一个文本文件,用于告诉 Git 哪些文件或文件夹应该被 Git 忽略,不进行版本控制。通常,这些是一些不需要被版本控制的文件,如日志文件、编译后的代码、依赖库文件等。
在 .gitignore 文件中,你可以列出文件或目录的路径,Git 会根据这些规则自动忽略对应的文件。例如:
*.log会忽略所有.log文件。node_modules/会忽略node_modules文件夹及其内容。*.class会忽略所有的.class文件。

一些常见的 .gitignore 例子包括:
# 忽略编译文件
*.o
*.exe# 忽略日志文件
*.log# 忽略依赖库
node_modules/# 忽略系统文件
.DS_Store
Thumbs.db
这样设置后,Git 会根据 .gitignore 的规则忽略文件,保持版本控制系统的干净和高效。
注意:.gitignore中的配置的文件生效有一个前提,就是文件没有被添加到版本库中。否则,要先将文件从版本库中删除掉,才不会追踪该文件的版本变化。可以使用如下命令。

还可以忽略文件夹(另:文件夹中要有文件,git才会将其提交到版本库中)

匹配规则。


github上可以找到常见的各个语言的gitignore模板。

10、远程仓库github
以GitHub为例。从github官网注册账户。


搜索你想要的仓库。

你就可以直接下载远程仓库的代码了。

新建一个仓库也很简单。


创建好仓库后,通过ssh方式clone仓库。一般我们就是用这种方式,所以只讲它。

按照提示需要填写密钥。
先在本机电脑生成密钥。


查看公钥文件,复制后粘贴到GitHub密钥配置页面。
cat id_rsa.pub

再回到之前创建的仓库,就可以git clone代码了。


接下来就可以在远程仓库和本地仓库之间同步代码了。


刷新下远程仓库,就可以看到文件内容了。

还可以直接在远程仓库创建一个新的仓库。
我们这里已经创建好了。

将远程仓库关联到本地仓库。

本地查看下对应的远程仓库信息。可以看到远程仓库的别名是origin ,地址是git@github.com:banjiubanjiu/gitsdemo.git

将本地 Git 仓库的主分支(默认分支)名称修改为 main,并将该分支推送到远程仓库。

注:完整的推送命令如下,因为本地分支名是main,所以可以省略。
git push -u origin main:main
补充:git clone 和 git remote add有什么区别?

可以使用git pull(自动合并代码)或者git fetch(手动合并代码)拉取远程仓库代码。如果有远程代码与本地仓库代码冲突,需要解决冲突。这里后面讲分支时再进行介绍。
11、gitee的介绍
gitee是国内的远程仓库,和github很类似,如果是国内部署项目,考虑避免github网速慢的问题,可以使用。这里不再赘述。

12、gitlab的使用
12.1 gitlab部署
gitlab是私有化部署的仓库,对信息安全要求高,可以使用。推荐使用docker部署。
编写compose.yaml文件如下,供参考。
version: '3.6'
services:web:image: 'gitlab/gitlab-ee:17.2.1-ee.0'restart: alwayshostname: 'gitlab.xxx.com'environment:TZ: Asia/ShanghaiGITLAB_OMNIBUS_CONFIG: |external_url 'http://192.168.100.121:3080'gitlab_rails['gitlab_shell_ssh_port'] = 222shm_size: '512m'ports:- '3080:80'- '3022:22'- '3443:443'- '35432:5432'deploy:resources:reservations:memory: 4Gvolumes:- '/volume1/docker/gitlab/config:/etc/gitlab'- '/volume1/docker/gitlab/logs:/var/log/gitlab'- '/volume1/docker/gitlab/data:/var/opt/gitlab'configs:- source: gitlabtarget: /omnibus_config.rbsecrets:- gitlab_passwordgitlab-runner:image: gitlab/gitlab-runner:alpineenvironment:TZ: Asia/Shanghaideploy:mode: replicatedreplicas: 4
configs:gitlab:file: /volume1/docker/gitlab/config/gitlab.rb
secrets:gitlab_root_password:file: /volume1/docker/gitlab/config/root_password.txt
记得替换上面的ip地址、密码等,端口如有需要也可以替换,配置中用于挂载的本地目录需要自己手工建立。
创建gitlab.rb 文件:
external_url 'https://my.domain.com/'
gitlab_rails['initial_root_password'] = File.read('/run/secrets/gitlab_root_password').gsub("\n", "")
创建 root_password.txt 文件:
password
运行。
docker compose up -d
其PJ方式如下。仅供学习使用。
进入容器目录:
docker exec -it gitlab-web-1 bash
创建文件
vi /etc/gitlab/license.rb
文件内容如下
#########仅供学习与交流,不得用于生产环境
require "openssl"
require "gitlab/license"key_pair = OpenSSL::PKey::RSA.generate(2048)
File.open("license_key", "w") { |f| f.write(key_pair.to_pem) }public_key = key_pair.public_key
File.open("license_key.pub", "w") { |f| f.write(public_key.to_pem) }private_key = OpenSSL::PKey::RSA.new File.read("license_key")
Gitlab::License.encryption_key = private_keylicense = Gitlab::License.new
license.licensee = {
"Name" => "none",
"Company" => "none",
"Email" => "example@test.com",
}
license.starts_at = Date.new(2020, 1, 1) # 开始时间
license.expires_at = Date.new(2050, 1, 1) # 结束时间
license.notify_admins_at = Date.new(2049, 12, 1)
license.notify_users_at = Date.new(2049, 12, 1)
license.block_changes_at = Date.new(2050, 1, 1)
license.restrictions = {
active_user_count: 10000,
}puts "License:"
puts licensedata = license.export
puts "Exported license:"
puts data
File.open("GitLabBV.gitlab-license", "w") { |f| f.write(data) }public_key = OpenSSL::PKey::RSA.new File.read("license_key.pub")
Gitlab::License.encryption_key = public_keydata = File.read("GitLabBV.gitlab-license")
$license = Gitlab::License.import(data)puts "Imported license:"
puts $licenseunless $license
raise "The license is invalid."
endif $license.restricted?(:active_user_count)
active_user_count = 10000
if active_user_count > $license.restrictions[:active_user_count]raise "The active user count exceeds the allowed amount!"
end
endif $license.notify_admins?
puts "The license is due to expire on #{$license.expires_at}."
endif $license.notify_users?
puts "The license is due to expire on #{$license.expires_at}."
endmodule Gitlab
class GitAccessdef check(cmd, changes = nil)if $license.block_changes?return build_status_object(false, "License expired")endend
end
endputs "This instance of GitLab Enterprise Edition is licensed to:"
$license.licensee.each do |key, value|
puts "#{key}: #{value}"
endif $license.expired?
puts "The license expired on #{$license.expires_at}"
elsif $license.will_expire?
puts "The license will expire on #{$license.expires_at}"
else
puts "The license will never expire."
end
生成证书 1
ruby license.rb
生成 GitLabBV.gitlab-license license_key license_key.pub 这三个文件。
替换默认公钥\
shell
cp -f license_key.pub /opt/gitlab/embedded/service/gitlab-rails/.license_encryption_key.pub
升级到 ULTIMATE 版本修改文件 /opt/gitlab/embedded/service/gitlab-rails/ee/app/models/license.rb 替换对应的这一行文本 restricted_attr(:plan).presence || STARTER_PLAN
为 restricted_attr(:plan).presence || ULTIMATE_PLAN
vi /opt/gitlab/embedded/service/gitlab-rails/ee/app/models/license.rb
重新配置 GitLabEE
gitlab-ctl reconfigure
gitlab-ctl restart
通过在浏览器中访问你的 IP 地址或主机名,后面加上你在端口映射中配置的端口号来访问 GitLab-EE主页。

这样就已经成功安装了 GitLab-EE。
导入许可证登录 gitlab 后台,管理中心-> 设置-> 通用-> 许可证文件,导入 GitLabBV.gitlab-license 可以选择 cat GitLabBV.gitlab-license 打印出文件内容后,把密钥复制后使用密钥文本,而不是上传文件



最终效果:

这里额外补充下我在实际使用gitlab时的一些私有的问题,最佳实践等。不需要的朋友请自行跳过,需要使用的建议收藏。读到就是赚到。
12.2 修改 GitLabHTTPurl【可跳】
进入 menu->admin->settings->general->Visibility and access controls->Custom Git clone URL for HTTP(S)
在这个文本框中填入 gitlab 的网址和端口号并点击保存即可生效
12.3 502错误【可跳】
容器rebuild之后,直接访问gitlab会出现502错误:

解决方法如下:
1、通过终端访问工具(如KiTTY)登录群晖的操作系统命令行,sudo -i获得超级用户权限。
2、运行 docker exec -it gitlab-web-1 bash
3、重新配置gitlab:gitlab-ctl reconfigure,gitlab-ctl restart。
4、用浏览器重新访问gitlab。注意给足时间,gitlab重启动需要花费的时间较长。
12.4 GitLab 头像无法加载问题【可跳】
可能出现用户的头像图片无法正常加载问题。
修改 gitlab.rb
添加以下内容,并重新载入配置
gitlab_rails['gravatar_plain_url'] = 'http://sdn.geekzu.org/avatar/%{hash}?s=%{size}&d=identicon'
gitlab_rails['gravatar_ssl_url'] = 'https://sdn.geekzu.org/avatar/%{hash}?s=%{size}&d=identicon'
#重启命令
gitlab-ctl reconfigure
gitlab-ctl restart
12.5 忘记登录密码【可跳】
在 Gitlab-ce 容器的 终端 新开bash 命令界面:
[root@xxxx ~] curl http://192.168.100.xxx:3080/api/v4/users?username=xxxx # 获取登录账号的 记录信息
[root@xxxx log] cd ~ # 切换到当前用户目录
[root@xxxx ~] gitlab-rails console production # 进入 rails 命令环境
=> \c gitlabhq_production # 切换当前数据库为 gitlabhq_production的数据库
=> select id , username from users where username ='root'; # 返回 root 登录账号的 id,username 字段值
=> u = User.where(id: <account id value>).first; # 查User Modle对象 ,条件是id = root 账号的id 值 的 结果记录,并将此对象赋值给 变量u
=> u.password='12341234'; # 重新对 root 的对象设置新密码
=> u.password_comfirmation='12341234'; # 重新对 root 的对象确认新密码
=> u.save! # 已经改变属性root 的 User 对象 保存到数据库
=> quit
12.6 没有root用户【可跳】
gitlab-rails console user = User.new( username: 'root', email: 'admin@example.com', # 你可以使用其他有效的电子邮件地址
name: 'Administrator', password: 'newpassword', password_confirmation: 'newpassword', admin: true ) user.skip_confirmation!
# 如果你的 GitLab 实例需要确认邮件,请添加这一行
user.save!
12.7 URL无法访问【可跳】
external_url设置不对引起的
确保在 GitLab 配置文件 (/etc/gitlab/gitlab.rb) 中正确设置了外部 URL。
external_url 'http://your.correct.url' gitlab-ctl reconfigure gitlab-ctl restart
13、GUI工具
Github-desktop,GitHub官方出品,功能比较简单,只支持GitHub。

sourcetree,功能比较强。
SourceTree 是一个由 Atlassian(同样开发了 Jira 和 Bitbucket 等工具)开发的免费 Git 和 Mercurial 图形化客户端。它为开发者提供了一种简单而直观的方式来管理 Git 或 Mercurial 仓库,适合那些不习惯使用命令行操作的开发者,尤其是想要通过图形界面来管理代码版本和仓库操作的人。

下载地址:https://confluence.atlassian.com/get-started-with-sourcetree/install-sourcetree-847359094.html
GitKraken,商业产品,免费版功能有限制,提供git和jira继承。功能比较强大。可以在vscode中使用,推荐从官网下载下。

如果不想下载上述工具,可以使用命令行命令查看提交历史记录图。

14、在VScode中使用git

15、分支简介和基本操作
分支很好理解,分支 就是代码的“分岔口”,你可以从主线(比如 main 或 master)上创建一个新的开发分支,在这个分支上进行开发,不会影响主线上的代码。
当你在一个分支上完成开发后,可以将这个分支的更改合并回主分支,保持代码的整洁和稳定。

创建新分支
git branch xxx
查看当前分支
git branch
切换分支
git checkout xxx
因为这个命令还可以用来恢复文件,分支名与文件名相同时,可能造成歧义。新版git使用switch代替它。
git switch xxx
将其它分支的代码合并到当前分支.下面案例就是将dev分支中的代码合并到main分支。

合并后,可以在ide中看到可视化的分支合并图,也可以使用命令命令查看。

删除分支。-d参数会检测是否已经合并,如果还没有合并到其它分支,不会允许删除。
git branch -d xxx
强制删除分支。
git branch -D xxx
16、解决合并冲突
如果两个分支修改了同一个文件的同一行代码,在合并分支时,git就不知道应该保留哪一个修改了。这就是合并冲突。
下面就是一个合并冲突的提示。

解决方法:
可以使用git status,查看具有冲突的文件,再使用git diff来查看具体的冲突。

手工修改这个文件,留下你想要的内容,重新提交即可。
17、rebase变基
之前我们使用的合并分支分发是,git merge,在哪个分支执行,代码就会被合并到哪个分支。这也就意味着,我们需要先切换到这个分支,只能在这个分支上执行命令。
执行合并后,其提交的分支记录图如下。当前分支会新增一个提交记录,这个记录就是我们合并时产生的。

使用git rebase,变基操作,可以在任意分支上执行。
举个例子,执行rebase前。

在dev分支上执行rebase操作后,dev上的提交记录,都会变基到main分支上。

而在main分支上执行rebase操作后,会把main分支上的提交记录,全部变基到dev分支上。

所谓变基,就是从分叉点,将整个分支都移动到目标分支的最新提交记录后面。就和嫁接一样。

对比下rebase和merge


18、分支恢复
恢复一个已经删除的分支,可以使用如下命令。

上面的图形化界面,就是我们之前介绍过的GitKraken,可以自行去官网下载。
你也可以直接使用命令行命令代替GitKraken,查看提交记录图。

上面命令很长,可以自定义别名。

19、分支管理与工作流模型
工作流模型(Workflow Model)是指通过图形化、抽象的方式来表示和管理任务、活动以及信息流的过程模型。在工作流模型中,每个任务和活动被视为一个节点,它们之间的流动关系则表现为边。通过这种方式,可以清晰地理解任务的执行顺序、并行处理和决策路径等,帮助企业或组织高效地进行任务分配、监控和优化。
19.1 Gitflow工作流模型
Gitflow是一个常见的工作流模型。

GitFlow 是一种基于 Git 的分支管理模型,旨在帮助团队高效地管理版本控制和发布过程。它通过规定分支的使用和命名规范,使得开发、测试、发布等工作流程更加清晰有序。GitFlow 由 Vincent Driessen 在 2010 年提出,是一种非常流行的 Git 工作流。
GitFlow 模型的核心分支:
GitFlow 中有几个关键的分支,每个分支有明确的目的和使用规则:
- master:
master分支是 GitFlow 中的主要分支,代表着生产环境中的代码。- 该分支上的代码是稳定的,可以随时发布到生产环境。
- 每次发布新的版本时,都会在
master分支上创建一个新的标签(Tag)。
- develop:
develop分支是用于日常开发的主分支,开发人员在这里合并他们的功能分支(feature branches)。- 它包含了下一版本的开发代码,相对
master分支,它是一个集成分支。 - 开发完成后,
develop分支的代码会合并回master分支,并发布一个新的版本。
GitFlow 模型的辅助分支:
除了 master 和 develop 这两个核心分支,GitFlow 还引入了以下几种辅助分支来处理不同的任务:
- feature(功能分支):
- 用于开发新功能或改进现有功能。
- 通常从
develop分支创建,并且开发完成后合并回develop分支。 - 分支命名通常采用
feature/feature-name格式。
- release(发布分支):
- 用于准备一个新的发布版本。
- 从
develop分支创建,当开发阶段完成,准备发布时,会创建一个发布分支进行最后的测试和修复。 - 发布分支完成后,会合并回
master(发布代码)和develop(保证开发代码同步)。 - 分支命名通常采用
release/version-number格式。
- hotfix(修复分支):
- 用于紧急修复生产环境中的问题。
- 从
master分支创建,修复问题后,合并回master和develop,确保代码在发布和开发分支中都得到同步更新。 - 分支命名通常采用
hotfix/fix-name格式
19.2 Github Flow模型
Gitflow有点小复杂,一般适用于团队水平适中,有一定的开发流程和规范的团队。很多人在实际开发中,并不会很严格的遵循。
很多团队会选择Github Flow模型,适用于开发技术水平比较高的团队或者开源项目。

GitHub Flow 是 GitHub 推出的一个简单而高效的 Git 工作流,特别适用于持续部署和频繁发布的开发环境。它是一种轻量级的分支模型,相对于 GitFlow 来说更加简洁灵活,适合快速迭代和敏捷开发。
GitHub Flow 主要特点是鼓励频繁地将代码变更合并到 main(或 master)分支上,并通过 Pull Request 进行代码审查和集成。它强调团队成员间的协作,并利用 GitHub 的协作功能来确保代码质量。
GitHub Flow 的核心原则:
- 单一的
main分支:所有的代码变更最终都会合并到main分支,这是生产环境的代码基础。 - 通过 PR 进行代码审查:所有的代码变更都需要通过 PR 进行审查,确保团队成员之间的合作和代码质量。
- 持续集成和持续部署:代码合并到
main分支后,通常会触发自动化的测试和部署流程,确保代码随时可以上线。
GitHub Flow 的工作流程:
GitHub Flow 的基本步骤如下:
- 创建一个功能分支(Feature Branch):
- 每个新任务(如新功能或修复)都应该从
main(或master)分支创建一个独立的功能分支。 - 功能分支应该有描述性名称,通常采用
feature/或bugfix/等前缀。 - 示例:
feature/add-user-authentication或bugfix/fix-login-issue
- 每个新任务(如新功能或修复)都应该从
- 进行开发和提交(Commit):
- 在功能分支上进行开发,完成后将更改提交到本地仓库,并推送到远程仓库。
- 确保提交信息清晰、简洁,并且聚焦于一个小的功能点或修复。
- 创建 Pull Request:
- 在 GitHub 上,提交完成后,创建一个 Pull Request(PR)来请求将功能分支的代码合并到
main分支。 - PR 是代码审查的核心,可以让团队成员进行代码审查、讨论和建议改进。
- PR 可以包含对代码的描述和更改的具体说明,以便团队成员理解该更改的目的。
- 在 GitHub 上,提交完成后,创建一个 Pull Request(PR)来请求将功能分支的代码合并到
- 代码审查和讨论:
- 团队成员会对 PR 进行审查,提出修改建议或确认没有问题。
- 开发者根据反馈进行必要的更改,并在功能分支上继续提交。
- 如果需要,PR 会进行多次迭代,直到审查通过。
- 合并 PR 到
main:- 一旦 PR 审查通过且没有冲突,就可以将其合并到
main分支。 - 合并时可以选择直接合并(merge)或者通过 “Squash and Merge”(合并并压缩提交)来优化提交记录。
- 一旦 PR 审查通过且没有冲突,就可以将其合并到
- 部署到生产环境:
- 一旦代码合并到
main分支,通常会触发持续集成(CI)和持续部署(CD)流程,自动将代码部署到生产环境。 - 因为 GitHub Flow 强调持续集成和频繁部署,所以新的更改通常会尽快上线。
- 一旦代码合并到
相关文章:
重构谷粒商城07:Git一小时快速起飞指南
重构谷粒商城07:Git一小时快速起飞指南 前言:这个系列将使用最前沿的cursor作为辅助编程工具,来快速开发一些基础的编程项目。目的是为了在真实项目中,帮助初级程序员快速进阶,以最快的速度,效率ÿ…...
设计模式教程:命令模式(Command Pattern)
1. 什么是命令模式? 命令模式(Command Pattern)是一种行为型设计模式。它将请求封装成一个对象,从而使你能够用不同的请求、队列和日志请求以及支持可撤销操作。 简单来说,命令模式通过把请求封装成对象的方式解耦了…...
Qt中使用QPdfWriter类结合QPainter类绘制并输出PDF文件
一.类的介绍 1.QPdfWriter介绍 Qt中提供了一个直接可以处理PDF的类,这就是QPdfWriter类。 (1)PDF文件生成 支持创建新的PDF文件或覆盖已有文件,通过构造函数直接绑定文件路径或QFile对象; 默认生成矢量图形PDF&#…...
Android开发-深入解析Android中的AIDL及其应用场景
深入解析 Android 中的 AIDL 及其应用场景 1. 前言2. AIDL 的核心概念3. AIDL 的实现步骤3.1. 定义 AIDL 接口文件3.2. 实现服务端(Service)3.3. 客户端绑定与调用 4. AIDL 的典型应用场景4.1. 多进程应用4.2. 与系统服务交互4.3. 高性能 IPC4.4. 跨应用…...
RT-Thread+STM32L475VET6实现红外遥控实验
文章目录 前言一、板载资源介绍二、具体步骤1. 确定红外接收头引脚编号2. 下载infrared软件包3. 配置infrared软件包4. 打开STM32CubeMX进行相关配置4.1 使用外部高速时钟,并修改时钟树4.2 打开定时器16(定时器根据自己需求调整)4.3 打开串口4.4 生成工程 5. 打开HW…...
【机器学习】衡量线性回归算法最好的指标:R Squared
衡量线性回归算法最好的指标:R Squared 一、摘要二、回归算法评价指标与R Squared指标介绍三、R Squared的编程实践 一、摘要 本文主要介绍了线性回归算法中用于衡量模型优劣的重要指标——R Squared(R方)。R方用于比较模型预测结果与实际结…...
设计模式-Java
一、创建型模式 1. 单例模式 定义 确保一个类只有一个实例,并提供一个全局访问点。 实现方式 饿汉式(线程安全,但可能浪费资源) public class Singleton {// 静态变量,类加载时初始化private static final Singlet…...
代码讲解系列-CV(五)——语义分割基础
文章目录 一、图像分割标注1.1 Labelme标注1.2 SAM辅助1.3 json格式 二、数据解析2.1 Dataset2.2 train.py2.2.1 取参2.2.2 分割和数据集的读取 三、Unet网络搭建3.1 Unet3.2 Network 四、损失函数和指标4.1 DICE系数4.2 损失函数4.3 半精度训练 五、SAM六、作业 语义分割是图片…...
在mfc中使用自定义三维向量类和计算多个三维向量的平均值
先添加一个普通类, Vector3.h, // Vector3.h: interface for the Vector3 class. // //#if !defined(AFX_VECTOR3_H__53D34D26_95FF_4377_BD54_57F4271918A4__INCLUDED_) #define AFX_VECTOR3_H__53D34D26_95FF_4377_BD54_57F4271918A4__INCLUDED_#if _MSC_VER > 1000 #p…...
RDMA ibverbs_API功能说明
设备管理 获取当前活动网卡 返回当前rdma设备列表 struct ibv_device **ibv_get_device_list(int *num_devices);//使用 struct ibv_device **dev_list ibv_get_device_list(NULL);获取网卡名 返回网卡名字字符串:如"mlx5_0",一般通过网卡…...
【C++语言】string 类
一、为什么要学习 string 类 C语言中,字符串是以 “\0” 结尾的一些字符的集合,为了操作方便,C标准库中提供了一些 str 系列的库函数,但是这些库函数与字符串是分离开的,不太符合 OOP 的思想,而且底层空间需…...
快速上手gdb/cgdb
Linux调试器-gdb使用 1.背景2.调试原理、技巧命令2.1指令2.2 本质2.3 技巧 1.背景 程序的发布方式有两种,debug模式和release模式 Linux gcc/g出来的二进制程序,默认是release模式 要使用gdb调试,必须在源代码生成二进制程序的时候, 加上 -g…...
《养生》(二)
一、基础生活调整 1.作息规律 固定每天7-8小时睡眠,尽量22:30前入睡,晨起后拉开窗帘晒太阳5分钟,调节生物钟 2.饮食优化 三餐定时,每餐细嚼慢咽20次以上,优先吃蔬菜和蛋白质(如鸡蛋、豆腐&#x…...
JAVA:集成 Drools 业务规则引擎的技术指南
1、简述 Drools 是一个强大的业务规则引擎,适用于需要动态决策或规则管理的场景。它允许开发人员将业务逻辑与应用代码分离,使得业务人员可以通过规则文件维护和更新规则,而无需修改应用代码。本文将介绍 Drools 的基本概念、配置方式&#…...
GeoHD - 一种用于智慧城市热点探测的Python工具箱
GeoHD - 一种用于智慧城市热点探测的Python工具箱 详细原理请参考:Yan, Y., Quan, W., Wang, H., 2024. A data‐driven adaptive geospatial hotspot detection approach in smart cities. Trans. GIS tgis.13137. 代码下载:下载 1. 简介 在城市数据…...
记一次Ngnix配置
记一次Ngnix配置 配置Ngnix配置防火墙 假设一个服务器中有一个公网IP、一个内网IP,另外已经部署好后台服务的接口地址为http://内网ip:8088。 配置Ngnix 找到Ngnix的配置文件,通过在Ngnix的安装路径下的 \conf\nginx.conf 文件。 worker_processes 1;…...
2024年国赛高教杯数学建模C题农作物的种植策略解题全过程文档及程序
2024年国赛高教杯数学建模 C题 农作物的种植策略 原题再现 根据乡村的实际情况,充分利用有限的耕地资源,因地制宜,发展有机种植产业,对乡村经济的可持续发展具有重要的现实意义。选择适宜的农作物,优化种植策略&…...
java基础语知识(8)
类之间的关系 在类之间,最常见的关系有: 依赖(“uses-a”);聚合(“has-a”);继承(“is-a”)。 依赖:一种使用关系,即一个类的实现需要另一个类的协助&#x…...
室内定位精度方案对比
室内定位精度方案对比:成本、开发难度与精度的权衡 索引 引言 Wi-Fi 定位方案 定位原理 成本分析 开发难度 定位精度 蓝牙定位方案 定位原理 成本分析 开发难度 定位精度 超宽带(UWB)定位方案 定位原理 成本分析 开发难度 定…...
Pytorch深度学习教程_5_编写第一个神经网络
欢迎来到《pytorch深度学习教程》系列的第五篇!在前面的四篇中,我们已经介绍了Python、numpy及pytorch的基本使用,并在上一个教程中介绍了梯度。今天,我们将探索神经网络,对于神经网络进行概述并进行简单的实践学习 欢…...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...
使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...
Python训练营-Day26-函数专题1:函数定义与参数
题目1:计算圆的面积 任务: 编写一个名为 calculate_circle_area 的函数,该函数接收圆的半径 radius 作为参数,并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求:函数接收一个位置参数 radi…...
加密通信 + 行为分析:运营商行业安全防御体系重构
在数字经济蓬勃发展的时代,运营商作为信息通信网络的核心枢纽,承载着海量用户数据与关键业务传输,其安全防御体系的可靠性直接关乎国家安全、社会稳定与企业发展。随着网络攻击手段的不断升级,传统安全防护体系逐渐暴露出局限性&a…...
32单片机——基本定时器
STM32F103有众多的定时器,其中包括2个基本定时器(TIM6和TIM7)、4个通用定时器(TIM2~TIM5)、2个高级控制定时器(TIM1和TIM8),这些定时器彼此完全独立,不共享任何资源 1、定…...
【大模型】RankRAG:基于大模型的上下文排序与检索增强生成的统一框架
文章目录 A 论文出处B 背景B.1 背景介绍B.2 问题提出B.3 创新点 C 模型结构C.1 指令微调阶段C.2 排名与生成的总和指令微调阶段C.3 RankRAG推理:检索-重排-生成 D 实验设计E 个人总结 A 论文出处 论文题目:RankRAG:Unifying Context Ranking…...
