#include "quote.h"
#include "xdiff-interface.h"
#include "cache-tree.h"
-#include "path-list.h"
+#include "string-list.h"
#include "mailmap.h"
#include "parse-options.h"
-static char blame_usage[] = "git-blame [options] [rev-opts] [rev] [--] file";
+static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file";
static const char *blame_opt_usage[] = {
blame_usage,
static int reverse;
static int blank_boundary;
static int incremental;
-static int cmd_is_annotate;
static int xdl_opts = XDF_NEED_MINIMAL;
-static struct path_list mailmap;
+static struct string_list mailmap;
#ifndef DEBUG
#define DEBUG 0
*/
char guilty;
+ /* true if the entry has been scanned for copies in the current parent
+ */
+ char scanned;
+
/* the line number of the first line of this group in the
* suspect's file; internally all line numbers are 0 based.
*/
while (made_progress) {
made_progress = 0;
for (e = sb->ent; e; e = e->next) {
- if (e->guilty || !same_suspect(e->suspect, target))
+ if (e->guilty || !same_suspect(e->suspect, target) ||
+ ent_score(sb, e) < blame_move_score)
continue;
find_copy_in_blob(sb, e, parent, split, &file_p);
if (split[1].suspect &&
*/
static struct blame_list *setup_blame_list(struct scoreboard *sb,
struct origin *target,
+ int min_score,
int *num_ents_p)
{
struct blame_entry *e;
struct blame_list *blame_list = NULL;
for (e = sb->ent, num_ents = 0; e; e = e->next)
- if (!e->guilty && same_suspect(e->suspect, target))
+ if (!e->scanned && !e->guilty &&
+ same_suspect(e->suspect, target) &&
+ min_score < ent_score(sb, e))
num_ents++;
if (num_ents) {
blame_list = xcalloc(num_ents, sizeof(struct blame_list));
for (e = sb->ent, i = 0; e; e = e->next)
- if (!e->guilty && same_suspect(e->suspect, target))
+ if (!e->scanned && !e->guilty &&
+ same_suspect(e->suspect, target) &&
+ min_score < ent_score(sb, e))
blame_list[i++].ent = e;
}
*num_ents_p = num_ents;
return blame_list;
}
+/*
+ * Reset the scanned status on all entries.
+ */
+static void reset_scanned_flag(struct scoreboard *sb)
+{
+ struct blame_entry *e;
+ for (e = sb->ent; e; e = e->next)
+ e->scanned = 0;
+}
+
/*
* For lines target is suspected for, see if we can find code movement
* across file boundary from the parent commit. porigin is the path
struct blame_list *blame_list;
int num_ents;
- blame_list = setup_blame_list(sb, target, &num_ents);
+ blame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents);
if (!blame_list)
return 1; /* nothing remains for this target */
if (!DIFF_FILE_VALID(p->one))
continue; /* does not exist in parent */
+ if (S_ISGITLINK(p->one->mode))
+ continue; /* ignore git links */
if (porigin && !strcmp(p->one->path, porigin->path))
/* find_move already dealt with this path */
continue;
split_blame(sb, split, blame_list[j].ent);
made_progress = 1;
}
+ else
+ blame_list[j].ent->scanned = 1;
decref_split(split);
}
free(blame_list);
if (!made_progress)
break;
- blame_list = setup_blame_list(sb, target, &num_ents);
+ blame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents);
if (!blame_list) {
retval = 1;
break;
}
}
+ reset_scanned_flag(sb);
diff_flush(&diff_opts);
diff_tree_release_paths(&diff_opts);
return retval;
if (suspect->commit->object.flags & UNINTERESTING) {
if (blank_boundary)
memset(hex, ' ', length);
- else if (!cmd_is_annotate) {
+ else if (!(opt & OUTPUT_ANNOTATE_COMPAT)) {
length--;
putchar('^');
}
/*
* Add phony grafts for use with -S; this is primarily to
- * support git-cvsserver that wants to give a linear history
+ * support git's cvsserver that wants to give a linear history
* to its clients.
*/
static int read_ancestry(const char *graft_file)
* Used for the command line parsing; check if the path exists
* in the working tree.
*/
-static int has_path_in_work_tree(const char *path)
+static int has_string_in_work_tree(const char *path)
{
struct stat st;
return !lstat(path, &st);
};
struct parse_opt_ctx_t ctx;
-
- cmd_is_annotate = !strcmp(argv[0], "annotate");
+ int cmd_is_annotate = !strcmp(argv[0], "annotate");
git_config(git_blame_config, NULL);
init_revisions(&revs, NULL);
parse_options_start(&ctx, argc, argv, PARSE_OPT_KEEP_DASHDASH |
PARSE_OPT_KEEP_ARGV0);
for (;;) {
- int n;
-
switch (parse_options_step(&ctx, options, blame_opt_usage)) {
case PARSE_OPT_HELP:
exit(129);
ctx.argv[0] = "--children";
reverse = 1;
}
- n = handle_revision_opt(&revs, ctx.argc, ctx.argv,
- &ctx.cpidx, ctx.out);
- if (n <= 0) {
- error("unknown option `%s'", ctx.argv[0]);
- usage_with_options(blame_opt_usage, options);
- }
- ctx.argv += n;
- ctx.argc -= n;
+ parse_revision_opt(&revs, &ctx, options, blame_opt_usage);
}
parse_done:
argc = parse_options_end(&ctx);
+ if (cmd_is_annotate)
+ output_option |= OUTPUT_ANNOTATE_COMPAT;
+
+ if (DIFF_OPT_TST(&revs.diffopt, FIND_COPIES_HARDER))
+ opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |
+ PICKAXE_BLAME_COPY_HARDER);
+
if (!blame_move_score)
blame_move_score = BLAME_DEFAULT_MOVE_SCORE;
if (!blame_copy_score)
if (argc < 2)
usage_with_options(blame_opt_usage, options);
path = add_prefix(prefix, argv[argc - 1]);
- if (argc == 3 && !has_path_in_work_tree(path)) { /* (2b) */
+ if (argc == 3 && !has_string_in_work_tree(path)) { /* (2b) */
path = add_prefix(prefix, argv[1]);
argv[1] = argv[2];
}
argv[argc - 1] = "--";
setup_work_tree();
- if (!has_path_in_work_tree(path))
+ if (!has_string_in_work_tree(path))
die("cannot stat path %s: %s", path, strerror(errno));
}