Convert Bazaar VCS

This commit is contained in:
Romain Failliot 2021-11-18 13:40:46 -05:00
parent b645d2cd38
commit a89f85db7a
3 changed files with 220 additions and 198 deletions

View File

@ -54,6 +54,7 @@ from urllib.parse import urlparse
from diffuse import utils from diffuse import utils
from diffuse import constants from diffuse import constants
from diffuse.vcs.folder_set import FolderSet from diffuse.vcs.folder_set import FolderSet
from diffuse.vcs.bzr import Bzr
from diffuse.vcs.git import Git from diffuse.vcs.git import Git
if not hasattr(__builtins__, 'WindowsError'): if not hasattr(__builtins__, 'WindowsError'):
@ -1196,17 +1197,6 @@ def drive_from_path(s):
return os.path.join(c[:4]) return os.path.join(c[:4])
return c[0] return c[0]
# helper function prevent files from being confused with command line options
# by prepending './' to the basename
def safeRelativePath(abspath1, name, prefs, cygwin_pref):
s = os.path.join(os.curdir, utils.relpath(abspath1, os.path.abspath(name)))
if utils.isWindows():
if prefs.getBool(cygwin_pref):
s = s.replace('\\', '/')
else:
s = s.replace('/', '\\')
return s
# escape arguments for use with bash # escape arguments for use with bash
def bashEscape(s): def bashEscape(s):
return "'" + s.replace("'", "'\\''") + "'" return "'" + s.replace("'", "'\\''") + "'"
@ -1222,183 +1212,10 @@ def _find_parent_dir_with(path, dir_name):
break break
path = newpath path = newpath
# Bazaar support
class _Bzr:
def __init__(self, root):
self.root = root
def getFileTemplate(self, prefs, name):
# merge conflict
left = name + '.OTHER'
right = name + '.THIS'
if os.path.isfile(left) and os.path.isfile(right):
return [ (left, None), (name, None), (right, None) ]
# default case
return [ (name, '-1'), (name, None) ]
def getCommitTemplate(self, prefs, rev, names):
# build command
args = [ prefs.getString('bzr_bin'), 'log', '-v', '-r', rev ]
# build list of interesting files
pwd, isabs = os.path.abspath(os.curdir), False
for name in names:
isabs |= os.path.isabs(name)
args.append(safeRelativePath(self.root, name, prefs, 'bzr_cygwin'))
# run command
ss = utils.popenReadLines(self.root, args, prefs, 'bzr_bash')
# parse response
prev = 'before:' + rev
fs = FolderSet(names)
added, modified, removed, renamed = {}, {}, {}, {}
i, n = 0, len(ss)
while i < n:
s = ss[i]
i += 1
if s.startswith('added:'):
# added files
while i < n and ss[i].startswith(' '):
k = prefs.convertToNativePath(ss[i][2:])
i += 1
if not k.endswith(os.sep):
k = os.path.join(self.root, k)
if fs.contains(k):
if not isabs:
k = utils.relpath(pwd, k)
added[k] = [ (None, None), (k, rev) ]
elif s.startswith('modified:'):
# modified files
while i < n and ss[i].startswith(' '):
k = prefs.convertToNativePath(ss[i][2:])
i += 1
if not k.endswith(os.sep):
k = os.path.join(self.root, k)
if fs.contains(k):
if not isabs:
k = utils.relpath(pwd, k)
modified[k] = [ (k, prev), (k, rev) ]
elif s.startswith('removed:'):
# removed files
while i < n and ss[i].startswith(' '):
k = prefs.convertToNativePath(ss[i][2:])
i += 1
if not k.endswith(os.sep):
k = os.path.join(self.root, k)
if fs.contains(k):
if not isabs:
k = utils.relpath(pwd, k)
removed[k] = [ (k, prev), (None, None) ]
elif s.startswith('renamed:'):
# renamed files
while i < n and ss[i].startswith(' '):
k = ss[i][2:].split(' => ')
i += 1
if len(k) == 2:
k0 = prefs.convertToNativePath(k[0])
k1 = prefs.convertToNativePath(k[1])
if not k0.endswith(os.sep) and not k1.endswith(os.sep):
k0 = os.path.join(self.root, k0)
k1 = os.path.join(self.root, k1)
if fs.contains(k0) or fs.contains(k1):
if not isabs:
k0 = utils.relpath(pwd, k0)
k1 = utils.relpath(pwd, k1)
renamed[k1] = [ (k0, prev), (k1, rev) ]
# sort the results
result, r = [], set()
for m in removed, added, modified, renamed:
r.update(m.keys())
for k in sorted(r):
for m in removed, added, modified, renamed:
if k in m:
result.append(m[k])
return result
def getFolderTemplate(self, prefs, names):
# build command
args = [ prefs.getString('bzr_bin'), 'status', '-SV' ]
# build list of interesting files
pwd, isabs = os.path.abspath(os.curdir), False
for name in names:
isabs |= os.path.isabs(name)
args.append(safeRelativePath(self.root, name, prefs, 'bzr_cygwin'))
# run command
prev = '-1'
fs = FolderSet(names)
added, modified, removed, renamed = {}, {}, {}, {}
for s in utils.popenReadLines(self.root, args, prefs, 'bzr_bash'):
# parse response
if len(s) < 5:
continue
y, k = s[1], s[4:]
if y == 'D':
# removed
k = prefs.convertToNativePath(k)
if not k.endswith(os.sep):
k = os.path.join(self.root, k)
if fs.contains(k):
if not isabs:
k = utils.relpath(pwd, k)
removed[k] = [ (k, prev), (None, None) ]
elif y == 'N':
# added
k = prefs.convertToNativePath(k)
if not k.endswith(os.sep):
k = os.path.join(self.root, k)
if fs.contains(k):
if not isabs:
k = utils.relpath(pwd, k)
added[k] = [ (None, None), (k, None) ]
elif y == 'M':
# modified or merge conflict
k = prefs.convertToNativePath(k)
if not k.endswith(os.sep):
k = os.path.join(self.root, k)
if fs.contains(k):
if not isabs:
k = utils.relpath(pwd, k)
modified[k] = self.getFileTemplate(prefs, k)
elif s[0] == 'R':
# renamed
k = k.split(' => ')
if len(k) == 2:
k0 = prefs.convertToNativePath(k[0])
k1 = prefs.convertToNativePath(k[1])
if not k0.endswith(os.sep) and not k1.endswith(os.sep):
k0 = os.path.join(self.root, k0)
k1 = os.path.join(self.root, k1)
if fs.contains(k0) or fs.contains(k1):
if not isabs:
k0 = utils.relpath(pwd, k0)
k1 = utils.relpath(pwd, k1)
renamed[k1] = [ (k0, prev), (k1, None) ]
# sort the results
result, r = [], set()
for m in removed, added, modified, renamed:
r.update(m.keys())
for k in sorted(r):
for m in removed, added, modified, renamed:
if k in m:
result.append(m[k])
return result
def getRevision(self, prefs, name, rev):
return utils.popenRead(
self.root,
[
prefs.getString('bzr_bin'),
'cat',
'--name-from-revision',
'-r',
rev,
safeRelativePath(self.root, name, prefs, 'bzr_cygwin')
],
prefs,
'bzr_bash')
def _get_bzr_repo(path, prefs): def _get_bzr_repo(path, prefs):
p = _find_parent_dir_with(path, '.bzr') p = _find_parent_dir_with(path, '.bzr')
if p: if p:
return _Bzr(p) return Bzr(p)
# CVS support # CVS support
class _Cvs: class _Cvs:
@ -1437,7 +1254,7 @@ class _Cvs:
pwd, isabs = os.path.abspath(os.curdir), False pwd, isabs = os.path.abspath(os.curdir), False
for name in names: for name in names:
isabs |= os.path.isabs(name) isabs |= os.path.isabs(name)
args.append(safeRelativePath(self.root, name, prefs, 'cvs_cygwin')) args.append(utils.safeRelativePath(self.root, name, prefs, 'cvs_cygwin'))
# run command # run command
prev = 'BASE' prev = 'BASE'
fs = FolderSet(names) fs = FolderSet(names)
@ -1471,7 +1288,7 @@ class _Cvs:
[ [
prefs.getString('cvs_bin'), prefs.getString('cvs_bin'),
'status', 'status',
safeRelativePath(self.root, name, prefs, 'cvs_cygwin') utils.safeRelativePath(self.root, name, prefs, 'cvs_cygwin')
], ],
prefs, prefs,
'cvs_bash'): 'cvs_bash'):
@ -1486,7 +1303,7 @@ class _Cvs:
'-p', '-p',
'-r', '-r',
rev, rev,
safeRelativePath(self.root, name, prefs, 'cvs_cygwin') utils.safeRelativePath(self.root, name, prefs, 'cvs_cygwin')
], ],
prefs, prefs,
'cvs_bash') 'cvs_bash')
@ -1520,7 +1337,7 @@ class _Darcs:
for name in names: for name in names:
isabs |= os.path.isabs(name) isabs |= os.path.isabs(name)
if mods: if mods:
args.append(safeRelativePath(self.root, name, prefs, 'darcs_cygwin')) args.append(utils.safeRelativePath(self.root, name, prefs, 'darcs_cygwin'))
# run command # run command
# 'darcs whatsnew' will return 1 if there are no changes # 'darcs whatsnew' will return 1 if there are no changes
ss = utils.popenReadLines(self.root, args, prefs, 'darcs_bash', [0, 1]) ss = utils.popenReadLines(self.root, args, prefs, 'darcs_bash', [0, 1])
@ -1615,7 +1432,7 @@ class _Darcs:
args.extend([ '-n', str(int(rev)) ]) args.extend([ '-n', str(int(rev)) ])
except ValueError: except ValueError:
args.extend([ '-h', rev ]) args.extend([ '-h', rev ])
args.append(safeRelativePath(self.root, name, prefs, 'darcs_cygwin')) args.append(utils.safeRelativePath(self.root, name, prefs, 'darcs_cygwin'))
return utils.popenRead(self.root, args, prefs, 'darcs_bash') return utils.popenRead(self.root, args, prefs, 'darcs_bash')
def _get_darcs_repo(path, prefs): def _get_darcs_repo(path, prefs):
@ -1689,7 +1506,7 @@ class _Hg:
pwd, isabs = os.path.abspath(os.curdir), False pwd, isabs = os.path.abspath(os.curdir), False
for name in names: for name in names:
isabs |= os.path.isabs(name) isabs |= os.path.isabs(name)
args.append(safeRelativePath(self.root, name, prefs, 'hg_cygwin')) args.append(utils.safeRelativePath(self.root, name, prefs, 'hg_cygwin'))
# run command # run command
prev = self._getPreviousRevision(prefs, rev) prev = self._getPreviousRevision(prefs, rev)
fs = FolderSet(names) fs = FolderSet(names)
@ -1728,7 +1545,7 @@ class _Hg:
'cat', 'cat',
'-r', '-r',
rev, rev,
safeRelativePath(self.root, name, prefs, 'hg_cygwin') utils.safeRelativePath(self.root, name, prefs, 'hg_cygwin')
], ],
prefs, prefs,
'hg_bash') 'hg_bash')
@ -1922,7 +1739,7 @@ class _Mtn:
'-q', '-q',
'-r', '-r',
rev, rev,
safeRelativePath(self.root, name, prefs, 'mtn_cygwin') utils.safeRelativePath(self.root, name, prefs, 'mtn_cygwin')
], ],
prefs, prefs,
'mtn_bash') 'mtn_bash')
@ -1938,7 +1755,7 @@ class _Rcs:
self.root = root self.root = root
def getFileTemplate(self, prefs, name): def getFileTemplate(self, prefs, name):
args = [ prefs.getString('rcs_bin_rlog'), '-L', '-h', safeRelativePath(self.root, name, prefs, 'rcs_cygwin') ] args = [ prefs.getString('rcs_bin_rlog'), '-L', '-h', utils.safeRelativePath(self.root, name, prefs, 'rcs_cygwin') ]
rev = '' rev = ''
for line in utils.popenReadLines(self.root, args, prefs, 'rcs_bash'): for line in utils.popenReadLines(self.root, args, prefs, 'rcs_bash'):
if line.startswith('head: '): if line.startswith('head: '):
@ -2013,7 +1830,7 @@ class _Rcs:
r.append(k) r.append(k)
for k in r: for k in r:
isabs |= os.path.isabs(k) isabs |= os.path.isabs(k)
args = [ safeRelativePath(self.root, k, prefs, 'rcs_cygwin') for k in r ] args = [ utils.safeRelativePath(self.root, k, prefs, 'rcs_cygwin') for k in r ]
# run command # run command
r, k = {}, '' r, k = {}, ''
for line in utils.popenXArgsReadLines(self.root, cmd, args, prefs, 'rcs_bash'): for line in utils.popenXArgsReadLines(self.root, cmd, args, prefs, 'rcs_bash'):
@ -2036,7 +1853,7 @@ class _Rcs:
'-p', '-p',
'-q', '-q',
'-r' + rev, '-r' + rev,
safeRelativePath(self.root, name, prefs, 'rcs_cygwin') utils.safeRelativePath(self.root, name, prefs, 'rcs_cygwin')
], ],
prefs, prefs,
'rcs_bash') 'rcs_bash')
@ -2133,7 +1950,7 @@ class _Svn:
for name in names: for name in names:
isabs |= os.path.isabs(name) isabs |= os.path.isabs(name)
if rev is None: if rev is None:
args.append(safeRelativePath(self.root, name, prefs, vcs + '_cygwin')) args.append(utils.safeRelativePath(self.root, name, prefs, vcs + '_cygwin'))
# run command # run command
fs = FolderSet(names) fs = FolderSet(names)
modified, added, removed = {}, set(), set() modified, added, removed = {}, set(), set()
@ -2271,7 +2088,7 @@ class _Svn:
[ [
vcs_bin, vcs_bin,
'cat', 'cat',
'{}@{}'.format(safeRelativePath(self.root, name, prefs, 'svn_cygwin'), rev) '{}@{}'.format(utils.safeRelativePath(self.root, name, prefs, 'svn_cygwin'), rev)
], ],
prefs, prefs,
'svn_bash') 'svn_bash')

