commit b00d56cde51fc646b2b3055ff80e6f5b2b8cccb1 from: Stefan Sperling date: Sun Apr 01 17:30:42 2018 UTC add a rudimentary diff command commit - 3235492e3fcec3882427de904e5be159b6380edf commit + b00d56cde51fc646b2b3055ff80e6f5b2b8cccb1 blob - a55e7d9e6f0317755b1aa6df1c5f7673ad9a6e0c blob + 41ca7c9696379c1899dc721f39eb4c60a8f672df --- got/got.c +++ got/got.c @@ -47,9 +47,11 @@ struct cmd { __dead void usage(void); __dead void usage_checkout(void); __dead void usage_log(void); +__dead void usage_diff(void); const struct got_error* cmd_checkout(int, char *[]); const struct got_error* cmd_log(int, char *[]); +const struct got_error* cmd_diff(int, char *[]); const struct got_error* cmd_status(int, char *[]); struct cmd got_commands[] = { @@ -57,6 +59,8 @@ struct cmd got_commands[] = { "check out a new work tree from a repository" }, { "log", cmd_log, usage_log, "show repository history" }, + { "diff", cmd_diff, usage_diff, + "compare files and directories" }, #ifdef notyet { "status", cmd_status, usage_status, "show modification status of files" }, @@ -480,8 +484,189 @@ cmd_log(int argc, char *argv[]) free(id); got_repo_close(repo); return error; +} + +__dead void +usage_diff(void) +{ + fprintf(stderr, "usage: %s diff repository-path object1 object2\n", + getprogname()); + exit(1); +} + +static const struct got_error * +diff_blobs(struct got_object *obj1, struct got_object *obj2, + struct got_repository *repo) +{ + const struct got_error *err; + struct got_blob_object *blob1 = NULL, *blob2 = NULL; + + err = got_object_blob_open(&blob1, repo, obj1, 8192); + if (err) + goto done; + err = got_object_blob_open(&blob2, repo, obj2, 81992); + if (err) + goto done; + + err = got_diff_blob(blob1, blob2, NULL, NULL, stdout); +done: + if (blob1) + got_object_blob_close(blob1); + if (blob2) + got_object_blob_close(blob2); + return err; +} + +static const struct got_error * +diff_trees(struct got_object *obj1, struct got_object *obj2, + struct got_repository *repo) +{ + const struct got_error *err; + struct got_tree_object *tree1 = NULL, *tree2 = NULL; + + err = got_object_tree_open(&tree1, repo, obj1); + if (err) + goto done; + err = got_object_tree_open(&tree2, repo, obj2); + if (err) + goto done; + + err = got_diff_tree(tree1, tree2, repo, stdout); +done: + if (tree1) + got_object_tree_close(tree1); + if (tree2) + got_object_tree_close(tree2); + return err; } +static const struct got_error * +diff_commits(struct got_object *obj1, struct got_object *obj2, + struct got_repository *repo) +{ + const struct got_error *err; + struct got_commit_object *commit1 = NULL, *commit2 = NULL; + struct got_object *tree_obj1 = NULL, *tree_obj2 = NULL; + + err = got_object_commit_open(&commit1, repo, obj1); + if (err) + goto done; + err = got_object_commit_open(&commit2, repo, obj2); + if (err) + goto done; + + err = got_object_open(&tree_obj1, repo, commit1->tree_id); + if (err) + goto done; + err = got_object_open(&tree_obj2, repo, commit2->tree_id); + if (err) + goto done; + + err = diff_trees(tree_obj1, tree_obj2, repo); +done: + if (tree_obj1) + got_object_close(tree_obj1); + if (tree_obj2) + got_object_close(tree_obj2); + if (commit1) + got_object_commit_close(commit1); + if (commit2) + got_object_commit_close(commit2); + return err; + +} + +const struct got_error * +cmd_diff(int argc, char *argv[]) +{ + const struct got_error *error; + struct got_repository *repo = NULL; + struct got_object_id *id1 = NULL, *id2 = NULL; + struct got_object *obj1 = NULL, *obj2 = NULL; + char *repo_path = NULL; + char *obj_id_str1 = NULL, *obj_id_str2 = NULL; + int ch; + +#ifndef PROFILE + if (pledge("stdio rpath wpath cpath", NULL) == -1) + err(1, "pledge"); +#endif + + while ((ch = getopt(argc, argv, "")) != -1) { + switch (ch) { + default: + usage(); + /* NOTREACHED */ + } + } + + argc -= optind; + argv += optind; + + if (argc == 0) { + usage_diff(); /* TODO show local worktree changes */ + } else if (argc == 3) { + repo_path = argv[0]; + obj_id_str1 = argv[1]; + obj_id_str2 = argv[2]; + } else + usage_diff(); + + error = got_repo_open(&repo, repo_path); + if (error != NULL) + goto done; + + error = got_object_open_by_id_str(&obj1, repo, obj_id_str1); + if (error == NULL) { + id1 = got_object_get_id(obj1); + if (id1 == NULL) + error = got_error_from_errno(); + } + if (error != NULL) + goto done; + + error = got_object_open_by_id_str(&obj2, repo, obj_id_str2); + if (error == NULL) { + id2 = got_object_get_id(obj2); + if (id2 == NULL) + error = got_error_from_errno(); + } + if (error != NULL) + goto done; + + if (got_object_get_type(obj1) != got_object_get_type(obj2)) { + error = got_error(GOT_ERR_OBJ_TYPE); + goto done; + } + + switch (got_object_get_type(obj1)) { + case GOT_OBJ_TYPE_BLOB: + error = diff_blobs(obj1, obj2, repo); + break; + case GOT_OBJ_TYPE_TREE: + error = diff_trees(obj1, obj2, repo); + break; + case GOT_OBJ_TYPE_COMMIT: + error = diff_commits(obj1, obj2, repo); + break; + default: + error = got_error(GOT_ERR_OBJ_TYPE); + } + +done: + if (obj1) + got_object_close(obj1); + if (obj2) + got_object_close(obj2); + if (id1) + free(id1); + if (id2) + free(id2); + if (repo) + got_repo_close(repo); + return error; +} + #ifdef notyet const struct got_error * cmd_status(int argc __unused, char *argv[] __unused)