builtin-log.c 4.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/*
 * Builtin "git log" and related commands (show, whatchanged)
 *
 * (C) Copyright 2006 Linus Torvalds
 *		 2006 Junio Hamano
 */
#include "cache.h"
#include "commit.h"
#include "diff.h"
#include "revision.h"
#include "log-tree.h"
12
#include "builtin.h"
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70

static int cmd_log_wc(int argc, const char **argv, char **envp,
		      struct rev_info *rev)
{
	struct commit *commit;

	rev->abbrev = DEFAULT_ABBREV;
	rev->commit_format = CMIT_FMT_DEFAULT;
	rev->verbose_header = 1;
	argc = setup_revisions(argc, argv, rev, "HEAD");

	if (argc > 1)
		die("unrecognized argument: %s", argv[1]);

	prepare_revision_walk(rev);
	setup_pager();
	while ((commit = get_revision(rev)) != NULL) {
		log_tree_commit(rev, commit);
		free(commit->buffer);
		commit->buffer = NULL;
	}
	return 0;
}

int cmd_whatchanged(int argc, const char **argv, char **envp)
{
	struct rev_info rev;

	init_revisions(&rev);
	rev.diff = 1;
	rev.diffopt.recursive = 1;
	return cmd_log_wc(argc, argv, envp, &rev);
}

int cmd_show(int argc, const char **argv, char **envp)
{
	struct rev_info rev;

	init_revisions(&rev);
	rev.diff = 1;
	rev.diffopt.recursive = 1;
	rev.combine_merges = 1;
	rev.dense_combined_merges = 1;
	rev.always_show_header = 1;
	rev.ignore_merges = 0;
	rev.no_walk = 1;
	return cmd_log_wc(argc, argv, envp, &rev);
}

int cmd_log(int argc, const char **argv, char **envp)
{
	struct rev_info rev;

	init_revisions(&rev);
	rev.always_show_header = 1;
	rev.diffopt.recursive = 1;
	return cmd_log_wc(argc, argv, envp, &rev);
}
71

72 73 74 75 76 77
static int istitlechar(char c)
{
	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
		(c >= '0' && c <= '9') || c == '.' || c == '_';
}

78
static FILE *realstdout = NULL;
79
static char *output_directory = NULL;
80

81
static void reopen_stdout(struct commit *commit, int nr, int keep_subject)
82 83 84
{
	char filename[1024];
	char *sol;
85
	int len = 0;
86

87 88 89 90 91 92
	if (output_directory) {
		strncpy(filename, output_directory, 1010);
		len = strlen(filename);
		if (filename[len - 1] != '/')
			filename[len++] = '/';
	}
93

94
	sprintf(filename + len, "%04d", nr);
95 96 97 98 99 100 101 102
	len = strlen(filename);

	sol = strstr(commit->buffer, "\n\n");
	if (sol) {
		int j, space = 1;

		sol += 2;
		/* strip [PATCH] or [PATCH blabla] */
103
		if (!keep_subject && !strncmp(sol, "[PATCH", 6)) {
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
			char *eos = strchr(sol + 6, ']');
			if (eos) {
				while (isspace(*eos))
					eos++;
				sol = eos;
			}
		}

		for (j = 0; len < 1024 - 6 && sol[j] && sol[j] != '\n'; j++) {
			if (istitlechar(sol[j])) {
				if (space) {
					filename[len++] = '-';
					space = 0;
				}
				filename[len++] = sol[j];
				if (sol[j] == '.')
					while (sol[j + 1] == '.')
						j++;
			} else
				space = 1;
		}
		while (filename[len - 1] == '.' || filename[len - 1] == '-')
			len--;
	}
	strcpy(filename + len, ".txt");
129
	fprintf(realstdout, "%s\n", filename);
130 131 132
	freopen(filename, "w", stdout);
}

133 134 135 136 137
int cmd_format_patch(int argc, const char **argv, char **envp)
{
	struct commit *commit;
	struct commit **list = NULL;
	struct rev_info rev;
138
	int nr = 0, total, i, j;
139
	int use_stdout = 0;
140
	int numbered = 0;
141
	int keep_subject = 0;
142 143 144 145 146 147 148 149 150 151 152

	init_revisions(&rev);
	rev.commit_format = CMIT_FMT_EMAIL;
	rev.verbose_header = 1;
	rev.diff = 1;
	rev.diffopt.with_raw = 0;
	rev.diffopt.with_stat = 1;
	rev.combine_merges = 0;
	rev.ignore_merges = 1;
	rev.diffopt.output_format = DIFF_FORMAT_PATCH;

153 154 155 156 157 158 159
	/*
	 * Parse the arguments before setup_revisions(), or something
	 * like "git fmt-patch -o a123 HEAD^.." may fail; a123 is
	 * possibly a valid SHA1.
	 */
	for (i = 1, j = 1; i < argc; i++) {
		if (!strcmp(argv[i], "--stdout"))
160
			use_stdout = 1;
161 162 163
		else if (!strcmp(argv[i], "-n") ||
				!strcmp(argv[i], "--numbered"))
			numbered = 1;
164 165 166 167 168
		else if (!strcmp(argv[i], "-k") ||
				!strcmp(argv[i], "--keep-subject")) {
			keep_subject = 1;
			rev.total = -1;
		} else if (!strcmp(argv[i], "-o")) {
169 170 171 172 173 174 175 176 177
			if (argc < 3)
				die ("Which directory?");
			if (mkdir(argv[i + 1], 0777) < 0 && errno != EEXIST)
				die("Could not create directory %s",
						argv[i + 1]);
			output_directory = strdup(argv[i + 1]);
			i++;
		} else
			argv[j++] = argv[i];
178
	}
179 180
	argc = j;

181 182 183
	if (numbered && keep_subject < 0)
		die ("-n and -k are mutually exclusive.");

184 185 186
	argc = setup_revisions(argc, argv, &rev, "HEAD");
	if (argc > 1)
		die ("unrecognized argument: %s", argv[1]);
187

188 189 190
	if (!use_stdout)
		realstdout = fdopen(dup(1), "w");

191 192
	prepare_revision_walk(&rev);
	while ((commit = get_revision(&rev)) != NULL) {
193 194 195
		/* ignore merges */
		if (commit->parents && commit->parents->next)
			continue;
196 197 198 199
		nr++;
		list = realloc(list, nr * sizeof(list[0]));
		list[nr - 1] = commit;
	}
200
	total = nr;
201 202
	if (numbered)
		rev.total = total;
203 204 205
	while (0 <= --nr) {
		int shown;
		commit = list[nr];
206
		rev.nr = total - nr;
207
		if (!use_stdout)
208
			reopen_stdout(commit, rev.nr, keep_subject);
209 210 211 212 213
		shown = log_tree_commit(&rev, commit);
		free(commit->buffer);
		commit->buffer = NULL;
		if (shown)
			printf("-- \n%s\n\n", git_version_string);
214 215
		if (!use_stdout)
			fclose(stdout);
216
	}
217 218
	if (output_directory)
		free(output_directory);
219 220 221 222
	free(list);
	return 0;
}