View File

@ -93,6 +93,17 @@ def relpath(a, b):
r.extend(c2[i:]) r.extend(c2[i:])
return os.sep.join(r) return os.sep.join(r)
# helper function prevent files from being confused with command line options
# by prepending './' to the basename
def safeRelativePath(abspath1, name, prefs, cygwin_pref):
s = os.path.join(os.curdir, utils.relpath(abspath1, os.path.abspath(name)))
if utils.isWindows():
if prefs.getBool(cygwin_pref):
s = s.replace('\\', '/')
else:
s = s.replace('/', '\\')
return s
# use popen to read the output of a command # use popen to read the output of a command
def popenRead(dn, cmd, prefs, bash_pref, success_results=None): def popenRead(dn, cmd, prefs, bash_pref, success_results=None):
if success_results is None: if success_results is None:

194
src/vcs/bzr.py Normal file
View File

@ -0,0 +1,194 @@
# Diffuse: a graphical tool for merging and comparing text files.
#
# Copyright (C) 2019 Derrick Moser <derrick_moser@yahoo.com>
# Copyright (C) 2021 Romain Failliot <romain.failliot@foolstep.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import os
from diffuse import utils
from diffuse.vcs.folder_set import FolderSet
from diffuse.vcs.vcs_interface import VcsInterface
# Bazaar support
class Bzr(VcsInterface):
def getFileTemplate(self, prefs, name):
# merge conflict
left = name + '.OTHER'
right = name + '.THIS'
if os.path.isfile(left) and os.path.isfile(right):
return [ (left, None), (name, None), (right, None) ]
# default case
return [ (name, '-1'), (name, None) ]
def getCommitTemplate(self, prefs, rev, names):
# build command
args = [ prefs.getString('bzr_bin'), 'log', '-v', '-r', rev ]
# build list of interesting files
pwd, isabs = os.path.abspath(os.curdir), False
for name in names:
isabs |= os.path.isabs(name)
args.append(utils.safeRelativePath(self.root, name, prefs, 'bzr_cygwin'))
# run command
ss = utils.popenReadLines(self.root, args, prefs, 'bzr_bash')
# parse response
prev = 'before:' + rev
fs = FolderSet(names)
added, modified, removed, renamed = {}, {}, {}, {}
i, n = 0, len(ss)
while i < n:
s = ss[i]
i += 1
if s.startswith('added:'):
# added files
while i < n and ss[i].startswith(' '):
k = prefs.convertToNativePath(ss[i][2:])
i += 1
if not k.endswith(os.sep):
k = os.path.join(self.root, k)
if fs.contains(k):
if not isabs:
k = utils.relpath(pwd, k)
added[k] = [ (None, None), (k, rev) ]
elif s.startswith('modified:'):
# modified files
while i < n and ss[i].startswith(' '):
k = prefs.convertToNativePath(ss[i][2:])
i += 1
if not k.endswith(os.sep):
k = os.path.join(self.root, k)
if fs.contains(k):
if not isabs:
k = utils.relpath(pwd, k)
modified[k] = [ (k, prev), (k, rev) ]
elif s.startswith('removed:'):
# removed files
while i < n and ss[i].startswith(' '):
k = prefs.convertToNativePath(ss[i][2:])
i += 1
if not k.endswith(os.sep):
k = os.path.join(self.root, k)
if fs.contains(k):
if not isabs:
k = utils.relpath(pwd, k)
removed[k] = [ (k, prev), (None, None) ]
elif s.startswith('renamed:'):
# renamed files
while i < n and ss[i].startswith(' '):
k = ss[i][2:].split(' => ')
i += 1
if len(k) == 2:
k0 = prefs.convertToNativePath(k[0])
k1 = prefs.convertToNativePath(k[1])
if not k0.endswith(os.sep) and not k1.endswith(os.sep):
k0 = os.path.join(self.root, k0)
k1 = os.path.join(self.root, k1)
if fs.contains(k0) or fs.contains(k1):
if not isabs:
k0 = utils.relpath(pwd, k0)
k1 = utils.relpath(pwd, k1)
renamed[k1] = [ (k0, prev), (k1, rev) ]
# sort the results
result, r = [], set()
for m in removed, added, modified, renamed:
r.update(m.keys())
for k in sorted(r):
for m in removed, added, modified, renamed:
if k in m:
result.append(m[k])
return result
def getFolderTemplate(self, prefs, names):
# build command
args = [ prefs.getString('bzr_bin'), 'status', '-SV' ]
# build list of interesting files
pwd, isabs = os.path.abspath(os.curdir), False
for name in names:
isabs |= os.path.isabs(name)
args.append(utils.safeRelativePath(self.root, name, prefs, 'bzr_cygwin'))
# run command
prev = '-1'
fs = FolderSet(names)
added, modified, removed, renamed = {}, {}, {}, {}
for s in utils.popenReadLines(self.root, args, prefs, 'bzr_bash'):
# parse response
if len(s) < 5:
continue
y, k = s[1], s[4:]
if y == 'D':
# removed
k = prefs.convertToNativePath(k)
if not k.endswith(os.sep):
k = os.path.join(self.root, k)
if fs.contains(k):
if not isabs:
k = utils.relpath(pwd, k)
removed[k] = [ (k, prev), (None, None) ]
elif y == 'N':
# added
k = prefs.convertToNativePath(k)
if not k.endswith(os.sep):
k = os.path.join(self.root, k)
if fs.contains(k):
if not isabs:
k = utils.relpath(pwd, k)
added[k] = [ (None, None), (k, None) ]
elif y == 'M':
# modified or merge conflict
k = prefs.convertToNativePath(k)
if not k.endswith(os.sep):
k = os.path.join(self.root, k)
if fs.contains(k):
if not isabs:
k = utils.relpath(pwd, k)
modified[k] = self.getFileTemplate(prefs, k)
elif s[0] == 'R':
# renamed
k = k.split(' => ')
if len(k) == 2:
k0 = prefs.convertToNativePath(k[0])
k1 = prefs.convertToNativePath(k[1])
if not k0.endswith(os.sep) and not k1.endswith(os.sep):
k0 = os.path.join(self.root, k0)
k1 = os.path.join(self.root, k1)
if fs.contains(k0) or fs.contains(k1):
if not isabs:
k0 = utils.relpath(pwd, k0)
k1 = utils.relpath(pwd, k1)
renamed[k1] = [ (k0, prev), (k1, None) ]
# sort the results
result, r = [], set()
for m in removed, added, modified, renamed:
r.update(m.keys())
for k in sorted(r):
for m in removed, added, modified, renamed:
if k in m:
result.append(m[k])
return result
def getRevision(self, prefs, name, rev):
return utils.popenRead(
self.root,
[
prefs.getString('bzr_bin'),
'cat',
'--name-from-revision',
'-r',
rev,
utils.safeRelativePath(self.root, name, prefs, 'bzr_cygwin')
],
prefs,
'bzr_bash')