Git

I recently took a formal git training and figured this would be a good time to sit down and write out a good workflow for git.

Configuring git

There are 3 levels of configuration that git uses. System, user, and repo. I will be ignoring the system level configuration because I’ve never needed to use it. The user and repo .gitconfig found in the home directory or in the repository root.

Git doesn’t use a standard format for the configs, but it’s close to the ini file format with sections and key value pairs. These can be updated by directly editing the file or by using the git command line.

For example to change your users config:

git config --global section.key value
# Change the default editor in the core section to vim
git config --global core.editor "vim"

which will set the config file with

[core]
    editor = vim

Branching

I was first introduced to git flow git branch strategy. This strategy has not work well for my projects. I tend to work on projects with smaller teams (3-5 people). The overhead introduced by the merge requests and large number of branches tends not to be worth it in these cases.

Preferred method: feature branch and release branches. Going forward I am going to try and get projects to follow the strategy of 1 branch per feature that gets merged back into main and 1 branch per release of the project. Ideally we could use tags for the release, but this seems to make bugfixes on the release more difficult.

The power of squashes

I’m a big believer of commit early, commit often, and push on commit. This is particularly important because I have to develop across a large number of machines (many virtual machines) and I don’t want crashes to ruin my progress. The big down side of this is a horrible commit history with 20 commits that have 3 lines changed and no good commit message.

This is where squashing commits together comes really handy. The idea is to take the 30 commits done in the last 5 hours to fix the bug and combine them into 1 commit that has all of the changes and a coherent commit message for your fellow developers.

git rebase -i HEAD~n 

Where n is the number of commits to squash and -i opens a editor to make it interactive. There should be a list of the commits. Go through and pick 1 to be the main commit and change the rest to s to squash them. You also edit the commit message at the time.

git amend can also be used to add to your previous commit if you don’t want to be constantly pushing to the remote.

Large files

Git was designed to efficiently handle source code files, not large binary blobs. However, projects often need to include large binary blobs inside of code repos. These can be pictures, audio, shell code, external dependencies. However, after a certain size (and number of these blobs) the git repo can slow down significantly.

The best built in solution is to use git large file storage or git-lfs which is a installable extension to git.

To use follow the below steps from https://github.com/git-lfs/git-lfs/wiki/Tutorial#using-git-lfs-migrate


  • Install Git LFS v2.2.1 (or later)
  • Rewrite e.g. all *.mp4 video files on the current branch that are not present on a remote:
git lfs migrate import --include="*.mp4"
  • Alternatively, rewrite all *.mp4 video files on a given branch(es) regardless of whether they are present on a remote (may require a force-push):
git lfs migrate import --include="*.mp4" --include-ref=refs/heads/master --include-ref=refs/heads/my-feature

git lfs migrate import --include="*.lib" --include-ref=refs/heads/master --include-ref=refs/heads/merge-gj-sdk

  • Push the converted repository as a new repository:
git push --force

Git Hooks

I have not done anything super fancy with git hooks yet, but they seem super fun to play with. The idea is a bash script is run on any git action inside of the repo. This can be anything from verifying the code is formatted correctly, to running tests, to installing backdoors in the repo.

I expect I will be playing with this more in the future.

Moving over an airgap

My projects often require some chunk of development and testing be done inside of a secure air gapped network. This can pose some really interesting challenges on how to manage a git repository and how to move external changes into the secure network.

Easy access to the internet, work from home, and powerful tools can dramatically speed up development and debug time. Moving stuff out of a secure network can also be incredibly difficult. So doing as much work low side before moving into a secure space is the best solution if it is possible.

However, moving a code repo across the air gap can be challenging. Here is the best way I’ve seen work.

Git was designed as a fully distributed system, so every clone of a repo has everything it needs to be a full setup. In order to do this method

  1. Clone the repo
  2. zip -> physical media
  3. unzip physical media -> air gapped network
  4. Inside of repo root from run git add remote AIR_GAPPED_GIT_REPO_URL
  5. Then just push the changes

One pro tip to make merging these changes easier, keep the names of the branches inside the airgap and outside the airgap different. Then you can create a merge request of outside-main -> inside-main and use whatever tools you have access to handle the conflicts.