[PATCH 4/4] strbuf_split*(): document functions
Document strbuf_split_buf(), strbuf_split_str(), strbuf_split_max(), strbuf_split(), and strbuf_list_free() in the header file and in api-strbuf.txt. (These functions were previously completely undocumented.) Signed-off-by: Michael Haggerty --- Documentation/technical/api-strbuf.txt | 16 strbuf.h | 33 + 2 files changed, 49 insertions(+) diff --git a/Documentation/technical/api-strbuf.txt b/Documentation/technical/api-strbuf.txt index 95a8bf3..84686b5 100644 --- a/Documentation/technical/api-strbuf.txt +++ b/Documentation/technical/api-strbuf.txt @@ -279,6 +279,22 @@ same behaviour as well. Strip whitespace from a buffer. The second parameter controls if comments are considered contents to be removed or not. +`strbuf_split_buf`:: +`strbuf_split_str`:: +`strbuf_split_max`:: +`strbuf_split`:: + + Split a string or strbuf into a list of strbufs at a specified + terminator character. The returned substrings include the + terminator characters. Some of these functions take a `max` + parameter, which, if positive, limits the output to that + number of substrings. + +`strbuf_list_free`:: + + Free a list of strbufs (for example, the return values of the + `strbuf_split()` functions). + `launch_editor`:: Launch the user preferred editor to edit a file and fill the buffer diff --git a/strbuf.h b/strbuf.h index c896a47..aa386c6 100644 --- a/strbuf.h +++ b/strbuf.h @@ -44,23 +44,56 @@ extern void strbuf_rtrim(struct strbuf *); extern void strbuf_ltrim(struct strbuf *); extern int strbuf_cmp(const struct strbuf *, const struct strbuf *); +/* + * Split str (of length slen) at the specified terminator character. + * Return a null-terminated array of pointers to strbuf objects + * holding the substrings. The substrings include the terminator, + * except for the last substring, which might be unterminated if the + * original string did not end with a terminator. If max is positive, + * then split the string into at most max substrings (with the last + * substring containing everything following the (max-1)th terminator + * character). + * + * For lighter-weight alternatives, see string_list_split() and + * string_list_split_in_place(). + */ extern struct strbuf **strbuf_split_buf(const char *, size_t, int terminator, int max); + +/* + * Split a NUL-terminated string at the specified terminator + * character. See strbuf_split_buf() for more information. + */ static inline struct strbuf **strbuf_split_str(const char *str, int terminator, int max) { return strbuf_split_buf(str, strlen(str), terminator, max); } + +/* + * Split a strbuf at the specified terminator character. See + * strbuf_split_buf() for more information. + */ static inline struct strbuf **strbuf_split_max(const struct strbuf *sb, int terminator, int max) { return strbuf_split_buf(sb->buf, sb->len, terminator, max); } + +/* + * Split a strbuf at the specified terminator character. See + * strbuf_split_buf() for more information. + */ static inline struct strbuf **strbuf_split(const struct strbuf *sb, int terminator) { return strbuf_split_max(sb, terminator, 0); } + +/* + * Free a NULL-terminated list of strbufs (for example, the return + * values of the strbuf_split*() functions). + */ extern void strbuf_list_free(struct strbuf **); /*- add data in your buffer -*/ -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 2/4] strbuf_split_buf(): simplify iteration
While iterating, update str and slen to keep track of the part of the string that hasn't been processed yet rather than computing things relative to the start of the original string. This eliminates one local variable, reduces the scope of another, and reduces the amount of arithmetic needed within the loop. Signed-off-by: Michael Haggerty --- strbuf.c | 23 ++- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/strbuf.c b/strbuf.c index 5256c2a..c7cd529 100644 --- a/strbuf.c +++ b/strbuf.c @@ -110,25 +110,22 @@ struct strbuf **strbuf_split_buf(const char *str, size_t slen, int delim, int ma { struct strbuf **ret = NULL; size_t nr = 0, alloc = 0; - const char *n, *p; struct strbuf *t; - p = n = str; - while (n < str + slen) { - int len; - if (max <= 0 || nr + 1 < max) - n = memchr(n, delim, slen - (n - str)); - else - n = NULL; - if (!n) - n = str + slen - 1; - len = n - p + 1; + while (slen) { + int len = slen; + if (max <= 0 || nr + 1 < max) { + const char *end = memchr(str, delim, slen); + if (end) + len = end - str + 1; + } t = xmalloc(sizeof(struct strbuf)); strbuf_init(t, len); - strbuf_add(t, p, len); + strbuf_add(t, str, len); ALLOC_GROW(ret, nr + 2, alloc); ret[nr++] = t; - p = ++n; + str += len; + slen -= len; } ALLOC_GROW(ret, nr + 1, alloc); /* In case string was empty */ ret[nr] = NULL; -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 3/4] strbuf_split*(): rename "delim" parameter to "terminator"
The word "delimiter" suggests that the argument separates the substrings, whereas in fact (1) the delimiter characters are included in the output, and (2) if the input string ends with the delimiter, then the output does not include a final empty string. So rename the "delim" arguments of the strbuf_split() family of functions to "terminator", which is more suggestive of how it is used. Signed-off-by: Michael Haggerty --- strbuf.c | 5 +++-- strbuf.h | 15 --- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/strbuf.c b/strbuf.c index c7cd529..05d0693 100644 --- a/strbuf.c +++ b/strbuf.c @@ -106,7 +106,8 @@ void strbuf_ltrim(struct strbuf *sb) sb->buf[sb->len] = '\0'; } -struct strbuf **strbuf_split_buf(const char *str, size_t slen, int delim, int max) +struct strbuf **strbuf_split_buf(const char *str, size_t slen, +int terminator, int max) { struct strbuf **ret = NULL; size_t nr = 0, alloc = 0; @@ -115,7 +116,7 @@ struct strbuf **strbuf_split_buf(const char *str, size_t slen, int delim, int ma while (slen) { int len = slen; if (max <= 0 || nr + 1 < max) { - const char *end = memchr(str, delim, slen); + const char *end = memchr(str, terminator, slen); if (end) len = end - str + 1; } diff --git a/strbuf.h b/strbuf.h index be941ee..c896a47 100644 --- a/strbuf.h +++ b/strbuf.h @@ -45,20 +45,21 @@ extern void strbuf_ltrim(struct strbuf *); extern int strbuf_cmp(const struct strbuf *, const struct strbuf *); extern struct strbuf **strbuf_split_buf(const char *, size_t, - int delim, int max); + int terminator, int max); static inline struct strbuf **strbuf_split_str(const char *str, - int delim, int max) + int terminator, int max) { - return strbuf_split_buf(str, strlen(str), delim, max); + return strbuf_split_buf(str, strlen(str), terminator, max); } static inline struct strbuf **strbuf_split_max(const struct strbuf *sb, - int delim, int max) + int terminator, int max) { - return strbuf_split_buf(sb->buf, sb->len, delim, max); + return strbuf_split_buf(sb->buf, sb->len, terminator, max); } -static inline struct strbuf **strbuf_split(const struct strbuf *sb, int delim) +static inline struct strbuf **strbuf_split(const struct strbuf *sb, + int terminator) { - return strbuf_split_max(sb, delim, 0); + return strbuf_split_max(sb, terminator, 0); } extern void strbuf_list_free(struct strbuf **); -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 1/4] strbuf_split_buf(): use ALLOC_GROW()
Use ALLOC_GROW() rather than inline code to manage memory in strbuf_split_buf(). Rename "pos" to "nr" because it better describes the use of the variable and it better conforms to the "ALLOC_GROW" idiom. Also, instead of adding a sentinal NULL value after each entry is added to the list, only add it once after all of the entries have been added. Signed-off-by: Michael Haggerty --- strbuf.c | 17 +++-- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/strbuf.c b/strbuf.c index 4b9e30c..5256c2a 100644 --- a/strbuf.c +++ b/strbuf.c @@ -108,33 +108,30 @@ void strbuf_ltrim(struct strbuf *sb) struct strbuf **strbuf_split_buf(const char *str, size_t slen, int delim, int max) { - int alloc = 2, pos = 0; + struct strbuf **ret = NULL; + size_t nr = 0, alloc = 0; const char *n, *p; - struct strbuf **ret; struct strbuf *t; - ret = xcalloc(alloc, sizeof(struct strbuf *)); p = n = str; while (n < str + slen) { int len; - if (max <= 0 || pos + 1 < max) + if (max <= 0 || nr + 1 < max) n = memchr(n, delim, slen - (n - str)); else n = NULL; - if (pos + 1 >= alloc) { - alloc = alloc * 2; - ret = xrealloc(ret, sizeof(struct strbuf *) * alloc); - } if (!n) n = str + slen - 1; len = n - p + 1; t = xmalloc(sizeof(struct strbuf)); strbuf_init(t, len); strbuf_add(t, p, len); - ret[pos] = t; - ret[++pos] = NULL; + ALLOC_GROW(ret, nr + 2, alloc); + ret[nr++] = t; p = ++n; } + ALLOC_GROW(ret, nr + 1, alloc); /* In case string was empty */ + ret[nr] = NULL; return ret; } -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 0/4] Simplify and document strbuf_split() functions
The strbuf_split() family of functions was completely undocumented. Add documentation and also simplify the definition of strbuf_split_buf(). Michael Haggerty (4): strbuf_split_buf(): use ALLOC_GROW() strbuf_split_buf(): simplify iteration strbuf_split*(): rename "delim" parameter to "terminator" strbuf_split*(): document functions Documentation/technical/api-strbuf.txt | 16 strbuf.c | 39 --- strbuf.h | 48 +- 3 files changed, 74 insertions(+), 29 deletions(-) -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v4 00/13] New remote-hg helper
On Fri, Nov 2, 2012 at 8:20 PM, Felipe Contreras wrote: > On Fri, Nov 2, 2012 at 7:39 PM, Felipe Contreras > wrote: > >> As a rule, I don't see much value in writing a framework that works >> only for one case, that smells more like over-engineering. If we had >> two cases (hg and bzr), then we might be able to know with a modicum >> of certainty what such a framework should have. So I would prefer to >> have two standalone remote-helpers, and _then_ do a framework to >> simplify both, but not before. But that's my personal opinion. >> >> Now that I have free time, I might be able to spend time writing such >> a proof-of-concept remote-bzr, and a simple framework. But I would be >> concentrated on remote-hg. > > Actually, there's no point in that; there's already a git-remote-bzr: > > http://bazaar.launchpad.net/~bzr-git/bzr-git/trunk/view/head:/git-remote-bzr Turns out the quality of that tools is not that great, so I decided to write a simple one using bzr-fastimport. It works nicely, although I wouldn't trust the quality of bzr-fastimport too much. It's so simple I don't see the need of a framework, but if needed, one could be done taking these git-remote-{hg,bzr} as a basis. Cheers. -- Felipe Contreras -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 3/3] remote-bzr: add simple tests
Signed-off-by: Felipe Contreras --- contrib/remote-helpers/test-bzr.sh | 111 + 1 file changed, 111 insertions(+) create mode 100755 contrib/remote-helpers/test-bzr.sh diff --git a/contrib/remote-helpers/test-bzr.sh b/contrib/remote-helpers/test-bzr.sh new file mode 100755 index 000..8594ffc --- /dev/null +++ b/contrib/remote-helpers/test-bzr.sh @@ -0,0 +1,111 @@ +#!/bin/sh +# +# Copyright (c) 2012 Felipe Contreras +# + +test_description='Test remote-bzr' + +. ./test-lib.sh + +if ! test_have_prereq PYTHON; then + skip_all='skipping remote-bzr tests; python not available' + test_done +fi + +if ! "$PYTHON_PATH" -c 'import bzrlib'; then + skip_all='skipping remote-bzr tests; bzr not available' + test_done +fi + +cmd=<&2 + skip_all='skipping remote-bzr tests; bzr-fastimport not available' + test_done +fi + +check () { + (cd $1 && + git log --format='%s' -1 && + git symbolic-ref HEAD) > actual && + (echo $2 && + echo "refs/heads/$3") > expected && + test_cmp expected actual +} + +bzr whoami "A U Thor " + +test_expect_success 'cloning' ' + (bzr init bzrrepo && + cd bzrrepo && + echo one > content && + bzr add content && + bzr commit -m one + ) && + + git clone "bzr::$PWD/bzrrepo" gitrepo && + check gitrepo one master +' + +test_expect_success 'pulling' ' + (cd bzrrepo && + echo two > content && + bzr commit -m two + ) && + + (cd gitrepo && git pull) && + + check gitrepo two master +' + +test_expect_success 'pushing' ' + (cd gitrepo && + echo three > content && + git commit -a -m three && + git push + ) && + + echo three > expected && + cat bzrrepo/content > actual && + test_cmp expected actual +' + +test_expect_success 'roundtrip' ' + (cd gitrepo && + git pull && + git log --format="%s" -1 origin/master > actual) && + echo three > expected && + test_cmp expected actual && + + (cd gitrepo && git push && git pull) && + + (cd bzrrepo && + echo four > content && + bzr commit -m four + ) && + + (cd gitrepo && git pull && git push) && + + check gitrepo four master && + + (cd gitrepo && + echo five > content && + git commit -a -m five && + git push && git pull + ) && + + (cd bzrrepo && bzr revert) && + + echo five > expected && + cat bzrrepo/content > actual && + test_cmp expected actual +' + +test_done -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 2/3] remote-bzr: add support for pushing
Signed-off-by: Felipe Contreras --- contrib/remote-helpers/git-remote-bzr | 34 ++ 1 file changed, 34 insertions(+) diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr index 76a609a..de37217 100755 --- a/contrib/remote-helpers/git-remote-bzr +++ b/contrib/remote-helpers/git-remote-bzr @@ -26,6 +26,9 @@ bzrlib.plugin.load_plugins() import bzrlib.revisionspec from bzrlib.plugins.fastimport import exporter as bzr_exporter +from bzrlib.plugins.fastimport.processors import generic_processor as bzr_generic_processor + +import fastimport.parser import sys import os @@ -140,9 +143,38 @@ def do_import(parser): sys.stdout.flush() +def do_export(parser): +global dirname + +parser.next() # can't handle 'done'? + +controldir = parser.repo.bzrdir + +path = os.path.join(dirname, 'marks-bzr') +params = { 'import-marks': path, 'export-marks': path } + +proc = bzr_generic_processor.GenericProcessor(controldir, params) +p = fastimport.parser.ImportParser(sys.stdin) +proc.process(p.iter_commands) + +if parser.repo.last_revision() != marks.get_tip('master'): +print 'ok %s' % 'refs/heads/master' + +print + def do_capabilities(parser): +global dirname + print "import" +print "export" print "refspec refs/heads/*:%s/heads/*" % prefix + +path = os.path.join(dirname, 'marks-git') + +if os.path.exists(path): +print "*import-marks %s" % path +print "*export-marks %s" % path + print def do_list(parser): @@ -178,6 +210,8 @@ def main(args): do_list(parser) elif parser.check('import'): do_import(parser) +elif parser.check('export'): +do_export(parser) else: die('unhandled command: %s' % line) sys.stdout.flush() -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 1/3] Add new remote-bzr transport helper
Signed-off-by: Felipe Contreras --- contrib/remote-helpers/git-remote-bzr | 187 ++ 1 file changed, 187 insertions(+) create mode 100755 contrib/remote-helpers/git-remote-bzr diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr new file mode 100755 index 000..76a609a --- /dev/null +++ b/contrib/remote-helpers/git-remote-bzr @@ -0,0 +1,187 @@ +#!/usr/bin/env python +# +# Copyright (c) 2012 Felipe Contreras +# + +# Just copy to your ~/bin, or anywhere in your $PATH. +# Then you can clone with: +# git clone bzr::/path/to/mercurial/repo/ +# +# You need to have bzr-fastimport installed: +# http://wiki.bazaar.canonical.com/BzrFastImport +# +# You might also need to find this line bzr-fastimport's +# code, and modify it: +# +# self._use_known_graph = False + +import sys + +import bzrlib +bzrlib.initialize() + +import bzrlib.plugin +bzrlib.plugin.load_plugins() + +import bzrlib.revisionspec + +from bzrlib.plugins.fastimport import exporter as bzr_exporter + +import sys +import os +import json + +def die(msg, *args): +sys.stderr.write('ERROR: %s\n' % (msg % args)) +sys.exit(1) + +def warn(msg, *args): +sys.stderr.write('WARNING: %s\n' % (msg % args)) + +class Marks: + +def __init__(self, path): +self.path = path +self.tips = {} +self.load() + +def load(self): +if not os.path.exists(self.path): +return + +tmp = json.load(open(self.path)) +self.tips = tmp['tips'] + +def dict(self): +return { 'tips': self.tips } + +def store(self): +json.dump(self.dict(), open(self.path, 'w')) + +def __str__(self): +return str(self.dict()) + +def get_tip(self, branch): +return self.tips.get(branch, '1') + +def set_tip(self, branch, tip): +self.tips[branch] = tip + +class Parser: + +def __init__(self, repo): +self.repo = repo +self.line = self.get_line() + +def get_line(self): +return sys.stdin.readline().strip() + +def __getitem__(self, i): +return self.line.split()[i] + +def check(self, word): +return self.line.startswith(word) + +def each_block(self, separator): +while self.line != separator: +yield self.line +self.line = self.get_line() + +def __iter__(self): +return self.each_block('') + +def next(self): +self.line = self.get_line() +if self.line == 'done': +self.line = None + +def export_branch(branch, name): +global prefix, dirname + +marks_path = os.path.join(dirname, 'marks-bzr') +ref = '%s/heads/%s' % (prefix, name) +tip = marks.get_tip(name) +start = "before:%s" % tip +rev1 = bzrlib.revisionspec.RevisionSpec.from_string(start) +rev2 = bzrlib.revisionspec.RevisionSpec.from_string(None) + +exporter = bzr_exporter.BzrFastExporter(branch, +outf=sys.stdout, ref=ref, +checkpoint=None, +import_marks_file=marks_path, +export_marks_file=marks_path, +revision=[rev1, rev2], +verbose=None, plain_format=True, +rewrite_tags=False) +exporter.run() + +marks.set_tip(name, branch.last_revision()) + +def do_import(parser): +global dirname + +branch = parser.repo +path = os.path.join(dirname, 'marks-git') + +print "feature done" +if os.path.exists(path): +print "feature import-marks=%s" % path +print "feature export-marks=%s" % path +sys.stdout.flush() + +while parser.check('import'): +ref = parser[1] +if ref.startswith('refs/heads/'): +name = ref[len('refs/heads/'):] +export_branch(branch, name) +parser.next() + +print 'done' + +sys.stdout.flush() + +def do_capabilities(parser): +print "import" +print "refspec refs/heads/*:%s/heads/*" % prefix +print + +def do_list(parser): +print "? refs/heads/%s" % 'master' +print "@refs/heads/%s HEAD" % 'master' +print + +def main(args): +global marks, prefix, dirname + +alias = args[1] +url = args[2] + +d = bzrlib.controldir.ControlDir.open(url) +repo = d.open_branch() + +prefix = 'refs/bzr/%s' % alias + +gitdir = os.environ['GIT_DIR'] +dirname = os.path.join(gitdir, 'bzr', alias) + +if not os.path.exists(dirname): +os.makedirs(dirname) + +marks_path = os.path.join(dirname, 'marks-int') +marks = Marks(marks_path) + +parser = Parser(repo) +for line in parser: +if parser.check('capabilities'): +do_capabilities(parser) +elif parser.check('list'): +do_list(parser) +elif parser.check('import'): +do_import(parser) +else: +die('unhandled command: %s' % line) +sys.stdout.flush() + +marks.store() + +sys.exit(main(sys.argv)) -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the
[PATCH 0/3] New remote-bzr remote helper
Hi, This is a proof-of-concept remote helper for bzr, that turns out to work rather well. It uses bzr-fastimport[1], which is what most current bzr<->git tools uses, but the quality is not that great. After applying an important fix[2], it works nicely, but you will get tons of verbose messages. Eventually we might want to implement the same functionality as bzr-fastimport, but for now this already works nicely enough. Cheers. [1] http://wiki.bazaar.canonical.com/BzrFastImport [2] https://launchpadlibrarian.net/121967844/bzr-fastimport-no-graph.patch Felipe Contreras (3): Add new remote-bzr transport helper remote-bzr: add support for pushing remote-bzr: add simple tests contrib/remote-helpers/git-remote-bzr | 221 ++ contrib/remote-helpers/test-bzr.sh| 111 + 2 files changed, 332 insertions(+) create mode 100755 contrib/remote-helpers/git-remote-bzr create mode 100755 contrib/remote-helpers/test-bzr.sh -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v6 16/16] remote-hg: the author email can be null
Like 'Foo <>'. Signed-off-by: Felipe Contreras --- contrib/remote-helpers/git-remote-hg | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg index a9ae844..7929eec 100755 --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@ -35,9 +35,9 @@ import urllib # NAME_RE = re.compile('^([^<>]+)') -AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]+)>$') -AUTHOR_HG_RE = re.compile('^(.*?) ?<(.+?)(?:>(.+)?)?$') -RAW_AUTHOR_RE = re.compile('^(\w+) (?:(.+)? )?<(.+)> (\d+) ([+-]\d+)') +AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]*)>$') +AUTHOR_HG_RE = re.compile('^(.*?) ?<(.*?)(?:>(.+)?)?$') +RAW_AUTHOR_RE = re.compile('^(\w+) (?:(.+)? )?<(.*)> (\d+) ([+-]\d+)') def die(msg, *args): sys.stderr.write('ERROR: %s\n' % (msg % args)) -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v6 15/16] remote-hg: add option to not track branches
Some people prefer it this way. % git config --global remote-hg.track-branches false Signed-off-by: Felipe Contreras --- contrib/remote-helpers/git-remote-hg | 22 ++ 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg index dbe309a..a9ae844 100755 --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@ -449,14 +449,9 @@ def list_head(repo, cur): g_head = (head, node) def do_list(parser): -global branches, bmarks, mode +global branches, bmarks, mode, track_branches repo = parser.repo -for branch in repo.branchmap(): -heads = repo.branchheads(branch) -if len(heads): -branches[branch] = heads - for bmark, node in bookmarks.listbookmarks(repo).iteritems(): bmarks[bmark] = repo[node] @@ -464,7 +459,12 @@ def do_list(parser): list_head(repo, cur) -if mode != 'hg': +if track_branches: +for branch in repo.branchmap(): +heads = repo.branchheads(branch) +if len(heads): +branches[branch] = heads + for branch in branches: print "? refs/heads/branches/%s" % branch @@ -713,16 +713,22 @@ def main(args): global prefix, dirname, branches, bmarks global marks, blob_marks, parsed_refs global peer, mode, bad_mail, bad_name +global track_branches alias = args[1] url = args[2] peer = None -cmd = ['git', 'config', '--get', 'remote-hg.hg-git-compat'] hg_git_compat = False +track_branches = True try: +cmd = ['git', 'config', '--get', 'remote-hg.hg-git-compat'] if subprocess.check_output(cmd) == 'true\n': hg_git_compat = True +track_branches = False +cmd = ['git', 'config', '--get', 'remote-hg.track-branches'] +if subprocess.check_output(cmd) == 'false\n': +track_branches = False except subprocess.CalledProcessError: pass -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v6 14/16] remote-hg: add extra author test
For hg.hg. Signed-off-by: Felipe Contreras --- contrib/remote-helpers/test-hg-hg-git.sh | 6 +- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/contrib/remote-helpers/test-hg-hg-git.sh b/contrib/remote-helpers/test-hg-hg-git.sh index e07bba5..3e76d9f 100755 --- a/contrib/remote-helpers/test-hg-hg-git.sh +++ b/contrib/remote-helpers/test-hg-hg-git.sh @@ -370,7 +370,11 @@ test_expect_success 'hg author' ' echo theta > theta && hg add theta && - hg commit -u "test >t...@example.com>" -m "add theta" + hg commit -u "test >t...@example.com>" -m "add theta" && + + echo iota > iota && + hg add iota && + hg commit -u "test example com>" -m "add iota" ) && hg_push_$x hgrepo-$x gitrepo-$x && -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v6 13/16] remote-hg: add tests to compare with hg-git
The base commands come from the tests of the hg-git project. Signed-off-by: Felipe Contreras --- contrib/remote-helpers/test-hg-hg-git.sh | 462 +++ 1 file changed, 462 insertions(+) create mode 100755 contrib/remote-helpers/test-hg-hg-git.sh diff --git a/contrib/remote-helpers/test-hg-hg-git.sh b/contrib/remote-helpers/test-hg-hg-git.sh new file mode 100755 index 000..e07bba5 --- /dev/null +++ b/contrib/remote-helpers/test-hg-hg-git.sh @@ -0,0 +1,462 @@ +#!/bin/sh +# +# Copyright (c) 2012 Felipe Contreras +# +# Base commands from hg-git tests: +# https://bitbucket.org/durin42/hg-git/src +# + +test_description='Test remote-hg output compared to hg-git' + +. ./test-lib.sh + +if ! test_have_prereq PYTHON; then + skip_all='skipping remote-hg tests; python not available' + test_done +fi + +if ! "$PYTHON_PATH" -c 'import mercurial'; then + skip_all='skipping remote-hg tests; mercurial not available' + test_done +fi + +if ! "$PYTHON_PATH" -c 'import hggit'; then + skip_all='skipping remote-hg tests; hg-git not available' + test_done +fi + +# clone to a git repo with git +git_clone_git () { + hg -R $1 bookmark -f -r tip master && + git clone -q "hg::$PWD/$1" $2 +} + +# clone to an hg repo with git +hg_clone_git () { + ( + hg init $2 && + cd $1 && + git push -q "hg::$PWD/../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' + ) && + + (cd $2 && hg -q update) +} + +# clone to a git repo with hg +git_clone_hg () { + ( + git init -q $2 && + cd $1 && + hg bookmark -f -r tip master && + hg -q push -r master ../$2 || true + ) +} + +# clone to an hg repo with hg +hg_clone_hg () { + hg -q clone $1 $2 +} + +# push an hg repo with git +hg_push_git () { + ( + cd $2 + old=$(git symbolic-ref --short HEAD) + git checkout -q -b tmp && + git fetch -q "hg::$PWD/../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' && + git checkout -q $old && + git branch -q -D tmp 2> /dev/null || true + ) +} + +# push an hg git repo with hg +hg_push_hg () { + ( + cd $1 && + hg -q push ../$2 || true + ) +} + +hg_log () { + hg -R $1 log --graph --debug | grep -v 'tag: *default/' +} + +git_log () { + git --git-dir=$1/.git fast-export --branches +} + +setup () { + ( + echo "[ui]" + echo "username = A U Thor " + echo "[defaults]" + echo "backout = -d \"0 0\"" + echo "commit = -d \"0 0\"" + echo "debugrawcommit = -d \"0 0\"" + echo "tag = -d \"0 0\"" + echo "[extensions]" + echo "hgext.bookmarks =" + echo "hggit =" + ) >> "$HOME"/.hgrc && + git config --global receive.denycurrentbranch warn + git config --global remote-hg.hg-git-compat true + + export HGEDITOR=/usr/bin/true + + export GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230" + export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" +} + +setup + +test_expect_success 'merge conflict 1' ' + mkdir -p tmp && cd tmp && + test_when_finished "cd .. && rm -rf tmp" && + + ( + hg init hgrepo1 && + cd hgrepo1 && + echo A > afile && + hg add afile && + hg ci -m "origin" && + + echo B > afile && + hg ci -m "A->B" && + + hg up -r0 && + echo C > afile && + hg ci -m "A->C" && + + hg merge -r1 || true && + echo C > afile && + hg resolve -m afile && + hg ci -m "merge to C" + ) && + + for x in hg git; do + git_clone_$x hgrepo1 gitrepo-$x && + hg_clone_$x gitrepo-$x hgrepo2-$x && + hg_log hgrepo2-$x > hg-log-$x && + git_log gitrepo-$x > git-log-$x + done && + + test_cmp hg-log-hg hg-log-git && + test_cmp git-log-hg git-log-git +' + +test_expect_success 'merge conflict 2' ' + mkdir -p tmp && cd tmp && + test_when_finished "cd .. && rm -rf tmp" && + + ( + hg init hgrepo1 && + cd hgrepo1 && + echo A > afile && + hg add afile && + hg ci -m "origin" && + + echo B > afile && + hg ci -m "A->B" && + + hg up -r0 && + echo C > afile && + hg ci -m "A->C" && + + hg merge -r1 || true && + echo B > afile && + hg resolve -m afile && + hg ci -m "merge to B" + ) && + + for x in hg git; do + git_clone_$x hgrepo1 gitrepo-$x && + hg_clone_$x gitrepo-$x hgrepo2-$x && + hg_log hgrepo2-$x > hg-log-$x && + git_log gitrepo-$x > git-log-$x + done && + + test_cmp hg-log-hg hg-log-git && + test_cmp git-log-hg git-log-git +' + +test_expect_success 'converged merge' ' + mkdir -p tmp && cd tmp && + test_when_finished "cd .. && rm -rf tmp" && + + ( + hg init hgrepo1 && + cd hgrepo1 && + echo A > af
[PATCH v6 11/16] test-lib: avoid full path to store test results
No reason to use the full path in case this is used externally. Otherwise we might get errors such as: ./test-lib.sh: line 394: /home/bob/dev/git/t/test-results//home/bob/dev/git/contrib/remote-hg/test-2894.counts: No such file or directory Signed-off-by: Felipe Contreras --- t/test-lib.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index 514282c..5a3d665 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -389,7 +389,8 @@ test_done () { then test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results" mkdir -p "$test_results_dir" - test_results_path="$test_results_dir/${0%.sh}-$$.counts" + base=${0##*/} + test_results_path="$test_results_dir/${base%.sh}-$$.counts" cat >>"$test_results_path" <<-EOF total $test_count -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v6 12/16] remote-hg: add bidirectional tests
Base commands from hg-git tests: https://bitbucket.org/durin42/hg-git/src Signed-off-by: Felipe Contreras --- contrib/remote-helpers/test-hg-bidi.sh | 243 + 1 file changed, 243 insertions(+) create mode 100755 contrib/remote-helpers/test-hg-bidi.sh diff --git a/contrib/remote-helpers/test-hg-bidi.sh b/contrib/remote-helpers/test-hg-bidi.sh new file mode 100755 index 000..a94eb28 --- /dev/null +++ b/contrib/remote-helpers/test-hg-bidi.sh @@ -0,0 +1,243 @@ +#!/bin/sh +# +# Copyright (c) 2012 Felipe Contreras +# +# Base commands from hg-git tests: +# https://bitbucket.org/durin42/hg-git/src +# + +test_description='Test biridectionality of remote-hg' + +. ./test-lib.sh + +if ! test_have_prereq PYTHON; then + skip_all='skipping remote-hg tests; python not available' + test_done +fi + +if ! "$PYTHON_PATH" -c 'import mercurial'; then + skip_all='skipping remote-hg tests; mercurial not available' + test_done +fi + +# clone to a git repo +git_clone () { + hg -R $1 bookmark -f -r tip master && + git clone -q "hg::$PWD/$1" $2 +} + +# clone to an hg repo +hg_clone () { + ( + hg init $2 && + cd $1 && + git push -q "hg::$PWD/../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' + ) && + + (cd $2 && hg -q update) +} + +# push an hg repo +hg_push () { + ( + cd $2 + old=$(git symbolic-ref --short HEAD) + git checkout -q -b tmp && + git fetch -q "hg::$PWD/../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' && + git checkout -q $old && + git branch -q -D tmp 2> /dev/null || true + ) +} + +hg_log () { + hg -R $1 log --graph --debug | grep -v 'tag: *default/' +} + +setup () { + ( + echo "[ui]" + echo "username = A U Thor " + echo "[defaults]" + echo "backout = -d \"0 0\"" + echo "commit = -d \"0 0\"" + echo "debugrawcommit = -d \"0 0\"" + echo "tag = -d \"0 0\"" + ) >> "$HOME"/.hgrc && + git config --global remote-hg.hg-git-compat true + + export HGEDITOR=/usr/bin/true + + export GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230" + export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" +} + +setup + +test_expect_success 'encoding' ' + mkdir -p tmp && cd tmp && + test_when_finished "cd .. && rm -rf tmp" && + + ( + git init -q gitrepo && + cd gitrepo && + + echo alpha > alpha && + git add alpha && + git commit -m "add älphà" && + + export GIT_AUTHOR_NAME="tést èncödîng" && + echo beta > beta && + git add beta && + git commit -m "add beta" && + + echo gamma > gamma && + git add gamma && + git commit -m "add gämmâ" && + + : TODO git config i18n.commitencoding latin-1 && + echo delta > delta && + git add delta && + git commit -m "add déltà" + ) && + + hg_clone gitrepo hgrepo && + git_clone hgrepo gitrepo2 && + hg_clone gitrepo2 hgrepo2 && + + HGENCODING=utf-8 hg_log hgrepo > expected && + HGENCODING=utf-8 hg_log hgrepo2 > actual && + + test_cmp expected actual +' + +test_expect_success 'file removal' ' + mkdir -p tmp && cd tmp && + test_when_finished "cd .. && rm -rf tmp" && + + ( + git init -q gitrepo && + cd gitrepo && + echo alpha > alpha && + git add alpha && + git commit -m "add alpha" && + echo beta > beta && + git add beta && + git commit -m "add beta" + mkdir foo && + echo blah > foo/bar && + git add foo && + git commit -m "add foo" && + git rm alpha && + git commit -m "remove alpha" && + git rm foo/bar && + git commit -m "remove foo/bar" + ) && + + hg_clone gitrepo hgrepo && + git_clone hgrepo gitrepo2 && + hg_clone gitrepo2 hgrepo2 && + + hg_log hgrepo > expected && + hg_log hgrepo2 > actual && + + test_cmp expected actual +' + +test_expect_success 'git tags' ' + mkdir -p tmp && cd tmp && + test_when_finished "cd .. && rm -rf tmp" && + + ( + git init -q gitrepo && + cd gitrepo && + git config receive.denyCurrentBranch ignore && + echo alpha > alpha && + git add alpha && + git commit -m "add alpha" && + git tag alpha && + + echo beta > beta && + git add beta && + git commit -m "add beta" && + git tag -a -m "added tag beta" beta + ) && + + hg_clone gitrepo hgrepo && + git_clone hgrepo gitrepo2 && + hg_clone gitrepo2 hgrepo2 && + + hg_log hgrepo > expected && + hg_log hgrepo2 > actual && + + test_cmp expected actual +' + +test_expect_success 'hg branch' ' + mkdir -p tmp && cd tmp && + test_when_finished "cd .. && rm -rf tmp" && + + ( + git init -q gitrepo && + cd gitrepo && + + echo alpha > alpha && + git add a
[PATCH v6 10/16] remote-hg: add basic tests
Signed-off-by: Felipe Contreras --- contrib/remote-helpers/Makefile | 13 + contrib/remote-helpers/test-hg.sh | 112 ++ 2 files changed, 125 insertions(+) create mode 100644 contrib/remote-helpers/Makefile create mode 100755 contrib/remote-helpers/test-hg.sh diff --git a/contrib/remote-helpers/Makefile b/contrib/remote-helpers/Makefile new file mode 100644 index 000..9a76575 --- /dev/null +++ b/contrib/remote-helpers/Makefile @@ -0,0 +1,13 @@ +TESTS := $(wildcard test*.sh) + +export T := $(addprefix $(CURDIR)/,$(TESTS)) +export MAKE := $(MAKE) -e +export PATH := $(CURDIR):$(PATH) + +test: + $(MAKE) -C ../../t $@ + +$(TESTS): + $(MAKE) -C ../../t $(CURDIR)/$@ + +.PHONY: $(TESTS) diff --git a/contrib/remote-helpers/test-hg.sh b/contrib/remote-helpers/test-hg.sh new file mode 100755 index 000..40e6e3c --- /dev/null +++ b/contrib/remote-helpers/test-hg.sh @@ -0,0 +1,112 @@ +#!/bin/sh +# +# Copyright (c) 2012 Felipe Contreras +# +# Base commands from hg-git tests: +# https://bitbucket.org/durin42/hg-git/src +# + +test_description='Test remote-hg' + +. ./test-lib.sh + +if ! test_have_prereq PYTHON; then + skip_all='skipping remote-hg tests; python not available' + test_done +fi + +if ! "$PYTHON_PATH" -c 'import mercurial'; then + skip_all='skipping remote-hg tests; mercurial not available' + test_done +fi + +check () { + (cd $1 && + git log --format='%s' -1 && + git symbolic-ref HEAD) > actual && + (echo $2 && + echo "refs/heads/$3") > expected && + test_cmp expected actual +} + +test_expect_success 'cloning' ' + test_when_finished "rm -rf gitrepo*" && + + ( + hg init hgrepo && + cd hgrepo && + echo zero > content && + hg add content && + hg commit -m zero + ) && + + git clone "hg::$PWD/hgrepo" gitrepo && + check gitrepo zero master +' + +test_expect_success 'cloning with branches' ' + test_when_finished "rm -rf gitrepo*" && + + ( + cd hgrepo && + hg branch next && + echo next > content && + hg commit -m next + ) && + + git clone "hg::$PWD/hgrepo" gitrepo && + check gitrepo next next && + + (cd hgrepo && hg checkout default) && + + git clone "hg::$PWD/hgrepo" gitrepo2 && + check gitrepo2 zero master +' + +test_expect_success 'cloning with bookmarks' ' + test_when_finished "rm -rf gitrepo*" && + + ( + cd hgrepo && + hg bookmark feature-a && + echo feature-a > content && + hg commit -m feature-a + ) && + + git clone "hg::$PWD/hgrepo" gitrepo && + check gitrepo feature-a feature-a +' + +test_expect_success 'cloning with detached head' ' + test_when_finished "rm -rf gitrepo*" && + + ( + cd hgrepo && + hg update -r 0 + ) && + + git clone "hg::$PWD/hgrepo" gitrepo && + check gitrepo zero master +' + +test_expect_success 'update bookmark' ' + test_when_finished "rm -rf gitrepo*" && + + ( + cd hgrepo && + hg bookmark devel + ) && + + ( + git clone "hg::$PWD/hgrepo" gitrepo && + cd gitrepo && + git checkout devel && + echo devel > content && + git commit -a -m devel && + git push + ) && + + hg -R hgrepo bookmarks | grep "devel\s\+3:" +' + +test_done -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v6 09/16] remote-hg: fake bookmark when there's none
Or at least no current bookmark. Signed-off-by: Felipe Contreras --- contrib/remote-helpers/git-remote-hg | 19 ++- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg index 9db4b7e..dbe309a 100755 --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@ -26,7 +26,7 @@ import urllib # git: # Sensible defaults for git. # hg bookmarks are exported as git branches, hg branches are prefixed -# with 'branches/'. +# with 'branches/', HEAD is a special case. # # hg: # Emulate hg-git. @@ -430,12 +430,21 @@ def get_branch_tip(repo, branch): return heads[0] def list_head(repo, cur): -global g_head +global g_head, bmarks head = bookmarks.readcurrent(repo) -if not head: -return -node = repo[head] +if head: +node = repo[head] +else: +# fake bookmark from current branch +head = cur +node = repo['.'] +if not node: +return +if head == 'default': +head = 'master' +bmarks[head] = node + print "@refs/heads/%s HEAD" % head g_head = (head, node) -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v6 08/16] remote-hg: add compat for hg-git author fixes
Signed-off-by: Felipe Contreras --- contrib/remote-helpers/git-remote-hg | 59 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg index d585756..9db4b7e 100755 --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@ -17,6 +17,7 @@ import os import json import shutil import subprocess +import urllib # # If you want to switch to hg-git compatibility mode: @@ -35,6 +36,7 @@ import subprocess NAME_RE = re.compile('^([^<>]+)') AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]+)>$') +AUTHOR_HG_RE = re.compile('^(.*?) ?<(.+?)(?:>(.+)?)?$') RAW_AUTHOR_RE = re.compile('^(\w+) (?:(.+)? )?<(.+)> (\d+) ([+-]\d+)') def die(msg, *args): @@ -152,12 +154,20 @@ class Parser: return sys.stdin.read(size) def get_author(self): +global bad_mail + +ex = None m = RAW_AUTHOR_RE.match(self.line) if not m: return None _, name, email, date, tz = m.groups() +if name and 'ext:' in name: +m = re.match('^(.+?) ext:\((.+)\)$', name) +if m: +name = m.group(1) +ex = urllib.unquote(m.group(2)) -if email != 'unknown': +if email != bad_mail: if name: user = '%s <%s>' % (name, email) else: @@ -165,6 +175,9 @@ class Parser: else: user = name +if ex: +user += ex + tz = int(tz) tz = ((tz / 100) * 3600) + ((tz % 100) * 60) return (user, int(date), -tz) @@ -194,9 +207,9 @@ def get_filechanges(repo, ctx, parent): return added | modified, removed -def fixup_user(user): -user = user.replace('"', '') +def fixup_user_git(user): name = mail = None +user = user.replace('"', '') m = AUTHOR_RE.match(user) if m: name = m.group(1) @@ -205,11 +218,41 @@ def fixup_user(user): m = NAME_RE.match(user) if m: name = m.group(1).strip() +return (name, mail) + +def fixup_user_hg(user): +def sanitize(name): +# stole this from hg-git +return re.sub('[<>\n]', '?', name.lstrip('< ').rstrip('> ')) + +m = AUTHOR_HG_RE.match(user) +if m: +name = sanitize(m.group(1)) +mail = sanitize(m.group(2)) +ex = m.group(3) +if ex: +name += ' ext:(' + urllib.quote(ex) + ')' +else: +name = sanitize(user) +if '@' in user: +mail = name +else: +mail = None + +return (name, mail) + +def fixup_user(user): +global mode, bad_mail + +if mode == 'git': +name, mail = fixup_user_git(user) +else: +name, mail = fixup_user_hg(user) if not name: -name = 'Unknown' +name = bad_name if not mail: -mail = 'unknown' +mail = bad_mail return '%s <%s>' % (name, mail) @@ -660,7 +703,7 @@ def do_export(parser): def main(args): global prefix, dirname, branches, bmarks global marks, blob_marks, parsed_refs -global peer, mode +global peer, mode, bad_mail, bad_name alias = args[1] url = args[2] @@ -676,8 +719,12 @@ def main(args): if hg_git_compat: mode = 'hg' +bad_mail = 'none@none' +bad_name = '' else: mode = 'git' +bad_mail = 'unknown' +bad_name = 'Unknown' if alias[4:] == url: is_tmp = True -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v6 06/16] remote-hg: match hg merge behavior
Signed-off-by: Felipe Contreras --- contrib/remote-helpers/git-remote-hg | 17 + 1 file changed, 17 insertions(+) diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg index 503a9fc..247b7cb 100755 --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@ -427,6 +427,14 @@ def parse_blob(parser): parser.next() return +def get_merge_files(repo, p1, p2, files): +for e in repo[p1].files(): +if e not in files: +if e not in repo[p1].manifest(): +continue +f = { 'ctx' : repo[p1][e] } +files[e] = f + def parse_commit(parser): global marks, blob_marks, bmarks, parsed_refs @@ -470,6 +478,8 @@ def parse_commit(parser): of = files[f] if 'deleted' in of: raise IOError +if 'ctx' in of: +return of['ctx'] is_exec = of['mode'] == 'x' is_link = of['mode'] == 'l' return context.memfilectx(f, of['data'], is_link, is_exec, None) @@ -492,6 +502,13 @@ def parse_commit(parser): else: p2 = '\0' * 20 +# +# If files changed from any of the parents, hg wants to know, but in git if +# nothing changed from the first parent, nothing changed. +# +if merge_mark: +get_merge_files(repo, p1, p2, files) + ctx = context.memctx(repo, (p1, p2), data, files.keys(), getfilectx, user, (date, tz), extra) -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v6 07/16] remote-hg: add support for hg-git compat mode
Signed-off-by: Felipe Contreras --- contrib/remote-helpers/git-remote-hg | 89 +--- 1 file changed, 83 insertions(+), 6 deletions(-) diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg index 247b7cb..d585756 100755 --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@ -16,6 +16,22 @@ import sys import os import json import shutil +import subprocess + +# +# If you want to switch to hg-git compatibility mode: +# git config --global remote-hg.hg-git-compat true +# +# git: +# Sensible defaults for git. +# hg bookmarks are exported as git branches, hg branches are prefixed +# with 'branches/'. +# +# hg: +# Emulate hg-git. +# Only hg bookmarks are exported as git branches. +# Commits are modified to preserve hg information and allow biridectionality. +# NAME_RE = re.compile('^([^<>]+)') AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]+)>$') @@ -226,7 +242,7 @@ def mark_to_rev(mark): return marks.to_rev(mark) def export_ref(repo, name, kind, head): -global prefix, marks +global prefix, marks, mode ename = '%s/%s' % (kind, name) tip = marks.get_tip(ename) @@ -261,6 +277,33 @@ def export_ref(repo, name, kind, head): else: modified, removed = get_filechanges(repo, c, parents[0]) +if mode == 'hg': +extra_msg = '' + +if rev_branch != 'default': +extra_msg += 'branch : %s\n' % rev_branch + +renames = [] +for f in c.files(): +if f not in c.manifest(): +continue +rename = c.filectx(f).renamed() +if rename: +renames.append((rename[0], f)) + +for e in renames: +extra_msg += "rename : %s => %s\n" % e + +for key, value in extra.iteritems(): +if key in ('author', 'committer', 'encoding', 'message', 'branch', 'hg-git'): +continue +else: +extra_msg += "extra : %s : %s\n" % (key, urllib.quote(value)) + +desc += '\n' +if extra_msg: +desc += '\n--HG--\n' + extra_msg + if len(parents) == 0 and rev: print 'reset %s/%s' % (prefix, ename) @@ -354,7 +397,7 @@ def list_head(repo, cur): g_head = (head, node) def do_list(parser): -global branches, bmarks +global branches, bmarks, mode repo = parser.repo for branch in repo.branchmap(): @@ -368,8 +411,11 @@ def do_list(parser): cur = repo.dirstate.branch() list_head(repo, cur) -for branch in branches: -print "? refs/heads/branches/%s" % branch + +if mode != 'hg': +for branch in branches: +print "? refs/heads/branches/%s" % branch + for bmark in bmarks: print "? refs/heads/%s" % bmark @@ -437,6 +483,7 @@ def get_merge_files(repo, p1, p2, files): def parse_commit(parser): global marks, blob_marks, bmarks, parsed_refs +global mode from_mark = merge_mark = None @@ -482,7 +529,9 @@ def parse_commit(parser): return of['ctx'] is_exec = of['mode'] == 'x' is_link = of['mode'] == 'l' -return context.memfilectx(f, of['data'], is_link, is_exec, None) +rename = of.get('rename', None) +return context.memfilectx(f, of['data'], +is_link, is_exec, rename) repo = parser.repo @@ -509,6 +558,21 @@ def parse_commit(parser): if merge_mark: get_merge_files(repo, p1, p2, files) +if mode == 'hg': +i = data.find('\n--HG--\n') +if i >= 0: +tmp = data[i + len('\n--HG--\n'):].strip() +for k, v in [e.split(' : ') for e in tmp.split('\n')]: +if k == 'rename': +old, new = v.split(' => ', 1) +files[new]['rename'] = old +elif k == 'branch': +extra[k] = v +elif k == 'extra': +ek, ev = v.split(' : ', 1) +extra[ek] = urllib.unquote(ev) +data = data[:i] + ctx = context.memctx(repo, (p1, p2), data, files.keys(), getfilectx, user, (date, tz), extra) @@ -596,12 +660,25 @@ def do_export(parser): def main(args): global prefix, dirname, branches, bmarks global marks, blob_marks, parsed_refs -global peer +global peer, mode alias = args[1] url = args[2] peer = None +cmd = ['git', 'config', '--get', 'remote-hg.hg-git-compat'] +hg_git_compat = False +try: +if subprocess.check_output(cmd) == 'true\n': +hg_git_compat = True +except subprocess.CalledProcessError: +pass + +if hg_git_compat: +mode = 'hg' +else: +mode = 'git' + if alias[4:] == url: is_tmp = True alias = util.sha1(alias).hex
[PATCH v6 05/16] remote-hg: make sure the encoding is correct
Independently of the environment. Signed-off-by: Felipe Contreras --- contrib/remote-helpers/git-remote-hg | 12 +++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg index a5023c9..503a9fc 100755 --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@ -9,7 +9,7 @@ # Then you can clone with: # git clone hg::/path/to/mercurial/repo/ -from mercurial import hg, ui, bookmarks, context, util +from mercurial import hg, ui, bookmarks, context, util, encoding import re import sys @@ -391,6 +391,9 @@ def do_import(parser): print "feature export-marks=%s" % path sys.stdout.flush() +tmp = encoding.encoding +encoding.encoding = 'utf-8' + # lets get all the import lines while parser.check('import'): ref = parser[1] @@ -409,6 +412,8 @@ def do_import(parser): parser.next() +encoding.encoding = tmp + print 'done' def parse_blob(parser): @@ -491,8 +496,13 @@ def parse_commit(parser): files.keys(), getfilectx, user, (date, tz), extra) +tmp = encoding.encoding +encoding.encoding = 'utf-8' + node = repo.commitctx(ctx) +encoding.encoding = tmp + rev = repo[node].rev() parsed_refs[ref] = node -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v6 04/16] remote-hg: add support to push URLs
Signed-off-by: Felipe Contreras --- contrib/remote-helpers/git-remote-hg | 14 -- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg index 45629e0..a5023c9 100755 --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@ -9,12 +9,13 @@ # Then you can clone with: # git clone hg::/path/to/mercurial/repo/ -from mercurial import hg, ui, bookmarks, context +from mercurial import hg, ui, bookmarks, context, util import re import sys import os import json +import shutil NAME_RE = re.compile('^([^<>]+)') AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]+)>$') @@ -574,6 +575,12 @@ def main(args): url = args[2] peer = None +if alias[4:] == url: +is_tmp = True +alias = util.sha1(alias).hexdigest() +else: +is_tmp = False + gitdir = os.environ['GIT_DIR'] dirname = os.path.join(gitdir, 'hg', alias) branches = {} @@ -604,6 +611,9 @@ def main(args): die('unhandled command: %s' % line) sys.stdout.flush() -marks.store() +if not is_tmp: +marks.store() +else: +shutil.rmtree(dirname) sys.exit(main(sys.argv)) -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v6 03/16] remote-hg: add support for remote pushing
Signed-off-by: Felipe Contreras --- contrib/remote-helpers/git-remote-hg | 9 +++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg index fcceede..45629e0 100755 --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@ -197,7 +197,7 @@ def fixup_user(user): return '%s <%s>' % (name, mail) def get_repo(url, alias): -global dirname +global dirname, peer myui = ui.ui() myui.setconfig('ui', 'interactive', 'off') @@ -526,7 +526,7 @@ def parse_tag(parser): # nothing to do def do_export(parser): -global parsed_refs, bmarks +global parsed_refs, bmarks, peer parser.next() @@ -562,12 +562,17 @@ def do_export(parser): print +if peer: +parser.repo.push(peer, force=False) + def main(args): global prefix, dirname, branches, bmarks global marks, blob_marks, parsed_refs +global peer alias = args[1] url = args[2] +peer = None gitdir = os.environ['GIT_DIR'] dirname = os.path.join(gitdir, 'hg', alias) -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v6 02/16] remote-hg: add support for pushing
Signed-off-by: Felipe Contreras --- contrib/remote-helpers/git-remote-hg | 217 ++- 1 file changed, 215 insertions(+), 2 deletions(-) diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg index e37e278..fcceede 100755 --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@ -9,7 +9,7 @@ # Then you can clone with: # git clone hg::/path/to/mercurial/repo/ -from mercurial import hg, ui, bookmarks +from mercurial import hg, ui, bookmarks, context import re import sys @@ -18,6 +18,7 @@ import json NAME_RE = re.compile('^([^<>]+)') AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]+)>$') +RAW_AUTHOR_RE = re.compile('^(\w+) (?:(.+)? )?<(.+)> (\d+) ([+-]\d+)') def die(msg, *args): sys.stderr.write('ERROR: %s\n' % (msg % args)) @@ -32,12 +33,17 @@ def gitmode(flags): def gittz(tz): return '%+03d%02d' % (-tz / 3600, -tz % 3600 / 60) +def hgmode(mode): +m = { '0100755': 'x', '012': 'l' } +return m.get(mode, '') + class Marks: def __init__(self, path): self.path = path self.tips = {} self.marks = {} +self.rev_marks = {} self.last_mark = 0 self.load() @@ -52,6 +58,9 @@ class Marks: self.marks = tmp['marks'] self.last_mark = tmp['last-mark'] +for rev, mark in self.marks.iteritems(): +self.rev_marks[mark] = int(rev) + def dict(self): return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark } @@ -64,11 +73,19 @@ class Marks: def from_rev(self, rev): return self.marks[str(rev)] +def to_rev(self, mark): +return self.rev_marks[mark] + def get_mark(self, rev): self.last_mark += 1 self.marks[str(rev)] = self.last_mark return self.last_mark +def new_mark(self, rev, mark): +self.marks[str(rev)] = mark +self.rev_marks[mark] = rev +self.last_mark = mark + def is_marked(self, rev): return self.marks.has_key(str(rev)) @@ -106,6 +123,35 @@ class Parser: if self.line == 'done': self.line = None +def get_mark(self): +i = self.line.index(':') + 1 +return int(self.line[i:]) + +def get_data(self): +if not self.check('data'): +return None +i = self.line.index(' ') + 1 +size = int(self.line[i:]) +return sys.stdin.read(size) + +def get_author(self): +m = RAW_AUTHOR_RE.match(self.line) +if not m: +return None +_, name, email, date, tz = m.groups() + +if email != 'unknown': +if name: +user = '%s <%s>' % (name, email) +else: +user = '<%s>' % (email) +else: +user = name + +tz = int(tz) +tz = ((tz / 100) * 3600) + ((tz % 100) * 60) +return (user, int(date), -tz) + def export_file(fc): d = fc.data() print "M %s inline %s" % (gitmode(fc.flags()), fc.path()) @@ -174,6 +220,10 @@ def rev_to_mark(rev): global marks return marks.from_rev(rev) +def mark_to_rev(mark): +global marks +return marks.to_rev(mark) + def export_ref(repo, name, kind, head): global prefix, marks @@ -263,9 +313,17 @@ def do_capabilities(parser): global prefix, dirname print "import" +print "export" print "refspec refs/heads/branches/*:%s/branches/*" % prefix print "refspec refs/heads/*:%s/bookmarks/*" % prefix print "refspec refs/tags/*:%s/tags/*" % prefix + +path = os.path.join(dirname, 'marks-git') + +if os.path.exists(path): +print "*import-marks %s" % path +print "*export-marks %s" % path + print def get_branch_tip(repo, branch): @@ -352,8 +410,161 @@ def do_import(parser): print 'done' +def parse_blob(parser): +global blob_marks + +parser.next() +mark = parser.get_mark() +parser.next() +data = parser.get_data() +blob_marks[mark] = data +parser.next() +return + +def parse_commit(parser): +global marks, blob_marks, bmarks, parsed_refs + +from_mark = merge_mark = None + +ref = parser[1] +parser.next() + +commit_mark = parser.get_mark() +parser.next() +author = parser.get_author() +parser.next() +committer = parser.get_author() +parser.next() +data = parser.get_data() +parser.next() +if parser.check('from'): +from_mark = parser.get_mark() +parser.next() +if parser.check('merge'): +merge_mark = parser.get_mark() +parser.next() +if parser.check('merge'): +die('octopus merges are not supported yet') + +files = {} + +for line in parser: +if parser.check('M'): +t, m, mark_ref, path = line.split(' ') +mark = int(mark_ref[1:]) +f = { 'mode' : hgmode(m), 'data' : blob_marks[mark] }
[PATCH v6 00/16] New remote-hg helper:w
Hi, Only a few updates, and has been moved to contrib/remote-helpers (git-remote-bzr is on the way). This remote-hg has advantages other tools don't have: * Uses transport-helper (git clone hg::path) * The code is small * The code is simple * No external dependencies (other than mercurial) * It's easy to install (put into your path) * Has extensive tests (for real) * Active development * Has compatibility with hg-git * The required patches are available * No changes necesary to git core * Support for bookmarks * Support for tags One important alternative is the one written by Sverre Rabbelier that is now maintained and distributed in msysgit. A list of issues with that approach (not exhaustive): * Doesn't work on newer versions of mercurial * There are multiple versions, each with different issues * Don't pass a single of this remote-hg's tests To use it add it to your $PATH (e.g. ~/bin). % git clone hd:///full/path/or/url/to/hg/repo To run the tests: % make -C contrib/remote-helpers test The only caveat is that you need 'python' in your $PATH. Changes since v5: * Move to contrib/remote-helpers * Reorganize tests * Fix update of bookmarks * Fix for older versions of python * Performance improvements * Improve default branch/bookmark handling * Add fixes test-lib * Cleanups Changes since v4: Felipe Contreras (16): Add new remote-hg transport helper remote-hg: add support for pushing remote-hg: add support for remote pushing remote-hg: add support to push URLs remote-hg: make sure the encoding is correct remote-hg: match hg merge behavior remote-hg: add support for hg-git compat mode remote-hg: add compat for hg-git author fixes remote-hg: fake bookmark when there's none remote-hg: add basic tests test-lib: avoid full path to store test results remote-hg: add bidirectional tests remote-hg: add tests to compare with hg-git remote-hg: add extra author test remote-hg: add option to not track branches remote-hg: the author email can be null contrib/remote-helpers/Makefile | 13 + contrib/remote-helpers/git-remote-hg | 785 +++ contrib/remote-helpers/test-hg-bidi.sh | 243 ++ contrib/remote-helpers/test-hg-hg-git.sh | 466 ++ contrib/remote-helpers/test-hg.sh| 112 + t/test-lib.sh| 3 +- 6 files changed, 1621 insertions(+), 1 deletion(-) create mode 100644 contrib/remote-helpers/Makefile create mode 100755 contrib/remote-helpers/git-remote-hg create mode 100755 contrib/remote-helpers/test-hg-bidi.sh create mode 100755 contrib/remote-helpers/test-hg-hg-git.sh create mode 100755 contrib/remote-helpers/test-hg.sh -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v6 01/16] Add new remote-hg transport helper
Signed-off-by: Felipe Contreras --- contrib/remote-helpers/git-remote-hg | 391 +++ 1 file changed, 391 insertions(+) create mode 100755 contrib/remote-helpers/git-remote-hg diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg new file mode 100755 index 000..e37e278 --- /dev/null +++ b/contrib/remote-helpers/git-remote-hg @@ -0,0 +1,391 @@ +#!/usr/bin/env python +# +# Copyright (c) 2012 Felipe Contreras +# + +# Inspired by Rocco Rutte's hg-fast-export + +# Just copy to your ~/bin, or anywhere in your $PATH. +# Then you can clone with: +# git clone hg::/path/to/mercurial/repo/ + +from mercurial import hg, ui, bookmarks + +import re +import sys +import os +import json + +NAME_RE = re.compile('^([^<>]+)') +AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]+)>$') + +def die(msg, *args): +sys.stderr.write('ERROR: %s\n' % (msg % args)) +sys.exit(1) + +def warn(msg, *args): +sys.stderr.write('WARNING: %s\n' % (msg % args)) + +def gitmode(flags): +return 'l' in flags and '12' or 'x' in flags and '100755' or '100644' + +def gittz(tz): +return '%+03d%02d' % (-tz / 3600, -tz % 3600 / 60) + +class Marks: + +def __init__(self, path): +self.path = path +self.tips = {} +self.marks = {} +self.last_mark = 0 + +self.load() + +def load(self): +if not os.path.exists(self.path): +return + +tmp = json.load(open(self.path)) + +self.tips = tmp['tips'] +self.marks = tmp['marks'] +self.last_mark = tmp['last-mark'] + +def dict(self): +return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark } + +def store(self): +json.dump(self.dict(), open(self.path, 'w')) + +def __str__(self): +return str(self.dict()) + +def from_rev(self, rev): +return self.marks[str(rev)] + +def get_mark(self, rev): +self.last_mark += 1 +self.marks[str(rev)] = self.last_mark +return self.last_mark + +def is_marked(self, rev): +return self.marks.has_key(str(rev)) + +def get_tip(self, branch): +return self.tips.get(branch, 0) + +def set_tip(self, branch, tip): +self.tips[branch] = tip + +class Parser: + +def __init__(self, repo): +self.repo = repo +self.line = self.get_line() + +def get_line(self): +return sys.stdin.readline().strip() + +def __getitem__(self, i): +return self.line.split()[i] + +def check(self, word): +return self.line.startswith(word) + +def each_block(self, separator): +while self.line != separator: +yield self.line +self.line = self.get_line() + +def __iter__(self): +return self.each_block('') + +def next(self): +self.line = self.get_line() +if self.line == 'done': +self.line = None + +def export_file(fc): +d = fc.data() +print "M %s inline %s" % (gitmode(fc.flags()), fc.path()) +print "data %d" % len(d) +print d + +def get_filechanges(repo, ctx, parent): +modified = set() +added = set() +removed = set() + +cur = ctx.manifest() +prev = repo[parent].manifest().copy() + +for fn in cur: +if fn in prev: +if (cur.flags(fn) != prev.flags(fn) or cur[fn] != prev[fn]): +modified.add(fn) +del prev[fn] +else: +added.add(fn) +removed |= set(prev.keys()) + +return added | modified, removed + +def fixup_user(user): +user = user.replace('"', '') +name = mail = None +m = AUTHOR_RE.match(user) +if m: +name = m.group(1) +mail = m.group(2).strip() +else: +m = NAME_RE.match(user) +if m: +name = m.group(1).strip() + +if not name: +name = 'Unknown' +if not mail: +mail = 'unknown' + +return '%s <%s>' % (name, mail) + +def get_repo(url, alias): +global dirname + +myui = ui.ui() +myui.setconfig('ui', 'interactive', 'off') + +if hg.islocal(url): +repo = hg.repository(myui, url) +else: +local_path = os.path.join(dirname, 'clone') +if not os.path.exists(local_path): +peer, dstpeer = hg.clone(myui, {}, url, local_path, update=False, pull=True) +repo = dstpeer.local() +else: +repo = hg.repository(myui, local_path) +peer = hg.peer(myui, {}, url) +repo.pull(peer, heads=None, force=True) + +return repo + +def rev_to_mark(rev): +global marks +return marks.from_rev(rev) + +def export_ref(repo, name, kind, head): +global prefix, marks + +ename = '%s/%s' % (kind, name) +tip = marks.get_tip(ename) + +# mercurial takes too much time checking this +if tip and tip == head.rev(): +# nothing to do +return +revs = repo.revs('%u:%u' % (tip, head)) +count = 0 + +
Re: git-p4 clone @all error
merlin...@gmail.com wrote on Wed, 31 Oct 2012 15:01 +0100: > On Tue, Oct 30, 2012 at 11:44 AM, Arthur wrote: > > The problem : > > > > Importing revision 7727 (100%)Traceback (most recent call last): > > File "/usr/bin/git-p4", line 3183, in > > main() > > File "/usr/bin/git-p4", line 3177, in main > > if not cmd.run(args): > > File "/usr/bin/git-p4", line 3048, in run > > if not P4Sync.run(self, depotPaths): > > File "/usr/bin/git-p4", line 2911, in run > > self.importChanges(changes) > > File "/usr/bin/git-p4", line 2618, in importChanges > > self.initialParent) > > File "/usr/bin/git-p4", line 2198, in commit > > epoch = details["time"] > > KeyError: 'time' > > Are you permanently converting a project, or are you planning to > continue submitting to perforce with git-p4? > > I have seen similar bugs myself when using the --detect-branches > option. I hope --detect-branches is unrelated to any problems related to p4 describe, because it is used identically both with and without the option. But you never know. > The branch detection in git-p4 is flaky anyway: it is limited > what it can handle, and it used to require correct perforce branch > specs at least, so I would recommend not using it unless you know what > it is doing under the hood. It relies on heuristics because the git idea of branches doesn't have a direct analog in p4, even though p4 users will organize their files and directories in ways that suggest branches. The way it works is described in the BRANCH DETECTION part of the git-p4.1 man page. The man page also explains how you can use a config variable to explicitly define the branch relationships. This might help: git-p4.branchList:: List of branches to be imported when branch detection is enabled. Each entry should be a pair of branch names separated by a colon (:). This example declares that both branchA and branchB were created from main: git config git-p4.branchList main:branchA git config --add git-p4.branchList main:branchB It still only works for branches at the same "level" of the depot path. > Instead I would just clone a single branch at a time (drop the > --detect-branches) and work on that. > > I do this even in the rare cases when I need more than one perforce > branch in the same git repo - there are other ways to achieve the same > thing. Yep, that should alway works. -- Pete -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] git p4: catch p4 describe errors
On Nov 3, 2012, at 16:07 , Pete Wyckoff wrote: > > Arthur and Matt, you've both had intermittent "p4 describe" > errors. I've not been able to repeat this or come up with > a possible root cause. But it is clear that the error handling > in this area is weak. I tried this week to find the commit that caused the problem, but there were too many to sift through so I haven't managed to see it again either-- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH] git p4: catch p4 describe errors
Group the two calls to "p4 describe" into a new helper function, and try to validate the p4 results. The current behavior when p4 describe fails is to die with a python backtrace. The new behavior will print the full response. Based-on-patch-by: Matt Arsenault Signed-off-by: Pete Wyckoff --- Arthur and Matt, you've both had intermittent "p4 describe" errors. I've not been able to repeat this or come up with a possible root cause. But it is clear that the error handling in this area is weak. Can you continue using git-p4 with this patch applied? If it fails again, at least we'll get some interesting output. This is appropriate for upstream too, since it should do no harm and might flush out a bug at some point. It includes "-s" on both p4 describe calls now as Matt suggested. -- Pete git-p4.py | 35 ++- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/git-p4.py b/git-p4.py index 882b1bb..e51a081 100755 --- a/git-p4.py +++ b/git-p4.py @@ -169,6 +169,29 @@ def p4_reopen(type, f): def p4_move(src, dest): p4_system(["move", "-k", wildcard_encode(src), wildcard_encode(dest)]) +def p4_describe(change): +"""Make sure it returns a valid result by checking for + the presence of field "time". Return a dict of the + results.""" + +ds = p4CmdList(["describe", "-s", str(change)]) +if len(ds) != 1: +die("p4 describe -s %d did not return 1 result: %s" % (change, str(ds))) + +d = ds[0] + +if "p4ExitCode" in d: +die("p4 describe -s %d exited with %d: %s" % (change, d["p4ExitCode"], + str(d))) +if "code" in d: +if d["code"] == "error": +die("p4 describe -s %d returned error code: %s" % (change, str(d))) + +if "time" not in d: +die("p4 describe -s %d returned no \"time\": %s" % (change, str(d))) + +return d + # # Canonicalize the p4 type and return a tuple of the # base type, plus any modifiers. See "p4 help filetypes" @@ -2543,7 +2566,7 @@ class P4Sync(Command, P4UserMap): def importChanges(self, changes): cnt = 1 for change in changes: -description = p4Cmd(["describe", str(change)]) +description = p4_describe(change) self.updateOptionDict(description) if not self.silent: @@ -2667,14 +2690,8 @@ class P4Sync(Command, P4UserMap): # Use time from top-most change so that all git p4 clones of # the same p4 repo have the same commit SHA1s. -res = p4CmdList("describe -s %d" % newestRevision) -newestTime = None -for r in res: -if r.has_key('time'): -newestTime = int(r['time']) -if newestTime is None: -die("\"describe -s\" on newest change %d did not give a time") -details["time"] = newestTime +res = p4_describe(newestRevision) +details["time"] = res["time"] self.updateOptionDict(details) try: -- 1.7.12.1.457.g468b3ef -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] Enable parallelism in git submodule update.
Am 03.11.2012 19:44, schrieb Phil Hord: > On Sat, Nov 3, 2012 at 11:42 AM, Jens Lehmann wrote: >> Am 30.10.2012 19:11, schrieb Stefan Zager: >>> This is a refresh of a conversation from a couple of months ago. >>> >>> I didn't try to implement all the desired features (e.g., smart logic >>> for passing a -j parameter to recursive submodule invocations), but I >>> did address the one issue that Junio insisted on: the code makes a >>> best effort to detect whether xargs supports parallel execution on the >>> host platform, and if it doesn't, then it prints a warning and falls >>> back to serial execution. >> >> I suspect not passing on --jobs recursively like you do here is the >> right thing to do, as that would give exponential growth of jobs with >> recursion depth, which makes no sense to me. > > On the other hand, since $jobs is still defined when the recursive > call to is made to 'eval cmd_update "$orig_flags"', I suspect the > value *is* passed down recursively. But for $jobs != 1 Stefan's code doesn't use eval cmd_update but starts the submodule script again: + xargs $max_lines -P "$jobs" git submodule update $orig_flags That should get rid of the $jobs setting, or am I missing something? > Maybe $jobs should be manually > reset before recursing -- unless it is "0" -- though I expect someone > would feel differently if she had one submodule on level 1 and 10 > submodules on level 2. She would be surprised, then, when --jobs=10 > seemed to have little affect on performance. Hmm, good point. However we implement that, it should at least be properly documented in the man page (and in the use case you describe a "git submodule foreach 'git submodule update -j 10'" could be the solution if we choose to not propagate the jobs option). > So maybe it is best to > leave it as it is, excepting that the apparent attempt not to pass the > switch down is probably misleading. I didn't test it, but I think it should work (famous last words ;-). -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] Enable parallelism in git submodule update.
Am 29.07.2012 17:37, schrieb Jens Lehmann: > Am 27.07.2012 20:37, schrieb Stefan Zager: >> The --jobs parameter may be used to set the degree of per-submodule >> parallel execution. > > I think this is a sound idea, but it would be good to see some > actual measurements. What are the performance numbers with and > without this change? Which cases do benefit and are there some > which run slower when run in parallel? ping? -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: Wrap commit messages on `git commit -m`
On Sat, Nov 3, 2012 at 3:41 AM, David Aguilar wrote: > On Fri, Nov 2, 2012 at 11:38 PM, Jonathan Nieder wrote: >> Ramkumar Ramachandra wrote: >>> Jonathan Nieder wrote: >> Ram, what platform do your colleagues use? >>> >>> Red Hat Enterprise Linux 5. >> >> Oh, ok. In that case I blame habit. >> >> I think the best option you have is to just complain to your >> colleagues about the long lines. Then they would get a chance to >> explore what the UI currently offers and to complain to this list >> (perhaps via you :)) about missing features that would make their work >> easier. >> >> To put it another way: I don't see yet how a hypothetical "git commit >> --wrap-lines -m 'long message'" would make life easier than just >> running "git commit" and entering the message using $EDITOR. There's >> probably a UI or documentation bug lurking somewhere, so thanks for >> thinking about these things. >> >> Regards, >> Jonathan > > If your colleagues do not mind using a GUI then git-cola > might help them form better commit messages. > > It auto-wraps long lines and enforces a very clear distinction > between the one-line summary and extended description. vim also does this nicely with syntax highlighting turned on, though I do not know how this feature is triggered. Specifically, it wraps all lines at 72 characters; it colors line 1 yellow out to 50 characters and white after that; and it colors any text on line 2 with a red background (should be empty). Phil -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] Enable parallelism in git submodule update.
On Sat, Nov 3, 2012 at 11:42 AM, Jens Lehmann wrote: > Am 30.10.2012 19:11, schrieb Stefan Zager: >> This is a refresh of a conversation from a couple of months ago. >> >> I didn't try to implement all the desired features (e.g., smart logic >> for passing a -j parameter to recursive submodule invocations), but I >> did address the one issue that Junio insisted on: the code makes a >> best effort to detect whether xargs supports parallel execution on the >> host platform, and if it doesn't, then it prints a warning and falls >> back to serial execution. > > I suspect not passing on --jobs recursively like you do here is the > right thing to do, as that would give exponential growth of jobs with > recursion depth, which makes no sense to me. On the other hand, since $jobs is still defined when the recursive call to is made to 'eval cmd_update "$orig_flags"', I suspect the value *is* passed down recursively. Maybe $jobs should be manually reset before recursing -- unless it is "0" -- though I expect someone would feel differently if she had one submodule on level 1 and 10 submodules on level 2. She would be surprised, then, when --jobs=10 seemed to have little affect on performance. So maybe it is best to leave it as it is, excepting that the apparent attempt not to pass the switch down is probably misleading. Phil -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] Enable parallelism in git submodule update.
Am 30.10.2012 19:11, schrieb Stefan Zager: > This is a refresh of a conversation from a couple of months ago. > > I didn't try to implement all the desired features (e.g., smart logic > for passing a -j parameter to recursive submodule invocations), but I > did address the one issue that Junio insisted on: the code makes a > best effort to detect whether xargs supports parallel execution on the > host platform, and if it doesn't, then it prints a warning and falls > back to serial execution. I suspect not passing on --jobs recursively like you do here is the right thing to do, as that would give exponential growth of jobs with recursion depth, which makes no sense to me. A still unsolved issue is the unstructured output from the different update jobs. It'll be hard (if not impossible) to see in what submodule which update took place (or failed). I think we should have a solution for that too (maybe one of those Heiko mentioned or something as simple as implying "-q"?). > Stefan > > On Tue, Oct 30, 2012 at 11:03 AM, wrote: >> The --jobs parameter may be used to set the degree of per-submodule >> parallel execution. > >> Signed-off-by: Stefan Zager >> --- >> Documentation/git-submodule.txt |8 ++- >> git-submodule.sh| 40 >> ++- >> 2 files changed, 46 insertions(+), 2 deletions(-) >> >> diff --git a/Documentation/git-submodule.txt >> b/Documentation/git-submodule.txt >> index b4683bb..cb23ba7 100644 >> --- a/Documentation/git-submodule.txt >> +++ b/Documentation/git-submodule.txt >> @@ -14,7 +14,8 @@ SYNOPSIS >> 'git submodule' [--quiet] status [--cached] [--recursive] [--] [...] >> 'git submodule' [--quiet] init [--] [...] >> 'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase] >> - [--reference ] [--merge] [--recursive] [--] >> [...] >> + [--reference ] [--merge] [--recursive] >> + [-j|--jobs [jobs]] [--] [...] >> 'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) >> ] >> [commit] [--] [...] >> 'git submodule' [--quiet] foreach [--recursive] >> @@ -146,6 +147,11 @@ If the submodule is not yet initialized, and you just >> want to use the >> setting as stored in .gitmodules, you can automatically initialize the >> submodule with the `--init` option. >> + >> +By default, each submodule is treated serially. You may specify a degree of >> +parallel execution with the --jobs flag. If a parameter is provided, it is >> +the maximum number of jobs to run in parallel; without a parameter, all >> jobs are >> +run in parallel. >> ++ The new "--jobs" option should be documented under "OPTIONS", (and maybe include that "--jobs 0" does the same as "--jobs" alone and that this is not supported on all platforms). >> If `--recursive` is specified, this command will recurse into the >> registered submodules, and update any nested submodules within. >> + >> diff --git a/git-submodule.sh b/git-submodule.sh >> index ab6b110..60a5f96 100755 >> --- a/git-submodule.sh >> +++ b/git-submodule.sh >> @@ -8,7 +8,7 @@ dashless=$(basename "$0" | sed -e 's/-/ /') >> USAGE="[--quiet] add [-b branch] [-f|--force] [--reference ] >> [--] [] >> or: $dashless [--quiet] status [--cached] [--recursive] [--] [...] >> or: $dashless [--quiet] init [--] [...] >> - or: $dashless [--quiet] update [--init] [-N|--no-fetch] [-f|--force] >> [--rebase] [--reference ] [--merge] [--recursive] [--] >> [...] >> + or: $dashless [--quiet] update [--init] [-N|--no-fetch] [-f|--force] >> [--rebase] [--reference ] [--merge] [--recursive] [-j|--jobs >> [jobs]] [--] [...] >> or: $dashless [--quiet] summary [--cached|--files] [--summary-limit ] >> [commit] [--] [...] >> or: $dashless [--quiet] foreach [--recursive] >> or: $dashless [--quiet] sync [--] [...]" >> @@ -500,6 +500,7 @@ cmd_update() >> { >> # parse $args after "submodule ... update". >> orig_flags= >> + jobs="1" >> while test $# -ne 0 >> do >> case "$1" in >> @@ -518,6 +519,20 @@ cmd_update() >> -r|--rebase) >> update="rebase" >> ;; >> + -j|--jobs) >> + case "$2" in >> + ''|-*) >> + jobs="0" >> + ;; >> + *) >> + jobs="$2" >> + shift >> + ;; >> + esac >> + # Don't preserve this arg. >> + shift >> + continue >> + ;; >> --reference) >> case "$2" in '') usage ;; esac >> reference="--reference=$2" >> @@ -551,11 +566,34 @@ cmd_update() >> shift >> done >> >> +
Re: git-p4 clone @all error
a.fou...@amesys.fr wrote on Tue, 30 Oct 2012 03:44 -0700: > So, i want import my perforce projet on my server git. > > perforce my project tree : > > depot > dev_data > mainline > release_1.0 > release_1.0.0 > > my command is : > > git-p4 clone -v --detect-branches //depot@all /home/user/projets/deport > > The problem : > > Importing revision 7727 (100%)Traceback (most recent call last): > File "/usr/bin/git-p4", line 3183, in > main() > File "/usr/bin/git-p4", line 3177, in main > if not cmd.run(args): > File "/usr/bin/git-p4", line 3048, in run > if not P4Sync.run(self, depotPaths): > File "/usr/bin/git-p4", line 2911, in run > self.importChanges(changes) > File "/usr/bin/git-p4", line 2618, in importChanges > self.initialParent) > File "/usr/bin/git-p4", line 2198, in commit > epoch = details["time"] > KeyError: 'time' This isn't the --detect-branches code path, as far as I can tell. Line 2618, importChanges, is the not-detect case. But either way, I suspect that p4 returns results we weren't expecting, and this is just the symptom of that problem. > if i make a p4 sync //depot/...#head on my perforce server i've this error : > Librarian checkout > depot/mainline/xxx/api429decryption.txt failed. > open for read: depot/mainline/xx/api429decryption.txt,v: Le > fichier spcifi est introuvable. > > My p4 clone can't checking out files after importing revision.. I've seen this before at our site. If you search for "librarian checout" you'll see lots of messages suggesting there's some sort of problem with the p4 server. I'm going to work on a patch to git-p4 to make it print a more useful diagnostic in the case of your error above. Can you make this problem occur reliably? -- Pete -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: Wrap commit messages on `git commit -m`
On Fri, Nov 2, 2012 at 11:38 PM, Jonathan Nieder wrote: > Ramkumar Ramachandra wrote: >> Jonathan Nieder wrote: > >>> Ram, what platform do your colleagues use? >> >> Red Hat Enterprise Linux 5. > > Oh, ok. In that case I blame habit. > > I think the best option you have is to just complain to your > colleagues about the long lines. Then they would get a chance to > explore what the UI currently offers and to complain to this list > (perhaps via you :)) about missing features that would make their work > easier. > > To put it another way: I don't see yet how a hypothetical "git commit > --wrap-lines -m 'long message'" would make life easier than just > running "git commit" and entering the message using $EDITOR. There's > probably a UI or documentation bug lurking somewhere, so thanks for > thinking about these things. > > Regards, > Jonathan If your colleagues do not mind using a GUI then git-cola might help them form better commit messages. It auto-wraps long lines and enforces a very clear distinction between the one-line summary and extended description. -- David -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html