util: add util/maint/show_changes_by_file.py tool
[gem5.git] / util / maint / show_changes_by_file.py
1 #!/usr/bin/env python2
2 #
3 # Copyright (c) 2018 Advanced Micro Devices, Inc.
4 # All rights reserved.
5 #
6 # For use for simulation and test purposes only
7 #
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions are met:
10 #
11 # 1. Redistributions of source code must retain the above copyright notice,
12 # this list of conditions and the following disclaimer.
13 #
14 # 2. Redistributions in binary form must reproduce the above copyright notice,
15 # this list of conditions and the following disclaimer in the documentation
16 # and/or other materials provided with the distribution.
17 #
18 # 3. Neither the name of the copyright holder nor the names of its contributors
19 # may be used to endorse or promote products derived from this software
20 # without specific prior written permission.
21 #
22 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
26 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 # POSSIBILITY OF SUCH DAMAGE.
33 #
34 # Author: Brandon Potter
35
36
37 import subprocess
38 from collections import OrderedDict, defaultdict
39
40 class OrderedDefaultDict(OrderedDict, defaultdict):
41 def __init__(self, default_factory=None, *args, **kwargs):
42 super(OrderedDefaultDict, self).__init__(*args, **kwargs)
43 self.default_factory = default_factory
44
45 def diff_files(upstream, feature, paths=[]):
46 """Given two git branches and an optional parameter 'path', determine
47 which files differ between the two branches. Afterwards, organize the
48 files with a printer-friendly data structure.
49
50 Returns: Dictionary of directories with their corresponding files
51 """
52
53 raw = subprocess.check_output(
54 [ "git", "diff", "--name-status", "%s..%s" % (upstream, feature),
55 "--" ] + paths
56 )
57
58 path = [line.split('\t')[1] for line in raw.splitlines()]
59
60 odd = OrderedDefaultDict(list)
61 for p in path:
62 direc = subprocess.check_output(["dirname", p]).strip() + "/"
63 filename = subprocess.check_output(["basename", p]).strip()
64 odd[direc].append("%s" % filename)
65
66 return odd
67
68 def cl_hash(upstream, feature, path):
69 """Given two git branches and full path, record the identifier hash
70 for changesets which diff between the upstream branch and feature branch.
71 The changesets are ordered from oldest to youngest changesets in the
72 list.
73
74 Returns: List of identifier hashes
75 """
76
77 raw = subprocess.check_output(
78 [ "git", "log", "--oneline", "%s..%s" % (upstream, feature),
79 "--", path ]
80 )
81
82 return [l.split()[0] for l in raw.splitlines()]
83
84 def _main():
85 import argparse
86 parser = argparse.ArgumentParser(
87 description="List all changes between an upstream branch and a " \
88 "feature branch by filename(s) and changeset hash(es).")
89
90 parser.add_argument("--upstream", "-u", type=str, default="origin/master",
91 help="Upstream branch for comparison. " \
92 "Default: %(default)s")
93 parser.add_argument("--feature", "-f", type=str, default="HEAD",
94 help="Feature branch for comparison. " \
95 "Default: %(default)s")
96 parser.add_argument("paths", metavar="PATH", type=str, nargs="*",
97 help="Paths to list changes for")
98
99 args = parser.parse_args()
100
101 odd = diff_files(args.upstream, args.feature, paths=args.paths)
102
103 for key, value in odd.iteritems():
104 print key
105 for entry in value:
106 print " %s" % entry
107 path = key + entry
108 sha = cl_hash(args.upstream, args.feature, path)
109 for s in sha:
110 print "\t%s" % s
111 print
112
113 if __name__ == "__main__":
114 _main()