Kiln » TortoiseHg » TortoiseHg
Clone URL:  
Pushed to one repository · View In Graph Contained in 2.1, 2.1.1, and 2.1.2

Merge with stable

Changeset dbe7b9b55bf1

Parents 075b6dd8684e

Parents 6a19669f2de7

by Adrian Buehlmann

Changes to 27 files · Browse files at dbe7b9b55bf1 Showing diff from parent 075b6dd8684e 6a19669f2de7 Diff from another changeset...

Show Entire File TortoiseHgOverlayServer.py Stacked
(No changes)
 
199
200
201
202
 
203
204
205
 
309
310
311
312
 
313
314
315
 
199
200
201
 
202
203
204
205
 
309
310
311
 
312
313
314
315
@@ -199,7 +199,7 @@
  return   repo.thgbackup(path)   if revertall: - commands.revert(repo.ui, repo, path) + commands.revert(repo.ui, repo, path, no_backup=True)   else:   wlock = repo.wlock()   try: @@ -309,7 +309,7 @@
  else:   repo.thgbackup(repo.wjoin(wfile))   wasadded = wfile in repo[None].added() - commands.revert(repo.ui, repo, repo.wjoin(wfile)) + commands.revert(repo.ui, repo, repo.wjoin(wfile), no_backup=True)   if wasadded:   os.unlink(repo.wjoin(wfile))   self.fileModified.emit()
 
249
250
251
252
253
254
255
256
257
258
259
260
 
 
 
 
 
261
262
263
264
265
266
267
268
269
 
 
 
 
270
271
272
 
 
 
 
 
 
 
 
 
 
 
273
274
275
 
249
250
251
 
 
 
 
 
 
 
 
 
252
253
254
255
256
257
 
 
 
 
 
 
 
 
258
259
260
261
262
 
 
263
264
265
266
267
268
269
270
271
272
273
274
275
276
@@ -249,27 +249,28 @@
  def resizeEvent(self, e):   # re-size columns the smart way: the column holding Description   # is re-sized according to the total widget size. - key = '%s/widget_width' % self.cfgname - widget_width, ok = QSettings().value(key).toInt() - if not ok: - widget_width = 0 - - if self.resized: - model = self.model() - vp_width = self.viewport().width() - total_width = stretch_col = 0 + if e.oldSize().width() != e.size().width(): + key = '%s/widget_width' % self.cfgname + widget_width, ok = QSettings().value(key).toInt() + if not ok: + widget_width = 0   - if vp_width != widget_width: - for c in range(model.columnCount(QModelIndex())): - if model._columns[c] in model._stretchs: - #save the description column - stretch_col = c - else: - #total the other widths - total_width += self.columnWidth(c) + if self.resized: + model = self.model() + vp_width = self.viewport().width() + total_width = stretch_col = 0   - width = max(vp_width - total_width, 100) - self.setColumnWidth(stretch_col, width) + if vp_width != widget_width: + for c in range(model.columnCount(QModelIndex())): + if model._columns[c] in model._stretchs: + #save the description column + stretch_col = c + else: + #total the other widths + total_width += self.columnWidth(c) + + width = max(vp_width - total_width, 100) + self.setColumnWidth(stretch_col, width)     super(HgRepoView, self).resizeEvent(e)  
 
66
67
68
 
69
70
71
 
66
67
68
69
70
71
72
@@ -66,6 +66,7 @@
  self.revset = set()   self.namedTabs = {}   self.repolen = len(repo) + self.destroyed.connect(self.repo.thginvalidate)     if repo.parents()[0].rev() == -1:   self._reload_rev = 'tip'
 
779
780
781
782
 
 
783
784
785
786
787
788
789
 
 
 
 
 
790
791
792
 
796
797
798
 
 
 
 
799
800
801
 
975
976
977
978
 
 
979
980
 
981
982
983
984
985
986
987
988
989
 
 
 
 
990
991
992
 
997
998
999
1000
 
1001
1002
1003
1004
1005
1006
1007
 
 
 
 
 
1008
1009
1010
 
 
1011
1012
1013
 
1023
1024
1025
1026
 
1027
1028
1029
 
779
780
781
 
782
783
784
785
786
787
788
789
 
790
791
792
793
794
795
796
797
 
801
802
803
804
805
806
807
808
809
810
 
984
985
986
 
987
988
989
 
990
991
992
993
994
 
 
 
 
 
995
996
997
998
999
1000
1001
 
1006
1007
1008
 
1009
1010
1011
 
 
 
 
 
1012
1013
1014
1015
1016
1017
 
 
1018
1019
1020
1021
1022
 
1032
1033
1034
 
1035
1036
1037
1038
@@ -779,14 +779,19 @@
  def refresh(self, *args):   # refresh config values   self.ini = self.loadIniFile(self.rcpath) - self.readonly = not (hasattr(self.ini, 'write') and os.access(self.fn, os.W_OK)) + self.readonly = not (hasattr(self.ini, 'write') + and os.access(self.fn, os.W_OK))   self.stack.setDisabled(self.readonly)   self.fnedit.setText(hglib.tounicode(self.fn))   for name, info, widgets in self.pages.values():   if name == 'extensions':   extsmentioned = False   for row, w in enumerate(widgets): - val = self.readCPath('extensions.' + w.opts['label']) + key = w.opts['label'] + for fullkey in (key, 'hgext.%s' % key, 'hgext/%s' % key): + val = self.readCPath('extensions.' + fullkey) + if val != None: + break   if val == None:   curvalue = False   elif len(val) and val[0] == '!': @@ -796,6 +801,10 @@
  curvalue = True   extsmentioned = True   w.setValue(curvalue) + if val == None: + w.opts['cpath'] = 'extensions.' + key + else: + w.opts['cpath'] = 'extensions.' + fullkey   if not extsmentioned:   # make sure widgets are shown properly,   # even when no extensions mentioned in the config file @@ -975,18 +984,18 @@
  if (not emitChanged) and chk.isDirty():   self.restartRequested.emit(_('Extensions'))   emitChanged = True - key = chk.opts['label'] + name = chk.opts['label'] + section, key = chk.opts['cpath'].split('.', 1)   newvalue = chk.value() - if newvalue and (key in enabledexts): + if newvalue and (name in enabledexts):   continue # unchanged   if newvalue:   self.ini.set(section, key, '')   else: - for cand in (key, 'hgext.%s' % key, 'hgext/%s' % key): - try: - del self.ini[section][cand] - except KeyError: - pass + try: + del self.ini[section][key] + except KeyError: + pass     @pyqtSlot()   def validateextensions(self): @@ -997,17 +1006,17 @@
  if chk.isChecked())   invalidexts = hglib.validateextensions(selectedexts)   - def getinival(name): + def getinival(cpath):   if section not in self.ini:   return None - for cand in (name, 'hgext.%s' % name, 'hgext/%s' % name): - try: - return self.ini[section][cand] - except KeyError: - pass + sect, key = cpath.split('.', 1) + try: + return self.ini[sect][key] + except KeyError: + pass   - def changable(name): - curval = getinival(name) + def changable(name, cpath): + curval = getinival(cpath)   if curval not in ('', None):   # enabled or unspecified, official extensions only   return False @@ -1023,7 +1032,7 @@
  allexts = hglib.allextensions()   for chk in self.pages['extensions'][2]:   name = chk.opts['label'] - chk.setEnabled(changable(name)) + chk.setEnabled(changable(name, chk.opts['cpath']))   invalmsg = invalidexts.get(name)   if invalmsg:   invalmsg = invalmsg.decode('utf-8')
 
448
449
450
 
451
452
453
 
455
456
457
 
458
459
460
 
448
449
450
451
452
453
454
 
456
457
458
459
460
461
462
@@ -448,6 +448,7 @@
  index = self.repoTabsWidget.currentIndex()   if widget.closeRepoWidget():   self.repoTabsWidget.removeTab(index) + widget.deleteLater()   self.updateMenu()     def repoTabCloseRequested(self, index): @@ -455,6 +456,7 @@
  w = tw.widget(index)   if w and w.closeRepoWidget():   tw.removeTab(index) + w.deleteLater()   self.updateMenu()     def repoTabChanged(self, index=0):
 
75
76
77
78
79
 
 
80
81
82
 
75
76
77
 
 
78
79
80
81
82
@@ -75,8 +75,8 @@
  ## tooltips   self.tips = gtklib.Tooltips()   self.tips.set_tip(frame, - _('Commit message text for new changeset that reverses the' - ' effect of the change being backed out.')) + _('Commit message text for new changeset that reverses the ' + 'effect of the change being backed out.'))     ## use English backout message option   self.eng_msg = gtk.CheckButton(_('Use English backout message'))
 
54
55
56
57
58
 
 
59
60
61
 
54
55
56
 
 
57
58
59
60
61
@@ -54,8 +54,8 @@
  def get_error_text(self):   if self.__error_text__ == None:   text = '{{{\n#!python\n' # Wrap in Bitbucket wiki preformat markers - text += _('** Please report this bug to' - ' http://bitbucket.org/tortoisehg/stable/issues\n') + text += _('** Please report this bug to ' + 'http://bitbucket.org/tortoisehg/stable/issues\n')   text += '** Mercurial version (%s). TortoiseHg version (%s)\n' % (   hglib.hgversion, version.version())   text += '** Command: %s\n' % (self.opts['cmd'])
 
76
77
78
79
 
 
 
 
80
81
82
 
133
134
135
136
 
137
138
139
 
76
77
78
 
79
80
81
82
83
84
85
 
136
137
138
 
139
140
141
142
@@ -76,7 +76,10 @@
  def _get_bugtraq_object(self):   if self.bugtr == None:   obj = CreateObject(self.guid) - self.bugtr = obj.QueryInterface(IBugTraqProvider2) + try: + self.bugtr = obj.QueryInterface(IBugTraqProvider2) + except COMError: + return None   return self.bugtr     def get_commit_message(self, parameters, logmessage): @@ -133,7 +136,7 @@
  try:   bugtr.HasOptions()   return True - except ValueError: + except (ValueError, AttributeError):   return False    
 
1116
1117
1118
1119
1120
1121
 
 
 
1122
1123
1124
1125
1126
1127
1128
1129
 
 
 
1130
1131
1132
 
1137
1138
1139
1140
1141
 
 
1142
1143
1144
 
1116
1117
1118
 
 
 
1119
1120
1121
1122
1123
1124
1125
1126
 
 
 
1127
1128
1129
1130
1131
1132
 
1137
1138
1139
 
 
1140
1141
1142
1143
1144
@@ -1116,17 +1116,17 @@
    if sumlen and len(lines[0].rstrip()) > sumlen:   resp = gdialog.Confirm(_('Confirm Commit'), [], self, - _('The summary line length of %i is greater than' - ' %i.\n\nIgnore format policy and continue' - ' commit?') % + _('The summary line length of %i is greater than ' + '%i.\n\nIgnore format policy and continue ' + 'commit?') %   (len(lines[0].rstrip()), sumlen)).run()   if resp != gtk.RESPONSE_YES:   return False   if sumlen and len(lines) > 1 and len(lines[1].strip()):   resp = gdialog.Confirm(_('Confirm Commit'), [], self, - _('The summary line is not followed by a blank' - ' line.\n\nIgnore format policy and continue' - ' commit?')).run() + _('The summary line is not followed by a blank ' + 'line.\n\nIgnore format policy and continue ' + 'commit?')).run()   if resp != gtk.RESPONSE_YES:   return False   if maxlen: @@ -1137,8 +1137,8 @@
  if errs:   resp = gdialog.Confirm(_('Confirm Commit'), [], self,   _('The following lines are over the %i-' - 'character limit: %s.\n\nIgnore format' - ' policy and continue commit?') % + 'character limit: %s.\n\nIgnore format ' + 'policy and continue commit?') %   (maxlen, ', '.join(errs))).run()   if resp != gtk.RESPONSE_YES:   return False
 
708
709
710
711
712
 
 
713
714
715
 
708
709
710
 
 
711
712
713
714
715
@@ -708,8 +708,8 @@
  spacer.set_width_chars(24)   sniplbl = gtk.Label()   snipbox.pack_start(sniplbl, False, False) - sniplbl.set_markup('<span size="large" weight="heavy"' - ' font_family="monospace">...</span>') + sniplbl.set_markup('<span size="large" weight="heavy" ' + 'font_family="monospace">...</span>')   sniplbl.set_angle(90)   snipbox.pack_start(gtk.Label())   self.csbox.pack_start(wrapbox, False, False, 2)
 
317
318
319
320
321
322
323
324
325
 
 
 
 
 
 
326
327
328
 
317
318
319
 
 
 
 
 
 
320
321
322
323
324
325
326
327
328
@@ -317,12 +317,12 @@
  search_hbox.pack_start(search, False, False, 4)   self.tooltips.set_tip(search, _('Start this search'))   self.tooltips.set_tip(regexp, _('Regular expression search pattern')) - self.tooltips.set_tip(includes, _('Comma separated list of' - ' inclusion patterns. By default, the entire repository' - ' is searched.')) - self.tooltips.set_tip(excludes, _('Comma separated list of' - ' exclusion patterns. Exclusion patterns are applied' - ' after inclusion patterns.')) + self.tooltips.set_tip(includes, _('Comma separated list of ' + 'inclusion patterns. By default, the entire repository ' + 'is searched.')) + self.tooltips.set_tip(excludes, _('Comma separated list of ' + 'exclusion patterns. Exclusion patterns are applied ' + 'after inclusion patterns.'))   vbox.pack_start(search_hbox, False, False, 4)     hbox = gtk.HBox()
 
235
236
237
238
 
 
239
240
241
 
235
236
237
 
238
239
240
241
242
@@ -235,7 +235,8 @@
  unkmodel = self.unktree.get_model()   while q.qsize():   wfile = q.get(0) - unkmodel.append( [wfile, hglib.toutf(wfile)] ) + if unkmodel is not None: + unkmodel.append( [wfile, hglib.toutf(wfile)] )   return thread.isAlive()     def save_settings(self):
 
98
99
100
101
102
103
 
 
 
104
105
106
107
108
109
110
111
 
 
 
112
113
114
115
116
117
118
119
 
 
 
120
121
122
123
124
125
126
127
128
 
 
 
129
130
131
132
133
 
 
134
135
136
 
229
230
231
232
233
234
235
236
237
 
 
 
 
 
 
238
239
240
 
98
99
100
 
 
 
101
102
103
104
105
106
107
108
 
 
 
109
110
111
112
113
114
115
116
 
 
 
117
118
119
120
121
122
123
124
125
 
 
 
126
127
128
129
130
131
 
 
132
133
134
135
136
 
229
230
231
 
 
 
 
 
 
232
233
234
235
236
237
238
239
240
@@ -98,39 +98,39 @@
  self._normal = gtk.RadioButton(None, _('Send changesets as Hg patches'))   table.add_row(self._normal)   self.tips.set_tip(self._normal, - _('Hg patches (as generated by export command) are compatible' - ' with most patch programs. They include a header which' - ' contains the most important changeset metadata.')) + _('Hg patches (as generated by export command) are compatible ' + 'with most patch programs. They include a header which ' + 'contains the most important changeset metadata.'))     self._git = gtk.RadioButton(self._normal,   _('Use extended (git) patch format'))   table.add_row(self._git)   self.tips.set_tip(self._git, - _('Git patches can describe binary files, copies, and' - ' permission changes, but recipients may not be able to' - ' use them if they are not using git or Mercurial.')) + _('Git patches can describe binary files, copies, and ' + 'permission changes, but recipients may not be able to ' + 'use them if they are not using git or Mercurial.'))     self._plain = gtk.RadioButton(self._normal,   _('Plain, do not prepend Hg header'))   table.add_row(self._plain)   self.tips.set_tip(self._plain, - _('Stripping Mercurial header removes username and parent' - ' information. Only useful if recipient is not using' - ' Mercurial (and does not like to see the headers).')) + _('Stripping Mercurial header removes username and parent ' + 'information. Only useful if recipient is not using ' + 'Mercurial (and does not like to see the headers).'))     self._bundle = gtk.RadioButton(self._normal,   _('Send single binary bundle, not patches'))   table.add_row(self._bundle)   if revargs[0] in ('--outgoing', '-o'):   self.tips.set_tip(self._bundle, - _('Bundles store complete changesets in binary form.' - ' Upstream users can pull from them. This is the safest' - ' way to send changes to recipient Mercurial users.')) + _('Bundles store complete changesets in binary form. ' + 'Upstream users can pull from them. This is the safest ' + 'way to send changes to recipient Mercurial users.'))   else:   self._bundle.set_sensitive(False)   self.tips.set_tip(self._bundle, - _('This feature is only available when sending outgoing' - ' changesets. It is not applicable with revision ranges.')) + _('This feature is only available when sending outgoing ' + 'changesets. It is not applicable with revision ranges.'))     self._attach = gtk.CheckButton(_('attach'))   self.tips.set_tip(self._attach, @@ -229,12 +229,12 @@
  self._frombox.child.set_text(hglib.fromutf(getfromaddr(repo.ui)))   self._subjbox.child.set_text(hglib.fromutf(repo.ui.config('email', 'subject', '')))   self.tips.set_tip(self._eventbox, - _('Patch series description is sent in initial summary' - ' email with [PATCH 0 of N] subject. It should describe' - ' the effects of the entire patch series. When emailing' - ' a bundle, these fields make up the message subject and' - ' body. Flags is a comma separated list of tags' - ' which are inserted into the message subject prefix.') + _('Patch series description is sent in initial summary ' + 'email with [PATCH 0 of N] subject. It should describe ' + 'the effects of the entire patch series. When emailing ' + 'a bundle, these fields make up the message subject and ' + 'body. Flags is a comma separated list of tags ' + 'which are inserted into the message subject prefix.')   )   gtklib.addspellcheck(self.descview, self.repo.ui)   fill_history(history, self._tolist, 'email.to')
 
224
225
226
227
 
 
 
 
 
 
228
229
230
 
252
253
254
255
 
256
257
258
 
224
225
226
 
227
228
229
230
231
232
233
234
235
 
257
258
259
 
260
261
262
263
@@ -224,7 +224,12 @@
  def refresh(self):   hglib.invalidaterepo(self.repo)   matcher = match.always(self.repo.root, self.repo.root) - unknown = self.repo.status(match=matcher, unknown=True)[4] + try: + unknown = self.repo.status(match=matcher, unknown=True)[4] + except (EnvironmentError, util.Abort), inst: + gdialog.Prompt(_('Error while reading status'), + hglib.toutf(str(inst)), self).run() + return   self.unkmodel.clear()   for u in unknown:   self.unkmodel.append([hglib.toutf(u), u]) @@ -252,7 +257,7 @@
  createmode=None)   f.writelines(out)   f.rename() - except IOError, e: + except EnvironmentError, e:   dialog.error_dialog(self, _('Unable to write .hgignore file'),   hglib.tounicode(str(e)))   shlib.shell_notify([self.ignorefile])
 
797
798
799
800
801
 
 
802
803
804
 
797
798
799
 
 
800
801
802
803
804
@@ -797,8 +797,8 @@
  [('', 'delay', None, _('wait until the second ticks over')),   ('n', 'notify', [], _('notify the shell for paths given')),   ('', 'remove', None, _('remove the status cache')), - ('s', 'show', None, _('show the contents of the' - ' status cache (no update)')), + ('s', 'show', None, _('show the contents of the ' + 'status cache (no update)')),   ('', 'all', None, _('udpate all repos in current dir')) ],   _('hgtk thgstatus [OPTION]')),   "^update|checkout|co": (update,
 
194
195
196
197
198
 
 
199
200
201
 
213
214
215
216
217
 
 
218
219
220
 
194
195
196
 
 
197
198
199
200
201
 
213
214
215
 
 
216
217
218
219
220
@@ -194,8 +194,8 @@
  def before_close(self):   if len(self.repo.parents()) == 2:   ret = gdialog.Confirm(_('Confirm Exit'), [], self, - _('To complete merging, you need to commit' - ' merged files in working directory.\n\n' + _('To complete merging, you need to commit ' + 'merged files in working directory.\n\n'   'Do you want to exit?')).run()   if ret != gtk.RESPONSE_YES:   return False @@ -213,8 +213,8 @@
  # '.' is safer than self.localrev, in case the user has   # pulled a fast one on us and updated from the CLI   ret = gdialog.Confirm(_('Confirm Discard Changes'), [], self, - _('The changes from revision %s and all unmerged parents' - ' will be discarded.\n\n' + _('The changes from revision %s and all unmerged parents ' + 'will be discarded.\n\n'   'Are you sure this is what you want to do?')   % (self.otherframe.get_data('revid'))).run()   if ret != gtk.RESPONSE_YES:
 
56
57
58
59
60
 
 
61
62
63
 
56
57
58
 
 
59
60
61
62
63
@@ -56,8 +56,8 @@
  self._toolbutton(gtk.STOCK_UNDO,   _('Rollback'),   self._rollback_clicked, - tip=_('Rollback (undo) last transaction to' - ' repository (pull, commit, etc)')), + tip=_('Rollback (undo) last transaction to ' + 'repository (pull, commit, etc)')),   gtk.SeparatorToolItem(),   self._toolbutton(gtk.STOCK_CLEAR,   _('Recover'),
 
365
366
367
368
369
 
 
370
371
372
 
365
366
367
 
 
368
369
370
371
372
@@ -365,8 +365,8 @@
  _('name to show in web pages (default: working dir)')),   ('', 'web-conf', '',   _('name of the hgweb config file (serve more than one repository)')), - ('', 'webdir-conf', '', _('name of the webdir config file' - ' (DEPRECATED)')), + ('', 'webdir-conf', '', _('name of the webdir config file ' + '(DEPRECATED)')),   ('', 'pid-file', '', _('name of file to write process ID to')),   ('', 'stdio', None, _('for remote clients')),   ('t', 'templates', '', _('web templates to use')),
 
23
24
25
26
27
28
29
 
149
150
151
152
 
153
154
155
 
189
190
191
192
 
193
194
195
 
208
209
210
211
212
 
 
213
214
215
 
23
24
25
 
26
27
28
 
148
149
150
 
151
152
153
154
 
188
189
190
 
191
192
193
194
 
207
208
209
 
 
210
211
212
213
214
@@ -23,7 +23,6 @@
    self.set_default_size(400, -1)   self.set_title(_('TortoiseHg Shell Configuration')) - self.setWindowIcon(qtlib.geticon('detect_rename'))     okay = gtk.Button(_('OK'))   cancel = gtk.Button(_('Cancel')) @@ -149,7 +148,7 @@
  hbox = gtk.HBox()   tvbox.pack_start(hbox, False, False, 2)   hbox.pack_start(gtk.Label( - _('Warning: affects all Tortoises, logoff required after change')), + _('Warning: affects all Tortoises, logoff required after change')),   False, False, 2)     hbox = gtk.HBox() @@ -189,7 +188,7 @@
  hbox = gtk.HBox()   tvbox.pack_start(hbox, False, False, 2)   hbox.pack_start(gtk.Label( - _('*: not used by TortoiseHg')), + _('*: not used by TortoiseHg')),   False, False, 2)     ## Taskbar group @@ -208,8 +207,8 @@
  # Tooltips   tips = gtklib.Tooltips()   - tooltip = _('Do not show menu items on unversioned folders' - ' (use shift + click to override)') + tooltip = _('Do not show menu items on unversioned folders ' + '(use shift + click to override)')   tips.set_tip(self.hide_context_menu, tooltip)   tooltip = _('Show overlay icons in Mercurial repositories')   tips.set_tip(self.ovenable, tooltip)
 
56
57
58
59
60
 
 
61
62
63
64
65
 
 
66
67
68
69
70
71
 
72
73
74
75
76
 
 
77
78
79
80
81
 
 
82
83
84
 
172
173
174
175
176
 
 
177
178
179
 
192
193
194
195
196
 
 
 
197
198
199
 
202
203
204
205
206
 
 
207
208
209
 
56
57
58
 
 
59
60
61
62
63
 
 
64
65
66
67
68
69
70
 
71
72
73
74
 
 
75
76
77
78
79
 
 
80
81
82
83
84
 
172
173
174
 
 
175
176
177
178
179
 
192
193
194
 
 
195
196
197
198
199
200
 
203
204
205
 
 
206
207
208
209
210
@@ -56,29 +56,29 @@
  self.toolbutton(gtk.STOCK_GO_DOWN,   _('Incoming'),   self.incoming_clicked, - tip=_('Display changes that can be pulled' - ' from selected repository')), + tip=_('Display changes that can be pulled ' + 'from selected repository')),   self.toolbutton(gtk.STOCK_GOTO_BOTTOM,   _(' Pull '),   self.pull_clicked, - tip=_('Pull changes from selected' - ' repository')), + tip=_('Pull changes from selected ' + 'repository')),   gtk.SeparatorToolItem(),   self.toolbutton(gtk.STOCK_GO_UP,   _('Outgoing'),   self.outgoing_clicked,   tip=_('Display local changes that will be ' - ' pushed to selected repository')), + 'pushed to selected repository')),   self.toolbutton(gtk.STOCK_GOTO_TOP,   _('Push'),   self.push_clicked, - tip=_('Push local changes to selected' - ' repository')), + tip=_('Push local changes to selected ' + 'repository')),   self.toolbutton(gtk.STOCK_GOTO_LAST,   _('Email'),   self.email_clicked, - tip=_('Email local outgoing changes to' - ' one or more recipients')), + tip=_('Email local outgoing changes to ' + 'one or more recipients')),   self.toolbutton(gtk.STOCK_UNDO,   _('Shelve'),   self.shelve_clicked, @@ -172,8 +172,8 @@
  ## checkbox options   chkopthbox = gtk.HBox()   self.force = gtk.CheckButton(_('Force pull or push')) - self.tips.set_tip(self.force, _('Run even when remote repository' - ' is unrelated.')) + self.tips.set_tip(self.force, + _('Run even when remote repository is unrelated.'))   self.newbranch = gtk.CheckButton(_('Push new branch'))   self.tips.set_tip(self.newbranch, _('Allow pushing a new branch'))   self.use_proxy = gtk.CheckButton(_('Use proxy server')) @@ -192,8 +192,9 @@
  revhbox.pack_start(self.reventry, True, True, 2)   reveventbox = gtk.EventBox()   reveventbox.add(revhbox) - self.tips.set_tip(reveventbox, _('A specific revision up to which you' - ' would like to push or pull.')) + self.tips.set_tip(reveventbox, + _('A specific revision up to which you ' + 'would like to push or pull.'))     ## remote command option   cmdhbox = gtk.HBox() @@ -202,8 +203,8 @@
  cmdhbox.pack_start(self.cmdentry, True, True, 2)   cmdeventbox = gtk.EventBox()   cmdeventbox.add(cmdhbox) - self.tips.set_tip(cmdeventbox, _('Name of hg executable on remote' - ' machine.')) + self.tips.set_tip(cmdeventbox, + _('Name of hg executable on remote machine.'))     revvbox = gtk.VBox()   revvbox.pack_start(chkopthbox, False, False, 8)
 
43
44
45
46
47
48
49
50
51
 
 
 
 
 
 
52
53
54
55
56
 
 
 
 
57
58
59
60
61
62
 
 
 
63
64
65
66
 
 
 
67
68
69
70
 
 
71
72
73
74
 
 
 
75
76
77
 
 
78
79
80
 
 
81
82
83
 
 
84
85
86
87
 
 
88
89
90
91
92
93
94
95
96
 
 
 
 
97
98
99
100
101
102
 
 
 
 
 
103
104
105
 
 
106
107
108
 
 
109
110
111
112
 
 
 
113
114
115
116
 
 
 
117
118
119
120
121
 
 
 
 
122
123
124
125
 
 
 
126
127
128
129
130
131
132
133
134
 
 
 
 
135
136
137
138
 
 
 
139
140
141
142
 
 
 
143
144
145
146
 
 
 
147
148
149
150
151
152
153
 
 
 
 
 
 
154
155
156
157
158
 
 
 
 
159
160
161
162
163
164
 
 
165
166
167
 
 
168
169
170
171
172
173
174
175
176
 
 
 
177
178
179
180
181
182
 
 
183
184
185
 
 
186
187
188
 
 
189
190
191
192
193
194
 
 
195
196
197
198
 
 
199
200
201
 
 
202
203
204
205
206
207
208
209
210
211
212
213
 
 
 
 
 
 
 
214
215
216
217
218
219
220
 
 
 
 
 
 
221
222
223
224
225
226
227
228
 
 
229
230
231
 
 
232
233
234
 
237
238
239
240
241
 
 
242
243
244
245
246
247
248
 
 
249
250
251
252
253
254
255
 
 
 
 
 
 
256
257
258
259
 
 
260
261
262
 
 
263
264
265
266
267
268
269
 
 
270
271
272
273
274
275
276
277
 
 
 
 
278
279
280
 
 
281
282
283
 
 
284
285
286
 
 
287
288
289
 
 
290
291
292
 
 
293
294
295
 
 
296
297
298
299
 
 
300
301
302
303
304
305
306
 
 
307
308
309
 
 
310
311
312
 
 
313
314
315
 
 
316
317
318
 
43
44
45
 
 
 
 
 
 
46
47
48
49
50
51
52
 
 
 
 
53
54
55
56
57
58
59
 
 
 
60
61
62
63
 
 
 
64
65
66
67
68
 
 
69
70
71
 
 
 
72
73
74
75
 
 
76
77
78
 
 
79
80
81
 
 
82
83
84
85
 
 
86
87
88
89
90
91
92
 
 
 
 
93
94
95
96
97
 
 
 
 
 
98
99
100
101
102
103
 
 
104
105
106
 
 
107
108
109
 
 
 
110
111
112
113
 
 
 
114
115
116
117
 
 
 
 
118
119
120
121
122
 
 
 
123
124
125
126
127
128
129
130
 
 
 
 
131
132
133
134
135
 
 
 
136
137
138
139
 
 
 
140
141
142
143
 
 
 
144
145
146
147
 
 
 
 
 
 
148
149
150
151
152
153
154
 
 
 
 
155
156
157
158
159
160
161
162
 
 
163
164
165
 
 
166
167
168
169
170
171
172
173
 
 
 
174
175
176
177
178
179
180
 
 
181
182
183
 
 
184
185
186
 
 
187
188
189
190
191
192
 
 
193
194
195
196
 
 
197
198
199
 
 
200
201
202
203
204
205
206
 
 
 
 
 
 
 
207
208
209
210
211
212
213
214
 
 
 
 
 
 
215
216
217
218
219
220
221
222
223
224
225
226
 
 
227
228
229
 
 
230
231
232
233
234
 
237
238
239
 
 
240
241
242
243
244
245
246
 
 
247
248
249
 
 
 
 
 
 
250
251
252
253
254
255
256
257
 
 
258
259
260
 
 
261
262
263
264
265
266
267
 
 
268
269
270
271
272
273
 
 
 
 
274
275
276
277
278
 
 
279
280
281
 
 
282
283
284
 
 
285
286
287
 
 
288
289
290
 
 
291
292
293
 
 
294
295
296
297
 
 
298
299
300
301
302
303
304
 
 
305
306
307
 
 
308
309
310
 
 
311
312
313
 
 
314
315
316
317
318
@@ -43,192 +43,192 @@
  (_('UI Language'), 'tortoisehg.ui.language', i18n.availablelanguages(),   _('Specify your preferred user interface language (restart needed)')),   (_('Three-way Merge Tool'), 'ui.merge', [], - _('Graphical merge program for resolving merge conflicts. If left' - ' unspecified, Mercurial will use the first applicable tool it finds' - ' on your system or use its internal merge tool that leaves conflict' - ' markers in place. Chose internal:merge to force conflict markers,' - ' internal:prompt to always select local or other, or internal:dump' - ' to leave files in the working directory for manual merging')), + _('Graphical merge program for resolving merge conflicts. If left ' + 'unspecified, Mercurial will use the first applicable tool it finds ' + 'on your system or use its internal merge tool that leaves conflict ' + 'markers in place. Chose internal:merge to force conflict markers, ' + 'internal:prompt to always select local or other, or internal:dump ' + 'to leave files in the working directory for manual merging')),   (_('Visual Diff Tool'), 'tortoisehg.vdiff', [], - _('Specify visual diff tool, as described in the [merge-tools]' - ' section of your Mercurial configuration files. If left' - ' unspecified, TortoiseHg will use the selected merge tool.' - ' Failing that it uses the first applicable tool it finds.')), + _('Specify visual diff tool, as described in the [merge-tools] ' + 'section of your Mercurial configuration files. If left ' + 'unspecified, TortoiseHg will use the selected merge tool. ' + 'Failing that it uses the first applicable tool it finds.')),   (_('Visual Editor'), 'tortoisehg.editor', [],   _('Specify the visual editor used to view files, etc')),   (_('CLI Editor'), 'ui.editor', [], - _('The editor to use during a commit and other instances where' - ' Mercurial needs multiline input from the user. Used by' - ' command line commands, including patch import.')), + _('The editor to use during a commit and other instances where ' + 'Mercurial needs multiline input from the user. Used by ' + 'command line commands, including patch import.')),   (_('Tab Width'), 'tortoisehg.tabwidth', [], - _('Specify the number of spaces that tabs expand to in various' - ' TortoiseHg windows.' - ' Default: Not expanded')), + _('Specify the number of spaces that tabs expand to in various ' + 'TortoiseHg windows. ' + 'Default: Not expanded')),   (_('Max Diff Size'), 'tortoisehg.maxdiff', ['1024', '0'],   _('The maximum size file (in KB) that TortoiseHg will ' - 'show changes for in the changelog, status, and commit windows.' - ' A value of zero implies no limit. Default: 1024 (1MB)')), + 'show changes for in the changelog, status, and commit windows. ' + 'A value of zero implies no limit. Default: 1024 (1MB)')),   (_('Bottom Diffs'), 'gtools.diffbottom', ['False', 'True'], - _('Show the diff panel below the file list in status, shelve, and' - ' commit dialogs.' - ' Default: False (show diffs to right of file list)')), + _('Show the diff panel below the file list in status, shelve, and ' + 'commit dialogs. ' + 'Default: False (show diffs to right of file list)')),   (_('Capture stderr'), 'tortoisehg.stderrcapt', ['True', 'False'], - _('Redirect stderr to a buffer which is parsed at the end of' - ' the process for runtime errors. Default: True')), + _('Redirect stderr to a buffer which is parsed at the end of ' + 'the process for runtime errors. Default: True')),   (_('Fork hgtk'), 'tortoisehg.hgtkfork', ['True', 'False'], - _('When running hgtk from the command line, fork a background' - ' process to run graphical dialogs. Default: True')), + _('When running hgtk from the command line, fork a background ' + 'process to run graphical dialogs. Default: True')),   (_('Full Path Title'), 'tortoisehg.fullpath', ['False', 'True'], - _('Show a full directory path of the repository in the dialog title' - ' instead of just the root directory name. Default: False')), + _('Show a full directory path of the repository in the dialog title ' + 'instead of just the root directory name. Default: False')),   ) + (gtklib.hasspellcheck() and   ((_('Spell Check Language'), 'tortoisehg.spellcheck', [], - _('Default language for spell check. System language is' - ' used if not specified. Examples: en, en_GB, en_US')),) or ())), + _('Default language for spell check. System language is ' + 'used if not specified. Examples: en, en_GB, en_US')),) or ())),    ({'name': 'commit', 'label': _('Commit'), 'icon': 'menucommit.ico'}, (   (_('Username'), 'ui.username', [],   _('Name associated with commits')),   (_('Summary Line Length'), 'tortoisehg.summarylen', ['0', '70'], - _('Maximum length of the commit message summary line.' - ' If set, TortoiseHg will issue a warning if the' - ' summary line is too long or not separated by a' - ' blank line. Default: 0 (unenforced)')), + _('Maximum length of the commit message summary line. ' + 'If set, TortoiseHg will issue a warning if the ' + 'summary line is too long or not separated by a ' + 'blank line. Default: 0 (unenforced)')),   (_('Message Line Length'), 'tortoisehg.messagewrap', ['0', '80'], - _('Word wrap length of the commit message. If' - ' set, the popup menu can be used to format' - ' the message and a warning will be issued' - ' if any lines are too long at commit.' - ' Default: 0 (unenforced)')), + _('Word wrap length of the commit message. If ' + 'set, the popup menu can be used to format ' + 'the message and a warning will be issued ' + 'if any lines are too long at commit. ' + 'Default: 0 (unenforced)')),   (_('Close After Commit'), 'tortoisehg.closeci', ['False', 'True'], - _('Close the commit tool after every successful' - ' commit. Default: False')), + _('Close the commit tool after every successful ' + 'commit. Default: False')),   (_('Push After Commit'), 'tortoisehg.pushafterci', ['False', 'True'], - _('Attempt to push to default push target after every successful' - ' commit. Default: False')), + _('Attempt to push to default push target after every successful ' + 'commit. Default: False')),   (_('Auto Commit List'), 'tortoisehg.autoinc', [], - _('Comma separated list of files that are automatically included' - ' in every commit. Intended for use only as a repository setting.' - ' Default: None (leave blank)')), + _('Comma separated list of files that are automatically included ' + 'in every commit. Intended for use only as a repository setting. ' + 'Default: None (leave blank)')),   (_('Auto Exclude List'), 'tortoisehg.ciexclude', [], - _('Comma separated list of files that are automatically unchecked' - ' when the status, commit, and shelve dialogs are opened.' - ' Default: None (leave blank)')), + _('Comma separated list of files that are automatically unchecked ' + 'when the status, commit, and shelve dialogs are opened. ' + 'Default: None (leave blank)')),   (_('English Messages'), 'tortoisehg.engmsg', ['False', 'True'], - _('Generate English commit messages even if LANGUAGE or LANG' - ' environment variables are set to a non-English language.' - ' This setting is used by the Merge, Tag and Backout dialogs.' - ' Default: False')), + _('Generate English commit messages even if LANGUAGE or LANG ' + 'environment variables are set to a non-English language. ' + 'This setting is used by the Merge, Tag and Backout dialogs. ' + 'Default: False')),   (_('Default Tab'), 'tortoisehg.statustab', ['0', '1', '2'], - _('The tab on which the status and commit tools will open.' - ' 0 - TextDiff, 1 - Hunk Selection, 2 - Commit Preview.' - ' Default: 0')), + _('The tab on which the status and commit tools will open. ' + '0 - TextDiff, 1 - Hunk Selection, 2 - Commit Preview. ' + 'Default: 0')),   )),    ({'name': 'log', 'label': _('Repository Explorer'),   'icon': 'menulog.ico'}, (   (_('Author Coloring'), 'tortoisehg.authorcolor', ['False', 'True'], - _('Color changesets by author name. If not enabled,' - ' the changes are colored green for merge, red for' - ' non-trivial parents, black for normal.' - ' Default: False')), + _('Color changesets by author name. If not enabled, ' + 'the changes are colored green for merge, red for ' + 'non-trivial parents, black for normal. ' + 'Default: False')),   (_('Long Summary'), 'tortoisehg.longsummary', ['False', 'True'], - _('If true, concatenate multiple lines of changeset summary' - ' until they reach 80 characters.' - ' Default: False')), + _('If true, concatenate multiple lines of changeset summary ' + 'until they reach 80 characters. ' + 'Default: False')),   (_('Log Batch Size'), 'tortoisehg.graphlimit', ['500'], - _('The number of revisions to read and display in the' - ' changelog viewer in a single batch.' - ' Default: 500')), + _('The number of revisions to read and display in the ' + 'changelog viewer in a single batch. ' + 'Default: 500')),   (_('Dead Branches'), 'tortoisehg.deadbranch', [], - _('Comma separated list of branch names that should be ignored' - ' when building a list of branch names for a repository.' - ' Default: None (leave blank)')), + _('Comma separated list of branch names that should be ignored ' + 'when building a list of branch names for a repository. ' + 'Default: None (leave blank)')),   (_('Branch Colors'), 'tortoisehg.branchcolors', [], - _('Space separated list of branch names and colors of the form' - ' branch:#XXXXXX. Spaces and colons in the branch name must be' - ' escaped using a backslash (\\). Likewise some other characters' - ' can be escaped in this way, e.g. \\u0040 will be decoded to the' - ' @ character, and \\n to a linefeed.' - ' Default: None (leave blank)')), + _('Space separated list of branch names and colors of the form ' + 'branch:#XXXXXX. Spaces and colons in the branch name must be' + 'escaped using a backslash (\\). Likewise some other characters ' + 'can be escaped in this way, e.g. \\u0040 will be decoded to the ' + '@ character, and \\n to a linefeed. ' + 'Default: None (leave blank)')),   (_('Hide Tags'), 'tortoisehg.hidetags', [], - _('Space separated list of tags that will not be shown.' - ' Useful example: Specify "qbase qparent qtip" to hide the' - ' standard tags inserted by the Mercurial Queues Extension.' - ' Default: None (leave blank)')), + _('Space separated list of tags that will not be shown. ' + 'Useful example: Specify "qbase qparent qtip" to hide the ' + 'standard tags inserted by the Mercurial Queues Extension. ' + 'Default: None (leave blank)')),   (_('Use Expander'), 'tortoisehg.changeset-expander', ['False', 'True'],   _('Show changeset details with an expander')),   (_('Toolbar Style'), 'tortoisehg.logtbarstyle',   ['small', 'large', 'theme'], - _('Adjust the display of the main toolbar in the Repository' - ' Explorer. Values: small, large, or theme. Default: theme')), + _('Adjust the display of the main toolbar in the Repository ' + 'Explorer. Values: small, large, or theme. Default: theme')),  # (_('F/S Encodings'), 'tortoisehg.fsencodings', [], -# _('Comma separated list of encodings used for filenames' -# ' on this computer. Default: none')), +# _('Comma separated list of encodings used for filenames ' +# 'on this computer. Default: none')),   )),    ({'name': 'sync', 'label': _('Synchronize'), 'icon': 'menusynch.ico',   'extra': True}, (   (_('After Pull Operation'), 'tortoisehg.postpull',   ['none', 'update', 'fetch', 'rebase'], - _('Operation which is performed directly after a successful pull.' - ' update equates to pull --update, fetch equates to the fetch' - ' extension, rebase equates to pull --rebase. Default: none')), + _('Operation which is performed directly after a successful pull. ' + 'update equates to pull --update, fetch equates to the fetch ' + 'extension, rebase equates to pull --rebase. Default: none')),   )),    ({'name': 'web', 'label': _('Web Server'), 'icon': 'proxy.ico'}, (   (_('Name'), 'web.name', ['unknown'], - _('Repository name to use in the web interface.' - ' Default is the working directory.')), + _('Repository name to use in the web interface. ' + 'Default is the working directory.')),   (_('Description'), 'web.description', ['unknown'], - _("Textual description of the repository's purpose or" - ' contents.')), + _("Textual description of the repository's purpose or " + 'contents.')),   (_('Contact'), 'web.contact', ['unknown'], - _('Name or email address of the person in charge of the' - ' repository.')), + _('Name or email address of the person in charge of the ' + 'repository.')),   (_('Style'), 'web.style',   ['paper', 'monoblue', 'coal', 'spartan', 'gitweb', 'old'],   _('Which template map style to use')),   (_('Archive Formats'), 'web.allow_archive', ['bz2', 'gz', 'zip'], - _('Comma separated list of archive formats allowed for' - ' downloading')), + _('Comma separated list of archive formats allowed for ' + 'downloading')),   (_('Port'), 'web.port', ['8000'], _('Port to listen on')),   (_('Push Requires SSL'), 'web.push_ssl', ['True', 'False'], - _('Whether to require that inbound pushes be transported' - ' over SSL to prevent password sniffing.')), + _('Whether to require that inbound pushes be transported ' + 'over SSL to prevent password sniffing.')),   (_('Stripes'), 'web.stripes', ['1', '0'], - _('How many lines a "zebra stripe" should span in multiline output.' - ' Default is 1; set to 0 to disable.')), + _('How many lines a "zebra stripe" should span in multiline output. ' + 'Default is 1; set to 0 to disable.')),   (_('Max Files'), 'web.maxfiles', ['10'],   _('Maximum number of files to list per changeset.')),   (_('Max Changes'), 'web.maxchanges', ['10'],   _('Maximum number of changes to list on the changelog.')),   (_('Allow Push'), 'web.allow_push', ['*'], - _('Whether to allow pushing to the repository. If empty or not' - ' set, push is not allowed. If the special value "*", any remote' - ' user can push, including unauthenticated users. Otherwise, the' - ' remote user must have been authenticated, and the authenticated' - ' user name must be present in this list (separated by whitespace' - ' or ","). The contents of the allow_push list are examined after' - ' the deny_push list.')), + _('Whether to allow pushing to the repository. If empty or not ' + 'set, push is not allowed. If the special value "*", any remote ' + 'user can push, including unauthenticated users. Otherwise, the ' + 'remote user must have been authenticated, and the authenticated ' + 'user name must be present in this list (separated by whitespace ' + 'or ","). The contents of the allow_push list are examined after ' + 'the deny_push list.')),   (_('Deny Push'), 'web.deny_push', ['*'], - _('Whether to deny pushing to the repository. If empty or not set,' - ' push is not denied. If the special value "*", all remote users' - ' are denied push. Otherwise, unauthenticated users are all' - ' denied, and any authenticated user name present in this list' - ' (separated by whitespace or ",") is also denied. The contents' - ' of the deny_push list are examined before the allow_push list.')), + _('Whether to deny pushing to the repository. If empty or not set, ' + 'push is not denied. If the special value "*", all remote users ' + 'are denied push. Otherwise, unauthenticated users are all ' + 'denied, and any authenticated user name present in this list ' + '(separated by whitespace or ",") is also denied. The contents ' + 'of the deny_push list are examined before the allow_push list.')),   (_('Encoding'), 'web.encoding', ['UTF-8'],   _('Character encoding name')),   )),    ({'name': 'proxy', 'label': _('Proxy'), 'icon': 'general.ico'}, (   (_('Host'), 'http_proxy.host', [], - _('Host name and (optional) port of proxy server, for' - ' example "myproxy:8000"')), + _('Host name and (optional) port of proxy server, for ' + 'example "myproxy:8000"')),   (_('Bypass List'), 'http_proxy.no', [], - _('Optional. Comma-separated list of host names that' - ' should bypass the proxy')), + _('Optional. Comma-separated list of host names that ' + 'should bypass the proxy')),   (_('User'), 'http_proxy.user', [],   _('Optional. User name to authenticate with at the proxy server')),   (_('Password'), 'http_proxy.passwd', [], @@ -237,82 +237,82 @@
   ({'name': 'email', 'label': _('Email'), 'icon': gtk.STOCK_GOTO_LAST}, (   (_('From'), 'email.from', [], - _('Email address to use in the "From" header and for' - ' the SMTP envelope')), + _('Email address to use in the "From" header and for ' + 'the SMTP envelope')),   (_('To'), 'email.to', [],   _('Comma-separated list of recipient email addresses')),   (_('Cc'), 'email.cc', [],   _('Comma-separated list of carbon copy recipient email addresses')),   (_('Bcc'), 'email.bcc', [], - _('Comma-separated list of blind carbon copy recipient' - ' email addresses')), + _('Comma-separated list of blind carbon copy recipient ' + 'email addresses')),   (_('method'), 'email.method', ['smtp'], - _('Optional. Method to use to send email messages. If value is' - ' "smtp" (default), use SMTP (configured below). Otherwise, use as' - ' name of program to run that acts like sendmail (takes "-f" option' - ' for sender, list of recipients on command line, message on stdin).' - ' Normally, setting this to "sendmail" or "/usr/sbin/sendmail"' - ' is enough to use sendmail to send messages.')), + _('Optional. Method to use to send email messages. If value is ' + '"smtp" (default), use SMTP (configured below). Otherwise, use as ' + 'name of program to run that acts like sendmail (takes "-f" option ' + 'for sender, list of recipients on command line, message on stdin). ' + 'Normally, setting this to "sendmail" or "/usr/sbin/sendmail" ' + 'is enough to use sendmail to send messages.')),   (_('SMTP Host'), 'smtp.host', [], _('Host name of mail server')),   (_('SMTP Port'), 'smtp.port', ['25'], - _('Port to connect to on mail server.' - ' Default: 25')), + _('Port to connect to on mail server. ' + 'Default: 25')),   (_('SMTP TLS'), 'smtp.tls', ['False', 'True'], - _('Connect to mail server using TLS.' - ' Default: False')), + _('Connect to mail server using TLS. ' + 'Default: False')),   (_('SMTP Username'), 'smtp.username', [],   _('Username to authenticate to mail server with')),   (_('SMTP Password'), 'smtp.password', [],   _('Password to authenticate to mail server with')),   (_('Local Hostname'), 'smtp.local_hostname', [], - _('Hostname the sender can use to identify itself to the' - ' mail server.')), + _('Hostname the sender can use to identify itself to the ' + 'mail server.')),   )),    ({'name': 'diff', 'label': _('Diff'), 'icon': gtk.STOCK_JUSTIFY_FILL}, (   (_('Patch EOL'), 'patch.eol', ['auto', 'strict', 'crlf', 'lf'], - _('Normalize file line endings during and after patch to lf or' - ' crlf. Strict does no normalization. Auto does per-file' - ' detection, and is the recommended setting.' - ' Default: strict')), + _('Normalize file line endings during and after patch to lf or ' + 'crlf. Strict does no normalization. Auto does per-file ' + 'detection, and is the recommended setting. ' + 'Default: strict')),   (_('Git Format'), 'diff.git', ['False', 'True'], - _('Use git extended diff header format.' - ' Default: False')), + _('Use git extended diff header format. ' + 'Default: False')),   (_('No Dates'), 'diff.nodates', ['False', 'True'], - _('Do not include modification dates in diff headers.' - ' Default: False')), + _('Do not include modification dates in diff headers. ' + 'Default: False')),   (_('Show Function'), 'diff.showfunc', ['False', 'True'], - _('Show which function each change is in.' - ' Default: False')), + _('Show which function each change is in. ' + 'Default: False')),   (_('Ignore White Space'), 'diff.ignorews', ['False', 'True'], - _('Ignore white space when comparing lines.' - ' Default: False')), + _('Ignore white space when comparing lines. ' + 'Default: False')),   (_('Ignore WS Amount'), 'diff.ignorewsamount', ['False', 'True'], - _('Ignore changes in the amount of white space.' - ' Default: False')), + _('Ignore changes in the amount of white space. ' + 'Default: False')),   (_('Ignore Blank Lines'), 'diff.ignoreblanklines', ['False', 'True'], - _('Ignore changes whose lines are all blank.' - ' Default: False')), + _('Ignore changes whose lines are all blank. ' + 'Default: False')),   (_('Coloring Style'), 'tortoisehg.diffcolorstyle',   ['none', 'foreground', 'background'], - _('Adjust the coloring style of diff lines in the changeset' - ' viewer. Default: foreground')), + _('Adjust the coloring style of diff lines in the changeset ' + 'viewer. Default: foreground')),   )),    ({'name': 'font', 'label': _('Font'), 'icon': gtk.STOCK_SELECT_FONT,   'extra': True, 'width': 16}, (   (_('Commit Message'), 'gtools.fontcomment', [], - _('Font used in changeset viewer and commit log text.' - ' Default: monospace 10')), + _('Font used in changeset viewer and commit log text. ' + 'Default: monospace 10')),   (_('Diff Text'), 'gtools.fontdiff', [], - _('Font used for diffs in status and commit tools.' - ' Default: monospace 10')), + _('Font used for diffs in status and commit tools. ' + 'Default: monospace 10')),   (_('File List'), 'gtools.fontlist', [], - _('Font used in file lists in status and commit tools.' - ' Default: sans 9')), + _('Font used in file lists in status and commit tools. ' + 'Default: sans 9')),   (_('Command Output'), 'gtools.fontlog', [], - _('Font used in command output window.' - ' Default: monospace 10')), + _('Font used in command output window. ' + 'Default: monospace 10')),   )),    ({'name': 'extensions', 'label': _('Extensions'), 'icon': gtk.STOCK_EXECUTE,
 
158
159
160
161
 
162
163
164
 
158
159
160
 
161
162
163
164
@@ -158,7 +158,7 @@
  dialog = gtklib.MessageDialog(flags=gtk.DIALOG_MODAL)   dialog.set_title(_('Shelve'))   dialog.set_markup(_('<b>Shelve file exists!</b>')) - dialog.add_buttons(_('Overwrite'), 1, + dialog.add_buttons(_('Replace'), 1,   _('Append'), 2,   _('Cancel'), -1)   dialog.set_transient_for(self)
 
86
87
88
89
90
 
 
91
92
93
 
109
110
111
112
113
 
 
114
115
116
 
212
213
214
215
216
 
 
217
218
219
 
86
87
88
 
 
89
90
91
92
93
 
109
110
111
 
 
112
113
114
115
116
 
212
213
214
 
 
215
216
217
218
219
@@ -86,8 +86,8 @@
  self.expander.connect('notify::expanded', self.options_expanded)     ### force option (fixed) - self.forceopt = gtk.CheckButton(_('Discard local changes, no backup' - ' (-f/--force)')) + self.forceopt = gtk.CheckButton(_('Discard local changes, no backup ' + '(-f/--force)'))   table.add_row(self.expander, self.forceopt)     # signal handlers @@ -109,8 +109,8 @@
  self.butable.add_row(None, radio, ypad=0)   return radio   self.buopt_all = add_type(_('Backup all (default)')) - self.buopt_part = add_type(_('Backup unrelated changesets' - ' (-b/--backup)')) + self.buopt_part = add_type(_('Backup unrelated changesets ' + '(-b/--backup)'))   self.buopt_none = add_type(_('No backup (-n/--nobackup)'))     # layout group @@ -212,8 +212,8 @@
  else:   if not isclean():   ret = gdialog.CustomPrompt(_('Confirm Strip'), - _('Detected uncommitted local changes.\nDo' - ' you want to discard them and continue?'), + _('Detected uncommitted local changes.\nDo ' + 'you want to discard them and continue?'),   self, (_('&Yes (--force)'), _('&No')),   default=1, esc=1).run()   if ret == 0:
 
367
368
369
370
371
 
 
372
373
374
 
367
368
369
 
 
370
371
372
373
374
@@ -367,8 +367,8 @@
  self.copies = cpy   self.ui = repo.ui   - lbl = gtk.Label(_('Temporary files are removed when this dialog' - ' is closed')) + lbl = gtk.Label(_('Temporary files are removed when this dialog ' + 'is closed'))   self.vbox.pack_start(lbl, False, False, 2)     scroller = gtk.ScrolledWindow()
 
176
177
178
179
 
180
181
182
 
176
177
178
 
179
180
181
182
@@ -176,7 +176,7 @@
  return type, rawpath.split('/', 1)[-1]     files = {} - pf = open(self._path) + pf = open(self._path, 'rb')   try:   try:   # consume comments and headers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
 
 
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
 
 
 
 
183
184
185
 
 
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
 # prej.py - mpatch functionality hacked into TortoiseHg  #  # Copyright 2006 Brendan Cully <brendan@kublai.com>  # Copyright 2006 Chris Mason <chris.mason@oracle.com>  # Copyright 2010 Steve Borho <steve@borho.org>  #  # This software may be used and distributed according to the terms  # of the GNU General Public License, incorporated herein by reference.    # This file is an amalgam of two files from the mpatch tool source:  # mpatch/patch.py - which started life as Mercurial's patch.py  # cmd/mpatch - mpatch's run script  #  # Where possible, it has been tied back into the current Mercurial  # sources.    from mercurial.i18n import _  from mercurial import demandimport  demandimport.disable()  from mercurial import base85, util, diffhelpers  from mercurial.patch import PatchError, copyfile  demandimport.enable()    import cStringIO, os, re, zlib  import bisect    # public functions    GP_PATCH = 1 << 0 # we have to run patch  GP_FILTER = 1 << 1 # there's some copy/rename operation  GP_BINARY = 1 << 2 # there's a binary patch    def readgitpatch(fp, firstline):   """extract git-style metadata about patches from <patchname>"""   class gitpatch:   "op is one of ADD, DELETE, RENAME, MODIFY or COPY"   def __init__(self, path):   self.path = path   self.oldpath = None   self.mode = None   self.op = 'MODIFY'   self.copymod = False   self.lineno = 0   self.binary = False     def reader(fp, firstline):   yield firstline   for line in fp:   yield line     # Filter patch for git information   gitre = re.compile('diff --git a/(.*) b/(.*)')   gitpatches = []   # Can have a git patch with only metadata, causing patch to complain   dopatch = 0   gp = None     lineno = 0   for line in reader(fp, firstline):   lineno += 1   if line.startswith('diff --git'):   m = gitre.match(line)   if m:   if gp:   gitpatches.append(gp)   src, dst = m.group(1, 2)   gp = gitpatch(dst)   gp.lineno = lineno   elif gp:   if line.startswith('--- '):   if gp.op in ('COPY', 'RENAME'):   gp.copymod = True   dopatch |= GP_FILTER   gitpatches.append(gp)   gp = None   dopatch |= GP_PATCH   continue   if line.startswith('rename from '):   gp.op = 'RENAME'   gp.oldpath = line[12:].rstrip()   elif line.startswith('rename to '):   gp.path = line[10:].rstrip()   elif line.startswith('copy from '):   gp.op = 'COPY'   gp.oldpath = line[10:].rstrip()   elif line.startswith('copy to '):   gp.path = line[8:].rstrip()   elif line.startswith('deleted file'):   gp.op = 'DELETE'   elif line.startswith('new file mode '):   gp.op = 'ADD'   gp.mode = int(line.rstrip()[-3:], 8)   elif line.startswith('new mode '):   gp.mode = int(line.rstrip()[-3:], 8)   elif line.startswith('GIT binary patch'):   dopatch |= GP_BINARY   gp.binary = True   if gp:   gitpatches.append(gp)     if not gitpatches:   dopatch = GP_PATCH     return (dopatch, gitpatches)    # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1  unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')  contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')    class patchfile:   def __init__(self, ui, fname):   self.fname = fname   self.ui = ui   try:   fp = file(fname, 'rb')   self.lines = fp.readlines()   self.exists = True   except IOError:   dirname = os.path.dirname(fname)   if dirname and not os.path.isdir(dirname):   dirs = dirname.split(os.path.sep)   d = ""   for x in dirs:   d = os.path.join(d, x)   if not os.path.isdir(d):   os.mkdir(d)   self.lines = []   self.exists = False     self.hash = {}   self.dirty = 0   self.offset = 0   self.rej = []   self.fileprinted = False   self.printfile(False)   self.hunks = 0     def printfile(self, warn):   if self.fileprinted:   return   if warn or self.ui.verbose:   self.fileprinted = True   s = _("patching file %s\n") % self.fname   if warn:   self.ui.warn(s)   else:   self.ui.note(s)       def findlines(self, l, linenum):   # looks through the hash and finds candidate lines. The   # result is a list of line numbers sorted based on distance   # from linenum   def sorter(a, b):   vala = abs(a - linenum)   valb = abs(b - linenum)   return cmp(vala, valb)     try:   cand = self.hash[l]   except:   return []     if len(cand) > 1:   # resort our list of potentials forward then back.   cand.sort(cmp=sorter)   return cand     def hashlines(self):   self.hash = {}   for x in xrange(len(self.lines)):   s = self.lines[x]   self.hash.setdefault(s, []).append(x)     def write_rej(self):   # our rejects are a little different from patch(1). This always   # creates rejects in the same form as the original patch. A file   # header is inserted so that you can run the reject through patch again   # without having to type the filename.     if not self.rej:   return - if self.hunks != 1: - hunkstr = "s" - else: - hunkstr = ""     fname = self.fname + ".rej"   self.ui.warn( - _("%d out of %d hunk%s FAILED -- saving rejects to file %s\n") % - (len(self.rej), self.hunks, hunkstr, fname)) + _("%d out of %d hunks FAILED -- saving rejects to file %s\n") % + (len(self.rej), self.hunks, fname))   try: os.unlink(fname)   except:   pass   fp = file(fname, 'wb')   base = os.path.basename(self.fname)   fp.write("--- %s\n+++ %s\n" % (base, base))   for x in self.rej:   for l in x.hunk:   fp.write(l)   if l[-1] != '\n':   fp.write("\n\ No newline at end of file\n")     def write(self, dest=None):   if self.dirty:   if not dest:   dest = self.fname   st = None   try:   st = os.lstat(dest)   if st.st_nlink > 1:   os.unlink(dest)   except: pass   fp = file(dest, 'wb')   if st:   os.chmod(dest, st.st_mode)   fp.writelines(self.lines)   fp.close()     def close(self):   self.write()   self.write_rej()     def apply(self, h, reverse):   if not h.complete():   raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %   (h.number, h.desc, len(h.a), h.lena, len(h.b),   h.lenb))     self.hunks += 1   if reverse:   h.reverse()     if self.exists and h.createfile():   self.ui.warn(_("file %s already exists\n") % self.fname)   self.rej.append(h)   return -1     if isinstance(h, binhunk):   if h.rmfile():   os.unlink(self.fname)   else:   self.lines[:] = h.new()   self.offset += len(h.new())   self.dirty = 1   return 0     # fast case first, no offsets, no fuzz   old = h.old()   # patch starts counting at 1 unless we are adding the file   if h.starta == 0:   start = 0   else:   start = h.starta + self.offset - 1   orig_start = start   if diffhelpers.testhunk(old, self.lines, start) == 0:   if h.rmfile():   os.unlink(self.fname)   else:   self.lines[start : start + h.lena] = h.new()   self.offset += h.lenb - h.lena   self.dirty = 1   return 0     # ok, we couldn't match the hunk. Lets look for offsets and fuzz it   self.hashlines()   if h.hunk[-1][0] != ' ':   # if the hunk tried to put something at the bottom of the file   # override the start line and use eof here   search_start = len(self.lines)   else:   search_start = orig_start     for fuzzlen in xrange(3):   for toponly in [ True, False ]:   old = h.old(fuzzlen, toponly)     cand = self.findlines(old[0][1:], search_start)   for l in cand:   if diffhelpers.testhunk(old, self.lines, l) == 0:   newlines = h.new(fuzzlen, toponly)   self.lines[l : l + len(old)] = newlines   self.offset += len(newlines) - len(old)   self.dirty = 1   offset = l - orig_start - fuzzlen   if fuzzlen:   msg = _("Hunk #%d succeeded at %d "   "with fuzz %d "   "(offset %d lines).\n")   self.printfile(True)   self.ui.warn(msg %   (h.number, l + 1, fuzzlen, offset))   else:   msg = _("Hunk #%d succeeded at %d "   "(offset %d lines).\n")   self.ui.note(msg % (h.number, l + 1, offset))   return fuzzlen   self.printfile(True)   self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))   self.rej.append(h)   return -1    class hunk:   def __init__(self, desc, num, lr, context):   self.number = num   self.desc = desc   self.hunk = [ desc ]   self.a = []   self.b = []   if context:   self.read_context_hunk(lr)   else:   self.read_unified_hunk(lr)     def read_unified_hunk(self, lr):   m = unidesc.match(self.desc)   if not m:   raise PatchError(_("bad hunk #%d") % self.number)   self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()   if self.lena == None:   self.lena = 1   else:   self.lena = int(self.lena)   if self.lenb == None:   self.lenb = 1   else:   self.lenb = int(self.lenb)   self.starta = int(self.starta)   self.startb = int(self.startb)   diffhelpers.addlines(lr.fp, self.hunk, self.lena, self.lenb, self.a, self.b)   # if we hit eof before finishing out the hunk, the last line will   # be zero length. Lets try to fix it up.   while len(self.hunk[-1]) == 0:   del self.hunk[-1]   del self.a[-1]   del self.b[-1]   self.lena -= 1   self.lenb -= 1     def read_context_hunk(self, lr):   self.desc = lr.readline()   m = contextdesc.match(self.desc)   if not m:   raise PatchError(_("bad hunk #%d") % self.number)   foo, self.starta, foo2, aend, foo3 = m.groups()   self.starta = int(self.starta)   if aend == None:   aend = self.starta   self.lena = int(aend) - self.starta   if self.starta:   self.lena += 1   for x in xrange(self.lena):   l = lr.readline()   if l.startswith('---'):   lr.push(l)   break   s = l[2:]   if l.startswith('- ') or l.startswith('! '):   u = '-' + s   elif l.startswith(' '):   u = ' ' + s   else:   raise PatchError(_("bad hunk #%d old text line %d") %   (self.number, x))   self.a.append(u)   self.hunk.append(u)     l = lr.readline()   if l.startswith('\ '):   s = self.a[-1][:-1]   self.a[-1] = s   self.hunk[-1] = s   l = lr.readline()   m = contextdesc.match(l)   if not m:   raise PatchError(_("bad hunk #%d") % self.number)   foo, self.startb, foo2, bend, foo3 = m.groups()   self.startb = int(self.startb)   if bend == None:   bend = self.startb   self.lenb = int(bend) - self.startb   if self.startb:   self.lenb += 1   hunki = 1   for x in xrange(self.lenb):   l = lr.readline()   if l.startswith('\ '):   s = self.b[-1][:-1]   self.b[-1] = s   self.hunk[hunki-1] = s   continue   if not l:   lr.push(l)   break   s = l[2:]   if l.startswith('+ ') or l.startswith('! '):   u = '+' + s   elif l.startswith(' '):   u = ' ' + s   elif len(self.b) == 0:   # this can happen when the hunk does not add any lines   lr.push(l)   break   else:   raise PatchError(_("bad hunk #%d old text line %d") %   (self.number, x))   self.b.append(s)   while True:   if hunki >= len(self.hunk):   h = ""   else:   h = self.hunk[hunki]   hunki += 1   if h == u:   break   elif h.startswith('-'):   continue   else:   self.hunk.insert(hunki-1, u)   break     if not self.a:   # this happens when lines were only added to the hunk   for x in self.hunk:   if x.startswith('-') or x.startswith(' '):   self.a.append(x)   if not self.b:   # this happens when lines were only deleted from the hunk   for x in self.hunk:   if x.startswith('+') or x.startswith(' '):   self.b.append(x[1:])   # @@ -start,len +start,len @@   self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,   self.startb, self.lenb)   self.hunk[0] = self.desc     def reverse(self):   origlena = self.lena   origstarta = self.starta   self.lena = self.lenb   self.starta = self.startb   self.lenb = origlena   self.startb = origstarta   self.a = []   self.b = []   # self.hunk[0] is the @@ description   for x in xrange(1, len(self.hunk)):   o = self.hunk[x]   if o.startswith('-'):   n = '+' + o[1:]   self.b.append(o[1:])   elif o.startswith('+'):   n = '-' + o[1:]   self.a.append(n)   else:   n = o   self.b.append(o[1:])   self.a.append(o)   self.hunk[x] = o     def fix_newline(self):   diffhelpers.fix_newline(self.hunk, self.a, self.b)     def complete(self):   return len(self.a) == self.lena and len(self.b) == self.lenb     def createfile(self):   return self.starta == 0 and self.lena == 0     def rmfile(self):   return self.startb == 0 and self.lenb == 0     def fuzzit(self, l, fuzz, toponly):   # this removes context lines from the top and bottom of list 'l'. It   # checks the hunk to make sure only context lines are removed, and then   # returns a new shortened list of lines.   fuzz = min(fuzz, len(l)-1)   if fuzz:   top = 0   bot = 0   hlen = len(self.hunk)   for x in xrange(hlen-1):   # the hunk starts with the @@ line, so use x+1   if self.hunk[x+1][0] == ' ':   top += 1   else:   break   if not toponly:   for x in xrange(hlen-1):   if self.hunk[hlen-bot-1][0] == ' ':   bot += 1   else:   break     # top and bot now count context in the hunk   # adjust them if either one is short   context = max(top, bot, 3)   if bot < context:   bot = max(0, fuzz - (context - bot))   else:   bot = min(fuzz, bot)   if top < context:   top = max(0, fuzz - (context - top))   else:   top = min(fuzz, top)     return l[top:len(l)-bot]   return l     def old(self, fuzz=0, toponly=False):   return self.fuzzit(self.a, fuzz, toponly)     def newctrl(self):   res = []   for x in self.hunk:   c = x[0]   if c == ' ' or c == '+':   res.append(x)   return res     def new(self, fuzz=0, toponly=False):   return self.fuzzit(self.b, fuzz, toponly)    class binhunk:   'A binary patch file. Only understands literals so far.'   def __init__(self, gitpatch):   self.gitpatch = gitpatch   self.text = None   self.hunk = ['GIT binary patch\n']     def createfile(self):   return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')     def rmfile(self):   return self.gitpatch.op == 'DELETE'     def complete(self):   return self.text is not None     def new(self):   return [self.text]     def extract(self, fp):   line = fp.readline()   self.hunk.append(line)   while line and not line.startswith('literal '):   line = fp.readline()   self.hunk.append(line)   if not line:   raise PatchError(_('could not extract binary patch'))   size = int(line[8:].rstrip())   dec = []   line = fp.readline()   self.hunk.append(line)   while len(line) > 1:   l = line[0]   if l <= 'Z' and l >= 'A':   l = ord(l) - ord('A') + 1   else:   l = ord(l) - ord('a') + 27   dec.append(base85.b85decode(line[1:-1])[:l])   line = fp.readline()   self.hunk.append(line)   text = zlib.decompress(''.join(dec))   if len(text) != size:   raise PatchError(_('binary patch is %d bytes, not %d') %   len(text), size)   self.text = text    def parsefilename(str, git=False):   # --- filename \t|space stuff   if git:   return s   s = str[4:]   i = s.find('\t')   if i < 0:   i = s.find(' ')   if i < 0:   return s   return s[:i]    def selectfile(afile_orig, bfile_orig, hunk, strip, reverse):   def pathstrip(path, count=1):   pathlen = len(path)   i = 0   if count == 0:   return path.rstrip()   while count > 0:   i = path.find('/', i)   if i == -1:   raise PatchError(_("unable to strip away %d dirs from %s") %   (count, path))   i += 1   # consume '//' in the path   while i < pathlen - 1 and path[i] == '/':   i += 1   count -= 1   return path[i:].rstrip()     nulla = afile_orig == "/dev/null"   nullb = bfile_orig == "/dev/null"   afile = pathstrip(afile_orig, strip)   gooda = os.path.exists(afile) and not nulla   bfile = pathstrip(bfile_orig, strip)   if afile == bfile:   goodb = gooda   else:   goodb = os.path.exists(bfile) and not nullb   createfunc = hunk.createfile   if reverse:   createfunc = hunk.rmfile   if not goodb and not gooda and not createfunc():   raise PatchError(_("unable to find %s or %s for patching") %   (afile, bfile))   if gooda and goodb:   fname = bfile   if afile in bfile:   fname = afile   elif gooda:   fname = afile   elif not nullb:   fname = bfile   if afile in bfile:   fname = afile   elif not nulla:   fname = afile   return fname    class linereader:   # simple class to allow pushing lines back into the input stream   def __init__(self, fp):   self.fp = fp   self.buf = []     def push(self, line):   self.buf.append(line)     def readline(self):   if self.buf:   l = self.buf[0]   del self.buf[0]   return l   return self.fp.readline()    def applydiff(ui, fp, changed, strip=1, sourcefile=None, reverse=False,   updatedir=None):   """reads a patch from fp and tries to apply it. The dict 'changed' is   filled in with all of the filenames changed by the patch. Returns 0   for a clean patch, -1 if any rejects were found and 1 if there was   any fuzz."""     def scangitpatch(fp, firstline, cwd=None):   '''git patches can modify a file, then copy that file to   a new file, but expect the source to be the unmodified form.   So we scan the patch looking for that case so we can do   the copies ahead of time.'''     pos = 0   try:   pos = fp.tell()   except IOError:   fp = cStringIO.StringIO(fp.read())     (dopatch, gitpatches) = readgitpatch(fp, firstline)   for gp in gitpatches:   if gp.copymod:   copyfile(gp.oldpath, gp.path, basedir=cwd)     fp.seek(pos)     return fp, dopatch, gitpatches     current_hunk = None   current_file = None   afile = ""   bfile = ""   state = None   hunknum = 0   rejects = 0     git = False   gitre = re.compile('diff --git (a/.*) (b/.*)')     # our states   BFILE = 1   err = 0   context = None   lr = linereader(fp)   dopatch = True   gitworkdone = False     while True:   newfile = False   x = lr.readline()   if not x:   break   if current_hunk:   if x.startswith('\ '):   current_hunk.fix_newline()   ret = current_file.apply(current_hunk, reverse)   if ret > 0:   err = 1   current_hunk = None   gitworkdone = False   if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or   ((context or context == None) and x.startswith('***************')))):   try:   if context == None and x.startswith('***************'):   context = True   current_hunk = hunk(x, hunknum + 1, lr, context)   except PatchError:   current_hunk = None   continue   hunknum += 1   if not current_file:   if sourcefile:   current_file = patchfile(ui, sourcefile)   else:   current_file = selectfile(afile, bfile, current_hunk,   strip, reverse)   current_file = patchfile(ui, current_file)   changed.setdefault(current_file.fname, (None, None))   elif state == BFILE and x.startswith('GIT binary patch'):   current_hunk = binhunk(changed[bfile[2:]][1])   if not current_file:   if sourcefile:   current_file = patchfile(ui, sourcefile)   else:   current_file = selectfile(afile, bfile, current_hunk,   strip, reverse)   current_file = patchfile(ui, current_file)   hunknum += 1   current_hunk.extract(fp)   elif x.startswith('diff --git'):   # check for git diff, scanning the whole patch file if needed   m = gitre.match(x)   if m:   afile, bfile = m.group(1, 2)   if not git:   git = True   fp, dopatch, gitpatches = scangitpatch(fp, x)   for gp in gitpatches:   changed[gp.path] = (gp.op, gp)   # else error?   # copy/rename + modify should modify target, not source   if changed.get(bfile[2:], (None, None))[0] in ('COPY',   'RENAME'):   afile = bfile   gitworkdone = True   newfile = True   elif x.startswith('---'):   # check for a unified diff   l2 = lr.readline()   if not l2.startswith('+++'):   lr.push(l2)   continue   newfile = True   context = False   afile = parsefilename(x, git)   bfile = parsefilename(l2, git)   elif x.startswith('***'):   # check for a context diff   l2 = lr.readline()   if not l2.startswith('---'):   lr.push(l2)   continue   l3 = lr.readline()   lr.push(l3)   if not l3.startswith("***************"):   lr.push(l2)   continue   newfile = True   context = True   afile = parsefilename(x, git)   bfile = parsefilename(l2, git)     if newfile:   if current_file:   current_file.close()   rejmerge(current_file)   rejects += len(current_file.rej)   state = BFILE   current_file = None   hunknum = 0   if current_hunk:   if current_hunk.complete():   ret = current_file.apply(current_hunk, reverse)   if ret > 0:   err = 1   else:   fname = current_file and current_file.fname or None   raise PatchError(_("malformed patch %s %s") % (fname,   current_hunk.desc))   if current_file:   current_file.close()   rejmerge(current_file)   rejects += len(current_file.rej)   if updatedir and git:   updatedir(gitpatches)   if rejects:   return -1   if hunknum == 0 and dopatch and not gitworkdone:   raise PatchError(_("No valid hunks found"))   return err      # This portion of the code came from the mpatch script    global_conflicts = 0  global_rejects = 0    wsre = re.compile('[ \r\t\n]+')    class rejhunk:   def __init__(self, h, f):   self.hunk = h   self.match = []   self.score = 0   self.start = 0   self.conficts = 0   self.file = f   self.direction = False     def findline(self, l, min):   # look for line l in the file's hash. min is the minimum line number   # to return   try:   res = self.file.hash[l]   except KeyError:   return None   i = bisect.bisect_right(res, min)   if i < len(res):   return res[i]   return None     def findforward(self):   # search for the new text of the hunk already in the file.   # the file should already be hashed   hlines = self.hunk.newctrl()   orig_start = self.hunk.startb   if len(hlines) < 6:   # only guess about applied hunks when there is some context.   return False   for fuzzlen in xrange(3):   lines = self.hunk.fuzzit(hlines, fuzzlen, False)   cand = self.file.findlines(lines[0][1:], orig_start)   for l in cand:   if diffhelpers.testhunk(lines, self.file.lines, l) == 0:   self.file.ui.warn(   _("hunk %d already applied at line %d (fuzz %d)\n"   % (self.hunk.number, l+1, fuzzlen)))   return True   return False     def search(self):   # search through the file for a good place to put our hunk.   # on return the rejhunk is setup for a call to apply. We will   # either have found a suitable location or have given up and   # set things up to put the hunk at the start of the file.   self.file.hashlines()   if self.findforward():   # this hunk was already applied   self.score = -2   return     scan = ((False, self.hunk.old(), self.hunk.starta),   (True, self.hunk.newctrl(), self.hunk.startb))   last = (-1, -1, 0, [], None)   for direction, hlines, hstart in scan:   maxlines = min(len(hlines)/2+1, 15)   # don't look forward if we already have a conflict free   # match of the old text   if direction and last[1] > 3 and last[2] == 0:   break   for i in xrange(maxlines):   try:   res = self.file.hash[hlines[i][1:]]   except KeyError:   continue   for s in res:   start, score, conflicts, match = self.align(s-1, i, hlines)     # new text matches are more likely to have false positives.   # use a higher bar for them.   if direction and score < 7:   score = -1     if score > last[1]:   update = True   # more special checks for replacing an match of the   # old text with the new. Check for conflicts   # and the size of the total match.   if direction and last[4] == False:   dist = len(match) - len(hlines)   if conflicts and dist > ((len(hlines) * 3) / 2):   update = False   if update:   last = (start, score, conflicts, match, direction)   elif score == last[1]:   distold = abs(last[0] - hstart)   distnew = abs(start - hstart)   if direction == False or last[4] == True:   # we prefer to match the old text, so if   # we don't replace a match of the old text   # with a match of the new   if distnew < distold:   last = (start, score, conflicts, match,   direction)   if last[1] > 3:   (self.start, self.score, self.conflicts,   self.match, self.direction) = last   else:   # no good locations found, lets just put it at the start of the   # file.   self.conflicts = 1     def scoreline(self, l):   ws = wsre.sub('', l)   if len(ws) == 0:   return .25   return 1     def apply(self):   # after calling search, call apply to merge the hunk into the file.   # on return the file is dirtied but not written   if self.direction:   # when merging into already applied data, the match array   # has it all   new = []   else:   new = self.hunk.newctrl()   newi = 0   filei = self.start   lines = []   i = 0   while i < len(self.match) or newi < len(new):   # the order is a little strange.   # ctrl of '|' means the file had lines the hunk did not. These   # need to go before any '+' lines from the new hunk   if i < len(self.match):   l = self.match[i]   ctrl = l[0]   if ctrl == '|':   lines.append(l[1:])   filei += 1   i += 1   continue     # find lines added by the hunk   if newi < len(new):   l = new[newi]   if l[0] == '+':   lines.append(l[1:])   newi += 1   continue   elif i >= len(self.match):   # if we've gone through the whole match array,   # the new hunk may have context that didn't exist   # in the file. Just skip past it.   newi += 1   continue     l = self.match[i]   i += 1   ctrl = l[0]   if ctrl == '-':   # deleted by the hunk, skip over it in the file   filei += 1   pass   elif ctrl == '^':   # in the hunk but missing from the file. skip over it in   # the hunk   newi += 1   elif ctrl == '<':   # deleted by the hunk but missing from the file. Let the   # user know by inserting the deletion marker   lines.append(l)   elif ctrl == '+':   # only happens when self.direction == True   lines.append(l[1:])   continue   elif ctrl == ' ':   # context from the hunk found in the file. Add it   lines.append(l[1:])   newi += 1   filei += 1   else:   raise PatchError("unknown control char %s" % l)     self.file.lines[self.start:filei] = lines   self.file.dirty = 1     def align(self, fstart, hstart, hlines):   hcur = hstart   fcur = fstart   flines = self.file.lines   retstart = None   match = []   score = 0   conflicts = 0     # add deletion markers for any lines removed by the parts of the   # hunk we could not find (between 0 and hstart)   for i in xrange(hstart):   if hlines[i][0] == '-':   match.append('<<<<del ' + hlines[i][1:])   elif hlines[i][0] == '+':   match.append(hlines[i])   consec = False   last_append = None   while hcur < len(hlines):   if hcur > len(hlines)/2 and score <= 0:   return (-1, -1, 1, [])   fnext = self.findline(hlines[hcur][1:], fcur)   ctrl = hlines[hcur][0]   if fnext == None or (fcur >=0 and fnext - fcur > 20):   consec = False   fnext = None   if ctrl == '-':   # we've failed to find a line the patch wanted to delete.   # record it as a conflict.   match.append('<<<<del ' + hlines[hcur][1:])   conflicts += 1   elif ctrl == '+':   match.append(hlines[hcur])   conflicts += 1   else:   match.append('^' + hlines[hcur][1:])   else:   if fcur >= 0 and retstart:   # any lines between fcur and fnext were in the file but   # not the hunk.   # decrement our score for a big block of missing lines   dist = fnext - fcur   while dist > 5:   score -= 1   dist -= 5   for x in xrange(fcur+1, fnext):   match.append('|' + flines[x])   if retstart == None:   retstart = fnext   # plus one just for finding a match   # plus one more if it was a consecutive match   # plus one more if it was a match on a line we're deleting   # or adding. But, only do it for non-ws lines and for   # lines relatlively close to the last match   inc = self.scoreline(hlines[hcur][1:])   score += inc   if consec and fnext == fcur + 1:   score += inc   if ctrl == '-' or ctrl == '+':   ws = hlines[hcur].rstrip()   if len(ws) > 1 and fnext - fcur < 5:   score += inc   consec = True   # record the line from the hunk we did find   if ctrl == '+':   # we matched a line added by the hunk. The code that   # merges needs to know to inc the file line number to   # include this line, so instead of adding a + ctrl   # use |   match.append('|' + hlines[hcur][1:])   else:   match.append(hlines[hcur])     hcur += 1   if fnext != None:   fcur = fnext   return (retstart, score, conflicts, match)    def rejmerge(pfile):   def backup(orig):   fname = orig + ".mergebackup"   try: os.unlink(fname)   except: pass   old = file(orig)   new = file(fname, 'w')   for x in old:   new.write(x)   return fname     rej = pfile.rej   if not rej:   return   backupf = None   badrej = []   for h in rej:   r = rejhunk(h, pfile)   r.search()   if r.score >= 0:   if not backupf:   backupf = backup(pfile.fname)   global global_conflicts   global global_rejects   if r.direction:   s = "warning file %s hunk %d: merging " % (   pfile.fname, h.number)   pfile.ui.warn(s + "with changes already applied\n")   r.apply()   if r.conflicts > 0:   global_conflicts += 1   global_rejects += 1   if backupf:   global merge_func   pfile.rej = badrej   pfile.write()   merge_func(pfile.ui, pfile.fname, backupf)   try:   os.unlink(backupf)   except:   pass    def updatedir(patches):   l = patches.keys()   l.sort()   for f in l:   ctype, gp = patches[f]   if gp.mode != None:   x = gp.mode & 0100 != 0   util.set_exec(gp.path, x)    global merge_func  def run(ui, rejfile, sourcefile, mergefunc):   '''   Attempt to apply the patch hunks in rejfile, When patch fails,   run merge_func to launch user's preferred visual diff tool to   resolve conflicts.   '''   global merge_func   merge_func = mergefunc   diffp = file(rejfile)   changed = {}   try:   ret = applydiff(ui, diffp, changed, strip=1,   sourcefile=sourcefile,   updatedir=updatedir,   reverse=False)   except PatchError, inst:   sys.stderr.write("Error: %s\n" % inst)