static int blank_boundary;
static int incremental;
static int xdl_opts;
+static int abbrev = -1;
static enum date_mode blame_date_mode = DATE_ISO8601;
static size_t blame_date_width;
timepos = tmp;
*tmp = 0;
- while (person < tmp && *tmp != ' ')
+ while (person < tmp && !(*tmp == ' ' && tmp[1] == '<'))
tmp--;
if (tmp <= person)
return;
/*
* Porcelain/Incremental format wants to show a lot of details per
* commit. Instead of repeating this every line, emit it only once,
- * the first time each commit appears in the output.
+ * the first time each commit appears in the output (unless the
+ * user has specifically asked for us to repeat).
*/
-static int emit_one_suspect_detail(struct origin *suspect)
+static int emit_one_suspect_detail(struct origin *suspect, int repeat)
{
struct commit_info ci;
- if (suspect->commit->object.flags & METAINFO_SHOWN)
+ if (!repeat && (suspect->commit->object.flags & METAINFO_SHOWN))
return 0;
suspect->commit->object.flags |= METAINFO_SHOWN;
printf("%s %d %d %d\n",
sha1_to_hex(suspect->commit->object.sha1),
ent->s_lno + 1, ent->lno + 1, ent->num_lines);
- emit_one_suspect_detail(suspect);
+ emit_one_suspect_detail(suspect, 0);
write_filename_info(suspect->path);
maybe_flush_or_die(stdout, "stdout");
}
int tz;
if (show_raw_time) {
- sprintf(time_buf, "%lu %s", time, tz_str);
+ snprintf(time_buf, sizeof(time_buf), "%lu %s", time, tz_str);
}
else {
tz = atoi(tz_str);
#define OUTPUT_SHOW_SCORE 0100
#define OUTPUT_NO_AUTHOR 0200
#define OUTPUT_SHOW_EMAIL 0400
+#define OUTPUT_LINE_PORCELAIN 01000
+
+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))
+ write_filename_info(suspect->path);
+}
-static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
+static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
+ int opt)
{
+ int repeat = opt & OUTPUT_LINE_PORCELAIN;
int cnt;
const char *cp;
struct origin *suspect = ent->suspect;
ent->s_lno + 1,
ent->lno + 1,
ent->num_lines);
- if (emit_one_suspect_detail(suspect) ||
- (suspect->commit->object.flags & MORE_THAN_ONE_PATH))
- write_filename_info(suspect->path);
+ emit_porcelain_details(suspect, repeat);
cp = nth_line(sb, ent->lno);
for (cnt = 0; cnt < ent->num_lines; cnt++) {
char ch;
- if (cnt)
+ if (cnt) {
printf("%s %d %d\n", hex,
ent->s_lno + 1 + cnt,
ent->lno + 1 + cnt);
+ if (repeat)
+ emit_porcelain_details(suspect, 1);
+ }
putchar('\t');
do {
ch = *cp++;
cp = nth_line(sb, ent->lno);
for (cnt = 0; cnt < ent->num_lines; cnt++) {
char ch;
- int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? 40 : 8;
+ int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? 40 : abbrev;
if (suspect->commit->object.flags & UNINTERESTING) {
if (blank_boundary)
for (ent = sb->ent; ent; ent = ent->next) {
if (option & OUTPUT_PORCELAIN)
- emit_porcelain(sb, ent);
+ emit_porcelain(sb, ent, option);
else {
emit_other(sb, ent, option);
}
return 0;
}
-/*
- * How many columns do we need to show line numbers in decimal?
- */
-static int lineno_width(int lines)
-{
- int i, width;
-
- for (width = 1, i = 10; i <= lines; width++)
- i *= 10;
- return width;
-}
-
/*
* How many columns do we need to show line numbers, authors,
* and filenames?
if (largest_score < ent_score(sb, e))
largest_score = ent_score(sb, e);
}
- max_orig_digits = lineno_width(longest_src_lines);
- max_digits = lineno_width(longest_dst_lines);
- max_score_digits = lineno_width(largest_score);
+ max_orig_digits = decimal_width(longest_src_lines);
+ max_digits = decimal_width(longest_dst_lines);
+ max_score_digits = decimal_width(largest_score);
}
/*
if (!contents_from || strcmp("-", contents_from)) {
struct stat st;
const char *read_from;
+ char *buf_ptr;
unsigned long buf_len;
if (contents_from) {
switch (st.st_mode & S_IFMT) {
case S_IFREG:
if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
- textconv_object(read_from, mode, null_sha1, &buf.buf, &buf_len))
- buf.len = buf_len;
+ textconv_object(read_from, mode, null_sha1, &buf_ptr, &buf_len))
+ strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
die_errno("cannot open or read '%s'", read_from);
break;
OPT_BIT('f', "show-name", &output_option, "Show original filename (Default: auto)", OUTPUT_SHOW_NAME),
OPT_BIT('n', "show-number", &output_option, "Show original linenumber (Default: off)", OUTPUT_SHOW_NUMBER),
OPT_BIT('p', "porcelain", &output_option, "Show in a format designed for machine consumption", OUTPUT_PORCELAIN),
+ OPT_BIT(0, "line-porcelain", &output_option, "Show porcelain format with per-line commit information", OUTPUT_PORCELAIN|OUTPUT_LINE_PORCELAIN),
OPT_BIT('c', NULL, &output_option, "Use the same output mode as git-annotate (Default: off)", OUTPUT_ANNOTATE_COMPAT),
OPT_BIT('t', NULL, &output_option, "Show raw timestamp (Default: off)", OUTPUT_RAW_TIMESTAMP),
OPT_BIT('l', NULL, &output_option, "Show long commit SHA1 (Default: off)", OUTPUT_LONG_OBJECT_NAME),
{ OPTION_CALLBACK, 'C', NULL, &opt, "score", "Find line copies within and across files", PARSE_OPT_OPTARG, blame_copy_callback },
{ OPTION_CALLBACK, 'M', NULL, &opt, "score", "Find line movements within and across files", PARSE_OPT_OPTARG, blame_move_callback },
OPT_CALLBACK('L', NULL, &bottomtop, "n,m", "Process only line range n,m, counting from 1", blame_bottomtop_callback),
+ OPT__ABBREV(&abbrev),
OPT_END()
};
parse_done:
argc = parse_options_end(&ctx);
+ if (abbrev == -1)
+ abbrev = default_abbrev;
+ /* one more abbrev length is needed for the boundary commit */
+ abbrev++;
+
if (revs_file && read_ancestry(revs_file))
die_errno("reading graft file '%s' failed", revs_file);