~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
#!/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.name = name
        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 = str(DiffStat(source.readlines()))
            if len(diff_stat) > 0:
                print >> sys.stderr, 'Diff status is now:\n', diff_stat
            else:
                print >> sys.stderr, 'No changes left in working tree.'

    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 to shelf '%s', patch '%s'" % \
                (self.name, os.path.basename(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)