1
0
mirror of https://github.com/openSUSE/osc.git synced 2024-09-20 09:16:16 +02:00

added initial revision handling:

- extended "osc co prj pac" to checkout a specific revision of pac
    - extended "osc up" to update to a specific revision
    - extended "osc diff" to diff the working copy against a
      specific revision on the server. NOTE: comparing two
      server-side revisions (osc diff -r 11:12) is currently
      not supported!
This commit is contained in:
Marcus Hüwe 2007-07-04 12:55:26 +00:00
parent adf9e633f9
commit 85e66362af
2 changed files with 199 additions and 41 deletions

View File

@ -375,6 +375,12 @@ class Osc(cmdln.Cmdln):
@cmdln.alias('di') @cmdln.alias('di')
@cmdln.option('-r', '--revision', metavar='rev1[:rev2]',
help='If rev1 is specified it will compare your working copy against '
'the revision (rev1) on the server. '
'If rev1 and rev2 are specified it will compare rev1 against rev2'
'(changes in your working copy are ignored in this case).\n'
'NOTE: if more than 1 package is specified --revision will be ignored!')
def do_diff(self, subcmd, opts, *args): def do_diff(self, subcmd, opts, *args):
"""${cmd_name}: Generates a diff """${cmd_name}: Generates a diff
@ -389,28 +395,95 @@ class Osc(cmdln.Cmdln):
args = parseargs(args) args = parseargs(args)
pacs = findpacs(args) pacs = findpacs(args)
difference_found = False difference_found = False
for p in pacs: d = []
if p.todo == []:
for i in p.filenamelist: rev1, rev2 = parseRevisionOption(opts.revision)
s = p.status(i) pac = pacs[0]
if s == 'M' or s == 'C':
p.todo.append(i) if rev1 and rev2 and (len(pacs) == 1):
# this is currently not implemented
print >>sys.stderr, 'this feature isn\'t implemented yet'
sys.exit(1)
elif rev1 and (pac.rev != rev1) and (len(pacs) == 1):
# make a temp dir for checking out the project
import tempfile
tmpdir = tempfile.mkdtemp(rev1, pac.name, '/tmp')
curdir = os.getcwd()
os.chdir(tmpdir)
init_package_dir(conf.config['apiurl'], pac.prjname, pac.name, tmpdir, rev1)
os.chdir(curdir)
tmppac = Package(tmpdir)
d = [] changed_files = []
for filename in p.todo: added_files = []
d.append('Index: %s\n' % filename) removed_files = []
if pac.todo:
for file in pac.todo:
if file in tmppac.filenamelist:
if dgst(os.path.join(pac.dir, file)) != tmppac.findfilebyname(file).md5:
changed_files.append(file)
else:
added_files.append(file)
else:
changed_files, added_files, removed_files = pac.comparePac(tmppac)
for file in changed_files:
tmppac.updatefile(file, rev1)
d.append('Index: %s\n' % file)
d.append('===================================================================\n') d.append('===================================================================\n')
d.append(get_source_file_diff(p.dir, filename, p.rev)) d.append(get_source_file_diff(pac.dir, file, rev1, file, tmppac.dir))
if d: tmppac.delete_localfile(file)
print ''.join(d) tmppac.delete_storefile(file)
difference_found = True
# this tempfile is used as a dummy file for difflib
(fd, filename) = tempfile.mkstemp(dir=tmppac.storedir)
for file in added_files:
d.append('Index: %s\n' % file)
d.append('===================================================================\n')
d.append(get_source_file_diff(pac.dir, file, rev1, \
os.path.basename(filename), \
tmppac.storedir, file))
for file in removed_files:
tmppac.updatefile(file, rev1)
d.append('Index: %s\n' % file)
d.append('===================================================================\n')
d.append(get_source_file_diff(tmppac.storedir, \
os.path.basename(filename), \
rev1, file, tmppac.dir, file))
tmppac.delete_localfile(file)
tmppac.delete_storefile(file)
# clean up
os.unlink(filename)
for dir, dirnames, files in os.walk(tmppac.storedir):
for file in files:
os.unlink(os.path.join(dir, file))
os.rmdir(tmppac.storedir)
os.rmdir(tmppac.dir)
else:
for p in pacs:
if p.todo == []:
for i in p.filenamelist:
s = p.status(i)
if s == 'M' or s == 'C':
p.todo.append(i)
for filename in p.todo:
d.append('Index: %s\n' % filename)
d.append('===================================================================\n')
d.append(get_source_file_diff(p.dir, filename, p.rev))
if d:
print ''.join(d)
difference_found = True
if difference_found: if difference_found:
return 1 return 1
def do_repourls(self, subcmd, opts, *args): def do_repourls(self, subcmd, opts, *args):
"""${cmd_name}: shows URLs of .repo files """${cmd_name}: shows URLs of .repo files
@ -434,7 +507,9 @@ class Osc(cmdln.Cmdln):
print url_tmpl % (p.prjname.replace(':', ':/'), platform, p.prjname) print url_tmpl % (p.prjname.replace(':', ':/'), platform, p.prjname)
@cmdln.option('-r', '--revision', metavar='rev',
help='checkout the specified revision')
@cmdln.alias('co') @cmdln.alias('co')
def do_checkout(self, subcmd, opts, *args): def do_checkout(self, subcmd, opts, *args):
"""${cmd_name}: check out content from the repository """${cmd_name}: check out content from the repository
@ -461,11 +536,13 @@ class Osc(cmdln.Cmdln):
except: except:
pass pass
rev, dummy = parseRevisionOption(opts.revision)
if filename: if filename:
get_source_file(conf.config['apiurl'], project, package, filename) get_source_file(conf.config['apiurl'], project, package, filename, revision=rev)
elif package: elif package:
checkout_package(conf.config['apiurl'], project, package) checkout_package(conf.config['apiurl'], project, package, rev)
elif project: elif project:
# all packages # all packages
@ -678,6 +755,10 @@ class Osc(cmdln.Cmdln):
print print
@cmdln.option('-r', '--revision', metavar='rev',
help='update to specified revision (this option will be ignored '
'if you are going to update the complete project or more than '
'one package)')
@cmdln.alias('up') @cmdln.alias('up')
def do_update(self, subcmd, opts, *args): def do_update(self, subcmd, opts, *args):
"""${cmd_name}: Update a working copy """${cmd_name}: Update a working copy
@ -719,6 +800,11 @@ class Osc(cmdln.Cmdln):
pacs = findpacs(args) pacs = findpacs(args)
if opts.revision and ( len(args) == 1):
rev, dummy = parseRevisionOption(opts.revision)
else:
rev = None
for p in pacs: for p in pacs:
if len(pacs) > 1: if len(pacs) > 1:
@ -728,7 +814,7 @@ class Osc(cmdln.Cmdln):
saved_modifiedfiles = [ f for f in p.filenamelist if p.status(f) == 'M' ] saved_modifiedfiles = [ f for f in p.filenamelist if p.status(f) == 'M' ]
oldp = p oldp = p
p.update_filesmeta() p.update_filesmeta(rev)
p = Package(p.dir) p = Package(p.dir)
# which files do no longer exist upstream? # which files do no longer exist upstream?
@ -754,13 +840,13 @@ class Osc(cmdln.Cmdln):
status_after_merge = p.mergefile(filename) status_after_merge = p.mergefile(filename)
print statfrmt(status_after_merge, filename) print statfrmt(status_after_merge, filename)
elif state == 'M': elif state == 'M':
p.updatefile(filename) p.updatefile(filename, rev)
print statfrmt('U', filename) print statfrmt('U', filename)
elif state == '!': elif state == '!':
p.updatefile(filename) p.updatefile(filename, rev)
print 'Restored \'%s\'' % filename print 'Restored \'%s\'' % filename
elif state == 'F': elif state == 'F':
p.updatefile(filename) p.updatefile(filename, rev)
print statfrmt('A', filename) print statfrmt('A', filename)
elif state == ' ': elif state == ' ':
pass pass

View File

@ -329,12 +329,12 @@ class Package:
f.write('\n') f.write('\n')
f.close() f.close()
def updatefile(self, n): def updatefile(self, n, revision):
filename = os.path.join(self.dir, n) filename = os.path.join(self.dir, n)
storefilename = os.path.join(self.storedir, n) storefilename = os.path.join(self.storedir, n)
mtime = self.findfilebyname(n).mtime mtime = self.findfilebyname(n).mtime
get_source_file(self.apiurl, self.prjname, self.name, n, targetfilename=filename) get_source_file(self.apiurl, self.prjname, self.name, n, targetfilename=filename, revision=revision)
os.utime(filename, (-1, mtime)) os.utime(filename, (-1, mtime))
shutil.copy2(filename, storefilename) shutil.copy2(filename, storefilename)
@ -386,8 +386,8 @@ class Package:
def update_filesmeta(self): def update_filesmeta(self, revision=None):
meta = ''.join(show_files_meta(self.apiurl, self.prjname, self.name)) meta = ''.join(show_files_meta(self.apiurl, self.prjname, self.name, revision))
f = open(os.path.join(self.storedir, '_files'), 'w') f = open(os.path.join(self.storedir, '_files'), 'w')
f.write(meta) f.write(meta)
f.close() f.close()
@ -462,6 +462,30 @@ class Package:
return state return state
def comparePac(self, pac):
"""
This method compares the local filelist with
the filelist of the passed package to see which files
were added, removed and changed.
"""
changed_files = []
added_files = []
removed_files = []
for file in self.filenamelist:
if not file in self.to_be_deleted:
if file in pac.filenamelist:
if dgst(file) != pac.findfilebyname(file).md5:
changed_files.append(file)
else:
added_files.append(file)
for file in pac.filenamelist:
if (not file in self.filenamelist) or (file in self.to_be_deleted):
removed_files.append(file)
return changed_files, added_files, removed_files
def merge(self, otherpac): def merge(self, otherpac):
self.todo += otherpac.todo self.todo += otherpac.todo
@ -733,7 +757,7 @@ def urlopen(url, data=None):
return fd return fd
def init_package_dir(apiurl, project, package, dir): def init_package_dir(apiurl, project, package, dir, revision=None):
if not os.path.isdir(store): if not os.path.isdir(store):
os.mkdir(store) os.mkdir(store)
os.chdir(store) os.chdir(store)
@ -745,7 +769,7 @@ def init_package_dir(apiurl, project, package, dir):
f.close f.close
f = open('_files', 'w') f = open('_files', 'w')
f.write(''.join(show_files_meta(apiurl, project, package))) f.write(''.join(show_files_meta(apiurl, project, package, revision)))
f.close() f.close()
f = open('_osclib_version', 'w') f = open('_osclib_version', 'w')
@ -986,8 +1010,11 @@ def edit_user_meta(user, change_is_required=True):
print 'Done.' print 'Done.'
def show_files_meta(apiurl, prj, pac): def show_files_meta(apiurl, prj, pac, revision=None):
f = http_GET(makeurl(apiurl, ['source', prj, pac])) query = []
if revision:
query.append('rev=%s' % revision)
f = http_GET(makeurl(apiurl, ['source', prj, pac], query=query))
return f.readlines() return f.readlines()
@ -1043,8 +1070,13 @@ def get_user_meta(apiurl, user):
return None return None
def get_source_file(apiurl, prj, package, filename, targetfilename=None): def get_source_file(apiurl, prj, package, filename, targetfilename=None, revision = None):
u = makeurl(apiurl, ['source', prj, package, pathname2url(filename)]) query = []
if revision:
query.append('rev=%s' % quote_plus(revision))
u = makeurl(apiurl, ['source', prj, package, pathname2url(filename)], query=query)
# print 'url: %s' % u
f = http_GET(u) f = http_GET(u)
o = open(targetfilename or filename, 'w') o = open(targetfilename or filename, 'w')
@ -1082,10 +1114,26 @@ def binary_file(fn):
return binary(open(fn, 'r').read(4096)) return binary(open(fn, 'r').read(4096))
def get_source_file_diff(dir, filename, rev): def get_source_file_diff(dir, filename, rev, oldfilename = None, olddir = None, origfilename = None):
"""
This methods diffs oldfilename against filename (so filename will
be shown as the new file).
The variable origfilename is used if filename and oldfilename differ
in their names (for instance if a tempfile is used for filename etc.)
"""
import difflib import difflib
file1 = os.path.join(dir, store, filename) # stored original if not oldfilename:
oldfilename = filename
if not olddir:
olddir = os.path.join(dir, store)
if not origfilename:
origfilename = filename
file1 = os.path.join(olddir, oldfilename) # old/stored original
file2 = os.path.join(dir, filename) # working copy file2 = os.path.join(dir, filename) # working copy
f1 = open(file1, 'r') f1 = open(file1, 'r')
@ -1097,14 +1145,14 @@ def get_source_file_diff(dir, filename, rev):
f2.close() f2.close()
if binary(s1) or binary (s2): if binary(s1) or binary (s2):
d = ['Binary file %s has changed\n' % filename] d = ['Binary file %s has changed\n' % origfilename]
else: else:
d = difflib.unified_diff(\ d = difflib.unified_diff(\
s1.splitlines(1), \ s1.splitlines(1), \
s2.splitlines(1), \ s2.splitlines(1), \
fromfile = '%s (revision %s)' % (filename, rev), \ fromfile = '%s (revision %s)' % (origfilename, rev), \
tofile = '%s (working copy)' % filename) tofile = '%s (working copy)' % origfilename)
# if file doesn't end with newline, we need to append one in the diff result # if file doesn't end with newline, we need to append one in the diff result
d = list(d) d = list(d)
@ -1136,15 +1184,15 @@ def make_dir(apiurl, project, package):
return(os.path.join(project, package)) return(os.path.join(project, package))
def checkout_package(apiurl, project, package): def checkout_package(apiurl, project, package, revision=None):
olddir = os.getcwd() olddir = os.getcwd()
os.chdir(make_dir(apiurl, project, package)) os.chdir(make_dir(apiurl, project, package))
init_package_dir(apiurl, project, package, store) init_package_dir(apiurl, project, package, store, revision)
p = Package(os.curdir) p = Package(os.curdir)
for filename in p.filenamelist: for filename in p.filenamelist:
p.updatefile(filename) p.updatefile(filename, revision)
print 'A ', os.path.join(project, package, filename) print 'A ', os.path.join(project, package, filename)
os.chdir(olddir) os.chdir(olddir)
@ -1535,3 +1583,27 @@ def wipebinaries(apiurl, project, package=None, arch=None, repo=None):
sys.exit(1) sys.exit(1)
root = ET.parse(f).getroot() root = ET.parse(f).getroot()
return root.get('code') return root.get('code')
def parseRevisionOption(string):
"""
retrun a tuple which contains the revisions
"""
if string:
if ':' in string:
splitted_rev = string.split(':')
try:
for i in splitted_rev:
int(i)
return splitted_rev
except ValueError:
print >>sys.stderr, 'your revision \'%s\' will be ignored' % string
return None, None
else:
if string.isdigit():
return string, None
else:
print >>sys.stderr, 'your revision \'%s\' will be ignored' % string
return None, None
else:
return None, None