1
0
mirror of https://github.com/openSUSE/osc.git synced 2025-03-03 22:52:10 +01:00

- add diff3 merge support. Locally modified files are merged with upstream changes

if possible, and go into Conflict state if that fails.
- add 'resolved' command to be used after manual merging.
This commit is contained in:
Dr. Peter Poeml 2006-05-22 14:12:06 +00:00
parent 2c37f7287d
commit 211b197b26
5 changed files with 120 additions and 19 deletions

6
NEWS
View File

@ -1,7 +1,11 @@
0.4: 0.4:
- allow 'up' inside a project directory (will automatically pull in all new - allow 'up' inside a project directory (will automatically pull in all new
packages) packages). (For past checkouts, you may need to put the project name into
$prjdir/.osc/_project yourself).
- checkout: preserve mtimes - checkout: preserve mtimes
- add diff3 merge support. Locally modified files are merged with upstream changes
if possible, and go into Conflict state if that fails.
- add 'resolved' command to be used after manual merging.
0.3: 0.3:

3
README
View File

@ -1,8 +1,5 @@
osc -- opensuse-commander with svn like handling osc -- opensuse-commander with svn like handling
>>> BUG: at the moment, 'up' overwrites files with
local modifications.
Please send patches to poeml@suse.de, or work directly on Please send patches to poeml@suse.de, or work directly on
https://forgesvn1.novell.com/svn/opensuse/trunk/buildservice/src/clientlib/python/osc/ https://forgesvn1.novell.com/svn/opensuse/trunk/buildservice/src/clientlib/python/osc/

2
TODO
View File

@ -2,10 +2,8 @@
- implement 'mv' command - implement 'mv' command
- implement 'trigger-rebuild' - implement 'trigger-rebuild'
update: handle local modifications
checkin: checkin:
- fix argument handling
- handle error if PUT fails, so the change is not committed to - handle error if PUT fails, so the change is not committed to
localmeta localmeta

View File

@ -52,7 +52,8 @@ def main():
for p in pacs: for p in pacs:
if p.todo == []: if p.todo == []:
for i in p.filenamelist: for i in p.filenamelist:
if p.status(i) == 'M': s = p.status(i)
if s == 'M' or s == 'C':
p.todo.append(i) p.todo.append(i)
d = [] d = []
@ -210,8 +211,12 @@ def main():
for p in pacs: for p in pacs:
# save filelist before replacing the meta file # save filelist and (modified) status before replacing the meta file
saved_filenames = p.filenamelist saved_filenames = p.filenamelist
saved_modifiedfiles = []
for i in p.filenamelist:
if p.status(i) == 'M':
saved_modifiedfiles.append(i)
p.update_filesmeta() p.update_filesmeta()
p = Package(p.dir) p = Package(p.dir)
@ -231,10 +236,13 @@ def main():
for filename in p.filenamelist: for filename in p.filenamelist:
state = p.status(filename) state = p.status(filename)
if state == 'M': if state == 'M' and filename in saved_modifiedfiles:
print 'file %s is locally modified... fixme' % filename print 'merging'
status_after_merge = p.mergefile(filename)
print statfrmt(status_after_merge, filename)
elif state == 'M':
p.updatefile(filename) p.updatefile(filename)
print statfrmt('U', filename) print statfrmt('M', filename)
elif state == '!': elif state == '!':
p.updatefile(filename) p.updatefile(filename)
print 'Restored \'%s\'' % filename print 'Restored \'%s\'' % filename
@ -247,7 +255,8 @@ def main():
p.update_pacmeta() p.update_pacmeta()
print ljust(p.name, 45), 'At revision %s.' % p.rev #print ljust(p.name, 45), 'At revision %s.' % p.rev
print 'At revision %s.' % p.rev
@ -274,6 +283,20 @@ def main():
print statfrmt('D', filename) print statfrmt('D', filename)
elif cmd == 'resolved':
if len(sys.argv) < 3:
print '%s requires at least one argument' % cmd
sys.exit(1)
args = parseargs()
pacs = findpacs(args)
for p in pacs:
for filename in p.todo:
print "Resolved conflicted state of '%s'" % filename
p.clear_from_conflictlist(filename)
elif cmd == 'id': elif cmd == 'id':
print ''.join(get_user_id(sys.argv[2])) print ''.join(get_user_id(sys.argv[2]))

View File

