~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to shelf.py

  • Committer: Aaron Bentley
  • Date: 2005-08-17 15:47:08 UTC
  • Revision ID: abentley@panoramicfeedback.com-20050817154708-dafc78180bcdc943
Updated shelve/unshelve to upstream latest

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/python
2
2
 
3
 
# Copyright (C) 2005 Michael Ellerman <michael@ellerman.id.au>
4
 
#
5
 
#    This program is free software; you can redistribute it and/or modify
6
 
#    it under the terms of the GNU General Public License as published by
7
 
#    the Free Software Foundation; either version 2 of the License, or
8
 
#    (at your option) any later version.
9
 
#
10
 
#    This program is distributed in the hope that it will be useful,
11
 
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 
#    GNU General Public License for more details.
14
 
#
15
 
#    You should have received a copy of the GNU General Public License
16
 
#    along with this program; if not, write to the Free Software
17
 
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
 
 
19
 
import patches
 
3
from patches import parse_patches
20
4
import os
21
5
import sys
22
6
import string
 
7
import tty, termios
23
8
from bzrlib.commands import Command
24
9
 
25
 
def parse_args(args):
26
 
    if len(args) == 2 and args[1] == '--bzr-usage':
27
 
        print '\n'
28
 
        return True
29
 
    elif len(args) == 2 and args[1] == '--bzr-help':
30
 
        print 'Shelve a patch, you can get it back later with unshelve.'
31
 
        return True
32
 
    elif len(args) == 1:
33
 
        pass
34
 
    else:
35
 
        raise Exception("Don't understand args %s" % args)
36
 
 
37
 
    return False
 
10
def main(args):
 
11
    name = os.path.basename(args.pop(0))
 
12
 
 
13
    if name not in ['shelve', 'unshelve']:
 
14
        raise Exception("Unknown command name '%s'" % name)
 
15
 
 
16
    if len(args) > 0:
 
17
        if args[0] == '--bzr-usage':
 
18
            print '\n'
 
19
            return 0
 
20
        elif args[0] == '--bzr-help':
 
21
            print 'Shelve a patch, you can get it back later with unshelve.'
 
22
            return 0
 
23
        else:
 
24
            raise Exception("Don't understand args %s" % args)
 
25
 
 
26
    if eval(name + "()"):
 
27
        return 0
 
28
 
 
29
    return 1
38
30
 
39
31
def unshelve():
40
32
    root = run_bzr('root')[0].strip()
59
51
 
60
52
    return True
61
53
 
 
54
class QuitException(Exception):
 
55
    pass
 
56
 
62
57
def shelve():
63
 
    diff_lines = run_bzr('diff')
64
 
    patch_list = patches.parse_patches(diff_lines.__iter__())
65
 
    to_shelve = prompt_user(patch_list)
 
58
    patches = parse_patches(run_bzr('diff'))
 
59
    try:
 
60
        patches = HunkSelector(patches).select()
 
61
    except QuitException:
 
62
        return False
66
63
 
67
 
    if len(to_shelve) == 0:
 
64
    if len(patches) == 0:
68
65
        print >>sys.stderr, 'Nothing to shelve'
69
66
        return True
70
67
 
73
70
    print >>sys.stderr, "Saving shelved patches to", shelf
74
71
    shelf = open(shelf, 'a')
75
72
 
76
 
    for patch in to_shelve:
 
73
    for patch in patches:
77
74
        shelf.write(str(patch))
78
75
 
79
76
    shelf.flush()
82
79
 
83
80
    print >>sys.stderr, "Reverting shelved patches"
84
81
    pipe = os.popen('patch -d %s -sR -p0' % root, 'w')
85
 
    for patch in to_shelve:
 
82
    for patch in patches:
86
83
       pipe.write(str(patch))
87
84
    pipe.flush()
88
85
 
103
100
        raise Exception("Failed running bzr")
104
101
    return lines
105
102
 
106
 
def prompt_user(patch_list):
107
 
    total = 0
108
 
    for patch in patch_list:
