Skip to content

Instantly share code, notes, and snippets.

@dylanpieper
Created April 6, 2025 17:44
Show Gist options
  • Save dylanpieper/45e70c7415607f413d71bd2043ba0dcb to your computer and use it in GitHub Desktop.
Save dylanpieper/45e70c7415607f413d71bd2043ba0dcb to your computer and use it in GitHub Desktop.
Classify and visualize {ellmer} git commits
library(gh)
library(dplyr)
library(hellmer)
library(ggplot2)
library(gganimate)
library(dplyr)
library(tibble)
library(lubridate)
library(scales)
library(cli)
library(tidyr)
# classification
get_commits <- function(owner, repo) {
page <- 1
all_commits <- list()
repeat {
new_commits <- gh("GET /repos/{owner}/{repo}/commits",
owner = owner,
repo = repo,
per_page = 100,
page = page
)
if (length(new_commits) == 0) break
all_commits <- c(all_commits, new_commits)
page <- page + 1
}
lapply(all_commits, function(commit) {
data.frame(
sha = commit$sha,
author = ifelse(is.null(commit$commit$author$name), NA, commit$commit$author$name),
date = commit$commit$author$date,
message = commit$commit$message,
stringsAsFactors = FALSE
)
}) |>
bind_rows()
}
commits <- get_commits("tidyverse", "ellmer")
chat <- chat_future(chat_openai())
type_commit <- type_object(
"Classify GitHub commits",
action = type_enum("The commit type", c(
"add-feature", "patch-function",
"breaking-change", "document-edit-rename",
"improve-UI-or-UX", "validate-test-check-errors"
))
)
batch <- chat$batch(
prompts = commits$message,
type_spec = type_commit,
judgements = 1,
state_path = "commits.rds"
)
dat <- batch$texts()
# viz
commit_types <- lapply(dat, function(x) x$action) |> unlist()
commit_data <- tibble(
action = commit_types,
date = as.POSIXct(commits$date, format = "%Y-%m-%dT%H:%M:%SZ")
)
all_dates <- unique(commit_data$date)
all_actions <- unique(commit_data$action)
cumulative_data <- expand.grid(
date = all_dates,
action = all_actions
) |>
as_tibble() |>
arrange(date, action)
commit_counts <- commit_data |>
arrange(date) |>
group_by(date, action) |>
summarize(daily_count = n(), .groups = "drop") |>
right_join(cumulative_data, by = c("date", "action")) |>
replace_na(list(daily_count = 0)) |>
arrange(action, date) |>
group_by(action) |>
mutate(count = as.integer(cumsum(daily_count))) |>
ungroup()
max_count <- max(commit_counts$count)
action_colors <- c(
"add-feature" = "#4CAF50",
"patch-function" = "#2196F3",
"breaking-change" = "#F44336",
"document-edit-rename" = "#9C27B0",
"improve-UI-or-UX" = "#FF9800",
"validate-test-check-errors" = "#FFEB3B"
)
p <- ggplot(commit_counts, aes(x = reorder(action, count), y = count, fill = action)) +
geom_col() +
geom_text(
aes(label = sprintf("%d", count)),
hjust = -0.2,
size = 5
) +
coord_flip(clip = "off") +
labs(
title = "{ellmer}: GitHub Commits",
subtitle = "Date: {format(frame_time, '%b %d, %Y')}",
x = NULL,
y = "Number of Commits"
) +
theme_minimal() +
scale_fill_manual(values = action_colors) +
scale_y_continuous(
limits = c(0, max_count * 1.15),
expand = expansion(mult = c(0, 0))
) +
theme(
legend.position = "none",
plot.title = element_text(size = 24, face = "bold"),
plot.subtitle = element_text(size = 18),
axis.title.x = element_text(size = 16),
axis.text.x = element_text(size = 14),
axis.text.y = element_text(size = 14, face = "bold"),
panel.grid.major.y = element_blank()
) +
transition_time(date) +
ease_aes("linear")
animation <- animate(p, nframes = 150, fps = 10, width = 800, height = 500, res = 120)
anim_save("ellmer_commits.gif", animation)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment