How to zip & backup changes in the git repository

If you're reading this you probably been in those situations where you're finishing for the day, and your current work is still in progress. I'm a paranoid person, and whenever this happen I keep on thinking that I will lose all that work due to my laptop dying, or it getting stolen.

One tip is to keep your commits small. Which is a very good tip, but some tasks are hard to split into small parts. Even if the task is small maybe you have just started it. You could rush committing some changes, but personally I like to take my time with commits, double checking that I have not missed anything, or there is nothing else that could be improved. Just doing a WIP commit to me looks messy, and the next time I start working I will need to undo it.

Another solution would be to archive your project, and copy-paste it to some cloud based location. Doing this manually is a PITA. First, you have to make sure to exclude vendor/log/cache/etc directories to keep the performance, and small size. When you have to move that archive.

Everyone knows that programmers are lazy, and we avoid doing the same thing multiple times. We're always writing all kinds of scripts to automate the boring parts of life, so we could focus on what is interesting.

Solution

After playing around I made myself a bash function which takes all the modified files in the git repository, archives them as a zip file (encrypting it if requested), and then backups to a different location (if requested). Now I just need to run one of the commands in the terminal to get the results I want:

# for making a zip archive
zipgitchanges

# for making an encrypted zip archive
zipgitchanges e

# for making an encrypted zip archive, and then backing it up
zipgitchanges eb

Please keep in mind that the backup location is hardcoded in the function. You will need to change it before using it. You could also replace it with an environment variable if you need more flexibility. You can see the function below:

# Zip modified files in git repository
# WARNING: it expects the git alias `mod` to be something like:
# `git ls-files --modified --others --exclude-standard`
# WARNING: Keep in mind that it won't zip the staged files.
# Usage:
#   zipgitchanges # for generating zip
#   zipgitchanges e # for generating encrypted zip
#   zipgitchanges eb # for generating encrypted zip and copy it to location
function zipgitchanges() {
    filename="$(printf '%s---%s' "${PWD##*/}" "$(date +"%Y-%m-%d--%H-%M-%S")").zip";

    # check if zip has to be encrypted
    encrypt=;

    if [[ $1 == *e* ]]; then
        encrypt='--encrypt';
    fi;

    # zip files
    zip $encrypt $filename $(git mod);

    # copy/backup to different location
    if [[ $1 == *b* ]]; then
        if [ ! -f $filename ]; then
            printf "\n\nCan not backup, file $red$filename$reset not found!\n";

            return;
        fi;

        backupLocation="/Users/ifdattic/Google Drive/gitbackups";

        mv $filename "$backupLocation";

        printf "\n\nGit changes backed up to $green$backupLocation$reset as $green$filename\n";
    fi;
}

I will go over it explaining that some of the lines does. If you understand the script well you can skip the explanation.

Function explanation

filename="$(printf '%s---%s' "${PWD##*/}" "$(date +"%Y-%m-%d--%H-%M-%S")").zip";

First it sets the filename which is made from the project name (the name of the directory the command is run from), and the full date with a divider between them. The generated file name might look something like project-name---2016-11-15--17-04-33.zip.

# check if zip has to be encrypted
encrypt=;

if [[ $1 == *e* ]]; then
    encrypt='--encrypt';
fi;

Next we check if the archive needs to be encrypted. If the e letter is provided with the command the --encrypt flag would be added to the zip command. Please keep in mind that zip security is weak, so this is more like an extra layer to avoid bored snoopers.

# zip files
zip $encrypt $filename $(git mod);

Now that the script has all the required information it makes an archive. I use the git mod command here to get all the files that changed from the last commit. The mod is an alias for ls-files --modified --others --exclude-standard. If you don't want to use the alias you can just use the extended command in here. This would mostly depend on how you want it to work, and will need to modify for your own usage. I decided to use the alias, because I might use it outside this function (to check changed files), and at the same time if I moved to a different format for listing those files I would only need to change the alias.

# copy/backup to different location
if [[ $1 == *b* ]]; then
    if [ ! -f $filename ]; then
        printf "\n\nCan not backup, file $red$filename$reset not found!\n";

        return;
    fi;

    # ...
fi;

The backup is optional, as you might just want to make an archive before making some changes with an unknown results (but you already have some changes you want to keep). If a letter b is provided to the command, it will check that the zip archive is available (for example, it could fail by providing incorrect passwords for encryption).

backupLocation="/Users/ifdattic/Google Drive/gitbackups";

mv $filename "$backupLocation";

printf "\n\nGit changes backed up to $green$backupLocation$reset as $green$filename\n";

If the file exists, it will move it to the backup location (which is hardcoded in this case), and will inform you with a colored output (you need to have colors set as variables in your bash profile, e.g., green="\e[1;32m";). You could use environment variable for backup location, but I hardcoded it as right now I have a hard time imagining a scenario where I would need to change it.

Finishing touches

That function name is long to type all the time you want to backup your changes. For that reason you can make an alias in your bash profile:

alias zgc="zipgitchanges eb"

Now every time you run zgz it will make an encrypted zip archive, and back it up.

Where you put the function, and alias depends on your machine. Most of the time you will want to edit your .bash_profile file. You can check my dotfiles for an example.

Now you can have a peace of mind by knowing that your work is safe until you will resume it.

Thanks for reading, and if you found the article useful please share it.

Posted in: Technical, Tips, git

Comments