[oe-commits] org.oe.dev contrib/mtn2git: mtn add is not recursive by default, actually add files ; )

freyther commit openembedded-commits at lists.openembedded.org
Tue Oct 9 15:41:56 UTC 2007


contrib/mtn2git: mtn add is not recursive by default, actually add files ;)

Author: freyther at openembedded.org
Branch: org.openembedded.dev
Revision: 57d4969164263d2e6a79acef3ffe321fe7ac12ad
ViewMTN: http://monotone.openembedded.org/revision/info/57d4969164263d2e6a79acef3ffe321fe7ac12ad
Files:
1
contrib/mtn2git/mtn
contrib/mtn2git/git2mtn.py
contrib/mtn2git/mtn/__init__.py
contrib/mtn2git/mtn/authors.py
contrib/mtn2git/mtn/common.py
contrib/mtn2git/mtn/genproxy.py
contrib/mtn2git/mtn/mtn.py
contrib/mtn2git/mtn/utility.py
contrib/mtn2git/mtn2git.py
contrib/mtn2git/status.py
mtn:execute
true
Diffs:

#
# mt diff -r3ff08e5338d5b8bc5c0a7b72b1ba6b8ad32f3498 -r57d4969164263d2e6a79acef3ffe321fe7ac12ad
#
# 
# 
# add_dir "contrib/mtn2git/mtn"
# 
# add_file "contrib/mtn2git/git2mtn.py"
#  content [052dc79ba2fb8ef0ea324e55549ae13233d144f4]
# 
# add_file "contrib/mtn2git/mtn/__init__.py"
#  content [512093425c7be8fc6da16e10a7cffcda07e45254]
# 
# add_file "contrib/mtn2git/mtn/authors.py"
#  content [0ed0661cf3ddf1b402a73dfc1ff0fccc64ad05f1]
# 
# add_file "contrib/mtn2git/mtn/common.py"
#  content [a071eb4d76e1a83824dacd8188199e00ad87fff1]
# 
# add_file "contrib/mtn2git/mtn/genproxy.py"
#  content [408f46a3f5fe0d792eb62e92a8faaf5c28c67a54]
# 
# add_file "contrib/mtn2git/mtn/mtn.py"
#  content [b7992918c918fb72a18396be361fc5eba1784e57]
# 
# add_file "contrib/mtn2git/mtn/utility.py"
#  content [78cd2c53af05e63bb76c097dc832b6f75394e40d]
# 
# add_file "contrib/mtn2git/mtn2git.py"
#  content [f336e19760df662e35b161bf5791041c5a6e1824]
# 
# add_file "contrib/mtn2git/status.py"
#  content [d3682877d9debb3e8439b6a0558bb11e6e2262b0]
# 
#   set "contrib/mtn2git/git2mtn.py"
#  attr "mtn:execute"
# value "true"
# 
#   set "contrib/mtn2git/mtn/genproxy.py"
#  attr "mtn:execute"
# value "true"
# 
#   set "contrib/mtn2git/mtn2git.py"
#  attr "mtn:execute"
# value "true"
# 
============================================================
--- contrib/mtn2git/git2mtn.py	052dc79ba2fb8ef0ea324e55549ae13233d144f4
+++ contrib/mtn2git/git2mtn.py	052dc79ba2fb8ef0ea324e55549ae13233d144f4
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+
+"""
+  Copyright (C) 2006, 2007 Holger Hans Peter Freyther
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+"""
+
+#############
+#
+# Use: This tool can merge one git-branch back to one branch in monotone
+#
+# Discussion:
+#   Merging from git to a monotone branch. Currently I see two modes which
+#   should be supported.
+#
+#    a) linear development. Only a couple of changes are done on top of the
+#       branch and nothing get merged. In this case we can merge everything
+#       back and each rev gets a cert with the branch.
+#       This should be possible using programs like git-rebase.
+#    b) we have merges inside our git-rev-list history. This means we need to
+#       merge every revision and can't attach any branch certs to the revision.
+#       And once we are done with this we will create a propagate like commit
+#       entry and we can give that new revision a cert with the branch name.
+#
+#       This means working in git is treated like a branch!
+#
+#       One difficulty is with git. This propagate like commit will create a new revision
+#       in monotone but none in git as both trees/manifests are the same. So what we have
+#       to make sure is to use the latest mtn revision for a given mark/git revision. This
+#       is where mtn2git.py needs to help. We will save a list of mtn revisions that have the
+#       same git version and then will read every of them and check the branch certs and will
+#       use the one matching our target branch!
+#############
============================================================
--- contrib/mtn2git/mtn/__init__.py	512093425c7be8fc6da16e10a7cffcda07e45254
+++ contrib/mtn2git/mtn/__init__.py	512093425c7be8fc6da16e10a7cffcda07e45254
@@ -0,0 +1,6 @@
+from mtn import Automate, Operations
+
+__all__ = [
+    "Automate",
+    "Operations"
+    ]
============================================================
--- contrib/mtn2git/mtn/authors.py	0ed0661cf3ddf1b402a73dfc1ff0fccc64ad05f1
+++ contrib/mtn2git/mtn/authors.py	0ed0661cf3ddf1b402a73dfc1ff0fccc64ad05f1
@@ -0,0 +1,11 @@
+authors='''Authors:
+Grahame Bowland <grahame at angrygoats.net>
+
+Contributors:
+Matt Johnston <matt at ucc.asn.au>
+Nathaniel Smith <njs at pobox.com>
+Bruce Stephens <monotone at cenderis.demon.co.uk>
+Lapo Luchini <lapo at lapo.it>
+David Reiss <davidn at gmail.com>
+
+'''
============================================================
--- contrib/mtn2git/mtn/common.py	a071eb4d76e1a83824dacd8188199e00ad87fff1
+++ contrib/mtn2git/mtn/common.py	a071eb4d76e1a83824dacd8188199e00ad87fff1
@@ -0,0 +1,49 @@
+
+import datetime
+import time
+import fcntl
+import os
+import signal
+import traceback
+import sys
+
+def parse_timecert(value):
+    return apply(datetime.datetime, time.strptime(value, "%Y-%m-%dT%H:%M:%S")[:6])
+
+def set_nonblocking(fd):
+	fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+	fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NDELAY)
+
+def terminate_popen3(process):
+        print >> sys.stderr, ("[%s] stopping process: %s" % (os.getpid(), process.pid))
+        try:
+            process.tochild.close()
+            process.fromchild.close()
+            process.childerr.close()
+            if process.poll() == -1:
+                # the process is still running, so kill it.
+                os.kill(process.pid, signal.SIGKILL)
+            process.wait()
+        except:
+            print >> sys.stderr, ("%s failed_to_stop %s (%s)" % (os.getpid(), process.pid, traceback.format_exc()))
+
+def ago(event):
+    def plural(v, singular, plural):
+        if v == 1:
+            return "%d %s" % (v, singular)
+        else:
+            return "%d %s" % (v, plural)
+    now = datetime.datetime.utcnow()
+    ago = now - event
+    if ago.days > 0:
+        rv = "%s" % (plural(ago.days, "day", "days"))
+    elif ago.seconds > 3600:
+        hours = ago.seconds / 3600
+        minutes = (ago.seconds - (hours * 3600)) / 60
+        rv = "%s" % (plural(hours, "hour", "hours"))
+    else:
+        minutes = ago.seconds / 60
+        seconds = (ago.seconds - (minutes * 60))
+        rv = "%s" % (plural(minutes, "minute", "minutes"))
+    return rv
+
============================================================
--- contrib/mtn2git/mtn/genproxy.py	408f46a3f5fe0d792eb62e92a8faaf5c28c67a54
+++ contrib/mtn2git/mtn/genproxy.py	408f46a3f5fe0d792eb62e92a8faaf5c28c67a54
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+class GeneratorProxy(object):
+    def __init__(self, generator):
+        self.generator = generator
+    def __iter__(self):
+        return self
+    def next(self):
+        return self.generator.next()
+
+class Seedy(GeneratorProxy):
+    def __del__(self):
+        print "testing"
+        
+def test():
+    yield 2
+    yield 3
+    yield 4
+
+if __name__ == '__main__':
+    a = test()
+    b = Seedy(test())
+    for i in b:
+        print i
+    
============================================================
--- contrib/mtn2git/mtn/mtn.py	b7992918c918fb72a18396be361fc5eba1784e57
+++ contrib/mtn2git/mtn/mtn.py	b7992918c918fb72a18396be361fc5eba1784e57
@@ -0,0 +1,419 @@
+
+import os
+import re
+import fcntl
+import pipes
+import select
+import threading
+import popen2
+from common import set_nonblocking, terminate_popen3
+from traceback import format_exc
+import genproxy
+import sys
+
+# regular expressions that are of general use when 
+# validating monotone output
+def group_compile(r):
+    return re.compile('('+r+')')
+
+hex_re = r'[A-Fa-f0-9]*'
+hex_re_c = group_compile(hex_re)
+revision_re = r'[A-Fa-f0-9]{40}'
+revision_re_c = group_compile(revision_re)
+name_re = r'^[\S]+'
+name_re_c = group_compile(name_re)
+
+class MonotoneException(Exception):
+    pass
+
+class Revision(str):
+    def __init__(self, v):
+        # special case that must be handled: empty (initial) revision ID ''
+        str.__init__(v)
+        self.obj_type = "revision"
+        if v != '' and not revision_re_c.match(self):
+            raise MonotoneException("Not a valid revision ID: %s" % (v))
+    def abbrev(self):
+        return '[' + self[:8] + '..]'
+
+class Author(str):
+    def __init__(self, v):
+        str.__init__(v)
+        self.obj_type = "author"
+        
+class Runner:
+    def __init__(self, monotone, database):
+        self.base_command = [monotone, "--db=%s" % pipes.quote(database)]
+
+packet_header_re = re.compile(r'^(\d+):(\d+):([lm]):(\d+):')
+
+class Automate(Runner):
+    """Runs commands via a particular monotone process. This 
+       process is started the first time run() is called, and 
+       stopped when this class instance is deleted or the stop()
+       method is called.
+       
+       If an error occurs, the monotone process may need to be 
+       stopped and a new one created.
+       """
+    def __init__(self, *args, **kwargs):
+        Runner.__init__(*[self] + list(args), **kwargs)
+        self.lock = threading.Lock()
+        self.process = None
+
+    def stop(self):
+        if not self.process:
+            return
+        terminate_popen3(self.process)
+        self.process = None
+ 
+    def __process_required(self):
+        if self.process != None:
+            return
+        to_run = self.base_command + ['automate', 'stdio']
+        self.process = popen2.Popen3(to_run, capturestderr=True)
+        # breaks down with toposort and a lot of input
+        #map (set_nonblocking, [ self.process.fromchild,
+        #                        self.process.tochild,
+        #                        self.process.childerr ])
+        map (set_nonblocking, [ self.process.fromchild,
+                                self.process.childerr ])
+
+    def run(self, *args, **kwargs):
+        print >> sys.stderr, (("automate is running:", args, kwargs))
+
+        lock = self.lock
+        stop = self.stop
+        class CleanRequest(genproxy.GeneratorProxy):
+            def __init__(self, *args, **kwargs):
+                genproxy.GeneratorProxy.__init__(self, *args, **kwargs)
+
+                # nb; this used to be False, but True seems to behave more sensibly.
+                # in particular, if someone holds down Refresh sometimes the code 
+                # gets here before __del__ is called on the previous iterator, 
+                # causing a pointless error to occur
+                if not lock.acquire(True):
+                    # I've checked; this exception does _not_ cause __del__ to run, so 
+                    # we don't accidentally unlock a lock below
+                    raise MonotoneException("Automate request cannot be called: it is already locked! This indicates a logic error in ViewMTN; please report.")
+
+            def __del__(self):
+                def read_any_unread_output():
+                    try:
+                        # this'll raise StopIteration if we're done
+                        self.next()
+                        # okay, we're not done..
+                        print >> sys.stderr, ("warning: Automate output not completely read; reading manually.")
+                        for stanza in self:
+                            pass
+                    except StopIteration:
+                        pass
+
+                try:
+                    read_any_unread_output()
+                    lock.release()
+                except:
+                    print >> sys.stderr, ("exception cleaning up after Automation; calling stop()!")
+                    stop()
+
+        return CleanRequest(self.__run(*args, **kwargs))
+
+    def __run(self, command, args):
+        enc = "l%d:%s" % (len(command), command)
+        enc += ''.join(["%d:%s" % (len(x), x) for x in args]) + 'e'
+
+        # number of tries to get a working mtn going..
+        for i in xrange(2):
+            self.__process_required()
+            try:
+                self.process.tochild.write(enc)
+                self.process.tochild.flush()
+                break
+            except:
+                # mtn has died underneath the automate; restart it
+                print >> sys.stderr, ("exception writing to child process; attempting restart: %s" % format_exc())
+                self.stop()
+
+        import sys
+        def read_result_packets():
+            buffer = ""
+            while True:
+                r_stdin, r_stdout, r_stderr = select.select([self.process.fromchild], [], [], None)
+                if not r_stdin and not r_stdout and not r_stderr:
+                    break
+
+                if self.process.fromchild in r_stdin:
+                    data = self.process.fromchild.read()
+                    if data == "":
+                        break
+                    buffer += data
+
+                # loop, trying to get complete packets out of our buffer
+                complete, in_packet = False, False
+                while not complete and buffer != '':
+                    if not in_packet:
+                        m = packet_header_re.match(buffer)
+                        if not m:
+                            break
+                        in_packet = True
+                        cmdnum, errnum, pstate, length = m.groups()
+                        errnum = int(errnum)
+                        length = int(length)
+                        header_length = m.end(m.lastindex) + 1 # the '1' is the colon
+
+                    if len(buffer) < length + header_length:
+                        # not enough data read from client yet; go round
+                        break
+                    else:
+                        result = buffer[header_length:header_length+length]
+                        buffer = buffer[header_length+length:]
+                        complete = pstate == 'l'
+                        in_packet = False
+                        yield errnum, complete, result
+
+                if complete:
+                    break
+                
+        # get our response, and yield() it back one line at a time
+        code_max = -1
+        data_buf = ''
+        for code, is_last, data in read_result_packets():
+            if code and code > code_max:
+                code_max = code
+            data_buf += data
+            while True:
+                nl_idx = data_buf.find('\n')
+                if nl_idx == -1:
+                    break
+                yield data_buf[:nl_idx+1]
+                data_buf = data_buf[nl_idx+1:]
+        # left over data?
+        if data_buf:
+            yield data_buf
+        if code_max > 0:
+            raise MonotoneException("error code %d in automate packet." % (code_max))
+
+class Standalone(Runner):
+    """Runs commands by running monotone. One monotone process 
+       per command"""
+
+    def run(self, command, args):
+        # as we pass popen3 as sequence, it executes monotone with these 
+        # arguments - and does not pass them through the shell according 
+        # to help(os.popen3)
+#       print(("standalone is running:", command, args))
+        to_run = self.base_command + [command] + args
+        process = popen2.Popen3(to_run, capturestderr=True)
+        for line in process.fromchild:
+            yield line
+        stderr_data = process.childerr.read()
+        if len(stderr_data) > 0:
+            raise MonotoneException("data on stderr for command '%s': %s" % (command, 
+                                                                             stderr_data))
+        terminate_popen3(process)
+
+class MtnObject:
+    def __init__(self, obj_type):
+        self.obj_type = obj_type
+
+class Tag(MtnObject):
+    def __init__(self, name, revision, author, branches):
+        MtnObject.__init__(self, "tag")
+        self.name, self.revision, self.author, self.branches = name, Revision(revision), author, branches
+
+class Branch(MtnObject):
+    def __init__(self, name):
+        MtnObject.__init__(self, "branch")
+        self.name = name
+
+class File(MtnObject):
+    def __init__(self, name, in_revision):
+        MtnObje%s
>>> DIFF TRUNCATED @ 16K






More information about the Openembedded-commits mailing list