blob: 392e5972d1bbe70a092a6dfdac7d435e70158991 [file] [log] [blame]
Julien Camperguedd654222014-01-09 16:21:37 +01001# Copyright (C) 2014 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15from color import Coloring
16from command import PagedCommand
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -040017from manifest_xml import RepoClient
Julien Camperguedd654222014-01-09 16:21:37 +010018
David Pursehouse819827a2020-02-12 15:20:19 +090019
Julien Camperguedd654222014-01-09 16:21:37 +010020class _Coloring(Coloring):
21 def __init__(self, config):
22 Coloring.__init__(self, config, "status")
23
David Pursehouse819827a2020-02-12 15:20:19 +090024
Julien Camperguedd654222014-01-09 16:21:37 +010025class Diffmanifests(PagedCommand):
26 """ A command to see logs in projects represented by manifests
27
28 This is used to see deeper differences between manifests. Where a simple
29 diff would only show a diff of sha1s for example, this command will display
30 the logs of the project between both sha1s, allowing user to see diff at a
31 deeper level.
32 """
33
34 common = True
35 helpSummary = "Manifest diff utility"
36 helpUsage = """%prog manifest1.xml [manifest2.xml] [options]"""
37
38 helpDescription = """
39The %prog command shows differences between project revisions of manifest1 and
40manifest2. if manifest2 is not specified, current manifest.xml will be used
41instead. Both absolute and relative paths may be used for manifests. Relative
42paths start from project's ".repo/manifests" folder.
43
44The --raw option Displays the diff in a way that facilitates parsing, the
45project pattern will be <status> <path> <revision from> [<revision to>] and the
46commit pattern will be <status> <onelined log> with status values respectively :
47
48 A = Added project
49 R = Removed project
50 C = Changed project
51 U = Project with unreachable revision(s) (revision(s) not found)
52
53for project, and
54
55 A = Added commit
56 R = Removed commit
57
58for a commit.
59
60Only changed projects may contain commits, and commit status always starts with
61a space, and are part of last printed project.
62Unreachable revisions may occur if project is not up to date or if repo has not
63been initialized with all the groups, in which case some projects won't be
64synced and their revisions won't be found.
65
66"""
67
68 def _Options(self, p):
69 p.add_option('--raw',
70 dest='raw', action='store_true',
71 help='Display raw diff.')
72 p.add_option('--no-color',
73 dest='color', action='store_false', default=True,
74 help='does not display the diff in color.')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +020075 p.add_option('--pretty-format',
76 dest='pretty_format', action='store',
77 metavar='<FORMAT>',
78 help='print the log using a custom git pretty format string')
Julien Camperguedd654222014-01-09 16:21:37 +010079
Connor Newton8b40c002020-03-11 17:25:20 +000080 def _printRawDiff(self, diff, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +010081 for project in diff['added']:
82 self.printText("A %s %s" % (project.relpath, project.revisionExpr))
83 self.out.nl()
84
85 for project in diff['removed']:
86 self.printText("R %s %s" % (project.relpath, project.revisionExpr))
87 self.out.nl()
88
89 for project, otherProject in diff['changed']:
90 self.printText("C %s %s %s" % (project.relpath, project.revisionExpr,
91 otherProject.revisionExpr))
92 self.out.nl()
Connor Newton8b40c002020-03-11 17:25:20 +000093 self._printLogs(project, otherProject, raw=True, color=False, pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +010094
95 for project, otherProject in diff['unreachable']:
96 self.printText("U %s %s %s" % (project.relpath, project.revisionExpr,
97 otherProject.revisionExpr))
98 self.out.nl()
99
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +0200100 def _printDiff(self, diff, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +0100101 if diff['added']:
102 self.out.nl()
103 self.printText('added projects : \n')
104 self.out.nl()
105 for project in diff['added']:
106 self.printProject('\t%s' % (project.relpath))
107 self.printText(' at revision ')
108 self.printRevision(project.revisionExpr)
109 self.out.nl()
110
111 if diff['removed']:
112 self.out.nl()
113 self.printText('removed projects : \n')
114 self.out.nl()
115 for project in diff['removed']:
116 self.printProject('\t%s' % (project.relpath))
117 self.printText(' at revision ')
118 self.printRevision(project.revisionExpr)
119 self.out.nl()
120
121 if diff['changed']:
122 self.out.nl()
123 self.printText('changed projects : \n')
124 self.out.nl()
125 for project, otherProject in diff['changed']:
126 self.printProject('\t%s' % (project.relpath))
127 self.printText(' changed from ')
128 self.printRevision(project.revisionExpr)
129 self.printText(' to ')
130 self.printRevision(otherProject.revisionExpr)
131 self.out.nl()
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +0200132 self._printLogs(project, otherProject, raw=False, color=color,
133 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +0100134 self.out.nl()
135
136 if diff['unreachable']:
137 self.out.nl()
138 self.printText('projects with unreachable revisions : \n')
139 self.out.nl()
140 for project, otherProject in diff['unreachable']:
141 self.printProject('\t%s ' % (project.relpath))
142 self.printRevision(project.revisionExpr)
143 self.printText(' or ')
144 self.printRevision(otherProject.revisionExpr)
145 self.printText(' not found')
146 self.out.nl()
147
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +0200148 def _printLogs(self, project, otherProject, raw=False, color=True,
149 pretty_format=None):
150
151 logs = project.getAddedAndRemovedLogs(otherProject,
152 oneline=(pretty_format is None),
153 color=color,
154 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +0100155 if logs['removed']:
156 removedLogs = logs['removed'].split('\n')
157 for log in removedLogs:
158 if log.strip():
159 if raw:
160 self.printText(' R ' + log)
161 self.out.nl()
162 else:
163 self.printRemoved('\t\t[-] ')
164 self.printText(log)
165 self.out.nl()
166
167 if logs['added']:
168 addedLogs = logs['added'].split('\n')
169 for log in addedLogs:
170 if log.strip():
171 if raw:
172 self.printText(' A ' + log)
173 self.out.nl()
174 else:
175 self.printAdded('\t\t[+] ')
176 self.printText(log)
177 self.out.nl()
178
Mike Frysingerae6cb082019-08-27 01:10:59 -0400179 def ValidateOptions(self, opt, args):
Julien Camperguedd654222014-01-09 16:21:37 +0100180 if not args or len(args) > 2:
Mike Frysingerae6cb082019-08-27 01:10:59 -0400181 self.OptionParser.error('missing manifests to diff')
Julien Camperguedd654222014-01-09 16:21:37 +0100182
Mike Frysingerae6cb082019-08-27 01:10:59 -0400183 def Execute(self, opt, args):
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400184 self.out = _Coloring(self.client.globalConfig)
Julien Camperguedd654222014-01-09 16:21:37 +0100185 self.printText = self.out.nofmt_printer('text')
186 if opt.color:
David Pursehousee5913ae2020-02-12 13:56:59 +0900187 self.printProject = self.out.nofmt_printer('project', attr='bold')
188 self.printAdded = self.out.nofmt_printer('green', fg='green', attr='bold')
189 self.printRemoved = self.out.nofmt_printer('red', fg='red', attr='bold')
190 self.printRevision = self.out.nofmt_printer('revision', fg='yellow')
Julien Camperguedd654222014-01-09 16:21:37 +0100191 else:
192 self.printProject = self.printAdded = self.printRemoved = self.printRevision = self.printText
193
Mike Frysingere3315bb2021-02-09 23:45:28 -0500194 manifest1 = RepoClient(self.repodir)
Basil Gelloc7453502018-05-25 20:23:52 +0300195 manifest1.Override(args[0], load_local_manifests=False)
Julien Camperguedd654222014-01-09 16:21:37 +0100196 if len(args) == 1:
197 manifest2 = self.manifest
198 else:
Mike Frysingere3315bb2021-02-09 23:45:28 -0500199 manifest2 = RepoClient(self.repodir)
Basil Gelloc7453502018-05-25 20:23:52 +0300200 manifest2.Override(args[1], load_local_manifests=False)
Julien Camperguedd654222014-01-09 16:21:37 +0100201
202 diff = manifest1.projectsDiff(manifest2)
203 if opt.raw:
Connor Newton8b40c002020-03-11 17:25:20 +0000204 self._printRawDiff(diff, pretty_format=opt.pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +0100205 else:
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +0200206 self._printDiff(diff, color=opt.color, pretty_format=opt.pretty_format)