├── .gitignore ├── README.rst ├── git-rewrite-author └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | git_rewrite_author.egg-info/ 2 | build/ 3 | dist/ 4 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Rewrite author/committer history of a git repository 2 | ==================================================== 3 | 4 | Have you ever accidentally committed to a git repository with a broken 5 | user config? No? But your co-workers have? 6 | 7 | So, you're stuck with commits like this:: 8 | 9 | Author: root 10 | 11 | Hotfix on the production server. This was urgent! 12 | 13 | Nasty. Or:: 14 | 15 | Author: John Doe 16 | 17 | Fixed bug #1. Committed on my laptop. 18 | 19 | Would it be nice to rewrite history? And take care of committers, as 20 | well as of authors? Without all the hassle? Now, you can! 21 | 22 | Usage:: 23 | 24 | $ git rewrite-author -w "John Doe " "John Doe " 25 | 26 | Then, to push your changes to the default remote:: 27 | 28 | $ git push --force 29 | 30 | Not using --force may duplicate the commits on origin, not replace them, so be careful with that. 31 | 32 | You're not sure which authors/committers are hidden in your repository? 33 | What about:: 34 | 35 | $ git rewrite-author -l 36 | 37 | Tags are rewritten automagically, too! 38 | 39 | After you've checked everything is okay, you may wish to remove the original refs backed up by git --filter-branch:: 40 | 41 | $ git for-each-ref --format="%(refname)" refs/original/ | xargs -r -n 1 git update-ref -d 42 | 43 | 44 | Enjoy! 45 | 46 | 47 | Installation 48 | ------------ 49 | 50 | Clone or download this repository and run:: 51 | 52 | $ python setup.py install 53 | -------------------------------------------------------------------------------- /git-rewrite-author: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Rewrite author/committer history of a git repository 4 | 5 | Have you ever accidentally committed to a git repository with a broken 6 | user config? No? But your co-workers have? 7 | 8 | So, you're stuck with commits like this:: 9 | 10 | Author: root 11 | 12 | Hotfix on the production server. This was urgent! 13 | 14 | Nasty. Or:: 15 | 16 | Author: John Doe 17 | 18 | Fixed bug #1. Committed on my laptop. 19 | 20 | Would it be nice to rewrite history? And take care of committers, as 21 | well as of authors? Without all the hassle? Now, you can! 22 | 23 | Usage:: 24 | 25 | $ git rewrite-author -w "John Doe " "John Doe " 26 | 27 | Then, to push your changes to the default remote:: 28 | 29 | $ git push --force 30 | 31 | Not using --force may duplicate the commits on origin, not replace them, so be careful with that. 32 | 33 | You're not sure which authors/committers are hidden in your repository? 34 | What about:: 35 | 36 | $ git rewrite-author -l 37 | 38 | Tags are rewritten automagically, too! 39 | 40 | Enjoy! 41 | 42 | 43 | Installation 44 | ------------ 45 | 46 | Clone or download this repository and run:: 47 | 48 | $ python setup.py install 49 | 50 | """ 51 | import argparse 52 | import re 53 | import subprocess 54 | import textwrap 55 | 56 | 57 | description = "Rewrite author/committer in git history" 58 | epilog = """ 59 | Example: 60 | 61 | $ git-rewrite-author -w "Name " "Full Name " 62 | """ 63 | 64 | git_rewrite_command = """git filter-branch --env-filter ' 65 | if [ "$GIT_AUTHOR_NAME" = "%(old_name)s" -a "$GIT_AUTHOR_EMAIL" = "%(old_email)s" ]; then 66 | export GIT_AUTHOR_NAME="%(new_name)s"; 67 | export GIT_AUTHOR_EMAIL="%(new_email)s"; 68 | fi; 69 | ' --tag-name-filter cat -f -- --all""" 70 | git_log_command = "git log --pretty=full" 71 | 72 | 73 | def parse_args(): 74 | """Parse command-line arguments""" 75 | 76 | parser = argparse.ArgumentParser(description=description, 77 | epilog=epilog, 78 | formatter_class=argparse.RawDescriptionHelpFormatter) 79 | parser.add_argument('-l', '--list', action='store_true', 80 | help="List all authors and committers") 81 | parser.add_argument('-w', '--rewrite', nargs=2, 82 | metavar=('old', 'new'), type=str, 83 | help="Rewrite authors and committers") 84 | return parser.parse_args() 85 | 86 | 87 | def main(args): 88 | """Rewrite history using args""" 89 | 90 | if args.list: 91 | list_git_authors() 92 | elif args.rewrite: 93 | old_name, old_email = parse_author_arg(args.rewrite[0]) 94 | new_name, new_email = parse_author_arg(args.rewrite[1]) 95 | 96 | rewrite_git_author(old_name, old_email, new_name, new_email) 97 | rewrite_git_committer(old_name, old_email, new_name, new_email) 98 | else: 99 | print("Doing nothing. Invoke with -h for help.") 100 | 101 | 102 | def parse_author_arg(arg): 103 | """Parse name/email argument""" 104 | 105 | name, email = re.match("(.+)\s<(.*)>", arg).groups() 106 | return name, email 107 | 108 | 109 | def rewrite_git_author(old_name, old_email, new_name, new_email): 110 | """Rewrite author history in git""" 111 | 112 | command = git_rewrite_command % \ 113 | { 'old_name': old_name, 'old_email': old_email, 'new_name': new_name, 'new_email': new_email} 114 | subprocess.call(command, shell=True) 115 | 116 | 117 | def rewrite_git_committer(old_name, old_email, new_name, new_email): 118 | """Rewrite committer history in git""" 119 | 120 | command = git_rewrite_command.replace('AUTHOR', 'COMMITTER') % \ 121 | { 'old_name': old_name, 'old_email': old_email, 'new_name': new_name, 'new_email': new_email} 122 | subprocess.call(command, shell=True) 123 | 124 | 125 | def list_git_authors(): 126 | """List authors and committers""" 127 | 128 | output = subprocess.check_output(git_log_command.split(' ')) 129 | matches = re.findall(b'(Author|Commit): (.*)\n', output) 130 | names = [u[1].decode('utf-8') for u in matches] 131 | unique_names = sorted(set(names)) 132 | 133 | print("The following authors and committers have contributed:\n") 134 | for name in unique_names: 135 | print(name) 136 | 137 | 138 | if __name__ == '__main__': 139 | args = parse_args() 140 | main(args) 141 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup 4 | 5 | setup(name='git rewrite author', 6 | version='1.0', 7 | description='Rewrite author/committer history of a git repository', 8 | author='David Fokkema', 9 | author_email='davidfokkema@icloud.com', 10 | url='https://github.com/davidfokkema/git-rewrite-author', 11 | classifiers=['Intended Audience :: Developers', 12 | 'Environment :: Console', 13 | 'Programming Language :: Python :: 2.7', 14 | 'Topic :: Software Development :: Version Control', 15 | 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)'], 16 | scripts=['git-rewrite-author']) 17 | --------------------------------------------------------------------------------