get the list of authors in a git repository efficiently with libgit2

Viewed 245

I need to get the list of authors for a git repository, like git shortlog -s does in the command line.

What I'm doing currently, and works, is to do a revwalk (going over all the revisions) and collecting the list of authors for all the commits one at a time, but this is quite slow, especially compared to the command-line git command.

I am using rust but I expect the solution is similar in C or another language binding to libgit2.

This is my current, working, but slow, code:

        let repo = Repository::open(&git_path)?;
        let mut revwalk = repo.revwalk()?;
        revwalk.push_head()?;
        let mut authors: Vec<String> = revwalk
            .map(|r| {
                let oid = r?;
                repo.find_commit(oid)
            })
            .filter_map(|c| match c {
                Ok(commit) => Some(commit),
                Err(e) => {
                    println!("Error walking the revisions {}, skipping", e);
                    None
                }
            })
            .fold(HashSet::new(), |mut sofar, cur| {
                if let Some(name) = cur.author().name() {
                    sofar.insert(name.to_string());
                }
                sofar
            })
            .into_iter()
            .collect();
        authors.sort();

EDIT Ok when building with rust in release mode it is noticeably faster. I still wonder whether there's not a more efficient way still.

2 Answers

There isn't a more efficient way, unfortunately, unless you know something about the repository structure (e.g., that all commits before a certain point are made by a certain user). If you want to know something about every commit, you have to necessarily traverse every commit. Git doesn't provide a cache for this information, so walking the history is required.

libgit2, being a bit more generic, may not be able to take advantage of the same optimizations as Git itself, but overall the algorithm is the same.

The general algorithm is as good as it gets: you must walk each commit in order to gather all author names, there's no way around it.

Nitpick: You are allocating a String for the author's name for each commit. It might be faster to check if the name is already in the set first, which works with just a &str (!sofar.contains(name)).

On nightly, you could change sofar.insert(name.to_string()); to sofar.get_or_insert_with(name, str::to_string);. get_or_insert_with will only perform one lookup, instead of two with the contains/insert pair.

Related