0.1.1
by Michael Ellerman
Initial import |
1 |
#!/usr/bin/python
|
2 |
||
3 |
import os |
|
4 |
import sys |
|
0.2.3
by Michael Ellerman
Factor out bzrisms. |
5 |
from errors import CommandError |
0.1.56
by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or |
6 |
from hunk_selector import ShelveHunkSelector, UnshelveHunkSelector |
0.1.35
by Michael Ellerman
Use DiffStat rather than calling out to /bin/diffstat |
7 |
from diffstat import DiffStat |
0.2.1
by Michael Ellerman
Factor out patch generation into PatchSource classes. |
8 |
from patchsource import PatchSource, FilePatchSource |
0.1.23
by Michael Ellerman
Incorporate Aaron's changes from bzrtools. |
9 |
|
0.1.27
by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the |
10 |
class Shelf(object): |
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
11 |
MESSAGE_PREFIX = "# Shelved patch: " |
12 |
||
0.2.22
by Michael Ellerman
Infrastructure to allow for multiple interchangeable shelves. |
13 |
_paths = { |
14 |
'base' : '.shelf', |
|
15 |
'shelves' : '.shelf/shelves', |
|
16 |
'current-shelf' : '.shelf/current-shelf', |
|
17 |
}
|
|
18 |
||
19 |
def __init__(self, base, name=None): |
|
20 |
self.base = base |
|
21 |
self.__setup() |
|
22 |
||
23 |
if name is None: |
|
24 |
current = os.path.join(self.base, self._paths['current-shelf']) |
|
25 |
name = open(current).read().strip() |
|
26 |
assert '\n' not in name |
|
27 |
||
0.2.5
by Michael Ellerman
Make some of the messages a bit more readable. |
28 |
self.name = name |
0.2.22
by Michael Ellerman
Infrastructure to allow for multiple interchangeable shelves. |
29 |
self.dir = os.path.join(self.base, self._paths['shelves'], name) |
30 |
if not os.path.isdir(self.dir): |
|
31 |
os.mkdir(self.dir) |
|
0.1.43
by Michael Ellerman
New shelf layout. Shelves now sit under .bzr/x-shelf/default/ |
32 |
|
0.2.22
by Michael Ellerman
Infrastructure to allow for multiple interchangeable shelves. |
33 |
def __setup(self): |
34 |
# Create required directories etc.
|
|
35 |
for dir in [self._paths['base'], self._paths['shelves']]: |
|
36 |
dir = os.path.join(self.base, dir) |
|
0.2.2
by Michael Ellerman
For the moment at least storing scads of stuff under .bzr isn't really |
37 |
if not os.path.isdir(dir): |
38 |
os.mkdir(dir) |
|
0.1.43
by Michael Ellerman
New shelf layout. Shelves now sit under .bzr/x-shelf/default/ |
39 |
|
0.2.22
by Michael Ellerman
Infrastructure to allow for multiple interchangeable shelves. |
40 |
current = os.path.join(self.base, self._paths['current-shelf']) |
41 |
if not os.path.exists(current): |
|
42 |
f = open(current, 'w') |
|
43 |
f.write('default') |
|
44 |
f.close() |
|
45 |
||
0.2.13
by Michael Ellerman
Add shelf command with subcommand "list" which lists the shelf contents. |
46 |
def log(self, msg): |
0.2.20
by Michael Ellerman
Make list output look nicer when the shelf is empty. |
47 |
sys.stderr.write(msg) |
0.2.13
by Michael Ellerman
Add shelf command with subcommand "list" which lists the shelf contents. |
48 |
|
0.2.19
by Michael Ellerman
Add shelf delete subcommand, make list barf if it gets an arg. |
49 |
def delete(self, patch): |
50 |
try: |
|
51 |
patch = int(patch) |
|
52 |
except TypeError: |
|
53 |
raise CommandError("Invalid patch name '%s'" % patch) |
|
54 |
||
55 |
path = self.__path(patch) |
|
56 |
||
57 |
if not os.path.exists(path): |
|
58 |
raise CommandError("Patch '%s' doesn't exist!" % path) |
|
59 |
||
60 |
os.remove(path) |
|
61 |
||
0.2.13
by Michael Ellerman
Add shelf command with subcommand "list" which lists the shelf contents. |
62 |
def list(self): |
63 |
self.log("Patches on shelf '%s':" % self.name) |
|
0.2.20
by Michael Ellerman
Make list output look nicer when the shelf is empty. |
64 |
indexes = self.__list() |
65 |
if len(indexes) == 0: |
|
66 |
self.log(' None\n') |
|
67 |
return
|
|
68 |
self.log('\n') |
|
69 |
for index in indexes: |
|
0.2.18
by Michael Ellerman
Make next_patch() cope with holes, eg. 00, 02 etc. |
70 |
msg = self.get_patch_message(self.__path(index)) |
0.2.13
by Michael Ellerman
Add shelf command with subcommand "list" which lists the shelf contents. |
71 |
if msg is None: |
72 |
msg = "No message saved with patch." |
|
0.2.20
by Michael Ellerman
Make list output look nicer when the shelf is empty. |
73 |
self.log(' %.2d: %s\n' % (index, msg)) |
0.2.13
by Michael Ellerman
Add shelf command with subcommand "list" which lists the shelf contents. |
74 |
|
0.2.18
by Michael Ellerman
Make next_patch() cope with holes, eg. 00, 02 etc. |
75 |
def __path(self, index): |
0.2.22
by Michael Ellerman
Infrastructure to allow for multiple interchangeable shelves. |
76 |
return os.path.join(self.dir, '%.2d' % index) |
0.1.27
by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the |
77 |
|
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
78 |
def next_patch(self): |
0.2.18
by Michael Ellerman
Make next_patch() cope with holes, eg. 00, 02 etc. |
79 |
indexes = self.__list() |
80 |
||
81 |
if len(indexes) == 0: |
|
82 |
next = 0 |
|
83 |
else: |
|
84 |
next = indexes[-1] + 1 |
|
85 |
return self.__path(next) |
|
0.1.27
by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the |
86 |
|
0.2.13
by Michael Ellerman
Add shelf command with subcommand "list" which lists the shelf contents. |
87 |
def __list(self): |
0.2.22
by Michael Ellerman
Infrastructure to allow for multiple interchangeable shelves. |
88 |
patches = os.listdir(self.dir) |
0.2.18
by Michael Ellerman
Make next_patch() cope with holes, eg. 00, 02 etc. |
89 |
indexes = [int(f) for f in patches] |
90 |
indexes.sort() |
|
91 |
return indexes |
|
0.2.13
by Michael Ellerman
Add shelf command with subcommand "list" which lists the shelf contents. |
92 |
|
93 |
def last_patch(self): |
|
0.2.18
by Michael Ellerman
Make next_patch() cope with holes, eg. 00, 02 etc. |
94 |
indexes = self.__list() |
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
95 |
|
0.2.18
by Michael Ellerman
Make next_patch() cope with holes, eg. 00, 02 etc. |
96 |
if len(indexes) == 0: |
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
97 |
return None |
98 |
||
0.2.18
by Michael Ellerman
Make next_patch() cope with holes, eg. 00, 02 etc. |
99 |
return self.__path(indexes[-1]) |
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
100 |
|
0.2.13
by Michael Ellerman
Add shelf command with subcommand "list" which lists the shelf contents. |
101 |
def get_patch_message(self, patch_path): |
102 |
patch = open(patch_path, 'r').read() |
|
103 |
||
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
104 |
if not patch.startswith(self.MESSAGE_PREFIX): |
105 |
return None |
|
106 |
return patch[len(self.MESSAGE_PREFIX):patch.index('\n')] |
|
0.1.27
by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the |
107 |
|
0.2.1
by Michael Ellerman
Factor out patch generation into PatchSource classes. |
108 |
def __show_status(self, source): |
109 |
if source.can_live_update(): |
|
0.2.5
by Michael Ellerman
Make some of the messages a bit more readable. |
110 |
diff_stat = str(DiffStat(source.readlines())) |
111 |
if len(diff_stat) > 0: |
|
0.2.20
by Michael Ellerman
Make list output look nicer when the shelf is empty. |
112 |
self.log('Diff status is now:\n' + diff_stat + '\n') |
0.2.5
by Michael Ellerman
Make some of the messages a bit more readable. |
113 |
else: |
0.2.20
by Michael Ellerman
Make list output look nicer when the shelf is empty. |
114 |
self.log('No changes left in working tree.\n') |
0.2.1
by Michael Ellerman
Factor out patch generation into PatchSource classes. |
115 |
|
116 |
def unshelve(self, patch_source, pick_hunks=False): |
|
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
117 |
patch_name = self.last_patch() |
118 |
||
119 |
if patch_name is None: |
|
120 |
raise CommandError("No patch found on shelf %s" % self.name) |
|
121 |
||
122 |
hunks = FilePatchSource(patch_name).readhunks() |
|
0.1.55
by Michael Ellerman
Add support for 'unshelve --pick'. This works but the UI is broken, as the |
123 |
if pick_hunks: |
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
124 |
to_unshelve, to_remain = UnshelveHunkSelector(hunks).select() |
0.1.58
by Michael Ellerman
Unshelve --pick was broken, because we deleted the whole patch, even when only |
125 |
else: |
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
126 |
to_unshelve = hunks |
0.1.58
by Michael Ellerman
Unshelve --pick was broken, because we deleted the whole patch, even when only |
127 |
to_remain = [] |
0.1.55
by Michael Ellerman
Add support for 'unshelve --pick'. This works but the UI is broken, as the |
128 |
|
0.1.58
by Michael Ellerman
Unshelve --pick was broken, because we deleted the whole patch, even when only |
129 |
if len(to_unshelve) == 0: |
0.2.3
by Michael Ellerman
Factor out bzrisms. |
130 |
raise CommandError('Nothing to unshelve') |
0.1.27
by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the |
131 |
|
0.2.13
by Michael Ellerman
Add shelf command with subcommand "list" which lists the shelf contents. |
132 |
message = self.get_patch_message(patch_name) |
0.2.14
by Michael Ellerman
Use new log function. |
133 |
if message is None: |
134 |
message = "" |
|
0.2.20
by Michael Ellerman
Make list output look nicer when the shelf is empty. |
135 |
self.log('Reapplying shelved patches "%s"\n' % message) |
0.1.58
by Michael Ellerman
Unshelve --pick was broken, because we deleted the whole patch, even when only |
136 |
|
0.2.2
by Michael Ellerman
For the moment at least storing scads of stuff under .bzr isn't really |
137 |
pipe = os.popen('patch -d %s -s -p0' % self.base, 'w') |
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
138 |
for hunk in to_unshelve: |
139 |
pipe.write(str(hunk)) |
|
0.1.27
by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the |
140 |
pipe.flush() |
141 |
||
142 |
if pipe.close() is not None: |
|
0.2.3
by Michael Ellerman
Factor out bzrisms. |
143 |
raise CommandError("Failed running patch!") |
0.1.27
by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the |
144 |
|
0.1.58
by Michael Ellerman
Unshelve --pick was broken, because we deleted the whole patch, even when only |
145 |
if len(to_remain) == 0: |
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
146 |
os.remove(patch_name) |
0.1.58
by Michael Ellerman
Unshelve --pick was broken, because we deleted the whole patch, even when only |
147 |
else: |
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
148 |
f = open(patch_name, 'w') |
149 |
for hunk in to_remain: |
|
150 |
f.write(str(hunk)) |
|
0.1.58
by Michael Ellerman
Unshelve --pick was broken, because we deleted the whole patch, even when only |
151 |
f.close() |
0.1.35
by Michael Ellerman
Use DiffStat rather than calling out to /bin/diffstat |
152 |
|
0.2.1
by Michael Ellerman
Factor out patch generation into PatchSource classes. |
153 |
self.__show_status(patch_source) |
154 |
||
155 |
def shelve(self, patch_source, pick_hunks=False, message=None): |
|
0.2.8
by Michael Ellerman
Always set shelf message and print it when shelving. |
156 |
from datetime import datetime |
157 |
||
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
158 |
hunks = patch_source.readhunks() |
0.1.31
by Michael Ellerman
- Keep our branch around, and use it directly instead of bzr_root. |
159 |
|
0.1.46
by Michael Ellerman
Shelve everything by default, use --pick to select hunks individually. |
160 |
if pick_hunks: |
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
161 |
to_shelve = ShelveHunkSelector(hunks).select()[0] |
0.1.58
by Michael Ellerman
Unshelve --pick was broken, because we deleted the whole patch, even when only |
162 |
else: |
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
163 |
to_shelve = hunks |
0.1.27
by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the |
164 |
|
0.1.58
by Michael Ellerman
Unshelve --pick was broken, because we deleted the whole patch, even when only |
165 |
if len(to_shelve) == 0: |
0.2.3
by Michael Ellerman
Factor out bzrisms. |
166 |
raise CommandError('Nothing to shelve') |
0.1.27
by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the |
167 |
|
0.2.8
by Michael Ellerman
Always set shelf message and print it when shelving. |
168 |
if message is None: |
169 |
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') |
|
170 |
message = "Changes shelved on %s" % timestamp |
|
171 |
||
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
172 |
patch_name = self.next_patch() |
0.2.20
by Michael Ellerman
Make list output look nicer when the shelf is empty. |
173 |
self.log('Shelving to %s/%s: "%s\n"' % \ |
0.2.14
by Michael Ellerman
Use new log function. |
174 |
(self.name, os.path.basename(patch_name), message)) |
0.2.8
by Michael Ellerman
Always set shelf message and print it when shelving. |
175 |
|
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
176 |
patch = open(patch_name, 'a') |
0.2.8
by Michael Ellerman
Always set shelf message and print it when shelving. |
177 |
|
178 |
assert '\n' not in message |
|
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
179 |
patch.write("%s%s\n" % (self.MESSAGE_PREFIX, message)) |
180 |
||
181 |
for hunk in to_shelve: |
|
182 |
patch.write(str(hunk)) |
|
183 |
||
184 |
patch.flush() |
|
185 |
os.fsync(patch.fileno()) |
|
186 |
patch.close() |
|
0.1.27
by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the |
187 |
|
0.2.2
by Michael Ellerman
For the moment at least storing scads of stuff under .bzr isn't really |
188 |
pipe = os.popen('patch -d %s -sR -p0' % self.base, 'w') |
0.2.12
by Michael Ellerman
Try to clear up terminology confusion. A shelf contains multiple patches, each |
189 |
for hunk in to_shelve: |
190 |
pipe.write(str(hunk)) |
|
0.1.27
by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the |
191 |
pipe.flush() |
192 |
||
193 |
if pipe.close() is not None: |
|
0.2.3
by Michael Ellerman
Factor out bzrisms. |
194 |
raise CommandError("Failed running patch!") |
0.1.27
by Michael Ellerman
Move all shelf functions into a class. Only logic change is we save the |
195 |
|
0.2.1
by Michael Ellerman
Factor out patch generation into PatchSource classes. |
196 |
self.__show_status(patch_source) |