Version control your install directory - GIT

Image credit: Jacob

What problem are we trying to Solve?

Context: Desktop software installed on client computer, inside a private network and no access to internet.

Use Cases:

  • Shipping and applying multiple patches for a given version of the software.
  • Reverting a patch
  • Keeping track of patches installed
  • Check for sanity of the installation and tack manual edits (configuration files etc..)
  • User can apply a temporary patch on a server (i.e. common machine) and test, once done revert

How are we trying to solve?

Bring the content of the install directory under version control. We will be using GIT version control, to explain how it could be achived.

PreRequisite “git” should be installed and available in the path

Why use GIT?

Git is a distributed version control system designed to handle everything from small to very large projects with speed and efficiency.

So, with Git any local folder can be converted into a git repository by just running the init command.

D:\Foo\1.0.0>git init
Initialized empty Git repository in D:/Foo/1.0.0/.git/

Now your installed directory is version controlled. You can add files, commit, create branch(s), switch branches etc… Since Git is distributed - N0 Server nor Internet required 😄

At any point of time, you could use status to check if the install directory has any modified, new or deleted files.

D:\Foo\1.0.0>git status
On branch base_install
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:   config/params.toml

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        config/menus.toml
        config/languages.toml

You could use git diff command to compare changes across two branches like base_install vs patch_1

D:\Foo\1.0.0>git diff base_install patch_1
diff --git a/Config/params.toml
index b32de60..e8e1694 100644
--- a/Config/params.toml
+++ b/Config/params.toml
@@ -29,7 +29,7 @@
-  main_menu = {align = "l", show_logo = true}
+  main_menu = {align = "l", show_logo = false}

Creating a branch is a trivial operation. As a user you could

  • Create a new (personal) branch from current (master) branch
  • Apply the patch files (edit or new) -> Commit your changes to your personal branch
  • Run your tests
  • Once you are done, switch to the original (master) branch

If you observe - you don’t have to delete your patch - at any point of time when you have server time you could come back and switch to your branch…

D:\Foo\1.0.0>git checkout jac_logo_patch
Switched to branch 'jac_logo_patch'

Installer workflow and Scripts sequence…

POST INSTALL

  1. Installer should copy this file into the root of destination directory and execute
  2. Example: “D:\Foo\«_Build_Number_»”
  3. Run this file post installation

Steps:

  1. Initialize git.
  2. Create a .gitignore file.
  3. Add contents into .gitignore.
  4. Create a branch base_Install.
  5. Commit all contents into git repo with msg Base Install Commit.
@echo off
echo Initializing Git
call git init

echo Add git ignore files
call touch .gitignore

echo Add files to .git ignore for installer cmd files
call PostInstall.cmd >> .gitignore

echo Add all the files and create a initial commit named Base Install. This would take a while!!
call git add -A && git commit -m "Base Install Commit"

echo Creating a new branch base_install
call git branch base_install

PATCH INSTALLER

PRE INSTALL

Copy this file into the root of destination directory - D:\Foo\«Build_Number»”

  1. Check if branch exists
  2. Add all existing UNTRACKED FILES into the current branch
  3. Add add a commit message PrePatch_<PATCH_NAME>
  4. Add the current branch to a known file LastKnownWorkingBranch; useful for un-installation
  5. Create a new branch PATCH_<PATCH_NAME>
  6. Switch to new branch with PATCH_<PATCH_NAME>
  7. Apply the patch using git am. Note: am would allow you to sign off an applied patch. This maybe useful for future reference.
  8. Add all existing UNTRACKED FILES into new/current branch i.e. PATCH_<PATCH_NAME>
  9. Done - we have a new branch with all patches
REM TODO: This could be an enviornment variable
set patchName=Patch_1
set branchName=Patch_%patchName%

echo Check if git installed
call git --version    
if ERRORLEVEL 1 (
    echo Git not installed!!!
    goto :Cleanup
)

echo Check if valid git repository
call git rev-parse --is-inside-work-tree
if ERRORLEVEL 1 (
    echo Not a valid git repo : %cd%
    REM TODO - Should we create a new git repo? or fall back scripts to use .keep solution
    goto :Cleanup
)

set commitMsg="PrePatch_%patchName%"
echo Add all un-tracked files and create a 
call git add -A && git commit -m %commitMsg%


echo Add the current branch name into a temporary file - LastKnownWorkingBranch
call git symbolic-ref --short HEAD > LastKnownWorkingBranch

echo Check if branch exits
git rev-parse --verify %branchName%
if ERRORLEVEL 0 (
    echo Branch exists : %branchName%
)

echo Create a new branch
git branch %branchName%

echo Checkout the new branch
git checkout %branchName%

echo Apply the .git patch
git am --signoff < %patchName%.patch

:Cleanup
echo exit

User can always go back to the base install by switching branches.

POST INSTALL

Steps

  1. Now we are in the new branch for the Patch
  2. And install script has already installed all the patch files
  3. Just commit all the changes and we are good to go!!
  4. Patch installation done!!
REM TODO: This should be an enviornment variable
set patchName=Patch_1
set branchName=Patch_%patchName%

set commitMsg="Patch_%patchName%"
echo Add all un-tracked files and create a 
call git add -A && git commit -m %commitMsg%

echo Patch installed and avaialble in branch %branchName%

PATCH UN-INSTALLER

Steps

  1. Read the lastKnownWorkingBranch from a file
  2. git checkout lastKnownWorkingBranch
  3. Voila thats it!!

Note: File “lastKnownWorkingBranch” file should be under .gitignore (?) so that its shared across branches and its updated only by the patch installer.

for /f "delims=" %%a in ('type LastKnownWorkingBranch') do (
set lastKnownWorkingBranch=%%a
break
)

echo lastKnownWorkingBranch: %lastKnownWorkingBranch%


echo Checkout the lastKnownWorkingBranch: %lastKnownWorkingBranch%
git checkout %lastKnownWorkingBranch%

echo Voila we have reverted the previous install

ADVANTAGES

  • User could compare what has changed across branches aka patches
  • User could always go back to base_install at any point of time
  • Files are never deleted
  • Uninstall is just switching a branch - no file deletes.
  • Any number of branches can be created for personal testing.
Jacob Aloysious
Jacob Aloysious
Software Enthusiast

35yr old coder, father and spouse - my interests include Software Architecture, CI/CD, TDD, Clean Code.

Related