threads / discuss / 2349

Fixing Commit And Author

Subject: Fixing Commit And Author

## tl;dr

3 messages between Nov 4, 2005 and Nov 4, 2005.

replies: 2people: 2as markdown or json

Darrin Thompson· Nov 4, 2005, 16:38 UTC · lore

I've got a small project in git where I made a dumb error. All my commits have author/committer information like this:

Author: Darrin Thompson <darrint@dhcp-1-211.(none)>  2005-10-20 16:50:38
Committer: Darrin Thompson <darrint@dhcp-1-211.(none)>  2005-10-20
16:50:38
Tags: svn-5099

I'd like to replace the commits (yes, I know that means all of them) with new ones with corrected email addresses and also manage to migrate my tags. A push in the right direction would be appreciated.

Next I'd like to do the same with the kernel sources... :-)

-- Darrin

Linus Torvalds· Nov 4, 2005, 17:30 UTC · re: Darrin Thompson · lore

Re: Fixing Commit And Author

On Fri, 4 Nov 2005, Darrin Thompson wrote:
Show 11 quoted lines
> I've got a small project in git where I made a dumb error. All my
> commits have author/committer information like this:
> 
> Author: Darrin Thompson <darrint@dhcp-1-211.(none)>  2005-10-20 16:50:38
> Committer: Darrin Thompson <darrint@dhcp-1-211.(none)>  2005-10-20
> 16:50:38
> Tags: svn-5099
> 
> I'd like to replace the commits (yes, I know that means all of them)
> with new ones with corrected email addresses and also manage to migrate
> my tags. A push in the right direction would be appreciated.
There's a program in the git sources called "git-convert-objects.c".

It basically knows how to walk the git object chains, and rewrite each object according to a few rules.

The rules currently do _not_ include changing the author/committer info, but it does know how to parse the really old-style dates, for example, which are on those same lines, so adding some code there to also re-write the author/committer name and email wouldn't be impossible.

The code isn't necessarily all that easy to understand, and usage-wise you also have to convert each head separately (you tell it which branch head you want to convert, it trawls every reachable object from that head, and will create the new objects and return the new head value).

What I'm trying to say is that it might not be _pleasant_, but it's certainly something you can automate and do in a timely manner (ie a small project will take just a few seconds - or minutes - to convert).

> Next I'd like to do the same with the kernel sources... :-)
The same program will work, but it will take some time.

Actually, as long as you only rewrite commits, it should even be reasonably efficient. It's when you start rewriting every single object (like I did when I switched the compression scheme around) that it gets _really_ expensive, and a project like the kernel would take a long long time.

Hint to the wise: don't do the conversion on the only copy of the repository you have. It's always worked for me, but hey, maybe I'm just lucky and never write buggy conversion software.

			Linus
Darrin Thompson· Nov 4, 2005, 21:35 UTC · re: Linus Torvalds · lore

Re: Fixing Commit And Author

On Fri, 2005-11-04 at 09:30 -0800, Linus Torvalds wrote:
Show 6 quoted lines
> The rules currently do _not_ include changing the author/committer info, 
> but it does know how to parse the really old-style dates, for example, 
> which are on those same lines, so adding some code there to also re-write 
> the author/committer name and email wouldn't be impossible.
> 
> The code isn't necessarily all that easy to understand, 

I can follow it mostly. If the commits were being read and built from scratch with strbuf's I'd be willing to give it a try. :-)

I was able to hack together a really stupid python script that worked fine (I think) for my less demanding case.

import sys import os

name = "Darrin Thompson" mail = "email@address" os.environ['GIT_AUTHOR_NAME'] = name os.environ['GIT_AUTHOR_EMAIL'] = mail os.environ['GIT_COMMITTER_NAME'] = name os.environ['GIT_COMMITTER_EMAIL'] = mail

commits = os.popen('git-rev-list HEAD | tac')
def parse_commit(commit):
    tree = '///'
    parents = []
    lines = os.popen('git-cat-file commit %s' % commit)
    for line in lines:
        if not line.strip():
            break
        key, value = line.strip().split(None, 1)
        if key == 'tree':
            tree = value
        elif key == 'parent':
            parents.append(value)
        elif key == 'author' or key == 'committer':
            parts = value.split()
            seconds, tz = parts[-2:]
            date = ' '.join([seconds, tz])
    message = ''.join(lines)
    return tree, parents, message, date
tag_map = {}
tags = os.popen('git-rev-parse --symbolic --all | grep ^refs/tags/')
for line in tags:
    filename = line.strip()
    tag_name = filename.split('/')[-1]
    tag_commit = os.popen('git-rev-parse %s' % tag_name).read().strip()
    tag_list = tag_map.setdefault(tag_commit, [])
    tag_list.append(tag_name)
commit_map = {}
for line in commits:
    commit = line.strip()
    tree, bio_parents, message, date = parse_commit(commit)
    real_parents = [ commit_map[p] for p in bio_parents ]
    parents_args = ' '.join([ '-p %s' % p for p in real_parents ])
    os.environ['GIT_COMMITTER_DATE'] = date
    os.environ['GIT_AUTHOR_DATE'] = date
    write_handle, read_handle \
        = os.popen2('git-commit-tree %s %s' % (tree, parents_args), 'w')
    write_handle.write(message)
    write_handle.close()
    new_commit = read_handle.read().strip()
    commit_map[commit] = new_commit
    for tag_name in tag_map.get(commit, []):
        os.system('git-tag -f %s %s' % (tag_name, new_commit))
    read_handle.close()
print new_commit

-- Darrin

← back to recent threads