Merge branch 'js/diffstat' into next
* js/diffstat: diff-options: add --stat (take 2) diff-options: add --stat (take 2)
This commit is contained in:
commit
ce032c5f9e
@ -7,6 +7,9 @@
|
||||
--patch-with-raw::
|
||||
Generate patch but keep also the default raw diff output.
|
||||
|
||||
--stat::
|
||||
Generate a diffstat instead of a patch.
|
||||
|
||||
-z::
|
||||
\0 line termination on output
|
||||
|
||||
|
222
diff.c
222
diff.c
@ -8,7 +8,7 @@
|
||||
#include "quote.h"
|
||||
#include "diff.h"
|
||||
#include "diffcore.h"
|
||||
#include "xdiff/xdiff.h"
|
||||
#include "xdiff-interface.h"
|
||||
|
||||
static int use_size_cache;
|
||||
|
||||
@ -195,6 +195,137 @@ static int fn_out(void *priv, mmbuffer_t *mb, int nbuf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct diffstat_t {
|
||||
struct xdiff_emit_state xm;
|
||||
|
||||
int nr;
|
||||
int alloc;
|
||||
struct diffstat_file {
|
||||
char *name;
|
||||
unsigned int added, deleted;
|
||||
} **files;
|
||||
};
|
||||
|
||||
static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
|
||||
const char *name)
|
||||
{
|
||||
struct diffstat_file *x;
|
||||
x = xcalloc(sizeof (*x), 1);
|
||||
if (diffstat->nr == diffstat->alloc) {
|
||||
diffstat->alloc = alloc_nr(diffstat->alloc);
|
||||
diffstat->files = xrealloc(diffstat->files,
|
||||
diffstat->alloc * sizeof(x));
|
||||
}
|
||||
diffstat->files[diffstat->nr++] = x;
|
||||
x->name = strdup(name);
|
||||
return x;
|
||||
}
|
||||
|
||||
static void diffstat_consume(void *priv, char *line, unsigned long len)
|
||||
{
|
||||
struct diffstat_t *diffstat = priv;
|
||||
struct diffstat_file *x = diffstat->files[diffstat->nr - 1];
|
||||
|
||||
if (line[0] == '+')
|
||||
x->added++;
|
||||
else if (line[0] == '-')
|
||||
x->deleted++;
|
||||
}
|
||||
|
||||
static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
|
||||
static const char minuses[]= "----------------------------------------------------------------------";
|
||||
|
||||
static void show_stats(struct diffstat_t* data)
|
||||
{
|
||||
char *prefix = "";
|
||||
int i, len, add, del, total, adds = 0, dels = 0;
|
||||
int max, max_change = 0, max_len = 0;
|
||||
int total_files = data->nr;
|
||||
|
||||
if (data->nr == 0)
|
||||
return;
|
||||
|
||||
printf("---\n");
|
||||
|
||||
for (i = 0; i < data->nr; i++) {
|
||||
struct diffstat_file *file = data->files[i];
|
||||
|
||||
if (max_change < file->added + file->deleted)
|
||||
max_change = file->added + file->deleted;
|
||||
len = strlen(file->name);
|
||||
if (max_len < len)
|
||||
max_len = len;
|
||||
}
|
||||
|
||||
for (i = 0; i < data->nr; i++) {
|
||||
char *name = data->files[i]->name;
|
||||
int added = data->files[i]->added;
|
||||
int deleted = data->files[i]->deleted;
|
||||
|
||||
if (0 < (len = quote_c_style(name, NULL, NULL, 0))) {
|
||||
char *qname = xmalloc(len + 1);
|
||||
quote_c_style(name, qname, NULL, 0);
|
||||
free(name);
|
||||
data->files[i]->name = name = qname;
|
||||
}
|
||||
|
||||
/*
|
||||
* "scale" the filename
|
||||
*/
|
||||
len = strlen(name);
|
||||
max = max_len;
|
||||
if (max > 50)
|
||||
max = 50;
|
||||
if (len > max) {
|
||||
char *slash;
|
||||
prefix = "...";
|
||||
max -= 3;
|
||||
name += len - max;
|
||||
slash = strchr(name, '/');
|
||||
if (slash)
|
||||
name = slash;
|
||||
}
|
||||
len = max;
|
||||
|
||||
/*
|
||||
* scale the add/delete
|
||||
*/
|
||||
max = max_change;
|
||||
if (max + len > 70)
|
||||
max = 70 - len;
|
||||
|
||||
if (added < 0) {
|
||||
/* binary file */
|
||||
printf(" %s%-*s | Bin\n", prefix, len, name);
|
||||
goto free_diffstat_file;
|
||||
} else if (added + deleted == 0) {
|
||||
total_files--;
|
||||
goto free_diffstat_file;
|
||||
}
|
||||
|
||||
add = added;
|
||||
del = deleted;
|
||||
total = add + del;
|
||||
adds += add;
|
||||
dels += del;
|
||||
|
||||
if (max_change > 0) {
|
||||
total = (total * max + max_change / 2) / max_change;
|
||||
add = (add * max + max_change / 2) / max_change;
|
||||
del = total - add;
|
||||
}
|
||||
printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
|
||||
len, name, added + deleted,
|
||||
add, pluses, del, minuses);
|
||||
free_diffstat_file:
|
||||
free(data->files[i]->name);
|
||||
free(data->files[i]);
|
||||
}
|
||||
free(data->files);
|
||||
printf(" %d files changed, %d insertions(+), %d deletions(-)\n",
|
||||
total_files, adds, dels);
|
||||
}
|
||||
|
||||
#define FIRST_FEW_BYTES 8000
|
||||
static int mmfile_is_binary(mmfile_t *mf)
|
||||
{
|
||||
@ -286,6 +417,35 @@ static void builtin_diff(const char *name_a,
|
||||
return;
|
||||
}
|
||||
|
||||
static void builtin_diffstat(const char *name_a, const char *name_b,
|
||||
struct diff_filespec *one, struct diff_filespec *two,
|
||||
struct diffstat_t *diffstat)
|
||||
{
|
||||
mmfile_t mf1, mf2;
|
||||
struct diffstat_file *data;
|
||||
|
||||
data = diffstat_add(diffstat, name_a ? name_a : name_b);
|
||||
|
||||
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
|
||||
die("unable to read files to diff");
|
||||
|
||||
if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))
|
||||
data->added = -1;
|
||||
else {
|
||||
/* Crazy xdl interfaces.. */
|
||||
xpparam_t xpp;
|
||||
xdemitconf_t xecfg;
|
||||
xdemitcb_t ecb;
|
||||
|
||||
xpp.flags = XDF_NEED_MINIMAL;
|
||||
xecfg.ctxlen = 3;
|
||||
xecfg.flags = XDL_EMIT_FUNCNAMES;
|
||||
ecb.outf = xdiff_outf;
|
||||
ecb.priv = diffstat;
|
||||
xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
|
||||
}
|
||||
}
|
||||
|
||||
struct diff_filespec *alloc_filespec(const char *path)
|
||||
{
|
||||
int namelen = strlen(path);
|
||||
@ -819,6 +979,27 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
|
||||
free(other_munged);
|
||||
}
|
||||
|
||||
static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
|
||||
struct diffstat_t *diffstat)
|
||||
{
|
||||
const char *name;
|
||||
const char *other;
|
||||
|
||||
if (DIFF_PAIR_UNMERGED(p)) {
|
||||
/* unmerged */
|
||||
builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat);
|
||||
return;
|
||||
}
|
||||
|
||||
name = p->one->path;
|
||||
other = (strcmp(name, p->two->path) ? p->two->path : NULL);
|
||||
|
||||
diff_fill_sha1_info(p->one);
|
||||
diff_fill_sha1_info(p->two);
|
||||
|
||||
builtin_diffstat(name, other, p->one, p->two, diffstat);
|
||||
}
|
||||
|
||||
void diff_setup(struct diff_options *options)
|
||||
{
|
||||
memset(options, 0, sizeof(*options));
|
||||
@ -866,6 +1047,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
|
||||
options->output_format = DIFF_FORMAT_PATCH;
|
||||
options->with_raw = 1;
|
||||
}
|
||||
else if (!strcmp(arg, "--stat"))
|
||||
options->output_format = DIFF_FORMAT_DIFFSTAT;
|
||||
else if (!strcmp(arg, "-z"))
|
||||
options->line_termination = 0;
|
||||
else if (!strncmp(arg, "-l", 2))
|
||||
@ -1160,11 +1343,24 @@ static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o)
|
||||
|
||||
if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
|
||||
(DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
|
||||
return; /* no tree diffs in patch format */
|
||||
return; /* no tree diffs in patch format */
|
||||
|
||||
run_diff(p, o);
|
||||
}
|
||||
|
||||
static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o,
|
||||
struct diffstat_t *diffstat)
|
||||
{
|
||||
if (diff_unmodified_pair(p))
|
||||
return;
|
||||
|
||||
if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
|
||||
(DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
|
||||
return; /* no tree diffs in patch format */
|
||||
|
||||
run_diffstat(p, o, diffstat);
|
||||
}
|
||||
|
||||
int diff_queue_is_empty(void)
|
||||
{
|
||||
struct diff_queue_struct *q = &diff_queued_diff;
|
||||
@ -1276,7 +1472,8 @@ static void diff_resolve_rename_copy(void)
|
||||
|
||||
static void flush_one_pair(struct diff_filepair *p,
|
||||
int diff_output_format,
|
||||
struct diff_options *options)
|
||||
struct diff_options *options,
|
||||
struct diffstat_t *diffstat)
|
||||
{
|
||||
int inter_name_termination = '\t';
|
||||
int line_termination = options->line_termination;
|
||||
@ -1291,6 +1488,9 @@ static void flush_one_pair(struct diff_filepair *p,
|
||||
break;
|
||||
default:
|
||||
switch (diff_output_format) {
|
||||
case DIFF_FORMAT_DIFFSTAT:
|
||||
diff_flush_stat(p, options, diffstat);
|
||||
break;
|
||||
case DIFF_FORMAT_PATCH:
|
||||
diff_flush_patch(p, options);
|
||||
break;
|
||||
@ -1316,19 +1516,31 @@ void diff_flush(struct diff_options *options)
|
||||
struct diff_queue_struct *q = &diff_queued_diff;
|
||||
int i;
|
||||
int diff_output_format = options->output_format;
|
||||
struct diffstat_t *diffstat = NULL;
|
||||
|
||||
if (diff_output_format == DIFF_FORMAT_DIFFSTAT) {
|
||||
diffstat = xcalloc(sizeof (struct diffstat_t), 1);
|
||||
diffstat->xm.consume = diffstat_consume;
|
||||
}
|
||||
|
||||
if (options->with_raw) {
|
||||
for (i = 0; i < q->nr; i++) {
|
||||
struct diff_filepair *p = q->queue[i];
|
||||
flush_one_pair(p, DIFF_FORMAT_RAW, options);
|
||||
flush_one_pair(p, DIFF_FORMAT_RAW, options, NULL);
|
||||
}
|
||||
putchar(options->line_termination);
|
||||
}
|
||||
for (i = 0; i < q->nr; i++) {
|
||||
struct diff_filepair *p = q->queue[i];
|
||||
flush_one_pair(p, diff_output_format, options);
|
||||
flush_one_pair(p, diff_output_format, options, diffstat);
|
||||
diff_free_filepair(p);
|
||||
}
|
||||
|
||||
if (diffstat) {
|
||||
show_stats(diffstat);
|
||||
free(diffstat);
|
||||
}
|
||||
|
||||
free(q->queue);
|
||||
q->queue = NULL;
|
||||
q->nr = q->alloc = 0;
|
||||
|
2
diff.h
2
diff.h
@ -119,6 +119,7 @@ extern void diffcore_std_no_resolve(struct diff_options *);
|
||||
" -u synonym for -p.\n" \
|
||||
" --patch-with-raw\n" \
|
||||
" output both a patch and the diff-raw format.\n" \
|
||||
" --stat show diffstat instead of patch.\n" \
|
||||
" --name-only show only names of changed files.\n" \
|
||||
" --name-status show names and status of changed files.\n" \
|
||||
" --full-index show full object name on index lines.\n" \
|
||||
@ -142,6 +143,7 @@ extern int diff_queue_is_empty(void);
|
||||
#define DIFF_FORMAT_NO_OUTPUT 3
|
||||
#define DIFF_FORMAT_NAME 4
|
||||
#define DIFF_FORMAT_NAME_STATUS 5
|
||||
#define DIFF_FORMAT_DIFFSTAT 6
|
||||
|
||||
extern void diff_flush(struct diff_options*);
|
||||
|
||||
|
@ -30,9 +30,11 @@ case " $flags " in
|
||||
cc_or_p=--cc ;;
|
||||
esac
|
||||
|
||||
# If we do not have --name-status, --name-only, -r, or -c default to --cc.
|
||||
# If we do not have --name-status, --name-only, -r, -c or --stat,
|
||||
# default to --cc.
|
||||
case " $flags " in
|
||||
*" '--name-status' "* | *" '--name-only' "* | *" '-r' "* | *" '-c' "* )
|
||||
*" '--name-status' "* | *" '--name-only' "* | *" '-r' "* | *" '-c' "* | \
|
||||
*" '--stat' "*)
|
||||
;;
|
||||
*)
|
||||
flags="$flags'$cc_or_p' " ;;
|
||||
|
Loading…
Reference in New Issue
Block a user