As a distributed version control system, Git always copies down the entire repository history to the client when you do a
git clone or
git fetch. For repositories containing large files and/or long commit histories, this clone process can take a long time, as every file version that ever existed has to be downloaded.
For Excel workbook repositories, it is only a question of time before this becomes a real problem: If your repository contains one 5MB-sized workbook which you commit once every business day, you end up with roughly 250 x 5MB = 1.3GB worth of workbook versions after one year. This figure is exaggerated as Git uses file compression, but it gives you an idea of how much data you have to download if you (or someone else) performs a
git clone or
git fetch; and it will only grow bigger as time goes by.
Git Large File Storage (LFS) is an open-source Git extension developed by Atlassian, GitHub, and other contributors. Git LFS reduces the impact of large files by downloading them lazily: Files, that are tracked by LFS, are only downloaded when you check out a specific version instead of downloading every file version that ever existed during the clone or fetch process.
This helps tremendously when managing our Excel workbook repositories: When you do a
git clone or
git pull, Git only downloads the head version so that you can get to work straight away (instead of having to wait for all those old versions that you are probably not interested in anyway at that point).
Git LFS handles large files by replacing them with tiny pointer files. These pointer files act as references to the actual files which are stored somewhere else. For your normal Git operations, you never get to see these pointer files as Git LFS handles them automatically:
When you add a file via
git add, Git LFS replaces its contents with a pointer, and writes the file contents to a local Git LFS cache.
When you push new commits to the server, any Git LFS files referenced by the newly pushed commits are transferred from your local Git LFS cache to the remote Git LFS store tied to your Git repository.
When you checkout a commit that contains Git LFS pointers, they are replaced with files from your local Git LFS cache, or downloaded from the remote Git LFS store.
In your local working copy you only see your actual file content. You can use
git add and
git commit as normal, there is no change to your normal Git workflow.
git clone and
git pull operations are faster because Git only downloads the versions of large files referenced by commits you check out.
Depending on your Git server system, Git LFS is either enabled by default or you might need to enable Git LFS manually. Please check the documentation for your Git host system.
Git LFS is a Git extension that you only need to install once. Once installed and initialised, Git LFS will bootstrap itself automatically when you clone a Git LFS repository.
Download the Git Large File Storage extension from the Git LFS project website and install it by double-clicking
git-lfs-windows-<version>.exe (or follow the Mac installation instructions if you are on a Mac).
git lfs install to initialize Git LFS:
C:\Users\Bjoern>git lfs install Git LFS initialized.
To create a new Git LFS-aware repository from scratch, you need to run
git lfs install after creating the repository:
C:\Users\Bjoern\Developer\workbooks>mkdir lfs C:\Users\Bjoern\Developer\workbooks>cd lfs C:\Users\Bjoern\Developer\workbooks\lfs>git init Initialized empty Git repository in C:/Users/Bjoern/Developer/workbooks/lfs/.git/ C:\Users\Bjoern\Developer\workbooks\lfs>git lfs install Updated git hooks. Git LFS initialized.
git lfs install sets up a handful of special Git hooks in your repository (pre-push, post-checkout, post-commit, post-merge). These hooks take care of the Git LFS relevant operations so that they get executed when using the standard Git commands.
Git LFS is now initialised for your repository. Next step is to tell LFS using
git lfs track that we want to track Excel files (workbooks and addins):
C:\Users\Bjoern\Developer\workbooks\lfs>git lfs track "*.xls*" Tracking "*.xls*" C:\Users\Bjoern\Developer\workbooks\lfs>git lfs track "*.xla*" Tracking "*.xla*"
Note that the quotes around “.xls” and “.xla” are important (otherwise the wildcard will be expanded by your shell, and individual entries will be created for each .xls* file in your current directory).
The patterns supported by Git LFS are the same as those supported by .gitignore. For instance, if you store all your Excel workbooks in a
Workbooks subfolder within your repository, you can simply track the entire subfolder using:
C:\Users\Bjoern\Developer\workbooks\lfs>git lfs track "Workbooks/*
git lfs track, you will notice a new file named
C:\Users\Bjoern\Developer\workbooks\lfs>git status On branch master Initial commit Untracked files: (use "git add <file>..." to include in what will be committed) .gitattributes nothing added to commit but untracked files present (use "git add" to track)
Git LFS uses
.gitattributes, which itself is a special Git file. You do not need to worry about the content of
.gitattributes, however you need to make sure that you always add, commit and push
.gitattributes (if you use
git add . this is already being taken care of automatically).
Create a remote repository (for example on GitHub cloud which is LFS-enabled by default) and configure the remote of your local repository. I use our example Excel LFS repository in the following example. Feel free to do the same for cloning and pulling, or simply fork the repository so that you can write to it, too.
C:\Users\Bjoern\Developer\workbooks\lfs>git remote add origin https://github.com/xlwings/workbooks-lfs.git
You can commit and push as normal to a repository that contains Git LFS content. The only difference you see is some additional output from git push as the Git LFS content is transferred to the server. Create a workbook named
git commit and
git push it all:
C:\Users\Bjoern\Developer\workbooks\lfs>git add . C:\Users\Bjoern\Developer\workbooks\lfs>git commit -m "First commit" [master (root-commit) 13cfa90] First commit warning: CRLF will be replaced by LF in .gitattributes. The file will have its original line endings in your working directory. 3 files changed, 7 insertions(+) create mode 100644 .gitattributes create mode 100644 Book1.xlsb create mode 100644 Book2.xls C:\Users\Bjoern\Developer\workbooks\lfs>git push -u origin master Git LFS: (2 of 2 files) 14.72 MB / 14.72 MB Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (4/4), done. Writing objects: 100% (5/5), 568 bytes | 0 bytes/s, done. Total 5 (delta 0), reused 0 (delta 0) To https://github.com/xlwings/workbooks-lfs.git * [new branch] master -> master Branch master set up to track remote branch master from origin.
As you can see from the output, we pushed 14.72MB worth of workbooks to the remote server. In order to see the difference Git LFS makes, let’s delete most of the workbook content to bring down the workbook file size and push a new commit:
C:\Users\Bjoern\Developer\workbooks\lfs>git status On branch master Your branch is up-to-date with 'origin/master'. Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: Book1.xlsb modified: Book2.xls no changes added to commit (use "git add" and/or "git commit -a") C:\Users\Bjoern\Developer\workbooks\lfs>git add . C:\Users\Bjoern\Developer\workbooks\lfs>git commit -m "Deleted a lot of stuff" [master 3453320] Deleted a lot of stuff 2 files changed, 4 insertions(+), 4 deletions(-) C:\Users\Bjoern\Developer\workbooks\lfs>git push origin master Git LFS: (2 of 2 files) 205.84 KB / 205.84 KB Counting objects: 4, done. Delta compression using up to 4 threads. Compressing objects: 100% (4/4), done. Writing objects: 100% (4/4), 561 bytes | 0 bytes/s, done. Total 4 (delta 0), reused 0 (delta 0) To https://github.com/xlwings/workbooks-lfs.git 13cfa90..3453320 master -> master
The head commit now contains two relatively small workbook files, whereas its parent commit is nearly 14MB big. Without LFS, a
git clone downloads the entire repository with both versions. As we have now Git LFS installed, Git should only download the significantly smaller head commit by default, resulting in a much faster
Clone the remote repository into
lfs-2 (or any other available folder). You can start straight away with our example Excel LFS repository:
C:\Users\Bjoern\Developer\workbooks>git clone https://github.com/xlwings/workbooks-lfs.git lfs-2 Cloning into 'lfs-2'... remote: Counting objects: 9, done. remote: Compressing objects: 100% (8/8), done. remote: Total 9 (delta 0), reused 9 (delta 0), pack-reused 0 Unpacking objects: 100% (9/9), done. Checking connectivity... done. Downloading Book1.xlsb (8.0 KB) Downloading Book2.xls (203 KB)
Voilà! As expected, Git LFS makes
git clone download only the Excel workbook files in the head commit. It’s worth mentioning though, that there is also an explicit
git lfs clone command. This gives you an even better performance, if you’re cloning a repository with a large number of LFS files:
C:\Users\Bjoern\Developer\workbooks>git lfs clone https://github.com/xlwings/workbooks-lfs.git lfs-3 Cloning into 'lfs-3'... remote: Counting objects: 9, done. remote: Compressing objects: 100% (8/8), done. remote: Total 9 (delta 0), reused 9 (delta 0), pack-reused 0 Unpacking objects: 100% (9/9), done. Checking connectivity... done. Git LFS: (2 of 2 files) 205.84 KB / 205.84 KB
git lfs clone command waits until the checkout is complete, and then downloads any required Git LFS files as a batch. This takes advantage of parallelised downloads, and dramatically reduces the number of HTTP requests and processes spawned. This is especially important for improving performance on Windows.
git clone, you can pull from a Git LFS repository using the normal
git pull command. Any required Git LFS files will be downloaded as part of the automatic checkout process once the pull completes. No explicit commands are needed to retrieve Git LFS content. Should the checkout fail for an unexpected reason, you can download any missing Git LFS content for the current commit with
git lfs pull.
Git LFS is an essential tool for managing and maintaining a healthy Excel workbook repository. It ensures a stable, predictable and smooth user experience and integrates seamlessly into your existing workflow. If you do not use Git LFS for your Excel repositories, I highly recommend it.
If you want to migrate from an existing non-LFS repository to a LFS-aware repository, please check back in here as we will cover this in depth in one of our next blog posts. If you have any questions, please comment below or get in touch: firstname.lastname@example.org.