Splitting Git Repos and Work Directories

all the fun things git can do

Published April 20, 2019

howto git hugo

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)

1
2
3
4
5
6
7
8
9
$ 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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/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
if [ -f index.md ]; then
    REMOTE=$(awk '/^remote:/ {print $2}' index.md |head -n 1)
else
    REMOTE=$(awk '/^\#\+remote:/ {print $2}' index.org |head -n 1)
fi    

if [ -z "${REMOTE}" ]; then
  echo remote is not defined in the front matter of index.md or index.org
  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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
$ 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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$ 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

Previously

Setting up Indieweb Homepage the dream of the nineties is alive on the indieweb

2019-04-19

Next

nerdgeneration/www.sh

2019-04-21

labnotes

Previously

Building a slimmer go Docker container All we need is the binary

2019-04-09

Next

Styling Hugo Diffs Showing just what you changed

2020-01-31