42

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?

M.R.
  • 31,425
  • 8
  • 90
  • 281

3 Answers3

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[
                    Thread@
                    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[{

             ActionMenu[
              "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]);
M.R.
  • 31,425
  • 8
  • 90
  • 281
  • 7
    +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 05 '12 at 18:53
  • 3
    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 05 '12 at 22:03
  • @SjoerdC.deVries Good catch, added that. – M.R. Jun 06 '12 at 05: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 06 '12 at 07:20
  • 1
    @SjoerdC.deVries you might use a timestamp in the filename to get rid of the overwriting issue. – Yves Klett Jun 06 '12 at 09:12
11

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

    git add besselPlot.nb

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.

Peeter Joot
  • 6,398
  • 4
  • 36
  • 55
  • 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 03 '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 04 '12 at 03:13
  • @PeeterJoot Why not rsync. I hope this doesn't turn into a unix thing from a mathematica thing. – dearN Nov 04 '12 at 04:00
  • @drN: How is rsync related? – Mechanical snail Nov 04 '12 at 07:51
  • 3
    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 04 '12 at 12:36
  • @drN, rsync has no version control capability – Peeter Joot Nov 04 '12 at 15:41
  • @Peeter Joot: I was hoping for a way to deal with version directly within Mathematica. Evidently one could shell out and use the commands you show, but that seems unnecessarily awkward (perhaps not "unnecessarily", though!). – murray Nov 04 '12 at 15:58
  • @PeeterJoot Gotcha! – dearN Nov 04 '12 at 21:18
  • @sebhofer: I think it is correct that there are drawbacks with putting notebooks under version control. But that doesn't make vcs for notebooks completely useless: they still will provide a version history, they will only save diffs and do much more compared to a normal backup system. I would certainly suggest to rather use a decent vcs over a hand written save strategy for notebooks or a backup system at any time. The only major problem is that a vcs can't do any automatic merges for notebooks -- and that isn't solved by any other approach either... – Albert Retey Nov 05 '12 at 08:47
  • this might also be of interest: http://mathematica.stackexchange.com/q/5518/169 – Albert Retey Nov 05 '12 at 09:57
  • @sebhofer: Version control works perfectly fine in a single-user scenario without merging. – Mechanical snail Nov 06 '12 at 01:12
  • @murray: You can do it on Linux (see my answer). Not sure if you're using a different OS though. – Mechanical snail Nov 06 '12 at 01:17
  • @AlbertRetey From my point of view, when you take away the diffing capabilities of a VCS, a normal backup system is just as good (for nb files at least). The contents of my notebooks is mostly presentation of results (the "real" code is in package files), so I don't need annotated commits. I prefer an automated backup system which runs every day without me having to think about it. – sebhofer Nov 06 '12 at 08:31
  • @sebhofer: a backup system and a VCS serve quite different needs and only have a very small overlap in functionality. If it's exactly that overlap that you need (as seems to be the case for your personal situation), then you might probably use either of them. In general I wouldn't suggest anyone to use one instead of the other -- usually you want both. – Albert Retey Nov 06 '12 at 09:31
11

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.

Mechanical snail
  • 2,422
  • 1
  • 16
  • 35