Version controlling your .Rprofile, .gitconfig and other dotfiles.
Dotfiles are an important part of coding on Linux and macOS. In my work, I find myself not only working on my macOS laptop but on several Linux servers. Each of these requires dotfiles to configure my R (.Rprofile
), git (.gitconfig
and .gitignore_global
), ssh (.ssh/config
but not key files), vim (.vimrc
) and shell (.zshrc
, .bashrc
, .bash_profile
, etc.).
My current .Rprofile
is focused on package development. It loads devtools
automatically when using R interactively, and give defaults when creating new R packages:
if (interactive()) {
suppressMessages(require(devtools))
}
options(
usethis.full_name = "Rick M Tankard",
usethis.description = list(
`Authors@R` = 'person("Rick M", "Tankard",
email = "rickmtankard@gmail.com", role = c("aut", "cre"),
comment = c(ORCID = "0000-0002-8847-9401"))',
License = "MIT + file LICENSE",
Version = "0.0.0.9000"
),
usethis.protocol = "ssh"
)
Many R users and package developers use git for version control. Why not extend that to your dotfiles? There are several motivations for version controlling dotfiles with git:
- Backup. My shell dotfiles have been curated over many years of work and between institutions; I wouldn’t want to lose them because I change organisation.
- Sync. There are settings I want to use regardless of the host device. Git helps to keep them in sync with a
push
command on one device, and apull
command on the others. Platform and host-specific commands are available in several dotfiles including.Rprofile
. For others you can have specific dotfiles for each device. - Version control. It’s good to track what’s changed and makes it easier to diagnose problems as they arise.
Setup
Choose a location to store the git repository for your dotfiles. This doesn’t need to be, and probably shouldn’t be your home directory. I like to keep all my GitHub repositories in one place, with the end of the path matching the GitHub URL. For me, this directory is:
cd ~/Documents/git/github/trickytank/dotfiles
Create a git repository called dotfiles
.
You can do this on the terminal with the bash or zsh shell:
# Replace <PATH> with the location you wish to store the dotfiles git repository.
DOTFILESDIR=<PATH>/dotfiles
mkdir -p "$DOTFILESDIR"
cd "$DOTFILESDIR"
git init
Version controlling dotfiles
I suggest you create directories in this repository to correspond to hosts and host groups. My dotfiles repository has directories for specific hosts (mac3570 and hypatia), server groups such as supercomputers at Pawsey Supercomputing Centre and common dotfiles (server_common).
Here we are going to version control your .Rprofile
.
If you use multiple hosts, you can define host-specific R code to run (later in this post), so we can create our R profile into a common directory.
For ease of use, I drop the dot from the dotfile name for version control
mkdir common
The following command will help you put your .Rprofile
in the git repository,
or create a file if Rprofile
does not exist.
if test -f ~/.Rprofile; then
if ! test -f ~/Rprofile_backup; then
cp -n ~/.Rprofile ~/Rprofile_backup # create a backup copy of your R profile
mv ~/.Rprofile common/Rprofile
ln -s "$(pwd)/common/Rprofile" ~/.Rprofile
else
echo "Error: ~/Rprofile_backup already exists"
fi
else
touch common/Rprofile
ln -s "$(pwd)/common/Rprofile" ~/.Rprofile
fi
If you already had an .Rprofile
, this also creates a copy of your original .Rprofile
to ~/Rprofile_backup
(assuming it didn’t already exist).
You may delete this backup after the tutorial.
You should check that common/Rprofile
has been created.
This command will show files in the common directory:
ls common
If so, it is time to add and commit your .Rprofile
:
git add common/Rprofile
git commit -m "Added .Rprofile"
Next, create a new repository on GitHub or Gitlab and follow the command line instructions to push an existing repository from the command line.
Now, whenever you make changes to your .Rprofile
(either by editing ~/.Rprofile or the file in your repository), you should commit the change and push.
(edit <PATH_TO_REPOSITORY> and <SOMETHING>)
cd <PATH_TO_REPOSITORY>
git add common/Rprofile
git commit -m "Added <SOMETHING> to .Rprofile"
git push
Using your .Rprofile on another machine
This section describes how to use the same .Rprofile
across multiple hosts or a new machine.
From GitHub or Gitlab, copy the clone URL.
On GitHub you press the green ‘Code’ button and copy the HTTPS or SSH string.
Pass this onto git clone
, for example:
cd <DIRECTORY_TO_HOLD_dotfiles>
git clone https://github.com/<username>/dotfiles.git
cd dotfiles
You might already have an ~/.Rprofile
on this machine.
If so, merge the contents of this file with the git repository version.
You may find the section Useful patterns for your .Rprofile later in the post handy when you want host-specific R commands.
if ! test -f ~/Rprofile_backup; then
if test -f ~/.Rprofile; then
mv ~/.Rprofile ~/Rprofile_backup
fi
ln -s "$(pwd)/common/Rprofile" ~/.Rprofile
else
echo "Error: ~/Rprofile_backup already exists"
fi
This once again creates the file ~/Rprofile_backup
(and does nothing if it's already there), then creates a soft link ~/.Rprofile
to the repository version of that file.
Now whenever change to your dotfiles on one machine, run git push
in the dotfiles repository on that machine, and run git pull
on every other machine.
Congratulations, your .Rprofile
is version controlled and in sync across the devices you use.
Subsequent dotfiles can be added by moving the dotfile to your git repository, soft-linking to the file using an absolute path, and committing the dotfile,
followed by a git push
(and git pull
on other machines).
Useful patterns for your .Rprofile
It may not be suitable for you to have the same R setup commands on all devices.
You can selectively run commands on particular hosts by it’s “nodename” given by as.character(Sys.info()["nodename"])
or platform.
For example, I could have commands specific to Pawsey’s zeus server by using:
if(as.character(Sys.info()["nodename"]) == "zeus") {
# Insert hostname "zeus" specific startup code here
}
Or could specify multiple servers with:
if(as.character(Sys.info()["nodename"]) %in% c("zeus", "magnus", "linux500")) {
# Insert specific startup code here for the given devices
}
You may also want commands specific to a platform such as Linux:
if(Sys.info()['sysname'] == "Linux") {
# Insert Linux specific startup code here
}
For macOS, replace ‘Linux’ with ‘Darwin’.
Final words
Using version control on my dotfiles is something I’ve done for years and has allowed me to move between laptops and bring my precious dotfiles with me. In the past I’ve broken my dotfiles accidentally; version control made it easy to see what I’d changed on GitHub and fix the issue. I hope this can be useful to other R users out there too!