Skip to content

Instantly share code, notes, and snippets.

@lshifr
Created December 19, 2012 02:42
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save lshifr/4333909 to your computer and use it in GitHub Desktop.
Simple version control system for Mathematica projects hosted on Github via Github gists
{
"author"->
{
"name" -> "Leonid Shifrin",
"email" -> "lshifr@gmail.com",
"url" -> "http://www.mathprogramming-intro.org"
},
"name" -> "RawVCS",
"mavcthematica_version" -> "8.0+",
"description" -> "Simple version control system for Mathematica projects hosted on Github via Github gists",
"url" -> "https://gist.github.com/4333909"
}
(* Mathematica Package *)
BeginPackage["RawVCS`", { "RuleTreeInfo`", "GithubGistClient`", "FileInfo`", "GithubGistProjectInfo`", "OO`", "OO`Methods`", "OO`Errors`"}]
(* Exported symbols added here with SymbolName::usage *)
RawVCS;
Begin["`Private`"] (* Begin Private Context *)
join = Function[{dir, file}, FileNameJoin[{dir, file}]];
SetAttributes[CleanUp, HoldAll];
CleanUp[expr_, cleanup_] :=
Module[{exprFn, result, abort = False, rethrow = True, seq},
exprFn[] := expr;
result =
CheckAbort[
Catch[Catch[result = exprFn[]; rethrow = False; result], _,
seq[##] &], abort = True];
cleanup;
If[abort, Abort[]];
If[rethrow, Throw[result /. seq -> Sequence]];
result];
ClearAll[createProject];
createProject[finfo_?(TypeQ[FileInfo]), files : {___String},
opts___?OptionQ] :=
createProject[finfo, GithubGistProjectInfo[{}]@new[finfo, opts],
files, opts];
createProject[finfo_?(TypeQ[FileInfo]),
prinfo_?(TypeQ[GithubGistProjectInfo]), files : {___String}] /;
prinfo@newProjectQ[] :=
Module[{ gist, result},
If[MemberQ[Map[finfo@get[#] &, files], $Failed],
ThrowError[createProject, "some_files_do_not_exist"]
];
(* Update the project.m in finfo with possible changes *)
finfo@replace["project.m" -> prinfo@toString[]];
gist =
createGist@makeGistCreateInfo[
makeFileCreateInfo @@@
Thread[files -> (files /. finfo@normal[])],
prinfo@get["description"]
];
If[gist === $Failed,
ThrowError[createProject, "error_create_gist"]
];
prinfo@replace["url" -> getGistHtmlURL[gist]];
CleanUp[
result = commitProject[finfo, prinfo, {"project.m"}];
gist = result,
(* Error second part of the commit *)
If[! ValueQ[result] || result === $Failed,
(* Delete a gist - commit wasn't successful *)
GithubGistClient`deleteGist[getGistID[gist]];
ThrowError[createProject,
"error_during_project_descriptor_commit"]
]];
gist
];
createProject[finfo_?(TypeQ[FileInfo]),
prinfo_?(TypeQ[GithubGistProjectInfo]), files_, opts___?OptionQ] :=
ThrowError[createProject, "need_new_project"];
createProject[___] := ThrowError[createProject];
ClearAll[commitProject];
Options[commitProject] = {
RenameFilesOnDisk -> True,
DeleteRenamedFiles -> True
};
commitProject[
finfo_?(TypeQ[FileInfo]),
files : {___String},
renamings : {(_String -> _String) ...} : {},
removals : {___String} : {},
opts___?OptionQ
] :=
commitProject[finfo, GithubGistProjectInfo[{}]@new[finfo, opts],
files, renamings, removals, opts];
commitProject[
finfo_?(TypeQ[FileInfo]),
prinfo_?(TypeQ[GithubGistProjectInfo]),
files : {___String},
renamings : {(_String -> _String) ...} : {},
removals : {___String} : {},
opts___?OptionQ
] :=
Module[{ gist, gistID, nonExistent, renameOnDisk, deleteRenamed},
renameOnDisk =
RenameFilesOnDisk /. Flatten[{opts}] /. Options[commitProject];
deleteRenamed =
DeleteRenamedFiles /. Flatten[{opts}] /. Options[commitProject];
If[prinfo@newProjectQ[],
ThrowError[commitProject, "need_project_url"]
];
nonExistent = Pick[#, Map[finfo@get[#] &, #], $Failed] &[
files~Join~renamings[[All, 1]]~Join~removals
];
If[nonExistent =!= {},
ThrowError[commitProject, "some_files_do_not_exist", nonExistent]
];
(* Always commit the project file *)
finfo@replace["project.m" -> prinfo@toString[]];
gistID = prinfo@get["gistID"];
Assert[gistID =!= $Failed];
gist =
editGist[gistID,
makeGistEditInfo[
Thread[# -> (# /. finfo@normal[])] &[
Append[files~Join~renamings[[All, 1]], "project.m"]
],
renamings,
removals,
prinfo@get["description"]
]
];
prinfo@save[];
finfo@replace[finfo@normal[] /. renamings];
finfo@delete[#] & /@ renamings[[All, 1]];
If[renamings =!= {} && TrueQ@renameOnDisk,
finfo@save[];
If[TrueQ@deleteRenamed,
With[{dir = finfo@getDir[]},
Map[DeleteFile[dir~join~#] &, renamings[[All, 1]]];
]
]];
gist
];
commitProject[___] := ThrowError[commitProject];
DeclareType[RawVCS][
OO`Methods`bindToDir[dir_String] :=
$self@AddMethods[
OO`Methods`getDir[] := dir
,
OO`Methods`create[files : {___String}, opts___?OptionQ] :=
OOBlock[
createProject[FileInfo[{}]@new[dir] , files, opts]
]
,
OO`Methods`getGistId[] :=
Module[{finfo, pinfo, gistID},
OOBlock[
finfo = FileInfo[{}]@new[dir];
pinfo = GithubGistProjectInfo[{}]@new[finfo];
gistID = pinfo@OO`Methods`get["gistID"];
If[gistID === $Failed,
ThrowError[RawVCS, OO`Methods`getGistId, "invalid_gist_id"]
]];
gistID]
,
(* Delete the gist associated with the project *)
OO`Methods`deleteGistForProject[] :=
Module[{finfo, pinfo},
OOBlock[
finfo = FileInfo[{}]@OO`Methods`new[dir];
GithubGistClient`deleteGist[$self@OO`Methods`getGistId[]];
pinfo = GithubGistProjectInfo[{}]@new[finfo];
pinfo@OO`Methods`delete["url"];
pinfo@OO`Methods`save[];
]]
,
OO`Methods`remove[files : {__String}] :=
OOBlock[
commitProject[FileInfo[{}]@new[dir ], {}, {}, files]
]
,
OO`Methods`rename[renamings : {(_String -> _String) ..}] :=
OOBlock[
commitProject[FileInfo[{}]@new[dir ], {}, renamings, {}]
]
,
OO`Methods`commit[files : {__String}] :=
OOBlock[
commitProject[FileInfo[{}]@new[dir ], files]
]
]
]
End[] (* End Private Context *)
EndPackage[]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment