.RS 2
6. \fBrmgroup\fR - remove submission group
.RE
+.RS 2
+7. \fBdiff\fR - show the difference between projects
+.RE
.\" ===========================================================================
.\" Global options
Print short help text about the "rmgroup" command and exit.
.RE
+.PP
+.\"
+.\" The "diff" command description
+.\"
+.SS \fBdiff\fR [\-\-help] <project1> <manifest of target project> <manifest1>
+
+.RS 2
+Show the difference between manifest of target project and manifest of <project1> in the following format:
+
+<Git path> <Revision in project1> <Revision in the target OBS Project> <Tag, accepted into project1> <status>
+.RS 0
+
+Example of output:
+
+$ repa -p Tizen:IVI diff Tizen:Common ivi_0140508.2_ia32.xml common_20140507.5_ia32.xml
+
+platform/core/appfw/aul-1 3644ad5459 e4f2b22012 submit/tizen/20140502.084937 pending
+.RS 0
+platform/core/appfw/shortcut 502c7807b3 27bbf892e0 submit/tizen/20140430.020549 pending
+.RS 0
+platform/core/connectivity/nfc-manager-neard ea952dc454 88793ab1ff
+.RS 0
+platform/core/connectivity/smartcard-service e45ae0bdfb 7d5da70551 submit/tizen/20140506.101200 pending
+.RS 0
+platform/core/messaging/msg-service c988cc8b55 a9eed6a734 submit/tizen/20140502.105814 pending
+.RS 0
+platform/core/system/tlm 3ddbc6bc14 dc72779317 submit/tizen/20140430.130050
+.RS 0
+platform/framework/web/crosswalk 42dcc1327b 4280e52757 submit/tizen/20140506.123703 pending
+.RS 0
+
+Status 'pending' means that submission is pending for the target project. In colorized mode pending submissions are colorized and 'pending' status is not shown.
+
+
+.RE
+
+.\"
+.\" The "diff" command's options
+.\"
+.RS 2
+\fBOPTIONS\fR
+.RS 2
+.B \-h, \-\-help
+.RS 2
+Print short help text about the "diff" command and exit.
+.RE
+
+
.SH CONFIGURATION FILE
.RS 2
--- /dev/null
+#!/usr/bin/env python
+#
+# This file is part of REPA: Release Engineering Process Assistant.
+#
+# Copyright (C) 2014 Intel Corporation
+#
+# REPA is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# version 2 as published by the Free Software Foundation.
+#
+# 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.
+
+"""
+Diff module.
+Show the difference between projects.
+"""
+
+import sys
+import os
+import json
+
+import xml.etree.ElementTree as ET
+
+from repa.common import RepaException, Colorizer, get_prerelease_projects
+from repa.obs import OBS
+from repa.main import sub_main
+
+
+def gen_data(manifest):
+ """Parse manifest. Yield git path and revision for every entry."""
+ try:
+ etree = ET.parse(manifest)
+ except ET.ParseError as err:
+ raise RepaException("Can't parse manifest %s: %s" % (manifest, err))
+
+ for project in etree.getroot().findall('project'):
+ yield project.get('path'), project.get('revision')
+
+
+def get_tag(obs, project, package, revision):
+ """Get tag from the latest accepted SR."""
+ for sreq in obs.get_srs(project, 'accepted', package):
+ descr = sreq[-1]
+ if descr and 'Commit: %s' % revision in descr:
+ return descr.split('Tag: ')[-1].split()[0]
+ return 'Not found'
+
+
+def get_pending(obs, target_proj):
+ """Get pair of git path: tag for pending prerelease projects."""
+ result = {}
+ for proj in get_prerelease_projects(obs, target_proj):
+ meta = json.loads(obs.get_description(proj))
+ for path in meta["projects"]:
+ result[path] = meta["git_tag"]
+ return result
+
+
+def diff(obs, target_project, project1, target_manifest, manifest1,
+ is_colorize=False):
+ """Show the difference between project1 and project2."""
+ colorizer = Colorizer(is_colorize)
+
+ target_data = dict(gen_data(target_manifest))
+ data1 = dict(gen_data(manifest1))
+
+ pending = get_pending(obs, target_project)
+
+ for path in sorted(data1):
+ # if submission is not pending in target project
+ # and revisions are the same - skip it
+ if path not in pending and \
+ (path in target_data and data1[path] == target_data[path]):
+ continue
+
+ package = os.path.basename(path)
+ tag1 = get_tag(obs, project1, package, data1[path])
+ target_tag = get_tag(obs, target_project, package,
+ target_data.get(path))
+
+ if tag1 in pending.values():
+ if is_colorize:
+ tag1 = colorizer.green(tag1)
+ else:
+ tag1 += ' ready'
+ print "%-50s %s %s %-37s %-37s %s" % \
+ (path, target_data.get(path, 'Not found ')[:10],
+ data1[path][:10], target_tag, tag1,
+ pending.get(path, 'Not found'))
+
+
+class Diff(object):
+ """Subcommand: diff projects."""
+
+ name = 'diff'
+ description = 'Show the difference between projects'
+ help = description
+
+ @staticmethod
+ def add_arguments(parser, _config):
+ """Adds arguments to the parser. Called from [sub_]main."""
+ parser.add_argument('project1', help='Compared OBS project')
+ parser.add_argument('manifest', help='Manifest of current project')
+ parser.add_argument('manifest1', help='Manifest of compared project')
+
+ @staticmethod
+ def run(argv):
+ """Command line entry point. Called from [sub_]main."""
+ obs = OBS(argv.apiurl, argv.apiuser, argv.apipasswd)
+ return diff(obs, argv.project, argv.project1,
+ argv.manifest, argv.manifest1, argv.colorize)
+
+if __name__ == '__main__':
+ sys.exit(sub_main(sys.argv[1:], Diff()))