#include "line-range.h"
#include "line-log.h"
#include "dir.h"
+#include "progress.h"
static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
static int xdl_opts;
static int abbrev = -1;
static int no_whole_file_rename;
+static int show_progress;
static struct date_mode blame_date_mode = { DATE_ISO8601 };
static size_t blame_date_width;
-static struct string_list mailmap;
+static struct string_list mailmap = STRING_LIST_INIT_NODUP;
#ifndef DEBUG
#define DEBUG 0
char path[FLEX_ARRAY];
};
-static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, long ctxlen,
+struct progress_info {
+ struct progress *progress;
+ int blamed_lines;
+};
+
+static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,
xdl_emit_hunk_consume_func_t hunk_func, void *cb_data)
{
xpparam_t xpp = {0};
xdemitcb_t ecb = {NULL};
xpp.flags = xdl_opts;
- xecfg.ctxlen = ctxlen;
xecfg.hunk_func = hunk_func;
ecb.priv = cb_data;
return xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb);
p->status);
case 'M':
porigin = get_origin(sb, parent, origin->path);
- hashcpy(porigin->blob_sha1, p->one->sha1);
+ hashcpy(porigin->blob_sha1, p->one->oid.hash);
porigin->mode = p->one->mode;
break;
case 'A':
}
}
diff_flush(&diff_opts);
- free_pathspec(&diff_opts.pathspec);
+ clear_pathspec(&diff_opts.pathspec);
return porigin;
}
if ((p->status == 'R' || p->status == 'C') &&
!strcmp(p->two->path, origin->path)) {
porigin = get_origin(sb, parent, p->one->path);
- hashcpy(porigin->blob_sha1, p->one->sha1);
+ hashcpy(porigin->blob_sha1, p->one->oid.hash);
porigin->mode = p->one->mode;
break;
}
}
diff_flush(&diff_opts);
- free_pathspec(&diff_opts.pathspec);
+ clear_pathspec(&diff_opts.pathspec);
return porigin;
}
fill_origin_blob(&sb->revs->diffopt, target, &file_o);
num_get_patch++;
- if (diff_hunks(&file_p, &file_o, 0, blame_chunk_cb, &d))
+ if (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d))
die("unable to generate diff (%s -> %s)",
oid_to_hex(&parent->commit->object.oid),
oid_to_hex(&target->commit->object.oid));
* file_p partially may match that image.
*/
memset(split, 0, sizeof(struct blame_entry [3]));
- if (diff_hunks(file_p, &file_o, 1, handle_split_cb, &d))
+ if (diff_hunks(file_p, &file_o, handle_split_cb, &d))
die("unable to generate diff (%s)",
oid_to_hex(&parent->commit->object.oid));
/* remainder, if any, all match the preimage */
continue;
norigin = get_origin(sb, parent, p->one->path);
- hashcpy(norigin->blob_sha1, p->one->sha1);
+ hashcpy(norigin->blob_sha1, p->one->oid.hash);
norigin->mode = p->one->mode;
fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
if (!file_p.ptr)
} while (unblamed);
target->suspects = reverse_blame(leftover, NULL);
diff_flush(&diff_opts);
- free_pathspec(&diff_opts.pathspec);
+ clear_pathspec(&diff_opts.pathspec);
}
/*
* The blame_entry is found to be guilty for the range.
* Show it in incremental output.
*/
-static void found_guilty_entry(struct blame_entry *ent)
+static void found_guilty_entry(struct blame_entry *ent,
+ struct progress_info *pi)
{
if (incremental) {
struct origin *suspect = ent->suspect;
write_filename_info(suspect->path);
maybe_flush_or_die(stdout, "stdout");
}
+ pi->blamed_lines += ent->num_lines;
+ display_progress(pi->progress, pi->blamed_lines);
}
/*
{
struct rev_info *revs = sb->revs;
struct commit *commit = prio_queue_get(&sb->commits);
+ struct progress_info pi = { NULL, 0 };
+
+ if (show_progress)
+ pi.progress = start_progress_delay(_("Blaming lines"),
+ sb->num_lines, 50, 1);
while (commit) {
struct blame_entry *ent;
suspect->guilty = 1;
for (;;) {
struct blame_entry *next = ent->next;
- found_guilty_entry(ent);
+ found_guilty_entry(ent, &pi);
if (next) {
ent = next;
continue;
if (DEBUG) /* sanity */
sanity_check_refcnt(sb);
}
+
+ stop_progress(&pi.progress);
}
static const char *format_time(unsigned long time, const char *tz_str,
static void verify_working_tree_path(struct commit *work_tree, const char *path)
{
struct commit_list *parents;
+ int pos;
for (parents = work_tree->parents; parents; parents = parents->next) {
const unsigned char *commit_sha1 = parents->item->object.oid.hash;
sha1_object_info(blob_sha1, NULL) == OBJ_BLOB)
return;
}
- die("no such path '%s' in HEAD", path);
+
+ pos = cache_name_pos(path, strlen(path));
+ if (pos >= 0)
+ ; /* path is in the index */
+ else if (-1 - pos < active_nr &&
+ !strcmp(active_cache[-1 - pos]->name, path))
+ ; /* path is in the index, unmerged */
+ else
+ die("no such path '%s' in HEAD", path);
}
static struct commit_list **append_parent(struct commit_list **tail, const unsigned char *sha1)
unsigned mode;
struct strbuf msg = STRBUF_INIT;
+ read_cache();
time(&now);
commit = alloc_commit_node();
commit->object.parsed = 1;
struct object *obj = revs->pending.objects[i].item;
if (obj->flags & UNINTERESTING)
continue;
- while (obj->type == OBJ_TAG)
- obj = deref_tag(obj, NULL, 0);
+ obj = deref_tag(obj, NULL, 0);
if (obj->type != OBJ_COMMIT)
die("Non commit %s?", revs->pending.objects[i].name);
if (found)
struct object *obj = revs->pending.objects[i].item;
if (!(obj->flags & UNINTERESTING))
continue;
- while (obj->type == OBJ_TAG)
- obj = deref_tag(obj, NULL, 0);
+ obj = deref_tag(obj, NULL, 0);
if (obj->type != OBJ_COMMIT)
die("Non commit %s?", revs->pending.objects[i].name);
if (sb->final)
enum object_type type;
struct commit *final_commit = NULL;
- static struct string_list range_list;
- static int output_option = 0, opt = 0;
- static int show_stats = 0;
- static const char *revs_file = NULL;
- static const char *contents_from = NULL;
- static const struct option options[] = {
+ struct string_list range_list = STRING_LIST_INIT_NODUP;
+ int output_option = 0, opt = 0;
+ int show_stats = 0;
+ const char *revs_file = NULL;
+ const char *contents_from = NULL;
+ const struct option options[] = {
OPT_BOOL(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")),
OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")),
OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")),
OPT_BOOL(0, "show-stats", &show_stats, N_("Show work cost statistics")),
+ OPT_BOOL(0, "progress", &show_progress, N_("Force progress reporting")),
OPT_BIT(0, "score-debug", &output_option, N_("Show output score for blame entries"), OUTPUT_SHOW_SCORE),
OPT_BIT('f', "show-name", &output_option, N_("Show original filename (Default: auto)"), OUTPUT_SHOW_NAME),
OPT_BIT('n', "show-number", &output_option, N_("Show original linenumber (Default: off)"), OUTPUT_SHOW_NUMBER),
save_commit_buffer = 0;
dashdash_pos = 0;
+ show_progress = -1;
parse_options_start(&ctx, argc, argv, prefix, options,
PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
DIFF_OPT_CLR(&revs.diffopt, FOLLOW_RENAMES);
argc = parse_options_end(&ctx);
+ if (incremental || (output_option & OUTPUT_PORCELAIN)) {
+ if (show_progress > 0)
+ die("--progress can't be used with --incremental or porcelain formats");
+ show_progress = 0;
+ } else if (show_progress < 0)
+ show_progress = isatty(2);
+
if (0 < abbrev)
/* one more abbrev length is needed for the boundary commit */
abbrev++;
case DATE_RAW:
blame_date_width = sizeof("1161298804 -0700");
break;
+ case DATE_UNIX:
+ blame_date_width = sizeof("1161298804");
+ break;
case DATE_SHORT:
blame_date_width = sizeof("2006-10-19");
break;
lno = prepare_lines(&sb);
if (lno && !range_list.nr)
- string_list_append(&range_list, xstrdup("1"));
+ string_list_append(&range_list, "1");
anchor = 1;
range_set_init(&ranges, range_list.nr);
read_mailmap(&mailmap, NULL);
+ assign_blame(&sb, opt);
+
if (!incremental)
setup_pager();
- assign_blame(&sb, opt);
-
free(final_commit_name);
if (incremental)