mirror of
https://github.com/openSUSE/osc.git
synced 2024-11-10 06:46:15 +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:
parent
2c37f7287d
commit
211b197b26
6
NEWS
6
NEWS
@ -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
3
README
@ -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
2
TODO
@ -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
|
||||
|
||||
|
@ -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]))
|
||||
|
93
osc/core.py
93
osc/core.py
@ -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:]
|
||||
|
Loading…
Reference in New Issue
Block a user