~abentley/bzrtools/bzrtools.dev

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# Copyright (C) 2004 Aaron Bentley
# <aaron.bentley@utoronto.ca>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

import pybaz
from pybaz.backends.baz import sequence_cmd
import re

__docformat__ = "restructuredtext"
__doc__ = "Utility functions to be used by commands"

def direct_merges(merges, excludes=[]):
    """Get a list of direct merges, from a list of direct and indirect
    
    :param merges: Iterator of merge patchlogs
    :type merges: iter of `pybaz.Patchlog`
    :return: The direct merges
    :rtype: list of `pybaz.Patchlog`
    """
    indirect = set()
    logs = list(merges)
    if not logs:
        return []
    for log in logs:
        try:
            this_indirect = set([str(f) for f in log.new_patches 
                                 if f != log.revision and 
                                     str(f) not in indirect])
        except pybaz.errors.NamespaceError:
            print
            print "log ", log, " unusable, attempting to use archive copy."
            log = pybaz.Revision(str(log.revision)).patchlog
            this_indirect = set([str(f) for f in log.new_patches 
                                 if f != log.revision and 
                                     str(f) not in indirect])
        indirect.update(this_indirect)
        if log.continuation_of is not None:
            # continuations list everything in new_patches
            continue
        try:
            # ask baz if we can
            ancestor = log.revision.ancestor
        except pybaz.errors.ExecProblem:
            # baz does not know
            # guess
            if log.revision.patchlevel != 'version-0': 
                ancestor = namespace_previous(log.revision)
            else:
                found = False
                ancestor = None
                # ancestor might be in the tree
                for tree_log in pybaz.WorkingTree(log.tree).iter_logs(
                    log.revision.version, reverse=True):
                    if found:
                        ancestor = tree_log.revision
                        break
                    if tree_log.revision == log.revision:
                        found = True
                print "ancestor of %s is %s" % (log.revision, ancestor)
        if ancestor is not None:
            indirect.add(str(ancestor))
    return [log.revision for log in logs if not str(log.revision) in indirect
            and log.revision not in excludes]


def namespace_previous(revision):
    if revision.patchlevel == 'base-0':
        return None
    if revision.patchlevel == 'patch-1':
        return revision.version['base-0']
    if revision.patchlevel.startswith('patch'):
        level = int(revision.patchlevel[len('patch-'):]) -1
        return revision.version['patch-%d' % level]
    if revision.patchlevel == 'version-0':
        raise RuntimeError("cannot determine prior namespace level for a "
                           "version-0 patch ('%s')" % revision)
    if revision.patchlevel == 'versionfix-1':
        return revision.version['version-0']
    if revision.patchlevel.startswith('versionfix'):
        level = int(revision.patchlevel[len('versionfix-'):]) -1
        return revision.version['versionfix-%d' % level]
    raise NotImplementedError

def iter_new_merges(tree, version, reverse=False):
    """List patchlogs that are new in this tree since the last commit.

    :param tree: The working tree to calculate new revisions in
    :type tree: str
    :param version: The version to use when determining new logs
    :type version: str
    :param reverse: If true, list backwards, from newest to oldest
    :type reverse: bool
    :return: An iterator for new revision logs in this tree
    :rtype: Iterator of `pybaz.Patchlog`
    """
    assert (isinstance(version, pybaz.Version))
    for line in _iter_new_merges(tree, version.fullname, reverse):
        yield pybaz.Patchlog(line, tree)

def _iter_new_merges(directory, version, reverse):
    """List patchlogs that are new in this tree since the last commit.

    :param directory: The working tree to calculate new revisions in
    :type directory: str
    :param version: The version to use when determining new logs
    :type version: str
    :param reverse: If true, list backwards, from newest to oldest
    :type reverse: bool
    :return: An iterator for names of revisions new in this tree
    :rtype: Iterator of str
    """
    args = [ 'new-merges', '--dir', directory ]
    if reverse: 
        args.append('--reverse')
    args.append(version)
    return sequence_cmd(args)