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.)
- First it checks to see if there’s a local .gitignore, and if not creates one.
- Then it loads the
.local-git
file where we are checkout out the repository. If this goes away we don’t care, because… - It pulls down the repo from the root.
- 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
- https://www.atlassian.com/git/tutorials/dotfiles