var XLog = 'd'; //todo: moved/renamed items from level x to level y //todo01: split status line at " -> " coming from git and create two // separate entries for renamed/moved items (for native status as well) // v0.3 // - added native status column (showing unmodified status indicators out of "git status .." output /////////////////////////////////////////////////////////////////////////////// function OnInit(data) { //uid added via script wizard (do not change after publishing this script) var uid = "464E82F7-079B-4CC6-B71D-1CE60E3ABBCB"; //thread url added via script wizard (required for updating) var url = "https://resource.dopus.com/t/column-git/29230"; data.name = "Column.Generic: Git"; data.desc = "Git columns."; data.version = "0.3"; data.default_enable = true; if (DOpus.Version.AtLeast("11.8.2")) data.log_prefix = "GIT "; /////////////////////////////////////////////////////////////////////////// function ConfigHelper(data){ //v1.2 var t=this; t.d=data; t.c=data.config; t.cd=DOpus.Create.Map(); t.add=function(name, val, des){ t.l={n:name,ln:name. toLowerCase()}; return t.val(val).des(des);} t.des=function(des){ if (!des) return t; if (t.cd.empty) t.d.config_desc=t.cd; t.cd(t.l.n)=des; return t;} t.val=function(val){ var l=t.l; if (l.v!==l.x&&typeof l.v=="object") l.v.push_back(val);else l.v=t.c[l.n]=val;return t;} t.trn=function(){return t.des(t("script.config."+t.l.ln));}} var cfg = new ConfigHelper(data); /////////////////////////////////////////////////////////////////////////// var cfg = new ConfigHelper(data); cfg.add('XLog', DOpus.Create.Vector()). val(5).val("Off").val("Exception").val("Error").val("Warning").val("Info").val("Normal").val("Trace").val("Dump").val("All"). des('Console output level. The higher, the more output will be visible in the console window. '); cfg.add("Git.exe", ""). des("Path to Git.exe. Leave empty to use Git.exe somewhere in your %path%."); cfg.add("Column.Status.Header", "Git-Status"). des("Header of status column."); cfg.add("Column.Status.Text.Unchanged", "-"). des("Column value for unchanged git status."); cfg.add("Column.Status.Text.Unknown", "?"). des("Column value for items outside of git repos."); cfg.add("Column.Status.Text.Repository", "Git Repository"). des("Column value for git repos."); cfg.add("Column.StatusNative.Header", "Git-Status (native)"). des("Header of native status column."); cfg.add("Cache.ExpirationTimeSpan", 2000). des("Time in milliseconds for cached repository status to expire."); } /////////////////////////////////////////////////////////////////////////////// var COMMAND_FAILURE = true; var COMMAND_USERABORT = true; var COMMAND_BADPARAMS = true; var COMMAND_SUCCESS = false; var gThis = this; /////////////////////////////////////////////////////////////////////////////// String.prototype.lTrim = function(chr){chr=chr||"\\s";return new String(this.replace(new RegExp("^"+chr+"*"),''));} String.prototype.rTrim = function(chr){chr=chr||"\\s";return new String(this.replace(new RegExp(chr+"*$"),''));} String.prototype.trim = function(chr){return this.lTrim(chr).rTrim(chr);} String.prototype.left = function(chrs){return this.substring(0,chrs);} String.prototype.right = function(chrs){return this.substring(this.length-chrs);} /////////////////////////////////////////////////////////////////////////////// var template = //main features.. "REFRESHCOLS/S,"+ //shared misc "NOFAIL/S,NODESELECT/S,XLOG/O[off,xit,error,warning,info,,trace,dump,all]"; /////////////////////////////////////////////////////////////////////////////// function OnAddCommands(data){ Log("Git.OnAddCommands():", "t", 1); var cmd = data.AddCommand(); cmd.name = "Git"; cmd.method = "Command_Git"; cmd.desc = "Git"; cmd.label = "Git"; cmd.template = template; Log("-", "t", -1); } /////////////////////////////////////////////////////////////////////////////// function Command_Git(data){ var args = ArgsMagic(data, null, template), result = null; //DumpObject(args); //return; if (args["XLOG"].exists){ XLog = args["XLOG"].value; } Log("CmdLine: " + data.cmdline,"d"); if (args["NODESELECT"].exists) data.func.command.deselect = false; if (args["NOFAIL"].exists) COMMAND_FAILURE = COMMAND_SUCCESS; if (0); //else if (args["CHECKOUT"].exists) return Command_Git_CHECKOUT(data.func.command, data.func.sourcetab, args); else if (args["REFRESHCOLS"].exists) return Command_Git_REFRESHCOLS(args); if (result===null){ Log("Bad/incomplete params.","X"); return COMMAND_BADPARAMS; } return result; } /////////////////////////////////////////////////////////////////////////////// function SetBusy(){ DOpus.Vars.Set("Script.Git.IsBusy", true); DOpus.Create.Command.UpdateToggle(); } /////////////////////////////////////////////////////////////////////////////// function SetNoBusy(){ DOpus.Vars.Delete("Script.Git.IsBusy"); DOpus.Create.Command.UpdateToggle(); } /////////////////////////////////////////////////////////////////////////////// function Command_Git_REFRESHCOLS(args){ RefreshColumns(); } /////////////////////////////////////////////////////////////////////////////// function RefreshColumns(){ Log("RefreshColumns():", 't'); Script.RefreshColumn("Status"); } /////////////////////////////////////////////////////////////////////////////// function RunHiddenAdvanced( exe, params, tmpFileBase, tmpFileExt, shell, fso){ //v0.5 - only check for existing exe if path given /////////////////////////////////////////////////////////////////////////// function ReadFile( path, fso ){ Log("ReadFile():", "t", 1); fso = fso || new ActiveXObject("Scripting.FilesystemObject"); var content = ""; if (!fso.FileExists(path)){ Log("ReadFile(), file ["+path+"] not found.", "e"); Log("-", "t", -1); return content; } Log("ReadFile(), opening ["+path+"]..", "d"); var file = fso.OpenTextFile( path, 1, -2); // Read, UseDefaultEncoding if (!file.AtEndOfStream) content = file.ReadAll(); file.Close(); Log("-", "t", -1); return content; } /////////////////////////////////////////////////////////////////////////// function CreateTmpFileName(prefix, extension, fso) { if (!fso) fso = new ActiveXObject("Scripting.FilesystemObject"); var tFolder = fso.GetSpecialFolder(2); // 2 = temp folder var tFile = fso.GetTempName(); if (prefix!=undefined) tFile=prefix+tFile; if (extension!=undefined) tFile+=extension; return { path : tFolder.Path, name : tFile, fullname: tFolder.Path+'\\'+tFile }; } /////////////////////////////////////////////////////////////////////////// Log("RunHiddenAdvanced():", "t", 1); if (!fso) fso = new ActiveXObject("Scripting.FilesystemObject"); if (!shell) shell = new ActiveXObject("WScript.Shell"); if (!tmpFileBase) tmpFileBase = "DO.Temp."; if (!tmpFileExt) tmpFileExt = ".txt"; var tmpFileStdout = CreateTmpFileName(tmpFileBase, tmpFileExt, fso).fullname; var tmpFileStderr = tmpFileStdout+".err"+tmpFileExt; var cmd = '%comspec% /c ""'+exe+'" '+params+' >"'+tmpFileStdout+'" 2>"'+tmpFileStderr+'""'; var result = {}; result.cmd = cmd; Log(" CMD: "+cmd, 'd'); if (exe.search(/^(\\\\.*|[a-z]\:|\/\/.*)/i)==0 && !fso.FileExists(exe)){ var msg = "Executable not found ["+exe+"]"; Log(msg, "e"); Log("-", "t", -1); throw msg; } result.returncode = shell.Run( cmd, 0, true); Log("Return: "+result.returncode, "d"); result.stdout = ReadFile(tmpFileStdout, fso); fso.DeleteFile(tmpFileStdout); result.stderr = ReadFile(tmpFileStderr, fso); fso.DeleteFile(tmpFileStderr); Log("-", "t", -1); return result; } /////////////////////////////////////////////////////////////////////////////// function OnAddColumns(data){ var cols = new Array(); var column = null; column = cols[cols.length] = data.AddColumn(); column.name = "Name"; column.justify = "left"; column.defwidth = "6px"; column = cols[cols.length] = data.AddColumn(); column.name = "Status"; column.header = Script.config["Column.Status.Header"]; column.justify = "left"; column.defwidth = "70px"; column = cols[cols.length] = data.AddColumn(); column.name = "StatusNative"; column.header = Script.config["Column.StatusNative.Header"]; column.justify = "left"; column.defwidth = "30px"; column = cols[cols.length] = data.AddColumn(); column.name = "Type"; column.justify = "right"; column.defwidth = "50px"; for(var c=0;cScript.config['Cache.ExpirationTimeSpan']) { Log("Reload needed (expired)", "t"); reload = true; } if (!reload) { Log("Skipping reload, column [" + data.col + "].", "t"); } else { var result = FetchRepoStatus(data, repo, fso); if (result == COMMAND_FAILURE){ SetNoBusy(); Log("-", "t", -1); return COMMAND_FAILURE; } } var after = new Date().getTime(); //millis var relRepoItemPath = GetRelativeRepoItemPath(data.item, repo); data.columns('Status').value = FetchItemStatus(data.item, relRepoItemPath, repo); data.columns('StatusNative').value = FetchItemStatusNative(data.item, relRepoItemPath, repo); Log("-", "t", -1); SetNoBusy(); return COMMAND_SUCCESS; } /////////////////////////////////////////////////////////////////////////////// function FetchRepoStatus(data, repo, fso) { Log('FetchRepoStatus('+data.col+')', 't', 1); DOpus.Output('Fetching repo ['+repo.repoRoot+']..'); var gitExe = Script.config["Git.exe"] || "Git.exe"; var result = RunHiddenAdvanced( gitExe, ' -C "'+data.item.path + '" status --ignored --porcelain', 'DO.GitColumn.', '.txt', null, fso); //Log("result.returncode : " + result.returncode , "d"); //Log("result.stdout : " + result.stdout , "d"); if (result.stderr || result.returncode) { Log('Error running git, code ['+result.returncode+'], msg ['+result.stderr+'].',"e"); Log("-", "t", -1); return COMMAND_FAILURE; } repo.status = {}; repo.statusNative = {}; var lines = result.stdout.split("\n"); var relRepoItemPath, line, status, isDir; for(var li=0;li tmp/delmesoon/readme.txt *and* // case (file) : tmp/delme/readme.txt -> tmp/delme/readme2.txt var oldName='', newName=''; var parts = relRepoItemPath.split(" -> "); //if (parts[0].indexOf('/')!=-1 && parts[1].indexOf('/')!=-1) { oldNameParts = parts[0].split('/'); newNameParts = parts[1].split('/'); if (oldNameParts.length == newNameParts.length) { //renamed, no move (same depth) for(var p=0;p"); if (parts.length>1) opValue = parts.slice(1).join(""); this.options[opName] = opValue; } if (!(!this.value && subOpsTemplate)) return; if (!(match = subOpsTemplate.match(/\<(.+?)\>/))) return; this.options[match[1]] = true; this.value = match[1]; } /////////////////////////////////////////////////////////////////////////// var types = {s:"switch",k:"keyword",o:"optional",n:"numeric",m:"multiple",r:"raw"}; var names = template.replace(/\/(.)\,/g,"/$1;").replace(/\]\,/g,"];").split(";"), args = []; for(arg in names) { var parts = names[arg].split("/"); var name=parts[0].toUpperCase(), subOpsTemplate=null; var parts2 = parts[1].split("["); var type=parts2[0].toLowerCase(); if (parts2.length>1) subOpsTemplate = parts2[1].substring(0,parts2[1].length-1); var exists=(data&&data.func.args.got_arg[name])||(jsonData&&(jsonData[name]!=undefined))||false; var value = (data && data.func.args[name]) || (jsonData && jsonData[name]) || undefined; args.push( args[name] = new Arg(name, types[type], exists, value, subOpsTemplate )); } /////////////////////////////////////////////////////////////////////////// args.Get = function( argName, configName, noneValue){ var arg = this[argName = argName.toUpperCase()]; if (!arg) throw new Error(0, "Unknown parameter ["+argName+"]"); if (arg.exists) return arg.value; //DOpus.Output("argName: " + argName); //DOpus.Output("configName: " + configName); if (!configName) return noneValue; if (typeof Script.config[configName] == "string") return new String(""+Script.config[configName]); return Script.config[configName]; } return args; } /////////////////////////////////////////////////////////////////////////////// function Log(text, lvl, ind){ //XLog v0.41 var u,lvs={o:0,x:1,e:2,w:3,i:4,n:5,t:6,d:7,a:8},i=["","X","E","W","I","","","",""]; if (typeof XLog==(u="undefined")){var v=DOpus.Vars; if (v.Exists("XLog") && typeof XLogForce==u) {XLog=v.Get("XLog"); }} if (typeof XLog==u){var c=Script.Config; if(typeof c!="undefined" && typeof c.XLog!=u){ XLog=c.XLog; }} if (typeof XLog==u){XLog="normal";} if(XLog.paused===undefined){ if(XLog===true) var L=5; else if(!isNaN(parseInt(XLog,10))) var L=XLog*1; else var L=lvs[(""+XLog).substring(0,1)]; XLog={paused:false,I:0,L:L};} lvl=(lvl==undefined?5:lvs[lvl.substring(0,1).toLowerCase()]); if (!(lvl && XLog.L && !XLog.paused && (lvl<=XLog.L))){ return;} var pad = (XLog.I==0?"":new Array(XLog.I+1).join(" ")); if (i[lvl])pad = i[lvl]+(!pad?" ":pad.substring(1)); if (text!="-"){ var d=DOpus; if (d.Version.AtLeast("11.13.1")) d.Output(pad+text,((lvl==1||lvl==2)?1:0)); else d.Output(pad+text);} ind=(ind!==undefined?ind:0);XLog.I+=ind;if(XLog.I<0)throw new Error("XLog indent went sub-zero."); } /////////////////////////////////////////////////////////////////////////////// function DumpObject(o, name, upperCaseProps, self, level){ var version = 0.4; name = name || "unnamed"; if (self == undefined) DOpus.Output("DumpObject(): ["+name+"]"); var maxLen = 0; upperCaseProps = upperCaseProps || false; self = self || false; level = level || " "; for(prop in o) if (prop.length > maxLen) maxLen = prop.length; for(prop in o) { var pad = ""; while (pad.length+prop.length