PageRenderTime 159ms CodeModel.GetById 141ms RepoModel.GetById 0ms app.codeStats 0ms

/tortoisehg/hgtk/archive.py

https://bitbucket.org/tortoisehg/hgtk/
Python | 245 lines | 223 code | 12 blank | 10 comment | 1 complexity | 1b24739535486b2765da0f09932f794e MD5 | raw file
Possible License(s): GPL-2.0
  1. # archive.py - TortoiseHg's dialog for archiving a repo revision
  2. #
  3. # Copyright 2009 Emmanuel Rosa <goaway1000@gmail.com>
  4. #
  5. # This software may be used and distributed according to the terms of the
  6. # GNU General Public License version 2, incorporated herein by reference.
  7. import os
  8. import gtk
  9. from mercurial import error
  10. from tortoisehg.util.i18n import _
  11. from tortoisehg.util import hglib
  12. from tortoisehg.hgtk import gtklib, gdialog
  13. WD_PARENT = _('= Working Directory Parent =')
  14. class ArchiveDialog(gdialog.GDialog):
  15. """ Dialog to archive a Mercurial repo """
  16. def __init__(self, rev=None):
  17. gdialog.GDialog.__init__(self)
  18. self.initrev = rev
  19. ### Start of Overriding Section ###
  20. def get_title(self, reponame):
  21. return _('Archive - %s') % reponame
  22. def get_icon(self):
  23. return 'menucheckout.ico'
  24. def get_body(self, vbox):
  25. self.prevtarget = None
  26. # layout table
  27. self.table = table = gtklib.LayoutTable()
  28. vbox.pack_start(table, True, True, 2)
  29. ## revision combo
  30. self.combo = gtk.combo_box_entry_new_text()
  31. self.combo.child.set_width_chars(28)
  32. self.combo.child.connect('activate',
  33. lambda b: self.response(gtk.RESPONSE_OK))
  34. if self.initrev == '':
  35. self.combo.append_text(WD_PARENT)
  36. else:
  37. self.combo.append_text(str(self.initrev))
  38. self.combo.set_active(0)
  39. for b in self.repo.branchtags():
  40. self.combo.append_text(b)
  41. tags = list(self.repo.tags())
  42. tags.sort()
  43. tags.reverse()
  44. for t in tags:
  45. self.combo.append_text(t)
  46. table.add_row(_('Archive revision:'), self.combo)
  47. self.opt_files_in_rev = gtk.CheckButton(
  48. _('Only files modified/created in this revision'))
  49. table.add_row('', self.opt_files_in_rev, ypad=0)
  50. ## dest combo & browse button
  51. self.destentry = gtk.Entry()
  52. self.destentry.set_width_chars(46)
  53. destbrowse = gtk.Button(_('Browse...'))
  54. destbrowse.connect('clicked', self.browse_clicked)
  55. table.add_row(_('Destination path:'), self.destentry, 0, destbrowse)
  56. ## archive types
  57. self.filesradio = gtk.RadioButton(None, _('Directory of files'))
  58. self.filesradio.connect('toggled', self.type_changed)
  59. table.add_row(_('Archive types:'), self.filesradio, ypad=0)
  60. def add_type(label):
  61. radio = gtk.RadioButton(self.filesradio, label)
  62. radio.connect('toggled', self.type_changed)
  63. table.add_row(None, radio, ypad=0)
  64. return radio
  65. self.tarradio = add_type(_('Uncompressed tar archive'))
  66. self.tbz2radio = add_type(_('Tar archive compressed using bzip2'))
  67. self.tgzradio = add_type(_('Tar archive compressed using gzip'))
  68. self.uzipradio = add_type(_('Uncompressed zip archive'))
  69. self.zipradio = add_type(_('Zip archive compressed using deflate'))
  70. # signal handler
  71. self.combo.connect('changed', lambda c: self.update_path())
  72. # prepare to show
  73. self.update_path(hglib.toutf(self.repo.root))
  74. def get_buttons(self):
  75. return [('archive', _('Archive'), gtk.RESPONSE_OK),
  76. ('close', gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)]
  77. def get_action_map(self):
  78. return {gtk.RESPONSE_OK: self.archive}
  79. def switch_to(self, normal, working, cmd):
  80. self.table.set_sensitive(normal)
  81. self.buttons['archive'].set_property('visible', normal)
  82. self.buttons['close'].set_property('visible', normal)
  83. if normal:
  84. self.buttons['close'].grab_focus()
  85. def command_done(self, returncode, useraborted, *args):
  86. if returncode == 0:
  87. self.cmd.set_result(_('Archived successfully'), style='ok')
  88. elif useraborted:
  89. self.cmd.set_result(_('Canceled archiving'), style='error')
  90. else:
  91. self.cmd.set_result(_('Failed to archive'), style='error')
  92. ### End of Overriding Section ###
  93. def type_changed(self, radio):
  94. if not radio.get_active():
  95. return
  96. self.update_path()
  97. def update_path(self, path=None):
  98. def remove_ext(path):
  99. for ext in ('.tar', '.tar.bz2', '.tar.gz', '.zip'):
  100. if path.endswith(ext):
  101. return path.replace(ext, '')
  102. return path
  103. def remove_rev(path):
  104. model = self.combo.get_model()
  105. revs = [rev[0] for rev in model]
  106. revs.append(wdrev)
  107. if not self.prevtarget is None:
  108. revs.append(self.prevtarget)
  109. for rev in ['_' + rev for rev in revs]:
  110. if path.endswith(rev):
  111. return path.replace(rev, '')
  112. return path
  113. def add_rev(path, rev):
  114. return '%s_%s' % (path, rev)
  115. def add_ext(path):
  116. select = self.get_selected_archive_type()
  117. if select['type'] != 'files':
  118. path += select['ext']
  119. return path
  120. text = self.combo.get_active_text()
  121. if len(text) == 0:
  122. return
  123. wdrev = str(self.repo['.'].rev())
  124. if text == WD_PARENT:
  125. text = wdrev
  126. else:
  127. try:
  128. self.repo[text]
  129. except (error.RepoError, error.LookupError):
  130. return
  131. if path is None:
  132. path = self.destentry.get_text()
  133. path = remove_ext(path)
  134. path = remove_rev(path)
  135. path = add_rev(path, text)
  136. path = add_ext(path)
  137. self.destentry.set_text(path)
  138. self.prevtarget = text
  139. def get_selected_archive_type(self):
  140. """Return a dictionary describing the selected archive type"""
  141. if self.tarradio.get_active():
  142. return {'type': 'tar', 'ext': '.tar',
  143. 'label': _('Tar archives')}
  144. elif self.tbz2radio.get_active():
  145. return {'type': 'tbz2', 'ext': '.tar.bz2',
  146. 'label': _('Bzip2 tar archives')}
  147. elif self.tgzradio.get_active():
  148. return {'type': 'tgz', 'ext': '.tar.gz',
  149. 'label': _('Gzip tar archives')}
  150. elif self.uzipradio.get_active():
  151. return {'type': 'uzip', 'ext': '.zip',
  152. 'label': ('Uncompressed zip archives')}
  153. elif self.zipradio.get_active():
  154. return {'type': 'zip', 'ext': '.zip',
  155. 'label': _('Compressed zip archives')}
  156. return {'type': 'files', 'ext': None, 'label': None}
  157. def browse_clicked(self, button):
  158. """Select the destination directory or file"""
  159. dest = hglib.fromutf(self.destentry.get_text())
  160. if not os.path.exists(dest):
  161. dest = os.path.dirname(dest)
  162. select = self.get_selected_archive_type()
  163. if select['type'] == 'files':
  164. response = gtklib.NativeFolderSelectDialog(
  165. initial=dest,
  166. title=_('Select Destination Folder')).run()
  167. else:
  168. ext = '*' + select['ext']
  169. label = '%s (%s)' % (select['label'], ext)
  170. response = gtklib.NativeSaveFileDialogWrapper(
  171. initial=dest,
  172. title=_('Select Destination File'),
  173. filter=((label, ext),
  174. (_('All Files (*.*)'), '*.*'))).run()
  175. if response:
  176. self.destentry.set_text(response)
  177. def archive(self):
  178. # verify input
  179. type = self.get_selected_archive_type()['type']
  180. dest = self.destentry.get_text()
  181. if os.path.exists(dest):
  182. if type != 'files':
  183. ret = gdialog.Confirm(_('Confirm Overwrite'), [], self,
  184. _('The destination "%s" already exists!\n\n'
  185. 'Do you want to overwrite it?') % dest).run()
  186. if ret != gtk.RESPONSE_YES:
  187. return False
  188. elif len(os.listdir(dest)) > 0:
  189. ret = gdialog.Confirm(_('Confirm Overwrite'), [], self,
  190. _('The directory "%s" isn\'t empty!\n\n'
  191. 'Do you want to overwrite it?') % dest).run()
  192. if ret != gtk.RESPONSE_YES:
  193. return False
  194. # prepare command line
  195. cmdline = ['hg', 'archive', '--verbose']
  196. rev = self.combo.get_active_text()
  197. if rev != WD_PARENT:
  198. cmdline.append('--rev')
  199. cmdline.append(rev)
  200. cmdline.append('-t')
  201. cmdline.append(type)
  202. if self.opt_files_in_rev.get_active():
  203. ctx = self.repo[rev]
  204. for f in ctx.files():
  205. cmdline.append('-I')
  206. cmdline.append(f)
  207. cmdline.append('--')
  208. cmdline.append(hglib.fromutf(dest))
  209. # start archiving
  210. self.execute_command(cmdline)
  211. def run(ui, *pats, **opts):
  212. return ArchiveDialog(opts.get('rev'))