I\'ve inherited some VBA in Excel and want to put it into git. As it stands, git sees it as binary and doesn\'t want to do file change deltas but duplicate the whole file. <
If you use Rubberduck VBA, after clicking
You can use the file menu to "Export Active Project", which exports the form binaries and the code as BAS objects, which are just plain text. Then you can commit to git.
You should be able to export modules as text to a git folder then commit as follows.
In The VBA Editor Add modules for each macro (Menu Insert/Module) copy each macros code into a module and save as a text file with control + E. Save into your git folder and use the normal git procedures to commit any changes.
When you change the vba code re save (control+E) the module and update git as normal.
I had this problem and solved it by creating a VBA module to export other VBA modules. Usage instructions for this, and the raw code, can be found at the following location:
https://github.com/ColmBhandal/VbaSync.
There is a C# alternative for putting VBA under Excel version control. The code has been added to a C# library which can be used to do version control e.g. via a CLI tool or via a VSTO AddIn:
https://gitlab.com/hectorjsmith/csharp-excel-vba-sync
I was involved in the coding of the above repos. They are both free to use and open source.
You can create a git pre-commit hook that runs the following Python script to automatically extract your VBA code and add it to your commit (see https://www.xltrail.com/blog/auto-export-vba-commit-hook):
import os
import shutil
from oletools.olevba3 import VBA_Parser
EXCEL_FILE_EXTENSIONS = ('xlsb', 'xls', 'xlsm', 'xla', 'xlt', 'xlam',)
def parse(workbook_path):
vba_path = workbook_path + '.vba'
vba_parser = VBA_Parser(workbook_path)
vba_modules = vba_parser.extract_all_macros() if vba_parser.detect_vba_macros() else []
for _, _, _, content in vba_modules:
decoded_content = content.decode('latin-1')
lines = []
if '\r\n' in decoded_content:
lines = decoded_content.split('\r\n')
else:
lines = decoded_content.split('\n')
if lines:
name = lines[0].replace('Attribute VB_Name = ', '').strip('"')
content = [line for line in lines[1:] if not (
line.startswith('Attribute') and 'VB_' in line)]
if content and content[-1] == '':
content.pop(len(content)-1)
lines_of_code = len(content)
non_empty_lines_of_code = len([c for c in content if c])
if non_empty_lines_of_code > 0:
if not os.path.exists(os.path.join(vba_path)):
os.makedirs(vba_path)
with open(os.path.join(vba_path, name + '.bas'), 'w') as f:
f.write('\n'.join(content))
if __name__ == '__main__':
for root, dirs, files in os.walk('.'):
for f in dirs:
if f.endswith('.vba'):
shutil.rmtree(os.path.join(root, f))
for f in files:
if f.endswith(EXCEL_FILE_EXTENSIONS):
parse(os.path.join(root, f))
For further details, have a look at https://www.xltrail.com/blog/auto-export-vba-commit-hook.