Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Script to create a single-file backup of a git repository
import os
import sys
import re
import shutil
from git import Repo
from zipfile import ZipFile
def create_backup_zip(directory_to_backup):
parent_directory, directory_name = os.path.split(directory_to_backup)
repository = Repo(directory_to_backup)
git_handle = repository.git
git_bundle_file_name = os.path.join(parent_directory, (directory_name + '.bundle'))
git_handle.bundle('create', git_bundle_file_name, '--all')
pattern_for_untracked_files_flag = re.compile(r'^\?+$')
non_staged_files_raw_output = git_handle.status('--porcelain')
if len(non_staged_files_raw_output.strip()) > 0:
non_staged_files = list(filter(
lambda x: pattern_for_untracked_files_flag.match(x[0]) is None,
list(map(lambda _: _.strip().split(), non_staged_files_raw_output.split(os.linesep)))
non_staged_files = []
if len(non_staged_files) > 0:
git_handle.stash('push', '-m', 'git-backup-stash')
stashes = git_handle.stash('list').strip()
stash_list = stashes.split(os.linesep)
patch_files = []
if (len(stashes) > 0) and (len(stash_list) > 0):
patch_files_directory = os.path.join(parent_directory, 'patch-files')
os.makedirs(patch_files_directory, exist_ok=True)
for stash in stash_list:
stash_name = stash.split(': ')[0]
stash_branch_name = stash.split(': ')[1].split()[1]
stash_message = ': '.join(stash.split(': ')[2:])
patch_file_name = ':'.join([stash_name, stash_branch_name, stash_message]) + '.txt'
patch_file_path = os.path.join(patch_files_directory, patch_file_name)
patch_contents = git_handle.stash('show', '-p', stash_name)
with open(patch_file_path, 'x') as f:
zip_file_name = directory_name + '.zip'
zip_file_path = os.path.join(parent_directory, zip_file_name)
with ZipFile(zip_file_path, 'x') as z:
z.write(git_bundle_file_name, arcname=os.path.split(git_bundle_file_name)[1])
for patch_file in patch_files:
z.write(patch_file, arcname=os.path.join('patch-files', os.path.split(patch_file)[1]))
if len(patch_files) > 0:
print(zip_file_name + ' created')
if __name__ == "__main__":
arguments = sys.argv[1:]
if len(arguments) == 0:
directory_to_backup = os.getcwd()
directory_to_backup = arguments[0]
directory_to_backup = os.path.abspath(directory_to_backup)
git_directory = os.path.join(directory_to_backup, '.git')
if os.path.exists(git_directory) and os.path.isdir(git_directory):
print(directory_to_backup + ' is not a git repository')
Copy link

flandersen commented Mar 21, 2020

I want to backup my bare git repos and give it a try, but this script cannot handle those.

Copy link

VarunBarad commented Mar 22, 2020

@flandersen I could not understand what you mean by "bare git repos". Can you please explain a bit more?

Copy link

flandersen commented Mar 22, 2020

@VarunBarad I want to backup my repos on my server which are created the following:

git init --bare <directory>

The --bare flag creates a repository that doesn’t have a working directory, making it impossible to edit files and commit changes in that repository. You would create a bare repository to git push and git pull from, but never directly commit to it.

Copy link

VarunBarad commented Mar 24, 2020

@flandersen I guess bare repos won't have any stashes. In that case all you need should be handled by git bundle

Copy link

flandersen commented Mar 29, 2020

@VarunBarad Thank you! I have tested git-bundle and it works fine.

If you are interested, here is the script I came up with which fits my purpose:

Copy link

VarunBarad commented Mar 30, 2020

Thanks @flandersen

I really liked how you have put the documentation at the top of the script in comments 💯

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment