summaryrefslogtreecommitdiff
path: root/builtin/log.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/log.c')
-rw-r--r--builtin/log.c207
1 files changed, 153 insertions, 54 deletions
diff --git a/builtin/log.c b/builtin/log.c
index 6e56a50002..7b343c1aaf 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -19,6 +19,7 @@
#include "remote.h"
#include "string-list.h"
#include "parse-options.h"
+#include "line-log.h"
#include "branch.h"
#include "streaming.h"
#include "version.h"
@@ -37,11 +38,17 @@ static const char *fmt_patch_subject_prefix = "PATCH";
static const char *fmt_pretty;
static const char * const builtin_log_usage[] = {
- N_("git log [<options>] [<revision range>] [[--] <path>...]\n")
- N_(" or: git show [options] <object>..."),
+ N_("git log [<options>] [<revision-range>] [[--] <path>...]"),
+ N_("git show [<options>] <object>..."),
NULL
};
+struct line_opt_callback_data {
+ struct rev_info *rev;
+ const char *prefix;
+ struct string_list args;
+};
+
static int parse_decoration_style(const char *var, const char *value)
{
switch (git_config_maybe_bool(var, value)) {
@@ -56,6 +63,8 @@ static int parse_decoration_style(const char *var, const char *value)
return DECORATE_FULL_REFS;
else if (!strcmp(value, "short"))
return DECORATE_SHORT_REFS;
+ else if (!strcmp(value, "auto"))
+ return (isatty(1) || pager_in_use()) ? DECORATE_SHORT_REFS : 0;
return -1;
}
@@ -69,13 +78,26 @@ static int decorate_callback(const struct option *opt, const char *arg, int unse
decoration_style = DECORATE_SHORT_REFS;
if (decoration_style < 0)
- die("invalid --decorate option: %s", arg);
+ die(_("invalid --decorate option: %s"), arg);
decoration_given = 1;
return 0;
}
+static int log_line_range_callback(const struct option *option, const char *arg, int unset)
+{
+ struct line_opt_callback_data *data = option->value;
+
+ if (!arg)
+ return -1;
+
+ data->rev->line_level_traverse = 1;
+ string_list_append(&data->args, arg);
+
+ return 0;
+}
+
static void cmd_log_init_defaults(struct rev_info *rev)
{
if (fmt_pretty)
@@ -91,6 +113,7 @@ static void cmd_log_init_defaults(struct rev_info *rev)
if (default_date_mode)
rev->date_mode = parse_date_format(default_date_mode);
+ rev->diffopt.touched_flags = 0;
}
static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
@@ -98,16 +121,23 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
{
struct userformat_want w;
int quiet = 0, source = 0, mailmap = 0;
+ static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP};
const struct option builtin_log_options[] = {
- OPT_BOOL(0, "quiet", &quiet, N_("suppress diff output")),
+ OPT__QUIET(&quiet, N_("suppress diff output")),
OPT_BOOL(0, "source", &source, N_("show source")),
OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")),
{ OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
PARSE_OPT_OPTARG, decorate_callback},
+ OPT_CALLBACK('L', NULL, &line_cb, "n,m:file",
+ N_("Process line range n,m in file, counting from 1"),
+ log_line_range_callback),
OPT_END()
};
+ line_cb.rev = rev;
+ line_cb.prefix = prefix;
+
mailmap = use_mailmap_config;
argc = parse_options(argc, argv, prefix,
builtin_log_options, builtin_log_usage,
@@ -120,7 +150,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
/* Any arguments at this point are not recognized */
if (argc > 1)
- die("unrecognized argument: %s", argv[1]);
+ die(_("unrecognized argument: %s"), argv[1]);
memset(&w, 0, sizeof(w));
userformat_find_requirements(NULL, &w);
@@ -130,13 +160,9 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
if (rev->show_notes)
init_display_notes(&rev->notes_opt);
- if (rev->diffopt.pickaxe || rev->diffopt.filter)
+ if (rev->diffopt.pickaxe || rev->diffopt.filter ||
+ DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES))
rev->always_show_header = 0;
- if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) {
- rev->always_show_header = 0;
- if (rev->diffopt.pathspec.nr != 1)
- usage("git logs can only follow renames on one pathname at a time");
- }
if (source)
rev->show_source = 1;
@@ -161,6 +187,10 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
rev->show_decorations = 1;
load_ref_decorations(decoration_style);
}
+
+ if (rev->line_level_traverse)
+ line_log_init(rev, line_cb.prefix, &line_cb.args);
+
setup_pager();
}
@@ -206,7 +236,7 @@ static void log_show_early(struct rev_info *revs, struct commit_list *list)
int i = revs->early_output;
int show_header = 1;
- sort_in_topological_order(&list, revs->lifo);
+ sort_in_topological_order(&list, revs->sort_order);
while (list && i) {
struct commit *commit = list->item;
switch (simplify_commit(revs, commit)) {
@@ -317,8 +347,7 @@ static int cmd_log_walk(struct rev_info *rev)
rev->max_count++;
if (!rev->reflog_info) {
/* we allow cycles in reflog ancestry */
- free(commit->buffer);
- commit->buffer = NULL;
+ free_commit_buffer(commit);
}
free_commit_list(commit->parents);
commit->parents = NULL;
@@ -339,6 +368,8 @@ static int cmd_log_walk(struct rev_info *rev)
static int git_log_config(const char *var, const char *value, void *cb)
{
+ const char *slot_name;
+
if (!strcmp(var, "format.pretty"))
return git_config_string(&fmt_pretty, var, value);
if (!strcmp(var, "format.subjectprefix"))
@@ -359,8 +390,8 @@ static int git_log_config(const char *var, const char *value, void *cb)
default_show_root = git_config_bool(var, value);
return 0;
}
- if (!prefixcmp(var, "color.decorate."))
- return parse_decorate_color_config(var, 15, value);
+ if (skip_prefix(var, "color.decorate.", &slot_name))
+ return parse_decorate_color_config(var, slot_name, value);
if (!strcmp(var, "log.mailmap")) {
use_mailmap_config = git_config_bool(var, value);
return 0;
@@ -405,10 +436,29 @@ static void show_tagger(char *buf, int len, struct rev_info *rev)
strbuf_release(&out);
}
-static int show_blob_object(const unsigned char *sha1, struct rev_info *rev)
+static int show_blob_object(const unsigned char *sha1, struct rev_info *rev, const char *obj_name)
{
+ unsigned char sha1c[20];
+ struct object_context obj_context;
+ char *buf;
+ unsigned long size;
+
fflush(stdout);
- return stream_blob_to_fd(1, sha1, NULL, 0);
+ if (!DIFF_OPT_TOUCHED(&rev->diffopt, ALLOW_TEXTCONV) ||
+ !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))
+ return stream_blob_to_fd(1, sha1, NULL, 0);
+
+ if (get_sha1_with_context(obj_name, 0, sha1c, &obj_context))
+ die(_("Not a valid object name %s"), obj_name);
+ if (!obj_context.path[0] ||
+ !textconv_object(obj_context.path, obj_context.mode, sha1c, 1, &buf, &size))
+ return stream_blob_to_fd(1, sha1, NULL, 0);
+
+ if (!buf)
+ die(_("git show %s: bad file"), obj_name);
+
+ write_or_die(1, buf, size);
+ return 0;
}
static int show_tag_object(const unsigned char *sha1, struct rev_info *rev)
@@ -426,7 +476,7 @@ static int show_tag_object(const unsigned char *sha1, struct rev_info *rev)
int new_offset = offset + 1;
while (new_offset < size && buf[new_offset++] != '\n')
; /* do nothing */
- if (!prefixcmp(buf + offset, "tagger "))
+ if (starts_with(buf + offset, "tagger "))
show_tagger(buf + offset + 7,
new_offset - offset - 7, rev);
offset = new_offset;
@@ -439,7 +489,7 @@ static int show_tag_object(const unsigned char *sha1, struct rev_info *rev)
}
static int show_tree_object(const unsigned char *sha1,
- const char *base, int baselen,
+ struct strbuf *base,
const char *pathname, unsigned mode, int stage, void *context)
{
printf("%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
@@ -452,7 +502,7 @@ static void show_rev_tweak_rev(struct rev_info *rev, struct setup_revision_opt *
/* There was no "-m" on the command line */
rev->ignore_merges = 0;
if (!rev->first_parent_only && !rev->combine_merges) {
- /* No "--first-parent", "-c", nor "--cc" */
+ /* No "--first-parent", "-c", or "--cc" */
rev->combine_merges = 1;
rev->dense_combined_merges = 1;
}
@@ -472,7 +522,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
init_grep_defaults();
git_config(git_log_config, NULL);
- init_pathspec(&match_all, NULL);
+ memset(&match_all, 0, sizeof(match_all));
init_revisions(&rev, prefix);
rev.diff = 1;
rev.always_show_header = 1;
@@ -494,7 +544,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
const char *name = objects[i].name;
switch (o->type) {
case OBJ_BLOB:
- ret = show_blob_object(o->sha1, NULL);
+ ret = show_blob_object(o->sha1, &rev, name);
break;
case OBJ_TAG: {
struct tag *t = (struct tag *)o;
@@ -622,6 +672,7 @@ static void add_header(const char *value)
static int thread;
static int do_signoff;
static const char *signature = git_version_string;
+static const char *signature_file;
static int config_cover_letter;
enum {
@@ -654,7 +705,7 @@ static int git_format_config(const char *var, const char *value, void *cb)
return 0;
}
if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff") ||
- !strcmp(var, "color.ui")) {
+ !strcmp(var, "color.ui") || !strcmp(var, "diff.submodule")) {
return 0;
}
if (!strcmp(var, "format.numbered")) {
@@ -691,6 +742,8 @@ static int git_format_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "format.signature"))
return git_config_string(&signature, var, value);
+ if (!strcmp(var, "format.signaturefile"))
+ return git_config_pathname(&signature_file, var, value);
if (!strcmp(var, "format.coverletter")) {
if (value && !strcasecmp(value, "auto")) {
config_cover_letter = COVER_AUTO;
@@ -742,7 +795,7 @@ static int reopen_stdout(struct commit *commit, const char *subject,
static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
{
struct rev_info check_rev;
- struct commit *commit;
+ struct commit *commit, *c1, *c2;
struct object *o1, *o2;
unsigned flags1, flags2;
@@ -750,9 +803,11 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
die(_("Need exactly one range."));
o1 = rev->pending.objects[0].item;
- flags1 = o1->flags;
o2 = rev->pending.objects[1].item;
+ flags1 = o1->flags;
flags2 = o2->flags;
+ c1 = lookup_commit_reference(o1->sha1);
+ c2 = lookup_commit_reference(o2->sha1);
if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
die(_("Not a range."));
@@ -774,10 +829,8 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
}
/* reset for next revision walk */
- clear_commit_marks((struct commit *)o1,
- SEEN | UNINTERESTING | SHOWN | ADDED);
- clear_commit_marks((struct commit *)o2,
- SEEN | UNINTERESTING | SHOWN | ADDED);
+ clear_commit_marks(c1, SEEN | UNINTERESTING | SHOWN | ADDED);
+ clear_commit_marks(c2, SEEN | UNINTERESTING | SHOWN | ADDED);
o1->flags = flags1;
o2->flags = flags2;
}
@@ -793,8 +846,13 @@ static void gen_message_id(struct rev_info *info, char *base)
static void print_signature(void)
{
- if (signature && *signature)
- printf("-- \n%s\n\n", signature);
+ if (!signature || !*signature)
+ return;
+
+ printf("-- \n%s", signature);
+ if (signature[strlen(signature)-1] != '\n')
+ putchar('\n');
+ putchar('\n');
}
static void add_branch_description(struct strbuf *buf, const char *branch_name)
@@ -805,9 +863,10 @@ static void add_branch_description(struct strbuf *buf, const char *branch_name)
read_branch_desc(&desc, branch_name);
if (desc.len) {
strbuf_addch(buf, '\n');
- strbuf_add(buf, desc.buf, desc.len);
+ strbuf_addbuf(buf, &desc);
strbuf_addch(buf, '\n');
}
+ strbuf_release(&desc);
}
static char *find_branch_name(struct rev_info *rev)
@@ -815,7 +874,7 @@ static char *find_branch_name(struct rev_info *rev)
int i, positive = -1;
unsigned char branch_sha1[20];
const unsigned char *tip_sha1;
- const char *ref;
+ const char *ref, *v;
char *full_ref, *branch = NULL;
for (i = 0; i < rev->cmdline.nr; i++) {
@@ -831,9 +890,9 @@ static char *find_branch_name(struct rev_info *rev)
ref = rev->cmdline.rev[positive].name;
tip_sha1 = rev->cmdline.rev[positive].item->sha1;
if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) &&
- !prefixcmp(full_ref, "refs/heads/") &&
+ skip_prefix(full_ref, "refs/heads/", &v) &&
!hashcmp(tip_sha1, branch_sha1))
- branch = xstrdup(full_ref + strlen("refs/heads/"));
+ branch = xstrdup(v);
free(full_ref);
return branch;
}
@@ -868,9 +927,12 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
&need_8bit_cte);
- for (i = 0; !need_8bit_cte && i < nr; i++)
- if (has_non_ascii(list[i]->buffer))
+ for (i = 0; !need_8bit_cte && i < nr; i++) {
+ const char *buf = get_commit_buffer(list[i], NULL);
+ if (has_non_ascii(buf))
need_8bit_cte = 1;
+ unuse_commit_buffer(list[i], buf);
+ }
if (!branch_name)
branch_name = find_branch_name(rev);
@@ -961,7 +1023,7 @@ static const char *set_outdir(const char *prefix, const char *output_directory)
}
static const char * const builtin_format_patch_usage[] = {
- N_("git format-patch [options] [<since> | <revision range>]"),
+ N_("git format-patch [<options>] [<since> | <revision-range>]"),
NULL
};
@@ -1081,6 +1143,21 @@ static int cc_callback(const struct option *opt, const char *arg, int unset)
return 0;
}
+static int from_callback(const struct option *opt, const char *arg, int unset)
+{
+ char **from = opt->value;
+
+ free(*from);
+
+ if (unset)
+ *from = NULL;
+ else if (arg)
+ *from = xstrdup(arg);
+ else
+ *from = xstrdup(git_committer_info(IDENT_NO_DATE));
+ return 0;
+}
+
int cmd_format_patch(int argc, const char **argv, const char *prefix)
{
struct commit *commit;
@@ -1103,6 +1180,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
int quiet = 0;
int reroll_count = -1;
char *branch_name = NULL;
+ char *from = NULL;
const struct option builtin_format_patch_options[] = {
{ OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
N_("use [PATCH n/m] even with a single patch"),
@@ -1132,13 +1210,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
{ OPTION_CALLBACK, 'k', "keep-subject", &rev, NULL,
N_("don't strip/add [PATCH]"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback },
- OPT_BOOLEAN(0, "no-binary", &no_binary_diff,
- N_("don't output binary diffs")),
- OPT_BOOLEAN(0, "ignore-if-in-upstream", &ignore_if_in_upstream,
- N_("don't include a patch matching a commit upstream")),
- { OPTION_BOOLEAN, 'p', "no-stat", &use_patch_format, NULL,
+ OPT_BOOL(0, "no-binary", &no_binary_diff,
+ N_("don't output binary diffs")),
+ OPT_BOOL(0, "ignore-if-in-upstream", &ignore_if_in_upstream,
+ N_("don't include a patch matching a commit upstream")),
+ { OPTION_SET_INT, 'p', "no-stat", &use_patch_format, NULL,
N_("show patch format instead of default (patch + stat)"),
- PARSE_OPT_NONEG | PARSE_OPT_NOARG },
+ PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1},
OPT_GROUP(N_("Messaging")),
{ OPTION_CALLBACK, 0, "add-header", NULL, N_("header"),
N_("add email header"), 0, header_callback },
@@ -1146,6 +1224,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
0, to_callback },
{ OPTION_CALLBACK, 0, "cc", NULL, N_("email"), N_("add Cc: header"),
0, cc_callback },
+ { OPTION_CALLBACK, 0, "from", &from, N_("ident"),
+ N_("set From address to <ident> (or committer ident if absent)"),
+ PARSE_OPT_OPTARG, from_callback },
OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"),
N_("make first mail a reply to <message-id>")),
{ OPTION_CALLBACK, 0, "attach", &rev, N_("boundary"),
@@ -1160,8 +1241,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
PARSE_OPT_OPTARG, thread_callback },
OPT_STRING(0, "signature", &signature, N_("signature"),
N_("add a signature")),
- OPT_BOOLEAN(0, "quiet", &quiet,
- N_("don't print the patch filenames")),
+ OPT_FILENAME(0, "signature-file", &signature_file,
+ N_("add a signature from a file")),
+ OPT__QUIET(&quiet, N_("don't print the patch filenames")),
OPT_END()
};
@@ -1233,6 +1315,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.extra_headers = strbuf_detach(&buf, NULL);
+ if (from) {
+ if (split_ident_line(&rev.from_ident, from, strlen(from)))
+ die(_("invalid ident line: %s"), from);
+ }
+
if (start_number < 0)
start_number = 1;
@@ -1312,10 +1399,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (check_head) {
unsigned char sha1[20];
- const char *ref;
- ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
- if (ref && !prefixcmp(ref, "refs/heads/"))
- branch_name = xstrdup(ref + strlen("refs/heads/"));
+ const char *ref, *v;
+ ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
+ sha1, NULL);
+ if (ref && skip_prefix(ref, "refs/heads/", &v))
+ branch_name = xstrdup(v);
else
branch_name = xstrdup(""); /* no branch */
}
@@ -1355,7 +1443,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
continue;
nr++;
- list = xrealloc(list, nr * sizeof(list[0]));
+ REALLOC_ARRAY(list, nr);
list[nr - 1] = commit;
}
if (nr == 0)
@@ -1373,6 +1461,18 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
cover_letter = (config_cover_letter == COVER_ON);
}
+ if (!signature) {
+ ; /* --no-signature inhibits all signatures */
+ } else if (signature && signature != git_version_string) {
+ ; /* non-default signature already set */
+ } else if (signature_file) {
+ struct strbuf buf = STRBUF_INIT;
+
+ if (strbuf_read_file(&buf, signature_file, 128) < 0)
+ die_errno(_("unable to read signature file '%s'"), signature_file);
+ signature = strbuf_detach(&buf, NULL);
+ }
+
if (in_reply_to || thread || cover_letter)
rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
if (in_reply_to) {
@@ -1434,8 +1534,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
die(_("Failed to create output files"));
shown = log_tree_commit(&rev, commit);
- free(commit->buffer);
- commit->buffer = NULL;
+ free_commit_buffer(commit);
/* We put one extra blank line between formatted
* patches and this flag is used by log-tree code