by
Changes to 16 files · Browse files at af1e7e35ef12 Showing diff from parent 5b5b03863da0 c6402e1b86ce Diff from another changeset...
|
|
@@ -0,0 +1,52 @@ + # bfprompt.py - prompt to add large files as bfiles
+#
+# Copyright 2011 Fog Creek Software
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+import os
+
+from mercurial import match
+from tortoisehg.hgqt import qtlib
+from tortoisehg.hgqt.i18n import _
+
+class BfilesPrompt(qtlib.CustomPrompt):
+ def __init__(self, parent, files=None):
+ qtlib.CustomPrompt.__init__(self, _('Confirm Add'),
+ _('Some of the files that you have selected are of a size '
+ 'over 10 MB. You may make more efficient use of disk space '
+ 'by adding these files as bfiles, which will store only the '
+ 'most recent revision of each file in your local repository, '
+ 'with older revisions available on the server. Do you wish '
+ 'to add these files as bfiles?'), parent,
+ (_('Add as &Bfiles'), _('Add as &Normal Files'), _('Cancel')),
+ 0, 2, files)
+
+def promptForBfiles(parent, ui, repo, files):
+ bfiles = []
+ usekbf = os.path.exists(repo.standin('.kbf'))
+ minsize = int(ui.config('kilnbfiles', 'size', default='10'))
+ patterns = ui.config('kilnbfiles', 'patterns', default=())
+ if patterns:
+ patterns = patterns.split(' ')
+ matcher = match.match(repo.root, '', list(patterns))
+ else:
+ matcher = None
+ for wfile in files:
+ if not matcher or not matcher(wfile) or not usekbf:
+ filesize = os.path.getsize(repo.wjoin(wfile))
+ if filesize >= 10*1024*1024 and (filesize < minsize*1024*1024 or not usekbf):
+ bfiles.append(wfile)
+ if bfiles:
+ ret = BfilesPrompt(parent, files).run()
+ if ret == 0:
+ # add as bfiles
+ for bfile in bfiles:
+ files.remove(bfile)
+ elif ret == 1:
+ # add as normal files
+ bfiles = []
+ elif ret == 2:
+ return None
+ return files, bfiles
|
@@ -47,7 +47,7 @@ return None
try:
data = fctx.data()
- if '\0' in data:
+ if '\0' in data or ctx.isKbf(wfile):
self.error = p + _('File is binary.\n')
return None
except (EnvironmentError, util.Abort), e:
@@ -72,6 +72,7 @@ return 'C'
return None
+ isbfile = False
repo = ctx._repo
self.flabel += u'<b>%s</b>' % hglib.tounicode(wfile)
@@ -302,6 +303,9 @@ else:
self.contents = olddata
self.flabel += _(' <i>(was deleted)</i>')
+ elif hasattr(ctx.p1(), 'hasBfile') and ctx.p1().hasBfile(wfile):
+ self.error = 'binary file'
+ self.flabel += _(' <i>(was deleted)</i>')
else:
self.flabel += _(' <i>(was added, now missing)</i>')
return
@@ -315,6 +319,8 @@ return
else:
data = util.posixfile(absfile, 'r').read()
+ elif ctx.hasBfile(wfile):
+ data = '\0'
else:
data = ctx.filectx(wfile).data()
if '\0' in data:
@@ -324,6 +330,9 @@ return
if status in ('M', 'A'):
+ if ctx.hasBfile(wfile):
+ wfile = ctx.standin(wfile)
+ isbfile = True
res = self.checkMaxDiff(ctx, wfile, maxdiff)
if res is None:
if status == 'A':
@@ -367,5 +376,8 @@ revs = [str(ctx), str(ctx2)]
diffopts = patch.diffopts(repo.ui, {})
diffopts.git = False
+ if isbfile:
+ olddata += '\0'
+ newdata += '\0'
self.diff = mdiff.unidiff(olddata, olddate, newdata, newdate,
oldname, wfile, revs, diffopts)
|
@@ -144,6 +144,8 @@ for lst, flag in ((added, 'A'), (modified, 'M'), (removed, 'R')):
for f in filter(func, lst):
wasmerged = ismerge and f in ctxfiles
+ if hasattr(self._ctx, 'removeKbf'):
+ f = self._ctx.removeKbf(f)
files.append({'path': f, 'status': flag, 'parent': parent,
'wasmerged': wasmerged})
return files
|
@@ -309,6 +309,9 @@ self.actionNextDiff.setEnabled(False)
self.actionPrevDiff.setEnabled(False)
+ self.maxWidth = 0
+ self.sci.showHScrollBar(False)
+
def displayFile(self, filename=None, status=None):
if isinstance(filename, (unicode, QString)):
filename = hglib.fromunicode(filename)
@@ -434,6 +437,20 @@ self.actionNextDiff.setEnabled(bool(self._diffs))
self.actionPrevDiff.setEnabled(bool(self._diffs))
+ lexer = self.sci.lexer()
+
+ if lexer:
+ font = self.sci.lexer().font(0)
+ else:
+ font = self.sci.font()
+
+ fm = QFontMetrics(font)
+ maxWidth = fm.maxWidth()
+ lines = self.sci.text().split('\n')
+ widths = [fm.width(line) + maxWidth for line in lines]
+ self.maxWidth = max(widths)
+ self.updateScrollBar()
+
#
# These four functions are used by Shift+Cursor actions in revdetails
#
@@ -674,6 +691,15 @@ add(name, func)
menu.exec_(point)
+ def resizeEvent(self, event):
+ super(HgFileView, self).resizeEvent(event)
+ self.updateScrollBar()
+
+ def updateScrollBar(self):
+ sbWidth = self.sci.verticalScrollBar().width()
+ scrollWidth = self.maxWidth + sbWidth - self.sci.width()
+ self.sci.showHScrollBar(scrollWidth > 0)
+ self.sci.horizontalScrollBar().setRange(0, scrollWidth)
class AnnotateView(qscilib.Scintilla):
'QScintilla widget capable of displaying annotations'
|
@@ -10,7 +10,7 @@
from mercurial import ui, hg, error, commands, match, util, subrepo
-from tortoisehg.hgqt import htmlui, visdiff, qtlib, htmldelegate, thgrepo, cmdui
+from tortoisehg.hgqt import htmlui, visdiff, qtlib, htmldelegate, thgrepo, cmdui, settings
from tortoisehg.util import paths, hglib, thread2
from tortoisehg.hgqt.i18n import _
@@ -470,9 +470,12 @@ unit = _('files')
total = len(ctx.manifest())
count = 0
+ hasKbf = settings.hasExtension('kbfiles')
for wfile in ctx: # walk manifest
if self.canceled:
break
+ if hasKbf and thgrepo.isKbf(wfile):
+ continue
self.progress.emit(topic, count, wfile, unit, total)
count += 1
if not matchfn(wfile):
|
@@ -251,6 +251,9 @@ if not pathinstatus(path, status, uncleanpaths):
continue
+ origpath = path
+ path = self._repo.removeKbf(path)
+
e = treeroot
for p in hglib.tounicode(path).split('/'):
if not p in e:
@@ -258,7 +261,7 @@ e = e[p]
for st, filesofst in status.iteritems():
- if path in filesofst:
+ if origpath in filesofst:
e.setstatus(st)
break
else:
|
@@ -97,14 +97,15 @@
def run(self):
try:
- wctx = repo[None]
- wctx.status(ignored=True, unknown=True)
+ repo.bfstatus = True
+ stat = repo.status(ignored=True, unknown=True)
+ repo.bfstatus = False
trashcan = repo.join('Trashcan')
if os.path.isdir(trashcan):
trash = os.listdir(trashcan)
else:
trash = []
- self.files = wctx.unknown(), wctx.ignored(), trash
+ self.files = stat[4], stat[5], trash
except Exception, e:
self.error = str(e)
@@ -213,8 +214,10 @@ self.showMessage.emit('')
match = hglib.matchall(repo)
match.dir = directories.append
+ repo.bfstatus = True
status = repo.status(match=match, ignored=opts['ignored'],
unknown=opts['unknown'], clean=False)
+ repo.bfstatus = False
files = status[4] + status[5]
def remove(remove_func, name):
|
@@ -12,7 +12,7 @@
from tortoisehg.util import hglib, shlib
from tortoisehg.hgqt.i18n import _
-from tortoisehg.hgqt import qtlib, status, cmdui
+from tortoisehg.hgqt import qtlib, status, cmdui, bfprompt
from PyQt4.QtCore import *
from PyQt4.QtGui import *
@@ -101,6 +101,11 @@ hbox.addWidget(bb)
toplayout.addLayout(hbox)
self.bb = bb
+
+ if self.command == 'add' and 'kbfiles' in self.repo.extensions():
+ self.addBfilesButton = QPushButton(_("Add &Bfiles"))
+ self.addBfilesButton.clicked.connect(self.addBfiles)
+ bb.addButton(self.addBfilesButton, BB.ActionRole)
layout.addWidget(self.statusbar)
@@ -136,14 +141,20 @@ parent=self)
return
if self.command == 'remove':
- wctx = self.repo[None]
+ self.repo.bfstatus = True
+ repostate = self.repo.status()
+ self.repo.bfstatus = False
+ unknown, ignored = repostate[4:6]
for wfile in files:
- if wfile not in wctx:
+ if wfile in unknown or wfile in ignored:
try:
util.unlink(wfile)
except EnvironmentError:
pass
files.remove(wfile)
+ elif self.command == 'add' and 'kbfiles' in self.repo.extensions():
+ self.addWithPrompt(files)
+ return
if files:
cmdline.extend(files)
self.files = files
@@ -164,6 +175,33 @@ s.setValue('quickop/nobackup', self.chk.isChecked())
QDialog.reject(self)
+ def addBfiles(self):
+ cmdline = ['add', '--bf']
+ files = self.stwidget.getChecked()
+ if not files:
+ qtlib.WarningMsgBox(_('No files selected'),
+ _('No operation to perform'),
+ parent=self)
+ return
+ cmdline.extend(files)
+ self.files = files
+ self.cmd.run(cmdline)
+
+ def addWithPrompt(self, files):
+ result = bfprompt.promptForBfiles(self, self.repo.ui, self.repo, files)
+ if not result:
+ return
+ files, bfiles = result
+ if files:
+ cmdline = ['add']
+ cmdline.extend(files)
+ self.files = files
+ self.cmd.run(cmdline)
+ if bfiles:
+ cmdline = ['add', '--bf']
+ cmdline.extend(bfiles)
+ self.files = bfiles
+ self.cmd.run(cmdline)
instance = None
class HeadlessQuickop(QWidget):
|
@@ -230,6 +230,7 @@
showMessage = pyqtSignal(QString)
openRepo = pyqtSignal(QString, bool)
+ removeRepo = pyqtSignal(QString)
def __init__(self, parent, showSubrepos=False, showNetworkSubrepos=False,
showShortPaths=False):
@@ -648,7 +649,9 @@ self.tview.model().addGroup(_('New Group'))
def removeSelected(self):
+ root = self.selitem.internalPointer().rootpath()
self.tview.removeSelected()
+ self.removeRepo.emit(hglib.tounicode(root))
@pyqtSlot(QString, QString)
def shortNameChanged(self, uroot, uname):
|
|
@@ -7,7 +7,7 @@
import os
-from mercurial import ui, util, error
+from mercurial import ui, util, error, extensions
from tortoisehg.util import hglib, settings, paths, wconfig, i18n, bugtraq
from tortoisehg.hgqt.i18n import _
@@ -24,6 +24,12 @@_unspecstr = _('<unspecified>')
ENTRY_WIDTH = 300
+def hasExtension(extname):
+ for name, module in extensions.extensions():
+ if name == extname:
+ return True
+ return False
+
class SettingsCombo(QComboBox):
def __init__(self, parent=None, **opts):
QComboBox.__init__(self, parent, toolTip=opts['tooltip'])
@@ -315,6 +321,46 @@ return self.value() != self.curvalue
+class PathBrowser(QWidget):
+ def __init__(self, parent=None, **opts):
+ QWidget.__init__(self, parent, toolTip=opts['tooltip'])
+ self.opts = opts
+
+ self.lineEdit = QLineEdit()
+ completer = QCompleter(self)
+ completer.setModel(QDirModel(completer))
+ self.lineEdit.setCompleter(completer)
+
+ self.browseButton = QPushButton(_('&Browse...'))
+ self.browseButton.clicked.connect(self.browse)
+
+ layout = QHBoxLayout()
+ layout.setContentsMargins(0, 0, 0, 0)
+ layout.addWidget(self.lineEdit)
+ layout.addWidget(self.browseButton)
+ self.setLayout(layout)
+
+ def browse(self):
+ dir = QFileDialog.getExistingDirectory(self, directory=self.lineEdit.text(),
+ options=QFileDialog.ShowDirsOnly)
+ if dir:
+ self.lineEdit.setText(dir)
+
+ ## common APIs for all edit widgets
+ def setValue(self, curvalue):
+ self.curvalue = curvalue
+ if curvalue:
+ self.lineEdit.setText(hglib.tounicode(curvalue))
+ else:
+ self.lineEdit.setText('')
+
+ def value(self):
+ utext = self.lineEdit.text()
+ return utext and hglib.fromunicode(utext) or None
+
+ def isDirty(self):
+ return self.value() != self.curvalue
+
def genEditCombo(opts, defaults=[]):
opts['canedit'] = True
opts['defaults'] = defaults
@@ -352,6 +398,9 @@def genBugTraqEdit(opts):
return BugTraqConfigureEntry(**opts)
+def genPathBrowser(opts):
+ return PathBrowser(**opts)
+
def findIssueTrackerPlugins():
plugins = bugtraq.get_issue_plugins_with_names()
names = [("%s %s" % (key[0], key[1])) for key in plugins]
@@ -737,6 +786,16 @@ _fi(_('Target People'), 'reviewboard.target_people', genEditCombo,
_('A comma separated list of target people')),
)),
+
+({'name': 'kbfiles', 'label': _('Kiln Bfiles'), 'icon': 'kiln', 'extension': 'kbfiles'}, (
+ _fi(_('Patterns'), 'kilnbfiles.patterns', genEditCombo,
+ _('Files with names meeting the specified patterns will be automatically '
+ 'added as bfiles')),
+ _fi(_('Size'), 'kilnbfiles.size', genEditCombo,
+ _('Files of at least the specified size (in megabytes) will be added as bfiles')),
+ _fi(_('System Cache'), 'kilnbfiles.systemcache', genPathBrowser,
+ _('Path to the directory where a system-wide cache of bfiles will be stored')),
+ )),
)
@@ -915,6 +974,8 @@
# add page items to treeview
for meta, info in INFO:
+ if 'extension' in meta and not hasExtension(meta['extension']):
+ continue
if isinstance(meta['icon'], str):
icon = qtlib.geticon(meta['icon'])
else:
|
@@ -11,7 +11,7 @@
from tortoisehg.util import paths, hglib
from tortoisehg.hgqt.i18n import _
-from tortoisehg.hgqt import qtlib, wctxactions, visdiff, cmdui, fileview
+from tortoisehg.hgqt import qtlib, wctxactions, visdiff, cmdui, fileview, thgrepo
from PyQt4.QtCore import *
from PyQt4.QtGui import *
@@ -413,7 +413,7 @@
def __init__(self, repo, pctx, pats, opts, parent=None):
super(StatusThread, self).__init__()
- self.repo = hg.repository(repo.ui, repo.root)
+ self.repo = thgrepo.repository(repo.ui, repo.root)
self.pctx = pctx
self.pats = pats
self.opts = opts
@@ -434,7 +434,9 @@ # status and commit only pre-check MAR files
precheckfn = lambda x: x < 4
m = hglib.match(self.repo[None], self.pats)
+ self.repo.bfstatus = True
status = self.repo.status(match=m, **stopts)
+ self.repo.bfstatus = False
# Record all matched files as initially checked
for i, stat in enumerate(StatusType.preferredOrder):
if stat == 'S':
@@ -446,11 +448,15 @@ wctx = context.workingctx(self.repo, changes=status)
self.patchecked = patchecked
elif self.pctx:
+ self.repo.bfstatus = True
status = self.repo.status(node1=self.pctx.p1().node(), **stopts)
+ self.repo.bfstatus = False
wctx = context.workingctx(self.repo, changes=status)
else:
wctx = self.repo[None]
+ self.repo.bfstatus = True
wctx.status(**stopts)
+ self.repo.bfstatus = False
self.wctx = wctx
wctx.dirtySubrepos = []
|
@@ -12,6 +12,7 @@ import sys
import shutil
import tempfile
+import re
from PyQt4.QtCore import *
@@ -24,6 +25,7 @@from tortoisehg.util.patchctx import patchctx
_repocache = {}
+_kbfregex = re.compile(r'^\.kbf/')
if 'THGDEBUG' in os.environ:
def dbgoutput(*args):
@@ -263,8 +265,8 @@def _extendrepo(repo):
class thgrepository(repo.__class__):
- def changectx(self, changeid):
- '''Extends Mercurial's standard changectx() method to
+ def __getitem__(self, changeid):
+ '''Extends Mercurial's standard __getitem__() method to
a) return a thgchangectx with additional methods
b) return a patchctx if changeid is the name of an MQ
unapplied patch
@@ -283,7 +285,7 @@ os.path.isabs(changeid) and os.path.isfile(changeid):
return genPatchContext(repo, changeid)
- changectx = super(thgrepository, self).changectx(changeid)
+ changectx = super(thgrepository, self).__getitem__(changeid)
changectx.__class__ = _extendchangectx(changectx)
return changectx
@@ -536,6 +538,17 @@ dest = tempfile.mktemp(ext+'.bak', root+'_', trashcan)
shutil.copyfile(path, dest)
+ def isKbf(self, path):
+ return 'kbfiles' in self.extensions() and _kbfregex.match(path)
+
+ def removeKbf(self, path):
+ if 'kbfiles' in self.extensions():
+ path = _kbfregex.sub('', path)
+ return path
+
+ def standin(self, path):
+ return '.kbf/' + path
+
return thgrepository
@@ -602,11 +615,21 @@ summary += u' \u2026' # ellipsis ...
return summary
+
+ def hasBfile(self, file):
+ return 'kbfiles' in self._repo.extensions() and self._repo.standin(file) in self.manifest()
+ def isKbf(self, path):
+ return self._repo.isKbf(path)
+
+ def removeKbf(self, path):
+ return self._repo.removeKbf(path)
+
+ def standin(self, path):
+ return self._repo.standin(path)
+
return thgchangectx
-
-
_pctxcache = {}
def genPatchContext(repo, patchpath, rev=None):
global _pctxcache
@@ -651,3 +674,6 @@ raise
else:
f.close()
+
+def isKbf(path):
+ return _kbfregex.match(path)
|
@@ -8,8 +8,8 @@ import os
import re
-from mercurial import util, error, merge, commands
-from tortoisehg.hgqt import qtlib, htmlui, visdiff
+from mercurial import util, error, merge, commands, extensions
+from tortoisehg.hgqt import qtlib, htmlui, visdiff, bfprompt
from tortoisehg.util import hglib, shlib
from tortoisehg.hgqt.i18n import _
@@ -52,6 +52,8 @@ allactions.append(None)
make(_('&Forget'), forget, frozenset('MAC!'), 'filedelete')
make(_('&Add'), add, frozenset('I?'), 'fileadd')
+ if 'kbfiles' in self.repo.extensions():
+ make(_('Add &Bfiles'), addbf, frozenset('I?'))
make(_('&Detect Renames...'), guessRename, frozenset('A?!'),
'detect_rename')
make(_('&Ignore...'), ignore, frozenset('?'), 'ignore')
@@ -266,9 +268,30 @@ return True
def add(parent, ui, repo, files):
+ if 'kbfiles' in repo.extensions():
+ result = bfprompt.promptForBfiles(parent, ui, repo, files)
+ if not result:
+ return False
+ files, bfiles = result
+ for name, module in extensions.extensions():
+ if name == 'kbfiles':
+ override_add = module.bfsetup.override_add
+ if files:
+ override_add(commands.add, ui, repo, *files)
+ if bfiles:
+ override_add(commands.add, ui, repo, bf=1, *bfiles)
+ return True
commands.add(ui, repo, *files)
return True
+def addbf(parent, ui, repo, files):
+ for name, module in extensions.extensions():
+ if name == 'kbfiles':
+ override_add = module.bfsetup.override_add
+ override_add(commands.add, ui, repo, bf=True, *files)
+ return True
+ return False
+
def guessRename(parent, ui, repo, files):
from tortoisehg.hgqt.guess import DetectRenameDialog
dlg = DetectRenameDialog(repo, parent, *files)
|
@@ -48,6 +48,7 @@ rr.setObjectName('RepoRegistryView')
rr.showMessage.connect(self.showMessage)
rr.openRepo.connect(self.openRepo)
+ rr.removeRepo.connect(self.removeRepo)
rr.hide()
self.addDockWidget(Qt.LeftDockWidgetArea, rr)
self.activeRepoChanged.connect(rr.setActiveTabRepo)
@@ -473,6 +474,15 @@ root = hglib.fromunicode(root)
self._openRepo(root, reuse)
+ def removeRepo(self, root):
+ """ Close tab if the repo is removed from reporegistry [unicode] """
+ root = hglib.fromunicode(root)
+ for i in xrange(self.repoTabsWidget.count()):
+ w = self.repoTabsWidget.widget(i)
+ if hglib.tounicode(w.repo.root) == os.path.normpath(root):
+ self.repoTabCloseRequested(i)
+ return
+
@pyqtSlot(QString)
def openLinkedRepo(self, path):
self.showRepo(path)
|
@@ -90,7 +90,9 @@ time.sleep(tdelta)
repo = hg.repository(ui, root) # a fresh repo object is needed
+ repo.bfstatus = True
repostate = repo.status() # will update .hg/dirstate as a side effect
+ repo.bfstatus = False
modified, added, removed, deleted = repostate[:4]
dirstatus = {}
|
Loading...