# How can I set up a versioning system within a notebook?

Since the Frontend crashes a lot, for important work it would be nice to have the notebook automatically branch and save itself every so many minutes.

For instance, commands like StartSavingCopies[5] would save a copy with a new name "BackupFileNumberX.nb" every 5 minutes.

How can we do this behind the scenes dynamically?

-
That is pretty impressive! You could create and run a ScheduledTask every X minutes to automatically branch the notebook... –  rm -rf Jun 5 '12 at 7:21
Perhaps this would better be formatted as a self-answered question? –  Szabolcs Jun 5 '12 at 7:46
Also, I get this error upon clicking "Branch" –  Ajasja Jun 5 '12 at 11:11
Great question, but indeed it should be more Q-A style (think Jeopardy) to match the policies of this site. Please note that self-answering is seen as positive here and recently a new self-answering checkbox was added to the question page to make self-answering more convenient (using it prevents others from seeing the question until you have provided the answer). –  Sjoerd C. de Vries Jun 5 '12 at 12:04
I have to add to the chorus requesting that you break this into a question and answer. –  rcollyer Jun 5 '12 at 13:21

I don't like to answer my own question, but to give an idea of what an answer might be here's my first stab at this (in the form of a toolbar), just try running UtilityDock[] and click "Branch" after saving it.

(Note: I think the best answer wouldn't make use of a toolbar)

 UtilityDock := (c =
Cell[BoxData[
ToBoxes[Grid[{{Item[
Row[{" ",
DynamicModule[{state = 1},
Button[Dynamic[If[state == 1, "Hover", "Sink"]],
Dynamic@If[state == 1,

SetOptions[EvaluationNotebook[],
"WindowFloating" -> True]; state = 2,

SetOptions[EvaluationNotebook[],
"WindowFloating" -> False]; state = 1],
BaseStyle -> {15, FontFamily -> "Bank Gothic", White},
Appearance -> None]], Style[" | ", White],

DynamicModule[{state = 1},
Button[Dynamic[If[state == 1, "Collapse", "Expand"]],
Dynamic@If[state == 1,

SetOptions[EvaluationNotebook[],
"WindowSize" -> {Automatic, 40}]; state = 2,

SetOptions[EvaluationNotebook[],
"WindowSize" -> {Automatic, 700}];
state = 1],
BaseStyle -> {15, FontFamily -> "Bank Gothic", White},
Appearance -> None]], Style[" | ", White],
Button["Branch", (SetDirectory[
NotebookDirectory[EvaluationNotebook[]]];

thisName =
StringTake[
Last[FileNameSplit[NotebookFileName[InputNotebook[]]]], {1, -4}];

latestBranch =
Max@Join[{0}, (ToExpression /@
Flatten[
StringCases[
StringTake[
Pick[FileNames[],
Not /@
StringFreeQ[FileNames[], thisName]], {1, -4}],
"B" ~~ x_ :> x]])];
Print[ToString[latestBranch + 1]];

NotebookSave[EvaluationNotebook[],
FileNameJoin[{NotebookDirectory[],
thisName <> " B" <> ToString[latestBranch + 1] <>
".nb"}]];

NotebookSave[EvaluationNotebook[],
FileNameJoin[{NotebookDirectory[],
thisName <> ".nb"}]];),
BaseStyle -> {15, FontFamily -> "Bank Gothic", White},
Appearance -> None]}], ItemSize -> Scaled[0.8],
Alignment -> Left],
Item[Row[{

"Zoom", {"75" :>
SetOptions[EvaluationNotebook[],
"Magnification" -> 0.75],
"100" :>
SetOptions[EvaluationNotebook[], "Magnification" -> 1],

"150" :>
SetOptions[EvaluationNotebook[],
"Magnification" -> 1.5],

"200" :>
SetOptions[EvaluationNotebook[],
"Magnification" -> 2]},
BaseStyle -> {15, FontFamily -> "Bank Gothic", White},
Appearance -> None], " "}]
, ItemSize -> Scaled[0.2], Alignment -> Right]}},
BaselinePosition -> Scaled[0.3], ItemSize -> All]]],
"DockedCell", Background -> Black, ImageMargins -> 0,
CellMargins -> {{0, 0}, {0, 0}},
CellFrameMargins -> {{0, 0}, {0, 0}}];
SetOptions[EvaluationNotebook[], "DockedCells" -> c]);

