2006-10-19 23:00:04 +00:00
|
|
|
/*
|
2009-02-20 22:51:11 +00:00
|
|
|
* Blame
|
2006-10-19 23:00:04 +00:00
|
|
|
*
|
2014-04-25 23:56:49 +00:00
|
|
|
* Copyright (c) 2006, 2014 by its authors
|
|
|
|
* See COPYING for licensing conditions
|
2006-10-19 23:00:04 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "cache.h"
|
2015-06-22 14:03:05 +00:00
|
|
|
#include "refs.h"
|
2006-10-19 23:00:04 +00:00
|
|
|
#include "builtin.h"
|
|
|
|
#include "blob.h"
|
|
|
|
#include "commit.h"
|
|
|
|
#include "tag.h"
|
|
|
|
#include "tree-walk.h"
|
|
|
|
#include "diff.h"
|
|
|
|
#include "diffcore.h"
|
|
|
|
#include "revision.h"
|
2007-01-28 09:34:06 +00:00
|
|
|
#include "quote.h"
|
2006-10-19 23:00:04 +00:00
|
|
|
#include "xdiff-interface.h"
|
2007-01-30 09:11:08 +00:00
|
|
|
#include "cache-tree.h"
|
2008-07-21 18:03:49 +00:00
|
|
|
#include "string-list.h"
|
2007-04-27 07:42:15 +00:00
|
|
|
#include "mailmap.h"
|
2014-04-25 23:56:49 +00:00
|
|
|
#include "mergesort.h"
|
2008-07-08 13:19:34 +00:00
|
|
|
#include "parse-options.h"
|
2014-04-25 23:56:49 +00:00
|
|
|
#include "prio-queue.h"
|
2009-01-30 09:41:29 +00:00
|
|
|
#include "utf8.h"
|
2010-06-15 13:58:48 +00:00
|
|
|
#include "userdiff.h"
|
2013-03-28 16:47:30 +00:00
|
|
|
#include "line-range.h"
|
2013-08-06 13:59:38 +00:00
|
|
|
#include "line-log.h"
|
2015-05-19 21:44:23 +00:00
|
|
|
#include "dir.h"
|
2015-12-13 00:51:03 +00:00
|
|
|
#include "progress.h"
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2015-04-02 21:26:56 +00:00
|
|
|
static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
|
2008-07-08 13:19:34 +00:00
|
|
|
|
|
|
|
static const char *blame_opt_usage[] = {
|
|
|
|
blame_usage,
|
|
|
|
"",
|
2015-01-13 07:44:47 +00:00
|
|
|
N_("<rev-opts> are documented in git-rev-list(1)"),
|
2008-07-08 13:19:34 +00:00
|
|
|
NULL
|
|
|
|
};
|
2006-10-19 23:00:04 +00:00
|
|
|
|
|
|
|
static int longest_file;
|
|
|
|
static int longest_author;
|
|
|
|
static int max_orig_digits;
|
|
|
|
static int max_digits;
|
2006-10-20 21:51:12 +00:00
|
|
|
static int max_score_digits;
|
2006-12-18 22:04:38 +00:00
|
|
|
static int show_root;
|
2008-04-03 05:17:53 +00:00
|
|
|
static int reverse;
|
2006-12-18 22:04:38 +00:00
|
|
|
static int blank_boundary;
|
2007-01-28 09:34:06 +00:00
|
|
|
static int incremental;
|
2010-05-02 13:04:41 +00:00
|
|
|
static int xdl_opts;
|
2011-04-06 02:20:50 +00:00
|
|
|
static int abbrev = -1;
|
2012-09-21 20:52:25 +00:00
|
|
|
static int no_whole_file_rename;
|
2015-12-13 00:51:03 +00:00
|
|
|
static int show_progress;
|
2009-02-20 22:51:11 +00:00
|
|
|
|
convert "enum date_mode" into a struct
In preparation for adding date modes that may carry extra
information beyond the mode itself, this patch converts the
date_mode enum into a struct.
Most of the conversion is fairly straightforward; we pass
the struct as a pointer and dereference the type field where
necessary. Locations that declare a date_mode can use a "{}"
constructor. However, the tricky case is where we use the
enum labels as constants, like:
show_date(t, tz, DATE_NORMAL);
Ideally we could say:
show_date(t, tz, &{ DATE_NORMAL });
but of course C does not allow that. Likewise, we cannot
cast the constant to a struct, because we need to pass an
actual address. Our options are basically:
1. Manually add a "struct date_mode d = { DATE_NORMAL }"
definition to each caller, and pass "&d". This makes
the callers uglier, because they sometimes do not even
have their own scope (e.g., they are inside a switch
statement).
2. Provide a pre-made global "date_normal" struct that can
be passed by address. We'd also need "date_rfc2822",
"date_iso8601", and so forth. But at least the ugliness
is defined in one place.
3. Provide a wrapper that generates the correct struct on
the fly. The big downside is that we end up pointing to
a single global, which makes our wrapper non-reentrant.
But show_date is already not reentrant, so it does not
matter.
This patch implements 3, along with a minor macro to keep
the size of the callers sane.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-25 16:55:02 +00:00
|
|
|
static struct date_mode blame_date_mode = { DATE_ISO8601 };
|
2009-02-20 22:51:11 +00:00
|
|
|
static size_t blame_date_width;
|
|
|
|
|
2016-06-13 10:04:20 +00:00
|
|
|
static struct string_list mailmap = STRING_LIST_INIT_NODUP;
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2006-10-29 11:07:40 +00:00
|
|
|
#ifndef DEBUG
|
|
|
|
#define DEBUG 0
|
|
|
|
#endif
|
|
|
|
|
2006-11-05 19:51:41 +00:00
|
|
|
/* stats */
|
|
|
|
static int num_read_blob;
|
|
|
|
static int num_get_patch;
|
|
|
|
static int num_commits;
|
|
|
|
|
2006-10-20 01:49:30 +00:00
|
|
|
#define PICKAXE_BLAME_MOVE 01
|
2006-10-20 01:50:17 +00:00
|
|
|
#define PICKAXE_BLAME_COPY 02
|
|
|
|
#define PICKAXE_BLAME_COPY_HARDER 04
|
2007-05-06 04:18:57 +00:00
|
|
|
#define PICKAXE_BLAME_COPY_HARDEST 010
|
2006-10-20 01:49:30 +00:00
|
|
|
|
2006-10-20 22:37:12 +00:00
|
|
|
/*
|
|
|
|
* blame for a blame_entry with score lower than these thresholds
|
|
|
|
* is not passed to the parent using move/copy logic.
|
|
|
|
*/
|
|
|
|
static unsigned blame_move_score;
|
|
|
|
static unsigned blame_copy_score;
|
|
|
|
#define BLAME_DEFAULT_MOVE_SCORE 20
|
|
|
|
#define BLAME_DEFAULT_COPY_SCORE 40
|
|
|
|
|
2014-03-25 13:23:26 +00:00
|
|
|
/* Remember to update object flag allocation in object.h */
|
2006-10-19 23:00:04 +00:00
|
|
|
#define METAINFO_SHOWN (1u<<12)
|
|
|
|
#define MORE_THAN_ONE_PATH (1u<<13)
|
|
|
|
|
|
|
|
/*
|
2006-10-29 11:07:40 +00:00
|
|
|
* One blob in a commit that is being suspected
|
2006-10-19 23:00:04 +00:00
|
|
|
*/
|
|
|
|
struct origin {
|
2006-10-29 11:07:40 +00:00
|
|
|
int refcnt;
|
2014-04-25 23:56:49 +00:00
|
|
|
/* Record preceding blame record for this blob */
|
2008-06-05 05:58:40 +00:00
|
|
|
struct origin *previous;
|
2014-04-25 23:56:49 +00:00
|
|
|
/* origins are put in a list linked via `next' hanging off the
|
|
|
|
* corresponding commit's util field in order to make finding
|
|
|
|
* them fast. The presence in this chain does not count
|
|
|
|
* towards the origin's reference count. It is tempting to
|
|
|
|
* let it count as long as the commit is pending examination,
|
|
|
|
* but even under circumstances where the commit will be
|
|
|
|
* present multiple times in the priority queue of unexamined
|
|
|
|
* commits, processing the first instance will not leave any
|
|
|
|
* work requiring the origin data for the second instance. An
|
|
|
|
* interspersed commit changing that would have to be
|
|
|
|
* preexisting with a different ancestry and with the same
|
|
|
|
* commit date in order to wedge itself between two instances
|
|
|
|
* of the same commit in the priority queue _and_ produce
|
|
|
|
* blame entries relevant for it. While we don't want to let
|
|
|
|
* us get tripped up by this case, it certainly does not seem
|
|
|
|
* worth optimizing for.
|
|
|
|
*/
|
|
|
|
struct origin *next;
|
2006-10-19 23:00:04 +00:00
|
|
|
struct commit *commit;
|
2014-04-25 23:56:49 +00:00
|
|
|
/* `suspects' contains blame entries that may be attributed to
|
|
|
|
* this origin's commit or to parent commits. When a commit
|
|
|
|
* is being processed, all suspects will be moved, either by
|
|
|
|
* assigning them to an origin in a different commit, or by
|
|
|
|
* shipping them to the scoreboard's ent list because they
|
|
|
|
* cannot be attributed to a different commit.
|
|
|
|
*/
|
|
|
|
struct blame_entry *suspects;
|
2006-11-05 19:51:41 +00:00
|
|
|
mmfile_t file;
|
2016-09-05 20:07:54 +00:00
|
|
|
struct object_id blob_oid;
|
2010-09-29 11:35:24 +00:00
|
|
|
unsigned mode;
|
2014-04-25 23:56:49 +00:00
|
|
|
/* guilty gets set when shipping any suspects to the final
|
|
|
|
* blame list instead of other commits
|
|
|
|
*/
|
|
|
|
char guilty;
|
2006-10-19 23:00:04 +00:00
|
|
|
char path[FLEX_ARRAY];
|
|
|
|
};
|
|
|
|
|
2015-12-13 00:51:03 +00:00
|
|
|
struct progress_info {
|
|
|
|
struct progress *progress;
|
|
|
|
int blamed_lines;
|
|
|
|
};
|
|
|
|
|
2016-05-28 09:13:53 +00:00
|
|
|
static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,
|
2012-05-09 20:23:39 +00:00
|
|
|
xdl_emit_hunk_consume_func_t hunk_func, void *cb_data)
|
|
|
|
{
|
|
|
|
xpparam_t xpp = {0};
|
|
|
|
xdemitconf_t xecfg = {0};
|
2012-05-13 21:41:16 +00:00
|
|
|
xdemitcb_t ecb = {NULL};
|
2012-05-09 20:23:39 +00:00
|
|
|
|
|
|
|
xpp.flags = xdl_opts;
|
|
|
|
xecfg.hunk_func = hunk_func;
|
|
|
|
ecb.priv = cb_data;
|
|
|
|
return xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb);
|
|
|
|
}
|
|
|
|
|
2010-06-15 13:58:48 +00:00
|
|
|
/*
|
|
|
|
* Prepare diff_filespec and convert it using diff textconv API
|
|
|
|
* if the textconv driver exists.
|
|
|
|
* Return 1 if the conversion succeeds, 0 otherwise.
|
|
|
|
*/
|
2010-06-15 15:50:28 +00:00
|
|
|
int textconv_object(const char *path,
|
2010-09-29 11:35:24 +00:00
|
|
|
unsigned mode,
|
2016-09-05 20:07:58 +00:00
|
|
|
const struct object_id *oid,
|
|
|
|
int oid_valid,
|
2010-06-15 15:50:28 +00:00
|
|
|
char **buf,
|
|
|
|
unsigned long *buf_size)
|
2010-06-15 13:58:48 +00:00
|
|
|
{
|
|
|
|
struct diff_filespec *df;
|
|
|
|
struct userdiff_driver *textconv;
|
|
|
|
|
|
|
|
df = alloc_filespec(path);
|
2016-09-05 20:07:58 +00:00
|
|
|
fill_filespec(df, oid->hash, oid_valid, mode);
|
2010-06-15 13:58:48 +00:00
|
|
|
textconv = get_textconv(df);
|
|
|
|
if (!textconv) {
|
|
|
|
free_filespec(df);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
*buf_size = fill_textconv(textconv, df, buf);
|
|
|
|
free_filespec(df);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* Given an origin, prepare mmfile_t structure to be used by the
|
|
|
|
* diff machinery
|
|
|
|
*/
|
2010-06-15 13:58:48 +00:00
|
|
|
static void fill_origin_blob(struct diff_options *opt,
|
|
|
|
struct origin *o, mmfile_t *file)
|
2006-11-05 19:51:41 +00:00
|
|
|
{
|
|
|
|
if (!o->file.ptr) {
|
2007-02-26 19:55:59 +00:00
|
|
|
enum object_type type;
|
2010-06-15 13:58:48 +00:00
|
|
|
unsigned long file_size;
|
|
|
|
|
2006-11-05 19:51:41 +00:00
|
|
|
num_read_blob++;
|
2010-06-15 13:58:48 +00:00
|
|
|
if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
|
2016-09-05 20:07:58 +00:00
|
|
|
textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size))
|
2010-06-15 13:58:48 +00:00
|
|
|
;
|
|
|
|
else
|
2016-09-05 20:07:54 +00:00
|
|
|
file->ptr = read_sha1_file(o->blob_oid.hash, &type,
|
|
|
|
&file_size);
|
2010-06-15 13:58:48 +00:00
|
|
|
file->size = file_size;
|
|
|
|
|
2007-08-25 08:26:20 +00:00
|
|
|
if (!file->ptr)
|
|
|
|
die("Cannot read blob %s for path %s",
|
2016-09-05 20:07:54 +00:00
|
|
|
oid_to_hex(&o->blob_oid),
|
2007-08-25 08:26:20 +00:00
|
|
|
o->path);
|
2006-11-05 19:51:41 +00:00
|
|
|
o->file = *file;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*file = o->file;
|
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* Origin is refcounted and usually we keep the blob contents to be
|
|
|
|
* reused.
|
|
|
|
*/
|
2006-10-29 11:07:40 +00:00
|
|
|
static inline struct origin *origin_incref(struct origin *o)
|
|
|
|
{
|
|
|
|
if (o)
|
|
|
|
o->refcnt++;
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void origin_decref(struct origin *o)
|
|
|
|
{
|
|
|
|
if (o && --o->refcnt <= 0) {
|
2014-04-25 23:56:49 +00:00
|
|
|
struct origin *p, *l = NULL;
|
2008-06-05 05:58:40 +00:00
|
|
|
if (o->previous)
|
|
|
|
origin_decref(o->previous);
|
Avoid unnecessary "if-before-free" tests.
This change removes all obvious useless if-before-free tests.
E.g., it replaces code like this:
if (some_expression)
free (some_expression);
with the now-equivalent:
free (some_expression);
It is equivalent not just because POSIX has required free(NULL)
to work for a long time, but simply because it has worked for
so long that no reasonable porting target fails the test.
Here's some evidence from nearly 1.5 years ago:
http://www.winehq.org/pipermail/wine-patches/2006-October/031544.html
FYI, the change below was prepared by running the following:
git ls-files -z | xargs -0 \
perl -0x3b -pi -e \
's/\bif\s*\(\s*(\S+?)(?:\s*!=\s*NULL)?\s*\)\s+(free\s*\(\s*\1\s*\))/$2/s'
Note however, that it doesn't handle brace-enclosed blocks like
"if (x) { free (x); }". But that's ok, since there were none like
that in git sources.
Beware: if you do use the above snippet, note that it can
produce syntactically invalid C code. That happens when the
affected "if"-statement has a matching "else".
E.g., it would transform this
if (x)
free (x);
else
foo ();
into this:
free (x);
else
foo ();
There were none of those here, either.
If you're interested in automating detection of the useless
tests, you might like the useless-if-before-free script in gnulib:
[it *does* detect brace-enclosed free statements, and has a --name=S
option to make it detect free-like functions with different names]
http://git.sv.gnu.org/gitweb/?p=gnulib.git;a=blob;f=build-aux/useless-if-before-free
Addendum:
Remove one more (in imap-send.c), spotted by Jean-Luc Herren <jlh@gmx.ch>.
Signed-off-by: Jim Meyering <meyering@redhat.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-01-31 17:26:32 +00:00
|
|
|
free(o->file.ptr);
|
2014-04-25 23:56:49 +00:00
|
|
|
/* Should be present exactly once in commit chain */
|
|
|
|
for (p = o->commit->util; p; l = p, p = p->next) {
|
|
|
|
if (p == o) {
|
|
|
|
if (l)
|
|
|
|
l->next = p->next;
|
|
|
|
else
|
|
|
|
o->commit->util = p->next;
|
|
|
|
free(o);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
die("internal error in blame::origin_decref");
|
2006-10-29 11:07:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-12-12 00:05:50 +00:00
|
|
|
static void drop_origin_blob(struct origin *o)
|
|
|
|
{
|
|
|
|
if (o->file.ptr) {
|
|
|
|
free(o->file.ptr);
|
|
|
|
o->file.ptr = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* Each group of lines is described by a blame_entry; it can be split
|
2014-04-25 23:56:49 +00:00
|
|
|
* as we pass blame to the parents. They are arranged in linked lists
|
|
|
|
* kept as `suspects' of some unprocessed origin, or entered (when the
|
|
|
|
* blame origin has been finalized) into the scoreboard structure.
|
|
|
|
* While the scoreboard structure is only sorted at the end of
|
|
|
|
* processing (according to final image line number), the lists
|
|
|
|
* attached to an origin are sorted by the target line number.
|
2007-01-30 01:36:22 +00:00
|
|
|
*/
|
2006-10-19 23:00:04 +00:00
|
|
|
struct blame_entry {
|
|
|
|
struct blame_entry *next;
|
|
|
|
|
|
|
|
/* the first line of this group in the final image;
|
|
|
|
* internally all line numbers are 0 based.
|
|
|
|
*/
|
|
|
|
int lno;
|
|
|
|
|
|
|
|
/* how many lines this group has */
|
|
|
|
int num_lines;
|
|
|
|
|
|
|
|
/* the commit that introduced this group into the final image */
|
|
|
|
struct origin *suspect;
|
|
|
|
|
|
|
|
/* the line number of the first line of this group in the
|
|
|
|
* suspect's file; internally all line numbers are 0 based.
|
|
|
|
*/
|
|
|
|
int s_lno;
|
2006-10-20 21:51:12 +00:00
|
|
|
|
|
|
|
/* how significant this entry is -- cached to avoid
|
2007-01-30 01:36:22 +00:00
|
|
|
* scanning the lines over and over.
|
2006-10-20 21:51:12 +00:00
|
|
|
*/
|
|
|
|
unsigned score;
|
2006-10-19 23:00:04 +00:00
|
|
|
};
|
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
/*
|
|
|
|
* Any merge of blames happens on lists of blames that arrived via
|
|
|
|
* different parents in a single suspect. In this case, we want to
|
|
|
|
* sort according to the suspect line numbers as opposed to the final
|
|
|
|
* image line numbers. The function body is somewhat longish because
|
|
|
|
* it avoids unnecessary writes.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static struct blame_entry *blame_merge(struct blame_entry *list1,
|
|
|
|
struct blame_entry *list2)
|
|
|
|
{
|
|
|
|
struct blame_entry *p1 = list1, *p2 = list2,
|
|
|
|
**tail = &list1;
|
|
|
|
|
|
|
|
if (!p1)
|
|
|
|
return p2;
|
|
|
|
if (!p2)
|
|
|
|
return p1;
|
|
|
|
|
|
|
|
if (p1->s_lno <= p2->s_lno) {
|
|
|
|
do {
|
|
|
|
tail = &p1->next;
|
|
|
|
if ((p1 = *tail) == NULL) {
|
|
|
|
*tail = p2;
|
|
|
|
return list1;
|
|
|
|
}
|
|
|
|
} while (p1->s_lno <= p2->s_lno);
|
|
|
|
}
|
|
|
|
for (;;) {
|
|
|
|
*tail = p2;
|
|
|
|
do {
|
|
|
|
tail = &p2->next;
|
|
|
|
if ((p2 = *tail) == NULL) {
|
|
|
|
*tail = p1;
|
|
|
|
return list1;
|
|
|
|
}
|
|
|
|
} while (p1->s_lno > p2->s_lno);
|
|
|
|
*tail = p1;
|
|
|
|
do {
|
|
|
|
tail = &p1->next;
|
|
|
|
if ((p1 = *tail) == NULL) {
|
|
|
|
*tail = p2;
|
|
|
|
return list1;
|
|
|
|
}
|
|
|
|
} while (p1->s_lno <= p2->s_lno);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *get_next_blame(const void *p)
|
|
|
|
{
|
|
|
|
return ((struct blame_entry *)p)->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void set_next_blame(void *p1, void *p2)
|
|
|
|
{
|
|
|
|
((struct blame_entry *)p1)->next = p2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Final image line numbers are all different, so we don't need a
|
|
|
|
* three-way comparison here.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int compare_blame_final(const void *p1, const void *p2)
|
|
|
|
{
|
|
|
|
return ((struct blame_entry *)p1)->lno > ((struct blame_entry *)p2)->lno
|
|
|
|
? 1 : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int compare_blame_suspect(const void *p1, const void *p2)
|
|
|
|
{
|
|
|
|
const struct blame_entry *s1 = p1, *s2 = p2;
|
|
|
|
/*
|
|
|
|
* to allow for collating suspects, we sort according to the
|
|
|
|
* respective pointer value as the primary sorting criterion.
|
|
|
|
* The actual relation is pretty unimportant as long as it
|
|
|
|
* establishes a total order. Comparing as integers gives us
|
|
|
|
* that.
|
|
|
|
*/
|
|
|
|
if (s1->suspect != s2->suspect)
|
|
|
|
return (intptr_t)s1->suspect > (intptr_t)s2->suspect ? 1 : -1;
|
|
|
|
if (s1->s_lno == s2->s_lno)
|
|
|
|
return 0;
|
|
|
|
return s1->s_lno > s2->s_lno ? 1 : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct blame_entry *blame_sort(struct blame_entry *head,
|
|
|
|
int (*compare_fn)(const void *, const void *))
|
|
|
|
{
|
|
|
|
return llist_mergesort (head, get_next_blame, set_next_blame, compare_fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int compare_commits_by_reverse_commit_date(const void *a,
|
|
|
|
const void *b,
|
|
|
|
void *c)
|
|
|
|
{
|
|
|
|
return -compare_commits_by_commit_date(a, b, c);
|
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* The current state of the blame assignment.
|
|
|
|
*/
|
2006-10-19 23:00:04 +00:00
|
|
|
struct scoreboard {
|
|
|
|
/* the final commit (i.e. where we started digging from) */
|
|
|
|
struct commit *final;
|
2014-04-25 23:56:49 +00:00
|
|
|
/* Priority queue for commits with unassigned blame records */
|
|
|
|
struct prio_queue commits;
|
2008-04-03 05:17:53 +00:00
|
|
|
struct rev_info *revs;
|
2006-10-19 23:00:04 +00:00
|
|
|
const char *path;
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* The contents in the final image.
|
|
|
|
* Used by many functions to obtain contents of the nth line,
|
|
|
|
* indexed with scoreboard.lineno[blame_entry.lno].
|
2006-10-19 23:00:04 +00:00
|
|
|
*/
|
|
|
|
const char *final_buf;
|
|
|
|
unsigned long final_buf_size;
|
|
|
|
|
|
|
|
/* linked list of blames */
|
|
|
|
struct blame_entry *ent;
|
|
|
|
|
2006-10-21 06:49:31 +00:00
|
|
|
/* look-up a line in the final buffer */
|
2006-10-19 23:00:04 +00:00
|
|
|
int num_lines;
|
|
|
|
int *lineno;
|
|
|
|
};
|
|
|
|
|
2006-10-29 11:07:40 +00:00
|
|
|
static void sanity_check_refcnt(struct scoreboard *);
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* If two blame entries that are next to each other came from
|
|
|
|
* contiguous lines in the same origin (i.e. <commit, path> pair),
|
|
|
|
* merge them together.
|
|
|
|
*/
|
2006-10-19 23:00:04 +00:00
|
|
|
static void coalesce(struct scoreboard *sb)
|
|
|
|
{
|
|
|
|
struct blame_entry *ent, *next;
|
|
|
|
|
|
|
|
for (ent = sb->ent; ent && (next = ent->next); ent = next) {
|
2014-01-22 00:20:15 +00:00
|
|
|
if (ent->suspect == next->suspect &&
|
2006-10-19 23:00:04 +00:00
|
|
|
ent->s_lno + ent->num_lines == next->s_lno) {
|
|
|
|
ent->num_lines += next->num_lines;
|
|
|
|
ent->next = next->next;
|
2006-10-29 11:07:40 +00:00
|
|
|
origin_decref(next->suspect);
|
2006-10-19 23:00:04 +00:00
|
|
|
free(next);
|
2006-10-21 07:41:38 +00:00
|
|
|
ent->score = 0;
|
2006-10-19 23:00:04 +00:00
|
|
|
next = ent; /* again */
|
|
|
|
}
|
|
|
|
}
|
2006-10-29 11:07:40 +00:00
|
|
|
|
|
|
|
if (DEBUG) /* sanity */
|
|
|
|
sanity_check_refcnt(sb);
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
/*
|
|
|
|
* Merge the given sorted list of blames into a preexisting origin.
|
|
|
|
* If there were no previous blames to that commit, it is entered into
|
|
|
|
* the commit priority queue of the score board.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void queue_blames(struct scoreboard *sb, struct origin *porigin,
|
|
|
|
struct blame_entry *sorted)
|
|
|
|
{
|
|
|
|
if (porigin->suspects)
|
|
|
|
porigin->suspects = blame_merge(porigin->suspects, sorted);
|
|
|
|
else {
|
|
|
|
struct origin *o;
|
|
|
|
for (o = porigin->commit->util; o; o = o->next) {
|
|
|
|
if (o->suspects) {
|
|
|
|
porigin->suspects = sorted;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
porigin->suspects = sorted;
|
|
|
|
prio_queue_put(&sb->commits, porigin->commit);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* Given a commit and a path in it, create a new origin structure.
|
|
|
|
* The callers that add blame to the scoreboard should use
|
|
|
|
* get_origin() to obtain shared, refcounted copy instead of calling
|
|
|
|
* this function directly.
|
|
|
|
*/
|
2006-11-05 03:18:50 +00:00
|
|
|
static struct origin *make_origin(struct commit *commit, const char *path)
|
|
|
|
{
|
|
|
|
struct origin *o;
|
2016-02-22 22:44:32 +00:00
|
|
|
FLEX_ALLOC_STR(o, path, path);
|
2006-11-05 03:18:50 +00:00
|
|
|
o->commit = commit;
|
|
|
|
o->refcnt = 1;
|
2014-04-25 23:56:49 +00:00
|
|
|
o->next = commit->util;
|
|
|
|
commit->util = o;
|
2006-11-05 03:18:50 +00:00
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* Locate an existing origin or create a new one.
|
2014-04-25 23:56:49 +00:00
|
|
|
* This moves the origin to front position in the commit util list.
|
2007-01-30 01:36:22 +00:00
|
|
|
*/
|
2006-10-21 09:56:33 +00:00
|
|
|
static struct origin *get_origin(struct scoreboard *sb,
|
|
|
|
struct commit *commit,
|
|
|
|
const char *path)
|
2006-10-19 23:00:04 +00:00
|
|
|
{
|
2014-04-25 23:56:49 +00:00
|
|
|
struct origin *o, *l;
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
for (o = commit->util, l = NULL; o; l = o, o = o->next) {
|
|
|
|
if (!strcmp(o->path, path)) {
|
|
|
|
/* bump to front */
|
|
|
|
if (l) {
|
|
|
|
l->next = o->next;
|
|
|
|
o->next = commit->util;
|
|
|
|
commit->util = o;
|
|
|
|
}
|
|
|
|
return origin_incref(o);
|
|
|
|
}
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
2006-11-05 03:18:50 +00:00
|
|
|
return make_origin(commit, path);
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* Fill the blob_sha1 field of an origin if it hasn't, so that later
|
|
|
|
* call to fill_origin_blob() can use it to locate the data. blob_sha1
|
|
|
|
* for an origin is also used to pass the blame for the entire file to
|
|
|
|
* the parent to detect the case where a child's blob is identical to
|
|
|
|
* that of its parent's.
|
2010-09-29 11:35:24 +00:00
|
|
|
*
|
|
|
|
* This also fills origin->mode for corresponding tree path.
|
2007-01-30 01:36:22 +00:00
|
|
|
*/
|
2010-09-29 11:35:24 +00:00
|
|
|
static int fill_blob_sha1_and_mode(struct origin *origin)
|
2006-10-21 09:56:33 +00:00
|
|
|
{
|
2016-09-05 20:07:54 +00:00
|
|
|
if (!is_null_oid(&origin->blob_oid))
|
2006-10-21 09:56:33 +00:00
|
|
|
return 0;
|
2015-11-10 02:22:29 +00:00
|
|
|
if (get_tree_entry(origin->commit->object.oid.hash,
|
2006-10-21 09:56:33 +00:00
|
|
|
origin->path,
|
2016-09-05 20:07:54 +00:00
|
|
|
origin->blob_oid.hash, &origin->mode))
|
2006-10-21 09:56:33 +00:00
|
|
|
goto error_out;
|
2016-09-05 20:07:54 +00:00
|
|
|
if (sha1_object_info(origin->blob_oid.hash, NULL) != OBJ_BLOB)
|
2006-10-21 09:56:33 +00:00
|
|
|
goto error_out;
|
|
|
|
return 0;
|
|
|
|
error_out:
|
2016-09-05 20:07:54 +00:00
|
|
|
oidclr(&origin->blob_oid);
|
2010-09-29 11:35:24 +00:00
|
|
|
origin->mode = S_IFINVALID;
|
2006-10-21 09:56:33 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* We have an origin -- check if the same path exists in the
|
|
|
|
* parent and return an origin structure to represent it.
|
|
|
|
*/
|
2006-10-21 09:56:33 +00:00
|
|
|
static struct origin *find_origin(struct scoreboard *sb,
|
2006-10-19 23:00:04 +00:00
|
|
|
struct commit *parent,
|
|
|
|
struct origin *origin)
|
|
|
|
{
|
2014-04-25 23:56:49 +00:00
|
|
|
struct origin *porigin;
|
2006-10-19 23:00:04 +00:00
|
|
|
struct diff_options diff_opts;
|
2006-10-21 09:56:33 +00:00
|
|
|
const char *paths[2];
|
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
/* First check any existing origins */
|
|
|
|
for (porigin = parent->util; porigin; porigin = porigin->next)
|
|
|
|
if (!strcmp(porigin->path, origin->path)) {
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* The same path between origin and its parent
|
|
|
|
* without renaming -- the most common case.
|
|
|
|
*/
|
2014-04-25 23:56:49 +00:00
|
|
|
return origin_incref (porigin);
|
2006-11-05 03:18:50 +00:00
|
|
|
}
|
2006-10-31 09:00:01 +00:00
|
|
|
|
2006-10-21 09:56:33 +00:00
|
|
|
/* See if the origin->path is different between parent
|
|
|
|
* and origin first. Most of the time they are the
|
|
|
|
* same and diff-tree is fairly efficient about this.
|
|
|
|
*/
|
|
|
|
diff_setup(&diff_opts);
|
2007-11-10 19:05:14 +00:00
|
|
|
DIFF_OPT_SET(&diff_opts, RECURSIVE);
|
2006-10-21 09:56:33 +00:00
|
|
|
diff_opts.detect_rename = 0;
|
|
|
|
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
|
|
|
|
paths[0] = origin->path;
|
|
|
|
paths[1] = NULL;
|
|
|
|
|
2013-10-26 02:09:20 +00:00
|
|
|
parse_pathspec(&diff_opts.pathspec,
|
|
|
|
PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
|
|
|
|
PATHSPEC_LITERAL_PATH, "", paths);
|
2012-08-03 12:16:24 +00:00
|
|
|
diff_setup_done(&diff_opts);
|
2007-01-30 09:11:08 +00:00
|
|
|
|
2015-11-10 02:22:28 +00:00
|
|
|
if (is_null_oid(&origin->commit->object.oid))
|
2017-05-06 22:10:35 +00:00
|
|
|
do_diff_cache(&parent->tree->object.oid, &diff_opts);
|
2007-01-30 09:11:08 +00:00
|
|
|
else
|
2015-11-10 02:22:29 +00:00
|
|
|
diff_tree_sha1(parent->tree->object.oid.hash,
|
|
|
|
origin->commit->tree->object.oid.hash,
|
2007-01-30 09:11:08 +00:00
|
|
|
"", &diff_opts);
|
2006-10-21 09:56:33 +00:00
|
|
|
diffcore_std(&diff_opts);
|
|
|
|
|
|
|
|
if (!diff_queued_diff.nr) {
|
|
|
|
/* The path is the same as parent */
|
|
|
|
porigin = get_origin(sb, parent, origin->path);
|
2016-09-05 20:07:54 +00:00
|
|
|
oidcpy(&porigin->blob_oid, &origin->blob_oid);
|
2010-09-29 11:35:24 +00:00
|
|
|
porigin->mode = origin->mode;
|
2009-06-03 07:43:22 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Since origin->path is a pathspec, if the parent
|
|
|
|
* commit had it as a directory, we will see a whole
|
|
|
|
* bunch of deletion of files in the directory that we
|
|
|
|
* do not care about.
|
|
|
|
*/
|
|
|
|
int i;
|
|
|
|
struct diff_filepair *p = NULL;
|
|
|
|
for (i = 0; i < diff_queued_diff.nr; i++) {
|
|
|
|
const char *name;
|
|
|
|
p = diff_queued_diff.queue[i];
|
|
|
|
name = p->one->path ? p->one->path : p->two->path;
|
|
|
|
if (!strcmp(name, origin->path))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!p)
|
|
|
|
die("internal error in blame::find_origin");
|
2006-10-21 09:56:33 +00:00
|
|
|
switch (p->status) {
|
|
|
|
default:
|
2006-11-09 02:47:54 +00:00
|
|
|
die("internal error in blame::find_origin (%c)",
|
2006-10-21 09:56:33 +00:00
|
|
|
p->status);
|
|
|
|
case 'M':
|
|
|
|
porigin = get_origin(sb, parent, origin->path);
|
2016-09-05 20:07:54 +00:00
|
|
|
oidcpy(&porigin->blob_oid, &p->one->oid);
|
2010-09-29 11:35:24 +00:00
|
|
|
porigin->mode = p->one->mode;
|
2006-10-21 09:56:33 +00:00
|
|
|
break;
|
|
|
|
case 'A':
|
|
|
|
case 'T':
|
|
|
|
/* Did not exist in parent, or type changed */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
diff_flush(&diff_opts);
|
2016-06-02 21:09:22 +00:00
|
|
|
clear_pathspec(&diff_opts.pathspec);
|
2006-10-31 01:17:41 +00:00
|
|
|
return porigin;
|
|
|
|
}
|
2006-10-21 09:56:33 +00:00
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* We have an origin -- find the path that corresponds to it in its
|
|
|
|
* parent and return an origin structure to represent it.
|
|
|
|
*/
|
2006-10-31 01:17:41 +00:00
|
|
|
static struct origin *find_rename(struct scoreboard *sb,
|
|
|
|
struct commit *parent,
|
|
|
|
struct origin *origin)
|
|
|
|
{
|
|
|
|
struct origin *porigin = NULL;
|
|
|
|
struct diff_options diff_opts;
|
|
|
|
int i;
|
2006-10-19 23:00:04 +00:00
|
|
|
|
|
|
|
diff_setup(&diff_opts);
|
2007-11-10 19:05:14 +00:00
|
|
|
DIFF_OPT_SET(&diff_opts, RECURSIVE);
|
2006-10-19 23:00:04 +00:00
|
|
|
diff_opts.detect_rename = DIFF_DETECT_RENAME;
|
|
|
|
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
|
2006-11-02 08:02:11 +00:00
|
|
|
diff_opts.single_follow = origin->path;
|
2012-08-03 12:16:24 +00:00
|
|
|
diff_setup_done(&diff_opts);
|
2007-01-30 09:11:08 +00:00
|
|
|
|
2015-11-10 02:22:28 +00:00
|
|
|
if (is_null_oid(&origin->commit->object.oid))
|
2017-05-06 22:10:35 +00:00
|
|
|
do_diff_cache(&parent->tree->object.oid, &diff_opts);
|
2007-01-30 09:11:08 +00:00
|
|
|
else
|
2015-11-10 02:22:29 +00:00
|
|
|
diff_tree_sha1(parent->tree->object.oid.hash,
|
|
|
|
origin->commit->tree->object.oid.hash,
|
2007-01-30 09:11:08 +00:00
|
|
|
"", &diff_opts);
|
2006-10-19 23:00:04 +00:00
|
|
|
diffcore_std(&diff_opts);
|
|
|
|
|
|
|
|
for (i = 0; i < diff_queued_diff.nr; i++) {
|
|
|
|
struct diff_filepair *p = diff_queued_diff.queue[i];
|
2006-10-21 06:49:31 +00:00
|
|
|
if ((p->status == 'R' || p->status == 'C') &&
|
2006-10-21 09:56:33 +00:00
|
|
|
!strcmp(p->two->path, origin->path)) {
|
|
|
|
porigin = get_origin(sb, parent, p->one->path);
|
2016-09-05 20:07:54 +00:00
|
|
|
oidcpy(&porigin->blob_oid, &p->one->oid);
|
2010-09-29 11:35:24 +00:00
|
|
|
porigin->mode = p->one->mode;
|
2006-10-19 23:00:04 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
diff_flush(&diff_opts);
|
2016-06-02 21:09:22 +00:00
|
|
|
clear_pathspec(&diff_opts.pathspec);
|
2006-10-19 23:00:04 +00:00
|
|
|
return porigin;
|
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
2014-04-25 23:56:49 +00:00
|
|
|
* Append a new blame entry to a given output queue.
|
2007-01-30 01:36:22 +00:00
|
|
|
*/
|
2017-03-10 00:12:59 +00:00
|
|
|
static void add_blame_entry(struct blame_entry ***queue,
|
|
|
|
const struct blame_entry *src)
|
2006-10-19 23:00:04 +00:00
|
|
|
{
|
2017-03-10 00:12:59 +00:00
|
|
|
struct blame_entry *e = xmalloc(sizeof(*e));
|
|
|
|
memcpy(e, src, sizeof(*e));
|
2006-10-29 11:07:40 +00:00
|
|
|
origin_incref(e->suspect);
|
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
e->next = **queue;
|
|
|
|
**queue = e;
|
|
|
|
*queue = &e->next;
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* src typically is on-stack; we want to copy the information in it to
|
2014-04-25 23:56:49 +00:00
|
|
|
* a malloced blame_entry that gets added to the given queue. The
|
|
|
|
* origin of dst loses a refcnt.
|
2007-01-30 01:36:22 +00:00
|
|
|
*/
|
2014-04-25 23:56:49 +00:00
|
|
|
static void dup_entry(struct blame_entry ***queue,
|
|
|
|
struct blame_entry *dst, struct blame_entry *src)
|
2006-10-19 23:00:04 +00:00
|
|
|
{
|
2006-10-29 11:07:40 +00:00
|
|
|
origin_incref(src->suspect);
|
|
|
|
origin_decref(dst->suspect);
|
2006-10-19 23:00:04 +00:00
|
|
|
memcpy(dst, src, sizeof(*src));
|
2014-04-25 23:56:49 +00:00
|
|
|
dst->next = **queue;
|
|
|
|
**queue = dst;
|
|
|
|
*queue = &dst->next;
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
|
2013-03-28 16:47:30 +00:00
|
|
|
static const char *nth_line(struct scoreboard *sb, long lno)
|
2006-10-19 23:00:04 +00:00
|
|
|
{
|
|
|
|
return sb->final_buf + sb->lineno[lno];
|
|
|
|
}
|
|
|
|
|
2013-03-28 16:47:30 +00:00
|
|
|
static const char *nth_line_cb(void *data, long lno)
|
|
|
|
{
|
|
|
|
return nth_line((struct scoreboard *)data, lno);
|
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* It is known that lines between tlno to same came from parent, and e
|
|
|
|
* has an overlap with that range. it also is known that parent's
|
|
|
|
* line plno corresponds to e's line tlno.
|
|
|
|
*
|
|
|
|
* <---- e ----->
|
|
|
|
* <------>
|
|
|
|
* <------------>
|
|
|
|
* <------------>
|
|
|
|
* <------------------>
|
|
|
|
*
|
|
|
|
* Split e into potentially three parts; before this chunk, the chunk
|
|
|
|
* to be blamed for the parent, and after that portion.
|
|
|
|
*/
|
2006-10-29 11:07:40 +00:00
|
|
|
static void split_overlap(struct blame_entry *split,
|
2006-10-19 23:00:04 +00:00
|
|
|
struct blame_entry *e,
|
|
|
|
int tlno, int plno, int same,
|
|
|
|
struct origin *parent)
|
|
|
|
{
|
|
|
|
int chunk_end_lno;
|
|
|
|
memset(split, 0, sizeof(struct blame_entry [3]));
|
|
|
|
|
|
|
|
if (e->s_lno < tlno) {
|
|
|
|
/* there is a pre-chunk part not blamed on parent */
|
2006-10-29 11:07:40 +00:00
|
|
|
split[0].suspect = origin_incref(e->suspect);
|
2006-10-19 23:00:04 +00:00
|
|
|
split[0].lno = e->lno;
|
|
|
|
split[0].s_lno = e->s_lno;
|
|
|
|
split[0].num_lines = tlno - e->s_lno;
|
|
|
|
split[1].lno = e->lno + tlno - e->s_lno;
|
|
|
|
split[1].s_lno = plno;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
split[1].lno = e->lno;
|
|
|
|
split[1].s_lno = plno + (e->s_lno - tlno);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (same < e->s_lno + e->num_lines) {
|
|
|
|
/* there is a post-chunk part not blamed on parent */
|
2006-10-29 11:07:40 +00:00
|
|
|
split[2].suspect = origin_incref(e->suspect);
|
2006-10-19 23:00:04 +00:00
|
|
|
split[2].lno = e->lno + (same - e->s_lno);
|
|
|
|
split[2].s_lno = e->s_lno + (same - e->s_lno);
|
|
|
|
split[2].num_lines = e->s_lno + e->num_lines - same;
|
|
|
|
chunk_end_lno = split[2].lno;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
chunk_end_lno = e->lno + e->num_lines;
|
|
|
|
split[1].num_lines = chunk_end_lno - split[1].lno;
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* if it turns out there is nothing to blame the parent for,
|
|
|
|
* forget about the splitting. !split[1].suspect signals this.
|
|
|
|
*/
|
2006-10-19 23:00:04 +00:00
|
|
|
if (split[1].num_lines < 1)
|
|
|
|
return;
|
2006-10-29 11:07:40 +00:00
|
|
|
split[1].suspect = origin_incref(parent);
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* split_overlap() divided an existing blame e into up to three parts
|
2014-04-25 23:56:49 +00:00
|
|
|
* in split. Any assigned blame is moved to queue to
|
2007-01-30 01:36:22 +00:00
|
|
|
* reflect the split.
|
|
|
|
*/
|
2014-04-25 23:56:49 +00:00
|
|
|
static void split_blame(struct blame_entry ***blamed,
|
|
|
|
struct blame_entry ***unblamed,
|
2006-10-29 11:07:40 +00:00
|
|
|
struct blame_entry *split,
|
2006-10-19 23:00:04 +00:00
|
|
|
struct blame_entry *e)
|
|
|
|
{
|
|
|
|
if (split[0].suspect && split[2].suspect) {
|
2007-01-30 01:36:22 +00:00
|
|
|
/* The first part (reuse storage for the existing entry e) */
|
2014-04-25 23:56:49 +00:00
|
|
|
dup_entry(unblamed, e, &split[0]);
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/* The last part -- me */
|
2017-03-10 00:12:59 +00:00
|
|
|
add_blame_entry(unblamed, &split[2]);
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/* ... and the middle part -- parent */
|
2017-03-10 00:12:59 +00:00
|
|
|
add_blame_entry(blamed, &split[1]);
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
else if (!split[0].suspect && !split[2].suspect)
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* The parent covers the entire area; reuse storage for
|
|
|
|
* e and replace it with the parent.
|
|
|
|
*/
|
2014-04-25 23:56:49 +00:00
|
|
|
dup_entry(blamed, e, &split[1]);
|
2006-10-19 23:00:04 +00:00
|
|
|
else if (split[0].suspect) {
|
2007-01-30 01:36:22 +00:00
|
|
|
/* me and then parent */
|
2014-04-25 23:56:49 +00:00
|
|
|
dup_entry(unblamed, e, &split[0]);
|
2017-03-10 00:12:59 +00:00
|
|
|
add_blame_entry(blamed, &split[1]);
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
else {
|
2007-01-30 01:36:22 +00:00
|
|
|
/* parent and then me */
|
2014-04-25 23:56:49 +00:00
|
|
|
dup_entry(blamed, e, &split[1]);
|
2017-03-10 00:12:59 +00:00
|
|
|
add_blame_entry(unblamed, &split[2]);
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* After splitting the blame, the origins used by the
|
|
|
|
* on-stack blame_entry should lose one refcnt each.
|
|
|
|
*/
|
2006-10-29 11:07:40 +00:00
|
|
|
static void decref_split(struct blame_entry *split)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
origin_decref(split[i].suspect);
|
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
2014-04-25 23:56:49 +00:00
|
|
|
* reverse_blame reverses the list given in head, appending tail.
|
|
|
|
* That allows us to build lists in reverse order, then reverse them
|
|
|
|
* afterwards. This can be faster than building the list in proper
|
|
|
|
* order right away. The reason is that building in proper order
|
|
|
|
* requires writing a link in the _previous_ element, while building
|
|
|
|
* in reverse order just requires placing the list head into the
|
|
|
|
* _current_ element.
|
2007-01-30 01:36:22 +00:00
|
|
|
*/
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
static struct blame_entry *reverse_blame(struct blame_entry *head,
|
|
|
|
struct blame_entry *tail)
|
2006-10-19 23:00:04 +00:00
|
|
|
{
|
2014-04-25 23:56:49 +00:00
|
|
|
while (head) {
|
|
|
|
struct blame_entry *next = head->next;
|
|
|
|
head->next = tail;
|
|
|
|
tail = head;
|
|
|
|
head = next;
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
2014-04-25 23:56:49 +00:00
|
|
|
return tail;
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* Process one hunk from the patch between the current suspect for
|
2014-04-25 23:56:49 +00:00
|
|
|
* blame_entry e and its parent. This first blames any unfinished
|
|
|
|
* entries before the chunk (which is where target and parent start
|
|
|
|
* differing) on the parent, and then splits blame entries at the
|
|
|
|
* start and at the end of the difference region. Since use of -M and
|
|
|
|
* -C options may lead to overlapping/duplicate source line number
|
|
|
|
* ranges, all we can rely on from sorting/merging is the order of the
|
|
|
|
* first suspect line number.
|
2007-01-30 01:36:22 +00:00
|
|
|
*/
|
2014-04-25 23:56:49 +00:00
|
|
|
static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,
|
|
|
|
int tlno, int offset, int same,
|
|
|
|
struct origin *parent)
|
2006-10-19 23:00:04 +00:00
|
|
|
{
|
2014-04-25 23:56:49 +00:00
|
|
|
struct blame_entry *e = **srcq;
|
|
|
|
struct blame_entry *samep = NULL, *diffp = NULL;
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
while (e && e->s_lno < tlno) {
|
|
|
|
struct blame_entry *next = e->next;
|
|
|
|
/*
|
|
|
|
* current record starts before differing portion. If
|
|
|
|
* it reaches into it, we need to split it up and
|
|
|
|
* examine the second part separately.
|
|
|
|
*/
|
|
|
|
if (e->s_lno + e->num_lines > tlno) {
|
|
|
|
/* Move second half to a new record */
|
|
|
|
int len = tlno - e->s_lno;
|
|
|
|
struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));
|
|
|
|
n->suspect = e->suspect;
|
|
|
|
n->lno = e->lno + len;
|
|
|
|
n->s_lno = e->s_lno + len;
|
|
|
|
n->num_lines = e->num_lines - len;
|
|
|
|
e->num_lines = len;
|
|
|
|
e->score = 0;
|
|
|
|
/* Push new record to diffp */
|
|
|
|
n->next = diffp;
|
|
|
|
diffp = n;
|
|
|
|
} else
|
|
|
|
origin_decref(e->suspect);
|
|
|
|
/* Pass blame for everything before the differing
|
|
|
|
* chunk to the parent */
|
|
|
|
e->suspect = origin_incref(parent);
|
|
|
|
e->s_lno += offset;
|
|
|
|
e->next = samep;
|
|
|
|
samep = e;
|
|
|
|
e = next;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* As we don't know how much of a common stretch after this
|
|
|
|
* diff will occur, the currently blamed parts are all that we
|
|
|
|
* can assign to the parent for now.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (samep) {
|
|
|
|
**dstq = reverse_blame(samep, **dstq);
|
|
|
|
*dstq = &samep->next;
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
2014-04-25 23:56:49 +00:00
|
|
|
/*
|
|
|
|
* Prepend the split off portions: everything after e starts
|
|
|
|
* after the blameable portion.
|
|
|
|
*/
|
|
|
|
e = reverse_blame(diffp, e);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now retain records on the target while parts are different
|
|
|
|
* from the parent.
|
|
|
|
*/
|
|
|
|
samep = NULL;
|
|
|
|
diffp = NULL;
|
|
|
|
while (e && e->s_lno < same) {
|
|
|
|
struct blame_entry *next = e->next;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If current record extends into sameness, need to split.
|
|
|
|
*/
|
|
|
|
if (e->s_lno + e->num_lines > same) {
|
|
|
|
/*
|
|
|
|
* Move second half to a new record to be
|
|
|
|
* processed by later chunks
|
|
|
|
*/
|
|
|
|
int len = same - e->s_lno;
|
|
|
|
struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));
|
|
|
|
n->suspect = origin_incref(e->suspect);
|
|
|
|
n->lno = e->lno + len;
|
|
|
|
n->s_lno = e->s_lno + len;
|
|
|
|
n->num_lines = e->num_lines - len;
|
|
|
|
e->num_lines = len;
|
|
|
|
e->score = 0;
|
|
|
|
/* Push new record to samep */
|
|
|
|
n->next = samep;
|
|
|
|
samep = n;
|
|
|
|
}
|
|
|
|
e->next = diffp;
|
|
|
|
diffp = e;
|
|
|
|
e = next;
|
|
|
|
}
|
|
|
|
**srcq = reverse_blame(diffp, reverse_blame(samep, e));
|
|
|
|
/* Move across elements that are in the unblamable portion */
|
|
|
|
if (diffp)
|
|
|
|
*srcq = &diffp->next;
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
|
2008-10-25 13:31:36 +00:00
|
|
|
struct blame_chunk_cb_data {
|
|
|
|
struct origin *parent;
|
2014-04-25 23:56:49 +00:00
|
|
|
long offset;
|
|
|
|
struct blame_entry **dstq;
|
|
|
|
struct blame_entry **srcq;
|
2008-10-25 13:31:36 +00:00
|
|
|
};
|
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
/* diff chunks are from parent to target */
|
2012-05-09 20:21:56 +00:00
|
|
|
static int blame_chunk_cb(long start_a, long count_a,
|
|
|
|
long start_b, long count_b, void *data)
|
2008-10-25 13:31:36 +00:00
|
|
|
{
|
|
|
|
struct blame_chunk_cb_data *d = data;
|
2014-04-25 23:56:49 +00:00
|
|
|
if (start_a - start_b != d->offset)
|
|
|
|
die("internal error in blame::blame_chunk_cb");
|
|
|
|
blame_chunk(&d->dstq, &d->srcq, start_b, start_a - start_b,
|
|
|
|
start_b + count_b, d->parent);
|
|
|
|
d->offset = start_a + count_a - (start_b + count_b);
|
2012-05-09 20:21:56 +00:00
|
|
|
return 0;
|
2008-10-25 13:31:36 +00:00
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* We are looking at the origin 'target' and aiming to pass blame
|
|
|
|
* for the lines it is suspected to its parent. Run diff to find
|
|
|
|
* which lines came from parent and pass blame for them.
|
|
|
|
*/
|
2014-04-25 23:56:49 +00:00
|
|
|
static void pass_blame_to_parent(struct scoreboard *sb,
|
|
|
|
struct origin *target,
|
|
|
|
struct origin *parent)
|
2006-10-19 23:00:04 +00:00
|
|
|
{
|
2008-10-25 13:30:22 +00:00
|
|
|
mmfile_t file_p, file_o;
|
2010-05-14 09:31:33 +00:00
|
|
|
struct blame_chunk_cb_data d;
|
2014-04-25 23:56:49 +00:00
|
|
|
struct blame_entry *newdest = NULL;
|
2012-05-09 20:21:56 +00:00
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
if (!target->suspects)
|
|
|
|
return; /* nothing remains for this target */
|
|
|
|
|
|
|
|
d.parent = parent;
|
|
|
|
d.offset = 0;
|
|
|
|
d.dstq = &newdest; d.srcq = &target->suspects;
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2010-06-15 13:58:48 +00:00
|
|
|
fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
|
|
|
|
fill_origin_blob(&sb->revs->diffopt, target, &file_o);
|
2008-10-25 13:30:22 +00:00
|
|
|
num_get_patch++;
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2016-05-28 09:13:53 +00:00
|
|
|
if (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d))
|
react to errors in xdi_diff
When we call into xdiff to perform a diff, we generally lose
the return code completely. Typically by ignoring the return
of our xdi_diff wrapper, but sometimes we even propagate
that return value up and then ignore it later. This can
lead to us silently producing incorrect diffs (e.g., "git
log" might produce no output at all, not even a diff header,
for a content-level diff).
In practice this does not happen very often, because the
typical reason for xdiff to report failure is that it
malloc() failed (it uses straight malloc, and not our
xmalloc wrapper). But it could also happen when xdiff
triggers one our callbacks, which returns an error (e.g.,
outf() in builtin/rerere.c tries to report a write failure
in this way). And the next patch also plans to add more
failure modes.
Let's notice an error return from xdiff and react
appropriately. In most of the diff.c code, we can simply
die(), which matches the surrounding code (e.g., that is
what we do if we fail to load a file for diffing in the
first place). This is not that elegant, but we are probably
better off dying to let the user know there was a problem,
rather than simply generating bogus output.
We could also just die() directly in xdi_diff, but the
callers typically have a bit more context, and can provide a
better message (and if we do later decide to pass errors up,
we're one step closer to doing so).
There is one interesting case, which is in diff_grep(). Here
if we cannot generate the diff, there is nothing to match,
and we silently return "no hits". This is actually what the
existing code does already, but we make it a little more
explicit.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-09-24 23:12:23 +00:00
|
|
|
die("unable to generate diff (%s -> %s)",
|
2015-11-10 02:22:28 +00:00
|
|
|
oid_to_hex(&parent->commit->object.oid),
|
|
|
|
oid_to_hex(&target->commit->object.oid));
|
2014-04-25 23:56:49 +00:00
|
|
|
/* The rest are the same as the parent */
|
|
|
|
blame_chunk(&d.dstq, &d.srcq, INT_MAX, d.offset, INT_MAX, parent);
|
|
|
|
*d.dstq = NULL;
|
|
|
|
queue_blames(sb, parent, newdest);
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
return;
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* The lines in blame_entry after splitting blames many times can become
|
|
|
|
* very small and trivial, and at some point it becomes pointless to
|
|
|
|
* blame the parents. E.g. "\t\t}\n\t}\n\n" appears everywhere in any
|
|
|
|
* ordinary C program, and it is not worth to say it was copied from
|
|
|
|
* totally unrelated file in the parent.
|
|
|
|
*
|
|
|
|
* Compute how trivial the lines in the blame_entry are.
|
|
|
|
*/
|
2006-10-20 21:51:12 +00:00
|
|
|
static unsigned ent_score(struct scoreboard *sb, struct blame_entry *e)
|
|
|
|
{
|
|
|
|
unsigned score;
|
|
|
|
const char *cp, *ep;
|
|
|
|
|
|
|
|
if (e->score)
|
|
|
|
return e->score;
|
|
|
|
|
2006-10-21 06:49:31 +00:00
|
|
|
score = 1;
|
2006-10-20 21:51:12 +00:00
|
|
|
cp = nth_line(sb, e->lno);
|
|
|
|
ep = nth_line(sb, e->lno + e->num_lines);
|
|
|
|
while (cp < ep) {
|
|
|
|
unsigned ch = *((unsigned char *)cp);
|
|
|
|
if (isalnum(ch))
|
|
|
|
score++;
|
|
|
|
cp++;
|
|
|
|
}
|
|
|
|
e->score = score;
|
|
|
|
return score;
|
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* best_so_far[] and this[] are both a split of an existing blame_entry
|
|
|
|
* that passes blame to the parent. Maintain best_so_far the best split
|
|
|
|
* so far, by comparing this and best_so_far and copying this into
|
|
|
|
* bst_so_far as needed.
|
|
|
|
*/
|
2006-10-20 21:51:12 +00:00
|
|
|
static void copy_split_if_better(struct scoreboard *sb,
|
2006-10-29 11:07:40 +00:00
|
|
|
struct blame_entry *best_so_far,
|
|
|
|
struct blame_entry *this)
|
2006-10-20 01:49:30 +00:00
|
|
|
{
|
2006-10-29 11:07:40 +00:00
|
|
|
int i;
|
|
|
|
|
2006-10-20 01:49:30 +00:00
|
|
|
if (!this[1].suspect)
|
|
|
|
return;
|
2006-10-20 21:51:12 +00:00
|
|
|
if (best_so_far[1].suspect) {
|
|
|
|
if (ent_score(sb, &this[1]) < ent_score(sb, &best_so_far[1]))
|
|
|
|
return;
|
|
|
|
}
|
2006-10-29 11:07:40 +00:00
|
|
|
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
origin_incref(this[i].suspect);
|
|
|
|
decref_split(best_so_far);
|
2006-10-20 01:49:30 +00:00
|
|
|
memcpy(best_so_far, this, sizeof(struct blame_entry [3]));
|
|
|
|
}
|
|
|
|
|
2007-05-05 16:13:26 +00:00
|
|
|
/*
|
|
|
|
* We are looking at a part of the final image represented by
|
|
|
|
* ent (tlno and same are offset by ent->s_lno).
|
|
|
|
* tlno is where we are looking at in the final image.
|
|
|
|
* up to (but not including) same match preimage.
|
|
|
|
* plno is where we are looking at in the preimage.
|
|
|
|
*
|
|
|
|
* <-------------- final image ---------------------->
|
|
|
|
* <------ent------>
|
|
|
|
* ^tlno ^same
|
|
|
|
* <---------preimage----->
|
|
|
|
* ^plno
|
|
|
|
*
|
|
|
|
* All line numbers are 0-based.
|
|
|
|
*/
|
|
|
|
static void handle_split(struct scoreboard *sb,
|
|
|
|
struct blame_entry *ent,
|
|
|
|
int tlno, int plno, int same,
|
|
|
|
struct origin *parent,
|
|
|
|
struct blame_entry *split)
|
|
|
|
{
|
|
|
|
if (ent->num_lines <= tlno)
|
|
|
|
return;
|
|
|
|
if (tlno < same) {
|
|
|
|
struct blame_entry this[3];
|
|
|
|
tlno += ent->s_lno;
|
|
|
|
same += ent->s_lno;
|
|
|
|
split_overlap(this, ent, tlno, plno, same, parent);
|
|
|
|
copy_split_if_better(sb, split, this);
|
|
|
|
decref_split(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-25 13:31:36 +00:00
|
|
|
struct handle_split_cb_data {
|
|
|
|
struct scoreboard *sb;
|
|
|
|
struct blame_entry *ent;
|
|
|
|
struct origin *parent;
|
|
|
|
struct blame_entry *split;
|
|
|
|
long plno;
|
|
|
|
long tlno;
|
|
|
|
};
|
|
|
|
|
2012-05-09 20:22:47 +00:00
|
|
|
static int handle_split_cb(long start_a, long count_a,
|
|
|
|
long start_b, long count_b, void *data)
|
2008-10-25 13:31:36 +00:00
|
|
|
{
|
|
|
|
struct handle_split_cb_data *d = data;
|
2012-05-09 20:22:47 +00:00
|
|
|
handle_split(d->sb, d->ent, d->tlno, d->plno, start_b, d->parent,
|
|
|
|
d->split);
|
|
|
|
d->plno = start_a + count_a;
|
|
|
|
d->tlno = start_b + count_b;
|
|
|
|
return 0;
|
2008-10-25 13:31:36 +00:00
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* Find the lines from parent that are the same as ent so that
|
|
|
|
* we can pass blames to it. file_p has the blob contents for
|
|
|
|
* the parent.
|
|
|
|
*/
|
2006-10-20 01:49:30 +00:00
|
|
|
static void find_copy_in_blob(struct scoreboard *sb,
|
|
|
|
struct blame_entry *ent,
|
|
|
|
struct origin *parent,
|
2006-10-29 11:07:40 +00:00
|
|
|
struct blame_entry *split,
|
2006-10-20 01:49:30 +00:00
|
|
|
mmfile_t *file_p)
|
|
|
|
{
|
|
|
|
const char *cp;
|
|
|
|
mmfile_t file_o;
|
2010-05-14 09:31:33 +00:00
|
|
|
struct handle_split_cb_data d;
|
2012-05-09 20:22:47 +00:00
|
|
|
|
2010-05-14 09:31:33 +00:00
|
|
|
memset(&d, 0, sizeof(d));
|
|
|
|
d.sb = sb; d.ent = ent; d.parent = parent; d.split = split;
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* Prepare mmfile that contains only the lines in ent.
|
|
|
|
*/
|
2006-10-20 01:49:30 +00:00
|
|
|
cp = nth_line(sb, ent->lno);
|
2009-05-01 09:06:36 +00:00
|
|
|
file_o.ptr = (char *) cp;
|
2014-02-22 16:02:47 +00:00
|
|
|
file_o.size = nth_line(sb, ent->lno + ent->num_lines) - cp;
|
2006-10-20 01:49:30 +00:00
|
|
|
|
2007-05-05 16:13:26 +00:00
|
|
|
/*
|
|
|
|
* file_o is a part of final image we are annotating.
|
|
|
|
* file_p partially may match that image.
|
|
|
|
*/
|
2006-10-20 01:49:30 +00:00
|
|
|
memset(split, 0, sizeof(struct blame_entry [3]));
|
2016-05-28 09:13:53 +00:00
|
|
|
if (diff_hunks(file_p, &file_o, handle_split_cb, &d))
|
react to errors in xdi_diff
When we call into xdiff to perform a diff, we generally lose
the return code completely. Typically by ignoring the return
of our xdi_diff wrapper, but sometimes we even propagate
that return value up and then ignore it later. This can
lead to us silently producing incorrect diffs (e.g., "git
log" might produce no output at all, not even a diff header,
for a content-level diff).
In practice this does not happen very often, because the
typical reason for xdiff to report failure is that it
malloc() failed (it uses straight malloc, and not our
xmalloc wrapper). But it could also happen when xdiff
triggers one our callbacks, which returns an error (e.g.,
outf() in builtin/rerere.c tries to report a write failure
in this way). And the next patch also plans to add more
failure modes.
Let's notice an error return from xdiff and react
appropriately. In most of the diff.c code, we can simply
die(), which matches the surrounding code (e.g., that is
what we do if we fail to load a file for diffing in the
first place). This is not that elegant, but we are probably
better off dying to let the user know there was a problem,
rather than simply generating bogus output.
We could also just die() directly in xdi_diff, but the
callers typically have a bit more context, and can provide a
better message (and if we do later decide to pass errors up,
we're one step closer to doing so).
There is one interesting case, which is in diff_grep(). Here
if we cannot generate the diff, there is nothing to match,
and we silently return "no hits". This is actually what the
existing code does already, but we make it a little more
explicit.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-09-24 23:12:23 +00:00
|
|
|
die("unable to generate diff (%s)",
|
2015-11-10 02:22:28 +00:00
|
|
|
oid_to_hex(&parent->commit->object.oid));
|
2007-05-05 16:13:26 +00:00
|
|
|
/* remainder, if any, all match the preimage */
|
2008-10-25 13:31:36 +00:00
|
|
|
handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
|
2006-10-20 01:49:30 +00:00
|
|
|
}
|
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
/* Move all blame entries from list *source that have a score smaller
|
|
|
|
* than score_min to the front of list *small.
|
|
|
|
* Returns a pointer to the link pointing to the old head of the small list.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static struct blame_entry **filter_small(struct scoreboard *sb,
|
|
|
|
struct blame_entry **small,
|
|
|
|
struct blame_entry **source,
|
|
|
|
unsigned score_min)
|
|
|
|
{
|
|
|
|
struct blame_entry *p = *source;
|
|
|
|
struct blame_entry *oldsmall = *small;
|
|
|
|
while (p) {
|
|
|
|
if (ent_score(sb, p) <= score_min) {
|
|
|
|
*small = p;
|
|
|
|
small = &p->next;
|
|
|
|
p = *small;
|
|
|
|
} else {
|
|
|
|
*source = p;
|
|
|
|
source = &p->next;
|
|
|
|
p = *source;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*small = oldsmall;
|
|
|
|
*source = NULL;
|
|
|
|
return small;
|
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* See if lines currently target is suspected for can be attributed to
|
|
|
|
* parent.
|
|
|
|
*/
|
2014-04-25 23:56:49 +00:00
|
|
|
static void find_move_in_parent(struct scoreboard *sb,
|
|
|
|
struct blame_entry ***blamed,
|
|
|
|
struct blame_entry **toosmall,
|
|
|
|
struct origin *target,
|
|
|
|
struct origin *parent)
|
2006-10-20 01:49:30 +00:00
|
|
|
{
|
2006-10-21 07:41:38 +00:00
|
|
|
struct blame_entry *e, split[3];
|
2014-04-25 23:56:49 +00:00
|
|
|
struct blame_entry *unblamed = target->suspects;
|
|
|
|
struct blame_entry *leftover = NULL;
|
2006-10-20 01:49:30 +00:00
|
|
|
mmfile_t file_p;
|
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
if (!unblamed)
|
|
|
|
return; /* nothing remains for this target */
|
2006-10-20 01:49:30 +00:00
|
|
|
|
2010-06-15 13:58:48 +00:00
|
|
|
fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
|
2006-11-05 19:51:41 +00:00
|
|
|
if (!file_p.ptr)
|
2014-04-25 23:56:49 +00:00
|
|
|
return;
|
2006-10-20 01:49:30 +00:00
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
/* At each iteration, unblamed has a NULL-terminated list of
|
|
|
|
* entries that have not yet been tested for blame. leftover
|
|
|
|
* contains the reversed list of entries that have been tested
|
|
|
|
* without being assignable to the parent.
|
|
|
|
*/
|
|
|
|
do {
|
|
|
|
struct blame_entry **unblamedtail = &unblamed;
|
|
|
|
struct blame_entry *next;
|
|
|
|
for (e = unblamed; e; e = next) {
|
|
|
|
next = e->next;
|
2006-11-04 20:37:02 +00:00
|
|
|
find_copy_in_blob(sb, e, parent, split, &file_p);
|
|
|
|
if (split[1].suspect &&
|
|
|
|
blame_move_score < ent_score(sb, &split[1])) {
|
2014-04-25 23:56:49 +00:00
|
|
|
split_blame(blamed, &unblamedtail, split, e);
|
|
|
|
} else {
|
|
|
|
e->next = leftover;
|
|
|
|
leftover = e;
|
2006-11-04 20:37:02 +00:00
|
|
|
}
|
|
|
|
decref_split(split);
|
|
|
|
}
|
2014-04-25 23:56:49 +00:00
|
|
|
*unblamedtail = NULL;
|
|
|
|
toosmall = filter_small(sb, toosmall, &unblamed, blame_move_score);
|
|
|
|
} while (unblamed);
|
|
|
|
target->suspects = reverse_blame(leftover, NULL);
|
2006-10-20 01:49:30 +00:00
|
|
|
}
|
|
|
|
|
2006-11-05 00:39:03 +00:00
|
|
|
struct blame_list {
|
|
|
|
struct blame_entry *ent;
|
|
|
|
struct blame_entry split[3];
|
|
|
|
};
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* Count the number of entries the target is suspected for,
|
|
|
|
* and prepare a list of entry and the best split.
|
|
|
|
*/
|
2014-04-25 23:56:49 +00:00
|
|
|
static struct blame_list *setup_blame_list(struct blame_entry *unblamed,
|
2006-11-05 00:39:03 +00:00
|
|
|
int *num_ents_p)
|
|
|
|
{
|
|
|
|
struct blame_entry *e;
|
|
|
|
int num_ents, i;
|
|
|
|
struct blame_list *blame_list = NULL;
|
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
for (e = unblamed, num_ents = 0; e; e = e->next)
|
|
|
|
num_ents++;
|
2006-11-05 00:39:03 +00:00
|
|
|
if (num_ents) {
|
|
|
|
blame_list = xcalloc(num_ents, sizeof(struct blame_list));
|
2014-04-25 23:56:49 +00:00
|
|
|
for (e = unblamed, i = 0; e; e = e->next)
|
|
|
|
blame_list[i++].ent = e;
|
2006-11-05 00:39:03 +00:00
|
|
|
}
|
|
|
|
*num_ents_p = num_ents;
|
|
|
|
return blame_list;
|
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* For lines target is suspected for, see if we can find code movement
|
|
|
|
* across file boundary from the parent commit. porigin is the path
|
|
|
|
* in the parent we already tried.
|
|
|
|
*/
|
2014-04-25 23:56:49 +00:00
|
|
|
static void find_copy_in_parent(struct scoreboard *sb,
|
|
|
|
struct blame_entry ***blamed,
|
|
|
|
struct blame_entry **toosmall,
|
|
|
|
struct origin *target,
|
|
|
|
struct commit *parent,
|
|
|
|
struct origin *porigin,
|
|
|
|
int opt)
|
2006-10-20 01:50:17 +00:00
|
|
|
{
|
|
|
|
struct diff_options diff_opts;
|
2006-10-21 10:30:53 +00:00
|
|
|
int i, j;
|
2006-11-05 00:39:03 +00:00
|
|
|
struct blame_list *blame_list;
|
2006-10-21 10:30:53 +00:00
|
|
|
int num_ents;
|
2014-04-25 23:56:49 +00:00
|
|
|
struct blame_entry *unblamed = target->suspects;
|
|
|
|
struct blame_entry *leftover = NULL;
|
2006-10-20 01:50:17 +00:00
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
if (!unblamed)
|
|
|
|
return; /* nothing remains for this target */
|
2006-10-20 01:50:17 +00:00
|
|
|
|
|
|
|
diff_setup(&diff_opts);
|
2007-11-10 19:05:14 +00:00
|
|
|
DIFF_OPT_SET(&diff_opts, RECURSIVE);
|
2006-10-20 01:50:17 +00:00
|
|
|
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
|
|
|
|
|
2012-08-03 12:16:24 +00:00
|
|
|
diff_setup_done(&diff_opts);
|
2006-11-05 00:39:03 +00:00
|
|
|
|
|
|
|
/* Try "find copies harder" on new path if requested;
|
|
|
|
* we do not want to use diffcore_rename() actually to
|
|
|
|
* match things up; find_copies_harder is set only to
|
|
|
|
* force diff_tree_sha1() to feed all filepairs to diff_queue,
|
|
|
|
* and this code needs to be after diff_setup_done(), which
|
|
|
|
* usually makes find-copies-harder imply copy detection.
|
|
|
|
*/
|
2007-05-06 04:18:57 +00:00
|
|
|
if ((opt & PICKAXE_BLAME_COPY_HARDEST)
|
|
|
|
|| ((opt & PICKAXE_BLAME_COPY_HARDER)
|
|
|
|
&& (!porigin || strcmp(target->path, porigin->path))))
|
2007-11-10 19:05:14 +00:00
|
|
|
DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
|
2006-11-05 00:39:03 +00:00
|
|
|
|
2015-11-10 02:22:28 +00:00
|
|
|
if (is_null_oid(&target->commit->object.oid))
|
2017-05-06 22:10:35 +00:00
|
|
|
do_diff_cache(&parent->tree->object.oid, &diff_opts);
|
2007-01-30 09:11:08 +00:00
|
|
|
else
|
2015-11-10 02:22:29 +00:00
|
|
|
diff_tree_sha1(parent->tree->object.oid.hash,
|
|
|
|
target->commit->tree->object.oid.hash,
|
2007-01-30 09:11:08 +00:00
|
|
|
"", &diff_opts);
|
2006-10-20 01:50:17 +00:00
|
|
|
|
2007-11-10 19:05:14 +00:00
|
|
|
if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER))
|
2006-11-05 00:39:03 +00:00
|
|
|
diffcore_std(&diff_opts);
|
2006-10-20 01:50:17 +00:00
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
do {
|
|
|
|
struct blame_entry **unblamedtail = &unblamed;
|
|
|
|
blame_list = setup_blame_list(unblamed, &num_ents);
|
2006-11-05 00:39:03 +00:00
|
|
|
|
|
|
|
for (i = 0; i < diff_queued_diff.nr; i++) {
|
|
|
|
struct diff_filepair *p = diff_queued_diff.queue[i];
|
|
|
|
struct origin *norigin;
|
|
|
|
mmfile_t file_p;
|
|
|
|
struct blame_entry this[3];
|
|
|
|
|
|
|
|
if (!DIFF_FILE_VALID(p->one))
|
|
|
|
continue; /* does not exist in parent */
|
2008-10-03 16:23:50 +00:00
|
|
|
if (S_ISGITLINK(p->one->mode))
|
|
|
|
continue; /* ignore git links */
|
2006-11-05 00:39:03 +00:00
|
|
|
if (porigin && !strcmp(p->one->path, porigin->path))
|
|
|
|
/* find_move already dealt with this path */
|
|
|
|
continue;
|
|
|
|
|
|
|
|
norigin = get_origin(sb, parent, p->one->path);
|
2016-09-05 20:07:54 +00:00
|
|
|
oidcpy(&norigin->blob_oid, &p->one->oid);
|
2010-09-29 11:35:24 +00:00
|
|
|
norigin->mode = p->one->mode;
|
2010-06-15 13:58:48 +00:00
|
|
|
fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
|
2006-11-05 00:39:03 +00:00
|
|
|
if (!file_p.ptr)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (j = 0; j < num_ents; j++) {
|
|
|
|
find_copy_in_blob(sb, blame_list[j].ent,
|
|
|
|
norigin, this, &file_p);
|
|
|
|
copy_split_if_better(sb, blame_list[j].split,
|
|
|
|
this);
|
|
|
|
decref_split(this);
|
|
|
|
}
|
|
|
|
origin_decref(norigin);
|
|
|
|
}
|
2006-10-20 01:50:17 +00:00
|
|
|
|
2006-10-21 10:30:53 +00:00
|
|
|
for (j = 0; j < num_ents; j++) {
|
2006-11-05 00:39:03 +00:00
|
|
|
struct blame_entry *split = blame_list[j].split;
|
|
|
|
if (split[1].suspect &&
|
|
|
|
blame_copy_score < ent_score(sb, &split[1])) {
|
2014-04-25 23:56:49 +00:00
|
|
|
split_blame(blamed, &unblamedtail, split,
|
|
|
|
blame_list[j].ent);
|
|
|
|
} else {
|
|
|
|
blame_list[j].ent->next = leftover;
|
|
|
|
leftover = blame_list[j].ent;
|
2006-11-05 00:39:03 +00:00
|
|
|
}
|
|
|
|
decref_split(split);
|
2006-10-20 01:50:17 +00:00
|
|
|
}
|
2006-11-05 00:39:03 +00:00
|
|
|
free(blame_list);
|
2014-04-25 23:56:49 +00:00
|
|
|
*unblamedtail = NULL;
|
|
|
|
toosmall = filter_small(sb, toosmall, &unblamed, blame_copy_score);
|
|
|
|
} while (unblamed);
|
|
|
|
target->suspects = reverse_blame(leftover, NULL);
|
2006-11-05 00:39:03 +00:00
|
|
|
diff_flush(&diff_opts);
|
2016-06-02 21:09:22 +00:00
|
|
|
clear_pathspec(&diff_opts.pathspec);
|
2006-10-20 01:50:17 +00:00
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* The blobs of origin and porigin exactly match, so everything
|
2006-11-05 19:51:41 +00:00
|
|
|
* origin is suspected for can be blamed on the parent.
|
|
|
|
*/
|
|
|
|
static void pass_whole_blame(struct scoreboard *sb,
|
|
|
|
struct origin *origin, struct origin *porigin)
|
|
|
|
{
|
2014-04-25 23:56:49 +00:00
|
|
|
struct blame_entry *e, *suspects;
|
2006-11-05 19:51:41 +00:00
|
|
|
|
|
|
|
if (!porigin->file.ptr && origin->file.ptr) {
|
|
|
|
/* Steal its file */
|
|
|
|
porigin->file = origin->file;
|
|
|
|
origin->file.ptr = NULL;
|
|
|
|
}
|
2014-04-25 23:56:49 +00:00
|
|
|
suspects = origin->suspects;
|
|
|
|
origin->suspects = NULL;
|
|
|
|
for (e = suspects; e; e = e->next) {
|
2006-11-05 19:51:41 +00:00
|
|
|
origin_incref(porigin);
|
|
|
|
origin_decref(e->suspect);
|
|
|
|
e->suspect = porigin;
|
|
|
|
}
|
2014-04-25 23:56:49 +00:00
|
|
|
queue_blames(sb, porigin, suspects);
|
2006-11-05 19:51:41 +00:00
|
|
|
}
|
|
|
|
|
2008-04-03 07:56:23 +00:00
|
|
|
/*
|
|
|
|
* We pass blame from the current commit to its parents. We keep saying
|
|
|
|
* "parent" (and "porigin"), but what we mean is to find scapegoat to
|
|
|
|
* exonerate ourselves.
|
|
|
|
*/
|
2008-04-03 05:17:53 +00:00
|
|
|
static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit)
|
2008-04-03 07:56:23 +00:00
|
|
|
{
|
2015-09-15 10:05:39 +00:00
|
|
|
if (!reverse) {
|
|
|
|
if (revs->first_parent_only &&
|
|
|
|
commit->parents &&
|
|
|
|
commit->parents->next) {
|
|
|
|
free_commit_list(commit->parents->next);
|
|
|
|
commit->parents->next = NULL;
|
|
|
|
}
|
2008-04-03 05:17:53 +00:00
|
|
|
return commit->parents;
|
2015-09-15 10:05:39 +00:00
|
|
|
}
|
2008-04-03 05:17:53 +00:00
|
|
|
return lookup_decoration(&revs->children, &commit->object);
|
2008-04-03 07:56:23 +00:00
|
|
|
}
|
|
|
|
|
2008-04-03 05:17:53 +00:00
|
|
|
static int num_scapegoats(struct rev_info *revs, struct commit *commit)
|
2008-04-03 07:56:23 +00:00
|
|
|
{
|
2008-04-03 05:17:53 +00:00
|
|
|
struct commit_list *l = first_scapegoat(revs, commit);
|
2014-07-16 23:52:09 +00:00
|
|
|
return commit_list_count(l);
|
2008-04-03 07:56:23 +00:00
|
|
|
}
|
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
/* Distribute collected unsorted blames to the respected sorted lists
|
|
|
|
* in the various origins.
|
|
|
|
*/
|
|
|
|
static void distribute_blame(struct scoreboard *sb, struct blame_entry *blamed)
|
|
|
|
{
|
|
|
|
blamed = blame_sort(blamed, compare_blame_suspect);
|
|
|
|
while (blamed)
|
|
|
|
{
|
|
|
|
struct origin *porigin = blamed->suspect;
|
|
|
|
struct blame_entry *suspects = NULL;
|
|
|
|
do {
|
|
|
|
struct blame_entry *next = blamed->next;
|
|
|
|
blamed->next = suspects;
|
|
|
|
suspects = blamed;
|
|
|
|
blamed = next;
|
|
|
|
} while (blamed && blamed->suspect == porigin);
|
|
|
|
suspects = reverse_blame(suspects, NULL);
|
|
|
|
queue_blames(sb, porigin, suspects);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-03 07:56:23 +00:00
|
|
|
#define MAXSG 16
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2006-10-20 01:49:30 +00:00
|
|
|
static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
|
2006-10-19 23:00:04 +00:00
|
|
|
{
|
2008-04-03 05:17:53 +00:00
|
|
|
struct rev_info *revs = sb->revs;
|
2008-04-03 07:56:23 +00:00
|
|
|
int i, pass, num_sg;
|
2006-10-19 23:00:04 +00:00
|
|
|
struct commit *commit = origin->commit;
|
2008-04-03 07:56:23 +00:00
|
|
|
struct commit_list *sg;
|
|
|
|
struct origin *sg_buf[MAXSG];
|
|
|
|
struct origin *porigin, **sg_origin = sg_buf;
|
2014-04-25 23:56:49 +00:00
|
|
|
struct blame_entry *toosmall = NULL;
|
|
|
|
struct blame_entry *blames, **blametail = &blames;
|
2008-04-03 07:56:23 +00:00
|
|
|
|
2008-04-03 05:17:53 +00:00
|
|
|
num_sg = num_scapegoats(revs, commit);
|
2008-04-03 07:56:23 +00:00
|
|
|
if (!num_sg)
|
|
|
|
goto finish;
|
|
|
|
else if (num_sg < ARRAY_SIZE(sg_buf))
|
|
|
|
memset(sg_buf, 0, sizeof(sg_buf));
|
|
|
|
else
|
|
|
|
sg_origin = xcalloc(num_sg, sizeof(*sg_origin));
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2008-04-03 07:56:23 +00:00
|
|
|
/*
|
|
|
|
* The first pass looks for unrenamed path to optimize for
|
2006-10-31 01:17:41 +00:00
|
|
|
* common cases, then we look for renames in the second pass.
|
|
|
|
*/
|
2012-09-21 20:52:25 +00:00
|
|
|
for (pass = 0; pass < 2 - no_whole_file_rename; pass++) {
|
2006-10-31 01:17:41 +00:00
|
|
|
struct origin *(*find)(struct scoreboard *,
|
|
|
|
struct commit *, struct origin *);
|
|
|
|
find = pass ? find_rename : find_origin;
|
|
|
|
|
2008-04-03 05:17:53 +00:00
|
|
|
for (i = 0, sg = first_scapegoat(revs, commit);
|
2008-04-03 07:56:23 +00:00
|
|
|
i < num_sg && sg;
|
|
|
|
sg = sg->next, i++) {
|
|
|
|
struct commit *p = sg->item;
|
2006-11-04 20:20:09 +00:00
|
|
|
int j, same;
|
2006-10-31 01:17:41 +00:00
|
|
|
|
2008-04-03 07:56:23 +00:00
|
|
|
if (sg_origin[i])
|
2006-10-31 01:17:41 +00:00
|
|
|
continue;
|
|
|
|
if (parse_commit(p))
|
|
|
|
continue;
|
2006-10-31 09:00:01 +00:00
|
|
|
porigin = find(sb, p, origin);
|
2006-10-31 01:17:41 +00:00
|
|
|
if (!porigin)
|
|
|
|
continue;
|
2016-09-05 20:07:54 +00:00
|
|
|
if (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) {
|
2006-11-05 19:51:41 +00:00
|
|
|
pass_whole_blame(sb, origin, porigin);
|
2006-10-31 01:17:41 +00:00
|
|
|
origin_decref(porigin);
|
|
|
|
goto finish;
|
|
|
|
}
|
2006-11-04 20:20:09 +00:00
|
|
|
for (j = same = 0; j < i; j++)
|
2008-04-03 07:56:23 +00:00
|
|
|
if (sg_origin[j] &&
|
2016-09-05 20:07:54 +00:00
|
|
|
!oidcmp(&sg_origin[j]->blob_oid, &porigin->blob_oid)) {
|
2006-11-04 20:20:09 +00:00
|
|
|
same = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!same)
|
2008-04-03 07:56:23 +00:00
|
|
|
sg_origin[i] = porigin;
|
2006-11-04 20:20:09 +00:00
|
|
|
else
|
|
|
|
origin_decref(porigin);
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-11-05 19:51:41 +00:00
|
|
|
num_commits++;
|
2008-04-03 05:17:53 +00:00
|
|
|
for (i = 0, sg = first_scapegoat(revs, commit);
|
2008-04-03 07:56:23 +00:00
|
|
|
i < num_sg && sg;
|
|
|
|
sg = sg->next, i++) {
|
|
|
|
struct origin *porigin = sg_origin[i];
|
2006-10-19 23:00:04 +00:00
|
|
|
if (!porigin)
|
|
|
|
continue;
|
2008-06-05 05:58:40 +00:00
|
|
|
if (!origin->previous) {
|
|
|
|
origin_incref(porigin);
|
|
|
|
origin->previous = porigin;
|
|
|
|
}
|
2014-04-25 23:56:49 +00:00
|
|
|
pass_blame_to_parent(sb, origin, porigin);
|
|
|
|
if (!origin->suspects)
|
2006-10-29 11:07:40 +00:00
|
|
|
goto finish;
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
2006-10-20 01:49:30 +00:00
|
|
|
|
|
|
|
/*
|
2007-01-30 01:36:22 +00:00
|
|
|
* Optionally find moves in parents' files.
|
2006-10-20 01:49:30 +00:00
|
|
|
*/
|
2014-04-25 23:56:49 +00:00
|
|
|
if (opt & PICKAXE_BLAME_MOVE) {
|
|
|
|
filter_small(sb, &toosmall, &origin->suspects, blame_move_score);
|
|
|
|
if (origin->suspects) {
|
|
|
|
for (i = 0, sg = first_scapegoat(revs, commit);
|
|
|
|
i < num_sg && sg;
|
|
|
|
sg = sg->next, i++) {
|
|
|
|
struct origin *porigin = sg_origin[i];
|
|
|
|
if (!porigin)
|
|
|
|
continue;
|
|
|
|
find_move_in_parent(sb, &blametail, &toosmall, origin, porigin);
|
|
|
|
if (!origin->suspects)
|
|
|
|
break;
|
|
|
|
}
|
2006-10-20 01:49:30 +00:00
|
|
|
}
|
2014-04-25 23:56:49 +00:00
|
|
|
}
|
2006-10-20 01:49:30 +00:00
|
|
|
|
2006-10-20 01:50:17 +00:00
|
|
|
/*
|
2007-01-30 01:36:22 +00:00
|
|
|
* Optionally find copies from parents' files.
|
2006-10-20 01:50:17 +00:00
|
|
|
*/
|
2014-04-25 23:56:49 +00:00
|
|
|
if (opt & PICKAXE_BLAME_COPY) {
|
|
|
|
if (blame_copy_score > blame_move_score)
|
|
|
|
filter_small(sb, &toosmall, &origin->suspects, blame_copy_score);
|
|
|
|
else if (blame_copy_score < blame_move_score) {
|
|
|
|
origin->suspects = blame_merge(origin->suspects, toosmall);
|
|
|
|
toosmall = NULL;
|
|
|
|
filter_small(sb, &toosmall, &origin->suspects, blame_copy_score);
|
|
|
|
}
|
|
|
|
if (!origin->suspects)
|
|
|
|
goto finish;
|
|
|
|
|
2008-04-03 05:17:53 +00:00
|
|
|
for (i = 0, sg = first_scapegoat(revs, commit);
|
2008-04-03 07:56:23 +00:00
|
|
|
i < num_sg && sg;
|
|
|
|
sg = sg->next, i++) {
|
|
|
|
struct origin *porigin = sg_origin[i];
|
2014-04-25 23:56:49 +00:00
|
|
|
find_copy_in_parent(sb, &blametail, &toosmall,
|
|
|
|
origin, sg->item, porigin, opt);
|
|
|
|
if (!origin->suspects)
|
2006-10-29 11:07:40 +00:00
|
|
|
goto finish;
|
2006-10-20 01:50:17 +00:00
|
|
|
}
|
2014-04-25 23:56:49 +00:00
|
|
|
}
|
2006-10-29 11:07:40 +00:00
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
finish:
|
|
|
|
*blametail = NULL;
|
|
|
|
distribute_blame(sb, blames);
|
|
|
|
/*
|
|
|
|
* prepend toosmall to origin->suspects
|
|
|
|
*
|
|
|
|
* There is no point in sorting: this ends up on a big
|
|
|
|
* unsorted list in the caller anyway.
|
|
|
|
*/
|
|
|
|
if (toosmall) {
|
|
|
|
struct blame_entry **tail = &toosmall;
|
|
|
|
while (*tail)
|
|
|
|
tail = &(*tail)->next;
|
|
|
|
*tail = origin->suspects;
|
|
|
|
origin->suspects = toosmall;
|
|
|
|
}
|
2008-04-03 07:56:23 +00:00
|
|
|
for (i = 0; i < num_sg; i++) {
|
|
|
|
if (sg_origin[i]) {
|
|
|
|
drop_origin_blob(sg_origin[i]);
|
|
|
|
origin_decref(sg_origin[i]);
|
2007-12-12 00:05:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
drop_origin_blob(origin);
|
2008-04-03 07:56:23 +00:00
|
|
|
if (sg_buf != sg_origin)
|
|
|
|
free(sg_origin);
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* Information on commits, used for output.
|
|
|
|
*/
|
2011-03-16 07:08:34 +00:00
|
|
|
struct commit_info {
|
2013-01-05 21:26:40 +00:00
|
|
|
struct strbuf author;
|
|
|
|
struct strbuf author_mail;
|
2017-04-26 19:29:31 +00:00
|
|
|
timestamp_t author_time;
|
2013-01-05 21:26:40 +00:00
|
|
|
struct strbuf author_tz;
|
2006-10-19 23:00:04 +00:00
|
|
|
|
|
|
|
/* filled only when asked for details */
|
2013-01-05 21:26:40 +00:00
|
|
|
struct strbuf committer;
|
|
|
|
struct strbuf committer_mail;
|
2017-04-26 19:29:31 +00:00
|
|
|
timestamp_t committer_time;
|
2013-01-05 21:26:40 +00:00
|
|
|
struct strbuf committer_tz;
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2013-01-05 21:26:40 +00:00
|
|
|
struct strbuf summary;
|
2006-10-19 23:00:04 +00:00
|
|
|
};
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* Parse author/committer line in the commit object buffer
|
|
|
|
*/
|
2006-10-19 23:00:04 +00:00
|
|
|
static void get_ac_line(const char *inbuf, const char *what,
|
2013-01-05 21:26:40 +00:00
|
|
|
struct strbuf *name, struct strbuf *mail,
|
2017-04-26 19:29:31 +00:00
|
|
|
timestamp_t *time, struct strbuf *tz)
|
2006-10-19 23:00:04 +00:00
|
|
|
{
|
2013-01-05 21:26:38 +00:00
|
|
|
struct ident_split ident;
|
2013-01-05 21:26:40 +00:00
|
|
|
size_t len, maillen, namelen;
|
|
|
|
char *tmp, *endp;
|
|
|
|
const char *namebuf, *mailbuf;
|
2006-10-19 23:00:04 +00:00
|
|
|
|
|
|
|
tmp = strstr(inbuf, what);
|
|
|
|
if (!tmp)
|
|
|
|
goto error_out;
|
|
|
|
tmp += strlen(what);
|
|
|
|
endp = strchr(tmp, '\n');
|
|
|
|
if (!endp)
|
|
|
|
len = strlen(tmp);
|
|
|
|
else
|
|
|
|
len = endp - tmp;
|
2013-01-05 21:26:38 +00:00
|
|
|
|
|
|
|
if (split_ident_line(&ident, tmp, len)) {
|
2006-10-19 23:00:04 +00:00
|
|
|
error_out:
|
|
|
|
/* Ugh */
|
2013-01-05 21:26:40 +00:00
|
|
|
tmp = "(unknown)";
|
|
|
|
strbuf_addstr(name, tmp);
|
|
|
|
strbuf_addstr(mail, tmp);
|
|
|
|
strbuf_addstr(tz, tmp);
|
2006-10-19 23:00:04 +00:00
|
|
|
*time = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-01-05 21:26:38 +00:00
|
|
|
namelen = ident.name_end - ident.name_begin;
|
2013-01-05 21:26:40 +00:00
|
|
|
namebuf = ident.name_begin;
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2013-01-05 21:26:40 +00:00
|
|
|
maillen = ident.mail_end - ident.mail_begin;
|
|
|
|
mailbuf = ident.mail_begin;
|
2007-04-27 07:42:15 +00:00
|
|
|
|
2013-04-17 18:33:54 +00:00
|
|
|
if (ident.date_begin && ident.date_end)
|
|
|
|
*time = strtoul(ident.date_begin, NULL, 10);
|
|
|
|
else
|
|
|
|
*time = 0;
|
2007-04-27 07:42:15 +00:00
|
|
|
|
2013-04-17 18:33:54 +00:00
|
|
|
if (ident.tz_begin && ident.tz_end)
|
|
|
|
strbuf_add(tz, ident.tz_begin, ident.tz_end - ident.tz_begin);
|
|
|
|
else
|
|
|
|
strbuf_addstr(tz, "(unknown)");
|
2007-04-27 07:42:15 +00:00
|
|
|
|
|
|
|
/*
|
2009-02-08 14:34:30 +00:00
|
|
|
* Now, convert both name and e-mail using mailmap
|
2007-04-27 07:42:15 +00:00
|
|
|
*/
|
2013-01-05 21:26:40 +00:00
|
|
|
map_user(&mailmap, &mailbuf, &maillen,
|
|
|
|
&namebuf, &namelen);
|
|
|
|
|
|
|
|
strbuf_addf(mail, "<%.*s>", (int)maillen, mailbuf);
|
|
|
|
strbuf_add(name, namebuf, namelen);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void commit_info_init(struct commit_info *ci)
|
|
|
|
{
|
|
|
|
|
|
|
|
strbuf_init(&ci->author, 0);
|
|
|
|
strbuf_init(&ci->author_mail, 0);
|
|
|
|
strbuf_init(&ci->author_tz, 0);
|
|
|
|
strbuf_init(&ci->committer, 0);
|
|
|
|
strbuf_init(&ci->committer_mail, 0);
|
|
|
|
strbuf_init(&ci->committer_tz, 0);
|
|
|
|
strbuf_init(&ci->summary, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void commit_info_destroy(struct commit_info *ci)
|
|
|
|
{
|
|
|
|
|
|
|
|
strbuf_release(&ci->author);
|
|
|
|
strbuf_release(&ci->author_mail);
|
|
|
|
strbuf_release(&ci->author_tz);
|
|
|
|
strbuf_release(&ci->committer);
|
|
|
|
strbuf_release(&ci->committer_mail);
|
|
|
|
strbuf_release(&ci->committer_tz);
|
|
|
|
strbuf_release(&ci->summary);
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void get_commit_info(struct commit *commit,
|
|
|
|
struct commit_info *ret,
|
|
|
|
int detailed)
|
|
|
|
{
|
|
|
|
int len;
|
2012-10-18 00:12:55 +00:00
|
|
|
const char *subject, *encoding;
|
2014-06-10 21:39:30 +00:00
|
|
|
const char *message;
|
2013-01-05 21:26:40 +00:00
|
|
|
|
|
|
|
commit_info_init(ret);
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2012-10-18 00:12:55 +00:00
|
|
|
encoding = get_log_output_encoding();
|
2013-04-18 23:08:40 +00:00
|
|
|
message = logmsg_reencode(commit, NULL, encoding);
|
2008-10-21 20:55:57 +00:00
|
|
|
get_ac_line(message, "\nauthor ",
|
2013-01-05 21:26:40 +00:00
|
|
|
&ret->author, &ret->author_mail,
|
2006-10-19 23:00:04 +00:00
|
|
|
&ret->author_time, &ret->author_tz);
|
|
|
|
|
2008-10-21 20:55:57 +00:00
|
|
|
if (!detailed) {
|
2014-06-10 21:41:39 +00:00
|
|
|
unuse_commit_buffer(commit, message);
|
2006-10-19 23:00:04 +00:00
|
|
|
return;
|
2008-10-21 20:55:57 +00:00
|
|
|
}
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2008-10-21 20:55:57 +00:00
|
|
|
get_ac_line(message, "\ncommitter ",
|
2013-01-05 21:26:40 +00:00
|
|
|
&ret->committer, &ret->committer_mail,
|
2006-10-19 23:00:04 +00:00
|
|
|
&ret->committer_time, &ret->committer_tz);
|
|
|
|
|
2010-07-22 13:18:35 +00:00
|
|
|
len = find_commit_subject(message, &subject);
|
2013-01-05 21:26:40 +00:00
|
|
|
if (len)
|
|
|
|
strbuf_add(&ret->summary, subject, len);
|
|
|
|
else
|
2015-11-10 02:22:28 +00:00
|
|
|
strbuf_addf(&ret->summary, "(%s)", oid_to_hex(&commit->object.oid));
|
2013-01-05 21:26:40 +00:00
|
|
|
|
2014-06-10 21:41:39 +00:00
|
|
|
unuse_commit_buffer(commit, message);
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
blame: output porcelain "previous" header for each file
It's possible for content currently found in one file to
have originated in two separate files, each of which may
have been modified in some single older commit. The
--porcelain output generates an incorrect "previous" header
in this case, whereas --line-porcelain gets it right. The
problem is that the porcelain output tries to omit repeated
details of commits, and treats "previous" as a property of
the commit, when it is really a property of the blamed block
of lines.
Let's look at an example. In a case like this, you might see
this output from --line-porcelain:
SOME_SHA1 1 1 1
author ...
committer ...
previous SOME_SHA1^ file_one
filename file_one
...some line content...
SOME_SHA1 2 1 1
author ...
committer ...
previous SOME_SHA1^ file_two
filename file_two
...some different content....
The "filename" fields tell us that the two lines are from
two different files. But notice that the filename also
appears in the "previous" field, which tells us where to
start a re-blame. The second content line never appeared in
file_one at all, so we would obviously need to re-blame from
file_two (or possibly even some other file, if had just been
renamed to file_two in SOME_SHA1).
So far so good. Now here's what --porcelain looks like:
SOME_SHA1 1 1 1
author ...
committer ...
previous SOME_SHA1^ file_one
filename file_one
...some line content...
SOME_SHA1 2 1 1
filename file_two
...some different content....
We've dropped the author and committer fields from the
second line, as they would just be repeats. But we can't
omit "filename", because it depends on the actual block of
blamed lines, not just the commit. This is handled by
emit_porcelain_details(), which will show the filename
either if it is the first mention of the commit _or_ if the
commit has multiple paths in it.
But we don't give "previous" the same handling. It's written
inside emit_one_suspect_detail(), which bails early if we've
already seen that commit. And so the output above is wrong;
a reader would assume that the correct place to re-blame
line two is from file_one, but that's obviously nonsense.
Let's treat "previous" the same as "filename", and show it
fresh whenever we know we are in a confusing case like this.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-01-06 04:20:51 +00:00
|
|
|
* Write out any suspect information which depends on the path. This must be
|
|
|
|
* handled separately from emit_one_suspect_detail(), because a given commit
|
|
|
|
* may have changes in multiple paths. So this needs to appear each time
|
|
|
|
* we mention a new group.
|
|
|
|
*
|
2007-01-30 01:36:22 +00:00
|
|
|
* To allow LF and other nonportable characters in pathnames,
|
|
|
|
* they are c-style quoted as needed.
|
|
|
|
*/
|
blame: output porcelain "previous" header for each file
It's possible for content currently found in one file to
have originated in two separate files, each of which may
have been modified in some single older commit. The
--porcelain output generates an incorrect "previous" header
in this case, whereas --line-porcelain gets it right. The
problem is that the porcelain output tries to omit repeated
details of commits, and treats "previous" as a property of
the commit, when it is really a property of the blamed block
of lines.
Let's look at an example. In a case like this, you might see
this output from --line-porcelain:
SOME_SHA1 1 1 1
author ...
committer ...
previous SOME_SHA1^ file_one
filename file_one
...some line content...
SOME_SHA1 2 1 1
author ...
committer ...
previous SOME_SHA1^ file_two
filename file_two
...some different content....
The "filename" fields tell us that the two lines are from
two different files. But notice that the filename also
appears in the "previous" field, which tells us where to
start a re-blame. The second content line never appeared in
file_one at all, so we would obviously need to re-blame from
file_two (or possibly even some other file, if had just been
renamed to file_two in SOME_SHA1).
So far so good. Now here's what --porcelain looks like:
SOME_SHA1 1 1 1
author ...
committer ...
previous SOME_SHA1^ file_one
filename file_one
...some line content...
SOME_SHA1 2 1 1
filename file_two
...some different content....
We've dropped the author and committer fields from the
second line, as they would just be repeats. But we can't
omit "filename", because it depends on the actual block of
blamed lines, not just the commit. This is handled by
emit_porcelain_details(), which will show the filename
either if it is the first mention of the commit _or_ if the
commit has multiple paths in it.
But we don't give "previous" the same handling. It's written
inside emit_one_suspect_detail(), which bails early if we've
already seen that commit. And so the output above is wrong;
a reader would assume that the correct place to re-blame
line two is from file_one, but that's obviously nonsense.
Let's treat "previous" the same as "filename", and show it
fresh whenever we know we are in a confusing case like this.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-01-06 04:20:51 +00:00
|
|
|
static void write_filename_info(struct origin *suspect)
|
2007-01-28 09:42:31 +00:00
|
|
|
{
|
blame: output porcelain "previous" header for each file
It's possible for content currently found in one file to
have originated in two separate files, each of which may
have been modified in some single older commit. The
--porcelain output generates an incorrect "previous" header
in this case, whereas --line-porcelain gets it right. The
problem is that the porcelain output tries to omit repeated
details of commits, and treats "previous" as a property of
the commit, when it is really a property of the blamed block
of lines.
Let's look at an example. In a case like this, you might see
this output from --line-porcelain:
SOME_SHA1 1 1 1
author ...
committer ...
previous SOME_SHA1^ file_one
filename file_one
...some line content...
SOME_SHA1 2 1 1
author ...
committer ...
previous SOME_SHA1^ file_two
filename file_two
...some different content....
The "filename" fields tell us that the two lines are from
two different files. But notice that the filename also
appears in the "previous" field, which tells us where to
start a re-blame. The second content line never appeared in
file_one at all, so we would obviously need to re-blame from
file_two (or possibly even some other file, if had just been
renamed to file_two in SOME_SHA1).
So far so good. Now here's what --porcelain looks like:
SOME_SHA1 1 1 1
author ...
committer ...
previous SOME_SHA1^ file_one
filename file_one
...some line content...
SOME_SHA1 2 1 1
filename file_two
...some different content....
We've dropped the author and committer fields from the
second line, as they would just be repeats. But we can't
omit "filename", because it depends on the actual block of
blamed lines, not just the commit. This is handled by
emit_porcelain_details(), which will show the filename
either if it is the first mention of the commit _or_ if the
commit has multiple paths in it.
But we don't give "previous" the same handling. It's written
inside emit_one_suspect_detail(), which bails early if we've
already seen that commit. And so the output above is wrong;
a reader would assume that the correct place to re-blame
line two is from file_one, but that's obviously nonsense.
Let's treat "previous" the same as "filename", and show it
fresh whenever we know we are in a confusing case like this.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-01-06 04:20:51 +00:00
|
|
|
if (suspect->previous) {
|
|
|
|
struct origin *prev = suspect->previous;
|
|
|
|
printf("previous %s ", oid_to_hex(&prev->commit->object.oid));
|
|
|
|
write_name_quoted(prev->path, stdout, '\n');
|
|
|
|
}
|
2007-01-28 09:42:31 +00:00
|
|
|
printf("filename ");
|
blame: output porcelain "previous" header for each file
It's possible for content currently found in one file to
have originated in two separate files, each of which may
have been modified in some single older commit. The
--porcelain output generates an incorrect "previous" header
in this case, whereas --line-porcelain gets it right. The
problem is that the porcelain output tries to omit repeated
details of commits, and treats "previous" as a property of
the commit, when it is really a property of the blamed block
of lines.
Let's look at an example. In a case like this, you might see
this output from --line-porcelain:
SOME_SHA1 1 1 1
author ...
committer ...
previous SOME_SHA1^ file_one
filename file_one
...some line content...
SOME_SHA1 2 1 1
author ...
committer ...
previous SOME_SHA1^ file_two
filename file_two
...some different content....
The "filename" fields tell us that the two lines are from
two different files. But notice that the filename also
appears in the "previous" field, which tells us where to
start a re-blame. The second content line never appeared in
file_one at all, so we would obviously need to re-blame from
file_two (or possibly even some other file, if had just been
renamed to file_two in SOME_SHA1).
So far so good. Now here's what --porcelain looks like:
SOME_SHA1 1 1 1
author ...
committer ...
previous SOME_SHA1^ file_one
filename file_one
...some line content...
SOME_SHA1 2 1 1
filename file_two
...some different content....
We've dropped the author and committer fields from the
second line, as they would just be repeats. But we can't
omit "filename", because it depends on the actual block of
blamed lines, not just the commit. This is handled by
emit_porcelain_details(), which will show the filename
either if it is the first mention of the commit _or_ if the
commit has multiple paths in it.
But we don't give "previous" the same handling. It's written
inside emit_one_suspect_detail(), which bails early if we've
already seen that commit. And so the output above is wrong;
a reader would assume that the correct place to re-blame
line two is from file_one, but that's obviously nonsense.
Let's treat "previous" the same as "filename", and show it
fresh whenever we know we are in a confusing case like this.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-01-06 04:20:51 +00:00
|
|
|
write_name_quoted(suspect->path, stdout, '\n');
|
2007-01-28 09:42:31 +00:00
|
|
|
}
|
|
|
|
|
2008-06-05 05:48:42 +00:00
|
|
|
/*
|
|
|
|
* Porcelain/Incremental format wants to show a lot of details per
|
|
|
|
* commit. Instead of repeating this every line, emit it only once,
|
2011-05-09 13:34:02 +00:00
|
|
|
* the first time each commit appears in the output (unless the
|
|
|
|
* user has specifically asked for us to repeat).
|
2008-06-05 05:48:42 +00:00
|
|
|
*/
|
2011-05-09 13:34:02 +00:00
|
|
|
static int emit_one_suspect_detail(struct origin *suspect, int repeat)
|
2008-06-05 05:48:42 +00:00
|
|
|
{
|
|
|
|
struct commit_info ci;
|
|
|
|
|
2011-05-09 13:34:02 +00:00
|
|
|
if (!repeat && (suspect->commit->object.flags & METAINFO_SHOWN))
|
2008-06-05 05:48:42 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
suspect->commit->object.flags |= METAINFO_SHOWN;
|
|
|
|
get_commit_info(suspect->commit, &ci, 1);
|
2013-01-05 21:26:40 +00:00
|
|
|
printf("author %s\n", ci.author.buf);
|
|
|
|
printf("author-mail %s\n", ci.author_mail.buf);
|
2017-04-21 10:45:48 +00:00
|
|
|
printf("author-time %"PRItime"\n", ci.author_time);
|
2013-01-05 21:26:40 +00:00
|
|
|
printf("author-tz %s\n", ci.author_tz.buf);
|
|
|
|
printf("committer %s\n", ci.committer.buf);
|
|
|
|
printf("committer-mail %s\n", ci.committer_mail.buf);
|
2017-04-21 10:45:48 +00:00
|
|
|
printf("committer-time %"PRItime"\n", ci.committer_time);
|
2013-01-05 21:26:40 +00:00
|
|
|
printf("committer-tz %s\n", ci.committer_tz.buf);
|
|
|
|
printf("summary %s\n", ci.summary.buf);
|
2008-06-05 05:48:42 +00:00
|
|
|
if (suspect->commit->object.flags & UNINTERESTING)
|
|
|
|
printf("boundary\n");
|
2013-01-05 21:26:40 +00:00
|
|
|
|
|
|
|
commit_info_destroy(&ci);
|
|
|
|
|
2008-06-05 05:48:42 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
2014-04-25 23:56:49 +00:00
|
|
|
* The blame_entry is found to be guilty for the range.
|
|
|
|
* Show it in incremental output.
|
2007-01-30 01:36:22 +00:00
|
|
|
*/
|
2015-12-13 00:51:03 +00:00
|
|
|
static void found_guilty_entry(struct blame_entry *ent,
|
|
|
|
struct progress_info *pi)
|
2007-01-28 09:34:06 +00:00
|
|
|
{
|
|
|
|
if (incremental) {
|
|
|
|
struct origin *suspect = ent->suspect;
|
|
|
|
|
|
|
|
printf("%s %d %d %d\n",
|
2015-11-10 02:22:28 +00:00
|
|
|
oid_to_hex(&suspect->commit->object.oid),
|
2007-01-28 09:34:06 +00:00
|
|
|
ent->s_lno + 1, ent->lno + 1, ent->num_lines);
|
2011-05-09 13:34:02 +00:00
|
|
|
emit_one_suspect_detail(suspect, 0);
|
blame: output porcelain "previous" header for each file
It's possible for content currently found in one file to
have originated in two separate files, each of which may
have been modified in some single older commit. The
--porcelain output generates an incorrect "previous" header
in this case, whereas --line-porcelain gets it right. The
problem is that the porcelain output tries to omit repeated
details of commits, and treats "previous" as a property of
the commit, when it is really a property of the blamed block
of lines.
Let's look at an example. In a case like this, you might see
this output from --line-porcelain:
SOME_SHA1 1 1 1
author ...
committer ...
previous SOME_SHA1^ file_one
filename file_one
...some line content...
SOME_SHA1 2 1 1
author ...
committer ...
previous SOME_SHA1^ file_two
filename file_two
...some different content....
The "filename" fields tell us that the two lines are from
two different files. But notice that the filename also
appears in the "previous" field, which tells us where to
start a re-blame. The second content line never appeared in
file_one at all, so we would obviously need to re-blame from
file_two (or possibly even some other file, if had just been
renamed to file_two in SOME_SHA1).
So far so good. Now here's what --porcelain looks like:
SOME_SHA1 1 1 1
author ...
committer ...
previous SOME_SHA1^ file_one
filename file_one
...some line content...
SOME_SHA1 2 1 1
filename file_two
...some different content....
We've dropped the author and committer fields from the
second line, as they would just be repeats. But we can't
omit "filename", because it depends on the actual block of
blamed lines, not just the commit. This is handled by
emit_porcelain_details(), which will show the filename
either if it is the first mention of the commit _or_ if the
commit has multiple paths in it.
But we don't give "previous" the same handling. It's written
inside emit_one_suspect_detail(), which bails early if we've
already seen that commit. And so the output above is wrong;
a reader would assume that the correct place to re-blame
line two is from file_one, but that's obviously nonsense.
Let's treat "previous" the same as "filename", and show it
fresh whenever we know we are in a confusing case like this.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-01-06 04:20:51 +00:00
|
|
|
write_filename_info(suspect);
|
2007-06-29 17:40:46 +00:00
|
|
|
maybe_flush_or_die(stdout, "stdout");
|
2007-01-28 09:34:06 +00:00
|
|
|
}
|
2015-12-13 00:51:03 +00:00
|
|
|
pi->blamed_lines += ent->num_lines;
|
|
|
|
display_progress(pi->progress, pi->blamed_lines);
|
2007-01-28 09:34:06 +00:00
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
2014-04-25 23:56:49 +00:00
|
|
|
* The main loop -- while we have blobs with lines whose true origin
|
|
|
|
* is still unknown, pick one blob, and allow its lines to pass blames
|
|
|
|
* to its parents. */
|
2008-04-03 05:17:53 +00:00
|
|
|
static void assign_blame(struct scoreboard *sb, int opt)
|
2007-01-28 09:34:06 +00:00
|
|
|
{
|
2008-04-03 05:17:53 +00:00
|
|
|
struct rev_info *revs = sb->revs;
|
2014-04-25 23:56:49 +00:00
|
|
|
struct commit *commit = prio_queue_get(&sb->commits);
|
2015-12-13 00:51:03 +00:00
|
|
|
struct progress_info pi = { NULL, 0 };
|
|
|
|
|
|
|
|
if (show_progress)
|
|
|
|
pi.progress = start_progress_delay(_("Blaming lines"),
|
|
|
|
sb->num_lines, 50, 1);
|
2008-04-03 05:17:53 +00:00
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
while (commit) {
|
2007-01-28 09:34:06 +00:00
|
|
|
struct blame_entry *ent;
|
2014-04-25 23:56:49 +00:00
|
|
|
struct origin *suspect = commit->util;
|
2007-01-28 09:34:06 +00:00
|
|
|
|
|
|
|
/* find one suspect to break down */
|
2014-04-25 23:56:49 +00:00
|
|
|
while (suspect && !suspect->suspects)
|
|
|
|
suspect = suspect->next;
|
|
|
|
|
|
|
|
if (!suspect) {
|
|
|
|
commit = prio_queue_get(&sb->commits);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(commit == suspect->commit);
|
2007-01-28 09:34:06 +00:00
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* We will use this suspect later in the loop,
|
|
|
|
* so hold onto it in the meantime.
|
|
|
|
*/
|
2007-01-28 09:34:06 +00:00
|
|
|
origin_incref(suspect);
|
2013-10-24 08:53:01 +00:00
|
|
|
parse_commit(commit);
|
2008-04-03 05:17:53 +00:00
|
|
|
if (reverse ||
|
|
|
|
(!(commit->object.flags & UNINTERESTING) &&
|
|
|
|
!(revs->max_age != -1 && commit->date < revs->max_age)))
|
2007-01-28 09:34:06 +00:00
|
|
|
pass_blame(sb, suspect, opt);
|
|
|
|
else {
|
|
|
|
commit->object.flags |= UNINTERESTING;
|
|
|
|
if (commit->object.parsed)
|
|
|
|
mark_parents_uninteresting(commit);
|
|
|
|
}
|
|
|
|
/* treat root commit as boundary */
|
|
|
|
if (!commit->parents && !show_root)
|
|
|
|
commit->object.flags |= UNINTERESTING;
|
|
|
|
|
|
|
|
/* Take responsibility for the remaining entries */
|
2014-04-25 23:56:49 +00:00
|
|
|
ent = suspect->suspects;
|
|
|
|
if (ent) {
|
|
|
|
suspect->guilty = 1;
|
|
|
|
for (;;) {
|
|
|
|
struct blame_entry *next = ent->next;
|
2015-12-13 00:51:03 +00:00
|
|
|
found_guilty_entry(ent, &pi);
|
2014-04-25 23:56:49 +00:00
|
|
|
if (next) {
|
|
|
|
ent = next;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ent->next = sb->ent;
|
|
|
|
sb->ent = suspect->suspects;
|
|
|
|
suspect->suspects = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2007-01-28 09:34:06 +00:00
|
|
|
origin_decref(suspect);
|
|
|
|
|
|
|
|
if (DEBUG) /* sanity */
|
|
|
|
sanity_check_refcnt(sb);
|
|
|
|
}
|
2015-12-13 00:51:03 +00:00
|
|
|
|
|
|
|
stop_progress(&pi.progress);
|
2007-01-28 09:34:06 +00:00
|
|
|
}
|
|
|
|
|
2017-04-26 19:29:31 +00:00
|
|
|
static const char *format_time(timestamp_t time, const char *tz_str,
|
2007-01-28 09:34:06 +00:00
|
|
|
int show_raw_time)
|
|
|
|
{
|
blame: fix broken time_buf paddings in relative timestamp
Command `git blame --date relative` aligns the date field with a
fixed-width (defined by blame_date_width), and if time_str is shorter
than that, it adds spaces for padding. But there are two bugs in the
following codes:
time_len = strlen(time_str);
...
memset(time_buf + time_len, ' ', blame_date_width - time_len);
1. The type of blame_date_width is size_t, which is unsigned. If
time_len is greater than blame_date_width, the result of
"blame_date_width - time_len" will never be a negative number, but a
really big positive number, and will cause memory overwrite.
This bug can be triggered if either l10n message for function
show_date_relative() in date.c is longer than 30 characters, then
`git blame --date relative` may exit abnormally.
2. When show blame information with relative time, the UTF-8 characters
in time_str will break the alignment of columns after the date field.
This is because the time_buf padding with spaces should have a
constant display width, not a fixed strlen size. So we should call
utf8_strwidth() instead of strlen() for width calibration.
Helped-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-04-21 06:02:03 +00:00
|
|
|
static struct strbuf time_buf = STRBUF_INIT;
|
2007-01-28 09:34:06 +00:00
|
|
|
|
blame: fix broken time_buf paddings in relative timestamp
Command `git blame --date relative` aligns the date field with a
fixed-width (defined by blame_date_width), and if time_str is shorter
than that, it adds spaces for padding. But there are two bugs in the
following codes:
time_len = strlen(time_str);
...
memset(time_buf + time_len, ' ', blame_date_width - time_len);
1. The type of blame_date_width is size_t, which is unsigned. If
time_len is greater than blame_date_width, the result of
"blame_date_width - time_len" will never be a negative number, but a
really big positive number, and will cause memory overwrite.
This bug can be triggered if either l10n message for function
show_date_relative() in date.c is longer than 30 characters, then
`git blame --date relative` may exit abnormally.
2. When show blame information with relative time, the UTF-8 characters
in time_str will break the alignment of columns after the date field.
This is because the time_buf padding with spaces should have a
constant display width, not a fixed strlen size. So we should call
utf8_strwidth() instead of strlen() for width calibration.
Helped-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-04-21 06:02:03 +00:00
|
|
|
strbuf_reset(&time_buf);
|
2007-01-28 09:34:06 +00:00
|
|
|
if (show_raw_time) {
|
2017-04-21 10:45:48 +00:00
|
|
|
strbuf_addf(&time_buf, "%"PRItime" %s", time, tz_str);
|
2007-01-28 09:34:06 +00:00
|
|
|
}
|
2009-02-20 22:51:11 +00:00
|
|
|
else {
|
2014-01-29 13:33:15 +00:00
|
|
|
const char *time_str;
|
blame: fix broken time_buf paddings in relative timestamp
Command `git blame --date relative` aligns the date field with a
fixed-width (defined by blame_date_width), and if time_str is shorter
than that, it adds spaces for padding. But there are two bugs in the
following codes:
time_len = strlen(time_str);
...
memset(time_buf + time_len, ' ', blame_date_width - time_len);
1. The type of blame_date_width is size_t, which is unsigned. If
time_len is greater than blame_date_width, the result of
"blame_date_width - time_len" will never be a negative number, but a
really big positive number, and will cause memory overwrite.
This bug can be triggered if either l10n message for function
show_date_relative() in date.c is longer than 30 characters, then
`git blame --date relative` may exit abnormally.
2. When show blame information with relative time, the UTF-8 characters
in time_str will break the alignment of columns after the date field.
This is because the time_buf padding with spaces should have a
constant display width, not a fixed strlen size. So we should call
utf8_strwidth() instead of strlen() for width calibration.
Helped-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-04-21 06:02:03 +00:00
|
|
|
size_t time_width;
|
2014-01-29 13:33:15 +00:00
|
|
|
int tz;
|
2009-02-20 22:51:11 +00:00
|
|
|
tz = atoi(tz_str);
|
convert "enum date_mode" into a struct
In preparation for adding date modes that may carry extra
information beyond the mode itself, this patch converts the
date_mode enum into a struct.
Most of the conversion is fairly straightforward; we pass
the struct as a pointer and dereference the type field where
necessary. Locations that declare a date_mode can use a "{}"
constructor. However, the tricky case is where we use the
enum labels as constants, like:
show_date(t, tz, DATE_NORMAL);
Ideally we could say:
show_date(t, tz, &{ DATE_NORMAL });
but of course C does not allow that. Likewise, we cannot
cast the constant to a struct, because we need to pass an
actual address. Our options are basically:
1. Manually add a "struct date_mode d = { DATE_NORMAL }"
definition to each caller, and pass "&d". This makes
the callers uglier, because they sometimes do not even
have their own scope (e.g., they are inside a switch
statement).
2. Provide a pre-made global "date_normal" struct that can
be passed by address. We'd also need "date_rfc2822",
"date_iso8601", and so forth. But at least the ugliness
is defined in one place.
3. Provide a wrapper that generates the correct struct on
the fly. The big downside is that we end up pointing to
a single global, which makes our wrapper non-reentrant.
But show_date is already not reentrant, so it does not
matter.
This patch implements 3, along with a minor macro to keep
the size of the callers sane.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-25 16:55:02 +00:00
|
|
|
time_str = show_date(time, tz, &blame_date_mode);
|
blame: fix broken time_buf paddings in relative timestamp
Command `git blame --date relative` aligns the date field with a
fixed-width (defined by blame_date_width), and if time_str is shorter
than that, it adds spaces for padding. But there are two bugs in the
following codes:
time_len = strlen(time_str);
...
memset(time_buf + time_len, ' ', blame_date_width - time_len);
1. The type of blame_date_width is size_t, which is unsigned. If
time_len is greater than blame_date_width, the result of
"blame_date_width - time_len" will never be a negative number, but a
really big positive number, and will cause memory overwrite.
This bug can be triggered if either l10n message for function
show_date_relative() in date.c is longer than 30 characters, then
`git blame --date relative` may exit abnormally.
2. When show blame information with relative time, the UTF-8 characters
in time_str will break the alignment of columns after the date field.
This is because the time_buf padding with spaces should have a
constant display width, not a fixed strlen size. So we should call
utf8_strwidth() instead of strlen() for width calibration.
Helped-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-04-21 06:02:03 +00:00
|
|
|
strbuf_addstr(&time_buf, time_str);
|
|
|
|
/*
|
|
|
|
* Add space paddings to time_buf to display a fixed width
|
|
|
|
* string, and use time_width for display width calibration.
|
|
|
|
*/
|
|
|
|
for (time_width = utf8_strwidth(time_str);
|
|
|
|
time_width < blame_date_width;
|
|
|
|
time_width++)
|
|
|
|
strbuf_addch(&time_buf, ' ');
|
2009-02-20 22:51:11 +00:00
|
|
|
}
|
blame: fix broken time_buf paddings in relative timestamp
Command `git blame --date relative` aligns the date field with a
fixed-width (defined by blame_date_width), and if time_str is shorter
than that, it adds spaces for padding. But there are two bugs in the
following codes:
time_len = strlen(time_str);
...
memset(time_buf + time_len, ' ', blame_date_width - time_len);
1. The type of blame_date_width is size_t, which is unsigned. If
time_len is greater than blame_date_width, the result of
"blame_date_width - time_len" will never be a negative number, but a
really big positive number, and will cause memory overwrite.
This bug can be triggered if either l10n message for function
show_date_relative() in date.c is longer than 30 characters, then
`git blame --date relative` may exit abnormally.
2. When show blame information with relative time, the UTF-8 characters
in time_str will break the alignment of columns after the date field.
This is because the time_buf padding with spaces should have a
constant display width, not a fixed strlen size. So we should call
utf8_strwidth() instead of strlen() for width calibration.
Helped-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-04-21 06:02:03 +00:00
|
|
|
return time_buf.buf;
|
2007-01-28 09:34:06 +00:00
|
|
|
}
|
|
|
|
|
2006-10-19 23:00:04 +00:00
|
|
|
#define OUTPUT_ANNOTATE_COMPAT 001
|
|
|
|
#define OUTPUT_LONG_OBJECT_NAME 002
|
|
|
|
#define OUTPUT_RAW_TIMESTAMP 004
|
|
|
|
#define OUTPUT_PORCELAIN 010
|
|
|
|
#define OUTPUT_SHOW_NAME 020
|
|
|
|
#define OUTPUT_SHOW_NUMBER 040
|
2006-10-20 21:51:12 +00:00
|
|
|
#define OUTPUT_SHOW_SCORE 0100
|
2007-04-12 22:50:45 +00:00
|
|
|
#define OUTPUT_NO_AUTHOR 0200
|
2010-10-16 06:57:51 +00:00
|
|
|
#define OUTPUT_SHOW_EMAIL 0400
|
2011-05-09 13:34:42 +00:00
|
|
|
#define OUTPUT_LINE_PORCELAIN 01000
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2011-05-09 13:34:02 +00:00
|
|
|
static void emit_porcelain_details(struct origin *suspect, int repeat)
|
|
|
|
{
|
|
|
|
if (emit_one_suspect_detail(suspect, repeat) ||
|
|
|
|
(suspect->commit->object.flags & MORE_THAN_ONE_PATH))
|
blame: output porcelain "previous" header for each file
It's possible for content currently found in one file to
have originated in two separate files, each of which may
have been modified in some single older commit. The
--porcelain output generates an incorrect "previous" header
in this case, whereas --line-porcelain gets it right. The
problem is that the porcelain output tries to omit repeated
details of commits, and treats "previous" as a property of
the commit, when it is really a property of the blamed block
of lines.
Let's look at an example. In a case like this, you might see
this output from --line-porcelain:
SOME_SHA1 1 1 1
author ...
committer ...
previous SOME_SHA1^ file_one
filename file_one
...some line content...
SOME_SHA1 2 1 1
author ...
committer ...
previous SOME_SHA1^ file_two
filename file_two
...some different content....
The "filename" fields tell us that the two lines are from
two different files. But notice that the filename also
appears in the "previous" field, which tells us where to
start a re-blame. The second content line never appeared in
file_one at all, so we would obviously need to re-blame from
file_two (or possibly even some other file, if had just been
renamed to file_two in SOME_SHA1).
So far so good. Now here's what --porcelain looks like:
SOME_SHA1 1 1 1
author ...
committer ...
previous SOME_SHA1^ file_one
filename file_one
...some line content...
SOME_SHA1 2 1 1
filename file_two
...some different content....
We've dropped the author and committer fields from the
second line, as they would just be repeats. But we can't
omit "filename", because it depends on the actual block of
blamed lines, not just the commit. This is handled by
emit_porcelain_details(), which will show the filename
either if it is the first mention of the commit _or_ if the
commit has multiple paths in it.
But we don't give "previous" the same handling. It's written
inside emit_one_suspect_detail(), which bails early if we've
already seen that commit. And so the output above is wrong;
a reader would assume that the correct place to re-blame
line two is from file_one, but that's obviously nonsense.
Let's treat "previous" the same as "filename", and show it
fresh whenever we know we are in a confusing case like this.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-01-06 04:20:51 +00:00
|
|
|
write_filename_info(suspect);
|
2011-05-09 13:34:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
|
|
|
|
int opt)
|
2006-10-19 23:00:04 +00:00
|
|
|
{
|
2011-05-09 13:34:42 +00:00
|
|
|
int repeat = opt & OUTPUT_LINE_PORCELAIN;
|
2006-10-19 23:00:04 +00:00
|
|
|
int cnt;
|
|
|
|
const char *cp;
|
|
|
|
struct origin *suspect = ent->suspect;
|
2017-03-26 16:01:24 +00:00
|
|
|
char hex[GIT_MAX_HEXSZ + 1];
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2017-01-28 22:03:03 +00:00
|
|
|
oid_to_hex_r(hex, &suspect->commit->object.oid);
|
2014-04-25 23:56:49 +00:00
|
|
|
printf("%s %d %d %d\n",
|
2006-10-19 23:00:04 +00:00
|
|
|
hex,
|
|
|
|
ent->s_lno + 1,
|
|
|
|
ent->lno + 1,
|
|
|
|
ent->num_lines);
|
2011-05-09 13:34:42 +00:00
|
|
|
emit_porcelain_details(suspect, repeat);
|
2006-10-19 23:00:04 +00:00
|
|
|
|
|
|
|
cp = nth_line(sb, ent->lno);
|
|
|
|
for (cnt = 0; cnt < ent->num_lines; cnt++) {
|
|
|
|
char ch;
|
2011-05-09 13:34:42 +00:00
|
|
|
if (cnt) {
|
2006-10-19 23:00:04 +00:00
|
|
|
printf("%s %d %d\n", hex,
|
|
|
|
ent->s_lno + 1 + cnt,
|
|
|
|
ent->lno + 1 + cnt);
|
2011-05-09 13:34:42 +00:00
|
|
|
if (repeat)
|
|
|
|
emit_porcelain_details(suspect, 1);
|
|
|
|
}
|
2006-10-19 23:00:04 +00:00
|
|
|
putchar('\t');
|
|
|
|
do {
|
|
|
|
ch = *cp++;
|
|
|
|
putchar(ch);
|
|
|
|
} while (ch != '\n' &&
|
|
|
|
cp < sb->final_buf + sb->final_buf_size);
|
|
|
|
}
|
2009-10-20 03:06:28 +00:00
|
|
|
|
|
|
|
if (sb->final_buf_size && cp[-1] != '\n')
|
|
|
|
putchar('\n');
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
|
|
|
|
{
|
|
|
|
int cnt;
|
|
|
|
const char *cp;
|
|
|
|
struct origin *suspect = ent->suspect;
|
|
|
|
struct commit_info ci;
|
2017-03-26 16:01:24 +00:00
|
|
|
char hex[GIT_MAX_HEXSZ + 1];
|
2006-10-19 23:00:04 +00:00
|
|
|
int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
|
|
|
|
|
|
|
|
get_commit_info(suspect->commit, &ci, 1);
|
2017-01-28 22:03:03 +00:00
|
|
|
oid_to_hex_r(hex, &suspect->commit->object.oid);
|
2006-10-19 23:00:04 +00:00
|
|
|
|
|
|
|
cp = nth_line(sb, ent->lno);
|
|
|
|
for (cnt = 0; cnt < ent->num_lines; cnt++) {
|
|
|
|
char ch;
|
2016-09-05 20:08:03 +00:00
|
|
|
int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev;
|
2006-12-02 04:45:45 +00:00
|
|
|
|
|
|
|
if (suspect->commit->object.flags & UNINTERESTING) {
|
2007-02-06 09:52:04 +00:00
|
|
|
if (blank_boundary)
|
|
|
|
memset(hex, ' ', length);
|
2008-09-05 07:57:35 +00:00
|
|
|
else if (!(opt & OUTPUT_ANNOTATE_COMPAT)) {
|
2006-12-18 22:04:38 +00:00
|
|
|
length--;
|
|
|
|
putchar('^');
|
|
|
|
}
|
2006-12-02 04:45:45 +00:00
|
|
|
}
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2006-12-02 04:45:45 +00:00
|
|
|
printf("%.*s", length, hex);
|
2010-10-16 06:57:51 +00:00
|
|
|
if (opt & OUTPUT_ANNOTATE_COMPAT) {
|
|
|
|
const char *name;
|
|
|
|
if (opt & OUTPUT_SHOW_EMAIL)
|
2013-01-05 21:26:40 +00:00
|
|
|
name = ci.author_mail.buf;
|
2010-10-16 06:57:51 +00:00
|
|
|
else
|
2013-01-05 21:26:40 +00:00
|
|
|
name = ci.author.buf;
|
2010-10-16 06:57:51 +00:00
|
|
|
printf("\t(%10s\t%10s\t%d)", name,
|
2013-01-05 21:26:40 +00:00
|
|
|
format_time(ci.author_time, ci.author_tz.buf,
|
2006-10-19 23:00:04 +00:00
|
|
|
show_raw_time),
|
|
|
|
ent->lno + 1 + cnt);
|
2010-10-16 06:57:51 +00:00
|
|
|
} else {
|
2006-10-20 21:51:12 +00:00
|
|
|
if (opt & OUTPUT_SHOW_SCORE)
|
2006-10-29 11:07:40 +00:00
|
|
|
printf(" %*d %02d",
|
|
|
|
max_score_digits, ent->score,
|
|
|
|
ent->suspect->refcnt);
|
2006-10-19 23:00:04 +00:00
|
|
|
if (opt & OUTPUT_SHOW_NAME)
|
|
|
|
printf(" %-*.*s", longest_file, longest_file,
|
|
|
|
suspect->path);
|
|
|
|
if (opt & OUTPUT_SHOW_NUMBER)
|
|
|
|
printf(" %*d", max_orig_digits,
|
|
|
|
ent->s_lno + 1 + cnt);
|
2007-04-12 22:50:45 +00:00
|
|
|
|
2009-01-30 09:41:29 +00:00
|
|
|
if (!(opt & OUTPUT_NO_AUTHOR)) {
|
2010-10-16 06:57:51 +00:00
|
|
|
const char *name;
|
|
|
|
int pad;
|
|
|
|
if (opt & OUTPUT_SHOW_EMAIL)
|
2013-01-05 21:26:40 +00:00
|
|
|
name = ci.author_mail.buf;
|
2010-10-16 06:57:51 +00:00
|
|
|
else
|
2013-01-05 21:26:40 +00:00
|
|
|
name = ci.author.buf;
|
2010-10-16 06:57:51 +00:00
|
|
|
pad = longest_author - utf8_strwidth(name);
|
2009-01-30 09:41:29 +00:00
|
|
|
printf(" (%s%*s %10s",
|
2010-10-16 06:57:51 +00:00
|
|
|
name, pad, "",
|
2007-04-12 22:50:45 +00:00
|
|
|
format_time(ci.author_time,
|
2013-01-05 21:26:40 +00:00
|
|
|
ci.author_tz.buf,
|
2007-04-12 22:50:45 +00:00
|
|
|
show_raw_time));
|
2009-01-30 09:41:29 +00:00
|
|
|
}
|
2007-04-12 22:50:45 +00:00
|
|
|
printf(" %*d) ",
|
2006-10-19 23:00:04 +00:00
|
|
|
max_digits, ent->lno + 1 + cnt);
|
|
|
|
}
|
|
|
|
do {
|
|
|
|
ch = *cp++;
|
|
|
|
putchar(ch);
|
|
|
|
} while (ch != '\n' &&
|
|
|
|
cp < sb->final_buf + sb->final_buf_size);
|
|
|
|
}
|
2009-10-20 03:06:28 +00:00
|
|
|
|
|
|
|
if (sb->final_buf_size && cp[-1] != '\n')
|
|
|
|
putchar('\n');
|
2013-01-05 21:26:40 +00:00
|
|
|
|
|
|
|
commit_info_destroy(&ci);
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void output(struct scoreboard *sb, int option)
|
|
|
|
{
|
|
|
|
struct blame_entry *ent;
|
|
|
|
|
|
|
|
if (option & OUTPUT_PORCELAIN) {
|
|
|
|
for (ent = sb->ent; ent; ent = ent->next) {
|
2014-04-25 23:56:49 +00:00
|
|
|
int count = 0;
|
|
|
|
struct origin *suspect;
|
|
|
|
struct commit *commit = ent->suspect->commit;
|
2006-10-19 23:00:04 +00:00
|
|
|
if (commit->object.flags & MORE_THAN_ONE_PATH)
|
|
|
|
continue;
|
2014-04-25 23:56:49 +00:00
|
|
|
for (suspect = commit->util; suspect; suspect = suspect->next) {
|
|
|
|
if (suspect->guilty && count++) {
|
|
|
|
commit->object.flags |= MORE_THAN_ONE_PATH;
|
|
|
|
break;
|
|
|
|
}
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ent = sb->ent; ent; ent = ent->next) {
|
|
|
|
if (option & OUTPUT_PORCELAIN)
|
2011-05-09 13:34:02 +00:00
|
|
|
emit_porcelain(sb, ent, option);
|
2006-10-20 21:51:12 +00:00
|
|
|
else {
|
2006-10-19 23:00:04 +00:00
|
|
|
emit_other(sb, ent, option);
|
2006-10-20 21:51:12 +00:00
|
|
|
}
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-13 19:53:03 +00:00
|
|
|
static const char *get_next_line(const char *start, const char *end)
|
|
|
|
{
|
|
|
|
const char *nl = memchr(start, '\n', end - start);
|
2014-06-13 19:54:59 +00:00
|
|
|
return nl ? nl + 1 : end;
|
2014-06-13 19:53:03 +00:00
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* To allow quick access to the contents of nth line in the
|
|
|
|
* final image, prepare an index in the scoreboard.
|
|
|
|
*/
|
2006-10-19 23:00:04 +00:00
|
|
|
static int prepare_lines(struct scoreboard *sb)
|
|
|
|
{
|
|
|
|
const char *buf = sb->final_buf;
|
|
|
|
unsigned long len = sb->final_buf_size;
|
2014-02-12 14:27:24 +00:00
|
|
|
const char *end = buf + len;
|
|
|
|
const char *p;
|
|
|
|
int *lineno;
|
2014-06-13 19:54:59 +00:00
|
|
|
int num = 0;
|
2014-02-12 14:27:24 +00:00
|
|
|
|
2014-06-13 19:54:59 +00:00
|
|
|
for (p = buf; p < end; p = get_next_line(p, end))
|
2014-06-13 19:53:03 +00:00
|
|
|
num++;
|
2014-02-12 14:27:24 +00:00
|
|
|
|
2016-02-22 22:44:25 +00:00
|
|
|
ALLOC_ARRAY(sb->lineno, num + 1);
|
|
|
|
lineno = sb->lineno;
|
2014-02-12 14:27:24 +00:00
|
|
|
|
2014-06-13 19:54:59 +00:00
|
|
|
for (p = buf; p < end; p = get_next_line(p, end))
|
2014-06-13 19:53:03 +00:00
|
|
|
*lineno++ = p - buf;
|
2014-02-12 14:27:24 +00:00
|
|
|
|
2014-06-13 19:54:59 +00:00
|
|
|
*lineno = len;
|
2014-02-12 14:27:24 +00:00
|
|
|
|
2014-06-13 19:54:59 +00:00
|
|
|
sb->num_lines = num;
|
2006-10-19 23:00:04 +00:00
|
|
|
return sb->num_lines;
|
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* Add phony grafts for use with -S; this is primarily to
|
2008-08-30 11:12:53 +00:00
|
|
|
* support git's cvsserver that wants to give a linear history
|
2007-01-30 01:36:22 +00:00
|
|
|
* to its clients.
|
|
|
|
*/
|
2006-10-19 23:00:04 +00:00
|
|
|
static int read_ancestry(const char *graft_file)
|
|
|
|
{
|
|
|
|
FILE *fp = fopen(graft_file, "r");
|
2013-12-27 20:49:57 +00:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
2006-10-19 23:00:04 +00:00
|
|
|
if (!fp)
|
|
|
|
return -1;
|
2013-12-27 20:49:57 +00:00
|
|
|
while (!strbuf_getwholeline(&buf, fp, '\n')) {
|
2006-10-19 23:00:04 +00:00
|
|
|
/* The format is just "Commit Parent1 Parent2 ...\n" */
|
2013-12-27 20:49:57 +00:00
|
|
|
struct commit_graft *graft = read_graft_line(buf.buf, buf.len);
|
2006-11-10 21:39:01 +00:00
|
|
|
if (graft)
|
|
|
|
register_commit_graft(graft, 0);
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
fclose(fp);
|
2013-12-27 20:49:57 +00:00
|
|
|
strbuf_release(&buf);
|
2006-10-19 23:00:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-02 07:54:00 +00:00
|
|
|
static int update_auto_abbrev(int auto_abbrev, struct origin *suspect)
|
|
|
|
{
|
2015-11-10 02:22:29 +00:00
|
|
|
const char *uniq = find_unique_abbrev(suspect->commit->object.oid.hash,
|
2012-07-02 07:54:00 +00:00
|
|
|
auto_abbrev);
|
|
|
|
int len = strlen(uniq);
|
|
|
|
if (auto_abbrev < len)
|
|
|
|
return len;
|
|
|
|
return auto_abbrev;
|
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* How many columns do we need to show line numbers, authors,
|
|
|
|
* and filenames?
|
|
|
|
*/
|
2006-10-19 23:00:04 +00:00
|
|
|
static void find_alignment(struct scoreboard *sb, int *option)
|
|
|
|
{
|
|
|
|
int longest_src_lines = 0;
|
|
|
|
int longest_dst_lines = 0;
|
2006-10-20 21:51:12 +00:00
|
|
|
unsigned largest_score = 0;
|
2006-10-19 23:00:04 +00:00
|
|
|
struct blame_entry *e;
|
2012-07-02 07:54:00 +00:00
|
|
|
int compute_auto_abbrev = (abbrev < 0);
|
2016-09-28 21:56:00 +00:00
|
|
|
int auto_abbrev = DEFAULT_ABBREV;
|
2006-10-19 23:00:04 +00:00
|
|
|
|
|
|
|
for (e = sb->ent; e; e = e->next) {
|
|
|
|
struct origin *suspect = e->suspect;
|
|
|
|
int num;
|
|
|
|
|
2012-07-02 07:54:00 +00:00
|
|
|
if (compute_auto_abbrev)
|
|
|
|
auto_abbrev = update_auto_abbrev(auto_abbrev, suspect);
|
2006-11-29 06:29:18 +00:00
|
|
|
if (strcmp(suspect->path, sb->path))
|
|
|
|
*option |= OUTPUT_SHOW_NAME;
|
|
|
|
num = strlen(suspect->path);
|
|
|
|
if (longest_file < num)
|
|
|
|
longest_file = num;
|
2006-10-19 23:00:04 +00:00
|
|
|
if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
|
2015-02-09 21:28:07 +00:00
|
|
|
struct commit_info ci;
|
2006-10-19 23:00:04 +00:00
|
|
|
suspect->commit->object.flags |= METAINFO_SHOWN;
|
|
|
|
get_commit_info(suspect->commit, &ci, 1);
|
2010-10-16 06:57:51 +00:00
|
|
|
if (*option & OUTPUT_SHOW_EMAIL)
|
2013-01-05 21:26:40 +00:00
|
|
|
num = utf8_strwidth(ci.author_mail.buf);
|
2010-10-16 06:57:51 +00:00
|
|
|
else
|
2013-01-05 21:26:40 +00:00
|
|
|
num = utf8_strwidth(ci.author.buf);
|
2006-10-19 23:00:04 +00:00
|
|
|
if (longest_author < num)
|
|
|
|
longest_author = num;
|
2015-02-09 21:28:07 +00:00
|
|
|
commit_info_destroy(&ci);
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
num = e->s_lno + e->num_lines;
|
|
|
|
if (longest_src_lines < num)
|
|
|
|
longest_src_lines = num;
|
|
|
|
num = e->lno + e->num_lines;
|
|
|
|
if (longest_dst_lines < num)
|
|
|
|
longest_dst_lines = num;
|
2006-10-20 21:51:12 +00:00
|
|
|
if (largest_score < ent_score(sb, e))
|
|
|
|
largest_score = ent_score(sb, e);
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
2012-02-12 14:16:20 +00:00
|
|
|
max_orig_digits = decimal_width(longest_src_lines);
|
|
|
|
max_digits = decimal_width(longest_dst_lines);
|
|
|
|
max_score_digits = decimal_width(largest_score);
|
2012-07-02 07:54:00 +00:00
|
|
|
|
|
|
|
if (compute_auto_abbrev)
|
|
|
|
/* one more abbrev length is needed for the boundary commit */
|
|
|
|
abbrev = auto_abbrev + 1;
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* For debugging -- origin is refcounted, and this asserts that
|
|
|
|
* we do not underflow.
|
|
|
|
*/
|
2006-10-29 11:07:40 +00:00
|
|
|
static void sanity_check_refcnt(struct scoreboard *sb)
|
|
|
|
{
|
|
|
|
int baa = 0;
|
|
|
|
struct blame_entry *ent;
|
|
|
|
|
|
|
|
for (ent = sb->ent; ent; ent = ent->next) {
|
2006-10-30 22:27:52 +00:00
|
|
|
/* Nobody should have zero or negative refcnt */
|
2006-11-05 03:18:50 +00:00
|
|
|
if (ent->suspect->refcnt <= 0) {
|
|
|
|
fprintf(stderr, "%s in %s has negative refcnt %d\n",
|
|
|
|
ent->suspect->path,
|
2015-11-10 02:22:28 +00:00
|
|
|
oid_to_hex(&ent->suspect->commit->object.oid),
|
2006-11-05 03:18:50 +00:00
|
|
|
ent->suspect->refcnt);
|
2006-10-30 22:27:52 +00:00
|
|
|
baa = 1;
|
2006-11-05 03:18:50 +00:00
|
|
|
}
|
2006-10-30 22:27:52 +00:00
|
|
|
}
|
2006-10-29 11:07:40 +00:00
|
|
|
if (baa) {
|
|
|
|
int opt = 0160;
|
|
|
|
find_alignment(sb, &opt);
|
|
|
|
output(sb, opt);
|
2006-11-05 03:18:50 +00:00
|
|
|
die("Baa %d!", baa);
|
2006-10-29 11:07:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-10-20 22:37:12 +00:00
|
|
|
static unsigned parse_score(const char *arg)
|
|
|
|
{
|
|
|
|
char *end;
|
|
|
|
unsigned long score = strtoul(arg, &end, 10);
|
|
|
|
if (*end)
|
|
|
|
return 0;
|
|
|
|
return score;
|
|
|
|
}
|
|
|
|
|
2006-11-02 07:22:49 +00:00
|
|
|
static const char *add_prefix(const char *prefix, const char *path)
|
|
|
|
{
|
2008-02-01 04:07:04 +00:00
|
|
|
return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
|
2006-11-02 07:22:49 +00:00
|
|
|
}
|
|
|
|
|
2008-05-14 17:46:53 +00:00
|
|
|
static int git_blame_config(const char *var, const char *value, void *cb)
|
2006-12-18 22:04:38 +00:00
|
|
|
{
|
|
|
|
if (!strcmp(var, "blame.showroot")) {
|
|
|
|
show_root = git_config_bool(var, value);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!strcmp(var, "blame.blankboundary")) {
|
|
|
|
blank_boundary = git_config_bool(var, value);
|
|
|
|
return 0;
|
|
|
|
}
|
2015-05-31 19:27:37 +00:00
|
|
|
if (!strcmp(var, "blame.showemail")) {
|
|
|
|
int *output_option = cb;
|
|
|
|
if (git_config_bool(var, value))
|
|
|
|
*output_option |= OUTPUT_SHOW_EMAIL;
|
|
|
|
else
|
|
|
|
*output_option &= ~OUTPUT_SHOW_EMAIL;
|
|
|
|
return 0;
|
|
|
|
}
|
2009-02-20 22:51:11 +00:00
|
|
|
if (!strcmp(var, "blame.date")) {
|
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(var);
|
convert "enum date_mode" into a struct
In preparation for adding date modes that may carry extra
information beyond the mode itself, this patch converts the
date_mode enum into a struct.
Most of the conversion is fairly straightforward; we pass
the struct as a pointer and dereference the type field where
necessary. Locations that declare a date_mode can use a "{}"
constructor. However, the tricky case is where we use the
enum labels as constants, like:
show_date(t, tz, DATE_NORMAL);
Ideally we could say:
show_date(t, tz, &{ DATE_NORMAL });
but of course C does not allow that. Likewise, we cannot
cast the constant to a struct, because we need to pass an
actual address. Our options are basically:
1. Manually add a "struct date_mode d = { DATE_NORMAL }"
definition to each caller, and pass "&d". This makes
the callers uglier, because they sometimes do not even
have their own scope (e.g., they are inside a switch
statement).
2. Provide a pre-made global "date_normal" struct that can
be passed by address. We'd also need "date_rfc2822",
"date_iso8601", and so forth. But at least the ugliness
is defined in one place.
3. Provide a wrapper that generates the correct struct on
the fly. The big downside is that we end up pointing to
a single global, which makes our wrapper non-reentrant.
But show_date is already not reentrant, so it does not
matter.
This patch implements 3, along with a minor macro to keep
the size of the callers sane.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-25 16:55:02 +00:00
|
|
|
parse_date_format(value, &blame_date_mode);
|
2009-02-20 22:51:11 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2010-06-15 13:58:48 +00:00
|
|
|
|
2016-09-05 09:44:53 +00:00
|
|
|
if (git_diff_heuristic_config(var, value, cb) < 0)
|
|
|
|
return -1;
|
drop odd return value semantics from userdiff_config
When the userdiff_config function was introduced in be58e70
(diff: unify external diff and funcname parsing code,
2008-10-05), it used a return value convention unlike any
other config callback. Like other callbacks, it used "-1" to
signal error. But it returned "1" to indicate that it found
something, and "0" otherwise; other callbacks simply
returned "0" to indicate that no error occurred.
This distinction was necessary at the time, because the
userdiff namespace overlapped slightly with the color
configuration namespace. So "diff.color.foo" could mean "the
'foo' slot of diff coloring" or "the 'foo' component of the
"color" userdiff driver". Because the color-parsing code
would die on an unknown color slot, we needed the userdiff
code to indicate that it had matched the variable, letting
us bypass the color-parsing code entirely.
Later, in 8b8e862 (ignore unknown color configuration,
2009-12-12), the color-parsing code learned to silently
ignore unknown slots. This means we no longer need to
protect userdiff-matched variables from reaching the
color-parsing code.
We can therefore change the userdiff_config calling
convention to a more normal one. This drops some code from
each caller, which is nice. But more importantly, it reduces
the cognitive load for readers who may wonder why
userdiff_config is unlike every other config callback.
There's no need to add a new test confirming that this
works; t4020 already contains a test that sets
diff.color.external.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-02-07 18:23:02 +00:00
|
|
|
if (userdiff_config(var, value) < 0)
|
2010-06-15 13:58:48 +00:00
|
|
|
return -1;
|
|
|
|
|
2008-05-14 17:46:53 +00:00
|
|
|
return git_default_config(var, value, cb);
|
2006-12-18 22:04:38 +00:00
|
|
|
}
|
|
|
|
|
2012-09-11 21:30:03 +00:00
|
|
|
static void verify_working_tree_path(struct commit *work_tree, const char *path)
|
2012-09-10 23:30:20 +00:00
|
|
|
{
|
2012-09-11 21:30:03 +00:00
|
|
|
struct commit_list *parents;
|
2016-07-15 23:23:45 +00:00
|
|
|
int pos;
|
2012-09-10 23:30:20 +00:00
|
|
|
|
2012-09-11 21:30:03 +00:00
|
|
|
for (parents = work_tree->parents; parents; parents = parents->next) {
|
2016-09-05 20:08:03 +00:00
|
|
|
const struct object_id *commit_oid = &parents->item->object.oid;
|
|
|
|
struct object_id blob_oid;
|
2012-09-11 21:30:03 +00:00
|
|
|
unsigned mode;
|
|
|
|
|
2016-09-05 20:08:03 +00:00
|
|
|
if (!get_tree_entry(commit_oid->hash, path, blob_oid.hash, &mode) &&
|
|
|
|
sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB)
|
2012-09-11 21:30:03 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-07-15 23:23:45 +00:00
|
|
|
|
|
|
|
pos = cache_name_pos(path, strlen(path));
|
|
|
|
if (pos >= 0)
|
|
|
|
; /* path is in the index */
|
blame: fix segfault on untracked files
Since 3b75ee9 ("blame: allow to blame paths freshly added to the index",
2016-07-16) git blame also looks at the index to determine if there is a
file that was freshly added to the index.
cache_name_pos returns -pos - 1 in case there is no match is found, or
if the name matches, but the entry has a stage other than 0. As git
blame should work for unmerged files, it uses strcmp to determine
whether the name of the returned position matches, in which case the
file exists, but is merely unmerged, or if the file actually doesn't
exist in the index.
If the repository is empty, or if the file would lexicographically be
sorted as the last file in the repository, -cache_name_pos - 1 is
outside of the length of the active_cache array, causing git blame to
segfault. Guard against that, and die() normally to restore the old
behaviour.
Reported-by: Simon Ruderich <simon@ruderich.org>
Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-08-27 20:01:50 +00:00
|
|
|
else if (-1 - pos < active_nr &&
|
|
|
|
!strcmp(active_cache[-1 - pos]->name, path))
|
2016-07-15 23:23:45 +00:00
|
|
|
; /* path is in the index, unmerged */
|
|
|
|
else
|
|
|
|
die("no such path '%s' in HEAD", path);
|
2012-09-11 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
2016-09-05 20:08:03 +00:00
|
|
|
static struct commit_list **append_parent(struct commit_list **tail, const struct object_id *oid)
|
2012-09-11 21:30:03 +00:00
|
|
|
{
|
|
|
|
struct commit *parent;
|
|
|
|
|
Convert lookup_commit* to struct object_id
Convert lookup_commit, lookup_commit_or_die,
lookup_commit_reference, and lookup_commit_reference_gently to take
struct object_id arguments.
Introduce a temporary in parse_object buffer in order to convert this
function. This is required since in order to convert parse_object and
parse_object_buffer, lookup_commit_reference_gently and
lookup_commit_or_die would need to be converted. Not introducing a
temporary would therefore require that lookup_commit_or_die take a
struct object_id *, but lookup_commit would take unsigned char *,
leaving a confusing and hard-to-use interface.
parse_object_buffer will lose this temporary in a later patch.
This commit was created with manual changes to commit.c, commit.h, and
object.c, plus the following semantic patch:
@@
expression E1, E2;
@@
- lookup_commit_reference_gently(E1.hash, E2)
+ lookup_commit_reference_gently(&E1, E2)
@@
expression E1, E2;
@@
- lookup_commit_reference_gently(E1->hash, E2)
+ lookup_commit_reference_gently(E1, E2)
@@
expression E1;
@@
- lookup_commit_reference(E1.hash)
+ lookup_commit_reference(&E1)
@@
expression E1;
@@
- lookup_commit_reference(E1->hash)
+ lookup_commit_reference(E1)
@@
expression E1;
@@
- lookup_commit(E1.hash)
+ lookup_commit(&E1)
@@
expression E1;
@@
- lookup_commit(E1->hash)
+ lookup_commit(E1)
@@
expression E1, E2;
@@
- lookup_commit_or_die(E1.hash, E2)
+ lookup_commit_or_die(&E1, E2)
@@
expression E1, E2;
@@
- lookup_commit_or_die(E1->hash, E2)
+ lookup_commit_or_die(E1, E2)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-06 22:10:10 +00:00
|
|
|
parent = lookup_commit_reference(oid);
|
2012-09-11 21:30:03 +00:00
|
|
|
if (!parent)
|
2016-09-05 20:08:03 +00:00
|
|
|
die("no such commit %s", oid_to_hex(oid));
|
2012-09-11 21:30:03 +00:00
|
|
|
return &commit_list_insert(parent, tail)->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void append_merge_parents(struct commit_list **tail)
|
|
|
|
{
|
|
|
|
int merge_head;
|
|
|
|
struct strbuf line = STRBUF_INIT;
|
|
|
|
|
memoize common git-path "constant" files
One of the most common uses of git_path() is to pass a
constant, like git_path("MERGE_MSG"). This has two
drawbacks:
1. The return value is a static buffer, and the lifetime
is dependent on other calls to git_path, etc.
2. There's no compile-time checking of the pathname. This
is OK for a one-off (after all, we have to spell it
correctly at least once), but many of these constant
strings appear throughout the code.
This patch introduces a series of functions to "memoize"
these strings, which are essentially globals for the
lifetime of the program. We compute the value once, take
ownership of the buffer, and return the cached value for
subsequent calls. cache.h provides a helper macro for
defining these functions as one-liners, and defines a few
common ones for global use.
Using a macro is a little bit gross, but it does nicely
document the purpose of the functions. If we need to touch
them all later (e.g., because we learned how to change the
git_dir variable at runtime, and need to invalidate all of
the stored values), it will be much easier to have the
complete list.
Note that the shared-global functions have separate, manual
declarations. We could do something clever with the macros
(e.g., expand it to a declaration in some places, and a
declaration _and_ a definition in path.c). But there aren't
that many, and it's probably better to stay away from
too-magical macros.
Likewise, if we abandon the C preprocessor in favor of
generating these with a script, we could get much fancier.
E.g., normalizing "FOO/BAR-BAZ" into "git_path_foo_bar_baz".
But the small amount of saved typing is probably not worth
the resulting confusion to readers who want to grep for the
function's definition.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-08-10 09:38:57 +00:00
|
|
|
merge_head = open(git_path_merge_head(), O_RDONLY);
|
2012-09-11 21:30:03 +00:00
|
|
|
if (merge_head < 0) {
|
|
|
|
if (errno == ENOENT)
|
|
|
|
return;
|
memoize common git-path "constant" files
One of the most common uses of git_path() is to pass a
constant, like git_path("MERGE_MSG"). This has two
drawbacks:
1. The return value is a static buffer, and the lifetime
is dependent on other calls to git_path, etc.
2. There's no compile-time checking of the pathname. This
is OK for a one-off (after all, we have to spell it
correctly at least once), but many of these constant
strings appear throughout the code.
This patch introduces a series of functions to "memoize"
these strings, which are essentially globals for the
lifetime of the program. We compute the value once, take
ownership of the buffer, and return the cached value for
subsequent calls. cache.h provides a helper macro for
defining these functions as one-liners, and defines a few
common ones for global use.
Using a macro is a little bit gross, but it does nicely
document the purpose of the functions. If we need to touch
them all later (e.g., because we learned how to change the
git_dir variable at runtime, and need to invalidate all of
the stored values), it will be much easier to have the
complete list.
Note that the shared-global functions have separate, manual
declarations. We could do something clever with the macros
(e.g., expand it to a declaration in some places, and a
declaration _and_ a definition in path.c). But there aren't
that many, and it's probably better to stay away from
too-magical macros.
Likewise, if we abandon the C preprocessor in favor of
generating these with a script, we could get much fancier.
E.g., normalizing "FOO/BAR-BAZ" into "git_path_foo_bar_baz".
But the small amount of saved typing is probably not worth
the resulting confusion to readers who want to grep for the
function's definition.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-08-10 09:38:57 +00:00
|
|
|
die("cannot open '%s' for reading", git_path_merge_head());
|
2012-09-11 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) {
|
2016-09-05 20:08:03 +00:00
|
|
|
struct object_id oid;
|
|
|
|
if (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid))
|
memoize common git-path "constant" files
One of the most common uses of git_path() is to pass a
constant, like git_path("MERGE_MSG"). This has two
drawbacks:
1. The return value is a static buffer, and the lifetime
is dependent on other calls to git_path, etc.
2. There's no compile-time checking of the pathname. This
is OK for a one-off (after all, we have to spell it
correctly at least once), but many of these constant
strings appear throughout the code.
This patch introduces a series of functions to "memoize"
these strings, which are essentially globals for the
lifetime of the program. We compute the value once, take
ownership of the buffer, and return the cached value for
subsequent calls. cache.h provides a helper macro for
defining these functions as one-liners, and defines a few
common ones for global use.
Using a macro is a little bit gross, but it does nicely
document the purpose of the functions. If we need to touch
them all later (e.g., because we learned how to change the
git_dir variable at runtime, and need to invalidate all of
the stored values), it will be much easier to have the
complete list.
Note that the shared-global functions have separate, manual
declarations. We could do something clever with the macros
(e.g., expand it to a declaration in some places, and a
declaration _and_ a definition in path.c). But there aren't
that many, and it's probably better to stay away from
too-magical macros.
Likewise, if we abandon the C preprocessor in favor of
generating these with a script, we could get much fancier.
E.g., normalizing "FOO/BAR-BAZ" into "git_path_foo_bar_baz".
But the small amount of saved typing is probably not worth
the resulting confusion to readers who want to grep for the
function's definition.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-08-10 09:38:57 +00:00
|
|
|
die("unknown line in '%s': %s", git_path_merge_head(), line.buf);
|
2016-09-05 20:08:03 +00:00
|
|
|
tail = append_parent(tail, &oid);
|
2012-09-11 21:30:03 +00:00
|
|
|
}
|
|
|
|
close(merge_head);
|
|
|
|
strbuf_release(&line);
|
2012-09-10 23:30:20 +00:00
|
|
|
}
|
|
|
|
|
2014-06-10 21:44:13 +00:00
|
|
|
/*
|
|
|
|
* This isn't as simple as passing sb->buf and sb->len, because we
|
|
|
|
* want to transfer ownership of the buffer to the commit (so we
|
|
|
|
* must use detach).
|
|
|
|
*/
|
|
|
|
static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
void *buf = strbuf_detach(sb, &len);
|
|
|
|
set_commit_buffer(c, buf, len);
|
|
|
|
}
|
|
|
|
|
2008-04-03 05:17:53 +00:00
|
|
|
/*
|
|
|
|
* Prepare a dummy commit that represents the work tree (or staged) item.
|
|
|
|
* Note that annotating work tree item never works in the reverse.
|
|
|
|
*/
|
2010-06-15 13:58:48 +00:00
|
|
|
static struct commit *fake_working_tree_commit(struct diff_options *opt,
|
|
|
|
const char *path,
|
|
|
|
const char *contents_from)
|
2007-01-30 09:11:08 +00:00
|
|
|
{
|
|
|
|
struct commit *commit;
|
|
|
|
struct origin *origin;
|
2012-09-11 21:30:03 +00:00
|
|
|
struct commit_list **parent_tail, *parent;
|
2016-09-05 20:08:03 +00:00
|
|
|
struct object_id head_oid;
|
2008-10-09 19:12:12 +00:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
2007-01-30 09:11:08 +00:00
|
|
|
const char *ident;
|
|
|
|
time_t now;
|
|
|
|
int size, len;
|
|
|
|
struct cache_entry *ce;
|
|
|
|
unsigned mode;
|
2012-09-11 21:30:03 +00:00
|
|
|
struct strbuf msg = STRBUF_INIT;
|
2007-01-30 09:11:08 +00:00
|
|
|
|
2016-04-05 19:23:54 +00:00
|
|
|
read_cache();
|
2007-01-30 09:11:08 +00:00
|
|
|
time(&now);
|
2014-06-10 21:39:11 +00:00
|
|
|
commit = alloc_commit_node();
|
2007-01-30 09:11:08 +00:00
|
|
|
commit->object.parsed = 1;
|
|
|
|
commit->date = now;
|
2012-09-11 21:30:03 +00:00
|
|
|
parent_tail = &commit->parents;
|
|
|
|
|
2016-09-05 20:08:03 +00:00
|
|
|
if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
|
2012-09-11 21:30:03 +00:00
|
|
|
die("no such ref: HEAD");
|
|
|
|
|
2016-09-05 20:08:03 +00:00
|
|
|
parent_tail = append_parent(parent_tail, &head_oid);
|
2012-09-11 21:30:03 +00:00
|
|
|
append_merge_parents(parent_tail);
|
|
|
|
verify_working_tree_path(commit, path);
|
2007-01-30 09:11:08 +00:00
|
|
|
|
|
|
|
origin = make_origin(commit, path);
|
|
|
|
|
2012-09-11 21:30:03 +00:00
|
|
|
ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0);
|
|
|
|
strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n");
|
|
|
|
for (parent = commit->parents; parent; parent = parent->next)
|
|
|
|
strbuf_addf(&msg, "parent %s\n",
|
2015-11-10 02:22:28 +00:00
|
|
|
oid_to_hex(&parent->item->object.oid));
|
2012-09-11 21:30:03 +00:00
|
|
|
strbuf_addf(&msg,
|
|
|
|
"author %s\n"
|
|
|
|
"committer %s\n\n"
|
|
|
|
"Version of %s from %s\n",
|
|
|
|
ident, ident, path,
|
|
|
|
(!contents_from ? path :
|
|
|
|
(!strcmp(contents_from, "-") ? "standard input" : contents_from)));
|
2014-06-10 21:44:13 +00:00
|
|
|
set_commit_buffer_from_strbuf(commit, &msg);
|
2012-09-11 21:30:03 +00:00
|
|
|
|
2007-01-30 09:11:08 +00:00
|
|
|
if (!contents_from || strcmp("-", contents_from)) {
|
|
|
|
struct stat st;
|
|
|
|
const char *read_from;
|
2011-11-07 17:33:34 +00:00
|
|
|
char *buf_ptr;
|
2010-06-15 13:58:48 +00:00
|
|
|
unsigned long buf_len;
|
2007-01-30 09:11:08 +00:00
|
|
|
|
|
|
|
if (contents_from) {
|
|
|
|
if (stat(contents_from, &st) < 0)
|
2009-06-27 15:58:47 +00:00
|
|
|
die_errno("Cannot stat '%s'", contents_from);
|
2007-01-30 09:11:08 +00:00
|
|
|
read_from = contents_from;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (lstat(path, &st) < 0)
|
2009-06-27 15:58:47 +00:00
|
|
|
die_errno("Cannot lstat '%s'", path);
|
2007-01-30 09:11:08 +00:00
|
|
|
read_from = path;
|
|
|
|
}
|
|
|
|
mode = canon_mode(st.st_mode);
|
2010-06-15 13:58:48 +00:00
|
|
|
|
2007-01-30 09:11:08 +00:00
|
|
|
switch (st.st_mode & S_IFMT) {
|
|
|
|
case S_IFREG:
|
2010-06-15 13:58:48 +00:00
|
|
|
if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
|
2016-09-05 20:07:58 +00:00
|
|
|
textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))
|
2011-11-07 17:33:34 +00:00
|
|
|
strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
|
2010-06-15 13:58:48 +00:00
|
|
|
else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
|
2009-06-27 15:58:47 +00:00
|
|
|
die_errno("cannot open or read '%s'", read_from);
|
2007-01-30 09:11:08 +00:00
|
|
|
break;
|
|
|
|
case S_IFLNK:
|
2008-12-17 20:37:53 +00:00
|
|
|
if (strbuf_readlink(&buf, read_from, st.st_size) < 0)
|
2009-06-27 15:58:47 +00:00
|
|
|
die_errno("cannot readlink '%s'", read_from);
|
2007-01-30 09:11:08 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
die("unsupported file type %s", read_from);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Reading from stdin */
|
|
|
|
mode = 0;
|
2007-09-10 10:35:04 +00:00
|
|
|
if (strbuf_read(&buf, 0, 0) < 0)
|
2009-06-27 15:58:46 +00:00
|
|
|
die_errno("failed to read from stdin");
|
2007-01-30 09:11:08 +00:00
|
|
|
}
|
2015-05-03 16:38:01 +00:00
|
|
|
convert_to_git(path, buf.buf, buf.len, &buf, 0);
|
2007-09-06 11:20:09 +00:00
|
|
|
origin->file.ptr = buf.buf;
|
|
|
|
origin->file.size = buf.len;
|
2016-09-05 20:07:54 +00:00
|
|
|
pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_oid.hash);
|
2007-01-30 09:11:08 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the current index, replace the path entry with
|
|
|
|
* origin->blob_sha1 without mucking with its mode or type
|
|
|
|
* bits; we are not going to write this index out -- we just
|
|
|
|
* want to run "diff-index --cached".
|
|
|
|
*/
|
|
|
|
discard_cache();
|
|
|
|
read_cache();
|
|
|
|
|
|
|
|
len = strlen(path);
|
|
|
|
if (!mode) {
|
|
|
|
int pos = cache_name_pos(path, len);
|
|
|
|
if (0 <= pos)
|
2008-01-15 00:03:17 +00:00
|
|
|
mode = active_cache[pos]->ce_mode;
|
2007-01-30 09:11:08 +00:00
|
|
|
else
|
|
|
|
/* Let's not bother reading from HEAD tree */
|
|
|
|
mode = S_IFREG | 0644;
|
|
|
|
}
|
|
|
|
size = cache_entry_size(len);
|
|
|
|
ce = xcalloc(1, size);
|
2016-09-05 20:07:54 +00:00
|
|
|
oidcpy(&ce->oid, &origin->blob_oid);
|
2007-01-30 09:11:08 +00:00
|
|
|
memcpy(ce->name, path, len);
|
2012-07-11 09:22:37 +00:00
|
|
|
ce->ce_flags = create_ce_flags(0);
|
|
|
|
ce->ce_namelen = len;
|
2007-01-30 09:11:08 +00:00
|
|
|
ce->ce_mode = create_ce_mode(mode);
|
|
|
|
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
|
|
|
|
|
2014-06-13 12:19:31 +00:00
|
|
|
cache_tree_invalidate_path(&the_index, path);
|
2007-01-30 09:11:08 +00:00
|
|
|
|
|
|
|
return commit;
|
|
|
|
}
|
|
|
|
|
blame: fix object casting regression
Commit 1b0d400 refactored the prepare_final() function so
that it could be reused in multiple places. Originally, the
loop had two outputs: a commit to stuff into sb->final, and
the name of the commit from the rev->pending array.
After the refactor, that loop is put in its own function
with a single return value: the object_array_entry from the
rev->pending array. This contains both the name and the object,
but with one important difference: the object is the
_original_ object found by the revision parser, not the
dereferenced commit. If one feeds a tag to "git blame", we
end up casting the tag object to a "struct commit", which
causes a segfault.
Instead, let's return the commit (properly casted) directly
from the function, and take the "name" as an optional
out-parameter. This does the right thing, and actually
simplifies the callers, who no longer need to cast or
dereference the object_array_entry themselves.
[test case by Max Kirillov <max@max630.net>]
Signed-off-by: Jeff King <peff@peff.net>
2015-11-17 23:22:37 +00:00
|
|
|
static struct commit *find_single_final(struct rev_info *revs,
|
|
|
|
const char **name_p)
|
2008-04-03 05:17:53 +00:00
|
|
|
{
|
|
|
|
int i;
|
blame: fix object casting regression
Commit 1b0d400 refactored the prepare_final() function so
that it could be reused in multiple places. Originally, the
loop had two outputs: a commit to stuff into sb->final, and
the name of the commit from the rev->pending array.
After the refactor, that loop is put in its own function
with a single return value: the object_array_entry from the
rev->pending array. This contains both the name and the object,
but with one important difference: the object is the
_original_ object found by the revision parser, not the
dereferenced commit. If one feeds a tag to "git blame", we
end up casting the tag object to a "struct commit", which
causes a segfault.
Instead, let's return the commit (properly casted) directly
from the function, and take the "name" as an optional
out-parameter. This does the right thing, and actually
simplifies the callers, who no longer need to cast or
dereference the object_array_entry themselves.
[test case by Max Kirillov <max@max630.net>]
Signed-off-by: Jeff King <peff@peff.net>
2015-11-17 23:22:37 +00:00
|
|
|
struct commit *found = NULL;
|
|
|
|
const char *name = NULL;
|
2008-04-03 05:17:53 +00:00
|
|
|
|
|
|
|
for (i = 0; i < revs->pending.nr; i++) {
|
|
|
|
struct object *obj = revs->pending.objects[i].item;
|
|
|
|
if (obj->flags & UNINTERESTING)
|
|
|
|
continue;
|
2016-06-14 20:38:14 +00:00
|
|
|
obj = deref_tag(obj, NULL, 0);
|
2008-04-03 05:17:53 +00:00
|
|
|
if (obj->type != OBJ_COMMIT)
|
|
|
|
die("Non commit %s?", revs->pending.objects[i].name);
|
2015-10-30 05:01:52 +00:00
|
|
|
if (found)
|
2008-04-03 05:17:53 +00:00
|
|
|
die("More than one commit to dig from %s and %s?",
|
blame: fix object casting regression
Commit 1b0d400 refactored the prepare_final() function so
that it could be reused in multiple places. Originally, the
loop had two outputs: a commit to stuff into sb->final, and
the name of the commit from the rev->pending array.
After the refactor, that loop is put in its own function
with a single return value: the object_array_entry from the
rev->pending array. This contains both the name and the object,
but with one important difference: the object is the
_original_ object found by the revision parser, not the
dereferenced commit. If one feeds a tag to "git blame", we
end up casting the tag object to a "struct commit", which
causes a segfault.
Instead, let's return the commit (properly casted) directly
from the function, and take the "name" as an optional
out-parameter. This does the right thing, and actually
simplifies the callers, who no longer need to cast or
dereference the object_array_entry themselves.
[test case by Max Kirillov <max@max630.net>]
Signed-off-by: Jeff King <peff@peff.net>
2015-11-17 23:22:37 +00:00
|
|
|
revs->pending.objects[i].name, name);
|
|
|
|
found = (struct commit *)obj;
|
|
|
|
name = revs->pending.objects[i].name;
|
2015-10-30 05:01:52 +00:00
|
|
|
}
|
blame: fix object casting regression
Commit 1b0d400 refactored the prepare_final() function so
that it could be reused in multiple places. Originally, the
loop had two outputs: a commit to stuff into sb->final, and
the name of the commit from the rev->pending array.
After the refactor, that loop is put in its own function
with a single return value: the object_array_entry from the
rev->pending array. This contains both the name and the object,
but with one important difference: the object is the
_original_ object found by the revision parser, not the
dereferenced commit. If one feeds a tag to "git blame", we
end up casting the tag object to a "struct commit", which
causes a segfault.
Instead, let's return the commit (properly casted) directly
from the function, and take the "name" as an optional
out-parameter. This does the right thing, and actually
simplifies the callers, who no longer need to cast or
dereference the object_array_entry themselves.
[test case by Max Kirillov <max@max630.net>]
Signed-off-by: Jeff King <peff@peff.net>
2015-11-17 23:22:37 +00:00
|
|
|
if (name_p)
|
|
|
|
*name_p = name;
|
2015-10-30 05:01:52 +00:00
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *prepare_final(struct scoreboard *sb)
|
|
|
|
{
|
blame: fix object casting regression
Commit 1b0d400 refactored the prepare_final() function so
that it could be reused in multiple places. Originally, the
loop had two outputs: a commit to stuff into sb->final, and
the name of the commit from the rev->pending array.
After the refactor, that loop is put in its own function
with a single return value: the object_array_entry from the
rev->pending array. This contains both the name and the object,
but with one important difference: the object is the
_original_ object found by the revision parser, not the
dereferenced commit. If one feeds a tag to "git blame", we
end up casting the tag object to a "struct commit", which
causes a segfault.
Instead, let's return the commit (properly casted) directly
from the function, and take the "name" as an optional
out-parameter. This does the right thing, and actually
simplifies the callers, who no longer need to cast or
dereference the object_array_entry themselves.
[test case by Max Kirillov <max@max630.net>]
Signed-off-by: Jeff King <peff@peff.net>
2015-11-17 23:22:37 +00:00
|
|
|
const char *name;
|
|
|
|
sb->final = find_single_final(sb->revs, &name);
|
|
|
|
return xstrdup_or_null(name);
|
2008-04-03 05:17:53 +00:00
|
|
|
}
|
|
|
|
|
2016-06-14 18:41:11 +00:00
|
|
|
static const char *dwim_reverse_initial(struct scoreboard *sb)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* DWIM "git blame --reverse ONE -- PATH" as
|
|
|
|
* "git blame --reverse ONE..HEAD -- PATH" but only do so
|
|
|
|
* when it makes sense.
|
|
|
|
*/
|
|
|
|
struct object *obj;
|
|
|
|
struct commit *head_commit;
|
2017-05-01 02:29:01 +00:00
|
|
|
struct object_id head_oid;
|
2016-06-14 18:41:11 +00:00
|
|
|
|
|
|
|
if (sb->revs->pending.nr != 1)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Is that sole rev a committish? */
|
|
|
|
obj = sb->revs->pending.objects[0].item;
|
|
|
|
obj = deref_tag(obj, NULL, 0);
|
|
|
|
if (obj->type != OBJ_COMMIT)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Do we have HEAD? */
|
2017-05-01 02:29:01 +00:00
|
|
|
if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
|
2016-06-14 18:41:11 +00:00
|
|
|
return NULL;
|
Convert lookup_commit* to struct object_id
Convert lookup_commit, lookup_commit_or_die,
lookup_commit_reference, and lookup_commit_reference_gently to take
struct object_id arguments.
Introduce a temporary in parse_object buffer in order to convert this
function. This is required since in order to convert parse_object and
parse_object_buffer, lookup_commit_reference_gently and
lookup_commit_or_die would need to be converted. Not introducing a
temporary would therefore require that lookup_commit_or_die take a
struct object_id *, but lookup_commit would take unsigned char *,
leaving a confusing and hard-to-use interface.
parse_object_buffer will lose this temporary in a later patch.
This commit was created with manual changes to commit.c, commit.h, and
object.c, plus the following semantic patch:
@@
expression E1, E2;
@@
- lookup_commit_reference_gently(E1.hash, E2)
+ lookup_commit_reference_gently(&E1, E2)
@@
expression E1, E2;
@@
- lookup_commit_reference_gently(E1->hash, E2)
+ lookup_commit_reference_gently(E1, E2)
@@
expression E1;
@@
- lookup_commit_reference(E1.hash)
+ lookup_commit_reference(&E1)
@@
expression E1;
@@
- lookup_commit_reference(E1->hash)
+ lookup_commit_reference(E1)
@@
expression E1;
@@
- lookup_commit(E1.hash)
+ lookup_commit(&E1)
@@
expression E1;
@@
- lookup_commit(E1->hash)
+ lookup_commit(E1)
@@
expression E1, E2;
@@
- lookup_commit_or_die(E1.hash, E2)
+ lookup_commit_or_die(&E1, E2)
@@
expression E1, E2;
@@
- lookup_commit_or_die(E1->hash, E2)
+ lookup_commit_or_die(E1, E2)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-06 22:10:10 +00:00
|
|
|
head_commit = lookup_commit_reference_gently(&head_oid, 1);
|
2016-06-14 18:41:11 +00:00
|
|
|
if (!head_commit)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Turn "ONE" into "ONE..HEAD" then */
|
|
|
|
obj->flags |= UNINTERESTING;
|
|
|
|
add_pending_object(sb->revs, &head_commit->object, "HEAD");
|
|
|
|
|
|
|
|
sb->final = (struct commit *)obj;
|
|
|
|
return sb->revs->pending.objects[0].name;
|
|
|
|
}
|
|
|
|
|
2015-01-13 01:59:26 +00:00
|
|
|
static char *prepare_initial(struct scoreboard *sb)
|
2008-04-03 05:17:53 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
const char *final_commit_name = NULL;
|
|
|
|
struct rev_info *revs = sb->revs;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There must be one and only one negative commit, and it must be
|
|
|
|
* the boundary.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < revs->pending.nr; i++) {
|
|
|
|
struct object *obj = revs->pending.objects[i].item;
|
|
|
|
if (!(obj->flags & UNINTERESTING))
|
|
|
|
continue;
|
2016-06-14 20:38:14 +00:00
|
|
|
obj = deref_tag(obj, NULL, 0);
|
2008-04-03 05:17:53 +00:00
|
|
|
if (obj->type != OBJ_COMMIT)
|
|
|
|
die("Non commit %s?", revs->pending.objects[i].name);
|
|
|
|
if (sb->final)
|
2016-06-14 17:46:06 +00:00
|
|
|
die("More than one commit to dig up from, %s and %s?",
|
2008-04-03 05:17:53 +00:00
|
|
|
revs->pending.objects[i].name,
|
|
|
|
final_commit_name);
|
|
|
|
sb->final = (struct commit *) obj;
|
|
|
|
final_commit_name = revs->pending.objects[i].name;
|
|
|
|
}
|
2016-06-14 18:41:11 +00:00
|
|
|
|
|
|
|
if (!final_commit_name)
|
|
|
|
final_commit_name = dwim_reverse_initial(sb);
|
2008-04-03 05:17:53 +00:00
|
|
|
if (!final_commit_name)
|
2016-06-14 17:46:06 +00:00
|
|
|
die("No commit to dig up from?");
|
2015-01-13 01:59:26 +00:00
|
|
|
return xstrdup(final_commit_name);
|
2008-04-03 05:17:53 +00:00
|
|
|
}
|
|
|
|
|
2008-07-08 13:19:34 +00:00
|
|
|
static int blame_copy_callback(const struct option *option, const char *arg, int unset)
|
|
|
|
{
|
|
|
|
int *opt = option->value;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* -C enables copy from removed files;
|
|
|
|
* -C -C enables copy from existing files, but only
|
|
|
|
* when blaming a new file;
|
|
|
|
* -C -C -C enables copy from existing files for
|
|
|
|
* everybody
|
|
|
|
*/
|
|
|
|
if (*opt & PICKAXE_BLAME_COPY_HARDER)
|
|
|
|
*opt |= PICKAXE_BLAME_COPY_HARDEST;
|
|
|
|
if (*opt & PICKAXE_BLAME_COPY)
|
|
|
|
*opt |= PICKAXE_BLAME_COPY_HARDER;
|
|
|
|
*opt |= PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE;
|
|
|
|
|
|
|
|
if (arg)
|
|
|
|
blame_copy_score = parse_score(arg);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int blame_move_callback(const struct option *option, const char *arg, int unset)
|
|
|
|
{
|
|
|
|
int *opt = option->value;
|
|
|
|
|
|
|
|
*opt |= PICKAXE_BLAME_MOVE;
|
|
|
|
|
|
|
|
if (arg)
|
|
|
|
blame_move_score = parse_score(arg);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-11-09 02:47:54 +00:00
|
|
|
int cmd_blame(int argc, const char **argv, const char *prefix)
|
2006-10-19 23:00:04 +00:00
|
|
|
{
|
|
|
|
struct rev_info revs;
|
|
|
|
const char *path;
|
|
|
|
struct scoreboard sb;
|
|
|
|
struct origin *o;
|
2013-08-06 13:59:38 +00:00
|
|
|
struct blame_entry *ent = NULL;
|
|
|
|
long dashdash_pos, lno;
|
2015-01-13 01:59:26 +00:00
|
|
|
char *final_commit_name = NULL;
|
2007-02-26 19:55:59 +00:00
|
|
|
enum object_type type;
|
2015-10-30 05:01:53 +00:00
|
|
|
struct commit *final_commit = NULL;
|
2008-07-08 13:19:34 +00:00
|
|
|
|
2016-06-13 05:39:28 +00:00
|
|
|
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[] = {
|
2013-08-03 11:51:19 +00:00
|
|
|
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")),
|
2015-12-13 00:51:03 +00:00
|
|
|
OPT_BOOL(0, "progress", &show_progress, N_("Force progress reporting")),
|
2012-08-20 12:31:54 +00:00
|
|
|
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),
|
|
|
|
OPT_BIT('p', "porcelain", &output_option, N_("Show in a format designed for machine consumption"), OUTPUT_PORCELAIN),
|
|
|
|
OPT_BIT(0, "line-porcelain", &output_option, N_("Show porcelain format with per-line commit information"), OUTPUT_PORCELAIN|OUTPUT_LINE_PORCELAIN),
|
|
|
|
OPT_BIT('c', NULL, &output_option, N_("Use the same output mode as git-annotate (Default: off)"), OUTPUT_ANNOTATE_COMPAT),
|
|
|
|
OPT_BIT('t', NULL, &output_option, N_("Show raw timestamp (Default: off)"), OUTPUT_RAW_TIMESTAMP),
|
|
|
|
OPT_BIT('l', NULL, &output_option, N_("Show long commit SHA1 (Default: off)"), OUTPUT_LONG_OBJECT_NAME),
|
|
|
|
OPT_BIT('s', NULL, &output_option, N_("Suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR),
|
|
|
|
OPT_BIT('e', "show-email", &output_option, N_("Show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL),
|
|
|
|
OPT_BIT('w', NULL, &xdl_opts, N_("Ignore whitespace differences"), XDF_IGNORE_WHITESPACE),
|
2016-09-05 09:44:53 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The following two options are parsed by parse_revision_opt()
|
|
|
|
* and are only included here to get included in the "-h"
|
|
|
|
* output:
|
|
|
|
*/
|
2016-12-23 20:32:22 +00:00
|
|
|
{ OPTION_LOWLEVEL_CALLBACK, 0, "indent-heuristic", NULL, NULL, N_("Use an experimental heuristic to improve diffs"), PARSE_OPT_NOARG, parse_opt_unknown_cb },
|
2016-09-05 09:44:53 +00:00
|
|
|
|
2012-08-20 12:31:54 +00:00
|
|
|
OPT_BIT(0, "minimal", &xdl_opts, N_("Spend extra cycles to find better match"), XDF_NEED_MINIMAL),
|
|
|
|
OPT_STRING('S', NULL, &revs_file, N_("file"), N_("Use revisions from <file> instead of calling git-rev-list")),
|
|
|
|
OPT_STRING(0, "contents", &contents_from, N_("file"), N_("Use <file>'s contents as the final image")),
|
|
|
|
{ OPTION_CALLBACK, 'C', NULL, &opt, N_("score"), N_("Find line copies within and across files"), PARSE_OPT_OPTARG, blame_copy_callback },
|
|
|
|
{ OPTION_CALLBACK, 'M', NULL, &opt, N_("score"), N_("Find line movements within and across files"), PARSE_OPT_OPTARG, blame_move_callback },
|
2013-08-06 13:59:38 +00:00
|
|
|
OPT_STRING_LIST('L', NULL, &range_list, N_("n,m"), N_("Process only line range n,m, counting from 1")),
|
2011-04-06 02:20:50 +00:00
|
|
|
OPT__ABBREV(&abbrev),
|
2008-07-08 13:19:34 +00:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
struct parse_opt_ctx_t ctx;
|
2008-09-05 07:57:35 +00:00
|
|
|
int cmd_is_annotate = !strcmp(argv[0], "annotate");
|
2013-08-06 13:59:38 +00:00
|
|
|
struct range_set ranges;
|
|
|
|
unsigned int range_i;
|
2013-08-06 13:59:42 +00:00
|
|
|
long anchor;
|
2007-02-06 09:52:04 +00:00
|
|
|
|
2015-05-31 19:27:37 +00:00
|
|
|
git_config(git_blame_config, &output_option);
|
2008-07-08 13:19:34 +00:00
|
|
|
init_revisions(&revs, NULL);
|
2009-02-20 22:51:11 +00:00
|
|
|
revs.date_mode = blame_date_mode;
|
2010-06-15 13:58:48 +00:00
|
|
|
DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV);
|
2012-09-21 20:52:25 +00:00
|
|
|
DIFF_OPT_SET(&revs.diffopt, FOLLOW_RENAMES);
|
2009-02-20 22:51:11 +00:00
|
|
|
|
2006-10-21 06:49:31 +00:00
|
|
|
save_commit_buffer = 0;
|
2008-07-08 13:19:35 +00:00
|
|
|
dashdash_pos = 0;
|
2015-12-13 00:51:03 +00:00
|
|
|
show_progress = -1;
|
2006-10-21 06:49:31 +00:00
|
|
|
|
2010-12-06 07:57:42 +00:00
|
|
|
parse_options_start(&ctx, argc, argv, prefix, options,
|
|
|
|
PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
|
2008-07-08 13:19:34 +00:00
|
|
|
for (;;) {
|
|
|
|
switch (parse_options_step(&ctx, options, blame_opt_usage)) {
|
|
|
|
case PARSE_OPT_HELP:
|
|
|
|
exit(129);
|
|
|
|
case PARSE_OPT_DONE:
|
2008-07-08 13:19:35 +00:00
|
|
|
if (ctx.argv[0])
|
|
|
|
dashdash_pos = ctx.cpidx;
|
2008-07-08 13:19:34 +00:00
|
|
|
goto parse_done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(ctx.argv[0], "--reverse")) {
|
|
|
|
ctx.argv[0] = "--children";
|
|
|
|
reverse = 1;
|
|
|
|
}
|
2008-07-09 21:38:34 +00:00
|
|
|
parse_revision_opt(&revs, &ctx, options, blame_opt_usage);
|
2008-07-08 13:19:34 +00:00
|
|
|
}
|
|
|
|
parse_done:
|
2012-09-21 20:52:25 +00:00
|
|
|
no_whole_file_rename = !DIFF_OPT_TST(&revs.diffopt, FOLLOW_RENAMES);
|
2016-12-23 20:32:22 +00:00
|
|
|
xdl_opts |= revs.diffopt.xdl_opts & XDF_INDENT_HEURISTIC;
|
2012-09-21 20:52:25 +00:00
|
|
|
DIFF_OPT_CLR(&revs.diffopt, FOLLOW_RENAMES);
|
2008-07-08 13:19:34 +00:00
|
|
|
argc = parse_options_end(&ctx);
|
|
|
|
|
2015-12-13 00:51:03 +00:00
|
|
|
if (incremental || (output_option & OUTPUT_PORCELAIN)) {
|
|
|
|
if (show_progress > 0)
|
2016-09-15 14:58:58 +00:00
|
|
|
die(_("--progress can't be used with --incremental or porcelain formats"));
|
2015-12-13 00:51:03 +00:00
|
|
|
show_progress = 0;
|
|
|
|
} else if (show_progress < 0)
|
|
|
|
show_progress = isatty(2);
|
|
|
|
|
blame: fix alignment with --abbrev=40
The blame command internally adds 1 to any requested sha1
abbreviation length, and then subtracts it when outputting a
boundary commit. This lets regular and boundary sha1s line
up visually, but it misses one corner case.
When the requested length is 40, we bump the value to 41.
But since we only have 40 characters, that's all we can show
(fortunately the truncation is done by a printf precision
field, so it never tries to read past the end of the
buffer). So a normal sha1 shows 40 hex characters, and a
boundary sha1 shows "^" plus 40 hex characters. The result
is misaligned.
The "-l" option to show long sha1s gets around this by
skipping the "abbrev" variable entirely and just always
using GIT_SHA1_HEXSZ. This avoids the "+1" issue, but it
does mean that boundary commits only have 39 characters
printed. This is somewhat odd, but it does look good
visually: the results are aligned and left-justified. The
alternative would be to allocate an extra column that would
contain either an extra space or the "^" boundary marker.
As this is by definition the human-readable view, it's
probably not that big a deal either way (and of course
--porcelain, etc, correctly produce correct 40-hex sha1s).
But for consistency, this patch teaches --abbrev=40 to
produce the same output as "-l" (always left-aligned, with
40-hex for normal sha1s, and "^" plus 39-hex for
boundaries).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-01-06 04:17:40 +00:00
|
|
|
if (0 < abbrev && abbrev < GIT_SHA1_HEXSZ)
|
2012-07-02 07:54:00 +00:00
|
|
|
/* one more abbrev length is needed for the boundary commit */
|
|
|
|
abbrev++;
|
2017-01-06 04:18:08 +00:00
|
|
|
else if (!abbrev)
|
|
|
|
abbrev = GIT_SHA1_HEXSZ;
|
2011-04-06 02:20:50 +00:00
|
|
|
|
2009-03-18 07:13:03 +00:00
|
|
|
if (revs_file && read_ancestry(revs_file))
|
2009-06-27 15:58:46 +00:00
|
|
|
die_errno("reading graft file '%s' failed", revs_file);
|
2009-03-18 07:13:03 +00:00
|
|
|
|
2009-02-20 22:51:11 +00:00
|
|
|
if (cmd_is_annotate) {
|
2008-09-05 07:57:35 +00:00
|
|
|
output_option |= OUTPUT_ANNOTATE_COMPAT;
|
convert "enum date_mode" into a struct
In preparation for adding date modes that may carry extra
information beyond the mode itself, this patch converts the
date_mode enum into a struct.
Most of the conversion is fairly straightforward; we pass
the struct as a pointer and dereference the type field where
necessary. Locations that declare a date_mode can use a "{}"
constructor. However, the tricky case is where we use the
enum labels as constants, like:
show_date(t, tz, DATE_NORMAL);
Ideally we could say:
show_date(t, tz, &{ DATE_NORMAL });
but of course C does not allow that. Likewise, we cannot
cast the constant to a struct, because we need to pass an
actual address. Our options are basically:
1. Manually add a "struct date_mode d = { DATE_NORMAL }"
definition to each caller, and pass "&d". This makes
the callers uglier, because they sometimes do not even
have their own scope (e.g., they are inside a switch
statement).
2. Provide a pre-made global "date_normal" struct that can
be passed by address. We'd also need "date_rfc2822",
"date_iso8601", and so forth. But at least the ugliness
is defined in one place.
3. Provide a wrapper that generates the correct struct on
the fly. The big downside is that we end up pointing to
a single global, which makes our wrapper non-reentrant.
But show_date is already not reentrant, so it does not
matter.
This patch implements 3, along with a minor macro to keep
the size of the callers sane.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-25 16:55:02 +00:00
|
|
|
blame_date_mode.type = DATE_ISO8601;
|
2009-02-20 22:51:11 +00:00
|
|
|
} else {
|
|
|
|
blame_date_mode = revs.date_mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The maximum width used to show the dates */
|
convert "enum date_mode" into a struct
In preparation for adding date modes that may carry extra
information beyond the mode itself, this patch converts the
date_mode enum into a struct.
Most of the conversion is fairly straightforward; we pass
the struct as a pointer and dereference the type field where
necessary. Locations that declare a date_mode can use a "{}"
constructor. However, the tricky case is where we use the
enum labels as constants, like:
show_date(t, tz, DATE_NORMAL);
Ideally we could say:
show_date(t, tz, &{ DATE_NORMAL });
but of course C does not allow that. Likewise, we cannot
cast the constant to a struct, because we need to pass an
actual address. Our options are basically:
1. Manually add a "struct date_mode d = { DATE_NORMAL }"
definition to each caller, and pass "&d". This makes
the callers uglier, because they sometimes do not even
have their own scope (e.g., they are inside a switch
statement).
2. Provide a pre-made global "date_normal" struct that can
be passed by address. We'd also need "date_rfc2822",
"date_iso8601", and so forth. But at least the ugliness
is defined in one place.
3. Provide a wrapper that generates the correct struct on
the fly. The big downside is that we end up pointing to
a single global, which makes our wrapper non-reentrant.
But show_date is already not reentrant, so it does not
matter.
This patch implements 3, along with a minor macro to keep
the size of the callers sane.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-25 16:55:02 +00:00
|
|
|
switch (blame_date_mode.type) {
|
2009-02-20 22:51:11 +00:00
|
|
|
case DATE_RFC2822:
|
|
|
|
blame_date_width = sizeof("Thu, 19 Oct 2006 16:00:04 -0700");
|
|
|
|
break;
|
2014-08-29 16:58:42 +00:00
|
|
|
case DATE_ISO8601_STRICT:
|
|
|
|
blame_date_width = sizeof("2006-10-19T16:00:04-07:00");
|
|
|
|
break;
|
2009-02-20 22:51:11 +00:00
|
|
|
case DATE_ISO8601:
|
|
|
|
blame_date_width = sizeof("2006-10-19 16:00:04 -0700");
|
|
|
|
break;
|
|
|
|
case DATE_RAW:
|
|
|
|
blame_date_width = sizeof("1161298804 -0700");
|
|
|
|
break;
|
2016-07-22 19:51:49 +00:00
|
|
|
case DATE_UNIX:
|
|
|
|
blame_date_width = sizeof("1161298804");
|
|
|
|
break;
|
2009-02-20 22:51:11 +00:00
|
|
|
case DATE_SHORT:
|
|
|
|
blame_date_width = sizeof("2006-10-19");
|
|
|
|
break;
|
|
|
|
case DATE_RELATIVE:
|
2014-04-22 14:39:10 +00:00
|
|
|
/* TRANSLATORS: This string is used to tell us the maximum
|
|
|
|
display width for a relative timestamp in "git blame"
|
|
|
|
output. For C locale, "4 years, 11 months ago", which
|
|
|
|
takes 22 places, is the longest among various forms of
|
|
|
|
relative timestamps, but your language may need more or
|
|
|
|
fewer display columns. */
|
|
|
|
blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */
|
|
|
|
break;
|
2009-02-20 22:51:11 +00:00
|
|
|
case DATE_NORMAL:
|
|
|
|
blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
|
|
|
|
break;
|
2015-06-25 16:55:45 +00:00
|
|
|
case DATE_STRFTIME:
|
|
|
|
blame_date_width = strlen(show_date(0, 0, &blame_date_mode)) + 1; /* add the null */
|
|
|
|
break;
|
2009-02-20 22:51:11 +00:00
|
|
|
}
|
|
|
|
blame_date_width -= 1; /* strip the null */
|
2008-09-05 07:57:35 +00:00
|
|
|
|
2008-07-31 07:05:22 +00:00
|
|
|
if (DIFF_OPT_TST(&revs.diffopt, FIND_COPIES_HARDER))
|
|
|
|
opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |
|
|
|
|
PICKAXE_BLAME_COPY_HARDER);
|
|
|
|
|
2006-10-20 22:37:12 +00:00
|
|
|
if (!blame_move_score)
|
|
|
|
blame_move_score = BLAME_DEFAULT_MOVE_SCORE;
|
|
|
|
if (!blame_copy_score)
|
|
|
|
blame_copy_score = BLAME_DEFAULT_COPY_SCORE;
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* We have collected options unknown to us in argv[1..unk]
|
2006-10-19 23:00:04 +00:00
|
|
|
* which are to be passed to revision machinery if we are
|
2007-02-04 04:49:16 +00:00
|
|
|
* going to do the "bottom" processing.
|
2006-10-19 23:00:04 +00:00
|
|
|
*
|
|
|
|
* The remaining are:
|
|
|
|
*
|
2010-08-22 11:12:12 +00:00
|
|
|
* (1) if dashdash_pos != 0, it is either
|
2008-07-08 13:19:35 +00:00
|
|
|
* "blame [revisions] -- <path>" or
|
|
|
|
* "blame -- <path> <rev>"
|
2006-10-19 23:00:04 +00:00
|
|
|
*
|
2010-08-22 11:12:12 +00:00
|
|
|
* (2) otherwise, it is one of the two:
|
2008-07-08 13:19:35 +00:00
|
|
|
* "blame [revisions] <path>"
|
|
|
|
* "blame <path> <rev>"
|
2006-10-19 23:00:04 +00:00
|
|
|
*
|
2008-07-08 13:19:35 +00:00
|
|
|
* Note that we must strip out <path> from the arguments: we do not
|
|
|
|
* want the path pruning but we may want "bottom" processing.
|
2006-10-19 23:00:04 +00:00
|
|
|
*/
|
2008-07-08 13:19:35 +00:00
|
|
|
if (dashdash_pos) {
|
|
|
|
switch (argc - dashdash_pos - 1) {
|
|
|
|
case 2: /* (1b) */
|
|
|
|
if (argc != 4)
|
2008-07-08 13:19:34 +00:00
|
|
|
usage_with_options(blame_opt_usage, options);
|
2008-07-08 13:19:35 +00:00
|
|
|
/* reorder for the new way: <rev> -- <path> */
|
|
|
|
argv[1] = argv[3];
|
|
|
|
argv[3] = argv[2];
|
|
|
|
argv[2] = "--";
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case 1: /* (1a) */
|
|
|
|
path = add_prefix(prefix, argv[--argc]);
|
|
|
|
argv[argc] = NULL;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
usage_with_options(blame_opt_usage, options);
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
2008-07-08 13:19:35 +00:00
|
|
|
} else {
|
|
|
|
if (argc < 2)
|
2008-07-08 13:19:34 +00:00
|
|
|
usage_with_options(blame_opt_usage, options);
|
2008-07-08 13:19:35 +00:00
|
|
|
path = add_prefix(prefix, argv[argc - 1]);
|
2015-05-19 21:44:23 +00:00
|
|
|
if (argc == 3 && !file_exists(path)) { /* (2b) */
|
2008-07-08 13:19:35 +00:00
|
|
|
path = add_prefix(prefix, argv[1]);
|
|
|
|
argv[1] = argv[2];
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
2008-07-08 13:19:35 +00:00
|
|
|
argv[argc - 1] = "--";
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2008-07-08 13:19:35 +00:00
|
|
|
setup_work_tree();
|
2015-05-19 21:44:23 +00:00
|
|
|
if (!file_exists(path))
|
2009-06-27 15:58:46 +00:00
|
|
|
die_errno("cannot stat path '%s'", path);
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
|
|
|
|
2009-11-03 14:59:18 +00:00
|
|
|
revs.disable_stdin = 1;
|
2008-07-08 13:19:35 +00:00
|
|
|
setup_revisions(argc, argv, &revs, NULL);
|
2006-10-19 23:00:04 +00:00
|
|
|
memset(&sb, 0, sizeof(sb));
|
|
|
|
|
2008-04-03 05:17:53 +00:00
|
|
|
sb.revs = &revs;
|
2014-04-25 23:56:49 +00:00
|
|
|
if (!reverse) {
|
2008-04-03 05:17:53 +00:00
|
|
|
final_commit_name = prepare_final(&sb);
|
2014-04-25 23:56:49 +00:00
|
|
|
sb.commits.compare = compare_commits_by_commit_date;
|
|
|
|
}
|
2008-04-03 05:17:53 +00:00
|
|
|
else if (contents_from)
|
2016-09-15 14:58:58 +00:00
|
|
|
die(_("--contents and --reverse do not blend well."));
|
2014-04-25 23:56:49 +00:00
|
|
|
else {
|
2008-04-03 05:17:53 +00:00
|
|
|
final_commit_name = prepare_initial(&sb);
|
2014-04-25 23:56:49 +00:00
|
|
|
sb.commits.compare = compare_commits_by_reverse_commit_date;
|
2015-10-30 05:01:53 +00:00
|
|
|
if (revs.first_parent_only)
|
|
|
|
revs.children.name = NULL;
|
2014-04-25 23:56:49 +00:00
|
|
|
}
|
2006-10-19 23:00:04 +00:00
|
|
|
|
|
|
|
if (!sb.final) {
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* "--not A B -- path" without anything positive;
|
2007-01-30 09:11:08 +00:00
|
|
|
* do not default to HEAD, but use the working tree
|
|
|
|
* or "--contents".
|
2007-01-30 01:36:22 +00:00
|
|
|
*/
|
2007-11-03 12:22:55 +00:00
|
|
|
setup_work_tree();
|
2010-06-15 13:58:48 +00:00
|
|
|
sb.final = fake_working_tree_commit(&sb.revs->diffopt,
|
|
|
|
path, contents_from);
|
2007-01-30 09:11:08 +00:00
|
|
|
add_pending_object(&revs, &(sb.final->object), ":");
|
2006-10-19 23:00:04 +00:00
|
|
|
}
|
2007-01-30 09:11:08 +00:00
|
|
|
else if (contents_from)
|
2016-09-15 14:58:58 +00:00
|
|
|
die(_("cannot use --contents with final commit object name"));
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2015-10-30 05:01:53 +00:00
|
|
|
if (reverse && revs.first_parent_only) {
|
blame: fix object casting regression
Commit 1b0d400 refactored the prepare_final() function so
that it could be reused in multiple places. Originally, the
loop had two outputs: a commit to stuff into sb->final, and
the name of the commit from the rev->pending array.
After the refactor, that loop is put in its own function
with a single return value: the object_array_entry from the
rev->pending array. This contains both the name and the object,
but with one important difference: the object is the
_original_ object found by the revision parser, not the
dereferenced commit. If one feeds a tag to "git blame", we
end up casting the tag object to a "struct commit", which
causes a segfault.
Instead, let's return the commit (properly casted) directly
from the function, and take the "name" as an optional
out-parameter. This does the right thing, and actually
simplifies the callers, who no longer need to cast or
dereference the object_array_entry themselves.
[test case by Max Kirillov <max@max630.net>]
Signed-off-by: Jeff King <peff@peff.net>
2015-11-17 23:22:37 +00:00
|
|
|
final_commit = find_single_final(sb.revs, NULL);
|
|
|
|
if (!final_commit)
|
2016-09-15 14:58:58 +00:00
|
|
|
die(_("--reverse and --first-parent together require specified latest commit"));
|
2015-10-30 05:01:53 +00:00
|
|
|
}
|
|
|
|
|
2007-01-30 01:36:22 +00:00
|
|
|
/*
|
|
|
|
* If we have bottom, this will mark the ancestors of the
|
2006-10-19 23:00:04 +00:00
|
|
|
* bottom commits we would reach while traversing as
|
|
|
|
* uninteresting.
|
|
|
|
*/
|
2008-02-18 07:31:56 +00:00
|
|
|
if (prepare_revision_walk(&revs))
|
2014-08-10 21:33:25 +00:00
|
|
|
die(_("revision walk setup failed"));
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2015-10-30 05:01:53 +00:00
|
|
|
if (reverse && revs.first_parent_only) {
|
|
|
|
struct commit *c = final_commit;
|
|
|
|
|
|
|
|
sb.revs->children.name = "children";
|
|
|
|
while (c->parents &&
|
2015-11-10 02:22:28 +00:00
|
|
|
oidcmp(&c->object.oid, &sb.final->object.oid)) {
|
2015-10-30 05:01:53 +00:00
|
|
|
struct commit_list *l = xcalloc(1, sizeof(*l));
|
|
|
|
|
|
|
|
l->item = c;
|
|
|
|
if (add_decoration(&sb.revs->children,
|
|
|
|
&c->parents->item->object, l))
|
|
|
|
die("BUG: not unique item in first-parent chain");
|
|
|
|
c = c->parents->item;
|
|
|
|
}
|
|
|
|
|
2015-11-10 02:22:28 +00:00
|
|
|
if (oidcmp(&c->object.oid, &sb.final->object.oid))
|
2016-09-15 14:58:58 +00:00
|
|
|
die(_("--reverse --first-parent together require range along first-parent chain"));
|
2015-10-30 05:01:53 +00:00
|
|
|
}
|
|
|
|
|
2015-11-10 02:22:28 +00:00
|
|
|
if (is_null_oid(&sb.final->object.oid)) {
|
2007-01-30 09:11:08 +00:00
|
|
|
o = sb.final->util;
|
2014-07-19 15:35:34 +00:00
|
|
|
sb.final_buf = xmemdupz(o->file.ptr, o->file.size);
|
2007-01-30 09:11:08 +00:00
|
|
|
sb.final_buf_size = o->file.size;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
o = get_origin(&sb, sb.final, path);
|
2010-09-29 11:35:24 +00:00
|
|
|
if (fill_blob_sha1_and_mode(o))
|
2016-09-15 14:58:58 +00:00
|
|
|
die(_("no such path %s in %s"), path, final_commit_name);
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2010-06-15 13:58:48 +00:00
|
|
|
if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
|
2016-09-05 20:07:58 +00:00
|
|
|
textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb.final_buf,
|
2010-06-15 13:58:48 +00:00
|
|
|
&sb.final_buf_size))
|
|
|
|
;
|
|
|
|
else
|
2016-09-05 20:07:54 +00:00
|
|
|
sb.final_buf = read_sha1_file(o->blob_oid.hash, &type,
|
2010-06-15 13:58:48 +00:00
|
|
|
&sb.final_buf_size);
|
|
|
|
|
2007-08-25 08:26:20 +00:00
|
|
|
if (!sb.final_buf)
|
2016-09-15 14:58:58 +00:00
|
|
|
die(_("cannot read blob %s for path %s"),
|
2016-09-05 20:07:54 +00:00
|
|
|
oid_to_hex(&o->blob_oid),
|
2007-08-25 08:26:20 +00:00
|
|
|
path);
|
2007-01-30 09:11:08 +00:00
|
|
|
}
|
2006-11-05 19:51:41 +00:00
|
|
|
num_read_blob++;
|
2006-10-19 23:00:04 +00:00
|
|
|
lno = prepare_lines(&sb);
|
|
|
|
|
2013-08-06 13:59:38 +00:00
|
|
|
if (lno && !range_list.nr)
|
2016-08-02 10:52:53 +00:00
|
|
|
string_list_append(&range_list, "1");
|
2013-08-06 13:59:38 +00:00
|
|
|
|
2013-08-06 13:59:42 +00:00
|
|
|
anchor = 1;
|
2013-08-06 13:59:38 +00:00
|
|
|
range_set_init(&ranges, range_list.nr);
|
|
|
|
for (range_i = 0; range_i < range_list.nr; ++range_i) {
|
|
|
|
long bottom, top;
|
|
|
|
if (parse_range_arg(range_list.items[range_i].string,
|
2013-08-06 13:59:42 +00:00
|
|
|
nth_line_cb, &sb, lno, anchor,
|
2013-08-06 13:59:38 +00:00
|
|
|
&bottom, &top, sb.path))
|
|
|
|
usage(blame_usage);
|
|
|
|
if (lno < top || ((lno || bottom) && lno < bottom))
|
2016-09-15 14:58:58 +00:00
|
|
|
die(Q_("file %s has only %lu line",
|
|
|
|
"file %s has only %lu lines",
|
|
|
|
lno), path, lno);
|
2013-08-06 13:59:38 +00:00
|
|
|
if (bottom < 1)
|
|
|
|
bottom = 1;
|
|
|
|
if (top < 1)
|
|
|
|
top = lno;
|
|
|
|
bottom--;
|
|
|
|
range_set_append_unsafe(&ranges, bottom, top);
|
2013-08-06 13:59:42 +00:00
|
|
|
anchor = top + 1;
|
2013-08-06 13:59:38 +00:00
|
|
|
}
|
|
|
|
sort_and_merge_range_set(&ranges);
|
|
|
|
|
|
|
|
for (range_i = ranges.nr; range_i > 0; --range_i) {
|
|
|
|
const struct range *r = &ranges.ranges[range_i - 1];
|
|
|
|
long bottom = r->start;
|
|
|
|
long top = r->end;
|
|
|
|
struct blame_entry *next = ent;
|
|
|
|
ent = xcalloc(1, sizeof(*ent));
|
|
|
|
ent->lno = bottom;
|
|
|
|
ent->num_lines = top - bottom;
|
|
|
|
ent->suspect = o;
|
|
|
|
ent->s_lno = bottom;
|
|
|
|
ent->next = next;
|
|
|
|
origin_incref(o);
|
|
|
|
}
|
2014-04-25 23:56:49 +00:00
|
|
|
|
|
|
|
o->suspects = ent;
|
|
|
|
prio_queue_put(&sb.commits, o->commit);
|
|
|
|
|
2013-08-06 13:59:38 +00:00
|
|
|
origin_decref(o);
|
|
|
|
|
|
|
|
range_set_release(&ranges);
|
|
|
|
string_list_clear(&range_list, 0);
|
2006-10-19 23:00:04 +00:00
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
sb.ent = NULL;
|
2006-10-19 23:00:04 +00:00
|
|
|
sb.path = path;
|
|
|
|
|
2009-02-08 14:34:27 +00:00
|
|
|
read_mailmap(&mailmap, NULL);
|
2007-04-27 07:42:15 +00:00
|
|
|
|
2015-12-13 00:51:03 +00:00
|
|
|
assign_blame(&sb, opt);
|
|
|
|
|
2007-11-03 12:22:53 +00:00
|
|
|
if (!incremental)
|
|
|
|
setup_pager();
|
|
|
|
|
2015-01-13 01:59:26 +00:00
|
|
|
free(final_commit_name);
|
|
|
|
|
2007-01-28 09:34:06 +00:00
|
|
|
if (incremental)
|
|
|
|
return 0;
|
|
|
|
|
2014-04-25 23:56:49 +00:00
|
|
|
sb.ent = blame_sort(sb.ent, compare_blame_final);
|
|
|
|
|
2006-10-19 23:00:04 +00:00
|
|
|
coalesce(&sb);
|
|
|
|
|
|
|
|
if (!(output_option & OUTPUT_PORCELAIN))
|
|
|
|
find_alignment(&sb, &output_option);
|
|
|
|
|
|
|
|
output(&sb, output_option);
|
|
|
|
free((void *)sb.final_buf);
|
|
|
|
for (ent = sb.ent; ent; ) {
|
|
|
|
struct blame_entry *e = ent->next;
|
|
|
|
free(ent);
|
|
|
|
ent = e;
|
|
|
|
}
|
2006-11-05 19:51:41 +00:00
|
|
|
|
2006-11-05 19:52:43 +00:00
|
|
|
if (show_stats) {
|
2006-11-05 19:51:41 +00:00
|
|
|
printf("num read blob: %d\n", num_read_blob);
|
|
|
|
printf("num get patch: %d\n", num_get_patch);
|
|
|
|
printf("num commits: %d\n", num_commits);
|
|
|
|
}
|
2006-10-19 23:00:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|