11 import os |
11 import os |
12 import re |
12 import re |
13 import shutil |
13 import shutil |
14 from urllib.parse import quote |
14 from urllib.parse import quote |
15 |
15 |
16 from PyQt5.QtCore import pyqtSignal, QProcess, QRegExp, QCoreApplication |
16 from PyQt5.QtCore import pyqtSignal, QProcess, QCoreApplication |
17 from PyQt5.QtWidgets import QLineEdit, QDialog, QInputDialog, QApplication |
17 from PyQt5.QtWidgets import QLineEdit, QDialog, QInputDialog, QApplication |
18 |
18 |
19 from E5Gui.E5Application import e5App |
19 from E5Gui.E5Application import e5App |
20 from E5Gui import E5MessageBox |
20 from E5Gui import E5MessageBox |
21 |
21 |
97 self.blame = None |
97 self.blame = None |
98 self.repoBrowser = None |
98 self.repoBrowser = None |
99 self.logBrowser = None |
99 self.logBrowser = None |
100 |
100 |
101 # regular expression object for evaluation of the status output |
101 # regular expression object for evaluation of the status output |
102 self.rx_status1 = QRegExp( |
102 self.rx_status1 = re.compile( |
103 '(.{8})\\s+([0-9-]+)\\s+([0-9?]+)\\s+(\\S+)\\s+(.+)') |
103 '(.{8})\\s+([0-9-]+)\\s+([0-9?]+)\\s+(\\S+)\\s+(.+)') |
104 self.rx_status2 = QRegExp('(.{8})\\s+(.+)\\s*') |
104 self.rx_status2 = re.compile('(.{8})\\s+(.+)\\s*') |
105 self.statusCache = {} |
105 self.statusCache = {} |
106 |
106 |
107 self.__commitData = {} |
107 self.__commitData = {} |
108 self.__commitDialog = None |
108 self.__commitDialog = None |
109 |
109 |
845 @param project reference to the project object |
845 @param project reference to the project object |
846 @param target new name of the file/directory (string) |
846 @param target new name of the file/directory (string) |
847 @param noDialog flag indicating quiet operations |
847 @param noDialog flag indicating quiet operations |
848 @return flag indicating successfull operation (boolean) |
848 @return flag indicating successfull operation (boolean) |
849 """ |
849 """ |
850 rx_prot = QRegExp('(file:|svn:|svn+ssh:|http:|https:).+') |
850 rx_prot = re.compile('(file:|svn:|svn+ssh:|http:|https:).+') |
851 opts = self.options['global'][:] |
851 opts = self.options['global'][:] |
852 force = '--force' in opts |
852 force = '--force' in opts |
853 if force: |
853 if force: |
854 del opts[opts.index('--force')] |
854 del opts[opts.index('--force')] |
855 |
855 |
866 if accepted: |
866 if accepted: |
867 target, force = dlg.getData() |
867 target, force = dlg.getData() |
868 if not target: |
868 if not target: |
869 return False |
869 return False |
870 |
870 |
871 if not rx_prot.exactMatch(target): |
871 if rx_prot.fullmatch(target) is None: |
872 isDir = os.path.isdir(name) |
872 isDir = os.path.isdir(name) |
873 else: |
873 else: |
874 isDir = False |
874 isDir = False |
875 |
875 |
876 if accepted: |
876 if accepted: |
877 args = [] |
877 args = [] |
878 args.append('move') |
878 args.append('move') |
879 self.addArguments(args, opts) |
879 self.addArguments(args, opts) |
880 if force: |
880 if force: |
881 args.append('--force') |
881 args.append('--force') |
882 if rx_prot.exactMatch(target): |
882 if rx_prot.fullmatch(target) is not None: |
883 args.append('--message') |
883 args.append('--message') |
884 args.append('Moving {0} to {1}'.format(name, target)) |
884 args.append('Moving {0} to {1}'.format(name, target)) |
885 target = self.__svnURL(target) |
885 target = self.__svnURL(target) |
886 args.append(name) |
886 args.append(name) |
887 args.append(target) |
887 args.append(target) |
893 .format(name)) |
893 .format(name)) |
894 res = dia.startProcess(args) |
894 res = dia.startProcess(args) |
895 if res: |
895 if res: |
896 dia.exec() |
896 dia.exec() |
897 res = dia.normalExit() |
897 res = dia.normalExit() |
898 if res and not rx_prot.exactMatch(target): |
898 if res and rx_prot.fullmatch(target) is None: |
899 if target.startswith(project.getProjectPath()): |
899 if target.startswith(project.getProjectPath()): |
900 if isDir: |
900 if isDir: |
901 project.moveDirectory(name, target) |
901 project.moveDirectory(name, target) |
902 else: |
902 else: |
903 project.renameFileInPdata(name, target) |
903 project.renameFileInPdata(name, target) |
990 self.allTagsBranchesList.insert(0, tag) |
990 self.allTagsBranchesList.insert(0, tag) |
991 else: |
991 else: |
992 return |
992 return |
993 |
993 |
994 if self.otherData["standardLayout"]: |
994 if self.otherData["standardLayout"]: |
995 rx_base = QRegExp('(.+)/(trunk|tags|branches).*') |
995 rx_base = re.compile('(.+)/(trunk|tags|branches).*') |
996 if not rx_base.exactMatch(reposURL): |
996 match = rx_base.fullmatch(reposURL) |
|
997 if match is None: |
997 E5MessageBox.critical( |
998 E5MessageBox.critical( |
998 self.__ui, |
999 self.__ui, |
999 self.tr("Subversion Error"), |
1000 self.tr("Subversion Error"), |
1000 self.tr( |
1001 self.tr( |
1001 """The URL of the project repository has an""" |
1002 """The URL of the project repository has an""" |
1002 """ invalid format. The tag operation will""" |
1003 """ invalid format. The tag operation will""" |
1003 """ be aborted""")) |
1004 """ be aborted""")) |
1004 return |
1005 return |
1005 |
1006 |
1006 reposRoot = rx_base.cap(1) |
1007 reposRoot = match.group(1) |
1007 if tagOp in [1, 4]: |
1008 if tagOp in [1, 4]: |
1008 url = '{0}/tags/{1}'.format(reposRoot, quote(tag)) |
1009 url = '{0}/tags/{1}'.format(reposRoot, quote(tag)) |
1009 elif tagOp in [2, 8]: |
1010 elif tagOp in [2, 8]: |
1010 url = '{0}/branches/{1}'.format(reposRoot, quote(tag)) |
1011 url = '{0}/branches/{1}'.format(reposRoot, quote(tag)) |
1011 else: |
1012 else: |
1112 self.allTagsBranchesList.insert(0, tag) |
1113 self.allTagsBranchesList.insert(0, tag) |
1113 else: |
1114 else: |
1114 return False |
1115 return False |
1115 |
1116 |
1116 if self.otherData["standardLayout"]: |
1117 if self.otherData["standardLayout"]: |
1117 rx_base = QRegExp('(.+)/(trunk|tags|branches).*') |
1118 rx_base = re.compile('(.+)/(trunk|tags|branches).*') |
1118 if not rx_base.exactMatch(reposURL): |
1119 match = rx_base.fullmatch(reposURL) |
|
1120 if match is None: |
1119 E5MessageBox.critical( |
1121 E5MessageBox.critical( |
1120 self.__ui, |
1122 self.__ui, |
1121 self.tr("Subversion Error"), |
1123 self.tr("Subversion Error"), |
1122 self.tr( |
1124 self.tr( |
1123 """The URL of the project repository has an""" |
1125 """The URL of the project repository has an""" |
1124 """ invalid format. The switch operation will""" |
1126 """ invalid format. The switch operation will""" |
1125 """ be aborted""")) |
1127 """ be aborted""")) |
1126 return False |
1128 return False |
1127 |
1129 |
1128 reposRoot = rx_base.cap(1) |
1130 reposRoot = match.group(1) |
1129 tn = tag |
1131 tn = tag |
1130 if tagType == 1: |
1132 if tagType == 1: |
1131 url = '{0}/tags/{1}'.format(reposRoot, quote(tag)) |
1133 url = '{0}/tags/{1}'.format(reposRoot, quote(tag)) |
1132 elif tagType == 2: |
1134 elif tagType == 2: |
1133 url = '{0}/branches/{1}'.format(reposRoot, quote(tag)) |
1135 url = '{0}/branches/{1}'.format(reposRoot, quote(tag)) |
1182 self.mergeList[0].insert(0, urlrev1) |
1184 self.mergeList[0].insert(0, urlrev1) |
1183 if urlrev2 in self.mergeList[1]: |
1185 if urlrev2 in self.mergeList[1]: |
1184 self.mergeList[1].remove(urlrev2) |
1186 self.mergeList[1].remove(urlrev2) |
1185 self.mergeList[1].insert(0, urlrev2) |
1187 self.mergeList[1].insert(0, urlrev2) |
1186 |
1188 |
1187 rx_rev = QRegExp('\\d+|HEAD') |
1189 rx_rev = re.compile('\\d+|HEAD') |
1188 |
1190 |
1189 args = [] |
1191 args = [] |
1190 args.append('merge') |
1192 args.append('merge') |
1191 self.addArguments(args, opts) |
1193 self.addArguments(args, opts) |
1192 if self.version >= (1, 5, 0): |
1194 if self.version >= (1, 5, 0): |
1193 args.append('--accept') |
1195 args.append('--accept') |
1194 args.append('postpone') |
1196 args.append('postpone') |
1195 if force: |
1197 if force: |
1196 args.append('--force') |
1198 args.append('--force') |
1197 if rx_rev.exactMatch(urlrev1): |
1199 if rx_rev.fullmatch(urlrev1) is not None: |
1198 args.append('-r') |
1200 args.append('-r') |
1199 args.append('{0}:{1}'.format(urlrev1, urlrev2)) |
1201 args.append('{0}:{1}'.format(urlrev1, urlrev2)) |
1200 if not target: |
1202 if not target: |
1201 args.append(name) |
1203 args.append(name) |
1202 else: |
1204 else: |
1352 finished = process.waitForFinished(30000) |
1354 finished = process.waitForFinished(30000) |
1353 if finished and process.exitCode() == 0: |
1355 if finished and process.exitCode() == 0: |
1354 output = str(process.readAllStandardOutput(), ioEncoding, |
1356 output = str(process.readAllStandardOutput(), ioEncoding, |
1355 'replace') |
1357 'replace') |
1356 for line in output.splitlines(): |
1358 for line in output.splitlines(): |
1357 if self.rx_status1.exactMatch(line): |
1359 match = self.rx_status1.fullmatch(line) |
1358 flags = str(self.rx_status1.cap(1)) |
1360 if match is not None: |
1359 path = self.rx_status1.cap(5).strip() |
1361 flags = self.rx_status1.group(1) |
1360 elif self.rx_status2.exactMatch(line): |
1362 path = self.rx_status1.group(5).strip() |
1361 flags = str(self.rx_status2.cap(1)) |
|
1362 path = self.rx_status2.cap(2).strip() |
|
1363 else: |
1363 else: |
1364 continue |
1364 match = self.rx_status2.fullmatch(line) |
|
1365 if match is not None: |
|
1366 flags = self.rx_status2.group(1) |
|
1367 path = self.rx_status2.group(2).strip() |
|
1368 else: |
|
1369 continue |
1365 name = os.path.normcase(path) |
1370 name = os.path.normcase(path) |
1366 if flags[0] not in "?I": |
1371 if flags[0] not in "?I": |
1367 if name in names: |
1372 if name in names: |
1368 names[name] = self.canBeCommitted |
1373 names[name] = self.canBeCommitted |
1369 self.statusCache[name] = self.canBeCommitted |
1374 self.statusCache[name] = self.canBeCommitted |
1418 finished = process.waitForFinished(30000) |
1423 finished = process.waitForFinished(30000) |
1419 if finished and process.exitCode() == 0: |
1424 if finished and process.exitCode() == 0: |
1420 output = str(process.readAllStandardOutput(), ioEncoding, |
1425 output = str(process.readAllStandardOutput(), ioEncoding, |
1421 'replace') |
1426 'replace') |
1422 for line in output.splitlines(): |
1427 for line in output.splitlines(): |
1423 if self.rx_status1.exactMatch(line): |
1428 match = self.rx_status1.fullmatch(line) |
1424 flags = self.rx_status1.cap(1) |
1429 if match is not None: |
1425 path = self.rx_status1.cap(5).strip() |
1430 flags = self.rx_status1.group(1) |
1426 elif self.rx_status2.exactMatch(line): |
1431 path = self.rx_status1.group(5).strip() |
1427 flags = self.rx_status2.cap(1) |
|
1428 path = self.rx_status2.cap(2).strip() |
|
1429 else: |
1432 else: |
1430 continue |
1433 match = self.rx_status2.fullmatch(line) |
|
1434 if match is not None: |
|
1435 flags = self.rx_status2.group(1) |
|
1436 path = self.rx_status2.group(2).strip() |
|
1437 else: |
|
1438 continue |
1431 name = os.path.normcase(path) |
1439 name = os.path.normcase(path) |
1432 if flags[0] not in "?I": |
1440 if flags[0] not in "?I": |
1433 if name in names: |
1441 if name in names: |
1434 names[name] = self.canBeCommitted |
1442 names[name] = self.canBeCommitted |
1435 self.statusCache[name] = self.canBeCommitted |
1443 self.statusCache[name] = self.canBeCommitted |
1695 @param name file/directory name to be copied (string) |
1703 @param name file/directory name to be copied (string) |
1696 @param project reference to the project object |
1704 @param project reference to the project object |
1697 @return flag indicating successfull operation (boolean) |
1705 @return flag indicating successfull operation (boolean) |
1698 """ |
1706 """ |
1699 from .SvnCopyDialog import SvnCopyDialog |
1707 from .SvnCopyDialog import SvnCopyDialog |
1700 rx_prot = QRegExp('(file:|svn:|svn+ssh:|http:|https:).+') |
1708 rx_prot = re.compile('(file:|svn:|svn+ssh:|http:|https:).+') |
1701 dlg = SvnCopyDialog(name) |
1709 dlg = SvnCopyDialog(name) |
1702 res = False |
1710 res = False |
1703 if dlg.exec() == QDialog.Accepted: |
1711 if dlg.exec() == QDialog.Accepted: |
1704 target, force = dlg.getData() |
1712 target, force = dlg.getData() |
1705 |
1713 |
1706 args = [] |
1714 args = [] |
1707 args.append('copy') |
1715 args.append('copy') |
1708 self.addArguments(args, self.options['global']) |
1716 self.addArguments(args, self.options['global']) |
1709 if rx_prot.exactMatch(target): |
1717 match = rx_prot.fullmatch(target) |
|
1718 if match is not None: |
1710 args.append('--message') |
1719 args.append('--message') |
1711 args.append('Copying {0} to {1}'.format(name, target)) |
1720 args.append('Copying {0} to {1}'.format(name, target)) |
1712 target = self.__svnURL(target) |
1721 target = self.__svnURL(target) |
1713 args.append(name) |
1722 args.append(name) |
1714 args.append(target) |
1723 args.append(target) |
2260 Public method to get a list of all defined change lists. |
2269 Public method to get a list of all defined change lists. |
2261 |
2270 |
2262 @return list of defined change list names (list of strings) |
2271 @return list of defined change list names (list of strings) |
2263 """ |
2272 """ |
2264 changelists = [] |
2273 changelists = [] |
2265 rx_changelist = QRegExp('--- \\S+ .([\\w\\s]+).:\\s*') |
2274 rx_changelist = re.compile('--- \\S+ .([\\w\\s]+).:\\s*') |
2266 # three dashes, Changelist (translated), quote, |
2275 # three dashes, Changelist (translated), quote, |
2267 # changelist name, quote, : |
2276 # changelist name, quote, : |
2268 |
2277 |
2269 args = [] |
2278 args = [] |
2270 args.append("status") |
2279 args.append("status") |
2282 output = str(process.readAllStandardOutput(), |
2291 output = str(process.readAllStandardOutput(), |
2283 Preferences.getSystem("IOEncoding"), |
2292 Preferences.getSystem("IOEncoding"), |
2284 'replace') |
2293 'replace') |
2285 if output: |
2294 if output: |
2286 for line in output.splitlines(): |
2295 for line in output.splitlines(): |
2287 if rx_changelist.exactMatch(line): |
2296 match = rx_changelist.fullmatch(line) |
2288 changelist = rx_changelist.cap(1) |
2297 if match is not None: |
|
2298 changelist = match.group(1) |
2289 if changelist not in changelists: |
2299 if changelist not in changelists: |
2290 changelists.append(changelist) |
2300 changelists.append(changelist) |
2291 |
2301 |
2292 return changelists |
2302 return changelists |
2293 |
2303 |