Here is a take for those using only the Mathematica front end and git. I recently starting using git so that I can work with other collaborators. It was relatively simple since we were using notebooks to create figures from imported data that was located in the same directory. In this case all the notebooks started with the line:
SetDirectory[NotebookDirectory[]];
The problem happened when we needed to use other packages. I did not want to bother them with getting their own copy and to put it in $UserBaseDirectory
. I figured instead that I can create a folder in the git project were I could place all of the external packages that we need and use them. Now we just need to tell Mathematica where to look for those files. Initially my collaborator used the absolute path: /Users/username/path/to/gitproject/mathematica/
. This path was obviously not going to work for me. The way to do this is to declare a relative path. Since we are working in git we can use
Import["!git rev-parse --show-toplevel", "Text"]
to obtain the path to the git project. Now we can create relative paths. So now
our Notebooks started with this:
SetDirectory[NotebookDirectory[]];
MMAROOT = ToFileName[{
Import["!git rev-parse --show-toplevel", "Text"],
"mathematica", "applications"}
];
If[! MemberQ[$Path, MMAROOT], PrependTo[$Path, MMAROOT]]
This however was getting annoying since we had to copy and paste this code every time we created a new notebook. Instead I came up with this function and put in the file $UserBaseDirectory/Kernel/init.m
.
(** User Mathematica initialization file **)
EMPH[a_, style_: "TI"] := ToString[Style[a, style], StandardForm]
LINK[label_, url_] := ToString[Hyperlink[label, url], StandardForm]
USAGE[str_, inputs__] := ToString[StringForm[str, inputs], StandardForm]
GitSetup::usage = USAGE["GitSetup[`1`, `2`, ...] sets the working \
directory to the directory of the calling notebook and appends the \
inputs to `3`. The input paths are relative to the git root \
directory.", EMPH["path1"], EMPH["path2"],
LINK["$Path", "paclet:ref/$Path"]]
GitSetup[input__] := Module[{path, gitpath, tmp},
SetDirectory[NotebookDirectory[]];
gitpath = Import["!git rev-parse --show-toplevel", "Text"];
path = {input};
Do[
tmp = ToFileName[{gitpath, path[[i]]}];
If[! MemberQ[$Path, tmp],
PrependTo[$Path, tmp];
],
{i, Length[path]}
];
$Path
]
Now I only bother my collaborators to put that file so that every time they start Mathematica they have access to the function GitSetup
. Now every notebook starts with
GitSetup["mathematica/applications"]
And in case they forget what this function does:

Now we can add or develop packages related only to the project we are working on without having to worry about any paths and without even having to call SetDirectory
.
Skip the function arguments:
If your git project contains source files from other languages it might be convenient to create a .bashrc
in the root git folder. This bash file should apply to all the collaborators of the group and not just yourself (you have ~/.bashrc
or ~/.bash_profile
for that). This is how the .bashrc
file looks
in the git directory (placed in the same folder as .gitignore
and .gitattributes
):
# Bash commands related to the git project
export GITROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PR=$GITROOT
export PATH=$PR/bin:$PATH
export PYTHONPATH=$PR/main/python:$PR/python:$PYTHONPATH
export MATLABPATH=$PR/main/matlab:$PR/matlab:$MATLABPATH
export C_INCLUDE_PATH=$PR/include:$C_INCLUDE_PATH
export CPLUS_INCLUDE_PATH=$PR/include:$CPLUS_INCLUDE_PATH
export LD_LIBRARY_PATH=$PR/lib:$LD_LIBRARY_PATH
export MMA_PATH=$PR/main/mathematica:$PR/mathematica
Whenever we want to work in the project we source
that file and all the packages in the project will be available. In this way we can specify where we want Mathematica to look for packages in the git project by creating an enviroment variable. I called it MMA_PATH
and added two paths to it. Now the $UserBaseDirectory/Kernel/init.m
file looks as follows:
(** User Mathematica initialization file **)
BeginPackage["mathrc`"]
Begin["`Private`"]
EMPH[a_, style_: "TI"] := Module[{},
ToString[Style[a, style], StandardForm]
]
LINK[url_, label_:Style["\[RightSkeleton]", "SR"]] := Module[{},
ToString[Hyperlink[url, label], StandardForm]
]
USAGE[str_, inputs__] := Module[{},
ToString[StringForm[str, inputs], StandardForm]
]
End[]
GitSetup::usage = `Private`USAGE["GitSetup[] sets the working \
directory to the directory of the calling notebook and appends the \
paths defined by `1` to `2`. The definition of `1` is found in the \
.bashrc file in the git root directory. In the event that you are \
not working in a git directory then an attempt will be made to look \
for the .bashrc file by looking in the parent directories up to your \
home directory.",
`Private`EMPH["$MMA_PATH", "Input"],
`Private`LINK["$Path", "paclet:ref/$Path"]
]
Begin["`Private`"]
GitSetup[] := Module[{gitpath, mmapath, path, parent, bashrc},
parent = NotebookDirectory[];
SetDirectory[parent];
gitpath = Import[
"!git rev-parse --show-toplevel", "Text"
];
If[gitpath != "",
bashrc = FileNameJoin[{gitpath, ".bashrc"}];
,
bashrc = FileNameJoin[{parent, ".bashrc"}];
While[!FileExistsQ[bashrc],
parent = ParentDirectory[parent];
bashrc = FileNameJoin[{parent, ".bashrc"}];
If[parent == $HomeDirectory,
Break[];
]
]
];
mmapath = Import[
"!source " <> bashrc <> "; echo $MMA_PATH", "Text"
];
path = StringSplit[mmapath, ":"];
Do[
If[!MemberQ[$Path, path[[i]]],
PrependTo[$Path, path[[i]]];
],
{i, Length[path]}
];
$Path
]
End[]
EndPackage[];
Now the notebooks start with GitSetup[]
and we are ready to work.
Deployment for non-git collaborators
If it so happens that some of your collaborators refuse to use git and prefer to use something say like dropbox, then the notebooks you produce with the command GitSetup[]
will also work for them.
To share your git repository with such collaborators with dropbox all you have to do is create a symbolic link from your git repository to a folder in dropbox. For instance
ln -s /path/to/git/repo/name_of_repo /path/to/folder/in/dropbox/name_of_repo
Since your repository already contains a .bashrc
file all the notebooks in your repository will also work for your non-git collaborator because the GitSetup
function will look for such file the momement it realizes that the user does not have git
.
Edit: For those who have been following the edits to the init.m
file, I have included the GitSetup
function inside a package so that the names in the Global
context does not get polluted when you start Mathematica
.