Tips for putting ~ under source control

I have $HOME under git. The first line of my .gitignore file is

/*

The rest are patterns to not ignore using the ! modifier. This first line means the default is to ignore all files in my home directory. Those files that I want to version control go into .gitignore like this:

!/.gitignore
!/.profile
[...]

A trickier pattern I have is:

!/.ssh
/.ssh/*
!/.ssh/config

That is, I only want to version .ssh/config - I don't want my keys and other files in .ssh to go into git. The above is how I achieve that.

Edit: Added slashes to start of all paths. This makes the ignore patterns match from the top of the repository ($HOME) instead of anywhere. For example, if !lib/ was a pattern (don't ignore everything in the lib directory) and you add a file .gitignore, previously the pattern (!.gitignore) was matching that. With the leading slash (!/.gitignore), it will only match .gitignore in my home directory and not in any subdirectories.

I haven't seen a case where this makes a practical difference with my ignore list, but it appears to me to be more technically accurate.


What I do (with the same objectives) is to put my configuration files in a subdirectory ~/lib and have symbolic links in my home directory, e.g., .emacs -> lib/emacs/dot.emacs. I only keep configuration files that I wrote explicitly under version control; my home directory contains plently of automatically-created dot files that are not under version control. Thus ~/lib is under version control, and my home directory is not.

I have a script that creates the symbolic links from the files under ~/lib. When I create an account on a new machine, I populate it by checking out ~/lib and running that script.

My experience is with CVS, not git, so it's not 100% transferable. One of the reasons I didn't put my home directory directly under CVS is that ~/.cvsignore would apply to all my CVS checkouts and not just my home directory; git doesn't have this problem. The downside of that approach compared with having the home directory under version control is that you can't use git status to distinguish between a file that you've explicitly decided to ignore (which would be listed in the ignore file, so not displayed) and a file that you have no opinion about (which would be displayed with a ?).

Some files need to be different on different machines. I put them in a directory called ~/Local/SITENAME/lib and either create symbolic links for them as well or (for file formats that support it) have an include directive in the file under ~/lib. I also have a symbolic link ~/Here -> ~/Local/SITENAME. Since git, unlike CVS, is designed to support mostly-similar-but-not-identical repositories, there may be a better way to manage machine-specific files. A few of my dot files are in fact not symbolic links, but automatically generated from content under ~/lib and ~/Here.


We can use Git's ability to continue tracking files even if they're listed in .gitignore. So, this is enough for .gitignore:

$ cat .gitignore
/*

For each file that you want to track, run add -f (the -f parameter overrides ignoring both in .gitignore and .git/info/exclude):

git add -f .gitignore
git add -f .profile
git add -f .zshrc
git add -f .ssh/config

Once having indexed a file, Git will track all changes despite the fact that the file is ignored. The same works for a directory, but only for the files that are actually present:

git add -f somedirname

If you want to track a whole directory with all new files that appear in it, it can be excluded from .gitignore in a way, described in the answer by camh:

!/somedirname

If you ever want to stop tracking a file, this commands removes a file from Git's index but leaves it untouched on the hard drive:

git rm --cached .ssh/config