-
+1. There is nothing wrong in answering your own question, particularly when an answer is as constructive as yours. You can wait with accepting it for some time, to keep other folks motivated to provide more answers. –  Leonid Shifrin Jun 5 '12 at 18:53
I get this error: "Part of the path C:\Users\Sjoerd\Desktop\C:\Users\Sjoerd\Desktop does not exist. Unable to save file C:\Users\Sjoerd\Desktop\C:\Users\Sjoerd\Desktop\tes2.nb." I assume this is caused by the StringSplit with "/", which is the wrong character for Windows. Try FileNameSplit instead. –  Sjoerd C. de Vries Jun 5 '12 at 22:03
@SjoerdC.deVries Good catch, added that. –  M.R. Jun 6 '12 at 5:39
A further optimization would be to use FileBaseName to obtain the file name without extension. This also completely removes the directory path. BTW I see some dangers in this app. If there are similarly named files in the same directory which use the same branching system (or even just contain an additional "B") they might be overwritten. A branch of fun1.nb might overwrite a branch of fun11.nb –  Sjoerd C. de Vries Jun 6 '12 at 7:20
@SjoerdC.deVries you might use a timestamp in the filename to get rid of the overwriting issue. –  Yves Klett Jun 6 '12 at 9:12

As a software developer, I'd suggest a non-mathematica solution to the general problem of versioning. Use a version control system, such as git, that supports text files (i.e. notebooks).

If you both (1) use an external version control system, where you explicitly commit after any "interesting" change, and (2) setting the default configuration to auto-save as detailed in this answer:

Is there a notebook autosave frequency configuration?

An example create-a-notebook workflow (using git) is:

• create a notebook, say besselPlot.nb, and save it to a directory in my git repository (~/physicsplay/mathematica say)
• from a command line (I happen to use a Windows cygwin on the laptop that I've got Mathematica on)

cd ~/physicsplay/mathematica

At points of interest, from the shell, commit your changes

cd ~/physicsplay/mathematica

git commit besselPlot.nb

# or

git commit -a


You'll get a chance to add a commit comment ("plot without labels", say), so that you can use your commit logs if you want to roll back to an older version (i.e. should you screw up what you are doing and get it auto-saved at an undesirable time).

While I've just only started using the autosave mechanism, I think that with a version control system as a backend for the notebooks, this should work out nicely as a general solution, without requiring you to sort through piles of BackupFileNumberX.nb's.

-
But what's still missing is how to do the saves from Mathematica to the git or svn repository. Any help on that? –  murray Nov 3 '12 at 18:10
I've expanded my answer with how to save (at least with git ... it's been too long since I've used svn and I don't recall the syntax) –  Peeter Joot Nov 4 '12 at 3:13
@PeeterJoot Why not rsync. I hope this doesn't turn into a unix thing from a mathematica thing. –  drN Nov 4 '12 at 4:00
@drN: How is rsync related? –  Mechanical snail Nov 4 '12 at 7:51
I always feel that version control is not very well suited to work with notebook files. As it's almost impossible to merge them, I'd rather go with a normal backup system. (@drN probably had the same thought.) This has been discussed on this site before, see here for example. Package (.m) files are a whole different matter of course. –  sebhofer Nov 4 '12 at 12:36

On Linux, you can combine your favorite version control system (Git in this example) with inotify to automatically commit your changes to the repository whenever your .nb file is saved.

Create a Git repository, and save your notebook (say notebook.nb) in the repository. Use inotifywait to automatically run git commit notebook.nb --allow-empty-message -m '' whenever notebook.nb changes. Start editing your notebook in Mathematica.

Then, to save a new version, simply press Ctrl-S, and it will automatically commit to the repo.

If you want to add a commit message after saving, run git commit --amend -m 'The commit message'. Alternatively, if you intend to always add a commit message, remove --allow-empty-message -m '' from the Git command, and set your preferred text editor; then whenever you save, your editor will pop up and prompt you for a commit message.

-
AFAIK, unfortunately no inotifywait on Mac OS X. –  murray Nov 6 '12 at 17:32