6.git项目实现变更拉取与上传

git项目实现变更拉取与上传

欢迎加入Gerapy二次开发教程专栏!

本专栏专为新手开发者精心策划了一系列内容,旨在引领你深入探索Gerapy框架的二次迭代之旅。

本专栏将全面剖析Gerapy与Vue的源码架构,让你从内部了解它们的运作机制。

我们将分享实用的技巧,教你如何有效修复Gerapy中的异常问题,如何在现有基础上添加多样化的功能,以及如何对已有功能进行重构优化。

写在前面

读本篇博客前需要你掌握的知识:

读完本篇博客你可以学习到的知识:

  • Vue前端界面如何增加按钮与点击事件
  • GitPython库使用介绍
  • git变更拉取与上传功能实现业务理解与技术实现过程

效果预览

需求明晰

我们想在项目管理 > 编辑进入项目编辑界面时,增加两个按钮拉取上传来实现项目变更记录快速同步到git,而不是还要使用命令行单独提交。

拉取就是使用python执行git pull命令;
上传就是使用python执行git add <file> git commit -m "common" git push origin master这三个命令;

这里我们使用GitPython库来快速实现此需求

技术实现

前端样式实现

下图是我们想要添加功能的位置,也就是项目编辑界面


所以我们要找的对应的代码代码文件是:

大家点击Edit.vue文件进去后会发现没有文字,这是因为代码使用了映射,它把这些按钮文本聚集到了lang/zh.js中,这个前面我们有说过。

也就是说,我们直接在Edit.vue参考前面按钮的实现方式复制两个即可。

Edit.vue

直接在最前面新增两个el-button

html 复制代码
<el-button
	v-if="activeFile && from_git"
	size="mini"
	type="primary"
	@click="onPullFile"
>
	<i class="fa fa-cloud-download"></i>
	拉取
</el-button>
<el-button
	v-if="activeFile && from_git"
	size="mini"
	type="primary"
	@click="onPushFile"
>
	<i class="fa fa-cloud-upload"></i>
	上传
</el-button>

按照下图操作,新增from_git参数,用来控制只有git项目才展示操作按钮

定义两个按钮的触发事件,这里先用测试代码

javascript 复制代码
onPullFile() {
	this.$message.success('正在执行代码拉取!');
},
onPushFile() {
	this.$message.success('正在执行代码上传!');
},

运行效果

处理好后,打包Vue代码,刷新浏览器界面看看效果

git项目标识

上面我们打算使用from_git参数来控制只有git项目才展示操作按钮,那我们怎么知道项目是不是来源于git呢?

看了一下project数据库并没有字段存储项目来源方式,那么这里可以判断项目下有没有.git目录。

这里前端是没法处理的,只能在后端实现传参,这种没必要单独开一个接口,我们看看项目编辑界面调用了哪些接口

老样子,在urls.py搜索project/(\S+)/tree,然后跳转进去views中对应的方法

project_tree方法代码理解起来很简单,它就是拿到项目的结构树,那我们可以在它拿到的结构树判断有没有存在.git目录,然后修改返回数据。修改如下:

python 复制代码
@log_exception()
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def project_tree(request, project_name):
    """
    get file tree of project
    :param request: request object
    :param project_name: project name
    :return: json of tree
    """
    if request.method == 'GET':
        path = os.path.abspath(join(os.getcwd(), PROJECTS_FOLDER))
        project_path = join(path, project_name)
        from_git = True if '.git' in os.listdir(project_path) else False
        # get tree data
        tree = get_tree(join(path, project_name))
        return JsonResponse({'from_git': from_git, 'tree': tree})

然后对应的Edit.vue需要修改结果获取方式

javascript 复制代码
getProjectTree(name) {
	// 获取目录树
	this.$http
		.get(
			this.formatString(this.$store.state.url.project.tree, {
				name: name,
			})
		)
		.then(({data: data}) => {
			this.tree = data['tree'];
			this.from_git = data['from_git'];
			if (this.tree.length > 0) {
				let lastNode = this.tree[this.tree.length - 1];
				this.onNodeClick(lastNode);
			}
		})
		.catch(() => {
			this.$message.error(this.$store.getters.$lang.messages.errorLoad);
		});
},

运行效果

处理好后,打包Vue代码,重新启动后端服务,刷新浏览器界面看看效果

功能实现

前面我们是把git变更记录拉取和上传前端样式开发好了,接下来就要开发后端接口以及前后端交互实现了。

urls.py

老样子,urls.py新增两个接口映射,并定义好方法名

views.py

这里就是开发两个方法的业务代码,具体理解可以看注释

python 复制代码
@log_exception()
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def project_file_pull(request):
    """
    pull project file
    :param request: request object
    :return: result of delete
    """
    if request.method == 'POST':
        # 接收请求body,body里面包含项目名(project_name),毕竟需要知道哪个项目要拉取代码
        data = json.loads(request.body)
        # 根据项目名定位到项目的路径
        project_path = join(PROJECTS_FOLDER, data['project_name'])
        # git打开本地仓库,得到实例repo
        repo = git.Repo(project_path)
        # 绑定远程
        origin = repo.remote(name='origin')
        # 拉取代码
        origin.pull()
        # 由于前面我们删除了requirements.txt,每次pull都会恢复它,所以这里还是要做删除处理
        for path_type, filenames in {'f': ['requirements.txt'], 'd': []}.items():
            for filename in filenames:
                rq = join(project_path, filename)
                optimize_rmtree(rq, path_type=path_type)
        return JsonResponse({'result': '1'})