109
 
        total += len(patch.hunks)
110
 
 
111
 
    out = sys.stdout
112
 
    inp = sys.stdin
113
 
 
114
 
    to_shelve = []
115
 
    i = 1
116
 
    for patch in patch_list:
117
 
        if patch.oldname != patch.newname:
118
 
            name = "%s -> %s" % (patch.oldname, patch.newname)
119
 
        else:
120
 
            name = patch.oldname
121
 
 
122
 
        hunks = patch.hunks[:]
123
 
        for hunk in hunks:
124
 
            print name
125
 
            print hunk
126
 
            while True:
127
 
                out.write('Shelve this change? (%d of %d) [yn] ' % (i, total))
128
 
                line = inp.readline().strip().lower()
129
 
                if line == 'y' or line == 'yes':
130
 
                    break
131
 
                elif line == 'n' or line == 'no':
132
 
                    patch.hunks.remove(hunk)
133
 
                    break
134
 
 
135
 
            i += 1
136
 
 
137
 
        if len(patch.hunks) > 0:
138
 
            to_shelve.append(patch)
139
 
 
140
 
    return to_shelve
141
 
 
142
103
class cmd_shelve(Command):
143
104
    """Temporarily remove some changes from the current tree.
144
105
    Use 'unshelve' to restore these changes.
153
114
    """
154
115
    def run(self):
155
116
        return unshelve()
 
117
 
 
118
class HunkSelector:
 
119
    class Option:
 
120
        def __init__(self, char, action, help, default=False):
 
121
            self.char = char
 
122
            self.action = action
 
123
            self.default = default
 
124
            self.help = help
 
125
 
 
126
    standard_options = [
 
127
        Option('n', 'shelve', 'shelve this change for the moment.',
 
128
            default=True),
 
129
        Option('y', 'keep', 'keep this change in your tree.'),
 
130
        Option('d', 'done', 'done, skip to the end.'),
 
131
        Option('i', 'invert', 'invert the current selection of all hunks.'),
 
132
        Option('s', 'status', 'show status of hunks.'),
 
133
        Option('q', 'quit', 'quit')
 
134
    ]
 
135
 
 
136
    end_options = [
 
137
        Option('y', 'continue', 'proceed to shelve selected changes.',
 
138
            default=True),
 
139
        Option('r', 'restart', 'restart the hunk selection loop.'),
 
140
        Option('s', 'status', 'show status of hunks.'),
 
141
        Option('i', 'invert', 'invert the current selection of all hunks.'),
 
142
        Option('q', 'quit', 'quit')
 
143
    ]
 
144
 
 
145
    def __init__(self, patches):
 
146
        self.patches = patches
 
147
        self.total_hunks = 0
 
148
        for patch in patches:
 
149
            for hunk in patch.hunks:
 
150
                # everything's shelved by default
 
151
                hunk.selected = True
 
152
                self.total_hunks += 1
 
153
 
 
154
    def __get_option(self, char):
 
155
        for opt in self.standard_options:
 
156
            if opt.char == char:
 
157
                return opt
 
158
        raise Exception('Option "%s" not found!' % char)
 
159
 
 
160
    def __select_loop(self):
 
161
        j = 0
 
162
        for patch in self.patches:
 
163
            i = 0
 
164
            lasti = -1
 
165
            while i < len(patch.hunks):
 
166
                hunk = patch.hunks[i]
 
167
                if lasti != i:
 
168
                    print patch.get_header(), hunk
 
169
                    j += 1
 
170
                lasti = i
 
171
 
 
172
                prompt = 'Keep this change? (%d of %d)' \
 
173
                            % (j, self.total_hunks)
 
174
 
 
175
                if hunk.selected:
 
176
                    self.__get_option('n').default = True
 
177
                    self.__get_option('y').default = False
 
178
                else:
 
179
                    self.__get_option('n').default = False
 
180
                    self.__get_option('y').default = True
 
181
 
 
182
                action = self.__ask_user(prompt, self.standard_options)
 
