#!/usr/bin/env python

"""Rewrite author/committer history of a git repository

Have you ever accidentally committed to a git repository with a broken
user config?  No?  But your co-workers have?

So, you're stuck with commits like this::

    Author: root <root@localhost>

        Hotfix on the production server.  This was urgent!

Nasty.  Or::

    Author: John Doe <john@localhost>

        Fixed bug #1.  Committed on my laptop.

Would it be nice to rewrite history?  And take care of committers, as
well as of authors?  Without all the hassle?  Now, you can!

Usage::

    $ git-rewrite-author -w "John Doe <john@localhost>" "John Doe <dearjohn@example.com>"

You're not sure which authors/committers are hidden in your repository?
What about::

    $ git-rewrite-author -l

Tags are rewritten automagically, too!

Enjoy!

"""
import argparse
import re
import subprocess
import textwrap


description = "Rewrite author/committer in git history"
epilog = """
Example:

$ git-rewrite-author -w "Name <me@localhost>" "Full Name <me@example.com>"
"""

git_rewrite_command = """git filter-branch --env-filter 'if [ "$GIT_AUTHOR_NAME" == "%s" -a "$GIT_AUTHOR_EMAIL" == "%s" ]; then GIT_AUTHOR_NAME="%s"; GIT_AUTHOR_EMAIL="%s"; fi; export GIT_AUTHOR_NAME; export GIT_AUTHOR_EMAIL' --tag-name-filter cat -f -- --all"""
git_log_command = "git log --pretty=full"


def parse_args():
    """Parse command-line arguments"""

    parser = argparse.ArgumentParser(description=description,
                         epilog=epilog,
                         formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('-l', '--list', action='store_true',
                        help="List all authors and committers")
    parser.add_argument('-f', '--file', nargs=1,
    					help="Process file of authors and committers to be changed. NOT WORKING YET!")
    parser.add_argument('-w', '--rewrite', nargs=2,
                        metavar=('old', 'new'), type=str,
                        help="Rewrite authors and committers")
    return parser.parse_args()


def main(args):
    """Rewrite history using args"""

    if args.list:
        list_git_authors()
    elif args.file:
    	rewrite_git_authors_from_file(args.file[0])    	
    elif args.rewrite:
        old_name, old_email = parse_author_arg(args.rewrite[0])
        new_name, new_email = parse_author_arg(args.rewrite[1])

        rewrite_git_author(old_name, old_email, new_name, new_email)
        rewrite_git_committer(old_name, old_email, new_name, new_email)
    else:
        print "Doing nothing.  Invoke with -h for help."
        
def rewrite_git_authors_from_file(filename):
	"""Get the and process git author history revision line by line"""
    	print "Feature not finished yet"
    	line_num = 0
    	print "Now attempting to open the file", filename
    	authors = open(filename,'r')
    	for line in authors.readlines():
    		line_num += 1
    		print "Now processing ", line_num, line
    		old_name, old_email, new_name, new_email = parse_file_line(line)
    		print "old name:", old_name, "and old email:", old_email
    		print "new name:", new_name, "and new email:", new_email
    		
    		rewrite_git_author(old_name, old_email, new_name, new_email)  		
    	authors.close();
    	print "You have rewritten ", line_num, "authors / committers in git history!"
	        
def parse_file_line(given_line):
	"""Parse out old name(s), old email, new names, new email from the text file"""
	authorTokens = given_line.split( )
	numTokens = len(authorTokens)
	""" print "There are", numTokens, " tokens in this line"
	print authorTokens """
	
	oldName = ""
	oldEmail = ""
	newName = ""
	newEmail = ""
	
	if numTokens >= 4:
		newName = authorTokens[numTokens-3] + " " + authorTokens[numTokens-2]
		newEmail = authorTokens[numTokens-1]
	
	if numTokens == 6:
		oldName = authorTokens[0] + " " + authorTokens[1]
		oldEmail = authorTokens[2]
		
	elif numTokens == 4:
		if authorTokens[0][0] == "<":
			oldEmail = authorTokens[0]
		else:
			oldName = authorTokens[0]
			
	elif numTokens == 5:
		oldName = authorTokens[0]
		if authorTokens[1][0] == "<":
			oldEmail = authorTokens[1]
		else:
			oldName = oldName + " " + authorTokens[1]
			
	return oldName, oldEmail, newName, newEmail

def parse_author_arg(arg):
    """Parse name/email argument"""

    name, email = re.match("([A-Za-z\(\)\. ]+) <(.*)>", arg).groups()
    return name, email


def rewrite_git_author(old_name, old_email, new_name, new_email):
    """Rewrite author history in git"""

    command = git_rewrite_command % (old_name, old_email, new_name,
                                     new_email)
    subprocess.call(command, shell=True)


def rewrite_git_committer(old_name, old_email, new_name, new_email):
    """Rewrite committer history in git"""

    command = git_rewrite_command.replace('AUTHOR', 'COMMITTER') % \
                (old_name, old_email, new_name, new_email)
    subprocess.call(command, shell=True)


def list_git_authors():
    """List authors and committers"""

    output = subprocess.check_output(git_log_command.split(' '))
    matches = re.findall('(Author|Commit): (.*)\n', output)
    names = [u[1] for u in matches]
    unique_names = sorted(set(names))

    print "The following authors and committers have contributed:"
    print
    for name in unique_names:
        print name


if __name__ == '__main__':
    args = parse_args()
    main(args)
