自动备份本地库程序并自动进行版本控制发出不久,有同学提出建议,能不能实时备份,同时设置2个任务的方式有点繁琐。而且每天定点提交,并不能完全解决时差带来的版本不一致问题。
之前讲Git与VSCode与M的时候,实际上利用VSCode连接IRIS库可以实现Git版本控制,但是也得需要去手动的导出,提交等操作。
经过一段时间思考琢磨后,我们能不能利用原生的Studio环境在编译时进行备份呢,因为每次编译会自动保存最新的代码,从而达到实时备份还能省去手动的导出,提交等操作。
实时备份可以解决如下问题:
那么我们该如何实现编译时进行备份呢?请看如下过程:
通过
IRIS调用Python的Git库,再利用M的代码生成器在编译时运行导出方法,继而再调用Git库的命令进行版本控制。
编写
M相关代码。
Parameter classPath = "E:\m\cls\";
ClassMethod ExportCls(cls = "", classPath = {..#classPath})
{
#; 判断路径文件夹是否存在
if ('##class(%File).DirectoryExists(classPath)){
#; 不存在创建文件夹
d ##class(%File).CreateNewDir(classPath, "")
}
#; 导出类
d $system.OBJ.Export(cls _ ".cls", classPath _ cls _ ".xml")
q $$$OK
}
autoBackup,有此标志的类在编译时进行导出。ClassMethod CompileBackUp() [ CodeMode = objectgenerator ]
{
q:(%class.ClientName '= "autoBackup") $$$OK
d ..ExportCls(%class.Name)
q $$$OK
}
M.Git,用于实现编译时进行版本控制。ClientName = autoBackup,用于标识该类是否自动备份。Class M.Test Extends (%RegisteredObject, M.Git) [ ClientName = autoBackup ]
{
}
M.Test进行编译,发现在E:\m\cls\文件夹内成功导出该类文件。

编写
Python相关代码
gitpython库,调用Git命令进行版本控制。C:\InterSystems\IRISHealth\bin,输入cmd,进入命令行控制台输入命令。安装gitpython库。irispip install --target C:\InterSystems\IRISHealth\mgr\python gitpython
C:\InterSystems\IRISHealth\bin>irispip install --target C:\InterSystems\IRISHealth\mgr\python gitpython
Collecting gitpython
Using cached GitPython-3.1.29-py3-none-any.whl (182 kB)
Collecting gitdb<5,>=4.0.1
Using cached gitdb-4.0.10-py3-none-any.whl (62 kB)
Collecting smmap<6,>=3.0.1
Using cached smmap-5.0.0-py3-none-any.whl (24 kB)
Installing collected packages: smmap, gitdb, gitpython
Successfully installed gitdb-4.0.10 gitpython-3.1.29 smmap-5.0.0
[notice] A new release of pip available: 22.2.2 -> 22.3.1
[notice] To update, run: python.exe -m pip install --upgrade pip
/// desc:初始化仓库
ClassMethod GitInit(path) As %String [ Language = python ]
{
from git import Repo
Repo.init(path)
}
Language = python无法给参数进行设置默认值,所以需要回调一下。/// desc:调用初始化
ClassMethod RunInit(path = {..#classPath}) As %String
{
d ..GitInit(path)
}
E:\m\cls\文件夹初始化仓库成功。d ##class(M.Git).RunInit()

/// desc:添加暂存区提交工作区
ClassMethod GitCommit(path) As %String [ Language = python ]
{
import os
from git.repo.fun import is_git_dir
from git.repo import Repo
repo = Repo(path)
commit_message = '自动备份'
repo.index.add("*.xml")
repo.index.commit(commit_message)
}
CompileBackUp()方法。/// desc:编译时备份,备份前要确保文件夹初始化Git仓库
/// d ##class(M.Git).ExportCls("M.BackUp")
ClassMethod CompileBackUp() [ CodeMode = objectgenerator ]
{
q:(%class.ClientName '= "autoBackup") $$$OK
d ..ExportCls(%class.Name)
d ..GitCommit(..#classPath)
q $$$OK
}
M.Test再次编译该类。
M.Test类添加方法再次编译,测试一下类变化进行版本控制。Class M.Test Extends (%RegisteredObject, M.Git) [ ClientName = autoBackup ]
{
/// desc:查询全局变量
/// w ##class(M.Test).QueryGlobalDetail1().%ToJSON()
ClassMethod QueryGlobalDetail1(pJson As %Library.DynamicObject)
{
s array = []
s rs = ##class(%ResultSet).%New("%Global:Get")
d rs.Execute("USER","^yx(99:100)","",2,2,1)
while(rs.Next()){
s obj = {}
s obj.name = rs.Data("Name")
s obj.value = rs.Data("Value")
s obj.nameFormat = rs.Data("Name Format")
s obj.valueFormat = rs.Data("Value Format")
s obj.permissions = rs.Data("Permissions")
d array.%Push(obj)
}
q array
}
}
Git日志。

按照上述方法,我们就实现在服务端或本地的一个版本控制。
可能有小伙伴想提交到本地库之后,还想Push到远程仓库。
其实也是可以的,下面我们来实现一下。
Gitlab创建了一个名为python的远程仓库。复制远程库的链接放到代码里。
/// desc:先拉取再推送,确保本地版本与远程仓库是一致的再,提交并推送远程仓库
ClassMethod GitCommitPush(path) As %String [ Language = python ]
{
import os
from git.repo.fun import is_git_dir
from git.repo import Repo
repo = Repo(path)
commit_message = '自动备份'
repo.index.add("*.xml")
repo.index.commit(commit_message)
git = repo.git
# 如果是你私有仓库能确保版本一致,可直接Push。
git.config("user.name","yx")
git.config("user.email","454115408@qq.com")
git.pull("http://username:password@8.142.29.250:7091/root/python.git","master","--allow-unrelated-histori")
git.push("http://username:password@8.142.29.250:7091/root/python.git","master")
}
CompileBackUp()方法。ClassMethod CompileBackUp() [ CodeMode = objectgenerator ]
{
q:(%class.ClientName '= "autoBackup") $$$OK
d ..ExportCls(%class.Name)
#; 没有远程仓库使用GitCommit
#; d ..GitCommit(..#classPath)
#; 有远程仓库例如Gitlab用GitCommitPush
d ..GitCommitPush(..#classPath)
q $$$OK
}
M.Test再次编译该类,查看GitLab发现提交成功。
M.Test文件查看文件。
ClassMethod PythonMath() As %String
{
s math = ##class(%SYS.Python).Import("math")
q math.pi
}

M.Test文件查看文件。
Git日志。

这样我们就可以在远程仓库与本地库进行版本控制,进行双保险。
如果提交失败,Studio会提示出具体报错信息。
1。
综上所诉:只需要对类继承M.Git,就可以实现编译时版本控制了。
Terminal中去执行ExportCls方法。autoBackup。2次编译时间小于5分钟不进行版本控制。注:此方案仅适用于IRIS,因为老版Caché无法使用嵌入式Python。
SELECT TimeChanged,TimeCreated FROM %Dictionary.CompiledClass WHERE ID = "M.M91"
M.GitClass M.Git Extends %RegisteredObject
{
Parameter classPath = "E:\m\cls\";
/// desc:编译时备份,备份前要确保文件夹初始化Git仓库
/// d ##class(M.Git).ExportCls("M.BackUp")
ClassMethod CompileBackUp() [ CodeMode = objectgenerator ]
{
q:(%class.ClientName '= "autoBackup") $$$OK
d ..ExportCls(%class.Name)
#; 没有远程仓库使用GitCommit
#; d ..GitCommit(..#classPath)
#; 有远程仓库例如Gitlab用GitCommitPush
d ..GitCommitPush(..#classPath)
q $$$OK
}
ClassMethod RunBackUp()
{
q ..ExportCls($this)
}
/// d ##class(M.Git).ExportCls("M.BackUp")
ClassMethod ExportCls(cls = "", classPath = {..#classPath})
{
#; 判断路径文件夹是否存在
if ('##class(%File).DirectoryExists(classPath)){
#; 不存在创建文件夹
d ##class(%File).CreateNewDir(classPath, "")
}
#; 导出类
d $system.OBJ.Export(cls _ ".cls", classPath _ cls _ ".xml")
q $$$OK
}
/// desc:调用初始化
/// d ##class(M.Git).RunInit()
ClassMethod RunInit(path = {..#classPath}) As %String
{
d ..GitInit(path)
}
/// desc:初始化
/// w ##class(M.Git).GitInit({..#classPath})
ClassMethod GitInit(path) As %String [ Language = python ]
{
from git import Repo
Repo.init(path)
}
/// desc:添加暂存区提交工作区
ClassMethod GitCommit(path) As %String [ Language = python ]
{
import os
from git.repo.fun import is_git_dir
from git.repo import Repo
repo = Repo(path)
commit_message = '自动备份'
repo.index.add("*.xml")
repo.index.commit(commit_message)
}
/// desc:先拉取再推送,确保本地版本与远程仓库是一致的再,提交并推送远程仓库
ClassMethod GitCommitPush(path) As %String [ Language = python ]
{
import os
from git.repo.fun import is_git_dir
from git.repo import Repo
repo = Repo(path)
commit_message = '自动备份'
repo.index.add("*.xml")
repo.index.commit(commit_message)
git = repo.git
# 如果是你私有仓库能确保版本一致,可直接Push。
git.config("user.name","yx")
git.config("user.email","454115408@qq.com")
git.pull("http://username:password@8.142.29.250:7091/root/python.git","master","--allow-unrelated-histori")
git.push("http://username:password@8.142.29.250:7091/root/python.git","master")
}
}
M.TestClass M.Test Extends (%RegisteredObject, M.Git) [ ClientName = autoBackup ]
{
/// desc:查询全局变量
/// w ##class(M.Test).QueryGlobalDetail1().%ToJSON()
ClassMethod QueryGlobalDetail1(pJson As %Library.DynamicObject)
{
s array = []
s rs = ##class(%ResultSet).%New("%Global:Get")
d rs.Execute("USER","^yx(99:100)","",2,2,1)
while(rs.Next()){
s obj = {}
s obj.name = rs.Data("Name")
s obj.value = rs.Data("Value")
s obj.nameFormat = rs.Data("Name Format")
s obj.valueFormat = rs.Data("Value Format")
s obj.permissions = rs.Data("Permissions")
d array.%Push(obj)
}
q array
}
ClassMethod PythonMath() As %String
{
s math = ##class(%SYS.Python).Import("math")
q math.pi
}
}