Tracking your home directory in Git Part 2

4 minute read

In Part 1 we created a repo to store our home directory and set it up on our first machine. That would be great if all we want to do is keep track of historical changes to our home directory files, but what if we’re trying to keep multiple workstations in sync? Let’s clone the repo to a second workstation and also commit any unique configurations from that workstation to our repo.

Cloning the repository on a second machine

It’s very possible we have some configurations that we like in our current home directory that we haven’t introduced to the repo yet. In that case we don’t want to just destroy those changes by doing a hard git reset like we did in Part 1.

Make sure you have a backup of your current home directory and test this process somewhere less important first. You can definitely wreck your home directory if you screw up past this point.

Just like last time we’re going to initialize an empty repository in our home directory, set up our origin, and fetch.

cd ~
git init
git remote add origin git@github.com:example/home-dir.git
git fetch
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 7 (delta 0), reused 7 (delta 0), pack-reused 0
Unpacking objects: 100% (7/7), 1.28 KiB | 41.00 KiB/s, done.
From github.com:ScottEvtuch/home-dir
 * [new branch]      linux      -> origin/linux
 * [new branch]      master     -> origin/master
 * [new branch]      windows    -> origin/windows

Next we’ll need to create a local branch that tracks our remote. We don’t want to do a checkout on this branch because that would destroy anything unique in our current home directory.

git branch linux origin/linux
Branch 'linux' set up to track remote branch 'linux' from 'origin'.

At this point we’re going to use some low-level git commands to effectively “checkout” this branch without actually modifying any files in our working directory. The end result should be that we have our local “linux” branch checked out and all of our local differences appear as unstaged changes.

This is very similar to what we did in Part 1 except instead of doing a soft git reset we are actually forcing HEAD to point to our linux branch so we can add commits as if we had checked it out normally.

git symbolic-ref HEAD refs/heads/linux
git reset HEAD .

Resolving conflicts with the repository

Now that we’re on the branch where we want our changes, we can run a diff and look for things to commit.

git diff
diff --git a/.gitconfig b/.gitconfig
index 6249887..d79918e 100644
--- a/.gitconfig
+++ b/.gitconfig
@@ -1,3 +1,5 @@
 [user]
  name = Scott Evtuch
  email = scott@example.net
+[alias]
+ com = checkout master
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 461f989..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-*
-!/.gitconfig
-!/.gitignore

We’re missing our .gitignore file, which is expected. We can ignore that for now since it will be fixed when we hard reset.

Also we have an alias in our .gitconfig file that isn’t present in the repo’s version. Let’s stage the file and commit it.

git add .gitconfig
git commit -m "Add useful git alias"

Once we’re satisfied that we have all of the changes in existing files we need committed, do a hard git reset to make sure we’re fully up to date.

git reset --hard
HEAD is now at e2dbae4 Add useful git alias

Adding new files

We might also have some new files we want to add to the repo that only exist on this machine right now. To add those we’ll need to modify .gitignore to include them.

For this example I’ll be adding my .ssh configuration file. Keep in mind directories need to be added as well as files or git will not traverse those directories.

.gitignore

*
!/.ssh
!/.ssh/config
!/.gitconfig
!/.gitignore

Git status should now show that we have a change to commit and an unstaged file.

git status
On branch linux
Your branch is up to date with 'origin/linux'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   .gitignore

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

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

Since we’ve been really specific in our .gitignore file we can safely stage “all” files and do our commit.

git add .
git commit -m "Add SSH configuration"

Now you can push your changes up to remote and pull them down on other machines as well.

git push

Conclusion

At this point in my example we now have two workstations that have the “linux” branch checked out in their respective home directories. Changes from either can be committed and push/pulled to keep things in sync. If we wanted to we could also split off this branch into a “linux-work” branch for configurations that are specific to a corporate-issued workstation, for example.

In Part 3 I explore how we deal with changes that happen in the various branches we’ve set up, and how we can move them up into master.