bzr branch
http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
| 
644
by Martin Pool
 - add aaron's pending patch for annotate  | 
1  | 
*** added file 'bzrlib/patches.py'
 | 
2  | 
--- /dev/null 
 | 
|
3  | 
+++ bzrlib/patches.py 
 | 
|
4  | 
@@ -0,0 +1,497 @@
 | 
|
5  | 
+# Copyright (C) 2004, 2005 Aaron Bentley
 | 
|
6  | 
+# <aaron.bentley@utoronto.ca>
 | 
|
7  | 
+#
 | 
|
8  | 
+#    This program is free software; you can redistribute it and/or modify
 | 
|
9  | 
+#    it under the terms of the GNU General Public License as published by
 | 
|
10  | 
+#    the Free Software Foundation; either version 2 of the License, or
 | 
|
11  | 
+#    (at your option) any later version.
 | 
|
12  | 
+#
 | 
|
13  | 
+#    This program is distributed in the hope that it will be useful,
 | 
|
14  | 
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
|
15  | 
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
|
16  | 
+#    GNU General Public License for more details.
 | 
|
17  | 
+#
 | 
|
18  | 
+#    You should have received a copy of the GNU General Public License
 | 
|
19  | 
+#    along with this program; if not, write to the Free Software
 | 
|
20  | 
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
|
21  | 
+import sys
 | 
|
22  | 
+import progress
 | 
|
23  | 
+class PatchSyntax(Exception):
 | 
|
24  | 
+    def __init__(self, msg):
 | 
|
25  | 
+        Exception.__init__(self, msg)
 | 
|
26  | 
+
 | 
|
27  | 
+
 | 
|
28  | 
+class MalformedPatchHeader(PatchSyntax):
 | 
|
29  | 
+    def __init__(self, desc, line):
 | 
|
30  | 
+        self.desc = desc
 | 
|
31  | 
+        self.line = line
 | 
|
32  | 
+        msg = "Malformed patch header.  %s\n%s" % (self.desc, self.line)
 | 
|
33  | 
+        PatchSyntax.__init__(self, msg)
 | 
|
34  | 
+
 | 
|
35  | 
+class MalformedHunkHeader(PatchSyntax):
 | 
|
36  | 
+    def __init__(self, desc, line):
 | 
|
37  | 
+        self.desc = desc
 | 
|
38  | 
+        self.line = line
 | 
|
39  | 
+        msg = "Malformed hunk header.  %s\n%s" % (self.desc, self.line)
 | 
|
40  | 
+        PatchSyntax.__init__(self, msg)
 | 
|
41  | 
+
 | 
|
42  | 
+class MalformedLine(PatchSyntax):
 | 
|
43  | 
+    def __init__(self, desc, line):
 | 
|
44  | 
+        self.desc = desc
 | 
|
45  | 
+        self.line = line
 | 
|
46  | 
+        msg = "Malformed line.  %s\n%s" % (self.desc, self.line)
 | 
|
47  | 
+        PatchSyntax.__init__(self, msg)
 | 
|
48  | 
+
 | 
|
49  | 
+def get_patch_names(iter_lines):
 | 
|
50  | 
+    try:
 | 
|
51  | 
+        line = iter_lines.next()
 | 
|
52  | 
+        if not line.startswith("--- "):
 | 
|
53  | 
+            raise MalformedPatchHeader("No orig name", line)
 | 
|
54  | 
+        else:
 | 
|
55  | 
+            orig_name = line[4:].rstrip("\n")
 | 
|
56  | 
+    except StopIteration:
 | 
|
57  | 
+        raise MalformedPatchHeader("No orig line", "")
 | 
|
58  | 
+    try:
 | 
|
59  | 
+        line = iter_lines.next()
 | 
|
60  | 
+        if not line.startswith("+++ "):
 | 
|
61  | 
+            raise PatchSyntax("No mod name")
 | 
|
62  | 
+        else:
 | 
|
63  | 
+            mod_name = line[4:].rstrip("\n")
 | 
|
64  | 
+    except StopIteration:
 | 
|
65  | 
+        raise MalformedPatchHeader("No mod line", "")
 | 
|
66  | 
+    return (orig_name, mod_name)
 | 
|
67  | 
+
 | 
|
68  | 
+def parse_range(textrange):
 | 
|
69  | 
+    """Parse a patch range, handling the "1" special-case
 | 
|
70  | 
+
 | 
|
71  | 
+    :param textrange: The text to parse
 | 
|
72  | 
+    :type textrange: str
 | 
|
73  | 
+    :return: the position and range, as a tuple
 | 
|
74  | 
+    :rtype: (int, int)
 | 
|
75  | 
+    """
 | 
|
76  | 
+    tmp = textrange.split(',')
 | 
|
77  | 
+    if len(tmp) == 1:
 | 
|
78  | 
+        pos = tmp[0]
 | 
|
79  | 
+        range = "1"
 | 
|
80  | 
+    else:
 | 
|
81  | 
+        (pos, range) = tmp
 | 
|
82  | 
+    pos = int(pos)
 | 
|
83  | 
+    range = int(range)
 | 
|
84  | 
+    return (pos, range)
 | 
|
85  | 
+
 | 
|
86  | 
+ 
 | 
|
87  | 
+def hunk_from_header(line):
 | 
|
88  | 
+    if not line.startswith("@@") or not line.endswith("@@\n") \
 | 
|
89  | 
+        or not len(line) > 4:
 | 
|
90  | 
+        raise MalformedHunkHeader("Does not start and end with @@.", line)
 | 
|
91  | 
+    try:
 | 
|
92  | 
+        (orig, mod) = line[3:-4].split(" ")
 | 
|
93  | 
+    except Exception, e:
 | 
|
94  | 
+        raise MalformedHunkHeader(str(e), line)
 | 
|
95  | 
+    if not orig.startswith('-') or not mod.startswith('+'):
 | 
|
96  | 
+        raise MalformedHunkHeader("Positions don't start with + or -.", line)
 | 
|
97  | 
+    try:
 | 
|
98  | 
+        (orig_pos, orig_range) = parse_range(orig[1:])
 | 
|
99  | 
+        (mod_pos, mod_range) = parse_range(mod[1:])
 | 
|
100  | 
+    except Exception, e:
 | 
|
101  | 
+        raise MalformedHunkHeader(str(e), line)
 | 
|
102  | 
+    if mod_range < 0 or orig_range < 0:
 | 
|
103  | 
+        raise MalformedHunkHeader("Hunk range is negative", line)
 | 
|
104  | 
+    return Hunk(orig_pos, orig_range, mod_pos, mod_range)
 | 
|
105  | 
+
 | 
|
106  | 
+
 | 
|
107  | 
+class HunkLine:
 | 
|
108  | 
+    def __init__(self, contents):
 | 
|
109  | 
+        self.contents = contents
 | 
|
110  | 
+
 | 
|
111  | 
+    def get_str(self, leadchar):
 | 
|
112  | 
+        if self.contents == "\n" and leadchar == " " and False:
 | 
|
113  | 
+            return "\n"
 | 
|
114  | 
+        return leadchar + self.contents
 | 
|
115  | 
+
 | 
|
116  | 
+class ContextLine(HunkLine):
 | 
|
117  | 
+    def __init__(self, contents):
 | 
|
118  | 
+        HunkLine.__init__(self, contents)
 | 
|
119  | 
+
 | 
|
120  | 
+    def __str__(self):
 | 
|
121  | 
+        return self.get_str(" ")
 | 
|
122  | 
+
 | 
|
123  | 
+
 | 
|
124  | 
+class InsertLine(HunkLine):
 | 
|
125  | 
+    def __init__(self, contents):
 | 
|
126  | 
+        HunkLine.__init__(self, contents)
 | 
|
127  | 
+
 | 
|
128  | 
+    def __str__(self):
 | 
|
129  | 
+        return self.get_str("+")
 | 
|
130  | 
+
 | 
|
131  | 
+
 | 
|
132  | 
+class RemoveLine(HunkLine):
 | 
|
133  | 
+    def __init__(self, contents):
 | 
|
134  | 
+        HunkLine.__init__(self, contents)
 | 
|
135  | 
+
 | 
|
136  | 
+    def __str__(self):
 | 
|
137  | 
+        return self.get_str("-")
 | 
|
138  | 
+
 | 
|
139  | 
+__pychecker__="no-returnvalues"
 | 
|
140  | 
+def parse_line(line):
 | 
|
141  | 
+    if line.startswith("\n"):
 | 
|
142  | 
+        return ContextLine(line)
 | 
|
143  | 
+    elif line.startswith(" "):
 | 
|
144  | 
+        return ContextLine(line[1:])
 | 
|
145  | 
+    elif line.startswith("+"):
 | 
|
146  | 
+        return InsertLine(line[1:])
 | 
|
147  | 
+    elif line.startswith("-"):
 | 
|
148  | 
+        return RemoveLine(line[1:])
 | 
|
149  | 
+    else:
 | 
|
150  | 
+        raise MalformedLine("Unknown line type", line)
 | 
|
151  | 
+__pychecker__=""
 | 
|
152  | 
+
 | 
|
153  | 
+
 | 
|
154  | 
+class Hunk:
 | 
|
155  | 
+    def __init__(self, orig_pos, orig_range, mod_pos, mod_range):
 | 
|
156  | 
+        self.orig_pos = orig_pos
 | 
|
157  | 
+        self.orig_range = orig_range
 | 
|
158  | 
+        self.mod_pos = mod_pos
 | 
|
159  | 
+        self.mod_range = mod_range
 | 
|
160  | 
+        self.lines = []
 | 
|
161  | 
+
 | 
|
162  | 
+    def get_header(self):
 | 
|
163  | 
+        return "@@ -%s +%s @@\n" % (self.range_str(self.orig_pos, 
 | 
|
164  | 
+                                                   self.orig_range),
 | 
|
165  | 
+                                    self.range_str(self.mod_pos, 
 | 
|
166  | 
+                                                   self.mod_range))
 | 
|
167  | 
+
 | 
|
168  | 
+    def range_str(self, pos, range):
 | 
|
169  | 
+        """Return a file range, special-casing for 1-line files.
 | 
|
170  | 
+
 | 
|
171  | 
+        :param pos: The position in the file
 | 
|
172  | 
+        :type pos: int
 | 
|
173  | 
+        :range: The range in the file
 | 
|
174  | 
+        :type range: int
 | 
|
175  | 
+        :return: a string in the format 1,4 except when range == pos == 1
 | 
|
176  | 
+        """
 | 
|
177  | 
+        if range == 1:
 | 
|
178  | 
+            return "%i" % pos
 | 
|
179  | 
+        else:
 | 
|
180  | 
+            return "%i,%i" % (pos, range)
 | 
|
181  | 
+
 | 
|
182  | 
+    def __str__(self):
 | 
|
183  | 
+        lines = [self.get_header()]
 | 
|
184  | 
+        for line in self.lines:
 | 
|
185  | 
+            lines.append(str(line))
 | 
|
186  | 
+        return "".join(lines)
 | 
|
187  | 
+
 | 
|
188  | 
+    def shift_to_mod(self, pos):
 | 
|
189  | 
+        if pos < self.orig_pos-1:
 | 
|
190  | 
+            return 0
 | 
|
191  | 
+        elif pos > self.orig_pos+self.orig_range:
 | 
|
192  | 
+            return self.mod_range - self.orig_range
 | 
|
193  | 
+        else:
 | 
|
194  | 
+            return self.shift_to_mod_lines(pos)
 | 
|
195  | 
+
 | 
|
196  | 
+    def shift_to_mod_lines(self, pos):
 | 
|
197  | 
+        assert (pos >= self.orig_pos-1 and pos <= self.orig_pos+self.orig_range)
 | 
|
198  | 
+        position = self.orig_pos-1
 | 
|
199  | 
+        shift = 0
 | 
|
200  | 
+        for line in self.lines:
 | 
|
201  | 
+            if isinstance(line, InsertLine):
 | 
|
202  | 
+                shift += 1
 | 
|
203  | 
+            elif isinstance(line, RemoveLine):
 | 
|
204  | 
+                if position == pos:
 | 
|
205  | 
+                    return None
 | 
|
206  | 
+                shift -= 1
 | 
|
207  | 
+                position += 1
 | 
|
208  | 
+            elif isinstance(line, ContextLine):
 | 
|
209  | 
+                position += 1
 | 
|
210  | 
+            if position > pos:
 | 
|
211  | 
+                break
 | 
|
212  | 
+        return shift
 | 
|
213  | 
+
 | 
|
214  | 
+def iter_hunks(iter_lines):
 | 
|
215  | 
+    hunk = None
 | 
|
216  | 
+    for line in iter_lines:
 | 
|
217  | 
+        if line.startswith("@@"):
 | 
|
218  | 
+            if hunk is not None:
 | 
|
219  | 
+                yield hunk
 | 
|
220  | 
+            hunk = hunk_from_header(line)
 | 
|
221  | 
+        else:
 | 
|
222  | 
+            hunk.lines.append(parse_line(line))
 | 
|
223  | 
+
 | 
|
224  | 
+    if hunk is not None:
 | 
|
225  | 
+        yield hunk
 | 
|
226  | 
+
 | 
|
227  | 
+class Patch:
 | 
|
228  | 
+    def __init__(self, oldname, newname):
 | 
|
229  | 
+        self.oldname = oldname
 | 
|
230  | 
+        self.newname = newname
 | 
|
231  | 
+        self.hunks = []
 | 
|
232  | 
+
 | 
|
233  | 
+    def __str__(self):
 | 
|
234  | 
+        ret =  "--- %s\n+++ %s\n" % (self.oldname, self.newname) 
 | 
|
235  | 
+        ret += "".join([str(h) for h in self.hunks])
 | 
|
236  | 
+        return ret
 | 
|
237  | 
+
 | 
|
238  | 
+    def stats_str(self):
 | 
|
239  | 
+        """Return a string of patch statistics"""
 | 
|
240  | 
+        removes = 0
 | 
|
241  | 
+        inserts = 0
 | 
|
242  | 
+        for hunk in self.hunks:
 | 
|
243  | 
+            for line in hunk.lines:
 | 
|
244  | 
+                if isinstance(line, InsertLine):
 | 
|
245  | 
+                     inserts+=1;
 | 
|
246  | 
+                elif isinstance(line, RemoveLine):
 | 
|
247  | 
+                     removes+=1;
 | 
|
248  | 
+        return "%i inserts, %i removes in %i hunks" % \
 | 
|
249  | 
+            (inserts, removes, len(self.hunks))
 | 
|
250  | 
+
 | 
|
251  | 
+    def pos_in_mod(self, position):
 | 
|
252  | 
+        newpos = position
 | 
|
253  | 
+        for hunk in self.hunks:
 | 
|
254  | 
+            shift = hunk.shift_to_mod(position)
 | 
|
255  | 
+            if shift is None:
 | 
|
256  | 
+                return None
 | 
|
257  | 
+            newpos += shift
 | 
|
258  | 
+        return newpos
 | 
|
259  | 
+            
 | 
|
260  | 
+    def iter_inserted(self):
 | 
|
261  | 
+        """Iteraties through inserted lines
 | 
|
262  | 
+        
 | 
|
263  | 
+        :return: Pair of line number, line
 | 
|
264  | 
+        :rtype: iterator of (int, InsertLine)
 | 
|
265  | 
+        """
 | 
|
266  | 
+        for hunk in self.hunks:
 | 
|
267  | 
+            pos = hunk.mod_pos - 1;
 | 
|
268  | 
+            for line in hunk.lines:
 | 
|
269  | 
+                if isinstance(line, InsertLine):
 | 
|
270  | 
+                    yield (pos, line)
 | 
|
271  | 
+                    pos += 1
 | 
|
272  | 
+                if isinstance(line, ContextLine):
 | 
|
273  | 
+                    pos += 1
 | 
|
274  | 
+
 | 
|
275  | 
+def parse_patch(iter_lines):
 | 
|
276  | 
+    (orig_name, mod_name) = get_patch_names(iter_lines)
 | 
|
277  | 
+    patch = Patch(orig_name, mod_name)
 | 
|
278  | 
+    for hunk in iter_hunks(iter_lines):
 | 
|
279  | 
+        patch.hunks.append(hunk)
 | 
|
280  | 
+    return patch
 | 
|
281  | 
+
 | 
|
282  | 
+
 | 
|
283  | 
+class AnnotateLine:
 | 
|
284  | 
+    """A line associated with the log that produced it"""
 | 
|
285  | 
+    def __init__(self, text, log=None):
 | 
|
286  | 
+        self.text = text
 | 
|
287  | 
+        self.log = log
 | 
|
288  | 
+
 | 
|
289  | 
+class CantGetRevisionData(Exception):
 | 
|
290  | 
+    def __init__(self, revision):
 | 
|
291  | 
+        Exception.__init__(self, "Can't get data for revision %s" % revision)
 | 
|
292  | 
+        
 | 
|
293  | 
+def annotate_file2(file_lines, anno_iter):
 | 
|
294  | 
+    for result in iter_annotate_file(file_lines, anno_iter):
 | 
|
295  | 
+        pass
 | 
|
296  | 
+    return result
 | 
|
297  | 
+
 | 
|
298  | 
+        
 | 
|
299  | 
+def iter_annotate_file(file_lines, anno_iter):
 | 
|
300  | 
+    lines = [AnnotateLine(f) for f in file_lines]
 | 
|
301  | 
+    patches = []
 | 
|
302  | 
+    try:
 | 
|
303  | 
+        for result in anno_iter:
 | 
|
304  | 
+            if isinstance(result, progress.Progress):
 | 
|
305  | 
+                yield result
 | 
|
306  | 
+                continue
 | 
|
307  | 
+            log, iter_inserted, patch = result
 | 
|
308  | 
+            for (num, line) in iter_inserted:
 | 
|
309  | 
+                old_num = num
 | 
|
310  | 
+
 | 
|
311  | 
+                for cur_patch in patches:
 | 
|
312  | 
+                    num = cur_patch.pos_in_mod(num)
 | 
|
313  | 
+                    if num == None: 
 | 
|
314  | 
+                        break
 | 
|
315  | 
+
 | 
|
316  | 
+                if num >= len(lines):
 | 
|
317  | 
+                    continue
 | 
|
318  | 
+                if num is not None and lines[num].log is None:
 | 
|
319  | 
+                    lines[num].log = log
 | 
|
320  | 
+            patches=[patch]+patches
 | 
|
321  | 
+    except CantGetRevisionData:
 | 
|
322  | 
+        pass
 | 
|
323  | 
+    yield lines
 | 
|
324  | 
+
 | 
|
325  | 
+
 | 
|
326  | 
+def difference_index(atext, btext):
 | 
|
327  | 
+    """Find the indext of the first character that differs betweeen two texts
 | 
|
328  | 
+
 | 
|
329  | 
+    :param atext: The first text
 | 
|
330  | 
+    :type atext: str
 | 
|
331  | 
+    :param btext: The second text
 | 
|
332  | 
+    :type str: str
 | 
|
333  | 
+    :return: The index, or None if there are no differences within the range
 | 
|
334  | 
+    :rtype: int or NoneType
 | 
|
335  | 
+    """
 | 
|
336  | 
+    length = len(atext)
 | 
|
337  | 
+    if len(btext) < length:
 | 
|
338  | 
+        length = len(btext)
 | 
|
339  | 
+    for i in range(length):
 | 
|
340  | 
+        if atext[i] != btext[i]:
 | 
|
341  | 
+            return i;
 | 
|
342  | 
+    return None
 | 
|
343  | 
+
 | 
|
344  | 
+
 | 
|
345  | 
+def test():
 | 
|
346  | 
+    import unittest
 | 
|
347  | 
+    class PatchesTester(unittest.TestCase):
 | 
|
348  | 
+        def testValidPatchHeader(self):
 | 
|
349  | 
+            """Parse a valid patch header"""
 | 
|
350  | 
+            lines = "--- orig/commands.py\n+++ mod/dommands.py\n".split('\n')
 | 
|
351  | 
+            (orig, mod) = get_patch_names(lines.__iter__())
 | 
|
352  | 
+            assert(orig == "orig/commands.py")
 | 
|
353  | 
+            assert(mod == "mod/dommands.py")
 | 
|
354  | 
+
 | 
|
355  | 
+        def testInvalidPatchHeader(self):
 | 
|
356  | 
+            """Parse an invalid patch header"""
 | 
|
357  | 
+            lines = "-- orig/commands.py\n+++ mod/dommands.py".split('\n')
 | 
|
358  | 
+            self.assertRaises(MalformedPatchHeader, get_patch_names,
 | 
|
359  | 
+                              lines.__iter__())
 | 
|
360  | 
+
 | 
|
361  | 
+        def testValidHunkHeader(self):
 | 
|
362  | 
+            """Parse a valid hunk header"""
 | 
|
363  | 
+            header = "@@ -34,11 +50,6 @@\n"
 | 
|
364  | 
+            hunk = hunk_from_header(header);
 | 
|
365  | 
+            assert (hunk.orig_pos == 34)
 | 
|
366  | 
+            assert (hunk.orig_range == 11)
 | 
|
367  | 
+            assert (hunk.mod_pos == 50)
 | 
|
368  | 
+            assert (hunk.mod_range == 6)
 | 
|
369  | 
+            assert (str(hunk) == header)
 | 
|
370  | 
+
 | 
|
371  | 
+        def testValidHunkHeader2(self):
 | 
|
372  | 
+            """Parse a tricky, valid hunk header"""
 | 
|
373  | 
+            header = "@@ -1 +0,0 @@\n"
 | 
|
374  | 
+            hunk = hunk_from_header(header);
 | 
|
375  | 
+            assert (hunk.orig_pos == 1)
 | 
|
376  | 
+            assert (hunk.orig_range == 1)
 | 
|
377  | 
+            assert (hunk.mod_pos == 0)
 | 
|
378  | 
+            assert (hunk.mod_range == 0)
 | 
|
379  | 
+            assert (str(hunk) == header)
 | 
|
380  | 
+
 | 
|
381  | 
+        def makeMalformed(self, header):
 | 
|
382  | 
+            self.assertRaises(MalformedHunkHeader, hunk_from_header, header)
 | 
|
383  | 
+
 | 
|
384  | 
+        def testInvalidHeader(self):
 | 
|
385  | 
+            """Parse an invalid hunk header"""
 | 
|
386  | 
+            self.makeMalformed(" -34,11 +50,6 \n")
 | 
|
387  | 
+            self.makeMalformed("@@ +50,6 -34,11 @@\n")
 | 
|
388  | 
+            self.makeMalformed("@@ -34,11 +50,6 @@")
 | 
|
389  | 
+            self.makeMalformed("@@ -34.5,11 +50,6 @@\n")
 | 
|
390  | 
+            self.makeMalformed("@@-34,11 +50,6@@\n")
 | 
|
391  | 
+            self.makeMalformed("@@ 34,11 50,6 @@\n")
 | 
|
392  | 
+            self.makeMalformed("@@ -34,11 @@\n")
 | 
|
393  | 
+            self.makeMalformed("@@ -34,11 +50,6.5 @@\n")
 | 
|
394  | 
+            self.makeMalformed("@@ -34,11 +50,-6 @@\n")
 | 
|
395  | 
+
 | 
|
396  | 
+        def lineThing(self,text, type):
 | 
|
397  | 
+            line = parse_line(text)
 | 
|
398  | 
+            assert(isinstance(line, type))
 | 
|
399  | 
+            assert(str(line)==text)
 | 
|
400  | 
+
 | 
|
401  | 
+        def makeMalformedLine(self, text):
 | 
|
402  | 
+            self.assertRaises(MalformedLine, parse_line, text)
 | 
|
403  | 
+
 | 
|
404  | 
+        def testValidLine(self):
 | 
|
405  | 
+            """Parse a valid hunk line"""
 | 
|
406  | 
+            self.lineThing(" hello\n", ContextLine)
 | 
|
407  | 
+            self.lineThing("+hello\n", InsertLine)
 | 
|
408  | 
+            self.lineThing("-hello\n", RemoveLine)
 | 
|
409  | 
+        
 | 
|
410  | 
+        def testMalformedLine(self):
 | 
|
411  | 
+            """Parse invalid valid hunk lines"""
 | 
|
412  | 
+            self.makeMalformedLine("hello\n")
 | 
|
413  | 
+        
 | 
|
414  | 
+        def compare_parsed(self, patchtext):
 | 
|
415  | 
+            lines = patchtext.splitlines(True)
 | 
|
416  | 
+            patch = parse_patch(lines.__iter__())
 | 
|
417  | 
+            pstr = str(patch)
 | 
|
418  | 
+            i = difference_index(patchtext, pstr)
 | 
|
419  | 
+            if i is not None:
 | 
|
420  | 
+                print "%i: \"%s\" != \"%s\"" % (i, patchtext[i], pstr[i])
 | 
|
421  | 
+            assert (patchtext == str(patch))
 | 
|
422  | 
+
 | 
|
423  | 
+        def testAll(self):
 | 
|
424  | 
+            """Test parsing a whole patch"""
 | 
|
425  | 
+            patchtext = """--- orig/commands.py
 | 
|
426  | 
++++ mod/commands.py
 | 
|
427  | 
+@@ -1337,7 +1337,8 @@
 | 
|
428  | 
+ 
 | 
|
429  | 
+     def set_title(self, command=None):
 | 
|
430  | 
+         try:
 | 
|
431  | 
+-            version = self.tree.tree_version.nonarch
 | 
|
432  | 
++            version = pylon.alias_or_version(self.tree.tree_version, self.tree,
 | 
|
433  | 
++                                             full=False)
 | 
|
434  | 
+         except:
 | 
|
435  | 
+             version = "[no version]"
 | 
|
436  | 
+         if command is None:
 | 
|
437  | 
+@@ -1983,7 +1984,11 @@
 | 
|
438  | 
+                                          version)
 | 
|
439  | 
+         if len(new_merges) > 0:
 | 
|
440  | 
+             if cmdutil.prompt("Log for merge"):
 | 
|
441  | 
+-                mergestuff = cmdutil.log_for_merge(tree, comp_version)
 | 
|
442  | 
++                if cmdutil.prompt("changelog for merge"):
 | 
|
443  | 
++                    mergestuff = "Patches applied:\\n"
 | 
|
444  | 
++                    mergestuff += pylon.changelog_for_merge(new_merges)
 | 
|
445  | 
++                else:
 | 
|
446  | 
++                    mergestuff = cmdutil.log_for_merge(tree, comp_version)
 | 
|
447  | 
+                 log.description += mergestuff
 | 
|
448  | 
+         log.save()
 | 
|
449  | 
+     try:
 | 
|
450  | 
+"""
 | 
|
451  | 
+            self.compare_parsed(patchtext)
 | 
|
452  | 
+
 | 
|
453  | 
+        def testInit(self):
 | 
|
454  | 
+            """Handle patches missing half the position, range tuple"""
 | 
|
455  | 
+            patchtext = \
 | 
|
456  | 
+"""--- orig/__init__.py
 | 
|
457  | 
++++ mod/__init__.py
 | 
|
458  | 
+@@ -1 +1,2 @@
 | 
|
459  | 
+ __docformat__ = "restructuredtext en"
 | 
|
460  | 
++__doc__ = An alternate Arch commandline interface"""
 | 
|
461  | 
+            self.compare_parsed(patchtext)
 | 
|
462  | 
+            
 | 
|
463  | 
+
 | 
|
464  | 
+
 | 
|
465  | 
+        def testLineLookup(self):
 | 
|
466  | 
+            """Make sure we can accurately look up mod line from orig"""
 | 
|
467  | 
+            patch = parse_patch(open("testdata/diff"))
 | 
|
468  | 
+            orig = list(open("testdata/orig"))
 | 
|
469  | 
+            mod = list(open("testdata/mod"))
 | 
|
470  | 
+            removals = []
 | 
|
471  | 
+            for i in range(len(orig)):
 | 
|
472  | 
+                mod_pos = patch.pos_in_mod(i)
 | 
|
473  | 
+                if mod_pos is None:
 | 
|
474  | 
+                    removals.append(orig[i])
 | 
|
475  | 
+                    continue
 | 
|
476  | 
+                assert(mod[mod_pos]==orig[i])
 | 
|
477  | 
+            rem_iter = removals.__iter__()
 | 
|
478  | 
+            for hunk in patch.hunks:
 | 
|
479  | 
+                for line in hunk.lines:
 | 
|
480  | 
+                    if isinstance(line, RemoveLine):
 | 
|
481  | 
+                        next = rem_iter.next()
 | 
|
482  | 
+                        if line.contents != next:
 | 
|
483  | 
+                            sys.stdout.write(" orig:%spatch:%s" % (next,
 | 
|
484  | 
+                                             line.contents))
 | 
|
485  | 
+                        assert(line.contents == next)
 | 
|
486  | 
+            self.assertRaises(StopIteration, rem_iter.next)
 | 
|
487  | 
+
 | 
|
488  | 
+        def testFirstLineRenumber(self):
 | 
|
489  | 
+            """Make sure we handle lines at the beginning of the hunk"""
 | 
|
490  | 
+            patch = parse_patch(open("testdata/insert_top.patch"))
 | 
|
491  | 
+            assert (patch.pos_in_mod(0)==1)
 | 
|
492  | 
+    
 | 
|
493  | 
+            
 | 
|
494  | 
+    patchesTestSuite = unittest.makeSuite(PatchesTester,'test')
 | 
|
495  | 
+    runner = unittest.TextTestRunner(verbosity=0)
 | 
|
496  | 
+    return runner.run(patchesTestSuite)
 | 
|
497  | 
+    
 | 
|
498  | 
+
 | 
|
499  | 
+if __name__ == "__main__":
 | 
|
500  | 
+    test()
 | 
|
501  | 
+# arch-tag: d1541a25-eac5-4de9-a476-08a7cecd5683
 | 
|
502  | 
||
503  | 
*** added directory 'testdata'
 | 
|
504  | 
*** added file 'testdata/diff'
 | 
|
505  | 
--- /dev/null 
 | 
|
506  | 
+++ testdata/diff 
 | 
|
507  | 
@@ -0,0 +1,1154 @@
 | 
|
508  | 
+--- orig/commands.py
 | 
|
509  | 
++++ mod/commands.py
 | 
|
510  | 
+@@ -19,25 +19,31 @@
 | 
|
511  | 
+ import arch
 | 
|
512  | 
+ import arch.util
 | 
|
513  | 
+ import arch.arch
 | 
|
514  | 
++
 | 
|
515  | 
++import pylon.errors
 | 
|
516  | 
++from pylon.errors import *
 | 
|
517  | 
++from pylon import errors
 | 
|
518  | 
++from pylon import util
 | 
|
519  | 
++from pylon import arch_core
 | 
|
520  | 
++from pylon import arch_compound
 | 
|
521  | 
++from pylon import ancillary
 | 
|
522  | 
++from pylon import misc
 | 
|
523  | 
++from pylon import paths 
 | 
|
524  | 
++
 | 
|
525  | 
+ import abacmds
 | 
|
526  | 
+ import cmdutil
 | 
|
527  | 
+ import shutil
 | 
|
528  | 
+ import os
 | 
|
529  | 
+ import options
 | 
|
530  | 
+-import paths 
 | 
|
531  | 
+ import time
 | 
|
532  | 
+ import cmd
 | 
|
533  | 
+ import readline
 | 
|
534  | 
+ import re
 | 
|
535  | 
+ import string
 | 
|
536  | 
+-import arch_core
 | 
|
537  | 
+-from errors import *
 | 
|
538  | 
+-import errors
 | 
|
539  | 
+ import terminal
 | 
|
540  | 
+-import ancillary
 | 
|
541  | 
+-import misc
 | 
|
542  | 
+ import email
 | 
|
543  | 
+ import smtplib
 | 
|
544  | 
++import textwrap
 | 
|
545  | 
+ 
 | 
|
546  | 
+ __docformat__ = "restructuredtext"
 | 
|
547  | 
+ __doc__ = "Implementation of user (sub) commands"
 | 
|
548  | 
+@@ -257,7 +263,7 @@
 | 
|
549  | 
+ 
 | 
|
550  | 
+         tree=arch.tree_root()
 | 
|
551  | 
+         if len(args) == 0:
 | 
|
552  | 
+-            a_spec = cmdutil.comp_revision(tree)
 | 
|
553  | 
++            a_spec = ancillary.comp_revision(tree)
 | 
|
554  | 
+         else:
 | 
|
555  | 
+             a_spec = cmdutil.determine_revision_tree(tree, args[0])
 | 
|
556  | 
+         cmdutil.ensure_archive_registered(a_spec.archive)
 | 
|
557  | 
+@@ -284,7 +290,7 @@
 | 
|
558  | 
+             changeset=options.changeset
 | 
|
559  | 
+             tmpdir = None
 | 
|
560  | 
+         else:
 | 
|
561  | 
+-            tmpdir=cmdutil.tmpdir()
 | 
|
562  | 
++            tmpdir=util.tmpdir()
 | 
|
563  | 
+             changeset=tmpdir+"/changeset"
 | 
|
564  | 
+         try:
 | 
|
565  | 
+             delta=arch.iter_delta(a_spec, b_spec, changeset)
 | 
|
566  | 
+@@ -304,14 +310,14 @@
 | 
|
567  | 
+             if status > 1:
 | 
|
568  | 
+                 return
 | 
|
569  | 
+             if (options.perform_diff):
 | 
|
570  | 
+-                chan = cmdutil.ChangesetMunger(changeset)
 | 
|
571  | 
++                chan = arch_compound.ChangesetMunger(changeset)
 | 
|
572  | 
+                 chan.read_indices()
 | 
|
573  | 
+-                if isinstance(b_spec, arch.Revision):
 | 
|
574  | 
+-                    b_dir = b_spec.library_find()
 | 
|
575  | 
+-                else:
 | 
|
576  | 
+-                    b_dir = b_spec
 | 
|
577  | 
+-                a_dir = a_spec.library_find()
 | 
|
578  | 
+                 if options.diffopts is not None:
 | 
|
579  | 
++                    if isinstance(b_spec, arch.Revision):
 | 
|
580  | 
++                        b_dir = b_spec.library_find()
 | 
|
581  | 
++                    else:
 | 
|
582  | 
++                        b_dir = b_spec
 | 
|
583  | 
++                    a_dir = a_spec.library_find()
 | 
|
584  | 
+                     diffopts = options.diffopts.split()
 | 
|
585  | 
+                     cmdutil.show_custom_diffs(chan, diffopts, a_dir, b_dir)
 | 
|
586  | 
+                 else:
 | 
|
587  | 
+@@ -517,7 +523,7 @@
 | 
|
588  | 
+         except arch.errors.TreeRootError, e:
 | 
|
589  | 
+             print e
 | 
|
590  | 
+             return
 | 
|
591  | 
+-        from_revision=cmdutil.tree_latest(tree)
 | 
|
592  | 
++        from_revision = arch_compound.tree_latest(tree)
 | 
|
593  | 
+         if from_revision==to_revision:
 | 
|
594  | 
+             print "Tree is already up to date with:\n"+str(to_revision)+"."
 | 
|
595  | 
+             return
 | 
|
596  | 
+@@ -592,6 +598,9 @@
 | 
|
597  | 
+ 
 | 
|
598  | 
+         if len(args) == 0:
 | 
|
599  | 
+             args = None
 | 
|
600  | 
++        if options.version is None:
 | 
|
601  | 
++            return options, tree.tree_version, args
 | 
|
602  | 
++
 | 
|
603  | 
+         revision=cmdutil.determine_revision_arch(tree, options.version)
 | 
|
604  | 
+         return options, revision.get_version(), args
 | 
|
605  | 
+ 
 | 
|
606  | 
+@@ -601,11 +610,16 @@
 | 
|
607  | 
+         """
 | 
|
608  | 
+         tree=arch.tree_root()
 | 
|
609  | 
+         options, version, files = self.parse_commandline(cmdargs, tree)
 | 
|
610  | 
++        ancestor = None
 | 
|
611  | 
+         if options.__dict__.has_key("base") and options.base:
 | 
|
612  | 
+             base = cmdutil.determine_revision_tree(tree, options.base)
 | 
|
613  | 
++            ancestor = base
 | 
|
614  | 
+         else:
 | 
|
615  | 
+-            base = cmdutil.submit_revision(tree)
 | 
|
616  | 
+-        
 | 
|
617  | 
++            base = ancillary.submit_revision(tree)
 | 
|
618  | 
++            ancestor = base
 | 
|
619  | 
++        if ancestor is None:
 | 
|
620  | 
++            ancestor = arch_compound.tree_latest(tree, version)
 | 
|
621  | 
++
 | 
|
622  | 
+         writeversion=version
 | 
|
623  | 
+         archive=version.archive
 | 
|
624  | 
+         source=cmdutil.get_mirror_source(archive)
 | 
|
625  | 
+@@ -625,18 +639,26 @@
 | 
|
626  | 
+         try:
 | 
|
627  | 
+             last_revision=tree.iter_logs(version, True).next().revision
 | 
|
628  | 
+         except StopIteration, e:
 | 
|
629  | 
+-            if cmdutil.prompt("Import from commit"):
 | 
|
630  | 
+-                return do_import(version)
 | 
|
631  | 
+-            else:
 | 
|
632  | 
+-                raise NoVersionLogs(version)
 | 
|
633  | 
+-        if last_revision!=version.iter_revisions(True).next():
 | 
|
634  | 
++            last_revision = None
 | 
|
635  | 
++            if ancestor is None:
 | 
|
636  | 
++                if cmdutil.prompt("Import from commit"):
 | 
|
637  | 
++                    return do_import(version)
 | 
|
638  | 
++                else:
 | 
|
639  | 
++                    raise NoVersionLogs(version)
 | 
|
640  | 
++        try:
 | 
|
641  | 
++            arch_last_revision = version.iter_revisions(True).next()
 | 
|
642  | 
++        except StopIteration, e:
 | 
|
643  | 
++            arch_last_revision = None
 | 
|
644  | 
++ 
 | 
|
645  | 
++        if last_revision != arch_last_revision:
 | 
|
646  | 
++            print "Tree is not up to date with %s" % str(version)
 | 
|
647  | 
+             if not cmdutil.prompt("Out of date"):
 | 
|
648  | 
+                 raise OutOfDate
 | 
|
649  | 
+             else:
 | 
|
650  | 
+                 allow_old=True
 | 
|
651  | 
+ 
 | 
|
652  | 
+         try:
 | 
|
653  | 
+-            if not cmdutil.has_changed(version):
 | 
|
654  | 
++            if not cmdutil.has_changed(ancestor):
 | 
|
655  | 
+                 if not cmdutil.prompt("Empty commit"):
 | 
|
656  | 
+                     raise EmptyCommit
 | 
|
657  | 
+         except arch.util.ExecProblem, e:
 | 
|
658  | 
+@@ -645,15 +667,15 @@
 | 
|
659  | 
+                 raise MissingID(e)
 | 
|
660  | 
+             else:
 | 
|
661  | 
+                 raise
 | 
|
662  | 
+-        log = tree.log_message(create=False)
 | 
|
663  | 
++        log = tree.log_message(create=False, version=version)
 | 
|
664  | 
+         if log is None:
 | 
|
665  | 
+             try:
 | 
|
666  | 
+                 if cmdutil.prompt("Create log"):
 | 
|
667  | 
+-                    edit_log(tree)
 | 
|
668  | 
++                    edit_log(tree, version)
 | 
|
669  | 
+ 
 | 
|
670  | 
+             except cmdutil.NoEditorSpecified, e:
 | 
|
671  | 
+                 raise CommandFailed(e)
 | 
|
672  | 
+-            log = tree.log_message(create=False)
 | 
|
673  | 
++            log = tree.log_message(create=False, version=version)
 | 
|
674  | 
+         if log is None: 
 | 
|
675  | 
+             raise NoLogMessage
 | 
|
676  | 
+         if log["Summary"] is None or len(log["Summary"].strip()) == 0:
 | 
|
677  | 
+@@ -837,23 +859,24 @@
 | 
|
678  | 
+             if spec is not None:
 | 
|
679  | 
+                 revision = cmdutil.determine_revision_tree(tree, spec)
 | 
|
680  | 
+             else:
 | 
|
681  | 
+-                revision = cmdutil.comp_revision(tree)
 | 
|
682  | 
++                revision = ancillary.comp_revision(tree)
 | 
|
683  | 
+         except cmdutil.CantDetermineRevision, e:
 | 
|
684  | 
+             raise CommandFailedWrapper(e)
 | 
|
685  | 
+         munger = None
 | 
|
686  | 
+ 
 | 
|
687  | 
+         if options.file_contents or options.file_perms or options.deletions\
 | 
|
688  | 
+             or options.additions or options.renames or options.hunk_prompt:
 | 
|
689  | 
+-            munger = cmdutil.MungeOpts()
 | 
|
690  | 
+-            munger.hunk_prompt = options.hunk_prompt
 | 
|
691  | 
++            munger = arch_compound.MungeOpts()
 | 
|
692  | 
++            munger.set_hunk_prompt(cmdutil.colorize, cmdutil.user_hunk_confirm,
 | 
|
693  | 
++                                   options.hunk_prompt)
 | 
|
694  | 
+ 
 | 
|
695  | 
+         if len(args) > 0 or options.logs or options.pattern_files or \
 | 
|
696  | 
+             options.control:
 | 
|
697  | 
+             if munger is None:
 | 
|
698  | 
+-                munger = cmdutil.MungeOpts(True)
 | 
|
699  | 
++                munger = cmdutil.arch_compound.MungeOpts(True)
 | 
|
700  | 
+                 munger.all_types(True)
 | 
|
701  | 
+         if len(args) > 0:
 | 
|
702  | 
+-            t_cwd = cmdutil.tree_cwd(tree)
 | 
|
703  | 
++            t_cwd = arch_compound.tree_cwd(tree)
 | 
|
704  | 
+             for name in args:
 | 
|
705  | 
+                 if len(t_cwd) > 0:
 | 
|
706  | 
+                     t_cwd += "/"
 | 
|
707  | 
+@@ -878,7 +901,7 @@
 | 
|
708  | 
+         if options.pattern_files:
 | 
|
709  | 
+             munger.add_keep_pattern(options.pattern_files)
 | 
|
710  | 
+                 
 | 
|
711  | 
+-        for line in cmdutil.revert(tree, revision, munger, 
 | 
|
712  | 
++        for line in arch_compound.revert(tree, revision, munger, 
 | 
|
713  | 
+                                    not options.no_output):
 | 
|
714  | 
+             cmdutil.colorize(line)
 | 
|
715  | 
+ 
 | 
|
716  | 
+@@ -1042,18 +1065,13 @@
 | 
|
717  | 
+         help_tree_spec()
 | 
|
718  | 
+         return
 | 
|
719  | 
+ 
 | 
|
720  | 
+-def require_version_exists(version, spec):
 | 
|
721  | 
+-    if not version.exists():
 | 
|
722  | 
+-        raise cmdutil.CantDetermineVersion(spec, 
 | 
|
723  | 
+-                                           "The version %s does not exist." \
 | 
|
724  | 
+-                                           % version)
 | 
|
725  | 
+-
 | 
|
726  | 
+ class Revisions(BaseCommand):
 | 
|
727  | 
+     """
 | 
|
728  | 
+     Print a revision name based on a revision specifier
 | 
|
729  | 
+     """
 | 
|
730  | 
+     def __init__(self):
 | 
|
731  | 
+         self.description="Lists revisions"
 | 
|
732  | 
++        self.cl_revisions = []
 | 
|
733  | 
+     
 | 
|
734  | 
+     def do_command(self, cmdargs):
 | 
|
735  | 
+         """
 | 
|
736  | 
+@@ -1066,224 +1084,68 @@
 | 
|
737  | 
+             self.tree = arch.tree_root()
 | 
|
738  | 
+         except arch.errors.TreeRootError:
 | 
|
739  | 
+             self.tree = None
 | 
|
740  | 
++        if options.type == "default":
 | 
|
741  | 
++            options.type = "archive"
 | 
|
742  | 
+         try:
 | 
|
743  | 
+-            iter = self.get_iterator(options.type, args, options.reverse, 
 | 
|
744  | 
+-                                     options.modified)
 | 
|
745  | 
++            iter = cmdutil.revision_iterator(self.tree, options.type, args, 
 | 
|
746  | 
++                                             options.reverse, options.modified,
 | 
|
747  | 
++                                             options.shallow)
 | 
|
748  | 
+         except cmdutil.CantDetermineRevision, e:
 | 
|
749  | 
+             raise CommandFailedWrapper(e)
 | 
|
750  | 
+-
 | 
|
751  | 
++        except cmdutil.CantDetermineVersion, e:
 | 
|
752  | 
++            raise CommandFailedWrapper(e)
 | 
|
753  | 
+         if options.skip is not None:
 | 
|
754  | 
+             iter = cmdutil.iter_skip(iter, int(options.skip))
 | 
|
755  | 
+ 
 | 
|
756  | 
+-        for revision in iter:
 | 
|
757  | 
+-            log = None
 | 
|
758  | 
+-            if isinstance(revision, arch.Patchlog):
 | 
|
759  | 
+-                log = revision
 | 
|
760  | 
+-                revision=revision.revision
 | 
|
761  | 
+-            print options.display(revision)
 | 
|
762  | 
+-            if log is None and (options.summary or options.creator or 
 | 
|
763  | 
+-                                options.date or options.merges):
 | 
|
764  | 
+-                log = revision.patchlog
 | 
|
765  | 
+-            if options.creator:
 | 
|
766  | 
+-                print "    %s" % log.creator
 | 
|
767  | 
+-            if options.date:
 | 
|
768  | 
+-                print "    %s" % time.strftime('%Y-%m-%d %H:%M:%S %Z', log.date)
 | 
|
769  | 
+-            if options.summary:
 | 
|
770  | 
+-                print "    %s" % log.summary
 | 
|
771  | 
+-            if options.merges:
 | 
|
772  | 
+-                showed_title = False
 | 
|
773  | 
+-                for revision in log.merged_patches:
 | 
|
774  | 
+-                    if not showed_title:
 | 
|
775  | 
+-                        print "    Merged:"
 | 
|
776  | 
+-                        showed_title = True
 | 
|
777  | 
+-                    print "    %s" % revision
 | 
|
778  | 
+-
 | 
|
779  | 
+-    def get_iterator(self, type, args, reverse, modified):
 | 
|
780  | 
+-        if len(args) > 0:
 | 
|
781  | 
+-            spec = args[0]
 | 
|
782  | 
+-        else:
 | 
|
783  | 
+-            spec = None
 | 
|
784  | 
+-        if modified is not None:
 | 
|
785  | 
+-            iter = cmdutil.modified_iter(modified, self.tree)
 | 
|
786  | 
+-            if reverse:
 | 
|
787  | 
+-                return iter
 | 
|
788  | 
+-            else:
 | 
|
789  | 
+-                return cmdutil.iter_reverse(iter)
 | 
|
790  | 
+-        elif type == "archive":
 | 
|
791  | 
+-            if spec is None:
 | 
|
792  | 
+-                if self.tree is None:
 | 
|
793  | 
+-                    raise cmdutil.CantDetermineRevision("", 
 | 
|
794  | 
+-                                                        "Not in a project tree")
 | 
|
795  | 
+-                version = cmdutil.determine_version_tree(spec, self.tree)
 | 
|
796  | 
+-            else:
 | 
|
797  | 
+-                version = cmdutil.determine_version_arch(spec, self.tree)
 | 
|
798  | 
+-                cmdutil.ensure_archive_registered(version.archive)
 | 
|
799  | 
+-                require_version_exists(version, spec)
 | 
|
800  | 
+-            return version.iter_revisions(reverse)
 | 
|
801  | 
+-        elif type == "cacherevs":
 | 
|
802  | 
+-            if spec is None:
 | 
|
803  | 
+-                if self.tree is None:
 | 
|
804  | 
+-                    raise cmdutil.CantDetermineRevision("", 
 | 
|
805  | 
+-                                                        "Not in a project tree")
 | 
|
806  | 
+-                version = cmdutil.determine_version_tree(spec, self.tree)
 | 
|
807  | 
+-            else:
 | 
|
808  | 
+-                version = cmdutil.determine_version_arch(spec, self.tree)
 | 
|
809  | 
+-                cmdutil.ensure_archive_registered(version.archive)
 | 
|
810  | 
+-                require_version_exists(version, spec)
 | 
|
811  | 
+-            return cmdutil.iter_cacherevs(version, reverse)
 | 
|
812  | 
+-        elif type == "library":
 | 
|
813  | 
+-            if spec is None:
 | 
|
814  | 
+-                if self.tree is None:
 | 
|
815  | 
+-                    raise cmdutil.CantDetermineRevision("", 
 | 
|
816  | 
+-                                                        "Not in a project tree")
 | 
|
817  | 
+-                version = cmdutil.determine_version_tree(spec, self.tree)
 | 
|
818  | 
+-            else:
 | 
|
819  | 
+-                version = cmdutil.determine_version_arch(spec, self.tree)
 | 
|
820  | 
+-            return version.iter_library_revisions(reverse)
 | 
|
821  | 
+-        elif type == "logs":
 | 
|
822  | 
+-            if self.tree is None:
 | 
|
823  | 
+-                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
 | 
|
824  | 
+-            return self.tree.iter_logs(cmdutil.determine_version_tree(spec, \
 | 
|
825  | 
+-                                  self.tree), reverse)
 | 
|
826  | 
+-        elif type == "missing" or type == "skip-present":
 | 
|
827  | 
+-            if self.tree is None:
 | 
|
828  | 
+-                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
 | 
|
829  | 
+-            skip = (type == "skip-present")
 | 
|
830  | 
+-            version = cmdutil.determine_version_tree(spec, self.tree)
 | 
|
831  | 
+-            cmdutil.ensure_archive_registered(version.archive)
 | 
|
832  | 
+-            require_version_exists(version, spec)
 | 
|
833  | 
+-            return cmdutil.iter_missing(self.tree, version, reverse,
 | 
|
834  | 
+-                                        skip_present=skip)
 | 
|
835  | 
+-
 | 
|
836  | 
+-        elif type == "present":
 | 
|
837  | 
+-            if self.tree is None:
 | 
|
838  | 
+-                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
 | 
|
839  | 
+-            version = cmdutil.determine_version_tree(spec, self.tree)
 | 
|
840  | 
+-            cmdutil.ensure_archive_registered(version.archive)
 | 
|
841  | 
+-            require_version_exists(version, spec)
 | 
|
842  | 
+-            return cmdutil.iter_present(self.tree, version, reverse)
 | 
|
843  | 
+-
 | 
|
844  | 
+-        elif type == "new-merges" or type == "direct-merges":
 | 
|
845  | 
+-            if self.tree is None:
 | 
|
846  | 
+-                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
 | 
|
847  | 
+-            version = cmdutil.determine_version_tree(spec, self.tree)
 | 
|
848  | 
+-            cmdutil.ensure_archive_registered(version.archive)
 | 
|
849  | 
+-            require_version_exists(version, spec)
 | 
|
850  | 
+-            iter = cmdutil.iter_new_merges(self.tree, version, reverse)
 | 
|
851  | 
+-            if type == "new-merges":
 | 
|
852  | 
+-                return iter
 | 
|
853  | 
+-            elif type == "direct-merges":
 | 
|
854  | 
+-                return cmdutil.direct_merges(iter)
 | 
|
855  | 
+-
 | 
|
856  | 
+-        elif type == "missing-from":
 | 
|
857  | 
+-            if self.tree is None:
 | 
|
858  | 
+-                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
 | 
|
859  | 
+-            revision = cmdutil.determine_revision_tree(self.tree, spec)
 | 
|
860  | 
+-            libtree = cmdutil.find_or_make_local_revision(revision)
 | 
|
861  | 
+-            return cmdutil.iter_missing(libtree, self.tree.tree_version,
 | 
|
862  | 
+-                                        reverse)
 | 
|
863  | 
+-
 | 
|
864  | 
+-        elif type == "partner-missing":
 | 
|
865  | 
+-            return cmdutil.iter_partner_missing(self.tree, reverse)
 | 
|
866  | 
+-
 | 
|
867  | 
+-        elif type == "ancestry":
 | 
|
868  | 
+-            revision = cmdutil.determine_revision_tree(self.tree, spec)
 | 
|
869  | 
+-            iter = cmdutil._iter_ancestry(self.tree, revision)
 | 
|
870  | 
+-            if reverse:
 | 
|
871  | 
+-                return iter
 | 
|
872  | 
+-            else:
 | 
|
873  | 
+-                return cmdutil.iter_reverse(iter)
 | 
|
874  | 
+-
 | 
|
875  | 
+-        elif type == "dependencies" or type == "non-dependencies":
 | 
|
876  | 
+-            nondeps = (type == "non-dependencies")
 | 
|
877  | 
+-            revision = cmdutil.determine_revision_tree(self.tree, spec)
 | 
|
878  | 
+-            anc_iter = cmdutil._iter_ancestry(self.tree, revision)
 | 
|
879  | 
+-            iter_depends = cmdutil.iter_depends(anc_iter, nondeps)
 | 
|
880  | 
+-            if reverse:
 | 
|
881  | 
+-                return iter_depends
 | 
|
882  | 
+-            else:
 | 
|
883  | 
+-                return cmdutil.iter_reverse(iter_depends)
 | 
|
884  | 
+-        elif type == "micro":
 | 
|
885  | 
+-            return cmdutil.iter_micro(self.tree)
 | 
|
886  | 
+-
 | 
|
887  | 
+-    
 | 
|
888  | 
++        try:
 | 
|
889  | 
++            for revision in iter:
 | 
|
890  | 
++                log = None
 | 
|
891  | 
++                if isinstance(revision, arch.Patchlog):
 | 
|
892  | 
++                    log = revision
 | 
|
893  | 
++                    revision=revision.revision
 | 
|
894  | 
++                out = options.display(revision)
 | 
|
895  | 
++                if out is not None:
 | 
|
896  | 
++                    print out
 | 
|
897  | 
++                if log is None and (options.summary or options.creator or 
 | 
|
898  | 
++                                    options.date or options.merges):
 | 
|
899  | 
++                    log = revision.patchlog
 | 
|
900  | 
++                if options.creator:
 | 
|
901  | 
++                    print "    %s" % log.creator
 | 
|
902  | 
++                if options.date:
 | 
|
903  | 
++                    print "    %s" % time.strftime('%Y-%m-%d %H:%M:%S %Z', log.date)
 | 
|
904  | 
++                if options.summary:
 | 
|
905  | 
++                    print "    %s" % log.summary
 | 
|
906  | 
++                if options.merges:
 | 
|
907  | 
++                    showed_title = False
 | 
|
908  | 
++                    for revision in log.merged_patches:
 | 
|
909  | 
++                        if not showed_title:
 | 
|
910  | 
++                            print "    Merged:"
 | 
|
911  | 
++                            showed_title = True
 | 
|
912  | 
++                        print "    %s" % revision
 | 
|
913  | 
++            if len(self.cl_revisions) > 0:
 | 
|
914  | 
++                print pylon.changelog_for_merge(self.cl_revisions)
 | 
|
915  | 
++        except pylon.errors.TreeRootNone:
 | 
|
916  | 
++            raise CommandFailedWrapper(
 | 
|
917  | 
++                Exception("This option can only be used in a project tree."))
 | 
|
918  | 
++
 | 
|
919  | 
++    def changelog_append(self, revision):
 | 
|
920  | 
++        if isinstance(revision, arch.Revision):
 | 
|
921  | 
++            revision=arch.Patchlog(revision)
 | 
|
922  | 
++        self.cl_revisions.append(revision)
 | 
|
923  | 
++   
 | 
|
924  | 
+     def get_parser(self):
 | 
|
925  | 
+         """
 | 
|
926  | 
+         Returns the options parser to use for the "revision" command.
 | 
|
927  | 
+ 
 | 
|
928  | 
+         :rtype: cmdutil.CmdOptionParser
 | 
|
929  | 
+         """
 | 
|
930  | 
+-        parser=cmdutil.CmdOptionParser("fai revisions [revision]")
 | 
|
931  | 
++        parser=cmdutil.CmdOptionParser("fai revisions [version/revision]")
 | 
|
932  | 
+         select = cmdutil.OptionGroup(parser, "Selection options",
 | 
|
933  | 
+                           "Control which revisions are listed.  These options"
 | 
|
934  | 
+                           " are mutually exclusive.  If more than one is"
 | 
|
935  | 
+                           " specified, the last is used.")
 | 
|
936  | 
+-        select.add_option("", "--archive", action="store_const", 
 | 
|
937  | 
+-                          const="archive", dest="type", default="archive",
 | 
|
938  | 
+-                          help="List all revisions in the archive")
 | 
|
939  | 
+-        select.add_option("", "--cacherevs", action="store_const", 
 | 
|
940  | 
+-                          const="cacherevs", dest="type",
 | 
|
941  | 
+-                          help="List all revisions stored in the archive as "
 | 
|
942  | 
+-                          "complete copies")
 | 
|
943  | 
+-        select.add_option("", "--logs", action="store_const", 
 | 
|
944  | 
+-                          const="logs", dest="type",
 | 
|
945  | 
+-                          help="List revisions that have a patchlog in the "
 | 
|
946  | 
+-                          "tree")
 | 
|
947  | 
+-        select.add_option("", "--missing", action="store_const", 
 | 
|
948  | 
+-                          const="missing", dest="type",
 | 
|
949  | 
+-                          help="List revisions from the specified version that"
 | 
|
950  | 
+-                          " have no patchlog in the tree")
 | 
|
951  | 
+-        select.add_option("", "--skip-present", action="store_const", 
 | 
|
952  | 
+-                          const="skip-present", dest="type",
 | 
|
953  | 
+-                          help="List revisions from the specified version that"
 | 
|
954  | 
+-                          " have no patchlogs at all in the tree")
 | 
|
955  | 
+-        select.add_option("", "--present", action="store_const", 
 | 
|
956  | 
+-                          const="present", dest="type",
 | 
|
957  | 
+-                          help="List revisions from the specified version that"
 | 
|
958  | 
+-                          " have no patchlog in the tree, but can't be merged")
 | 
|
959  | 
+-        select.add_option("", "--missing-from", action="store_const", 
 | 
|
960  | 
+-                          const="missing-from", dest="type",
 | 
|
961  | 
+-                          help="List revisions from the specified revision "
 | 
|
962  | 
+-                          "that have no patchlog for the tree version")
 | 
|
963  | 
+-        select.add_option("", "--partner-missing", action="store_const", 
 | 
|
964  | 
+-                          const="partner-missing", dest="type",
 | 
|
965  | 
+-                          help="List revisions in partner versions that are"
 | 
|
966  | 
+-                          " missing")
 | 
|
967  | 
+-        select.add_option("", "--new-merges", action="store_const", 
 | 
|
968  | 
+-                          const="new-merges", dest="type",
 | 
|
969  | 
+-                          help="List revisions that have had patchlogs added"
 | 
|
970  | 
+-                          " to the tree since the last commit")
 | 
|
971  | 
+-        select.add_option("", "--direct-merges", action="store_const", 
 | 
|
972  | 
+-                          const="direct-merges", dest="type",
 | 
|
973  | 
+-                          help="List revisions that have been directly added"
 | 
|
974  | 
+-                          " to tree since the last commit ")
 | 
|
975  | 
+-        select.add_option("", "--library", action="store_const", 
 | 
|
976  | 
+-                          const="library", dest="type",
 | 
|
977  | 
+-                          help="List revisions in the revision library")
 | 
|
978  | 
+-        select.add_option("", "--ancestry", action="store_const", 
 | 
|
979  | 
+-                          const="ancestry", dest="type",
 | 
|
980  | 
+-                          help="List revisions that are ancestors of the "
 | 
|
981  | 
+-                          "current tree version")
 | 
|
982  | 
+-
 | 
|
983  | 
+-        select.add_option("", "--dependencies", action="store_const", 
 | 
|
984  | 
+-                          const="dependencies", dest="type",
 | 
|
985  | 
+-                          help="List revisions that the given revision "
 | 
|
986  | 
+-                          "depends on")
 | 
|
987  | 
+-
 | 
|
988  | 
+-        select.add_option("", "--non-dependencies", action="store_const", 
 | 
|
989  | 
+-                          const="non-dependencies", dest="type",
 | 
|
990  | 
+-                          help="List revisions that the given revision "
 | 
|
991  | 
+-                          "does not depend on")
 | 
|
992  | 
+-
 | 
|
993  | 
+-        select.add_option("--micro", action="store_const", 
 | 
|
994  | 
+-                          const="micro", dest="type",
 | 
|
995  | 
+-                          help="List partner revisions aimed for this "
 | 
|
996  | 
+-                          "micro-branch")
 | 
|
997  | 
+-
 | 
|
998  | 
+-        select.add_option("", "--modified", dest="modified", 
 | 
|
999  | 
+-                          help="List tree ancestor revisions that modified a "
 | 
|
1000  | 
+-                          "given file", metavar="FILE[:LINE]")
 | 
|
1001  | 
+ 
 | 
|
1002  | 
++        cmdutil.add_revision_iter_options(select)
 | 
|
1003  | 
+         parser.add_option("", "--skip", dest="skip", 
 | 
|
1004  | 
+                           help="Skip revisions.  Positive numbers skip from "
 | 
|
1005  | 
+                           "beginning, negative skip from end.",
 | 
|
1006  | 
+@@ -1312,6 +1174,9 @@
 | 
|
1007  | 
+         format.add_option("--cacherev", action="store_const", 
 | 
|
1008  | 
+                          const=paths.determine_cacherev_path, dest="display",
 | 
|
1009  | 
+                          help="Show location of cacherev file")
 | 
|
1010  | 
++        format.add_option("--changelog", action="store_const", 
 | 
|
1011  | 
++                         const=self.changelog_append, dest="display",
 | 
|
1012  | 
++                         help="Show location of cacherev file")
 | 
|
1013  | 
+         parser.add_option_group(format)
 | 
|
1014  | 
+         display = cmdutil.OptionGroup(parser, "Display format options",
 | 
|
1015  | 
+                           "These control the display of data")
 | 
|
1016  | 
+@@ -1448,6 +1313,7 @@
 | 
|
1017  | 
+         if os.access(self.history_file, os.R_OK) and \
 | 
|
1018  | 
+             os.path.isfile(self.history_file):
 | 
|
1019  | 
+             readline.read_history_file(self.history_file)
 | 
|
1020  | 
++        self.cwd = os.getcwd()
 | 
|
1021  | 
+ 
 | 
|
1022  | 
+     def write_history(self):
 | 
|
1023  | 
+         readline.write_history_file(self.history_file)
 | 
|
1024  | 
+@@ -1470,16 +1336,21 @@
 | 
|
1025  | 
+     def set_prompt(self):
 | 
|
1026  | 
+         if self.tree is not None:
 | 
|
1027  | 
+             try:
 | 
|
1028  | 
+-                version = " "+self.tree.tree_version.nonarch
 | 
|
1029  | 
++                prompt = pylon.alias_or_version(self.tree.tree_version, 
 | 
|
1030  | 
++                                                self.tree, 
 | 
|
1031  | 
++                                                full=False)
 | 
|
1032  | 
++                if prompt is not None:
 | 
|
1033  | 
++                    prompt = " " + prompt
 | 
|
1034  | 
+             except:
 | 
|
1035  | 
+-                version = ""
 | 
|
1036  | 
++                prompt = ""
 | 
|
1037  | 
+         else:
 | 
|
1038  | 
+-            version = ""
 | 
|
1039  | 
+-        self.prompt = "Fai%s> " % version
 | 
|
1040  | 
++            prompt = ""
 | 
|
1041  | 
++        self.prompt = "Fai%s> " % prompt
 | 
|
1042  | 
+ 
 | 
|
1043  | 
+     def set_title(self, command=None):
 | 
|
1044  | 
+         try:
 | 
|
1045  | 
+-            version = self.tree.tree_version.nonarch
 | 
|
1046  | 
++            version = pylon.alias_or_version(self.tree.tree_version, self.tree, 
 | 
|
1047  | 
++                                             full=False)
 | 
|
1048  | 
+         except:
 | 
|
1049  | 
+             version = "[no version]"
 | 
|
1050  | 
+         if command is None:
 | 
|
1051  | 
+@@ -1489,8 +1360,15 @@
 | 
|
1052  | 
+     def do_cd(self, line):
 | 
|
1053  | 
+         if line == "":
 | 
|
1054  | 
+             line = "~"
 | 
|
1055  | 
++        line = os.path.expanduser(line)
 | 
|
1056  | 
++        if os.path.isabs(line):
 | 
|
1057  | 
++            newcwd = line
 | 
|
1058  | 
++        else:
 | 
|
1059  | 
++            newcwd = self.cwd+'/'+line
 | 
|
1060  | 
++        newcwd = os.path.normpath(newcwd)
 | 
|
1061  | 
+         try:
 | 
|
1062  | 
+-            os.chdir(os.path.expanduser(line))
 | 
|
1063  | 
++            os.chdir(newcwd)
 | 
|
1064  | 
++            self.cwd = newcwd
 | 
|
1065  | 
+         except Exception, e:
 | 
|
1066  | 
+             print e
 | 
|
1067  | 
+         try:
 | 
|
1068  | 
+@@ -1523,7 +1401,7 @@
 | 
|
1069  | 
+             except cmdutil.CantDetermineRevision, e:
 | 
|
1070  | 
+                 print e
 | 
|
1071  | 
+             except Exception, e:
 | 
|
1072  | 
+-                print "Unhandled error:\n%s" % cmdutil.exception_str(e)
 | 
|
1073  | 
++                print "Unhandled error:\n%s" % errors.exception_str(e)
 | 
|
1074  | 
+ 
 | 
|
1075  | 
+         elif suggestions.has_key(args[0]):
 | 
|
1076  | 
+             print suggestions[args[0]]
 | 
|
1077  | 
+@@ -1574,7 +1452,7 @@
 | 
|
1078  | 
+                 arg = line.split()[-1]
 | 
|
1079  | 
+             else:
 | 
|
1080  | 
+                 arg = ""
 | 
|
1081  | 
+-            iter = iter_munged_completions(iter, arg, text)
 | 
|
1082  | 
++            iter = cmdutil.iter_munged_completions(iter, arg, text)
 | 
|
1083  | 
+         except Exception, e:
 | 
|
1084  | 
+             print e
 | 
|
1085  | 
+         return list(iter)
 | 
|
1086  | 
+@@ -1604,10 +1482,11 @@
 | 
|
1087  | 
+                 else:
 | 
|
1088  | 
+                     arg = ""
 | 
|
1089  | 
+                 if arg.startswith("-"):
 | 
|
1090  | 
+-                    return list(iter_munged_completions(iter, arg, text))
 | 
|
1091  | 
++                    return list(cmdutil.iter_munged_completions(iter, arg, 
 | 
|
1092  | 
++                                                                text))
 | 
|
1093  | 
+                 else:
 | 
|
1094  | 
+-                    return list(iter_munged_completions(
 | 
|
1095  | 
+-                        iter_file_completions(arg), arg, text))
 | 
|
1096  | 
++                    return list(cmdutil.iter_munged_completions(
 | 
|
1097  | 
++                        cmdutil.iter_file_completions(arg), arg, text))
 | 
|
1098  | 
+ 
 | 
|
1099  | 
+ 
 | 
|
1100  | 
+             elif cmd == "cd":
 | 
|
1101  | 
+@@ -1615,13 +1494,13 @@
 | 
|
1102  | 
+                     arg = args.split()[-1]
 | 
|
1103  | 
+                 else:
 | 
|
1104  | 
+                     arg = ""
 | 
|
1105  | 
+-                iter = iter_dir_completions(arg)
 | 
|
1106  | 
+-                iter = iter_munged_completions(iter, arg, text)
 | 
|
1107  | 
++                iter = cmdutil.iter_dir_completions(arg)
 | 
|
1108  | 
++                iter = cmdutil.iter_munged_completions(iter, arg, text)
 | 
|
1109  | 
+                 return list(iter)
 | 
|
1110  | 
+             elif len(args)>0:
 | 
|
1111  | 
+                 arg = args.split()[-1]
 | 
|
1112  | 
+-                return list(iter_munged_completions(iter_file_completions(arg),
 | 
|
1113  | 
+-                                                    arg, text))
 | 
|
1114  | 
++                iter = cmdutil.iter_file_completions(arg)
 | 
|
1115  | 
++                return list(cmdutil.iter_munged_completions(iter, arg, text))
 | 
|
1116  | 
+             else:
 | 
|
1117  | 
+                 return self.completenames(text, line, begidx, endidx)
 | 
|
1118  | 
+         except Exception, e:
 | 
|
1119  | 
+@@ -1636,44 +1515,8 @@
 | 
|
1120  | 
+             yield entry
 | 
|
1121  | 
+ 
 | 
|
1122  | 
+ 
 | 
|
1123  | 
+-def iter_file_completions(arg, only_dirs = False):
 | 
|
1124  | 
+-    """Generate an iterator that iterates through filename completions.
 | 
|
1125  | 
+-
 | 
|
1126  | 
+-    :param arg: The filename fragment to match
 | 
|
1127  | 
+-    :type arg: str
 | 
|
1128  | 
+-    :param only_dirs: If true, match only directories
 | 
|
1129  | 
+-    :type only_dirs: bool
 | 
|
1130  | 
+-    """
 | 
|
1131  | 
+-    cwd = os.getcwd()
 | 
|
1132  | 
+-    if cwd != "/":
 | 
|
1133  | 
+-        extras = [".", ".."]
 | 
|
1134  | 
+-    else:
 | 
|
1135  | 
+-        extras = []
 | 
|
1136  | 
+-    (dir, file) = os.path.split(arg)
 | 
|
1137  | 
+-    if dir != "":
 | 
|
1138  | 
+-        listingdir = os.path.expanduser(dir)
 | 
|
1139  | 
+-    else:
 | 
|
1140  | 
+-        listingdir = cwd
 | 
|
1141  | 
+-    for file in cmdutil.iter_combine([os.listdir(listingdir), extras]):
 | 
|
1142  | 
+-        if dir != "":
 | 
|
1143  | 
+-            userfile = dir+'/'+file
 | 
|
1144  | 
+-        else:
 | 
|
1145  | 
+-            userfile = file
 | 
|
1146  | 
+-        if userfile.startswith(arg):
 | 
|
1147  | 
+-            if os.path.isdir(listingdir+'/'+file):
 | 
|
1148  | 
+-                userfile+='/'
 | 
|
1149  | 
+-                yield userfile
 | 
|
1150  | 
+-            elif not only_dirs:
 | 
|
1151  | 
+-                yield userfile
 | 
|
1152  | 
+-
 | 
|
1153  | 
+-def iter_munged_completions(iter, arg, text):
 | 
|
1154  | 
+-    for completion in iter:
 | 
|
1155  | 
+-        completion = str(completion)
 | 
|
1156  | 
+-        if completion.startswith(arg):
 | 
|
1157  | 
+-            yield completion[len(arg)-len(text):]
 | 
|
1158  | 
+-
 | 
|
1159  | 
+ def iter_source_file_completions(tree, arg):
 | 
|
1160  | 
+-    treepath = cmdutil.tree_cwd(tree)
 | 
|
1161  | 
++    treepath = arch_compound.tree_cwd(tree)
 | 
|
1162  | 
+     if len(treepath) > 0:
 | 
|
1163  | 
+         dirs = [treepath]
 | 
|
1164  | 
+     else:
 | 
|
1165  | 
+@@ -1701,7 +1544,7 @@
 | 
|
1166  | 
+     :return: An iterator of all matching untagged files
 | 
|
1167  | 
+     :rtype: iterator of str
 | 
|
1168  | 
+     """
 | 
|
1169  | 
+-    treepath = cmdutil.tree_cwd(tree)
 | 
|
1170  | 
++    treepath = arch_compound.tree_cwd(tree)
 | 
|
1171  | 
+     if len(treepath) > 0:
 | 
|
1172  | 
+         dirs = [treepath]
 | 
|
1173  | 
+     else:
 | 
|
1174  | 
+@@ -1743,8 +1586,8 @@
 | 
|
1175  | 
+     :param arg: The prefix to match
 | 
|
1176  | 
+     :type arg: str
 | 
|
1177  | 
+     """
 | 
|
1178  | 
+-    treepath = cmdutil.tree_cwd(tree)
 | 
|
1179  | 
+-    tmpdir = cmdutil.tmpdir()
 | 
|
1180  | 
++    treepath = arch_compound.tree_cwd(tree)
 | 
|
1181  | 
++    tmpdir = util.tmpdir()
 | 
|
1182  | 
+     changeset = tmpdir+"/changeset"
 | 
|
1183  | 
+     completions = []
 | 
|
1184  | 
+     revision = cmdutil.determine_revision_tree(tree)
 | 
|
1185  | 
+@@ -1756,14 +1599,6 @@
 | 
|
1186  | 
+     shutil.rmtree(tmpdir)
 | 
|
1187  | 
+     return completions
 | 
|
1188  | 
+ 
 | 
|
1189  | 
+-def iter_dir_completions(arg):
 | 
|
1190  | 
+-    """Generate an iterator that iterates through directory name completions.
 | 
|
1191  | 
+-
 | 
|
1192  | 
+-    :param arg: The directory name fragment to match
 | 
|
1193  | 
+-    :type arg: str
 | 
|
1194  | 
+-    """
 | 
|
1195  | 
+-    return iter_file_completions(arg, True)
 | 
|
1196  | 
+-
 | 
|
1197  | 
+ class Shell(BaseCommand):
 | 
|
1198  | 
+     def __init__(self):
 | 
|
1199  | 
+         self.description = "Runs Fai as a shell"
 | 
|
1200  | 
+@@ -1795,7 +1630,11 @@
 | 
|
1201  | 
+         parser=self.get_parser()
 | 
|
1202  | 
+         (options, args) = parser.parse_args(cmdargs)
 | 
|
1203  | 
+ 
 | 
|
1204  | 
+-        tree = arch.tree_root()
 | 
|
1205  | 
++        try:
 | 
|
1206  | 
++            tree = arch.tree_root()
 | 
|
1207  | 
++        except arch.errors.TreeRootError, e:
 | 
|
1208  | 
++            raise pylon.errors.CommandFailedWrapper(e)
 | 
|
1209  | 
++            
 | 
|
1210  | 
+ 
 | 
|
1211  | 
+         if (len(args) == 0) == (options.untagged == False):
 | 
|
1212  | 
+             raise cmdutil.GetHelp
 | 
|
1213  | 
+@@ -1809,13 +1648,22 @@
 | 
|
1214  | 
+         if options.id_type == "tagline":
 | 
|
1215  | 
+             if method != "tagline":
 | 
|
1216  | 
+                 if not cmdutil.prompt("Tagline in other tree"):
 | 
|
1217  | 
+-                    if method == "explicit":
 | 
|
1218  | 
+-                        options.id_type == explicit
 | 
|
1219  | 
++                    if method == "explicit" or method == "implicit":
 | 
|
1220  | 
++                        options.id_type == method
 | 
|
1221  | 
+                     else:
 | 
|
1222  | 
+                         print "add-id not supported for \"%s\" tagging method"\
 | 
|
1223  | 
+                             % method 
 | 
|
1224  | 
+                         return
 | 
|
1225  | 
+         
 | 
|
1226  | 
++        elif options.id_type == "implicit":
 | 
|
1227  | 
++            if method != "implicit":
 | 
|
1228  | 
++                if not cmdutil.prompt("Implicit in other tree"):
 | 
|
1229  | 
++                    if method == "explicit" or method == "tagline":
 | 
|
1230  | 
++                        options.id_type == method
 | 
|
1231  | 
++                    else:
 | 
|
1232  | 
++                        print "add-id not supported for \"%s\" tagging method"\
 | 
|
1233  | 
++                            % method 
 | 
|
1234  | 
++                        return
 | 
|
1235  | 
+         elif options.id_type == "explicit":
 | 
|
1236  | 
+             if method != "tagline" and method != explicit:
 | 
|
1237  | 
+                 if not prompt("Explicit in other tree"):
 | 
|
1238  | 
+@@ -1824,7 +1672,8 @@
 | 
|
1239  | 
+                     return
 | 
|
1240  | 
+         
 | 
|
1241  | 
+         if options.id_type == "auto":
 | 
|
1242  | 
+-            if method != "tagline" and method != "explicit":
 | 
|
1243  | 
++            if method != "tagline" and method != "explicit" \
 | 
|
1244  | 
++                and method !="implicit":
 | 
|
1245  | 
+                 print "add-id not supported for \"%s\" tagging method" % method
 | 
|
1246  | 
+                 return
 | 
|
1247  | 
+             else:
 | 
|
1248  | 
+@@ -1852,10 +1701,12 @@
 | 
|
1249  | 
+             previous_files.extend(files)
 | 
|
1250  | 
+             if id_type == "explicit":
 | 
|
1251  | 
+                 cmdutil.add_id(files)
 | 
|
1252  | 
+-            elif id_type == "tagline":
 | 
|
1253  | 
++            elif id_type == "tagline" or id_type == "implicit":
 | 
|
1254  | 
+                 for file in files:
 | 
|
1255  | 
+                     try:
 | 
|
1256  | 
+-                        cmdutil.add_tagline_or_explicit_id(file)
 | 
|
1257  | 
++                        implicit = (id_type == "implicit")
 | 
|
1258  | 
++                        cmdutil.add_tagline_or_explicit_id(file, False,
 | 
|
1259  | 
++                                                           implicit)
 | 
|
1260  | 
+                     except cmdutil.AlreadyTagged:
 | 
|
1261  | 
+                         print "\"%s\" already has a tagline." % file
 | 
|
1262  | 
+                     except cmdutil.NoCommentSyntax:
 | 
|
1263  | 
+@@ -1888,6 +1739,9 @@
 | 
|
1264  | 
+         parser.add_option("--tagline", action="store_const", 
 | 
|
1265  | 
+                          const="tagline", dest="id_type", 
 | 
|
1266  | 
+                          help="Use a tagline id")
 | 
|
1267  | 
++        parser.add_option("--implicit", action="store_const", 
 | 
|
1268  | 
++                         const="implicit", dest="id_type", 
 | 
|
1269  | 
++                         help="Use an implicit id (deprecated)")
 | 
|
1270  | 
+         parser.add_option("--untagged", action="store_true", 
 | 
|
1271  | 
+                          dest="untagged", default=False, 
 | 
|
1272  | 
+                          help="tag all untagged files")
 | 
|
1273  | 
+@@ -1926,27 +1780,7 @@
 | 
|
1274  | 
+     def get_completer(self, arg, index):
 | 
|
1275  | 
+         if self.tree is None:
 | 
|
1276  | 
+             raise arch.errors.TreeRootError
 | 
|
1277  | 
+-        completions = list(ancillary.iter_partners(self.tree, 
 | 
|
1278  | 
+-                                                   self.tree.tree_version))
 | 
|
1279  | 
+-        if len(completions) == 0:
 | 
|
1280  | 
+-            completions = list(self.tree.iter_log_versions())
 | 
|
1281  | 
+-
 | 
|
1282  | 
+-        aliases = []
 | 
|
1283  | 
+-        try:
 | 
|
1284  | 
+-            for completion in completions:
 | 
|
1285  | 
+-                alias = ancillary.compact_alias(str(completion), self.tree)
 | 
|
1286  | 
+-                if alias:
 | 
|
1287  | 
+-                    aliases.extend(alias)
 | 
|
1288  | 
+-
 | 
|
1289  | 
+-            for completion in completions:
 | 
|
1290  | 
+-                if completion.archive == self.tree.tree_version.archive:
 | 
|
1291  | 
+-                    aliases.append(completion.nonarch)
 | 
|
1292  | 
+-
 | 
|
1293  | 
+-        except Exception, e:
 | 
|
1294  | 
+-            print e
 | 
|
1295  | 
+-            
 | 
|
1296  | 
+-        completions.extend(aliases)
 | 
|
1297  | 
+-        return completions
 | 
|
1298  | 
++        return cmdutil.merge_completions(self.tree, arg, index)
 | 
|
1299  | 
+ 
 | 
|
1300  | 
+     def do_command(self, cmdargs):
 | 
|
1301  | 
+         """
 | 
|
1302  | 
+@@ -1961,7 +1795,7 @@
 | 
|
1303  | 
+         
 | 
|
1304  | 
+         if self.tree is None:
 | 
|
1305  | 
+             raise arch.errors.TreeRootError(os.getcwd())
 | 
|
1306  | 
+-        if cmdutil.has_changed(self.tree.tree_version):
 | 
|
1307  | 
++        if cmdutil.has_changed(ancillary.comp_revision(self.tree)):
 | 
|
1308  | 
+             raise UncommittedChanges(self.tree)
 | 
|
1309  | 
+ 
 | 
|
1310  | 
+         if len(args) > 0:
 | 
|
1311  | 
+@@ -2027,14 +1861,14 @@
 | 
|
1312  | 
+         :type other_revision: `arch.Revision`
 | 
|
1313  | 
+         :return: 0 if the merge was skipped, 1 if it was applied
 | 
|
1314  | 
+         """
 | 
|
1315  | 
+-        other_tree = cmdutil.find_or_make_local_revision(other_revision)
 | 
|
1316  | 
++        other_tree = arch_compound.find_or_make_local_revision(other_revision)
 | 
|
1317  | 
+         try:
 | 
|
1318  | 
+             if action == "native-merge":
 | 
|
1319  | 
+-                ancestor = cmdutil.merge_ancestor2(self.tree, other_tree, 
 | 
|
1320  | 
+-                                                   other_revision)
 | 
|
1321  | 
++                ancestor = arch_compound.merge_ancestor2(self.tree, other_tree, 
 | 
|
1322  | 
++                                                         other_revision)
 | 
|
1323  | 
+             elif action == "update":
 | 
|
1324  | 
+-                ancestor = cmdutil.tree_latest(self.tree, 
 | 
|
1325  | 
+-                                               other_revision.version)
 | 
|
1326  | 
++                ancestor = arch_compound.tree_latest(self.tree, 
 | 
|
1327  | 
++                                                     other_revision.version)
 | 
|
1328  | 
+         except CantDetermineRevision, e:
 | 
|
1329  | 
+             raise CommandFailedWrapper(e)
 | 
|
1330  | 
+         cmdutil.colorize(arch.Chatter("* Found common ancestor %s" % ancestor))
 | 
|
1331  | 
+@@ -2104,7 +1938,10 @@
 | 
|
1332  | 
+         if self.tree is None:
 | 
|
1333  | 
+             raise arch.errors.TreeRootError
 | 
|
1334  | 
+ 
 | 
|
1335  | 
+-        edit_log(self.tree)
 | 
|
1336  | 
++        try:
 | 
|
1337  | 
++            edit_log(self.tree, self.tree.tree_version)
 | 
|
1338  | 
++        except pylon.errors.NoEditorSpecified, e:
 | 
|
1339  | 
++            raise pylon.errors.CommandFailedWrapper(e)
 | 
|
1340  | 
+ 
 | 
|
1341  | 
+     def get_parser(self):
 | 
|
1342  | 
+         """
 | 
|
1343  | 
+@@ -2132,7 +1969,7 @@
 | 
|
1344  | 
+         """
 | 
|
1345  | 
+         return
 | 
|
1346  | 
+ 
 | 
|
1347  | 
+-def edit_log(tree):
 | 
|
1348  | 
++def edit_log(tree, version):
 | 
|
1349  | 
+     """Makes and edits the log for a tree.  Does all kinds of fancy things
 | 
|
1350  | 
+     like log templates and merge summaries and log-for-merge
 | 
|
1351  | 
+     
 | 
|
1352  | 
+@@ -2141,28 +1978,29 @@
 | 
|
1353  | 
+     """
 | 
|
1354  | 
+     #ensure we have an editor before preparing the log
 | 
|
1355  | 
+     cmdutil.find_editor()
 | 
|
1356  | 
+-    log = tree.log_message(create=False)
 | 
|
1357  | 
++    log = tree.log_message(create=False, version=version)
 | 
|
1358  | 
+     log_is_new = False
 | 
|
1359  | 
+     if log is None or cmdutil.prompt("Overwrite log"):
 | 
|
1360  | 
+         if log is not None:
 | 
|
1361  | 
+            os.remove(log.name)
 | 
|
1362  | 
+-        log = tree.log_message(create=True)
 | 
|
1363  | 
++        log = tree.log_message(create=True, version=version)
 | 
|
1364  | 
+         log_is_new = True
 | 
|
1365  | 
+         tmplog = log.name
 | 
|
1366  | 
+-        template = tree+"/{arch}/=log-template"
 | 
|
1367  | 
+-        if not os.path.exists(template):
 | 
|
1368  | 
+-            template = os.path.expanduser("~/.arch-params/=log-template")
 | 
|
1369  | 
+-            if not os.path.exists(template):
 | 
|
1370  | 
+-                template = None
 | 
|
1371  | 
++        template = pylon.log_template_path(tree)
 | 
|
1372  | 
+         if template:
 | 
|
1373  | 
+             shutil.copyfile(template, tmplog)
 | 
|
1374  | 
+-        
 | 
|
1375  | 
+-        new_merges = list(cmdutil.iter_new_merges(tree, 
 | 
|
1376  | 
+-                                                  tree.tree_version))
 | 
|
1377  | 
+-        log["Summary"] = merge_summary(new_merges, tree.tree_version)
 | 
|
1378  | 
++        comp_version = ancillary.comp_revision(tree).version
 | 
|
1379  | 
++        new_merges = cmdutil.iter_new_merges(tree, comp_version)
 | 
|
1380  | 
++        new_merges = cmdutil.direct_merges(new_merges)
 | 
|
1381  | 
++        log["Summary"] = pylon.merge_summary(new_merges, 
 | 
|
1382  | 
++                                         version)
 | 
|
1383  | 
+         if len(new_merges) > 0:   
 | 
|
1384  | 
+             if cmdutil.prompt("Log for merge"):
 | 
|
1385  | 
+-                mergestuff = cmdutil.log_for_merge(tree)
 | 
|
1386  | 
++                if cmdutil.prompt("changelog for merge"):
 | 
|
1387  | 
++                    mergestuff = "Patches applied:\n"
 | 
|
1388  | 
++                    mergestuff += pylon.changelog_for_merge(new_merges)
 | 
|
1389  | 
++                else:
 | 
|
1390  | 
++                    mergestuff = cmdutil.log_for_merge(tree, comp_version)
 | 
|
1391  | 
+                 log.description += mergestuff
 | 
|
1392  | 
+         log.save()
 | 
|
1393  | 
+     try:
 | 
|
1394  | 
+@@ -2172,29 +2010,6 @@
 | 
|
1395  | 
+             os.remove(log.name)
 | 
|
1396  | 
+         raise
 | 
|
1397  | 
+ 
 | 
|
1398  | 
+-def merge_summary(new_merges, tree_version):
 | 
|
1399  | 
+-    if len(new_merges) == 0:
 | 
|
1400  | 
+-        return ""
 | 
|
1401  | 
+-    if len(new_merges) == 1:
 | 
|
1402  | 
+-        summary = new_merges[0].summary
 | 
|
1403  | 
+-    else:
 | 
|
1404  | 
+-        summary = "Merge"
 | 
|
1405  | 
+-
 | 
|
1406  | 
+-    credits = []
 | 
|
1407  | 
+-    for merge in new_merges:
 | 
|
1408  | 
+-        if arch.my_id() != merge.creator:
 | 
|
1409  | 
+-            name = re.sub("<.*>", "", merge.creator).rstrip(" ");
 | 
|
1410  | 
+-            if not name in credits:
 | 
|
1411  | 
+-                credits.append(name)
 | 
|
1412  | 
+-        else:
 | 
|
1413  | 
+-            version = merge.revision.version
 | 
|
1414  | 
+-            if version.archive == tree_version.archive:
 | 
|
1415  | 
+-                if not version.nonarch in credits:
 | 
|
1416  | 
+-                    credits.append(version.nonarch)
 | 
|
1417  | 
+-            elif not str(version) in credits:
 | 
|
1418  | 
+-                credits.append(str(version))
 | 
|
1419  | 
+-
 | 
|
1420  | 
+-    return ("%s (%s)") % (summary, ", ".join(credits))
 | 
|
1421  | 
+ 
 | 
|
1422  | 
+ class MirrorArchive(BaseCommand):
 | 
|
1423  | 
+     """
 | 
|
1424  | 
+@@ -2268,31 +2083,73 @@
 | 
|
1425  | 
+ 
 | 
|
1426  | 
+ Use "alias" to list available (user and automatic) aliases."""
 | 
|
1427  | 
+ 
 | 
|
1428  | 
++auto_alias = [
 | 
|
1429  | 
++"acur", 
 | 
|
1430  | 
++"The latest revision in the archive of the tree-version.  You can specify \
 | 
|
1431  | 
++a different version like so: acur:foo--bar--0 (aliases can be used)",
 | 
|
1432  | 
++"tcur",
 | 
|
1433  | 
++"""(tree current) The latest revision in the tree of the tree-version. \
 | 
|
1434  | 
++You can specify a different version like so: tcur:foo--bar--0 (aliases can be \
 | 
|
1435  | 
++used).""",
 | 
|
1436  | 
++"tprev" , 
 | 
|
1437  | 
++"""(tree previous) The previous revision in the tree of the tree-version.  To \
 | 
|
1438  | 
++specify an older revision, use a number, e.g. "tprev:4" """,
 | 
|
1439  | 
++"tanc" , 
 | 
|
1440  | 
++"""(tree ancestor) The ancestor revision of the tree To specify an older \
 | 
|
1441  | 
++revision, use a number, e.g. "tanc:4".""",
 | 
|
1442  | 
++"tdate" , 
 | 
|
1443  | 
++"""(tree date) The latest revision from a given date, e.g. "tdate:July 6".""",
 | 
|
1444  | 
++"tmod" , 
 | 
|
1445  | 
++""" (tree modified) The latest revision to modify a given file, e.g. \
 | 
|
1446  | 
++"tmod:engine.cpp" or "tmod:engine.cpp:16".""",
 | 
|
1447  | 
++"ttag" , 
 | 
|
1448  | 
++"""(tree tag) The revision that was tagged into the current tree revision, \
 | 
|
1449  | 
++according to the tree""",
 | 
|
1450  | 
++"tagcur", 
 | 
|
1451  | 
++"""(tag current) The latest revision of the version that the current tree \
 | 
|
1452  | 
++was tagged from.""",
 | 
|
1453  | 
++"mergeanc" , 
 | 
|
1454  | 
++"""The common ancestor of the current tree and the specified revision. \
 | 
|
1455  | 
++Defaults to the first partner-version's latest revision or to tagcur.""",
 | 
|
1456  | 
++]
 | 
|
1457  | 
++
 | 
|
1458  | 
++
 | 
|
1459  | 
++def is_auto_alias(name):
 | 
|
1460  | 
++    """Determine whether a name is an auto alias name
 | 
|
1461  | 
++
 | 
|
1462  | 
++    :param name: the name to check
 | 
|
1463  | 
++    :type name: str
 | 
|
1464  | 
++    :return: True if the name is an auto alias, false if not
 | 
|
1465  | 
++    :rtype: bool
 | 
|
1466  | 
++    """
 | 
|
1467  | 
++    return name in [f for (f, v) in pylon.util.iter_pairs(auto_alias)]
 | 
|
1468  | 
++
 | 
|
1469  | 
++
 | 
|
1470  | 
++def display_def(iter, wrap = 80):
 | 
|
1471  | 
++    """Display a list of definitions
 | 
|
1472  | 
++
 | 
|
1473  | 
++    :param iter: iter of name, definition pairs
 | 
|
1474  | 
++    :type iter: iter of (str, str)
 | 
|
1475  | 
++    :param wrap: The width for text wrapping
 | 
|
1476  | 
++    :type wrap: int
 | 
|
1477  | 
++    """
 | 
|
1478  | 
++    vals = list(iter)
 | 
|
1479  | 
++    maxlen = 0
 | 
|
1480  | 
++    for (key, value) in vals:
 | 
|
1481  | 
++        if len(key) > maxlen:
 | 
|
1482  | 
++            maxlen = len(key)
 | 
|
1483  | 
++    for (key, value) in vals:
 | 
|
1484  | 
++        tw=textwrap.TextWrapper(width=wrap, 
 | 
|
1485  | 
++                                initial_indent=key.rjust(maxlen)+" : ",
 | 
|
1486  | 
++                                subsequent_indent="".rjust(maxlen+3))
 | 
|
1487  | 
++        print tw.fill(value)
 | 
|
1488  | 
++
 | 
|
1489  | 
++
 | 
|
1490  | 
+ def help_aliases(tree):
 | 
|
1491  | 
+-    print """Auto-generated aliases
 | 
|
1492  | 
+- acur : The latest revision in the archive of the tree-version.  You can specfy
 | 
|
1493  | 
+-        a different version like so: acur:foo--bar--0 (aliases can be used)
 | 
|
1494  | 
+- tcur : (tree current) The latest revision in the tree of the tree-version.
 | 
|
1495  | 
+-        You can specify a different version like so: tcur:foo--bar--0 (aliases
 | 
|
1496  | 
+-        can be used).
 | 
|
1497  | 
+-tprev : (tree previous) The previous revision in the tree of the tree-version.
 | 
|
1498  | 
+-        To specify an older revision, use a number, e.g. "tprev:4"
 | 
|
1499  | 
+- tanc : (tree ancestor) The ancestor revision of the tree
 | 
|
1500  | 
+-        To specify an older revision, use a number, e.g. "tanc:4"
 | 
|
1501  | 
+-tdate : (tree date) The latest revision from a given date (e.g. "tdate:July 6")
 | 
|
1502  | 
+- tmod : (tree modified) The latest revision to modify a given file 
 | 
|
1503  | 
+-        (e.g. "tmod:engine.cpp" or "tmod:engine.cpp:16")
 | 
|
1504  | 
+- ttag : (tree tag) The revision that was tagged into the current tree revision,
 | 
|
1505  | 
+-        according to the tree.
 | 
|
1506  | 
+-tagcur: (tag current) The latest revision of the version that the current tree
 | 
|
1507  | 
+-        was tagged from.
 | 
|
1508  | 
+-mergeanc : The common ancestor of the current tree and the specified revision.
 | 
|
1509  | 
+-        Defaults to the first partner-version's latest revision or to tagcur.
 | 
|
1510  | 
+-   """
 | 
|
1511  | 
++    print """Auto-generated aliases"""
 | 
|
1512  | 
++    display_def(pylon.util.iter_pairs(auto_alias))
 | 
|
1513  | 
+     print "User aliases"
 | 
|
1514  | 
+-    for parts in ancillary.iter_all_alias(tree):
 | 
|
1515  | 
+-        print parts[0].rjust(10)+" : "+parts[1]
 | 
|
1516  | 
+-
 | 
|
1517  | 
++    display_def(ancillary.iter_all_alias(tree))
 | 
|
1518  | 
+ 
 | 
|
1519  | 
+ class Inventory(BaseCommand):
 | 
|
1520  | 
+     """List the status of files in the tree"""
 | 
|
1521  | 
+@@ -2428,6 +2285,11 @@
 | 
|
1522  | 
+         except cmdutil.ForbiddenAliasSyntax, e:
 | 
|
1523  | 
+             raise CommandFailedWrapper(e)
 | 
|
1524  | 
+ 
 | 
|
1525  | 
++    def no_prefix(self, alias):
 | 
|
1526  | 
++        if alias.startswith("^"):
 | 
|
1527  | 
++            alias = alias[1:]
 | 
|
1528  | 
++        return alias
 | 
|
1529  | 
++        
 | 
|
1530  | 
+     def arg_dispatch(self, args, options):
 | 
|
1531  | 
+         """Add, modify, or list aliases, depending on number of arguments
 | 
|
1532  | 
+ 
 | 
|
1533  | 
+@@ -2438,15 +2300,20 @@
 | 
|
1534  | 
+         if len(args) == 0:
 | 
|
1535  | 
+             help_aliases(self.tree)
 | 
|
1536  | 
+             return
 | 
|
1537  | 
+-        elif len(args) == 1:
 | 
|
1538  | 
+-            self.print_alias(args[0])
 | 
|
1539  | 
+-        elif (len(args)) == 2:
 | 
|
1540  | 
+-            self.add(args[0], args[1], options)
 | 
|
1541  | 
+         else:
 | 
|
1542  | 
+-            raise cmdutil.GetHelp
 | 
|
1543  | 
++            alias = self.no_prefix(args[0])
 | 
|
1544  | 
++            if len(args) == 1:
 | 
|
1545  | 
++                self.print_alias(alias)
 | 
|
1546  | 
++            elif (len(args)) == 2:
 | 
|
1547  | 
++                self.add(alias, args[1], options)
 | 
|
1548  | 
++            else:
 | 
|
1549  | 
++                raise cmdutil.GetHelp
 | 
|
1550  | 
+ 
 | 
|
1551  | 
+     def print_alias(self, alias):
 | 
|
1552  | 
+         answer = None
 | 
|
1553  | 
++        if is_auto_alias(alias):
 | 
|
1554  | 
++            raise pylon.errors.IsAutoAlias(alias, "\"%s\" is an auto alias."
 | 
|
1555  | 
++                "  Use \"revision\" to expand auto aliases." % alias)
 | 
|
1556  | 
+         for pair in ancillary.iter_all_alias(self.tree):
 | 
|
1557  | 
+             if pair[0] == alias:
 | 
|
1558  | 
+                 answer = pair[1]
 | 
|
1559  | 
+@@ -2464,6 +2331,8 @@
 | 
|
1560  | 
+         :type expansion: str
 | 
|
1561  | 
+         :param options: The commandline options
 | 
|
1562  | 
+         """
 | 
|
1563  | 
++        if is_auto_alias(alias):
 | 
|
1564  | 
++            raise IsAutoAlias(alias)
 | 
|
1565  | 
+         newlist = ""
 | 
|
1566  | 
+         written = False
 | 
|
1567  | 
+         new_line = "%s=%s\n" % (alias, cmdutil.expand_alias(expansion, 
 | 
|
1568  | 
+@@ -2490,14 +2359,17 @@
 | 
|
1569  | 
+         deleted = False
 | 
|
1570  | 
+         if len(args) != 1:
 | 
|
1571  | 
+             raise cmdutil.GetHelp
 | 
|
1572  | 
++        alias = self.no_prefix(args[0])
 | 
|
1573  | 
++        if is_auto_alias(alias):
 | 
|
1574  | 
++            raise IsAutoAlias(alias)
 | 
|
1575  | 
+         newlist = ""
 | 
|
1576  | 
+         for pair in self.get_iterator(options):
 | 
|
1577  | 
+-            if pair[0] != args[0]:
 | 
|
1578  | 
++            if pair[0] != alias:
 | 
|
1579  | 
+                 newlist+="%s=%s\n" % (pair[0], pair[1])
 | 
|
1580  | 
+             else:
 | 
|
1581  | 
+                 deleted = True
 | 
|
1582  | 
+         if not deleted:
 | 
|
1583  | 
+-            raise errors.NoSuchAlias(args[0])
 | 
|
1584  | 
++            raise errors.NoSuchAlias(alias)
 | 
|
1585  | 
+         self.write_aliases(newlist, options)
 | 
|
1586  | 
+ 
 | 
|
1587  | 
+     def get_alias_file(self, options):
 | 
|
1588  | 
+@@ -2526,7 +2398,7 @@
 | 
|
1589  | 
+         :param options: The commandline options
 | 
|
1590  | 
+         """
 | 
|
1591  | 
+         filename = os.path.expanduser(self.get_alias_file(options))
 | 
|
1592  | 
+-        file = cmdutil.NewFileVersion(filename)
 | 
|
1593  | 
++        file = util.NewFileVersion(filename)
 | 
|
1594  | 
+         file.write(newlist)
 | 
|
1595  | 
+         file.commit()
 | 
|
1596  | 
+ 
 | 
|
1597  | 
+@@ -2588,10 +2460,13 @@
 | 
|
1598  | 
+         :param cmdargs: The commandline arguments
 | 
|
1599  | 
+         :type cmdargs: list of str
 | 
|
1600  | 
+         """
 | 
|
1601  | 
+-        cmdutil.find_editor()
 | 
|
1602  | 
+         parser = self.get_parser()
 | 
|
1603  | 
+         (options, args) = parser.parse_args(cmdargs)
 | 
|
1604  | 
+         try:
 | 
|
1605  | 
++            cmdutil.find_editor()
 | 
|
1606  | 
++        except pylon.errors.NoEditorSpecified, e:
 | 
|
1607  | 
++            raise pylon.errors.CommandFailedWrapper(e)
 | 
|
1608  | 
++        try:
 | 
|
1609  | 
+             self.tree=arch.tree_root()
 | 
|
1610  | 
+         except:
 | 
|
1611  | 
+             self.tree=None
 | 
|
1612  | 
+@@ -2655,7 +2530,7 @@
 | 
|
1613  | 
+             target_revision = cmdutil.determine_revision_arch(self.tree, 
 | 
|
1614  | 
+                                                               args[0])
 | 
|
1615  | 
+         else:
 | 
|
1616  | 
+-            target_revision = cmdutil.tree_latest(self.tree)
 | 
|
1617  | 
++            target_revision = arch_compound.tree_latest(self.tree)
 | 
|
1618  | 
+         if len(args) > 1:
 | 
|
1619  | 
+             merges = [ arch.Patchlog(cmdutil.determine_revision_arch(
 | 
|
1620  | 
+                        self.tree, f)) for f in args[1:] ]
 | 
|
1621  | 
+@@ -2711,7 +2586,7 @@
 | 
|
1622  | 
+ 
 | 
|
1623  | 
+         :param message: The message to send
 | 
|
1624  | 
+         :type message: `email.Message`"""
 | 
|
1625  | 
+-        server = smtplib.SMTP()
 | 
|
1626  | 
++        server = smtplib.SMTP("localhost")
 | 
|
1627  | 
+         server.sendmail(message['From'], message['To'], message.as_string())
 | 
|
1628  | 
+         server.quit()
 | 
|
1629  | 
+ 
 | 
|
1630  | 
+@@ -2763,6 +2638,22 @@
 | 
|
1631  | 
+ 'alias' : Alias,
 | 
|
1632  | 
+ 'request-merge': RequestMerge,
 | 
|
1633  | 
+ }
 | 
|
1634  | 
++
 | 
|
1635  | 
++def my_import(mod_name):
 | 
|
1636  | 
++    module = __import__(mod_name)
 | 
|
1637  | 
++    components = mod_name.split('.')
 | 
|
1638  | 
++    for comp in components[1:]:
 | 
|
1639  | 
++        module = getattr(module, comp)
 | 
|
1640  | 
++    return module
 | 
|
1641  | 
++
 | 
|
1642  | 
++def plugin(mod_name):
 | 
|
1643  | 
++    module = my_import(mod_name)
 | 
|
1644  | 
++    module.add_command(commands)
 | 
|
1645  | 
++
 | 
|
1646  | 
++for file in os.listdir(sys.path[0]+"/command"):
 | 
|
1647  | 
++    if len(file) > 3 and file[-3:] == ".py" and file != "__init__.py":
 | 
|
1648  | 
++        plugin("command."+file[:-3])
 | 
|
1649  | 
++
 | 
|
1650  | 
+ suggestions = {
 | 
|
1651  | 
+ 'apply-delta' : "Try \"apply-changes\".",
 | 
|
1652  | 
+ 'delta' : "To compare two revisions, use \"changes\".",
 | 
|
1653  | 
+@@ -2784,6 +2675,7 @@
 | 
|
1654  | 
+ 'tagline' : "Use add-id.  It uses taglines in tagline trees",
 | 
|
1655  | 
+ 'emlog' : "Use elog.  It automatically adds log-for-merge text, if any",
 | 
|
1656  | 
+ 'library-revisions' : "Use revisions --library",
 | 
|
1657  | 
+-'file-revert' : "Use revert FILE"
 | 
|
1658  | 
++'file-revert' : "Use revert FILE",
 | 
|
1659  | 
++'join-branch' : "Use replay --logs-only"
 | 
|
1660  | 
+ }
 | 
|
1661  | 
+ # arch-tag: 19d5739d-3708-486c-93ba-deecc3027fc7
 | 
|
1662  | 
||
1663  | 
*** added file 'testdata/insert_top.patch'
 | 
|
1664  | 
--- /dev/null 
 | 
|
1665  | 
+++ testdata/insert_top.patch 
 | 
|
1666  | 
@@ -0,0 +1,7 @@
 | 
|
1667  | 
+--- orig/pylon/patches.py
 | 
|
1668  | 
++++ mod/pylon/patches.py
 | 
|
1669  | 
+@@ -1,3 +1,4 @@
 | 
|
1670  | 
++#test
 | 
|
1671  | 
+ import util
 | 
|
1672  | 
+ import sys
 | 
|
1673  | 
+ class PatchSyntax(Exception):
 | 
|
1674  | 
||
1675  | 
*** added file 'testdata/mod'
 | 
|
1676  | 
--- /dev/null 
 | 
|
1677  | 
+++ testdata/mod 
 | 
|
1678  | 
@@ -0,0 +1,2681 @@
 | 
|
1679  | 
+# Copyright (C) 2004 Aaron Bentley
 | 
|
1680  | 
+# <aaron.bentley@utoronto.ca>
 | 
|
1681  | 
+#
 | 
|
1682  | 
+#    This program is free software; you can redistribute it and/or modify
 | 
|
1683  | 
+#    it under the terms of the GNU General Public License as published by
 | 
|
1684  | 
+#    the Free Software Foundation; either version 2 of the License, or
 | 
|
1685  | 
+#    (at your option) any later version.
 | 
|
1686  | 
+#
 | 
|
1687  | 
+#    This program is distributed in the hope that it will be useful,
 | 
|
1688  | 
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
|
1689  | 
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
|
1690  | 
+#    GNU General Public License for more details.
 | 
|
1691  | 
+#
 | 
|
1692  | 
+#    You should have received a copy of the GNU General Public License
 | 
|
1693  | 
+#    along with this program; if not, write to the Free Software
 | 
|
1694  | 
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
|
1695  | 
+
 | 
|
1696  | 
+import sys
 | 
|
1697  | 
+import arch
 | 
|
1698  | 
+import arch.util
 | 
|
1699  | 
+import arch.arch
 | 
|
1700  | 
+
 | 
|
1701  | 
+import pylon.errors
 | 
|
1702  | 
+from pylon.errors import *
 | 
|
1703  | 
+from pylon import errors
 | 
|
1704  | 
+from pylon import util
 | 
|
1705  | 
+from pylon import arch_core
 | 
|
1706  | 
+from pylon import arch_compound
 | 
|
1707  | 
+from pylon import ancillary
 | 
|
1708  | 
+from pylon import misc
 | 
|
1709  | 
+from pylon import paths 
 | 
|
1710  | 
+
 | 
|
1711  | 
+import abacmds
 | 
|
1712  | 
+import cmdutil
 | 
|
1713  | 
+import shutil
 | 
|
1714  | 
+import os
 | 
|
1715  | 
+import options
 | 
|
1716  | 
+import time
 | 
|
1717  | 
+import cmd
 | 
|
1718  | 
+import readline
 | 
|
1719  | 
+import re
 | 
|
1720  | 
+import string
 | 
|
1721  | 
+import terminal
 | 
|
1722  | 
+import email
 | 
|
1723  | 
+import smtplib
 | 
|
1724  | 
+import textwrap
 | 
|
1725  | 
+
 | 
|
1726  | 
+__docformat__ = "restructuredtext"
 | 
|
1727  | 
+__doc__ = "Implementation of user (sub) commands"
 | 
|
1728  | 
+commands = {}
 | 
|
1729  | 
+
 | 
|
1730  | 
+def find_command(cmd):
 | 
|
1731  | 
+    """
 | 
|
1732  | 
+    Return an instance of a command type.  Return None if the type isn't
 | 
|
1733  | 
+    registered.
 | 
|
1734  | 
+
 | 
|
1735  | 
+    :param cmd: the name of the command to look for
 | 
|
1736  | 
+    :type cmd: the type of the command
 | 
|
1737  | 
+    """
 | 
|
1738  | 
+    if commands.has_key(cmd):
 | 
|
1739  | 
+        return commands[cmd]()
 | 
|
1740  | 
+    else:
 | 
|
1741  | 
+        return None
 | 
|
1742  | 
+
 | 
|
1743  | 
+class BaseCommand:
 | 
|
1744  | 
+    def __call__(self, cmdline):
 | 
|
1745  | 
+        try:
 | 
|
1746  | 
+            self.do_command(cmdline.split())
 | 
|
1747  | 
+        except cmdutil.GetHelp, e:
 | 
|
1748  | 
+            self.help()
 | 
|
1749  | 
+        except Exception, e:
 | 
|
1750  | 
+            print e
 | 
|
1751  | 
+
 | 
|
1752  | 
+    def get_completer(index):
 | 
|
1753  | 
+        return None
 | 
|
1754  | 
+
 | 
|
1755  | 
+    def complete(self, args, text):
 | 
|
1756  | 
+        """
 | 
|
1757  | 
+        Returns a list of possible completions for the given text.
 | 
|
1758  | 
+
 | 
|
1759  | 
+        :param args: The complete list of arguments
 | 
|
1760  | 
+        :type args: List of str
 | 
|
1761  | 
+        :param text: text to complete (may be shorter than args[-1])
 | 
|
1762  | 
+        :type text: str
 | 
|
1763  | 
+        :rtype: list of str
 | 
|
1764  | 
+        """
 | 
|
1765  | 
+        matches = []
 | 
|
1766  | 
+        candidates = None
 | 
|
1767  | 
+
 | 
|
1768  | 
+        if len(args) > 0: 
 | 
|
1769  | 
+            realtext = args[-1]
 | 
|
1770  | 
+        else:
 | 
|
1771  | 
+            realtext = ""
 | 
|
1772  | 
+
 | 
|
1773  | 
+        try:
 | 
|
1774  | 
+            parser=self.get_parser()
 | 
|
1775  | 
+            if realtext.startswith('-'):
 | 
|
1776  | 
+                candidates = parser.iter_options()
 | 
|
1777  | 
+            else:
 | 
|
1778  | 
+                (options, parsed_args) = parser.parse_args(args)
 | 
|
1779  | 
+
 | 
|
1780  | 
+                if len (parsed_args) > 0:
 | 
|
1781  | 
+                    candidates = self.get_completer(parsed_args[-1], len(parsed_args) -1)
 | 
|
1782  | 
+                else:
 | 
|
1783  | 
+                    candidates = self.get_completer("", 0)
 | 
|
1784  | 
+        except:
 | 
|
1785  | 
+            pass
 | 
|
1786  | 
+        if candidates is None:
 | 
|
1787  | 
+            return
 | 
|
1788  | 
+        for candidate in candidates:
 | 
|
1789  | 
+            candidate = str(candidate)
 | 
|
1790  | 
+            if candidate.startswith(realtext):
 | 
|
1791  | 
+                matches.append(candidate[len(realtext)- len(text):])
 | 
|
1792  | 
+        return matches
 | 
|
1793  | 
+
 | 
|
1794  | 
+
 | 
|
1795  | 
+class Help(BaseCommand):
 | 
|
1796  | 
+    """
 | 
|
1797  | 
+    Lists commands, prints help messages.
 | 
|
1798  | 
+    """
 | 
|
1799  | 
+    def __init__(self):
 | 
|
1800  | 
+        self.description="Prints help mesages"
 | 
|
1801  | 
+        self.parser = None
 | 
|
1802  | 
+
 | 
|
1803  | 
+    def do_command(self, cmdargs):
 | 
|
1804  | 
+        """
 | 
|
1805  | 
+        Prints a help message.
 | 
|
1806  | 
+        """
 | 
|
1807  | 
+        options, args = self.get_parser().parse_args(cmdargs)
 | 
|
1808  | 
+        if len(args) > 1:
 | 
|
1809  | 
+            raise cmdutil.GetHelp
 | 
|
1810  | 
+
 | 
|
1811  | 
+        if options.native or options.suggestions or options.external:
 | 
|
1812  | 
+            native = options.native
 | 
|
1813  | 
+            suggestions = options.suggestions
 | 
|
1814  | 
+            external = options.external
 | 
|
1815  | 
+        else:
 | 
|
1816  | 
+            native = True
 | 
|
1817  | 
+            suggestions = False
 | 
|
1818  | 
+            external = True
 | 
|
1819  | 
+        
 | 
|
1820  | 
+        if len(args) == 0:
 | 
|
1821  | 
+            self.list_commands(native, suggestions, external)
 | 
|
1822  | 
+            return
 | 
|
1823  | 
+        elif len(args) == 1:
 | 
|
1824  | 
+            command_help(args[0])
 | 
|
1825  | 
+            return
 | 
|
1826  | 
+
 | 
|
1827  | 
+    def help(self):
 | 
|
1828  | 
+        self.get_parser().print_help()
 | 
|
1829  | 
+        print """
 | 
|
1830  | 
+If no command is specified, commands are listed.  If a command is
 | 
|
1831  | 
+specified, help for that command is listed.
 | 
|
1832  | 
+        """
 | 
|
1833  | 
+
 | 
|
1834  | 
+    def get_parser(self):
 | 
|
1835  | 
+        """
 | 
|
1836  | 
+        Returns the options parser to use for the "revision" command.
 | 
|
1837  | 
+
 | 
|
1838  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
1839  | 
+        """
 | 
|
1840  | 
+        if self.parser is not None:
 | 
|
1841  | 
+            return self.parser
 | 
|
1842  | 
+        parser=cmdutil.CmdOptionParser("fai help [command]")
 | 
|
1843  | 
+        parser.add_option("-n", "--native", action="store_true", 
 | 
|
1844  | 
+                         dest="native", help="Show native commands")
 | 
|
1845  | 
+        parser.add_option("-e", "--external", action="store_true", 
 | 
|
1846  | 
+                         dest="external", help="Show external commands")
 | 
|
1847  | 
+        parser.add_option("-s", "--suggest", action="store_true", 
 | 
|
1848  | 
+                         dest="suggestions", help="Show suggestions")
 | 
|
1849  | 
+        self.parser = parser
 | 
|
1850  | 
+        return parser 
 | 
|
1851  | 
+      
 | 
|
1852  | 
+    def list_commands(self, native=True, suggest=False, external=True):
 | 
|
1853  | 
+        """
 | 
|
1854  | 
+        Lists supported commands.
 | 
|
1855  | 
+
 | 
|
1856  | 
+        :param native: list native, python-based commands
 | 
|
1857  | 
+        :type native: bool
 | 
|
1858  | 
+        :param external: list external aba-style commands
 | 
|
1859  | 
+        :type external: bool
 | 
|
1860  | 
+        """
 | 
|
1861  | 
+        if native:
 | 
|
1862  | 
+            print "Native Fai commands"
 | 
|
1863  | 
+            keys=commands.keys()
 | 
|
1864  | 
+            keys.sort()
 | 
|
1865  | 
+            for k in keys:
 | 
|
1866  | 
+                space=""
 | 
|
1867  | 
+                for i in range(28-len(k)):
 | 
|
1868  | 
+                    space+=" "
 | 
|
1869  | 
+                print space+k+" : "+commands[k]().description
 | 
|
1870  | 
+            print
 | 
|
1871  | 
+        if suggest:
 | 
|
1872  | 
+            print "Unavailable commands and suggested alternatives"
 | 
|
1873  | 
+            key_list = suggestions.keys()
 | 
|
1874  | 
+            key_list.sort()
 | 
|
1875  | 
+            for key in key_list:
 | 
|
1876  | 
+                print "%28s : %s" % (key, suggestions[key])
 | 
|
1877  | 
+            print
 | 
|
1878  | 
+        if external:
 | 
|
1879  | 
+            fake_aba = abacmds.AbaCmds()
 | 
|
1880  | 
+            if (fake_aba.abadir == ""):
 | 
|
1881  | 
+                return
 | 
|
1882  | 
+            print "External commands"
 | 
|
1883  | 
+            fake_aba.list_commands()
 | 
|
1884  | 
+            print
 | 
|
1885  | 
+        if not suggest:
 | 
|
1886  | 
+            print "Use help --suggest to list alternatives to tla and aba"\
 | 
|
1887  | 
+                " commands."
 | 
|
1888  | 
+        if options.tla_fallthrough and (native or external):
 | 
|
1889  | 
+            print "Fai also supports tla commands."
 | 
|
1890  | 
+
 | 
|
1891  | 
+def command_help(cmd):
 | 
|
1892  | 
+    """
 | 
|
1893  | 
+    Prints help for a command.
 | 
|
1894  | 
+
 | 
|
1895  | 
+    :param cmd: The name of the command to print help for
 | 
|
1896  | 
+    :type cmd: str
 | 
|
1897  | 
+    """
 | 
|
1898  | 
+    fake_aba = abacmds.AbaCmds()
 | 
|
1899  | 
+    cmdobj = find_command(cmd)
 | 
|
1900  | 
+    if cmdobj != None:
 | 
|
1901  | 
+        cmdobj.help()
 | 
|
1902  | 
+    elif suggestions.has_key(cmd):
 | 
|
1903  | 
+        print "Not available\n" + suggestions[cmd]
 | 
|
1904  | 
+    else:
 | 
|
1905  | 
+        abacmd = fake_aba.is_command(cmd)
 | 
|
1906  | 
+        if abacmd:
 | 
|
1907  | 
+            abacmd.help()
 | 
|
1908  | 
+        else:
 | 
|
1909  | 
+            print "No help is available for \""+cmd+"\". Maybe try \"tla "+cmd+" -H\"?"
 | 
|
1910  | 
+
 | 
|
1911  | 
+
 | 
|
1912  | 
+
 | 
|
1913  | 
+class Changes(BaseCommand):
 | 
|
1914  | 
+    """
 | 
|
1915  | 
+    the "changes" command: lists differences between trees/revisions:
 | 
|
1916  | 
+    """
 | 
|
1917  | 
+    
 | 
|
1918  | 
+    def __init__(self):
 | 
|
1919  | 
+        self.description="Lists what files have changed in the project tree"
 | 
|
1920  | 
+
 | 
|
1921  | 
+    def get_completer(self, arg, index):
 | 
|
1922  | 
+        if index > 1:
 | 
|
1923  | 
+            return None
 | 
|
1924  | 
+        try:
 | 
|
1925  | 
+            tree = arch.tree_root()
 | 
|
1926  | 
+        except:
 | 
|
1927  | 
+            tree = None
 | 
|
1928  | 
+        return cmdutil.iter_revision_completions(arg, tree)
 | 
|
1929  | 
+    
 | 
|
1930  | 
+    def parse_commandline(self, cmdline):
 | 
|
1931  | 
+        """
 | 
|
1932  | 
+        Parse commandline arguments.  Raises cmdutil.GetHelp if help is needed.
 | 
|
1933  | 
+        
 | 
|
1934  | 
+        :param cmdline: A list of arguments to parse
 | 
|
1935  | 
+        :rtype: (options, Revision, Revision/WorkingTree)
 | 
|
1936  | 
+        """
 | 
|
1937  | 
+        parser=self.get_parser()
 | 
|
1938  | 
+        (options, args) = parser.parse_args(cmdline)
 | 
|
1939  | 
+        if len(args) > 2:
 | 
|
1940  | 
+            raise cmdutil.GetHelp
 | 
|
1941  | 
+
 | 
|
1942  | 
+        tree=arch.tree_root()
 | 
|
1943  | 
+        if len(args) == 0:
 | 
|
1944  | 
+            a_spec = ancillary.comp_revision(tree)
 | 
|
1945  | 
+        else:
 | 
|
1946  | 
+            a_spec = cmdutil.determine_revision_tree(tree, args[0])
 | 
|
1947  | 
+        cmdutil.ensure_archive_registered(a_spec.archive)
 | 
|
1948  | 
+        if len(args) == 2:
 | 
|
1949  | 
+            b_spec = cmdutil.determine_revision_tree(tree, args[1])
 | 
|
1950  | 
+            cmdutil.ensure_archive_registered(b_spec.archive)
 | 
|
1951  | 
+        else:
 | 
|
1952  | 
+            b_spec=tree
 | 
|
1953  | 
+        return options, a_spec, b_spec
 | 
|
1954  | 
+
 | 
|
1955  | 
+    def do_command(self, cmdargs):
 | 
|
1956  | 
+        """
 | 
|
1957  | 
+        Master function that perfoms the "changes" command.
 | 
|
1958  | 
+        """
 | 
|
1959  | 
+        try:
 | 
|
1960  | 
+            options, a_spec, b_spec = self.parse_commandline(cmdargs);
 | 
|
1961  | 
+        except cmdutil.CantDetermineRevision, e:
 | 
|
1962  | 
+            print e
 | 
|
1963  | 
+            return
 | 
|
1964  | 
+        except arch.errors.TreeRootError, e:
 | 
|
1965  | 
+            print e
 | 
|
1966  | 
+            return
 | 
|
1967  | 
+        if options.changeset:
 | 
|
1968  | 
+            changeset=options.changeset
 | 
|
1969  | 
+            tmpdir = None
 | 
|
1970  | 
+        else:
 | 
|
1971  | 
+            tmpdir=util.tmpdir()
 | 
|
1972  | 
+            changeset=tmpdir+"/changeset"
 | 
|
1973  | 
+        try:
 | 
|
1974  | 
+            delta=arch.iter_delta(a_spec, b_spec, changeset)
 | 
|
1975  | 
+            try:
 | 
|
1976  | 
+                for line in delta:
 | 
|
1977  | 
+                    if cmdutil.chattermatch(line, "changeset:"):
 | 
|
1978  | 
+                        pass
 | 
|
1979  | 
+                    else:
 | 
|
1980  | 
+                        cmdutil.colorize(line, options.suppress_chatter)
 | 
|
1981  | 
+            except arch.util.ExecProblem, e:
 | 
|
1982  | 
+                if e.proc.error and e.proc.error.startswith(
 | 
|
1983  | 
+                    "missing explicit id for file"):
 | 
|
1984  | 
+                    raise MissingID(e)
 | 
|
1985  | 
+                else:
 | 
|
1986  | 
+                    raise
 | 
|
1987  | 
+            status=delta.status
 | 
|
1988  | 
+            if status > 1:
 | 
|
1989  | 
+                return
 | 
|
1990  | 
+            if (options.perform_diff):
 | 
|
1991  | 
+                chan = arch_compound.ChangesetMunger(changeset)
 | 
|
1992  | 
+                chan.read_indices()
 | 
|
1993  | 
+                if options.diffopts is not None:
 | 
|
1994  | 
+                    if isinstance(b_spec, arch.Revision):
 | 
|
1995  | 
+                        b_dir = b_spec.library_find()
 | 
|
1996  | 
+                    else:
 | 
|
1997  | 
+                        b_dir = b_spec
 | 
|
1998  | 
+                    a_dir = a_spec.library_find()
 | 
|
1999  | 
+                    diffopts = options.diffopts.split()
 | 
|
2000  | 
+                    cmdutil.show_custom_diffs(chan, diffopts, a_dir, b_dir)
 | 
|
2001  | 
+                else:
 | 
|
2002  | 
+                    cmdutil.show_diffs(delta.changeset)
 | 
|
2003  | 
+        finally:
 | 
|
2004  | 
+            if tmpdir and (os.access(tmpdir, os.X_OK)):
 | 
|
2005  | 
+                shutil.rmtree(tmpdir)
 | 
|
2006  | 
+
 | 
|
2007  | 
+    def get_parser(self):
 | 
|
2008  | 
+        """
 | 
|
2009  | 
+        Returns the options parser to use for the "changes" command.
 | 
|
2010  | 
+
 | 
|
2011  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
2012  | 
+        """
 | 
|
2013  | 
+        parser=cmdutil.CmdOptionParser("fai changes [options] [revision]"
 | 
|
2014  | 
+                                       " [revision]")
 | 
|
2015  | 
+        parser.add_option("-d", "--diff", action="store_true", 
 | 
|
2016  | 
+                          dest="perform_diff", default=False, 
 | 
|
2017  | 
+                          help="Show diffs in summary")
 | 
|
2018  | 
+        parser.add_option("-c", "--changeset", dest="changeset", 
 | 
|
2019  | 
+                          help="Store a changeset in the given directory", 
 | 
|
2020  | 
+                          metavar="DIRECTORY")
 | 
|
2021  | 
+        parser.add_option("-s", "--silent", action="store_true", 
 | 
|
2022  | 
+                          dest="suppress_chatter", default=False, 
 | 
|
2023  | 
+                          help="Suppress chatter messages")
 | 
|
2024  | 
+        parser.add_option("--diffopts", dest="diffopts", 
 | 
|
2025  | 
+                          help="Use the specified diff options", 
 | 
|
2026  | 
+                          metavar="OPTIONS")
 | 
|
2027  | 
+
 | 
|
2028  | 
+        return parser
 | 
|
2029  | 
+
 | 
|
2030  | 
+    def help(self, parser=None):
 | 
|
2031  | 
+        """
 | 
|
2032  | 
+        Prints a help message.
 | 
|
2033  | 
+
 | 
|
2034  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
2035  | 
+        not supplied, it is retrieved.
 | 
|
2036  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
2037  | 
+        """
 | 
|
2038  | 
+        if parser is None:
 | 
|
2039  | 
+            parser=self.get_parser()
 | 
|
2040  | 
+        parser.print_help()
 | 
|
2041  | 
+        print """
 | 
|
2042  | 
+Performs source-tree comparisons
 | 
|
2043  | 
+
 | 
|
2044  | 
+If no revision is specified, the current project tree is compared to the
 | 
|
2045  | 
+last-committed revision.  If one revision is specified, the current project
 | 
|
2046  | 
+tree is compared to that revision.  If two revisions are specified, they are
 | 
|
2047  | 
+compared to each other.
 | 
|
2048  | 
+        """
 | 
|
2049  | 
+        help_tree_spec() 
 | 
|
2050  | 
+        return
 | 
|
2051  | 
+
 | 
|
2052  | 
+
 | 
|
2053  | 
+class ApplyChanges(BaseCommand):
 | 
|
2054  | 
+    """
 | 
|
2055  | 
+    Apply differences between two revisions to a tree
 | 
|
2056  | 
+    """
 | 
|
2057  | 
+    
 | 
|
2058  | 
+    def __init__(self):
 | 
|
2059  | 
+        self.description="Applies changes to a project tree"
 | 
|
2060  | 
+    
 | 
|
2061  | 
+    def get_completer(self, arg, index):
 | 
|
2062  | 
+        if index > 1:
 | 
|
2063  | 
+            return None
 | 
|
2064  | 
+        try:
 | 
|
2065  | 
+            tree = arch.tree_root()
 | 
|
2066  | 
+        except:
 | 
|
2067  | 
+            tree = None
 | 
|
2068  | 
+        return cmdutil.iter_revision_completions(arg, tree)
 | 
|
2069  | 
+
 | 
|
2070  | 
+    def parse_commandline(self, cmdline, tree):
 | 
|
2071  | 
+        """
 | 
|
2072  | 
+        Parse commandline arguments.  Raises cmdutil.GetHelp if help is needed.
 | 
|
2073  | 
+        
 | 
|
2074  | 
+        :param cmdline: A list of arguments to parse
 | 
|
2075  | 
+        :rtype: (options, Revision, Revision/WorkingTree)
 | 
|
2076  | 
+        """
 | 
|
2077  | 
+        parser=self.get_parser()
 | 
|
2078  | 
+        (options, args) = parser.parse_args(cmdline)
 | 
|
2079  | 
+        if len(args) != 2:
 | 
|
2080  | 
+            raise cmdutil.GetHelp
 | 
|
2081  | 
+
 | 
|
2082  | 
+        a_spec = cmdutil.determine_revision_tree(tree, args[0])
 | 
|
2083  | 
+        cmdutil.ensure_archive_registered(a_spec.archive)
 | 
|
2084  | 
+        b_spec = cmdutil.determine_revision_tree(tree, args[1])
 | 
|
2085  | 
+        cmdutil.ensure_archive_registered(b_spec.archive)
 | 
|
2086  | 
+        return options, a_spec, b_spec
 | 
|
2087  | 
+
 | 
|
2088  | 
+    def do_command(self, cmdargs):
 | 
|
2089  | 
+        """
 | 
|
2090  | 
+        Master function that performs "apply-changes".
 | 
|
2091  | 
+        """
 | 
|
2092  | 
+        try:
 | 
|
2093  | 
+            tree = arch.tree_root()
 | 
|
2094  | 
+            options, a_spec, b_spec = self.parse_commandline(cmdargs, tree);
 | 
|
2095  | 
+        except cmdutil.CantDetermineRevision, e:
 | 
|
2096  | 
+            print e
 | 
|
2097  | 
+            return
 | 
|
2098  | 
+        except arch.errors.TreeRootError, e:
 | 
|
2099  | 
+            print e
 | 
|
2100  | 
+            return
 | 
|
2101  | 
+        delta=cmdutil.apply_delta(a_spec, b_spec, tree)
 | 
|
2102  | 
+        for line in cmdutil.iter_apply_delta_filter(delta):
 | 
|
2103  | 
+            cmdutil.colorize(line, options.suppress_chatter)
 | 
|
2104  | 
+
 | 
|
2105  | 
+    def get_parser(self):
 | 
|
2106  | 
+        """
 | 
|
2107  | 
+        Returns the options parser to use for the "apply-changes" command.
 | 
|
2108  | 
+
 | 
|
2109  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
2110  | 
+        """
 | 
|
2111  | 
+        parser=cmdutil.CmdOptionParser("fai apply-changes [options] revision"
 | 
|
2112  | 
+                                       " revision")
 | 
|
2113  | 
+        parser.add_option("-d", "--diff", action="store_true", 
 | 
|
2114  | 
+                          dest="perform_diff", default=False, 
 | 
|
2115  | 
+                          help="Show diffs in summary")
 | 
|
2116  | 
+        parser.add_option("-c", "--changeset", dest="changeset", 
 | 
|
2117  | 
+                          help="Store a changeset in the given directory", 
 | 
|
2118  | 
+                          metavar="DIRECTORY")
 | 
|
2119  | 
+        parser.add_option("-s", "--silent", action="store_true", 
 | 
|
2120  | 
+                          dest="suppress_chatter", default=False, 
 | 
|
2121  | 
+                          help="Suppress chatter messages")
 | 
|
2122  | 
+        return parser
 | 
|
2123  | 
+
 | 
|
2124  | 
+    def help(self, parser=None):
 | 
|
2125  | 
+        """
 | 
|
2126  | 
+        Prints a help message.
 | 
|
2127  | 
+
 | 
|
2128  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
2129  | 
+        not supplied, it is retrieved.
 | 
|
2130  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
2131  | 
+        """
 | 
|
2132  | 
+        if parser is None:
 | 
|
2133  | 
+            parser=self.get_parser()
 | 
|
2134  | 
+        parser.print_help()
 | 
|
2135  | 
+        print """
 | 
|
2136  | 
+Applies changes to a project tree
 | 
|
2137  | 
+
 | 
|
2138  | 
+Compares two revisions and applies the difference between them to the current
 | 
|
2139  | 
+tree.
 | 
|
2140  | 
+        """
 | 
|
2141  | 
+        help_tree_spec() 
 | 
|
2142  | 
+        return
 | 
|
2143  | 
+
 | 
|
2144  | 
+class Update(BaseCommand):
 | 
|
2145  | 
+    """
 | 
|
2146  | 
+    Updates a project tree to a given revision, preserving un-committed hanges. 
 | 
|
2147  | 
+    """
 | 
|
2148  | 
+    
 | 
|
2149  | 
+    def __init__(self):
 | 
|
2150  | 
+        self.description="Apply the latest changes to the current directory"
 | 
|
2151  | 
+
 | 
|
2152  | 
+    def get_completer(self, arg, index):
 | 
|
2153  | 
+        if index > 0:
 | 
|
2154  | 
+            return None
 | 
|
2155  | 
+        try:
 | 
|
2156  | 
+            tree = arch.tree_root()
 | 
|
2157  | 
+        except:
 | 
|
2158  | 
+            tree = None
 | 
|
2159  | 
+        return cmdutil.iter_revision_completions(arg, tree)
 | 
|
2160  | 
+    
 | 
|
2161  | 
+    def parse_commandline(self, cmdline, tree):
 | 
|
2162  | 
+        """
 | 
|
2163  | 
+        Parse commandline arguments.  Raises cmdutil.GetHelp if help is needed.
 | 
|
2164  | 
+        
 | 
|
2165  | 
+        :param cmdline: A list of arguments to parse
 | 
|
2166  | 
+        :rtype: (options, Revision, Revision/WorkingTree)
 | 
|
2167  | 
+        """
 | 
|
2168  | 
+        parser=self.get_parser()
 | 
|
2169  | 
+        (options, args) = parser.parse_args(cmdline)
 | 
|
2170  | 
+        if len(args) > 2:
 | 
|
2171  | 
+            raise cmdutil.GetHelp
 | 
|
2172  | 
+
 | 
|
2173  | 
+        spec=None
 | 
|
2174  | 
+        if len(args)>0:
 | 
|
2175  | 
+            spec=args[0]
 | 
|
2176  | 
+        revision=cmdutil.determine_revision_arch(tree, spec)
 | 
|
2177  | 
+        cmdutil.ensure_archive_registered(revision.archive)
 | 
|
2178  | 
+
 | 
|
2179  | 
+        mirror_source = cmdutil.get_mirror_source(revision.archive)
 | 
|
2180  | 
+        if mirror_source != None:
 | 
|
2181  | 
+            if cmdutil.prompt("Mirror update"):
 | 
|
2182  | 
+                cmd=cmdutil.mirror_archive(mirror_source, 
 | 
|
2183  | 
+                    revision.archive, arch.NameParser(revision).get_package_version())
 | 
|
2184  | 
+                for line in arch.chatter_classifier(cmd):
 | 
|
2185  | 
+                    cmdutil.colorize(line, options.suppress_chatter)
 | 
|
2186  | 
+
 | 
|
2187  | 
+                revision=cmdutil.determine_revision_arch(tree, spec)
 | 
|
2188  | 
+
 | 
|
2189  | 
+        return options, revision 
 | 
|
2190  | 
+
 | 
|
2191  | 
+    def do_command(self, cmdargs):
 | 
|
2192  | 
+        """
 | 
|
2193  | 
+        Master function that perfoms the "update" command.
 | 
|
2194  | 
+        """
 | 
|
2195  | 
+        tree=arch.tree_root()
 | 
|
2196  | 
+        try:
 | 
|
2197  | 
+            options, to_revision = self.parse_commandline(cmdargs, tree);
 | 
|
2198  | 
+        except cmdutil.CantDetermineRevision, e:
 | 
|
2199  | 
+            print e
 | 
|
2200  | 
+            return
 | 
|
2201  | 
+        except arch.errors.TreeRootError, e:
 | 
|
2202  | 
+            print e
 | 
|
2203  | 
+            return
 | 
|
2204  | 
+        from_revision = arch_compound.tree_latest(tree)
 | 
|
2205  | 
+        if from_revision==to_revision:
 | 
|
2206  | 
+            print "Tree is already up to date with:\n"+str(to_revision)+"."
 | 
|
2207  | 
+            return
 | 
|
2208  | 
+        cmdutil.ensure_archive_registered(from_revision.archive)
 | 
|
2209  | 
+        cmd=cmdutil.apply_delta(from_revision, to_revision, tree,
 | 
|
2210  | 
+            options.patch_forward)
 | 
|
2211  | 
+        for line in cmdutil.iter_apply_delta_filter(cmd):
 | 
|
2212  | 
+            cmdutil.colorize(line)
 | 
|
2213  | 
+        if to_revision.version != tree.tree_version:
 | 
|
2214  | 
+            if cmdutil.prompt("Update version"):
 | 
|
2215  | 
+                tree.tree_version = to_revision.version
 | 
|
2216  | 
+
 | 
|
2217  | 
+    def get_parser(self):
 | 
|
2218  | 
+        """
 | 
|
2219  | 
+        Returns the options parser to use for the "update" command.
 | 
|
2220  | 
+
 | 
|
2221  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
2222  | 
+        """
 | 
|
2223  | 
+        parser=cmdutil.CmdOptionParser("fai update [options]"
 | 
|
2224  | 
+                                       " [revision/version]")
 | 
|
2225  | 
+        parser.add_option("-f", "--forward", action="store_true", 
 | 
|
2226  | 
+                          dest="patch_forward", default=False, 
 | 
|
2227  | 
+                          help="pass the --forward option to 'patch'")
 | 
|
2228  | 
+        parser.add_option("-s", "--silent", action="store_true", 
 | 
|
2229  | 
+                          dest="suppress_chatter", default=False, 
 | 
|
2230  | 
+                          help="Suppress chatter messages")
 | 
|
2231  | 
+        return parser
 | 
|
2232  | 
+
 | 
|
2233  | 
+    def help(self, parser=None):
 | 
|
2234  | 
+        """
 | 
|
2235  | 
+        Prints a help message.
 | 
|
2236  | 
+
 | 
|
2237  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
2238  | 
+        not supplied, it is retrieved.
 | 
|
2239  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
2240  | 
+        """
 | 
|
2241  | 
+        if parser is None:
 | 
|
2242  | 
+            parser=self.get_parser()
 | 
|
2243  | 
+        parser.print_help()
 | 
|
2244  | 
+        print """
 | 
|
2245  | 
+Updates a working tree to the current archive revision
 | 
|
2246  | 
+
 | 
|
2247  | 
+If a revision or version is specified, that is used instead 
 | 
|
2248  | 
+        """
 | 
|
2249  | 
+        help_tree_spec() 
 | 
|
2250  | 
+        return
 | 
|
2251  | 
+
 | 
|
2252  | 
+
 | 
|
2253  | 
+class Commit(BaseCommand):
 | 
|
2254  | 
+    """
 | 
|
2255  | 
+    Create a revision based on the changes in the current tree.
 | 
|
2256  | 
+    """
 | 
|
2257  | 
+    
 | 
|
2258  | 
+    def __init__(self):
 | 
|
2259  | 
+        self.description="Write local changes to the archive"
 | 
|
2260  | 
+
 | 
|
2261  | 
+    def get_completer(self, arg, index):
 | 
|
2262  | 
+        if arg is None:
 | 
|
2263  | 
+            arg = ""
 | 
|
2264  | 
+        return iter_modified_file_completions(arch.tree_root(), arg)
 | 
|
2265  | 
+#        return iter_source_file_completions(arch.tree_root(), arg)
 | 
|
2266  | 
+    
 | 
|
2267  | 
+    def parse_commandline(self, cmdline, tree):
 | 
|
2268  | 
+        """
 | 
|
2269  | 
+        Parse commandline arguments.  Raise cmtutil.GetHelp if help is needed.
 | 
|
2270  | 
+        
 | 
|
2271  | 
+        :param cmdline: A list of arguments to parse
 | 
|
2272  | 
+        :rtype: (options, Revision, Revision/WorkingTree)
 | 
|
2273  | 
+        """
 | 
|
2274  | 
+        parser=self.get_parser()
 | 
|
2275  | 
+        (options, args) = parser.parse_args(cmdline)
 | 
|
2276  | 
+
 | 
|
2277  | 
+        if len(args) == 0:
 | 
|
2278  | 
+            args = None
 | 
|
2279  | 
+        if options.version is None:
 | 
|
2280  | 
+            return options, tree.tree_version, args
 | 
|
2281  | 
+
 | 
|
2282  | 
+        revision=cmdutil.determine_revision_arch(tree, options.version)
 | 
|
2283  | 
+        return options, revision.get_version(), args
 | 
|
2284  | 
+
 | 
|
2285  | 
+    def do_command(self, cmdargs):
 | 
|
2286  | 
+        """
 | 
|
2287  | 
+        Master function that perfoms the "commit" command.
 | 
|
2288  | 
+        """
 | 
|
2289  | 
+        tree=arch.tree_root()
 | 
|
2290  | 
+        options, version, files = self.parse_commandline(cmdargs, tree)
 | 
|
2291  | 
+        ancestor = None
 | 
|
2292  | 
+        if options.__dict__.has_key("base") and options.base:
 | 
|
2293  | 
+            base = cmdutil.determine_revision_tree(tree, options.base)
 | 
|
2294  | 
+            ancestor = base
 | 
|
2295  | 
+        else:
 | 
|
2296  | 
+            base = ancillary.submit_revision(tree)
 | 
|
2297  | 
+            ancestor = base
 | 
|
2298  | 
+        if ancestor is None:
 | 
|
2299  | 
+            ancestor = arch_compound.tree_latest(tree, version)
 | 
|
2300  | 
+
 | 
|
2301  | 
+        writeversion=version
 | 
|
2302  | 
+        archive=version.archive
 | 
|
2303  | 
+        source=cmdutil.get_mirror_source(archive)
 | 
|
2304  | 
+        allow_old=False
 | 
|
2305  | 
+        writethrough="implicit"
 | 
|
2306  | 
+
 | 
|
2307  | 
+        if source!=None:
 | 
|
2308  | 
+            if writethrough=="explicit" and \
 | 
|
2309  | 
+                cmdutil.prompt("Writethrough"):
 | 
|
2310  | 
+                writeversion=arch.Version(str(source)+"/"+str(version.get_nonarch()))
 | 
|
2311  | 
+            elif writethrough=="none":
 | 
|
2312  | 
+                raise CommitToMirror(archive)
 | 
|
2313  | 
+
 | 
|
2314  | 
+        elif archive.is_mirror:
 | 
|
2315  | 
+            raise CommitToMirror(archive)
 | 
|
2316  | 
+
 | 
|
2317  | 
+        try:
 | 
|
2318  | 
+            last_revision=tree.iter_logs(version, True).next().revision
 | 
|
2319  | 
+        except StopIteration, e:
 | 
|
2320  | 
+            last_revision = None
 | 
|
2321  | 
+            if ancestor is None:
 | 
|
2322  | 
+                if cmdutil.prompt("Import from commit"):
 | 
|
2323  | 
+                    return do_import(version)
 | 
|
2324  | 
+                else:
 | 
|
2325  | 
+                    raise NoVersionLogs(version)
 | 
|
2326  | 
+        try:
 | 
|
2327  | 
+            arch_last_revision = version.iter_revisions(True).next()
 | 
|
2328  | 
+        except StopIteration, e:
 | 
|
2329  | 
+            arch_last_revision = None
 | 
|
2330  | 
+ 
 | 
|
2331  | 
+        if last_revision != arch_last_revision:
 | 
|
2332  | 
+            print "Tree is not up to date with %s" % str(version)
 | 
|
2333  | 
+            if not cmdutil.prompt("Out of date"):
 | 
|
2334  | 
+                raise OutOfDate
 | 
|
2335  | 
+            else:
 | 
|
2336  | 
+                allow_old=True
 | 
|
2337  | 
+
 | 
|
2338  | 
+        try:
 | 
|
2339  | 
+            if not cmdutil.has_changed(ancestor):
 | 
|
2340  | 
+                if not cmdutil.prompt("Empty commit"):
 | 
|
2341  | 
+                    raise EmptyCommit
 | 
|
2342  | 
+        except arch.util.ExecProblem, e:
 | 
|
2343  | 
+            if e.proc.error and e.proc.error.startswith(
 | 
|
2344  | 
+                "missing explicit id for file"):
 | 
|
2345  | 
+                raise MissingID(e)
 | 
|
2346  | 
+            else:
 | 
|
2347  | 
+                raise
 | 
|
2348  | 
+        log = tree.log_message(create=False, version=version)
 | 
|
2349  | 
+        if log is None:
 | 
|
2350  | 
+            try:
 | 
|
2351  | 
+                if cmdutil.prompt("Create log"):
 | 
|
2352  | 
+                    edit_log(tree, version)
 | 
|
2353  | 
+
 | 
|
2354  | 
+            except cmdutil.NoEditorSpecified, e:
 | 
|
2355  | 
+                raise CommandFailed(e)
 | 
|
2356  | 
+            log = tree.log_message(create=False, version=version)
 | 
|
2357  | 
+        if log is None: 
 | 
|
2358  | 
+            raise NoLogMessage
 | 
|
2359  | 
+        if log["Summary"] is None or len(log["Summary"].strip()) == 0:
 | 
|
2360  | 
+            if not cmdutil.prompt("Omit log summary"):
 | 
|
2361  | 
+                raise errors.NoLogSummary
 | 
|
2362  | 
+        try:
 | 
|
2363  | 
+            for line in tree.iter_commit(version, seal=options.seal_version,
 | 
|
2364  | 
+                base=base, out_of_date_ok=allow_old, file_list=files):
 | 
|
2365  | 
+                cmdutil.colorize(line, options.suppress_chatter)
 | 
|
2366  | 
+
 | 
|
2367  | 
+        except arch.util.ExecProblem, e:
 | 
|
2368  | 
+            if e.proc.error and e.proc.error.startswith(
 | 
|
2369  | 
+                "These files violate naming conventions:"):
 | 
|
2370  | 
+                raise LintFailure(e.proc.error)
 | 
|
2371  | 
+            else:
 | 
|
2372  | 
+                raise
 | 
|
2373  | 
+
 | 
|
2374  | 
+    def get_parser(self):
 | 
|
2375  | 
+        """
 | 
|
2376  | 
+        Returns the options parser to use for the "commit" command.
 | 
|
2377  | 
+
 | 
|
2378  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
2379  | 
+        """
 | 
|
2380  | 
+
 | 
|
2381  | 
+        parser=cmdutil.CmdOptionParser("fai commit [options] [file1]"
 | 
|
2382  | 
+                                       " [file2...]")
 | 
|
2383  | 
+        parser.add_option("--seal", action="store_true", 
 | 
|
2384  | 
+                          dest="seal_version", default=False, 
 | 
|
2385  | 
+                          help="seal this version")
 | 
|
2386  | 
+        parser.add_option("-v", "--version", dest="version", 
 | 
|
2387  | 
+                          help="Use the specified version", 
 | 
|
2388  | 
+                          metavar="VERSION")
 | 
|
2389  | 
+        parser.add_option("-s", "--silent", action="store_true", 
 | 
|
2390  | 
+                          dest="suppress_chatter", default=False, 
 | 
|
2391  | 
+                          help="Suppress chatter messages")
 | 
|
2392  | 
+        if cmdutil.supports_switch("commit", "--base"):
 | 
|
2393  | 
+            parser.add_option("--base", dest="base", help="", 
 | 
|
2394  | 
+                              metavar="REVISION")
 | 
|
2395  | 
+        return parser
 | 
|
2396  | 
+
 | 
|
2397  | 
+    def help(self, parser=None):
 | 
|
2398  | 
+        """
 | 
|
2399  | 
+        Prints a help message.
 | 
|
2400  | 
+
 | 
|
2401  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
2402  | 
+        not supplied, it is retrieved.
 | 
|
2403  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
2404  | 
+        """
 | 
|
2405  | 
+        if parser is None:
 | 
|
2406  | 
+            parser=self.get_parser()
 | 
|
2407  | 
+        parser.print_help()
 | 
|
2408  | 
+        print """
 | 
|
2409  | 
+Updates a working tree to the current archive revision
 | 
|
2410  | 
+
 | 
|
2411  | 
+If a version is specified, that is used instead 
 | 
|
2412  | 
+        """
 | 
|
2413  | 
+#        help_tree_spec() 
 | 
|
2414  | 
+        return
 | 
|
2415  | 
+
 | 
|
2416  | 
+
 | 
|
2417  | 
+
 | 
|
2418  | 
+class CatLog(BaseCommand):
 | 
|
2419  | 
+    """
 | 
|
2420  | 
+    Print the log of a given file (from current tree)
 | 
|
2421  | 
+    """
 | 
|
2422  | 
+    def __init__(self):
 | 
|
2423  | 
+        self.description="Prints the patch log for a revision"
 | 
|
2424  | 
+
 | 
|
2425  | 
+    def get_completer(self, arg, index):
 | 
|
2426  | 
+        if index > 0:
 | 
|
2427  | 
+            return None
 | 
|
2428  | 
+        try:
 | 
|
2429  | 
+            tree = arch.tree_root()
 | 
|
2430  | 
+        except:
 | 
|
2431  | 
+            tree = None
 | 
|
2432  | 
+        return cmdutil.iter_revision_completions(arg, tree)
 | 
|
2433  | 
+
 | 
|
2434  | 
+    def do_command(self, cmdargs):
 | 
|
2435  | 
+        """
 | 
|
2436  | 
+        Master function that perfoms the "cat-log" command.
 | 
|
2437  | 
+        """
 | 
|
2438  | 
+        parser=self.get_parser()
 | 
|
2439  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
2440  | 
+        try:
 | 
|
2441  | 
+            tree = arch.tree_root()
 | 
|
2442  | 
+        except arch.errors.TreeRootError, e:
 | 
|
2443  | 
+            tree = None
 | 
|
2444  | 
+        spec=None
 | 
|
2445  | 
+        if len(args) > 0:
 | 
|
2446  | 
+            spec=args[0]
 | 
|
2447  | 
+        if len(args) > 1:
 | 
|
2448  | 
+            raise cmdutil.GetHelp()
 | 
|
2449  | 
+        try:
 | 
|
2450  | 
+            if tree:
 | 
|
2451  | 
+                revision = cmdutil.determine_revision_tree(tree, spec)
 | 
|
2452  | 
+            else:
 | 
|
2453  | 
+                revision = cmdutil.determine_revision_arch(tree, spec)
 | 
|
2454  | 
+        except cmdutil.CantDetermineRevision, e:
 | 
|
2455  | 
+            raise CommandFailedWrapper(e)
 | 
|
2456  | 
+        log = None
 | 
|
2457  | 
+        
 | 
|
2458  | 
+        use_tree = (options.source == "tree" or \
 | 
|
2459  | 
+            (options.source == "any" and tree))
 | 
|
2460  | 
+        use_arch = (options.source == "archive" or options.source == "any")
 | 
|
2461  | 
+        
 | 
|
2462  | 
+        log = None
 | 
|
2463  | 
+        if use_tree:
 | 
|
2464  | 
+            for log in tree.iter_logs(revision.get_version()):
 | 
|
2465  | 
+                if log.revision == revision:
 | 
|
2466  | 
+                    break
 | 
|
2467  | 
+                else:
 | 
|
2468  | 
+                    log = None
 | 
|
2469  | 
+        if log is None and use_arch:
 | 
|
2470  | 
+            cmdutil.ensure_revision_exists(revision)
 | 
|
2471  | 
+            log = arch.Patchlog(revision)
 | 
|
2472  | 
+        if log is not None:
 | 
|
2473  | 
+            for item in log.items():
 | 
|
2474  | 
+                print "%s: %s" % item
 | 
|
2475  | 
+            print log.description
 | 
|
2476  | 
+
 | 
|
2477  | 
+    def get_parser(self):
 | 
|
2478  | 
+        """
 | 
|
2479  | 
+        Returns the options parser to use for the "cat-log" command.
 | 
|
2480  | 
+
 | 
|
2481  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
2482  | 
+        """
 | 
|
2483  | 
+        parser=cmdutil.CmdOptionParser("fai cat-log [revision]")
 | 
|
2484  | 
+        parser.add_option("--archive", action="store_const", dest="source",
 | 
|
2485  | 
+                          const="archive", default="any",
 | 
|
2486  | 
+                          help="Always get the log from the archive")
 | 
|
2487  | 
+        parser.add_option("--tree", action="store_const", dest="source",
 | 
|
2488  | 
+                          const="tree", help="Always get the log from the tree")
 | 
|
2489  | 
+        return parser 
 | 
|
2490  | 
+
 | 
|
2491  | 
+    def help(self, parser=None):
 | 
|
2492  | 
+        """
 | 
|
2493  | 
+        Prints a help message.
 | 
|
2494  | 
+
 | 
|
2495  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
2496  | 
+        not supplied, it is retrieved.
 | 
|
2497  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
2498  | 
+        """
 | 
|
2499  | 
+        if parser==None:
 | 
|
2500  | 
+            parser=self.get_parser()
 | 
|
2501  | 
+        parser.print_help()
 | 
|
2502  | 
+        print """
 | 
|
2503  | 
+Prints the log for the specified revision
 | 
|
2504  | 
+        """
 | 
|
2505  | 
+        help_tree_spec()
 | 
|
2506  | 
+        return
 | 
|
2507  | 
+
 | 
|
2508  | 
+class Revert(BaseCommand):
 | 
|
2509  | 
+    """ Reverts a tree (or aspects of it) to a revision
 | 
|
2510  | 
+    """
 | 
|
2511  | 
+    def __init__(self):
 | 
|
2512  | 
+        self.description="Reverts a tree (or aspects of it) to a revision "
 | 
|
2513  | 
+
 | 
|
2514  | 
+    def get_completer(self, arg, index):
 | 
|
2515  | 
+        if index > 0:
 | 
|
2516  | 
+            return None
 | 
|
2517  | 
+        try:
 | 
|
2518  | 
+            tree = arch.tree_root()
 | 
|
2519  | 
+        except:
 | 
|
2520  | 
+            tree = None
 | 
|
2521  | 
+        return iter_modified_file_completions(tree, arg)
 | 
|
2522  | 
+
 | 
|
2523  | 
+    def do_command(self, cmdargs):
 | 
|
2524  | 
+        """
 | 
|
2525  | 
+        Master function that perfoms the "revert" command.
 | 
|
2526  | 
+        """
 | 
|
2527  | 
+        parser=self.get_parser()
 | 
|
2528  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
2529  | 
+        try:
 | 
|
2530  | 
+            tree = arch.tree_root()
 | 
|
2531  | 
+        except arch.errors.TreeRootError, e:
 | 
|
2532  | 
+            raise CommandFailed(e)
 | 
|
2533  | 
+        spec=None
 | 
|
2534  | 
+        if options.revision is not None:
 | 
|
2535  | 
+            spec=options.revision
 | 
|
2536  | 
+        try:
 | 
|
2537  | 
+            if spec is not None:
 | 
|
2538  | 
+                revision = cmdutil.determine_revision_tree(tree, spec)
 | 
|
2539  | 
+            else:
 | 
|
2540  | 
+                revision = ancillary.comp_revision(tree)
 | 
|
2541  | 
+        except cmdutil.CantDetermineRevision, e:
 | 
|
2542  | 
+            raise CommandFailedWrapper(e)
 | 
|
2543  | 
+        munger = None
 | 
|
2544  | 
+
 | 
|
2545  | 
+        if options.file_contents or options.file_perms or options.deletions\
 | 
|
2546  | 
+            or options.additions or options.renames or options.hunk_prompt:
 | 
|
2547  | 
+            munger = arch_compound.MungeOpts()
 | 
|
2548  | 
+            munger.set_hunk_prompt(cmdutil.colorize, cmdutil.user_hunk_confirm,
 | 
|
2549  | 
+                                   options.hunk_prompt)
 | 
|
2550  | 
+
 | 
|
2551  | 
+        if len(args) > 0 or options.logs or options.pattern_files or \
 | 
|
2552  | 
+            options.control:
 | 
|
2553  | 
+            if munger is None:
 | 
|
2554  | 
+                munger = cmdutil.arch_compound.MungeOpts(True)
 | 
|
2555  | 
+                munger.all_types(True)
 | 
|
2556  | 
+        if len(args) > 0:
 | 
|
2557  | 
+            t_cwd = arch_compound.tree_cwd(tree)
 | 
|
2558  | 
+            for name in args:
 | 
|
2559  | 
+                if len(t_cwd) > 0:
 | 
|
2560  | 
+                    t_cwd += "/"
 | 
|
2561  | 
+                name = "./" + t_cwd + name
 | 
|
2562  | 
+                munger.add_keep_file(name);
 | 
|
2563  | 
+
 | 
|
2564  | 
+        if options.file_perms:
 | 
|
2565  | 
+            munger.file_perms = True
 | 
|
2566  | 
+        if options.file_contents:
 | 
|
2567  | 
+            munger.file_contents = True
 | 
|
2568  | 
+        if options.deletions:
 | 
|
2569  | 
+            munger.deletions = True
 | 
|
2570  | 
+        if options.additions:
 | 
|
2571  | 
+            munger.additions = True
 | 
|
2572  | 
+        if options.renames:
 | 
|
2573  | 
+            munger.renames = True
 | 
|
2574  | 
+        if options.logs:
 | 
|
2575  | 
+            munger.add_keep_pattern('^\./\{arch\}/[^=].*')
 | 
|
2576  | 
+        if options.control:
 | 
|
2577  | 
+            munger.add_keep_pattern("/\.arch-ids|^\./\{arch\}|"\
 | 
|
2578  | 
+                                    "/\.arch-inventory$")
 | 
|
2579  | 
+        if options.pattern_files:
 | 
|
2580  | 
+            munger.add_keep_pattern(options.pattern_files)
 | 
|
2581  | 
+                
 | 
|
2582  | 
+        for line in arch_compound.revert(tree, revision, munger, 
 | 
|
2583  | 
+                                   not options.no_output):
 | 
|
2584  | 
+            cmdutil.colorize(line)
 | 
|
2585  | 
+
 | 
|
2586  | 
+
 | 
|
2587  | 
+    def get_parser(self):
 | 
|
2588  | 
+        """
 | 
|
2589  | 
+        Returns the options parser to use for the "cat-log" command.
 | 
|
2590  | 
+
 | 
|
2591  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
2592  | 
+        """
 | 
|
2593  | 
+        parser=cmdutil.CmdOptionParser("fai revert [options] [FILE...]")
 | 
|
2594  | 
+        parser.add_option("", "--contents", action="store_true", 
 | 
|
2595  | 
+                          dest="file_contents", 
 | 
|
2596  | 
+                          help="Revert file content changes")
 | 
|
2597  | 
+        parser.add_option("", "--permissions", action="store_true", 
 | 
|
2598  | 
+                          dest="file_perms", 
 | 
|
2599  | 
+                          help="Revert file permissions changes")
 | 
|
2600  | 
+        parser.add_option("", "--deletions", action="store_true", 
 | 
|
2601  | 
+                          dest="deletions", 
 | 
|
2602  | 
+                          help="Restore deleted files")
 | 
|
2603  | 
+        parser.add_option("", "--additions", action="store_true", 
 | 
|
2604  | 
+                          dest="additions", 
 | 
|
2605  | 
+                          help="Remove added files")
 | 
|
2606  | 
+        parser.add_option("", "--renames", action="store_true", 
 | 
|
2607  | 
+                          dest="renames", 
 | 
|
2608  | 
+                          help="Revert file names")
 | 
|
2609  | 
+        parser.add_option("--hunks", action="store_true", 
 | 
|
2610  | 
+                          dest="hunk_prompt", default=False,
 | 
|
2611  | 
+                          help="Prompt which hunks to revert")
 | 
|
2612  | 
+        parser.add_option("--pattern-files", dest="pattern_files", 
 | 
|
2613  | 
+                          help="Revert files that match this pattern", 
 | 
|
2614  | 
+                          metavar="REGEX")
 | 
|
2615  | 
+        parser.add_option("--logs", action="store_true", 
 | 
|
2616  | 
+                          dest="logs", default=False,
 | 
|
2617  | 
+                          help="Revert only logs")
 | 
|
2618  | 
+        parser.add_option("--control-files", action="store_true", 
 | 
|
2619  | 
+                          dest="control", default=False,
 | 
|
2620  | 
+                          help="Revert logs and other control files")
 | 
|
2621  | 
+        parser.add_option("-n", "--no-output", action="store_true", 
 | 
|
2622  | 
+                          dest="no_output", 
 | 
|
2623  | 
+                          help="Don't keep an undo changeset")
 | 
|
2624  | 
+        parser.add_option("--revision", dest="revision", 
 | 
|
2625  | 
+                          help="Revert to the specified revision", 
 | 
|
2626  | 
+                          metavar="REVISION")
 | 
|
2627  | 
+        return parser 
 | 
|
2628  | 
+
 | 
|
2629  | 
+    def help(self, parser=None):
 | 
|
2630  | 
+        """
 | 
|
2631  | 
+        Prints a help message.
 | 
|
2632  | 
+
 | 
|
2633  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
2634  | 
+        not supplied, it is retrieved.
 | 
|
2635  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
2636  | 
+        """
 | 
|
2637  | 
+        if parser==None:
 | 
|
2638  | 
+            parser=self.get_parser()
 | 
|
2639  | 
+        parser.print_help()
 | 
|
2640  | 
+        print """
 | 
|
2641  | 
+Reverts changes in the current working tree.  If no flags are specified, all
 | 
|
2642  | 
+types of changes are reverted.  Otherwise, only selected types of changes are
 | 
|
2643  | 
+reverted.  
 | 
|
2644  | 
+
 | 
|
2645  | 
+If a revision is specified on the commandline, differences between the current
 | 
|
2646  | 
+tree and that revision are reverted.  If a version is specified, the current
 | 
|
2647  | 
+tree is used to determine the revision.
 | 
|
2648  | 
+
 | 
|
2649  | 
+If files are specified, only those files listed will have any changes applied.
 | 
|
2650  | 
+To specify a renamed file, you can use either the old or new name. (or both!)
 | 
|
2651  | 
+
 | 
|
2652  | 
+Unless "-n" is specified, reversions can be undone with "redo".
 | 
|
2653  | 
+        """
 | 
|
2654  | 
+        return
 | 
|
2655  | 
+
 | 
|
2656  | 
+class Revision(BaseCommand):
 | 
|
2657  | 
+    """
 | 
|
2658  | 
+    Print a revision name based on a revision specifier
 | 
|
2659  | 
+    """
 | 
|
2660  | 
+    def __init__(self):
 | 
|
2661  | 
+        self.description="Prints the name of a revision"
 | 
|
2662  | 
+
 | 
|
2663  | 
+    def get_completer(self, arg, index):
 | 
|
2664  | 
+        if index > 0:
 | 
|
2665  | 
+            return None
 | 
|
2666  | 
+        try:
 | 
|
2667  | 
+            tree = arch.tree_root()
 | 
|
2668  | 
+        except:
 | 
|
2669  | 
+            tree = None
 | 
|
2670  | 
+        return cmdutil.iter_revision_completions(arg, tree)
 | 
|
2671  | 
+
 | 
|
2672  | 
+    def do_command(self, cmdargs):
 | 
|
2673  | 
+        """
 | 
|
2674  | 
+        Master function that perfoms the "revision" command.
 | 
|
2675  | 
+        """
 | 
|
2676  | 
+        parser=self.get_parser()
 | 
|
2677  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
2678  | 
+
 | 
|
2679  | 
+        try:
 | 
|
2680  | 
+            tree = arch.tree_root()
 | 
|
2681  | 
+        except arch.errors.TreeRootError:
 | 
|
2682  | 
+            tree = None
 | 
|
2683  | 
+
 | 
|
2684  | 
+        spec=None
 | 
|
2685  | 
+        if len(args) > 0:
 | 
|
2686  | 
+            spec=args[0]
 | 
|
2687  | 
+        if len(args) > 1:
 | 
|
2688  | 
+            raise cmdutil.GetHelp
 | 
|
2689  | 
+        try:
 | 
|
2690  | 
+            if tree:
 | 
|
2691  | 
+                revision = cmdutil.determine_revision_tree(tree, spec)
 | 
|
2692  | 
+            else:
 | 
|
2693  | 
+                revision = cmdutil.determine_revision_arch(tree, spec)
 | 
|
2694  | 
+        except cmdutil.CantDetermineRevision, e:
 | 
|
2695  | 
+            print str(e)
 | 
|
2696  | 
+            return
 | 
|
2697  | 
+        print options.display(revision)
 | 
|
2698  | 
+
 | 
|
2699  | 
+    def get_parser(self):
 | 
|
2700  | 
+        """
 | 
|
2701  | 
+        Returns the options parser to use for the "revision" command.
 | 
|
2702  | 
+
 | 
|
2703  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
2704  | 
+        """
 | 
|
2705  | 
+        parser=cmdutil.CmdOptionParser("fai revision [revision]")
 | 
|
2706  | 
+        parser.add_option("", "--location", action="store_const", 
 | 
|
2707  | 
+                         const=paths.determine_path, dest="display", 
 | 
|
2708  | 
+                         help="Show location instead of name", default=str)
 | 
|
2709  | 
+        parser.add_option("--import", action="store_const", 
 | 
|
2710  | 
+                         const=paths.determine_import_path, dest="display",  
 | 
|
2711  | 
+                         help="Show location of import file")
 | 
|
2712  | 
+        parser.add_option("--log", action="store_const", 
 | 
|
2713  | 
+                         const=paths.determine_log_path, dest="display", 
 | 
|
2714  | 
+                         help="Show location of log file")
 | 
|
2715  | 
+        parser.add_option("--patch", action="store_const", 
 | 
|
2716  | 
+                         dest="display", const=paths.determine_patch_path,
 | 
|
2717  | 
+                         help="Show location of patchfile")
 | 
|
2718  | 
+        parser.add_option("--continuation", action="store_const", 
 | 
|
2719  | 
+                         const=paths.determine_continuation_path, 
 | 
|
2720  | 
+                         dest="display",
 | 
|
2721  | 
+                         help="Show location of continuation file")
 | 
|
2722  | 
+        parser.add_option("--cacherev", action="store_const", 
 | 
|
2723  | 
+                         const=paths.determine_cacherev_path, dest="display",
 | 
|
2724  | 
+                         help="Show location of cacherev file")
 | 
|
2725  | 
+        return parser 
 | 
|
2726  | 
+
 | 
|
2727  | 
+    def help(self, parser=None):
 | 
|
2728  | 
+        """
 | 
|
2729  | 
+        Prints a help message.
 | 
|
2730  | 
+
 | 
|
2731  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
2732  | 
+        not supplied, it is retrieved.
 | 
|
2733  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
2734  | 
+        """
 | 
|
2735  | 
+        if parser==None:
 | 
|
2736  | 
+            parser=self.get_parser()
 | 
|
2737  | 
+        parser.print_help()
 | 
|
2738  | 
+        print """
 | 
|
2739  | 
+Expands aliases and prints the name of the specified revision.  Instead of
 | 
|
2740  | 
+the name, several options can be used to print locations.  If more than one is
 | 
|
2741  | 
+specified, the last one is used.
 | 
|
2742  | 
+        """
 | 
|
2743  | 
+        help_tree_spec()
 | 
|
2744  | 
+        return
 | 
|
2745  | 
+
 | 
|
2746  | 
+class Revisions(BaseCommand):
 | 
|
2747  | 
+    """
 | 
|
2748  | 
+    Print a revision name based on a revision specifier
 | 
|
2749  | 
+    """
 | 
|
2750  | 
+    def __init__(self):
 | 
|
2751  | 
+        self.description="Lists revisions"
 | 
|
2752  | 
+        self.cl_revisions = []
 | 
|
2753  | 
+    
 | 
|
2754  | 
+    def do_command(self, cmdargs):
 | 
|
2755  | 
+        """
 | 
|
2756  | 
+        Master function that perfoms the "revision" command.
 | 
|
2757  | 
+        """
 | 
|
2758  | 
+        (options, args) = self.get_parser().parse_args(cmdargs)
 | 
|
2759  | 
+        if len(args) > 1:
 | 
|
2760  | 
+            raise cmdutil.GetHelp
 | 
|
2761  | 
+        try:
 | 
|
2762  | 
+            self.tree = arch.tree_root()
 | 
|
2763  | 
+        except arch.errors.TreeRootError:
 | 
|
2764  | 
+            self.tree = None
 | 
|
2765  | 
+        if options.type == "default":
 | 
|
2766  | 
+            options.type = "archive"
 | 
|
2767  | 
+        try:
 | 
|
2768  | 
+            iter = cmdutil.revision_iterator(self.tree, options.type, args, 
 | 
|
2769  | 
+                                             options.reverse, options.modified,
 | 
|
2770  | 
+                                             options.shallow)
 | 
|
2771  | 
+        except cmdutil.CantDetermineRevision, e:
 | 
|
2772  | 
+            raise CommandFailedWrapper(e)
 | 
|
2773  | 
+        except cmdutil.CantDetermineVersion, e:
 | 
|
2774  | 
+            raise CommandFailedWrapper(e)
 | 
|
2775  | 
+        if options.skip is not None:
 | 
|
2776  | 
+            iter = cmdutil.iter_skip(iter, int(options.skip))
 | 
|
2777  | 
+
 | 
|
2778  | 
+        try:
 | 
|
2779  | 
+            for revision in iter:
 | 
|
2780  | 
+                log = None
 | 
|
2781  | 
+                if isinstance(revision, arch.Patchlog):
 | 
|
2782  | 
+                    log = revision
 | 
|
2783  | 
+                    revision=revision.revision
 | 
|
2784  | 
+                out = options.display(revision)
 | 
|
2785  | 
+                if out is not None:
 | 
|
2786  | 
+                    print out
 | 
|
2787  | 
+                if log is None and (options.summary or options.creator or 
 | 
|
2788  | 
+                                    options.date or options.merges):
 | 
|
2789  | 
+                    log = revision.patchlog
 | 
|
2790  | 
+                if options.creator:
 | 
|
2791  | 
+                    print "    %s" % log.creator
 | 
|
2792  | 
+                if options.date:
 | 
|
2793  | 
+                    print "    %s" % time.strftime('%Y-%m-%d %H:%M:%S %Z', log.date)
 | 
|
2794  | 
+                if options.summary:
 | 
|
2795  | 
+                    print "    %s" % log.summary
 | 
|
2796  | 
+                if options.merges:
 | 
|
2797  | 
+                    showed_title = False
 | 
|
2798  | 
+                    for revision in log.merged_patches:
 | 
|
2799  | 
+                        if not showed_title:
 | 
|
2800  | 
+                            print "    Merged:"
 | 
|
2801  | 
+                            showed_title = True
 | 
|
2802  | 
+                        print "    %s" % revision
 | 
|
2803  | 
+            if len(self.cl_revisions) > 0:
 | 
|
2804  | 
+                print pylon.changelog_for_merge(self.cl_revisions)
 | 
|
2805  | 
+        except pylon.errors.TreeRootNone:
 | 
|
2806  | 
+            raise CommandFailedWrapper(
 | 
|
2807  | 
+                Exception("This option can only be used in a project tree."))
 | 
|
2808  | 
+
 | 
|
2809  | 
+    def changelog_append(self, revision):
 | 
|
2810  | 
+        if isinstance(revision, arch.Revision):
 | 
|
2811  | 
+            revision=arch.Patchlog(revision)
 | 
|
2812  | 
+        self.cl_revisions.append(revision)
 | 
|
2813  | 
+   
 | 
|
2814  | 
+    def get_parser(self):
 | 
|
2815  | 
+        """
 | 
|
2816  | 
+        Returns the options parser to use for the "revision" command.
 | 
|
2817  | 
+
 | 
|
2818  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
2819  | 
+        """
 | 
|
2820  | 
+        parser=cmdutil.CmdOptionParser("fai revisions [version/revision]")
 | 
|
2821  | 
+        select = cmdutil.OptionGroup(parser, "Selection options",
 | 
|
2822  | 
+                          "Control which revisions are listed.  These options"
 | 
|
2823  | 
+                          " are mutually exclusive.  If more than one is"
 | 
|
2824  | 
+                          " specified, the last is used.")
 | 
|
2825  | 
+
 | 
|
2826  | 
+        cmdutil.add_revision_iter_options(select)
 | 
|
2827  | 
+        parser.add_option("", "--skip", dest="skip", 
 | 
|
2828  | 
+                          help="Skip revisions.  Positive numbers skip from "
 | 
|
2829  | 
+                          "beginning, negative skip from end.",
 | 
|
2830  | 
+                          metavar="NUMBER")
 | 
|
2831  | 
+
 | 
|
2832  | 
+        parser.add_option_group(select)
 | 
|
2833  | 
+
 | 
|
2834  | 
+        format = cmdutil.OptionGroup(parser, "Revision format options",
 | 
|
2835  | 
+                          "These control the appearance of listed revisions")
 | 
|
2836  | 
+        format.add_option("", "--location", action="store_const", 
 | 
|
2837  | 
+                         const=paths.determine_path, dest="display", 
 | 
|
2838  | 
+                         help="Show location instead of name", default=str)
 | 
|
2839  | 
+        format.add_option("--import", action="store_const", 
 | 
|
2840  | 
+                         const=paths.determine_import_path, dest="display",  
 | 
|
2841  | 
+                         help="Show location of import file")
 | 
|
2842  | 
+        format.add_option("--log", action="store_const", 
 | 
|
2843  | 
+                         const=paths.determine_log_path, dest="display", 
 | 
|
2844  | 
+                         help="Show location of log file")
 | 
|
2845  | 
+        format.add_option("--patch", action="store_const", 
 | 
|
2846  | 
+                         dest="display", const=paths.determine_patch_path,
 | 
|
2847  | 
+                         help="Show location of patchfile")
 | 
|
2848  | 
+        format.add_option("--continuation", action="store_const", 
 | 
|
2849  | 
+                         const=paths.determine_continuation_path, 
 | 
|
2850  | 
+                         dest="display",
 | 
|
2851  | 
+                         help="Show location of continuation file")
 | 
|
2852  | 
+        format.add_option("--cacherev", action="store_const", 
 | 
|
2853  | 
+                         const=paths.determine_cacherev_path, dest="display",
 | 
|
2854  | 
+                         help="Show location of cacherev file")
 | 
|
2855  | 
+        format.add_option("--changelog", action="store_const", 
 | 
|
2856  | 
+                         const=self.changelog_append, dest="display",
 | 
|
2857  | 
+                         help="Show location of cacherev file")
 | 
|
2858  | 
+        parser.add_option_group(format)
 | 
|
2859  | 
+        display = cmdutil.OptionGroup(parser, "Display format options",
 | 
|
2860  | 
+                          "These control the display of data")
 | 
|
2861  | 
+        display.add_option("-r", "--reverse", action="store_true", 
 | 
|
2862  | 
+                          dest="reverse", help="Sort from newest to oldest")
 | 
|
2863  | 
+        display.add_option("-s", "--summary", action="store_true", 
 | 
|
2864  | 
+                          dest="summary", help="Show patchlog summary")
 | 
|
2865  | 
+        display.add_option("-D", "--date", action="store_true", 
 | 
|
2866  | 
+                          dest="date", help="Show patchlog date")
 | 
|
2867  | 
+        display.add_option("-c", "--creator", action="store_true", 
 | 
|
2868  | 
+                          dest="creator", help="Show the id that committed the"
 | 
|
2869  | 
+                          " revision")
 | 
|
2870  | 
+        display.add_option("-m", "--merges", action="store_true", 
 | 
|
2871  | 
+                          dest="merges", help="Show the revisions that were"
 | 
|
2872  | 
+                          " merged")
 | 
|
2873  | 
+        parser.add_option_group(display)
 | 
|
2874  | 
+        return parser 
 | 
|
2875  | 
+    def help(self, parser=None):
 | 
|
2876  | 
+        """Attempt to explain the revisions command
 | 
|
2877  | 
+        
 | 
|
2878  | 
+        :param parser: If supplied, used to determine options
 | 
|
2879  | 
+        """
 | 
|
2880  | 
+        if parser==None:
 | 
|
2881  | 
+            parser=self.get_parser()
 | 
|
2882  | 
+        parser.print_help()
 | 
|
2883  | 
+        print """List revisions.
 | 
|
2884  | 
+        """
 | 
|
2885  | 
+        help_tree_spec()
 | 
|
2886  | 
+
 | 
|
2887  | 
+
 | 
|
2888  | 
+class Get(BaseCommand):
 | 
|
2889  | 
+    """
 | 
|
2890  | 
+    Retrieve a revision from the archive
 | 
|
2891  | 
+    """
 | 
|
2892  | 
+    def __init__(self):
 | 
|
2893  | 
+        self.description="Retrieve a revision from the archive"
 | 
|
2894  | 
+        self.parser=self.get_parser()
 | 
|
2895  | 
+
 | 
|
2896  | 
+
 | 
|
2897  | 
+    def get_completer(self, arg, index):
 | 
|
2898  | 
+        if index > 0:
 | 
|
2899  | 
+            return None
 | 
|
2900  | 
+        try:
 | 
|
2901  | 
+            tree = arch.tree_root()
 | 
|
2902  | 
+        except:
 | 
|
2903  | 
+            tree = None
 | 
|
2904  | 
+        return cmdutil.iter_revision_completions(arg, tree)
 | 
|
2905  | 
+
 | 
|
2906  | 
+
 | 
|
2907  | 
+    def do_command(self, cmdargs):
 | 
|
2908  | 
+        """
 | 
|
2909  | 
+        Master function that perfoms the "get" command.
 | 
|
2910  | 
+        """
 | 
|
2911  | 
+        (options, args) = self.parser.parse_args(cmdargs)
 | 
|
2912  | 
+        if len(args) < 1:
 | 
|
2913  | 
+            return self.help()            
 | 
|
2914  | 
+        try:
 | 
|
2915  | 
+            tree = arch.tree_root()
 | 
|
2916  | 
+        except arch.errors.TreeRootError:
 | 
|
2917  | 
+            tree = None
 | 
|
2918  | 
+        
 | 
|
2919  | 
+        arch_loc = None
 | 
|
2920  | 
+        try:
 | 
|
2921  | 
+            revision, arch_loc = paths.full_path_decode(args[0])
 | 
|
2922  | 
+        except Exception, e:
 | 
|
2923  | 
+            revision = cmdutil.determine_revision_arch(tree, args[0], 
 | 
|
2924  | 
+                check_existence=False, allow_package=True)
 | 
|
2925  | 
+        if len(args) > 1:
 | 
|
2926  | 
+            directory = args[1]
 | 
|
2927  | 
+        else:
 | 
|
2928  | 
+            directory = str(revision.nonarch)
 | 
|
2929  | 
+        if os.path.exists(directory):
 | 
|
2930  | 
+            raise DirectoryExists(directory)
 | 
|
2931  | 
+        cmdutil.ensure_archive_registered(revision.archive, arch_loc)
 | 
|
2932  | 
+        try:
 | 
|
2933  | 
+            cmdutil.ensure_revision_exists(revision)
 | 
|
2934  | 
+        except cmdutil.NoSuchRevision, e:
 | 
|
2935  | 
+            raise CommandFailedWrapper(e)
 | 
|
2936  | 
+
 | 
|
2937  | 
+        link = cmdutil.prompt ("get link")
 | 
|
2938  | 
+        for line in cmdutil.iter_get(revision, directory, link,
 | 
|
2939  | 
+                                     options.no_pristine,
 | 
|
2940  | 
+                                     options.no_greedy_add):
 | 
|
2941  | 
+            cmdutil.colorize(line)
 | 
|
2942  | 
+
 | 
|
2943  | 
+    def get_parser(self):
 | 
|
2944  | 
+        """
 | 
|
2945  | 
+        Returns the options parser to use for the "get" command.
 | 
|
2946  | 
+
 | 
|
2947  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
2948  | 
+        """
 | 
|
2949  | 
+        parser=cmdutil.CmdOptionParser("fai get revision [dir]")
 | 
|
2950  | 
+        parser.add_option("--no-pristine", action="store_true", 
 | 
|
2951  | 
+                         dest="no_pristine", 
 | 
|
2952  | 
+                         help="Do not make pristine copy for reference")
 | 
|
2953  | 
+        parser.add_option("--no-greedy-add", action="store_true", 
 | 
|
2954  | 
+                         dest="no_greedy_add", 
 | 
|
2955  | 
+                         help="Never add to greedy libraries")
 | 
|
2956  | 
+
 | 
|
2957  | 
+        return parser 
 | 
|
2958  | 
+
 | 
|
2959  | 
+    def help(self, parser=None):
 | 
|
2960  | 
+        """
 | 
|
2961  | 
+        Prints a help message.
 | 
|
2962  | 
+
 | 
|
2963  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
2964  | 
+        not supplied, it is retrieved.
 | 
|
2965  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
2966  | 
+        """
 | 
|
2967  | 
+        if parser==None:
 | 
|
2968  | 
+            parser=self.get_parser()
 | 
|
2969  | 
+        parser.print_help()
 | 
|
2970  | 
+        print """
 | 
|
2971  | 
+Expands aliases and constructs a project tree for a revision.  If the optional
 | 
|
2972  | 
+"dir" argument is provided, the project tree will be stored in this directory.
 | 
|
2973  | 
+        """
 | 
|
2974  | 
+        help_tree_spec()
 | 
|
2975  | 
+        return
 | 
|
2976  | 
+
 | 
|
2977  | 
+class PromptCmd(cmd.Cmd):
 | 
|
2978  | 
+    def __init__(self):
 | 
|
2979  | 
+        cmd.Cmd.__init__(self)
 | 
|
2980  | 
+        self.prompt = "Fai> "
 | 
|
2981  | 
+        try:
 | 
|
2982  | 
+            self.tree = arch.tree_root()
 | 
|
2983  | 
+        except:
 | 
|
2984  | 
+            self.tree = None
 | 
|
2985  | 
+        self.set_title()
 | 
|
2986  | 
+        self.set_prompt()
 | 
|
2987  | 
+        self.fake_aba = abacmds.AbaCmds()
 | 
|
2988  | 
+        self.identchars += '-'
 | 
|
2989  | 
+        self.history_file = os.path.expanduser("~/.fai-history")
 | 
|
2990  | 
+        readline.set_completer_delims(string.whitespace)
 | 
|
2991  | 
+        if os.access(self.history_file, os.R_OK) and \
 | 
|
2992  | 
+            os.path.isfile(self.history_file):
 | 
|
2993  | 
+            readline.read_history_file(self.history_file)
 | 
|
2994  | 
+        self.cwd = os.getcwd()
 | 
|
2995  | 
+
 | 
|
2996  | 
+    def write_history(self):
 | 
|
2997  | 
+        readline.write_history_file(self.history_file)
 | 
|
2998  | 
+
 | 
|
2999  | 
+    def do_quit(self, args):
 | 
|
3000  | 
+        self.write_history()
 | 
|
3001  | 
+        sys.exit(0)
 | 
|
3002  | 
+
 | 
|
3003  | 
+    def do_exit(self, args):
 | 
|
3004  | 
+        self.do_quit(args)
 | 
|
3005  | 
+
 | 
|
3006  | 
+    def do_EOF(self, args):
 | 
|
3007  | 
+        print
 | 
|
3008  | 
+        self.do_quit(args)
 | 
|
3009  | 
+
 | 
|
3010  | 
+    def postcmd(self, line, bar):
 | 
|
3011  | 
+        self.set_title()
 | 
|
3012  | 
+        self.set_prompt()
 | 
|
3013  | 
+
 | 
|
3014  | 
+    def set_prompt(self):
 | 
|
3015  | 
+        if self.tree is not None:
 | 
|
3016  | 
+            try:
 | 
|
3017  | 
+                prompt = pylon.alias_or_version(self.tree.tree_version, 
 | 
|
3018  | 
+                                                self.tree, 
 | 
|
3019  | 
+                                                full=False)
 | 
|
3020  | 
+                if prompt is not None:
 | 
|
3021  | 
+                    prompt = " " + prompt
 | 
|
3022  | 
+            except:
 | 
|
3023  | 
+                prompt = ""
 | 
|
3024  | 
+        else:
 | 
|
3025  | 
+            prompt = ""
 | 
|
3026  | 
+        self.prompt = "Fai%s> " % prompt
 | 
|
3027  | 
+
 | 
|
3028  | 
+    def set_title(self, command=None):
 | 
|
3029  | 
+        try:
 | 
|
3030  | 
+            version = pylon.alias_or_version(self.tree.tree_version, self.tree, 
 | 
|
3031  | 
+                                             full=False)
 | 
|
3032  | 
+        except:
 | 
|
3033  | 
+            version = "[no version]"
 | 
|
3034  | 
+        if command is None:
 | 
|
3035  | 
+            command = ""
 | 
|
3036  | 
+        sys.stdout.write(terminal.term_title("Fai %s %s" % (command, version)))
 | 
|
3037  | 
+
 | 
|
3038  | 
+    def do_cd(self, line):
 | 
|
3039  | 
+        if line == "":
 | 
|
3040  | 
+            line = "~"
 | 
|
3041  | 
+        line = os.path.expanduser(line)
 | 
|
3042  | 
+        if os.path.isabs(line):
 | 
|
3043  | 
+            newcwd = line
 | 
|
3044  | 
+        else:
 | 
|
3045  | 
+            newcwd = self.cwd+'/'+line
 | 
|
3046  | 
+        newcwd = os.path.normpath(newcwd)
 | 
|
3047  | 
+        try:
 | 
|
3048  | 
+            os.chdir(newcwd)
 | 
|
3049  | 
+            self.cwd = newcwd
 | 
|
3050  | 
+        except Exception, e:
 | 
|
3051  | 
+            print e
 | 
|
3052  | 
+        try:
 | 
|
3053  | 
+            self.tree = arch.tree_root()
 | 
|
3054  | 
+        except:
 | 
|
3055  | 
+            self.tree = None
 | 
|
3056  | 
+
 | 
|
3057  | 
+    def do_help(self, line):
 | 
|
3058  | 
+        Help()(line)
 | 
|
3059  | 
+
 | 
|
3060  | 
+    def default(self, line):
 | 
|
3061  | 
+        args = line.split()
 | 
|
3062  | 
+        if find_command(args[0]):
 | 
|
3063  | 
+            try:
 | 
|
3064  | 
+                find_command(args[0]).do_command(args[1:])
 | 
|
3065  | 
+            except cmdutil.BadCommandOption, e:
 | 
|
3066  | 
+                print e
 | 
|
3067  | 
+            except cmdutil.GetHelp, e:
 | 
|
3068  | 
+                find_command(args[0]).help()
 | 
|
3069  | 
+            except CommandFailed, e:
 | 
|
3070  | 
+                print e
 | 
|
3071  | 
+            except arch.errors.ArchiveNotRegistered, e:
 | 
|
3072  | 
+                print e
 | 
|
3073  | 
+            except KeyboardInterrupt, e:
 | 
|
3074  | 
+                print "Interrupted"
 | 
|
3075  | 
+            except arch.util.ExecProblem, e:
 | 
|
3076  | 
+                print e.proc.error.rstrip('\n')
 | 
|
3077  | 
+            except cmdutil.CantDetermineVersion, e:
 | 
|
3078  | 
+                print e
 | 
|
3079  | 
+            except cmdutil.CantDetermineRevision, e:
 | 
|
3080  | 
+                print e
 | 
|
3081  | 
+            except Exception, e:
 | 
|
3082  | 
+                print "Unhandled error:\n%s" % errors.exception_str(e)
 | 
|
3083  | 
+
 | 
|
3084  | 
+        elif suggestions.has_key(args[0]):
 | 
|
3085  | 
+            print suggestions[args[0]]
 | 
|
3086  | 
+
 | 
|
3087  | 
+        elif self.fake_aba.is_command(args[0]):
 | 
|
3088  | 
+            tree = None
 | 
|
3089  | 
+            try:
 | 
|
3090  | 
+                tree = arch.tree_root()
 | 
|
3091  | 
+            except arch.errors.TreeRootError:
 | 
|
3092  | 
+                pass
 | 
|
3093  | 
+            cmd = self.fake_aba.is_command(args[0])
 | 
|
3094  | 
+            try:
 | 
|
3095  | 
+                cmd.run(cmdutil.expand_prefix_alias(args[1:], tree))
 | 
|
3096  | 
+            except KeyboardInterrupt, e:
 | 
|
3097  | 
+                print "Interrupted"
 | 
|
3098  | 
+
 | 
|
3099  | 
+        elif options.tla_fallthrough and args[0] != "rm" and \
 | 
|
3100  | 
+            cmdutil.is_tla_command(args[0]):
 | 
|
3101  | 
+            try:
 | 
|
3102  | 
+                tree = None
 | 
|
3103  | 
+                try:
 | 
|
3104  | 
+                    tree = arch.tree_root()
 | 
|
3105  | 
+                except arch.errors.TreeRootError:
 | 
|
3106  | 
+                    pass
 | 
|
3107  | 
+                args = cmdutil.expand_prefix_alias(args, tree)
 | 
|
3108  | 
+                arch.util.exec_safe('tla', args, stderr=sys.stderr,
 | 
|
3109  | 
+                expected=(0, 1))
 | 
|
3110  | 
+            except arch.util.ExecProblem, e:
 | 
|
3111  | 
+                pass
 | 
|
3112  | 
+            except KeyboardInterrupt, e:
 | 
|
3113  | 
+                print "Interrupted"
 | 
|
3114  | 
+        else:
 | 
|
3115  | 
+            try:
 | 
|
3116  | 
+                try:
 | 
|
3117  | 
+                    tree = arch.tree_root()
 | 
|
3118  | 
+                except arch.errors.TreeRootError:
 | 
|
3119  | 
+                    tree = None
 | 
|
3120  | 
+                args=line.split()
 | 
|
3121  | 
+                os.system(" ".join(cmdutil.expand_prefix_alias(args, tree)))
 | 
|
3122  | 
+            except KeyboardInterrupt, e:
 | 
|
3123  | 
+                print "Interrupted"
 | 
|
3124  | 
+
 | 
|
3125  | 
+    def completenames(self, text, line, begidx, endidx):
 | 
|
3126  | 
+        completions = []
 | 
|
3127  | 
+        iter = iter_command_names(self.fake_aba)
 | 
|
3128  | 
+        try:
 | 
|
3129  | 
+            if len(line) > 0:
 | 
|
3130  | 
+                arg = line.split()[-1]
 | 
|
3131  | 
+            else:
 | 
|
3132  | 
+                arg = ""
 | 
|
3133  | 
+            iter = cmdutil.iter_munged_completions(iter, arg, text)
 | 
|
3134  | 
+        except Exception, e:
 | 
|
3135  | 
+            print e
 | 
|
3136  | 
+        return list(iter)
 | 
|
3137  | 
+
 | 
|
3138  | 
+    def completedefault(self, text, line, begidx, endidx):
 | 
|
3139  | 
+        """Perform completion for native commands.
 | 
|
3140  | 
+        
 | 
|
3141  | 
+        :param text: The text to complete
 | 
|
3142  | 
+        :type text: str
 | 
|
3143  | 
+        :param line: The entire line to complete
 | 
|
3144  | 
+        :type line: str
 | 
|
3145  | 
+        :param begidx: The start of the text in the line
 | 
|
3146  | 
+        :type begidx: int
 | 
|
3147  | 
+        :param endidx: The end of the text in the line
 | 
|
3148  | 
+        :type endidx: int
 | 
|
3149  | 
+        """
 | 
|
3150  | 
+        try:
 | 
|
3151  | 
+            (cmd, args, foo) = self.parseline(line)
 | 
|
3152  | 
+            command_obj=find_command(cmd)
 | 
|
3153  | 
+            if command_obj is not None:
 | 
|
3154  | 
+                return command_obj.complete(args.split(), text)
 | 
|
3155  | 
+            elif not self.fake_aba.is_command(cmd) and \
 | 
|
3156  | 
+                cmdutil.is_tla_command(cmd):
 | 
|
3157  | 
+                iter = cmdutil.iter_supported_switches(cmd)
 | 
|
3158  | 
+                if len(args) > 0:
 | 
|
3159  | 
+                    arg = args.split()[-1]
 | 
|
3160  | 
+                else:
 | 
|
3161  | 
+                    arg = ""
 | 
|
3162  | 
+                if arg.startswith("-"):
 | 
|
3163  | 
+                    return list(cmdutil.iter_munged_completions(iter, arg, 
 | 
|
3164  | 
+                                                                text))
 | 
|
3165  | 
+                else:
 | 
|
3166  | 
+                    return list(cmdutil.iter_munged_completions(
 | 
|
3167  | 
+                        cmdutil.iter_file_completions(arg), arg, text))
 | 
|
3168  | 
+
 | 
|
3169  | 
+
 | 
|
3170  | 
+            elif cmd == "cd":
 | 
|
3171  | 
+                if len(args) > 0:
 | 
|
3172  | 
+                    arg = args.split()[-1]
 | 
|
3173  | 
+                else:
 | 
|
3174  | 
+                    arg = ""
 | 
|
3175  | 
+                iter = cmdutil.iter_dir_completions(arg)
 | 
|
3176  | 
+                iter = cmdutil.iter_munged_completions(iter, arg, text)
 | 
|
3177  | 
+                return list(iter)
 | 
|
3178  | 
+            elif len(args)>0:
 | 
|
3179  | 
+                arg = args.split()[-1]
 | 
|
3180  | 
+                iter = cmdutil.iter_file_completions(arg)
 | 
|
3181  | 
+                return list(cmdutil.iter_munged_completions(iter, arg, text))
 | 
|
3182  | 
+            else:
 | 
|
3183  | 
+                return self.completenames(text, line, begidx, endidx)
 | 
|
3184  | 
+        except Exception, e:
 | 
|
3185  | 
+            print e
 | 
|
3186  | 
+
 | 
|
3187  | 
+
 | 
|
3188  | 
+def iter_command_names(fake_aba):
 | 
|
3189  | 
+    for entry in cmdutil.iter_combine([commands.iterkeys(), 
 | 
|
3190  | 
+                                     fake_aba.get_commands(), 
 | 
|
3191  | 
+                                     cmdutil.iter_tla_commands(False)]):
 | 
|
3192  | 
+        if not suggestions.has_key(str(entry)):
 | 
|
3193  | 
+            yield entry
 | 
|
3194  | 
+
 | 
|
3195  | 
+
 | 
|
3196  | 
+def iter_source_file_completions(tree, arg):
 | 
|
3197  | 
+    treepath = arch_compound.tree_cwd(tree)
 | 
|
3198  | 
+    if len(treepath) > 0:
 | 
|
3199  | 
+        dirs = [treepath]
 | 
|
3200  | 
+    else:
 | 
|
3201  | 
+        dirs = None
 | 
|
3202  | 
+    for file in tree.iter_inventory(dirs, source=True, both=True):
 | 
|
3203  | 
+        file = file_completion_match(file, treepath, arg)
 | 
|
3204  | 
+        if file is not None:
 | 
|
3205  | 
+            yield file
 | 
|
3206  | 
+
 | 
|
3207  | 
+
 | 
|
3208  | 
+def iter_untagged(tree, dirs):
 | 
|
3209  | 
+    for file in arch_core.iter_inventory_filter(tree, dirs, tagged=False, 
 | 
|
3210  | 
+                                                categories=arch_core.non_root,
 | 
|
3211  | 
+                                                control_files=True):
 | 
|
3212  | 
+        yield file.name 
 | 
|
3213  | 
+
 | 
|
3214  | 
+
 | 
|
3215  | 
+def iter_untagged_completions(tree, arg):
 | 
|
3216  | 
+    """Generate an iterator for all visible untagged files that match arg.
 | 
|
3217  | 
+
 | 
|
3218  | 
+    :param tree: The tree to look for untagged files in
 | 
|
3219  | 
+    :type tree: `arch.WorkingTree`
 | 
|
3220  | 
+    :param arg: The argument to match
 | 
|
3221  | 
+    :type arg: str
 | 
|
3222  | 
+    :return: An iterator of all matching untagged files
 | 
|
3223  | 
+    :rtype: iterator of str
 | 
|
3224  | 
+    """
 | 
|
3225  | 
+    treepath = arch_compound.tree_cwd(tree)
 | 
|
3226  | 
+    if len(treepath) > 0:
 | 
|
3227  | 
+        dirs = [treepath]
 | 
|
3228  | 
+    else:
 | 
|
3229  | 
+        dirs = None
 | 
|
3230  | 
+
 | 
|
3231  | 
+    for file in iter_untagged(tree, dirs):
 | 
|
3232  | 
+        file = file_completion_match(file, treepath, arg)
 | 
|
3233  | 
+        if file is not None:
 | 
|
3234  | 
+            yield file
 | 
|
3235  | 
+
 | 
|
3236  | 
+
 | 
|
3237  | 
+def file_completion_match(file, treepath, arg):
 | 
|
3238  | 
+    """Determines whether a file within an arch tree matches the argument.
 | 
|
3239  | 
+
 | 
|
3240  | 
+    :param file: The rooted filename
 | 
|
3241  | 
+    :type file: str
 | 
|
3242  | 
+    :param treepath: The path to the cwd within the tree
 | 
|
3243  | 
+    :type treepath: str
 | 
|
3244  | 
+    :param arg: The prefix to match
 | 
|
3245  | 
+    :return: The completion name, or None if not a match
 | 
|
3246  | 
+    :rtype: str
 | 
|
3247  | 
+    """
 | 
|
3248  | 
+    if not file.startswith(treepath):
 | 
|
3249  | 
+        return None
 | 
|
3250  | 
+    if treepath != "":
 | 
|
3251  | 
+        file = file[len(treepath)+1:]
 | 
|
3252  | 
+
 | 
|
3253  | 
+    if not file.startswith(arg):
 | 
|
3254  | 
+        return None 
 | 
|
3255  | 
+    if os.path.isdir(file):
 | 
|
3256  | 
+        file += '/'
 | 
|
3257  | 
+    return file
 | 
|
3258  | 
+
 | 
|
3259  | 
+def iter_modified_file_completions(tree, arg):
 | 
|
3260  | 
+    """Returns a list of modified files that match the specified prefix.
 | 
|
3261  | 
+
 | 
|
3262  | 
+    :param tree: The current tree
 | 
|
3263  | 
+    :type tree: `arch.WorkingTree`
 | 
|
3264  | 
+    :param arg: The prefix to match
 | 
|
3265  | 
+    :type arg: str
 | 
|
3266  | 
+    """
 | 
|
3267  | 
+    treepath = arch_compound.tree_cwd(tree)
 | 
|
3268  | 
+    tmpdir = util.tmpdir()
 | 
|
3269  | 
+    changeset = tmpdir+"/changeset"
 | 
|
3270  | 
+    completions = []
 | 
|
3271  | 
+    revision = cmdutil.determine_revision_tree(tree)
 | 
|
3272  | 
+    for line in arch.iter_delta(revision, tree, changeset):
 | 
|
3273  | 
+        if isinstance(line, arch.FileModification):
 | 
|
3274  | 
+            file = file_completion_match(line.name[1:], treepath, arg)
 | 
|
3275  | 
+            if file is not None:
 | 
|
3276  | 
+                completions.append(file)
 | 
|
3277  | 
+    shutil.rmtree(tmpdir)
 | 
|
3278  | 
+    return completions
 | 
|
3279  | 
+
 | 
|
3280  | 
+class Shell(BaseCommand):
 | 
|
3281  | 
+    def __init__(self):
 | 
|
3282  | 
+        self.description = "Runs Fai as a shell"
 | 
|
3283  | 
+
 | 
|
3284  | 
+    def do_command(self, cmdargs):
 | 
|
3285  | 
+        if len(cmdargs)!=0:
 | 
|
3286  | 
+            raise cmdutil.GetHelp
 | 
|
3287  | 
+        prompt = PromptCmd()
 | 
|
3288  | 
+        try:
 | 
|
3289  | 
+            prompt.cmdloop()
 | 
|
3290  | 
+        finally:
 | 
|
3291  | 
+            prompt.write_history()
 | 
|
3292  | 
+
 | 
|
3293  | 
+class AddID(BaseCommand):
 | 
|
3294  | 
+    """
 | 
|
3295  | 
+    Adds an inventory id for the given file
 | 
|
3296  | 
+    """
 | 
|
3297  | 
+    def __init__(self):
 | 
|
3298  | 
+        self.description="Add an inventory id for a given file"
 | 
|
3299  | 
+
 | 
|
3300  | 
+    def get_completer(self, arg, index):
 | 
|
3301  | 
+        tree = arch.tree_root()
 | 
|
3302  | 
+        return iter_untagged_completions(tree, arg)
 | 
|
3303  | 
+
 | 
|
3304  | 
+    def do_command(self, cmdargs):
 | 
|
3305  | 
+        """
 | 
|
3306  | 
+        Master function that perfoms the "revision" command.
 | 
|
3307  | 
+        """
 | 
|
3308  | 
+        parser=self.get_parser()
 | 
|
3309  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
3310  | 
+
 | 
|
3311  | 
+        try:
 | 
|
3312  | 
+            tree = arch.tree_root()
 | 
|
3313  | 
+        except arch.errors.TreeRootError, e:
 | 
|
3314  | 
+            raise pylon.errors.CommandFailedWrapper(e)
 | 
|
3315  | 
+            
 | 
|
3316  | 
+
 | 
|
3317  | 
+        if (len(args) == 0) == (options.untagged == False):
 | 
|
3318  | 
+            raise cmdutil.GetHelp
 | 
|
3319  | 
+
 | 
|
3320  | 
+       #if options.id and len(args) != 1:
 | 
|
3321  | 
+       #    print "If --id is specified, only one file can be named."
 | 
|
3322  | 
+       #    return
 | 
|
3323  | 
+        
 | 
|
3324  | 
+        method = tree.tagging_method
 | 
|
3325  | 
+        
 | 
|
3326  | 
+        if options.id_type == "tagline":
 | 
|
3327  | 
+            if method != "tagline":
 | 
|
3328  | 
+                if not cmdutil.prompt("Tagline in other tree"):
 | 
|
3329  | 
+                    if method == "explicit" or method == "implicit":
 | 
|
3330  | 
+                        options.id_type == method
 | 
|
3331  | 
+                    else:
 | 
|
3332  | 
+                        print "add-id not supported for \"%s\" tagging method"\
 | 
|
3333  | 
+                            % method 
 | 
|
3334  | 
+                        return
 | 
|
3335  | 
+        
 | 
|
3336  | 
+        elif options.id_type == "implicit":
 | 
|
3337  | 
+            if method != "implicit":
 | 
|
3338  | 
+                if not cmdutil.prompt("Implicit in other tree"):
 | 
|
3339  | 
+                    if method == "explicit" or method == "tagline":
 | 
|
3340  | 
+                        options.id_type == method
 | 
|
3341  | 
+                    else:
 | 
|
3342  | 
+                        print "add-id not supported for \"%s\" tagging method"\
 | 
|
3343  | 
+                            % method 
 | 
|
3344  | 
+                        return
 | 
|
3345  | 
+        elif options.id_type == "explicit":
 | 
|
3346  | 
+            if method != "tagline" and method != explicit:
 | 
|
3347  | 
+                if not prompt("Explicit in other tree"):
 | 
|
3348  | 
+                    print "add-id not supported for \"%s\" tagging method" % \
 | 
|
3349  | 
+                        method
 | 
|
3350  | 
+                    return
 | 
|
3351  | 
+        
 | 
|
3352  | 
+        if options.id_type == "auto":
 | 
|
3353  | 
+            if method != "tagline" and method != "explicit" \
 | 
|
3354  | 
+                and method !="implicit":
 | 
|
3355  | 
+                print "add-id not supported for \"%s\" tagging method" % method
 | 
|
3356  | 
+                return
 | 
|
3357  | 
+            else:
 | 
|
3358  | 
+                options.id_type = method
 | 
|
3359  | 
+        if options.untagged:
 | 
|
3360  | 
+            args = None
 | 
|
3361  | 
+        self.add_ids(tree, options.id_type, args)
 | 
|
3362  | 
+
 | 
|
3363  | 
+    def add_ids(self, tree, id_type, files=()):
 | 
|
3364  | 
+        """Add inventory ids to files.
 | 
|
3365  | 
+        
 | 
|
3366  | 
+        :param tree: the tree the files are in
 | 
|
3367  | 
+        :type tree: `arch.WorkingTree`
 | 
|
3368  | 
+        :param id_type: the type of id to add: "explicit" or "tagline"
 | 
|
3369  | 
+        :type id_type: str
 | 
|
3370  | 
+        :param files: The list of files to add.  If None do all untagged.
 | 
|
3371  | 
+        :type files: tuple of str
 | 
|
3372  | 
+        """
 | 
|
3373  | 
+
 | 
|
3374  | 
+        untagged = (files is None)
 | 
|
3375  | 
+        if untagged:
 | 
|
3376  | 
+            files = list(iter_untagged(tree, None))
 | 
|
3377  | 
+        previous_files = []
 | 
|
3378  | 
+        while len(files) > 0:
 | 
|
3379  | 
+            previous_files.extend(files)
 | 
|
3380  | 
+            if id_type == "explicit":
 | 
|
3381  | 
+                cmdutil.add_id(files)
 | 
|
3382  | 
+            elif id_type == "tagline" or id_type == "implicit":
 | 
|
3383  | 
+                for file in files:
 | 
|
3384  | 
+                    try:
 | 
|
3385  | 
+                        implicit = (id_type == "implicit")
 | 
|
3386  | 
+                        cmdutil.add_tagline_or_explicit_id(file, False,
 | 
|
3387  | 
+                                                           implicit)
 | 
|
3388  | 
+                    except cmdutil.AlreadyTagged:
 | 
|
3389  | 
+                        print "\"%s\" already has a tagline." % file
 | 
|
3390  | 
+                    except cmdutil.NoCommentSyntax:
 | 
|
3391  | 
+                        pass
 | 
|
3392  | 
+            #do inventory after tagging until no untagged files are encountered
 | 
|
3393  | 
+            if untagged:
 | 
|
3394  | 
+                files = []
 | 
|
3395  | 
+                for file in iter_untagged(tree, None):
 | 
|
3396  | 
+                    if not file in previous_files:
 | 
|
3397  | 
+                        files.append(file)
 | 
|
3398  | 
+
 | 
|
3399  | 
+            else:
 | 
|
3400  | 
+                break
 | 
|
3401  | 
+
 | 
|
3402  | 
+    def get_parser(self):
 | 
|
3403  | 
+        """
 | 
|
3404  | 
+        Returns the options parser to use for the "revision" command.
 | 
|
3405  | 
+
 | 
|
3406  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
3407  | 
+        """
 | 
|
3408  | 
+        parser=cmdutil.CmdOptionParser("fai add-id file1 [file2] [file3]...")
 | 
|
3409  | 
+# ddaa suggests removing this to promote GUIDs.  Let's see who squalks.
 | 
|
3410  | 
+#        parser.add_option("-i", "--id", dest="id", 
 | 
|
3411  | 
+#                         help="Specify id for a single file", default=None)
 | 
|
3412  | 
+        parser.add_option("--tltl", action="store_true", 
 | 
|
3413  | 
+                         dest="lord_style",  help="Use Tom Lord's style of id.")
 | 
|
3414  | 
+        parser.add_option("--explicit", action="store_const", 
 | 
|
3415  | 
+                         const="explicit", dest="id_type", 
 | 
|
3416  | 
+                         help="Use an explicit id", default="auto")
 | 
|
3417  | 
+        parser.add_option("--tagline", action="store_const", 
 | 
|
3418  | 
+                         const="tagline", dest="id_type", 
 | 
|
3419  | 
+                         help="Use a tagline id")
 | 
|
3420  | 
+        parser.add_option("--implicit", action="store_const", 
 | 
|
3421  | 
+                         const="implicit", dest="id_type", 
 | 
|
3422  | 
+                         help="Use an implicit id (deprecated)")
 | 
|
3423  | 
+        parser.add_option("--untagged", action="store_true", 
 | 
|
3424  | 
+                         dest="untagged", default=False, 
 | 
|
3425  | 
+                         help="tag all untagged files")
 | 
|
3426  | 
+        return parser 
 | 
|
3427  | 
+
 | 
|
3428  | 
+    def help(self, parser=None):
 | 
|
3429  | 
+        """
 | 
|
3430  | 
+        Prints a help message.
 | 
|
3431  | 
+
 | 
|
3432  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
3433  | 
+        not supplied, it is retrieved.
 | 
|
3434  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
3435  | 
+        """
 | 
|
3436  | 
+        if parser==None:
 | 
|
3437  | 
+            parser=self.get_parser()
 | 
|
3438  | 
+        parser.print_help()
 | 
|
3439  | 
+        print """
 | 
|
3440  | 
+Adds an inventory to the specified file(s) and directories.  If --untagged is
 | 
|
3441  | 
+specified, adds inventory to all untagged files and directories.
 | 
|
3442  | 
+        """
 | 
|
3443  | 
+        return
 | 
|
3444  | 
+
 | 
|
3445  | 
+
 | 
|
3446  | 
+class Merge(BaseCommand):
 | 
|
3447  | 
+    """
 | 
|
3448  | 
+    Merges changes from other versions into the current tree
 | 
|
3449  | 
+    """
 | 
|
3450  | 
+    def __init__(self):
 | 
|
3451  | 
+        self.description="Merges changes from other versions"
 | 
|
3452  | 
+        try:
 | 
|
3453  | 
+            self.tree = arch.tree_root()
 | 
|
3454  | 
+        except:
 | 
|
3455  | 
+            self.tree = None
 | 
|
3456  | 
+
 | 
|
3457  | 
+
 | 
|
3458  | 
+    def get_completer(self, arg, index):
 | 
|
3459  | 
+        if self.tree is None:
 | 
|
3460  | 
+            raise arch.errors.TreeRootError
 | 
|
3461  | 
+        return cmdutil.merge_completions(self.tree, arg, index)
 | 
|
3462  | 
+
 | 
|
3463  | 
+    def do_command(self, cmdargs):
 | 
|
3464  | 
+        """
 | 
|
3465  | 
+        Master function that perfoms the "merge" command.
 | 
|
3466  | 
+        """
 | 
|
3467  | 
+        parser=self.get_parser()
 | 
|
3468  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
3469  | 
+        if options.diff3:
 | 
|
3470  | 
+            action="star-merge"
 | 
|
3471  | 
+        else:
 | 
|
3472  | 
+            action = options.action
 | 
|
3473  | 
+        
 | 
|
3474  | 
+        if self.tree is None:
 | 
|
3475  | 
+            raise arch.errors.TreeRootError(os.getcwd())
 | 
|
3476  | 
+        if cmdutil.has_changed(ancillary.comp_revision(self.tree)):
 | 
|
3477  | 
+            raise UncommittedChanges(self.tree)
 | 
|
3478  | 
+
 | 
|
3479  | 
+        if len(args) > 0:
 | 
|
3480  | 
+            revisions = []
 | 
|
3481  | 
+            for arg in args:
 | 
|
3482  | 
+                revisions.append(cmdutil.determine_revision_arch(self.tree, 
 | 
|
3483  | 
+                                                                 arg))
 | 
|
3484  | 
+            source = "from commandline"
 | 
|
3485  | 
+        else:
 | 
|
3486  | 
+            revisions = ancillary.iter_partner_revisions(self.tree, 
 | 
|
3487  | 
+                                                         self.tree.tree_version)
 | 
|
3488  | 
+            source = "from partner version"
 | 
|
3489  | 
+        revisions = misc.rewind_iterator(revisions)
 | 
|
3490  | 
+        try:
 | 
|
3491  | 
+            revisions.next()
 | 
|
3492  | 
+            revisions.rewind()
 | 
|
3493  | 
+        except StopIteration, e:
 | 
|
3494  | 
+            revision = cmdutil.tag_cur(self.tree)
 | 
|
3495  | 
+            if revision is None:
 | 
|
3496  | 
+                raise CantDetermineRevision("", "No version specified, no "
 | 
|
3497  | 
+                                            "partner-versions, and no tag"
 | 
|
3498  | 
+                                            " source")
 | 
|
3499  | 
+            revisions = [revision]
 | 
|
3500  | 
+            source = "from tag source"
 | 
|
3501  | 
+        for revision in revisions:
 | 
|
3502  | 
+            cmdutil.ensure_archive_registered(revision.archive)
 | 
|
3503  | 
+            cmdutil.colorize(arch.Chatter("* Merging %s [%s]" % 
 | 
|
3504  | 
+                             (revision, source)))
 | 
|
3505  | 
+            if action=="native-merge" or action=="update":
 | 
|
3506  | 
+                if self.native_merge(revision, action) == 0:
 | 
|
3507  | 
+                    continue
 | 
|
3508  | 
+            elif action=="star-merge":
 | 
|
3509  | 
+                try: 
 | 
|
3510  | 
+                    self.star_merge(revision, options.diff3)
 | 
|
3511  | 
+                except errors.MergeProblem, e:
 | 
|
3512  | 
+                    break
 | 
|
3513  | 
+            if cmdutil.has_changed(self.tree.tree_version):
 | 
|
3514  | 
+                break
 | 
|
3515  | 
+
 | 
|
3516  | 
+    def star_merge(self, revision, diff3):
 | 
|
3517  | 
+        """Perform a star-merge on the current tree.
 | 
|
3518  | 
+        
 | 
|
3519  | 
+        :param revision: The revision to use for the merge
 | 
|
3520  | 
+        :type revision: `arch.Revision`
 | 
|
3521  | 
+        :param diff3: If true, do a diff3 merge
 | 
|
3522  | 
+        :type diff3: bool
 | 
|
3523  | 
+        """
 | 
|
3524  | 
+        try:
 | 
|
3525  | 
+            for line in self.tree.iter_star_merge(revision, diff3=diff3):
 | 
|
3526  | 
+                cmdutil.colorize(line)
 | 
|
3527  | 
+        except arch.util.ExecProblem, e:
 | 
|
3528  | 
+            if e.proc.status is not None and e.proc.status == 1:
 | 
|
3529  | 
+                if e.proc.error:
 | 
|
3530  | 
+                    print e.proc.error
 | 
|
3531  | 
+                raise MergeProblem
 | 
|
3532  | 
+            else:
 | 
|
3533  | 
+                raise
 | 
|
3534  | 
+
 | 
|
3535  | 
+    def native_merge(self, other_revision, action):
 | 
|
3536  | 
+        """Perform a native-merge on the current tree.
 | 
|
3537  | 
+        
 | 
|
3538  | 
+        :param other_revision: The revision to use for the merge
 | 
|
3539  | 
+        :type other_revision: `arch.Revision`
 | 
|
3540  | 
+        :return: 0 if the merge was skipped, 1 if it was applied
 | 
|
3541  | 
+        """
 | 
|
3542  | 
+        other_tree = arch_compound.find_or_make_local_revision(other_revision)
 | 
|
3543  | 
+        try:
 | 
|
3544  | 
+            if action == "native-merge":
 | 
|
3545  | 
+                ancestor = arch_compound.merge_ancestor2(self.tree, other_tree, 
 | 
|
3546  | 
+                                                         other_revision)
 | 
|
3547  | 
+            elif action == "update":
 | 
|
3548  | 
+                ancestor = arch_compound.tree_latest(self.tree, 
 | 
|
3549  | 
+                                                     other_revision.version)
 | 
|
3550  | 
+        except CantDetermineRevision, e:
 | 
|
3551  | 
+            raise CommandFailedWrapper(e)
 | 
|
3552  | 
+        cmdutil.colorize(arch.Chatter("* Found common ancestor %s" % ancestor))
 | 
|
3553  | 
+        if (ancestor == other_revision):
 | 
|
3554  | 
+            cmdutil.colorize(arch.Chatter("* Skipping redundant merge" 
 | 
|
3555  | 
+                                          % ancestor))
 | 
|
3556  | 
+            return 0
 | 
|
3557  | 
+        delta = cmdutil.apply_delta(ancestor, other_tree, self.tree)    
 | 
|
3558  | 
+        for line in cmdutil.iter_apply_delta_filter(delta):
 | 
|
3559  | 
+            cmdutil.colorize(line)
 | 
|
3560  | 
+        return 1
 | 
|
3561  | 
+
 | 
|
3562  | 
+
 | 
|
3563  | 
+
 | 
|
3564  | 
+    def get_parser(self):
 | 
|
3565  | 
+        """
 | 
|
3566  | 
+        Returns the options parser to use for the "merge" command.
 | 
|
3567  | 
+
 | 
|
3568  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
3569  | 
+        """
 | 
|
3570  | 
+        parser=cmdutil.CmdOptionParser("fai merge [VERSION]")
 | 
|
3571  | 
+        parser.add_option("-s", "--star-merge", action="store_const",
 | 
|
3572  | 
+                          dest="action", help="Use star-merge",
 | 
|
3573  | 
+                          const="star-merge", default="native-merge")
 | 
|
3574  | 
+        parser.add_option("--update", action="store_const",
 | 
|
3575  | 
+                          dest="action", help="Use update picker",
 | 
|
3576  | 
+                          const="update")
 | 
|
3577  | 
+        parser.add_option("--diff3", action="store_true", 
 | 
|
3578  | 
+                         dest="diff3",  
 | 
|
3579  | 
+                         help="Use diff3 for merge (implies star-merge)")
 | 
|
3580  | 
+        return parser 
 | 
|
3581  | 
+
 | 
|
3582  | 
+    def help(self, parser=None):
 | 
|
3583  | 
+        """
 | 
|
3584  | 
+        Prints a help message.
 | 
|
3585  | 
+
 | 
|
3586  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
3587  | 
+        not supplied, it is retrieved.
 | 
|
3588  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
3589  | 
+        """
 | 
|
3590  | 
+        if parser==None:
 | 
|
3591  | 
+            parser=self.get_parser()
 | 
|
3592  | 
+        parser.print_help()
 | 
|
3593  | 
+        print """
 | 
|
3594  | 
+Performs a merge operation using the specified version.
 | 
|
3595  | 
+        """
 | 
|
3596  | 
+        return
 | 
|
3597  | 
+
 | 
|
3598  | 
+class ELog(BaseCommand):
 | 
|
3599  | 
+    """
 | 
|
3600  | 
+    Produces a raw patchlog and invokes the user's editor
 | 
|
3601  | 
+    """
 | 
|
3602  | 
+    def __init__(self):
 | 
|
3603  | 
+        self.description="Edit a patchlog to commit"
 | 
|
3604  | 
+        try:
 | 
|
3605  | 
+            self.tree = arch.tree_root()
 | 
|
3606  | 
+        except:
 | 
|
3607  | 
+            self.tree = None
 | 
|
3608  | 
+
 | 
|
3609  | 
+
 | 
|
3610  | 
+    def do_command(self, cmdargs):
 | 
|
3611  | 
+        """
 | 
|
3612  | 
+        Master function that perfoms the "elog" command.
 | 
|
3613  | 
+        """
 | 
|
3614  | 
+        parser=self.get_parser()
 | 
|
3615  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
3616  | 
+        if self.tree is None:
 | 
|
3617  | 
+            raise arch.errors.TreeRootError
 | 
|
3618  | 
+
 | 
|
3619  | 
+        try:
 | 
|
3620  | 
+            edit_log(self.tree, self.tree.tree_version)
 | 
|
3621  | 
+        except pylon.errors.NoEditorSpecified, e:
 | 
|
3622  | 
+            raise pylon.errors.CommandFailedWrapper(e)
 | 
|
3623  | 
+
 | 
|
3624  | 
+    def get_parser(self):
 | 
|
3625  | 
+        """
 | 
|
3626  | 
+        Returns the options parser to use for the "merge" command.
 | 
|
3627  | 
+
 | 
|
3628  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
3629  | 
+        """
 | 
|
3630  | 
+        parser=cmdutil.CmdOptionParser("fai elog")
 | 
|
3631  | 
+        return parser 
 | 
|
3632  | 
+
 | 
|
3633  | 
+
 | 
|
3634  | 
+    def help(self, parser=None):
 | 
|
3635  | 
+        """
 | 
|
3636  | 
+        Invokes $EDITOR to produce a log for committing.
 | 
|
3637  | 
+
 | 
|
3638  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
3639  | 
+        not supplied, it is retrieved.
 | 
|
3640  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
3641  | 
+        """
 | 
|
3642  | 
+        if parser==None:
 | 
|
3643  | 
+            parser=self.get_parser()
 | 
|
3644  | 
+        parser.print_help()
 | 
|
3645  | 
+        print """
 | 
|
3646  | 
+Invokes $EDITOR to produce a log for committing.
 | 
|
3647  | 
+        """
 | 
|
3648  | 
+        return
 | 
|
3649  | 
+
 | 
|
3650  | 
+def edit_log(tree, version):
 | 
|
3651  | 
+    """Makes and edits the log for a tree.  Does all kinds of fancy things
 | 
|
3652  | 
+    like log templates and merge summaries and log-for-merge
 | 
|
3653  | 
+    
 | 
|
3654  | 
+    :param tree: The tree to edit the log for
 | 
|
3655  | 
+    :type tree: `arch.WorkingTree`
 | 
|
3656  | 
+    """
 | 
|
3657  | 
+    #ensure we have an editor before preparing the log
 | 
|
3658  | 
+    cmdutil.find_editor()
 | 
|
3659  | 
+    log = tree.log_message(create=False, version=version)
 | 
|
3660  | 
+    log_is_new = False
 | 
|
3661  | 
+    if log is None or cmdutil.prompt("Overwrite log"):
 | 
|
3662  | 
+        if log is not None:
 | 
|
3663  | 
+           os.remove(log.name)
 | 
|
3664  | 
+        log = tree.log_message(create=True, version=version)
 | 
|
3665  | 
+        log_is_new = True
 | 
|
3666  | 
+        tmplog = log.name
 | 
|
3667  | 
+        template = pylon.log_template_path(tree)
 | 
|
3668  | 
+        if template:
 | 
|
3669  | 
+            shutil.copyfile(template, tmplog)
 | 
|
3670  | 
+        comp_version = ancillary.comp_revision(tree).version
 | 
|
3671  | 
+        new_merges = cmdutil.iter_new_merges(tree, comp_version)
 | 
|
3672  | 
+        new_merges = cmdutil.direct_merges(new_merges)
 | 
|
3673  | 
+        log["Summary"] = pylon.merge_summary(new_merges, 
 | 
|
3674  | 
+                                         version)
 | 
|
3675  | 
+        if len(new_merges) > 0:   
 | 
|
3676  | 
+            if cmdutil.prompt("Log for merge"):
 | 
|
3677  | 
+                if cmdutil.prompt("changelog for merge"):
 | 
|
3678  | 
+                    mergestuff = "Patches applied:\n"
 | 
|
3679  | 
+                    mergestuff += pylon.changelog_for_merge(new_merges)
 | 
|
3680  | 
+                else:
 | 
|
3681  | 
+                    mergestuff = cmdutil.log_for_merge(tree, comp_version)
 | 
|
3682  | 
+                log.description += mergestuff
 | 
|
3683  | 
+        log.save()
 | 
|
3684  | 
+    try:
 | 
|
3685  | 
+        cmdutil.invoke_editor(log.name)
 | 
|
3686  | 
+    except:
 | 
|
3687  | 
+        if log_is_new:
 | 
|
3688  | 
+            os.remove(log.name)
 | 
|
3689  | 
+        raise
 | 
|
3690  | 
+
 | 
|
3691  | 
+
 | 
|
3692  | 
+class MirrorArchive(BaseCommand):
 | 
|
3693  | 
+    """
 | 
|
3694  | 
+    Updates a mirror from an archive
 | 
|
3695  | 
+    """
 | 
|
3696  | 
+    def __init__(self):
 | 
|
3697  | 
+        self.description="Update a mirror from an archive"
 | 
|
3698  | 
+
 | 
|
3699  | 
+    def do_command(self, cmdargs):
 | 
|
3700  | 
+        """
 | 
|
3701  | 
+        Master function that perfoms the "revision" command.
 | 
|
3702  | 
+        """
 | 
|
3703  | 
+
 | 
|
3704  | 
+        parser=self.get_parser()
 | 
|
3705  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
3706  | 
+        if len(args) > 1:
 | 
|
3707  | 
+            raise GetHelp
 | 
|
3708  | 
+        try:
 | 
|
3709  | 
+            tree = arch.tree_root()
 | 
|
3710  | 
+        except:
 | 
|
3711  | 
+            tree = None
 | 
|
3712  | 
+
 | 
|
3713  | 
+        if len(args) == 0:
 | 
|
3714  | 
+            if tree is not None:
 | 
|
3715  | 
+                name = tree.tree_version()
 | 
|
3716  | 
+        else:
 | 
|
3717  | 
+            name = cmdutil.expand_alias(args[0], tree)
 | 
|
3718  | 
+            name = arch.NameParser(name)
 | 
|
3719  | 
+
 | 
|
3720  | 
+        to_arch = name.get_archive()
 | 
|
3721  | 
+        from_arch = cmdutil.get_mirror_source(arch.Archive(to_arch))
 | 
|
3722  | 
+        limit = name.get_nonarch()
 | 
|
3723  | 
+
 | 
|
3724  | 
+        iter = arch_core.mirror_archive(from_arch,to_arch, limit)
 | 
|
3725  | 
+        for line in arch.chatter_classifier(iter):
 | 
|
3726  | 
+            cmdutil.colorize(line)
 | 
|
3727  | 
+
 | 
|
3728  | 
+    def get_parser(self):
 | 
|
3729  | 
+        """
 | 
|
3730  | 
+        Returns the options parser to use for the "revision" command.
 | 
|
3731  | 
+
 | 
|
3732  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
3733  | 
+        """
 | 
|
3734  | 
+        parser=cmdutil.CmdOptionParser("fai mirror-archive ARCHIVE")
 | 
|
3735  | 
+        return parser 
 | 
|
3736  | 
+
 | 
|
3737  | 
+    def help(self, parser=None):
 | 
|
3738  | 
+        """
 | 
|
3739  | 
+        Prints a help message.
 | 
|
3740  | 
+
 | 
|
3741  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
3742  | 
+        not supplied, it is retrieved.
 | 
|
3743  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
3744  | 
+        """
 | 
|
3745  | 
+        if parser==None:
 | 
|
3746  | 
+            parser=self.get_parser()
 | 
|
3747  | 
+        parser.print_help()
 | 
|
3748  | 
+        print """
 | 
|
3749  | 
+Updates a mirror from an archive.  If a branch, package, or version is
 | 
|
3750  | 
+supplied, only changes under it are mirrored.
 | 
|
3751  | 
+        """
 | 
|
3752  | 
+        return
 | 
|
3753  | 
+
 | 
|
3754  | 
+def help_tree_spec():
 | 
|
3755  | 
+    print """Specifying revisions (default: tree)
 | 
|
3756  | 
+Revisions may be specified by alias, revision, version or patchlevel.
 | 
|
3757  | 
+Revisions or versions may be fully qualified.  Unqualified revisions, versions, 
 | 
|
3758  | 
+or patchlevels use the archive of the current project tree.  Versions will
 | 
|
3759  | 
+use the latest patchlevel in the tree.  Patchlevels will use the current tree-
 | 
|
3760  | 
+version.
 | 
|
3761  | 
+
 | 
|
3762  | 
+Use "alias" to list available (user and automatic) aliases."""
 | 
|
3763  | 
+
 | 
|
3764  | 
+auto_alias = [
 | 
|
3765  | 
+"acur", 
 | 
|
3766  | 
+"The latest revision in the archive of the tree-version.  You can specify \
 | 
|
3767  | 
+a different version like so: acur:foo--bar--0 (aliases can be used)",
 | 
|
3768  | 
+"tcur",
 | 
|
3769  | 
+"""(tree current) The latest revision in the tree of the tree-version. \
 | 
|
3770  | 
+You can specify a different version like so: tcur:foo--bar--0 (aliases can be \
 | 
|
3771  | 
+used).""",
 | 
|
3772  | 
+"tprev" , 
 | 
|
3773  | 
+"""(tree previous) The previous revision in the tree of the tree-version.  To \
 | 
|
3774  | 
+specify an older revision, use a number, e.g. "tprev:4" """,
 | 
|
3775  | 
+"tanc" , 
 | 
|
3776  | 
+"""(tree ancestor) The ancestor revision of the tree To specify an older \
 | 
|
3777  | 
+revision, use a number, e.g. "tanc:4".""",
 | 
|
3778  | 
+"tdate" , 
 | 
|
3779  | 
+"""(tree date) The latest revision from a given date, e.g. "tdate:July 6".""",
 | 
|
3780  | 
+"tmod" , 
 | 
|
3781  | 
+""" (tree modified) The latest revision to modify a given file, e.g. \
 | 
|
3782  | 
+"tmod:engine.cpp" or "tmod:engine.cpp:16".""",
 | 
|
3783  | 
+"ttag" , 
 | 
|
3784  | 
+"""(tree tag) The revision that was tagged into the current tree revision, \
 | 
|
3785  | 
+according to the tree""",
 | 
|
3786  | 
+"tagcur", 
 | 
|
3787  | 
+"""(tag current) The latest revision of the version that the current tree \
 | 
|
3788  | 
+was tagged from.""",
 | 
|
3789  | 
+"mergeanc" , 
 | 
|
3790  | 
+"""The common ancestor of the current tree and the specified revision. \
 | 
|
3791  | 
+Defaults to the first partner-version's latest revision or to tagcur.""",
 | 
|
3792  | 
+]
 | 
|
3793  | 
+
 | 
|
3794  | 
+
 | 
|
3795  | 
+def is_auto_alias(name):
 | 
|
3796  | 
+    """Determine whether a name is an auto alias name
 | 
|
3797  | 
+
 | 
|
3798  | 
+    :param name: the name to check
 | 
|
3799  | 
+    :type name: str
 | 
|
3800  | 
+    :return: True if the name is an auto alias, false if not
 | 
|
3801  | 
+    :rtype: bool
 | 
|
3802  | 
+    """
 | 
|
3803  | 
+    return name in [f for (f, v) in pylon.util.iter_pairs(auto_alias)]
 | 
|
3804  | 
+
 | 
|
3805  | 
+
 | 
|
3806  | 
+def display_def(iter, wrap = 80):
 | 
|
3807  | 
+    """Display a list of definitions
 | 
|
3808  | 
+
 | 
|
3809  | 
+    :param iter: iter of name, definition pairs
 | 
|
3810  | 
+    :type iter: iter of (str, str)
 | 
|
3811  | 
+    :param wrap: The width for text wrapping
 | 
|
3812  | 
+    :type wrap: int
 | 
|
3813  | 
+    """
 | 
|
3814  | 
+    vals = list(iter)
 | 
|
3815  | 
+    maxlen = 0
 | 
|
3816  | 
+    for (key, value) in vals:
 | 
|
3817  | 
+        if len(key) > maxlen:
 | 
|
3818  | 
+            maxlen = len(key)
 | 
|
3819  | 
+    for (key, value) in vals:
 | 
|
3820  | 
+        tw=textwrap.TextWrapper(width=wrap, 
 | 
|
3821  | 
+                                initial_indent=key.rjust(maxlen)+" : ",
 | 
|
3822  | 
+                                subsequent_indent="".rjust(maxlen+3))
 | 
|
3823  | 
+        print tw.fill(value)
 | 
|
3824  | 
+
 | 
|
3825  | 
+
 | 
|
3826  | 
+def help_aliases(tree):
 | 
|
3827  | 
+    print """Auto-generated aliases"""
 | 
|
3828  | 
+    display_def(pylon.util.iter_pairs(auto_alias))
 | 
|
3829  | 
+    print "User aliases"
 | 
|
3830  | 
+    display_def(ancillary.iter_all_alias(tree))
 | 
|
3831  | 
+
 | 
|
3832  | 
+class Inventory(BaseCommand):
 | 
|
3833  | 
+    """List the status of files in the tree"""
 | 
|
3834  | 
+    def __init__(self):
 | 
|
3835  | 
+        self.description=self.__doc__
 | 
|
3836  | 
+
 | 
|
3837  | 
+    def do_command(self, cmdargs):
 | 
|
3838  | 
+        """
 | 
|
3839  | 
+        Master function that perfoms the "revision" command.
 | 
|
3840  | 
+        """
 | 
|
3841  | 
+
 | 
|
3842  | 
+        parser=self.get_parser()
 | 
|
3843  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
3844  | 
+        tree = arch.tree_root()
 | 
|
3845  | 
+        categories = []
 | 
|
3846  | 
+
 | 
|
3847  | 
+        if (options.source):
 | 
|
3848  | 
+            categories.append(arch_core.SourceFile)
 | 
|
3849  | 
+        if (options.precious):
 | 
|
3850  | 
+            categories.append(arch_core.PreciousFile)
 | 
|
3851  | 
+        if (options.backup):
 | 
|
3852  | 
+            categories.append(arch_core.BackupFile)
 | 
|
3853  | 
+        if (options.junk):
 | 
|
3854  | 
+            categories.append(arch_core.JunkFile)
 | 
|
3855  | 
+
 | 
|
3856  | 
+        if len(categories) == 1:
 | 
|
3857  | 
+            show_leading = False
 | 
|
3858  | 
+        else:
 | 
|
3859  | 
+            show_leading = True
 | 
|
3860  | 
+
 | 
|
3861  | 
+        if len(categories) == 0:
 | 
|
3862  | 
+            categories = None
 | 
|
3863  | 
+
 | 
|
3864  | 
+        if options.untagged:
 | 
|
3865  | 
+            categories = arch_core.non_root
 | 
|
3866  | 
+            show_leading = False
 | 
|
3867  | 
+            tagged = False
 | 
|
3868  | 
+        else:
 | 
|
3869  | 
+            tagged = None
 | 
|
3870  | 
+        
 | 
|
3871  | 
+        for file in arch_core.iter_inventory_filter(tree, None, 
 | 
|
3872  | 
+            control_files=options.control_files, 
 | 
|
3873  | 
+            categories = categories, tagged=tagged):
 | 
|
3874  | 
+            print arch_core.file_line(file, 
 | 
|
3875  | 
+                                      category = show_leading, 
 | 
|
3876  | 
+                                      untagged = show_leading,
 | 
|
3877  | 
+                                      id = options.ids)
 | 
|
3878  | 
+
 | 
|
3879  | 
+    def get_parser(self):
 | 
|
3880  | 
+        """
 | 
|
3881  | 
+        Returns the options parser to use for the "revision" command.
 | 
|
3882  | 
+
 | 
|
3883  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
3884  | 
+        """
 | 
|
3885  | 
+        parser=cmdutil.CmdOptionParser("fai inventory [options]")
 | 
|
3886  | 
+        parser.add_option("--ids", action="store_true", dest="ids", 
 | 
|
3887  | 
+                          help="Show file ids")
 | 
|
3888  | 
+        parser.add_option("--control", action="store_true", 
 | 
|
3889  | 
+                          dest="control_files", help="include control files")
 | 
|
3890  | 
+        parser.add_option("--source", action="store_true", dest="source",
 | 
|
3891  | 
+                          help="List source files")
 | 
|
3892  | 
+        parser.add_option("--backup", action="store_true", dest="backup",
 | 
|
3893  | 
+                          help="List backup files")
 | 
|
3894  | 
+        parser.add_option("--precious", action="store_true", dest="precious",
 | 
|
3895  | 
+                          help="List precious files")
 | 
|
3896  | 
+        parser.add_option("--junk", action="store_true", dest="junk",
 | 
|
3897  | 
+                          help="List junk files")
 | 
|
3898  | 
+        parser.add_option("--unrecognized", action="store_true", 
 | 
|
3899  | 
+                          dest="unrecognized", help="List unrecognized files")
 | 
|
3900  | 
+        parser.add_option("--untagged", action="store_true", 
 | 
|
3901  | 
+                          dest="untagged", help="List only untagged files")
 | 
|
3902  | 
+        return parser 
 | 
|
3903  | 
+
 | 
|
3904  | 
+    def help(self, parser=None):
 | 
|
3905  | 
+        """
 | 
|
3906  | 
+        Prints a help message.
 | 
|
3907  | 
+
 | 
|
3908  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
3909  | 
+        not supplied, it is retrieved.
 | 
|
3910  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
3911  | 
+        """
 | 
|
3912  | 
+        if parser==None:
 | 
|
3913  | 
+            parser=self.get_parser()
 | 
|
3914  | 
+        parser.print_help()
 | 
|
3915  | 
+        print """
 | 
|
3916  | 
+Lists the status of files in the archive:
 | 
|
3917  | 
+S source
 | 
|
3918  | 
+P precious
 | 
|
3919  | 
+B backup
 | 
|
3920  | 
+J junk
 | 
|
3921  | 
+U unrecognized
 | 
|
3922  | 
+T tree root
 | 
|
3923  | 
+? untagged-source
 | 
|
3924  | 
+Leading letter are not displayed if only one kind of file is shown
 | 
|
3925  | 
+        """
 | 
|
3926  | 
+        return
 | 
|
3927  | 
+
 | 
|
3928  | 
+
 | 
|
3929  | 
+class Alias(BaseCommand):
 | 
|
3930  | 
+    """List or adjust aliases"""
 | 
|
3931  | 
+    def __init__(self):
 | 
|
3932  | 
+        self.description=self.__doc__
 | 
|
3933  | 
+
 | 
|
3934  | 
+    def get_completer(self, arg, index):
 | 
|
3935  | 
+        if index > 2:
 | 
|
3936  | 
+            return ()
 | 
|
3937  | 
+        try:
 | 
|
3938  | 
+            self.tree = arch.tree_root()
 | 
|
3939  | 
+        except:
 | 
|
3940  | 
+            self.tree = None
 | 
|
3941  | 
+
 | 
|
3942  | 
+        if index == 0:
 | 
|
3943  | 
+            return [part[0]+" " for part in ancillary.iter_all_alias(self.tree)]
 | 
|
3944  | 
+        elif index == 1:
 | 
|
3945  | 
+            return cmdutil.iter_revision_completions(arg, self.tree)
 | 
|
3946  | 
+
 | 
|
3947  | 
+
 | 
|
3948  | 
+    def do_command(self, cmdargs):
 | 
|
3949  | 
+        """
 | 
|
3950  | 
+        Master function that perfoms the "revision" command.
 | 
|
3951  | 
+        """
 | 
|
3952  | 
+
 | 
|
3953  | 
+        parser=self.get_parser()
 | 
|
3954  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
3955  | 
+        try:
 | 
|
3956  | 
+            self.tree =  arch.tree_root()
 | 
|
3957  | 
+        except:
 | 
|
3958  | 
+            self.tree = None
 | 
|
3959  | 
+
 | 
|
3960  | 
+
 | 
|
3961  | 
+        try:
 | 
|
3962  | 
+            options.action(args, options)
 | 
|
3963  | 
+        except cmdutil.ForbiddenAliasSyntax, e:
 | 
|
3964  | 
+            raise CommandFailedWrapper(e)
 | 
|
3965  | 
+
 | 
|
3966  | 
+    def no_prefix(self, alias):
 | 
|
3967  | 
+        if alias.startswith("^"):
 | 
|
3968  | 
+            alias = alias[1:]
 | 
|
3969  | 
+        return alias
 | 
|
3970  | 
+        
 | 
|
3971  | 
+    def arg_dispatch(self, args, options):
 | 
|
3972  | 
+        """Add, modify, or list aliases, depending on number of arguments
 | 
|
3973  | 
+
 | 
|
3974  | 
+        :param args: The list of commandline arguments
 | 
|
3975  | 
+        :type args: list of str
 | 
|
3976  | 
+        :param options: The commandline options
 | 
|
3977  | 
+        """
 | 
|
3978  | 
+        if len(args) == 0:
 | 
|
3979  | 
+            help_aliases(self.tree)
 | 
|
3980  | 
+            return
 | 
|
3981  | 
+        else:
 | 
|
3982  | 
+            alias = self.no_prefix(args[0])
 | 
|
3983  | 
+            if len(args) == 1:
 | 
|
3984  | 
+                self.print_alias(alias)
 | 
|
3985  | 
+            elif (len(args)) == 2:
 | 
|
3986  | 
+                self.add(alias, args[1], options)
 | 
|
3987  | 
+            else:
 | 
|
3988  | 
+                raise cmdutil.GetHelp
 | 
|
3989  | 
+
 | 
|
3990  | 
+    def print_alias(self, alias):
 | 
|
3991  | 
+        answer = None
 | 
|
3992  | 
+        if is_auto_alias(alias):
 | 
|
3993  | 
+            raise pylon.errors.IsAutoAlias(alias, "\"%s\" is an auto alias."
 | 
|
3994  | 
+                "  Use \"revision\" to expand auto aliases." % alias)
 | 
|
3995  | 
+        for pair in ancillary.iter_all_alias(self.tree):
 | 
|
3996  | 
+            if pair[0] == alias:
 | 
|
3997  | 
+                answer = pair[1]
 | 
|
3998  | 
+        if answer is not None:
 | 
|
3999  | 
+            print answer
 | 
|
4000  | 
+        else:
 | 
|
4001  | 
+            print "The alias %s is not assigned." % alias
 | 
|
4002  | 
+
 | 
|
4003  | 
+    def add(self, alias, expansion, options):
 | 
|
4004  | 
+        """Add or modify aliases
 | 
|
4005  | 
+
 | 
|
4006  | 
+        :param alias: The alias name to create/modify
 | 
|
4007  | 
+        :type alias: str
 | 
|
4008  | 
+        :param expansion: The expansion to assign to the alias name
 | 
|
4009  | 
+        :type expansion: str
 | 
|
4010  | 
+        :param options: The commandline options
 | 
|
4011  | 
+        """
 | 
|
4012  | 
+        if is_auto_alias(alias):
 | 
|
4013  | 
+            raise IsAutoAlias(alias)
 | 
|
4014  | 
+        newlist = ""
 | 
|
4015  | 
+        written = False
 | 
|
4016  | 
+        new_line = "%s=%s\n" % (alias, cmdutil.expand_alias(expansion, 
 | 
|
4017  | 
+            self.tree))
 | 
|
4018  | 
+        ancillary.check_alias(new_line.rstrip("\n"), [alias, expansion])
 | 
|
4019  | 
+
 | 
|
4020  | 
+        for pair in self.get_iterator(options):
 | 
|
4021  | 
+            if pair[0] != alias:
 | 
|
4022  | 
+                newlist+="%s=%s\n" % (pair[0], pair[1])
 | 
|
4023  | 
+            elif not written:
 | 
|
4024  | 
+                newlist+=new_line
 | 
|
4025  | 
+                written = True
 | 
|
4026  | 
+        if not written:
 | 
|
4027  | 
+            newlist+=new_line
 | 
|
4028  | 
+        self.write_aliases(newlist, options)
 | 
|
4029  | 
+            
 | 
|
4030  | 
+    def delete(self, args, options):
 | 
|
4031  | 
+        """Delete the specified alias
 | 
|
4032  | 
+
 | 
|
4033  | 
+        :param args: The list of arguments
 | 
|
4034  | 
+        :type args: list of str
 | 
|
4035  | 
+        :param options: The commandline options
 | 
|
4036  | 
+        """
 | 
|
4037  | 
+        deleted = False
 | 
|
4038  | 
+        if len(args) != 1:
 | 
|
4039  | 
+            raise cmdutil.GetHelp
 | 
|
4040  | 
+        alias = self.no_prefix(args[0])
 | 
|
4041  | 
+        if is_auto_alias(alias):
 | 
|
4042  | 
+            raise IsAutoAlias(alias)
 | 
|
4043  | 
+        newlist = ""
 | 
|
4044  | 
+        for pair in self.get_iterator(options):
 | 
|
4045  | 
+            if pair[0] != alias:
 | 
|
4046  | 
+                newlist+="%s=%s\n" % (pair[0], pair[1])
 | 
|
4047  | 
+            else:
 | 
|
4048  | 
+                deleted = True
 | 
|
4049  | 
+        if not deleted:
 | 
|
4050  | 
+            raise errors.NoSuchAlias(alias)
 | 
|
4051  | 
+        self.write_aliases(newlist, options)
 | 
|
4052  | 
+
 | 
|
4053  | 
+    def get_alias_file(self, options):
 | 
|
4054  | 
+        """Return the name of the alias file to use
 | 
|
4055  | 
+
 | 
|
4056  | 
+        :param options: The commandline options
 | 
|
4057  | 
+        """
 | 
|
4058  | 
+        if options.tree:
 | 
|
4059  | 
+            if self.tree is None:
 | 
|
4060  | 
+                self.tree == arch.tree_root()
 | 
|
4061  | 
+            return str(self.tree)+"/{arch}/+aliases"
 | 
|
4062  | 
+        else:
 | 
|
4063  | 
+            return "~/.aba/aliases"
 | 
|
4064  | 
+
 | 
|
4065  | 
+    def get_iterator(self, options):
 | 
|
4066  | 
+        """Return the alias iterator to use
 | 
|
4067  | 
+
 | 
|
4068  | 
+        :param options: The commandline options
 | 
|
4069  | 
+        """
 | 
|
4070  | 
+        return ancillary.iter_alias(self.get_alias_file(options))
 | 
|
4071  | 
+
 | 
|
4072  | 
+    def write_aliases(self, newlist, options):
 | 
|
4073  | 
+        """Safely rewrite the alias file
 | 
|
4074  | 
+        :param newlist: The new list of aliases
 | 
|
4075  | 
+        :type newlist: str
 | 
|
4076  | 
+        :param options: The commandline options
 | 
|
4077  | 
+        """
 | 
|
4078  | 
+        filename = os.path.expanduser(self.get_alias_file(options))
 | 
|
4079  | 
+        file = util.NewFileVersion(filename)
 | 
|
4080  | 
+        file.write(newlist)
 | 
|
4081  | 
+        file.commit()
 | 
|
4082  | 
+
 | 
|
4083  | 
+
 | 
|
4084  | 
+    def get_parser(self):
 | 
|
4085  | 
+        """
 | 
|
4086  | 
+        Returns the options parser to use for the "alias" command.
 | 
|
4087  | 
+
 | 
|
4088  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
4089  | 
+        """
 | 
|
4090  | 
+        parser=cmdutil.CmdOptionParser("fai alias [ALIAS] [NAME]")
 | 
|
4091  | 
+        parser.add_option("-d", "--delete", action="store_const", dest="action",
 | 
|
4092  | 
+                          const=self.delete, default=self.arg_dispatch, 
 | 
|
4093  | 
+                          help="Delete an alias")
 | 
|
4094  | 
+        parser.add_option("--tree", action="store_true", dest="tree", 
 | 
|
4095  | 
+                          help="Create a per-tree alias", default=False)
 | 
|
4096  | 
+        return parser 
 | 
|
4097  | 
+
 | 
|
4098  | 
+    def help(self, parser=None):
 | 
|
4099  | 
+        """
 | 
|
4100  | 
+        Prints a help message.
 | 
|
4101  | 
+
 | 
|
4102  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
4103  | 
+        not supplied, it is retrieved.
 | 
|
4104  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
4105  | 
+        """
 | 
|
4106  | 
+        if parser==None:
 | 
|
4107  | 
+            parser=self.get_parser()
 | 
|
4108  | 
+        parser.print_help()
 | 
|
4109  | 
+        print """
 | 
|
4110  | 
+Lists current aliases or modifies the list of aliases.
 | 
|
4111  | 
+
 | 
|
4112  | 
+If no arguments are supplied, aliases will be listed.  If two arguments are
 | 
|
4113  | 
+supplied, the specified alias will be created or modified.  If -d or --delete
 | 
|
4114  | 
+is supplied, the specified alias will be deleted.
 | 
|
4115  | 
+
 | 
|
4116  | 
+You can create aliases that refer to any fully-qualified part of the
 | 
|
4117  | 
+Arch namespace, e.g. 
 | 
|
4118  | 
+archive, 
 | 
|
4119  | 
+archive/category, 
 | 
|
4120  | 
+archive/category--branch, 
 | 
|
4121  | 
+archive/category--branch--version (my favourite)
 | 
|
4122  | 
+archive/category--branch--version--patchlevel
 | 
|
4123  | 
+
 | 
|
4124  | 
+Aliases can be used automatically by native commands.  To use them
 | 
|
4125  | 
+with external or tla commands, prefix them with ^ (you can do this
 | 
|
4126  | 
+with native commands, too).
 | 
|
4127  | 
+"""
 | 
|
4128  | 
+
 | 
|
4129  | 
+
 | 
|
4130  | 
+class RequestMerge(BaseCommand):
 | 
|
4131  | 
+    """Submit a merge request to Bug Goo"""
 | 
|
4132  | 
+    def __init__(self):
 | 
|
4133  | 
+        self.description=self.__doc__
 | 
|
4134  | 
+
 | 
|
4135  | 
+    def do_command(self, cmdargs):
 | 
|
4136  | 
+        """Submit a merge request
 | 
|
4137  | 
+
 | 
|
4138  | 
+        :param cmdargs: The commandline arguments
 | 
|
4139  | 
+        :type cmdargs: list of str
 | 
|
4140  | 
+        """
 | 
|
4141  | 
+        parser = self.get_parser()
 | 
|
4142  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
4143  | 
+        try:
 | 
|
4144  | 
+            cmdutil.find_editor()
 | 
|
4145  | 
+        except pylon.errors.NoEditorSpecified, e:
 | 
|
4146  | 
+            raise pylon.errors.CommandFailedWrapper(e)
 | 
|
4147  | 
+        try:
 | 
|
4148  | 
+            self.tree=arch.tree_root()
 | 
|
4149  | 
+        except:
 | 
|
4150  | 
+            self.tree=None
 | 
|
4151  | 
+        base, revisions = self.revision_specs(args)
 | 
|
4152  | 
+        message = self.make_headers(base, revisions)
 | 
|
4153  | 
+        message += self.make_summary(revisions)
 | 
|
4154  | 
+        path = self.edit_message(message)
 | 
|
4155  | 
+        message = self.tidy_message(path)
 | 
|
4156  | 
+        if cmdutil.prompt("Send merge"):
 | 
|
4157  | 
+            self.send_message(message)
 | 
|
4158  | 
+            print "Merge request sent"
 | 
|
4159  | 
+
 | 
|
4160  | 
+    def make_headers(self, base, revisions):
 | 
|
4161  | 
+        """Produce email and Bug Goo header strings
 | 
|
4162  | 
+
 | 
|
4163  | 
+        :param base: The base revision to apply merges to
 | 
|
4164  | 
+        :type base: `arch.Revision`
 | 
|
4165  | 
+        :param revisions: The revisions to replay into the base
 | 
|
4166  | 
+        :type revisions: list of `arch.Patchlog`
 | 
|
4167  | 
+        :return: The headers
 | 
|
4168  | 
+        :rtype: str
 | 
|
4169  | 
+        """
 | 
|
4170  | 
+        headers = "To: gnu-arch-users@gnu.org\n"
 | 
|
4171  | 
+        headers += "From: %s\n" % options.fromaddr
 | 
|
4172  | 
+        if len(revisions) == 1:
 | 
|
4173  | 
+            headers += "Subject: [MERGE REQUEST] %s\n" % revisions[0].summary
 | 
|
4174  | 
+        else:
 | 
|
4175  | 
+            headers += "Subject: [MERGE REQUEST]\n"
 | 
|
4176  | 
+        headers += "\n"
 | 
|
4177  | 
+        headers += "Base-Revision: %s\n" % base
 | 
|
4178  | 
+        for revision in revisions:
 | 
|
4179  | 
+            headers += "Revision: %s\n" % revision.revision
 | 
|
4180  | 
+        headers += "Bug: \n\n"
 | 
|
4181  | 
+        return headers
 | 
|
4182  | 
+
 | 
|
4183  | 
+    def make_summary(self, logs):
 | 
|
4184  | 
+        """Generate a summary of merges
 | 
|
4185  | 
+
 | 
|
4186  | 
+        :param logs: the patchlogs that were directly added by the merges
 | 
|
4187  | 
+        :type logs: list of `arch.Patchlog`
 | 
|
4188  | 
+        :return: the summary
 | 
|
4189  | 
+        :rtype: str
 | 
|
4190  | 
+        """ 
 | 
|
4191  | 
+        summary = ""
 | 
|
4192  | 
+        for log in logs:
 | 
|
4193  | 
+            summary+=str(log.revision)+"\n"
 | 
|
4194  | 
+            summary+=log.summary+"\n"
 | 
|
4195  | 
+            if log.description.strip():
 | 
|
4196  | 
+                summary+=log.description.strip('\n')+"\n\n"
 | 
|
4197  | 
+        return summary
 | 
|
4198  | 
+
 | 
|
4199  | 
+    def revision_specs(self, args):
 | 
|
4200  | 
+        """Determine the base and merge revisions from tree and arguments.
 | 
|
4201  | 
+
 | 
|
4202  | 
+        :param args: The parsed arguments
 | 
|
4203  | 
+        :type args: list of str
 | 
|
4204  | 
+        :return: The base revision and merge revisions 
 | 
|
4205  | 
+        :rtype: `arch.Revision`, list of `arch.Patchlog`
 | 
|
4206  | 
+        """
 | 
|
4207  | 
+        if len(args) > 0:
 | 
|
4208  | 
+            target_revision = cmdutil.determine_revision_arch(self.tree, 
 | 
|
4209  | 
+                                                              args[0])
 | 
|
4210  | 
+        else:
 | 
|
4211  | 
+            target_revision = arch_compound.tree_latest(self.tree)
 | 
|
4212  | 
+        if len(args) > 1:
 | 
|
4213  | 
+            merges = [ arch.Patchlog(cmdutil.determine_revision_arch(
 | 
|
4214  | 
+                       self.tree, f)) for f in args[1:] ]
 | 
|
4215  | 
+        else:
 | 
|
4216  | 
+            if self.tree is None:
 | 
|
4217  | 
+                raise CantDetermineRevision("", "Not in a project tree")
 | 
|
4218  | 
+            merge_iter = cmdutil.iter_new_merges(self.tree, 
 | 
|
4219  | 
+                                                 target_revision.version, 
 | 
|
4220  | 
+                                                 False)
 | 
|
4221  | 
+            merges = [f for f in cmdutil.direct_merges(merge_iter)]
 | 
|
4222  | 
+        return (target_revision, merges)
 | 
|
4223  | 
+
 | 
|
4224  | 
+    def edit_message(self, message):
 | 
|
4225  | 
+        """Edit an email message in the user's standard editor
 | 
|
4226  | 
+
 | 
|
4227  | 
+        :param message: The message to edit
 | 
|
4228  | 
+        :type message: str
 | 
|
4229  | 
+        :return: the path of the edited message
 | 
|
4230  | 
+        :rtype: str
 | 
|
4231  | 
+        """
 | 
|
4232  | 
+        if self.tree is None:
 | 
|
4233  | 
+            path = os.get_cwd()
 | 
|
4234  | 
+        else:
 | 
|
4235  | 
+            path = self.tree
 | 
|
4236  | 
+        path += "/,merge-request"
 | 
|
4237  | 
+        file = open(path, 'w')
 | 
|
4238  | 
+        file.write(message)
 | 
|
4239  | 
+        file.flush()
 | 
|
4240  | 
+        cmdutil.invoke_editor(path)
 | 
|
4241  | 
+        return path
 | 
|
4242  | 
+
 | 
|
4243  | 
+    def tidy_message(self, path):
 | 
|
4244  | 
+        """Validate and clean up message.
 | 
|
4245  | 
+
 | 
|
4246  | 
+        :param path: The path to the message to clean up
 | 
|
4247  | 
+        :type path: str
 | 
|
4248  | 
+        :return: The parsed message
 | 
|
4249  | 
+        :rtype: `email.Message`
 | 
|
4250  | 
+        """
 | 
|
4251  | 
+        mail = email.message_from_file(open(path))
 | 
|
4252  | 
+        if mail["Subject"].strip() == "[MERGE REQUEST]":
 | 
|
4253  | 
+            raise BlandSubject
 | 
|
4254  | 
+        
 | 
|
4255  | 
+        request = email.message_from_string(mail.get_payload())
 | 
|
4256  | 
+        if request.has_key("Bug"):
 | 
|
4257  | 
+            if request["Bug"].strip()=="":
 | 
|
4258  | 
+                del request["Bug"]
 | 
|
4259  | 
+        mail.set_payload(request.as_string())
 | 
|
4260  | 
+        return mail
 | 
|
4261  | 
+
 | 
|
4262  | 
+    def send_message(self, message):
 | 
|
4263  | 
+        """Send a message, using its headers to address it.
 | 
|
4264  | 
+
 | 
|
4265  | 
+        :param message: The message to send
 | 
|
4266  | 
+        :type message: `email.Message`"""
 | 
|
4267  | 
+        server = smtplib.SMTP("localhost")
 | 
|
4268  | 
+        server.sendmail(message['From'], message['To'], message.as_string())
 | 
|
4269  | 
+        server.quit()
 | 
|
4270  | 
+
 | 
|
4271  | 
+    def help(self, parser=None):
 | 
|
4272  | 
+        """Print a usage message
 | 
|
4273  | 
+
 | 
|
4274  | 
+        :param parser: The options parser to use
 | 
|
4275  | 
+        :type parser: `cmdutil.CmdOptionParser`
 | 
|
4276  | 
+        """
 | 
|
4277  | 
+        if parser is None:
 | 
|
4278  | 
+            parser = self.get_parser()
 | 
|
4279  | 
+        parser.print_help()
 | 
|
4280  | 
+        print """
 | 
|
4281  | 
+Sends a merge request formatted for Bug Goo.  Intended use: get the tree
 | 
|
4282  | 
+you'd like to merge into.  Apply the merges you want.  Invoke request-merge.
 | 
|
4283  | 
+The merge request will open in your $EDITOR.
 | 
|
4284  | 
+
 | 
|
4285  | 
+When no TARGET is specified, it uses the current tree revision.  When
 | 
|
4286  | 
+no MERGE is specified, it uses the direct merges (as in "revisions
 | 
|
4287  | 
+--direct-merges").  But you can specify just the TARGET, or all the MERGE
 | 
|
4288  | 
+revisions.
 | 
|
4289  | 
+"""
 | 
|
4290  | 
+
 | 
|
4291  | 
+    def get_parser(self):
 | 
|
4292  | 
+        """Produce a commandline parser for this command.
 | 
|
4293  | 
+
 | 
|
4294  | 
+        :rtype: `cmdutil.CmdOptionParser`
 | 
|
4295  | 
+        """
 | 
|
4296  | 
+        parser=cmdutil.CmdOptionParser("request-merge [TARGET] [MERGE1...]")
 | 
|
4297  | 
+        return parser
 | 
|
4298  | 
+
 | 
|
4299  | 
+commands = { 
 | 
|
4300  | 
+'changes' : Changes,
 | 
|
4301  | 
+'help' : Help,
 | 
|
4302  | 
+'update': Update,
 | 
|
4303  | 
+'apply-changes':ApplyChanges,
 | 
|
4304  | 
+'cat-log': CatLog,
 | 
|
4305  | 
+'commit': Commit,
 | 
|
4306  | 
+'revision': Revision,
 | 
|
4307  | 
+'revisions': Revisions,
 | 
|
4308  | 
+'get': Get,
 | 
|
4309  | 
+'revert': Revert,
 | 
|
4310  | 
+'shell': Shell,
 | 
|
4311  | 
+'add-id': AddID,
 | 
|
4312  | 
+'merge': Merge,
 | 
|
4313  | 
+'elog': ELog,
 | 
|
4314  | 
+'mirror-archive': MirrorArchive,
 | 
|
4315  | 
+'ninventory': Inventory,
 | 
|
4316  | 
+'alias' : Alias,
 | 
|
4317  | 
+'request-merge': RequestMerge,
 | 
|
4318  | 
+}
 | 
|
4319  | 
+
 | 
|
4320  | 
+def my_import(mod_name):
 | 
|
4321  | 
+    module = __import__(mod_name)
 | 
|
4322  | 
+    components = mod_name.split('.')
 | 
|
4323  | 
+    for comp in components[1:]:
 | 
|
4324  | 
+        module = getattr(module, comp)
 | 
|
4325  | 
+    return module
 | 
|
4326  | 
+
 | 
|
4327  | 
+def plugin(mod_name):
 | 
|
4328  | 
+    module = my_import(mod_name)
 | 
|
4329  | 
+    module.add_command(commands)
 | 
|
4330  | 
+
 | 
|
4331  | 
+for file in os.listdir(sys.path[0]+"/command"):
 | 
|
4332  | 
+    if len(file) > 3 and file[-3:] == ".py" and file != "__init__.py":
 | 
|
4333  | 
+        plugin("command."+file[:-3])
 | 
|
4334  | 
+
 | 
|
4335  | 
+suggestions = {
 | 
|
4336  | 
+'apply-delta' : "Try \"apply-changes\".",
 | 
|
4337  | 
+'delta' : "To compare two revisions, use \"changes\".",
 | 
|
4338  | 
+'diff-rev' : "To compare two revisions, use \"changes\".",
 | 
|
4339  | 
+'undo' : "To undo local changes, use \"revert\".",
 | 
|
4340  | 
+'undelete' : "To undo only deletions, use \"revert --deletions\"",
 | 
|
4341  | 
+'missing-from' : "Try \"revisions --missing-from\".",
 | 
|
4342  | 
+'missing' : "Try \"revisions --missing\".",
 | 
|
4343  | 
+'missing-merge' : "Try \"revisions --partner-missing\".",
 | 
|
4344  | 
+'new-merges' : "Try \"revisions --new-merges\".",
 | 
|
4345  | 
+'cachedrevs' : "Try \"revisions --cacherevs\". (no 'd')",
 | 
|
4346  | 
+'logs' : "Try \"revisions --logs\"",
 | 
|
4347  | 
+'tree-source' : "Use the \"^ttag\" alias (\"revision ^ttag\")",
 | 
|
4348  | 
+'latest-revision' : "Use the \"^acur\" alias (\"revision ^acur\")",
 | 
|
4349  | 
+'change-version' : "Try \"update REVISION\"",
 | 
|
4350  | 
+'tree-revision' : "Use the \"^tcur\" alias (\"revision ^tcur\")",
 | 
|
4351  | 
+'rev-depends' : "Use revisions --dependencies",
 | 
|
4352  | 
+'auto-get' : "Plain get will do archive lookups",
 | 
|
4353  | 
+'tagline' : "Use add-id.  It uses taglines in tagline trees",
 | 
|
4354  | 
+'emlog' : "Use elog.  It automatically adds log-for-merge text, if any",
 | 
|
4355  | 
+'library-revisions' : "Use revisions --library",
 | 
|
4356  | 
+'file-revert' : "Use revert FILE",
 | 
|
4357  | 
+'join-branch' : "Use replay --logs-only"
 | 
|
4358  | 
+}
 | 
|
4359  | 
+# arch-tag: 19d5739d-3708-486c-93ba-deecc3027fc7
 | 
|
4360  | 
||
4361  | 
*** added file 'testdata/orig'
 | 
|
4362  | 
--- /dev/null 
 | 
|
4363  | 
+++ testdata/orig 
 | 
|
4364  | 
@@ -0,0 +1,2789 @@
 | 
|
4365  | 
+# Copyright (C) 2004 Aaron Bentley
 | 
|
4366  | 
+# <aaron.bentley@utoronto.ca>
 | 
|
4367  | 
+#
 | 
|
4368  | 
+#    This program is free software; you can redistribute it and/or modify
 | 
|
4369  | 
+#    it under the terms of the GNU General Public License as published by
 | 
|
4370  | 
+#    the Free Software Foundation; either version 2 of the License, or
 | 
|
4371  | 
+#    (at your option) any later version.
 | 
|
4372  | 
+#
 | 
|
4373  | 
+#    This program is distributed in the hope that it will be useful,
 | 
|
4374  | 
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
|
4375  | 
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
|
4376  | 
+#    GNU General Public License for more details.
 | 
|
4377  | 
+#
 | 
|
4378  | 
+#    You should have received a copy of the GNU General Public License
 | 
|
4379  | 
+#    along with this program; if not, write to the Free Software
 | 
|
4380  | 
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
|
4381  | 
+
 | 
|
4382  | 
+import sys
 | 
|
4383  | 
+import arch
 | 
|
4384  | 
+import arch.util
 | 
|
4385  | 
+import arch.arch
 | 
|
4386  | 
+import abacmds
 | 
|
4387  | 
+import cmdutil
 | 
|
4388  | 
+import shutil
 | 
|
4389  | 
+import os
 | 
|
4390  | 
+import options
 | 
|
4391  | 
+import paths 
 | 
|
4392  | 
+import time
 | 
|
4393  | 
+import cmd
 | 
|
4394  | 
+import readline
 | 
|
4395  | 
+import re
 | 
|
4396  | 
+import string
 | 
|
4397  | 
+import arch_core
 | 
|
4398  | 
+from errors import *
 | 
|
4399  | 
+import errors
 | 
|
4400  | 
+import terminal
 | 
|
4401  | 
+import ancillary
 | 
|
4402  | 
+import misc
 | 
|
4403  | 
+import email
 | 
|
4404  | 
+import smtplib
 | 
|
4405  | 
+
 | 
|
4406  | 
+__docformat__ = "restructuredtext"
 | 
|
4407  | 
+__doc__ = "Implementation of user (sub) commands"
 | 
|
4408  | 
+commands = {}
 | 
|
4409  | 
+
 | 
|
4410  | 
+def find_command(cmd):
 | 
|
4411  | 
+    """
 | 
|
4412  | 
+    Return an instance of a command type.  Return None if the type isn't
 | 
|
4413  | 
+    registered.
 | 
|
4414  | 
+
 | 
|
4415  | 
+    :param cmd: the name of the command to look for
 | 
|
4416  | 
+    :type cmd: the type of the command
 | 
|
4417  | 
+    """
 | 
|
4418  | 
+    if commands.has_key(cmd):
 | 
|
4419  | 
+        return commands[cmd]()
 | 
|
4420  | 
+    else:
 | 
|
4421  | 
+        return None
 | 
|
4422  | 
+
 | 
|
4423  | 
+class BaseCommand:
 | 
|
4424  | 
+    def __call__(self, cmdline):
 | 
|
4425  | 
+        try:
 | 
|
4426  | 
+            self.do_command(cmdline.split())
 | 
|
4427  | 
+        except cmdutil.GetHelp, e:
 | 
|
4428  | 
+            self.help()
 | 
|
4429  | 
+        except Exception, e:
 | 
|
4430  | 
+            print e
 | 
|
4431  | 
+
 | 
|
4432  | 
+    def get_completer(index):
 | 
|
4433  | 
+        return None
 | 
|
4434  | 
+
 | 
|
4435  | 
+    def complete(self, args, text):
 | 
|
4436  | 
+        """
 | 
|
4437  | 
+        Returns a list of possible completions for the given text.
 | 
|
4438  | 
+
 | 
|
4439  | 
+        :param args: The complete list of arguments
 | 
|
4440  | 
+        :type args: List of str
 | 
|
4441  | 
+        :param text: text to complete (may be shorter than args[-1])
 | 
|
4442  | 
+        :type text: str
 | 
|
4443  | 
+        :rtype: list of str
 | 
|
4444  | 
+        """
 | 
|
4445  | 
+        matches = []
 | 
|
4446  | 
+        candidates = None
 | 
|
4447  | 
+
 | 
|
4448  | 
+        if len(args) > 0: 
 | 
|
4449  | 
+            realtext = args[-1]
 | 
|
4450  | 
+        else:
 | 
|
4451  | 
+            realtext = ""
 | 
|
4452  | 
+
 | 
|
4453  | 
+        try:
 | 
|
4454  | 
+            parser=self.get_parser()
 | 
|
4455  | 
+            if realtext.startswith('-'):
 | 
|
4456  | 
+                candidates = parser.iter_options()
 | 
|
4457  | 
+            else:
 | 
|
4458  | 
+                (options, parsed_args) = parser.parse_args(args)
 | 
|
4459  | 
+
 | 
|
4460  | 
+                if len (parsed_args) > 0:
 | 
|
4461  | 
+                    candidates = self.get_completer(parsed_args[-1], len(parsed_args) -1)
 | 
|
4462  | 
+                else:
 | 
|
4463  | 
+                    candidates = self.get_completer("", 0)
 | 
|
4464  | 
+        except:
 | 
|
4465  | 
+            pass
 | 
|
4466  | 
+        if candidates is None:
 | 
|
4467  | 
+            return
 | 
|
4468  | 
+        for candidate in candidates:
 | 
|
4469  | 
+            candidate = str(candidate)
 | 
|
4470  | 
+            if candidate.startswith(realtext):
 | 
|
4471  | 
+                matches.append(candidate[len(realtext)- len(text):])
 | 
|
4472  | 
+        return matches
 | 
|
4473  | 
+
 | 
|
4474  | 
+
 | 
|
4475  | 
+class Help(BaseCommand):
 | 
|
4476  | 
+    """
 | 
|
4477  | 
+    Lists commands, prints help messages.
 | 
|
4478  | 
+    """
 | 
|
4479  | 
+    def __init__(self):
 | 
|
4480  | 
+        self.description="Prints help mesages"
 | 
|
4481  | 
+        self.parser = None
 | 
|
4482  | 
+
 | 
|
4483  | 
+    def do_command(self, cmdargs):
 | 
|
4484  | 
+        """
 | 
|
4485  | 
+        Prints a help message.
 | 
|
4486  | 
+        """
 | 
|
4487  | 
+        options, args = self.get_parser().parse_args(cmdargs)
 | 
|
4488  | 
+        if len(args) > 1:
 | 
|
4489  | 
+            raise cmdutil.GetHelp
 | 
|
4490  | 
+
 | 
|
4491  | 
+        if options.native or options.suggestions or options.external:
 | 
|
4492  | 
+            native = options.native
 | 
|
4493  | 
+            suggestions = options.suggestions
 | 
|
4494  | 
+            external = options.external
 | 
|
4495  | 
+        else:
 | 
|
4496  | 
+            native = True
 | 
|
4497  | 
+            suggestions = False
 | 
|
4498  | 
+            external = True
 | 
|
4499  | 
+        
 | 
|
4500  | 
+        if len(args) == 0:
 | 
|
4501  | 
+            self.list_commands(native, suggestions, external)
 | 
|
4502  | 
+            return
 | 
|
4503  | 
+        elif len(args) == 1:
 | 
|
4504  | 
+            command_help(args[0])
 | 
|
4505  | 
+            return
 | 
|
4506  | 
+
 | 
|
4507  | 
+    def help(self):
 | 
|
4508  | 
+        self.get_parser().print_help()
 | 
|
4509  | 
+        print """
 | 
|
4510  | 
+If no command is specified, commands are listed.  If a command is
 | 
|
4511  | 
+specified, help for that command is listed.
 | 
|
4512  | 
+        """
 | 
|
4513  | 
+
 | 
|
4514  | 
+    def get_parser(self):
 | 
|
4515  | 
+        """
 | 
|
4516  | 
+        Returns the options parser to use for the "revision" command.
 | 
|
4517  | 
+
 | 
|
4518  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
4519  | 
+        """
 | 
|
4520  | 
+        if self.parser is not None:
 | 
|
4521  | 
+            return self.parser
 | 
|
4522  | 
+        parser=cmdutil.CmdOptionParser("fai help [command]")
 | 
|
4523  | 
+        parser.add_option("-n", "--native", action="store_true", 
 | 
|
4524  | 
+                         dest="native", help="Show native commands")
 | 
|
4525  | 
+        parser.add_option("-e", "--external", action="store_true", 
 | 
|
4526  | 
+                         dest="external", help="Show external commands")
 | 
|
4527  | 
+        parser.add_option("-s", "--suggest", action="store_true", 
 | 
|
4528  | 
+                         dest="suggestions", help="Show suggestions")
 | 
|
4529  | 
+        self.parser = parser
 | 
|
4530  | 
+        return parser 
 | 
|
4531  | 
+      
 | 
|
4532  | 
+    def list_commands(self, native=True, suggest=False, external=True):
 | 
|
4533  | 
+        """
 | 
|
4534  | 
+        Lists supported commands.
 | 
|
4535  | 
+
 | 
|
4536  | 
+        :param native: list native, python-based commands
 | 
|
4537  | 
+        :type native: bool
 | 
|
4538  | 
+        :param external: list external aba-style commands
 | 
|
4539  | 
+        :type external: bool
 | 
|
4540  | 
+        """
 | 
|
4541  | 
+        if native:
 | 
|
4542  | 
+            print "Native Fai commands"
 | 
|
4543  | 
+            keys=commands.keys()
 | 
|
4544  | 
+            keys.sort()
 | 
|
4545  | 
+            for k in keys:
 | 
|
4546  | 
+                space=""
 | 
|
4547  | 
+                for i in range(28-len(k)):
 | 
|
4548  | 
+                    space+=" "
 | 
|
4549  | 
+                print space+k+" : "+commands[k]().description
 | 
|
4550  | 
+            print
 | 
|
4551  | 
+        if suggest:
 | 
|
4552  | 
+            print "Unavailable commands and suggested alternatives"
 | 
|
4553  | 
+            key_list = suggestions.keys()
 | 
|
4554  | 
+            key_list.sort()
 | 
|
4555  | 
+            for key in key_list:
 | 
|
4556  | 
+                print "%28s : %s" % (key, suggestions[key])
 | 
|
4557  | 
+            print
 | 
|
4558  | 
+        if external:
 | 
|
4559  | 
+            fake_aba = abacmds.AbaCmds()
 | 
|
4560  | 
+            if (fake_aba.abadir == ""):
 | 
|
4561  | 
+                return
 | 
|
4562  | 
+            print "External commands"
 | 
|
4563  | 
+            fake_aba.list_commands()
 | 
|
4564  | 
+            print
 | 
|
4565  | 
+        if not suggest:
 | 
|
4566  | 
+            print "Use help --suggest to list alternatives to tla and aba"\
 | 
|
4567  | 
+                " commands."
 | 
|
4568  | 
+        if options.tla_fallthrough and (native or external):
 | 
|
4569  | 
+            print "Fai also supports tla commands."
 | 
|
4570  | 
+
 | 
|
4571  | 
+def command_help(cmd):
 | 
|
4572  | 
+    """
 | 
|
4573  | 
+    Prints help for a command.
 | 
|
4574  | 
+
 | 
|
4575  | 
+    :param cmd: The name of the command to print help for
 | 
|
4576  | 
+    :type cmd: str
 | 
|
4577  | 
+    """
 | 
|
4578  | 
+    fake_aba = abacmds.AbaCmds()
 | 
|
4579  | 
+    cmdobj = find_command(cmd)
 | 
|
4580  | 
+    if cmdobj != None:
 | 
|
4581  | 
+        cmdobj.help()
 | 
|
4582  | 
+    elif suggestions.has_key(cmd):
 | 
|
4583  | 
+        print "Not available\n" + suggestions[cmd]
 | 
|
4584  | 
+    else:
 | 
|
4585  | 
+        abacmd = fake_aba.is_command(cmd)
 | 
|
4586  | 
+        if abacmd:
 | 
|
4587  | 
+            abacmd.help()
 | 
|
4588  | 
+        else:
 | 
|
4589  | 
+            print "No help is available for \""+cmd+"\". Maybe try \"tla "+cmd+" -H\"?"
 | 
|
4590  | 
+
 | 
|
4591  | 
+
 | 
|
4592  | 
+
 | 
|
4593  | 
+class Changes(BaseCommand):
 | 
|
4594  | 
+    """
 | 
|
4595  | 
+    the "changes" command: lists differences between trees/revisions:
 | 
|
4596  | 
+    """
 | 
|
4597  | 
+    
 | 
|
4598  | 
+    def __init__(self):
 | 
|
4599  | 
+        self.description="Lists what files have changed in the project tree"
 | 
|
4600  | 
+
 | 
|
4601  | 
+    def get_completer(self, arg, index):
 | 
|
4602  | 
+        if index > 1:
 | 
|
4603  | 
+            return None
 | 
|
4604  | 
+        try:
 | 
|
4605  | 
+            tree = arch.tree_root()
 | 
|
4606  | 
+        except:
 | 
|
4607  | 
+            tree = None
 | 
|
4608  | 
+        return cmdutil.iter_revision_completions(arg, tree)
 | 
|
4609  | 
+    
 | 
|
4610  | 
+    def parse_commandline(self, cmdline):
 | 
|
4611  | 
+        """
 | 
|
4612  | 
+        Parse commandline arguments.  Raises cmdutil.GetHelp if help is needed.
 | 
|
4613  | 
+        
 | 
|
4614  | 
+        :param cmdline: A list of arguments to parse
 | 
|
4615  | 
+        :rtype: (options, Revision, Revision/WorkingTree)
 | 
|
4616  | 
+        """
 | 
|
4617  | 
+        parser=self.get_parser()
 | 
|
4618  | 
+        (options, args) = parser.parse_args(cmdline)
 | 
|
4619  | 
+        if len(args) > 2:
 | 
|
4620  | 
+            raise cmdutil.GetHelp
 | 
|
4621  | 
+
 | 
|
4622  | 
+        tree=arch.tree_root()
 | 
|
4623  | 
+        if len(args) == 0:
 | 
|
4624  | 
+            a_spec = cmdutil.comp_revision(tree)
 | 
|
4625  | 
+        else:
 | 
|
4626  | 
+            a_spec = cmdutil.determine_revision_tree(tree, args[0])
 | 
|
4627  | 
+        cmdutil.ensure_archive_registered(a_spec.archive)
 | 
|
4628  | 
+        if len(args) == 2:
 | 
|
4629  | 
+            b_spec = cmdutil.determine_revision_tree(tree, args[1])
 | 
|
4630  | 
+            cmdutil.ensure_archive_registered(b_spec.archive)
 | 
|
4631  | 
+        else:
 | 
|
4632  | 
+            b_spec=tree
 | 
|
4633  | 
+        return options, a_spec, b_spec
 | 
|
4634  | 
+
 | 
|
4635  | 
+    def do_command(self, cmdargs):
 | 
|
4636  | 
+        """
 | 
|
4637  | 
+        Master function that perfoms the "changes" command.
 | 
|
4638  | 
+        """
 | 
|
4639  | 
+        try:
 | 
|
4640  | 
+            options, a_spec, b_spec = self.parse_commandline(cmdargs);
 | 
|
4641  | 
+        except cmdutil.CantDetermineRevision, e:
 | 
|
4642  | 
+            print e
 | 
|
4643  | 
+            return
 | 
|
4644  | 
+        except arch.errors.TreeRootError, e:
 | 
|
4645  | 
+            print e
 | 
|
4646  | 
+            return
 | 
|
4647  | 
+        if options.changeset:
 | 
|
4648  | 
+            changeset=options.changeset
 | 
|
4649  | 
+            tmpdir = None
 | 
|
4650  | 
+        else:
 | 
|
4651  | 
+            tmpdir=cmdutil.tmpdir()
 | 
|
4652  | 
+            changeset=tmpdir+"/changeset"
 | 
|
4653  | 
+        try:
 | 
|
4654  | 
+            delta=arch.iter_delta(a_spec, b_spec, changeset)
 | 
|
4655  | 
+            try:
 | 
|
4656  | 
+                for line in delta:
 | 
|
4657  | 
+                    if cmdutil.chattermatch(line, "changeset:"):
 | 
|
4658  | 
+                        pass
 | 
|
4659  | 
+                    else:
 | 
|
4660  | 
+                        cmdutil.colorize(line, options.suppress_chatter)
 | 
|
4661  | 
+            except arch.util.ExecProblem, e:
 | 
|
4662  | 
+                if e.proc.error and e.proc.error.startswith(
 | 
|
4663  | 
+                    "missing explicit id for file"):
 | 
|
4664  | 
+                    raise MissingID(e)
 | 
|
4665  | 
+                else:
 | 
|
4666  | 
+                    raise
 | 
|
4667  | 
+            status=delta.status
 | 
|
4668  | 
+            if status > 1:
 | 
|
4669  | 
+                return
 | 
|
4670  | 
+            if (options.perform_diff):
 | 
|
4671  | 
+                chan = cmdutil.ChangesetMunger(changeset)
 | 
|
4672  | 
+                chan.read_indices()
 | 
|
4673  | 
+                if isinstance(b_spec, arch.Revision):
 | 
|
4674  | 
+                    b_dir = b_spec.library_find()
 | 
|
4675  | 
+                else:
 | 
|
4676  | 
+                    b_dir = b_spec
 | 
|
4677  | 
+                a_dir = a_spec.library_find()
 | 
|
4678  | 
+                if options.diffopts is not None:
 | 
|
4679  | 
+                    diffopts = options.diffopts.split()
 | 
|
4680  | 
+                    cmdutil.show_custom_diffs(chan, diffopts, a_dir, b_dir)
 | 
|
4681  | 
+                else:
 | 
|
4682  | 
+                    cmdutil.show_diffs(delta.changeset)
 | 
|
4683  | 
+        finally:
 | 
|
4684  | 
+            if tmpdir and (os.access(tmpdir, os.X_OK)):
 | 
|
4685  | 
+                shutil.rmtree(tmpdir)
 | 
|
4686  | 
+
 | 
|
4687  | 
+    def get_parser(self):
 | 
|
4688  | 
+        """
 | 
|
4689  | 
+        Returns the options parser to use for the "changes" command.
 | 
|
4690  | 
+
 | 
|
4691  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
4692  | 
+        """
 | 
|
4693  | 
+        parser=cmdutil.CmdOptionParser("fai changes [options] [revision]"
 | 
|
4694  | 
+                                       " [revision]")
 | 
|
4695  | 
+        parser.add_option("-d", "--diff", action="store_true", 
 | 
|
4696  | 
+                          dest="perform_diff", default=False, 
 | 
|
4697  | 
+                          help="Show diffs in summary")
 | 
|
4698  | 
+        parser.add_option("-c", "--changeset", dest="changeset", 
 | 
|
4699  | 
+                          help="Store a changeset in the given directory", 
 | 
|
4700  | 
+                          metavar="DIRECTORY")
 | 
|
4701  | 
+        parser.add_option("-s", "--silent", action="store_true", 
 | 
|
4702  | 
+                          dest="suppress_chatter", default=False, 
 | 
|
4703  | 
+                          help="Suppress chatter messages")
 | 
|
4704  | 
+        parser.add_option("--diffopts", dest="diffopts", 
 | 
|
4705  | 
+                          help="Use the specified diff options", 
 | 
|
4706  | 
+                          metavar="OPTIONS")
 | 
|
4707  | 
+
 | 
|
4708  | 
+        return parser
 | 
|
4709  | 
+
 | 
|
4710  | 
+    def help(self, parser=None):
 | 
|
4711  | 
+        """
 | 
|
4712  | 
+        Prints a help message.
 | 
|
4713  | 
+
 | 
|
4714  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
4715  | 
+        not supplied, it is retrieved.
 | 
|
4716  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
4717  | 
+        """
 | 
|
4718  | 
+        if parser is None:
 | 
|
4719  | 
+            parser=self.get_parser()
 | 
|
4720  | 
+        parser.print_help()
 | 
|
4721  | 
+        print """
 | 
|
4722  | 
+Performs source-tree comparisons
 | 
|
4723  | 
+
 | 
|
4724  | 
+If no revision is specified, the current project tree is compared to the
 | 
|
4725  | 
+last-committed revision.  If one revision is specified, the current project
 | 
|
4726  | 
+tree is compared to that revision.  If two revisions are specified, they are
 | 
|
4727  | 
+compared to each other.
 | 
|
4728  | 
+        """
 | 
|
4729  | 
+        help_tree_spec() 
 | 
|
4730  | 
+        return
 | 
|
4731  | 
+
 | 
|
4732  | 
+
 | 
|
4733  | 
+class ApplyChanges(BaseCommand):
 | 
|
4734  | 
+    """
 | 
|
4735  | 
+    Apply differences between two revisions to a tree
 | 
|
4736  | 
+    """
 | 
|
4737  | 
+    
 | 
|
4738  | 
+    def __init__(self):
 | 
|
4739  | 
+        self.description="Applies changes to a project tree"
 | 
|
4740  | 
+    
 | 
|
4741  | 
+    def get_completer(self, arg, index):
 | 
|
4742  | 
+        if index > 1:
 | 
|
4743  | 
+            return None
 | 
|
4744  | 
+        try:
 | 
|
4745  | 
+            tree = arch.tree_root()
 | 
|
4746  | 
+        except:
 | 
|
4747  | 
+            tree = None
 | 
|
4748  | 
+        return cmdutil.iter_revision_completions(arg, tree)
 | 
|
4749  | 
+
 | 
|
4750  | 
+    def parse_commandline(self, cmdline, tree):
 | 
|
4751  | 
+        """
 | 
|
4752  | 
+        Parse commandline arguments.  Raises cmdutil.GetHelp if help is needed.
 | 
|
4753  | 
+        
 | 
|
4754  | 
+        :param cmdline: A list of arguments to parse
 | 
|
4755  | 
+        :rtype: (options, Revision, Revision/WorkingTree)
 | 
|
4756  | 
+        """
 | 
|
4757  | 
+        parser=self.get_parser()
 | 
|
4758  | 
+        (options, args) = parser.parse_args(cmdline)
 | 
|
4759  | 
+        if len(args) != 2:
 | 
|
4760  | 
+            raise cmdutil.GetHelp
 | 
|
4761  | 
+
 | 
|
4762  | 
+        a_spec = cmdutil.determine_revision_tree(tree, args[0])
 | 
|
4763  | 
+        cmdutil.ensure_archive_registered(a_spec.archive)
 | 
|
4764  | 
+        b_spec = cmdutil.determine_revision_tree(tree, args[1])
 | 
|
4765  | 
+        cmdutil.ensure_archive_registered(b_spec.archive)
 | 
|
4766  | 
+        return options, a_spec, b_spec
 | 
|
4767  | 
+
 | 
|
4768  | 
+    def do_command(self, cmdargs):
 | 
|
4769  | 
+        """
 | 
|
4770  | 
+        Master function that performs "apply-changes".
 | 
|
4771  | 
+        """
 | 
|
4772  | 
+        try:
 | 
|
4773  | 
+            tree = arch.tree_root()
 | 
|
4774  | 
+            options, a_spec, b_spec = self.parse_commandline(cmdargs, tree);
 | 
|
4775  | 
+        except cmdutil.CantDetermineRevision, e:
 | 
|
4776  | 
+            print e
 | 
|
4777  | 
+            return
 | 
|
4778  | 
+        except arch.errors.TreeRootError, e:
 | 
|
4779  | 
+            print e
 | 
|
4780  | 
+            return
 | 
|
4781  | 
+        delta=cmdutil.apply_delta(a_spec, b_spec, tree)
 | 
|
4782  | 
+        for line in cmdutil.iter_apply_delta_filter(delta):
 | 
|
4783  | 
+            cmdutil.colorize(line, options.suppress_chatter)
 | 
|
4784  | 
+
 | 
|
4785  | 
+    def get_parser(self):
 | 
|
4786  | 
+        """
 | 
|
4787  | 
+        Returns the options parser to use for the "apply-changes" command.
 | 
|
4788  | 
+
 | 
|
4789  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
4790  | 
+        """
 | 
|
4791  | 
+        parser=cmdutil.CmdOptionParser("fai apply-changes [options] revision"
 | 
|
4792  | 
+                                       " revision")
 | 
|
4793  | 
+        parser.add_option("-d", "--diff", action="store_true", 
 | 
|
4794  | 
+                          dest="perform_diff", default=False, 
 | 
|
4795  | 
+                          help="Show diffs in summary")
 | 
|
4796  | 
+        parser.add_option("-c", "--changeset", dest="changeset", 
 | 
|
4797  | 
+                          help="Store a changeset in the given directory", 
 | 
|
4798  | 
+                          metavar="DIRECTORY")
 | 
|
4799  | 
+        parser.add_option("-s", "--silent", action="store_true", 
 | 
|
4800  | 
+                          dest="suppress_chatter", default=False, 
 | 
|
4801  | 
+                          help="Suppress chatter messages")
 | 
|
4802  | 
+        return parser
 | 
|
4803  | 
+
 | 
|
4804  | 
+    def help(self, parser=None):
 | 
|
4805  | 
+        """
 | 
|
4806  | 
+        Prints a help message.
 | 
|
4807  | 
+
 | 
|
4808  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
4809  | 
+        not supplied, it is retrieved.
 | 
|
4810  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
4811  | 
+        """
 | 
|
4812  | 
+        if parser is None:
 | 
|
4813  | 
+            parser=self.get_parser()
 | 
|
4814  | 
+        parser.print_help()
 | 
|
4815  | 
+        print """
 | 
|
4816  | 
+Applies changes to a project tree
 | 
|
4817  | 
+
 | 
|
4818  | 
+Compares two revisions and applies the difference between them to the current
 | 
|
4819  | 
+tree.
 | 
|
4820  | 
+        """
 | 
|
4821  | 
+        help_tree_spec() 
 | 
|
4822  | 
+        return
 | 
|
4823  | 
+
 | 
|
4824  | 
+class Update(BaseCommand):
 | 
|
4825  | 
+    """
 | 
|
4826  | 
+    Updates a project tree to a given revision, preserving un-committed hanges. 
 | 
|
4827  | 
+    """
 | 
|
4828  | 
+    
 | 
|
4829  | 
+    def __init__(self):
 | 
|
4830  | 
+        self.description="Apply the latest changes to the current directory"
 | 
|
4831  | 
+
 | 
|
4832  | 
+    def get_completer(self, arg, index):
 | 
|
4833  | 
+        if index > 0:
 | 
|
4834  | 
+            return None
 | 
|
4835  | 
+        try:
 | 
|
4836  | 
+            tree = arch.tree_root()
 | 
|
4837  | 
+        except:
 | 
|
4838  | 
+            tree = None
 | 
|
4839  | 
+        return cmdutil.iter_revision_completions(arg, tree)
 | 
|
4840  | 
+    
 | 
|
4841  | 
+    def parse_commandline(self, cmdline, tree):
 | 
|
4842  | 
+        """
 | 
|
4843  | 
+        Parse commandline arguments.  Raises cmdutil.GetHelp if help is needed.
 | 
|
4844  | 
+        
 | 
|
4845  | 
+        :param cmdline: A list of arguments to parse
 | 
|
4846  | 
+        :rtype: (options, Revision, Revision/WorkingTree)
 | 
|
4847  | 
+        """
 | 
|
4848  | 
+        parser=self.get_parser()
 | 
|
4849  | 
+        (options, args) = parser.parse_args(cmdline)
 | 
|
4850  | 
+        if len(args) > 2:
 | 
|
4851  | 
+            raise cmdutil.GetHelp
 | 
|
4852  | 
+
 | 
|
4853  | 
+        spec=None
 | 
|
4854  | 
+        if len(args)>0:
 | 
|
4855  | 
+            spec=args[0]
 | 
|
4856  | 
+        revision=cmdutil.determine_revision_arch(tree, spec)
 | 
|
4857  | 
+        cmdutil.ensure_archive_registered(revision.archive)
 | 
|
4858  | 
+
 | 
|
4859  | 
+        mirror_source = cmdutil.get_mirror_source(revision.archive)
 | 
|
4860  | 
+        if mirror_source != None:
 | 
|
4861  | 
+            if cmdutil.prompt("Mirror update"):
 | 
|
4862  | 
+                cmd=cmdutil.mirror_archive(mirror_source, 
 | 
|
4863  | 
+                    revision.archive, arch.NameParser(revision).get_package_version())
 | 
|
4864  | 
+                for line in arch.chatter_classifier(cmd):
 | 
|
4865  | 
+                    cmdutil.colorize(line, options.suppress_chatter)
 | 
|
4866  | 
+
 | 
|
4867  | 
+                revision=cmdutil.determine_revision_arch(tree, spec)
 | 
|
4868  | 
+
 | 
|
4869  | 
+        return options, revision 
 | 
|
4870  | 
+
 | 
|
4871  | 
+    def do_command(self, cmdargs):
 | 
|
4872  | 
+        """
 | 
|
4873  | 
+        Master function that perfoms the "update" command.
 | 
|
4874  | 
+        """
 | 
|
4875  | 
+        tree=arch.tree_root()
 | 
|
4876  | 
+        try:
 | 
|
4877  | 
+            options, to_revision = self.parse_commandline(cmdargs, tree);
 | 
|
4878  | 
+        except cmdutil.CantDetermineRevision, e:
 | 
|
4879  | 
+            print e
 | 
|
4880  | 
+            return
 | 
|
4881  | 
+        except arch.errors.TreeRootError, e:
 | 
|
4882  | 
+            print e
 | 
|
4883  | 
+            return
 | 
|
4884  | 
+        from_revision=cmdutil.tree_latest(tree)
 | 
|
4885  | 
+        if from_revision==to_revision:
 | 
|
4886  | 
+            print "Tree is already up to date with:\n"+str(to_revision)+"."
 | 
|
4887  | 
+            return
 | 
|
4888  | 
+        cmdutil.ensure_archive_registered(from_revision.archive)
 | 
|
4889  | 
+        cmd=cmdutil.apply_delta(from_revision, to_revision, tree,
 | 
|
4890  | 
+            options.patch_forward)
 | 
|
4891  | 
+        for line in cmdutil.iter_apply_delta_filter(cmd):
 | 
|
4892  | 
+            cmdutil.colorize(line)
 | 
|
4893  | 
+        if to_revision.version != tree.tree_version:
 | 
|
4894  | 
+            if cmdutil.prompt("Update version"):
 | 
|
4895  | 
+                tree.tree_version = to_revision.version
 | 
|
4896  | 
+
 | 
|
4897  | 
+    def get_parser(self):
 | 
|
4898  | 
+        """
 | 
|
4899  | 
+        Returns the options parser to use for the "update" command.
 | 
|
4900  | 
+
 | 
|
4901  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
4902  | 
+        """
 | 
|
4903  | 
+        parser=cmdutil.CmdOptionParser("fai update [options]"
 | 
|
4904  | 
+                                       " [revision/version]")
 | 
|
4905  | 
+        parser.add_option("-f", "--forward", action="store_true", 
 | 
|
4906  | 
+                          dest="patch_forward", default=False, 
 | 
|
4907  | 
+                          help="pass the --forward option to 'patch'")
 | 
|
4908  | 
+        parser.add_option("-s", "--silent", action="store_true", 
 | 
|
4909  | 
+                          dest="suppress_chatter", default=False, 
 | 
|
4910  | 
+                          help="Suppress chatter messages")
 | 
|
4911  | 
+        return parser
 | 
|
4912  | 
+
 | 
|
4913  | 
+    def help(self, parser=None):
 | 
|
4914  | 
+        """
 | 
|
4915  | 
+        Prints a help message.
 | 
|
4916  | 
+
 | 
|
4917  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
4918  | 
+        not supplied, it is retrieved.
 | 
|
4919  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
4920  | 
+        """
 | 
|
4921  | 
+        if parser is None:
 | 
|
4922  | 
+            parser=self.get_parser()
 | 
|
4923  | 
+        parser.print_help()
 | 
|
4924  | 
+        print """
 | 
|
4925  | 
+Updates a working tree to the current archive revision
 | 
|
4926  | 
+
 | 
|
4927  | 
+If a revision or version is specified, that is used instead 
 | 
|
4928  | 
+        """
 | 
|
4929  | 
+        help_tree_spec() 
 | 
|
4930  | 
+        return
 | 
|
4931  | 
+
 | 
|
4932  | 
+
 | 
|
4933  | 
+class Commit(BaseCommand):
 | 
|
4934  | 
+    """
 | 
|
4935  | 
+    Create a revision based on the changes in the current tree.
 | 
|
4936  | 
+    """
 | 
|
4937  | 
+    
 | 
|
4938  | 
+    def __init__(self):
 | 
|
4939  | 
+        self.description="Write local changes to the archive"
 | 
|
4940  | 
+
 | 
|
4941  | 
+    def get_completer(self, arg, index):
 | 
|
4942  | 
+        if arg is None:
 | 
|
4943  | 
+            arg = ""
 | 
|
4944  | 
+        return iter_modified_file_completions(arch.tree_root(), arg)
 | 
|
4945  | 
+#        return iter_source_file_completions(arch.tree_root(), arg)
 | 
|
4946  | 
+    
 | 
|
4947  | 
+    def parse_commandline(self, cmdline, tree):
 | 
|
4948  | 
+        """
 | 
|
4949  | 
+        Parse commandline arguments.  Raise cmtutil.GetHelp if help is needed.
 | 
|
4950  | 
+        
 | 
|
4951  | 
+        :param cmdline: A list of arguments to parse
 | 
|
4952  | 
+        :rtype: (options, Revision, Revision/WorkingTree)
 | 
|
4953  | 
+        """
 | 
|
4954  | 
+        parser=self.get_parser()
 | 
|
4955  | 
+        (options, args) = parser.parse_args(cmdline)
 | 
|
4956  | 
+
 | 
|
4957  | 
+        if len(args) == 0:
 | 
|
4958  | 
+            args = None
 | 
|
4959  | 
+        revision=cmdutil.determine_revision_arch(tree, options.version)
 | 
|
4960  | 
+        return options, revision.get_version(), args
 | 
|
4961  | 
+
 | 
|
4962  | 
+    def do_command(self, cmdargs):
 | 
|
4963  | 
+        """
 | 
|
4964  | 
+        Master function that perfoms the "commit" command.
 | 
|
4965  | 
+        """
 | 
|
4966  | 
+        tree=arch.tree_root()
 | 
|
4967  | 
+        options, version, files = self.parse_commandline(cmdargs, tree)
 | 
|
4968  | 
+        if options.__dict__.has_key("base") and options.base:
 | 
|
4969  | 
+            base = cmdutil.determine_revision_tree(tree, options.base)
 | 
|
4970  | 
+        else:
 | 
|
4971  | 
+            base = cmdutil.submit_revision(tree)
 | 
|
4972  | 
+        
 | 
|
4973  | 
+        writeversion=version
 | 
|
4974  | 
+        archive=version.archive
 | 
|
4975  | 
+        source=cmdutil.get_mirror_source(archive)
 | 
|
4976  | 
+        allow_old=False
 | 
|
4977  | 
+        writethrough="implicit"
 | 
|
4978  | 
+
 | 
|
4979  | 
+        if source!=None:
 | 
|
4980  | 
+            if writethrough=="explicit" and \
 | 
|
4981  | 
+                cmdutil.prompt("Writethrough"):
 | 
|
4982  | 
+                writeversion=arch.Version(str(source)+"/"+str(version.get_nonarch()))
 | 
|
4983  | 
+            elif writethrough=="none":
 | 
|
4984  | 
+                raise CommitToMirror(archive)
 | 
|
4985  | 
+
 | 
|
4986  | 
+        elif archive.is_mirror:
 | 
|
4987  | 
+            raise CommitToMirror(archive)
 | 
|
4988  | 
+
 | 
|
4989  | 
+        try:
 | 
|
4990  | 
+            last_revision=tree.iter_logs(version, True).next().revision
 | 
|
4991  | 
+        except StopIteration, e:
 | 
|
4992  | 
+            if cmdutil.prompt("Import from commit"):
 | 
|
4993  | 
+                return do_import(version)
 | 
|
4994  | 
+            else:
 | 
|
4995  | 
+                raise NoVersionLogs(version)
 | 
|
4996  | 
+        if last_revision!=version.iter_revisions(True).next():
 | 
|
4997  | 
+            if not cmdutil.prompt("Out of date"):
 | 
|
4998  | 
+                raise OutOfDate
 | 
|
4999  | 
+            else:
 | 
|
5000  | 
+                allow_old=True
 | 
|
5001  | 
+
 | 
|
5002  | 
+        try:
 | 
|
5003  | 
+            if not cmdutil.has_changed(version):
 | 
|
5004  | 
+                if not cmdutil.prompt("Empty commit"):
 | 
|
5005  | 
+                    raise EmptyCommit
 | 
|
5006  | 
+        except arch.util.ExecProblem, e:
 | 
|
5007  | 
+            if e.proc.error and e.proc.error.startswith(
 | 
|
5008  | 
+                "missing explicit id for file"):
 | 
|
5009  | 
+                raise MissingID(e)
 | 
|
5010  | 
+            else:
 | 
|
5011  | 
+                raise
 | 
|
5012  | 
+        log = tree.log_message(create=False)
 | 
|
5013  | 
+        if log is None:
 | 
|
5014  | 
+            try:
 | 
|
5015  | 
+                if cmdutil.prompt("Create log"):
 | 
|
5016  | 
+                    edit_log(tree)
 | 
|
5017  | 
+
 | 
|
5018  | 
+            except cmdutil.NoEditorSpecified, e:
 | 
|
5019  | 
+                raise CommandFailed(e)
 | 
|
5020  | 
+            log = tree.log_message(create=False)
 | 
|
5021  | 
+        if log is None: 
 | 
|
5022  | 
+            raise NoLogMessage
 | 
|
5023  | 
+        if log["Summary"] is None or len(log["Summary"].strip()) == 0:
 | 
|
5024  | 
+            if not cmdutil.prompt("Omit log summary"):
 | 
|
5025  | 
+                raise errors.NoLogSummary
 | 
|
5026  | 
+        try:
 | 
|
5027  | 
+            for line in tree.iter_commit(version, seal=options.seal_version,
 | 
|
5028  | 
+                base=base, out_of_date_ok=allow_old, file_list=files):
 | 
|
5029  | 
+                cmdutil.colorize(line, options.suppress_chatter)
 | 
|
5030  | 
+
 | 
|
5031  | 
+        except arch.util.ExecProblem, e:
 | 
|
5032  | 
+            if e.proc.error and e.proc.error.startswith(
 | 
|
5033  | 
+                "These files violate naming conventions:"):
 | 
|
5034  | 
+                raise LintFailure(e.proc.error)
 | 
|
5035  | 
+            else:
 | 
|
5036  | 
+                raise
 | 
|
5037  | 
+
 | 
|
5038  | 
+    def get_parser(self):
 | 
|
5039  | 
+        """
 | 
|
5040  | 
+        Returns the options parser to use for the "commit" command.
 | 
|
5041  | 
+
 | 
|
5042  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
5043  | 
+        """
 | 
|
5044  | 
+
 | 
|
5045  | 
+        parser=cmdutil.CmdOptionParser("fai commit [options] [file1]"
 | 
|
5046  | 
+                                       " [file2...]")
 | 
|
5047  | 
+        parser.add_option("--seal", action="store_true", 
 | 
|
5048  | 
+                          dest="seal_version", default=False, 
 | 
|
5049  | 
+                          help="seal this version")
 | 
|
5050  | 
+        parser.add_option("-v", "--version", dest="version", 
 | 
|
5051  | 
+                          help="Use the specified version", 
 | 
|
5052  | 
+                          metavar="VERSION")
 | 
|
5053  | 
+        parser.add_option("-s", "--silent", action="store_true", 
 | 
|
5054  | 
+                          dest="suppress_chatter", default=False, 
 | 
|
5055  | 
+                          help="Suppress chatter messages")
 | 
|
5056  | 
+        if cmdutil.supports_switch("commit", "--base"):
 | 
|
5057  | 
+            parser.add_option("--base", dest="base", help="", 
 | 
|
5058  | 
+                              metavar="REVISION")
 | 
|
5059  | 
+        return parser
 | 
|
5060  | 
+
 | 
|
5061  | 
+    def help(self, parser=None):
 | 
|
5062  | 
+        """
 | 
|
5063  | 
+        Prints a help message.
 | 
|
5064  | 
+
 | 
|
5065  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
5066  | 
+        not supplied, it is retrieved.
 | 
|
5067  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
5068  | 
+        """
 | 
|
5069  | 
+        if parser is None:
 | 
|
5070  | 
+            parser=self.get_parser()
 | 
|
5071  | 
+        parser.print_help()
 | 
|
5072  | 
+        print """
 | 
|
5073  | 
+Updates a working tree to the current archive revision
 | 
|
5074  | 
+
 | 
|
5075  | 
+If a version is specified, that is used instead 
 | 
|
5076  | 
+        """
 | 
|
5077  | 
+#        help_tree_spec() 
 | 
|
5078  | 
+        return
 | 
|
5079  | 
+
 | 
|
5080  | 
+
 | 
|
5081  | 
+
 | 
|
5082  | 
+class CatLog(BaseCommand):
 | 
|
5083  | 
+    """
 | 
|
5084  | 
+    Print the log of a given file (from current tree)
 | 
|
5085  | 
+    """
 | 
|
5086  | 
+    def __init__(self):
 | 
|
5087  | 
+        self.description="Prints the patch log for a revision"
 | 
|
5088  | 
+
 | 
|
5089  | 
+    def get_completer(self, arg, index):
 | 
|
5090  | 
+        if index > 0:
 | 
|
5091  | 
+            return None
 | 
|
5092  | 
+        try:
 | 
|
5093  | 
+            tree = arch.tree_root()
 | 
|
5094  | 
+        except:
 | 
|
5095  | 
+            tree = None
 | 
|
5096  | 
+        return cmdutil.iter_revision_completions(arg, tree)
 | 
|
5097  | 
+
 | 
|
5098  | 
+    def do_command(self, cmdargs):
 | 
|
5099  | 
+        """
 | 
|
5100  | 
+        Master function that perfoms the "cat-log" command.
 | 
|
5101  | 
+        """
 | 
|
5102  | 
+        parser=self.get_parser()
 | 
|
5103  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
5104  | 
+        try:
 | 
|
5105  | 
+            tree = arch.tree_root()
 | 
|
5106  | 
+        except arch.errors.TreeRootError, e:
 | 
|
5107  | 
+            tree = None
 | 
|
5108  | 
+        spec=None
 | 
|
5109  | 
+        if len(args) > 0:
 | 
|
5110  | 
+            spec=args[0]
 | 
|
5111  | 
+        if len(args) > 1:
 | 
|
5112  | 
+            raise cmdutil.GetHelp()
 | 
|
5113  | 
+        try:
 | 
|
5114  | 
+            if tree:
 | 
|
5115  | 
+                revision = cmdutil.determine_revision_tree(tree, spec)
 | 
|
5116  | 
+            else:
 | 
|
5117  | 
+                revision = cmdutil.determine_revision_arch(tree, spec)
 | 
|
5118  | 
+        except cmdutil.CantDetermineRevision, e:
 | 
|
5119  | 
+            raise CommandFailedWrapper(e)
 | 
|
5120  | 
+        log = None
 | 
|
5121  | 
+        
 | 
|
5122  | 
+        use_tree = (options.source == "tree" or \
 | 
|
5123  | 
+            (options.source == "any" and tree))
 | 
|
5124  | 
+        use_arch = (options.source == "archive" or options.source == "any")
 | 
|
5125  | 
+        
 | 
|
5126  | 
+        log = None
 | 
|
5127  | 
+        if use_tree:
 | 
|
5128  | 
+            for log in tree.iter_logs(revision.get_version()):
 | 
|
5129  | 
+                if log.revision == revision:
 | 
|
5130  | 
+                    break
 | 
|
5131  | 
+                else:
 | 
|
5132  | 
+                    log = None
 | 
|
5133  | 
+        if log is None and use_arch:
 | 
|
5134  | 
+            cmdutil.ensure_revision_exists(revision)
 | 
|
5135  | 
+            log = arch.Patchlog(revision)
 | 
|
5136  | 
+        if log is not None:
 | 
|
5137  | 
+            for item in log.items():
 | 
|
5138  | 
+                print "%s: %s" % item
 | 
|
5139  | 
+            print log.description
 | 
|
5140  | 
+
 | 
|
5141  | 
+    def get_parser(self):
 | 
|
5142  | 
+        """
 | 
|
5143  | 
+        Returns the options parser to use for the "cat-log" command.
 | 
|
5144  | 
+
 | 
|
5145  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
5146  | 
+        """
 | 
|
5147  | 
+        parser=cmdutil.CmdOptionParser("fai cat-log [revision]")
 | 
|
5148  | 
+        parser.add_option("--archive", action="store_const", dest="source",
 | 
|
5149  | 
+                          const="archive", default="any",
 | 
|
5150  | 
+                          help="Always get the log from the archive")
 | 
|
5151  | 
+        parser.add_option("--tree", action="store_const", dest="source",
 | 
|
5152  | 
+                          const="tree", help="Always get the log from the tree")
 | 
|
5153  | 
+        return parser 
 | 
|
5154  | 
+
 | 
|
5155  | 
+    def help(self, parser=None):
 | 
|
5156  | 
+        """
 | 
|
5157  | 
+        Prints a help message.
 | 
|
5158  | 
+
 | 
|
5159  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
5160  | 
+        not supplied, it is retrieved.
 | 
|
5161  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
5162  | 
+        """
 | 
|
5163  | 
+        if parser==None:
 | 
|
5164  | 
+            parser=self.get_parser()
 | 
|
5165  | 
+        parser.print_help()
 | 
|
5166  | 
+        print """
 | 
|
5167  | 
+Prints the log for the specified revision
 | 
|
5168  | 
+        """
 | 
|
5169  | 
+        help_tree_spec()
 | 
|
5170  | 
+        return
 | 
|
5171  | 
+
 | 
|
5172  | 
+class Revert(BaseCommand):
 | 
|
5173  | 
+    """ Reverts a tree (or aspects of it) to a revision
 | 
|
5174  | 
+    """
 | 
|
5175  | 
+    def __init__(self):
 | 
|
5176  | 
+        self.description="Reverts a tree (or aspects of it) to a revision "
 | 
|
5177  | 
+
 | 
|
5178  | 
+    def get_completer(self, arg, index):
 | 
|
5179  | 
+        if index > 0:
 | 
|
5180  | 
+            return None
 | 
|
5181  | 
+        try:
 | 
|
5182  | 
+            tree = arch.tree_root()
 | 
|
5183  | 
+        except:
 | 
|
5184  | 
+            tree = None
 | 
|
5185  | 
+        return iter_modified_file_completions(tree, arg)
 | 
|
5186  | 
+
 | 
|
5187  | 
+    def do_command(self, cmdargs):
 | 
|
5188  | 
+        """
 | 
|
5189  | 
+        Master function that perfoms the "revert" command.
 | 
|
5190  | 
+        """
 | 
|
5191  | 
+        parser=self.get_parser()
 | 
|
5192  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
5193  | 
+        try:
 | 
|
5194  | 
+            tree = arch.tree_root()
 | 
|
5195  | 
+        except arch.errors.TreeRootError, e:
 | 
|
5196  | 
+            raise CommandFailed(e)
 | 
|
5197  | 
+        spec=None
 | 
|
5198  | 
+        if options.revision is not None:
 | 
|
5199  | 
+            spec=options.revision
 | 
|
5200  | 
+        try:
 | 
|
5201  | 
+            if spec is not None:
 | 
|
5202  | 
+                revision = cmdutil.determine_revision_tree(tree, spec)
 | 
|
5203  | 
+            else:
 | 
|
5204  | 
+                revision = cmdutil.comp_revision(tree)
 | 
|
5205  | 
+        except cmdutil.CantDetermineRevision, e:
 | 
|
5206  | 
+            raise CommandFailedWrapper(e)
 | 
|
5207  | 
+        munger = None
 | 
|
5208  | 
+
 | 
|
5209  | 
+        if options.file_contents or options.file_perms or options.deletions\
 | 
|
5210  | 
+            or options.additions or options.renames or options.hunk_prompt:
 | 
|
5211  | 
+            munger = cmdutil.MungeOpts()
 | 
|
5212  | 
+            munger.hunk_prompt = options.hunk_prompt
 | 
|
5213  | 
+
 | 
|
5214  | 
+        if len(args) > 0 or options.logs or options.pattern_files or \
 | 
|
5215  | 
+            options.control:
 | 
|
5216  | 
+            if munger is None:
 | 
|
5217  | 
+                munger = cmdutil.MungeOpts(True)
 | 
|
5218  | 
+                munger.all_types(True)
 | 
|
5219  | 
+        if len(args) > 0:
 | 
|
5220  | 
+            t_cwd = cmdutil.tree_cwd(tree)
 | 
|
5221  | 
+            for name in args:
 | 
|
5222  | 
+                if len(t_cwd) > 0:
 | 
|
5223  | 
+                    t_cwd += "/"
 | 
|
5224  | 
+                name = "./" + t_cwd + name
 | 
|
5225  | 
+                munger.add_keep_file(name);
 | 
|
5226  | 
+
 | 
|
5227  | 
+        if options.file_perms:
 | 
|
5228  | 
+            munger.file_perms = True
 | 
|
5229  | 
+        if options.file_contents:
 | 
|
5230  | 
+            munger.file_contents = True
 | 
|
5231  | 
+        if options.deletions:
 | 
|
5232  | 
+            munger.deletions = True
 | 
|
5233  | 
+        if options.additions:
 | 
|
5234  | 
+            munger.additions = True
 | 
|
5235  | 
+        if options.renames:
 | 
|
5236  | 
+            munger.renames = True
 | 
|
5237  | 
+        if options.logs:
 | 
|
5238  | 
+            munger.add_keep_pattern('^\./\{arch\}/[^=].*')
 | 
|
5239  | 
+        if options.control:
 | 
|
5240  | 
+            munger.add_keep_pattern("/\.arch-ids|^\./\{arch\}|"\
 | 
|
5241  | 
+                                    "/\.arch-inventory$")
 | 
|
5242  | 
+        if options.pattern_files:
 | 
|
5243  | 
+            munger.add_keep_pattern(options.pattern_files)
 | 
|
5244  | 
+                
 | 
|
5245  | 
+        for line in cmdutil.revert(tree, revision, munger, 
 | 
|
5246  | 
+                                   not options.no_output):
 | 
|
5247  | 
+            cmdutil.colorize(line)
 | 
|
5248  | 
+
 | 
|
5249  | 
+
 | 
|
5250  | 
+    def get_parser(self):
 | 
|
5251  | 
+        """
 | 
|
5252  | 
+        Returns the options parser to use for the "cat-log" command.
 | 
|
5253  | 
+
 | 
|
5254  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
5255  | 
+        """
 | 
|
5256  | 
+        parser=cmdutil.CmdOptionParser("fai revert [options] [FILE...]")
 | 
|
5257  | 
+        parser.add_option("", "--contents", action="store_true", 
 | 
|
5258  | 
+                          dest="file_contents", 
 | 
|
5259  | 
+                          help="Revert file content changes")
 | 
|
5260  | 
+        parser.add_option("", "--permissions", action="store_true", 
 | 
|
5261  | 
+                          dest="file_perms", 
 | 
|
5262  | 
+                          help="Revert file permissions changes")
 | 
|
5263  | 
+        parser.add_option("", "--deletions", action="store_true", 
 | 
|
5264  | 
+                          dest="deletions", 
 | 
|
5265  | 
+                          help="Restore deleted files")
 | 
|
5266  | 
+        parser.add_option("", "--additions", action="store_true", 
 | 
|
5267  | 
+                          dest="additions", 
 | 
|
5268  | 
+                          help="Remove added files")
 | 
|
5269  | 
+        parser.add_option("", "--renames", action="store_true", 
 | 
|
5270  | 
+                          dest="renames", 
 | 
|
5271  | 
+                          help="Revert file names")
 | 
|
5272  | 
+        parser.add_option("--hunks", action="store_true", 
 | 
|
5273  | 
+                          dest="hunk_prompt", default=False,
 | 
|
5274  | 
+                          help="Prompt which hunks to revert")
 | 
|
5275  | 
+        parser.add_option("--pattern-files", dest="pattern_files", 
 | 
|
5276  | 
+                          help="Revert files that match this pattern", 
 | 
|
5277  | 
+                          metavar="REGEX")
 | 
|
5278  | 
+        parser.add_option("--logs", action="store_true", 
 | 
|
5279  | 
+                          dest="logs", default=False,
 | 
|
5280  | 
+                          help="Revert only logs")
 | 
|
5281  | 
+        parser.add_option("--control-files", action="store_true", 
 | 
|
5282  | 
+                          dest="control", default=False,
 | 
|
5283  | 
+                          help="Revert logs and other control files")
 | 
|
5284  | 
+        parser.add_option("-n", "--no-output", action="store_true", 
 | 
|
5285  | 
+                          dest="no_output", 
 | 
|
5286  | 
+                          help="Don't keep an undo changeset")
 | 
|
5287  | 
+        parser.add_option("--revision", dest="revision", 
 | 
|
5288  | 
+                          help="Revert to the specified revision", 
 | 
|
5289  | 
+                          metavar="REVISION")
 | 
|
5290  | 
+        return parser 
 | 
|
5291  | 
+
 | 
|
5292  | 
+    def help(self, parser=None):
 | 
|
5293  | 
+        """
 | 
|
5294  | 
+        Prints a help message.
 | 
|
5295  | 
+
 | 
|
5296  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
5297  | 
+        not supplied, it is retrieved.
 | 
|
5298  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
5299  | 
+        """
 | 
|
5300  | 
+        if parser==None:
 | 
|
5301  | 
+            parser=self.get_parser()
 | 
|
5302  | 
+        parser.print_help()
 | 
|
5303  | 
+        print """
 | 
|
5304  | 
+Reverts changes in the current working tree.  If no flags are specified, all
 | 
|
5305  | 
+types of changes are reverted.  Otherwise, only selected types of changes are
 | 
|
5306  | 
+reverted.  
 | 
|
5307  | 
+
 | 
|
5308  | 
+If a revision is specified on the commandline, differences between the current
 | 
|
5309  | 
+tree and that revision are reverted.  If a version is specified, the current
 | 
|
5310  | 
+tree is used to determine the revision.
 | 
|
5311  | 
+
 | 
|
5312  | 
+If files are specified, only those files listed will have any changes applied.
 | 
|
5313  | 
+To specify a renamed file, you can use either the old or new name. (or both!)
 | 
|
5314  | 
+
 | 
|
5315  | 
+Unless "-n" is specified, reversions can be undone with "redo".
 | 
|
5316  | 
+        """
 | 
|
5317  | 
+        return
 | 
|
5318  | 
+
 | 
|
5319  | 
+class Revision(BaseCommand):
 | 
|
5320  | 
+    """
 | 
|
5321  | 
+    Print a revision name based on a revision specifier
 | 
|
5322  | 
+    """
 | 
|
5323  | 
+    def __init__(self):
 | 
|
5324  | 
+        self.description="Prints the name of a revision"
 | 
|
5325  | 
+
 | 
|
5326  | 
+    def get_completer(self, arg, index):
 | 
|
5327  | 
+        if index > 0:
 | 
|
5328  | 
+            return None
 | 
|
5329  | 
+        try:
 | 
|
5330  | 
+            tree = arch.tree_root()
 | 
|
5331  | 
+        except:
 | 
|
5332  | 
+            tree = None
 | 
|
5333  | 
+        return cmdutil.iter_revision_completions(arg, tree)
 | 
|
5334  | 
+
 | 
|
5335  | 
+    def do_command(self, cmdargs):
 | 
|
5336  | 
+        """
 | 
|
5337  | 
+        Master function that perfoms the "revision" command.
 | 
|
5338  | 
+        """
 | 
|
5339  | 
+        parser=self.get_parser()
 | 
|
5340  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
5341  | 
+
 | 
|
5342  | 
+        try:
 | 
|
5343  | 
+            tree = arch.tree_root()
 | 
|
5344  | 
+        except arch.errors.TreeRootError:
 | 
|
5345  | 
+            tree = None
 | 
|
5346  | 
+
 | 
|
5347  | 
+        spec=None
 | 
|
5348  | 
+        if len(args) > 0:
 | 
|
5349  | 
+            spec=args[0]
 | 
|
5350  | 
+        if len(args) > 1:
 | 
|
5351  | 
+            raise cmdutil.GetHelp
 | 
|
5352  | 
+        try:
 | 
|
5353  | 
+            if tree:
 | 
|
5354  | 
+                revision = cmdutil.determine_revision_tree(tree, spec)
 | 
|
5355  | 
+            else:
 | 
|
5356  | 
+                revision = cmdutil.determine_revision_arch(tree, spec)
 | 
|
5357  | 
+        except cmdutil.CantDetermineRevision, e:
 | 
|
5358  | 
+            print str(e)
 | 
|
5359  | 
+            return
 | 
|
5360  | 
+        print options.display(revision)
 | 
|
5361  | 
+
 | 
|
5362  | 
+    def get_parser(self):
 | 
|
5363  | 
+        """
 | 
|
5364  | 
+        Returns the options parser to use for the "revision" command.
 | 
|
5365  | 
+
 | 
|
5366  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
5367  | 
+        """
 | 
|
5368  | 
+        parser=cmdutil.CmdOptionParser("fai revision [revision]")
 | 
|
5369  | 
+        parser.add_option("", "--location", action="store_const", 
 | 
|
5370  | 
+                         const=paths.determine_path, dest="display", 
 | 
|
5371  | 
+                         help="Show location instead of name", default=str)
 | 
|
5372  | 
+        parser.add_option("--import", action="store_const", 
 | 
|
5373  | 
+                         const=paths.determine_import_path, dest="display",  
 | 
|
5374  | 
+                         help="Show location of import file")
 | 
|
5375  | 
+        parser.add_option("--log", action="store_const", 
 | 
|
5376  | 
+                         const=paths.determine_log_path, dest="display", 
 | 
|
5377  | 
+                         help="Show location of log file")
 | 
|
5378  | 
+        parser.add_option("--patch", action="store_const", 
 | 
|
5379  | 
+                         dest="display", const=paths.determine_patch_path,
 | 
|
5380  | 
+                         help="Show location of patchfile")
 | 
|
5381  | 
+        parser.add_option("--continuation", action="store_const", 
 | 
|
5382  | 
+                         const=paths.determine_continuation_path, 
 | 
|
5383  | 
+                         dest="display",
 | 
|
5384  | 
+                         help="Show location of continuation file")
 | 
|
5385  | 
+        parser.add_option("--cacherev", action="store_const", 
 | 
|
5386  | 
+                         const=paths.determine_cacherev_path, dest="display",
 | 
|
5387  | 
+                         help="Show location of cacherev file")
 | 
|
5388  | 
+        return parser 
 | 
|
5389  | 
+
 | 
|
5390  | 
+    def help(self, parser=None):
 | 
|
5391  | 
+        """
 | 
|
5392  | 
+        Prints a help message.
 | 
|
5393  | 
+
 | 
|
5394  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
5395  | 
+        not supplied, it is retrieved.
 | 
|
5396  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
5397  | 
+        """
 | 
|
5398  | 
+        if parser==None:
 | 
|
5399  | 
+            parser=self.get_parser()
 | 
|
5400  | 
+        parser.print_help()
 | 
|
5401  | 
+        print """
 | 
|
5402  | 
+Expands aliases and prints the name of the specified revision.  Instead of
 | 
|
5403  | 
+the name, several options can be used to print locations.  If more than one is
 | 
|
5404  | 
+specified, the last one is used.
 | 
|
5405  | 
+        """
 | 
|
5406  | 
+        help_tree_spec()
 | 
|
5407  | 
+        return
 | 
|
5408  | 
+
 | 
|
5409  | 
+def require_version_exists(version, spec):
 | 
|
5410  | 
+    if not version.exists():
 | 
|
5411  | 
+        raise cmdutil.CantDetermineVersion(spec, 
 | 
|
5412  | 
+                                           "The version %s does not exist." \
 | 
|
5413  | 
+                                           % version)
 | 
|
5414  | 
+
 | 
|
5415  | 
+class Revisions(BaseCommand):
 | 
|
5416  | 
+    """
 | 
|
5417  | 
+    Print a revision name based on a revision specifier
 | 
|
5418  | 
+    """
 | 
|
5419  | 
+    def __init__(self):
 | 
|
5420  | 
+        self.description="Lists revisions"
 | 
|
5421  | 
+    
 | 
|
5422  | 
+    def do_command(self, cmdargs):
 | 
|
5423  | 
+        """
 | 
|
5424  | 
+        Master function that perfoms the "revision" command.
 | 
|
5425  | 
+        """
 | 
|
5426  | 
+        (options, args) = self.get_parser().parse_args(cmdargs)
 | 
|
5427  | 
+        if len(args) > 1:
 | 
|
5428  | 
+            raise cmdutil.GetHelp
 | 
|
5429  | 
+        try:
 | 
|
5430  | 
+            self.tree = arch.tree_root()
 | 
|
5431  | 
+        except arch.errors.TreeRootError:
 | 
|
5432  | 
+            self.tree = None
 | 
|
5433  | 
+        try:
 | 
|
5434  | 
+            iter = self.get_iterator(options.type, args, options.reverse, 
 | 
|
5435  | 
+                                     options.modified)
 | 
|
5436  | 
+        except cmdutil.CantDetermineRevision, e:
 | 
|
5437  | 
+            raise CommandFailedWrapper(e)
 | 
|
5438  | 
+
 | 
|
5439  | 
+        if options.skip is not None:
 | 
|
5440  | 
+            iter = cmdutil.iter_skip(iter, int(options.skip))
 | 
|
5441  | 
+
 | 
|
5442  | 
+        for revision in iter:
 | 
|
5443  | 
+            log = None
 | 
|
5444  | 
+            if isinstance(revision, arch.Patchlog):
 | 
|
5445  | 
+                log = revision
 | 
|
5446  | 
+                revision=revision.revision
 | 
|
5447  | 
+            print options.display(revision)
 | 
|
5448  | 
+            if log is None and (options.summary or options.creator or 
 | 
|
5449  | 
+                                options.date or options.merges):
 | 
|
5450  | 
+                log = revision.patchlog
 | 
|
5451  | 
+            if options.creator:
 | 
|
5452  | 
+                print "    %s" % log.creator
 | 
|
5453  | 
+            if options.date:
 | 
|
5454  | 
+                print "    %s" % time.strftime('%Y-%m-%d %H:%M:%S %Z', log.date)
 | 
|
5455  | 
+            if options.summary:
 | 
|
5456  | 
+                print "    %s" % log.summary
 | 
|
5457  | 
+            if options.merges:
 | 
|
5458  | 
+                showed_title = False
 | 
|
5459  | 
+                for revision in log.merged_patches:
 | 
|
5460  | 
+                    if not showed_title:
 | 
|
5461  | 
+                        print "    Merged:"
 | 
|
5462  | 
+                        showed_title = True
 | 
|
5463  | 
+                    print "    %s" % revision
 | 
|
5464  | 
+
 | 
|
5465  | 
+    def get_iterator(self, type, args, reverse, modified):
 | 
|
5466  | 
+        if len(args) > 0:
 | 
|
5467  | 
+            spec = args[0]
 | 
|
5468  | 
+        else:
 | 
|
5469  | 
+            spec = None
 | 
|
5470  | 
+        if modified is not None:
 | 
|
5471  | 
+            iter = cmdutil.modified_iter(modified, self.tree)
 | 
|
5472  | 
+            if reverse:
 | 
|
5473  | 
+                return iter
 | 
|
5474  | 
+            else:
 | 
|
5475  | 
+                return cmdutil.iter_reverse(iter)
 | 
|
5476  | 
+        elif type == "archive":
 | 
|
5477  | 
+            if spec is None:
 | 
|
5478  | 
+                if self.tree is None:
 | 
|
5479  | 
+                    raise cmdutil.CantDetermineRevision("", 
 | 
|
5480  | 
+                                                        "Not in a project tree")
 | 
|
5481  | 
+                version = cmdutil.determine_version_tree(spec, self.tree)
 | 
|
5482  | 
+            else:
 | 
|
5483  | 
+                version = cmdutil.determine_version_arch(spec, self.tree)
 | 
|
5484  | 
+                cmdutil.ensure_archive_registered(version.archive)
 | 
|
5485  | 
+                require_version_exists(version, spec)
 | 
|
5486  | 
+            return version.iter_revisions(reverse)
 | 
|
5487  | 
+        elif type == "cacherevs":
 | 
|
5488  | 
+            if spec is None:
 | 
|
5489  | 
+                if self.tree is None:
 | 
|
5490  | 
+                    raise cmdutil.CantDetermineRevision("", 
 | 
|
5491  | 
+                                                        "Not in a project tree")
 | 
|
5492  | 
+                version = cmdutil.determine_version_tree(spec, self.tree)
 | 
|
5493  | 
+            else:
 | 
|
5494  | 
+                version = cmdutil.determine_version_arch(spec, self.tree)
 | 
|
5495  | 
+                cmdutil.ensure_archive_registered(version.archive)
 | 
|
5496  | 
+                require_version_exists(version, spec)
 | 
|
5497  | 
+            return cmdutil.iter_cacherevs(version, reverse)
 | 
|
5498  | 
+        elif type == "library":
 | 
|
5499  | 
+            if spec is None:
 | 
|
5500  | 
+                if self.tree is None:
 | 
|
5501  | 
+                    raise cmdutil.CantDetermineRevision("", 
 | 
|
5502  | 
+                                                        "Not in a project tree")
 | 
|
5503  | 
+                version = cmdutil.determine_version_tree(spec, self.tree)
 | 
|
5504  | 
+            else:
 | 
|
5505  | 
+                version = cmdutil.determine_version_arch(spec, self.tree)
 | 
|
5506  | 
+            return version.iter_library_revisions(reverse)
 | 
|
5507  | 
+        elif type == "logs":
 | 
|
5508  | 
+            if self.tree is None:
 | 
|
5509  | 
+                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
 | 
|
5510  | 
+            return self.tree.iter_logs(cmdutil.determine_version_tree(spec, \
 | 
|
5511  | 
+                                  self.tree), reverse)
 | 
|
5512  | 
+        elif type == "missing" or type == "skip-present":
 | 
|
5513  | 
+            if self.tree is None:
 | 
|
5514  | 
+                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
 | 
|
5515  | 
+            skip = (type == "skip-present")
 | 
|
5516  | 
+            version = cmdutil.determine_version_tree(spec, self.tree)
 | 
|
5517  | 
+            cmdutil.ensure_archive_registered(version.archive)
 | 
|
5518  | 
+            require_version_exists(version, spec)
 | 
|
5519  | 
+            return cmdutil.iter_missing(self.tree, version, reverse,
 | 
|
5520  | 
+                                        skip_present=skip)
 | 
|
5521  | 
+
 | 
|
5522  | 
+        elif type == "present":
 | 
|
5523  | 
+            if self.tree is None:
 | 
|
5524  | 
+                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
 | 
|
5525  | 
+            version = cmdutil.determine_version_tree(spec, self.tree)
 | 
|
5526  | 
+            cmdutil.ensure_archive_registered(version.archive)
 | 
|
5527  | 
+            require_version_exists(version, spec)
 | 
|
5528  | 
+            return cmdutil.iter_present(self.tree, version, reverse)
 | 
|
5529  | 
+
 | 
|
5530  | 
+        elif type == "new-merges" or type == "direct-merges":
 | 
|
5531  | 
+            if self.tree is None:
 | 
|
5532  | 
+                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
 | 
|
5533  | 
+            version = cmdutil.determine_version_tree(spec, self.tree)
 | 
|
5534  | 
+            cmdutil.ensure_archive_registered(version.archive)
 | 
|
5535  | 
+            require_version_exists(version, spec)
 | 
|
5536  | 
+            iter = cmdutil.iter_new_merges(self.tree, version, reverse)
 | 
|
5537  | 
+            if type == "new-merges":
 | 
|
5538  | 
+                return iter
 | 
|
5539  | 
+            elif type == "direct-merges":
 | 
|
5540  | 
+                return cmdutil.direct_merges(iter)
 | 
|
5541  | 
+
 | 
|
5542  | 
+        elif type == "missing-from":
 | 
|
5543  | 
+            if self.tree is None:
 | 
|
5544  | 
+                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
 | 
|
5545  | 
+            revision = cmdutil.determine_revision_tree(self.tree, spec)
 | 
|
5546  | 
+            libtree = cmdutil.find_or_make_local_revision(revision)
 | 
|
5547  | 
+            return cmdutil.iter_missing(libtree, self.tree.tree_version,
 | 
|
5548  | 
+                                        reverse)
 | 
|
5549  | 
+
 | 
|
5550  | 
+        elif type == "partner-missing":
 | 
|
5551  | 
+            return cmdutil.iter_partner_missing(self.tree, reverse)
 | 
|
5552  | 
+
 | 
|
5553  | 
+        elif type == "ancestry":
 | 
|
5554  | 
+            revision = cmdutil.determine_revision_tree(self.tree, spec)
 | 
|
5555  | 
+            iter = cmdutil._iter_ancestry(self.tree, revision)
 | 
|
5556  | 
+            if reverse:
 | 
|
5557  | 
+                return iter
 | 
|
5558  | 
+            else:
 | 
|
5559  | 
+                return cmdutil.iter_reverse(iter)
 | 
|
5560  | 
+
 | 
|
5561  | 
+        elif type == "dependencies" or type == "non-dependencies":
 | 
|
5562  | 
+            nondeps = (type == "non-dependencies")
 | 
|
5563  | 
+            revision = cmdutil.determine_revision_tree(self.tree, spec)
 | 
|
5564  | 
+            anc_iter = cmdutil._iter_ancestry(self.tree, revision)
 | 
|
5565  | 
+            iter_depends = cmdutil.iter_depends(anc_iter, nondeps)
 | 
|
5566  | 
+            if reverse:
 | 
|
5567  | 
+                return iter_depends
 | 
|
5568  | 
+            else:
 | 
|
5569  | 
+                return cmdutil.iter_reverse(iter_depends)
 | 
|
5570  | 
+        elif type == "micro":
 | 
|
5571  | 
+            return cmdutil.iter_micro(self.tree)
 | 
|
5572  | 
+
 | 
|
5573  | 
+    
 | 
|
5574  | 
+    def get_parser(self):
 | 
|
5575  | 
+        """
 | 
|
5576  | 
+        Returns the options parser to use for the "revision" command.
 | 
|
5577  | 
+
 | 
|
5578  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
5579  | 
+        """
 | 
|
5580  | 
+        parser=cmdutil.CmdOptionParser("fai revisions [revision]")
 | 
|
5581  | 
+        select = cmdutil.OptionGroup(parser, "Selection options",
 | 
|
5582  | 
+                          "Control which revisions are listed.  These options"
 | 
|
5583  | 
+                          " are mutually exclusive.  If more than one is"
 | 
|
5584  | 
+                          " specified, the last is used.")
 | 
|
5585  | 
+        select.add_option("", "--archive", action="store_const", 
 | 
|
5586  | 
+                          const="archive", dest="type", default="archive",
 | 
|
5587  | 
+                          help="List all revisions in the archive")
 | 
|
5588  | 
+        select.add_option("", "--cacherevs", action="store_const", 
 | 
|
5589  | 
+                          const="cacherevs", dest="type",
 | 
|
5590  | 
+                          help="List all revisions stored in the archive as "
 | 
|
5591  | 
+                          "complete copies")
 | 
|
5592  | 
+        select.add_option("", "--logs", action="store_const", 
 | 
|
5593  | 
+                          const="logs", dest="type",
 | 
|
5594  | 
+                          help="List revisions that have a patchlog in the "
 | 
|
5595  | 
+                          "tree")
 | 
|
5596  | 
+        select.add_option("", "--missing", action="store_const", 
 | 
|
5597  | 
+                          const="missing", dest="type",
 | 
|
5598  | 
+                          help="List revisions from the specified version that"
 | 
|
5599  | 
+                          " have no patchlog in the tree")
 | 
|
5600  | 
+        select.add_option("", "--skip-present", action="store_const", 
 | 
|
5601  | 
+                          const="skip-present", dest="type",
 | 
|
5602  | 
+                          help="List revisions from the specified version that"
 | 
|
5603  | 
+                          " have no patchlogs at all in the tree")
 | 
|
5604  | 
+        select.add_option("", "--present", action="store_const", 
 | 
|
5605  | 
+                          const="present", dest="type",
 | 
|
5606  | 
+                          help="List revisions from the specified version that"
 | 
|
5607  | 
+                          " have no patchlog in the tree, but can't be merged")
 | 
|
5608  | 
+        select.add_option("", "--missing-from", action="store_const", 
 | 
|
5609  | 
+                          const="missing-from", dest="type",
 | 
|
5610  | 
+                          help="List revisions from the specified revision "
 | 
|
5611  | 
+                          "that have no patchlog for the tree version")
 | 
|
5612  | 
+        select.add_option("", "--partner-missing", action="store_const", 
 | 
|
5613  | 
+                          const="partner-missing", dest="type",
 | 
|
5614  | 
+                          help="List revisions in partner versions that are"
 | 
|
5615  | 
+                          " missing")
 | 
|
5616  | 
+        select.add_option("", "--new-merges", action="store_const", 
 | 
|
5617  | 
+                          const="new-merges", dest="type",
 | 
|
5618  | 
+                          help="List revisions that have had patchlogs added"
 | 
|
5619  | 
+                          " to the tree since the last commit")
 | 
|
5620  | 
+        select.add_option("", "--direct-merges", action="store_const", 
 | 
|
5621  | 
+                          const="direct-merges", dest="type",
 | 
|
5622  | 
+                          help="List revisions that have been directly added"
 | 
|
5623  | 
+                          " to tree since the last commit ")
 | 
|
5624  | 
+        select.add_option("", "--library", action="store_const", 
 | 
|
5625  | 
+                          const="library", dest="type",
 | 
|
5626  | 
+                          help="List revisions in the revision library")
 | 
|
5627  | 
+        select.add_option("", "--ancestry", action="store_const", 
 | 
|
5628  | 
+                          const="ancestry", dest="type",
 | 
|
5629  | 
+                          help="List revisions that are ancestors of the "
 | 
|
5630  | 
+                          "current tree version")
 | 
|
5631  | 
+
 | 
|
5632  | 
+        select.add_option("", "--dependencies", action="store_const", 
 | 
|
5633  | 
+                          const="dependencies", dest="type",
 | 
|
5634  | 
+                          help="List revisions that the given revision "
 | 
|
5635  | 
+                          "depends on")
 | 
|
5636  | 
+
 | 
|
5637  | 
+        select.add_option("", "--non-dependencies", action="store_const", 
 | 
|
5638  | 
+                          const="non-dependencies", dest="type",
 | 
|
5639  | 
+                          help="List revisions that the given revision "
 | 
|
5640  | 
+                          "does not depend on")
 | 
|
5641  | 
+
 | 
|
5642  | 
+        select.add_option("--micro", action="store_const", 
 | 
|
5643  | 
+                          const="micro", dest="type",
 | 
|
5644  | 
+                          help="List partner revisions aimed for this "
 | 
|
5645  | 
+                          "micro-branch")
 | 
|
5646  | 
+
 | 
|
5647  | 
+        select.add_option("", "--modified", dest="modified", 
 | 
|
5648  | 
+                          help="List tree ancestor revisions that modified a "
 | 
|
5649  | 
+                          "given file", metavar="FILE[:LINE]")
 | 
|
5650  | 
+
 | 
|
5651  | 
+        parser.add_option("", "--skip", dest="skip", 
 | 
|
5652  | 
+                          help="Skip revisions.  Positive numbers skip from "
 | 
|
5653  | 
+                          "beginning, negative skip from end.",
 | 
|
5654  | 
+                          metavar="NUMBER")
 | 
|
5655  | 
+
 | 
|
5656  | 
+        parser.add_option_group(select)
 | 
|
5657  | 
+
 | 
|
5658  | 
+        format = cmdutil.OptionGroup(parser, "Revision format options",
 | 
|
5659  | 
+                          "These control the appearance of listed revisions")
 | 
|
5660  | 
+        format.add_option("", "--location", action="store_const", 
 | 
|
5661  | 
+                         const=paths.determine_path, dest="display", 
 | 
|
5662  | 
+                         help="Show location instead of name", default=str)
 | 
|
5663  | 
+        format.add_option("--import", action="store_const", 
 | 
|
5664  | 
+                         const=paths.determine_import_path, dest="display",  
 | 
|
5665  | 
+                         help="Show location of import file")
 | 
|
5666  | 
+        format.add_option("--log", action="store_const", 
 | 
|
5667  | 
+                         const=paths.determine_log_path, dest="display", 
 | 
|
5668  | 
+                         help="Show location of log file")
 | 
|
5669  | 
+        format.add_option("--patch", action="store_const", 
 | 
|
5670  | 
+                         dest="display", const=paths.determine_patch_path,
 | 
|
5671  | 
+                         help="Show location of patchfile")
 | 
|
5672  | 
+        format.add_option("--continuation", action="store_const", 
 | 
|
5673  | 
+                         const=paths.determine_continuation_path, 
 | 
|
5674  | 
+                         dest="display",
 | 
|
5675  | 
+                         help="Show location of continuation file")
 | 
|
5676  | 
+        format.add_option("--cacherev", action="store_const", 
 | 
|
5677  | 
+                         const=paths.determine_cacherev_path, dest="display",
 | 
|
5678  | 
+                         help="Show location of cacherev file")
 | 
|
5679  | 
+        parser.add_option_group(format)
 | 
|
5680  | 
+        display = cmdutil.OptionGroup(parser, "Display format options",
 | 
|
5681  | 
+                          "These control the display of data")
 | 
|
5682  | 
+        display.add_option("-r", "--reverse", action="store_true", 
 | 
|
5683  | 
+                          dest="reverse", help="Sort from newest to oldest")
 | 
|
5684  | 
+        display.add_option("-s", "--summary", action="store_true", 
 | 
|
5685  | 
+                          dest="summary", help="Show patchlog summary")
 | 
|
5686  | 
+        display.add_option("-D", "--date", action="store_true", 
 | 
|
5687  | 
+                          dest="date", help="Show patchlog date")
 | 
|
5688  | 
+        display.add_option("-c", "--creator", action="store_true", 
 | 
|
5689  | 
+                          dest="creator", help="Show the id that committed the"
 | 
|
5690  | 
+                          " revision")
 | 
|
5691  | 
+        display.add_option("-m", "--merges", action="store_true", 
 | 
|
5692  | 
+                          dest="merges", help="Show the revisions that were"
 | 
|
5693  | 
+                          " merged")
 | 
|
5694  | 
+        parser.add_option_group(display)
 | 
|
5695  | 
+        return parser 
 | 
|
5696  | 
+    def help(self, parser=None):
 | 
|
5697  | 
+        """Attempt to explain the revisions command
 | 
|
5698  | 
+        
 | 
|
5699  | 
+        :param parser: If supplied, used to determine options
 | 
|
5700  | 
+        """
 | 
|
5701  | 
+        if parser==None:
 | 
|
5702  | 
+            parser=self.get_parser()
 | 
|
5703  | 
+        parser.print_help()
 | 
|
5704  | 
+        print """List revisions.
 | 
|
5705  | 
+        """
 | 
|
5706  | 
+        help_tree_spec()
 | 
|
5707  | 
+
 | 
|
5708  | 
+
 | 
|
5709  | 
+class Get(BaseCommand):
 | 
|
5710  | 
+    """
 | 
|
5711  | 
+    Retrieve a revision from the archive
 | 
|
5712  | 
+    """
 | 
|
5713  | 
+    def __init__(self):
 | 
|
5714  | 
+        self.description="Retrieve a revision from the archive"
 | 
|
5715  | 
+        self.parser=self.get_parser()
 | 
|
5716  | 
+
 | 
|
5717  | 
+
 | 
|
5718  | 
+    def get_completer(self, arg, index):
 | 
|
5719  | 
+        if index > 0:
 | 
|
5720  | 
+            return None
 | 
|
5721  | 
+        try:
 | 
|
5722  | 
+            tree = arch.tree_root()
 | 
|
5723  | 
+        except:
 | 
|
5724  | 
+            tree = None
 | 
|
5725  | 
+        return cmdutil.iter_revision_completions(arg, tree)
 | 
|
5726  | 
+
 | 
|
5727  | 
+
 | 
|
5728  | 
+    def do_command(self, cmdargs):
 | 
|
5729  | 
+        """
 | 
|
5730  | 
+        Master function that perfoms the "get" command.
 | 
|
5731  | 
+        """
 | 
|
5732  | 
+        (options, args) = self.parser.parse_args(cmdargs)
 | 
|
5733  | 
+        if len(args) < 1:
 | 
|
5734  | 
+            return self.help()            
 | 
|
5735  | 
+        try:
 | 
|
5736  | 
+            tree = arch.tree_root()
 | 
|
5737  | 
+        except arch.errors.TreeRootError:
 | 
|
5738  | 
+            tree = None
 | 
|
5739  | 
+        
 | 
|
5740  | 
+        arch_loc = None
 | 
|
5741  | 
+        try:
 | 
|
5742  | 
+            revision, arch_loc = paths.full_path_decode(args[0])
 | 
|
5743  | 
+        except Exception, e:
 | 
|
5744  | 
+            revision = cmdutil.determine_revision_arch(tree, args[0], 
 | 
|
5745  | 
+                check_existence=False, allow_package=True)
 | 
|
5746  | 
+        if len(args) > 1:
 | 
|
5747  | 
+            directory = args[1]
 | 
|
5748  | 
+        else:
 | 
|
5749  | 
+            directory = str(revision.nonarch)
 | 
|
5750  | 
+        if os.path.exists(directory):
 | 
|
5751  | 
+            raise DirectoryExists(directory)
 | 
|
5752  | 
+        cmdutil.ensure_archive_registered(revision.archive, arch_loc)
 | 
|
5753  | 
+        try:
 | 
|
5754  | 
+            cmdutil.ensure_revision_exists(revision)
 | 
|
5755  | 
+        except cmdutil.NoSuchRevision, e:
 | 
|
5756  | 
+            raise CommandFailedWrapper(e)
 | 
|
5757  | 
+
 | 
|
5758  | 
+        link = cmdutil.prompt ("get link")
 | 
|
5759  | 
+        for line in cmdutil.iter_get(revision, directory, link,
 | 
|
5760  | 
+                                     options.no_pristine,
 | 
|
5761  | 
+                                     options.no_greedy_add):
 | 
|
5762  | 
+            cmdutil.colorize(line)
 | 
|
5763  | 
+
 | 
|
5764  | 
+    def get_parser(self):
 | 
|
5765  | 
+        """
 | 
|
5766  | 
+        Returns the options parser to use for the "get" command.
 | 
|
5767  | 
+
 | 
|
5768  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
5769  | 
+        """
 | 
|
5770  | 
+        parser=cmdutil.CmdOptionParser("fai get revision [dir]")
 | 
|
5771  | 
+        parser.add_option("--no-pristine", action="store_true", 
 | 
|
5772  | 
+                         dest="no_pristine", 
 | 
|
5773  | 
+                         help="Do not make pristine copy for reference")
 | 
|
5774  | 
+        parser.add_option("--no-greedy-add", action="store_true", 
 | 
|
5775  | 
+                         dest="no_greedy_add", 
 | 
|
5776  | 
+                         help="Never add to greedy libraries")
 | 
|
5777  | 
+
 | 
|
5778  | 
+        return parser 
 | 
|
5779  | 
+
 | 
|
5780  | 
+    def help(self, parser=None):
 | 
|
5781  | 
+        """
 | 
|
5782  | 
+        Prints a help message.
 | 
|
5783  | 
+
 | 
|
5784  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
5785  | 
+        not supplied, it is retrieved.
 | 
|
5786  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
5787  | 
+        """
 | 
|
5788  | 
+        if parser==None:
 | 
|
5789  | 
+            parser=self.get_parser()
 | 
|
5790  | 
+        parser.print_help()
 | 
|
5791  | 
+        print """
 | 
|
5792  | 
+Expands aliases and constructs a project tree for a revision.  If the optional
 | 
|
5793  | 
+"dir" argument is provided, the project tree will be stored in this directory.
 | 
|
5794  | 
+        """
 | 
|
5795  | 
+        help_tree_spec()
 | 
|
5796  | 
+        return
 | 
|
5797  | 
+
 | 
|
5798  | 
+class PromptCmd(cmd.Cmd):
 | 
|
5799  | 
+    def __init__(self):
 | 
|
5800  | 
+        cmd.Cmd.__init__(self)
 | 
|
5801  | 
+        self.prompt = "Fai> "
 | 
|
5802  | 
+        try:
 | 
|
5803  | 
+            self.tree = arch.tree_root()
 | 
|
5804  | 
+        except:
 | 
|
5805  | 
+            self.tree = None
 | 
|
5806  | 
+        self.set_title()
 | 
|
5807  | 
+        self.set_prompt()
 | 
|
5808  | 
+        self.fake_aba = abacmds.AbaCmds()
 | 
|
5809  | 
+        self.identchars += '-'
 | 
|
5810  | 
+        self.history_file = os.path.expanduser("~/.fai-history")
 | 
|
5811  | 
+        readline.set_completer_delims(string.whitespace)
 | 
|
5812  | 
+        if os.access(self.history_file, os.R_OK) and \
 | 
|
5813  | 
+            os.path.isfile(self.history_file):
 | 
|
5814  | 
+            readline.read_history_file(self.history_file)
 | 
|
5815  | 
+
 | 
|
5816  | 
+    def write_history(self):
 | 
|
5817  | 
+        readline.write_history_file(self.history_file)
 | 
|
5818  | 
+
 | 
|
5819  | 
+    def do_quit(self, args):
 | 
|
5820  | 
+        self.write_history()
 | 
|
5821  | 
+        sys.exit(0)
 | 
|
5822  | 
+
 | 
|
5823  | 
+    def do_exit(self, args):
 | 
|
5824  | 
+        self.do_quit(args)
 | 
|
5825  | 
+
 | 
|
5826  | 
+    def do_EOF(self, args):
 | 
|
5827  | 
+        print
 | 
|
5828  | 
+        self.do_quit(args)
 | 
|
5829  | 
+
 | 
|
5830  | 
+    def postcmd(self, line, bar):
 | 
|
5831  | 
+        self.set_title()
 | 
|
5832  | 
+        self.set_prompt()
 | 
|
5833  | 
+
 | 
|
5834  | 
+    def set_prompt(self):
 | 
|
5835  | 
+        if self.tree is not None:
 | 
|
5836  | 
+            try:
 | 
|
5837  | 
+                version = " "+self.tree.tree_version.nonarch
 | 
|
5838  | 
+            except:
 | 
|
5839  | 
+                version = ""
 | 
|
5840  | 
+        else:
 | 
|
5841  | 
+            version = ""
 | 
|
5842  | 
+        self.prompt = "Fai%s> " % version
 | 
|
5843  | 
+
 | 
|
5844  | 
+    def set_title(self, command=None):
 | 
|
5845  | 
+        try:
 | 
|
5846  | 
+            version = self.tree.tree_version.nonarch
 | 
|
5847  | 
+        except:
 | 
|
5848  | 
+            version = "[no version]"
 | 
|
5849  | 
+        if command is None:
 | 
|
5850  | 
+            command = ""
 | 
|
5851  | 
+        sys.stdout.write(terminal.term_title("Fai %s %s" % (command, version)))
 | 
|
5852  | 
+
 | 
|
5853  | 
+    def do_cd(self, line):
 | 
|
5854  | 
+        if line == "":
 | 
|
5855  | 
+            line = "~"
 | 
|
5856  | 
+        try:
 | 
|
5857  | 
+            os.chdir(os.path.expanduser(line))
 | 
|
5858  | 
+        except Exception, e:
 | 
|
5859  | 
+            print e
 | 
|
5860  | 
+        try:
 | 
|
5861  | 
+            self.tree = arch.tree_root()
 | 
|
5862  | 
+        except:
 | 
|
5863  | 
+            self.tree = None
 | 
|
5864  | 
+
 | 
|
5865  | 
+    def do_help(self, line):
 | 
|
5866  | 
+        Help()(line)
 | 
|
5867  | 
+
 | 
|
5868  | 
+    def default(self, line):
 | 
|
5869  | 
+        args = line.split()
 | 
|
5870  | 
+        if find_command(args[0]):
 | 
|
5871  | 
+            try:
 | 
|
5872  | 
+                find_command(args[0]).do_command(args[1:])
 | 
|
5873  | 
+            except cmdutil.BadCommandOption, e:
 | 
|
5874  | 
+                print e
 | 
|
5875  | 
+            except cmdutil.GetHelp, e:
 | 
|
5876  | 
+                find_command(args[0]).help()
 | 
|
5877  | 
+            except CommandFailed, e:
 | 
|
5878  | 
+                print e
 | 
|
5879  | 
+            except arch.errors.ArchiveNotRegistered, e:
 | 
|
5880  | 
+                print e
 | 
|
5881  | 
+            except KeyboardInterrupt, e:
 | 
|
5882  | 
+                print "Interrupted"
 | 
|
5883  | 
+            except arch.util.ExecProblem, e:
 | 
|
5884  | 
+                print e.proc.error.rstrip('\n')
 | 
|
5885  | 
+            except cmdutil.CantDetermineVersion, e:
 | 
|
5886  | 
+                print e
 | 
|
5887  | 
+            except cmdutil.CantDetermineRevision, e:
 | 
|
5888  | 
+                print e
 | 
|
5889  | 
+            except Exception, e:
 | 
|
5890  | 
+                print "Unhandled error:\n%s" % cmdutil.exception_str(e)
 | 
|
5891  | 
+
 | 
|
5892  | 
+        elif suggestions.has_key(args[0]):
 | 
|
5893  | 
+            print suggestions[args[0]]
 | 
|
5894  | 
+
 | 
|
5895  | 
+        elif self.fake_aba.is_command(args[0]):
 | 
|
5896  | 
+            tree = None
 | 
|
5897  | 
+            try:
 | 
|
5898  | 
+                tree = arch.tree_root()
 | 
|
5899  | 
+            except arch.errors.TreeRootError:
 | 
|
5900  | 
+                pass
 | 
|
5901  | 
+            cmd = self.fake_aba.is_command(args[0])
 | 
|
5902  | 
+            try:
 | 
|
5903  | 
+                cmd.run(cmdutil.expand_prefix_alias(args[1:], tree))
 | 
|
5904  | 
+            except KeyboardInterrupt, e:
 | 
|
5905  | 
+                print "Interrupted"
 | 
|
5906  | 
+
 | 
|
5907  | 
+        elif options.tla_fallthrough and args[0] != "rm" and \
 | 
|
5908  | 
+            cmdutil.is_tla_command(args[0]):
 | 
|
5909  | 
+            try:
 | 
|
5910  | 
+                tree = None
 | 
|
5911  | 
+                try:
 | 
|
5912  | 
+                    tree = arch.tree_root()
 | 
|
5913  | 
+                except arch.errors.TreeRootError:
 | 
|
5914  | 
+                    pass
 | 
|
5915  | 
+                args = cmdutil.expand_prefix_alias(args, tree)
 | 
|
5916  | 
+                arch.util.exec_safe('tla', args, stderr=sys.stderr,
 | 
|
5917  | 
+                expected=(0, 1))
 | 
|
5918  | 
+            except arch.util.ExecProblem, e:
 | 
|
5919  | 
+                pass
 | 
|
5920  | 
+            except KeyboardInterrupt, e:
 | 
|
5921  | 
+                print "Interrupted"
 | 
|
5922  | 
+        else:
 | 
|
5923  | 
+            try:
 | 
|
5924  | 
+                try:
 | 
|
5925  | 
+                    tree = arch.tree_root()
 | 
|
5926  | 
+                except arch.errors.TreeRootError:
 | 
|
5927  | 
+                    tree = None
 | 
|
5928  | 
+                args=line.split()
 | 
|
5929  | 
+                os.system(" ".join(cmdutil.expand_prefix_alias(args, tree)))
 | 
|
5930  | 
+            except KeyboardInterrupt, e:
 | 
|
5931  | 
+                print "Interrupted"
 | 
|
5932  | 
+
 | 
|
5933  | 
+    def completenames(self, text, line, begidx, endidx):
 | 
|
5934  | 
+        completions = []
 | 
|
5935  | 
+        iter = iter_command_names(self.fake_aba)
 | 
|
5936  | 
+        try:
 | 
|
5937  | 
+            if len(line) > 0:
 | 
|
5938  | 
+                arg = line.split()[-1]
 | 
|
5939  | 
+            else:
 | 
|
5940  | 
+                arg = ""
 | 
|
5941  | 
+            iter = iter_munged_completions(iter, arg, text)
 | 
|
5942  | 
+        except Exception, e:
 | 
|
5943  | 
+            print e
 | 
|
5944  | 
+        return list(iter)
 | 
|
5945  | 
+
 | 
|
5946  | 
+    def completedefault(self, text, line, begidx, endidx):
 | 
|
5947  | 
+        """Perform completion for native commands.
 | 
|
5948  | 
+        
 | 
|
5949  | 
+        :param text: The text to complete
 | 
|
5950  | 
+        :type text: str
 | 
|
5951  | 
+        :param line: The entire line to complete
 | 
|
5952  | 
+        :type line: str
 | 
|
5953  | 
+        :param begidx: The start of the text in the line
 | 
|
5954  | 
+        :type begidx: int
 | 
|
5955  | 
+        :param endidx: The end of the text in the line
 | 
|
5956  | 
+        :type endidx: int
 | 
|
5957  | 
+        """
 | 
|
5958  | 
+        try:
 | 
|
5959  | 
+            (cmd, args, foo) = self.parseline(line)
 | 
|
5960  | 
+            command_obj=find_command(cmd)
 | 
|
5961  | 
+            if command_obj is not None:
 | 
|
5962  | 
+                return command_obj.complete(args.split(), text)
 | 
|
5963  | 
+            elif not self.fake_aba.is_command(cmd) and \
 | 
|
5964  | 
+                cmdutil.is_tla_command(cmd):
 | 
|
5965  | 
+                iter = cmdutil.iter_supported_switches(cmd)
 | 
|
5966  | 
+                if len(args) > 0:
 | 
|
5967  | 
+                    arg = args.split()[-1]
 | 
|
5968  | 
+                else:
 | 
|
5969  | 
+                    arg = ""
 | 
|
5970  | 
+                if arg.startswith("-"):
 | 
|
5971  | 
+                    return list(iter_munged_completions(iter, arg, text))
 | 
|
5972  | 
+                else:
 | 
|
5973  | 
+                    return list(iter_munged_completions(
 | 
|
5974  | 
+                        iter_file_completions(arg), arg, text))
 | 
|
5975  | 
+
 | 
|
5976  | 
+
 | 
|
5977  | 
+            elif cmd == "cd":
 | 
|
5978  | 
+                if len(args) > 0:
 | 
|
5979  | 
+                    arg = args.split()[-1]
 | 
|
5980  | 
+                else:
 | 
|
5981  | 
+                    arg = ""
 | 
|
5982  | 
+                iter = iter_dir_completions(arg)
 | 
|
5983  | 
+                iter = iter_munged_completions(iter, arg, text)
 | 
|
5984  | 
+                return list(iter)
 | 
|
5985  | 
+            elif len(args)>0:
 | 
|
5986  | 
+                arg = args.split()[-1]
 | 
|
5987  | 
+                return list(iter_munged_completions(iter_file_completions(arg),
 | 
|
5988  | 
+                                                    arg, text))
 | 
|
5989  | 
+            else:
 | 
|
5990  | 
+                return self.completenames(text, line, begidx, endidx)
 | 
|
5991  | 
+        except Exception, e:
 | 
|
5992  | 
+            print e
 | 
|
5993  | 
+
 | 
|
5994  | 
+
 | 
|
5995  | 
+def iter_command_names(fake_aba):
 | 
|
5996  | 
+    for entry in cmdutil.iter_combine([commands.iterkeys(), 
 | 
|
5997  | 
+                                     fake_aba.get_commands(), 
 | 
|
5998  | 
+                                     cmdutil.iter_tla_commands(False)]):
 | 
|
5999  | 
+        if not suggestions.has_key(str(entry)):
 | 
|
6000  | 
+            yield entry
 | 
|
6001  | 
+
 | 
|
6002  | 
+
 | 
|
6003  | 
+def iter_file_completions(arg, only_dirs = False):
 | 
|
6004  | 
+    """Generate an iterator that iterates through filename completions.
 | 
|
6005  | 
+
 | 
|
6006  | 
+    :param arg: The filename fragment to match
 | 
|
6007  | 
+    :type arg: str
 | 
|
6008  | 
+    :param only_dirs: If true, match only directories
 | 
|
6009  | 
+    :type only_dirs: bool
 | 
|
6010  | 
+    """
 | 
|
6011  | 
+    cwd = os.getcwd()
 | 
|
6012  | 
+    if cwd != "/":
 | 
|
6013  | 
+        extras = [".", ".."]
 | 
|
6014  | 
+    else:
 | 
|
6015  | 
+        extras = []
 | 
|
6016  | 
+    (dir, file) = os.path.split(arg)
 | 
|
6017  | 
+    if dir != "":
 | 
|
6018  | 
+        listingdir = os.path.expanduser(dir)
 | 
|
6019  | 
+    else:
 | 
|
6020  | 
+        listingdir = cwd
 | 
|
6021  | 
+    for file in cmdutil.iter_combine([os.listdir(listingdir), extras]):
 | 
|
6022  | 
+        if dir != "":
 | 
|
6023  | 
+            userfile = dir+'/'+file
 | 
|
6024  | 
+        else:
 | 
|
6025  | 
+            userfile = file
 | 
|
6026  | 
+        if userfile.startswith(arg):
 | 
|
6027  | 
+            if os.path.isdir(listingdir+'/'+file):
 | 
|
6028  | 
+                userfile+='/'
 | 
|
6029  | 
+                yield userfile
 | 
|
6030  | 
+            elif not only_dirs:
 | 
|
6031  | 
+                yield userfile
 | 
|
6032  | 
+
 | 
|
6033  | 
+def iter_munged_completions(iter, arg, text):
 | 
|
6034  | 
+    for completion in iter:
 | 
|
6035  | 
+        completion = str(completion)
 | 
|
6036  | 
+        if completion.startswith(arg):
 | 
|
6037  | 
+            yield completion[len(arg)-len(text):]
 | 
|
6038  | 
+
 | 
|
6039  | 
+def iter_source_file_completions(tree, arg):
 | 
|
6040  | 
+    treepath = cmdutil.tree_cwd(tree)
 | 
|
6041  | 
+    if len(treepath) > 0:
 | 
|
6042  | 
+        dirs = [treepath]
 | 
|
6043  | 
+    else:
 | 
|
6044  | 
+        dirs = None
 | 
|
6045  | 
+    for file in tree.iter_inventory(dirs, source=True, both=True):
 | 
|
6046  | 
+        file = file_completion_match(file, treepath, arg)
 | 
|
6047  | 
+        if file is not None:
 | 
|
6048  | 
+            yield file
 | 
|
6049  | 
+
 | 
|
6050  | 
+
 | 
|
6051  | 
+def iter_untagged(tree, dirs):
 | 
|
6052  | 
+    for file in arch_core.iter_inventory_filter(tree, dirs, tagged=False, 
 | 
|
6053  | 
+                                                categories=arch_core.non_root,
 | 
|
6054  | 
+                                                control_files=True):
 | 
|
6055  | 
+        yield file.name 
 | 
|
6056  | 
+
 | 
|
6057  | 
+
 | 
|
6058  | 
+def iter_untagged_completions(tree, arg):
 | 
|
6059  | 
+    """Generate an iterator for all visible untagged files that match arg.
 | 
|
6060  | 
+
 | 
|
6061  | 
+    :param tree: The tree to look for untagged files in
 | 
|
6062  | 
+    :type tree: `arch.WorkingTree`
 | 
|
6063  | 
+    :param arg: The argument to match
 | 
|
6064  | 
+    :type arg: str
 | 
|
6065  | 
+    :return: An iterator of all matching untagged files
 | 
|
6066  | 
+    :rtype: iterator of str
 | 
|
6067  | 
+    """
 | 
|
6068  | 
+    treepath = cmdutil.tree_cwd(tree)
 | 
|
6069  | 
+    if len(treepath) > 0:
 | 
|
6070  | 
+        dirs = [treepath]
 | 
|
6071  | 
+    else:
 | 
|
6072  | 
+        dirs = None
 | 
|
6073  | 
+
 | 
|
6074  | 
+    for file in iter_untagged(tree, dirs):
 | 
|
6075  | 
+        file = file_completion_match(file, treepath, arg)
 | 
|
6076  | 
+        if file is not None:
 | 
|
6077  | 
+            yield file
 | 
|
6078  | 
+
 | 
|
6079  | 
+
 | 
|
6080  | 
+def file_completion_match(file, treepath, arg):
 | 
|
6081  | 
+    """Determines whether a file within an arch tree matches the argument.
 | 
|
6082  | 
+
 | 
|
6083  | 
+    :param file: The rooted filename
 | 
|
6084  | 
+    :type file: str
 | 
|
6085  | 
+    :param treepath: The path to the cwd within the tree
 | 
|
6086  | 
+    :type treepath: str
 | 
|
6087  | 
+    :param arg: The prefix to match
 | 
|
6088  | 
+    :return: The completion name, or None if not a match
 | 
|
6089  | 
+    :rtype: str
 | 
|
6090  | 
+    """
 | 
|
6091  | 
+    if not file.startswith(treepath):
 | 
|
6092  | 
+        return None
 | 
|
6093  | 
+    if treepath != "":
 | 
|
6094  | 
+        file = file[len(treepath)+1:]
 | 
|
6095  | 
+
 | 
|
6096  | 
+    if not file.startswith(arg):
 | 
|
6097  | 
+        return None 
 | 
|
6098  | 
+    if os.path.isdir(file):
 | 
|
6099  | 
+        file += '/'
 | 
|
6100  | 
+    return file
 | 
|
6101  | 
+
 | 
|
6102  | 
+def iter_modified_file_completions(tree, arg):
 | 
|
6103  | 
+    """Returns a list of modified files that match the specified prefix.
 | 
|
6104  | 
+
 | 
|
6105  | 
+    :param tree: The current tree
 | 
|
6106  | 
+    :type tree: `arch.WorkingTree`
 | 
|
6107  | 
+    :param arg: The prefix to match
 | 
|
6108  | 
+    :type arg: str
 | 
|
6109  | 
+    """
 | 
|
6110  | 
+    treepath = cmdutil.tree_cwd(tree)
 | 
|
6111  | 
+    tmpdir = cmdutil.tmpdir()
 | 
|
6112  | 
+    changeset = tmpdir+"/changeset"
 | 
|
6113  | 
+    completions = []
 | 
|
6114  | 
+    revision = cmdutil.determine_revision_tree(tree)
 | 
|
6115  | 
+    for line in arch.iter_delta(revision, tree, changeset):
 | 
|
6116  | 
+        if isinstance(line, arch.FileModification):
 | 
|
6117  | 
+            file = file_completion_match(line.name[1:], treepath, arg)
 | 
|
6118  | 
+            if file is not None:
 | 
|
6119  | 
+                completions.append(file)
 | 
|
6120  | 
+    shutil.rmtree(tmpdir)
 | 
|
6121  | 
+    return completions
 | 
|
6122  | 
+
 | 
|
6123  | 
+def iter_dir_completions(arg):
 | 
|
6124  | 
+    """Generate an iterator that iterates through directory name completions.
 | 
|
6125  | 
+
 | 
|
6126  | 
+    :param arg: The directory name fragment to match
 | 
|
6127  | 
+    :type arg: str
 | 
|
6128  | 
+    """
 | 
|
6129  | 
+    return iter_file_completions(arg, True)
 | 
|
6130  | 
+
 | 
|
6131  | 
+class Shell(BaseCommand):
 | 
|
6132  | 
+    def __init__(self):
 | 
|
6133  | 
+        self.description = "Runs Fai as a shell"
 | 
|
6134  | 
+
 | 
|
6135  | 
+    def do_command(self, cmdargs):
 | 
|
6136  | 
+        if len(cmdargs)!=0:
 | 
|
6137  | 
+            raise cmdutil.GetHelp
 | 
|
6138  | 
+        prompt = PromptCmd()
 | 
|
6139  | 
+        try:
 | 
|
6140  | 
+            prompt.cmdloop()
 | 
|
6141  | 
+        finally:
 | 
|
6142  | 
+            prompt.write_history()
 | 
|
6143  | 
+
 | 
|
6144  | 
+class AddID(BaseCommand):
 | 
|
6145  | 
+    """
 | 
|
6146  | 
+    Adds an inventory id for the given file
 | 
|
6147  | 
+    """
 | 
|
6148  | 
+    def __init__(self):
 | 
|
6149  | 
+        self.description="Add an inventory id for a given file"
 | 
|
6150  | 
+
 | 
|
6151  | 
+    def get_completer(self, arg, index):
 | 
|
6152  | 
+        tree = arch.tree_root()
 | 
|
6153  | 
+        return iter_untagged_completions(tree, arg)
 | 
|
6154  | 
+
 | 
|
6155  | 
+    def do_command(self, cmdargs):
 | 
|
6156  | 
+        """
 | 
|
6157  | 
+        Master function that perfoms the "revision" command.
 | 
|
6158  | 
+        """
 | 
|
6159  | 
+        parser=self.get_parser()
 | 
|
6160  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
6161  | 
+
 | 
|
6162  | 
+        tree = arch.tree_root()
 | 
|
6163  | 
+
 | 
|
6164  | 
+        if (len(args) == 0) == (options.untagged == False):
 | 
|
6165  | 
+            raise cmdutil.GetHelp
 | 
|
6166  | 
+
 | 
|
6167  | 
+       #if options.id and len(args) != 1:
 | 
|
6168  | 
+       #    print "If --id is specified, only one file can be named."
 | 
|
6169  | 
+       #    return
 | 
|
6170  | 
+        
 | 
|
6171  | 
+        method = tree.tagging_method
 | 
|
6172  | 
+        
 | 
|
6173  | 
+        if options.id_type == "tagline":
 | 
|
6174  | 
+            if method != "tagline":
 | 
|
6175  | 
+                if not cmdutil.prompt("Tagline in other tree"):
 | 
|
6176  | 
+                    if method == "explicit":
 | 
|
6177  | 
+                        options.id_type == explicit
 | 
|
6178  | 
+                    else:
 | 
|
6179  | 
+                        print "add-id not supported for \"%s\" tagging method"\
 | 
|
6180  | 
+                            % method 
 | 
|
6181  | 
+                        return
 | 
|
6182  | 
+        
 | 
|
6183  | 
+        elif options.id_type == "explicit":
 | 
|
6184  | 
+            if method != "tagline" and method != explicit:
 | 
|
6185  | 
+                if not prompt("Explicit in other tree"):
 | 
|
6186  | 
+                    print "add-id not supported for \"%s\" tagging method" % \
 | 
|
6187  | 
+                        method
 | 
|
6188  | 
+                    return
 | 
|
6189  | 
+        
 | 
|
6190  | 
+        if options.id_type == "auto":
 | 
|
6191  | 
+            if method != "tagline" and method != "explicit":
 | 
|
6192  | 
+                print "add-id not supported for \"%s\" tagging method" % method
 | 
|
6193  | 
+                return
 | 
|
6194  | 
+            else:
 | 
|
6195  | 
+                options.id_type = method
 | 
|
6196  | 
+        if options.untagged:
 | 
|
6197  | 
+            args = None
 | 
|
6198  | 
+        self.add_ids(tree, options.id_type, args)
 | 
|
6199  | 
+
 | 
|
6200  | 
+    def add_ids(self, tree, id_type, files=()):
 | 
|
6201  | 
+        """Add inventory ids to files.
 | 
|
6202  | 
+        
 | 
|
6203  | 
+        :param tree: the tree the files are in
 | 
|
6204  | 
+        :type tree: `arch.WorkingTree`
 | 
|
6205  | 
+        :param id_type: the type of id to add: "explicit" or "tagline"
 | 
|
6206  | 
+        :type id_type: str
 | 
|
6207  | 
+        :param files: The list of files to add.  If None do all untagged.
 | 
|
6208  | 
+        :type files: tuple of str
 | 
|
6209  | 
+        """
 | 
|
6210  | 
+
 | 
|
6211  | 
+        untagged = (files is None)
 | 
|
6212  | 
+        if untagged:
 | 
|
6213  | 
+            files = list(iter_untagged(tree, None))
 | 
|
6214  | 
+        previous_files = []
 | 
|
6215  | 
+        while len(files) > 0:
 | 
|
6216  | 
+            previous_files.extend(files)
 | 
|
6217  | 
+            if id_type == "explicit":
 | 
|
6218  | 
+                cmdutil.add_id(files)
 | 
|
6219  | 
+            elif id_type == "tagline":
 | 
|
6220  | 
+                for file in files:
 | 
|
6221  | 
+                    try:
 | 
|
6222  | 
+                        cmdutil.add_tagline_or_explicit_id(file)
 | 
|
6223  | 
+                    except cmdutil.AlreadyTagged:
 | 
|
6224  | 
+                        print "\"%s\" already has a tagline." % file
 | 
|
6225  | 
+                    except cmdutil.NoCommentSyntax:
 | 
|
6226  | 
+                        pass
 | 
|
6227  | 
+            #do inventory after tagging until no untagged files are encountered
 | 
|
6228  | 
+            if untagged:
 | 
|
6229  | 
+                files = []
 | 
|
6230  | 
+                for file in iter_untagged(tree, None):
 | 
|
6231  | 
+                    if not file in previous_files:
 | 
|
6232  | 
+                        files.append(file)
 | 
|
6233  | 
+
 | 
|
6234  | 
+            else:
 | 
|
6235  | 
+                break
 | 
|
6236  | 
+
 | 
|
6237  | 
+    def get_parser(self):
 | 
|
6238  | 
+        """
 | 
|
6239  | 
+        Returns the options parser to use for the "revision" command.
 | 
|
6240  | 
+
 | 
|
6241  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
6242  | 
+        """
 | 
|
6243  | 
+        parser=cmdutil.CmdOptionParser("fai add-id file1 [file2] [file3]...")
 | 
|
6244  | 
+# ddaa suggests removing this to promote GUIDs.  Let's see who squalks.
 | 
|
6245  | 
+#        parser.add_option("-i", "--id", dest="id", 
 | 
|
6246  | 
+#                         help="Specify id for a single file", default=None)
 | 
|
6247  | 
+        parser.add_option("--tltl", action="store_true", 
 | 
|
6248  | 
+                         dest="lord_style",  help="Use Tom Lord's style of id.")
 | 
|
6249  | 
+        parser.add_option("--explicit", action="store_const", 
 | 
|
6250  | 
+                         const="explicit", dest="id_type", 
 | 
|
6251  | 
+                         help="Use an explicit id", default="auto")
 | 
|
6252  | 
+        parser.add_option("--tagline", action="store_const", 
 | 
|
6253  | 
+                         const="tagline", dest="id_type", 
 | 
|
6254  | 
+                         help="Use a tagline id")
 | 
|
6255  | 
+        parser.add_option("--untagged", action="store_true", 
 | 
|
6256  | 
+                         dest="untagged", default=False, 
 | 
|
6257  | 
+                         help="tag all untagged files")
 | 
|
6258  | 
+        return parser 
 | 
|
6259  | 
+
 | 
|
6260  | 
+    def help(self, parser=None):
 | 
|
6261  | 
+        """
 | 
|
6262  | 
+        Prints a help message.
 | 
|
6263  | 
+
 | 
|
6264  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
6265  | 
+        not supplied, it is retrieved.
 | 
|
6266  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
6267  | 
+        """
 | 
|
6268  | 
+        if parser==None:
 | 
|
6269  | 
+            parser=self.get_parser()
 | 
|
6270  | 
+        parser.print_help()
 | 
|
6271  | 
+        print """
 | 
|
6272  | 
+Adds an inventory to the specified file(s) and directories.  If --untagged is
 | 
|
6273  | 
+specified, adds inventory to all untagged files and directories.
 | 
|
6274  | 
+        """
 | 
|
6275  | 
+        return
 | 
|
6276  | 
+
 | 
|
6277  | 
+
 | 
|
6278  | 
+class Merge(BaseCommand):
 | 
|
6279  | 
+    """
 | 
|
6280  | 
+    Merges changes from other versions into the current tree
 | 
|
6281  | 
+    """
 | 
|
6282  | 
+    def __init__(self):
 | 
|
6283  | 
+        self.description="Merges changes from other versions"
 | 
|
6284  | 
+        try:
 | 
|
6285  | 
+            self.tree = arch.tree_root()
 | 
|
6286  | 
+        except:
 | 
|
6287  | 
+            self.tree = None
 | 
|
6288  | 
+
 | 
|
6289  | 
+
 | 
|
6290  | 
+    def get_completer(self, arg, index):
 | 
|
6291  | 
+        if self.tree is None:
 | 
|
6292  | 
+            raise arch.errors.TreeRootError
 | 
|
6293  | 
+        completions = list(ancillary.iter_partners(self.tree, 
 | 
|
6294  | 
+                                                   self.tree.tree_version))
 | 
|
6295  | 
+        if len(completions) == 0:
 | 
|
6296  | 
+            completions = list(self.tree.iter_log_versions())
 | 
|
6297  | 
+
 | 
|
6298  | 
+        aliases = []
 | 
|
6299  | 
+        try:
 | 
|
6300  | 
+            for completion in completions:
 | 
|
6301  | 
+                alias = ancillary.compact_alias(str(completion), self.tree)
 | 
|
6302  | 
+                if alias:
 | 
|
6303  | 
+                    aliases.extend(alias)
 | 
|
6304  | 
+
 | 
|
6305  | 
+            for completion in completions:
 | 
|
6306  | 
+                if completion.archive == self.tree.tree_version.archive:
 | 
|
6307  | 
+                    aliases.append(completion.nonarch)
 | 
|
6308  | 
+
 | 
|
6309  | 
+        except Exception, e:
 | 
|
6310  | 
+            print e
 | 
|
6311  | 
+            
 | 
|
6312  | 
+        completions.extend(aliases)
 | 
|
6313  | 
+        return completions
 | 
|
6314  | 
+
 | 
|
6315  | 
+    def do_command(self, cmdargs):
 | 
|
6316  | 
+        """
 | 
|
6317  | 
+        Master function that perfoms the "merge" command.
 | 
|
6318  | 
+        """
 | 
|
6319  | 
+        parser=self.get_parser()
 | 
|
6320  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
6321  | 
+        if options.diff3:
 | 
|
6322  | 
+            action="star-merge"
 | 
|
6323  | 
+        else:
 | 
|
6324  | 
+            action = options.action
 | 
|
6325  | 
+        
 | 
|
6326  | 
+        if self.tree is None:
 | 
|
6327  | 
+            raise arch.errors.TreeRootError(os.getcwd())
 | 
|
6328  | 
+        if cmdutil.has_changed(self.tree.tree_version):
 | 
|
6329  | 
+            raise UncommittedChanges(self.tree)
 | 
|
6330  | 
+
 | 
|
6331  | 
+        if len(args) > 0:
 | 
|
6332  | 
+            revisions = []
 | 
|
6333  | 
+            for arg in args:
 | 
|
6334  | 
+                revisions.append(cmdutil.determine_revision_arch(self.tree, 
 | 
|
6335  | 
+                                                                 arg))
 | 
|
6336  | 
+            source = "from commandline"
 | 
|
6337  | 
+        else:
 | 
|
6338  | 
+            revisions = ancillary.iter_partner_revisions(self.tree, 
 | 
|
6339  | 
+                                                         self.tree.tree_version)
 | 
|
6340  | 
+            source = "from partner version"
 | 
|
6341  | 
+        revisions = misc.rewind_iterator(revisions)
 | 
|
6342  | 
+        try:
 | 
|
6343  | 
+            revisions.next()
 | 
|
6344  | 
+            revisions.rewind()
 | 
|
6345  | 
+        except StopIteration, e:
 | 
|
6346  | 
+            revision = cmdutil.tag_cur(self.tree)
 | 
|
6347  | 
+            if revision is None:
 | 
|
6348  | 
+                raise CantDetermineRevision("", "No version specified, no "
 | 
|
6349  | 
+                                            "partner-versions, and no tag"
 | 
|
6350  | 
+                                            " source")
 | 
|
6351  | 
+            revisions = [revision]
 | 
|
6352  | 
+            source = "from tag source"
 | 
|
6353  | 
+        for revision in revisions:
 | 
|
6354  | 
+            cmdutil.ensure_archive_registered(revision.archive)
 | 
|
6355  | 
+            cmdutil.colorize(arch.Chatter("* Merging %s [%s]" % 
 | 
|
6356  | 
+                             (revision, source)))
 | 
|
6357  | 
+            if action=="native-merge" or action=="update":
 | 
|
6358  | 
+                if self.native_merge(revision, action) == 0:
 | 
|
6359  | 
+                    continue
 | 
|
6360  | 
+            elif action=="star-merge":
 | 
|
6361  | 
+                try: 
 | 
|
6362  | 
+                    self.star_merge(revision, options.diff3)
 | 
|
6363  | 
+                except errors.MergeProblem, e:
 | 
|
6364  | 
+                    break
 | 
|
6365  | 
+            if cmdutil.has_changed(self.tree.tree_version):
 | 
|
6366  | 
+                break
 | 
|
6367  | 
+
 | 
|
6368  | 
+    def star_merge(self, revision, diff3):
 | 
|
6369  | 
+        """Perform a star-merge on the current tree.
 | 
|
6370  | 
+        
 | 
|
6371  | 
+        :param revision: The revision to use for the merge
 | 
|
6372  | 
+        :type revision: `arch.Revision`
 | 
|
6373  | 
+        :param diff3: If true, do a diff3 merge
 | 
|
6374  | 
+        :type diff3: bool
 | 
|
6375  | 
+        """
 | 
|
6376  | 
+        try:
 | 
|
6377  | 
+            for line in self.tree.iter_star_merge(revision, diff3=diff3):
 | 
|
6378  | 
+                cmdutil.colorize(line)
 | 
|
6379  | 
+        except arch.util.ExecProblem, e:
 | 
|
6380  | 
+            if e.proc.status is not None and e.proc.status == 1:
 | 
|
6381  | 
+                if e.proc.error:
 | 
|
6382  | 
+                    print e.proc.error
 | 
|
6383  | 
+                raise MergeProblem
 | 
|
6384  | 
+            else:
 | 
|
6385  | 
+                raise
 | 
|
6386  | 
+
 | 
|
6387  | 
+    def native_merge(self, other_revision, action):
 | 
|
6388  | 
+        """Perform a native-merge on the current tree.
 | 
|
6389  | 
+        
 | 
|
6390  | 
+        :param other_revision: The revision to use for the merge
 | 
|
6391  | 
+        :type other_revision: `arch.Revision`
 | 
|
6392  | 
+        :return: 0 if the merge was skipped, 1 if it was applied
 | 
|
6393  | 
+        """
 | 
|
6394  | 
+        other_tree = cmdutil.find_or_make_local_revision(other_revision)
 | 
|
6395  | 
+        try:
 | 
|
6396  | 
+            if action == "native-merge":
 | 
|
6397  | 
+                ancestor = cmdutil.merge_ancestor2(self.tree, other_tree, 
 | 
|
6398  | 
+                                                   other_revision)
 | 
|
6399  | 
+            elif action == "update":
 | 
|
6400  | 
+                ancestor = cmdutil.tree_latest(self.tree, 
 | 
|
6401  | 
+                                               other_revision.version)
 | 
|
6402  | 
+        except CantDetermineRevision, e:
 | 
|
6403  | 
+            raise CommandFailedWrapper(e)
 | 
|
6404  | 
+        cmdutil.colorize(arch.Chatter("* Found common ancestor %s" % ancestor))
 | 
|
6405  | 
+        if (ancestor == other_revision):
 | 
|
6406  | 
+            cmdutil.colorize(arch.Chatter("* Skipping redundant merge" 
 | 
|
6407  | 
+                                          % ancestor))
 | 
|
6408  | 
+            return 0
 | 
|
6409  | 
+        delta = cmdutil.apply_delta(ancestor, other_tree, self.tree)    
 | 
|
6410  | 
+        for line in cmdutil.iter_apply_delta_filter(delta):
 | 
|
6411  | 
+            cmdutil.colorize(line)
 | 
|
6412  | 
+        return 1
 | 
|
6413  | 
+
 | 
|
6414  | 
+
 | 
|
6415  | 
+
 | 
|
6416  | 
+    def get_parser(self):
 | 
|
6417  | 
+        """
 | 
|
6418  | 
+        Returns the options parser to use for the "merge" command.
 | 
|
6419  | 
+
 | 
|
6420  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
6421  | 
+        """
 | 
|
6422  | 
+        parser=cmdutil.CmdOptionParser("fai merge [VERSION]")
 | 
|
6423  | 
+        parser.add_option("-s", "--star-merge", action="store_const",
 | 
|
6424  | 
+                          dest="action", help="Use star-merge",
 | 
|
6425  | 
+                          const="star-merge", default="native-merge")
 | 
|
6426  | 
+        parser.add_option("--update", action="store_const",
 | 
|
6427  | 
+                          dest="action", help="Use update picker",
 | 
|
6428  | 
+                          const="update")
 | 
|
6429  | 
+        parser.add_option("--diff3", action="store_true", 
 | 
|
6430  | 
+                         dest="diff3",  
 | 
|
6431  | 
+                         help="Use diff3 for merge (implies star-merge)")
 | 
|
6432  | 
+        return parser 
 | 
|
6433  | 
+
 | 
|
6434  | 
+    def help(self, parser=None):
 | 
|
6435  | 
+        """
 | 
|
6436  | 
+        Prints a help message.
 | 
|
6437  | 
+
 | 
|
6438  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
6439  | 
+        not supplied, it is retrieved.
 | 
|
6440  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
6441  | 
+        """
 | 
|
6442  | 
+        if parser==None:
 | 
|
6443  | 
+            parser=self.get_parser()
 | 
|
6444  | 
+        parser.print_help()
 | 
|
6445  | 
+        print """
 | 
|
6446  | 
+Performs a merge operation using the specified version.
 | 
|
6447  | 
+        """
 | 
|
6448  | 
+        return
 | 
|
6449  | 
+
 | 
|
6450  | 
+class ELog(BaseCommand):
 | 
|
6451  | 
+    """
 | 
|
6452  | 
+    Produces a raw patchlog and invokes the user's editor
 | 
|
6453  | 
+    """
 | 
|
6454  | 
+    def __init__(self):
 | 
|
6455  | 
+        self.description="Edit a patchlog to commit"
 | 
|
6456  | 
+        try:
 | 
|
6457  | 
+            self.tree = arch.tree_root()
 | 
|
6458  | 
+        except:
 | 
|
6459  | 
+            self.tree = None
 | 
|
6460  | 
+
 | 
|
6461  | 
+
 | 
|
6462  | 
+    def do_command(self, cmdargs):
 | 
|
6463  | 
+        """
 | 
|
6464  | 
+        Master function that perfoms the "elog" command.
 | 
|
6465  | 
+        """
 | 
|
6466  | 
+        parser=self.get_parser()
 | 
|
6467  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
6468  | 
+        if self.tree is None:
 | 
|
6469  | 
+            raise arch.errors.TreeRootError
 | 
|
6470  | 
+
 | 
|
6471  | 
+        edit_log(self.tree)
 | 
|
6472  | 
+
 | 
|
6473  | 
+    def get_parser(self):
 | 
|
6474  | 
+        """
 | 
|
6475  | 
+        Returns the options parser to use for the "merge" command.
 | 
|
6476  | 
+
 | 
|
6477  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
6478  | 
+        """
 | 
|
6479  | 
+        parser=cmdutil.CmdOptionParser("fai elog")
 | 
|
6480  | 
+        return parser 
 | 
|
6481  | 
+
 | 
|
6482  | 
+
 | 
|
6483  | 
+    def help(self, parser=None):
 | 
|
6484  | 
+        """
 | 
|
6485  | 
+        Invokes $EDITOR to produce a log for committing.
 | 
|
6486  | 
+
 | 
|
6487  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
6488  | 
+        not supplied, it is retrieved.
 | 
|
6489  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
6490  | 
+        """
 | 
|
6491  | 
+        if parser==None:
 | 
|
6492  | 
+            parser=self.get_parser()
 | 
|
6493  | 
+        parser.print_help()
 | 
|
6494  | 
+        print """
 | 
|
6495  | 
+Invokes $EDITOR to produce a log for committing.
 | 
|
6496  | 
+        """
 | 
|
6497  | 
+        return
 | 
|
6498  | 
+
 | 
|
6499  | 
+def edit_log(tree):
 | 
|
6500  | 
+    """Makes and edits the log for a tree.  Does all kinds of fancy things
 | 
|
6501  | 
+    like log templates and merge summaries and log-for-merge
 | 
|
6502  | 
+    
 | 
|
6503  | 
+    :param tree: The tree to edit the log for
 | 
|
6504  | 
+    :type tree: `arch.WorkingTree`
 | 
|
6505  | 
+    """
 | 
|
6506  | 
+    #ensure we have an editor before preparing the log
 | 
|
6507  | 
+    cmdutil.find_editor()
 | 
|
6508  | 
+    log = tree.log_message(create=False)
 | 
|
6509  | 
+    log_is_new = False
 | 
|
6510  | 
+    if log is None or cmdutil.prompt("Overwrite log"):
 | 
|
6511  | 
+        if log is not None:
 | 
|
6512  | 
+           os.remove(log.name)
 | 
|
6513  | 
+        log = tree.log_message(create=True)
 | 
|
6514  | 
+        log_is_new = True
 | 
|
6515  | 
+        tmplog = log.name
 | 
|
6516  | 
+        template = tree+"/{arch}/=log-template"
 | 
|
6517  | 
+        if not os.path.exists(template):
 | 
|
6518  | 
+            template = os.path.expanduser("~/.arch-params/=log-template")
 | 
|
6519  | 
+            if not os.path.exists(template):
 | 
|
6520  | 
+                template = None
 | 
|
6521  | 
+        if template:
 | 
|
6522  | 
+            shutil.copyfile(template, tmplog)
 | 
|
6523  | 
+        
 | 
|
6524  | 
+        new_merges = list(cmdutil.iter_new_merges(tree, 
 | 
|
6525  | 
+                                                  tree.tree_version))
 | 
|
6526  | 
+        log["Summary"] = merge_summary(new_merges, tree.tree_version)
 | 
|
6527  | 
+        if len(new_merges) > 0:   
 | 
|
6528  | 
+            if cmdutil.prompt("Log for merge"):
 | 
|
6529  | 
+                mergestuff = cmdutil.log_for_merge(tree)
 | 
|
6530  | 
+                log.description += mergestuff
 | 
|
6531  | 
+        log.save()
 | 
|
6532  | 
+    try:
 | 
|
6533  | 
+        cmdutil.invoke_editor(log.name)
 | 
|
6534  | 
+    except:
 | 
|
6535  | 
+        if log_is_new:
 | 
|
6536  | 
+            os.remove(log.name)
 | 
|
6537  | 
+        raise
 | 
|
6538  | 
+
 | 
|
6539  | 
+def merge_summary(new_merges, tree_version):
 | 
|
6540  | 
+    if len(new_merges) == 0:
 | 
|
6541  | 
+        return ""
 | 
|
6542  | 
+    if len(new_merges) == 1:
 | 
|
6543  | 
+        summary = new_merges[0].summary
 | 
|
6544  | 
+    else:
 | 
|
6545  | 
+        summary = "Merge"
 | 
|
6546  | 
+
 | 
|
6547  | 
+    credits = []
 | 
|
6548  | 
+    for merge in new_merges:
 | 
|
6549  | 
+        if arch.my_id() != merge.creator:
 | 
|
6550  | 
+            name = re.sub("<.*>", "", merge.creator).rstrip(" ");
 | 
|
6551  | 
+            if not name in credits:
 | 
|
6552  | 
+                credits.append(name)
 | 
|
6553  | 
+        else:
 | 
|
6554  | 
+            version = merge.revision.version
 | 
|
6555  | 
+            if version.archive == tree_version.archive:
 | 
|
6556  | 
+                if not version.nonarch in credits:
 | 
|
6557  | 
+                    credits.append(version.nonarch)
 | 
|
6558  | 
+            elif not str(version) in credits:
 | 
|
6559  | 
+                credits.append(str(version))
 | 
|
6560  | 
+
 | 
|
6561  | 
+    return ("%s (%s)") % (summary, ", ".join(credits))
 | 
|
6562  | 
+
 | 
|
6563  | 
+class MirrorArchive(BaseCommand):
 | 
|
6564  | 
+    """
 | 
|
6565  | 
+    Updates a mirror from an archive
 | 
|
6566  | 
+    """
 | 
|
6567  | 
+    def __init__(self):
 | 
|
6568  | 
+        self.description="Update a mirror from an archive"
 | 
|
6569  | 
+
 | 
|
6570  | 
+    def do_command(self, cmdargs):
 | 
|
6571  | 
+        """
 | 
|
6572  | 
+        Master function that perfoms the "revision" command.
 | 
|
6573  | 
+        """
 | 
|
6574  | 
+
 | 
|
6575  | 
+        parser=self.get_parser()
 | 
|
6576  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
6577  | 
+        if len(args) > 1:
 | 
|
6578  | 
+            raise GetHelp
 | 
|
6579  | 
+        try:
 | 
|
6580  | 
+            tree = arch.tree_root()
 | 
|
6581  | 
+        except:
 | 
|
6582  | 
+            tree = None
 | 
|
6583  | 
+
 | 
|
6584  | 
+        if len(args) == 0:
 | 
|
6585  | 
+            if tree is not None:
 | 
|
6586  | 
+                name = tree.tree_version()
 | 
|
6587  | 
+        else:
 | 
|
6588  | 
+            name = cmdutil.expand_alias(args[0], tree)
 | 
|
6589  | 
+            name = arch.NameParser(name)
 | 
|
6590  | 
+
 | 
|
6591  | 
+        to_arch = name.get_archive()
 | 
|
6592  | 
+        from_arch = cmdutil.get_mirror_source(arch.Archive(to_arch))
 | 
|
6593  | 
+        limit = name.get_nonarch()
 | 
|
6594  | 
+
 | 
|
6595  | 
+        iter = arch_core.mirror_archive(from_arch,to_arch, limit)
 | 
|
6596  | 
+        for line in arch.chatter_classifier(iter):
 | 
|
6597  | 
+            cmdutil.colorize(line)
 | 
|
6598  | 
+
 | 
|
6599  | 
+    def get_parser(self):
 | 
|
6600  | 
+        """
 | 
|
6601  | 
+        Returns the options parser to use for the "revision" command.
 | 
|
6602  | 
+
 | 
|
6603  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
6604  | 
+        """
 | 
|
6605  | 
+        parser=cmdutil.CmdOptionParser("fai mirror-archive ARCHIVE")
 | 
|
6606  | 
+        return parser 
 | 
|
6607  | 
+
 | 
|
6608  | 
+    def help(self, parser=None):
 | 
|
6609  | 
+        """
 | 
|
6610  | 
+        Prints a help message.
 | 
|
6611  | 
+
 | 
|
6612  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
6613  | 
+        not supplied, it is retrieved.
 | 
|
6614  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
6615  | 
+        """
 | 
|
6616  | 
+        if parser==None:
 | 
|
6617  | 
+            parser=self.get_parser()
 | 
|
6618  | 
+        parser.print_help()
 | 
|
6619  | 
+        print """
 | 
|
6620  | 
+Updates a mirror from an archive.  If a branch, package, or version is
 | 
|
6621  | 
+supplied, only changes under it are mirrored.
 | 
|
6622  | 
+        """
 | 
|
6623  | 
+        return
 | 
|
6624  | 
+
 | 
|
6625  | 
+def help_tree_spec():
 | 
|
6626  | 
+    print """Specifying revisions (default: tree)
 | 
|
6627  | 
+Revisions may be specified by alias, revision, version or patchlevel.
 | 
|
6628  | 
+Revisions or versions may be fully qualified.  Unqualified revisions, versions, 
 | 
|
6629  | 
+or patchlevels use the archive of the current project tree.  Versions will
 | 
|
6630  | 
+use the latest patchlevel in the tree.  Patchlevels will use the current tree-
 | 
|
6631  | 
+version.
 | 
|
6632  | 
+
 | 
|
6633  | 
+Use "alias" to list available (user and automatic) aliases."""
 | 
|
6634  | 
+
 | 
|
6635  | 
+def help_aliases(tree):
 | 
|
6636  | 
+    print """Auto-generated aliases
 | 
|
6637  | 
+ acur : The latest revision in the archive of the tree-version.  You can specfy
 | 
|
6638  | 
+        a different version like so: acur:foo--bar--0 (aliases can be used)
 | 
|
6639  | 
+ tcur : (tree current) The latest revision in the tree of the tree-version.
 | 
|
6640  | 
+        You can specify a different version like so: tcur:foo--bar--0 (aliases
 | 
|
6641  | 
+        can be used).
 | 
|
6642  | 
+tprev : (tree previous) The previous revision in the tree of the tree-version.
 | 
|
6643  | 
+        To specify an older revision, use a number, e.g. "tprev:4"
 | 
|
6644  | 
+ tanc : (tree ancestor) The ancestor revision of the tree
 | 
|
6645  | 
+        To specify an older revision, use a number, e.g. "tanc:4"
 | 
|
6646  | 
+tdate : (tree date) The latest revision from a given date (e.g. "tdate:July 6")
 | 
|
6647  | 
+ tmod : (tree modified) The latest revision to modify a given file 
 | 
|
6648  | 
+        (e.g. "tmod:engine.cpp" or "tmod:engine.cpp:16")
 | 
|
6649  | 
+ ttag : (tree tag) The revision that was tagged into the current tree revision,
 | 
|
6650  | 
+        according to the tree.
 | 
|
6651  | 
+tagcur: (tag current) The latest revision of the version that the current tree
 | 
|
6652  | 
+        was tagged from.
 | 
|
6653  | 
+mergeanc : The common ancestor of the current tree and the specified revision.
 | 
|
6654  | 
+        Defaults to the first partner-version's latest revision or to tagcur.
 | 
|
6655  | 
+   """
 | 
|
6656  | 
+    print "User aliases"
 | 
|
6657  | 
+    for parts in ancillary.iter_all_alias(tree):
 | 
|
6658  | 
+        print parts[0].rjust(10)+" : "+parts[1]
 | 
|
6659  | 
+
 | 
|
6660  | 
+
 | 
|
6661  | 
+class Inventory(BaseCommand):
 | 
|
6662  | 
+    """List the status of files in the tree"""
 | 
|
6663  | 
+    def __init__(self):
 | 
|
6664  | 
+        self.description=self.__doc__
 | 
|
6665  | 
+
 | 
|
6666  | 
+    def do_command(self, cmdargs):
 | 
|
6667  | 
+        """
 | 
|
6668  | 
+        Master function that perfoms the "revision" command.
 | 
|
6669  | 
+        """
 | 
|
6670  | 
+
 | 
|
6671  | 
+        parser=self.get_parser()
 | 
|
6672  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
6673  | 
+        tree = arch.tree_root()
 | 
|
6674  | 
+        categories = []
 | 
|
6675  | 
+
 | 
|
6676  | 
+        if (options.source):
 | 
|
6677  | 
+            categories.append(arch_core.SourceFile)
 | 
|
6678  | 
+        if (options.precious):
 | 
|
6679  | 
+            categories.append(arch_core.PreciousFile)
 | 
|
6680  | 
+        if (options.backup):
 | 
|
6681  | 
+            categories.append(arch_core.BackupFile)
 | 
|
6682  | 
+        if (options.junk):
 | 
|
6683  | 
+            categories.append(arch_core.JunkFile)
 | 
|
6684  | 
+
 | 
|
6685  | 
+        if len(categories) == 1:
 | 
|
6686  | 
+            show_leading = False
 | 
|
6687  | 
+        else:
 | 
|
6688  | 
+            show_leading = True
 | 
|
6689  | 
+
 | 
|
6690  | 
+        if len(categories) == 0:
 | 
|
6691  | 
+            categories = None
 | 
|
6692  | 
+
 | 
|
6693  | 
+        if options.untagged:
 | 
|
6694  | 
+            categories = arch_core.non_root
 | 
|
6695  | 
+            show_leading = False
 | 
|
6696  | 
+            tagged = False
 | 
|
6697  | 
+        else:
 | 
|
6698  | 
+            tagged = None
 | 
|
6699  | 
+        
 | 
|
6700  | 
+        for file in arch_core.iter_inventory_filter(tree, None, 
 | 
|
6701  | 
+            control_files=options.control_files, 
 | 
|
6702  | 
+            categories = categories, tagged=tagged):
 | 
|
6703  | 
+            print arch_core.file_line(file, 
 | 
|
6704  | 
+                                      category = show_leading, 
 | 
|
6705  | 
+                                      untagged = show_leading,
 | 
|
6706  | 
+                                      id = options.ids)
 | 
|
6707  | 
+
 | 
|
6708  | 
+    def get_parser(self):
 | 
|
6709  | 
+        """
 | 
|
6710  | 
+        Returns the options parser to use for the "revision" command.
 | 
|
6711  | 
+
 | 
|
6712  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
6713  | 
+        """
 | 
|
6714  | 
+        parser=cmdutil.CmdOptionParser("fai inventory [options]")
 | 
|
6715  | 
+        parser.add_option("--ids", action="store_true", dest="ids", 
 | 
|
6716  | 
+                          help="Show file ids")
 | 
|
6717  | 
+        parser.add_option("--control", action="store_true", 
 | 
|
6718  | 
+                          dest="control_files", help="include control files")
 | 
|
6719  | 
+        parser.add_option("--source", action="store_true", dest="source",
 | 
|
6720  | 
+                          help="List source files")
 | 
|
6721  | 
+        parser.add_option("--backup", action="store_true", dest="backup",
 | 
|
6722  | 
+                          help="List backup files")
 | 
|
6723  | 
+        parser.add_option("--precious", action="store_true", dest="precious",
 | 
|
6724  | 
+                          help="List precious files")
 | 
|
6725  | 
+        parser.add_option("--junk", action="store_true", dest="junk",
 | 
|
6726  | 
+                          help="List junk files")
 | 
|
6727  | 
+        parser.add_option("--unrecognized", action="store_true", 
 | 
|
6728  | 
+                          dest="unrecognized", help="List unrecognized files")
 | 
|
6729  | 
+        parser.add_option("--untagged", action="store_true", 
 | 
|
6730  | 
+                          dest="untagged", help="List only untagged files")
 | 
|
6731  | 
+        return parser 
 | 
|
6732  | 
+
 | 
|
6733  | 
+    def help(self, parser=None):
 | 
|
6734  | 
+        """
 | 
|
6735  | 
+        Prints a help message.
 | 
|
6736  | 
+
 | 
|
6737  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
6738  | 
+        not supplied, it is retrieved.
 | 
|
6739  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
6740  | 
+        """
 | 
|
6741  | 
+        if parser==None:
 | 
|
6742  | 
+            parser=self.get_parser()
 | 
|
6743  | 
+        parser.print_help()
 | 
|
6744  | 
+        print """
 | 
|
6745  | 
+Lists the status of files in the archive:
 | 
|
6746  | 
+S source
 | 
|
6747  | 
+P precious
 | 
|
6748  | 
+B backup
 | 
|
6749  | 
+J junk
 | 
|
6750  | 
+U unrecognized
 | 
|
6751  | 
+T tree root
 | 
|
6752  | 
+? untagged-source
 | 
|
6753  | 
+Leading letter are not displayed if only one kind of file is shown
 | 
|
6754  | 
+        """
 | 
|
6755  | 
+        return
 | 
|
6756  | 
+
 | 
|
6757  | 
+
 | 
|
6758  | 
+class Alias(BaseCommand):
 | 
|
6759  | 
+    """List or adjust aliases"""
 | 
|
6760  | 
+    def __init__(self):
 | 
|
6761  | 
+        self.description=self.__doc__
 | 
|
6762  | 
+
 | 
|
6763  | 
+    def get_completer(self, arg, index):
 | 
|
6764  | 
+        if index > 2:
 | 
|
6765  | 
+            return ()
 | 
|
6766  | 
+        try:
 | 
|
6767  | 
+            self.tree = arch.tree_root()
 | 
|
6768  | 
+        except:
 | 
|
6769  | 
+            self.tree = None
 | 
|
6770  | 
+
 | 
|
6771  | 
+        if index == 0:
 | 
|
6772  | 
+            return [part[0]+" " for part in ancillary.iter_all_alias(self.tree)]
 | 
|
6773  | 
+        elif index == 1:
 | 
|
6774  | 
+            return cmdutil.iter_revision_completions(arg, self.tree)
 | 
|
6775  | 
+
 | 
|
6776  | 
+
 | 
|
6777  | 
+    def do_command(self, cmdargs):
 | 
|
6778  | 
+        """
 | 
|
6779  | 
+        Master function that perfoms the "revision" command.
 | 
|
6780  | 
+        """
 | 
|
6781  | 
+
 | 
|
6782  | 
+        parser=self.get_parser()
 | 
|
6783  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
6784  | 
+        try:
 | 
|
6785  | 
+            self.tree =  arch.tree_root()
 | 
|
6786  | 
+        except:
 | 
|
6787  | 
+            self.tree = None
 | 
|
6788  | 
+
 | 
|
6789  | 
+
 | 
|
6790  | 
+        try:
 | 
|
6791  | 
+            options.action(args, options)
 | 
|
6792  | 
+        except cmdutil.ForbiddenAliasSyntax, e:
 | 
|
6793  | 
+            raise CommandFailedWrapper(e)
 | 
|
6794  | 
+
 | 
|
6795  | 
+    def arg_dispatch(self, args, options):
 | 
|
6796  | 
+        """Add, modify, or list aliases, depending on number of arguments
 | 
|
6797  | 
+
 | 
|
6798  | 
+        :param args: The list of commandline arguments
 | 
|
6799  | 
+        :type args: list of str
 | 
|
6800  | 
+        :param options: The commandline options
 | 
|
6801  | 
+        """
 | 
|
6802  | 
+        if len(args) == 0:
 | 
|
6803  | 
+            help_aliases(self.tree)
 | 
|
6804  | 
+            return
 | 
|
6805  | 
+        elif len(args) == 1:
 | 
|
6806  | 
+            self.print_alias(args[0])
 | 
|
6807  | 
+        elif (len(args)) == 2:
 | 
|
6808  | 
+            self.add(args[0], args[1], options)
 | 
|
6809  | 
+        else:
 | 
|
6810  | 
+            raise cmdutil.GetHelp
 | 
|
6811  | 
+
 | 
|
6812  | 
+    def print_alias(self, alias):
 | 
|
6813  | 
+        answer = None
 | 
|
6814  | 
+        for pair in ancillary.iter_all_alias(self.tree):
 | 
|
6815  | 
+            if pair[0] == alias:
 | 
|
6816  | 
+                answer = pair[1]
 | 
|
6817  | 
+        if answer is not None:
 | 
|
6818  | 
+            print answer
 | 
|
6819  | 
+        else:
 | 
|
6820  | 
+            print "The alias %s is not assigned." % alias
 | 
|
6821  | 
+
 | 
|
6822  | 
+    def add(self, alias, expansion, options):
 | 
|
6823  | 
+        """Add or modify aliases
 | 
|
6824  | 
+
 | 
|
6825  | 
+        :param alias: The alias name to create/modify
 | 
|
6826  | 
+        :type alias: str
 | 
|
6827  | 
+        :param expansion: The expansion to assign to the alias name
 | 
|
6828  | 
+        :type expansion: str
 | 
|
6829  | 
+        :param options: The commandline options
 | 
|
6830  | 
+        """
 | 
|
6831  | 
+        newlist = ""
 | 
|
6832  | 
+        written = False
 | 
|
6833  | 
+        new_line = "%s=%s\n" % (alias, cmdutil.expand_alias(expansion, 
 | 
|
6834  | 
+            self.tree))
 | 
|
6835  | 
+        ancillary.check_alias(new_line.rstrip("\n"), [alias, expansion])
 | 
|
6836  | 
+
 | 
|
6837  | 
+        for pair in self.get_iterator(options):
 | 
|
6838  | 
+            if pair[0] != alias:
 | 
|
6839  | 
+                newlist+="%s=%s\n" % (pair[0], pair[1])
 | 
|
6840  | 
+            elif not written:
 | 
|
6841  | 
+                newlist+=new_line
 | 
|
6842  | 
+                written = True
 | 
|
6843  | 
+        if not written:
 | 
|
6844  | 
+            newlist+=new_line
 | 
|
6845  | 
+        self.write_aliases(newlist, options)
 | 
|
6846  | 
+            
 | 
|
6847  | 
+    def delete(self, args, options):
 | 
|
6848  | 
+        """Delete the specified alias
 | 
|
6849  | 
+
 | 
|
6850  | 
+        :param args: The list of arguments
 | 
|
6851  | 
+        :type args: list of str
 | 
|
6852  | 
+        :param options: The commandline options
 | 
|
6853  | 
+        """
 | 
|
6854  | 
+        deleted = False
 | 
|
6855  | 
+        if len(args) != 1:
 | 
|
6856  | 
+            raise cmdutil.GetHelp
 | 
|
6857  | 
+        newlist = ""
 | 
|
6858  | 
+        for pair in self.get_iterator(options):
 | 
|
6859  | 
+            if pair[0] != args[0]:
 | 
|
6860  | 
+                newlist+="%s=%s\n" % (pair[0], pair[1])
 | 
|
6861  | 
+            else:
 | 
|
6862  | 
+                deleted = True
 | 
|
6863  | 
+        if not deleted:
 | 
|
6864  | 
+            raise errors.NoSuchAlias(args[0])
 | 
|
6865  | 
+        self.write_aliases(newlist, options)
 | 
|
6866  | 
+
 | 
|
6867  | 
+    def get_alias_file(self, options):
 | 
|
6868  | 
+        """Return the name of the alias file to use
 | 
|
6869  | 
+
 | 
|
6870  | 
+        :param options: The commandline options
 | 
|
6871  | 
+        """
 | 
|
6872  | 
+        if options.tree:
 | 
|
6873  | 
+            if self.tree is None:
 | 
|
6874  | 
+                self.tree == arch.tree_root()
 | 
|
6875  | 
+            return str(self.tree)+"/{arch}/+aliases"
 | 
|
6876  | 
+        else:
 | 
|
6877  | 
+            return "~/.aba/aliases"
 | 
|
6878  | 
+
 | 
|
6879  | 
+    def get_iterator(self, options):
 | 
|
6880  | 
+        """Return the alias iterator to use
 | 
|
6881  | 
+
 | 
|
6882  | 
+        :param options: The commandline options
 | 
|
6883  | 
+        """
 | 
|
6884  | 
+        return ancillary.iter_alias(self.get_alias_file(options))
 | 
|
6885  | 
+
 | 
|
6886  | 
+    def write_aliases(self, newlist, options):
 | 
|
6887  | 
+        """Safely rewrite the alias file
 | 
|
6888  | 
+        :param newlist: The new list of aliases
 | 
|
6889  | 
+        :type newlist: str
 | 
|
6890  | 
+        :param options: The commandline options
 | 
|
6891  | 
+        """
 | 
|
6892  | 
+        filename = os.path.expanduser(self.get_alias_file(options))
 | 
|
6893  | 
+        file = cmdutil.NewFileVersion(filename)
 | 
|
6894  | 
+        file.write(newlist)
 | 
|
6895  | 
+        file.commit()
 | 
|
6896  | 
+
 | 
|
6897  | 
+
 | 
|
6898  | 
+    def get_parser(self):
 | 
|
6899  | 
+        """
 | 
|
6900  | 
+        Returns the options parser to use for the "alias" command.
 | 
|
6901  | 
+
 | 
|
6902  | 
+        :rtype: cmdutil.CmdOptionParser
 | 
|
6903  | 
+        """
 | 
|
6904  | 
+        parser=cmdutil.CmdOptionParser("fai alias [ALIAS] [NAME]")
 | 
|
6905  | 
+        parser.add_option("-d", "--delete", action="store_const", dest="action",
 | 
|
6906  | 
+                          const=self.delete, default=self.arg_dispatch, 
 | 
|
6907  | 
+                          help="Delete an alias")
 | 
|
6908  | 
+        parser.add_option("--tree", action="store_true", dest="tree", 
 | 
|
6909  | 
+                          help="Create a per-tree alias", default=False)
 | 
|
6910  | 
+        return parser 
 | 
|
6911  | 
+
 | 
|
6912  | 
+    def help(self, parser=None):
 | 
|
6913  | 
+        """
 | 
|
6914  | 
+        Prints a help message.
 | 
|
6915  | 
+
 | 
|
6916  | 
+        :param parser: If supplied, the parser to use for generating help.  If \
 | 
|
6917  | 
+        not supplied, it is retrieved.
 | 
|
6918  | 
+        :type parser: cmdutil.CmdOptionParser
 | 
|
6919  | 
+        """
 | 
|
6920  | 
+        if parser==None:
 | 
|
6921  | 
+            parser=self.get_parser()
 | 
|
6922  | 
+        parser.print_help()
 | 
|
6923  | 
+        print """
 | 
|
6924  | 
+Lists current aliases or modifies the list of aliases.
 | 
|
6925  | 
+
 | 
|
6926  | 
+If no arguments are supplied, aliases will be listed.  If two arguments are
 | 
|
6927  | 
+supplied, the specified alias will be created or modified.  If -d or --delete
 | 
|
6928  | 
+is supplied, the specified alias will be deleted.
 | 
|
6929  | 
+
 | 
|
6930  | 
+You can create aliases that refer to any fully-qualified part of the
 | 
|
6931  | 
+Arch namespace, e.g. 
 | 
|
6932  | 
+archive, 
 | 
|
6933  | 
+archive/category, 
 | 
|
6934  | 
+archive/category--branch, 
 | 
|
6935  | 
+archive/category--branch--version (my favourite)
 | 
|
6936  | 
+archive/category--branch--version--patchlevel
 | 
|
6937  | 
+
 | 
|
6938  | 
+Aliases can be used automatically by native commands.  To use them
 | 
|
6939  | 
+with external or tla commands, prefix them with ^ (you can do this
 | 
|
6940  | 
+with native commands, too).
 | 
|
6941  | 
+"""
 | 
|
6942  | 
+
 | 
|
6943  | 
+
 | 
|
6944  | 
+class RequestMerge(BaseCommand):
 | 
|
6945  | 
+    """Submit a merge request to Bug Goo"""
 | 
|
6946  | 
+    def __init__(self):
 | 
|
6947  | 
+        self.description=self.__doc__
 | 
|
6948  | 
+
 | 
|
6949  | 
+    def do_command(self, cmdargs):
 | 
|
6950  | 
+        """Submit a merge request
 | 
|
6951  | 
+
 | 
|
6952  | 
+        :param cmdargs: The commandline arguments
 | 
|
6953  | 
+        :type cmdargs: list of str
 | 
|
6954  | 
+        """
 | 
|
6955  | 
+        cmdutil.find_editor()
 | 
|
6956  | 
+        parser = self.get_parser()
 | 
|
6957  | 
+        (options, args) = parser.parse_args(cmdargs)
 | 
|
6958  | 
+        try:
 | 
|
6959  | 
+            self.tree=arch.tree_root()
 | 
|
6960  | 
+        except:
 | 
|
6961  | 
+            self.tree=None
 | 
|
6962  | 
+        base, revisions = self.revision_specs(args)
 | 
|
6963  | 
+        message = self.make_headers(base, revisions)
 | 
|
6964  | 
+        message += self.make_summary(revisions)
 | 
|
6965  | 
+        path = self.edit_message(message)
 | 
|
6966  | 
+        message = self.tidy_message(path)
 | 
|
6967  | 
+        if cmdutil.prompt("Send merge"):
 | 
|
6968  | 
+            self.send_message(message)
 | 
|
6969  | 
+            print "Merge request sent"
 | 
|
6970  | 
+
 | 
|
6971  | 
+    def make_headers(self, base, revisions):
 | 
|
6972  | 
+        """Produce email and Bug Goo header strings
 | 
|
6973  | 
+
 | 
|
6974  | 
+        :param base: The base revision to apply merges to
 | 
|
6975  | 
+        :type base: `arch.Revision`
 | 
|
6976  | 
+        :param revisions: The revisions to replay into the base
 | 
|
6977  | 
+        :type revisions: list of `arch.Patchlog`
 | 
|
6978  | 
+        :return: The headers
 | 
|
6979  | 
+        :rtype: str
 | 
|
6980  | 
+        """
 | 
|
6981  | 
+        headers = "To: gnu-arch-users@gnu.org\n"
 | 
|
6982  | 
+        headers += "From: %s\n" % options.fromaddr
 | 
|
6983  | 
+        if len(revisions) == 1:
 | 
|
6984  | 
+            headers += "Subject: [MERGE REQUEST] %s\n" % revisions[0].summary
 | 
|
6985  | 
+        else:
 | 
|
6986  | 
+            headers += "Subject: [MERGE REQUEST]\n"
 | 
|
6987  | 
+        headers += "\n"
 | 
|
6988  | 
+        headers += "Base-Revision: %s\n" % base
 | 
|
6989  | 
+        for revision in revisions:
 | 
|
6990  | 
+            headers += "Revision: %s\n" % revision.revision
 | 
|
6991  | 
+        headers += "Bug: \n\n"
 | 
|
6992  | 
+        return headers
 | 
|
6993  | 
+
 | 
|
6994  | 
+    def make_summary(self, logs):
 | 
|
6995  | 
+        """Generate a summary of merges
 | 
|
6996  | 
+
 | 
|
6997  | 
+        :param logs: the patchlogs that were directly added by the merges
 | 
|
6998  | 
+        :type logs: list of `arch.Patchlog`
 | 
|
6999  | 
+        :return: the summary
 | 
|
7000  | 
+        :rtype: str
 | 
|
7001  | 
+        """ 
 | 
|
7002  | 
+        summary = ""
 | 
|
7003  | 
+        for log in logs:
 | 
|
7004  | 
+            summary+=str(log.revision)+"\n"
 | 
|
7005  | 
+            summary+=log.summary+"\n"
 | 
|
7006  | 
+            if log.description.strip():
 | 
|
7007  | 
+                summary+=log.description.strip('\n')+"\n\n"
 | 
|
7008  | 
+        return summary
 | 
|
7009  | 
+
 | 
|
7010  | 
+    def revision_specs(self, args):
 | 
|
7011  | 
+        """Determine the base and merge revisions from tree and arguments.
 | 
|
7012  | 
+
 | 
|
7013  | 
+        :param args: The parsed arguments
 | 
|
7014  | 
+        :type args: list of str
 | 
|
7015  | 
+        :return: The base revision and merge revisions 
 | 
|
7016  | 
+        :rtype: `arch.Revision`, list of `arch.Patchlog`
 | 
|
7017  | 
+        """
 | 
|
7018  | 
+        if len(args) > 0:
 | 
|
7019  | 
+            target_revision = cmdutil.determine_revision_arch(self.tree, 
 | 
|
7020  | 
+                                                              args[0])
 | 
|
7021  | 
+        else:
 | 
|
7022  | 
+            target_revision = cmdutil.tree_latest(self.tree)
 | 
|
7023  | 
+        if len(args) > 1:
 | 
|
7024  | 
+            merges = [ arch.Patchlog(cmdutil.determine_revision_arch(
 | 
|
7025  | 
+                       self.tree, f)) for f in args[1:] ]
 | 
|
7026  | 
+        else:
 | 
|
7027  | 
+            if self.tree is None:
 | 
|
7028  | 
+                raise CantDetermineRevision("", "Not in a project tree")
 | 
|
7029  | 
+            merge_iter = cmdutil.iter_new_merges(self.tree, 
 | 
|
7030  | 
+                                                 target_revision.version, 
 | 
|
7031  | 
+                                                 False)
 | 
|
7032  | 
+            merges = [f for f in cmdutil.direct_merges(merge_iter)]
 | 
|
7033  | 
+        return (target_revision, merges)
 | 
|
7034  | 
+
 | 
|
7035  | 
+    def edit_message(self, message):
 | 
|
7036  | 
+        """Edit an email message in the user's standard editor
 | 
|
7037  | 
+
 | 
|
7038  | 
+        :param message: The message to edit
 | 
|
7039  | 
+        :type message: str
 | 
|
7040  | 
+        :return: the path of the edited message
 | 
|
7041  | 
+        :rtype: str
 | 
|
7042  | 
+        """
 | 
|
7043  | 
+        if self.tree is None:
 | 
|
7044  | 
+            path = os.get_cwd()
 | 
|
7045  | 
+        else:
 | 
|
7046  | 
+            path = self.tree
 | 
|
7047  | 
+        path += "/,merge-request"
 | 
|
7048  | 
+        file = open(path, 'w')
 | 
|
7049  | 
+        file.write(message)
 | 
|
7050  | 
+        file.flush()
 | 
|
7051  | 
+        cmdutil.invoke_editor(path)
 | 
|
7052  | 
+        return path
 | 
|
7053  | 
+
 | 
|
7054  | 
+    def tidy_message(self, path):
 | 
|
7055  | 
+        """Validate and clean up message.
 | 
|
7056  | 
+
 | 
|
7057  | 
+        :param path: The path to the message to clean up
 | 
|
7058  | 
+        :type path: str
 | 
|
7059  | 
+        :return: The parsed message
 | 
|
7060  | 
+        :rtype: `email.Message`
 | 
|
7061  | 
+        """
 | 
|
7062  | 
+        mail = email.message_from_file(open(path))
 | 
|
7063  | 
+        if mail["Subject"].strip() == "[MERGE REQUEST]":
 | 
|
7064  | 
+            raise BlandSubject
 | 
|
7065  | 
+        
 | 
|
7066  | 
+        request = email.message_from_string(mail.get_payload())
 | 
|
7067  | 
+        if request.has_key("Bug"):
 | 
|
7068  | 
+            if request["Bug"].strip()=="":
 | 
|
7069  | 
+                del request["Bug"]
 | 
|
7070  | 
+        mail.set_payload(request.as_string())
 | 
|
7071  | 
+        return mail
 | 
|
7072  | 
+
 | 
|
7073  | 
+    def send_message(self, message):
 | 
|
7074  | 
+        """Send a message, using its headers to address it.
 | 
|
7075  | 
+
 | 
|
7076  | 
+        :param message: The message to send
 | 
|
7077  | 
+        :type message: `email.Message`"""
 | 
|
7078  | 
+        server = smtplib.SMTP()
 | 
|
7079  | 
+        server.sendmail(message['From'], message['To'], message.as_string())
 | 
|
7080  | 
+        server.quit()
 | 
|
7081  | 
+
 | 
|
7082  | 
+    def help(self, parser=None):
 | 
|
7083  | 
+        """Print a usage message
 | 
|
7084  | 
+
 | 
|
7085  | 
+        :param parser: The options parser to use
 | 
|
7086  | 
+        :type parser: `cmdutil.CmdOptionParser`
 | 
|
7087  | 
+        """
 | 
|
7088  | 
+        if parser is None:
 | 
|
7089  | 
+            parser = self.get_parser()
 | 
|
7090  | 
+        parser.print_help()
 | 
|
7091  | 
+        print """
 | 
|
7092  | 
+Sends a merge request formatted for Bug Goo.  Intended use: get the tree
 | 
|
7093  | 
+you'd like to merge into.  Apply the merges you want.  Invoke request-merge.
 | 
|
7094  | 
+The merge request will open in your $EDITOR.
 | 
|
7095  | 
+
 | 
|
7096  | 
+When no TARGET is specified, it uses the current tree revision.  When
 | 
|
7097  | 
+no MERGE is specified, it uses the direct merges (as in "revisions
 | 
|
7098  | 
+--direct-merges").  But you can specify just the TARGET, or all the MERGE
 | 
|
7099  | 
+revisions.
 | 
|
7100  | 
+"""
 | 
|
7101  | 
+
 | 
|
7102  | 
+    def get_parser(self):
 | 
|
7103  | 
+        """Produce a commandline parser for this command.
 | 
|
7104  | 
+
 | 
|
7105  | 
+        :rtype: `cmdutil.CmdOptionParser`
 | 
|
7106  | 
+        """
 | 
|
7107  | 
+        parser=cmdutil.CmdOptionParser("request-merge [TARGET] [MERGE1...]")
 | 
|
7108  | 
+        return parser
 | 
|
7109  | 
+
 | 
|
7110  | 
+commands = { 
 | 
|
7111  | 
+'changes' : Changes,
 | 
|
7112  | 
+'help' : Help,
 | 
|
7113  | 
+'update': Update,
 | 
|
7114  | 
+'apply-changes':ApplyChanges,
 | 
|
7115  | 
+'cat-log': CatLog,
 | 
|
7116  | 
+'commit': Commit,
 | 
|
7117  | 
+'revision': Revision,
 | 
|
7118  | 
+'revisions': Revisions,
 | 
|
7119  | 
+'get': Get,
 | 
|
7120  | 
+'revert': Revert,
 | 
|
7121  | 
+'shell': Shell,
 | 
|
7122  | 
+'add-id': AddID,
 | 
|
7123  | 
+'merge': Merge,
 | 
|
7124  | 
+'elog': ELog,
 | 
|
7125  | 
+'mirror-archive': MirrorArchive,
 | 
|
7126  | 
+'ninventory': Inventory,
 | 
|
7127  | 
+'alias' : Alias,
 | 
|
7128  | 
+'request-merge': RequestMerge,
 | 
|
7129  | 
+}
 | 
|
7130  | 
+suggestions = {
 | 
|
7131  | 
+'apply-delta' : "Try \"apply-changes\".",
 | 
|
7132  | 
+'delta' : "To compare two revisions, use \"changes\".",
 | 
|
7133  | 
+'diff-rev' : "To compare two revisions, use \"changes\".",
 | 
|
7134  | 
+'undo' : "To undo local changes, use \"revert\".",
 | 
|
7135  | 
+'undelete' : "To undo only deletions, use \"revert --deletions\"",
 | 
|
7136  | 
+'missing-from' : "Try \"revisions --missing-from\".",
 | 
|
7137  | 
+'missing' : "Try \"revisions --missing\".",
 | 
|
7138  | 
+'missing-merge' : "Try \"revisions --partner-missing\".",
 | 
|
7139  | 
+'new-merges' : "Try \"revisions --new-merges\".",
 | 
|
7140  | 
+'cachedrevs' : "Try \"revisions --cacherevs\". (no 'd')",
 | 
|
7141  | 
+'logs' : "Try \"revisions --logs\"",
 | 
|
7142  | 
+'tree-source' : "Use the \"^ttag\" alias (\"revision ^ttag\")",
 | 
|
7143  | 
+'latest-revision' : "Use the \"^acur\" alias (\"revision ^acur\")",
 | 
|
7144  | 
+'change-version' : "Try \"update REVISION\"",
 | 
|
7145  | 
+'tree-revision' : "Use the \"^tcur\" alias (\"revision ^tcur\")",
 | 
|
7146  | 
+'rev-depends' : "Use revisions --dependencies",
 | 
|
7147  | 
+'auto-get' : "Plain get will do archive lookups",
 | 
|
7148  | 
+'tagline' : "Use add-id.  It uses taglines in tagline trees",
 | 
|
7149  | 
+'emlog' : "Use elog.  It automatically adds log-for-merge text, if any",
 | 
|
7150  | 
+'library-revisions' : "Use revisions --library",
 | 
|
7151  | 
+'file-revert' : "Use revert FILE"
 | 
|
7152  | 
+}
 | 
|
7153  | 
+# arch-tag: 19d5739d-3708-486c-93ba-deecc3027fc7
 | 
|
7154  | 
||
7155  | 
*** modified file 'bzrlib/branch.py'
 | 
|
7156  | 
--- bzrlib/branch.py 
 | 
|
7157  | 
+++ bzrlib/branch.py 
 | 
|
7158  | 
@@ -31,6 +31,8 @@
 | 
|
7159  | 
 from revision import Revision
 | 
|
7160  | 
 from errors import bailout, BzrError
 | 
|
7161  | 
 from textui import show_status
 | 
|
7162  | 
+import patches
 | 
|
7163  | 
+from bzrlib import progress
 | 
|
7164  | 
 
 | 
|
7165  | 
 BZR_BRANCH_FORMAT = "Bazaar-NG branch, format 0.0.4\n"
 | 
|
7166  | 
 ## TODO: Maybe include checks for common corruption of newlines, etc?
 | 
|
7167  | 
@@ -802,3 +804,36 @@
 | 
|
7168  | 
 
 | 
|
7169  | 
     s = hexlify(rand_bytes(8))
 | 
|
7170  | 
     return '-'.join((name, compact_date(time.time()), s))
 | 
|
7171  | 
+
 | 
|
7172  | 
+
 | 
|
7173  | 
+def iter_anno_data(branch, file_id):
 | 
|
7174  | 
+    later_revision = branch.revno()
 | 
|
7175  | 
+    q = range(branch.revno())
 | 
|
7176  | 
+    q.reverse()
 | 
|
7177  | 
+    later_text_id = branch.basis_tree().inventory[file_id].text_id
 | 
|
7178  | 
+    i = 0
 | 
|
7179  | 
+    for revno in q:
 | 
|
7180  | 
+        i += 1
 | 
|
7181  | 
+        cur_tree = branch.revision_tree(branch.lookup_revision(revno))
 | 
|
7182  | 
+        if file_id not in cur_tree.inventory:
 | 
|
7183  | 
+            text_id = None
 | 
|
7184  | 
+        else:
 | 
|
7185  | 
+            text_id = cur_tree.inventory[file_id].text_id
 | 
|
7186  | 
+        if text_id != later_text_id:
 | 
|
7187  | 
+            patch = get_patch(branch, revno, later_revision, file_id)
 | 
|
7188  | 
+            yield revno, patch.iter_inserted(), patch
 | 
|
7189  | 
+            later_revision = revno
 | 
|
7190  | 
+            later_text_id = text_id
 | 
|
7191  | 
+        yield progress.Progress("revisions", i)
 | 
|
7192  | 
+
 | 
|
7193  | 
+def get_patch(branch, old_revno, new_revno, file_id):
 | 
|
7194  | 
+    old_tree = branch.revision_tree(branch.lookup_revision(old_revno))
 | 
|
7195  | 
+    new_tree = branch.revision_tree(branch.lookup_revision(new_revno))
 | 
|
7196  | 
+    if file_id in old_tree.inventory:
 | 
|
7197  | 
+        old_file = old_tree.get_file(file_id).readlines()
 | 
|
7198  | 
+    else:
 | 
|
7199  | 
+        old_file = []
 | 
|
7200  | 
+    ud = difflib.unified_diff(old_file, new_tree.get_file(file_id).readlines())
 | 
|
7201  | 
+    return patches.parse_patch(ud)
 | 
|
7202  | 
+
 | 
|
7203  | 
+
 | 
|
7204  | 
||
7205  | 
*** modified file 'bzrlib/commands.py'
 | 
|
7206  | 
--- bzrlib/commands.py 
 | 
|
7207  | 
+++ bzrlib/commands.py 
 | 
|
7208  | 
@@ -27,6 +27,9 @@
 | 
|
7209  | 
 from bzrlib import Branch, Inventory, InventoryEntry, ScratchBranch, BZRDIR, \
 | 
|
7210  | 
      format_date
 | 
|
7211  | 
 from bzrlib import merge
 | 
|
7212  | 
+from bzrlib.branch import iter_anno_data
 | 
|
7213  | 
+from bzrlib import patches
 | 
|
7214  | 
+from bzrlib import progress
 | 
|
7215  | 
 
 | 
|
7216  | 
 
 | 
|
7217  | 
 def _squish_command_name(cmd):
 | 
|
7218  | 
@@ -882,7 +885,15 @@
 | 
|
7219  | 
                 print '%3d FAILED!' % mf
 | 
|
7220  | 
             else:
 | 
|
7221  | 
                 print
 | 
|
7222  | 
-
 | 
|
7223  | 
+        result = bzrlib.patches.test()
 | 
|
7224  | 
+        resultFailed = len(result.errors) + len(result.failures)
 | 
|
7225  | 
+        print '%-40s %3d tests' % ('bzrlib.patches', result.testsRun),
 | 
|
7226  | 
+        if resultFailed:
 | 
|
7227  | 
+            print '%3d FAILED!' % resultFailed
 | 
|
7228  | 
+        else:
 | 
|
7229  | 
+            print
 | 
|
7230  | 
+        tests += result.testsRun
 | 
|
7231  | 
+        failures += resultFailed
 | 
|
7232  | 
         print '%-40s %3d tests' % ('total', tests),
 | 
|
7233  | 
         if failures:
 | 
|
7234  | 
             print '%3d FAILED!' % failures
 | 
|
7235  | 
@@ -897,6 +908,27 @@
 | 
|
7236  | 
     """Show version of bzr"""
 | 
|
7237  | 
     def run(self):
 | 
|
7238  | 
         show_version()
 | 
|
7239  | 
+
 | 
|
7240  | 
+class cmd_annotate(Command):
 | 
|
7241  | 
+    """Show which revision added each line in a file"""
 | 
|
7242  | 
+
 | 
|
7243  | 
+    takes_args = ['filename']
 | 
|
7244  | 
+    def run(self, filename):
 | 
|
7245  | 
+        branch = (Branch(filename))
 | 
|
7246  | 
+        file_id = branch.working_tree().path2id(filename)
 | 
|
7247  | 
+        lines = branch.basis_tree().get_file(file_id)
 | 
|
7248  | 
+        total = branch.revno()
 | 
|
7249  | 
+        anno_d_iter = iter_anno_data(branch, file_id)
 | 
|
7250  | 
+        for result in patches.iter_annotate_file(lines, anno_d_iter):
 | 
|
7251  | 
+            if isinstance(result, progress.Progress):
 | 
|
7252  | 
+                result.total = total
 | 
|
7253  | 
+                progress.progress_bar(result)
 | 
|
7254  | 
+            else:
 | 
|
7255  | 
+                progress.clear_progress_bar()
 | 
|
7256  | 
+                anno_lines = result
 | 
|
7257  | 
+        for line in anno_lines:
 | 
|
7258  | 
+            sys.stdout.write("%4s:%s" % (str(line.log), line.text))
 | 
|
7259  | 
+
 | 
|
7260  | 
 
 | 
|
7261  | 
 def show_version():
 | 
|
7262  | 
     print "bzr (bazaar-ng) %s" % bzrlib.__version__
 | 
|
7263  |