Sub SaveCodeModules()
'This code Exports all VBA modules
Dim i%, sName$
With ThisWorkbook.VBProject
For i% = 1 To .VBComponents.Count
If .VBComponents(i%).CodeModule.CountOfLines > 0 Then
sName$ = .VBComponents(i%).CodeModule.Name
.VBComponents(i%).Export "X:\Tools\MyExcelMacros\" & sName$ & ".vba"
End If
Next i
End With
End Sub
Sub ImportCodeModules()
With ThisWorkbook.VBProject
For i% = 1 To .VBComponents.Count
ModuleName = .VBComponents(i%).CodeModule.Name
If ModuleName <> "VersionControl" Then
If Right(ModuleName, 6) = "Macros" Then
.VBComponents.Remove .VBComponents(ModuleName)
.VBComponents.Import "X:\Data\MySheet\" & ModuleName & ".vba"
End If
End If
Next i
End With
End Sub
Private Sub Workbook_Open()
ImportCodeModules
End Sub
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
SaveCodeModules
End Sub
I save module files in git\ dir beside workbook location. Change that to your liking.
This will NOT track changes to Workbook code. So it's up to you to synchronize them.
Sub SaveCodeModules()
'This code Exports all VBA modules
Dim i As Integer, name As String
With ThisWorkbook.VBProject
For i = .VBComponents.count To 1 Step -1
If .VBComponents(i).Type <> vbext_ct_Document Then
If .VBComponents(i).CodeModule.CountOfLines > 0 Then
name = .VBComponents(i).CodeModule.name
.VBComponents(i).Export Application.ThisWorkbook.Path & _
"\git\" & name & ".vba"
End If
End If
Next i
End With
End Sub
Sub ImportCodeModules()
Dim i As Integer
Dim ModuleName As String
With ThisWorkbook.VBProject
For i = .VBComponents.count To 1 Step -1
ModuleName = .VBComponents(i).CodeModule.name
If ModuleName <> "VersionControl" Then
If .VBComponents(i).Type <> vbext_ct_Document Then
.VBComponents.Remove .VBComponents(ModuleName)
.VBComponents.Import Application.ThisWorkbook.Path & _
"\git\" & ModuleName & ".vba"
End If
End If
Next i
End With
End Sub
然后在Workbook模块中:
Private Sub Workbook_Open()
ImportCodeModules
End Sub
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
SaveCodeModules
End Sub
Sub SaveCodeModules(dir As String)
'This code Exports all VBA modules
Dim moduleName As String
Dim vbaType As Integer
With ThisWorkbook.VBProject
For i = 1 To .VBComponents.count
If .VBComponents(i).CodeModule.CountOfLines > 0 Then
moduleName = .VBComponents(i).CodeModule.Name
vbaType = .VBComponents(i).Type
If vbaType = 1 Then
.VBComponents(i).Export dir & moduleName & ".vba"
ElseIf vbaType = 3 Then
.VBComponents(i).Export dir & moduleName & ".frm"
ElseIf vbaType = 100 Then
.VBComponents(i).Export dir & moduleName & ".cls"
End If
End If
Next i
End With
End Sub
Sub ImportCodeModules(dir As String)
Dim modList(0 To 0) As String
Dim vbaType As Integer
' delete all forms, modules, and code in MEOs
With ThisWorkbook.VBProject
For Each comp In .VBComponents
moduleName = comp.CodeModule.Name
vbaType = .VBComponents(moduleName).Type
If moduleName <> "DevTools" Then
If vbaType = 1 Or _
vbaType = 3 Then
.VBComponents.Remove .VBComponents(moduleName)
ElseIf vbaType = 100 Then
' we can't simply delete these objects, so instead we empty them
.VBComponents(moduleName).CodeModule.DeleteLines 1, .VBComponents(moduleName).CodeModule.CountOfLines
End If
End If
Next comp
End With
' make a list of files in the target directory
Set FSO = CreateObject("Scripting.FileSystemObject")
Set dirContents = FSO.getfolder(dir) ' figure out what is in the directory we're importing
' import modules, forms, and MEO code back into workbook
With ThisWorkbook.VBProject
For Each moduleName In dirContents.Files
' I don't want to import the module this script is in
If moduleName.Name <> "DevTools.vba" Then
' if the current code is a module or form
If Right(moduleName.Name, 4) = ".vba" Or _
Right(moduleName.Name, 4) = ".frm" Then
' just import it normally
.VBComponents.Import dir & moduleName.Name
' if the current code is a microsoft excel object
ElseIf Right(moduleName.Name, 4) = ".cls" Then
Dim count As Integer
Dim fullmoduleString As String
Open moduleName.Path For Input As #1
count = 0 ' count which line we're on
fullmoduleString = "" ' build the string we want to put into the MEO
Do Until EOF(1) ' loop through all the lines in the file
Line Input #1, moduleString ' the current line is moduleString
If count > 8 Then ' skip the junk at the top of the file
' append the current line `to the string we'll insert into the MEO
fullmoduleString = fullmoduleString & moduleString & vbNewLine
End If
count = count + 1
Loop
' insert the lines into the MEO
.VBComponents(Replace(moduleName.Name, ".cls", "")).CodeModule.InsertLines .VBComponents(Replace(moduleName.Name, ".cls", "")).CodeModule.CountOfLines + 1, fullmoduleString
Close #1
End If
End If
Next moduleName
End With
End Sub
Sub SaveCodeModules()
'This code Exports all VBA modules
Dim i%, sName$
With ThisWorkbook.VBProject
For i% = 1 To .VBComponents.Count
If .VBComponents(i%).CodeModule.CountOfLines > 0 Then
sName$ = .VBComponents(i%).CodeModule.Name
.VBComponents(i%).Export "C:\Code\" & sName$ & ".vba"
End If
Next i
End With
End Sub
25 回答
我刚刚设置了一个使用Bazaar的电子表格,通过TortiseBZR手动签到/退出 . 鉴于该主题帮助我保存部分,我想在这里发布我的解决方案 .
对我来说,解决方案是创建一个电子表格,在导出时导出所有模块,并在打开时删除并重新导入模块 . 是的,这可能对转换现有电子表格有潜在危险 .
这允许我通过 Emacs (是的,emacs)或本机在Excel中编辑模块中的宏,并在重大更改后提交我的BZR存储库 . 因为所有模块都是文本文件,所以BZR中的标准diff-style命令适用于我的源,但Excel文件本身除外 .
我为我的BZR存储库设置了一个目录,X:\ Data \ MySheet . 在repo中是我的每个模块的MySheet.xls和一个.vba文件(即:Module1Macros) . 在我的电子表格中,我添加了一个免于导出/导入周期的模块,名为“VersionControl” . 要导出和重新导入的每个模块必须以“宏”结尾 .
Contents of the "VersionControl" module:
接下来,我们必须设置事件挂钩以进行打开/保存以运行这些宏 . 在代码查看器中,右键单击“ThisWorkbook”并选择“查看代码” . 您可能需要下拉代码窗口顶部的选择框以从“(常规)”视图更改为“工作簿”视图 .
Contents of "Workbook" view:
我将在接下来的几周内完成这个工作流程,如果我有任何问题,我会发布 .
感谢分享VBComponent代码!
TortoiseSVN是Subversion版本控制系统的一个非常好的Windows客户端 . 我刚刚发现它的一个特性是当你单击以获得Excel文件版本之间的差异时,它将在Excel中打开两个版本并突出显示(红色)已更改的单元格 . 这是通过vbs脚本的魔力完成的,描述为here .
即使不使用TortoiseSVN,您也会发现这很有用 .
这取决于您是在谈论数据还是电子表格中包含的代码 . 虽然我非常不喜欢微软的Visual Sourcesafe,并且通常不会推荐它,但它确实可以轻松地与Access和Excel集成,并提供模块的源代码控制 .
[实际上与Access集成,包括查询,报告和模块作为可以版本化的单个对象]
MSDN链接是here .
我不知道有这么好的工具,但我已经看到了各种本土解决方案 . 这些的共同点是在版本控制下最小化二进制数据并最大化文本数据以利用传统scc系统的能力 . 去做这个:
像处理任何其他应用程序一样处理工作簿 . 分离逻辑,配置和数据 .
从工作簿中分离代码 .
以编程方式构建UI .
编写构建脚本以重构工作簿 .
让我总结一下您希望版本控制的内容以及原因:
什么:
代码(VBA)
电子表格(公式)
电子表格(值)
图表
......
为什么:
审核日志
协作
版本比较("diffing")
合并
正如其他人在此发布的那样,在现有版本控制系统之上有几个解决方案,例如:
Git
Mercurial
Subversion
Bazaar
如果您唯一关心的是工作簿中的VBA代码,那么上面提到的Demosthenex方法或VbaGit(https://github.com/brucemcpherson/VbaGit)工作得非常好并且实现起来相对简单 . 优点是你可以依靠成熟的版本控制系统,并根据你的需要选择一个(看看https://help.github.com/articles/what-are-the-differences-between-svn-and-git/,以便在Git和Subversion之间进行简单的比较) .
如果你不是只关心代码而且关于工作表中的数据("hardcoded"值和公式结果),您可以使用类似的策略:将工作表的内容序列化为某种文本格式(通过Range.Value)并使用现有版本控制系统 . 这是一篇关于此的非常好的博文:https://wiki.ucl.ac.uk/display/~ucftpw2/2013/10/18/Using+git+for+version+control+of+spreadsheet+models+-+part+1+of+3
但是,电子表格比较是一个非常重要的算法问题 . 有一些工具,如微软的电子表格比较(https://support.office.com/en-us/article/Overview-of-Spreadsheet-Compare-13fafa61-62aa-451b-8674-242ce5f2c986),Exceldiff(http://exceldiff.arstdesign.com/)和DiffEngineX(https://www.florencesoft.com/compare-excel-workbooks-differences.html) . 但是将这些比较与像Git这样的版本控制系统集成是另一个挑战 .
最后,您必须确定适合您需求的工作流程 . 有关简单,量身定制的Git for Excel工作流程,请查看https://www.xltrail.com/blog/git-workflow-for-excel .
致力于@Demosthenex工作,@Tmdean和@Jon Crowell宝贵的评论! (1他们)
I save module files in git\ dir beside workbook location. Change that to your liking.
This will NOT track changes to Workbook code. So it's up to you to synchronize them.
然后在Workbook模块中:
将@Demosthenex的答案更进一步,如果你想跟踪Microsoft Excel对象和用户表格中的代码,你必须有点棘手 .
首先,我修改了我的
SaveCodeModules()
函数,以解释我计划导出的不同类型的代码:UserForms可以像VBA代码一样导出和导入 . 唯一的区别是导出表单时将创建两个文件(您将获得每个UserForm的
.frm
和.frx
文件) . 其中一个保存了您确定的表单布局的软件 .Microsoft Excel对象(MEO)(意思是
Sheet1
,Sheet2
,ThisWorkbook
等)可以导出为.cls
文件 . 但是,如果要将此代码恢复到工作簿中,如果尝试以与VBA模块相同的方式导入它,则如果工作簿中已存在该工作表,则会出现错误 .为了解决这个问题,我决定不尝试将.cls文件导入Excel,而是将
.cls
文件作为字符串读入excel,然后将此字符串粘贴到空的MEO中 . 这是我的ImportCodeModules:如果你对这两个函数的
dir
输入感到困惑,那只是你的代码库!所以,你将这些函数称为:您可以做的一件事是在工作簿中包含以下代码段:
我在互联网上找到了这个片段 .
之后,您可以使用Subversion来维护版本控制 . 例如,通过在VBA中使用Subversion的命令行界面和“shell”命令 . 那样做会 . 我甚至想过自己这样做:)
我使用 git ,今天我将this (git-xlsx-textconv)移植到Python,因为我的项目基于Python代码,并且它与Excel文件交互 . 这适用于至少 .xlsx 文件,但我认为它也适用于 .xls . Here's github链接 . 我写了两个版本,一个是每一行都有自己的行,另一个是每个单元格都在自己的行上(后者是因为 git diff 不喜欢在默认情况下包装长行,至少在Windows上是这样) .
这是我的 .gitconfig 文件(这允许不同的脚本驻留在我的项目的repo中):
如果您希望脚本可用于许多不同的repos,那么使用以下内容:
我的 .gitattributes 文件:
如果您正在寻找具有常规办公室非技术用户的办公环境,那么Sharepoint是一个可行的选择 . 您可以设置启用了版本控制以及签入和签出的文档文件夹 . 使常规办公用户更自由 .
使用任何标准版本控制工具,如SVN或CVS . 限制取决于目标是什么 . 除了存储库大小的小幅增加外,我没有遇到任何问题
为响应mattlant的回复 - 只有在文档库中打开版本控制功能时,sharepoint才能作为版本控件使用 . 另外请注意,通过相对路径调用其他文件的任何代码都不会起作用 . 最后,当文件保存在sharepoint中时,任何指向外部文件的链接都会中断 .
你应该尝试DiffEngineX . 它可以通过编程方式调用,也可以从命令行获取命令行参数 . 它不仅可以比较Excel电子表格单元格,还可以比较工作簿中嵌入的Visual Basic宏 . 还比较了Excel定义的名称和注释,这是许多免费软件工具所遗漏的出 . 它可以从下载
http://www.florencesoft.com/excel-differences-download.html
我确定您的版本控制系统有一个选项或框,因此您可以使用原始和修改的Excel工作簿自动调用DiffEngineX .
我也一直在研究这个问题 . 它表明最新的Team Foundation Server 2010可能具有Excel加载项 .
这是一个线索:
http://team-foundation-server.blogspot.com/2009/07/tf84037-there-was-problem-initializing.html
搜索了几年并尝试了许多不同的工具后,我在这里找到了对vba版本控制问题的答案:https://stackoverflow.com/a/25984759/2780179
这是一个简单的excel插件,可以找到代码here
导入后没有重复的模块 . 只要您保存工作簿,它就会自动导出您的代码,而无需修改任何现有工作簿 . 它与vba代码格式化程序一起使用 .
实际上,只有少数几种解决方案可以跟踪和比较宏代码中的变化 - 其中大部分已在此处命名 . 我一直在浏览网页,并且遇到了值得一提的这个新工具:
XLTools Version Control for VBA macros
Excel工作表和VBA模块的
版本控制
在提交版本之前
预览和差异更改
非常适 Contract 一个文件上的多个用户的协同工作(跟踪谁更改了什么/何时/评论)
比较版本并逐行突出显示代码中的更改
适合那些不懂技术或精通Excel的用户
版本历史存储在您自己的PC上的Git-repository中 - 任何版本都可以轻松恢复
VBA code versions side by side, changes are visualized
我想推荐一个名为Rubberduck的开源工具,它内置了VBA代码的版本控制 . 试试吧!
您可能已经尝试在zip容器(.xlsx和.xslm)中使用Microsoft的Excel XML进行版本控制,并发现vba存储在vbaProject.bin中(对于版本控制来说没用) .
解决方案很简单 .
使用LibreOffice Calc打开excel文件
在LibreOffice Calc中
文件
另存为
另存为类型:ODF电子表格(.ods)
关闭LibreOffice Calc
将新文件的文件扩展名从.ods重命名为.zip
在GIT维护区域中为电子表格创建一个文件夹
将zip解压缩到它的GIT文件夹中
承诺给GIT
当您使用下一版本的电子表格重复此操作时,您必须确保使文件夹的文件与zip容器中的文件完全匹配(并且不要留下任何已删除的文件) .
还有一个名为 Beyond Compare 的程序,它有一个非常好的Excel文件比较 . 我找到了一个中文截图,简要说明了这一点:
Original image source
他们的page有30天的试用期
我的公司在自动化Microsoft Office解决方案方面做了大量工作,因此我编写了一个.DLL,每次保存模板时都会导出解决方案的来源 . 它创建一个名为Source的文件夹作为保存模板的文件夹的子文件夹,在Source下面创建一个与VBA项目同名的文件夹 . 在项目文件夹中,它导出模块,类和用户表单的所有源代码 . 选择这种安排是为了便于管理大量模板集合的源 . 如果您有本地配置文件或全局配置文件,则DLL能够解锁锁定的项目以访问VBA项目 . 使用此工具,开发人员可以根据自己的内容处理模板,并使用他们喜欢的修订控制工具来管理他们的工作 . 我们主要在我们的环境中使用Git,我们保留完整的模板二进制文件以及版本控制下的VBA资源 .
这是一个GitHub项目,它解决了OP问题的第1点和第4点:https://github.com/ColmBhandal/VbaMisc . 它仅适用于VBA模块的VC解决方案 . 通过复制GitHub上看到的项目结构并将要放在VC下的任何模块添加到ExportImport模块中定义的whiteList,可以轻松地为任何项目定制它 . 该模块控制VBA模块白名单的导出和导入,可能包括自身 . 有关如何使用的说明,请参阅GitHub存储库 .
我找到了一个满足我需求的非常简单的解决方案 . 我在我的所有宏的底部添加一行,每次运行时都会导出
*.txt
文件和整个宏代码 . 代码:(在Tom's Tutorials上找到,它也包含了一些你可能需要的设置才能使它工作 . )
自从我'll always run the macro whenever I'米工作了代码,我保证git会接受更改 . 唯一令人讨厌的部分是,如果我需要签出早期版本,我必须手动从
*.txt
复制/粘贴到电子表格中 .这取决于你想要的集成级别,我使用了Subversion / TortoiseSVN,这对于简单的使用来说似乎很好 . 我还添加了关键字,但似乎存在文件损坏的风险 . 在Subversion中有一个选项可以使关键字替换固定长度,据我所知,如果固定长度是偶数但不是奇数,它将起作用 . 在任何情况下你都没有得到任何有用的差异功能,我认为有些商业产品会做'差异' . 我确实找到了基于将内容转换为纯文本并进行比较的差异,但这并不是很好 .
它应该适用于大多数VCS(取决于您可能选择SVN,CVS,Darc,TFS等的其他标准),但它实际上是完整的文件(因为它是二进制格式),这意味着“改变了什么”的问题是不那么容易回答 .
如果人们完成日志消息,您仍然可以依赖日志消息,但您也可以尝试使用Office 2007中基于XML的新格式来获得更多可见性(尽管仍然很难清除大量的XML,加上AFAIK XML文件在磁盘上压缩,因此你需要一个预提交钩子来解压缩它以使文本差异正常工作) .
我使用VBA编写了一个修订控制的电子表格 . 它适用于工程报告,其中您有多个人在处理物料清单或计划,然后在某个时间点您要创建一个快照修订版本,显示上一次修订版本中的添加,删除和更新 .
注意:它是一个启用宏的工作簿,您需要登录才能从我的站点下载(您可以使用OpenID)
所有代码都已解锁 .
Rev Controlled Spreadsheet