#include "log-tree.h"
#include "builtin.h"
#include "tag.h"
+#include "reflog-walk.h"
static int default_show_root = 1;
static void cmd_log_init(int argc, const char **argv, const char *prefix,
struct rev_info *rev)
{
+ int i;
+
rev->abbrev = DEFAULT_ABBREV;
rev->commit_format = CMIT_FMT_DEFAULT;
rev->verbose_header = 1;
argc = setup_revisions(argc, argv, rev, "HEAD");
if (rev->diffopt.pickaxe || rev->diffopt.filter)
rev->always_show_header = 0;
- if (argc > 1)
- die("unrecognized argument: %s", argv[1]);
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (!prefixcmp(arg, "--encoding=")) {
+ arg += 11;
+ if (strcmp(arg, "none"))
+ git_log_output_encoding = strdup(arg);
+ else
+ git_log_output_encoding = "";
+ }
+ else
+ die("unrecognized argument: %s", arg);
+ }
}
static int cmd_log_walk(struct rev_info *rev)
prepare_revision_walk(rev);
while ((commit = get_revision(rev)) != NULL) {
log_tree_commit(rev, commit);
- free(commit->buffer);
- commit->buffer = NULL;
+ if (!rev->reflog_info) {
+ /* we allow cycles in reflog ancestry */
+ free(commit->buffer);
+ commit->buffer = NULL;
+ }
free_commit_list(commit->parents);
commit->parents = NULL;
}
static int show_object(const unsigned char *sha1, int suppress_header)
{
unsigned long size;
- char type[20];
- char *buf = read_sha1_file(sha1, type, &size);
+ enum object_type type;
+ char *buf = read_sha1_file(sha1, &type, &size);
int offset = 0;
if (!buf)
return ret;
}
+/*
+ * This is equivalent to "git log -g --abbrev-commit --pretty=oneline"
+ */
+int cmd_log_reflog(int argc, const char **argv, const char *prefix)
+{
+ struct rev_info rev;
+
+ git_config(git_log_config);
+ init_revisions(&rev, prefix);
+ init_reflog_walk(&rev.reflog_info);
+ rev.abbrev_commit = 1;
+ rev.verbose_header = 1;
+ cmd_log_init(argc, argv, prefix, &rev);
+
+ /*
+ * This means that we override whatever commit format the user gave
+ * on the cmd line. Sad, but cmd_log_init() currently doesn't
+ * allow us to set a different default.
+ */
+ rev.commit_format = CMIT_FMT_ONELINE;
+ rev.always_show_header = 1;
+
+ /*
+ * We get called through "git reflog", so unlike the other log
+ * routines, we need to set up our pager manually..
+ */
+ setup_pager();
+
+ return cmd_log_walk(&rev);
+}
+
int cmd_log(int argc, const char **argv, const char *prefix)
{
struct rev_info rev;
return cmd_log_walk(&rev);
}
+/* format-patch */
+#define FORMAT_PATCH_NAME_MAX 64
+
static int istitlechar(char c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
static char *extra_headers = NULL;
static int extra_headers_size = 0;
+static const char *fmt_patch_suffix = ".patch";
static int git_format_config(const char *var, const char *value)
{
if (!strcmp(var, "format.headers")) {
- int len = strlen(value);
+ int len;
+
+ if (!value)
+ die("format.headers without value");
+ len = strlen(value);
extra_headers_size += len + 1;
extra_headers = xrealloc(extra_headers, extra_headers_size);
extra_headers[extra_headers_size - len - 1] = 0;
strcat(extra_headers, value);
return 0;
}
+ if (!strcmp(var, "format.suffix")) {
+ if (!value)
+ die("format.suffix without value");
+ fmt_patch_suffix = xstrdup(value);
+ return 0;
+ }
if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
return 0;
}
static FILE *realstdout = NULL;
static const char *output_directory = NULL;
-static void reopen_stdout(struct commit *commit, int nr, int keep_subject)
+static int reopen_stdout(struct commit *commit, int nr, int keep_subject)
{
- char filename[1024];
+ char filename[PATH_MAX];
char *sol;
int len = 0;
+ int suffix_len = strlen(fmt_patch_suffix) + 1;
if (output_directory) {
- strlcpy(filename, output_directory, 1010);
+ if (strlen(output_directory) >=
+ sizeof(filename) - FORMAT_PATCH_NAME_MAX - suffix_len)
+ return error("name of output directory is too long");
+ strlcpy(filename, output_directory, sizeof(filename) - suffix_len);
len = strlen(filename);
if (filename[len - 1] != '/')
filename[len++] = '/';
sol += 2;
/* strip [PATCH] or [PATCH blabla] */
- if (!keep_subject && !strncmp(sol, "[PATCH", 6)) {
+ if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
char *eos = strchr(sol + 6, ']');
if (eos) {
while (isspace(*eos))
}
}
- for (j = 0; len < 1024 - 6 && sol[j] && sol[j] != '\n'; j++) {
+ for (j = 0;
+ j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
+ len < sizeof(filename) - suffix_len &&
+ sol[j] && sol[j] != '\n';
+ j++) {
if (istitlechar(sol[j])) {
if (space) {
filename[len++] = '-';
}
while (filename[len - 1] == '.' || filename[len - 1] == '-')
len--;
+ filename[len] = 0;
}
- strcpy(filename + len, ".txt");
+ if (len + suffix_len >= sizeof(filename))
+ return error("Patch pathname too long");
+ strcpy(filename + len, fmt_patch_suffix);
fprintf(realstdout, "%s\n", filename);
- freopen(filename, "w", stdout);
+ if (freopen(filename, "w", stdout) == NULL)
+ return error("Cannot open patch file %s",filename);
+ return 0;
+
}
static int get_patch_id(struct commit *commit, struct diff_options *options,
static void gen_message_id(char *dest, unsigned int length, char *base)
{
- const char *committer = git_committer_info(1);
+ const char *committer = git_committer_info(-1);
const char *email_start = strrchr(committer, '<');
const char *email_end = strrchr(committer, '>');
if(!email_start || !email_end || email_start > email_end - 1)
char message_id[1024];
char ref_message_id[1024];
- setup_ident();
git_config(git_format_config);
init_revisions(&rev, prefix);
rev.commit_format = CMIT_FMT_EMAIL;
else if (!strcmp(argv[i], "-n") ||
!strcmp(argv[i], "--numbered"))
numbered = 1;
- else if (!strncmp(argv[i], "--start-number=", 15))
+ else if (!prefixcmp(argv[i], "--start-number="))
start_number = strtol(argv[i] + 15, NULL, 10);
else if (!strcmp(argv[i], "--start-number")) {
i++;
memcpy(add_signoff, committer, endpos - committer + 1);
add_signoff[endpos - committer + 1] = 0;
}
- else if (!strcmp(argv[i], "--attach"))
+ else if (!strcmp(argv[i], "--attach")) {
rev.mime_boundary = git_version_string;
- else if (!strncmp(argv[i], "--attach=", 9))
+ rev.no_inline = 1;
+ }
+ else if (!prefixcmp(argv[i], "--attach=")) {
rev.mime_boundary = argv[i] + 9;
+ rev.no_inline = 1;
+ }
+ else if (!strcmp(argv[i], "--inline")) {
+ rev.mime_boundary = git_version_string;
+ rev.no_inline = 0;
+ }
+ else if (!prefixcmp(argv[i], "--inline=")) {
+ rev.mime_boundary = argv[i] + 9;
+ rev.no_inline = 0;
+ }
else if (!strcmp(argv[i], "--ignore-if-in-upstream"))
ignore_if_in_upstream = 1;
else if (!strcmp(argv[i], "--thread"))
thread = 1;
- else if (!strncmp(argv[i], "--in-reply-to=", 14))
+ else if (!prefixcmp(argv[i], "--in-reply-to="))
in_reply_to = argv[i] + 14;
else if (!strcmp(argv[i], "--in-reply-to")) {
i++;
die("Need a Message-Id for --in-reply-to");
in_reply_to = argv[i];
}
+ else if (!prefixcmp(argv[i], "--suffix="))
+ fmt_patch_suffix = argv[i] + 9;
else
argv[j++] = argv[i];
}
die ("unrecognized argument: %s", argv[1]);
if (!rev.diffopt.output_format)
- rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;
+ rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH;
+
+ if (!rev.diffopt.text)
+ rev.diffopt.binary = 1;
- if (!output_directory)
+ if (!output_directory && !use_stdout)
output_directory = prefix;
if (output_directory) {
}
if (rev.pending.nr == 1) {
- rev.pending.objects[0].item->flags |= UNINTERESTING;
- add_head(&rev);
+ if (rev.max_count < 0) {
+ rev.pending.objects[0].item->flags |= UNINTERESTING;
+ add_head(&rev);
+ }
+ /* Otherwise, it is "format-patch -22 HEAD", and
+ * get_revision() would return only the specified count.
+ */
}
if (ignore_if_in_upstream)
rev.message_id = message_id;
}
if (!use_stdout)
- reopen_stdout(commit, rev.nr, keep_subject);
+ if (reopen_stdout(commit, rev.nr, keep_subject))
+ die("Failed to create output files");
shown = log_tree_commit(&rev, commit);
free(commit->buffer);
commit->buffer = NULL;