Splitting Git Repos and Work Directories

all the fun things git can do

Published April 20, 2019 #howto #git #hugo

The code from this article can be found at https://github.com/wschenk/split-git

I found a tutorial on how to manage your dotfiles, that works by splitting up the git repository (normally the .git directory) from the work directory. Since I have a lot of code that I put in my tutorials, I adapted the technique to have individual article directories mirrored in their own github repository.

Repositories and Work Directories

The normal usage of git is to type git clone <remote> to get a copy of the local directory, mess with stuff, and then add and commit your changes. If you have multiple branches, you switch the files in your work directory to a different branch, but you are still using the same repository.

The structure looks like this (you’ll see more files in the lsit if you do this command, I edited them out for clarity)

$ git clone https://github.com/wschenk/willschenk.com
$ ls -la willschenk.com
drwxr-xr-x 1 wschenk wschenk  280 Apr 20 14:32 .
drwxr-xr-x 1 wschenk wschenk  870 Apr 20 14:58 ..
drwxr-xr-x 1 wschenk wschenk  102 Apr 18 13:11 content
drwxr-xr-x 1 wschenk wschenk  204 Apr 20 14:54 .git
-rw-r--r-- 1 wschenk wschenk  501 Apr 20 14:32 .gitignore
drwxr-xr-x 1 wschenk wschenk   64 Apr 20 14:22 layouts
drwxr-xr-x 1 wschenk wschenk   18 Mar 31 22:21 themes

The .git directory inside of this folder is where the git database lives, all of the commits and history and branches and all that. At the same level you have the working copy, which in this case is the master branch.

What I’d like to do is to keep this whole structure intact, but also have the files in content/articles/2019/splitting_git_repos_and_workdirectories/ tracked in an additional github repository for better sharing and collaboration.

Splitting

This script is meant to be used as a replacement to the git command line on the shell to interact with the mirrored repository.

For this to work great create a remote repository somewhere like GitHub and then put the resulting repostory in the front matter. (I use yml so adjust the awk parsing if you use something else.)

  1. First it checks to see if there’s a local .gitignore, and if not creates one.
  2. Then it loads the .local-git file where we are checkout out the repository. If this goes away we don’t care, because…
  3. It pulls down the repo from the root.
  4. Then it passes all your commands to git pointing to the seperate repository and with the working dir set to the current directory.

seperate-git.bash:

#!/usr/bin/env bash

# Ensure that we dont inadvertantly checkin any node node_modules
# or the file that we use to track our local_git repository
if [ ! -f .gitignore ]; then
  cat > .gitignore << ENDIGNORE
node_modules
.local_git
ENDIGNORE
fi

# Look inside of our post to load up where the remote
# is configured, bail if not set
REMOTE=$(awk '/^remote:/ {print $2}' index.md |head -n 1)

if [ -z "${REMOTE}" ]; then
  echo remote is not defined in the front matter of index.md
  exit 1
fi

LOCAL_GIT=$(cat .local_git 2> /dev/null)

# If blank or doest exist checkout the bare repository
if [ -z "${LOCAL_GIT}" ] || [ ! -d "${LOCAL_GIT}" ]; then
  LOCAL_GIT=$(mktemp -d)
  echo Pulling down remote git repository into $LOCAL_GIT
  git clone --bare --progress $REMOTE $LOCAL_GIT
  echo $LOCAL_GIT > .local_git
fi

git --git-dir=${LOCAL_GIT} --work-tree=. $@

Running

Here’s a comparison of what running looks like using the new command

$ bash seperate-git.bash status
Pulling down remote git repository into /tmp/tmp.p4KSGNKQNh
Cloning into bare repository '/tmp/tmp.p4KSGNKQNh'...
warning: You appear to have cloned an empty repository.
On branch master

Initial commit

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

        .gitignore
        index.md
        seperate-git.bash

nothing added to commit but untracked files present (use "git add" to track)

$ bash seperate-git.bash remote -v
origin  git@github.com:wschenk/split-git.git (fetch)
origin  git@github.com:wschenk/split-git.git (push)

And compare that with the regular git command:

$ git status
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:   index.md

no changes added to commit (use "git add" and/or "git commit -a")

$ git remote -v
origin  git@github.com:wschenk/willschenk.com.git (fetch)
origin  git@github.com:wschenk/willschenk.com.git (push)

And if this script actually works, you should be able to see the results here: https://github.com/wschenk/split-git

References

  1. https://www.atlassian.com/git/tutorials/dotfiles
The code from this article can be found at https://github.com/wschenk/split-git

Read next

See also

Easy scraping with httpie and jq

Pulling my GitHub starred repositories into Hugo

I recently saw a tweet mentioning the combination of using HTTPie (a command line HTTP client), jq (a lightweight and flexible command-line JSON processor) and Gron (Make JSON greppable!) was “all you needed to build a scraper.” Lets see if that’s true.

Read more

Automating hugo builds using CircleCI

Let someone else run your build server

Here's a simple CircleCI configuration to pull down the latest version of your hugo site on GitHub commits, build it, and then push it to github pages.

Read more

Adding a CMS to hugo

Static doesn’t mean dead

Just because we have a static site doesn’t mean that we can’t have an admin tool to write and edit posts! Lets go through how we can add the NetlifyCMS to the site and host it wherever we want. In my case I’m storing the code on GitHub and also serving the pages from GitHub Pages. Netlify also seems like a really promising company with a number of other services that they offer, so I’d encourage you to check it out.

Read more