~abentley/bzrtools/bzrtools.dev

0.1.1 by Michael Ellerman
Initial import
1
#!/usr/bin/python
2
0.1.6 by Michael Ellerman
Move selection code into HunkSelector class
3
from patches import parse_patches
0.1.1 by Michael Ellerman
Initial import
4
import os
5
import sys
6
import string
0.1.23 by Michael Ellerman
Incorporate Aaron's changes from bzrtools.
7
import glob
8
import bzrlib
9
from bzrlib.commands import Command
10
from bzrlib.branch import Branch
11
from bzrlib import DEFAULT_IGNORE
12
from hunk_selector import HunkSelector
0.1.35 by Michael Ellerman
Use DiffStat rather than calling out to /bin/diffstat
13
from diffstat import DiffStat
281 by Aaron Bentley
Handled whitespace branch names better
14
from subprocess import Popen, PIPE
0.1.23 by Michael Ellerman
Incorporate Aaron's changes from bzrtools.
15
16
DEFAULT_IGNORE.append('./.bzr-shelf*')
0.1.1 by Michael Ellerman
Initial import
17
0.1.27 by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the
18
class QuitException(Exception):
19
    pass
20
21
class Shelf(object):
0.1.38 by Michael Ellerman
Shelf() takes a location which specifies where to open the branch.
22
    def __init__(self, location):
23
        self.branch = Branch.open_containing(location)[0]
0.1.27 by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the
24
25
    def shelf_suffix(self, index):
26
        if index == 0:
27
            return ""
28
        else:
29
            return "-%d" % index
30
31
    def next_shelf(self):
32
        def name_sequence():
33
            i = 0
34
            while True:
35
                yield self.shelf_suffix(i)
36
                i = i + 1
37
0.1.31 by Michael Ellerman
- Keep our branch around, and use it directly instead of bzr_root.
38
        stem = os.path.join(self.branch.base, '.bzr-shelf')
0.1.27 by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the
39
        for end in name_sequence():
40
            name = stem + end
41
            if not os.path.exists(name):
42
                return name
43
44
    def last_shelf(self):
0.1.31 by Michael Ellerman
- Keep our branch around, and use it directly instead of bzr_root.
45
        stem = os.path.join(self.branch.base, '.bzr-shelf')
0.1.27 by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the
46
        shelves = glob.glob(stem)
47
        shelves.extend(glob.glob(stem + '-*'))
48
        def shelf_index(name):
49
            if name == stem:
50
                return 0
51
            return int(name[len(stem)+1:])
52
        shelvenums = [shelf_index(f) for f in shelves]
53
        shelvenums.sort()
54
55
        if len(shelvenums) == 0:
56
            return None
57
        return stem + self.shelf_suffix(shelvenums[-1])
58
59
    def get_shelf_message(self, shelf):
60
        prefix = "# shelf: "
61
        if not shelf.startswith(prefix):
62
            return None
63
        return shelf[len(prefix):shelf.index('\n')]
64
65
    def unshelve(self):
66
        shelf = self.last_shelf()
67
68
        if shelf is None:
0.1.31 by Michael Ellerman
- Keep our branch around, and use it directly instead of bzr_root.
69
            raise Exception("No shelf found in '%s'" % self.branch.base)
0.1.27 by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the
70
71
        patch = open(shelf, 'r').read()
72
73
        print >>sys.stderr, "Reapplying shelved patches",
74
        message = self.get_shelf_message(patch)
75
        if message is not None:
76
            print >>sys.stderr, ' "%s"' % message
77
        else:
78
            print >>sys.stderr, ""
281 by Aaron Bentley
Handled whitespace branch names better
79
        run_patch(self.branch.base, (patch,))
0.1.27 by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the
80
        os.remove(shelf)
0.1.35 by Michael Ellerman
Use DiffStat rather than calling out to /bin/diffstat
81
82
        diff_stat = DiffStat(self.get_patches(None, None))
83
        print 'Diff status is now:\n', diff_stat
0.1.27 by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the
84
281 by Aaron Bentley
Handled whitespace branch names better
85
        return 1
0.1.27 by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the
86
0.1.31 by Michael Ellerman
- Keep our branch around, and use it directly instead of bzr_root.
87
    def get_patches(self, revision, file_list):
88
        from StringIO import StringIO
89
        from bzrlib.diff import show_diff
90
        out = StringIO()
91
        show_diff(self.branch, revision, specific_files=file_list, output=out)
92
        out.seek(0)
93
        return out.readlines()
94
0.1.28 by Michael Ellerman
Implement "bzr shelve --all".
95
    def shelve(self, all_hunks=False, message=None, revision=None,
96
             file_list=None):
0.1.31 by Michael Ellerman
- Keep our branch around, and use it directly instead of bzr_root.
97
        patches = parse_patches(self.get_patches(revision, file_list))
98
0.1.28 by Michael Ellerman
Implement "bzr shelve --all".
99
        if not all_hunks:
100
            try:
101
                patches = HunkSelector(patches).select()
102
            except QuitException:
103
                return False
0.1.27 by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the
104
105
        if len(patches) == 0:
106
            print >>sys.stderr, 'Nothing to shelve'
281 by Aaron Bentley
Handled whitespace branch names better
107
            return 0
0.1.27 by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the
108
109
        shelf = self.next_shelf()
110
        print >>sys.stderr, "Saving shelved patches to", shelf
111
        shelf = open(shelf, 'a')
112
        if message is not None:
113
            assert '\n' not in message
114
            shelf.write("# shelf: %s\n" % message)
115
        for patch in patches:
116
            shelf.write(str(patch))
117
118
        shelf.flush()
119
        os.fsync(shelf.fileno())
120
        shelf.close()
121
122
        print >>sys.stderr, "Reverting shelved patches"
281 by Aaron Bentley
Handled whitespace branch names better
123
        run_patch(self.branch.base, patches, reverse=True)
0.1.27 by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the
124
0.1.35 by Michael Ellerman
Use DiffStat rather than calling out to /bin/diffstat
125
        diff_stat = DiffStat(self.get_patches(None, None))
126
        print 'Diff status is now:\n', diff_stat
0.1.27 by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the
127
281 by Aaron Bentley
Handled whitespace branch names better
128
        return 1
0.1.27 by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the
129
281 by Aaron Bentley
Handled whitespace branch names better
130
def run_patch(branch_base, patches, reverse=False):
320 by Aaron Bentley
Updated to match new bzr diff behaviour
131
    args = ['patch', '-d', branch_base, '-s', '-p1', '-f']
281 by Aaron Bentley
Handled whitespace branch names better
132
    if reverse:
133
        args.append('-R')
134
    process = Popen(args, stdin=PIPE)
135
    for patch in patches:
136
        process.stdin.write(str(patch))
137
    process.stdin.close()
138
    result = process.wait()
139
    if result not in (0, 1):
140
        raise Exception("Error applying patches")
141
    return result