From: Mark Jamsek Subject: got: add diffstat to got diff To: Game of Trees Date: Mon, 9 Jan 2023 03:11:01 +1100 Like the recent 'got log -d' addition, display a diffstat if the -d switch is passed to 'got diff'. Output is slightly different, we don't add a blank line between the last file line and the summary line like we do with got log and the tog diff view; I think it looks a bit disconnected when we do it in this case. And for the `got diff blob-hash blob-hash` case, like `git diff --stat`, we show blob hashes rather than a file path; for example: ----8<--------------------------------------------------------------- ~/src/got » g diff -d 5e9010e76c3a c9a5f834b0a M 5e9010e76c -> c9a5f834b0 | 193+ 81- 1 file changed, 193 insertions(+), 81 deletions(-) --- ~/src/got » g diff -d 6b5e6124584 333a3ff1f9e < diff 6b5e61245844733fe2df78892399d5956f422311 333a3ff1f9e9a32f813e293d900492b4ec7c9ca6 commit - 6b5e61245844733fe2df78892399d5956f422311 commit + 333a3ff1f9e9a32f813e293d900492b4ec7c9ca6 M got/got.1 | 3+ 1- M got/got.c | 70+ 31- M include/got_diff.h | 12+ 7- M lib/diff.c | 193+ 81- M lib/worktree.c | 3+ 3- M tog/tog.c | 4+ 3- 6 files changed, 285 insertions(+), 126 deletions(-) --- ~/src/got » g diff -d -c 6b5e6124584 -c 333a3ff1f9e got diff 6b5e61245844733fe2df78892399d5956f422311 333a3ff1f9e9a32f813e293d900492b4ec7c9ca6 commit - 6b5e61245844733fe2df78892399d5956f422311 commit + 333a3ff1f9e9a32f813e293d900492b4ec7c9ca6 M got/got.1 | 3+ 1- M got/got.c | 70+ 31- 2 files changed, 73 insertions(+), 32 deletions(-) --------------------------------------------------------------->8---- ----------------------------------------------- commit 333a3ff1f9e9a32f813e293d900492b4ec7c9ca6 from: Mark Jamsek date: Sun Jan 8 16:03:25 2023 UTC got: expand diffstat -d option to 'got diff' Like got log -d, add the switch to got diff to display a diffstat of the specified diff. M got/got.1 | 3+ 1- M got/got.c | 70+ 31- M include/got_diff.h | 12+ 7- M lib/diff.c | 193+ 81- M lib/worktree.c | 3+ 3- M tog/tog.c | 4+ 3- 6 files changed, 285 insertions(+), 126 deletions(-) diff 6b5e61245844733fe2df78892399d5956f422311 333a3ff1f9e9a32f813e293d900492b4ec7c9ca6 commit - 6b5e61245844733fe2df78892399d5956f422311 commit + 333a3ff1f9e9a32f813e293d900492b4ec7c9ca6 blob - c86779cca9d917f301a8ac66e723282c6c0bcb57 blob + 531645e51924cf65f5a19918c0bb0c56bf4ac847 --- got/got.1 +++ got/got.1 @@ -904,7 +904,7 @@ is never traversed. .Tg di .It Xo .Cm diff -.Op Fl aPsw +.Op Fl adPsw .Op Fl C Ar number .Op Fl c Ar commit .Op Fl r Ar repository-path @@ -962,6 +962,8 @@ option. Cannot be used together with the .Fl P option. +.It Fl d +Display diffstat of changes. .It Fl P Interpret all arguments as paths only. This option can be used to resolve ambiguity in cases where paths blob - 9a89d5b62f1635fb4b385534abdd217ee4a06611 blob + bbac2d276a4495a9976cd76f1ce352bbd4797f1c --- got/got.c +++ got/got.c @@ -3624,7 +3624,8 @@ diff_blobs(struct got_object_id *blob_id1, struct got_ static const struct got_error * diff_blobs(struct got_object_id *blob_id1, struct got_object_id *blob_id2, const char *path, int diff_context, int ignore_whitespace, - int force_text_diff, struct got_repository *repo, FILE *outfile) + int force_text_diff, int show_diffstat, struct got_repository *repo, + FILE *outfile) { const struct got_error *err = NULL; struct got_blob_object *blob1 = NULL, *blob2 = NULL; @@ -3666,7 +3667,7 @@ diff_blobs(struct got_object_id *blob_id1, struct got_ path++; err = got_diff_blob(NULL, NULL, blob1, blob2, f1, f2, path, path, GOT_DIFF_ALGORITHM_PATIENCE, diff_context, ignore_whitespace, - force_text_diff, outfile); + force_text_diff, show_diffstat, NULL, outfile); done: if (fd1 != -1 && close(fd1) == -1 && err == NULL) err = got_error_from_errno("close"); @@ -3727,6 +3728,7 @@ diff_trees(struct got_object_id *tree_id1, struct got_ arg.diff_context = diff_context; arg.ignore_whitespace = ignore_whitespace; arg.force_text_diff = force_text_diff; + arg.show_diffstat = 0; arg.diff_algo = GOT_DIFF_ALGORITHM_PATIENCE; arg.outfile = outfile; arg.lines = NULL; @@ -3891,7 +3893,7 @@ print_patch(struct got_commit_object *commit, struct g switch (obj_type) { case GOT_OBJ_TYPE_BLOB: err = diff_blobs(obj_id1, obj_id2, path, diff_context, - 0, 0, repo, outfile); + 0, 0, 0, repo, outfile); break; case GOT_OBJ_TYPE_TREE: err = diff_trees(obj_id1, obj_id2, path, diff_context, @@ -4163,6 +4165,32 @@ static const struct got_error * return err; } +static void +print_diffstat(struct got_diffstat_cb_arg *dsa, struct got_pathlist_head *paths, + int newline) +{ + struct got_pathlist_entry *pe; + + TAILQ_FOREACH(pe, paths, entry) { + struct got_diff_changed_path *cp = pe->data; + int pad = dsa->max_path_len - pe->path_len + 1; + + printf(" %c %s%*c | %*d+ %*d-\n", cp->status, pe->path, pad, + ' ', dsa->add_cols + 1, cp->add, dsa->rm_cols + 1, cp->rm); + + free(pe->data); + free((char *)pe->path); + } + if (newline) + printf("\n"); + printf("%d file%s changed, %d insertions(+), %d deletions(-)\n", + dsa->nfiles, dsa->nfiles > 1 ? "s" : "", dsa->ins, dsa->del); + if (newline) + printf("\n"); + + got_pathlist_free(paths); +} + static const struct got_error * print_commit(struct got_commit_object *commit, struct got_object_id *id, struct got_repository *repo, const char *path, @@ -4237,31 +4265,16 @@ print_commit(struct got_commit_object *commit, struct } while (line); free(logmsg0); - if (changed_paths) { + if (dsa && changed_paths) + print_diffstat(dsa, changed_paths, 1); + else if (changed_paths) { struct got_pathlist_entry *pe; TAILQ_FOREACH(pe, changed_paths, entry) { struct got_diff_changed_path *cp = pe->data; - char *stat = NULL; - if (dsa) { - int pad = dsa->max_path_len - pe->path_len + 1; - - if (asprintf(&stat, "%*c | %*d+ %*d-", - pad, ' ', dsa->add_cols + 1, cp->add, - dsa->rm_cols + 1, cp->rm) == -1) { - err = got_error_from_errno("asprintf"); - goto done; - } - } - printf(" %c %s%s\n", cp->status, pe->path, - stat ? stat : ""); - free(stat); + printf(" %c %s\n", cp->status, pe->path); } - if (dsa) - printf("\n%d file%s changed, %d insertions(+), " - "%d deletions(-)\n", dsa->nfiles, - dsa->nfiles > 1 ? "s" : "", dsa->ins, dsa->del); printf("\n"); } if (show_patch) { @@ -4727,7 +4740,7 @@ usage_diff(void) __dead static void usage_diff(void) { - fprintf(stderr, "usage: %s diff [-aPsw] [-C number] [-c commit] " + fprintf(stderr, "usage: %s diff [-adPsw] [-C number] [-c commit] " "[-r repository-path] [object1 object2 | path ...]\n", getprogname()); exit(1); @@ -4736,6 +4749,7 @@ struct print_diff_arg { struct print_diff_arg { struct got_repository *repo; struct got_worktree *worktree; + struct got_diffstat_cb_arg *diffstat; int diff_context; const char *id_str; int header_shown; @@ -4743,6 +4757,7 @@ struct print_diff_arg { enum got_diff_algorithm diff_algo; int ignore_whitespace; int force_text_diff; + int show_diffstat; FILE *f1; FILE *f2; }; @@ -4874,7 +4889,8 @@ print_diff(void *arg, unsigned char status, unsigned c err = got_diff_objects_as_blobs(NULL, NULL, a->f1, a->f2, fd1, fd2, blob_id, staged_blob_id, label1, label2, a->diff_algo, a->diff_context, a->ignore_whitespace, - a->force_text_diff, a->repo, stdout); + a->force_text_diff, a->show_diffstat, a->diffstat, a->repo, + stdout); goto done; } @@ -4964,7 +4980,8 @@ print_diff(void *arg, unsigned char status, unsigned c err = got_diff_blob_file(blob1, a->f1, size1, label1, f2 ? f2 : a->f2, f2_exists, &sb, path, GOT_DIFF_ALGORITHM_PATIENCE, a->diff_context, - a->ignore_whitespace, a->force_text_diff, stdout); + a->ignore_whitespace, a->force_text_diff, a->show_diffstat, + a->diffstat, stdout); done: if (fd1 != -1 && close(fd1) == -1 && err == NULL) err = got_error_from_errno("close"); @@ -4993,17 +5010,19 @@ cmd_diff(int argc, char *argv[]) char *labels[2] = { NULL, NULL }; int type1 = GOT_OBJ_TYPE_ANY, type2 = GOT_OBJ_TYPE_ANY; int diff_context = 3, diff_staged = 0, ignore_whitespace = 0, ch, i; - int force_text_diff = 0, force_path = 0, rflag = 0; + int force_text_diff = 0, force_path = 0, rflag = 0, show_diffstat = 0; const char *errstr; struct got_reflist_head refs; - struct got_pathlist_head paths; + struct got_pathlist_head diffstat_paths, paths; struct got_pathlist_entry *pe; FILE *f1 = NULL, *f2 = NULL; int fd1 = -1, fd2 = -1; int *pack_fds = NULL; + struct got_diffstat_cb_arg dsa; TAILQ_INIT(&refs); TAILQ_INIT(&paths); + TAILQ_INIT(&diffstat_paths); #ifndef PROFILE if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil", @@ -5011,7 +5030,7 @@ cmd_diff(int argc, char *argv[]) err(1, "pledge"); #endif - while ((ch = getopt(argc, argv, "aC:c:Pr:sw")) != -1) { + while ((ch = getopt(argc, argv, "aC:c:dPr:sw")) != -1) { switch (ch) { case 'a': force_text_diff = 1; @@ -5028,6 +5047,9 @@ cmd_diff(int argc, char *argv[]) errx(1, "too many -c options used"); commit_args[ncommit_args++] = optarg; break; + case 'd': + show_diffstat = 1; + break; case 'P': force_path = 1; break; @@ -5091,6 +5113,14 @@ cmd_diff(int argc, char *argv[]) if (error != NULL) goto done; + if (show_diffstat) { + memset(&dsa, 0, sizeof(dsa)); + dsa.paths = &diffstat_paths; + dsa.force_text = force_text_diff; + dsa.ignore_ws = ignore_whitespace; + dsa.diff_algo = GOT_DIFF_ALGORITHM_PATIENCE; + } + if (rflag || worktree == NULL || ncommit_args > 0) { if (force_path) { error = got_error_msg(GOT_ERR_NOT_IMPL, @@ -5189,6 +5219,8 @@ cmd_diff(int argc, char *argv[]) arg.diff_staged = diff_staged; arg.ignore_whitespace = ignore_whitespace; arg.force_text_diff = force_text_diff; + arg.show_diffstat = show_diffstat; + arg.diffstat = &dsa; arg.f1 = f1; arg.f2 = f2; @@ -5329,25 +5361,31 @@ cmd_diff(int argc, char *argv[]) error = got_diff_objects_as_blobs(NULL, NULL, f1, f2, fd1, fd2, ids[0], ids[1], NULL, NULL, GOT_DIFF_ALGORITHM_PATIENCE, diff_context, - ignore_whitespace, force_text_diff, repo, stdout); + ignore_whitespace, force_text_diff, show_diffstat, + show_diffstat ? &dsa : NULL, repo, stdout); break; case GOT_OBJ_TYPE_TREE: error = got_diff_objects_as_trees(NULL, NULL, f1, f2, fd1, fd2, ids[0], ids[1], &paths, "", "", GOT_DIFF_ALGORITHM_PATIENCE, diff_context, - ignore_whitespace, force_text_diff, repo, stdout); + ignore_whitespace, force_text_diff, show_diffstat, + show_diffstat ? &dsa : NULL, repo, stdout); break; case GOT_OBJ_TYPE_COMMIT: printf("diff %s %s\n", labels[0], labels[1]); error = got_diff_objects_as_commits(NULL, NULL, f1, f2, fd1, fd2, ids[0], ids[1], &paths, GOT_DIFF_ALGORITHM_PATIENCE, diff_context, - ignore_whitespace, force_text_diff, repo, stdout); + ignore_whitespace, force_text_diff, show_diffstat, + show_diffstat ? &dsa : NULL, repo, stdout); break; default: error = got_error(GOT_ERR_OBJ_TYPE); } + done: + if (show_diffstat && dsa.nfiles > 0 && error == NULL) + print_diffstat(&dsa, &diffstat_paths, 0); free(labels[0]); free(labels[1]); free(ids[0]); @@ -5368,6 +5406,7 @@ done: TAILQ_FOREACH(pe, &paths, entry) free((char *)pe->path); got_pathlist_free(&paths); + got_pathlist_free(&diffstat_paths); got_ref_list_free(&refs); if (f1 && fclose(f1) == EOF && error == NULL) error = got_error_from_errno("fclose"); blob - 18fe6a19ccc9b5b2450e313f81d9246ce51b630d blob + 8f6632dd374680ed883152c72ac77c5ace39531e --- include/got_diff.h +++ include/got_diff.h @@ -44,6 +44,7 @@ struct got_diff_line { uint8_t type; }; +struct got_diffstat_cb_arg; /* * Compute the differences between two blobs and write unified diff text * to the provided output file. Two open temporary files must be provided @@ -61,8 +62,8 @@ const struct got_error *got_diff_blob(struct got_diff_ */ const struct got_error *got_diff_blob(struct got_diff_line **, size_t *, struct got_blob_object *, struct got_blob_object *, FILE *, FILE *, - const char *, const char *, enum got_diff_algorithm, int, int, int, - FILE *); + const char *, const char *, enum got_diff_algorithm, int, int, int, int, + struct got_diffstat_cb_arg *, FILE *); /* * Compute the differences between a blob and a file and write unified diff @@ -75,7 +76,8 @@ const struct got_error *got_diff_blob_file(struct got_ */ const struct got_error *got_diff_blob_file(struct got_blob_object *, FILE *, off_t, const char *, FILE *, int, struct stat *, const char *, - enum got_diff_algorithm, int, int, int, FILE *); + enum got_diff_algorithm, int, int, int, int, struct got_diffstat_cb_arg *, + FILE *); /* * A callback function invoked to handle the differences between two blobs @@ -105,6 +107,7 @@ struct got_diff_blob_output_unidiff_arg { int diff_context; /* Sets the number of context lines. */ int ignore_whitespace; /* Ignore whitespace differences. */ int force_text_diff; /* Assume text even if binary data detected. */ + int show_diffstat; enum got_diff_algorithm diff_algo; /* Diffing algorithm to use. */ /* @@ -204,7 +207,8 @@ const struct got_error *got_diff_objects_as_blobs(stru const struct got_error *got_diff_objects_as_blobs(struct got_diff_line **, size_t *, FILE *, FILE *, int, int, struct got_object_id *, struct got_object_id *, const char *, const char *, enum got_diff_algorithm, - int, int, int, struct got_repository *, FILE *); + int, int, int, int, struct got_diffstat_cb_arg *, struct got_repository *, + FILE *); struct got_pathlist_head; @@ -226,8 +230,8 @@ const struct got_error *got_diff_objects_as_trees(stru const struct got_error *got_diff_objects_as_trees(struct got_diff_line **, size_t *, FILE *, FILE *, int, int, struct got_object_id *, struct got_object_id *, struct got_pathlist_head *, const char *, - const char *, enum got_diff_algorithm, int, int, int, - struct got_repository *, FILE *); + const char *, enum got_diff_algorithm, int, int, int, int, + struct got_diffstat_cb_arg *, struct got_repository *, FILE *); /* * Diff two objects, assuming both objects are commits. @@ -244,6 +248,7 @@ const struct got_error *got_diff_objects_as_commits(st const struct got_error *got_diff_objects_as_commits(struct got_diff_line **, size_t *, FILE *, FILE *, int, int, struct got_object_id *, struct got_object_id *, struct got_pathlist_head *, enum got_diff_algorithm, - int, int, int, struct got_repository *, FILE *); + int, int, int, int, struct got_diffstat_cb_arg *, struct got_repository *, + FILE *); #define GOT_DIFF_MAX_CONTEXT 64 blob - 5e9010e76c3aa5de6de83e6b5cd54a4749c4f51a blob + c9a5f834b0a8edd0dc02cf506d56dbc4d77e85b8 --- lib/diff.c +++ lib/diff.c @@ -59,12 +59,75 @@ static const struct got_error * return NULL; } +static void +diffstat_field_width(size_t *maxlen, int *add_cols, int *rm_cols, size_t len, + uint32_t add, uint32_t rm) +{ + int d1 = 1, d2 = 1; + + if (maxlen) + *maxlen = MAX(*maxlen, len); + + while (add /= 10) + ++d1; + *add_cols = MAX(*add_cols, d1); + + while (rm /= 10) + ++d2; + *rm_cols = MAX(*rm_cols, d2); +} + static const struct got_error * +get_diffstat(struct got_diffstat_cb_arg *ds, const char *path, + struct got_diff_changed_path *change, struct diff_result *r, int force_text) +{ + const struct got_error *err; + struct got_pathlist_entry *pe; + int flags = (r->left->atomizer_flags | r->right->atomizer_flags); + int isbin = (flags & DIFF_ATOMIZER_FOUND_BINARY_DATA); + int i; + + change->add = 0; + change->rm = 0; + + if (!isbin || force_text) { + for (i = 0; i < r->chunks.len; ++i) { + struct diff_chunk *c; + int clc, crc; + + c = diff_chunk_get(r, i); + clc = diff_chunk_get_left_count(c); + crc = diff_chunk_get_right_count(c); + + if (crc && !clc) + change->add += crc; + if (clc && !crc) + change->rm += clc; + } + } + + ds->ins += change->add; + ds->del += change->rm; + ++ds->nfiles; + + err = got_pathlist_append(ds->paths, path, change); + if (err) + return err; + + pe = TAILQ_LAST(ds->paths, got_pathlist_head); + diffstat_field_width(&ds->max_path_len, &ds->add_cols, &ds->rm_cols, + pe->path_len, change->add, change->rm); + + return NULL; +} + +static const struct got_error * diff_blobs(struct got_diff_line **lines, size_t *nlines, struct got_diffreg_result **resultp, struct got_blob_object *blob1, struct got_blob_object *blob2, FILE *f1, FILE *f2, const char *label1, const char *label2, mode_t mode1, mode_t mode2, - int diff_context, int ignore_whitespace, int force_text_diff, FILE *outfile, + int diff_context, int ignore_whitespace, int force_text_diff, + int show_diffstat, struct got_diffstat_cb_arg *ds, FILE *outfile, enum got_diff_algorithm diff_algo) { const struct got_error *err = NULL, *free_err; @@ -118,7 +181,7 @@ diff_blobs(struct got_diff_line **lines, size_t *nline } else idstr2 = "/dev/null"; - if (outfile) { + if (outfile && !show_diffstat) { char *modestr1 = NULL, *modestr2 = NULL; int modebits; if (mode1 && mode1 != mode2) { @@ -170,12 +233,59 @@ diff_blobs(struct got_diff_line **lines, size_t *nline free(modestr1); free(modestr2); } + err = got_diffreg(&result, f1, f2, diff_algo, ignore_whitespace, force_text_diff); if (err) goto done; - if (outfile) { + if (show_diffstat) { + struct got_diff_changed_path *change = NULL; + char *path = NULL; + + if (label1 == NULL && label2 == NULL) { + /* diffstat of blobs, show hash instead of path */ + if (asprintf(&path, "%.10s -> %.10s", + idstr1, idstr2) == -1) { + err = got_error_from_errno("asprintf"); + goto done; + } + } else { + path = strdup(label2 ? label2 : label1); + if (path == NULL) { + err = got_error_from_errno("malloc"); + goto done; + } + } + + change = malloc(sizeof(*change)); + if (change == NULL) { + free(path); + err = got_error_from_errno("malloc"); + goto done; + } + + /* + * Ignore 'm'ode status change: if there's no accompanying + * content change, there'll be no diffstat, and if there + * are actual changes, 'M'odified takes precedence. + */ + change->status = GOT_STATUS_NO_CHANGE; + if (blob1 == NULL) + change->status = GOT_STATUS_ADD; + else if (blob2 == NULL) + change->status = GOT_STATUS_DELETE; + else + change->status = GOT_STATUS_MODIFY; + + err = get_diffstat(ds, path, change, result->result, + force_text_diff); + if (err) { + free(change); + free(path); + goto done; + } + } else if (outfile) { err = got_diffreg_output(lines, nlines, result, blob1 != NULL, blob2 != NULL, label1 ? label1 : idstr1, @@ -208,7 +318,8 @@ got_diff_blob_output_unidiff(void *arg, struct got_blo return diff_blobs(&a->lines, &a->nlines, NULL, blob1, blob2, f1, f2, label1, label2, mode1, mode2, a->diff_context, - a->ignore_whitespace, a->force_text_diff, a->outfile, a->diff_algo); + a->ignore_whitespace, a->force_text_diff, a->show_diffstat, NULL, + a->outfile, a->diff_algo); } const struct got_error * @@ -216,11 +327,12 @@ got_diff_blob(struct got_diff_line **lines, size_t*nli struct got_blob_object *blob1, struct got_blob_object *blob2, FILE *f1, FILE *f2, const char *label1, const char *label2, enum got_diff_algorithm diff_algo, int diff_context, - int ignore_whitespace, int force_text_diff, FILE *outfile) + int ignore_whitespace, int force_text_diff, int show_diffstat, + struct got_diffstat_cb_arg *ds, FILE *outfile) { return diff_blobs(lines, nlines, NULL, blob1, blob2, f1, f2, label1, label2, 0, 0, diff_context, ignore_whitespace, - force_text_diff, outfile, diff_algo); + force_text_diff, show_diffstat, ds, outfile, diff_algo); } static const struct got_error * @@ -228,7 +340,8 @@ diff_blob_file(struct got_diffreg_result **resultp, struct got_blob_object *blob1, FILE *f1, off_t size1, const char *label1, FILE *f2, int f2_exists, struct stat *sb2, const char *label2, enum got_diff_algorithm diff_algo, int diff_context, int ignore_whitespace, - int force_text_diff, FILE *outfile) + int force_text_diff, int show_diffstat, struct got_diffstat_cb_arg *ds, + FILE *outfile) { const struct got_error *err = NULL, *free_err; char hex1[SHA1_DIGEST_STRING_LENGTH]; @@ -243,7 +356,7 @@ diff_blob_file(struct got_diffreg_result **resultp, else idstr1 = "/dev/null"; - if (outfile) { + if (outfile && !show_diffstat) { char *mode = NULL; /* display file mode for new added files only */ @@ -267,7 +380,7 @@ diff_blob_file(struct got_diffreg_result **resultp, if (err) goto done; - if (outfile) { + if (outfile && !show_diffstat) { err = got_diffreg_output(NULL, NULL, result, blob1 != NULL, f2_exists, label2, /* show local file's path, not a blob ID */ @@ -275,6 +388,43 @@ diff_blob_file(struct got_diffreg_result **resultp, diff_context, outfile); if (err) goto done; + } else if (show_diffstat) { + struct got_diff_changed_path *change = NULL; + char *path = NULL; + + path = strdup(label2 ? label2 : label1); + if (path == NULL) { + err = got_error_from_errno("malloc"); + goto done; + } + + change = malloc(sizeof(*change)); + if (change == NULL) { + free(path); + err = got_error_from_errno("malloc"); + goto done; + } + + /* + * Ignore 'm'ode status change: if there's no accompanying + * content change, there'll be no diffstat, and if there + * are actual changes, 'M'odified takes precedence. + */ + change->status = GOT_STATUS_NO_CHANGE; + if (blob1 == NULL) + change->status = GOT_STATUS_ADD; + else if (!f2_exists) + change->status = GOT_STATUS_DELETE; + else + change->status = GOT_STATUS_MODIFY; + + err = get_diffstat(ds, path, change, result->result, + force_text_diff); + if (err) { + free(change); + free(path); + goto done; + } } done: @@ -292,11 +442,12 @@ got_diff_blob_file(struct got_blob_object *blob1, FILE got_diff_blob_file(struct got_blob_object *blob1, FILE *f1, off_t size1, const char *label1, FILE *f2, int f2_exists, struct stat *sb2, const char *label2, enum got_diff_algorithm diff_algo, int diff_context, - int ignore_whitespace, int force_text_diff, FILE *outfile) + int ignore_whitespace, int force_text_diff, int show_diffstat, + struct got_diffstat_cb_arg *ds, FILE *outfile) { return diff_blob_file(NULL, blob1, f1, size1, label1, f2, f2_exists, sb2, label2, diff_algo, diff_context, ignore_whitespace, - force_text_diff, outfile); + force_text_diff, show_diffstat, ds, outfile); } static const struct got_error * @@ -611,23 +762,6 @@ static void NULL, label2, 0, te2->mode, repo); } -static void -diffstat_field_width(size_t *maxlen, int *add_cols, int *rm_cols, size_t len, - uint32_t add, uint32_t rm) -{ - int d1 = 1, d2 = 1; - - *maxlen = MAX(*maxlen, len); - - while (add /= 10) - ++d1; - *add_cols = MAX(*add_cols, d1); - - while (rm /= 10) - ++d2; - *rm_cols = MAX(*rm_cols, d2); -} - const struct got_error * got_diff_tree_compute_diffstat(void *arg, struct got_blob_object *blob1, struct got_blob_object *blob2, FILE *f1, FILE *f2, @@ -637,12 +771,9 @@ got_diff_tree_compute_diffstat(void *arg, struct got_b { const struct got_error *err = NULL; struct got_diffreg_result *result = NULL; - struct diff_result *r; struct got_diff_changed_path *change = NULL; struct got_diffstat_cb_arg *a = arg; - struct got_pathlist_entry *pe; char *path = NULL; - int i; path = strdup(label2 ? label2 : label1); if (path == NULL) @@ -697,36 +828,8 @@ got_diff_tree_compute_diffstat(void *arg, struct got_b if (err) goto done; - for (i = 0, r = result->result; i < r->chunks.len; ++i) { - int flags = (r->left->atomizer_flags | r->right->atomizer_flags); - int isbin = (flags & DIFF_ATOMIZER_FOUND_BINARY_DATA); + err = get_diffstat(a, path, change, result->result, a->force_text); - if (!isbin || a->force_text) { - struct diff_chunk *c; - int clc, crc; - - c = diff_chunk_get(r, i); - clc = diff_chunk_get_left_count(c); - crc = diff_chunk_get_right_count(c); - - if (clc && !crc) - change->rm += clc; - else if (crc && !clc) - change->add += crc; - } - } - - err = got_pathlist_append(a->paths, path, change); - if (err) - goto done; - - pe = TAILQ_LAST(a->paths, got_pathlist_head); - diffstat_field_width(&a->max_path_len, &a->add_cols, &a->rm_cols, - pe->path_len, change->add, change->rm); - a->ins += change->add; - a->del += change->rm; - ++a->nfiles; - done: if (result) { const struct got_error *free_err; @@ -885,8 +988,8 @@ got_diff_objects_as_blobs(struct got_diff_line **lines struct got_object_id *id1, struct got_object_id *id2, const char *label1, const char *label2, enum got_diff_algorithm diff_algo, int diff_context, - int ignore_whitespace, int force_text_diff, - struct got_repository *repo, FILE *outfile) + int ignore_whitespace, int force_text_diff, int show_diffstat, + struct got_diffstat_cb_arg *ds, struct got_repository *repo, FILE *outfile) { const struct got_error *err; struct got_blob_object *blob1 = NULL, *blob2 = NULL; @@ -906,7 +1009,7 @@ got_diff_objects_as_blobs(struct got_diff_line **lines } err = got_diff_blob(lines, nlines, blob1, blob2, f1, f2, label1, label2, diff_algo, diff_context, ignore_whitespace, force_text_diff, - outfile); + show_diffstat, ds, outfile); done: if (blob1) got_object_blob_close(blob1); @@ -1080,12 +1183,14 @@ diff_objects_as_trees(struct got_diff_line **lines, si struct got_object_id *id1, struct got_object_id *id2, struct got_pathlist_head *paths, const char *label1, const char *label2, int diff_context, int ignore_whitespace, int force_text_diff, + int show_diffstat, struct got_diffstat_cb_arg *dsa, struct got_repository *repo, FILE *outfile, enum got_diff_algorithm diff_algo) { const struct got_error *err; struct got_tree_object *tree1 = NULL, *tree2 = NULL; struct got_diff_blob_output_unidiff_arg arg; + got_diff_blob_cb cb = got_diff_blob_output_unidiff; int want_linemeta = (lines != NULL && *lines != NULL); if (id1 == NULL && id2 == NULL) @@ -1102,25 +1207,30 @@ diff_objects_as_trees(struct got_diff_line **lines, si goto done; } - arg.diff_algo = diff_algo; - arg.diff_context = diff_context; - arg.ignore_whitespace = ignore_whitespace; - arg.force_text_diff = force_text_diff; - arg.outfile = outfile; - if (want_linemeta) { - arg.lines = *lines; - arg.nlines = *nlines; - } else { - arg.lines = NULL; - arg.nlines = 0; + if (show_diffstat) + cb = got_diff_tree_compute_diffstat; + else { + arg.diff_algo = diff_algo; + arg.diff_context = diff_context; + arg.ignore_whitespace = ignore_whitespace; + arg.force_text_diff = force_text_diff; + arg.show_diffstat = show_diffstat; + arg.outfile = outfile; + if (want_linemeta) { + arg.lines = *lines; + arg.nlines = *nlines; + } else { + arg.lines = NULL; + arg.nlines = 0; + } } if (paths == NULL || TAILQ_EMPTY(paths)) { err = got_diff_tree(tree1, tree2, f1, f2, fd1, fd2, - label1, label2, repo, - got_diff_blob_output_unidiff, &arg, 1); + label1, label2, repo, cb, + show_diffstat ? (void *)dsa : &arg, 1); } else { err = diff_paths(tree1, tree2, f1, f2, fd1, fd2, paths, repo, - got_diff_blob_output_unidiff, &arg); + cb, show_diffstat ? (void *)dsa : &arg); } if (want_linemeta) { *lines = arg.lines; /* was likely re-allocated */ @@ -1140,7 +1250,8 @@ got_diff_objects_as_trees(struct got_diff_line **lines struct got_object_id *id1, struct got_object_id *id2, struct got_pathlist_head *paths, const char *label1, const char *label2, enum got_diff_algorithm diff_algo, int diff_context, int ignore_whitespace, - int force_text_diff, struct got_repository *repo, FILE *outfile) + int force_text_diff, int show_diffstat, struct got_diffstat_cb_arg *dsa, + struct got_repository *repo, FILE *outfile) { const struct got_error *err; char *idstr = NULL; @@ -1182,7 +1293,7 @@ got_diff_objects_as_trees(struct got_diff_line **lines err = diff_objects_as_trees(lines, nlines, f1, f2, fd1, fd2, id1, id2, paths, label1, label2, diff_context, ignore_whitespace, - force_text_diff, repo, outfile, diff_algo); + force_text_diff, show_diffstat, dsa, repo, outfile, diff_algo); done: free(idstr); return err; @@ -1194,6 +1305,7 @@ got_diff_objects_as_commits(struct got_diff_line **lin struct got_object_id *id1, struct got_object_id *id2, struct got_pathlist_head *paths, enum got_diff_algorithm diff_algo, int diff_context, int ignore_whitespace, int force_text_diff, + int show_diffstat, struct got_diffstat_cb_arg *dsa, struct got_repository *repo, FILE *outfile) { const struct got_error *err; @@ -1237,8 +1349,8 @@ got_diff_objects_as_commits(struct got_diff_line **lin err = diff_objects_as_trees(lines, nlines, f1, f2, fd1, fd2, commit1 ? got_object_commit_get_tree_id(commit1) : NULL, got_object_commit_get_tree_id(commit2), paths, "", "", - diff_context, ignore_whitespace, force_text_diff, repo, outfile, - diff_algo); + diff_context, ignore_whitespace, force_text_diff, show_diffstat, + dsa, repo, outfile, diff_algo); done: if (commit1) got_object_commit_close(commit1); blob - b40f67cf87889e4cd271c0870db2a357160443ad blob + e92fe5aa6a9932cfb59504fd20073ebc1fb4f6d4 --- lib/worktree.c +++ lib/worktree.c @@ -5083,8 +5083,8 @@ append_ct_diff(struct got_commitable *ct, int *diff_he } err = got_diff_objects_as_blobs(NULL, NULL, f1, f2, fd1, fd2, ct->base_blob_id, ct->staged_blob_id, - label1, label2, GOT_DIFF_ALGORITHM_PATIENCE, 3, 0, 0, - repo, diff_outfile); + label1, label2, GOT_DIFF_ALGORITHM_PATIENCE, 3, 0, 0, 0, + NULL, repo, diff_outfile); goto done; } @@ -5154,7 +5154,7 @@ append_ct_diff(struct got_commitable *ct, int *diff_he err = got_diff_blob_file(blob1, f1, size1, label1, ondisk_file ? ondisk_file : f2, f2_exists, &sb, ct->path, - GOT_DIFF_ALGORITHM_PATIENCE, 3, 0, 0, diff_outfile); + GOT_DIFF_ALGORITHM_PATIENCE, 3, 0, 0, 0, NULL, diff_outfile); done: if (fd1 != -1 && close(fd1) == -1 && err == NULL) err = got_error_from_errno("close"); blob - 7a91723cf327c438d7801b8d486b165bd84ba1ea blob + e05667c5078d9ea47470c917b489eef99e99c0a7 --- tog/tog.c +++ tog/tog.c @@ -4782,13 +4782,14 @@ create_diff(struct tog_diff_view_state *s) err = got_diff_objects_as_blobs(&s->lines, &s->nlines, s->f1, s->f2, s->fd1, s->fd2, s->id1, s->id2, s->label1, s->label2, tog_diff_algo, s->diff_context, - s->ignore_whitespace, s->force_text_diff, s->repo, s->f); + s->ignore_whitespace, s->force_text_diff, 0, NULL, s->repo, + s->f); break; case GOT_OBJ_TYPE_TREE: err = got_diff_objects_as_trees(&s->lines, &s->nlines, s->f1, s->f2, s->fd1, s->fd2, s->id1, s->id2, NULL, "", "", tog_diff_algo, s->diff_context, s->ignore_whitespace, - s->force_text_diff, s->repo, s->f); + s->force_text_diff, 0, NULL, s->repo, s->f); break; case GOT_OBJ_TYPE_COMMIT: { const struct got_object_id_queue *parent_ids; @@ -4826,7 +4827,7 @@ create_diff(struct tog_diff_view_state *s) err = got_diff_objects_as_commits(&s->lines, &s->nlines, s->f1, s->f2, s->fd1, s->fd2, s->id1, s->id2, NULL, tog_diff_algo, s->diff_context, s->ignore_whitespace, - s->force_text_diff, s->repo, s->f); + s->force_text_diff, 0, NULL, s->repo, s->f); break; } default: -- Mark Jamsek GPG: F2FF 13DE 6A06 C471 CA80 E6E2 2930 DC66 86EE CF68