3
from patches import parse_patches
6
from bzrlib.commands import Command
7
from bzrlib.branch import Branch
8
from bzrlib.errors import BzrCommandError
5
from errors import CommandError
9
6
from hunk_selector import ShelveHunkSelector, UnshelveHunkSelector
10
7
from diffstat import DiffStat
8
from patchsource import PatchSource, FilePatchSource
12
10
class Shelf(object):
13
def __init__(self, location, name='default'):
14
self.branch = Branch.open_containing(location)[0]
15
base = self.branch.controlfilename('x-shelf')
16
self.shelf_dir = os.path.join(base, name)
18
# FIXME surely there's an easier way to do this?
19
t = self.branch._transport
20
for dir in [base, self.shelf_dir]:
24
def __path(self, idx):
25
return os.path.join(self.shelf_dir, '%.2d' % idx)
30
name = self.__path(index)
31
if not os.path.exists(name):
36
shelves = os.listdir(self.shelf_dir)
37
indexes = [int(f) for f in shelves]
11
MESSAGE_PREFIX = "# Shelved patch: "
15
'shelves' : '.shelf/shelves',
16
'current-shelf' : '.shelf/current-shelf',
19
def __init__(self, base, name=None):
24
current = os.path.join(self.base, self._paths['current-shelf'])
25
name = open(current).read().strip()
27
assert '\n' not in name
30
self.dir = os.path.join(self.base, self._paths['shelves'], name)
31
if not os.path.isdir(self.dir):
35
# Create required directories etc.
36
for dir in [self._paths['base'], self._paths['shelves']]:
37
dir = os.path.join(self.base, dir)
38
if not os.path.isdir(dir):
41
current = os.path.join(self.base, self._paths['current-shelf'])
42
if not os.path.exists(current):
43
f = open(current, 'w')
47
def make_default(self):
48
f = open(os.path.join(self.base, self._paths['current-shelf']), 'w')
51
self.log("Default shelf is now '%s'\n" % self.name)
56
def delete(self, patch):
57
path = self.__path_from_user(patch)
60
def display(self, patch):
61
path = self.__path_from_user(patch)
62
sys.stdout.write(open(path).read())
65
self.log("Patches on shelf '%s':" % self.name)
66
indexes = self.__list()
72
msg = self.get_patch_message(self.__path(index))
74
msg = "No message saved with patch."
75
self.log(' %.2d: %s\n' % (index, msg))
77
def __path_from_user(self, patch_id):
79
patch_index = int(patch_id)
81
raise CommandError("Invalid patch name '%s'" % patch_id)
83
path = self.__path(patch_index)
85
if not os.path.exists(path):
86
raise CommandError("Patch '%s' doesn't exist on shelf %s!" % \
87
(patch_id, self.name))
91
def __path(self, index):
92
return os.path.join(self.dir, '%.2d' % index)
95
indexes = self.__list()
100
next = indexes[-1] + 1
101
return self.__path(next)
104
patches = os.listdir(self.dir)
105
indexes = [int(f) for f in patches]
109
def last_patch(self):
110
indexes = self.__list()
40
112
if len(indexes) == 0:
43
115
return self.__path(indexes[-1])
45
def get_shelf_message(self, shelf):
47
if not shelf.startswith(prefix):
117
def get_patch_message(self, patch_path):
118
patch = open(patch_path, 'r').read()
120
if not patch.startswith(self.MESSAGE_PREFIX):
49
return shelf[len(prefix):shelf.index('\n')]
51
def unshelve(self, pick_hunks=False):
52
shelf = self.last_shelf()
55
raise BzrCommandError("No shelf found in branch '%s'" % \
58
patches = parse_patches(open(shelf, 'r').readlines())
122
return patch[len(self.MESSAGE_PREFIX):patch.index('\n')]
124
def __show_status(self, source):
125
if source.can_live_update():
126
diff_stat = str(DiffStat(source.readlines()))
127
if len(diff_stat) > 0:
128
self.log('Diff status is now:\n' + diff_stat + '\n')
130
self.log('No changes left in working tree.\n')
132
def unshelve(self, patch_source, pick_hunks=False):
133
patch_name = self.last_patch()
135
if patch_name is None:
136
raise CommandError("No patch found on shelf %s" % self.name)
138
hunks = FilePatchSource(patch_name).readhunks()
60
to_unshelve, to_remain = UnshelveHunkSelector(patches).select()
140
to_unshelve, to_remain = UnshelveHunkSelector(hunks).select()
65
145
if len(to_unshelve) == 0:
66
raise BzrCommandError('Nothing to unshelve')
68
print >>sys.stderr, "Reapplying shelved patches",
69
message = self.get_shelf_message(open(shelf, 'r').read())
70
if message is not None:
71
print >>sys.stderr, ' "%s"' % message
73
print >>sys.stderr, ""
75
pipe = os.popen('patch -d %s -s -p0' % self.branch.base, 'w')
76
for patch in to_unshelve:
77
pipe.write(str(patch))
146
raise CommandError('Nothing to unshelve')
148
message = self.get_patch_message(patch_name)
151
self.log('Reapplying shelved patches "%s"\n' % message)
153
pipe = os.popen('patch -d %s -s -p0' % self.base, 'w')
154
for hunk in to_unshelve:
155
pipe.write(str(hunk))
80
158
if pipe.close() is not None:
81
raise BzrCommandError("Failed running patch!")
159
raise CommandError("Failed running patch!")
83
161
if len(to_remain) == 0:
162
os.remove(patch_name)
87
for patch in to_remain:
164
f = open(patch_name, 'w')
165
for hunk in to_remain:
91
diff_stat = DiffStat(self.get_patches(None, None))
92
print 'Diff status is now:\n', diff_stat
94
def get_patches(self, revision, file_list):
95
from StringIO import StringIO
96
from bzrlib.diff import show_diff
98
show_diff(self.branch, revision, specific_files=file_list, output=out)
100
return out.readlines()
102
def shelve(self, pick_hunks=False, message=None, revision=None,
104
patches = parse_patches(self.get_patches(revision, file_list))
169
self.__show_status(patch_source)
171
def shelve(self, patch_source, pick_hunks=False, message=None):
172
from datetime import datetime
174
hunks = patch_source.readhunks()
107
to_shelve = ShelveHunkSelector(patches).select()[0]
177
to_shelve = ShelveHunkSelector(hunks).select()[0]
111
181
if len(to_shelve) == 0:
112
raise BzrCommandError('Nothing to shelve')
114
shelf = self.next_shelf()
115
print >>sys.stderr, "Saving shelved patches to", shelf
116
shelf = open(shelf, 'a')
117
if message is not None:
118
assert '\n' not in message
119
shelf.write("# shelf: %s\n" % message)
120
for patch in to_shelve:
121
shelf.write(str(patch))
124
os.fsync(shelf.fileno())
127
print >>sys.stderr, "Reverting shelved patches"
128
pipe = os.popen('patch -d %s -sR -p0' % self.branch.base, 'w')
129
for patch in to_shelve:
130
pipe.write(str(patch))
182
raise CommandError('Nothing to shelve')
185
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
186
message = "Changes shelved on %s" % timestamp
188
patch_name = self.next_patch()
189
self.log('Shelving to %s/%s: "%s"\n' % \
190
(self.name, os.path.basename(patch_name), message))
192
patch = open(patch_name, 'a')
194
assert '\n' not in message
195
patch.write("%s%s\n" % (self.MESSAGE_PREFIX, message))
197
for hunk in to_shelve:
198
patch.write(str(hunk))
201
os.fsync(patch.fileno())
204
pipe = os.popen('patch -d %s -sR -p0' % self.base, 'w')
205
for hunk in to_shelve:
206
pipe.write(str(hunk))
133
209
if pipe.close() is not None:
134
raise BzrCommandError("Failed running patch!")
210
raise CommandError("Failed running patch!")
136
diff_stat = DiffStat(self.get_patches(None, None))
137
print 'Diff status is now:\n', diff_stat
212
self.__show_status(patch_source)