blob: 9bdb5e14b1d376aed254fc6496750105a5bed60d [file] [log] [blame]
Mike Frysingerf6013762019-06-13 02:30:51 -04001# -*- coding:utf-8 -*-
Julien Camperguedd654222014-01-09 16:21:37 +01002#
3# Copyright (C) 2014 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17from color import Coloring
18from command import PagedCommand
19from manifest_xml import XmlManifest
20
21class _Coloring(Coloring):
22 def __init__(self, config):
23 Coloring.__init__(self, config, "status")
24
25class 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
80 def _printRawDiff(self, diff):
81 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()
93 self._printLogs(project, otherProject, raw=True, color=False)
94
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):
Julien Camperguedd654222014-01-09 16:21:37 +0100184 self.out = _Coloring(self.manifest.globalConfig)
185 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
194 manifest1 = XmlManifest(self.manifest.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:
199 manifest2 = XmlManifest(self.manifest.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:
204 self._printRawDiff(diff)
205 else:
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +0200206 self._printDiff(diff, color=opt.color, pretty_format=opt.pretty_format)