1#!/usr/bin/env python2.7
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
37import subprocess
38from collections import OrderedDict, defaultdict
39
40class 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
45def 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
68def 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
84def _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
113if __name__ == "__main__":
114    _main()
115