1#include <err.h>2#include <libgen.h>3#include <libgen.h>4#include <limits.h>5#include <stdbool.h>6#include <stdint.h>7#include <stdio.h>8#include <stdlib.h>9#include <string.h>10#include <time.h>11#include <unistd.h>1213#include <git2.h>14#include <sys/types.h>1516static bool amend_single = false;17static bool verbose = false;18static git_repository *repo;1920static void21giterr(const char *fn)22{23 const git_error *err;2425 err = git_error_last();26 errx(EXIT_FAILURE, "%s failed: %s", fn, err->message);27}2829static void30usage(char *prog)31{32 const char *usage = "[-a] [-v] revspec";3334 fprintf(stderr, "USAGE: %s %s\n", basename(prog), usage);35 exit(EXIT_FAILURE);36}3738static void39randtime(git_time *dest, const git_time *orig)40{41 time_t t;42 struct tm *tm;43 uint8_t rbytes[3];4445 if (getentropy(&rbytes, sizeof(rbytes)) == -1)46 err(EXIT_FAILURE, "getentropy failed");4748 /* https://github.com/libgit2/libgit2/blob/79d0e0c10ffec81152b5b1eaeb47b59adf1d4bcc/examples/log.c#L321 */49 t = (time_t)orig->time + (orig->offset * 60);50 if (!(tm = gmtime(&t)))51 errx(EXIT_FAILURE, "gmtime failed");5253 /* randomize time but retain current date */54 tm->tm_hour = rbytes[0] % 24;55 tm->tm_min = rbytes[1] % 60;56 tm->tm_sec = rbytes[2] % 60;5758 /* convert broken-down time to UTC epoch and use the59 * offset given in orig to convert it to a local time */60 memcpy(dest, orig, sizeof(git_time));61 if ((dest->time = timegm(tm)) == (time_t)-1)62 err(EXIT_FAILURE, "timegm failed");63 dest->time -= orig->offset * 60; /* convert back to local time */64}6566static git_signature *67redate(git_commit *commit)68{69 char buf[GIT_OID_HEXSZ + 1];70 git_signature *newauth;71 const git_signature *origauth;7273 if (verbose) {74 git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));75 printf("Updating time of commit %s\n", buf);76 }7778 origauth = git_commit_author(commit);79 if (git_signature_dup(&newauth, origauth))80 err(EXIT_FAILURE, "git_signature_dup failed");81 randtime(&newauth->when, &origauth->when);8283 return newauth;84}8586static void87rebase(const char *revspec)88{89 git_oid oid;90 git_rebase *rebase;91 git_annotated_commit *upstream;92 git_rebase_operation *op;9394 if (git_annotated_commit_from_revspec(&upstream, repo, revspec))95 giterr("git_annotated_commit_from_revspec");96 if (git_rebase_init(&rebase, repo, NULL, upstream, NULL, NULL))97 giterr("git_rebase_init");9899 while (!git_rebase_next(&op, rebase)) {100 git_commit *commit;101 git_signature *author;102103 if (git_commit_lookup(&commit, repo, &op->id))104 goto err;105 author = redate(commit);106 if (git_rebase_commit(&oid, rebase, author, author, NULL, NULL))107 goto err;108109 git_commit_free(commit);110 git_signature_free(author);111 }112113 if (git_rebase_finish(rebase, NULL))114 giterr("git_rebase_finish");115 return;116117err:118 warnx("rebase failed: %s", git_error_last()->message);119 if (git_rebase_abort(rebase))120 giterr("git_rebase_abort");121}122123static void124amend(void)125{126 git_oid oidcpy;127 git_commit *commit;128 const git_oid *oid;129 git_reference *head;130 const char *refname;131 git_signature *author;132 git_annotated_commit *annotated;133134 if (git_repository_head(&head, repo))135 giterr("git_repository_head");136 if (git_annotated_commit_from_ref(&annotated, repo, head))137 giterr("git_annotated_commit_from_ref");138139 oid = git_annotated_commit_id(annotated);140 if (git_oid_cpy(&oidcpy, oid))141 giterr("git_oid_cpy");142143 if (git_commit_lookup(&commit, repo, oid))144 giterr("git_commit_lookup");145 refname = git_reference_name(head);146147 author = redate(commit);148 if (git_commit_amend(&oidcpy, commit, refname, author, author, NULL, NULL, NULL))149 giterr("git_commit_amend");150}151152int153main(int argc, char **argv)154{155 int opt;156 static char cwd[PATH_MAX + 1];157 static git_buf rfp;158159 if (!getcwd(cwd, sizeof(cwd)))160 err(EXIT_FAILURE, "getcwd failed");161162 while ((opt = getopt(argc, argv, "av")) != -1) {163 switch (opt) {164 case 'a':165 amend_single = true;166 break;167 case 'v':168 verbose = true;169 break;170 default:171 usage(argv[0]);172 break;173 }174 }175176 git_libgit2_init();177 if (git_repository_discover(&rfp, cwd, 0, NULL))178 giterr("git_repository_discover");179 if (git_repository_open(&repo, rfp.ptr))180 giterr("git_repository_open");181182 if (amend_single) {183 amend();184 } else {185 if (argc <= 1 || optind >= argc)186 usage(argv[0]);187 rebase(argv[optind]);188 }189}