summaryrefslogtreecommitdiff
path: root/examples/last-changed.c
diff options
context:
space:
mode:
authorRussell Belfer <rb@github.com>2013-11-15 14:02:54 -0800
committerRussell Belfer <rb@github.com>2014-04-21 10:34:33 -0700
commit044afa4172ee46acf55f943eb9ea1210017b76d3 (patch)
tree1d48802e25f5ea148640ba746a2e230ef551a1ea /examples/last-changed.c
parent28750a7d98ce5e23bac5c1d119109ded8e8aab73 (diff)
downloadlibgit2-rb/commit-modified-file.tar.gz
Add git_diff_commit and last-changed examplerb/commit-modified-file
This adds a new diff API `git_diff_commit` which makes it easy to generate a `git_diff` object that represents the changes in a given commit. It follows the core Git rules for considering changes in a merge commit - i.e. if a file has an exact match in any parent of the commit, then the file is considered unmodified by the merge. This also adds a new example program "last-changed" which takes a list of filenames and for each one displays the SHA of the last commit that changed that file.
Diffstat (limited to 'examples/last-changed.c')
-rw-r--r--examples/last-changed.c138
1 files changed, 138 insertions, 0 deletions
diff --git a/examples/last-changed.c b/examples/last-changed.c
new file mode 100644
index 000000000..fd9d278b5
--- /dev/null
+++ b/examples/last-changed.c
@@ -0,0 +1,138 @@
+/*
+ * libgit2 "last-changed" example - get last commit modifying a file
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+static void usage(void)
+{
+ fprintf(stderr, "usage: last-changed [--git-dir=DIR] pathname ...\n");
+ exit(1);
+}
+
+static int mark_pathspec_match(
+ const git_diff *, const git_diff_delta *, const char *, void *);
+
+typedef struct {
+ git_diff_options opts;
+ git_oid oid;
+ char str[GIT_OID_HEXSZ + 1];
+} change_info;
+
+int main(int argc, char *argv[])
+{
+ const char *repodir = ".";
+ change_info info = { GIT_DIFF_OPTIONS_INIT };
+ int start_pathspec = 1;
+ size_t i;
+ git_repository *repo;
+ git_revwalk *walker;
+
+ git_threads_init();
+
+ /* allow you to specific a git repo other than the current one */
+ if (argc > 1 && !strncmp(argv[1], "--git-dir=", strlen("--git-dir="))) {
+ repodir = argv[1] + strlen("--git-dir=");
+ start_pathspec++;
+ }
+
+ /* convert arguments to a "pathspec" of interesting files */
+ info.opts.pathspec.strings = &argv[start_pathspec];
+ info.opts.pathspec.count = argc - start_pathspec;
+ if (!info.opts.pathspec.count)
+ usage();
+ info.opts.ignore_submodules = GIT_SUBMODULE_IGNORE_DIRTY |
+ GIT_DIFF_DISABLE_PATHSPEC_MATCH;
+ info.opts.notify_cb = mark_pathspec_match;
+ info.opts.notify_payload = &info;
+
+ /* create repo and walker */
+ check_lg2(git_repository_open_ext(&repo, repodir, 0, NULL),
+ "Could not open repository", repodir);
+ check_lg2(git_revwalk_new(&walker, repo),
+ "Could not create revision walker", NULL);
+
+ /* start at HEAD and walk backwards through time */
+ git_revwalk_sorting(walker, GIT_SORT_TOPOLOGICAL | GIT_SORT_TIME);
+ check_lg2(git_revwalk_push_head(walker),
+ "Could not find repository HEAD", NULL);
+
+ while (info.opts.pathspec.count > 0 &&
+ !git_revwalk_next(&info.oid, walker))
+ {
+ git_commit *commit;
+ git_diff *diff;
+
+ git_oid_tostr(info.str, sizeof(info.str), &info.oid);
+
+ check_lg2(git_commit_lookup(&commit, repo, &info.oid),
+ "Failed to look up commit", NULL);
+ check_lg2(git_diff_commit(&diff, commit, &info.opts),
+ "Failed to get diff for commit", NULL);
+
+ /* notification callback will take care of reporting on
+ * items in the diff and reducing the pathspec count
+ */
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+ }
+
+ for (i = 0; i < info.opts.pathspec.count; ++i) {
+ const char *path = info.opts.pathspec.strings[i];
+ if (path)
+ printf("never found %s\n", path);
+ }
+
+ git_revwalk_free(walker);
+ git_repository_free(repo);
+ git_threads_shutdown();
+
+ return 0;
+}
+
+static int mark_pathspec_match(
+ const git_diff *diff_so_far,
+ const git_diff_delta *delta_to_add,
+ const char *matched_pathspec,
+ void *payload)
+{
+ change_info *info = payload;
+ git_strarray *paths = &info->opts.pathspec;
+ size_t i;
+ int found = 0;
+
+ (void)diff_so_far; (void)delta_to_add;
+
+ for (i = 0; i < paths->count; ++i) {
+ /* remove matched item from list */
+ if (found)
+ paths->strings[i - 1] = paths->strings[i];
+ else if (!strcmp(paths->strings[i], matched_pathspec))
+ found = 1;
+ }
+
+ if (found) {
+ const char *verb = "modified";
+ if (delta_to_add->status == GIT_DELTA_ADDED)
+ verb = "added";
+ else if (delta_to_add->status == GIT_DELTA_DELETED)
+ verb = "deleted";
+
+ printf("%s has %s %s\n", info->str, verb, matched_pathspec);
+
+ paths->count--;
+ }
+
+ return 0;
+}