Noob script needs some input: CVS PatchMaker

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Holger

    #1

    Noob script needs some input: CVS PatchMaker

    I needed a tool for extracting patches from CVS based on the log
    messages. I.e. we mark our fixes and features with a "Bugdb XYZ"
    And sometimes you need to move a fix/feature to another branch or maybe
    you just want to inspect exactly what changes were related to a
    specific bugdb issue.

    Now I've searched hi and low for this and I now it's out there
    somewhere bleeding obvious - can't imagine I'm the first to have this
    thought. I just haven't been able to find it...
    Well, that was an excellent opportunity to get some python practice, so
    below is my first shot at the problem.

    Any feedback on what would be "the pythonic way" to do this would be
    much appreciated!

    Usage:
    cd myproject
    patchmaker <regxpr>

    Ouput is a diff of involved files+revs


    Thank you,
    /Holger
    ----------------------------------------------------------------------------------------------------------------------
    #!/usr/bin/env python
    # Copyright 2006 Holger Lindeberg Bille

    import sys, re, os
    import popen2

    workingfile = re.compile("^Wo rking file: *(.*)$")
    revision = re.compile("^re vision *(.*)$")
    fileend =
    re.compile("^== =============== =============== =============== =============== =============")
    details = re.compile("^da te: *")
    entryend = re.compile("^----------------------------")
    branches = re.compile("^br anches:( *(.*);)*")

    class LogEntry:
    def __init__(self):
    self.rev = 0
    self.prevrev = 0
    self.text = []

    def setName(self, name):
    self.name = name

    def read(self, file):
    done = 0
    for line in file:
    regx = details.search( line)
    if regx:
    pass
    else:
    if entryend.search (line):
    break
    else:
    if fileend.search( line):
    done = 1
    break
    else:
    self.text.appen d(line.strip())
    return done

    def GuessPrevRev(se lf):
    pass

    def filter(self, filter):
    found = 0
    for line in self.text:
    if filter.search(l ine):
    found = 1
    break
    return found

    def calcPrevRev(sel f):
    # todo: get this from CVS instead of guessing
    self.rev = "1.1"
    self.prevrev = "1.1"
    ver = self.name.split (".")
    n = int(ver.pop()) - 1
    while len(ver) >= 1:
    if n >= 1:
    ver.append(str( n))
    self.prevrev = ".".join(ve r)
    self.rev = self.name
    break
    else:
    ver.pop() # throw this away
    n = int(ver.pop())

    def patchDump(self, file):
    cmd = "cvs -q diff -u -b -r %s -r %s %s" % (self.prevrev,
    self.rev, file)
    # print cmd
    outp, inp = popen2.popen2(c md)
    for line in outp:
    print line,
    outp.close()
    inp.close()

    def dump(self):
    print "------------------------------------------"
    print "rev = %s" % self.name
    for line in self.text:
    print line


    class FileLog:
    def __init__(self):
    self.revs = []

    def setName(self, name):
    self.name = name

    def read(self, file):
    for line in file:
    regx = revision.search (line)
    if regx:
    rev = LogEntry()
    rev.setName(reg x.group(1))
    done = rev.read(file)
    self.revs.appen d(rev)
    if done:
    break

    def filter(self, filter):
    found = 0
    newrevs = []
    for rev in self.revs:
    if rev.filter(filt er):
    found = 1
    newrevs.append( rev)
    self.revs = newrevs
    return found

    def calcPrevRev(sel f):
    for rev in self.revs:
    rev.calcPrevRev ()

    def patchDump(self) :
    for rev in self.revs:
    rev.patchDump(s elf.name)

    def dump(self):
    print "File = %s" % self.name
    print "No. of revs %d" % len(self.revs)
    for rev in self.revs:
    rev.dump()
    print "============== =============== =============== ==="



    class LogDB:
    def __init__(self):
    self.flogs = []

    def read(self):
    outp, inp = popen2.popen2(' cvs -q log -N')
    found = 0
    for line in outp:
    regx = workingfile.sea rch(line)
    if regx:
    flog = FileLog()
    flog.setName(re gx.group(1))
    flog.read(outp)
    self.flogs.appe nd(flog)
    outp.close()
    inp.close()

    def filter(self, filter):
    newflogs = []
    for flog in self.flogs:
    if flog.filter(fil ter):
    newflogs.append (flog)
    self.flogs = newflogs

    def calcPrevRev(sel f):
    for flog in self.flogs:
    flog.calcPrevRe v()

    def patchDump(self) :
    for flog in self.flogs:
    flog.patchDump( )

    def dump(self):
    print "Starting dump"
    print "============== =============== =============== ==="
    for flog in self.flogs:
    flog.dump()

    if len(sys.argv) != 2:
    sys.stderr.writ e("wrong number of args")
    sys.exit()
    a = sys.argv[1]
    a.encode('latin-1')
    #print "arg = %s" % a
    db = LogDB()
    db.read()
    #db.dump()
    myfilter = re.compile(a)
    db.filter(myfil ter)
    #db.dump()
    db.calcPrevRev( )
    db.patchDump()

  • John Machin

    #2
    Re: Noob script needs some input: CVS PatchMaker

    On 16/06/2006 7:28 PM, Holger wrote:[color=blue]
    > Well, that was an excellent opportunity to get some python practice, so
    > below is my first shot at the problem.
    >
    > Any feedback on what would be "the pythonic way" to do this would be
    > much appreciated!
    >[/color]
    [color=blue]
    > #!/usr/bin/env python
    > # Copyright 2006 Holger Lindeberg Bille
    >
    > import sys, re, os
    > import popen2
    >
    > workingfile = re.compile("^Wo rking file: *(.*)$")
    > revision = re.compile("^re vision *(.*)$")
    > fileend =
    > re.compile("^== =============== =============== =============== =============== =============")
    > details = re.compile("^da te: *")
    > entryend = re.compile("^----------------------------")
    > branches = re.compile("^br anches:( *(.*);)*")
    >
    > class LogEntry:
    > def __init__(self):
    > self.rev = 0
    > self.prevrev = 0
    > self.text = []
    >
    > def setName(self, name):
    > self.name = name
    >
    > def read(self, file):
    > done = 0
    > for line in file:
    > regx = details.search( line)
    > if regx:
    > pass
    > else:
    > if entryend.search (line):
    > break
    > else:
    > if fileend.search( line):
    > done = 1
    > break
    > else:
    > self.text.appen d(line.strip())
    > return done[/color]

    IMHO that flight of geese heading equatorwards for winter is not Xic for
    any language X. Compare with:
    | def read(self, file):
    | done = 0
    | for line in file:
    | regx = details.search( line)
    | if regx:
    | pass
    | elif entryend.search (line):
    | break
    | elif fileend.search( line):
    | done = 1
    | break
    | else:
    | self.text.appen d(line.strip())
    | return done

    2nd comment: Make a habit of NOT using the names of built-ins like
    "file" for your own names. Pretend they are reserved words. Doesn't
    matter in this case, but will save you grief some day soon.

    3rd comment: Read the section in the re manual that explains the
    difference between search and match. Searching for "^foo" will give the
    same results as using match() with "foo" or the redundantly anchored
    "^foo". However some regex engines when presented with
    re.search("^foo ", "x" * 10000)
    will note that there is no joy at offset 0, there is no point (given the
    anchor "^") of looking at offset 1, and return almost immediately.
    Others (cough, cough) will check at offset 1, 2, ...
    Ponder these results:

    python -mtimeit -s"import re;rx=re.compil e('^foo');txt=' x'*10000"
    "rx.match(t xt)"
    100000 loops, best of 3: 1.2 usec per loop

    python -mtimeit -s"import re;rx=re.compil e('foo');txt='x '*10000" "
    rx.search(txt)"
    10000 loops, best of 3: 19.8 usec per loop

    python -mtimeit -s"import re;rx=re.compil e('^foo');txt=' x'*10000"
    "rx.search(txt) "
    1000 loops, best of 3: 201 usec per loop

    4th comment: what you have called "regx" is a match object. "mobj" might
    be a better choice. The term "regex" is applied to a pattern, or
    sometimes to the compiled re object.
    [color=blue]
    > def GuessPrevRev(se lf):
    > pass
    >
    > def filter(self, filter):[/color]

    Ugh. THREE filters: the built-in, the argument, and the method.
    In any case, this method doesn't perform a filtering operation, and the
    arg is not a filter, it's an re pattern. Suggestion:
    def anyLinesMatch(s elf, pattern):
    [color=blue]
    > found = 0
    > for line in self.text:
    > if filter.search(l ine):
    > found = 1
    > break
    > return found[/color]

    [snip]
    [color=blue]
    > class FileLog:
    > def __init__(self):
    > self.revs = []
    >[/color]
    [snip][color=blue]
    >
    > def filter(self, filter):
    > found = 0
    > newrevs = []
    > for rev in self.revs:
    > if rev.filter(filt er):[/color]

    Waahhh! The filter count has now hit 4.
    [color=blue]
    > found = 1
    > newrevs.append( rev)
    > self.revs = newrevs
    > return found
    >[/color]
    [snip]
    [color=blue]
    >
    > class LogDB:
    > def __init__(self):
    > self.flogs = [][/color]
    [snip][color=blue]
    > def filter(self, filter):
    > newflogs = []
    > for flog in self.flogs:
    > if flog.filter(fil ter):[/color]
    See above.[color=blue]
    > newflogs.append (flog)
    > self.flogs = newflogs
    >[/color]
    [snip]

    HTH,
    John

    Comment

    • Holger

      #3
      Re: Noob script needs some input: CVS PatchMaker


      John Machin wrote:[color=blue][color=green]
      > > Any feedback on what would be "the pythonic way" to do this would be
      > > much appreciated![/color][/color]
      --snip--[color=blue]
      >
      > IMHO that flight of geese heading equatorwards for winter is not Xic for
      > any language X. Compare with:[/color]

      He he, good point.
      [color=blue]
      > 2nd comment: Make a habit of NOT using the names of built-ins like
      > "file" for your own names. Pretend they are reserved words. Doesn't
      > matter in this case, but will save you grief some day soon.
      >[/color]

      Agree.
      [color=blue]
      > 3rd comment: Read the section in the re manual that explains the
      > difference between search and match. Searching for "^foo" will give the
      > same results as using match() with "foo" or the redundantly anchored
      > "^foo". However some regex engines when presented with[/color]

      Good point.
      [color=blue]
      > 4th comment: what you have called "regx" is a match object. "mobj" might
      > be a better choice. The term "regex" is applied to a pattern, or
      > sometimes to the compiled re object.[/color]
      ][color=blue]
      >
      > HTH,
      > John[/color]

      Thank you for taking the time :-)
      All points are noted.

      Holger,

      Comment

      Working...