~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
#!/usr/bin/python

import os
import sys
from errors import CommandError
from hunk_selector import ShelveHunkSelector, UnshelveHunkSelector
from diffstat import DiffStat
from patchsource import PatchSource, FilePatchSource

BASE_DIR = '.shelf'

class Shelf(object):
    def __init__(self, base, name='default'):
        self.base = base
        shelf_base = os.path.join(self.base, BASE_DIR)
        self.shelf_dir = os.path.join(shelf_base, name)

        for dir in [shelf_base, self.shelf_dir]:
            if not os.path.isdir(dir):
                os.mkdir(dir)

    def __path(self, idx):
        return os.path.join(self.shelf_dir, '%.2d' % idx)

    def next_shelf(self):
        index = 0
        while True:
            name = self.__path(index)
            if not os.path.exists(name):
                return name
            index += 1

    def last_shelf(self):
        shelves = os.listdir(self.shelf_dir)
        indexes = [int(f) for f in shelves]
        indexes.sort()

        if len(indexes) == 0:
            return None

        return self.__path(indexes[-1])

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

    def __show_status(self, source):
        if source.can_live_update():
            diff_stat = DiffStat(source.readlines())
            print 'Diff status is now:\n', diff_stat

    def unshelve(self, patch_source, pick_hunks=False):
        shelf = self.last_shelf()

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

        patches = FilePatchSource(shelf).readpatches()
        if pick_hunks:
            to_unshelve, to_remain = UnshelveHunkSelector(patches).select()
        else:
            to_unshelve = patches
            to_remain = []

        if len(to_unshelve) == 0:
            raise CommandError('Nothing to unshelve')

        print >>sys.stderr, "Reapplying shelved patches",
        message = self.get_shelf_message(open(shelf, 'r').read())
        if message is not None:
            print >>sys.stderr, ' "%s"' % message
        else:
            print >>sys.stderr, ""

        pipe = os.popen('patch -d %s -s -p0' % self.base, 'w')
        for patch in to_unshelve:
            pipe.write(str(patch))
        pipe.flush()

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

        if len(to_remain) == 0:
            os.remove(shelf)
        else:
            f = open(shelf, 'w')
            for patch in to_remain:
                f.write(str(patch))
            f.close()

        self.__show_status(patch_source)

    def shelve(self, patch_source, pick_hunks=False, message=None):
        patches = patch_source.readpatches()

        if pick_hunks:
            to_shelve = ShelveHunkSelector(patches).select()[0]
        else:
            to_shelve = patches

        if len(to_shelve) == 0:
            raise CommandError('Nothing to shelve')

        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 to_shelve:
            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.base, 'w')
        for patch in to_shelve:
            pipe.write(str(patch))
        pipe.flush()

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

        self.__show_status(patch_source)