[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Monotone-devel] user-defined external mergers...
From: |
Richard Levitte - VMS Whacker |
Subject: |
[Monotone-devel] user-defined external mergers... |
Date: |
Fri, 17 Jun 2005 02:41:20 +0200 (CEST) |
Hi,
while experimenting with rebuilding a repository with a different
branch name, I thought that there could be some way to specify a
arbitrary merging commands (one for 2-way and one for 3-way merge) to
be used by monotone.
I haven't done any thorough tests yet, but have produced some code
that should be usable as a start (I'm gonna test it soon).
The current idea is that the user can specify commands in the
environment variables MONOTONE_MERGE2 and MONOTONE_MERGE3. They can
contain the following printf-like items:
%L is replaced with left_path
%A is replaced with and_path (only for MONOTONE_MERGE3)
%R is replaced with right_path
%M is replaced with merged_path
%l is replaced with lfile
%a is replaced with afile (only for MONOTONE_MERGE3)
%r is replaced with rfile
%o is replaced with outfile
I have tested that the replacement of those elements work as expected,
and that the string is properly split into an argv table. I have not
checked that execution works as expected.
Comments?
Cheers,
Richard
P.S At least, I'm getting quite a bit better at hacking Lua :-)
-----
Please consider sponsoring my work on free software.
See http://www.free.lp.se/sponsoring.html for details.
--
Richard Levitte address@hidden
http://richard.levitte.org/
"When I became a man I put away childish things, including
the fear of childishness and the desire to be very grown up."
-- C.S. Lewis
#
# patch "lua.cc"
# from [c5a01c71aef633d6ad5ad78650e4e0601295b349]
# to [3eb5a2685c16c5e4af1c26da98ec66c0833fde19]
#
# patch "std_hooks.lua"
# from [9b1a27e30129929aa366223e51f1c11ec918fc93]
# to [9fffd02ab6d987a9f2c0edc50987d38cf0441d71]
#
--- lua.cc
+++ lua.cc
@@ -166,6 +166,137 @@
lua_pushboolean(L, guess_binary(std::string(path, lua_strlen(L, -1))));
return 1;
}
+
+ int
+ add_to_string(char **str, size_t *str_size, size_t *str_len,
+ const char *add, size_t add_len)
+ {
+ while ((*str_len) + add_len + 1 > (*str_size))
+ {
+ char *new_str = (char *)realloc(*str, (*str_size) + 1024);
+ if (new_str == NULL)
+ return 0;
+ *str = new_str;
+ (*str_size) += 1024;
+ }
+ strncpy((*str) + (*str_len), add, add_len);
+ (*str_len) += add_len;
+ (*str)[*str_len] = '\0';
+ return 1;
+ }
+
+ static int
+ monotone_split_command_string_for_lua(lua_State *L)
+ {
+ int n = lua_gettop(L);
+ if (n != 1)
+ return 0;
+ const char *str = lua_tostring(L, -n);
+ char *result = (char *)malloc(1024);
+ size_t result_size = 1024;
+ size_t result_len = 0;
+ int result_any = 0; // set to 1 when there is a string, even
+ // an empty one.
+ size_t amount = 0; // Number of returned strings.
+
+ char state = '\0'; // '\0', '"' or '\''
+ const char *start = str;
+ while(1)
+ {
+ int c = *str;
+
+ // Check to see if we've found the end of an argument.
+ if ((c == '\0' || isspace(c)) && state == '\0')
+ {
+ // If there's more to add to the result string since last
+ // addition, do it.
+ if (start != str)
+ {
+ add_to_string(&result, &result_size, &result_len,
+ start, str - start);
+ result_any = 1;
+ }
+
+ // If there was a result at all, push it. If not, we've just
+ // going to walk past a bunch of spaces.
+ if (result_any)
+ {
+ lua_pushlstring(L, result, result_len);
+ amount++;
+ result_len = 0;
+ result_any = 0;
+ }
+
+ // If we've found the end of the command string, it's to to quit.
+ if (c == '\0')
+ break;
+
+ // Walk past all spaces but the last (because str is incremented
+ // once more further down)
+ while(isspace(str[1]))
+ str++;
+
+ // Set the start of the next chunk to be after the last space.
+ start = &str[1];
+ }
+ // Escape, escape, escape.
+ else if (c == '\\')
+ {
+ if (str[1] != '\0')
+ {
+ add_to_string(&result, &result_size, &result_len,
+ start, str - start);
+ start = ++str;
+ }
+ }
+ // Check if we've found the end of the current string. We've
+ // already taken care of the case where c and state are '\0'.
+ else if (c == state)
+ {
+ // If there's more to add to the result string since last
+ // addition, do it.
+ if (start != str)
+ {
+ add_to_string(&result, &result_size, &result_len,
+ start, str - start);
+ result_any = 1;
+ }
+
+ // Set the state to be "outside of any quoted string".
+ state = '\0';
+
+ // Set the start of the next chunk to be after the quote.
+ start = &str[1];
+ }
+ else if (c == '"' || c == '\'')
+ {
+ // If there's more to add to the result string since last
+ // addition, do it.
+ if (start != str)
+ {
+ add_to_string(&result, &result_size, &result_len,
+ start, str - start);
+ result_any = 1;
+ }
+
+ // Set the state to be "inside a quoted string".
+ state = c;
+
+ // Set the start of the next chunk to be after the quote.
+ start = &str[1];
+ }
+ else if (c == '\0')
+ {
+ // End of string in the middle of a quote is an error.
+ // Respond by removing all the pushed strings and return 0.
+ while(amount)
+ lua_remove(L, amount--);
+ break;
+ }
+ str++;
+ }
+ return amount;
+ }
}
@@ -194,6 +325,7 @@
lua_register(st, "kill", monotone_kill_for_lua);
lua_register(st, "sleep", monotone_sleep_for_lua);
lua_register(st, "guess_binary", monotone_guess_binary_for_lua);
+ lua_register(st, "split_command_string",
monotone_split_command_string_for_lua);
}
lua_hooks::~lua_hooks()
--- std_hooks.lua
+++ std_hooks.lua
@@ -19,8 +19,8 @@
return ret
end
+
-
-- attributes are persistent metadata about files (such as execute
-- bit, ACLs, various special flags) which we want to have set and
-- re-set any time the files are modified. the attributes themselves
@@ -310,6 +310,42 @@
end
end
+function merge2_generic_cmd(cmd_format,
+ left_path, right_path, merged_path,
+ lfile, rfile, outfile)
+ return
+ function()
+ local tmp = {split_command_string(cmd_format)}
+ local t = {L=left_path, R=right_path, M=merged_path,
+ l=lfile, r=rfile, o=outfile}
+ local argv = {}
+ for k, v in tmp do
+ argv[k] = string.gsub(v, "%%(%w)", function (x)
+ return t[x]
+ end)
+ end
+ return execute(unpack(argv))
+ end
+end
+
+function merge3_generic_cmd(cmd_format,
+ left_path, anc_path, right_path, merged_path,
+ lfile, afile, rfile, outfile)
+ return
+ function()
+ local tmp = {split_command_string(cmd_format)}
+ local t = {L=left_path, A=anc_path, R=right_path, M=merged_path,
+ l=lfile, a=afile, r=rfile, o=outfile}
+ local argv = {}
+ for k, v in tmp do
+ argv[k] = string.gsub(v, "%%(%w)", function (x)
+ return t[x]
+ end)
+ end
+ return execute(unpack(argv))
+ end
+end
+
function write_to_temporary_file(data)
tmp, filename = temp_file()
if (tmp == nil) then
@@ -343,11 +379,15 @@
local rfile = tbl.rfile
local outfile = tbl.outfile
+ local generic_merger = os.getenv("MONOTONE_MERGE2")
local editor = os.getenv("EDITOR")
if editor ~= nil then editor = string.lower(editor) else editor = "" end
-
- if program_exists_in_path("kdiff3") then
+ if generic_merger ~= nil then
+ cmd = merge2_generic_cmd (generic_merger,
+ left_path, right_path, merged_path,
+ lfile, rfile, outfile)
+ elseif program_exists_in_path("kdiff3") then
cmd = merge2_kdiff3_cmd (left_path, right_path, merged_path, lfile,
rfile, outfile)
elseif program_exists_in_path ("meld") then
tbl.meld_exists = true
@@ -396,7 +436,7 @@
local cmd = get_preferred_merge2_command (tbl)
- if cmd ~=nil
+ if cmd ~= nil
then
io.write (string.format("executing external 2-way merge command\n"))
cmd ()
@@ -433,10 +473,15 @@
local rfile = tbl.rfile
local outfile = tbl.outfile
+ local generic_merger = os.getenv("MONOTONE_MERGE3")
local editor = os.getenv("EDITOR")
if editor ~= nil then editor = string.lower(editor) else editor = "" end
- if program_exists_in_path("kdiff3") then
+ if generic_merger ~= nil then
+ cmd = merge3_generic_cmd (generic_merger,
+ left_path, anc_path, right_path, merged_path,
+ lfile, afile, rfile, outfile)
+ elseif program_exists_in_path("kdiff3") then
cmd = merge3_kdiff3_cmd (left_path, anc_path, right_path, merged_path,
lfile, afile, rfile, outfile)
elseif program_exists_in_path ("meld") then
tbl.meld_exists = true
@@ -488,7 +533,7 @@
if tbl.lfile ~= nil and tbl.rfile ~= nil and tbl.afile ~= nil and
tbl.outfile ~= nil
then
local cmd = get_preferred_merge3_command (tbl)
- if cmd ~=nil
+ if cmd ~= nil
then
io.write (string.format("executing external 3-way merge command\n"))
cmd ()
- [Monotone-devel] user-defined external mergers...,
Richard Levitte - VMS Whacker <=