Git Backed Home Directory and Bash History

Posted by jaredtrog in Technology | Tagged , , | Leave a comment

Situation

Have you ever shown up to a newly provisioned server and tried to run a shell alias before performing the sacred ritual of copying your finely tuned environment? I have. Have you ever wanted to find a command you ran on another server but can’t remember which server you ran it on? I have.

To that end I’m working on building a home environment that pulls certain files like .bashrc, .vimrc, and history from a git backed directory. I would also like to skip the manual step of updating the repository as long as a connection to the server is available.

One way to do it would be to simply track my home directory with git and use selective inclusion in the .gitignore to only handle the files I want.

The problem with that solution is it makes it a pain to deal with other git checkouts in my home since all subdirectories are tracked from ~/ down. This solution will have to work from a subdirectory of ~/.

The following process is my first take on this problem.

For this to work as expected, you must already have SSH keys configured between hosts. Otherwise you’ll be entering your password many times as you interact with systems.

Setup a bare remote repository on

This is where all other servers will pull updates. I chose to make this repository in my home directory on a central git server.

ssh git.domain.com
mkdir homedir
cd homedir
git init --bare

Create repository on local machine

Now that we have a place to store files, head to the system that has your perfect home environment create a directory and init a new repository.

mkdir homedir
mkdir homedir/history
cd homedir
git init

Copy files into the git repository

Here are the files I copied into homedir/. Don’t worry about the .bash_history file for now. A bit further down we’ll handle moving the .bash_history file into the homedir/history/.bash_history.HOSTNAME

.bash_aliases
.bash_logout 
.bashrc 
.profile 
.screenrc 
.vimrc 
.ssh/known_hosts  
.gitconfig

Add the files to git

cd homedir/
git add .bash_aliases .bash_logout .bashrc .profile .screenrc .vimrc .ssh/known_hosts .gitconfig

Perform the initial commit

git commit -am "initial commit"

Push the repository to the remote location

git remote add origin git.domain.com:~/homedir
git push origin master

Setup .bashrc and .bash_logout

.bashrc

Add the following to your .bashrc. This will handle ensuring the git repo is up to date and will symlink files that should be controlled into your home directory.

This script does NOT delete existing files. It moves them out of the way with a .old extension

######## Git backed home directory ##########

# Options
## Specify the name of the repo to checkout
GITREPO="homedir"

## Set default variables
GITREPOPATH="${HOME}/${GITREPO}"
GITBIN=`which git`
GITHOST='git.domain.com'
GITFAIL=0

## History related variables
HISTFILE="${HOME}/.bash_history"
NEWHISTFILE="${GITREPOPATH}/history/.bash_history.${HOSTNAME}"

# Execution
## Attempt to update the repo

### Check if git is available and the host is reachable
if test -x ${GITBIN} && test `ping -c1 ${GITHOST} &> /dev/null; echo $?` -eq 0; then

    ### Check if the specified repo exists and is backed by git
    if test -d ${GITREPOPATH} && test -d ${GITREPOPATH}/.git; then

        ### Change into the repo directory, execute git pull, and change back to ~
        cd ${GITREPOPATH}
        git pull &> /dev/null || echo "git pull for homedir failed"
        cd ~
    else
        echo "${GITREPOPATH} not properly initialized!" && GITFAIL=1
    fi
else
    echo "Ensure git is available and the remote host is accessible!" && GITFAIL=1
fi

## Symlink all files in the repo into the home directory
if test ${GITFAIL} -eq 0; then
    for FILE in `find ${GITREPOPATH} -path "${GITREPOPATH}/.git" -prune -o -path "${GITREPOPATH}/history" -prune -o -type f -print`; do
    EXISTING_FILE=`echo ${FILE}|sed "s/${GITREPO}///g"`;

        ### If the file exists in the home directory and is not a symlink
        if test ! -h ${EXISTING_FILE}; then

            #### Move the old file out of the way and make it a symlink to the git backed file
            test -f ${EXISTING_FILE} && mv ${EXISTING_FILE} ${EXISTING_FILE}.old.${$}
            ln -s ${FILE} ${EXISTING_FILE}
        else
            true
            # echo "Doing nothing with: ${FILE} and ${EXISTING_FILE}" 
        fi
    done
fi

## History
if test ! -h ${HISTFILE}; then
    # link up
    if test -f ${HISTFILE}; then
        mv ${HISTFILE} ${NEWHISTFILE}
        ln -s ${NEWHISTFILE} ${HISTFILE}
    else
        touch ${NEWHISTFILE}
        ln -s ${NEWHISTFILE} ${HISTFILE}
    fi
else
    rm ${HISTFILE}
    ln -s ${NEWHISTFILE} ${HISTFILE}
fi
############# End git backed homedir ###############

.bash_logout

Commit the changes when you logout. Add this part to your .bash_logout.

# Handle git clone
GITBIN=`which git`
GITHOST='git.domain.com'
GITREPOPATH="${HOME}/homedir"

if test -d ${GITREPOPATH} && test -x ${GITBIN} && test \`ping -c1 ${GITHOST} &> /dev/null; echo $?\` -eq 0; then
    cd ${GITREPOPATH}
    # Add any history files
    if test -d ${GITREPOPATH}/history; then
        git add ${GITREPOPATH}/history/.bash_history*
    fi
    git commit -am "auto commit ${HOSTNAME}"
    git push &> /dev/null || echo "git push for homedir failed"
fi  

Set up a new host

Now connect to another server, checkout the git repository, and source .bashrc

git clone git.domain.com:~/homedir
source homedir/.bashrc

Your should now see all the files that were added into ~/homedir linked into your home directory and the originals moved to .old and history files linked into ~/homedir/history/.bash_history.HOSTNAME

Complete

Leave a Reply

Your email address will not be published.

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>