Page MenuHomePhabricator
Paste P1998

git-files-commits-table.py
ActivePublic

Authored by avivey on Jul 7 2016, 6:08 PM.
Tags
None
Referenced Files
F7549156: git-files-commits-table.py
Jun 7 2020, 7:40 AM
F4950861: git-files-commits-table.py
May 10 2017, 9:08 PM
F1844872: git-files-commits-table.py
Sep 23 2016, 12:46 AM
F1715128: git-files-commits-table.py
Jul 7 2016, 6:08 PM
Subscribers
None
#! /usr/bin/env python3
# Produces a table of filenames x commits, showing where a file was touched.
# Useful for rebasing feature branch into reasonable commits.
# usage:
# git log --name-status --oneline origin/master.. | $0
# git log --name-status --oneline origin/master.. | $0 --html > file.html
# A variant can also be found at https://secure.phabricator.com/P1998
import argparse
import sys
args_parser = None
def parse_args():
global args_parser
usage = \
'''git log --name-status --oneline origin/master.. | %(prog)s --html > file.html
git log --name-status --oneline origin/master.. | %(prog)s
'''
args_parser = argparse.ArgumentParser(
description='Show nice table to assist cleaning up git commits.',
usage=usage,
)
args_parser.add_argument('-H', '--html', dest='mode', action='store_const',
const='html', default='text',
help='Output formatted HTML table')
args_parser.add_argument('-t', '-T', '--text', dest='mode',
action='store_const',
const='text', default='text',
help='Output plain-text table')
return args_parser.parse_args()
def collect_data():
files = set()
commits = list()
class Commit(object):
def __init__(self, title):
self.title = title
self.files = dict()
def addFile(self, filename, status):
self.files[filename] = status
curr_commit = None
for line in sys.stdin:
try:
if line[0] in '0123456789abcdef':
curr_commit = Commit(line)
commits.insert(0, curr_commit)
elif line[0] == 'R':
v, f1, f2 = line.split('\t')
f1 = f1.strip()
f2 = f2.strip()
curr_commit.addFile(f1, '\u2563')
curr_commit.addFile(f2, '\u2560')
files.add(f1)
files.add(f2)
else:
v, f = line.split('\t', 2)
f = f.strip()
curr_commit.addFile(f, v)
files.add(f)
except:
print(line)
raise
return commits, files
def output_html(commits, files):
if sys.stdout.isatty():
print("Output is set to html, redirect this to file.")
sys.exit(4)
CSS = '''
td {
border: 1px solid black;
}
table {
border-collapse: collapse;
}
td:nth-child(even),
th.rotate:nth-child(even) > div > span {
background: #CCC
}
th.rotate {
height: 350px;
}
th.rotate > div {
transform:
translate(20px, 164px)
rotate(315deg);
transform-origin: left;
width: 30px;
white-space: nowrap;
}
th.rotate > div > span {
border-bottom: 1px solid black;
padding: 5px 0;
}
tbody th {
text-align: right;
}
tbody td {
text-align: center;
}
'''
print('<html><head><style>')
print(CSS)
print('</style></head>')
print('''
<body>
<table>
<colgroup>''')
print('<col>')
print(len(commits) * '<col>')
print('''</colgroup>
<thead><tr>
<th></th>
''')
for c in commits:
print('<th class="rotate"><div><span>')
print(c.title)
print('</span></div></th>')
print('</tr> </thead><tbody>')
for f in sorted(files):
print('<tr>')
print('<th>%s</th>' % f, end='')
for c in commits:
s = c.files.get(f, ' ')
print('<td>%s</td>' % s, end='')
print('</tr>')
print('</tbody></table></html>')
def output_text(commits, files):
name_len = 0
for f in files:
name_len = max(name_len, len(f))
name_pattern = '%' + str(name_len) + 's'
for f in sorted(files):
print(name_pattern % f, end=' ')
for c in commits:
s = c.files.get(f, ' ')
print(s, end=' ')
print()
args = parse_args()
if sys.stdin.isatty():
print("\n\t\tstdin is tty, not processing.\n")
args_parser.print_help()
sys.exit(1)
commits, files = collect_data()
if args.mode == 'text':
output_text(commits, files)
elif args.mode == 'html':
output_html(commits, files)
else:
print("500 Internal Server Error")
args_parser.print_help()
sys.exit(3)

Event Timeline

avivey changed the visibility from "All Users" to "Public (No Login Required)".