~abentley/bzrtools/bzrtools.dev

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#!/usr/bin/python

from patches import parse_patches
import os
import sys
import string
import glob
import bzrlib
from bzrlib.commands import Command
from bzrlib.branch import Branch
from bzrlib import DEFAULT_IGNORE
from hunk_selector import HunkSelector

DEFAULT_IGNORE.append('./.bzr-shelf*')

def run_bzr(args):
    if type(args) is str:
        args = [ args ]
    pipe = os.popen('bzr %s' % string.join(args, ' '), 'r')
    lines = pipe.readlines()
    if pipe.close() is not None:
        raise Exception("Failed running bzr")
    return lines

class QuitException(Exception):
    pass

class Shelf(object):
    def __init__(self):
        b = Branch.open_containing('.')[0]
        self.bzr_root = b.base

    def shelf_suffix(self, index):
        if index == 0:
            return ""
        else:
            return "-%d" % index

    def next_shelf(self):
        def name_sequence():
            i = 0
            while True:
                yield self.shelf_suffix(i)
                i = i + 1

        stem = os.path.join(self.bzr_root, '.bzr-shelf')
        for end in name_sequence():
            name = stem + end
            if not os.path.exists(name):
                return name

    def last_shelf(self):
        stem = os.path.join(self.bzr_root, '.bzr-shelf')
        shelves = glob.glob(stem)
        shelves.extend(glob.glob(stem + '-*'))
        def shelf_index(name):
            if name == stem:
                return 0
            return int(name[len(stem)+1:])
        shelvenums = [shelf_index(f) for f in shelves]
        shelvenums.sort()

        if len(shelvenums) == 0:
            return None
        return stem + self.shelf_suffix(shelvenums[-1])

    def get_shelf_message(self, shelf):
        prefix = "# shelf: "
        if not shelf.startswith(prefix):
            return None
        return shelf[len(prefix):shelf.index('\n')]

    def unshelve(self):
        shelf = self.last_shelf()

        if shelf is None:
            raise Exception("No shelf found in '%s'" % self.bzr_root)

        patch = open(shelf, 'r').read()

        print >>sys.stderr, "Reapplying shelved patches",
        message = self.get_shelf_message(patch)
        if message is not None:
            print >>sys.stderr, ' "%s"' % message
        else:
            print >>sys.stderr, ""
        pipe = os.popen('patch -d %s -s -p0' % self.bzr_root, 'w')
        pipe.write(patch)
        pipe.flush()

        if pipe.close() is not None:
            raise Exception("Failed running patch!")

        os.remove(shelf)
        print 'Diff status is now:'
        os.system('bzr diff | diffstat')

        return True

    def shelve(self, all_hunks=False, message=None, revision=None,
             file_list=None):
        cmd = ['diff']
        if revision is not None:
            cmd.extend(['--revision', str(revision[0])])
        if file_list is not None:
            cmd.extend(file_list)
        patches = parse_patches(run_bzr(cmd))
        if not all_hunks:
            try:
                patches = HunkSelector(patches).select()
            except QuitException:
                return False

        if len(patches) == 0:
            print >>sys.stderr, 'Nothing to shelve'
            return True

        shelf = self.next_shelf()
        print >>sys.stderr, "Saving shelved patches to", shelf
        shelf = open(shelf, 'a')
        if message is not None:
            assert '\n' not in message
            shelf.write("# shelf: %s\n" % message)
        for patch in patches:
            shelf.write(str(patch))

        shelf.flush()
        os.fsync(shelf.fileno())
        shelf.close()

        print >>sys.stderr, "Reverting shelved patches"
        pipe = os.popen('patch -d %s -sR -p0' % self.bzr_root, 'w')
        for patch in patches:
            pipe.write(str(patch))
        pipe.flush()

        if pipe.close() is not None:
            raise Exception("Failed running patch!")

        print 'Diff status is now:'
        os.system('bzr diff | diffstat')

        return True