Commit ca8c98e3 authored by Yuya Nishihara's avatar Yuya Nishihara

Merge with default (freeze for 5.4)

--HG--
branch : stable
......@@ -13,7 +13,7 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
BuildArch: noarch
BuildRequires: python, python-devel, gettext, python-sphinx
BuildRequires: PyQt4-devel, desktop-file-utils
Requires: python >= 2.7, python-iniparse, mercurial >= 5.2
Requires: python >= 2.7, python-iniparse, mercurial >= 5.3
# gconf needed at util/shlib.py for browse_url(url).
Requires: gnome-python2-gconf
Requires: PyQt4 >= 4.7.5, qscintilla-python, python-pygments
......
......@@ -508,8 +508,10 @@ class GraphBuilder(object):
else:
uargs = []
try:
hg.graft('-r', source, '-t', tool, *uargs)
ret = hg.graft('-r', source, '-t', tool, *uargs)[0]
except error.Abort:
ret = 1
if ret:
self._resolveall(parents[0], int(source))
self._runcmd(hg.graft, '-c', *uargs)
......
# close_branch.py - Close branch dialog for TortoiseHg
#
# Copyright 2020 Bram Belpaire <belpairebram@hotmail.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2, incorporated herein by reference.
from __future__ import absolute_import
from mercurial import (
pycompat,
)
from .qtgui import (
QSizePolicy,
QLineEdit,
QFormLayout,
QLabel
)
from ..util import (
hglib,
i18n,
)
from ..util.i18n import _
from . import (
cmdui,
)
class CloseWidget(cmdui.AbstractCmdWidget):
def __init__(self, repoagent, rev, parent=None):
super(CloseWidget, self).__init__(parent)
self._repoagent = repoagent
self._repo = repoagent.rawRepo()
self._rev = rev
form = QFormLayout()
form.setContentsMargins(0, 0, 0, 0)
# simple widget with only an editable commit message textbox
self.setLayout(form)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
# add revision information about selected revision
form.addRow(_('Revision:'), QLabel('%d (%s)' % (rev, self._repo[rev])))
# commit message
self.hg_commit = QLineEdit()
# automatic message
msgset = i18n.keepgettext()._('Close %s branch')
str_msg = msgset['str']
self.hg_commit.setText(pycompat.unicode(str_msg) %
hglib.tounicode(self._repo[self._rev].branch()))
form.addRow(_('Commit message:'), self.hg_commit)
def compose_command(self):
rev = '%d' % self._rev
cmdline = hglib.buildcmdargs('close-head', m=self.hg_commit.text(),
r=rev)
return cmdline
def runCommand(self):
cmdline = self.compose_command()
return self._repoagent.runCommand(cmdline, self)
def canRunCommand(self):
return True
def createCloseBranchDialog(repoagent, rev, parent):
dlg = cmdui.CmdControlDialog(parent)
dlg.setWindowTitle(_('Close Branch - %s') % repoagent.displayName())
dlg.setRunButtonText(_('&Close Branch'))
dlg.setCommandWidget(CloseWidget(repoagent, rev, dlg))
return dlg
......@@ -1007,6 +1007,7 @@ class CommitWidget(QWidget, qtlib.TaskWidget):
if c.excludecount > 0 and c.excludecount < len(c.hunks):
partials.append(fname)
self.files = set(files + partials)
self.files.update(self.stwidget.getCheckedAmends())
canemptycommit = bool(brcmd or newbranch or amend)
if not (self.files or canemptycommit or merge):
qtlib.WarningMsgBox(_('No files checked'),
......
......@@ -215,10 +215,7 @@ class GraftDialog(QDialog):
def abort(self):
self.abortbtn.setDisabled(True)
if os.path.exists(self._graftstatefile):
# Remove the existing graftstate file!
os.remove(self._graftstatefile)
cmdline = hglib.buildcmdargs('update', clean=True, rev='p1()')
cmdline = hglib.buildcmdargs('graft', abort=True)
sess = self._runCommand(cmdline)
sess.commandFinished.connect(self._abortFinished)
......
......@@ -38,6 +38,10 @@ from .qtgui import (
QVBoxLayout,
)
from hgext.largefiles import (
lfutil,
)
from mercurial import (
hg,
patch,
......@@ -203,10 +207,9 @@ class DetectRenameDialog(QDialog):
def refresh(self):
self.repo.thginvalidate()
self.repo.lfstatus = True
wctx = self.repo[None]
ws = wctx.status(listunknown=True)
self.repo.lfstatus = False
with lfutil.lfstatus(self.repo):
wctx = self.repo[None]
ws = wctx.status(listunknown=True)
self.unrevlist.clear()
dests = []
for u in ws.unknown:
......
......@@ -33,6 +33,10 @@ from .qtgui import (
QVBoxLayout,
)
from hgext.largefiles import (
lfutil,
)
from mercurial import (
commands,
error,
......@@ -289,9 +293,8 @@ class HgignoreDialog(QDialog):
try:
self.repo.thginvalidate()
self.repo.lfstatus = True
self.lclunknowns = self.repo.status(unknown=True).unknown
self.repo.lfstatus = False
with lfutil.lfstatus(self.repo):
self.lclunknowns = self.repo.status(unknown=True).unknown
except (EnvironmentError, error.RepoError) as e:
qtlib.WarningMsgBox(_('Unable to read repository status'),
uni(str(e)), parent=self)
......
......@@ -28,6 +28,10 @@ from .qtgui import (
QIcon,
)
from hgext.largefiles import (
lfutil,
)
from mercurial import (
error,
match as matchmod,
......@@ -609,11 +613,10 @@ def _populaterepo(roote, repo, nodeop, statusfilter, match):
ctx = roote.ctx
pctx = roote.pctx
repo.lfstatus = True
try:
with lfutil.lfstatus(repo):
stat = repo.status(pctx, ctx, match, clean='C' in statusfilter)
finally:
repo.lfstatus = False
for st, files in zip('MAR!?IC', stat):
if st not in statusfilter:
continue
......
......@@ -7,8 +7,6 @@
from __future__ import absolute_import
import json
from mercurial import (
pycompat,
)
......@@ -82,6 +80,8 @@ class PhabReviewDialog(QDialog):
self._repoagent = repoagent
self._cmdsession = cmdcore.nullCmdSession()
self._rescansession = cmdcore.nullCmdSession()
self._namesession = cmdcore.nullCmdSession()
self._my_username = None
self._qui = Ui_PhabReviewDialog()
self._qui.setupUi(self)
......@@ -432,7 +432,9 @@ class PhabReviewDialog(QDialog):
username = fields.get('username')
roles = fields.get('roles', [])
if not realname or not username:
# An error is raised when a user self assigns a review, so skip the
# current user.
if not realname or not username or username == self._my_username:
continue
u = user(username, realname, roles)
......@@ -484,8 +486,37 @@ class PhabReviewDialog(QDialog):
availablereviewers.clear()
self._qui.rescan_button.setEnabled(False)
# On the first click, fetch the current user's name for the purpose of
# excluding them from the list. If the username is known, trust it.
if self._my_username is None:
self._get_my_username()
self._queryreviewers(None)
def _get_my_username(self):
"""Spawns the phabricator command to return the current user's info."""
buf = QBuffer()
buf.setData(b'{}')
buf.open(QIODevice.ReadOnly)
cmdline = hglib.buildcmdargs("debugcallconduit", "user.whoami")
self._namesession = sess = self._repoagent.runCommand(cmdline)
sess.setCaptureOutput(True)
sess.setInputDevice(buf)
sess.commandFinished.connect(self._update_self_username)
@pyqtSlot()
def _update_self_username(self):
"""Handles the completion of the current user info fetch."""
exitcode = self._namesession.exitCode()
if exitcode == 0:
output = bytes(self._namesession.readAll())
results = pycompat.json_loads(output)
self._my_username = results.get("userName")
@pyqtSlot()
def _updateavailablereviewers(self):
......
# pick.py - Pick dialog for TortoiseHg
#
# Copyright 2010 Steve Borho <steve@borho.org>
# Copyright 2020 Matt Harbison <mharbison72@gmail.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2, incorporated herein by reference.
from __future__ import absolute_import
from mercurial import (
state as statemod,
)
from .qtcore import (
QSettings,
QTimer,
Qt,
pyqtSlot,
)
from .qtgui import (
QCheckBox,
QDialog,
QDialogButtonBox,
QGroupBox,
QMessageBox,
QVBoxLayout,
)
from ..util import hglib
from ..util.i18n import _
from . import (
cmdcore,
cmdui,
csinfo,
qtlib,
resolve,
thgrepo,
wctxcleaner,
)
class PickDialog(QDialog):
def __init__(self, repoagent, parent, **opts):
super(PickDialog, self).__init__(parent)
self.cmdstate = statemod.cmdstate(repoagent.rawRepo(), b"pickstate")
# TODO: self.setWindowIcon(qtlib.geticon('hg-pick'))
self.setWindowFlags(self.windowFlags()
& ~Qt.WindowContextHelpButtonHint)
self._repoagent = repoagent
self._cmdsession = cmdcore.nullCmdSession()
self.opts = opts
box = QVBoxLayout()
box.setSpacing(8)
box.setContentsMargins(*(6,)*4)
self.setLayout(box)
style = csinfo.panelstyle(selectable=True)
srcb = QGroupBox(_('Pick changeset'))
srcb.setLayout(QVBoxLayout())
srcb.layout().setContentsMargins(*(2,)*4)
source = csinfo.create(self.repo, opts['rev'], style, withupdate=True)
srcb.layout().addWidget(source)
self.sourcecsinfo = source
box.addWidget(srcb)
sep = qtlib.LabeledSeparator(_('Options'))
box.addWidget(sep)
self.autoresolvechk = QCheckBox(_('Automatically resolve merge '
'conflicts where possible'))
box.addWidget(self.autoresolvechk)
self._cmdlog = cmdui.LogWidget(self)
self._cmdlog.hide()
box.addWidget(self._cmdlog, 2)
self._stbar = cmdui.ThgStatusBar(self)
self._stbar.setSizeGripEnabled(False)
self._stbar.linkActivated.connect(self.linkActivated)
box.addWidget(self._stbar)
bbox = QDialogButtonBox()
self.cancelbtn = bbox.addButton(QDialogButtonBox.Cancel)
self.cancelbtn.clicked.connect(self.reject)
self.runbtn = bbox.addButton(_('Pick'),
QDialogButtonBox.ActionRole)
self.runbtn.clicked.connect(self.runCommand)
self.abortbtn = bbox.addButton(_('Abort'),
QDialogButtonBox.ActionRole)
self.abortbtn.clicked.connect(self.abort)
box.addWidget(bbox)
self._wctxcleaner = wctxcleaner.WctxCleaner(repoagent, self)
self._wctxcleaner.checkFinished.connect(self._onWctxCheckFinished)
if self.checkResolve():
for w in (srcb, sep):
w.setHidden(True)
self._cmdlog.show()
else:
self._stbar.showMessage(_('Checking...'))
self.abortbtn.setEnabled(False)
self.runbtn.setEnabled(False)
QTimer.singleShot(0, self._wctxcleaner.check)
self.setMinimumWidth(480)
self.setMaximumHeight(800)
self.resize(0, 340)
self.setWindowTitle(_('Pick - %s') % repoagent.displayName())
self._readSettings()
@property
def repo(self):
return self._repoagent.rawRepo()
def _readSettings(self):
"""Initialize widgets on this dialog from persistent storage.
"""
qs = QSettings()
qs.beginGroup('pick')
self.autoresolvechk.setChecked(
self._repoagent.configBool('tortoisehg', 'autoresolve',
qtlib.readBool(qs, 'autoresolve', True)))
qs.endGroup()
def _writeSettings(self):
"""Save the option widget states on this dialog to persistent storage.
"""
qs = QSettings()
qs.beginGroup('pick')
qs.setValue('autoresolve', self.autoresolvechk.isChecked())
qs.endGroup()
@pyqtSlot(bool)
def _onWctxCheckFinished(self, clean):
"""The callback when the command to check that wdir is clean completes.
"""
if not clean:
self.runbtn.setEnabled(False)
txt = _('Before pick, you must '
'<a href="commit"><b>commit</b></a>, '
'<a href="shelve"><b>shelve</b></a> to patch, '
'or <a href="discard"><b>discard</b></a> changes.')
else:
self.runbtn.setEnabled(True)
txt = _('You may continue the pick')
self._stbar.showMessage(txt)
def runCommand(self):
"""The handler for clicking the operation action button.
This issues the main command for the dialog, or ``--continue`` if there
is an interrupted operation.
"""
self.runbtn.setEnabled(False)
self.cancelbtn.setVisible(False)
itool = self.autoresolvechk.isChecked() and 'merge' or 'fail'
opts = {'config': 'ui.merge=internal:%s' % itool}
if self.cmdstate.exists():
opts['continue'] = True
else:
opts['rev'] = hglib.tounicode(str(self.opts.get('rev')))
cmdline = hglib.buildcmdargs('pick', **opts)
sess = self._runCommand(cmdline)
sess.commandFinished.connect(self._commandFinished)
def abort(self):
"""The handler for clicking the Abort button.
This issues a command to abort the interrupted operation.
"""
cmdline = hglib.buildcmdargs('pick', abort=True)
sess = self._runCommand(cmdline)
sess.commandFinished.connect(self._abortFinished)
def _runCommand(self, cmdline):
assert self._cmdsession.isFinished()
self._cmdsession = sess = self._repoagent.runCommand(cmdline, self)
sess.commandFinished.connect(self._stbar.clearProgress)
sess.outputReceived.connect(self._cmdlog.appendLog)
sess.progressReceived.connect(self._stbar.setProgress)
cmdui.updateStatusMessage(self._stbar, sess)
return sess
@pyqtSlot(int)
def _commandFinished(self, ret):
"""The callback when the main command (or ``--continue``) completes."""
# TODO since hg 2.6, pick will end with ret=1 in case of "unresolved
# conflicts", so we can fine-tune checkResolve() later.
if self.checkResolve() is False:
msg = _('Pick is complete')
if ret == 255:
msg = _('Pick failed')
self._cmdlog.show() # contains hint
self._stbar.showMessage(msg, error=(ret == 255))
self._makeCloseButton()
@pyqtSlot()
def _abortFinished(self):
"""The callback when the abort command completes."""
if self.checkResolve() is False:
self._stbar.showMessage(_('Pick aborted'))
self._makeCloseButton()
def _makeCloseButton(self):
self.runbtn.setEnabled(True)
self.runbtn.setText(_('Close'))
self.runbtn.clicked.disconnect(self.runCommand)
self.runbtn.clicked.connect(self.accept)
def checkResolve(self):
for root, path, status in thgrepo.recursiveMergeStatus(self.repo):
if status == b'u':
txt = _('Pick generated merge <b>conflicts</b> that must '
'be <a href="resolve"><b>resolved</b></a>')
self.runbtn.setEnabled(False)
break
else:
self.runbtn.setEnabled(True)
txt = _('You may continue the pick')
self._stbar.showMessage(txt)
if self.cmdstate.exists():
self.abortbtn.setEnabled(True)
self.runbtn.setText('Continue')
return True
else:
self.abortbtn.setEnabled(False)
return False
def linkActivated(self, cmd):
if cmd == 'resolve':
dlg = resolve.ResolveDialog(self._repoagent, self)
dlg.exec_()
self.checkResolve()
else:
self._wctxcleaner.runCleaner(cmd)
def reject(self):
"""The handler for clicking the Cancel button.
If the user confirms the cancellation, the operation is left in the
interrupted state.
"""
if self.cmdstate.exists():
main = _('Exiting with an unfinished pick is not recommended.')
text = _('Consider aborting the pick first.')
labels = ((QMessageBox.Yes, _('&Exit')),
(QMessageBox.No, _('Cancel')))
if not qtlib.QuestionMsgBox(_('Confirm Exit'), main, text,
labels=labels, parent=self):
return
super(PickDialog, self).reject()
def done(self, r):
self._writeSettings()
super(PickDialog, self).done(r)
......@@ -27,6 +27,10 @@ from .qtgui import (
qApp,
)
from hgext.largefiles import (
lfutil,
)
from mercurial import (
hg,
scmutil,
......@@ -126,9 +130,8 @@ class PurgeDialog(QDialog):
def run(self):
try:
repo.lfstatus = True
stat = repo.status(ignored=True, unknown=True)
repo.lfstatus = False
with lfutil.lfstatus(repo):
stat = repo.status(ignored=True, unknown=True)
trashcan = repo.vfs.join(b'Trashcan')
if os.path.isdir(trashcan):
trash = os.listdir(trashcan)
......@@ -249,10 +252,9 @@ class PurgeThread(QThread):
self.showMessage.emit('')
match = scmutil.matchall(repo)
match.explicitdir = match.traversedir = directories.append
repo.lfstatus = True
status = repo.status(match=match, ignored=opts['ignored'],
unknown=opts['unknown'], clean=False)
repo.lfstatus = False
with lfutil.lfstatus(repo):
status = repo.status(match=match, ignored=opts['ignored'],
unknown=opts['unknown'], clean=False)
files = []
if opts['unknown']:
files.extend(status.unknown)
......
......@@ -27,6 +27,10 @@ from .qtgui import (
QVBoxLayout,
)
from hgext.largefiles import (
lfutil,
)
from mercurial import (
error,
pycompat,
......@@ -217,15 +221,14 @@ class QuickOpDialog(QDialog):
parent=self)
return
if self.command == 'remove':
self.repo.lfstatus = True
try:
repostate = self.repo.status()
except (EnvironmentError, error.Abort) as e:
qtlib.WarningMsgBox(_('Unable to read repository status'),
hglib.tounicode(str(e)), parent=self)
return
finally:
self.repo.lfstatus = False
with lfutil.lfstatus(self.repo):
try:
repostate = self.repo.status()
except (EnvironmentError, error.Abort) as e:
qtlib.WarningMsgBox(_('Unable to read repository status'),
hglib.tounicode(str(e)), parent=self)
return
if not self.chk.isChecked():
selmodified = []
for wfile in files:
......
......@@ -62,6 +62,7 @@ from . import (
backout,
bisect,
bookmark,
close_branch,
cmdcore,
cmdui,
compress,
......@@ -71,6 +72,7 @@ from . import (
matching,
merge,
mq,
pick,