commit-graph: expire commit-graph files

As we merge commit-graph files in a commit-graph chain, we should clean up the files that are no longer used. This change introduces an 'expiry_window' value to the context, which is always zero (for now). We then check the modified time of each graph-{hash}.graph file in the $OBJDIR/info/commit-graphs folder and unlink the files that are older than the expiry_window. Since this is always zero, this immediately clears all unused graph files. We will update the value to match a config setting in a future change. Signed-off-by: Derrick Stolee <dstolee@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Derrick Stolee committed Jun 18, 2019 at 11:14 UTC 8d84097f9655a954ea7e94ba08d93e5940ccb2ae
3 files changed +85 -1
Documentation/technical/commit-graph.txt
+15
@@ -266,6 +266,21 @@ The merge strategy values (2 for the size multiple, 64,000 for the maximum
266 number of commits) could be extracted into config settings for full
267 flexibility.
268
269 +## Deleting graph-{hash} files
270 +
271 +After a new tip file is written, some `graph-{hash}` files may no longer
272 +be part of a chain. It is important to remove these files from disk, eventually.
273 +The main reason to delay removal is that another process could read the
274 +`commit-graph-chain` file before it is rewritten, but then look for the
275 +`graph-{hash}` files after they are deleted.
276 +
277 +To allow holding old split commit-graphs for a while after they are unreferenced,
278 +we update the modified times of the files when they become unreferenced. Then,
279 +we scan the `$OBJDIR/info/commit-graphs/` directory for `graph-{hash}`
280 +files whose modified times are older than a given expiry window. This window
281 +defaults to zero, but can be changed using command-line arguments or a config
282 +setting.
283 +
284 ## Chains across multiple object directories
285
286 In a repo with alternates, we look for the `commit-graph-chain` file starting
commit-graph.c
+69
@@ -1652,6 +1652,70 @@ static void merge_commit_graphs(struct write_commit_graph_context *ctx)
1652 sort_and_scan_merged_commits(ctx);
1653 }
1654
1655 +static void mark_commit_graphs(struct write_commit_graph_context *ctx)
1656 +{
1657 + uint32_t i;
1658 + time_t now = time(NULL);
1659 +
1660 + for (i = ctx->num_commit_graphs_after - 1; i < ctx->num_commit_graphs_before; i++) {
1661 + struct stat st;
1662 + struct utimbuf updated_time;
1663 +
1664 + stat(ctx->commit_graph_filenames_before[i], &st);
1665 +
1666 + updated_time.actime = st.st_atime;
1667 + updated_time.modtime = now;
1668 + utime(ctx->commit_graph_filenames_before[i], &updated_time);
1669 + }
1670 +}
1671 +
1672 +static void expire_commit_graphs(struct write_commit_graph_context *ctx)
1673 +{
1674 + struct strbuf path = STRBUF_INIT;
1675 + DIR *dir;
1676 + struct dirent *de;
1677 + size_t dirnamelen;
1678 + time_t expire_time = time(NULL);
1679 +
1680 + strbuf_addstr(&path, ctx->obj_dir);
1681 + strbuf_addstr(&path, "/info/commit-graphs");
1682 + dir = opendir(path.buf);
1683 +
1684 + if (!dir) {
1685 + strbuf_release(&path);
1686 + return;
1687 + }
1688 +
1689 + strbuf_addch(&path, '/');
1690 + dirnamelen = path.len;
1691 + while ((de = readdir(dir)) != NULL) {
1692 + struct stat st;
1693 + uint32_t i, found = 0;
1694 +
1695 + strbuf_setlen(&path, dirnamelen);
1696 + strbuf_addstr(&path, de->d_name);
1697 +
1698 + stat(path.buf, &st);
1699 +
1700 + if (st.st_mtime > expire_time)
1701 + continue;
1702 + if (path.len < 6 || strcmp(path.buf + path.len - 6, ".graph"))
1703 + continue;
1704 +
1705 + for (i = 0; i < ctx->num_commit_graphs_after; i++) {
1706 + if (!strcmp(ctx->commit_graph_filenames_after[i],
1707 + path.buf)) {
1708 + found = 1;
1709 + break;
1710 + }
1711 + }
1712 +
1713 + if (!found)
1714 + unlink(path.buf);
1715 +
1716 + }
1717 +}
1718 +
1719 int write_commit_graph(const char *obj_dir,
1720 struct string_list *pack_indexes,
1721 struct string_list *commit_hex,
@@ -1764,6 +1828,11 @@ int write_commit_graph(const char *obj_dir,
1828
1829 res = write_commit_graph_file(ctx);
1830
1831 + if (ctx->split) {
1832 + mark_commit_graphs(ctx);
1833 + expire_commit_graphs(ctx);
1834 + }
1835 +
1836 cleanup:
1837 free(ctx->graph_name);
1838 free(ctx->commits.list);
t/t5324-split-commit-graph.sh
+1 -1
@@ -141,7 +141,7 @@ test_expect_success 'add one commit, write a merged graph' '
141 test_path_is_file $graphdir/commit-graph-chain &&
142 test_line_count = 2 $graphdir/commit-graph-chain &&
143 ls $graphdir/graph-*.graph >graph-files &&
144 - test_line_count = 4 graph-files &&
144 + test_line_count = 2 graph-files &&
145 verify_chain_files_exist $graphdir
146 '
147