commit - 5dd8d22b08edd56b5b81f36643ab026dd6dc5047
commit + a76e88e58fb716d5dded83442b153b60687283cb
blob - c86779cca9d917f301a8ac66e723282c6c0bcb57
blob + 3014446b87c1bbbc0c5271ec13deecb93f5ccc0c
--- got/got.1
+++ got/got.1
.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
Cannot be used together with the
.Fl P
option.
+.It Fl d
+Display diffstat of changes before the actual diff by annotating each file path
+or blob hash being diffed with the total number of lines added and removed.
+A summary line will display the total number of changes across all files.
.It Fl P
Interpret all arguments as paths only.
This option can be used to resolve ambiguity in cases where paths
blob - 9a89d5b62f1635fb4b385534abdd217ee4a06611
blob + 9c142650fd13686238b78aa2d167eaa7b856cffd
--- got/got.c
+++ got/got.c
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;
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");
arg.diff_context = diff_context;
arg.ignore_whitespace = ignore_whitespace;
arg.force_text_diff = force_text_diff;
+ arg.show_diffstat = 0;
+ arg.diffstat = NULL;
arg.diff_algo = GOT_DIFF_ALGORITHM_PATIENCE;
arg.outfile = outfile;
arg.lines = NULL;
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,
}
static const struct got_error *
+print_diffstat(struct got_diffstat_cb_arg *dsa, struct got_pathlist_head *paths,
+ const char *header)
+{
+ struct got_pathlist_entry *pe;
+
+ if (header != NULL)
+ printf("%s\n", header);
+
+ 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);
+ }
+ printf("\n%d file%s changed, %d insertions(+), %d deletions(-)\n\n",
+ dsa->nfiles, dsa->nfiles > 1 ? "s" : "", dsa->ins, dsa->del);
+
+ if (fflush(stdout) != 0)
+ return got_error_from_errno("fflush");
+
+ return NULL;
+}
+
+static const struct got_error *
print_commit(struct got_commit_object *commit, struct got_object_id *id,
struct got_repository *repo, const char *path,
struct got_pathlist_head *changed_paths, struct got_diffstat_cb_arg *dsa,
} while (line);
free(logmsg0);
- if (changed_paths) {
+ if (dsa && changed_paths) {
+ err = print_diffstat(dsa, changed_paths, NULL);
+ if (err)
+ goto done;
+ } 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) {
__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);
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;
enum got_diff_algorithm diff_algo;
int ignore_whitespace;
int force_text_diff;
+ int show_diffstat;
FILE *f1;
FILE *f2;
+ FILE *outfile;
};
/*
return got_error_from_errno("got_opentemp_truncate");
if (!a->header_shown) {
- printf("diff %s%s\n", a->diff_staged ? "-s " : "",
- got_worktree_get_root_path(a->worktree));
- printf("commit - %s\n", a->id_str);
- printf("path + %s%s\n",
+ if (fprintf(a->outfile, "diff %s%s\n",
+ a->diff_staged ? "-s " : "",
+ got_worktree_get_root_path(a->worktree)) < 0) {
+ err = got_error_from_errno("fprintf");
+ goto done;
+ }
+ if (fprintf(a->outfile, "commit - %s\n", a->id_str) < 0) {
+ err = got_error_from_errno("fprintf");
+ goto done;
+ }
+ if (fprintf(a->outfile, "path + %s%s\n",
got_worktree_get_root_path(a->worktree),
- a->diff_staged ? " (staged changes)" : "");
+ a->diff_staged ? " (staged changes)" : "") < 0) {
+ err = got_error_from_errno("fprintf");
+ goto done;
+ }
a->header_shown = 1;
}
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,
+ a->outfile);
goto done;
}
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, a->outfile);
done:
if (fd1 != -1 && close(fd1) == -1 && err == NULL)
err = got_error_from_errno("close");
}
static const struct got_error *
+printfile(FILE *f)
+{
+ char buf[8192];
+ size_t r;
+
+ if (fseeko(f, 0L, SEEK_SET) == -1)
+ return got_error_from_errno("fseek");
+
+ for (;;) {
+ r = fread(buf, 1, sizeof(buf), f);
+ if (r == 0) {
+ if (ferror(f))
+ return got_error_from_errno("fread");
+ if (feof(f))
+ break;
+ }
+ if (fwrite(buf, 1, r, stdout) != r)
+ return got_ferror(stdout, GOT_ERR_IO);
+ }
+
+ return NULL;
+}
+
+static const struct got_error *
cmd_diff(int argc, char *argv[])
{
const struct got_error *error;
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;
+ FILE *f1 = NULL, *f2 = NULL, *outfile = NULL;
int fd1 = -1, fd2 = -1;
int *pack_fds = NULL;
+ struct got_diffstat_cb_arg dsa;
+ memset(&dsa, 0, sizeof(dsa));
+
TAILQ_INIT(&refs);
TAILQ_INIT(&paths);
+ TAILQ_INIT(&diffstat_paths);
#ifndef PROFILE
if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
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;
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;
if (error != NULL)
goto done;
+ if (show_diffstat) {
+ 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,
goto done;
}
+ outfile = got_opentemp();
+ if (outfile == NULL) {
+ error = got_error_from_errno("got_opentemp");
+ goto done;
+ }
+
if (ncommit_args == 0 && (ids[0] == NULL || ids[1] == NULL)) {
struct print_diff_arg arg;
char *id_str;
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;
+ arg.outfile = outfile;
error = got_worktree_status(worktree, &paths, repo, 0,
print_diff, &arg, check_cancelled, NULL);
free(id_str);
+ if (error)
+ goto done;
+
+ if (show_diffstat && dsa.nfiles > 0) {
+ char *header;
+
+ if (asprintf(&header, "diffstat %s%s",
+ diff_staged ? "-s " : "",
+ got_worktree_get_root_path(worktree)) == -1)
+ goto done;
+
+ error = print_diffstat(&dsa, &diffstat_paths, header);
+ free(header);
+ if (error)
+ goto done;
+ }
+
+ error = printfile(outfile);
goto done;
}
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, outfile);
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, outfile);
break;
case GOT_OBJ_TYPE_COMMIT:
- printf("diff %s %s\n", labels[0], labels[1]);
+ fprintf(outfile, "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, outfile);
break;
default:
error = got_error(GOT_ERR_OBJ_TYPE);
+ }
+ if (error)
+ goto done;
+
+ if (show_diffstat && dsa.nfiles > 0) {
+ char *header = NULL;
+
+ if (asprintf(&header, "diffstat %s %s",
+ labels[0], labels[1]) == -1)
+ goto done;
+
+ error = print_diffstat(&dsa, &diffstat_paths, header);
+ free(header);
+ if (error)
+ goto done;
}
+
+ error = printfile(outfile);
+
done:
free(labels[0]);
free(labels[1]);
TAILQ_FOREACH(pe, &paths, entry)
free((char *)pe->path);
got_pathlist_free(&paths);
+ TAILQ_FOREACH(pe, &diffstat_paths, entry) {
+ free((char *)pe->path);
+ free(pe->data);
+ }
+ got_pathlist_free(&diffstat_paths);
got_ref_list_free(&refs);
+ if (outfile && fclose(outfile) == EOF && error == NULL)
+ error = got_error_from_errno("fclose");
if (f1 && fclose(f1) == EOF && error == NULL)
error = got_error_from_errno("fclose");
if (f2 && fclose(f2) == EOF && error == NULL)
blob - f8b05cf0a3f3223ffcb6a59216871982fd980bc9
blob + da6c79a3e639404327d4a27ce17468d32e06c119
--- gotwebd/got_operations.c
+++ gotwebd/got_operations.c
case GOT_OBJ_TYPE_BLOB:
error = got_diff_objects_as_blobs(NULL, NULL, f1, f2, fd4, fd5,
id1, id2, NULL, NULL, GOT_DIFF_ALGORITHM_MYERS, 3, 0, 0,
- repo, f3);
+ 0, NULL, repo, f3);
break;
case GOT_OBJ_TYPE_TREE:
error = got_diff_objects_as_trees(NULL, NULL, f1, f2, fd4, fd5,
id1, id2, NULL, "", "", GOT_DIFF_ALGORITHM_MYERS, 3, 0, 0,
- repo, f3);
+ 0, NULL, repo, f3);
break;
case GOT_OBJ_TYPE_COMMIT:
error = got_diff_objects_as_commits(NULL, NULL, f1, f2, fd4,
fd5, id1, id2, NULL, GOT_DIFF_ALGORITHM_MYERS, 3, 0, 0,
- repo, f3);
+ 0, NULL, repo, f3);
break;
default:
error = got_error(GOT_ERR_OBJ_TYPE);
blob - 18fe6a19ccc9b5b2450e313f81d9246ce51b630d
blob + ba881aed5067fd49ac9a5f284d1df97cc01b1def
--- include/got_diff.h
+++ include/got_diff.h
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
*/
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
*/
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
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; /* Compute diffstat of changes */
+ struct got_diffstat_cb_arg *diffstat;
enum got_diff_algorithm diff_algo; /* Diffing algorithm to use. */
/*
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;
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.
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 + b5900dcf76baf3e7af894d4817aa447491a287fc
--- lib/diff.c
+++ lib/diff.c
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 diff_result *r, int force_text, int status)
+{
+ const struct got_error *err;
+ struct got_pathlist_entry *pe;
+ struct got_diff_changed_path *change = NULL;
+ int flags = (r->left->atomizer_flags | r->right->atomizer_flags);
+ int isbin = (flags & DIFF_ATOMIZER_FOUND_BINARY_DATA);
+ int i;
+
+ change = calloc(1, sizeof(*change));
+ if (change == NULL)
+ return got_error_from_errno("malloc");
+
+ 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;
+ }
+ }
+
+ change->status = status;
+ 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;
free(modestr1);
free(modestr2);
}
+
err = got_diffreg(&result, f1, f2, diff_algo, ignore_whitespace,
force_text_diff);
if (err)
goto done;
+ if (show_diffstat) {
+ char *path = NULL;
+ int status = GOT_STATUS_NO_CHANGE;
+
+ 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;
+ }
+ }
+
+ /*
+ * 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.
+ */
+ if (blob1 == NULL)
+ status = GOT_STATUS_ADD;
+ else if (blob2 == NULL)
+ status = GOT_STATUS_DELETE;
+ else
+ status = GOT_STATUS_MODIFY;
+
+ err = get_diffstat(ds, path, result->result, force_text_diff,
+ status);
+ if (err) {
+ free(path);
+ goto done;
+ }
+ }
+
if (outfile) {
err = got_diffreg_output(lines, nlines, result,
blob1 != NULL, blob2 != NULL,
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,
+ a->diffstat, a->outfile, a->diff_algo);
}
const struct got_error *
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 *
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];
label2, GOT_DIFF_OUTPUT_UNIDIFF,
diff_context, outfile);
if (err)
+ goto done;
+ }
+
+ if (show_diffstat) {
+ char *path = NULL;
+ int status = GOT_STATUS_NO_CHANGE;
+
+ path = strdup(label2 ? label2 : label1);
+ if (path == NULL) {
+ 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.
+ */
+ if (blob1 == NULL)
+ status = GOT_STATUS_ADD;
+ else if (!f2_exists)
+ status = GOT_STATUS_DELETE;
+ else
+ status = GOT_STATUS_MODIFY;
+
+ err = get_diffstat(ds, path, result->result, force_text_diff,
+ status);
+ if (err) {
+ free(path);
goto done;
+ }
}
done:
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 *
return cb(cb_arg, NULL, NULL, NULL, NULL, NULL, &te2->id,
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,
{
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;
+ int status = GOT_STATUS_NO_CHANGE;
path = strdup(label2 ? label2 : label1);
if (path == NULL)
return got_error_from_errno("malloc");
- change = malloc(sizeof(*change));
- if (change == NULL) {
- err = got_error_from_errno("malloc");
- goto done;
- }
-
- change->add = 0;
- change->rm = 0;
- change->status = GOT_STATUS_NO_CHANGE;
if (id1 == NULL)
- change->status = GOT_STATUS_ADD;
+ status = GOT_STATUS_ADD;
else if (id2 == NULL)
- change->status = GOT_STATUS_DELETE;
+ status = GOT_STATUS_DELETE;
else {
if (got_object_id_cmp(id1, id2) != 0)
- change->status = GOT_STATUS_MODIFY;
+ status = GOT_STATUS_MODIFY;
else if (mode1 != mode2)
- change->status = GOT_STATUS_MODE_CHANGE;
+ status = GOT_STATUS_MODE_CHANGE;
}
if (f1) {
err = got_diffreg(&result, f1, f2, a->diff_algo, a->ignore_ws,
a->force_text);
- 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);
-
- 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;
+ err = get_diffstat(a, path, result->result, a->force_text, status);
done:
if (result) {
if (free_err && err == NULL)
err = free_err;
}
- if (err) {
+ if (err)
free(path);
- free(change);
- }
return err;
}
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;
}
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);
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)
{
arg.diff_context = diff_context;
arg.ignore_whitespace = ignore_whitespace;
arg.force_text_diff = force_text_diff;
+ arg.show_diffstat = show_diffstat;
+ arg.diffstat = dsa;
arg.outfile = outfile;
if (want_linemeta) {
arg.lines = *lines;
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);
- } else {
+ 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);
+ else
err = diff_paths(tree1, tree2, f1, f2, fd1, fd2, paths, repo,
got_diff_blob_output_unidiff, &arg);
- }
if (want_linemeta) {
*lines = arg.lines; /* was likely re-allocated */
*nlines = arg.nlines;
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;
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;
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;
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
}
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;
}
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 - d3d296f8b7c8ece84d0d00349fe8949bb2ca9eeb
blob + 145254f50168ae87993a5b803db3c57555873c4b
--- tog/tog.c
+++ tog/tog.c
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;
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: