by
Changes to 34 files · Browse files at c24ea27f9b21 Showing diff from parent 4833809e1016 7a747b414310 Diff from another changeset...
@@ -6,6 +6,34 @@ what bugs have been fixed, and what features have been added in the current
version.
+New features and improvement since 0.6:
+
+ * Status window now supports shelving of selected changes.
+ Unknown files are now displayed by default, hg status convention.
+
+ * Commit window now supports commit of selected changes (record-style)
+ or shelving of selected changes.
+ Modified, Added, and Removed files are preselected for commit.
+ Added a ctrl-o key accelerator for initiating a commit.
+ Added support for refreshing an applied MQ patch.
+ Improved support for merge (two parent) commits.
+
+ * Synchronize window now supports bundle files (*.hg).
+ Recognizes whether it has been launched in push or pull mode
+
+ * New rename window for simple file or directory renames.
+
+ * New rename guessing dialog for detecting renames after they happen.
+
+ * New tool for managing an .hgignore file.
+
+ * The configuration window has been reorganized for simplicity.
+
+ * Nautilus: Fixed file version detection.
+
+ * Hgtk: added --limit option to log.
+ added aliases to dialogs that support many operations.
+ added rename and guess commands.
New features and improvement in 0.6:
|
|
@@ -18,11 +18,15 @@ import shlex
from mercurial.i18n import _
from mercurial import hg, util, fancyopts, commands, cmdutil
-from mercurial.repo import RepoError
import mercurial.ui as _ui
-class ParseError(Exception):
- """Exception raised on errors in parsing the command line."""
+try:
+ from mercurial.error import RepoError, UnknownCommand, AmbiguousCommand
+ from mercurial.error import ParseError
+except ImportError:
+ from mercurial.repo import RepoError
+ from mercurial.cmdutil import UnknownCommand, AmbiguousCommand
+ from mercurial.dispatch import ParseError
nonrepo_commands = 'userconfig clone init about help'
@@ -82,11 +86,11 @@ help_(ui, inst.args[0])
else:
ui.warn(_("hgtk: %s\n") % inst.args[1])
- help_('shortlist')
- except cmdutil.AmbiguousCommand, inst:
+ help_(ui, 'shortlist')
+ except AmbiguousCommand, inst:
ui.warn(_("hgtk: command '%s' is ambiguous:\n %s\n") %
(inst.args[0], " ".join(inst.args[1])))
- except cmdutil.UnknownCommand, inst:
+ except UnknownCommand, inst:
ui.warn(_("hgtk: unknown command '%s'\n") % inst.args[0])
help_(ui, 'shortlist')
except RepoError, inst:
@@ -204,9 +208,28 @@ opts['files'] = opts['root']
run(**opts)
+def rename(ui, *pats, **opts):
+ """rename a single file or directory"""
+ from hggtk.rename import run
+ if not pats or len(pats) > 2:
+ raise util.Abort("rename takes one or two path arguments")
+ try:
+ opts['fname'] = pats[0]
+ opts['target'] = pats[1] or pats[0]
+ except IndexError:
+ pass
+ run(**opts)
+
+def guess(ui, *pats, **opts):
+ """guess previous renames or copies"""
+ from hggtk.rename import run
+ opts['detect'] = True
+ run(**opts)
+
def datamine(ui, *pats, **opts):
"""repository search and annotate tool"""
from hggtk.datamine import run
+ opts['files'] = sys.argv[2:] or []
run(**opts)
def hginit(ui, dest=None, **opts):
@@ -250,6 +273,11 @@def synch(ui, **opts):
"""repository synchronization tool"""
from hggtk.synch import run
+ cmd = sys.argv[1]
+ if 'push'.startswith(cmd) or 'outgoing'.startswith(cmd):
+ opts['pushmode'] = True
+ else:
+ opts['pushmode'] = False
run(**opts)
def update(ui, **opts):
@@ -454,15 +482,19 @@ "^about": (about, [], _('hgtk about')),
"^clone": (clone, [], _('hgtk clone SOURCE [DEST]')),
"^commit|ci": (commit, [], _('hgtk commit [FILE]...')),
- "^datamine": (datamine, [], _('hgtk datamine')),
+ "^datamine|annotate|blame": (datamine, [], _('hgtk datamine')),
"^init": (hginit, [], _('hgtk init [DEST]')),
- "^log|history": (log, [], _('hgtk log [FILE]')),
+ "^log|history": (log,
+ [('l', 'limit', '', _('limit number of changes displayed'))],
+ _('hgtk log [OPTIONS] [FILE]')),
"^merge": (merge, [], _('hgtk merge')),
- "^recovery": (recovery, [], _('hgtk recovery')),
- "^synch": (synch, [], _('hgtk synch')),
+ "^recovery|rollback|verify": (recovery, [], _('hgtk recovery')),
+ "^synch|pull|push|incoming|outgoing|email": (synch, [], _('hgtk synch')),
"^status|st": (status, [], _('hgtk status [FILE]...')),
"^userconfig": (userconfig, [], _('hgtk userconfig')),
"^repoconfig": (repoconfig, [], _('hgtk repoconfig')),
+ "^guess": (guess, [], _('hgtk guess')),
+ "^rename|mv": (rename, [], _('hgtk rename SOURCE [DEST]')),
"^serve":
(serve,
[('', 'webdir-conf', '', _('name of the webdir config file'))],
|
@@ -12,7 +12,7 @@ import gconf
import gtk
import gobject
-from mercurial import hg, ui, repo, match, util
+from mercurial import hg, ui, match, util
from mercurial.node import short
import nautilus
import os
@@ -21,6 +21,12 @@import tempfile
import time
import urllib
+
+try:
+ from mercurial.error import RepoError
+except ImportError:
+ from mercurial.repo import RepoError
+
TORTOISEHG_PATH = '~/tools/tortoisehg-dev'
TERMINAL_KEY = '/desktop/gnome/applications/terminal/exec'
@@ -71,7 +77,7 @@ self.cacheroot = p
self.cacherepo = hg.repository(ui.ui(), path=p)
return self.cacherepo
- except repo.RepoError:
+ except RepoError:
self.cacheroot = None
self.cacherepo = None
return None
@@ -507,7 +513,7 @@ ctx = repo.changectx(None).parents()[0]
try:
fctx = ctx.filectx(localpath)
- rev = fctx.filelog().linkrev(fctx.filenode())
+ rev = fctx.filelog().linkrev(fctx.filerev())
except:
rev = ctx.rev()
ctx = repo.changectx(rev)
|
@@ -14,8 +14,20 @@ import pango
import shlib
-import tortoise.version
-import mercurial.version
+try:
+ # post 1.1.2
+ from mercurial import util
+ hgversion = util.version()
+except AttributeError:
+ # <= 1.1.2
+ from mercurial import version
+ hgversion = version.get_version()
+
+try:
+ import tortoise.version
+ thgversion = tortoise.version.get_version()
+except ImportError:
+ thgversion = "unknown"
def browse_url(url):
import threading
@@ -46,7 +58,7 @@ super(AboutDialog, self).__init__()
lib_versions = ', '.join([
- "Mercurial-%s" % mercurial.version.get_version(),
+ "Mercurial-%s" % hgversion,
"Python-%s" % make_version(sys.version_info[0:3]),
"PyGTK-%s" % make_version(gtk.pygtk_version),
"GTK-%s" % make_version(gtk.gtk_version),
@@ -56,7 +68,7 @@
self.set_website("http://tortoisehg.sourceforge.net/")
self.set_name("TortoiseHg")
- self.set_version("(version %s)" % tortoise.version.get_version())
+ self.set_version("(version %s)" % thgversion)
if hasattr(self, 'set_wrap_license'):
self.set_wrap_license(True)
self.set_copyright("Copyright 2008 TK Soh and others")
|
@@ -16,15 +16,18 @@ import pango
import StringIO
-from mercurial.i18n import _
from mercurial.node import *
-from mercurial import cmdutil, util, ui, hg, commands
-from mercurial import context, patch, revlog
+from mercurial import cmdutil, context, util, ui, hg, patch
from gdialog import *
from hgcmd import CmdDialog
-from hglib import toutf, fromutf, displaytime
+from hglib import toutf, fromutf, displaytime, hgcmd_toq, diffexpand
from gtklib import StatusBar
+try:
+ from mercurial.error import LookupError
+except ImportError:
+ from mercurial.revlog import LookupError
+
class ChangeSet(GDialog):
"""GTK+ based dialog for displaying repository logs
"""
@@ -117,7 +120,6 @@ buf.insert_with_tags_by_name(eob, utext, tag)
buf.insert(eob, "\n")
- # TODO: Add toggle for gmtime/localtime
eob = buf.get_end_iter()
date = displaytime(ctx.date())
if self.clipboard:
@@ -269,7 +271,7 @@ if f in files:
try:
src = getfilectx(f, c).renamed()
- except revlog.LookupError:
+ except LookupError:
return None
if src:
f = src[0]
@@ -294,7 +296,7 @@ s = 'A'
ctx1.filectx(f)
s = 'M'
- except revlog.LookupError:
+ except LookupError:
pass
status[f] = s
return s
@@ -375,8 +377,6 @@
def prepare_diff(self, difflines, offset, fname):
'''Borrowed from hgview; parses changeset diffs'''
- import hglib
- tw = hglib.gettabwidth(self.ui)
DIFFHDR = "=== %s ===\n"
idx = 0
outlines = []
@@ -411,17 +411,14 @@ elif l.startswith("+"):
tag = "green"
stats[0] += 1
- if tw:
- l = l[0] + l[1:].expandtabs(tw)
+ l = diffexpand(l)
elif l.startswith("-"):
stats[1] += 1
tag = "red"
- if tw:
- l = l[0] + l[1:].expandtabs(tw)
+ l = diffexpand(l)
else:
tag = "black"
- if tw:
- l = l[0] + l[1:].expandtabs(tw)
+ l = diffexpand(l)
l = l+"\n"
length = len(l.decode('utf-8'))
addtag( tag, offset, length )
@@ -629,7 +626,7 @@ try:
fctx = ctx.filectx(self.curfile)
has_filelog = fctx.filelog().linkrev(fctx.filerev()) == ctx.rev()
- except revlog.LookupError:
+ except LookupError:
has_filelog = False
self._ann_menu.set_sensitive(has_filelog)
self._save_menu.set_sensitive(has_filelog)
@@ -651,10 +648,9 @@ result = fd.run()
if result:
import Queue
- import hglib
q = Queue.Queue()
cpath = util.canonpath(self.repo.root, self.cwd, self.curfile)
- hglib.hgcmd_toq(self.repo.root, q, 'cat', '--rev',
+ hgcmd_toq(self.repo.root, q, 'cat', '--rev',
str(self.currev), '--output', result, cpath)
def _view_file_rev(self, menuitem):
|
|
|
@@ -3,31 +3,24 @@ #
# Copyright 2007 Brad Schick, brad at gmail . com
# Copyright (C) 2007 TK Soh <teekaysoh@gmail.com>
+# Copyright (C) 2009 Steve Borho <steve@borho.org>
#
import os
-import threading
-import StringIO
-import sys
-import shutil
-import tempfile
-import datetime
-import cPickle
-
import pygtk
pygtk.require('2.0')
+import errno
import gtk
-import gobject
import pango
+import tempfile
+import cStringIO
from mercurial.i18n import _
from mercurial.node import *
-from mercurial import cmdutil, util, ui, hg, commands, patch
-from hgext import extdiff
+from mercurial import ui, hg
from shlib import shell_notify
from gdialog import *
-from gtools import cmdtable
-from status import GStatus
+from status import *
from hgcmd import CmdDialog
from hglib import fromutf
@@ -71,9 +64,9 @@
def auto_check(self):
if self.test_opt('check'):
- for entry in self.model :
- if entry[1] in 'MAR':
- entry[0] = True
+ for entry in self.filemodel :
+ if entry[FM_STATUS] in 'MAR':
+ entry[FM_CHECKED] = True
self._update_check_count()
@@ -96,9 +89,10 @@ tbbuttons.insert(2, gtk.SeparatorToolItem())
self._undo_button = self.make_toolbutton(gtk.STOCK_UNDO, '_Undo',
self._undo_clicked, tip='undo recent commit')
+ self._commit_button = self.make_toolbutton(gtk.STOCK_OK, '_Commit',
+ self._commit_clicked, tip='commit')
tbbuttons.insert(2, self._undo_button)
- tbbuttons.insert(2, self.make_toolbutton(gtk.STOCK_OK, '_Commit',
- self._commit_clicked, tip='commit'))
+ tbbuttons.insert(2, self._commit_button)
return tbbuttons
@@ -134,6 +128,12 @@ vbox = gtk.VBox()
mbox = gtk.HBox()
+
+ label = gtk.Label('Branch: ')
+ mbox.pack_start(label, False, False, 2)
+ self.branchentry = gtk.Entry()
+ mbox.pack_start(self.branchentry, False, False, 2)
+
label = gtk.Label('Recent Commit Messages: ')
mbox.pack_start(label, False, False, 2)
self.msg_cbbox = gtk.combo_box_new_text()
@@ -167,6 +167,12 @@ self._vpaned.add1(vbox)
self._vpaned.add2(status_body)
self._vpaned.set_position(self._setting_vpos)
+
+ # make ctrl-o trigger commit button
+ accel_group = gtk.AccelGroup()
+ self.add_accel_group(accel_group)
+ self._commit_button.add_accelerator("clicked", accel_group, ord("o"),
+ gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
return self._vpaned
@@ -190,16 +196,25 @@ # If there are more than a few character typed into the commit
# message, ask if the exit should continue.
live = False
- if self.text.get_buffer().get_char_count() > 10:
- dialog = Confirm('Exit', [], self, 'Discard commit message and exit?')
- if dialog.run() == gtk.RESPONSE_NO:
+ buffer = self.text.get_buffer()
+ begin, end = buffer.get_bounds()
+ cur_msg = buffer.get_text(begin, end)
+ if buffer.get_char_count() > 10 and cur_msg != self.qheader:
+ dialog = Confirm('Exit', [], self, 'Save commit message at exit?')
+ res = dialog.run()
+ if res == gtk.RESPONSE_YES:
+ self._update_recent_messages(cur_msg)
+ elif res != gtk.RESPONSE_NO:
live = True
return live
def reload_status(self):
+ if not self._ready: return False
success = GStatus.reload_status(self)
+ self.branchentry.set_text(self.repo.dirstate.branch())
self._check_merge()
+ self._check_patch_queue()
self._check_undo()
return success
@@ -215,24 +230,36 @@ def _check_merge(self):
# disable the checkboxes on the filelist if repo in merging state
merged = len(self.repo.changectx(None).parents()) > 1
- cbcell = self.tree.get_column(0).get_cell_renderers()[0]
- cbcell.set_property("activatable", not merged)
self.get_toolbutton('Re_vert').set_sensitive(not merged)
self.get_toolbutton('_Add').set_sensitive(not merged)
self.get_toolbutton('_Remove').set_sensitive(not merged)
+ self.get_toolbutton('Move').set_sensitive(not merged)
if merged:
# select all changes if repo is merged
- for entry in self.model:
- if entry[1] in 'MARD':
- entry[0] = True
+ for entry in self.filemodel:
+ if entry[FM_STATUS] in 'MARD':
+ entry[FM_CHECKED] = True
self._update_check_count()
# pre-fill commit message
self.text.get_buffer().set_text('merge')
+ def _check_patch_queue(self):
+ '''See if an MQ patch is applied, switch to qrefresh mode'''
+ self.qheader = None
+ if not hasattr(self.repo, 'mq'): return
+ if not self.repo.mq.applied: return
+ patch = self.repo.mq.lookup('qtip')
+ ph = self.repo.mq.readheaders(patch)
+ title = os.path.basename(self.repo.root) + ' qrefresh ' + patch
+ self.set_title(title)
+ self.qheader = '\n'.join(ph.message)
+ self.text.get_buffer().set_text(self.qheader)
+ self.get_toolbutton('_Commit').set_label('QRefresh')
+
def _commit_clicked(self, toolbutton, data=None):
if not self._ready_message():
return True
@@ -241,6 +268,7 @@ # as of Mercurial 1.0, merges must be committed without
# specifying file list.
self._hg_commit([])
+ self.reload_status()
else:
commitable = 'MAR'
addremove_list = self._relevant_files('?!')
@@ -249,11 +277,98 @@
commit_list = self._relevant_files(commitable)
if len(commit_list) > 0:
- self._hg_commit(commit_list)
+ self._commit_selected(commit_list)
+ return True
else:
Prompt('Nothing Commited', 'No committable files selected', self).run()
return True
+ def _commit_selected(self, files):
+ import hgshelve
+ # 1a. get list of chunks not rejected
+ hlist = [x[DM_CHUNK_ID] for x in self.diff_model if not x[DM_REJECTED]]
+ repo, chunks, ui = self.repo, self._shelve_chunks, self.ui
+
+ # 2. backup changed files, so we can restore them in the end
+ backups = {}
+ backupdir = repo.join('record-backups')
+ try:
+ os.mkdir(backupdir)
+ except OSError, err:
+ if err.errno != errno.EEXIST:
+ Prompt('Commit', 'Unable to create ' + backupdir,
+ self).run()
+ return
+ try:
+ # backup continues
+ for f in files:
+ if f not in self._filechunks: continue
+ if len(self._filechunks[f]) == 1: continue
+ if f not in self.modified: continue
+ fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
+ dir=backupdir)
+ os.close(fd)
+ ui.debug(_('backup %r as %r\n') % (f, tmpname))
+ util.copyfile(repo.wjoin(f), tmpname)
+ backups[f] = tmpname
+
+ fp = cStringIO.StringIO()
+ for n, c in enumerate(chunks):
+ if c.filename() in backups and n in hlist:
+ c.write(fp)
+ dopatch = fp.tell()
+ fp.seek(0)
+
+ if backups:
+ if self.qheader is not None:
+ # 3a. apply filtered patch to top patch's parent
+ hg.revert(repo, self._node1, backups.has_key)
+ else:
+ # 3a. apply filtered patch to clean repo (clean)
+ hg.revert(repo, repo.dirstate.parents()[0], backups.has_key)
+
+ # 3b. (apply)
+ if dopatch:
+ try:
+ ui.debug(_('applying patch\n'))
+ ui.debug(fp.getvalue())
+ pfiles = {}
+ patch.internalpatch(fp, ui, 1, repo.root, files=pfiles)
+ patch.updatedir(ui, repo, pfiles)
+ except patch.PatchError, err:
+ s = str(err)
+ if s:
+ raise util.Abort(s)
+ else:
+ Prompt('Commit', 'Unable to apply patch', self).run()
+ raise util.Abort(_('patch failed to apply'))
+ del fp
+
+ # 4. We prepared working directory according to filtered patch.
+ # Now is the time to delegate the job to commit/qrefresh or the like!
+
+ # it is important to first chdir to repo root -- we'll call a
+ # highlevel command with list of pathnames relative to repo root
+ cwd = os.getcwd()
+ os.chdir(repo.root)
+ try:
+ self._hg_commit(files)
+ finally:
+ os.chdir(cwd)
+
+ return 0
+ finally:
+ # 5. finally restore backed-up files
+ try:
+ for realname, tmpname in backups.iteritems():
+ ui.debug(_('restoring %r to %r\n') % (tmpname, realname))
+ util.copyfile(tmpname, repo.wjoin(realname))
+ os.unlink(tmpname)
+ os.rmdir(backupdir)
+ except OSError:
+ pass
+ self.reload_status()
+
def _commit_file(self, stat, file):
if self._ready_message():
@@ -280,8 +395,7 @@ self._last_commit_id = None
self.reload_status()
except:
- Prompt('Undo commit', 'Errors during rollback!',
- self).run()
+ Prompt('Undo commit', 'Errors during rollback!', self).run()
def _should_addremove(self, files):
@@ -329,10 +443,29 @@ self.ui = self.repo.ui
return
+ newbranch = fromutf(self.branchentry.get_text())
+ if newbranch != self.repo.dirstate.branch():
+ if newbranch in self.repo.branchtags():
+ if newbranch not in [p.branch() for p in self.repo.parents()]:
+ response = Confirm('Override Branch', [], self,
+ 'A branch named "%s" already exists,\n'
+ 'override?' % newbranch).run()
+ else:
+ response = gtk.RESPONSE_YES
+ else:
+ response = Confirm('New Branch', [], self,
+ 'Create new named branch "%s"?' % newbranch).run()
+ if response == gtk.RESPONSE_YES:
+ self.repo.dirstate.setbranch(newbranch)
+ elif response != gtk.RESPONSE_NO:
+ return
+
# call the threaded CmdDialog to do the commit, so the the large commit
# won't get locked up by potential large commit. CmdDialog will also
# display the progress of the commit operation.
- cmdline = ["hg", "commit", "--verbose", "--repository", self.repo.root]
+ cmdline = ['hg', 'commit', '--verbose', '--repository', self.repo.root]
+ if self.qheader is not None:
+ cmdline[1] = 'qrefresh'
if self.opts['addremove']:
cmdline += ['--addremove']
cmdline += ['--message', fromutf(self.opts['message'])]
@@ -344,11 +477,11 @@
# refresh overlay icons and commit dialog
if dialog.return_code() == 0:
- self.text.set_buffer(gtk.TextBuffer())
- self._update_recent_messages(self.opts['message'])
shell_notify([self.cwd] + files)
- self._last_commit_id = self._get_tip_rev(True)
- self.reload_status()
+ if self.qheader is None:
+ self.text.set_buffer(gtk.TextBuffer())
+ self._update_recent_messages(self.opts['message'])
+ self._last_commit_id = self._get_tip_rev(True)
def _get_tip_rev(self, refresh=False):
if refresh:
@@ -382,9 +515,9 @@ cmdoptions = {
'user':'', 'date':'',
'modified':True, 'added':True, 'removed':True, 'deleted':True,
- 'unknown':False, 'ignored':False,
+ 'unknown':True, 'ignored':False,
'exclude':[], 'include':[],
- 'check': False, 'git':False, 'logfile':'', 'addremove':False,
+ 'check': True, 'git':False, 'logfile':'', 'addremove':False,
}
dialog = GCommit(u, repo, cwd, files, cmdoptions, main)
|
@@ -150,6 +150,7 @@ summary = summary.split('\n')[0]
date = displaytime(ctx.date())
desc = author+'@'+str(rev)+' '+date+' "'+summary+'"'
+ desc = gobject.markup_escape_text(desc)
self.changedesc[rev] = (desc, author)
return (desc, author)
@@ -460,7 +461,7 @@ ('Rev', 10, self.COL_REVID, pango.ELLIPSIZE_NONE, True),
('File', 15, self.COL_PATH, pango.ELLIPSIZE_START, False),
('User', 15, self.COL_USER, pango.ELLIPSIZE_END, False),
- ('Matches', 80, self.COL_TEXT, pango.ELLIPSIZE_END, True)):
+ ('Source', 80, self.COL_TEXT, pango.ELLIPSIZE_END, True)):
cell = gtk.CellRendererText()
cell.set_property("width-chars", width)
cell.set_property("ellipsize", emode)
|
@@ -12,7 +12,7 @@ import gtk
import pango
import sys
-from hglib import gettabwidth
+from hglib import gettabwidth, RepoError
from mercurial import hg, ui, cmdutil, util, patch
from mercurial.i18n import _
from shlib import set_tortoise_icon
@@ -110,7 +110,7 @@ self.ui = ui.ui()
try:
self.repo = hg.repository(self.ui, path=self.root)
- except hg.RepoError:
+ except RepoError:
return None
matcher = cmdutil.match(self.repo, self.files)
|
@@ -24,8 +24,8 @@ import pango
from mercurial.i18n import _
-from mercurial.node import *
-from mercurial import cmdutil, util, ui, hg, commands, patch
+from mercurial.node import short
+from mercurial import cmdutil, util, ui, hg, commands
from hgext import extdiff
from shlib import shell_notify, set_tortoise_icon, Settings
from thgconfig import ConfigDialog
|
@@ -28,6 +28,7 @@ set_tortoise_icon(self, 'hg.ico')
self.cmdline = cmdline
self.returncode = None
+ self.hgthread = None
# construct dialog
self.set_default_size(width, height)
@@ -87,13 +88,14 @@ self.response(gtk.RESPONSE_ACCEPT)
def _on_stop_clicked(self, button):
- self.hgthread.terminate()
+ if self.hgthread:
+ self.hgthread.terminate()
def _delete(self, widget, event):
return True
def _response(self, widget, response_id):
- if self.hgthread.isAlive():
+ if self.hgthread and self.hgthread.isAlive():
widget.emit_stop_by_name('response')
def _on_window_map_event(self, event, param):
@@ -126,8 +128,9 @@ pass
self.update_progress()
if not self.hgthread.isAlive():
+ self._button_stop.set_sensitive(False)
self._button_ok.set_sensitive(True)
- self._button_stop.set_sensitive(False)
+ self._button_ok.grab_focus()
self.returncode = self.hgthread.return_code()
if self.returncode is None:
self.write("\n[command interrupted]")
@@ -150,7 +153,10 @@ self.pbar.pulse()
def return_code(self):
- return self.hgthread.return_code()
+ if self.hgthread:
+ return self.hgthread.return_code()
+ else:
+ return False
def run(cmdline=[], gui=True, **opts):
if not gui:
|
@@ -14,7 +14,7 @@ from tempfile import mkstemp
from dialog import *
from mercurial import hg, ui, extensions
-from mercurial.repo import RepoError
+from hglib import RepoError
from thgconfig import ConfigDialog
from hgcmd import CmdDialog
|
|
|
@@ -1,27 +1,25 @@ #
# hgignore.py - TortoiseHg's dialog for editing .hgignore
#
-# Copyright (C) 2008 Steve Borho <steve@borho.org>
+# Copyright (C) 2008-2009 Steve Borho <steve@borho.org>
#
import os
-import gobject
import gtk
-import pango
-import string
from dialog import *
-import hglib
-from mercurial import hg, ui
+from shlib import shell_notify
+from mercurial import hg, ui, match
class HgIgnoreDialog(gtk.Window):
""" Edit a reposiory .hgignore file """
- def __init__(self, root=''):
+ def __init__(self, root='', fileglob=''):
""" Initialize the Dialog """
gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
self.root = root
self.set_title('Ignore mask for ' + os.path.basename(root))
self.set_default_size(630, 400)
+ self.notify_func = None
self.tbar = gtk.Toolbar()
self.tips = gtk.Tooltips()
@@ -49,6 +47,8 @@ hbox.pack_start(glob_button, False, False, 4)
glob_button.connect('clicked', self.add_glob, glob_entry)
glob_entry.connect('activate', self.add_glob, glob_entry)
+ glob_entry.set_text(fileglob)
+ self.glob_entry = glob_entry
mainvbox.pack_start(hbox, False, False)
hbox = gtk.HBox()
@@ -68,18 +68,24 @@ frame = gtk.Frame('Filters')
hbox.pack_start(frame, True, True, 4)
pattree = gtk.TreeView()
+ pattree.set_reorderable(False)
sel = pattree.get_selection()
+ sel.set_mode(gtk.SELECTION_SINGLE)
sel.connect("changed", self.pattern_rowchanged)
col = gtk.TreeViewColumn('Patterns', gtk.CellRendererText(), text=0)
pattree.append_column(col)
+ pattree.set_headers_visible(False)
+ self.pattree = pattree
scrolledwindow = gtk.ScrolledWindow()
scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scrolledwindow.set_border_width(4)
scrolledwindow.add(pattree)
- pattree.set_headers_visible(False)
- self.pattree = pattree
- frame.add(scrolledwindow)
-
+ remove = gtk.Button("Remove Selected")
+ remove.connect("pressed", self.remove_pressed, sel)
+ vbox = gtk.VBox()
+ vbox.pack_start(scrolledwindow, True, True, 2)
+ vbox.pack_start(remove, False, False, 2)
+ frame.add(vbox)
frame = gtk.Frame('Unknown Files')
hbox.pack_start(frame, True, True, 4)
@@ -92,9 +98,10 @@ scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scrolledwindow.set_border_width(4)
scrolledwindow.add(unknowntree)
- model = gtk.ListStore(gobject.TYPE_STRING)
+ model = gtk.ListStore(str)
unknowntree.set_model(model)
unknowntree.set_headers_visible(False)
+ self.unkmodel = model
frame.add(scrolledwindow)
mainvbox.pack_start(hbox, True, True)
@@ -102,6 +109,13 @@ glob_entry.grab_focus()
self.connect('map_event', self._on_window_map_event)
+ def remove_pressed(self, widget, selection):
+ model, rows = selection.get_selected_rows()
+ del model[rows[0]]
+ del self.ignorelines[rows[0][0]]
+ self.write_ignore_lines()
+ self.refresh()
+
def pattern_rowchanged(self, sel):
model, iter = sel.get_selected()
if not iter:
@@ -111,43 +125,66 @@ model, iter = sel.get_selected()
if not iter:
return
+ self.glob_entry.set_text(model[iter][0])
def add_glob(self, widget, glob_entry):
- pass
+ newglob = glob_entry.get_text()
+ self.ignorelines.append('glob:' + newglob)
+ self.write_ignore_lines()
+ self.refresh()
- def add_regexp(self, widget, glob_entry):
- pass
+ def add_regexp(self, widget, regexp_entry):
+ newregexp = regexp_entry.get_text()
+ self.ignorelines.append('regexp:' + newregexp)
+ self.write_ignore_lines()
+ self.refresh()
def _on_window_map_event(self, event, param):
- self._refresh_clicked(None)
+ self.refresh()
def _refresh_clicked(self, togglebutton, data=None):
+ self.refresh()
+
+ def set_notify_func(self, func):
+ self.notify_func = func
+
+ def refresh(self):
+ try: repo = hg.repository(ui.ui(), path=self.root)
+ except: gtk.main_quit()
+ matcher = match.always(repo.root, repo.root)
+ changes = repo.dirstate.status(matcher, ignored=False, clean=False, unknown=True)
+ (lookup, modified, added, removed, deleted, unknown, ignored, clean) = changes
+ self.unkmodel.clear()
+ for u in unknown:
+ self.unkmodel.append([u])
try:
- l = open(os.path.join(self.root, '.hgignore'), 'rb').readlines()
- if l[0].endswith('\r\n'):
- self.doseoln = True
+ l = open(repo.wjoin('.hgignore'), 'rb').readlines()
+ self.doseoln = l[0].endswith('\r\n')
except IOError, ValueError:
self.doseoln = os.name == 'nt'
l = []
- model = gtk.ListStore(gobject.TYPE_STRING)
- l = [string.strip(line) for line in l]
+ model = gtk.ListStore(str)
+ self.ignorelines = []
for line in l:
- model.append([line])
+ model.append([line.strip()])
+ self.ignorelines.append(line.strip())
self.pattree.set_model(model)
- self.ignorelines = l
+ self.repo = repo
def write_ignore_lines(self):
- if doseoln:
+ if self.doseoln:
out = [line + '\r\n' for line in self.ignorelines]
else:
out = [line + '\n' for line in self.ignorelines]
try:
- f = open(os.path.join(self.root, '.hgignore'), 'wb')
+ f = open(self.repo.wjoin('.hgignore'), 'wb')
f.writelines(out)
f.close()
except IOError:
pass
+ shell_notify(self.repo.wjoin('.hgignore'))
+ if self.notify_func: self.notify_func()
def _toolbutton(self, stock, label, handler, tip):
tbutton = gtk.ToolButton(stock)
@@ -166,5 +203,6 @@ gtk.gdk.threads_leave()
if __name__ == "__main__":
+ import hglib
opts = {'root' : hglib.rootpath()}
run(**opts)
|
@@ -11,7 +11,8 @@ import gtk
import pango
from dialog import error_dialog, info_dialog
-from mercurial import hg, ui, util, repo
+from mercurial import hg, ui, util
+from hglib import RepoError
import shlib
class InitDialog(gtk.Window):
@@ -146,7 +147,7 @@
try:
hg.repository(u, dest, create=1)
- except repo.RepoError, inst:
+ except RepoError, inst:
error_dialog(self, "Unable to create new repository",
str(inst))
return False
|
@@ -5,12 +5,16 @@ import threading, thread2
import Queue
from mercurial import hg, ui, util, extensions, commands, hook
-from mercurial.repo import RepoError
-from mercurial.node import *
from mercurial.i18n import _
from dialog import entry_dialog
try:
+ from mercurial.error import RepoError, ParseError
+except ImportError:
+ from mercurial.repo import RepoError
+ from mercurial.dispatch import ParseError
+
+try:
try:
from mercurial import demandimport
except:
@@ -72,16 +76,27 @@ return ''
return p
+_tabwidth = None
def gettabwidth(ui):
+ global _tabwidth
tabwidth = ui.config('tortoisehg', 'tabwidth')
try:
tabwidth = int(tabwidth)
if tabwidth < 1 or tabwidth > 16:
- tabwidth = None
+ tabwidth = 0
except (ValueError, TypeError), e:
- tabwidth = None
+ tabwidth = 0
+ _tabwidth = tabwidth
return tabwidth
+def diffexpand(line):
+ 'Expand tabs in a line of diff/patch text'
+ if _tabwidth is None:
+ gettabwidth(ui.ui())
+ if not _tabwidth or len(line) < 2:
+ return line
+ return line[0] + line[1:].expandtabs(_tabwidth)
+
class GtkUi(ui.ui):
'''
|
|
|
@@ -0,0 +1,565 @@ + # shelve.py
+#
+# Copyright 2007 Bryan O'Sullivan <bos@serpentine.com>
+# Copyright 2007 TK Soh <teekaysoh@gmailcom>
+#
+# This software may be used and distributed according to the terms of
+# the GNU General Public License, incorporated herein by reference.
+
+'''interactive change selection to set aside that may be restored later'''
+
+from mercurial.i18n import _
+from mercurial import cmdutil, commands, cmdutil, hg, mdiff, patch, revlog
+from mercurial import util, fancyopts
+import copy, cStringIO, errno, operator, os, re, shutil, tempfile
+
+lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
+
+def internalpatch(patchobj, ui, strip, cwd, reverse=False, files={}):
+ """use builtin patch to apply <patchobj> to the working directory.
+ returns whether patch was applied with fuzz factor.
+
+ Adapted from patch.internalpatch() to support reverse patching.
+ """
+ try:
+ fp = file(patchobj, 'rb')
+ except TypeError:
+ fp = patchobj
+ if cwd:
+ curdir = os.getcwd()
+ os.chdir(cwd)
+ try:
+ ret = patch.applydiff(ui, fp, files, strip=strip,
+ reverse=reverse)
+ finally:
+ if cwd:
+ os.chdir(curdir)
+ if ret < 0:
+ raise PatchError
+ return ret > 0
+
+def scanpatch(fp):
+ lr = patch.linereader(fp)
+
+ def scanwhile(first, p):
+ lines = [first]
+ while True:
+ line = lr.readline()
+ if not line:
+ break
+ if p(line):
+ lines.append(line)
+ else:
+ lr.push(line)
+ break
+ return lines
+
+ while True:
+ line = lr.readline()
+ if not line:
+ break
+ if line.startswith('diff --git a/'):
+ def notheader(line):
+ s = line.split(None, 1)
+ return not s or s[0] not in ('---', 'diff')
+ header = scanwhile(line, notheader)
+ fromfile = lr.readline()
+ if fromfile.startswith('---'):
+ tofile = lr.readline()
+ header += [fromfile, tofile]
+ else:
+ lr.push(fromfile)
+ yield 'file', header
+ elif line[0] == ' ':
+ yield 'context', scanwhile(line, lambda l: l[0] in ' \\')
+ elif line[0] in '-+':
+ yield 'hunk', scanwhile(line, lambda l: l[0] in '-+\\')
+ else:
+ m = lines_re.match(line)
+ if m:
+ yield 'range', m.groups()
+ else:
+ raise patch.PatchError('unknown patch content: %r' % line)
+
+class header(object):
+ diff_re = re.compile('diff --git a/(.*) b/(.*)$')
+ allhunks_re = re.compile('(?:index|new file|deleted file) ')
+ pretty_re = re.compile('(?:new file|deleted file) ')
+ special_re = re.compile('(?:index|new file|deleted|copy|rename) ')
+
+ def __init__(self, header):
+ self.header = header
+ self.hunks = []
+
+ def binary(self):
+ for h in self.header:
+ if h.startswith('index '):
+ return True
+
+ def pretty(self, fp):
+ for h in self.header:
+ if h.startswith('index '):
+ fp.write(_('this modifies a binary file (all or nothing)\n'))
+ break
+ if self.pretty_re.match(h):
+ fp.write(h)
+ if self.binary():
+ fp.write(_('this is a binary file\n'))
+ break
+ if h.startswith('---'):
+ fp.write(_('%d hunks, %d lines changed\n') %
+ (len(self.hunks),
+ sum([h.added + h.removed for h in self.hunks])))
+ break
+ fp.write(h)
+
+ def write(self, fp):
+ fp.write(''.join(self.header))
+
+ def allhunks(self):
+ for h in self.header:
+ if self.allhunks_re.match(h):
+ return True
+
+ def files(self):
+ fromfile, tofile = self.diff_re.match(self.header[0]).groups()
+ if fromfile == tofile:
+ return [fromfile]
+ return [fromfile, tofile]
+
+ def filename(self):
+ return self.files()[-1]
+
+ def __repr__(self):
+ return '<header %s>' % (' '.join(map(repr, self.files())))
+
+ def special(self):
+ for h in self.header:
+ if self.special_re.match(h):
+ return True
+
+ def __cmp__(self, other):
+ return cmp(repr(self), repr(other))
+
+def countchanges(hunk):
+ add = len([h for h in hunk if h[0] == '+'])
+ rem = len([h for h in hunk if h[0] == '-'])
+ return add, rem
+
+class hunk(object):
+ maxcontext = 3
+
+ def __init__(self, header, fromline, toline, proc, before, hunk, after):
+ def trimcontext(number, lines):
+ delta = len(lines) - self.maxcontext
+ if False and delta > 0:
+ return number + delta, lines[:self.maxcontext]
+ return number, lines
+
+ self.header = header
+ self.fromline, self.before = trimcontext(fromline, before)
+ self.toline, self.after = trimcontext(toline, after)
+ self.proc = proc
+ self.hunk = hunk
+ self.added, self.removed = countchanges(self.hunk)
+
+ def write(self, fp):
+ delta = len(self.before) + len(self.after)
+ fromlen = delta + self.removed
+ tolen = delta + self.added
+ fp.write('@@ -%d,%d +%d,%d @@%s\n' %
+ (self.fromline, fromlen, self.toline, tolen,
+ self.proc and (' ' + self.proc)))
+ fp.write(''.join(self.before + self.hunk + self.after))
+
+ pretty = write
+
+ def filename(self):
+ return self.header.filename()
+
+ def __repr__(self):
+ return '<hunk %r@%d>' % (self.filename(), self.fromline)
+
+ def __cmp__(self, other):
+ return cmp(repr(self), repr(other))
+
+def parsepatch(fp):
+ class parser(object):
+ def __init__(self):
+ self.fromline = 0
+ self.toline = 0
+ self.proc = ''
+ self.header = None
+ self.context = []
+ self.before = []
+ self.hunk = []
+ self.stream = []
+
+ def addrange(self, (fromstart, fromend, tostart, toend, proc)):
+ self.fromline = int(fromstart)
+ self.toline = int(tostart)
+ self.proc = proc
+
+ def addcontext(self, context):
+ if self.hunk:
+ h = hunk(self.header, self.fromline, self.toline, self.proc,
+ self.before, self.hunk, context)
+ self.header.hunks.append(h)
+ self.stream.append(h)
+ self.fromline += len(self.before) + h.removed
+ self.toline += len(self.before) + h.added
+ self.before = []
+ self.hunk = []
+ self.proc = ''
+ self.context = context
+
+ def addhunk(self, hunk):
+ if self.context:
+ self.before = self.context
+ self.context = []
+ self.hunk = data
+
+ def newfile(self, hdr):
+ self.addcontext([])
+ h = header(hdr)
+ self.stream.append(h)
+ self.header = h
+
+ def finished(self):
+ self.addcontext([])
+ return self.stream
+
+ transitions = {
+ 'file': {'context': addcontext,
+ 'file': newfile,
+ 'hunk': addhunk,
+ 'range': addrange},
+ 'context': {'file': newfile,
+ 'hunk': addhunk,
+ 'range': addrange},
+ 'hunk': {'context': addcontext,
+ 'file': newfile,
+ 'range': addrange},
+ 'range': {'context': addcontext,
+ 'hunk': addhunk},
+ }
+
+ p = parser()
+
+ state = 'context'
+ for newstate, data in scanpatch(fp):
+ try:
+ p.transitions[state][newstate](p, data)
+ except KeyError:
+ raise patch.PatchError('unhandled transition: %s -> %s' %
+ (state, newstate))
+ state = newstate
+ return p.finished()
+
+def filterpatch(ui, chunks):
+ chunks = list(chunks)
+ chunks.reverse()
+ seen = {}
+ def consumefile():
+ consumed = []
+ while chunks:
+ if isinstance(chunks[-1], header):
+ break
+ else:
+ consumed.append(chunks.pop())
+ return consumed
+ resp_all = [None]
+ resp_file = [None]
+ applied = {}
+ def prompt(query):
+ if resp_all[0] is not None:
+ return resp_all[0]
+ if resp_file[0] is not None:
+ return resp_file[0]
+ while True:
+ r = (ui.prompt(query + _(' [Ynsfdaq?] '), '(?i)[Ynsfdaq?]?$')
+ or 'y').lower()
+ if r == '?':
+ c = shelve.__doc__.find('y - shelve this change')
+ for l in shelve.__doc__[c:].splitlines():
+ if l: ui.write(_(l.strip()), '\n')
+ continue
+ elif r == 's':
+ r = resp_file[0] = 'n'
+ elif r == 'f':
+ r = resp_file[0] = 'y'
+ elif r == 'd':
+ r = resp_all[0] = 'n'
+ elif r == 'a':
+ r = resp_all[0] = 'y'
+ elif r == 'q':
+ raise util.Abort(_('user quit'))
+ return r
+ while chunks:
+ chunk = chunks.pop()
+ if isinstance(chunk, header):
+ resp_file = [None]
+ fixoffset = 0
+ hdr = ''.join(chunk.header)
+ if hdr in seen:
+ consumefile()
+ continue
+ seen[hdr] = True
+ if resp_all[0] is None:
+ chunk.pretty(ui)
+ r = prompt(_('shelve changes to %s?') %
+ _(' and ').join(map(repr, chunk.files())))
+ if r == 'y':
+ applied[chunk.filename()] = [chunk]
+ if chunk.allhunks():
+ applied[chunk.filename()] += consumefile()
+ else:
+ consumefile()
+ else:
+ if resp_file[0] is None and resp_all[0] is None:
+ chunk.pretty(ui)
+ r = prompt(_('shelve this change to %r?') %
+ chunk.filename())
+ if r == 'y':
+ if fixoffset:
+ chunk = copy.copy(chunk)
+ chunk.toline += fixoffset
+ applied[chunk.filename()].append(chunk)
+ else:
+ fixoffset += chunk.removed - chunk.added
+ return reduce(operator.add, [h for h in applied.itervalues()
+ if h[0].special() or len(h) > 1], [])
+
+def refilterpatch(allchunk, selected):
+ ''' return unshelved chunks of files to be shelved '''
+ l = []
+ fil = []
+ for c in allchunk:
+ if isinstance(c, header):
+ if len(l) > 1 and l[0] in selected:
+ fil += l
+ l = [c]
+ elif c not in selected:
+ l.append(c)
+ if len(l) > 1 and l[0] in selected:
+ fil += l
+ return fil
+
+def makebackup(ui, repo, dir, files):
+ try:
+ os.mkdir(dir)
+ except OSError, err:
+ if err.errno != errno.EEXIST:
+ raise
+
+ backups = {}
+ for f in files:
+ fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
+ dir=dir)
+ os.close(fd)
+ ui.debug('backup %r as %r\n' % (f, tmpname))
+ util.copyfile(repo.wjoin(f), tmpname)
+ backups[f] = tmpname
+
+ return backups
+
+def get_shelve_filename(repo):
+ return repo.join('shelve')
+
+def shelve(ui, repo, *pats, **opts):
+ '''interactively select changes to set aside
+
+ If a list of files is omitted, all changes reported by "hg status"
+ will be candidates for shelveing.
+
+ You will be prompted for whether to shelve changes to each
+ modified file, and for files with multiple changes, for each
+ change to use. For each query, the following responses are
+ possible:
+
+ y - shelve this change
+ n - skip this change
+
+ s - skip remaining changes to this file
+ f - shelve remaining changes to this file
+
+ d - done, skip remaining changes and files
+ a - shelve all changes to all remaining files
+ q - quit, shelveing no changes
+
+ ? - display help'''
+
+ if not ui.interactive:
+ raise util.Abort(_('shelve can only be run interactively'))
+
+ forced = opts['force'] or opts['append']
+ if os.path.exists(repo.join('shelve')) and not forced:
+ raise util.Abort(_('shelve data already exists'))
+
+ def shelvefunc(ui, repo, message, match, opts):
+ # If an MQ patch is applied, consider all qdiff changes
+ if hasattr(repo, 'mq') and repo.mq.applied:
+ basenode = repo.lookup(-3)
+ else:
+ basenode = repo.dirstate.parents()[0]
+
+ changes = repo.status(match=match)[:5]
+ modified, added, removed = changes[:3]
+ files = modified + added + removed
+ diffopts = mdiff.diffopts(git=True, nodates=True)
+ patch_diff = ''.join(patch.diff(repo, basenode, match=match,
+ changes=changes, opts=diffopts))
+
+ fp = cStringIO.StringIO(patch_diff)
+ ac = parsepatch(fp)
+ fp.close()
+ chunks = filterpatch(ui, ac)
+ rc = refilterpatch(ac, chunks)
+
+ contenders = {}
+ for h in chunks:
+ try: contenders.update(dict.fromkeys(h.files()))
+ except AttributeError: pass
+
+ newfiles = [f for f in files if f in contenders]
+
+ if not newfiles:
+ ui.status(_('no changes to shelve\n'))
+ return 0
+
+ modified = dict.fromkeys(changes[0])
+
+ backupdir = repo.join('shelve-backups')
+
+ try:
+ bkfiles = [f for f in newfiles if f in modified]
+ backups = makebackup(ui, repo, backupdir, bkfiles)
+
+ # patch to shelve
+ sp = cStringIO.StringIO()
+ for c in chunks:
+ if c.filename() in backups:
+ c.write(sp)
+ doshelve = sp.tell()
+ sp.seek(0)
+
+ # patch to apply to shelved files
+ fp = cStringIO.StringIO()
+ for c in rc:
+ if c.filename() in backups:
+ c.write(fp)
+ dopatch = fp.tell()
+ fp.seek(0)
+
+ try:
+ # 3a. apply filtered patch to clean repo (clean)
+ if backups:
+ hg.revert(repo, basenode, backups.has_key)
+
+ # 3b. apply filtered patch to clean repo (apply)
+ if dopatch:
+ ui.debug('applying patch\n')
+ ui.debug(fp.getvalue())
+ patch.internalpatch(fp, ui, 1, repo.root)
+ del fp
+
+ # 3c. apply filtered patch to clean repo (shelve)
+ if doshelve:
+ ui.debug("saving patch to shelve\n")
+ if opts['append']:
+ f = repo.opener('shelve', "a")
+ else:
+ f = repo.opener('shelve', "w")
+ f.write(sp.getvalue())
+ del f
+ del sp
+ except:
+ try:
+ for realname, tmpname in backups.iteritems():
+ ui.debug('restoring %r to %r\n' % (tmpname, realname))
+ util.copyfile(tmpname, repo.wjoin(realname))
+ ui.debug('removing shelve file\n')
+ os.unlink(repo.join('shelve'))
+ except OSError:
+ pass
+
+ return 0
+ finally:
+ try:
+ for realname, tmpname in backups.iteritems():
+ ui.debug('removing backup for %r : %r\n' % (realname, tmpname))
+ os.unlink(tmpname)
+ os.rmdir(backupdir)
+ except OSError:
+ pass
+ fancyopts.fancyopts([], commands.commitopts, opts)
+ return cmdutil.commit(ui, repo, shelvefunc, pats, opts)
+
+
+def unshelve(ui, repo, *pats, **opts):
+ '''restore shelved changes'''
+
+ try:
+ fp = cStringIO.StringIO()
+ fp.write(repo.opener('shelve').read())
+ if opts['inspect']:
+ ui.status(fp.getvalue())
+ else:
+ files = []
+ for chunk in parsepatch(fp):
+ if isinstance(chunk, header):
+ files += chunk.files()
+ backupdir = repo.join('shelve-backups')
+ backups = makebackup(ui, repo, backupdir, set(files))
+
+ ui.debug('applying shelved patch\n')
+ patchdone = 0
+ try:
+ try:
+ fp.seek(0)
+ pfiles = {}
+ internalpatch(fp, ui, 1, repo.root, files=pfiles)
+ patch.updatedir(ui, repo, pfiles)
+ patchdone = 1
+ except:
+ if opts['force']:
+ patchdone = 1
+ else:
+ ui.status('restoring backup files\n')
+ for realname, tmpname in backups.iteritems():
+ ui.debug('restoring %r to %r\n' %
+ (tmpname, realname))
+ util.copyfile(tmpname, repo.wjoin(realname))
+ finally:
+ try:
+ ui.debug('removing backup files\n')
+ shutil.rmtree(backupdir, True)
+ except OSError:
+ pass
+
+ if patchdone:
+ ui.debug("removing shelved patches\n")
+ os.unlink(repo.join('shelve'))
+ ui.status("unshelve completed\n")
+ except IOError:
+ ui.warn('nothing to unshelve\n')
+
+cmdtable = {
+ "shelve":
+ (shelve,
+ [('A', 'addremove', None,
+ _('mark new/missing files as added/removed before shelving')),
+ ('f', 'force', None,
+ _('overwrite existing shelve data')),
+ ('a', 'append', None,
+ _('append to existing shelve data')),
+ ] + commands.walkopts,
+ _('hg shelve [OPTION]... [FILE]...')),
+ "unshelve":
+ (unshelve,
+ [('i', 'inspect', None, _('inspect shelved changes only')),
+ ('f', 'force', None,
+ _('proceed even if patches do not unshelve cleanly')),
+ ],
+ _('hg unshelve [OPTION]... [FILE]...')),
+}
|
|
@@ -69,7 +69,7 @@ self.graphview.set_property(property, bool)
def _more_clicked(self, button):
- self.graphview.next_revision_batch()
+ self.graphview.next_revision_batch(self.limit)
def _load_all_clicked(self, button):
self.graphview.load_all_revisions()
@@ -221,23 +221,30 @@ self._hpaned.get_position())
return settings
+ def get_graphlimit(self, suggestion):
+ limit_opt = self.repo.ui.config('tortoisehg', 'graphlimit', '500')
+ l = 0
+ for limit in (suggestion, limit_opt):
+ try:
+ l = int(limit)
+ if l > 0:
+ return l
+ except (TypeError, ValueError), e:
+ pass
+ return l or 500
+
def load_settings(self, settings):
'''Called at beginning of display() method'''
- limit_opt = self.repo.ui.config('tortoisehg', 'graphlimit', '500')
- if limit_opt:
- try:
- limit = int(limit_opt)
- except ValueError:
- limit = 0
- if limit <= 0:
- limit = None
- else:
- limit = None
+
+ self.stbar = gtklib.StatusBar()
+ self.limit = self.get_graphlimit(None)
# Allocate TreeView instance to use internally
- self.limit = limit
- self.stbar = gtklib.StatusBar()
- self.graphview = TreeView(self.repo, limit, self.stbar)
+ if 'limit' in self.opts:
+ firstlimit = self.get_graphlimit(self.opts['limit'])
+ self.graphview = TreeView(self.repo, firstlimit, self.stbar)
+ else:
+ self.graphview = TreeView(self.repo, self.limit, self.stbar)
# Allocate ChangeSet instance to use internally
self.changeview = ChangeSet(self.ui, self.repo, self.cwd, [],
@@ -310,6 +317,7 @@ _menu.append(self._cmenu_merge)
_menu.append(create_menu('_export patch', self._export_patch))
_menu.append(create_menu('e_mail patch', self._email_patch))
+ _menu.append(create_menu('_bundle rev:tip', self._bundle_rev_to_tip))
_menu.append(create_menu('add/remove _tag', self._add_tag))
_menu.append(create_menu('backout revision', self._backout_rev))
_menu.append(create_menu('_revert', self._revert))
@@ -511,6 +519,20 @@ commands.export(self.ui,self.repo,str(rev),**exportOpts)
success, outtext = self._hg_call_wrapper("Export",dohgexport,False)
+ def _bundle_rev_to_tip(self, menuitem):
+ rev = self.currow[treemodel.REVID]
+ filename = "%s_rev%s_to_tip.hg" % (os.path.basename(self.repo.root), rev)
+ result = NativeSaveFileDialogWrapper(Title = "Write bundle to",
+ InitialDir=self.repo.root,
+ FileName=filename).run()
+ if result:
+ from hgcmd import CmdDialog
+ cmdline = ['hg', 'bundle', '--base', str(rev), result]
+ dlg = CmdDialog(cmdline)
+ dlg.show_all()
+ dlg.run()
+ dlg.hide()
+
def _email_patch(self, menuitem):
from hgemail import EmailDialog
rev = self.currow[treemodel.REVID]
@@ -613,7 +635,7 @@ self._menu.get_children()[0].activate()
return True
-def run(root='', cwd='', files=[], **opts):
+def run(root='', cwd='', files=[], limit='', **opts):
u = ui.ui()
u.updateopts(debug=False, traceback=False)
repo = hg.repository(u, path=root)
@@ -622,7 +644,7 @@
cmdoptions = {
'follow':False, 'follow-first':False, 'copies':False, 'keyword':[],
- 'limit':0, 'rev':[], 'removed':False, 'no_merges':False, 'date':None,
+ 'limit':limit, 'rev':[], 'removed':False, 'no_merges':False, 'date':None,
'only_merges':None, 'prune':[], 'git':False, 'verbose':False,
'include':[], 'exclude':[]
}
@@ -641,4 +663,5 @@ path = len(sys.argv) > 1 and sys.argv[1] or os.getcwd()
opts['root'] = os.path.abspath(path)
opts['files'] = [opts['root']]
+ opts['limit'] = ''
run(**opts)
|
@@ -11,9 +11,8 @@ import sys
import gtk
from gdialog import *
-from mercurial.node import *
from mercurial import cmdutil, util, hg, ui
-from mercurial.repo import RepoError
+from hglib import RepoError
from shlib import shell_notify, set_tortoise_icon
class FilterDialog(gtk.Dialog):
|
@@ -12,9 +12,9 @@ from dialog import *
from mercurial.node import *
from mercurial import util, hg, ui
+from hglib import RepoError
from hgcmd import CmdDialog
from shlib import set_tortoise_icon, shell_notify
-from mercurial.repo import RepoError
import histselect
class MergeDialog(gtk.Window):
|
@@ -17,11 +17,9 @@ import Queue
import os
import threading
-from mercurial import hg, ui, util
-from mercurial.repo import RepoError
-from mercurial.node import *
+from mercurial import hg, ui, util
from dialog import error_dialog, question_dialog
-from hglib import HgThread, toutf
+from hglib import HgThread, toutf, RepoError
from shlib import set_tortoise_icon, shell_notify
import gtklib
|
|
|
@@ -0,0 +1,418 @@ + #
+# rename.py - TortoiseHg's dialogs for handling renames
+#
+# Copyright (C) 2009 Steve Borho <steve@borho.org>
+#
+
+import os
+import sys
+import gtk
+import gobject
+import pango
+import cStringIO
+import shlib
+import Queue
+import threading, thread2
+from dialog import error_dialog
+from mercurial import hg, ui, mdiff, cmdutil, match, util
+from hglib import toutf, diffexpand
+import gtklib
+try:
+ from mercurial.repo import RepoError
+except ImportError:
+ from mercurial.error import RepoError
+
+# This function and some key bits below borrowed ruthelessly from
+# Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
+# Thanks!
+def findmoves(repo, added, removed, threshold):
+ '''find renamed files -- yields (before, after, score) tuples'''
+ ctx = repo['.']
+ for r in removed:
+ rr = ctx.filectx(r).data()
+ bestname, bestscore = None, threshold
+ for a in added:
+ aa = repo.wread(a)
+ if aa == rr:
+ yield r, a, 1.0
+ break
+
+class DetectRenameDialog(gtk.Window):
+ 'Detect renames after they occur'
+ def __init__(self, root=''):
+ 'Initialize the Dialog'
+ gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
+
+ self.root = root
+ self.notify_func = None
+ self.set_title('Detect Copies/Renames in %s' % os.path.basename(root))
+ settings = shlib.Settings('rename')
+ dims = settings.get_value('dims', (800, 600))
+ self.set_default_size(dims[0], dims[1])
+
+ adjustment = gtk.Adjustment(50, 0, 100, 1)
+ value = settings.get_value('percent', None)
+ if value: adjustment.set_value(value)
+ hscale = gtk.HScale(adjustment)
+ frame = gtk.Frame('Minimum Simularity Percentage')
+ frame.add(hscale)
+ topvbox = gtk.VBox()
+ topvbox.pack_start(frame, False, False, 2)
+
+ unkmodel = gtk.ListStore(str)
+ unknowntree = gtk.TreeView(unkmodel)
+ unknowntree.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
+ cell = gtk.CellRendererText()
+ cell.set_property("ellipsize", pango.ELLIPSIZE_START)
+ col = gtk.TreeViewColumn('File', cell, text=0)
+ unknowntree.append_column(col)
+ unknowntree.set_enable_search(True)
+ unknowntree.set_headers_visible(False)
+ scroller = gtk.ScrolledWindow()
+ scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scroller.add(unknowntree)
+
+ vbox = gtk.VBox()
+ vbox.pack_start(scroller, True, True, 2)
+ fr = gtk.Button('Find Renames')
+ fc = gtk.Button('Find Copies')
+ hbox = gtk.HBox()
+ hbox.pack_start(fr, True, True, 2)
+ hbox.pack_start(fc, True, True, 2)
+ vbox.pack_start(hbox, False, False, 2)
+
+ unknownframe = gtk.Frame('Unrevisioned Files')
+ unknownframe.add(vbox)
+
+ # source, dest, percent match, sensitive
+ cmodel = gtk.ListStore(str, str, str, bool)
+ ctree = gtk.TreeView(cmodel)
+ ctree.set_rules_hint(True)
+ ctree.set_reorderable(False)
+ ctree.set_enable_search(True)
+ ctree.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
+
+ cell = gtk.CellRendererText()
+ cell.set_property("width-chars", 30)
+ cell.set_property("ellipsize", pango.ELLIPSIZE_START)
+ col = gtk.TreeViewColumn('Source', cell, text=0, sensitive=3)
+ col.set_resizable(True)
+ ctree.append_column(col)
+
+ cell = gtk.CellRendererText()
+ cell.set_property("width-chars", 30)
+ cell.set_property("ellipsize", pango.ELLIPSIZE_START)
+ col = gtk.TreeViewColumn('Dest', cell, text=1, sensitive=3)
+ col.set_resizable(True)
+ ctree.append_column(col)
+
+ cell = gtk.CellRendererText()
+ cell.set_property("width-chars", 5)
+ cell.set_property("ellipsize", pango.ELLIPSIZE_NONE)
+ col = gtk.TreeViewColumn('%', cell, text=2, sensitive=3)
+ col.set_resizable(True)
+ ctree.append_column(col)
+
+ ctree.connect('row-activated', self.candidate_row_act, unknowntree)
+ scroller = gtk.ScrolledWindow()
+ scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scroller.add(ctree)
+
+ stbar = gtklib.StatusBar()
+ args = (unknowntree, ctree, adjustment, stbar)
+ vbox = gtk.VBox()
+ vbox.pack_start(scroller, True, True, 2)
+ ac = gtk.Button('Accept Match')
+ fc.connect('pressed', self.find_copies, *args)
+ fr.connect('pressed', self.find_renames, *args)
+ ac.connect('pressed', self.accept_match, *args)
+ hbox = gtk.HBox()
+ hbox.pack_start(ac, False, False, 2)
+ vbox.pack_start(hbox, False, False, 2)
+
+ candidateframe = gtk.Frame('Candidate Matches')
+ candidateframe.add(vbox)
+
+ hpaned = gtk.HPaned()
+ hpaned.pack1(unknownframe, True, True)
+ hpaned.pack2(candidateframe, True, True)
+ pos = settings.get_value('hpaned', None)
+ if pos: hpaned.set_position(pos)
+
+ topvbox.pack_start(hpaned, True, True, 2)
+
+ diffframe = gtk.Frame('Differences from Source to Dest')
+ diffframe.set_shadow_type(gtk.SHADOW_ETCHED_IN)
+ scroller = gtk.ScrolledWindow()
+ scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ diffframe.add(scroller)
+
+ buffer = gtk.TextBuffer()
+ buffer.create_tag('removed', foreground='#900000')
+ buffer.create_tag('added', foreground='#006400')
+ buffer.create_tag('position', foreground='#FF8000')
+ buffer.create_tag('header', foreground='#000090')
+
+ diffview = gtk.TextView(buffer)
+ diffview.modify_font(pango.FontDescription('monospace'))
+ diffview.set_wrap_mode(gtk.WRAP_NONE)
+ diffview.set_editable(False)
+ scroller.add(diffview)
+
+ vpaned = gtk.VPaned()
+ vpaned.pack1(topvbox, True, False)
+ vpaned.pack2(diffframe)
+ pos = settings.get_value('vpaned', None)
+ if pos: vpaned.set_position(pos)
+
+ vbox = gtk.VBox()
+ vbox.pack_start(vpaned, True, True, 2)
+ vbox.pack_start(stbar, False, False, 2)
+ self.add(vbox)
+
+ ctree.connect('cursor-changed', self.show_diff, buffer)
+ self.connect('map_event', self.on_window_map_event, unkmodel)
+ self.connect('delete-event', self.save_settings,
+ settings, hpaned, vpaned, adjustment)
+
+ def set_notify_func(self, func):
+ self.notify_func = func
+
+ def on_window_map_event(self, event, param, unkmodel):
+ self.refresh(unkmodel)
+
+ def refresh(self, unkmodel):
+ q = Queue.Queue()
+ unkmodel.clear()
+ thread = thread2.Thread(target=self.unknown_thread,
+ args=(self.root, q))
+ thread.start()
+ gobject.timeout_add(50, self.unknown_wait, thread, q, unkmodel)
+
+ def unknown_thread(self, root, q):
+ try:
+ repo = hg.repository(ui.ui(), root)
+ except RepoError:
+ return
+ matcher = match.always(repo.root, repo.root)
+ status = repo.status(node1=repo.dirstate.parents()[0], node2=None,
+ match=matcher, ignored=False, clean=False, unknown=True)
+ (modified, added, removed, deleted, unknown, ignored, clean) = status
+ for u in unknown:
+ q.put( u )
+ for a in added:
+ if not repo.dirstate.copied(a):
+ q.put( a )
+
+ def unknown_wait(self, thread, q, unkmodel):
+ while q.qsize():
+ unkmodel.append( [q.get(0)] )
+ return thread.isAlive()
+
+ def save_settings(self, w, event, settings, hpaned, vpaned, adjustment):
+ settings.set_value('vpaned', vpaned.get_position())
+ settings.set_value('hpaned', hpaned.get_position())
+ settings.set_value('percent', adjustment.get_value())
+ rect = self.get_allocation()
+ settings.set_value('dims', (rect.width, rect.height))
+ settings.write()
+
+ def find_renames(self, widget, unktree, ctree, adj, stbar):
+ 'User pressed "find renames" button'
+ cmodel = ctree.get_model()
+ cmodel.clear()
+ umodel, paths = unktree.get_selection().get_selected_rows()
+ if not paths:
+ return
+ tgts = [ umodel[p][0] for p in paths ]
+ q = Queue.Queue()
+ thread = thread2.Thread(target=self.search_thread,
+ args=(self.root, q, tgts, adj))
+ thread.start()
+ stbar.begin()
+ stbar.set_status_text('finding source of ' + ', '.join(tgts))
+ gobject.timeout_add(50, self.search_wait, thread, q, cmodel, stbar)
+
+ def search_thread(self, root, q, tgts, adj):
+ try:
+ repo = hg.repository(ui.ui(), root)
+ except RepoError:
+ return
+ srcs = []
+ audit_path = util.path_auditor(repo.root)
+ m = cmdutil.match(repo)
+ for abs in repo.walk(m):
+ target = repo.wjoin(abs)
+ good = True
+ try:
+ audit_path(abs)
+ except:
+ good = False
+ status = repo.dirstate[abs]
+ if (not good or not util.lexists(target)
+ or (os.path.isdir(target) and not os.path.islink(target))):
+ srcs.append(abs)
+ elif not adj and status == 'n':
+ # looking for copies, so any revisioned file is a
+ # potential source (yes, this will be expensive)
+ # Added and removed files are not considered as copy
+ # sources.
+ srcs.append(abs)
+ if adj:
+ simularity = adj.get_value() / 100.0;
+ gen = cmdutil.findrenames
+ else:
+ simularity = 1.0
+ gen = findmoves
+ for old, new, score in gen(repo, tgts, srcs, simularity):
+ q.put( [old, new, '%d%%' % (score*100), True] )
+
+ def search_wait(self, thread, q, cmodel, stbar):
+ while q.qsize():
+ cmodel.append( q.get(0) )
+ if thread.isAlive():
+ return True
+ else:
+ stbar.end()
+ return False
+
+ def find_copies(self, widget, unktree, ctree, adj, stbar):
+ 'User pressed "find copies" button'
+ # call rename function with simularity = 100%
+ self.find_renames(widget, unktree, ctree, None, stbar)
+
+ def accept_match(self, widget, unktree, ctree, adj, stbar):
+ 'User pressed "accept match" button'
+ try:
+ repo = hg.repository(ui.ui(), self.root)
+ except RepoError:
+ return
+ cmodel, paths = ctree.get_selection().get_selected_rows()
+ for path in paths:
+ row = cmodel[path]
+ src, dest, percent, sensitive = row
+ if not sensitive:
+ continue
+ if not os.path.exists(repo.wjoin(src)):
+ # Mark missing rename source as removed
+ repo.remove([src])
+ repo.copy(src, dest)
+ shlib.shell_notify([src, dest])
+ if self.notify_func:
+ self.notify_func()
+ # Mark all rows with this target file as non-sensitive
+ for row in cmodel:
+ if row[1] == dest:
+ row[3] = False
+ self.refresh(unktree.get_model())
+
+ def candidate_row_act(self, ctree, path, column, unktree):
+ 'User activated row of candidate list'
+ self.accept_match(ctree, unktree, ctree, None)
+
+ def show_diff(self, tree, buffer):
+ 'User selected a row in the candidate tree'
+ try:
+ repo = hg.repository(ui.ui(), self.root)
+ except RepoError:
+ return
+
+ buffer.set_text('')
+ iter = buffer.get_start_iter()
+ model, paths = tree.get_selection().get_selected_rows()
+ for path in paths:
+ row = model[path]
+ src, dest, percent, sensitive = row
+ if not sensitive:
+ continue
+ ctx = repo['.']
+ aa = repo.wread(dest)
+ rr = ctx.filectx(src).data()
+ opts = mdiff.defaultopts
+ difftext = mdiff.unidiff(rr, '', aa, '', src, dest, None, opts=opts)
+ if not difftext:
+ l = '== %s and %s have identical contents ==\n\n' % (src, dest)
+ buffer.insert(iter, l)
+ continue
+ difflines = difftext.splitlines(True)
+ for line in difflines:
+ line = toutf(line)
+ if line.startswith('---') or line.startswith('+++'):
+ buffer.insert_with_tags_by_name(iter, line, 'header')
+ elif line.startswith('-'):
+ line = diffexpand(line)
+ buffer.insert_with_tags_by_name(iter, line, 'removed')
+ elif line.startswith('+'):
+ line = diffexpand(line)
+ buffer.insert_with_tags_by_name(iter, line, 'added')
+ elif line.startswith('@@'):
+ buffer.insert_with_tags_by_name(iter, line, 'position')
+ else:
+ line = diffexpand(line)
+ buffer.insert(iter, line)
+
+def run(fname='', target='', detect=True, root='', **opts):
+ if detect:
+ dialog = DetectRenameDialog(root)
+ dialog.show_all()
+ dialog.connect('destroy', gtk.main_quit)
+ else:
+ from dialog import entry_dialog
+ title = 'Rename ' + fname
+ dialog = entry_dialog(None, title, True, target or fname, rename_resp)
+ dialog.orig = fname
+ dialog.show_all()
+ dialog.connect('destroy', gtk.main_quit)
+ gtk.gdk.threads_init()
+ gtk.gdk.threads_enter()
+ gtk.main()
+ gtk.gdk.threads_leave()
+
+def rename_resp(dialog, response):
+ if response != gtk.RESPONSE_OK:
+ gtk.main_quit()
+ return
+ try:
+ import hglib
+ root = hglib.rootpath()
+ repo = hg.repository(ui.ui(), root)
+ except ImportError, RepoError:
+ gtk.main_quit()
+ return
+
+ new_name = dialog.entry.get_text()
+ opts = {}
+ opts['force'] = False # Checkbox? Nah.
+ opts['after'] = False
+ opts['dry_run'] = False
+
+ saved = sys.stderr
+ errors = cStringIO.StringIO()
+ quit = False
+ try:
+ sys.stderr = errors
+ repo.ui.pushbuffer()
+ try:
+ commands.rename(repo.ui, repo, dialog.orig, new_name, **opts)
+ quit = True
+ except util.Abort, inst:
+ error_dialog(None, 'rename error', str(inst))
+ quit = False
+ finally:
+ sys.stderr = saved
+ textout = errors.getvalue() + repo.ui.popbuffer()
+ errors.close()
+ if len(textout) > 1:
+ error_dialog(None, 'rename error', textout)
+ elif quit:
+ gtk.main_quit()
+
+if __name__ == "__main__":
+ opts = {'fname' : sys.argv[1]}
+ if '--detect' in sys.argv:
+ import hglib
+ opts['root'] = hglib.rootpath()
+ opts['detect'] = True
+ elif len(sys.argv) == 3:
+ opts['target'] = sys.argv[2]
+ run(**opts)
|
@@ -14,12 +14,11 @@ import gobject
import pango
from mercurial import hg, ui, cmdutil, util
-from mercurial.repo import RepoError
from mercurial.i18n import _
-from mercurial.node import *
from dialog import error_dialog, question_dialog
from revtree import RevisionTree
from shlib import set_tortoise_icon
+from hglib import RepoError
class RevisionDialog(gtk.Dialog):
def __init__(self, root=''):
|
@@ -24,7 +24,6 @@ import hglib
from dialog import question_dialog, error_dialog
from mercurial import hg, ui, commands, cmdutil, util
-from mercurial.repo import RepoError
from mercurial.hgweb import server
from mercurial.i18n import _
from shlib import set_tortoise_icon
@@ -117,7 +116,7 @@ def _get_config(self):
try:
repo = hg.repository(ui.ui(), path=self._root)
- except RepoError:
+ except hglib.RepoError:
print 'no repository found'
gtk.main_quit()
self.defport = repo.ui.config('web', 'port') or '8000'
|
@@ -8,6 +8,7 @@ """
import os
+import gtk
import shelve
import time
@@ -129,6 +130,13 @@
def set_tortoise_icon(window, icon):
window.set_icon_from_file(get_tortoise_icon(icon))
+ # Global keybindings for TortoiseHg
+ window.connect('key-press-event', window_key)
+
+def window_key(window, event):
+ if event.keyval == ord('q') and (event.state & gtk.gdk.CONTROL_MASK):
+ devent = gtk.gdk.Event(gtk.gdk.DELETE)
+ window.emit('delete_event', devent)
def get_tortoise_icon(icon):
'''Find a tortoise icon, apply to PyGtk window'''
|
This file's diff was not loaded because this changeset is very large. Load changes Loading... |
This file's diff was not loaded because this changeset is very large. Load changes Loading... |
@@ -15,9 +15,9 @@ import gtk
from dialog import question_dialog, error_dialog, info_dialog
from mercurial import hg, ui, cmdutil, util
-from mercurial.repo import RepoError
from mercurial.i18n import _
-from mercurial.node import *
+from mercurial.node import short
+from hglib import RepoError
class TagAddDialog(gtk.Window):
""" Dialog to add tag to Mercurial repo """
|
@@ -14,8 +14,8 @@ import gtk
import gobject
from mercurial import hg, ui, cmdutil, util, node
-from mercurial.repo import RepoError
from mercurial.i18n import _
+from hglib import RepoError
def get_tag_list(path):
root = path
|
This file's diff was not loaded because this changeset is very large. Load changes Loading... |
@@ -11,11 +11,10 @@ import sys
import gtk
from dialog import *
+from mercurial import util, hg, ui
from mercurial.node import *
-from mercurial import util, hg, ui
-from mercurial.repo import RepoError
from shlib import shell_notify, set_tortoise_icon
-from hglib import rootpath
+from hglib import rootpath, RepoError
class UpdateDialog(gtk.Window):
""" Dialog to update Mercurial repo """
|
@@ -101,7 +101,14 @@ ctx = self.repo.changectx(revid)
summary = ctx.description().replace('\0', '')
- summary = summary.split('\n')[0]
+ if self.repo.ui.configbool('tortoisehg', 'longsummary'):
+ lines = summary.split('\n')
+ summary = lines.pop(0)
+ while len(summary) < 80 and lines:
+ summary += ' ' + lines.pop(0)
+ summary = summary[0:80]
+ else:
+ summary = summary.split('\n')[0]
summary = gobject.markup_escape_text(toutf(summary))
node = self.repo.lookup(revid)
tags = self.repo.nodetags(node)
|
@@ -215,7 +215,8 @@ else:
raise AttributeError, 'unknown property %s' % property.name
- def next_revision_batch(self):
+ def next_revision_batch(self, size):
+ self.batchsize = size
self.limit += self.batchsize
self.pbar.begin()
gobject.idle_add(self.populate)
|
@@ -10,7 +10,7 @@ import os
# non-Win32 platforms doesn't require setup
-if os.name != 'nt':
+if os.name != 'nt' and '--version' not in sys.argv:
sys.stderr.write("abort: %s is for Win32 platforms only" % sys.argv[0])
sys.exit(1)
@@ -32,7 +32,10 @@ pass
from distutils.core import setup
-import py2exe
+try: import py2exe
+except ImportError:
+ if '--version' not in sys.argv:
+ raise
_data_files = []
extra = {}
@@ -71,14 +74,24 @@ }
}
-# specify version string, otherwise 'hg identify' will be used:
-version = ''
+try:
+ l = os.popen('hg id -it').read().split()
+ while len(l) > 1 and l[-1][0].isalpha(): # remove non-numbered tags
+ l.pop()
+ version = l and l[-1] or 'unknown' # latest tag or revision number
+ if version.endswith('+'):
+ version += time.strftime('%Y%m%d')
-import tortoise.version
-tortoise.version.remember_version(version)
+except OSError:
+ version = "unknown"
+
+f = file("tortoise/__version__.py", "w")
+f.write('# this file is autogenerated by setup.py\n')
+f.write('version = "%s"\n' % version)
+f.close()
setup(name="TortoiseHg",
- version=tortoise.version.get_version(),
+ version=version,
author='TK Soh',
author_email='teekaysoh@gmail.com',
url='http://tortoisehg.sourceforge.net',
|
This file's diff was not loaded because this changeset is very large. Load changes Loading... |
This file's diff was not loaded because this changeset is very large. Load changes Loading... |
Loading...