monotone

monotone Mtn Source Tree

Root/std_hooks.lua

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
16 local ret = -1
17 pid = spawn(path, unpack(arg))
18 if (pid ~= -1) then ret, pid = wait(pid) end
19 return ret
20end
21
22-- Wrapper around execute to let user confirm in the case where a subprocess
23-- returns immediately
24-- This is needed to work around some brokenness with some merge tools
25-- (e.g. on OS X)
26function execute_confirm(path, ...)
27 execute(path, unpack(arg))
28 print(gettext("Press enter when the subprocess has completed"))
29 io.read()
30 return ret
31end
32
33-- attributes are persistent metadata about files (such as execute
34-- bit, ACLs, various special flags) which we want to have set and
35-- re-set any time the files are modified. the attributes themselves
36-- are stored in a file .mt-attrs, in the working copy (and
37-- manifest). each (f,k,v) triple in an attribute file turns into a
38-- call to attr_functions[k](f,v) in lua.
39
40if (attr_init_functions == nil) then
41 attr_init_functions = {}
42end
43
44attr_init_functions["execute"] =
45 function(filename)
46 if (is_executable(filename)) then
47 return "true"
48 else
49 return nil
50 end
51 end
52
53attr_init_functions["manual_merge"] =
54 function(filename)
55 if (binary_file(filename)) then
56 return "true" -- binary files must merged manually
57 else
58 return nil
59 end
60 end
61
62if (attr_functions == nil) then
63 attr_functions = {}
64end
65
66attr_functions["execute"] =
67 function(filename, value)
68 if (value == "true") then
69 make_executable(filename)
70 end
71 end
72
73
74function ignore_file(name)
75 -- project specific
76 if (ignored_files == nil) then
77 ignored_files = {}
78 local ignfile = io.open(".mt-ignore", "r")
79 if (ignfile ~= nil) then
80 local line = ignfile:read()
81 while (line ~= nil)
82 do
83 table.insert(ignored_files, line)
84 line = ignfile:read()
85 end
86 io.close(ignfile)
87 end
88 end
89 for i, line in pairs(ignored_files)
90 do
91 if (regex.search(line, name)) then return true end
92 end
93 -- c/c++
94 if (string.find(name, "%.a$")) then return true end
95 if (string.find(name, "%.so$")) then return true end
96 if (string.find(name, "%.o$")) then return true end
97 if (string.find(name, "%.la$")) then return true end
98 if (string.find(name, "%.lo$")) then return true end
99 if (string.find(name, "^core$")) then return true end
100 if (string.find(name, "/core$")) then return true end
101 -- python
102 if (string.find(name, "%.pyc$")) then return true end
103 if (string.find(name, "%.pyo$")) then return true end
104 -- TeX
105 if (string.find(name, "%.aux$")) then return true end
106 -- backup files
107 if (string.find(name, "%.bak$")) then return true end
108 if (string.find(name, "%.orig$")) then return true end
109 if (string.find(name, "%.rej$")) then return true end
110 if (string.find(name, "%~$")) then return true end
111 -- editor temp files
112 -- vim creates .foo.swp files
113 if (string.find(name, "%.[^/]*%.swp$")) then return true end
114 -- emacs creates #foo# files
115 if (string.find(name, "%#[^/]*%#$")) then return true end
116 -- autotools detritus:
117 if (string.find(name, "^autom4te.cache/")) then return true end
118 if (string.find(name, "/autom4te.cache/")) then return true end
119 if (string.find(name, "^.deps/")) then return true end
120 if (string.find(name, "/.deps/")) then return true end
121 -- Cons/SCons detritus:
122 if (string.find(name, "^.consign$")) then return true end
123 if (string.find(name, "/.consign$")) then return true end
124 if (string.find(name, "^.sconsign$")) then return true end
125 if (string.find(name, "/.sconsign$")) then return true end
126 -- other VCSes:
127 if (string.find(name, "^CVS/")) then return true end
128 if (string.find(name, "/CVS/")) then return true end
129 if (string.find(name, "^%.svn/")) then return true end
130 if (string.find(name, "/%.svn/")) then return true end
131 if (string.find(name, "^SCCS/")) then return true end
132 if (string.find(name, "/SCCS/")) then return true end
133 if (string.find(name, "^_darcs/")) then return true end
134 if (string.find(name, "^.cdv/")) then return true end
135 if (string.find(name, "^.git/")) then return true end
136 if (string.find(name, "%.scc$")) then return true end
137 -- desktop/directory configuration metadata
138 if (string.find(name, "^.DS_Store$")) then return true end
139 if (string.find(name, "/.DS_Store$")) then return true end
140 if (string.find(name, "^desktop.ini$")) then return true end
141 if (string.find(name, "/desktop.ini$")) then return true end
142 return false;
143end
144
145-- return true means "binary", false means "text",
146-- nil means "unknown, try to guess"
147function binary_file(name)
148 local lowname=string.lower(name)
149 -- some known binaries, return true
150 if (string.find(lowname, "%.gif$")) then return true end
151 if (string.find(lowname, "%.jpe?g$")) then return true end
152 if (string.find(lowname, "%.png$")) then return true end
153 if (string.find(lowname, "%.bz2$")) then return true end
154 if (string.find(lowname, "%.gz$")) then return true end
155 if (string.find(lowname, "%.zip$")) then return true end
156 -- some known text, return false
157 if (string.find(lowname, "%.cc?$")) then return false end
158 if (string.find(lowname, "%.cxx$")) then return false end
159 if (string.find(lowname, "%.hh?$")) then return false end
160 if (string.find(lowname, "%.hxx$")) then return false end
161 if (string.find(lowname, "%.lua$")) then return false end
162 if (string.find(lowname, "%.texi$")) then return false end
163 if (string.find(lowname, "%.sql$")) then return false end
164 -- unknown - read file and use the guess-binary
165 -- monotone built-in function
166 return guess_binary_file_contents(name)
167end
168
169function edit_comment(basetext, user_log_message)
170 local exe = nil
171 if (program_exists_in_path("vi")) then exe = "vi" end
172 if (program_exists_in_path("notepad.exe")) then exe = "notepad.exe" end
173 local visual = os.getenv("VISUAL")
174 if (visual ~= nil) then exe = visual end
175 local editor = os.getenv("EDITOR")
176 if (editor ~= nil) then exe = editor end
177
178 if (exe == nil) then
179 io.write("Could not find editor to enter commit message\n"
180 .. "Try setting the environment variable EDITOR\n")
181 return nil
182 end
183
184 local tmp, tname = temp_file()
185 if (tmp == nil) then return nil end
186 basetext = "MT: " .. string.gsub(basetext, "\n", "\nMT: ") .. "\n"
187 tmp:write(user_log_message)
188 tmp:write(basetext)
189 io.close(tmp)
190
191 if (execute(exe, tname) ~= 0) then
192 io.write(string.format(gettext("Error running editor '%s' to enter log message\n"),
193 exe))
194 os.remove(tname)
195 return nil
196 end
197
198 tmp = io.open(tname, "r")
199 if (tmp == nil) then os.remove(tname); return nil end
200 local res = ""
201 local line = tmp:read()
202 while(line ~= nil) do
203 if (not string.find(line, "^MT:")) then
204 res = res .. line .. "\n"
205 end
206 line = tmp:read()
207 end
208 io.close(tmp)
209 os.remove(tname)
210 return res
211end
212
213
214function non_blocking_rng_ok()
215 return true
216end
217
218
219function persist_phrase_ok()
220 return true
221end
222
223-- trust evaluation hooks
224
225function intersection(a,b)
226 local s={}
227 local t={}
228 for k,v in pairs(a) do s[v] = 1 end
229 for k,v in pairs(b) do if s[v] ~= nil then table.insert(t,v) end end
230 return t
231end
232
233function get_revision_cert_trust(signers, id, name, val)
234 return true
235end
236
237function get_manifest_cert_trust(signers, id, name, val)
238 return true
239end
240
241function get_file_cert_trust(signers, id, name, val)
242 return true
243end
244
245function accept_testresult_change(old_results, new_results)
246 local reqfile = io.open("MT/wanted-testresults", "r")
247 if (reqfile == nil) then return true end
248 local line = reqfile:read()
249 local required = {}
250 while (line ~= nil)
251 do
252 required[line] = true
253 line = reqfile:read()
254 end
255 io.close(reqfile)
256 for test, res in pairs(required)
257 do
258 if old_results[test] == true and new_results[test] ~= true
259 then
260 return false
261 end
262 end
263 return true
264end
265
266-- merger support
267
268function merge2_meld_cmd(lfile, rfile)
269 return
270 function()
271 return execute("meld", lfile, rfile)
272 end
273end
274
275function merge3_meld_cmd(lfile, afile, rfile)
276 return
277 function()
278 return execute("meld", lfile, afile, rfile)
279 end
280end
281
282function merge2_tortoise_cmd(lfile, rfile, outfile)
283 return
284 function()
285 return execute("tortoisemerge",
286 string.format("/theirs:%s", lfile),
287 string.format("/mine:%s", rfile),
288 string.format("/merged:%s", outfile))
289 end
290end
291
292function merge3_tortoise_cmd(lfile, afile, rfile, outfile)
293 return
294 function()
295 return execute("tortoisemerge",
296 string.format("/base:%s", afile),
297 string.format("/theirs:%s", lfile),
298 string.format("/mine:%s", rfile),
299 string.format("/merged:%s", outfile))
300 end
301end
302
303function merge2_vim_cmd(vim, lfile, rfile, outfile)
304 return
305 function()
306 return execute(vim, "-f", "-d", "-c", string.format("file %s", outfile),
307 lfile, rfile)
308 end
309end
310
311function merge3_vim_cmd(vim, afile, lfile, rfile, outfile)
312 return
313 function()
314 return execute(vim, "-f", "-d", "-c", string.format("file %s", outfile),
315 afile, lfile, rfile)
316 end
317end
318
319function merge3_rcsmerge_vim_cmd(merge, vim, lfile, afile, rfile, outfile)
320 return
321 function()
322 -- XXX: This is tough - should we check if conflict markers stay or not?
323 -- If so, we should certainly give the user some way to still force
324 -- the merge to proceed since they can appear in the files (and I saw
325 -- that). --pasky
326 if execute(merge, lfile, afile, rfile) == 0 then
327 copy_text_file(lfile, outfile);
328 return 0
329 end
330 return execute(vim, "-f", "-c", string.format("file %s", outfile),
331 lfile)
332 end
333end
334
335function merge2_emacs_cmd(emacs, lfile, rfile, outfile)
336 local elisp = "(ediff-merge-files \"%s\" \"%s\" nil \"%s\")"
337 return
338 function()
339 return execute(emacs, "-eval",
340 string.format(elisp, lfile, rfile, outfile))
341 end
342end
343
344function merge3_emacs_cmd(emacs, lfile, afile, rfile, outfile)
345 local elisp = "(ediff-merge-files-with-ancestor \"%s\" \"%s\" \"%s\" nil \"%s\")"
346 local cmd_fmt = "%s -eval " .. elisp
347 return
348 function()
349 execute(emacs, "-eval",
350 string.format(elisp, lfile, rfile, afile, outfile))
351 end
352end
353
354function merge2_xxdiff_cmd(left_path, right_path, merged_path, lfile, rfile, outfile)
355 return
356 function()
357 return execute("xxdiff",
358 "--title1", left_path,
359 "--title2", right_path,
360 lfile, rfile,
361 "--merged-filename", outfile)
362 end
363end
364
365function merge3_xxdiff_cmd(left_path, anc_path, right_path, merged_path,
366 lfile, afile, rfile, outfile)
367 return
368 function()
369 return execute("xxdiff",
370 "--title1", left_path,
371 "--title2", right_path,
372 "--title3", merged_path,
373 lfile, afile, rfile,
374 "--merge",
375 "--merged-filename", outfile)
376 end
377end
378
379function merge2_kdiff3_cmd(left_path, right_path, merged_path, lfile, rfile, outfile)
380 return
381 function()
382 return execute("kdiff3",
383 "--L1", left_path,
384 "--L2", right_path,
385 lfile, rfile,
386 "-o", outfile)
387 end
388end
389
390function merge3_kdiff3_cmd(left_path, anc_path, right_path, merged_path,
391 lfile, afile, rfile, outfile)
392 return
393 function()
394 return execute("kdiff3",
395 "--L1", anc_path,
396 "--L2", left_path,
397 "--L3", right_path,
398 afile, lfile, rfile,
399 "--merge",
400 "--o", outfile)
401 end
402end
403
404function merge2_opendiff_cmd(left_path, right_path, merged_path, lfile, rfile, outfile)
405 return
406 function()
407 -- As opendiff immediately returns, let user confirm manually
408 return execute_confirm("opendiff",lfile,rfile,"-merge",outfile)
409 end
410end
411
412function merge3_opendiff_cmd(left_path, anc_path, right_path, merged_path, lfile, afile, rfile, outfile)
413 return
414 function()
415 -- As opendiff immediately returns, let user confirm manually
416 execute_confirm("opendiff",lfile,rfile,"-ancestor",afile,"-merge",outfile)
417 end
418end
419
420function write_to_temporary_file(data)
421 tmp, filename = temp_file()
422 if (tmp == nil) then
423 return nil
424 end;
425 tmp:write(data)
426 io.close(tmp)
427 return filename
428end
429
430function copy_text_file(srcname, destname)
431 src = io.open(srcname, "r")
432 if (src == nil) then return nil end
433 dest = io.open(destname, "w")
434 if (dest == nil) then return nil end
435
436 while true do
437 local line = src:read()
438 if line == nil then break end
439 dest:write(line, "\n")
440 end
441
442 io.close(dest)
443 io.close(src)
444end
445
446function read_contents_of_file(filename, mode)
447 tmp = io.open(filename, mode)
448 if (tmp == nil) then
449 return nil
450 end
451 local data = tmp:read("*a")
452 io.close(tmp)
453 return data
454end
455
456function program_exists_in_path(program)
457 return existsonpath(program) == 0
458end
459
460function get_preferred_merge2_command (tbl)
461 local cmd = nil
462 local left_path = tbl.left_path
463 local right_path = tbl.right_path
464 local merged_path = tbl.merged_path
465 local lfile = tbl.lfile
466 local rfile = tbl.rfile
467 local outfile = tbl.outfile
468
469 local editor = os.getenv("EDITOR")
470 if editor ~= nil then editor = string.lower(editor) else editor = "" end
471
472
473 if program_exists_in_path("kdiff3") then
474 cmd = merge2_kdiff3_cmd (left_path, right_path, merged_path, lfile, rfile, outfile)
475 elseif program_exists_in_path ("xxdiff") then
476 cmd = merge2_xxdiff_cmd (left_path, right_path, merged_path, lfile, rfile, outfile)
477 elseif program_exists_in_path ("opendiff") then
478 cmd = merge2_opendiff_cmd (left_path, right_path, merged_path, lfile, rfile, outfile)
479 elseif program_exists_in_path ("TortoiseMerge") then
480 cmd = merge2_tortoise_cmd(lfile, rfile, outfile)
481 elseif string.find(editor, "emacs") ~= nil or string.find(editor, "gnu") ~= nil then
482 if string.find(editor, "xemacs") and program_exists_in_path("xemacs") then
483 cmd = merge2_emacs_cmd ("xemacs", lfile, rfile, outfile)
484 elseif program_exists_in_path("emacs") then
485 cmd = merge2_emacs_cmd ("emacs", lfile, rfile, outfile)
486 end
487 elseif string.find(editor, "vim") ~= nil then
488 io.write (string.format("\nWARNING: 'vim' was choosen to perform external 2-way merge.\n"..
489 "You should merge all changes to *LEFT* file due to limitation of program\n"..
490 "arguments.\n\n"))
491 if os.getenv ("DISPLAY") ~= nil and program_exists_in_path ("gvim") then
492 cmd = merge2_vim_cmd ("gvim", lfile, rfile, outfile)
493 elseif program_exists_in_path ("vim") then
494 cmd = merge2_vim_cmd ("vim", lfile, rfile, outfile)
495 end
496 elseif program_exists_in_path ("meld") then
497 tbl.meld_exists = true
498 io.write (string.format("\nWARNING: 'meld' was choosen to perform external 2-way merge.\n"..
499 "You should merge all changes to *LEFT* file due to limitation of program\n"..
500 "arguments.\n\n"))
501 cmd = merge2_meld_cmd (lfile, rfile)
502 end
503 return cmd
504end
505
506function merge2 (left_path, right_path, merged_path, left, right)
507 local ret = nil
508 local tbl = {}
509
510 tbl.lfile = nil
511 tbl.rfile = nil
512 tbl.outfile = nil
513 tbl.meld_exists = false
514
515 tbl.lfile = write_to_temporary_file (left)
516 tbl.rfile = write_to_temporary_file (right)
517 tbl.outfile = write_to_temporary_file ("")
518
519 if tbl.lfile ~= nil and tbl.rfile ~= nil and tbl.outfile ~= nil
520 then
521 tbl.left_path = left_path
522 tbl.right_path = right_path
523 tbl.merged_path = merged_path
524
525 local cmd = get_preferred_merge2_command (tbl)
526
527 if cmd ~=nil
528 then
529 io.write (string.format(gettext("executing external 2-way merge command\n")))
530 cmd ()
531 if tbl.meld_exists
532 then
533 ret = read_contents_of_file (tbl.lfile, "r")
534 else
535 ret = read_contents_of_file (tbl.outfile, "r")
536 end
537 if string.len (ret) == 0
538 then
539 ret = nil
540 end
541 else
542 io.write (string.format("No external 2-way merge command found.\n"..
543 "You may want to check that $EDITOR is set to an editor that supports 2-way merge,\n"..
544 "set this explicitly in your get_preferred_merge2_command hook,\n"..
545 "or add a 2-way merge program to your path.\n\n"))
546 end
547 end
548
549 os.remove (tbl.lfile)
550 os.remove (tbl.rfile)
551 os.remove (tbl.outfile)
552
553 return ret
554end
555
556function get_preferred_merge3_command (tbl)
557 local cmd = nil
558 local left_path = tbl.left_path
559 local anc_path = tbl.anc_path
560 local right_path = tbl.right_path
561 local merged_path = tbl.merged_path
562 local lfile = tbl.lfile
563 local afile = tbl.afile
564 local rfile = tbl.rfile
565 local outfile = tbl.outfile
566
567 local editor = os.getenv("EDITOR")
568 if editor ~= nil then editor = string.lower(editor) else editor = "" end
569
570 local merge = os.getenv("MTMERGE")
571 -- TODO: Support for rcsmerge_emacs
572 if merge ~= nil and string.find(editor, "vim") ~= nil then
573 if os.getenv ("DISPLAY") ~= nil and program_exists_in_path ("gvim") then
574 cmd = merge3_rcsmerge_vim_cmd (merge, "gvim", lfile, afile, rfile, outfile)
575 elseif program_exists_in_path ("vim") then
576 cmd = merge3_rcsmerge_vim_cmd (merge, "vim", lfile, afile, rfile, outfile)
577 end
578
579 elseif program_exists_in_path("kdiff3") then
580 cmd = merge3_kdiff3_cmd (left_path, anc_path, right_path, merged_path, lfile, afile, rfile, outfile)
581 elseif program_exists_in_path ("xxdiff") then
582 cmd = merge3_xxdiff_cmd (left_path, anc_path, right_path, merged_path, lfile, afile, rfile, outfile)
583 elseif program_exists_in_path ("opendiff") then
584 cmd = merge3_opendiff_cmd (left_path, anc_path, right_path, merged_path, lfile, afile, rfile, outfile)
585 elseif program_exists_in_path ("TortoiseMerge") then
586 cmd = merge3_tortoise_cmd(lfile, afile, rfile, outfile)
587 elseif string.find(editor, "emacs") ~= nil or string.find(editor, "gnu") ~= nil then
588 if string.find(editor, "xemacs") and program_exists_in_path ("xemacs") then
589 cmd = merge3_emacs_cmd ("xemacs", lfile, afile, rfile, outfile)
590 elseif program_exists_in_path ("emacs") then
591 cmd = merge3_emacs_cmd ("emacs", lfile, afile, rfile, outfile)
592 end
593 elseif string.find(editor, "vim") ~= nil then
594 io.write (string.format("\nWARNING: 'vim' was choosen to perform external 2-way merge.\n"..
595 "You should merge all changes to *LEFT* file due to limitation of program\n"..
596 "arguments. The order of the files is ancestor, left, right.\n\n"))
597 if os.getenv ("DISPLAY") ~= nil and program_exists_in_path ("gvim") then
598 cmd = merge3_vim_cmd ("gvim", afile, lfile, rfile, outfile)
599 elseif program_exists_in_path ("vim") then
600 cmd = merge3_vim_cmd ("vim", afile, lfile, rfile, outfile)
601 end
602 elseif program_exists_in_path ("meld") then
603 tbl.meld_exists = true
604 io.write (string.format("\nWARNING: 'meld' was choosen to perform external 3-way merge.\n"..
605 "You should merge all changes to *CENTER* file due to limitation of program\n"..
606 "arguments.\n\n"))
607 cmd = merge3_meld_cmd (lfile, afile, rfile)
608 end
609
610 return cmd
611end
612
613function merge3 (anc_path, left_path, right_path, merged_path, ancestor, left, right)
614 local ret
615 local tbl = {}
616
617 tbl.anc_path = anc_path
618 tbl.left_path = left_path
619 tbl.right_path = right_path
620
621 tbl.merged_path = merged_path
622 tbl.afile = nil
623 tbl.lfile = nil
624 tbl.rfile = nil
625 tbl.outfile = nil
626 tbl.meld_exists = false
627 tbl.lfile = write_to_temporary_file (left)
628 tbl.afile = write_to_temporary_file (ancestor)
629 tbl.rfile = write_to_temporary_file (right)
630 tbl.outfile = write_to_temporary_file ("")
631
632 if tbl.lfile ~= nil and tbl.rfile ~= nil and tbl.afile ~= nil and tbl.outfile ~= nil
633 then
634 local cmd = get_preferred_merge3_command (tbl)
635 if cmd ~=nil
636 then
637 io.write (string.format(gettext("executing external 3-way merge command\n")))
638 cmd ()
639 if tbl.meld_exists
640 then
641 ret = read_contents_of_file (tbl.afile, "r")
642 else
643 ret = read_contents_of_file (tbl.outfile, "r")
644 end
645 if string.len (ret) == 0
646 then
647 ret = nil
648 end
649 else
650 io.write (string.format("No external 3-way merge command found.\n"..
651 "You may want to check that $EDITOR is set to an editor that supports 3-way merge,\n"..
652 "set this explicitly in your get_preferred_merge3_command hook,\n"..
653 "or add a 3-way merge program to your path.\n\n"))
654 end
655 end
656
657 os.remove (tbl.lfile)
658 os.remove (tbl.rfile)
659 os.remove (tbl.afile)
660 os.remove (tbl.outfile)
661
662 return ret
663end
664
665-- expansion of values used in selector completion
666
667function expand_selector(str)
668
669 -- something which looks like a generic cert pattern
670 if string.find(str, "^[^=]*=.*$")
671 then
672 return ("c:" .. str)
673 end
674
675 -- something which looks like an email address
676 if string.find(str, "[%w%-_]+@[%w%-_]+")
677 then
678 return ("a:" .. str)
679 end
680
681 -- something which looks like a branch name
682 if string.find(str, "[%w%-]+%.[%w%-]+")
683 then
684 return ("b:" .. str)
685 end
686
687 -- a sequence of nothing but hex digits
688 if string.find(str, "^%x+$")
689 then
690 return ("i:" .. str)
691 end
692
693 -- tries to expand as a date
694 local dtstr = expand_date(str)
695 if dtstr ~= nil
696 then
697 return ("d:" .. dtstr)
698 end
699
700 return nil
701end
702
703-- expansion of a date expression
704function expand_date(str)
705 -- simple date patterns
706 if string.find(str, "^19%d%d%-%d%d")
707 or string.find(str, "^20%d%d%-%d%d")
708 then
709 return (str)
710 end
711
712 -- "now"
713 if str == "now"
714 then
715 local t = os.time(os.date('!*t'))
716 return os.date("%FT%T", t)
717 end
718
719 -- today don't uses the time
720 if str == "today"
721 then
722 local t = os.time(os.date('!*t'))
723 return os.date("%F", t)
724 end
725
726 -- "yesterday", the source of all hangovers
727 if str == "yesterday"
728 then
729 local t = os.time(os.date('!*t'))
730 return os.date("%F", t - 86400)
731 end
732
733 -- "CVS style" relative dates such as "3 weeks ago"
734 local trans = {
735 minute = 60;
736 hour = 3600;
737 day = 86400;
738 week = 604800;
739 month = 2678400;
740 year = 31536000
741 }
742 local pos, len, n, type = string.find(str, "(%d+) ([minutehordaywk]+)s? ago")
743 if trans[type] ~= nil
744 then
745 local t = os.time(os.date('!*t'))
746 if trans[type] <= 3600
747 then
748 return os.date("%FT%T", t - (n * trans[type]))
749 else
750 return os.date("%F", t - (n * trans[type]))
751 end
752 end
753
754 return nil
755end
756
757function use_inodeprints()
758 return false
759end
760
761external_diff_default_args = "-u"
762
763-- default external diff, works for gnu diff
764function external_diff(file_path, data_old, data_new, is_binary, diff_args, rev_old, rev_new)
765 local old_file = write_to_temporary_file(data_old);
766 local new_file = write_to_temporary_file(data_new);
767
768 if diff_args == nil then diff_args = external_diff_default_args end
769 execute("diff", diff_args, "--label", file_path .. "\told", old_file, "--label", file_path .. "\tnew", new_file);
770
771 os.remove (old_file);
772 os.remove (new_file);
773end
774

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status