1 """Basic quilt-like functionality
5 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License version 2 as
9 published by the Free Software Foundation.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 from stgit.utils import *
24 from stgit import git, basedir
25 from stgit.config import config
28 # stack exception class
29 class StackException(Exception):
34 self.should_print = True
35 def __call__(self, x, until_test, prefix):
37 self.should_print = False
39 return x[0:len(prefix)] != prefix
45 __comment_prefix = 'STG:'
46 __patch_prefix = 'STG_PATCH:'
48 def __clean_comments(f):
49 """Removes lines marked for status in a commit file
53 # remove status-prefixed lines
56 patch_filter = FilterUntil()
57 until_test = lambda t: t == (__patch_prefix + '\n')
58 lines = [l for l in lines if patch_filter(l, until_test, __comment_prefix)]
60 # remove empty lines at the end
61 while len(lines) != 0 and lines[-1] == '\n':
64 f.seek(0); f.truncate()
67 def edit_file(series, line, comment, show_patch = True):
69 tmpl = os.path.join(basedir.get(), 'patchdescr.tmpl')
74 elif os.path.isfile(tmpl):
75 print >> f, file(tmpl).read().rstrip()
78 print >> f, __comment_prefix, comment
79 print >> f, __comment_prefix, \
80 'Lines prefixed with "%s" will be automatically removed.' \
82 print >> f, __comment_prefix, \
83 'Trailing empty lines will be automatically removed.'
86 print >> f, __patch_prefix
87 # series.get_patch(series.get_current()).get_top()
88 git.diff([], series.get_patch(series.get_current()).get_bottom(), None, f)
90 #Vim modeline must be near the end.
91 print >> f, __comment_prefix, 'vi: set textwidth=75 filetype=diff nobackup:'
95 if config.has_option('stgit', 'editor'):
96 editor = config.get('stgit', 'editor')
97 elif 'EDITOR' in os.environ:
98 editor = os.environ['EDITOR']
101 editor += ' %s' % fname
103 print 'Invoking the editor: "%s"...' % editor,
105 print 'done (exit code: %d)' % os.system(editor)
107 f = file(fname, 'r+')
123 """Basic patch implementation
125 def __init__(self, name, series_dir, refs_dir):
126 self.__series_dir = series_dir
128 self.__dir = os.path.join(self.__series_dir, self.__name)
129 self.__refs_dir = refs_dir
130 self.__top_ref_file = os.path.join(self.__refs_dir, self.__name)
134 create_empty_file(os.path.join(self.__dir, 'bottom'))
135 create_empty_file(os.path.join(self.__dir, 'top'))
138 for f in os.listdir(self.__dir):
139 os.remove(os.path.join(self.__dir, f))
141 os.remove(self.__top_ref_file)
146 def rename(self, newname):
148 old_ref_file = self.__top_ref_file
149 self.__name = newname
150 self.__dir = os.path.join(self.__series_dir, self.__name)
151 self.__top_ref_file = os.path.join(self.__refs_dir, self.__name)
153 os.rename(olddir, self.__dir)
154 os.rename(old_ref_file, self.__top_ref_file)
156 def __update_top_ref(self, ref):
157 write_string(self.__top_ref_file, ref)
159 def update_top_ref(self):
162 self.__update_top_ref(top)
164 def __get_field(self, name, multiline = False):
165 id_file = os.path.join(self.__dir, name)
166 if os.path.isfile(id_file):
167 line = read_string(id_file, multiline)
175 def __set_field(self, name, value, multiline = False):
176 fname = os.path.join(self.__dir, name)
177 if value and value != '':
178 write_string(fname, value, multiline)
179 elif os.path.isfile(fname):
182 def get_old_bottom(self):
183 return self.__get_field('bottom.old')
185 def get_bottom(self):
186 return self.__get_field('bottom')
188 def set_bottom(self, value, backup = False):
190 curr = self.__get_field('bottom')
191 self.__set_field('bottom.old', curr)
192 self.__set_field('bottom', value)
194 def get_old_top(self):
195 return self.__get_field('top.old')
198 return self.__get_field('top')
200 def set_top(self, value, backup = False):
202 curr = self.__get_field('top')
203 self.__set_field('top.old', curr)
204 self.__set_field('top', value)
205 self.__update_top_ref(value)
207 def restore_old_boundaries(self):
208 bottom = self.__get_field('bottom.old')
209 top = self.__get_field('top.old')
212 self.__set_field('bottom', bottom)
213 self.__set_field('top', top)
214 self.__update_top_ref(top)
219 def get_description(self):
220 return self.__get_field('description', True)
222 def set_description(self, line):
223 self.__set_field('description', line, True)
225 def get_authname(self):
226 return self.__get_field('authname')
228 def set_authname(self, name):
230 if config.has_option('stgit', 'authname'):
231 name = config.get('stgit', 'authname')
232 elif 'GIT_AUTHOR_NAME' in os.environ:
233 name = os.environ['GIT_AUTHOR_NAME']
234 self.__set_field('authname', name)
236 def get_authemail(self):
237 return self.__get_field('authemail')
239 def set_authemail(self, address):
241 if config.has_option('stgit', 'authemail'):
242 address = config.get('stgit', 'authemail')
243 elif 'GIT_AUTHOR_EMAIL' in os.environ:
244 address = os.environ['GIT_AUTHOR_EMAIL']
245 self.__set_field('authemail', address)
247 def get_authdate(self):
248 return self.__get_field('authdate')
250 def set_authdate(self, date):
251 if not date and 'GIT_AUTHOR_DATE' in os.environ:
252 date = os.environ['GIT_AUTHOR_DATE']
253 self.__set_field('authdate', date)
255 def get_commname(self):
256 return self.__get_field('commname')
258 def set_commname(self, name):
260 if config.has_option('stgit', 'commname'):
261 name = config.get('stgit', 'commname')
262 elif 'GIT_COMMITTER_NAME' in os.environ:
263 name = os.environ['GIT_COMMITTER_NAME']
264 self.__set_field('commname', name)
266 def get_commemail(self):
267 return self.__get_field('commemail')
269 def set_commemail(self, address):
271 if config.has_option('stgit', 'commemail'):
272 address = config.get('stgit', 'commemail')
273 elif 'GIT_COMMITTER_EMAIL' in os.environ:
274 address = os.environ['GIT_COMMITTER_EMAIL']
275 self.__set_field('commemail', address)
279 """Class including the operations on series
281 def __init__(self, name = None):
282 """Takes a series name as the parameter.
288 self.__name = git.get_head_file()
289 self.__base_dir = basedir.get()
290 except git.GitException, ex:
291 raise StackException, 'GIT tree not initialised: %s' % ex
293 self.__series_dir = os.path.join(self.__base_dir, 'patches',
295 self.__refs_dir = os.path.join(self.__base_dir, 'refs', 'patches',
297 self.__base_file = os.path.join(self.__base_dir, 'refs', 'bases',
300 self.__applied_file = os.path.join(self.__series_dir, 'applied')
301 self.__unapplied_file = os.path.join(self.__series_dir, 'unapplied')
302 self.__current_file = os.path.join(self.__series_dir, 'current')
303 self.__descr_file = os.path.join(self.__series_dir, 'description')
305 # where this series keeps its patches
306 self.__patch_dir = os.path.join(self.__series_dir, 'patches')
307 if not os.path.isdir(self.__patch_dir):
308 self.__patch_dir = self.__series_dir
310 # if no __refs_dir, create and populate it (upgrade old repositories)
311 if self.is_initialised() and not os.path.isdir(self.__refs_dir):
312 os.makedirs(self.__refs_dir)
313 for patch in self.get_applied() + self.get_unapplied():
314 self.get_patch(patch).update_top_ref()
316 def get_branch(self):
317 """Return the branch name for the Series object
321 def __set_current(self, name):
322 """Sets the topmost patch
325 write_string(self.__current_file, name)
327 create_empty_file(self.__current_file)
329 def get_patch(self, name):
330 """Return a Patch object for the given name
332 return Patch(name, self.__patch_dir, self.__refs_dir)
334 def get_current(self):
335 """Return a Patch object representing the topmost patch
337 if os.path.isfile(self.__current_file):
338 name = read_string(self.__current_file)
346 def get_applied(self):
347 if not os.path.isfile(self.__applied_file):
348 raise StackException, 'Branch "%s" not initialised' % self.__name
349 f = file(self.__applied_file)
350 names = [line.strip() for line in f.readlines()]
354 def get_unapplied(self):
355 if not os.path.isfile(self.__unapplied_file):
356 raise StackException, 'Branch "%s" not initialised' % self.__name
357 f = file(self.__unapplied_file)
358 names = [line.strip() for line in f.readlines()]
362 def get_base_file(self):
363 self.__begin_stack_check()
364 return self.__base_file
366 def get_protected(self):
367 return os.path.isfile(os.path.join(self.__series_dir, 'protected'))
370 protect_file = os.path.join(self.__series_dir, 'protected')
371 if not os.path.isfile(protect_file):
372 create_empty_file(protect_file)
375 protect_file = os.path.join(self.__series_dir, 'protected')
376 if os.path.isfile(protect_file):
377 os.remove(protect_file)
379 def get_description(self):
380 if os.path.isfile(self.__descr_file):
381 return read_string(self.__descr_file)
385 def __patch_is_current(self, patch):
386 return patch.get_name() == read_string(self.__current_file)
388 def __patch_applied(self, name):
389 """Return true if the patch exists in the applied list
391 return name in self.get_applied()
393 def __patch_unapplied(self, name):
394 """Return true if the patch exists in the unapplied list
396 return name in self.get_unapplied()
398 def __begin_stack_check(self):
399 """Save the current HEAD into .git/refs/heads/base if the stack
402 if len(self.get_applied()) == 0:
403 head = git.get_head()
404 write_string(self.__base_file, head)
406 def __end_stack_check(self):
407 """Remove .git/refs/heads/base if the stack is empty.
408 This warning should never happen
410 if len(self.get_applied()) == 0 \
411 and read_string(self.__base_file) != git.get_head():
412 print 'Warning: stack empty but the HEAD and base are different'
414 def head_top_equal(self):
415 """Return true if the head and the top are the same
417 crt = self.get_current()
419 # we don't care, no patches applied
421 return git.get_head() == Patch(crt, self.__patch_dir,
422 self.__refs_dir).get_top()
424 def is_initialised(self):
425 """Checks if series is already initialised
427 return os.path.isdir(self.__patch_dir)
429 def init(self, create_at=False):
430 """Initialises the stgit series
432 bases_dir = os.path.join(self.__base_dir, 'refs', 'bases')
434 if os.path.exists(self.__patch_dir):
435 raise StackException, self.__patch_dir + ' already exists'
436 if os.path.exists(self.__refs_dir):
437 raise StackException, self.__refs_dir + ' already exists'
438 if os.path.exists(self.__base_file):
439 raise StackException, self.__base_file + ' already exists'
441 if (create_at!=False):
442 git.create_branch(self.__name, create_at)
444 os.makedirs(self.__patch_dir)
446 if not os.path.isdir(bases_dir):
447 os.makedirs(bases_dir)
449 create_empty_file(self.__applied_file)
450 create_empty_file(self.__unapplied_file)
451 create_empty_file(self.__descr_file)
452 os.makedirs(os.path.join(self.__series_dir, 'patches'))
453 os.makedirs(self.__refs_dir)
454 self.__begin_stack_check()
457 """Either convert to use a separate patch directory, or
458 unconvert to place the patches in the same directory with
461 if self.__patch_dir == self.__series_dir:
462 print 'Converting old-style to new-style...',
465 self.__patch_dir = os.path.join(self.__series_dir, 'patches')
466 os.makedirs(self.__patch_dir)
468 for p in self.get_applied() + self.get_unapplied():
469 src = os.path.join(self.__series_dir, p)
470 dest = os.path.join(self.__patch_dir, p)
476 print 'Converting new-style to old-style...',
479 for p in self.get_applied() + self.get_unapplied():
480 src = os.path.join(self.__patch_dir, p)
481 dest = os.path.join(self.__series_dir, p)
484 if not os.listdir(self.__patch_dir):
485 os.rmdir(self.__patch_dir)
488 print 'Patch directory %s is not empty.' % self.__name
490 self.__patch_dir = self.__series_dir
492 def rename(self, to_name):
495 to_stack = Series(to_name)
497 if to_stack.is_initialised():
498 raise StackException, '"%s" already exists' % to_stack.get_branch()
499 if os.path.exists(to_stack.__base_file):
500 os.remove(to_stack.__base_file)
502 git.rename_branch(self.__name, to_name)
504 if os.path.isdir(self.__series_dir):
505 os.rename(self.__series_dir, to_stack.__series_dir)
506 if os.path.exists(self.__base_file):
507 os.rename(self.__base_file, to_stack.__base_file)
509 self.__init__(to_name)
511 def clone(self, target_series):
514 base = read_string(self.get_base_file())
515 Series(target_series).init(create_at = base)
516 new_series = Series(target_series)
518 # generate an artificial description file
519 write_string(new_series.__descr_file, 'clone of "%s"' % self.__name)
521 # clone self's entire series as unapplied patches
522 patches = self.get_applied() + self.get_unapplied()
525 patch = self.get_patch(p)
526 new_series.new_patch(p, message = patch.get_description(),
527 can_edit = False, unapplied = True,
528 bottom = patch.get_bottom(),
529 top = patch.get_top(),
530 author_name = patch.get_authname(),
531 author_email = patch.get_authemail(),
532 author_date = patch.get_authdate())
534 # fast forward the cloned series to self's top
535 new_series.forward_patches(self.get_applied())
537 def delete(self, force = False):
538 """Deletes an stgit series
540 if self.is_initialised():
541 patches = self.get_unapplied() + self.get_applied()
542 if not force and patches:
543 raise StackException, \
544 'Cannot delete: the series still contains patches'
546 Patch(p, self.__patch_dir, self.__refs_dir).delete()
548 if os.path.exists(self.__applied_file):
549 os.remove(self.__applied_file)
550 if os.path.exists(self.__unapplied_file):
551 os.remove(self.__unapplied_file)
552 if os.path.exists(self.__current_file):
553 os.remove(self.__current_file)
554 if os.path.exists(self.__descr_file):
555 os.remove(self.__descr_file)
556 if not os.listdir(self.__patch_dir):
557 os.rmdir(self.__patch_dir)
559 print 'Patch directory %s is not empty.' % self.__name
560 if not os.listdir(self.__series_dir):
561 os.rmdir(self.__series_dir)
563 print 'Series directory %s is not empty.' % self.__name
564 if not os.listdir(self.__refs_dir):
565 os.rmdir(self.__refs_dir)
567 print 'Refs directory %s is not empty.' % self.__refs_dir
569 if os.path.exists(self.__base_file):
570 os.remove(self.__base_file)
572 def refresh_patch(self, files = None, message = None, edit = False,
575 author_name = None, author_email = None,
577 committer_name = None, committer_email = None,
579 """Generates a new commit for the given patch
581 name = self.get_current()
583 raise StackException, 'No patches applied'
585 patch = Patch(name, self.__patch_dir, self.__refs_dir)
587 descr = patch.get_description()
588 if not (message or descr):
594 if not message and edit:
595 descr = edit_file(self, descr.rstrip(), \
596 'Please edit the description for patch "%s" ' \
597 'above.' % name, show_patch)
600 author_name = patch.get_authname()
602 author_email = patch.get_authemail()
604 author_date = patch.get_authdate()
605 if not committer_name:
606 committer_name = patch.get_commname()
607 if not committer_email:
608 committer_email = patch.get_commemail()
610 bottom = patch.get_bottom()
612 commit_id = git.commit(files = files,
613 message = descr, parents = [bottom],
614 cache_update = cache_update,
616 author_name = author_name,
617 author_email = author_email,
618 author_date = author_date,
619 committer_name = committer_name,
620 committer_email = committer_email)
622 patch.set_bottom(bottom, backup = backup)
623 patch.set_top(commit_id, backup = backup)
624 patch.set_description(descr)
625 patch.set_authname(author_name)
626 patch.set_authemail(author_email)
627 patch.set_authdate(author_date)
628 patch.set_commname(committer_name)
629 patch.set_commemail(committer_email)
633 def undo_refresh(self):
634 """Undo the patch boundaries changes caused by 'refresh'
636 name = self.get_current()
639 patch = Patch(name, self.__patch_dir, self.__refs_dir)
640 old_bottom = patch.get_old_bottom()
641 old_top = patch.get_old_top()
643 # the bottom of the patch is not changed by refresh. If the
644 # old_bottom is different, there wasn't any previous 'refresh'
645 # command (probably only a 'push')
646 if old_bottom != patch.get_bottom() or old_top == patch.get_top():
647 raise StackException, 'No refresh undo information available'
649 git.reset(tree_id = old_top, check_out = False)
650 patch.restore_old_boundaries()
652 def new_patch(self, name, message = None, can_edit = True,
653 unapplied = False, show_patch = False,
654 top = None, bottom = None,
655 author_name = None, author_email = None, author_date = None,
656 committer_name = None, committer_email = None,
657 before_existing = False):
658 """Creates a new patch
660 if self.__patch_applied(name) or self.__patch_unapplied(name):
661 raise StackException, 'Patch "%s" already exists' % name
663 if not message and can_edit:
664 descr = edit_file(self, None, \
665 'Please enter the description for patch "%s" ' \
666 'above.' % name, show_patch)
670 head = git.get_head()
672 self.__begin_stack_check()
674 patch = Patch(name, self.__patch_dir, self.__refs_dir)
678 patch.set_bottom(bottom)
680 patch.set_bottom(head)
686 patch.set_description(descr)
687 patch.set_authname(author_name)
688 patch.set_authemail(author_email)
689 patch.set_authdate(author_date)
690 patch.set_commname(committer_name)
691 patch.set_commemail(committer_email)
694 patches = [patch.get_name()] + self.get_unapplied()
696 f = file(self.__unapplied_file, 'w+')
697 f.writelines([line + '\n' for line in patches])
701 insert_string(self.__applied_file, patch.get_name())
702 if not self.get_current():
703 self.__set_current(name)
705 append_string(self.__applied_file, patch.get_name())
706 self.__set_current(name)
708 def delete_patch(self, name):
711 patch = Patch(name, self.__patch_dir, self.__refs_dir)
713 if self.__patch_is_current(patch):
715 elif self.__patch_applied(name):
716 raise StackException, 'Cannot remove an applied patch, "%s", ' \
717 'which is not current' % name
718 elif not name in self.get_unapplied():
719 raise StackException, 'Unknown patch "%s"' % name
723 unapplied = self.get_unapplied()
724 unapplied.remove(name)
725 f = file(self.__unapplied_file, 'w+')
726 f.writelines([line + '\n' for line in unapplied])
728 self.__begin_stack_check()
730 def forward_patches(self, names):
731 """Try to fast-forward an array of patches.
733 On return, patches in names[0:returned_value] have been pushed on the
734 stack. Apply the rest with push_patch
736 unapplied = self.get_unapplied()
737 self.__begin_stack_check()
743 assert(name in unapplied)
745 patch = Patch(name, self.__patch_dir, self.__refs_dir)
748 bottom = patch.get_bottom()
749 top = patch.get_top()
751 # top != bottom always since we have a commit for each patch
753 # reset the backup information
754 patch.set_bottom(head, backup = True)
755 patch.set_top(top, backup = True)
758 head_tree = git.get_commit(head).get_tree()
759 bottom_tree = git.get_commit(bottom).get_tree()
760 if head_tree == bottom_tree:
761 # We must just reparent this patch and create a new commit
763 descr = patch.get_description()
764 author_name = patch.get_authname()
765 author_email = patch.get_authemail()
766 author_date = patch.get_authdate()
767 committer_name = patch.get_commname()
768 committer_email = patch.get_commemail()
770 top_tree = git.get_commit(top).get_tree()
772 top = git.commit(message = descr, parents = [head],
773 cache_update = False,
776 author_name = author_name,
777 author_email = author_email,
778 author_date = author_date,
779 committer_name = committer_name,
780 committer_email = committer_email)
782 patch.set_bottom(head, backup = True)
783 patch.set_top(top, backup = True)
786 # stop the fast-forwarding, must do a real merge
790 unapplied.remove(name)
797 append_strings(self.__applied_file, names[0:forwarded])
799 f = file(self.__unapplied_file, 'w+')
800 f.writelines([line + '\n' for line in unapplied])
803 self.__set_current(name)
807 def merged_patches(self, names):
808 """Test which patches were merged upstream by reverse-applying
809 them in reverse order. The function returns the list of
810 patches detected to have been applied. The state of the tree
811 is restored to the original one
813 patches = [Patch(name, self.__patch_dir, self.__refs_dir)
819 if git.apply_diff(p.get_top(), p.get_bottom(), False):
820 merged.append(p.get_name())
827 def push_patch(self, name, empty = False):
828 """Pushes a patch on the stack
830 unapplied = self.get_unapplied()
831 assert(name in unapplied)
833 self.__begin_stack_check()
835 patch = Patch(name, self.__patch_dir, self.__refs_dir)
837 head = git.get_head()
838 bottom = patch.get_bottom()
839 top = patch.get_top()
844 # top != bottom always since we have a commit for each patch
846 # just make an empty patch (top = bottom = HEAD). This
847 # option is useful to allow undoing already merged
848 # patches. The top is updated by refresh_patch since we
849 # need an empty commit
850 patch.set_bottom(head, backup = True)
851 patch.set_top(head, backup = True)
854 # reset the backup information
855 patch.set_bottom(bottom, backup = True)
856 patch.set_top(top, backup = True)
860 # new patch needs to be refreshed.
861 # The current patch is empty after merge.
862 patch.set_bottom(head, backup = True)
863 patch.set_top(head, backup = True)
865 # Try the fast applying first. If this fails, fall back to the
867 if not git.apply_diff(bottom, top):
868 # if git.apply_diff() fails, the patch requires a diff3
869 # merge and can be reported as modified
872 # merge can fail but the patch needs to be pushed
874 git.merge(bottom, head, top)
875 except git.GitException, ex:
876 print >> sys.stderr, \
877 'The merge failed during "push". ' \
878 'Use "refresh" after fixing the conflicts'
880 append_string(self.__applied_file, name)
882 unapplied.remove(name)
883 f = file(self.__unapplied_file, 'w+')
884 f.writelines([line + '\n' for line in unapplied])
887 self.__set_current(name)
889 # head == bottom case doesn't need to refresh the patch
890 if empty or head != bottom:
892 # if the merge was OK and no conflicts, just refresh the patch
893 # The GIT cache was already updated by the merge operation
894 self.refresh_patch(cache_update = False)
896 raise StackException, str(ex)
901 name = self.get_current()
904 patch = Patch(name, self.__patch_dir, self.__refs_dir)
905 old_bottom = patch.get_old_bottom()
906 old_top = patch.get_old_top()
908 # the top of the patch is changed by a push operation only
909 # together with the bottom (otherwise the top was probably
910 # modified by 'refresh'). If they are both unchanged, there
912 if old_bottom == patch.get_bottom() and old_top != patch.get_top():
913 raise StackException, 'No push undo information available'
917 return patch.restore_old_boundaries()
919 def pop_patch(self, name):
920 """Pops the top patch from the stack
922 applied = self.get_applied()
924 assert(name in applied)
926 patch = Patch(name, self.__patch_dir, self.__refs_dir)
928 git.switch(patch.get_bottom())
930 # save the new applied list
931 idx = applied.index(name) + 1
933 popped = applied[:idx]
935 unapplied = popped + self.get_unapplied()
937 f = file(self.__unapplied_file, 'w+')
938 f.writelines([line + '\n' for line in unapplied])
944 f = file(self.__applied_file, 'w+')
945 f.writelines([line + '\n' for line in applied])
949 self.__set_current(None)
951 self.__set_current(applied[-1])
953 self.__end_stack_check()
955 def empty_patch(self, name):
956 """Returns True if the patch is empty
958 patch = Patch(name, self.__patch_dir, self.__refs_dir)
959 bottom = patch.get_bottom()
960 top = patch.get_top()
964 elif git.get_commit(top).get_tree() \
965 == git.get_commit(bottom).get_tree():
970 def rename_patch(self, oldname, newname):
971 applied = self.get_applied()
972 unapplied = self.get_unapplied()
974 if oldname == newname:
975 raise StackException, '"To" name and "from" name are the same'
977 if newname in applied or newname in unapplied:
978 raise StackException, 'Patch "%s" already exists' % newname
980 if oldname in unapplied:
981 Patch(oldname, self.__patch_dir, self.__refs_dir).rename(newname)
982 unapplied[unapplied.index(oldname)] = newname
984 f = file(self.__unapplied_file, 'w+')
985 f.writelines([line + '\n' for line in unapplied])
987 elif oldname in applied:
988 Patch(oldname, self.__patch_dir, self.__refs_dir).rename(newname)
989 if oldname == self.get_current():
990 self.__set_current(newname)
992 applied[applied.index(oldname)] = newname
994 f = file(self.__applied_file, 'w+')
995 f.writelines([line + '\n' for line in applied])
998 raise StackException, 'Unknown patch "%s"' % oldname