183
 
 
184
                if action == 'keep':
 
185
                    hunk.selected = False
 
186
                elif action == 'shelve':
 
187
                    hunk.selected = True
 
188
                elif action == 'done':
 
189
                    return True
 
190
                elif action == 'invert':
 
191
                    self.__invert_selection()
 
192
                    self.__show_status()
 
193
                    continue
 
194
                elif action == 'status':
 
195
                    self.__show_status()
 
196
                    continue
 
197
                elif action == 'quit':
 
198
                    return False
 
199
 
 
200
                i += 1
 
201
        return True
 
202
 
 
203
    def select(self):
 
204
        if self.total_hunks == 0:
 
205
            return []
 
206
 
 
207
        done = False
 
208
        while not done:
 
209
            if not self.__select_loop():
 
210
                return []
 
211
 
 
212
            while True:
 
213
                self.__show_status()
 
214
                prompt = "Shelve these changes, or restart?"
 
215
                action = self.__ask_user(prompt, self.end_options)
 
216
 
 
217
                if action == 'continue':
 
218
                    done = True
 
219
                    break
 
220
                elif action == 'quit':
 
221
                    return []
 
222
                elif action == 'status':
 
223
                    self.__show_status()
 
224
                elif action == 'invert':
 
225
                    self.__invert_selection()
 
226
                elif action == 'restart':
 
227
                    break
 
228
 
 
229
 
 
230
        for patch in self.patches:
 
231
            tmp = []
 
232
            for hunk in patch.hunks:
 
233
                if hunk.selected:
 
234
                    tmp.append(hunk)
 
235
            patch.hunks = tmp
 
236
 
 
237
        tmp = []
 
238
        for patch in self.patches:
 
239
            if len(patch.hunks):
 
240
                tmp.append(patch)
 
241
        self.patches = tmp
 
242
 
 
243
        return self.patches
 
244
 
 
245
    def __invert_selection(self):
 
246
        for patch in self.patches:
 
247
            for hunk in patch.hunks:
 
248
                if hunk.__dict__.has_key('selected'):
 
249
                    hunk.selected = not hunk.selected
 
250
                else:
 
251
                    hunk.selected = True
 
252
 
 
253
    def __show_status(self):
 
254
        print '\nStatus:'
 
255
        for patch in self.patches:
 
256
            print '  %s' % patch.oldname
 
257
            shelve = 0
 
258
            keep = 0
 
259
            for hunk in patch.hunks:
 
260
                if hunk.selected:
 
261
                    shelve += 1
 
262
                else:
 
263
                    keep += 1
 
264
 
 
265
            print '    %d hunks to be shelved' % shelve
 
266
            print '    %d hunks to be kept' % keep
 
267
            print
 
268
 
 
269
    def __getchar(self):
 
270
        fd = sys.stdin.fileno()
 
271
        settings = termios.tcgetattr(fd)
 
272
        try:
 
273
            tty.setraw(fd)
 
274
            ch = sys.stdin.read(1)
 
275
        finally:
 
276
            termios.tcsetattr(fd, termios.TCSADRAIN, settings)
 
277
        return ch
 
278
 
 
279
    def __ask_user(self, prompt, options):
 
280
        while True:
 
281
            sys.stdout.write(prompt)
 
282
            sys.stdout.write(' [')
 
283
            for opt in options:
 
284
                if opt.default:
 
285
                    default = opt
 
286
                sys.stdout.write(opt.char)
 
287
            sys.stdout.write('?] (%s): ' % default.char)
 
288
 
 
289
            response = self.__getchar()
 
290
 
 
291
            # default, which we see as newline, is 'n'
 
292
            if response in ['\n', '\r', '\r\n']:
 
293
                response = default.char
 
294
 
 
295
            print response # because echo is off
 
296
 
 
297
            for opt in options:
 
298
                if opt.char == response:
 
299
                    return opt.action
 
300
 
 
301
            for opt in options:
 
302
                print '  %s - %s' % (opt.char, opt.help)