{"thread":{"id":"2349","subject":"Fixing Commit And Author","startedAt":"2005-11-04T16:38:45Z","lastAt":"2005-11-04T21:35:21Z","messageCount":3,"participants":["Darrin Thompson","Linus Torvalds"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"11132","messageId":"1131122325.5446.14.camel@localhost.localdomain","threadId":"2349","inReplyTo":null,"subject":"Fixing Commit And Author","fromName":"Darrin Thompson","fromEmail":"darrint@progeny.com","sentAt":"2005-11-04T16:38:45Z","receivedAt":"2005-11-04T16:38:45Z","isPatch":false,"sender":{"key":"darrint@progeny.com","avatar":null},"body":"I've got a small project in git where I made a dumb error. All my\ncommits have author/committer information like this:\n\nAuthor: Darrin Thompson <darrint@dhcp-1-211.(none)>  2005-10-20 16:50:38\nCommitter: Darrin Thompson <darrint@dhcp-1-211.(none)>  2005-10-20\n16:50:38\nTags: svn-5099\n\nI'd like to replace the commits (yes, I know that means all of them)\nwith new ones with corrected email addresses and also manage to migrate\nmy tags. A push in the right direction would be appreciated.\n\nNext I'd like to do the same with the kernel sources... :-)\n\n--\nDarrin\n"},{"id":"11137","messageId":"Pine.LNX.4.64.0511040924400.27915@g5.osdl.org","threadId":"2349","inReplyTo":"1131122325.5446.14.camel@localhost.localdomain","subject":"Re: Fixing Commit And Author","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-11-04T17:30:50Z","receivedAt":"2005-11-04T17:30:50Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Fri, 4 Nov 2005, Darrin Thompson wrote:\n\n> I've got a small project in git where I made a dumb error. All my\n> commits have author/committer information like this:\n> \n> Author: Darrin Thompson <darrint@dhcp-1-211.(none)>  2005-10-20 16:50:38\n> Committer: Darrin Thompson <darrint@dhcp-1-211.(none)>  2005-10-20\n> 16:50:38\n> Tags: svn-5099\n> \n> I'd like to replace the commits (yes, I know that means all of them)\n> with new ones with corrected email addresses and also manage to migrate\n> my tags. A push in the right direction would be appreciated.\n\nThere's a program in the git sources called \"git-convert-objects.c\".\n\nIt basically knows how to walk the git object chains, and rewrite each \nobject according to a few rules.\n\nThe rules currently do _not_ include changing the author/committer info, \nbut it does know how to parse the really old-style dates, for example, \nwhich are on those same lines, so adding some code there to also re-write \nthe author/committer name and email wouldn't be impossible.\n\nThe code isn't necessarily all that easy to understand, and usage-wise you \nalso have to convert each head separately (you tell it which branch head \nyou want to convert, it trawls every reachable object from that head, and \nwill create the new objects and return the new head value).\n\nWhat I'm trying to say is that it might not be _pleasant_, but it's \ncertainly something you can automate and do in a timely manner (ie a small \nproject will take just a few seconds - or minutes - to convert).\n\n> Next I'd like to do the same with the kernel sources... :-)\n\nThe same program will work, but it will take some time.\n\nActually, as long as you only rewrite commits, it should even be \nreasonably efficient. It's when you start rewriting every single object \n(like I did when I switched the compression scheme around) that it gets \n_really_ expensive, and a project like the kernel would take a long long \ntime.\n\nHint to the wise: don't do the conversion on the only copy of the \nrepository you have. It's always worked for me, but hey, maybe I'm just \nlucky and never write buggy conversion software.\n\n\t\t\tLinus\n"},{"id":"11152","messageId":"1131140121.24792.9.camel@localhost.localdomain","threadId":"2349","inReplyTo":"Pine.LNX.4.64.0511040924400.27915@g5.osdl.org","subject":"Re: Fixing Commit And Author","fromName":"Darrin Thompson","fromEmail":"darrint@progeny.com","sentAt":"2005-11-04T21:35:21Z","receivedAt":"2005-11-04T21:35:21Z","isPatch":false,"sender":{"key":"darrint@progeny.com","avatar":null},"body":"On Fri, 2005-11-04 at 09:30 -0800, Linus Torvalds wrote:\n> The rules currently do _not_ include changing the author/committer info, \n> but it does know how to parse the really old-style dates, for example, \n> which are on those same lines, so adding some code there to also re-write \n> the author/committer name and email wouldn't be impossible.\n> \n> The code isn't necessarily all that easy to understand, \n\nI can follow it mostly. If the commits were being read and built from\nscratch with strbuf's I'd be willing to give it a try. :-)\n\nI was able to hack together a really stupid python script that worked\nfine (I think) for my less demanding case.\n\n\nimport sys\nimport os\n\nname = \"Darrin Thompson\"\nmail = \"email@address\"\nos.environ['GIT_AUTHOR_NAME'] = name\nos.environ['GIT_AUTHOR_EMAIL'] = mail\nos.environ['GIT_COMMITTER_NAME'] = name\nos.environ['GIT_COMMITTER_EMAIL'] = mail\n\ncommits = os.popen('git-rev-list HEAD | tac')\n\ndef parse_commit(commit):\n    tree = '///'\n    parents = []\n    lines = os.popen('git-cat-file commit %s' % commit)\n    for line in lines:\n        if not line.strip():\n            break\n        key, value = line.strip().split(None, 1)\n        if key == 'tree':\n            tree = value\n        elif key == 'parent':\n            parents.append(value)\n        elif key == 'author' or key == 'committer':\n            parts = value.split()\n            seconds, tz = parts[-2:]\n            date = ' '.join([seconds, tz])\n\n    message = ''.join(lines)\n    return tree, parents, message, date\n\n\ntag_map = {}\ntags = os.popen('git-rev-parse --symbolic --all | grep ^refs/tags/')\nfor line in tags:\n    filename = line.strip()\n    tag_name = filename.split('/')[-1]\n    tag_commit = os.popen('git-rev-parse %s' % tag_name).read().strip()\n    tag_list = tag_map.setdefault(tag_commit, [])\n    tag_list.append(tag_name)\n\ncommit_map = {}\nfor line in commits:\n    commit = line.strip()\n    tree, bio_parents, message, date = parse_commit(commit)\n    real_parents = [ commit_map[p] for p in bio_parents ]\n    parents_args = ' '.join([ '-p %s' % p for p in real_parents ])\n    os.environ['GIT_COMMITTER_DATE'] = date\n    os.environ['GIT_AUTHOR_DATE'] = date\n    write_handle, read_handle \\\n        = os.popen2('git-commit-tree %s %s' % (tree, parents_args), 'w')\n    write_handle.write(message)\n    write_handle.close()\n    new_commit = read_handle.read().strip()\n    commit_map[commit] = new_commit\n    for tag_name in tag_map.get(commit, []):\n        os.system('git-tag -f %s %s' % (tag_name, new_commit))\n    read_handle.close()\n\nprint new_commit\n\n\n--\nDarrin\n"}]}