1 2-- this is the standard set of lua hooks for monotone; 3-- user-provided files can override it or add to it. 4 5function temp_file() 6 local tdir 7 tdir = os.getenv("TMPDIR") 8 if tdir == nil then tdir = os.getenv("TMP") end 9 if tdir == nil then tdir = os.getenv("TEMP") end 10 if tdir == nil then tdir = "/tmp" end 11 return mkstemp(string.format("%s/mt.XXXXXX", tdir)) 12end 13 14function execute(path, ...) 15 local pid = posix.fork() 16 local ret = -1 17 if pid == 0 then 18 posix.exec(path, unpack(arg)) 19 else 20 ret, pid = posix.wait(pid) 21 end 22 return ret 23end 24 25 26 27-- attributes are persistent metadata about files (such as execute 28-- bit, ACLs, various special flags) which we want to have set and 29-- re-set any time the files are modified. the attributes themselves 30-- are stored in a file .mt-attrs, in the working copy (and 31-- manifest). each (f,k,v) triple in an atribute file turns into a 32-- call to attr_functions[k](f,v) in lua. 33 34if (attr_functions == nil) then 35 attr_functions = {} 36end 37 38 39attr_functions["execute"] = 40 function(filename, value) 41 if (value == "true") then 42 posix.chmod(filename, "u+x") 43 end 44 end 45 46 47function ignore_file(name) 48 if (string.find(name, "%.a$")) then return true end 49 if (string.find(name, "%.so$")) then return true end 50 if (string.find(name, "%.o$")) then return true end 51 if (string.find(name, "%.la$")) then return true end 52 if (string.find(name, "%.lo$")) then return true end 53 if (string.find(name, "%.aux$")) then return true end 54 if (string.find(name, "%.bak$")) then return true end 55 if (string.find(name, "%.orig$")) then return true end 56 if (string.find(name, "%.rej$")) then return true end 57 if (string.find(name, "%~$")) then return true end 58 if (string.find(name, "/core$")) then return true end 59 if (string.find(name, "^CVS/")) then return true end 60 if (string.find(name, "/CVS/")) then return true end 61 if (string.find(name, "^%.svn/")) then return true end 62 if (string.find(name, "/%.svn/")) then return true end 63 if (string.find(name, "^SCCS/")) then return true end 64 if (string.find(name, "/SCCS/")) then return true end 65 return false; 66end 67 68 69function edit_comment(basetext) 70 local exe = "vi" 71 local visual = os.getenv("VISUAL") 72 if (visual ~= nil) then exe = visual end 73 local editor = os.getenv("EDITOR") 74 if (editor ~= nil) then exe = editor end 75 76 local tmp, tname = temp_file() 77 if (tmp == nil) then return nil end 78 basetext = "MT: " .. string.gsub(basetext, "\n", "\nMT: ") .. "\n" 79 tmp:write(basetext) 80 io.close(tmp) 81 82 if (execute(exe, tname) ~= 0) then 83 os.remove(tname) 84 return nil 85 end 86 87 tmp = io.open(tname, "r") 88 if (tmp == nil) then os.remove(tname); return nil end 89 local res = "" 90 local line = tmp:read() 91 while(line ~= nil) do 92 if (not string.find(line, "^MT:")) then 93 res = res .. line .. "\n" 94 end 95 line = tmp:read() 96 end 97 io.close(tmp) 98 os.remove(tname) 99 return res 100end 101 102 103function non_blocking_rng_ok() 104 return true 105end 106 107 108function persist_phrase_ok() 109 return true 110end 111 112-- trust evaluation hooks 113 114function intersection(a,b) 115 local s={} 116 local t={} 117 for k,v in pairs(a) do s[v] = 1 end 118 for k,v in pairs(b) do if s[v] ~= nil then table.insert(t,v) end end 119 return t 120end 121 122function get_revision_cert_trust(signers, id, name, val) 123 return true 124end 125 126function get_manifest_cert_trust(signers, id, name, val) 127 return true 128end 129 130function get_file_cert_trust(signers, id, name, val) 131 return true 132end 133 134function accept_testresult_change(old_results, new_results) 135 for test,res in pairs(old_results) 136 do 137 if res == true and new_results[test] ~= true 138 then 139 return false 140 end 141 end 142 return true 143end 144 145-- merger support 146 147function merge2_meld_cmd(lfile, rfile) 148 return 149 function() 150 return execute("meld", lfile, rfile) 151 end 152end 153 154function merge3_meld_cmd(lfile, afile, rfile) 155 return 156 function() 157 return execute("meld", lfile, afile, rfile) 158 end 159end 160 161 162function merge2_vim_cmd(vim, lfile, rfile, outfile) 163 return 164 function() 165 return execute(vim, "-f", "-d", "-c", string.format("file %s", outfile), 166 lfile, rfile) 167 end 168end 169 170function merge3_vim_cmd(vim, lfile, afile, rfile, outfile) 171 return 172 function() 173 return execute(vim, "-f", "-d", "-c", string.format("file %s", outfile), 174 lfile, afile, rfile) 175 end 176end 177 178function merge2_emacs_cmd(emacs, lfile, rfile, outfile) 179 local elisp = "(ediff-merge-files \"%s\" \"%s\" nil \"%s\")" 180 return 181 function() 182 return execute(emacs, "-no-init-file", "-eval", 183 string.format(elisp, lfile, rfile, outfile)) 184 end 185end 186 187function merge3_emacs_cmd(emacs, lfile, afile, rfile, outfile) 188 local elisp = "(ediff-merge-files-with-ancestor \"%s\" \"%s\" \"%s\" nil \"%s\")" 189 local cmd_fmt = "%s -no-init-file -eval " .. elisp 190 return 191 function() 192 execute(emacs, "-no-init-file", "-eval", 193 string.format(elisp, lfile, rfile, afile, outfile)) 194 end 195end 196 197function merge2_xxdiff_cmd(left_path, right_path, merged_path, lfile, rfile, outfile) 198 return 199 function() 200 return execute("xxdiff", 201 "--title1", left_path, 202 "--title2", right_path, 203 lfile, rfile, 204 "--merged-filename", outfile) 205 end 206end 207 208function merge3_xxdiff_cmd(left_path, anc_path, right_path, merged_path, 209 lfile, afile, rfile, outfile) 210 return 211 function() 212 return execute("xxdiff", 213 "--title1", left_path, 214 "--title2", right_path, 215 "--title3", merged_path, 216 lfile, afile, rfile, 217 "--merge", 218 "--merged-filename", outfile) 219 end 220end 221 222function write_to_temporary_file(data) 223 tmp, filename = temp_file() 224 if (tmp == nil) then 225 return nil 226 end; 227 tmp:write(data) 228 io.close(tmp) 229 return filename 230end 231 232function read_contents_of_file(filename) 233 tmp = io.open(filename, "r") 234 if (tmp == nil) then 235 return nil 236 end 237 local data = tmp:read("*a") 238 io.close(tmp) 239 return data 240end 241 242function program_exists_in_path(program) 243 return execute("which", program) == 0 244end 245 246function merge2(left_path, right_path, merged_path, left, right) 247 local lfile = nil 248 local rfile = nil 249 local outfile = nil 250 local data = nil 251 local meld_exists = false 252 253 lfile = write_to_temporary_file(left) 254 rfile = write_to_temporary_file(right) 255 outfile = write_to_temporary_file("") 256 257 if lfile ~= nil and 258 rfile ~= nil and 259 outfile ~= nil 260 then 261 local cmd = nil 262 if program_exists_in_path("meld") then 263 meld_exists = true 264 io.write(string.format("\nWARNING: 'meld' was choosen to perform external 2-way merge.\n" .. 265 "You should merge all changes to *LEFT* file due to limitation of program\n" .. 266 "arguments.\n\n")) 267 cmd = merge2_meld_cmd(lfile, rfile) 268 elseif program_exists_in_path("xxdiff") then 269 cmd = merge2_xxdiff_cmd(left_path, right_path, merged_path, 270 lfile, rfile, outfile) 271 elseif program_exists_in_path("emacs") then 272 cmd = merge2_emacs_cmd("emacs", lfile, rfile, outfile) 273 elseif program_exists_in_path("xemacs") then 274 cmd = merge2_emacs_cmd("xemacs", lfile, rfile, outfile) 275 elseif program_exists_in_path("gvim") then 276 cmd = merge2_vim_cmd("gvim", lfile, rfile, outfile) 277 elseif program_exists_in_path("vim") then 278 cmd = merge2_vim_cmd("vim", lfile, rfile, outfile) 279 end 280 281 if cmd ~= nil 282 then 283 io.write(string.format("executing external 2-way merge command\n")) 284 cmd() 285 if meld_exists then 286 data = read_contents_of_file(lfile) 287 else 288 data = read_contents_of_file(outfile) 289 end 290 if string.len(data) == 0 291 then 292 data = nil 293 end 294 else 295 io.write("no external 2-way merge command found\n") 296 end 297 end 298 299 os.remove(lfile) 300 os.remove(rfile) 301 os.remove(outfile) 302 303 return data 304end 305 306function merge3(anc_path, left_path, right_path, merged_path, ancestor, left, right) 307 local afile = nil 308 local lfile = nil 309 local rfile = nil 310 local outfile = nil 311 local data = nil 312 local meld_exists = false 313 314 lfile = write_to_temporary_file(left) 315 afile = write_to_temporary_file(ancestor) 316 rfile = write_to_temporary_file(right) 317 outfile = write_to_temporary_file("") 318 319 if lfile ~= nil and 320 rfile ~= nil and 321 afile ~= nil and 322 outfile ~= nil 323 then 324 local cmd = nil 325 if program_exists_in_path("meld") then 326 meld_exists = true 327 io.write(string.format("\nWARNING: 'meld' was choosen to perform external 3-way merge.\n" .. 328 "You should merge all changes to *CENTER* file due to limitation of program\n" .. 329 "arguments.\n\n")) 330 cmd = merge3_meld_cmd(lfile, afile, rfile) 331 elseif program_exists_in_path("xxdiff") then 332 cmd = merge3_xxdiff_cmd(left_path, anc_path, right_path, merged_path, 333 lfile, afile, rfile, outfile) 334 elseif program_exists_in_path("emacs") then 335 cmd = merge3_emacs_cmd("emacs", lfile, afile, rfile, outfile) 336 elseif program_exists_in_path("xemacs") then 337 cmd = merge3_emacs_cmd("xemacs", lfile, afile, rfile, outfile) 338 elseif program_exists_in_path("gvim") then 339 cmd = merge3_vim_cmd("gvim", lfile, afile, rfile, outfile) 340 elseif program_exists_in_path("vim") then 341 cmd = merge3_vim_cmd("vim", lfile, afile, rfile, outfile) 342 end 343 344 if cmd ~= nil 345 then 346 io.write(string.format("executing external 3-way merge command\n")) 347 cmd() 348 if meld_exists then 349 data = read_contents_of_file(afile) 350 else 351 data = read_contents_of_file(outfile) 352 end 353 if string.len(data) == 0 354 then 355 data = nil 356 end 357 else 358 io.write("no external 3-way merge command found\n") 359 end 360 end 361 362 os.remove(lfile) 363 os.remove(rfile) 364 os.remove(afile) 365 os.remove(outfile) 366 367 return data 368end 369 370 371-- expansion of values used in selector completion 372 373function expand_selector(str) 374 375 -- simple date patterns 376 if string.find(str, "^19%d%d%-%d%d") 377 or string.find(str, "^20%d%d%-%d%d") 378 then 379 return ("d:" .. str) 380 end 381 382 -- something which looks like an email address 383 if string.find(str, "[%w%-_]+@[%w%-_]+") 384 then 385 return ("a:" .. str) 386 end 387 388 -- something which looks like a branch name 389 if string.find(str, "[%w%-]+%.[%w%-]+") 390 then 391 return ("b:" .. str) 392 end 393 394 -- a sequence of nothing but hex digits 395 if string.find(str, "^%x+$") 396 then 397 return ("i:" .. str) 398 end 399 400 -- "yesterday", the source of all hangovers 401 if str == "yesterday" 402 then 403 local t = os.time(os.date('!*t')) 404 return os.date("d:%F", t - 86400) 405 end 406 407 -- "CVS style" relative dates such as "3 weeks ago" 408 local trans = { 409 minute = 60; 410 hour = 3600; 411 day = 86400; 412 week = 604800; 413 month = 2678400; 414 year = 31536000 415 } 416 local pos, len, n, type = string.find(str, "(%d+) ([minutehordaywk]+)s? ago") 417 if trans[type] ~= nil 418 then 419 local t = os.time(os.date('!*t')) 420 return os.date("d:%F", t - (n * trans[type])) 421 end 422 423 return nil 424end 425