@log_exception()
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def project_file_push(request):
    """
    push project file
    :param request: request object
    :return: result of delete
    """
    if request.method == 'POST':
        # 接收请求body,body里面包含项目名(project_name),毕竟需要知道哪个项目要拉取代码
        data = json.loads(request.body)
        # 根据项目名定位到项目的路径
        project_path = join(PROJECTS_FOLDER, data['project_name'])
        # 打印日志,便于调试判断
        logger.debug('push git %s', project_path)
        # git打开本地仓库,得到实例repo
        repo = git.Repo(project_path)
        # 添加所有新增的文件
        repo.index.add(repo.untracked_files)
        # 添加所有更新的文件
        repo.git.add(update=True)

        # 由于前面我们删除了requirements.txt,但是我们不想将其同步到git上,所以需要在最后抛弃同步
        for file_list in {'f': ['requirements.txt'], 'd': []}.values():
            for file_name in file_list:
                if not file_name:
                    continue
                repo.git.restore('--staged', file_name)

        # 定义commit内容
        commit_message = "add gerapy update"
        # 提交commit
        repo.index.commit(commit_message)
        # 绑定远程
        origin = repo.remote(name='origin')
        # 提交push
        origin.push(refspec='master')
        return JsonResponse({'result': '1'})

Edit.vue

需要实现原先定义的两个按钮点击事件,它们就是传入项目名调用接口:

javascript 复制代码
onPullFile() {
	this.$confirm(
		this.$store.getters.$lang.messages.confirm,
		this.$store.getters.$lang.buttons.confirm,
		{
			confirmButtonText: this.$store.getters.$lang.buttons.yes,
			cancelButtonText: this.$store.getters.$lang.buttons.no,
			type: "warning",
		}
	).then(() => {
		this.$http
			.post("/api/project/file/pull", {
				project_name: this.projectName,
			})
			.then(() => {
				this.$message.success("拉取成功");
				this.getProjectTree(this.projectName);
			})
			.catch(() => {
				this.$message.error("拉取失败");
			});
	});
},
onPushFile() {
	this.$confirm(
		this.$store.getters.$lang.messages.confirm,
		this.$store.getters.$lang.buttons.confirm,
		{
			confirmButtonText: this.$store.getters.$lang.buttons.yes,
			cancelButtonText: this.$store.getters.$lang.buttons.no,
			type: "warning",
		}
	).then(() => {
		this.$http
			.post("/api/project/file/push", {
				project_name: this.projectName,
			})
			.then(() => {
				this.$message.success("上传成功");
				this.getProjectTree(this.projectName);
			})
			.catch(() => {
				this.$message.error("上传失败");
			});
	});
},

运行效果

处理好后,打包Vue代码,重新启动后端服务,刷新浏览器

push功能测试

README.md增加些内容,然后点击上传按钮


看看gitee该项目的提交日志,确实是同步变更了。

pull功能测试

我们在gitee直接把前面对README.md增加的内容删掉,保存后点击拉取按钮。

总结

通过本篇文章讲解后,我们对前后端交互开发的理解更加深刻了,同时加深了我们对Gerapy项目的了解,后续我们将进一步开发新功能。

相关推荐
呵呵哒( ̄▽ ̄)"32 分钟前
线性代数:同解(1)
python·线性代数·机器学习
SweetCode38 分钟前
裴蜀定理:整数解的奥秘
数据结构·python·线性代数·算法·机器学习
CryptoPP1 小时前
springboot 对接马来西亚数据源API等多个国家的数据源
spring boot·后端·python·金融·区块链
xcLeigh1 小时前
OpenCV从零开始:30天掌握图像处理基础
图像处理·人工智能·python·opencv
大乔乔布斯1 小时前
AttributeError: module ‘smtplib‘ has no attribute ‘SMTP_SSL‘ 解决方法
python·bash·ssl
明灯L1 小时前
《函数基础与内存机制深度剖析:从 return 语句到各类经典编程题详解》
经验分享·python·算法·链表·经典例题
databook1 小时前
不平衡样本数据的救星:数据再分配策略
python·机器学习·scikit-learn
碳基学AI1 小时前
哈尔滨工业大学DeepSeek公开课:探索大模型原理、技术与应用从GPT到DeepSeek|附视频与讲义免费下载方法
大数据·人工智能·python·gpt·算法·语言模型·集成学习
niuniu_6661 小时前
简单的自动化场景(以 Chrome 浏览器 为例)
运维·chrome·python·selenium·测试工具·自动化·安全性测试
FearlessBlot1 小时前
Pyinstaller 打包flask_socketio为exe程序后出现:ValueError: Invalid async_mode specified
python·flask