1
0
mirror of https://github.com/openSUSE/osc.git synced 2024-09-20 01:06:17 +02: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:
- 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
- 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:

3
README
View File

@ -1,8 +1,5 @@
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
https://forgesvn1.novell.com/svn/opensuse/trunk/buildservice/src/clientlib/python/osc/

2
TODO
View File

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

View File

@ -52,7 +52,8 @@ def main():
for p in pacs:
if p.todo == []:
for i in p.filenamelist:
if p.status(i) == 'M':
s = p.status(i)
if s == 'M' or s == 'C':
p.todo.append(i)
d = []
@ -210,8 +211,12 @@ def main():
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_modifiedfiles = []
for i in p.filenamelist:
if p.status(i) == 'M':
saved_modifiedfiles.append(i)
p.update_filesmeta()
p = Package(p.dir)
@ -231,10 +236,13 @@ def main():
for filename in p.filenamelist:
state = p.status(filename)
if state == 'M':
print 'file %s is locally modified... fixme' % filename
if state == 'M' and filename in saved_modifiedfiles:
print 'merging'
status_after_merge = p.mergefile(filename)
print statfrmt(status_after_merge, filename)
elif state == 'M':
p.updatefile(filename)
print statfrmt('U', filename)
print statfrmt('M', filename)
elif state == '!':
p.updatefile(filename)
print 'Restored \'%s\'' % filename
@ -247,7 +255,8 @@ def main():
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)
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':
print ''.join(get_user_id(sys.argv[2]))

View File

@ -13,7 +13,6 @@ import urllib2
from urlparse import urlunsplit
import cElementTree as ET
from cStringIO import StringIO
from string import ljust
from xml.dom.ext.reader import Sax2
@ -102,6 +101,12 @@ class Package:
self.filenamelist = []
self.filelist = []
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'),
node.get('md5'),
int(node.get('size')),
@ -110,6 +115,7 @@ class Package:
self.filenamelist.append(f.name)
self.to_be_deleted = read_tobedeleted(self.dir)
self.in_conflict = read_inconflict(self.dir)
self.todo = []
self.todo_send = []
@ -139,12 +145,45 @@ class Package:
if n not in self.to_be_deleted:
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):
fname = os.path.join(self.storedir, '_to_be_deleted')
f = open(fname, 'w')
f.write('\n'.join(self.to_be_deleted))
f.write('\n')
f.close()
if len(self.to_be_deleted) == 0:
os.unlink(os.path.join(self.storedir, '_to_be_deleted'))
else:
fname = os.path.join(self.storedir, '_to_be_deleted')
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):
filename = os.path.join(self.dir, n)
@ -157,6 +196,31 @@ class Package:
copy_file(filename, storefilename)
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):
meta = ''.join(show_files_meta(self.prjname, self.name))
f = open(os.path.join(self.storedir, '_files'), 'w')
@ -182,7 +246,8 @@ class Package:
exists exists in _files
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 'D' and listed in _to_be_deleted
- x x '!'
@ -207,6 +272,8 @@ class Package:
state = 'D'
elif n in self.to_be_deleted:
state = 'D'
elif n in self.in_conflict:
state = 'C'
elif exists and exists_in_store and known_by_meta:
#print self.findfilebyname(n)
if dgst(os.path.join(self.dir, n)) != self.findfilebyname(n).md5:
@ -295,6 +362,18 @@ def read_tobedeleted(dir):
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():
if len(sys.argv) > 2:
args = sys.argv[2:]