@ -13,7 +13,6 @@ import urllib2
from urlparse import urlunsplit from urlparse import urlunsplit
import cElementTree as ET import cElementTree as ET
from cStringIO import StringIO from cStringIO import StringIO
from string import ljust
from xml.dom.ext.reader import Sax2 from xml.dom.ext.reader import Sax2
@ -102,6 +101,12 @@ class Package:
self.filenamelist = [] self.filenamelist = []
self.filelist = [] self.filelist = []
for node in files_tree_root.findall('entry'): for node in files_tree_root.findall('entry'):
try:
int(node.get('size'))
except:
print 'old _files metadata found.'
print 'run \'osc up\' after manually removing all "entry" lines from .osc/_files to upgrade.'
sys.exit(1)
f = File(node.get('name'), f = File(node.get('name'),
node.get('md5'), node.get('md5'),
int(node.get('size')), int(node.get('size')),
@ -110,6 +115,7 @@ class Package:
self.filenamelist.append(f.name) self.filenamelist.append(f.name)
self.to_be_deleted = read_tobedeleted(self.dir) self.to_be_deleted = read_tobedeleted(self.dir)
self.in_conflict = read_inconflict(self.dir)
self.todo = [] self.todo = []
self.todo_send = [] self.todo_send = []
@ -139,12 +145,45 @@ class Package:
if n not in self.to_be_deleted: if n not in self.to_be_deleted:
self.to_be_deleted.append(n) self.to_be_deleted.append(n)
def put_on_conflictlist(self, n):
if n not in self.in_conflict:
self.in_conflict.append(n)
def clear_from_conflictlist(self, n):
"""delete an entry from the file, and remove the file if it would be empty"""
if n in self.in_conflict:
filename = os.path.join(self.dir, n)
storefilename = os.path.join(self.storedir, n)
myfilename = os.path.join(self.dir, n + '.mine')
upfilename = os.path.join(self.dir, n + '.r' + self.rev)
os.unlink(myfilename)
os.rename(upfilename, storefilename)
self.in_conflict.remove(n)
self.write_deletelist()
def write_deletelist(self): def write_deletelist(self):
fname = os.path.join(self.storedir, '_to_be_deleted') if len(self.to_be_deleted) == 0:
f = open(fname, 'w') os.unlink(os.path.join(self.storedir, '_to_be_deleted'))
f.write('\n'.join(self.to_be_deleted)) else:
f.write('\n') fname = os.path.join(self.storedir, '_to_be_deleted')
f.close() f = open(fname, 'w')
f.write('\n'.join(self.to_be_deleted))
f.write('\n')
f.close()
def write_conflictlist(self):
if len(self.in_conflict) == 0:
os.unlink(os.path.join(self.storedir, '_in_conflict'))
else:
fname = os.path.join(self.storedir, '_in_conflict')
f = open(fname, 'w')
f.write('\n'.join(self.in_conflict))
f.write('\n')
f.close()
def updatefile(self, n): def updatefile(self, n):
filename = os.path.join(self.dir, n) filename = os.path.join(self.dir, n)
@ -157,6 +196,31 @@ class Package:
copy_file(filename, storefilename) copy_file(filename, storefilename)
os.utime(storefilename, (-1, mtime)) os.utime(storefilename, (-1, mtime))
def mergefile(self, n):
filename = os.path.join(self.dir, n)
storefilename = os.path.join(self.storedir, n)
myfilename = os.path.join(self.dir, n + '.mine')
upfilename = os.path.join(self.dir, n + '.r' + self.rev)
os.rename(filename, myfilename)
get_source_file(self.prjname, self.name, n, targetfilename=upfilename)
ret = os.system('cd %s; diff3 -m -E %s %s %s > %s' \
% (self.dir, myfilename, storefilename, upfilename, filename))
if ret == 0:
# merge was successful... clean up
os.rename(upfilename, filename)
copy_file(filename, storefilename)
os.unlink(myfilename)
return 'M'
else:
# unsuccessful merge
self.in_conflict.append(n)
self.write_conflictlist()
return 'C'
def update_filesmeta(self): def update_filesmeta(self):
meta = ''.join(show_files_meta(self.prjname, self.name)) meta = ''.join(show_files_meta(self.prjname, self.name))
f = open(os.path.join(self.storedir, '_files'), 'w') f = open(os.path.join(self.storedir, '_files'), 'w')
@ -182,7 +246,8 @@ class Package:
exists exists in _files exists exists in _files
x x - 'A' x x - 'A'
x x x 'M', if digest differs, else ' ' x x x ' ' if digest differs: 'M'
and if in conflicts file: 'C'
x - - '?' x - - '?'
x - x 'D' and listed in _to_be_deleted x - x 'D' and listed in _to_be_deleted
- x x '!' - x x '!'
@ -207,6 +272,8 @@ class Package:
state = 'D' state = 'D'
elif n in self.to_be_deleted: elif n in self.to_be_deleted:
state = 'D' state = 'D'
elif n in self.in_conflict:
state = 'C'
elif exists and exists_in_store and known_by_meta: elif exists and exists_in_store and known_by_meta:
#print self.findfilebyname(n) #print self.findfilebyname(n)
if dgst(os.path.join(self.dir, n)) != self.findfilebyname(n).md5: if dgst(os.path.join(self.dir, n)) != self.findfilebyname(n).md5:
@ -295,6 +362,18 @@ def read_tobedeleted(dir):
return r return r
def read_inconflict(dir):
r = []
fname = os.path.join(dir, store, '_in_conflict')
if os.path.exists(fname):
for i in open(fname, 'r').readlines():
r.append(i.strip())
return r
def parseargs(): def parseargs():
if len(sys.argv) > 2: if len(sys.argv) > 2:
args = sys.argv[2:] args = sys.argv[2:]