From 3eefc189172b88dece6fb6d479b3ed13cc483dbc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 18 Apr 2006 16:45:27 -0700 Subject: [PATCH 01/40] Tentative built-in format-patch. This only does --stdout right now. To write into separate files with pretty-printed filenames like the real thing does, it needs a bit mroe work. Signed-off-by: Junio C Hamano --- commit.c | 36 +++++++++++++++++++++++++++++++----- commit.h | 1 + git.c | 38 ++++++++++++++++++++++++++++++++++++++ log-tree.c | 27 +++++++++++++++++++-------- 4 files changed, 89 insertions(+), 13 deletions(-) diff --git a/commit.c b/commit.c index 2717dd81c3..af747bde61 100644 --- a/commit.c +++ b/commit.c @@ -36,6 +36,8 @@ enum cmit_fmt get_commit_format(const char *arg) return CMIT_FMT_FULL; if (!strcmp(arg, "=fuller")) return CMIT_FMT_FULLER; + if (!strcmp(arg, "=email")) + return CMIT_FMT_EMAIL; if (!strcmp(arg, "=oneline")) return CMIT_FMT_ONELINE; die("invalid --pretty format"); @@ -428,6 +430,10 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c time = strtoul(date, &date, 10); tz = strtol(date, NULL, 10); + if (fmt == CMIT_FMT_EMAIL) { + what = "From"; + filler = ""; + } ret = sprintf(buf, "%s: %.*s%.*s\n", what, (fmt == CMIT_FMT_FULLER) ? 4 : 0, filler, namelen, line); @@ -435,6 +441,9 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c case CMIT_FMT_MEDIUM: ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz)); break; + case CMIT_FMT_EMAIL: + ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz)); + break; case CMIT_FMT_FULLER: ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz)); break; @@ -445,10 +454,12 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c return ret; } -static int is_empty_line(const char *line, int len) +static int is_empty_line(const char *line, int *len_p) { + int len = *len_p; while (len && isspace(line[len-1])) len--; + *len_p = len; return !len; } @@ -457,7 +468,8 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com struct commit_list *parent = commit->parents; int offset; - if ((fmt == CMIT_FMT_ONELINE) || !parent || !parent->next) + if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) || + !parent || !parent->next) return 0; offset = sprintf(buf, "Merge:"); @@ -480,9 +492,15 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit { int hdr = 1, body = 0; unsigned long offset = 0; - int indent = (fmt == CMIT_FMT_ONELINE) ? 0 : 4; + int indent = 4; int parents_shown = 0; const char *msg = commit->buffer; + const char *subject = NULL; + + if (fmt == CMIT_FMT_EMAIL) + subject = "Subject: "; + if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) + indent = 0; for (;;) { const char *line = msg; @@ -506,7 +524,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit if (hdr) { if (linelen == 1) { hdr = 0; - if (fmt != CMIT_FMT_ONELINE) + if ((fmt != CMIT_FMT_ONELINE) && !subject) buf[offset++] = '\n'; continue; } @@ -544,20 +562,28 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit continue; } - if (is_empty_line(line, linelen)) { + if (is_empty_line(line, &linelen)) { if (!body) continue; + if (subject) + continue; if (fmt == CMIT_FMT_SHORT) break; } else { body = 1; } + if (subject) { + memcpy(buf + offset, subject, 9); + offset += 9; + } memset(buf + offset, ' ', indent); memcpy(buf + offset + indent, line, linelen); offset += linelen + indent; + buf[offset++] = '\n'; if (fmt == CMIT_FMT_ONELINE) break; + subject = NULL; } while (offset && isspace(buf[offset-1])) offset--; diff --git a/commit.h b/commit.h index de142afe73..01eec60a1e 100644 --- a/commit.h +++ b/commit.h @@ -45,6 +45,7 @@ enum cmit_fmt { CMIT_FMT_FULL, CMIT_FMT_FULLER, CMIT_FMT_ONELINE, + CMIT_FMT_EMAIL, CMIT_FMT_UNSPECIFIED, }; diff --git a/git.c b/git.c index 0be14bb487..0c40773a87 100644 --- a/git.c +++ b/git.c @@ -276,6 +276,43 @@ static int cmd_help(int argc, const char **argv, char **envp) return 0; } +static int cmd_format_patch(int argc, const char **argv, char **envp) +{ + struct commit *commit; + struct commit **list = NULL; + struct rev_info rev; + int nr = 0; + + 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; + argc = setup_revisions(argc, argv, &rev, "HEAD"); + + prepare_revision_walk(&rev); + while ((commit = get_revision(&rev)) != NULL) { + nr++; + list = realloc(list, nr * sizeof(list[0])); + list[nr - 1] = commit; + } + while (0 <= --nr) { + int shown; + commit = list[nr]; + shown = log_tree_commit(&rev, commit); + free(commit->buffer); + commit->buffer = NULL; + if (shown) + printf("-- \n%s\n\n", GIT_VERSION); + } + free(list); + return 0; +} + static int cmd_log_wc(int argc, const char **argv, char **envp, struct rev_info *rev) { @@ -348,6 +385,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "log", cmd_log }, { "whatchanged", cmd_wc }, { "show", cmd_show }, + { "format-patch", cmd_format_patch }, }; int i; diff --git a/log-tree.c b/log-tree.c index 9634c4677f..aaf2b9423f 100644 --- a/log-tree.c +++ b/log-tree.c @@ -37,12 +37,20 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) /* * Print header line of header.. */ - printf("%s%s", - opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ", - diff_unique_abbrev(commit->object.sha1, abbrev_commit)); - if (parent) - printf(" (from %s)", diff_unique_abbrev(parent->object.sha1, abbrev_commit)); - putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n'); + + if (opt->commit_format == CMIT_FMT_EMAIL) + printf("From %s Thu Apr 7 15:13:13 2005\n", + sha1_to_hex(commit->object.sha1)); + else { + printf("%s%s", + opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ", + diff_unique_abbrev(commit->object.sha1, abbrev_commit)); + if (parent) + printf(" (from %s)", + diff_unique_abbrev(parent->object.sha1, + abbrev_commit)); + putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n'); + } /* * And then the pretty-printed message itself @@ -152,15 +160,18 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log int log_tree_commit(struct rev_info *opt, struct commit *commit) { struct log_info log; + int shown; log.commit = commit; log.parent = NULL; opt->loginfo = &log; - if (!log_tree_diff(opt, commit, &log) && opt->loginfo && opt->always_show_header) { + shown = log_tree_diff(opt, commit, &log); + if (!shown && opt->loginfo && opt->always_show_header) { log.parent = NULL; show_log(opt, opt->loginfo, ""); + shown = 1; } opt->loginfo = NULL; - return 0; + return shown; } From 4c4b158b8f31a19f724a84b11fa538bfbf465d32 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 19 Apr 2006 15:16:08 -0700 Subject: [PATCH 02/40] Minor tweak on subject line in --pretty=email Signed-off-by: Junio C Hamano --- commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commit.c b/commit.c index af747bde61..06e00987cc 100644 --- a/commit.c +++ b/commit.c @@ -498,7 +498,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit const char *subject = NULL; if (fmt == CMIT_FMT_EMAIL) - subject = "Subject: "; + subject = "Subject: [PATCH] "; if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) indent = 0; From 43885c2a557871aedcbb1a412fc18e98c3dd1dc4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 20 Apr 2006 12:45:02 -0700 Subject: [PATCH 03/40] rename internal format-patch wip Otherwise "git format-patch" would invoke unfinished internal one that does only --stdout Signed-off-by: Junio C Hamano --- git.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git.c b/git.c index 0c40773a87..d5aa41df92 100644 --- a/git.c +++ b/git.c @@ -385,7 +385,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "log", cmd_log }, { "whatchanged", cmd_wc }, { "show", cmd_show }, - { "format-patch", cmd_format_patch }, + { "fmt-patch", cmd_format_patch }, }; int i; From 53f420ef00ca6cc3554084c4c9fb89f50c634f58 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 22 Apr 2006 03:06:13 -0700 Subject: [PATCH 04/40] git-fmt-patch: thinkofix to show [PATCH] properly. Updating "subject" variable without changing the hardcoded number of bytes to memcpy from it would not help much. Signed-off-by: Junio C Hamano --- commit.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/commit.c b/commit.c index 06e00987cc..f4e4eea014 100644 --- a/commit.c +++ b/commit.c @@ -574,8 +574,9 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit } if (subject) { - memcpy(buf + offset, subject, 9); - offset += 9; + int slen = strlen(subject); + memcpy(buf + offset, subject, slen); + offset += slen; } memset(buf + offset, ' ', indent); memcpy(buf + offset + indent, line, linelen); From 2a3870432395e78c82a870bf1a177b8d5b8fbdf0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 1 May 2006 01:44:33 -0700 Subject: [PATCH 05/40] Use RFC2822 dates from "git fmt-patch". Still Work-in-progress git fmt-patch (should it be known as format-patch-ng?) is matched with the fix made by Huw Davies in 262a6ef76a1dde97ab50d79fa5cd6d3f9f125765 commit to use RFC2822 date format. Signed-off-by: Junio C Hamano --- cache.h | 1 + commit.c | 3 ++- date.c | 29 +++++++++++++++++++++++++---- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/cache.h b/cache.h index 69801b02d5..f6c5909598 100644 --- a/cache.h +++ b/cache.h @@ -249,6 +249,7 @@ extern void *read_object_with_reference(const unsigned char *sha1, unsigned char *sha1_ret); const char *show_date(unsigned long time, int timezone); +const char *show_rfc2822_date(unsigned long time, int timezone); int parse_date(const char *date, char *buf, int bufsize); void datestamp(char *buf, int bufsize); unsigned long approxidate(const char *); diff --git a/commit.c b/commit.c index f4e4eea014..42b44bba52 100644 --- a/commit.c +++ b/commit.c @@ -442,7 +442,8 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz)); break; case CMIT_FMT_EMAIL: - ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz)); + ret += sprintf(buf + ret, "Date: %s\n", + show_rfc2822_date(time, tz)); break; case CMIT_FMT_FULLER: ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz)); diff --git a/date.c b/date.c index 034d7228bf..365dc3b14e 100644 --- a/date.c +++ b/date.c @@ -42,18 +42,24 @@ static const char *weekday_names[] = { * thing, which means that tz -0100 is passed in as the integer -100, * even though it means "sixty minutes off" */ -const char *show_date(unsigned long time, int tz) +static struct tm *time_to_tm(unsigned long time, int tz) { - struct tm *tm; time_t t; - static char timebuf[200]; int minutes; minutes = tz < 0 ? -tz : tz; minutes = (minutes / 100)*60 + (minutes % 100); minutes = tz < 0 ? -minutes : minutes; t = time + minutes * 60; - tm = gmtime(&t); + return gmtime(&t); +} + +const char *show_date(unsigned long time, int tz) +{ + struct tm *tm; + static char timebuf[200]; + + tm = time_to_tm(time, tz); if (!tm) return NULL; sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d %+05d", @@ -65,6 +71,21 @@ const char *show_date(unsigned long time, int tz) return timebuf; } +const char *show_rfc2822_date(unsigned long time, int tz) +{ + struct tm *tm; + static char timebuf[200]; + + tm = time_to_tm(time, tz); + if (!tm) + return NULL; + sprintf(timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d", + weekday_names[tm->tm_wday], tm->tm_mday, + month_names[tm->tm_mon], tm->tm_year + 1900, + tm->tm_hour, tm->tm_min, tm->tm_sec, tz); + return timebuf; +} + /* * Check these. And note how it doesn't do the summer-time conversion. * From 0377db77da3f66b8a2bd2f8a9391d22f7a576e34 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 5 May 2006 01:16:40 +0200 Subject: [PATCH 06/40] Teach fmt-patch to write individual files. When called with "--stdout", it still writes to standard output. Notable differences to git-format-patch: - since fmt-patch uses the standardized logging machinery, it is no longer "From nobody", but "From ", - the empty lines before and after the "---" just before the diffstat are no longer there, - git-format-patch outputs the commit_sha1 just before the first diff, which fmt-patch does not, - the file names are no longer output to stdout, but to stderr (since stdout is freopen()ed all the time), and - "git fmt-patch HEAD^" does not work as expected: it outputs *all* commits reachable from HEAD^! The last one is possibly a showstopper. At least I used to call that command quite often... Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-log.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/builtin-log.c b/builtin-log.c index a39aed6d86..576703c47e 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -69,12 +69,65 @@ int cmd_log(int argc, const char **argv, char **envp) return cmd_log_wc(argc, argv, envp, &rev); } +static int istitlechar(char c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || c == '.' || c == '_'; +} + +static void reopen_stdout(struct commit *commit, int nr) +{ + char filename[1024]; + char *sol; + int len; + + + sprintf(filename, "%04d", nr); + len = strlen(filename); + + sol = strstr(commit->buffer, "\n\n"); + if (sol) { + int j, space = 1; + + sol += 2; + /* strip [PATCH] or [PATCH blabla] */ + if (!strncmp(sol, "[PATCH", 6)) { + 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"); + fprintf(stderr, "%s\n", filename); + freopen(filename, "w", stdout); +} + int cmd_format_patch(int argc, const char **argv, char **envp) { struct commit *commit; struct commit **list = NULL; struct rev_info rev; - int nr = 0; + int nr = 0, total; + int use_stdout = 0; init_revisions(&rev); rev.commit_format = CMIT_FMT_EMAIL; @@ -87,20 +140,37 @@ int cmd_format_patch(int argc, const char **argv, char **envp) rev.diffopt.output_format = DIFF_FORMAT_PATCH; argc = setup_revisions(argc, argv, &rev, "HEAD"); + while (argc > 1) { + if (!strcmp(argv[1], "--stdout")) + use_stdout = 1; + else + die ("unrecognized argument: %s", argv[1]); + argc--; + argv++; + } + prepare_revision_walk(&rev); while ((commit = get_revision(&rev)) != NULL) { + /* ignore merges */ + if (commit->parents && commit->parents->next) + continue; nr++; list = realloc(list, nr * sizeof(list[0])); list[nr - 1] = commit; } + total = nr; while (0 <= --nr) { int shown; commit = list[nr]; + if (!use_stdout) + reopen_stdout(commit, total - nr); shown = log_tree_commit(&rev, commit); free(commit->buffer); commit->buffer = NULL; if (shown) printf("-- \n%s\n\n", git_version_string); + if (!use_stdout) + fclose(stdout); } free(list); return 0; From 81f3a188a3781fa4a818ed27ee83430682a98ec4 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 5 May 2006 03:33:05 +0200 Subject: [PATCH 07/40] fmt-patch: output file names to stdout Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-log.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/builtin-log.c b/builtin-log.c index 576703c47e..1649f4943e 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -75,6 +75,8 @@ static int istitlechar(char c) (c >= '0' && c <= '9') || c == '.' || c == '_'; } +static FILE *realstdout = NULL; + static void reopen_stdout(struct commit *commit, int nr) { char filename[1024]; @@ -117,7 +119,7 @@ static void reopen_stdout(struct commit *commit, int nr) len--; } strcpy(filename + len, ".txt"); - fprintf(stderr, "%s\n", filename); + fprintf(realstdout, "%s\n", filename); freopen(filename, "w", stdout); } @@ -149,6 +151,9 @@ int cmd_format_patch(int argc, const char **argv, char **envp) argv++; } + if (!use_stdout) + realstdout = fdopen(dup(1), "w"); + prepare_revision_walk(&rev); while ((commit = get_revision(&rev)) != NULL) { /* ignore merges */ From 2448482b3d5e265dd29fa38c3827565f6f2f31ec Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 5 May 2006 03:33:32 +0200 Subject: [PATCH 08/40] fmt-patch: implement -o I had to move the command line parsing around a little; setup_revisions() could mistaken for a valid ref. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-log.c | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/builtin-log.c b/builtin-log.c index 1649f4943e..53a47c9aa4 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -76,15 +76,22 @@ static int istitlechar(char c) } static FILE *realstdout = NULL; +static char *output_directory = NULL; static void reopen_stdout(struct commit *commit, int nr) { char filename[1024]; char *sol; - int len; + int len = 0; + if (output_directory) { + strncpy(filename, output_directory, 1010); + len = strlen(filename); + if (filename[len - 1] != '/') + filename[len++] = '/'; + } - sprintf(filename, "%04d", nr); + sprintf(filename + len, "%04d", nr); len = strlen(filename); sol = strstr(commit->buffer, "\n\n"); @@ -128,7 +135,7 @@ int cmd_format_patch(int argc, const char **argv, char **envp) struct commit *commit; struct commit **list = NULL; struct rev_info rev; - int nr = 0, total; + int nr = 0, total, i, j; int use_stdout = 0; init_revisions(&rev); @@ -140,16 +147,31 @@ int cmd_format_patch(int argc, const char **argv, char **envp) rev.combine_merges = 0; rev.ignore_merges = 1; rev.diffopt.output_format = DIFF_FORMAT_PATCH; - argc = setup_revisions(argc, argv, &rev, "HEAD"); - while (argc > 1) { - if (!strcmp(argv[1], "--stdout")) + /* + * 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")) use_stdout = 1; - else - die ("unrecognized argument: %s", argv[1]); - argc--; - argv++; + else if (!strcmp(argv[i], "-o")) { + 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]; } + argc = j; + + argc = setup_revisions(argc, argv, &rev, "HEAD"); + if (argc > 1) + die ("unrecognized argument: %s", argv[1]); if (!use_stdout) realstdout = fdopen(dup(1), "w"); @@ -177,6 +199,8 @@ int cmd_format_patch(int argc, const char **argv, char **envp) if (!use_stdout) fclose(stdout); } + if (output_directory) + free(output_directory); free(list); return 0; } From 596524b33d50e47e2375cec9e00aff59f0e8278b Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 5 May 2006 04:30:52 +0200 Subject: [PATCH 09/40] Teach fmt-patch about --numbered Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-log.c | 9 ++++++++- commit.c | 5 +---- commit.h | 2 +- log-tree.c | 15 ++++++++++++--- rev-list.c | 2 +- revision.h | 1 + show-branch.c | 2 +- 7 files changed, 25 insertions(+), 11 deletions(-) diff --git a/builtin-log.c b/builtin-log.c index 53a47c9aa4..43c7ecd5e9 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -137,6 +137,7 @@ int cmd_format_patch(int argc, const char **argv, char **envp) struct rev_info rev; int nr = 0, total, i, j; int use_stdout = 0; + int numbered = 0; init_revisions(&rev); rev.commit_format = CMIT_FMT_EMAIL; @@ -156,6 +157,9 @@ int cmd_format_patch(int argc, const char **argv, char **envp) for (i = 1, j = 1; i < argc; i++) { if (!strcmp(argv[i], "--stdout")) use_stdout = 1; + else if (!strcmp(argv[i], "-n") || + !strcmp(argv[i], "--numbered")) + numbered = 1; else if (!strcmp(argv[i], "-o")) { if (argc < 3) die ("Which directory?"); @@ -186,11 +190,14 @@ int cmd_format_patch(int argc, const char **argv, char **envp) list[nr - 1] = commit; } total = nr; + if (numbered) + rev.total = total; while (0 <= --nr) { int shown; commit = list[nr]; + rev.nr = total - nr; if (!use_stdout) - reopen_stdout(commit, total - nr); + reopen_stdout(commit, rev.nr); shown = log_tree_commit(&rev, commit); free(commit->buffer); commit->buffer = NULL; diff --git a/commit.c b/commit.c index 42b44bba52..93b3903ea7 100644 --- a/commit.c +++ b/commit.c @@ -489,17 +489,14 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com return offset; } -unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev) +unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject) { int hdr = 1, body = 0; unsigned long offset = 0; int indent = 4; int parents_shown = 0; const char *msg = commit->buffer; - const char *subject = NULL; - if (fmt == CMIT_FMT_EMAIL) - subject = "Subject: [PATCH] "; if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) indent = 0; diff --git a/commit.h b/commit.h index 01eec60a1e..8d7514cd00 100644 --- a/commit.h +++ b/commit.h @@ -51,7 +51,7 @@ enum cmit_fmt { }; extern enum cmit_fmt get_commit_format(const char *arg); -extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev); +extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject); /** Removes the first commit from a list sorted by date, and adds all * of its parents. diff --git a/log-tree.c b/log-tree.c index aaf2b9423f..1ca529d9fe 100644 --- a/log-tree.c +++ b/log-tree.c @@ -11,6 +11,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40; const char *extra; int len; + char* subject = NULL; opt->loginfo = NULL; if (!opt->verbose_header) { @@ -38,10 +39,18 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) * Print header line of header.. */ - if (opt->commit_format == CMIT_FMT_EMAIL) + if (opt->commit_format == CMIT_FMT_EMAIL) { + if (opt->total > 0) { + static char buffer[64]; + snprintf(buffer, sizeof(buffer), + "Subject: [PATCH %d/%d] ", + opt->nr, opt->total); + subject = buffer; + } else + subject = "Subject: [PATCH] "; printf("From %s Thu Apr 7 15:13:13 2005\n", sha1_to_hex(commit->object.sha1)); - else { + } else { printf("%s%s", opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ", diff_unique_abbrev(commit->object.sha1, abbrev_commit)); @@ -55,7 +64,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) /* * And then the pretty-printed message itself */ - len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev); + len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev, subject); printf("%s%s%s", this_header, extra, sep); } diff --git a/rev-list.c b/rev-list.c index 8b0ec388fa..235ae4c7e1 100644 --- a/rev-list.c +++ b/rev-list.c @@ -84,7 +84,7 @@ static void show_commit(struct commit *commit) static char pretty_header[16384]; pretty_print_commit(revs.commit_format, commit, ~0, pretty_header, sizeof(pretty_header), - revs.abbrev); + revs.abbrev, NULL); printf("%s%c", pretty_header, hdr_termination); } fflush(stdout); diff --git a/revision.h b/revision.h index 48d7b4ca94..62759f7bc0 100644 --- a/revision.h +++ b/revision.h @@ -58,6 +58,7 @@ struct rev_info { unsigned int abbrev; enum cmit_fmt commit_format; struct log_info *loginfo; + int nr, total; /* special limits */ int max_count; diff --git a/show-branch.c b/show-branch.c index 24efb65e62..5da3a1a90b 100644 --- a/show-branch.c +++ b/show-branch.c @@ -259,7 +259,7 @@ static void show_one_commit(struct commit *commit, int no_name) struct commit_name *name = commit->object.util; if (commit->object.parsed) pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, - pretty, sizeof(pretty), 0); + pretty, sizeof(pretty), 0, NULL); else strcpy(pretty, "(unavailable)"); if (!strncmp(pretty, "[PATCH] ", 8)) From 8ac80a5701780547404523a84f4b1ae67bfa6823 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 5 May 2006 04:31:29 +0200 Subject: [PATCH 10/40] Teach fmt-patch about --keep-subject Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-log.c | 16 ++++++++++++---- log-tree.c | 5 ++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/builtin-log.c b/builtin-log.c index 43c7ecd5e9..0027998f10 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -78,7 +78,7 @@ static int istitlechar(char c) static FILE *realstdout = NULL; static char *output_directory = NULL; -static void reopen_stdout(struct commit *commit, int nr) +static void reopen_stdout(struct commit *commit, int nr, int keep_subject) { char filename[1024]; char *sol; @@ -100,7 +100,7 @@ static void reopen_stdout(struct commit *commit, int nr) sol += 2; /* strip [PATCH] or [PATCH blabla] */ - if (!strncmp(sol, "[PATCH", 6)) { + if (!keep_subject && !strncmp(sol, "[PATCH", 6)) { char *eos = strchr(sol + 6, ']'); if (eos) { while (isspace(*eos)) @@ -138,6 +138,7 @@ int cmd_format_patch(int argc, const char **argv, char **envp) int nr = 0, total, i, j; int use_stdout = 0; int numbered = 0; + int keep_subject = 0; init_revisions(&rev); rev.commit_format = CMIT_FMT_EMAIL; @@ -160,7 +161,11 @@ int cmd_format_patch(int argc, const char **argv, char **envp) else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--numbered")) numbered = 1; - else if (!strcmp(argv[i], "-o")) { + else if (!strcmp(argv[i], "-k") || + !strcmp(argv[i], "--keep-subject")) { + keep_subject = 1; + rev.total = -1; + } else if (!strcmp(argv[i], "-o")) { if (argc < 3) die ("Which directory?"); if (mkdir(argv[i + 1], 0777) < 0 && errno != EEXIST) @@ -173,6 +178,9 @@ int cmd_format_patch(int argc, const char **argv, char **envp) } argc = j; + if (numbered && keep_subject < 0) + die ("-n and -k are mutually exclusive."); + argc = setup_revisions(argc, argv, &rev, "HEAD"); if (argc > 1) die ("unrecognized argument: %s", argv[1]); @@ -197,7 +205,7 @@ int cmd_format_patch(int argc, const char **argv, char **envp) commit = list[nr]; rev.nr = total - nr; if (!use_stdout) - reopen_stdout(commit, rev.nr); + reopen_stdout(commit, rev.nr, keep_subject); shown = log_tree_commit(&rev, commit); free(commit->buffer); commit->buffer = NULL; diff --git a/log-tree.c b/log-tree.c index 1ca529d9fe..4a3cd6791b 100644 --- a/log-tree.c +++ b/log-tree.c @@ -46,8 +46,11 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) "Subject: [PATCH %d/%d] ", opt->nr, opt->total); subject = buffer; - } else + } else if (opt->total == 0) subject = "Subject: [PATCH] "; + else + subject = "Subject: "; + printf("From %s Thu Apr 7 15:13:13 2005\n", sha1_to_hex(commit->object.sha1)); } else { From e686eb9870d6b382f0760e3e859e93c8c2dfb31b Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 6 May 2006 22:56:38 +0200 Subject: [PATCH 11/40] fmt-patch: understand old notation When calling "git fmt-patch HEAD~5", you now get the same as if you would have said "git fmt-patch HEAD~5..". This makes it easier for my fingers which are so used to the old syntax. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-diff.c | 2 +- builtin-log.c | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/builtin-diff.c b/builtin-diff.c index 636edbf2a7..20873162f9 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -232,7 +232,7 @@ static int builtin_diff_combined(struct rev_info *revs, return 0; } -static void add_head(struct rev_info *revs) +void add_head(struct rev_info *revs) { unsigned char sha1[20]; struct object *obj; diff --git a/builtin-log.c b/builtin-log.c index 0027998f10..d5bbc1cc06 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -11,6 +11,9 @@ #include "log-tree.h" #include "builtin.h" +/* this is in builtin-diff.c */ +void add_head(struct rev_info *revs); + static int cmd_log_wc(int argc, const char **argv, char **envp, struct rev_info *rev) { @@ -185,6 +188,11 @@ int cmd_format_patch(int argc, const char **argv, char **envp) if (argc > 1) die ("unrecognized argument: %s", argv[1]); + if (rev.pending_objects && rev.pending_objects->next == NULL) { + rev.pending_objects->item->flags |= UNINTERESTING; + add_head(&rev); + } + if (!use_stdout) realstdout = fdopen(dup(1), "w"); From 453ec4bdf403c2e89892266a0a660c21680d3f9d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 16 May 2006 19:02:14 -0700 Subject: [PATCH 12/40] libify git-ls-files directory traversal This moves the core directory traversal and filename exclusion logic into the general git library, making it available for other users directly. If we ever want to do "git commit" or "git add" as a built-in (and we do), we want to be able to handle most of git-ls-files as a library. NOTE! Not all of git-ls-files is libified by this. The index matching and pathspec prefix calculation is still in ls-files.c, but this is a big part of it. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- Makefile | 4 +- dir.c | 295 +++++++++++++++++++++++++++++++++++++++++++ dir.h | 50 ++++++++ ls-files.c | 363 +++++------------------------------------------------ 4 files changed, 376 insertions(+), 336 deletions(-) create mode 100644 dir.c create mode 100644 dir.h diff --git a/Makefile b/Makefile index 9ba608c805..f43ac63c93 100644 --- a/Makefile +++ b/Makefile @@ -199,7 +199,7 @@ LIB_H = \ blob.h cache.h commit.h csum-file.h delta.h \ diff.h object.h pack.h pkt-line.h quote.h refs.h \ run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \ - tree-walk.h log-tree.h + tree-walk.h log-tree.h dir.h DIFF_OBJS = \ diff.o diff-lib.o diffcore-break.o diffcore-order.o \ @@ -210,7 +210,7 @@ LIB_OBJS = \ blob.o commit.o connect.o csum-file.o base85.o \ date.o diff-delta.o entry.o exec_cmd.o ident.o index.o \ object.o pack-check.o patch-delta.o path.o pkt-line.o \ - quote.o read-cache.o refs.o run-command.o \ + quote.o read-cache.o refs.o run-command.o dir.o \ server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \ tag.o tree.o usage.o config.o environment.o ctype.o copy.o \ fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \ diff --git a/dir.c b/dir.c new file mode 100644 index 0000000000..3f41a5dfea --- /dev/null +++ b/dir.c @@ -0,0 +1,295 @@ +/* + * This handles recursive filename detection with exclude + * files, index knowledge etc.. + * + * Copyright (C) Linus Torvalds, 2005-2006 + * Junio Hamano, 2005-2006 + */ +#include +#include + +#include "cache.h" +#include "dir.h" + +void add_exclude(const char *string, const char *base, + int baselen, struct exclude_list *which) +{ + struct exclude *x = xmalloc(sizeof (*x)); + + x->pattern = string; + x->base = base; + x->baselen = baselen; + if (which->nr == which->alloc) { + which->alloc = alloc_nr(which->alloc); + which->excludes = realloc(which->excludes, + which->alloc * sizeof(x)); + } + which->excludes[which->nr++] = x; +} + +static int add_excludes_from_file_1(const char *fname, + const char *base, + int baselen, + struct exclude_list *which) +{ + int fd, i; + long size; + char *buf, *entry; + + fd = open(fname, O_RDONLY); + if (fd < 0) + goto err; + size = lseek(fd, 0, SEEK_END); + if (size < 0) + goto err; + lseek(fd, 0, SEEK_SET); + if (size == 0) { + close(fd); + return 0; + } + buf = xmalloc(size+1); + if (read(fd, buf, size) != size) + goto err; + close(fd); + + buf[size++] = '\n'; + entry = buf; + for (i = 0; i < size; i++) { + if (buf[i] == '\n') { + if (entry != buf + i && entry[0] != '#') { + buf[i - (i && buf[i-1] == '\r')] = 0; + add_exclude(entry, base, baselen, which); + } + entry = buf + i + 1; + } + } + return 0; + + err: + if (0 <= fd) + close(fd); + return -1; +} + +void add_excludes_from_file(struct dir_struct *dir, const char *fname) +{ + if (add_excludes_from_file_1(fname, "", 0, + &dir->exclude_list[EXC_FILE]) < 0) + die("cannot use %s as an exclude file", fname); +} + +int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen) +{ + char exclude_file[PATH_MAX]; + struct exclude_list *el = &dir->exclude_list[EXC_DIRS]; + int current_nr = el->nr; + + if (dir->exclude_per_dir) { + memcpy(exclude_file, base, baselen); + strcpy(exclude_file + baselen, dir->exclude_per_dir); + add_excludes_from_file_1(exclude_file, base, baselen, el); + } + return current_nr; +} + +static void pop_exclude_per_directory(struct dir_struct *dir, int stk) +{ + struct exclude_list *el = &dir->exclude_list[EXC_DIRS]; + + while (stk < el->nr) + free(el->excludes[--el->nr]); +} + +/* Scan the list and let the last match determines the fate. + * Return 1 for exclude, 0 for include and -1 for undecided. + */ +static int excluded_1(const char *pathname, + int pathlen, + struct exclude_list *el) +{ + int i; + + if (el->nr) { + for (i = el->nr - 1; 0 <= i; i--) { + struct exclude *x = el->excludes[i]; + const char *exclude = x->pattern; + int to_exclude = 1; + + if (*exclude == '!') { + to_exclude = 0; + exclude++; + } + + if (!strchr(exclude, '/')) { + /* match basename */ + const char *basename = strrchr(pathname, '/'); + basename = (basename) ? basename+1 : pathname; + if (fnmatch(exclude, basename, 0) == 0) + return to_exclude; + } + else { + /* match with FNM_PATHNAME: + * exclude has base (baselen long) implicitly + * in front of it. + */ + int baselen = x->baselen; + if (*exclude == '/') + exclude++; + + if (pathlen < baselen || + (baselen && pathname[baselen-1] != '/') || + strncmp(pathname, x->base, baselen)) + continue; + + if (fnmatch(exclude, pathname+baselen, + FNM_PATHNAME) == 0) + return to_exclude; + } + } + } + return -1; /* undecided */ +} + +int excluded(struct dir_struct *dir, const char *pathname) +{ + int pathlen = strlen(pathname); + int st; + + for (st = EXC_CMDL; st <= EXC_FILE; st++) { + switch (excluded_1(pathname, pathlen, &dir->exclude_list[st])) { + case 0: + return 0; + case 1: + return 1; + } + } + return 0; +} + +static void add_name(struct dir_struct *dir, const char *pathname, int len) +{ + struct dir_entry *ent; + + if (cache_name_pos(pathname, len) >= 0) + return; + + if (dir->nr == dir->alloc) { + int alloc = alloc_nr(dir->alloc); + dir->alloc = alloc; + dir->entries = xrealloc(dir->entries, alloc*sizeof(ent)); + } + ent = xmalloc(sizeof(*ent) + len + 1); + ent->len = len; + memcpy(ent->name, pathname, len); + ent->name[len] = 0; + dir->entries[dir->nr++] = ent; +} + +static int dir_exists(const char *dirname, int len) +{ + int pos = cache_name_pos(dirname, len); + if (pos >= 0) + return 1; + pos = -pos-1; + if (pos >= active_nr) /* can't */ + return 0; + return !strncmp(active_cache[pos]->name, dirname, len); +} + +/* + * Read a directory tree. We currently ignore anything but + * directories, regular files and symlinks. That's because git + * doesn't handle them at all yet. Maybe that will change some + * day. + * + * Also, we ignore the name ".git" (even if it is not a directory). + * That likely will not change. + */ +static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen) +{ + DIR *fdir = opendir(path); + int contents = 0; + + if (fdir) { + int exclude_stk; + struct dirent *de; + char fullname[MAXPATHLEN + 1]; + memcpy(fullname, base, baselen); + + exclude_stk = push_exclude_per_directory(dir, base, baselen); + + while ((de = readdir(fdir)) != NULL) { + int len; + + if ((de->d_name[0] == '.') && + (de->d_name[1] == 0 || + !strcmp(de->d_name + 1, ".") || + !strcmp(de->d_name + 1, "git"))) + continue; + len = strlen(de->d_name); + memcpy(fullname + baselen, de->d_name, len+1); + if (excluded(dir, fullname) != dir->show_ignored) { + if (!dir->show_ignored || DTYPE(de) != DT_DIR) { + continue; + } + } + + switch (DTYPE(de)) { + struct stat st; + int subdir, rewind_base; + default: + continue; + case DT_UNKNOWN: + if (lstat(fullname, &st)) + continue; + if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) + break; + if (!S_ISDIR(st.st_mode)) + continue; + /* fallthrough */ + case DT_DIR: + memcpy(fullname + baselen + len, "/", 2); + len++; + rewind_base = dir->nr; + subdir = read_directory_recursive(dir, fullname, fullname, + baselen + len); + if (dir->show_other_directories && + (subdir || !dir->hide_empty_directories) && + !dir_exists(fullname, baselen + len)) { + // Rewind the read subdirectory + while (dir->nr > rewind_base) + free(dir->entries[--dir->nr]); + break; + } + contents += subdir; + continue; + case DT_REG: + case DT_LNK: + break; + } + add_name(dir, fullname, baselen + len); + contents++; + } + closedir(fdir); + + pop_exclude_per_directory(dir, exclude_stk); + } + + return contents; +} + +static int cmp_name(const void *p1, const void *p2) +{ + const struct dir_entry *e1 = *(const struct dir_entry **)p1; + const struct dir_entry *e2 = *(const struct dir_entry **)p2; + + return cache_name_compare(e1->name, e1->len, + e2->name, e2->len); +} + +int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen) +{ + read_directory_recursive(dir, path, base, baselen); + qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name); + return dir->nr; +} diff --git a/dir.h b/dir.h new file mode 100644 index 0000000000..e8fc441e7f --- /dev/null +++ b/dir.h @@ -0,0 +1,50 @@ +#ifndef DIR_H +#define DIR_H + +/* + * We maintain three exclude pattern lists: + * EXC_CMDL lists patterns explicitly given on the command line. + * EXC_DIRS lists patterns obtained from per-directory ignore files. + * EXC_FILE lists patterns from fallback ignore files. + */ +#define EXC_CMDL 0 +#define EXC_DIRS 1 +#define EXC_FILE 2 + + +struct dir_entry { + int len; + char name[FLEX_ARRAY]; /* more */ +}; + +struct exclude_list { + int nr; + int alloc; + struct exclude { + const char *pattern; + const char *base; + int baselen; + } **excludes; +}; + +struct dir_struct { + int nr, alloc; + unsigned int show_ignored:1, + show_other_directories:1, + hide_empty_directories:1; + struct dir_entry **entries; + + /* Exclude info */ + const char *exclude_per_dir; + struct exclude_list exclude_list[3]; +}; + +extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen); +extern int excluded(struct dir_struct *, const char *); +extern void add_excludes_from_file(struct dir_struct *, const char *fname); +extern void add_exclude(const char *string, const char *base, + int baselen, struct exclude_list *which); +extern int push_exclude_per_directory(struct dir_struct *, + const char *base, int baselen); + +#endif diff --git a/ls-files.c b/ls-files.c index 4a4af1ca3b..89941a3ff8 100644 --- a/ls-files.c +++ b/ls-files.c @@ -5,23 +5,20 @@ * * Copyright (C) Linus Torvalds, 2005 */ -#include #include #include "cache.h" #include "quote.h" +#include "dir.h" static int abbrev = 0; static int show_deleted = 0; static int show_cached = 0; static int show_others = 0; -static int show_ignored = 0; static int show_stage = 0; static int show_unmerged = 0; static int show_modified = 0; static int show_killed = 0; -static int show_other_directories = 0; -static int hide_empty_directories = 0; static int show_valid_bit = 0; static int line_terminator = '\n'; @@ -38,309 +35,6 @@ static const char *tag_other = ""; static const char *tag_killed = ""; static const char *tag_modified = ""; -static const char *exclude_per_dir = NULL; - -/* We maintain three exclude pattern lists: - * EXC_CMDL lists patterns explicitly given on the command line. - * EXC_DIRS lists patterns obtained from per-directory ignore files. - * EXC_FILE lists patterns from fallback ignore files. - */ -#define EXC_CMDL 0 -#define EXC_DIRS 1 -#define EXC_FILE 2 -static struct exclude_list { - int nr; - int alloc; - struct exclude { - const char *pattern; - const char *base; - int baselen; - } **excludes; -} exclude_list[3]; - -static void add_exclude(const char *string, const char *base, - int baselen, struct exclude_list *which) -{ - struct exclude *x = xmalloc(sizeof (*x)); - - x->pattern = string; - x->base = base; - x->baselen = baselen; - if (which->nr == which->alloc) { - which->alloc = alloc_nr(which->alloc); - which->excludes = realloc(which->excludes, - which->alloc * sizeof(x)); - } - which->excludes[which->nr++] = x; -} - -static int add_excludes_from_file_1(const char *fname, - const char *base, - int baselen, - struct exclude_list *which) -{ - int fd, i; - long size; - char *buf, *entry; - - fd = open(fname, O_RDONLY); - if (fd < 0) - goto err; - size = lseek(fd, 0, SEEK_END); - if (size < 0) - goto err; - lseek(fd, 0, SEEK_SET); - if (size == 0) { - close(fd); - return 0; - } - buf = xmalloc(size+1); - if (read(fd, buf, size) != size) - goto err; - close(fd); - - buf[size++] = '\n'; - entry = buf; - for (i = 0; i < size; i++) { - if (buf[i] == '\n') { - if (entry != buf + i && entry[0] != '#') { - buf[i - (i && buf[i-1] == '\r')] = 0; - add_exclude(entry, base, baselen, which); - } - entry = buf + i + 1; - } - } - return 0; - - err: - if (0 <= fd) - close(fd); - return -1; -} - -static void add_excludes_from_file(const char *fname) -{ - if (add_excludes_from_file_1(fname, "", 0, - &exclude_list[EXC_FILE]) < 0) - die("cannot use %s as an exclude file", fname); -} - -static int push_exclude_per_directory(const char *base, int baselen) -{ - char exclude_file[PATH_MAX]; - struct exclude_list *el = &exclude_list[EXC_DIRS]; - int current_nr = el->nr; - - if (exclude_per_dir) { - memcpy(exclude_file, base, baselen); - strcpy(exclude_file + baselen, exclude_per_dir); - add_excludes_from_file_1(exclude_file, base, baselen, el); - } - return current_nr; -} - -static void pop_exclude_per_directory(int stk) -{ - struct exclude_list *el = &exclude_list[EXC_DIRS]; - - while (stk < el->nr) - free(el->excludes[--el->nr]); -} - -/* Scan the list and let the last match determines the fate. - * Return 1 for exclude, 0 for include and -1 for undecided. - */ -static int excluded_1(const char *pathname, - int pathlen, - struct exclude_list *el) -{ - int i; - - if (el->nr) { - for (i = el->nr - 1; 0 <= i; i--) { - struct exclude *x = el->excludes[i]; - const char *exclude = x->pattern; - int to_exclude = 1; - - if (*exclude == '!') { - to_exclude = 0; - exclude++; - } - - if (!strchr(exclude, '/')) { - /* match basename */ - const char *basename = strrchr(pathname, '/'); - basename = (basename) ? basename+1 : pathname; - if (fnmatch(exclude, basename, 0) == 0) - return to_exclude; - } - else { - /* match with FNM_PATHNAME: - * exclude has base (baselen long) implicitly - * in front of it. - */ - int baselen = x->baselen; - if (*exclude == '/') - exclude++; - - if (pathlen < baselen || - (baselen && pathname[baselen-1] != '/') || - strncmp(pathname, x->base, baselen)) - continue; - - if (fnmatch(exclude, pathname+baselen, - FNM_PATHNAME) == 0) - return to_exclude; - } - } - } - return -1; /* undecided */ -} - -static int excluded(const char *pathname) -{ - int pathlen = strlen(pathname); - int st; - - for (st = EXC_CMDL; st <= EXC_FILE; st++) { - switch (excluded_1(pathname, pathlen, &exclude_list[st])) { - case 0: - return 0; - case 1: - return 1; - } - } - return 0; -} - -struct nond_on_fs { - int len; - char name[FLEX_ARRAY]; /* more */ -}; - -static struct nond_on_fs **dir; -static int nr_dir; -static int dir_alloc; - -static void add_name(const char *pathname, int len) -{ - struct nond_on_fs *ent; - - if (cache_name_pos(pathname, len) >= 0) - return; - - if (nr_dir == dir_alloc) { - dir_alloc = alloc_nr(dir_alloc); - dir = xrealloc(dir, dir_alloc*sizeof(ent)); - } - ent = xmalloc(sizeof(*ent) + len + 1); - ent->len = len; - memcpy(ent->name, pathname, len); - ent->name[len] = 0; - dir[nr_dir++] = ent; -} - -static int dir_exists(const char *dirname, int len) -{ - int pos = cache_name_pos(dirname, len); - if (pos >= 0) - return 1; - pos = -pos-1; - if (pos >= active_nr) /* can't */ - return 0; - return !strncmp(active_cache[pos]->name, dirname, len); -} - -/* - * Read a directory tree. We currently ignore anything but - * directories, regular files and symlinks. That's because git - * doesn't handle them at all yet. Maybe that will change some - * day. - * - * Also, we ignore the name ".git" (even if it is not a directory). - * That likely will not change. - */ -static int read_directory(const char *path, const char *base, int baselen) -{ - DIR *fdir = opendir(path); - int contents = 0; - - if (fdir) { - int exclude_stk; - struct dirent *de; - char fullname[MAXPATHLEN + 1]; - memcpy(fullname, base, baselen); - - exclude_stk = push_exclude_per_directory(base, baselen); - - while ((de = readdir(fdir)) != NULL) { - int len; - - if ((de->d_name[0] == '.') && - (de->d_name[1] == 0 || - !strcmp(de->d_name + 1, ".") || - !strcmp(de->d_name + 1, "git"))) - continue; - len = strlen(de->d_name); - memcpy(fullname + baselen, de->d_name, len+1); - if (excluded(fullname) != show_ignored) { - if (!show_ignored || DTYPE(de) != DT_DIR) { - continue; - } - } - - switch (DTYPE(de)) { - struct stat st; - int subdir, rewind_base; - default: - continue; - case DT_UNKNOWN: - if (lstat(fullname, &st)) - continue; - if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) - break; - if (!S_ISDIR(st.st_mode)) - continue; - /* fallthrough */ - case DT_DIR: - memcpy(fullname + baselen + len, "/", 2); - len++; - rewind_base = nr_dir; - subdir = read_directory(fullname, fullname, - baselen + len); - if (show_other_directories && - (subdir || !hide_empty_directories) && - !dir_exists(fullname, baselen + len)) { - // Rewind the read subdirectory - while (nr_dir > rewind_base) - free(dir[--nr_dir]); - break; - } - contents += subdir; - continue; - case DT_REG: - case DT_LNK: - break; - } - add_name(fullname, baselen + len); - contents++; - } - closedir(fdir); - - pop_exclude_per_directory(exclude_stk); - } - - return contents; -} - -static int cmp_name(const void *p1, const void *p2) -{ - const struct nond_on_fs *e1 = *(const struct nond_on_fs **)p1; - const struct nond_on_fs *e2 = *(const struct nond_on_fs **)p2; - - return cache_name_compare(e1->name, e1->len, - e2->name, e2->len); -} /* * Match a pathspec against a filename. The first "len" characters @@ -377,7 +71,7 @@ static int match(const char **spec, char *ps_matched, return 0; } -static void show_dir_entry(const char *tag, struct nond_on_fs *ent) +static void show_dir_entry(const char *tag, struct dir_entry *ent) { int len = prefix_len; int offset = prefix_offset; @@ -393,14 +87,14 @@ static void show_dir_entry(const char *tag, struct nond_on_fs *ent) putchar(line_terminator); } -static void show_other_files(void) +static void show_other_files(struct dir_struct *dir) { int i; - for (i = 0; i < nr_dir; i++) { + for (i = 0; i < dir->nr; i++) { /* We should not have a matching entry, but we * may have an unmerged entry for this path. */ - struct nond_on_fs *ent = dir[i]; + struct dir_entry *ent = dir->entries[i]; int pos = cache_name_pos(ent->name, ent->len); struct cache_entry *ce; if (0 <= pos) @@ -416,11 +110,11 @@ static void show_other_files(void) } } -static void show_killed_files(void) +static void show_killed_files(struct dir_struct *dir) { int i; - for (i = 0; i < nr_dir; i++) { - struct nond_on_fs *ent = dir[i]; + for (i = 0; i < dir->nr; i++) { + struct dir_entry *ent = dir->entries[i]; char *cp, *sp; int pos, len, killed = 0; @@ -461,7 +155,7 @@ static void show_killed_files(void) } } if (killed) - show_dir_entry(tag_killed, dir[i]); + show_dir_entry(tag_killed, dir->entries[i]); } } @@ -512,7 +206,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce) } } -static void show_files(void) +static void show_files(struct dir_struct *dir) { int i; @@ -523,14 +217,14 @@ static void show_files(void) if (baselen) { path = base = prefix; - if (exclude_per_dir) { + if (dir->exclude_per_dir) { char *p, *pp = xmalloc(baselen+1); memcpy(pp, prefix, baselen+1); p = pp; while (1) { char save = *p; *p = 0; - push_exclude_per_directory(pp, p-pp); + push_exclude_per_directory(dir, pp, p-pp); *p++ = save; if (!save) break; @@ -543,17 +237,16 @@ static void show_files(void) free(pp); } } - read_directory(path, base, baselen); - qsort(dir, nr_dir, sizeof(struct nond_on_fs *), cmp_name); + read_directory(dir, path, base, baselen); if (show_others) - show_other_files(); + show_other_files(dir); if (show_killed) - show_killed_files(); + show_killed_files(dir); } if (show_cached | show_stage) { for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; - if (excluded(ce->name) != show_ignored) + if (excluded(dir, ce->name) != dir->show_ignored) continue; if (show_unmerged && !ce_stage(ce)) continue; @@ -565,7 +258,7 @@ static void show_files(void) struct cache_entry *ce = active_cache[i]; struct stat st; int err; - if (excluded(ce->name) != show_ignored) + if (excluded(dir, ce->name) != dir->show_ignored) continue; err = lstat(ce->name, &st); if (show_deleted && err) @@ -652,7 +345,9 @@ int main(int argc, const char **argv) { int i; int exc_given = 0; + struct dir_struct dir; + memset(&dir, 0, sizeof(dir)); prefix = setup_git_directory(); if (prefix) prefix_offset = strlen(prefix); @@ -697,7 +392,7 @@ int main(int argc, const char **argv) continue; } if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) { - show_ignored = 1; + dir.show_ignored = 1; continue; } if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) { @@ -709,11 +404,11 @@ int main(int argc, const char **argv) continue; } if (!strcmp(arg, "--directory")) { - show_other_directories = 1; + dir.show_other_directories = 1; continue; } if (!strcmp(arg, "--no-empty-directory")) { - hide_empty_directories = 1; + dir.hide_empty_directories = 1; continue; } if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) { @@ -726,27 +421,27 @@ int main(int argc, const char **argv) } if (!strcmp(arg, "-x") && i+1 < argc) { exc_given = 1; - add_exclude(argv[++i], "", 0, &exclude_list[EXC_CMDL]); + add_exclude(argv[++i], "", 0, &dir.exclude_list[EXC_CMDL]); continue; } if (!strncmp(arg, "--exclude=", 10)) { exc_given = 1; - add_exclude(arg+10, "", 0, &exclude_list[EXC_CMDL]); + add_exclude(arg+10, "", 0, &dir.exclude_list[EXC_CMDL]); continue; } if (!strcmp(arg, "-X") && i+1 < argc) { exc_given = 1; - add_excludes_from_file(argv[++i]); + add_excludes_from_file(&dir, argv[++i]); continue; } if (!strncmp(arg, "--exclude-from=", 15)) { exc_given = 1; - add_excludes_from_file(arg+15); + add_excludes_from_file(&dir, arg+15); continue; } if (!strncmp(arg, "--exclude-per-directory=", 24)) { exc_given = 1; - exclude_per_dir = arg + 24; + dir.exclude_per_dir = arg + 24; continue; } if (!strcmp(arg, "--full-name")) { @@ -788,7 +483,7 @@ int main(int argc, const char **argv) ps_matched = xcalloc(1, num); } - if (show_ignored && !exc_given) { + if (dir.show_ignored && !exc_given) { fprintf(stderr, "%s: --ignored needs some exclude pattern\n", argv[0]); exit(1); @@ -802,7 +497,7 @@ int main(int argc, const char **argv) read_cache(); if (prefix) prune_cache(); - show_files(); + show_files(&dir); if (ps_matched) { /* We need to make sure all pathspec matched otherwise From b4189aa84873718f80c62846cd53e803b5f72362 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 16 May 2006 19:46:16 -0700 Subject: [PATCH 13/40] Clean up git-ls-file directory walking library interface This moves the code to add the per-directory ignore files for the base directory into the library routine. That not only allows us to turn the function push_exclude_per_directory() static again, it also simplifies the library interface a lot (the caller no longer needs to worry about any of the per-directory exclude files at all). Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- dir.c | 28 +++++++++++++++++++++++++++- dir.h | 2 -- ls-files.c | 22 +--------------------- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/dir.c b/dir.c index 3f41a5dfea..d40b62e1c1 100644 --- a/dir.c +++ b/dir.c @@ -78,7 +78,7 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname) die("cannot use %s as an exclude file", fname); } -int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen) +static int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen) { char exclude_file[PATH_MAX]; struct exclude_list *el = &dir->exclude_list[EXC_DIRS]; @@ -289,6 +289,32 @@ static int cmp_name(const void *p1, const void *p2) int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen) { + /* + * Make sure to do the per-directory exclude for all the + * directories leading up to our base. + */ + if (baselen) { + if (dir->exclude_per_dir) { + char *p, *pp = xmalloc(baselen+1); + memcpy(pp, base, baselen+1); + p = pp; + while (1) { + char save = *p; + *p = 0; + push_exclude_per_directory(dir, pp, p-pp); + *p++ = save; + if (!save) + break; + p = strchr(p, '/'); + if (p) + p++; + else + p = pp + baselen; + } + free(pp); + } + } + read_directory_recursive(dir, path, base, baselen); qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name); return dir->nr; diff --git a/dir.h b/dir.h index e8fc441e7f..4f65f57085 100644 --- a/dir.h +++ b/dir.h @@ -44,7 +44,5 @@ extern int excluded(struct dir_struct *, const char *); extern void add_excludes_from_file(struct dir_struct *, const char *fname); extern void add_exclude(const char *string, const char *base, int baselen, struct exclude_list *which); -extern int push_exclude_per_directory(struct dir_struct *, - const char *base, int baselen); #endif diff --git a/ls-files.c b/ls-files.c index 89941a3ff8..dfe1481a35 100644 --- a/ls-files.c +++ b/ls-files.c @@ -215,28 +215,8 @@ static void show_files(struct dir_struct *dir) const char *path = ".", *base = ""; int baselen = prefix_len; - if (baselen) { + if (baselen) path = base = prefix; - if (dir->exclude_per_dir) { - char *p, *pp = xmalloc(baselen+1); - memcpy(pp, prefix, baselen+1); - p = pp; - while (1) { - char save = *p; - *p = 0; - push_exclude_per_directory(dir, pp, p-pp); - *p++ = save; - if (!save) - break; - p = strchr(p, '/'); - if (p) - p++; - else - p = pp + baselen; - } - free(pp); - } - } read_directory(dir, path, base, baselen); if (show_others) show_other_files(dir); From 0d78153952e70c21e94dc6b7eefcb2ac5337a902 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 17 May 2006 09:33:32 -0700 Subject: [PATCH 14/40] Do "git add" as a builtin First try. Let's see how well this works. In many ways, the hard parts of "git commit" are not so different from this, and a builtin commit would share a lot of the code, I think. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- Makefile | 2 +- builtin-add.c | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++ builtin.h | 1 + git.c | 1 + 4 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 builtin-add.c diff --git a/Makefile b/Makefile index f43ac63c93..e6f7794ad0 100644 --- a/Makefile +++ b/Makefile @@ -218,7 +218,7 @@ LIB_OBJS = \ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ - builtin-grep.o + builtin-grep.o builtin-add.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/builtin-add.c b/builtin-add.c new file mode 100644 index 0000000000..d225988e53 --- /dev/null +++ b/builtin-add.c @@ -0,0 +1,228 @@ +/* + * "git add" builtin command + * + * Copyright (C) 2006 Linus Torvalds + */ +#include + +#include "cache.h" +#include "builtin.h" +#include "dir.h" + +static const char builtin_add_usage[] = +"git-add [-n] [-v] ..."; + +static int common_prefix(const char **pathspec) +{ + const char *path, *slash, *next; + int prefix; + + if (!pathspec) + return 0; + + path = *pathspec; + slash = strrchr(path, '/'); + if (!slash) + return 0; + + prefix = slash - path + 1; + while ((next = *++pathspec) != NULL) { + int len = strlen(next); + if (len >= prefix && !memcmp(path, next, len)) + continue; + for (;;) { + if (!len) + return 0; + if (next[--len] != '/') + continue; + if (memcmp(path, next, len+1)) + continue; + prefix = len + 1; + break; + } + } + return prefix; +} + +static int match(const char **pathspec, const char *name, int namelen, int prefix) +{ + const char *match; + + name += prefix; + namelen -= prefix; + + while ((match = *pathspec++) != NULL) { + int matchlen; + + match += prefix; + matchlen = strlen(match); + if (!matchlen) + return 1; + if (!strncmp(match, name, matchlen)) { + if (match[matchlen-1] == '/') + return 1; + switch (name[matchlen]) { + case '/': case '\0': + return 1; + } + } + if (!fnmatch(match, name, 0)) + return 1; + } + return 0; +} + +static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix) +{ + int i; + struct dir_entry **src, **dst; + + src = dst = dir->entries; + i = dir->nr; + while (--i >= 0) { + struct dir_entry *entry = *src++; + if (!match(pathspec, entry->name, entry->len, prefix)) { + free(entry); + continue; + } + *dst++ = entry; + } + dir->nr = dst - dir->entries; +} + +static void fill_directory(struct dir_struct *dir, const char **pathspec) +{ + const char *path, *base; + int baselen; + + /* Set up the default git porcelain excludes */ + memset(dir, 0, sizeof(*dir)); + dir->exclude_per_dir = ".gitignore"; + path = git_path("info/exclude"); + if (!access(path, R_OK)) + add_excludes_from_file(dir, path); + + /* + * Calculate common prefix for the pathspec, and + * use that to optimize the directory walk + */ + baselen = common_prefix(pathspec); + path = "."; + base = ""; + if (baselen) { + char *common = xmalloc(baselen + 1); + common = xmalloc(baselen + 1); + memcpy(common, *pathspec, baselen); + common[baselen] = 0; + path = base = common; + } + + /* Read the directory and prune it */ + read_directory(dir, path, base, baselen); + if (pathspec) + prune_directory(dir, pathspec, baselen); +} + +static int add_file_to_index(const char *path, int verbose) +{ + int size, namelen; + struct stat st; + struct cache_entry *ce; + + if (lstat(path, &st)) + die("%s: unable to stat (%s)", path, strerror(errno)); + + if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) + die("%s: can only add regular files or symbolic links", path); + + namelen = strlen(path); + size = cache_entry_size(namelen); + ce = xcalloc(1, size); + memcpy(ce->name, path, namelen); + ce->ce_flags = htons(namelen); + fill_stat_cache_info(ce, &st); + + ce->ce_mode = create_ce_mode(st.st_mode); + if (!trust_executable_bit) { + /* If there is an existing entry, pick the mode bits + * from it. + */ + int pos = cache_name_pos(path, namelen); + if (pos >= 0) + ce->ce_mode = active_cache[pos]->ce_mode; + } + + if (index_path(ce->sha1, path, &st, 1)) + die("unable to index file %s", path); + if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD)) + die("unable to add %s to index",path); + if (verbose) + printf("add '%s'\n", path); + return 0; +} + +static struct cache_file cache_file; + +int cmd_add(int argc, const char **argv, char **envp) +{ + int i, newfd; + int verbose = 0, show_only = 0; + const char *prefix = setup_git_directory(); + const char **pathspec; + struct dir_struct dir; + + git_config(git_default_config); + + newfd = hold_index_file_for_update(&cache_file, get_index_file()); + if (newfd < 0) + die("unable to create new cachefile"); + + if (read_cache() < 0) + die("index file corrupt"); + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + if (arg[0] != '-') + break; + if (!strcmp(arg, "--")) { + i++; + break; + } + if (!strcmp(arg, "-n")) { + show_only = 1; + continue; + } + if (!strcmp(arg, "-v")) { + verbose = 1; + continue; + } + die(builtin_add_usage); + } + git_config(git_default_config); + pathspec = get_pathspec(prefix, argv + i); + + fill_directory(&dir, pathspec); + + if (show_only) { + const char *sep = "", *eof = ""; + for (i = 0; i < dir.nr; i++) { + printf("%s%s", sep, dir.entries[i]->name); + sep = " "; + eof = "\n"; + } + fputs(eof, stdout); + return 0; + } + + for (i = 0; i < dir.nr; i++) + add_file_to_index(dir.entries[i]->name, verbose); + + if (active_cache_changed) { + if (write_cache(newfd, active_cache, active_nr) || + commit_index_file(&cache_file)) + die("Unable to write new index file"); + } + + return 0; +} diff --git a/builtin.h b/builtin.h index 7744f7d2f6..1b77f4b0ca 100644 --- a/builtin.h +++ b/builtin.h @@ -24,5 +24,6 @@ extern int cmd_count_objects(int argc, const char **argv, char **envp); extern int cmd_push(int argc, const char **argv, char **envp); extern int cmd_grep(int argc, const char **argv, char **envp); +extern int cmd_add(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index a94d9ee5a7..fac46af057 100644 --- a/git.c +++ b/git.c @@ -50,6 +50,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "count-objects", cmd_count_objects }, { "diff", cmd_diff }, { "grep", cmd_grep }, + { "add", cmd_add }, }; int i; From f25933987f29070e9cd79dfddf03018010e82e80 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 17 May 2006 13:23:19 -0700 Subject: [PATCH 15/40] builtin-add: warn on unmatched pathspecs This is in the same spirit as what bba319b5 and 45e48120 tried to do to help users. A command such as "git add Documentaiton" with misspelled pathspecs would give a friendly reminder with this. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-add.c | 77 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 19 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index d225988e53..089c7a89b3 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -44,50 +44,89 @@ static int common_prefix(const char **pathspec) return prefix; } -static int match(const char **pathspec, const char *name, int namelen, int prefix) +static int match_one(const char *match, const char *name, int namelen) { + int matchlen; + + /* If the match was just the prefix, we matched */ + matchlen = strlen(match); + if (!matchlen) + return 1; + + /* + * If we don't match the matchstring exactly, + * we need to match by fnmatch + */ + if (strncmp(match, name, matchlen)) + return !fnmatch(match, name, 0); + + /* + * If we did match the string exactly, we still + * need to make sure that it happened on a path + * component boundary (ie either the last character + * of the match was '/', or the next character of + * the name was '/' or the terminating NUL. + */ + return match[matchlen-1] == '/' || + name[matchlen] == '/' || + !name[matchlen]; +} + +static int match(const char **pathspec, const char *name, int namelen, int prefix, char *seen) +{ + int retval; const char *match; name += prefix; namelen -= prefix; - while ((match = *pathspec++) != NULL) { - int matchlen; - + for (retval = 0; (match = *pathspec++) != NULL; seen++) { + if (retval & *seen) + continue; match += prefix; - matchlen = strlen(match); - if (!matchlen) - return 1; - if (!strncmp(match, name, matchlen)) { - if (match[matchlen-1] == '/') - return 1; - switch (name[matchlen]) { - case '/': case '\0': - return 1; - } + if (match_one(match, name, namelen)) { + retval = 1; + *seen = 1; } - if (!fnmatch(match, name, 0)) - return 1; } - return 0; + return retval; } static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix) { - int i; + char *seen; + int i, specs; struct dir_entry **src, **dst; + for (specs = 0; pathspec[specs]; specs++) + /* nothing */; + seen = xmalloc(specs); + memset(seen, 0, specs); + src = dst = dir->entries; i = dir->nr; while (--i >= 0) { struct dir_entry *entry = *src++; - if (!match(pathspec, entry->name, entry->len, prefix)) { + if (!match(pathspec, entry->name, entry->len, prefix, seen)) { free(entry); continue; } *dst++ = entry; } dir->nr = dst - dir->entries; + + for (i = 0; i < specs; i++) { + struct stat st; + const char *match; + if (seen[i]) + continue; + + /* Existing file? We must have ignored it */ + match = pathspec[i]; + if (!lstat(match, &st)) + continue; + die("pathspec '%s' did not match any files", match); + } } static void fill_directory(struct dir_struct *dir, const char **pathspec) From c699f9b924d763b762df932769d91e3d053634a8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 17 May 2006 21:21:04 -0700 Subject: [PATCH 16/40] Remove old "git-add.sh" remnants Repeat after me: "It's now a built-in" Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- Makefile | 4 ++-- git-add.sh | 56 ------------------------------------------------------ 2 files changed, 2 insertions(+), 58 deletions(-) delete mode 100755 git-add.sh diff --git a/Makefile b/Makefile index e6f7794ad0..48e2a9cb22 100644 --- a/Makefile +++ b/Makefile @@ -113,7 +113,7 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ ### --- END CONFIGURATION SECTION --- SCRIPT_SH = \ - git-add.sh git-bisect.sh git-branch.sh git-checkout.sh \ + git-bisect.sh git-branch.sh git-checkout.sh \ git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \ git-fetch.sh \ git-format-patch.sh git-ls-remote.sh \ @@ -170,7 +170,7 @@ PROGRAMS = \ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ - git-grep$X + git-grep$X git-add$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) diff --git a/git-add.sh b/git-add.sh deleted file mode 100755 index d6a4bc7d09..0000000000 --- a/git-add.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/sh - -USAGE='[-n] [-v] ...' -SUBDIRECTORY_OK='Yes' -. git-sh-setup - -show_only= -verbose= -while : ; do - case "$1" in - -n) - show_only=true - ;; - -v) - verbose=--verbose - ;; - --) - shift - break - ;; - -*) - usage - ;; - *) - break - ;; - esac - shift -done - -# Check misspelled pathspec -case "$#" in -0) ;; -*) - git-ls-files --error-unmatch --others --cached -- "$@" >/dev/null || { - echo >&2 "Maybe you misspelled it?" - exit 1 - } - ;; -esac - -if test -f "$GIT_DIR/info/exclude" -then - git-ls-files -z \ - --exclude-from="$GIT_DIR/info/exclude" \ - --others --exclude-per-directory=.gitignore -- "$@" -else - git-ls-files -z \ - --others --exclude-per-directory=.gitignore -- "$@" -fi | -case "$show_only" in -true) - xargs -0 echo ;; -*) - git-update-index --add $verbose -z --stdin ;; -esac From e8f990b4e4b56f214138cc475c19e5a253e9148e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 18 May 2006 01:29:36 -0700 Subject: [PATCH 17/40] builtin-add: fix unmatched pathspec warnings. "git add Documentation/" when Documentation directory exists does not barf (as it should not), but "git add ." barfed when it did not add anything. This was because we checked for the path prefix ("Documentation/" in the former case, and an empty string in the latter case) for existence, and lstat("", &st) would say "Huh?". Signed-off-by: Junio C Hamano --- builtin-add.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-add.c b/builtin-add.c index 089c7a89b3..37243f8c36 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -123,7 +123,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p /* Existing file? We must have ignored it */ match = pathspec[i]; - if (!lstat(match, &st)) + if (!match[0] || !lstat(match, &st)) continue; die("pathspec '%s' did not match any files", match); } From 8dcf39c46e2931ca02b18b1ea3a6b21f446d8de8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 18 May 2006 12:07:31 -0700 Subject: [PATCH 18/40] Prevent bogus paths from being added to the index. With this one, it's now a fatal error to try to add a pathname that cannot be added with "git add", i.e. [torvalds@g5 git]$ git add .git/config fatal: unable to add .git/config to index and [torvalds@g5 git]$ git add foo/../bar fatal: unable to add foo/../bar to index instead of the old "Ignoring path xyz" warning that would end up silently succeeding on any other paths. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- cache.h | 1 + read-cache.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ update-index.c | 64 ------------------------------------------------ 3 files changed, 67 insertions(+), 64 deletions(-) diff --git a/cache.h b/cache.h index 4b7a439253..89aa4f6c7e 100644 --- a/cache.h +++ b/cache.h @@ -142,6 +142,7 @@ extern void verify_non_filename(const char *prefix, const char *name); /* Initialize and use the cache information */ extern int read_cache(void); extern int write_cache(int newfd, struct cache_entry **cache, int entries); +extern int verify_path(const char *path); extern int cache_name_pos(const char *name, int namelen); #define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */ #define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */ diff --git a/read-cache.c b/read-cache.c index a917ab0cfe..e8fa6d0812 100644 --- a/read-cache.c +++ b/read-cache.c @@ -331,6 +331,70 @@ int ce_path_match(const struct cache_entry *ce, const char **pathspec) return 0; } +/* + * We fundamentally don't like some paths: we don't want + * dot or dot-dot anywhere, and for obvious reasons don't + * want to recurse into ".git" either. + * + * Also, we don't want double slashes or slashes at the + * end that can make pathnames ambiguous. + */ +static int verify_dotfile(const char *rest) +{ + /* + * The first character was '.', but that + * has already been discarded, we now test + * the rest. + */ + switch (*rest) { + /* "." is not allowed */ + case '\0': case '/': + return 0; + + /* + * ".git" followed by NUL or slash is bad. This + * shares the path end test with the ".." case. + */ + case 'g': + if (rest[1] != 'i') + break; + if (rest[2] != 't') + break; + rest += 2; + /* fallthrough */ + case '.': + if (rest[1] == '\0' || rest[1] == '/') + return 0; + } + return 1; +} + +int verify_path(const char *path) +{ + char c; + + goto inside; + for (;;) { + if (!c) + return 1; + if (c == '/') { +inside: + c = *path++; + switch (c) { + default: + continue; + case '/': case '\0': + break; + case '.': + if (verify_dotfile(path)) + continue; + } + return 0; + } + c = *path++; + } +} + /* * Do we have another file that has the beginning components being a * proper superset of the name we're trying to add? @@ -472,6 +536,8 @@ int add_cache_entry(struct cache_entry *ce, int option) if (!ok_to_add) return -1; + if (!verify_path(ce->name)) + return -1; if (!skip_df_check && check_file_directory_conflict(ce, pos, ok_to_replace)) { diff --git a/update-index.c b/update-index.c index 3d7e02db2c..859efc7916 100644 --- a/update-index.c +++ b/update-index.c @@ -237,70 +237,6 @@ static int refresh_cache(int really) return has_errors; } -/* - * We fundamentally don't like some paths: we don't want - * dot or dot-dot anywhere, and for obvious reasons don't - * want to recurse into ".git" either. - * - * Also, we don't want double slashes or slashes at the - * end that can make pathnames ambiguous. - */ -static int verify_dotfile(const char *rest) -{ - /* - * The first character was '.', but that - * has already been discarded, we now test - * the rest. - */ - switch (*rest) { - /* "." is not allowed */ - case '\0': case '/': - return 0; - - /* - * ".git" followed by NUL or slash is bad. This - * shares the path end test with the ".." case. - */ - case 'g': - if (rest[1] != 'i') - break; - if (rest[2] != 't') - break; - rest += 2; - /* fallthrough */ - case '.': - if (rest[1] == '\0' || rest[1] == '/') - return 0; - } - return 1; -} - -static int verify_path(const char *path) -{ - char c; - - goto inside; - for (;;) { - if (!c) - return 1; - if (c == '/') { -inside: - c = *path++; - switch (c) { - default: - continue; - case '/': case '\0': - break; - case '.': - if (verify_dotfile(path)) - continue; - } - return 0; - } - c = *path++; - } -} - static int add_cacheinfo(unsigned int mode, const unsigned char *sha1, const char *path, int stage) { From 3c6a370b0ee78114a656acba04fddf306252b9d5 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 19 May 2006 16:07:51 -0700 Subject: [PATCH 19/40] Move pathspec matching from builtin-add.c into dir.c I'll use it for builtin-rm.c too. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-add.c | 82 +-------------------------------------------------- dir.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ dir.h | 3 ++ 3 files changed, 84 insertions(+), 81 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index 37243f8c36..6166f66bce 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -12,86 +12,6 @@ static const char builtin_add_usage[] = "git-add [-n] [-v] ..."; -static int common_prefix(const char **pathspec) -{ - const char *path, *slash, *next; - int prefix; - - if (!pathspec) - return 0; - - path = *pathspec; - slash = strrchr(path, '/'); - if (!slash) - return 0; - - prefix = slash - path + 1; - while ((next = *++pathspec) != NULL) { - int len = strlen(next); - if (len >= prefix && !memcmp(path, next, len)) - continue; - for (;;) { - if (!len) - return 0; - if (next[--len] != '/') - continue; - if (memcmp(path, next, len+1)) - continue; - prefix = len + 1; - break; - } - } - return prefix; -} - -static int match_one(const char *match, const char *name, int namelen) -{ - int matchlen; - - /* If the match was just the prefix, we matched */ - matchlen = strlen(match); - if (!matchlen) - return 1; - - /* - * If we don't match the matchstring exactly, - * we need to match by fnmatch - */ - if (strncmp(match, name, matchlen)) - return !fnmatch(match, name, 0); - - /* - * If we did match the string exactly, we still - * need to make sure that it happened on a path - * component boundary (ie either the last character - * of the match was '/', or the next character of - * the name was '/' or the terminating NUL. - */ - return match[matchlen-1] == '/' || - name[matchlen] == '/' || - !name[matchlen]; -} - -static int match(const char **pathspec, const char *name, int namelen, int prefix, char *seen) -{ - int retval; - const char *match; - - name += prefix; - namelen -= prefix; - - for (retval = 0; (match = *pathspec++) != NULL; seen++) { - if (retval & *seen) - continue; - match += prefix; - if (match_one(match, name, namelen)) { - retval = 1; - *seen = 1; - } - } - return retval; -} - static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix) { char *seen; @@ -107,7 +27,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p i = dir->nr; while (--i >= 0) { struct dir_entry *entry = *src++; - if (!match(pathspec, entry->name, entry->len, prefix, seen)) { + if (!match_pathspec(pathspec, entry->name, entry->len, prefix, seen)) { free(entry); continue; } diff --git a/dir.c b/dir.c index d40b62e1c1..d778ecd890 100644 --- a/dir.c +++ b/dir.c @@ -11,6 +11,86 @@ #include "cache.h" #include "dir.h" +int common_prefix(const char **pathspec) +{ + const char *path, *slash, *next; + int prefix; + + if (!pathspec) + return 0; + + path = *pathspec; + slash = strrchr(path, '/'); + if (!slash) + return 0; + + prefix = slash - path + 1; + while ((next = *++pathspec) != NULL) { + int len = strlen(next); + if (len >= prefix && !memcmp(path, next, len)) + continue; + for (;;) { + if (!len) + return 0; + if (next[--len] != '/') + continue; + if (memcmp(path, next, len+1)) + continue; + prefix = len + 1; + break; + } + } + return prefix; +} + +static int match_one(const char *match, const char *name, int namelen) +{ + int matchlen; + + /* If the match was just the prefix, we matched */ + matchlen = strlen(match); + if (!matchlen) + return 1; + + /* + * If we don't match the matchstring exactly, + * we need to match by fnmatch + */ + if (strncmp(match, name, matchlen)) + return !fnmatch(match, name, 0); + + /* + * If we did match the string exactly, we still + * need to make sure that it happened on a path + * component boundary (ie either the last character + * of the match was '/', or the next character of + * the name was '/' or the terminating NUL. + */ + return match[matchlen-1] == '/' || + name[matchlen] == '/' || + !name[matchlen]; +} + +int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen) +{ + int retval; + const char *match; + + name += prefix; + namelen -= prefix; + + for (retval = 0; (match = *pathspec++) != NULL; seen++) { + if (retval & *seen) + continue; + match += prefix; + if (match_one(match, name, namelen)) { + retval = 1; + *seen = 1; + } + } + return retval; +} + void add_exclude(const char *string, const char *base, int baselen, struct exclude_list *which) { diff --git a/dir.h b/dir.h index 4f65f57085..56a1b7fce2 100644 --- a/dir.h +++ b/dir.h @@ -39,6 +39,9 @@ struct dir_struct { struct exclude_list exclude_list[3]; }; +extern int common_prefix(const char **pathspec); +extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen); + extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen); extern int excluded(struct dir_struct *, const char *); extern void add_excludes_from_file(struct dir_struct *, const char *fname); From d9b814cc97f16daac06566a5340121c446136d22 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 19 May 2006 16:19:34 -0700 Subject: [PATCH 20/40] Add builtin "git rm" command This changes semantics very subtly, because it adds a new atomicity guarantee. In particular, if you "git rm" several files, it will now do all or nothing. The old shell-script really looped over the removed files one by one, and would basically randomly fail in the middle if "-f" was used and one of the files didn't exist in the working directory. This C builtin one will not re-write the index after each remove, but instead remove all files at once. However, that means that if "-f" is used (to also force removal of the file from the working directory), and some files have already been removed from the workspace, it won't stop in the middle in some half-way state like the old one did. So what happens is that if the _first_ file fails to be removed with "-f", we abort the whole "git rm". But once we've started removing, we don't leave anything half done. If some of the other files don't exist, we'll just ignore errors of removal from the working tree. This is only an issue with "-f", of course. I think the new behaviour is strictly an improvement, but perhaps more importantly, it is _different_. As a special case, the semantics are identical for the single-file case (which is the only one our test-suite seems to test). The other question is what to do with leading directories. The old "git rm" script didn't do anything, which is somewhat inconsistent. This one will actually clean up directories that have become empty as a result of removing the last file, but maybe we want to have a flag to decide the behaviour? Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- Makefile | 8 +-- builtin-rm.c | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++ builtin.h | 1 + git-rm.sh | 70 ------------------------ git.c | 1 + 5 files changed, 157 insertions(+), 73 deletions(-) create mode 100644 builtin-rm.c delete mode 100755 git-rm.sh diff --git a/Makefile b/Makefile index 48e2a9cb22..d4a91135c3 100644 --- a/Makefile +++ b/Makefile @@ -120,7 +120,7 @@ SCRIPT_SH = \ git-merge-one-file.sh git-parse-remote.sh \ git-prune.sh git-pull.sh git-rebase.sh \ git-repack.sh git-request-pull.sh git-reset.sh \ - git-resolve.sh git-revert.sh git-rm.sh git-sh-setup.sh \ + git-resolve.sh git-revert.sh git-sh-setup.sh \ git-tag.sh git-verify-tag.sh \ git-applymbox.sh git-applypatch.sh git-am.sh \ git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \ @@ -170,7 +170,8 @@ PROGRAMS = \ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ - git-grep$X git-add$X + git-grep$X git-add$X git-rm$X git-rev-list$X \ + git-check-ref-format$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -218,7 +219,8 @@ LIB_OBJS = \ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ - builtin-grep.o builtin-add.o + builtin-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.o \ + builtin-rm.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/builtin-rm.c b/builtin-rm.c new file mode 100644 index 0000000000..9014c61556 --- /dev/null +++ b/builtin-rm.c @@ -0,0 +1,150 @@ +/* + * "git rm" builtin command + * + * Copyright (C) Linus Torvalds 2006 + */ +#include "cache.h" +#include "builtin.h" +#include "dir.h" + +static const char builtin_rm_usage[] = +"git-rm [-n] [-v] [-f] ..."; + +static struct { + int nr, alloc; + const char **name; +} list; + +static void add_list(const char *name) +{ + if (list.nr >= list.alloc) { + list.alloc = alloc_nr(list.alloc); + list.name = xrealloc(list.name, list.alloc * sizeof(const char *)); + } + list.name[list.nr++] = name; +} + +static int remove_file(const char *name) +{ + int ret; + char *slash; + + ret = unlink(name); + if (!ret && (slash = strrchr(name, '/'))) { + char *n = strdup(name); + do { + n[slash - name] = 0; + name = n; + } while (!rmdir(name) && (slash = strrchr(name, '/'))); + } + return ret; +} + +static struct cache_file cache_file; + +int cmd_rm(int argc, const char **argv, char **envp) +{ + int i, newfd; + int verbose = 0, show_only = 0, force = 0; + const char *prefix = setup_git_directory(); + const char **pathspec; + char *seen; + + git_config(git_default_config); + + newfd = hold_index_file_for_update(&cache_file, get_index_file()); + if (newfd < 0) + die("unable to create new index file"); + + if (read_cache() < 0) + die("index file corrupt"); + + for (i = 1 ; i < argc ; i++) { + const char *arg = argv[i]; + + if (*arg != '-') + break; + if (!strcmp(arg, "--")) { + i++; + break; + } + if (!strcmp(arg, "-n")) { + show_only = 1; + continue; + } + if (!strcmp(arg, "-v")) { + verbose = 1; + continue; + } + if (!strcmp(arg, "-f")) { + force = 1; + continue; + } + die(builtin_rm_usage); + } + pathspec = get_pathspec(prefix, argv + i); + + seen = NULL; + if (pathspec) { + for (i = 0; pathspec[i] ; i++) + /* nothing */; + seen = xmalloc(i); + memset(seen, 0, i); + } + + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen)) + continue; + add_list(ce->name); + } + + if (pathspec) { + const char *match; + for (i = 0; (match = pathspec[i]) != NULL ; i++) { + if (*match && !seen[i]) + die("pathspec '%s' did not match any files", match); + } + } + + /* + * First remove the names from the index: we won't commit + * the index unless all of them succeed + */ + for (i = 0; i < list.nr; i++) { + const char *path = list.name[i]; + printf("rm '%s'\n", path); + + if (remove_file_from_cache(path)) + die("git rm: unable to remove %s", path); + } + + /* + * Then, if we used "-f", remove the filenames from the + * workspace. If we fail to remove the first one, we + * abort the "git rm" (but once we've successfully removed + * any file at all, we'll go ahead and commit to it all: + * by then we've already committed ourself and can't fail + * in the middle) + */ + if (force) { + int removed = 0; + for (i = 0; i < list.nr; i++) { + const char *path = list.name[i]; + if (!remove_file(path)) { + removed = 1; + continue; + } + if (!removed) + die("git rm: %s: %s", path, strerror(errno)); + } + } + + if (active_cache_changed) { + if (write_cache(newfd, active_cache, active_nr) || + commit_index_file(&cache_file)) + die("Unable to write new index file"); + } + + return 0; +} diff --git a/builtin.h b/builtin.h index 1b77f4b0ca..c1cb765dea 100644 --- a/builtin.h +++ b/builtin.h @@ -24,6 +24,7 @@ extern int cmd_count_objects(int argc, const char **argv, char **envp); extern int cmd_push(int argc, const char **argv, char **envp); extern int cmd_grep(int argc, const char **argv, char **envp); +extern int cmd_rm(int argc, const char **argv, char **envp); extern int cmd_add(int argc, const char **argv, char **envp); #endif diff --git a/git-rm.sh b/git-rm.sh deleted file mode 100755 index fda4541c76..0000000000 --- a/git-rm.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/sh - -USAGE='[-f] [-n] [-v] [--] ...' -SUBDIRECTORY_OK='Yes' -. git-sh-setup - -remove_files= -show_only= -verbose= -while : ; do - case "$1" in - -f) - remove_files=true - ;; - -n) - show_only=true - ;; - -v) - verbose=--verbose - ;; - --) - shift; break - ;; - -*) - usage - ;; - *) - break - ;; - esac - shift -done - -# This is typo-proofing. If some paths match and some do not, we want -# to do nothing. -case "$#" in -0) ;; -*) - git-ls-files --error-unmatch -- "$@" >/dev/null || { - echo >&2 "Maybe you misspelled it?" - exit 1 - } - ;; -esac - -if test -f "$GIT_DIR/info/exclude" -then - git-ls-files -z \ - --exclude-from="$GIT_DIR/info/exclude" \ - --exclude-per-directory=.gitignore -- "$@" -else - git-ls-files -z \ - --exclude-per-directory=.gitignore -- "$@" -fi | -case "$show_only,$remove_files" in -true,*) - xargs -0 echo - ;; -*,true) - xargs -0 sh -c " - while [ \$# -gt 0 ]; do - file=\$1; shift - rm -- \"\$file\" && git-update-index --remove $verbose \"\$file\" - done - " inline - ;; -*) - git-update-index --force-remove $verbose -z --stdin - ;; -esac diff --git a/git.c b/git.c index fac46af057..20c0f197a3 100644 --- a/git.c +++ b/git.c @@ -50,6 +50,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "count-objects", cmd_count_objects }, { "diff", cmd_diff }, { "grep", cmd_grep }, + { "rm", cmd_rm }, { "add", cmd_add }, }; int i; From 217542640ed219c980fff2b3c307c4520120f20f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 18 May 2006 18:46:44 -0700 Subject: [PATCH 21/40] built-in tar-tree and remote tar-tree This makes tar-tree a built-in. As an added bonus, you can now say: git tar-tree --remote=remote-repository [] This does not work with git-daemon yet, but should work with localhost and git over ssh transports. Signed-off-by: Junio C Hamano --- Makefile | 6 +-- tar-tree.c => builtin-tar-tree.c | 62 +++++++++++++++++++++++++- builtin-upload-tar.c | 74 ++++++++++++++++++++++++++++++++ builtin.h | 2 + git.c | 4 +- 5 files changed, 142 insertions(+), 6 deletions(-) rename tar-tree.c => builtin-tar-tree.c (85%) create mode 100644 builtin-upload-tar.c diff --git a/Makefile b/Makefile index 4fd6520b7e..f4bcec496a 100644 --- a/Makefile +++ b/Makefile @@ -161,7 +161,7 @@ PROGRAMS = \ git-receive-pack$X git-rev-parse$X \ git-send-pack$X git-show-branch$X git-shell$X \ git-show-index$X git-ssh-fetch$X \ - git-ssh-upload$X git-tar-tree$X git-unpack-file$X \ + git-ssh-upload$X git-unpack-file$X \ git-unpack-objects$X git-update-index$X git-update-server-info$X \ git-upload-pack$X git-verify-pack$X git-write-tree$X \ git-update-ref$X git-symbolic-ref$X \ @@ -171,7 +171,7 @@ PROGRAMS = \ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ - git-init-db$X + git-init-db$X git-tar-tree$X git-upload-tar$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -220,7 +220,7 @@ LIB_OBJS = \ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ - builtin-init-db.o + builtin-init-db.o builtin-tar-tree.o builtin-upload-tar.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/tar-tree.c b/builtin-tar-tree.c similarity index 85% rename from tar-tree.c rename to builtin-tar-tree.c index 33087366c3..e97e0af985 100644 --- a/tar-tree.c +++ b/builtin-tar-tree.c @@ -7,11 +7,14 @@ #include "commit.h" #include "strbuf.h" #include "tar.h" +#include "builtin.h" +#include "pkt-line.h" #define RECORDSIZE (512) #define BLOCKSIZE (RECORDSIZE * 20) -static const char tar_tree_usage[] = "git-tar-tree [basedir]"; +static const char tar_tree_usage[] = +"git-tar-tree [--remote=] [basedir]"; static char block[BLOCKSIZE]; static unsigned long offset; @@ -301,7 +304,7 @@ static void traverse_tree(struct tree_desc *tree, struct strbuf *path) } } -int main(int argc, char **argv) +int generate_tar(int argc, const char **argv) { unsigned char sha1[20], tree_sha1[20]; struct commit *commit; @@ -348,3 +351,58 @@ int main(int argc, char **argv) free(current_path.buf); return 0; } + +static const char *exec = "git-upload-tar"; + +static int remote_tar(int argc, const char **argv) +{ + int fd[2], ret, len; + pid_t pid; + char buf[1024]; + char *url; + + if (argc < 3 || 4 < argc) + usage(tar_tree_usage); + + /* --remote= */ + url = strdup(argv[1]+9); + pid = git_connect(fd, url, exec); + if (pid < 0) + return 1; + + packet_write(fd[1], "want %s\n", argv[2]); + if (argv[3]) + packet_write(fd[1], "base %s\n", argv[3]); + packet_flush(fd[1]); + + len = packet_read_line(fd[0], buf, sizeof(buf)); + if (!len) + die("git-tar-tree: expected ACK/NAK, got EOF"); + if (buf[len-1] == '\n') + buf[--len] = 0; + if (strcmp(buf, "ACK")) { + if (5 < len && !strncmp(buf, "NACK ", 5)) + die("git-tar-tree: NACK %s", buf + 5); + die("git-tar-tree: protocol error"); + } + /* expect a flush */ + len = packet_read_line(fd[0], buf, sizeof(buf)); + if (len) + die("git-tar-tree: expected a flush"); + + /* Now, start reading from fd[0] and spit it out to stdout */ + ret = copy_fd(fd[0], 1); + close(fd[0]); + + ret |= finish_connect(pid); + return !!ret; +} + +int cmd_tar_tree(int argc, const char **argv, char **envp) +{ + if (argc < 2) + usage(tar_tree_usage); + if (!strncmp("--remote=", argv[1], 9)) + return remote_tar(argc, argv); + return generate_tar(argc, argv); +} diff --git a/builtin-upload-tar.c b/builtin-upload-tar.c new file mode 100644 index 0000000000..d4fa7b56c3 --- /dev/null +++ b/builtin-upload-tar.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2006 Junio C Hamano + */ +#include "cache.h" +#include "pkt-line.h" +#include "exec_cmd.h" +#include "builtin.h" + +static const char upload_tar_usage[] = "git-upload-tar "; + +static int nak(const char *reason) +{ + packet_write(1, "NACK %s\n", reason); + packet_flush(1); + return 1; +} + +int cmd_upload_tar(int argc, const char **argv, char **envp) +{ + int len; + const char *dir = argv[1]; + char buf[8192]; + unsigned char sha1[20]; + char *base = NULL; + char hex[41]; + int ac; + const char *av[4]; + + if (argc != 2) + usage(upload_tar_usage); + if (strlen(dir) < sizeof(buf)-1) + strcpy(buf, dir); /* enter-repo smudges its argument */ + else + packet_write(1, "NACK insanely long repository name %s\n", dir); + if (!enter_repo(buf, 0)) { + packet_write(1, "NACK not a git archive %s\n", dir); + packet_flush(1); + return 1; + } + + len = packet_read_line(0, buf, sizeof(buf)); + if (len < 5 || strncmp("want ", buf, 5)) + return nak("expected want"); + if (buf[len-1] == '\n') + buf[--len] = 0; + if (get_sha1(buf + 5, sha1)) + return nak("expected sha1"); + strcpy(hex, sha1_to_hex(sha1)); + + len = packet_read_line(0, buf, sizeof(buf)); + if (len) { + if (len < 5 || strncmp("base ", buf, 5)) + return nak("expected (optional) base"); + if (buf[len-1] == '\n') + buf[--len] = 0; + base = strdup(buf + 5); + len = packet_read_line(0, buf, sizeof(buf)); + } + if (len) + return nak("expected flush"); + + packet_write(1, "ACK\n"); + packet_flush(1); + + ac = 0; + av[ac++] = "tar-tree"; + av[ac++] = hex; + if (base) + av[ac++] = base; + av[ac++] = NULL; + execv_git_cmd(av); + /* should it return that is an error */ + return 1; +} diff --git a/builtin.h b/builtin.h index 60541262c4..f22783c499 100644 --- a/builtin.h +++ b/builtin.h @@ -27,5 +27,7 @@ extern int cmd_grep(int argc, const char **argv, char **envp); extern int cmd_rev_list(int argc, const char **argv, char **envp); extern int cmd_check_ref_format(int argc, const char **argv, char **envp); extern int cmd_init_db(int argc, const char **argv, char **envp); +extern int cmd_tar_tree(int argc, const char **argv, char **envp); +extern int cmd_upload_tar(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index 3216d311b2..fd8e9bf7f2 100644 --- a/git.c +++ b/git.c @@ -50,8 +50,10 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "count-objects", cmd_count_objects }, { "diff", cmd_diff }, { "grep", cmd_grep }, - { "rev-list", cmd_rev_list }, { "init-db", cmd_init_db }, + { "tar-tree", cmd_tar_tree }, + { "upload-tar", cmd_upload_tar }, + { "rev-list", cmd_rev_list }, { "check-ref-format", cmd_check_ref_format } }; int i; From 698ce6f87e0d6db380f7306e190e8586da184577 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 20 May 2006 15:40:29 +0200 Subject: [PATCH 22/40] fmt-patch: Support --attach This patch touches a couple of files, because it adds options to print a custom text just after the subject of a commit, and just after the diffstat. [jc: made "many dashes" used as the boundary leader into a single variable, to reduce the possibility of later tweaks to miscount the number of dashes to break it.] Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-log.c | 17 ++++++++++++++--- builtin-rev-list.c | 2 +- commit.c | 10 +++++++++- commit.h | 2 +- diff.c | 6 +++++- diff.h | 3 +++ log-tree.c | 37 +++++++++++++++++++++++++++++++++---- revision.h | 1 + show-branch.c | 2 +- 9 files changed, 68 insertions(+), 12 deletions(-) diff --git a/builtin-log.c b/builtin-log.c index 12a6d19203..c8feb0f795 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -183,7 +183,12 @@ int cmd_format_patch(int argc, const char **argv, char **envp) argv[i + 1]); output_directory = strdup(argv[i + 1]); i++; - } else + } + else if (!strcmp(argv[i], "--attach")) + rev.mime_boundary = git_version_string; + else if (!strncmp(argv[i], "--attach=", 9)) + rev.mime_boundary = argv[i] + 9; + else argv[j++] = argv[i]; } argc = j; @@ -224,8 +229,14 @@ int cmd_format_patch(int argc, const char **argv, char **envp) shown = log_tree_commit(&rev, commit); free(commit->buffer); commit->buffer = NULL; - if (shown) - printf("-- \n%s\n\n", git_version_string); + if (shown) { + if (rev.mime_boundary) + printf("\n--%s%s--\n\n\n", + mime_boundary_leader, + rev.mime_boundary); + else + printf("-- \n%s\n\n", git_version_string); + } if (!use_stdout) fclose(stdout); } diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 7942297d13..f11dbd65c1 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -85,7 +85,7 @@ static void show_commit(struct commit *commit) static char pretty_header[16384]; pretty_print_commit(revs.commit_format, commit, ~0, pretty_header, sizeof(pretty_header), - revs.abbrev, NULL); + revs.abbrev, NULL, NULL); printf("%s%c", pretty_header, hdr_termination); } fflush(stdout); diff --git a/commit.c b/commit.c index 84558bac29..0b163d4857 100644 --- a/commit.c +++ b/commit.c @@ -498,7 +498,7 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com return offset; } -unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject) +unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject) { int hdr = 1, body = 0; unsigned long offset = 0; @@ -591,6 +591,14 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit buf[offset++] = '\n'; if (fmt == CMIT_FMT_ONELINE) break; + if (after_subject) { + int slen = strlen(after_subject); + if (slen > space - offset - 1) + slen = space - offset - 1; + memcpy(buf + offset, after_subject, slen); + offset += slen; + after_subject = NULL; + } subject = NULL; } while (offset && isspace(buf[offset-1])) diff --git a/commit.h b/commit.h index 8d7514cd00..c9de1677e9 100644 --- a/commit.h +++ b/commit.h @@ -51,7 +51,7 @@ enum cmit_fmt { }; extern enum cmit_fmt get_commit_format(const char *arg); -extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject); +extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject); /** Removes the first commit from a list sorted by date, and adds all * of its parents. diff --git a/diff.c b/diff.c index af5db0ec12..be459cac69 100644 --- a/diff.c +++ b/diff.c @@ -299,6 +299,7 @@ static void diffstat_consume(void *priv, char *line, unsigned long len) static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"; static const char minuses[]= "----------------------------------------------------------------------"; +const char mime_boundary_leader[] = "------------"; static void show_stats(struct diffstat_t* data) { @@ -1980,7 +1981,10 @@ void diff_flush(struct diff_options *options) show_stats(diffstat); free(diffstat); diffstat = NULL; - putchar(options->line_termination); + if (options->stat_sep) + fputs(options->stat_sep, stdout); + else + putchar(options->line_termination); } for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; diff --git a/diff.h b/diff.h index c672277df2..4fc597c594 100644 --- a/diff.h +++ b/diff.h @@ -44,6 +44,7 @@ struct diff_options { int rename_limit; int setup; int abbrev; + const char *stat_sep; int nr_paths; const char **paths; @@ -52,6 +53,8 @@ struct diff_options { add_remove_fn_t add_remove; }; +extern const char mime_boundary_leader[]; + extern void diff_tree_setup_paths(const char **paths, struct diff_options *); extern void diff_tree_release_paths(struct diff_options *); extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2, diff --git a/log-tree.c b/log-tree.c index 526d578e98..7e23e42788 100644 --- a/log-tree.c +++ b/log-tree.c @@ -20,7 +20,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40; const char *extra; int len; - char* subject = NULL; + char *subject = NULL, *after_subject = NULL; opt->loginfo = NULL; if (!opt->verbose_header) { @@ -52,6 +52,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) */ if (opt->commit_format == CMIT_FMT_EMAIL) { + char *sha1 = sha1_to_hex(commit->object.sha1); if (opt->total > 0) { static char buffer[64]; snprintf(buffer, sizeof(buffer), @@ -63,8 +64,36 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) else subject = "Subject: "; - printf("From %s Thu Apr 7 15:13:13 2005\n", - sha1_to_hex(commit->object.sha1)); + printf("From %s Mon Sep 17 00:00:00 2001\n", sha1); + if (opt->mime_boundary) { + static char subject_buffer[1024]; + static char buffer[1024]; + snprintf(subject_buffer, sizeof(subject_buffer) - 1, + "MIME-Version: 1.0\n" + "Content-Type: multipart/mixed;\n" + " boundary=\"%s%s\"\n" + "\n" + "This is a multi-part message in MIME " + "format.\n" + "--%s%s\n" + "Content-Type: text/plain; " + "charset=UTF-8; format=fixed\n" + "Content-Transfer-Encoding: 8bit\n\n", + mime_boundary_leader, opt->mime_boundary, + mime_boundary_leader, opt->mime_boundary); + after_subject = subject_buffer; + + snprintf(buffer, sizeof(buffer) - 1, + "--%s%s\n" + "Content-Type: text/x-patch;\n" + " name=\"%s.diff\"\n" + "Content-Transfer-Encoding: 8bit\n" + "Content-Disposition: inline;\n" + " filename=\"%s.diff\"\n\n", + mime_boundary_leader, opt->mime_boundary, + sha1, sha1); + opt->diffopt.stat_sep = buffer; + } } else { printf("%s%s", opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ", @@ -81,7 +110,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) /* * And then the pretty-printed message itself */ - len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev, subject); + len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev, subject, after_subject); printf("%s%s%s", this_header, extra, sep); } diff --git a/revision.h b/revision.h index 62759f7bc0..bdbdd235d8 100644 --- a/revision.h +++ b/revision.h @@ -59,6 +59,7 @@ struct rev_info { enum cmit_fmt commit_format; struct log_info *loginfo; int nr, total; + const char *mime_boundary; /* special limits */ int max_count; diff --git a/show-branch.c b/show-branch.c index bbe26c2e7a..684ffd187c 100644 --- a/show-branch.c +++ b/show-branch.c @@ -259,7 +259,7 @@ static void show_one_commit(struct commit *commit, int no_name) struct commit_name *name = commit->object.util; if (commit->object.parsed) pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, - pretty, sizeof(pretty), 0, NULL); + pretty, sizeof(pretty), 0, NULL, NULL); else strcpy(pretty, "(unavailable)"); if (!strncmp(pretty, "[PATCH] ", 8)) From 685637381a967cd7388495f97b12b7cf177abbb4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 21 May 2006 02:48:21 -0700 Subject: [PATCH 23/40] git-format-patch: now built-in. Signed-off-by: Junio C Hamano --- Makefile | 4 +- git-format-patch.sh | 344 -------------------------------------------- git.c | 2 +- 3 files changed, 3 insertions(+), 347 deletions(-) delete mode 100755 git-format-patch.sh diff --git a/Makefile b/Makefile index 4fd6520b7e..fbb3dca2f3 100644 --- a/Makefile +++ b/Makefile @@ -116,7 +116,7 @@ SCRIPT_SH = \ git-add.sh git-bisect.sh git-branch.sh git-checkout.sh \ git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \ git-fetch.sh \ - git-format-patch.sh git-ls-remote.sh \ + git-ls-remote.sh \ git-merge-one-file.sh git-parse-remote.sh \ git-prune.sh git-pull.sh git-rebase.sh \ git-repack.sh git-request-pull.sh git-reset.sh \ @@ -171,7 +171,7 @@ PROGRAMS = \ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ - git-init-db$X + git-init-db$X git-format-patch$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) diff --git a/git-format-patch.sh b/git-format-patch.sh deleted file mode 100755 index 8a16eadfbd..0000000000 --- a/git-format-patch.sh +++ /dev/null @@ -1,344 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005 Junio C Hamano -# - -USAGE='[-n | -k] [-o | --stdout] [--signoff] [--check] [--diff-options] [--attach] []' -LONG_USAGE='Prepare each commit with its patch since head forked from - head, one file per patch formatted to resemble UNIX mailbox -format, for e-mail submission or use with git-am. - -Each output file is numbered sequentially from 1, and uses the -first line of the commit message (massaged for pathname safety) -as the filename. - -When -o is specified, output files are created in ; otherwise -they are created in the current working directory. This option -is ignored if --stdout is specified. - -When -n is specified, instead of "[PATCH] Subject", the first -line is formatted as "[PATCH N/M] Subject", unless you have only -one patch. - -When --attach is specified, patches are attached, not inlined.' - -. git-sh-setup - -# Force diff to run in C locale. -LANG=C LC_ALL=C -export LANG LC_ALL - -diff_opts= -LF=' -' - -outdir=./ -while case "$#" in 0) break;; esac -do - case "$1" in - -c|--c|--ch|--che|--chec|--check) - check=t ;; - -a|--a|--au|--aut|--auth|--autho|--author|\ - -d|--d|--da|--dat|--date|\ - -m|--m|--mb|--mbo|--mbox) # now noop - ;; - --at|--att|--atta|--attac|--attach) - attach=t ;; - -k|--k|--ke|--kee|--keep|--keep-|--keep-s|--keep-su|--keep-sub|\ - --keep-subj|--keep-subje|--keep-subjec|--keep-subject) - keep_subject=t ;; - -n|--n|--nu|--num|--numb|--numbe|--number|--numbere|--numbered) - numbered=t ;; - -s|--s|--si|--sig|--sign|--signo|--signof|--signoff) - signoff=t ;; - --st|--std|--stdo|--stdou|--stdout) - stdout=t ;; - -o=*|--o=*|--ou=*|--out=*|--outp=*|--outpu=*|--output=*|--output-=*|\ - --output-d=*|--output-di=*|--output-dir=*|--output-dire=*|\ - --output-direc=*|--output-direct=*|--output-directo=*|\ - --output-director=*|--output-directory=*) - outdir=`expr "$1" : '-[^=]*=\(.*\)'` ;; - -o|--o|--ou|--out|--outp|--outpu|--output|--output-|--output-d|\ - --output-di|--output-dir|--output-dire|--output-direc|--output-direct|\ - --output-directo|--output-director|--output-directory) - case "$#" in 1) usage ;; esac; shift - outdir="$1" ;; - -h|--h|--he|--hel|--help) - usage - ;; - -*' '* | -*"$LF"* | -*' '*) - # Ignore diff option that has whitespace for now. - ;; - -*) diff_opts="$diff_opts$1 " ;; - *) break ;; - esac - shift -done - -case "$keep_subject$numbered" in -tt) - die '--keep-subject and --numbered are incompatible.' ;; -esac - -tmp=.tmp-series$$ -trap 'rm -f $tmp-*' 0 1 2 3 15 - -series=$tmp-series -commsg=$tmp-commsg -filelist=$tmp-files - -# Backward compatible argument parsing hack. -# -# Historically, we supported: -# 1. "rev1" is equivalent to "rev1..HEAD" -# 2. "rev1..rev2" -# 3. "rev1" "rev2 is equivalent to "rev1..rev2" -# -# We want to take a sequence of "rev1..rev2" in general. -# Also, "rev1.." should mean "rev1..HEAD"; git-diff users are -# familiar with that syntax. - -case "$#,$1$2" in -1,?*..?*) - # single "rev1..rev2" - ;; -1,?*..) - # single "rev1.." should mean "rev1..HEAD" - set x "$1"HEAD - shift - ;; -1,*) - # single rev1 - set x "$1..HEAD" - shift - ;; -2,?*..?*) - # not traditional "rev1" "rev2" - ;; -2,*) - set x "$1..$2" - shift - ;; -esac - -# Now we have what we want in $@ -for revpair -do - case "$revpair" in - ?*..?*) - rev1=`expr "z$revpair" : 'z\(.*\)\.\.'` - rev2=`expr "z$revpair" : 'z.*\.\.\(.*\)'` - ;; - *) - rev1="$revpair^" - rev2="$revpair" - ;; - esac - git-rev-parse --verify "$rev1^0" >/dev/null 2>&1 || - die "Not a valid rev $rev1 ($revpair)" - git-rev-parse --verify "$rev2^0" >/dev/null 2>&1 || - die "Not a valid rev $rev2 ($revpair)" - git-cherry -v "$rev1" "$rev2" | - while read sign rev comment - do - case "$sign" in - '-') - echo >&2 "Merged already: $comment" - ;; - *) - echo $rev - ;; - esac - done -done >$series - -me=`git-var GIT_AUTHOR_IDENT | sed -e 's/>.*/>/'` -headers=`git-repo-config --get format.headers` -case "$attach" in -"") ;; -*) - mimemagic="050802040500080604070107" -esac - -case "$outdir" in -*/) ;; -*) outdir="$outdir/" ;; -esac -test -d "$outdir" || mkdir -p "$outdir" || exit - -titleScript=' - /./d - /^$/n - s/^\[PATCH[^]]*\] *// - s/[^-a-z.A-Z_0-9]/-/g - s/\.\.\.*/\./g - s/\.*$// - s/--*/-/g - s/^-// - s/-$// - s/$/./ - p - q -' - -process_one () { - perl -w -e ' -my ($keep_subject, $num, $signoff, $headers, $mimemagic, $commsg) = @ARGV; -my ($signoff_pattern, $done_header, $done_subject, $done_separator, $signoff_seen, - $last_was_signoff); - -if ($signoff) { - $signoff = "Signed-off-by: " . `git-var GIT_COMMITTER_IDENT`; - $signoff =~ s/>.*/>/; - $signoff_pattern = quotemeta($signoff); -} - -my @weekday_names = qw(Sun Mon Tue Wed Thu Fri Sat); -my @month_names = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); - -sub show_date { - my ($time, $tz) = @_; - my $minutes = abs($tz); - $minutes = int($minutes / 100) * 60 + ($minutes % 100); - if ($tz < 0) { - $minutes = -$minutes; - } - my $t = $time + $minutes * 60; - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($t); - return sprintf("%s, %d %s %d %02d:%02d:%02d %+05d", - $weekday_names[$wday], $mday, - $month_names[$mon], $year+1900, - $hour, $min, $sec, $tz); -} - -print "From nobody Mon Sep 17 00:00:00 2001\n"; -open FH, "git stripspace <$commsg |" or die "open $commsg pipe"; -while () { - unless ($done_header) { - if (/^$/) { - $done_header = 1; - } - elsif (/^author (.*>) (.*)$/) { - my ($author_ident, $author_date) = ($1, $2); - my ($utc, $off) = ($author_date =~ /^(\d+) ([-+]?\d+)$/); - $author_date = show_date($utc, $off); - - print "From: $author_ident\n"; - print "Date: $author_date\n"; - } - next; - } - unless ($done_subject) { - unless ($keep_subject) { - s/^\[PATCH[^]]*\]\s*//; - s/^/[PATCH$num] /; - } - if ($headers) { - print "$headers\n"; - } - print "Subject: $_"; - if ($mimemagic) { - print "MIME-Version: 1.0\n"; - print "Content-Type: multipart/mixed;\n"; - print " boundary=\"------------$mimemagic\"\n"; - print "\n"; - print "This is a multi-part message in MIME format.\n"; - print "--------------$mimemagic\n"; - print "Content-Type: text/plain; charset=UTF-8; format=fixed\n"; - print "Content-Transfer-Encoding: 8bit\n"; - } - $done_subject = 1; - next; - } - unless ($done_separator) { - print "\n"; - $done_separator = 1; - next if (/^$/); - } - - $last_was_signoff = 0; - if (/Signed-off-by:/i) { - if ($signoff ne "" && /Signed-off-by:\s*$signoff_pattern$/i) { - $signoff_seen = 1; - } - } - print $_; -} -if (!$signoff_seen && $signoff ne "") { - if (!$last_was_signoff) { - print "\n"; - } - print "$signoff\n"; -} -print "\n---\n\n"; -close FH or die "close $commsg pipe"; -' "$keep_subject" "$num" "$signoff" "$headers" "$mimemagic" $commsg - - git-diff-tree -p --stat --summary $diff_opts "$commit" - echo - case "$mimemagic" in - '');; - *) - echo "--------------$mimemagic" - echo "Content-Type: text/x-patch;" - echo " name=\"$commit.diff\"" - echo "Content-Transfer-Encoding: 8bit" - echo "Content-Disposition: inline;" - echo " filename=\"$commit.diff\"" - echo - esac - git-diff-tree -p $diff_opts "$commit" - case "$mimemagic" in - '') - echo "-- " - echo "@@GIT_VERSION@@" - ;; - *) - echo - echo "--------------$mimemagic--" - echo - ;; - esac - echo -} - -total=`wc -l <$series | tr -dc "[0-9]"` -case "$total,$numbered" in -1,*) - numfmt='' ;; -*,t) - numfmt=`echo "$total" | wc -c` - numfmt=$(($numfmt-1)) - numfmt=" %0${numfmt}d/$total" -esac - -i=1 -while read commit -do - git-cat-file commit "$commit" | git-stripspace >$commsg - title=`sed -ne "$titleScript" <$commsg` - case "$numbered" in - '') num= ;; - *) - num=`printf "$numfmt" $i` ;; - esac - - file=`printf '%04d-%stxt' $i "$title"` - if test '' = "$stdout" - then - echo "$file" - process_one >"$outdir$file" - if test t = "$check" - then - # This is slightly modified from Andrew Morton's Perfect Patch. - # Lines you introduce should not have trailing whitespace. - # Also check for an indentation that has SP before a TAB. - grep -n '^+\([ ]* .*\|.*[ ]\)$' "$outdir$file" - : - fi - else - echo >&2 "$file" - process_one - fi - i=`expr "$i" + 1` -done <$series diff --git a/git.c b/git.c index f4dff02bd3..ff498e674a 100644 --- a/git.c +++ b/git.c @@ -47,7 +47,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "whatchanged", cmd_whatchanged }, { "show", cmd_show }, { "push", cmd_push }, - { "fmt-patch", cmd_format_patch }, + { "format-patch", cmd_format_patch }, { "count-objects", cmd_count_objects }, { "diff", cmd_diff }, { "grep", cmd_grep }, From efbff23609654ac0542d3421d19eea8dbc1f80cd Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 21 May 2006 03:16:38 -0700 Subject: [PATCH 24/40] git-rebase: use canonical A..B syntax to format-patch Signed-off-by: Junio C Hamano --- git-rebase.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-rebase.sh b/git-rebase.sh index 6ff6088d18..e6b57b8ab9 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -152,6 +152,6 @@ then exit 0 fi -git-format-patch -k --stdout --full-index "$upstream" ORIG_HEAD | +git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD | git am --binary -3 -k --resolvemsg="$RESOLVEMSG" From cdd406e389bd6e0b2ad9394340a366c0c5ae27fc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 16 May 2006 02:29:42 -0700 Subject: [PATCH 25/40] CMIT_FMT_EMAIL: Q-encode Subject: and display-name part of From: fields. By convention, the commit message and the author/committer names in the commit objects are UTF-8 encoded. When formatting for e-mails, Q-encode them according to RFC 2047. While we are at it, generate the content-type and content-transfer-encoding headers as well. Signed-off-by: Junio C Hamano --- commit.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 7 deletions(-) diff --git a/commit.c b/commit.c index 0b163d4857..7597fba70a 100644 --- a/commit.c +++ b/commit.c @@ -422,6 +422,46 @@ static int get_one_line(const char *msg, unsigned long len) return ret; } +static int is_rfc2047_special(char ch) +{ + return ((ch & 0x80) || (ch == '=') || (ch == '?') || (ch == '_')); +} + +static int add_rfc2047(char *buf, const char *line, int len) +{ + char *bp = buf; + int i, needquote; + static const char q_utf8[] = "=?utf-8?q?"; + + for (i = needquote = 0; !needquote && i < len; i++) { + unsigned ch = line[i]; + if (ch & 0x80) + needquote++; + if ((i + 1 < len) && + (ch == '=' && line[i+1] == '?')) + needquote++; + } + if (!needquote) + return sprintf(buf, "%.*s", len, line); + + memcpy(bp, q_utf8, sizeof(q_utf8)-1); + bp += sizeof(q_utf8)-1; + for (i = 0; i < len; i++) { + unsigned ch = line[i]; + if (is_rfc2047_special(ch)) { + sprintf(bp, "=%02X", ch); + bp += 3; + } + else if (ch == ' ') + *bp++ = '_'; + else + *bp++ = ch; + } + memcpy(bp, "?=", 2); + bp += 2; + return bp - buf; +} + static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const char *line) { char *date; @@ -440,12 +480,26 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c tz = strtol(date, NULL, 10); if (fmt == CMIT_FMT_EMAIL) { - what = "From"; + char *name_tail = strchr(line, '<'); + int display_name_length; + if (!name_tail) + return 0; + while (line < name_tail && isspace(name_tail[-1])) + name_tail--; + display_name_length = name_tail - line; filler = ""; + strcpy(buf, "From: "); + ret = strlen(buf); + ret += add_rfc2047(buf + ret, line, display_name_length); + memcpy(buf + ret, name_tail, namelen - display_name_length); + ret += namelen - display_name_length; + buf[ret++] = '\n'; + } + else { + ret = sprintf(buf, "%s: %.*s%.*s\n", what, + (fmt == CMIT_FMT_FULLER) ? 4 : 0, + filler, namelen, line); } - ret = sprintf(buf, "%s: %.*s%.*s\n", what, - (fmt == CMIT_FMT_FULLER) ? 4 : 0, - filler, namelen, line); switch (fmt) { case CMIT_FMT_MEDIUM: ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz)); @@ -584,13 +638,24 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit int slen = strlen(subject); memcpy(buf + offset, subject, slen); offset += slen; + offset += add_rfc2047(buf + offset, line, linelen); + } + else { + memset(buf + offset, ' ', indent); + memcpy(buf + offset + indent, line, linelen); + offset += linelen + indent; } - memset(buf + offset, ' ', indent); - memcpy(buf + offset + indent, line, linelen); - offset += linelen + indent; buf[offset++] = '\n'; if (fmt == CMIT_FMT_ONELINE) break; + if (subject) { + static const char header[] = + "Content-Type: text/plain; charset=UTF-8\n" + "Content-Transfer-Encoding: 8bit\n"; + memcpy(buf + offset, header, sizeof(header)-1); + offset += sizeof(header)-1; + subject = NULL; + } if (after_subject) { int slen = strlen(after_subject); if (slen > space - offset - 1) From c831da664799eb4dbf082a843c34c66889548768 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 21 May 2006 23:55:00 -0700 Subject: [PATCH 26/40] builtin format-patch: squelch content-type for 7-bit ASCII When --attach is not used, usually we do not say Content-Type: and fluff, but if the commit message is not 7-bit ASCII, mark it as "text/plain; charset=UTF-8". This unclutters output somewhat. Signed-off-by: Junio C Hamano --- commit.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/commit.c b/commit.c index 7597fba70a..94f470b75c 100644 --- a/commit.c +++ b/commit.c @@ -559,10 +559,24 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit int indent = 4; int parents_shown = 0; const char *msg = commit->buffer; + int plain_non_ascii = 0; if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) indent = 0; + /* After-subject is used to pass in Content-Type: multipart + * MIME header; in that case we do not have to do the + * plaintext content type even if the commit message has + * non 7-bit ASCII character. Otherwise, check if we need + * to say this is not a 7-bit ASCII. + */ + if (fmt == CMIT_FMT_EMAIL && !after_subject) { + int i; + for (i = 0; !plain_non_ascii && msg[i] && i < len; i++) + if (msg[i] & 0x80) + plain_non_ascii = 1; + } + for (;;) { const char *line = msg; int linelen = get_one_line(msg, len); @@ -648,13 +662,12 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit buf[offset++] = '\n'; if (fmt == CMIT_FMT_ONELINE) break; - if (subject) { + if (subject && plain_non_ascii) { static const char header[] = "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n"; memcpy(buf + offset, header, sizeof(header)-1); offset += sizeof(header)-1; - subject = NULL; } if (after_subject) { int slen = strlen(after_subject); From 9546010b7bbc25e037029a7dd83de9c57b7a935c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 11 May 2006 15:28:44 -0700 Subject: [PATCH 27/40] fetch-pack: output refs in the order they were given on the command line. Currently, fetched refs are output in the order the remote side happened to send them. This changes the order to match the order of refs that were given on the command line. To the existing core callers (git-fetch and git-clone) this does not make any difference, but for other Porcelain use, it would be more intuitive. Signed-off-by: Junio C Hamano --- connect.c | 2 +- fetch-pack.c | 64 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/connect.c b/connect.c index 6a8f8a6a24..54f7bf7915 100644 --- a/connect.c +++ b/connect.c @@ -100,7 +100,7 @@ int path_match(const char *path, int nr, char **match) if (pathlen > len && path[pathlen - len - 1] != '/') continue; *s = 0; - return 1; + return (i + 1); } return 0; } diff --git a/fetch-pack.c b/fetch-pack.c index a3bcad016f..8daa93d024 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -262,22 +262,58 @@ static void mark_recent_complete_commits(unsigned long cutoff) static void filter_refs(struct ref **refs, int nr_match, char **match) { - struct ref *prev, *current, *next; + struct ref **return_refs; + struct ref *newlist = NULL; + struct ref **newtail = &newlist; + struct ref *ref, *next; + struct ref *fastarray[32]; - for (prev = NULL, current = *refs; current; current = next) { - next = current->next; - if ((!memcmp(current->name, "refs/", 5) && - check_ref_format(current->name + 5)) || - (!fetch_all && - !path_match(current->name, nr_match, match))) { - if (prev == NULL) - *refs = next; - else - prev->next = next; - free(current); - } else - prev = current; + if (nr_match && !fetch_all) { + if (ARRAY_SIZE(fastarray) < nr_match) + return_refs = xcalloc(nr_match, sizeof(struct ref *)); + else { + return_refs = fastarray; + memset(return_refs, 0, sizeof(struct ref *) * nr_match); + } } + else + return_refs = NULL; + + for (ref = *refs; ref; ref = next) { + next = ref->next; + if (!memcmp(ref->name, "refs/", 5) && + check_ref_format(ref->name + 5)) + ; /* trash */ + else if (fetch_all) { + *newtail = ref; + ref->next = NULL; + newtail = &ref->next; + continue; + } + else { + int order = path_match(ref->name, nr_match, match); + if (order) { + return_refs[order-1] = ref; + continue; /* we will link it later */ + } + } + free(ref); + } + + if (!fetch_all) { + int i; + for (i = 0; i < nr_match; i++) { + ref = return_refs[i]; + if (ref) { + *newtail = ref; + ref->next = NULL; + newtail = &ref->next; + } + } + if (return_refs != fastarray) + free(return_refs); + } + *refs = newlist; } static int everything_local(struct ref **refs, int nr_match, char **match) From 0864f26421b3c599b462bc867de948d14b268d76 Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Tue, 23 May 2006 14:15:29 +0200 Subject: [PATCH 28/40] Builtin git-ls-files. Signed-off-by: Peter Eriksen Signed-off-by: Junio C Hamano --- Makefile | 6 +++--- ls-files.c => builtin-ls-files.c | 3 ++- builtin.h | 1 + git.c | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) rename ls-files.c => builtin-ls-files.c (99%) diff --git a/Makefile b/Makefile index 5423b7a79b..c540d7d9f1 100644 --- a/Makefile +++ b/Makefile @@ -155,7 +155,7 @@ PROGRAMS = \ git-diff-index$X git-diff-stages$X \ git-diff-tree$X git-fetch-pack$X git-fsck-objects$X \ git-hash-object$X git-index-pack$X git-local-fetch$X \ - git-ls-files$X git-ls-tree$X git-mailinfo$X git-merge-base$X \ + git-ls-tree$X git-mailinfo$X git-merge-base$X \ git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \ git-peek-remote$X git-prune-packed$X git-read-tree$X \ git-receive-pack$X git-rev-parse$X \ @@ -171,7 +171,7 @@ PROGRAMS = \ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ - git-init-db$X + git-init-db$X git-ls-files$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -220,7 +220,7 @@ LIB_OBJS = \ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ - builtin-init-db.o + builtin-init-db.o builtin-ls-files.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/ls-files.c b/builtin-ls-files.c similarity index 99% rename from ls-files.c rename to builtin-ls-files.c index 4a4af1ca3b..3a0c5f2150 100644 --- a/ls-files.c +++ b/builtin-ls-files.c @@ -10,6 +10,7 @@ #include "cache.h" #include "quote.h" +#include "builtin.h" static int abbrev = 0; static int show_deleted = 0; @@ -648,7 +649,7 @@ static const char ls_files_usage[] = "[ --exclude-per-directory= ] [--full-name] [--abbrev] " "[--] []*"; -int main(int argc, const char **argv) +int cmd_ls_files(int argc, const char **argv, char** envp) { int i; int exc_given = 0; diff --git a/builtin.h b/builtin.h index 60541262c4..a0713d3747 100644 --- a/builtin.h +++ b/builtin.h @@ -27,5 +27,6 @@ extern int cmd_grep(int argc, const char **argv, char **envp); extern int cmd_rev_list(int argc, const char **argv, char **envp); extern int cmd_check_ref_format(int argc, const char **argv, char **envp); extern int cmd_init_db(int argc, const char **argv, char **envp); +extern int cmd_ls_files(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index 3216d311b2..9cfa9ebced 100644 --- a/git.c +++ b/git.c @@ -52,7 +52,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "grep", cmd_grep }, { "rev-list", cmd_rev_list }, { "init-db", cmd_init_db }, - { "check-ref-format", cmd_check_ref_format } + { "check-ref-format", cmd_check_ref_format }, + { "ls-files", cmd_ls_files } }; int i; From aae01bda7f6d3224cf6b2ce0aa9aa668ce35d0b7 Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Tue, 23 May 2006 14:15:30 +0200 Subject: [PATCH 29/40] Builtin git-ls-tree. Signed-off-by: Peter Eriksen Signed-off-by: Junio C Hamano --- Makefile | 6 +++--- ls-tree.c => builtin-ls-tree.c | 5 +++-- builtin.h | 1 + git.c | 3 ++- 4 files changed, 9 insertions(+), 6 deletions(-) rename ls-tree.c => builtin-ls-tree.c (96%) diff --git a/Makefile b/Makefile index c540d7d9f1..2afd089a0d 100644 --- a/Makefile +++ b/Makefile @@ -155,7 +155,7 @@ PROGRAMS = \ git-diff-index$X git-diff-stages$X \ git-diff-tree$X git-fetch-pack$X git-fsck-objects$X \ git-hash-object$X git-index-pack$X git-local-fetch$X \ - git-ls-tree$X git-mailinfo$X git-merge-base$X \ + git-mailinfo$X git-merge-base$X \ git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \ git-peek-remote$X git-prune-packed$X git-read-tree$X \ git-receive-pack$X git-rev-parse$X \ @@ -171,7 +171,7 @@ PROGRAMS = \ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ - git-init-db$X git-ls-files$X + git-init-db$X git-ls-files$X git-ls-tree$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -220,7 +220,7 @@ LIB_OBJS = \ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ - builtin-init-db.o builtin-ls-files.o + builtin-init-db.o builtin-ls-files.o builtin-ls-tree.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/ls-tree.c b/builtin-ls-tree.c similarity index 96% rename from ls-tree.c rename to builtin-ls-tree.c index f2b3bc1231..48385d59f6 100644 --- a/ls-tree.c +++ b/builtin-ls-tree.c @@ -7,6 +7,7 @@ #include "blob.h" #include "tree.h" #include "quote.h" +#include "builtin.h" static int line_termination = '\n'; #define LS_RECURSIVE 1 @@ -15,7 +16,7 @@ static int line_termination = '\n'; #define LS_NAME_ONLY 8 static int abbrev = 0; static int ls_options = 0; -const char **pathspec; +static const char **pathspec; static int chomp_prefix = 0; static const char *prefix; @@ -84,7 +85,7 @@ static int show_tree(unsigned char *sha1, const char *base, int baselen, return retval; } -int main(int argc, const char **argv) +int cmd_ls_tree(int argc, const char **argv, char **envp) { unsigned char sha1[20]; struct tree *tree; diff --git a/builtin.h b/builtin.h index a0713d3747..951f206372 100644 --- a/builtin.h +++ b/builtin.h @@ -28,5 +28,6 @@ extern int cmd_rev_list(int argc, const char **argv, char **envp); extern int cmd_check_ref_format(int argc, const char **argv, char **envp); extern int cmd_init_db(int argc, const char **argv, char **envp); extern int cmd_ls_files(int argc, const char **argv, char **envp); +extern int cmd_ls_tree(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index 9cfa9ebced..8574775418 100644 --- a/git.c +++ b/git.c @@ -53,7 +53,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "rev-list", cmd_rev_list }, { "init-db", cmd_init_db }, { "check-ref-format", cmd_check_ref_format }, - { "ls-files", cmd_ls_files } + { "ls-files", cmd_ls_files }, + { "ls-tree", cmd_ls_tree } }; int i; From 56d1398ad305498faf57d6e433f97ad393d7909e Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Tue, 23 May 2006 14:15:31 +0200 Subject: [PATCH 30/40] Builtin git-tar-tree. Signed-off-by: Peter Eriksen Signed-off-by: Junio C Hamano --- Makefile | 8 +++++--- tar-tree.c => builtin-tar-tree.c | 3 ++- builtin.h | 1 + git.c | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) rename tar-tree.c => builtin-tar-tree.c (99%) diff --git a/Makefile b/Makefile index 2afd089a0d..a3164f8ef0 100644 --- a/Makefile +++ b/Makefile @@ -161,7 +161,7 @@ PROGRAMS = \ git-receive-pack$X git-rev-parse$X \ git-send-pack$X git-show-branch$X git-shell$X \ git-show-index$X git-ssh-fetch$X \ - git-ssh-upload$X git-tar-tree$X git-unpack-file$X \ + git-ssh-upload$X git-unpack-file$X \ git-unpack-objects$X git-update-index$X git-update-server-info$X \ git-upload-pack$X git-verify-pack$X git-write-tree$X \ git-update-ref$X git-symbolic-ref$X \ @@ -171,7 +171,8 @@ PROGRAMS = \ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ - git-init-db$X git-ls-files$X git-ls-tree$X + git-init-db$X git-ls-files$X git-ls-tree$X \ + git-tar-tree$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -220,7 +221,8 @@ LIB_OBJS = \ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ - builtin-init-db.o builtin-ls-files.o builtin-ls-tree.o + builtin-init-db.o builtin-ls-files.o builtin-ls-tree.o \ + builtin-tar-tree.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/tar-tree.c b/builtin-tar-tree.c similarity index 99% rename from tar-tree.c rename to builtin-tar-tree.c index 33087366c3..6ada04ce38 100644 --- a/tar-tree.c +++ b/builtin-tar-tree.c @@ -7,6 +7,7 @@ #include "commit.h" #include "strbuf.h" #include "tar.h" +#include "builtin.h" #define RECORDSIZE (512) #define BLOCKSIZE (RECORDSIZE * 20) @@ -301,7 +302,7 @@ static void traverse_tree(struct tree_desc *tree, struct strbuf *path) } } -int main(int argc, char **argv) +int cmd_tar_tree(int argc, const char **argv, char** envp) { unsigned char sha1[20], tree_sha1[20]; struct commit *commit; diff --git a/builtin.h b/builtin.h index 951f206372..d210543948 100644 --- a/builtin.h +++ b/builtin.h @@ -29,5 +29,6 @@ extern int cmd_check_ref_format(int argc, const char **argv, char **envp); extern int cmd_init_db(int argc, const char **argv, char **envp); extern int cmd_ls_files(int argc, const char **argv, char **envp); extern int cmd_ls_tree(int argc, const char **argv, char **envp); +extern int cmd_tar_tree(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index 8574775418..c253e60953 100644 --- a/git.c +++ b/git.c @@ -54,7 +54,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "init-db", cmd_init_db }, { "check-ref-format", cmd_check_ref_format }, { "ls-files", cmd_ls_files }, - { "ls-tree", cmd_ls_tree } + { "ls-tree", cmd_ls_tree }, + { "tar-tree", cmd_tar_tree } }; int i; From d147e501f37a596e73a430ce46f125f83e06aa07 Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Tue, 23 May 2006 14:15:32 +0200 Subject: [PATCH 31/40] Builtin git-read-tree. Signed-off-by: Peter Eriksen Signed-off-by: Junio C Hamano --- Makefile | 6 +++--- read-tree.c => builtin-read-tree.c | 3 ++- builtin.h | 1 + git.c | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) rename read-tree.c => builtin-read-tree.c (99%) diff --git a/Makefile b/Makefile index a3164f8ef0..1aebcf5978 100644 --- a/Makefile +++ b/Makefile @@ -157,7 +157,7 @@ PROGRAMS = \ git-hash-object$X git-index-pack$X git-local-fetch$X \ git-mailinfo$X git-merge-base$X \ git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \ - git-peek-remote$X git-prune-packed$X git-read-tree$X \ + git-peek-remote$X git-prune-packed$X \ git-receive-pack$X git-rev-parse$X \ git-send-pack$X git-show-branch$X git-shell$X \ git-show-index$X git-ssh-fetch$X \ @@ -172,7 +172,7 @@ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ git-init-db$X git-ls-files$X git-ls-tree$X \ - git-tar-tree$X + git-tar-tree$X git-read-tree$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -222,7 +222,7 @@ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ builtin-init-db.o builtin-ls-files.o builtin-ls-tree.o \ - builtin-tar-tree.o + builtin-tar-tree.o builtin-read-tree.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/read-tree.c b/builtin-read-tree.c similarity index 99% rename from read-tree.c rename to builtin-read-tree.c index 82e2a9a4d3..ec40d013c4 100644 --- a/read-tree.c +++ b/builtin-read-tree.c @@ -11,6 +11,7 @@ #include "tree.h" #include #include +#include "builtin.h" static int reset = 0; static int merge = 0; @@ -763,7 +764,7 @@ static const char read_tree_usage[] = "git-read-tree ( | -m [--aggressive] static struct cache_file cache_file; -int main(int argc, char **argv) +int cmd_read_tree(int argc, const char **argv, char **envp) { int i, newfd, stage = 0; unsigned char sha1[20]; diff --git a/builtin.h b/builtin.h index d210543948..88b3523c25 100644 --- a/builtin.h +++ b/builtin.h @@ -30,5 +30,6 @@ extern int cmd_init_db(int argc, const char **argv, char **envp); extern int cmd_ls_files(int argc, const char **argv, char **envp); extern int cmd_ls_tree(int argc, const char **argv, char **envp); extern int cmd_tar_tree(int argc, const char **argv, char **envp); +extern int cmd_read_tree(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index c253e60953..300e2b269c 100644 --- a/git.c +++ b/git.c @@ -55,7 +55,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "check-ref-format", cmd_check_ref_format }, { "ls-files", cmd_ls_files }, { "ls-tree", cmd_ls_tree }, - { "tar-tree", cmd_tar_tree } + { "tar-tree", cmd_tar_tree }, + { "read-tree", cmd_read_tree } }; int i; From 6d96ac18e52aca19ff1087ffa64e2d616cc75c6f Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Tue, 23 May 2006 14:15:33 +0200 Subject: [PATCH 32/40] Builtin git-commit-tree. Signed-off-by: Peter Eriksen Signed-off-by: Junio C Hamano --- Makefile | 6 +++--- commit-tree.c => builtin-commit-tree.c | 5 +++-- builtin.h | 1 + git.c | 3 ++- 4 files changed, 9 insertions(+), 6 deletions(-) rename commit-tree.c => builtin-commit-tree.c (96%) diff --git a/Makefile b/Makefile index 1aebcf5978..a1bee61e57 100644 --- a/Makefile +++ b/Makefile @@ -150,7 +150,7 @@ SIMPLE_PROGRAMS = \ # ... and all the rest that could be moved out of bindir to gitexecdir PROGRAMS = \ git-apply$X git-cat-file$X \ - git-checkout-index$X git-clone-pack$X git-commit-tree$X \ + git-checkout-index$X git-clone-pack$X \ git-convert-objects$X git-diff-files$X \ git-diff-index$X git-diff-stages$X \ git-diff-tree$X git-fetch-pack$X git-fsck-objects$X \ @@ -172,7 +172,7 @@ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ git-init-db$X git-ls-files$X git-ls-tree$X \ - git-tar-tree$X git-read-tree$X + git-tar-tree$X git-read-tree$X git-commit-tree$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -222,7 +222,7 @@ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ builtin-init-db.o builtin-ls-files.o builtin-ls-tree.o \ - builtin-tar-tree.o builtin-read-tree.o + builtin-tar-tree.o builtin-read-tree.o builtin-commit-tree.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/commit-tree.c b/builtin-commit-tree.c similarity index 96% rename from commit-tree.c rename to builtin-commit-tree.c index 0320036e80..ec082bf754 100644 --- a/commit-tree.c +++ b/builtin-commit-tree.c @@ -6,6 +6,7 @@ #include "cache.h" #include "commit.h" #include "tree.h" +#include "builtin.h" #define BLOCKING (1ul << 14) @@ -76,7 +77,7 @@ static int new_parent(int idx) return 1; } -int main(int argc, char **argv) +int cmd_commit_tree(int argc, const char **argv, char **envp) { int i; int parents = 0; @@ -98,7 +99,7 @@ int main(int argc, char **argv) check_valid(tree_sha1, tree_type); for (i = 2; i < argc; i += 2) { - char *a, *b; + const char *a, *b; a = argv[i]; b = argv[i+1]; if (!b || strcmp(a, "-p")) usage(commit_tree_usage); diff --git a/builtin.h b/builtin.h index 88b3523c25..c6b07d9a65 100644 --- a/builtin.h +++ b/builtin.h @@ -31,5 +31,6 @@ extern int cmd_ls_files(int argc, const char **argv, char **envp); extern int cmd_ls_tree(int argc, const char **argv, char **envp); extern int cmd_tar_tree(int argc, const char **argv, char **envp); extern int cmd_read_tree(int argc, const char **argv, char **envp); +extern int cmd_commit_tree(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index 300e2b269c..4c2c062e36 100644 --- a/git.c +++ b/git.c @@ -56,7 +56,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "ls-files", cmd_ls_files }, { "ls-tree", cmd_ls_tree }, { "tar-tree", cmd_tar_tree }, - { "read-tree", cmd_read_tree } + { "read-tree", cmd_read_tree }, + { "commit-tree", cmd_commit_tree } }; int i; From ac6245e31a359200b65bfdd910bba9a0fbe90c11 Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Tue, 23 May 2006 14:15:34 +0200 Subject: [PATCH 33/40] Builtin git-apply. Signed-off-by: Peter Eriksen Signed-off-by: Junio C Hamano --- Makefile | 8 +++++--- apply.c => builtin-apply.c | 3 ++- builtin.h | 1 + git.c | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) rename apply.c => builtin-apply.c (99%) diff --git a/Makefile b/Makefile index a1bee61e57..3da576838f 100644 --- a/Makefile +++ b/Makefile @@ -149,7 +149,7 @@ SIMPLE_PROGRAMS = \ # ... and all the rest that could be moved out of bindir to gitexecdir PROGRAMS = \ - git-apply$X git-cat-file$X \ + git-cat-file$X \ git-checkout-index$X git-clone-pack$X \ git-convert-objects$X git-diff-files$X \ git-diff-index$X git-diff-stages$X \ @@ -172,7 +172,8 @@ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ git-init-db$X git-ls-files$X git-ls-tree$X \ - git-tar-tree$X git-read-tree$X git-commit-tree$X + git-tar-tree$X git-read-tree$X git-commit-tree$X \ + git-apply$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -222,7 +223,8 @@ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ builtin-init-db.o builtin-ls-files.o builtin-ls-tree.o \ - builtin-tar-tree.o builtin-read-tree.o builtin-commit-tree.o + builtin-tar-tree.o builtin-read-tree.o builtin-commit-tree.o \ + builtin-apply.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/apply.c b/builtin-apply.c similarity index 99% rename from apply.c rename to builtin-apply.c index 0ed9d132e8..4056b9d67b 100644 --- a/apply.c +++ b/builtin-apply.c @@ -11,6 +11,7 @@ #include "quote.h" #include "blob.h" #include "delta.h" +#include "builtin.h" // --check turns on checking that the working tree matches the // files that are being modified, but doesn't apply the patch @@ -2151,7 +2152,7 @@ static int git_apply_config(const char *var, const char *value) } -int main(int argc, char **argv) +int cmd_apply(int argc, const char **argv, char **envp) { int i; int read_stdin = 1; diff --git a/builtin.h b/builtin.h index c6b07d9a65..d6ff88ebc1 100644 --- a/builtin.h +++ b/builtin.h @@ -32,5 +32,6 @@ extern int cmd_ls_tree(int argc, const char **argv, char **envp); extern int cmd_tar_tree(int argc, const char **argv, char **envp); extern int cmd_read_tree(int argc, const char **argv, char **envp); extern int cmd_commit_tree(int argc, const char **argv, char **envp); +extern int cmd_apply(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index 4c2c062e36..f44e08b9ce 100644 --- a/git.c +++ b/git.c @@ -57,7 +57,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "ls-tree", cmd_ls_tree }, { "tar-tree", cmd_tar_tree }, { "read-tree", cmd_read_tree }, - { "commit-tree", cmd_commit_tree } + { "commit-tree", cmd_commit_tree }, + { "apply", cmd_apply } }; int i; From 51ce34b9923d9b119ac53414584f80e05520abea Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Tue, 23 May 2006 14:15:35 +0200 Subject: [PATCH 34/40] Builtin git-show-branch. Signed-off-by: Peter Eriksen Signed-off-by: Junio C Hamano --- Makefile | 6 +++--- show-branch.c => builtin-show-branch.c | 7 ++++--- builtin.h | 1 + git.c | 3 ++- 4 files changed, 10 insertions(+), 7 deletions(-) rename show-branch.c => builtin-show-branch.c (99%) diff --git a/Makefile b/Makefile index 3da576838f..69377e37b4 100644 --- a/Makefile +++ b/Makefile @@ -159,7 +159,7 @@ PROGRAMS = \ git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \ git-peek-remote$X git-prune-packed$X \ git-receive-pack$X git-rev-parse$X \ - git-send-pack$X git-show-branch$X git-shell$X \ + git-send-pack$X git-shell$X \ git-show-index$X git-ssh-fetch$X \ git-ssh-upload$X git-unpack-file$X \ git-unpack-objects$X git-update-index$X git-update-server-info$X \ @@ -173,7 +173,7 @@ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ git-init-db$X git-ls-files$X git-ls-tree$X \ git-tar-tree$X git-read-tree$X git-commit-tree$X \ - git-apply$X + git-apply$X git-show-branch$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -224,7 +224,7 @@ BUILTIN_OBJS = \ builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ builtin-init-db.o builtin-ls-files.o builtin-ls-tree.o \ builtin-tar-tree.o builtin-read-tree.o builtin-commit-tree.o \ - builtin-apply.o + builtin-apply.o builtin-show-branch.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/show-branch.c b/builtin-show-branch.c similarity index 99% rename from show-branch.c rename to builtin-show-branch.c index 268c57b180..3af24e767b 100644 --- a/show-branch.c +++ b/builtin-show-branch.c @@ -3,13 +3,14 @@ #include "cache.h" #include "commit.h" #include "refs.h" +#include "builtin.h" static const char show_branch_usage[] = "git-show-branch [--dense] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [...]"; static int default_num = 0; static int default_alloc = 0; -static char **default_arg = NULL; +static const char **default_arg = NULL; #define UNINTERESTING 01 @@ -548,7 +549,7 @@ static int omit_in_dense(struct commit *commit, struct commit **rev, int n) return 0; } -int main(int ac, char **av) +int cmd_show_branch(int ac, const char **av, char **envp) { struct commit *rev[MAX_REVS], *commit; struct commit_list *list = NULL, *seen = NULL; @@ -581,7 +582,7 @@ int main(int ac, char **av) } while (1 < ac && av[1][0] == '-') { - char *arg = av[1]; + const char *arg = av[1]; if (!strcmp(arg, "--")) { ac--; av++; break; diff --git a/builtin.h b/builtin.h index d6ff88ebc1..01882ec93d 100644 --- a/builtin.h +++ b/builtin.h @@ -33,5 +33,6 @@ extern int cmd_tar_tree(int argc, const char **argv, char **envp); extern int cmd_read_tree(int argc, const char **argv, char **envp); extern int cmd_commit_tree(int argc, const char **argv, char **envp); extern int cmd_apply(int argc, const char **argv, char **envp); +extern int cmd_show_branch(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index f44e08b9ce..d29505c4c9 100644 --- a/git.c +++ b/git.c @@ -58,7 +58,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "tar-tree", cmd_tar_tree }, { "read-tree", cmd_read_tree }, { "commit-tree", cmd_commit_tree }, - { "apply", cmd_apply } + { "apply", cmd_apply }, + { "show-branch", cmd_show_branch } }; int i; From e8cc9cd98e2ecd7fd8bb03e725d470405c8e2b94 Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Tue, 23 May 2006 14:15:36 +0200 Subject: [PATCH 35/40] Builtin git-diff-files, git-diff-index, git-diff-stages, and git-diff-tree. Signed-off-by: Peter Eriksen Signed-off-by: Junio C Hamano --- Makefile | 12 ++++++------ diff-files.c => builtin-diff-files.c | 3 ++- diff-index.c => builtin-diff-index.c | 3 ++- diff-stages.c => builtin-diff-stages.c | 3 ++- diff-tree.c => builtin-diff-tree.c | 3 ++- builtin.h | 5 +++++ git.c | 6 +++++- 7 files changed, 24 insertions(+), 11 deletions(-) rename diff-files.c => builtin-diff-files.c (94%) rename diff-index.c => builtin-diff-index.c (91%) rename diff-stages.c => builtin-diff-stages.c (96%) rename diff-tree.c => builtin-diff-tree.c (97%) diff --git a/Makefile b/Makefile index 69377e37b4..fc5f98b908 100644 --- a/Makefile +++ b/Makefile @@ -151,9 +151,7 @@ SIMPLE_PROGRAMS = \ PROGRAMS = \ git-cat-file$X \ git-checkout-index$X git-clone-pack$X \ - git-convert-objects$X git-diff-files$X \ - git-diff-index$X git-diff-stages$X \ - git-diff-tree$X git-fetch-pack$X git-fsck-objects$X \ + git-convert-objects$X git-fetch-pack$X git-fsck-objects$X \ git-hash-object$X git-index-pack$X git-local-fetch$X \ git-mailinfo$X git-merge-base$X \ git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \ @@ -173,7 +171,8 @@ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ git-init-db$X git-ls-files$X git-ls-tree$X \ git-tar-tree$X git-read-tree$X git-commit-tree$X \ - git-apply$X git-show-branch$X + git-apply$X git-show-branch$X git-diff-files$X \ + git-diff-index$X git-diff-stages$X git-diff-tree$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -223,8 +222,9 @@ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ builtin-init-db.o builtin-ls-files.o builtin-ls-tree.o \ - builtin-tar-tree.o builtin-read-tree.o builtin-commit-tree.o \ - builtin-apply.o builtin-show-branch.o + builtin-tar-tree.o builtin-read-tree.o builtin-commit-tree.o \ + builtin-apply.o builtin-show-branch.o builtin-diff-files.o \ + builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/diff-files.c b/builtin-diff-files.c similarity index 94% rename from diff-files.c rename to builtin-diff-files.c index b9d193d506..cebda828ee 100644 --- a/diff-files.c +++ b/builtin-diff-files.c @@ -7,12 +7,13 @@ #include "diff.h" #include "commit.h" #include "revision.h" +#include "builtin.h" static const char diff_files_usage[] = "git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [] [...]" COMMON_DIFF_OPTIONS_HELP; -int main(int argc, const char **argv) +int cmd_diff_files(int argc, const char **argv, char **envp) { struct rev_info rev; int silent = 0; diff --git a/diff-index.c b/builtin-diff-index.c similarity index 91% rename from diff-index.c rename to builtin-diff-index.c index 8c9f60173b..1958580d82 100644 --- a/diff-index.c +++ b/builtin-diff-index.c @@ -2,13 +2,14 @@ #include "diff.h" #include "commit.h" #include "revision.h" +#include "builtin.h" static const char diff_cache_usage[] = "git-diff-index [-m] [--cached] " "[] [...]" COMMON_DIFF_OPTIONS_HELP; -int main(int argc, const char **argv) +int cmd_diff_index(int argc, const char **argv, char **envp) { struct rev_info rev; int cached = 0; diff --git a/diff-stages.c b/builtin-diff-stages.c similarity index 96% rename from diff-stages.c rename to builtin-diff-stages.c index dcd20e79e4..7c157ca889 100644 --- a/diff-stages.c +++ b/builtin-diff-stages.c @@ -4,6 +4,7 @@ #include "cache.h" #include "diff.h" +#include "builtin.h" static struct diff_options diff_options; @@ -54,7 +55,7 @@ static void diff_stages(int stage1, int stage2, const char **pathspec) } } -int main(int ac, const char **av) +int cmd_diff_stages(int ac, const char **av, char **envp) { int stage1, stage2; const char *prefix = setup_git_directory(); diff --git a/diff-tree.c b/builtin-diff-tree.c similarity index 97% rename from diff-tree.c rename to builtin-diff-tree.c index 69bb74b310..cc53b81ac4 100644 --- a/diff-tree.c +++ b/builtin-diff-tree.c @@ -2,6 +2,7 @@ #include "diff.h" #include "commit.h" #include "log-tree.h" +#include "builtin.h" static struct rev_info log_tree_opt; @@ -58,7 +59,7 @@ static const char diff_tree_usage[] = " --root include the initial commit as diff against /dev/null\n" COMMON_DIFF_OPTIONS_HELP; -int main(int argc, const char **argv) +int cmd_diff_tree(int argc, const char **argv, char **envp) { int nr_sha1; char line[1000]; diff --git a/builtin.h b/builtin.h index 01882ec93d..7620984624 100644 --- a/builtin.h +++ b/builtin.h @@ -34,5 +34,10 @@ extern int cmd_read_tree(int argc, const char **argv, char **envp); extern int cmd_commit_tree(int argc, const char **argv, char **envp); extern int cmd_apply(int argc, const char **argv, char **envp); extern int cmd_show_branch(int argc, const char **argv, char **envp); +extern int cmd_diff_files(int argc, const char **argv, char **envp); +extern int cmd_diff_index(int argc, const char **argv, char **envp); +extern int cmd_diff_stages(int argc, const char **argv, char **envp); +extern int cmd_diff_tree(int argc, const char **argv, char **envp); + #endif diff --git a/git.c b/git.c index d29505c4c9..874974874b 100644 --- a/git.c +++ b/git.c @@ -59,7 +59,11 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "read-tree", cmd_read_tree }, { "commit-tree", cmd_commit_tree }, { "apply", cmd_apply }, - { "show-branch", cmd_show_branch } + { "show-branch", cmd_show_branch }, + { "diff-files", cmd_diff_files }, + { "diff-index", cmd_diff_index }, + { "diff-stages", cmd_diff_stages }, + { "diff-tree", cmd_diff_tree } }; int i; From e7332f96b32bedbc39ff6ff7e845c902056099f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Engelmann?= Date: Tue, 23 May 2006 20:19:04 +0200 Subject: [PATCH 36/40] remove the artificial restriction tagsize < 8kb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Engelmann Signed-off-by: Junio C Hamano --- cache.h | 1 + mktag.c | 19 +++++++++---------- sha1_file.c | 46 ++++++++++++++++++++++++++++++++++++---------- 3 files changed, 46 insertions(+), 20 deletions(-) diff --git a/cache.h b/cache.h index afa8e4f0ac..b19d84470b 100644 --- a/cache.h +++ b/cache.h @@ -154,6 +154,7 @@ extern int ce_match_stat(struct cache_entry *ce, struct stat *st, int); extern int ce_modified(struct cache_entry *ce, struct stat *st, int); extern int ce_path_match(const struct cache_entry *ce, const char **pathspec); extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type); +extern int read_pipe(int fd, char** return_buf, unsigned long* return_size); extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object); extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object); extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); diff --git a/mktag.c b/mktag.c index 23288781cf..f1598dbd21 100644 --- a/mktag.c +++ b/mktag.c @@ -45,7 +45,7 @@ static int verify_tag(char *buffer, unsigned long size) unsigned char sha1[20]; const char *object, *type_line, *tag_line, *tagger_line; - if (size < 64 || size > MAXSIZE-1) + if (size < 64) return -1; buffer[size] = 0; @@ -105,8 +105,8 @@ static int verify_tag(char *buffer, unsigned long size) int main(int argc, char **argv) { - unsigned long size; - char buffer[MAXSIZE]; + unsigned long size = 4096; + char *buffer = malloc(size); unsigned char result_sha1[20]; if (argc != 1) @@ -114,13 +114,9 @@ int main(int argc, char **argv) setup_git_directory(); - // Read the signature - size = 0; - for (;;) { - int ret = xread(0, buffer + size, MAXSIZE - size); - if (ret <= 0) - break; - size += ret; + if (read_pipe(0, &buffer, &size)) { + free(buffer); + die("could not read from stdin"); } // Verify it for some basic sanity: it needs to start with "object \ntype\ntagger " @@ -129,6 +125,9 @@ int main(int argc, char **argv) if (write_sha1_file(buffer, size, tag_type, result_sha1) < 0) die("unable to write tag file"); + + free(buffer); + printf("%s\n", sha1_to_hex(result_sha1)); return 0; } diff --git a/sha1_file.c b/sha1_file.c index 223001033c..e444d9df1b 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1645,16 +1645,24 @@ int has_sha1_file(const unsigned char *sha1) return find_sha1_file(sha1, &st) ? 1 : 0; } -int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) +/* + * reads from fd as long as possible into a supplied buffer of size bytes. + * If neccessary the buffer's size is increased using realloc() + * + * returns 0 if anything went fine and -1 otherwise + * + * NOTE: both buf and size may change, but even when -1 is returned + * you still have to free() it yourself. + */ +int read_pipe(int fd, char** return_buf, unsigned long* return_size) { - unsigned long size = 4096; - char *buf = malloc(size); - int iret, ret; + char* buf = *return_buf; + unsigned long size = *return_size; + int iret; unsigned long off = 0; - unsigned char hdr[50]; - int hdrlen; + do { - iret = read(fd, buf + off, size - off); + iret = xread(fd, buf + off, size - off); if (iret > 0) { off += iret; if (off == size) { @@ -1663,16 +1671,34 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) } } } while (iret > 0); - if (iret < 0) { + + *return_buf = buf; + *return_size = off; + + if (iret < 0) + return -1; + return 0; +} + +int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) +{ + unsigned long size = 4096; + char *buf = malloc(size); + int ret; + unsigned char hdr[50]; + int hdrlen; + + if (read_pipe(fd, &buf, &size)) { free(buf); return -1; } + if (!type) type = blob_type; if (write_object) - ret = write_sha1_file(buf, off, type, sha1); + ret = write_sha1_file(buf, size, type, sha1); else { - write_sha1_file_prepare(buf, off, type, sha1, hdr, &hdrlen); + write_sha1_file_prepare(buf, size, type, sha1, hdr, &hdrlen); ret = 0; } free(buf); From cfba045930423551ec9936fa15cbea7c04a841cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Engelmann?= Date: Tue, 23 May 2006 20:20:09 +0200 Subject: [PATCH 37/40] add more informative error messages to git-mktag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Engelmann Signed-off-by: Junio C Hamano --- mktag.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/mktag.c b/mktag.c index f1598dbd21..f0fe5285b2 100644 --- a/mktag.c +++ b/mktag.c @@ -46,41 +46,45 @@ static int verify_tag(char *buffer, unsigned long size) const char *object, *type_line, *tag_line, *tagger_line; if (size < 64) - return -1; + return error("wanna fool me ? you obviously got the size wrong !\n"); + buffer[size] = 0; /* Verify object line */ object = buffer; if (memcmp(object, "object ", 7)) - return -1; + return error("char%d: does not start with \"object \"\n", 0); + if (get_sha1_hex(object + 7, sha1)) - return -1; + return error("char%d: could not get SHA1 hash\n", 7); /* Verify type line */ type_line = object + 48; if (memcmp(type_line - 1, "\ntype ", 6)) - return -1; + return error("char%d: could not find \"\\ntype \"\n", 47); /* Verify tag-line */ tag_line = strchr(type_line, '\n'); if (!tag_line) - return -1; + return error("char%td: could not find next \"\\n\"\n", type_line - buffer); tag_line++; if (memcmp(tag_line, "tag ", 4) || tag_line[4] == '\n') - return -1; + return error("char%td: no \"tag \" found\n", tag_line - buffer); /* Get the actual type */ typelen = tag_line - type_line - strlen("type \n"); if (typelen >= sizeof(type)) - return -1; + return error("char%td: type too long\n", type_line+5 - buffer); + memcpy(type, type_line+5, typelen); type[typelen] = 0; /* Verify that the object matches */ if (get_sha1_hex(object + 7, sha1)) - return -1; + return error("char%d: could not get SHA1 hash but this is really odd since i got it before !\n", 7); + if (verify_object(sha1, type)) - return -1; + return error("char%d: could not verify object %s\n", 7, sha1); /* Verify the tag-name: we don't allow control characters or spaces in it */ tag_line += 4; @@ -90,14 +94,14 @@ static int verify_tag(char *buffer, unsigned long size) break; if (c > ' ') continue; - return -1; + return error("char%td: could not verify tag name\n", tag_line - buffer); } /* Verify the tagger line */ tagger_line = tag_line; if (memcmp(tagger_line, "tagger", 6) || (tagger_line[6] == '\n')) - return -1; + return error("char%td: could not find \"tagger\"\n", tagger_line - buffer); /* The actual stuff afterwards we don't care about.. */ return 0; From fad70686b25c414b6224406d42a4dfb7c4136e85 Mon Sep 17 00:00:00 2001 From: Sean Date: Tue, 23 May 2006 05:34:23 -0400 Subject: [PATCH 38/40] --summary output should print immediately after stats. Currently the summary is displayed after the patch. Fix this so that the output order is stat-summary-patch. As a consequence of the way this is coded, the --summary option will only actually display summary data if combined with either the --stat or --patch-with-stat option. Signed-off-by: Sean Estabrooks Signed-off-by: Junio C Hamano --- diff.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/diff.c b/diff.c index 3a2a175c7e..77c09a87b0 100644 --- a/diff.c +++ b/diff.c @@ -1988,6 +1988,9 @@ void diff_flush(struct diff_options *options) show_stats(diffstat); free(diffstat); diffstat = NULL; + if (options->summary) + for (i = 0; i < q->nr; i++) + diff_summary(q->queue[i]); putchar(options->line_termination); } for (i = 0; i < q->nr; i++) { @@ -2001,7 +2004,7 @@ void diff_flush(struct diff_options *options) } for (i = 0; i < q->nr; i++) { - if (options->summary) + if (diffstat && options->summary) diff_summary(q->queue[i]); diff_free_filepair(q->queue[i]); } From f81daefe56b3c97b93a851e1ada14eeca0dea47a Mon Sep 17 00:00:00 2001 From: Timo Hirvonen Date: Wed, 24 May 2006 14:08:46 +0300 Subject: [PATCH 39/40] Builtin git-cat-file Signed-off-by: Timo Hirvonen Signed-off-by: Junio C Hamano --- Makefile | 6 +++--- cat-file.c => builtin-cat-file.c | 3 ++- builtin.h | 1 + git.c | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) rename cat-file.c => builtin-cat-file.c (97%) diff --git a/Makefile b/Makefile index 7e6517f62b..dbf19c6277 100644 --- a/Makefile +++ b/Makefile @@ -149,7 +149,6 @@ SIMPLE_PROGRAMS = \ # ... and all the rest that could be moved out of bindir to gitexecdir PROGRAMS = \ - git-cat-file$X \ git-checkout-index$X git-clone-pack$X \ git-convert-objects$X git-fetch-pack$X git-fsck-objects$X \ git-hash-object$X git-index-pack$X git-local-fetch$X \ @@ -174,7 +173,7 @@ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-ls-files$X git-ls-tree$X \ git-read-tree$X git-commit-tree$X \ git-apply$X git-show-branch$X git-diff-files$X \ - git-diff-index$X git-diff-stages$X git-diff-tree$X + git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -228,7 +227,8 @@ BUILTIN_OBJS = \ builtin-ls-files.o builtin-ls-tree.o \ builtin-read-tree.o builtin-commit-tree.o \ builtin-apply.o builtin-show-branch.o builtin-diff-files.o \ - builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o + builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \ + builtin-cat-file.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/cat-file.c b/builtin-cat-file.c similarity index 97% rename from cat-file.c rename to builtin-cat-file.c index 7413feed78..8ab136e981 100644 --- a/cat-file.c +++ b/builtin-cat-file.c @@ -7,6 +7,7 @@ #include "exec_cmd.h" #include "tag.h" #include "tree.h" +#include "builtin.h" static void flush_buffer(const char *buf, unsigned long size) { @@ -93,7 +94,7 @@ static int pprint_tag(const unsigned char *sha1, const char *buf, unsigned long return 0; } -int main(int argc, char **argv) +int cmd_cat_file(int argc, const char **argv, char **envp) { unsigned char sha1[20]; char type[20]; diff --git a/builtin.h b/builtin.h index 714b97578c..738ec3d945 100644 --- a/builtin.h +++ b/builtin.h @@ -42,5 +42,6 @@ extern int cmd_diff_files(int argc, const char **argv, char **envp); extern int cmd_diff_index(int argc, const char **argv, char **envp); extern int cmd_diff_stages(int argc, const char **argv, char **envp); extern int cmd_diff_tree(int argc, const char **argv, char **envp); +extern int cmd_cat_file(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index 5a884bb07a..10ea934bcf 100644 --- a/git.c +++ b/git.c @@ -68,7 +68,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "diff-files", cmd_diff_files }, { "diff-index", cmd_diff_index }, { "diff-stages", cmd_diff_stages }, - { "diff-tree", cmd_diff_tree } + { "diff-tree", cmd_diff_tree }, + { "cat-file", cmd_cat_file } }; int i; From 4d548150ace0816dd5fe678cdbde75b13d5e5249 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 24 May 2006 08:30:54 -0700 Subject: [PATCH 40/40] Clean up sha1 file writing This cleans up and future-proofs the sha1 file writing in sha1_file.c. In particular, instead of doing a simple "write()" call and just verifying that it succeeds (or - as in one place - just assuming it does), it uses "write_buffer()" to write data to the file descriptor while correctly checking for partial writes, EINTR etc. It also splits up write_sha1_to_fd() to be a lot more readable: if we need to re-create the compressed object, we do so in a separate helper function, making the logic a whole lot more modular and obvious. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- sha1_file.c | 145 +++++++++++++++++++++++++++++----------------------- 1 file changed, 81 insertions(+), 64 deletions(-) diff --git a/sha1_file.c b/sha1_file.c index e444d9df1b..f77c18934a 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1399,6 +1399,25 @@ int move_temp_to_file(const char *tmpfile, char *filename) return 0; } +static int write_buffer(int fd, const void *buf, size_t len) +{ + while (len) { + ssize_t size; + + size = write(fd, buf, len); + if (!size) + return error("file write: disk full"); + if (size < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return error("file write error (%s)", strerror(errno)); + } + len -= size; + buf += size; + } + return 0; +} + int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1) { int size; @@ -1465,8 +1484,8 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha deflateEnd(&stream); size = stream.total_out; - if (write(fd, compressed, size) != size) - die("unable to write file"); + if (write_buffer(fd, compressed, size) < 0) + die("unable to write sha1 file"); fchmod(fd, 0444); close(fd); free(compressed); @@ -1474,73 +1493,70 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha return move_temp_to_file(tmpfile, filename); } +/* + * We need to unpack and recompress the object for writing + * it out to a different file. + */ +static void *repack_object(const unsigned char *sha1, unsigned long *objsize) +{ + size_t size; + z_stream stream; + unsigned char *unpacked; + unsigned long len; + char type[20]; + char hdr[50]; + int hdrlen; + void *buf; + + // need to unpack and recompress it by itself + unpacked = read_packed_sha1(sha1, type, &len); + + hdrlen = sprintf(hdr, "%s %lu", type, len) + 1; + + /* Set it up */ + memset(&stream, 0, sizeof(stream)); + deflateInit(&stream, Z_BEST_COMPRESSION); + size = deflateBound(&stream, len + hdrlen); + buf = xmalloc(size); + + /* Compress it */ + stream.next_out = buf; + stream.avail_out = size; + + /* First header.. */ + stream.next_in = (void *)hdr; + stream.avail_in = hdrlen; + while (deflate(&stream, 0) == Z_OK) + /* nothing */; + + /* Then the data itself.. */ + stream.next_in = unpacked; + stream.avail_in = len; + while (deflate(&stream, Z_FINISH) == Z_OK) + /* nothing */; + deflateEnd(&stream); + free(unpacked); + + *objsize = stream.total_out; + return buf; +} + int write_sha1_to_fd(int fd, const unsigned char *sha1) { - ssize_t size; + int retval; unsigned long objsize; - int posn = 0; - void *map = map_sha1_file_internal(sha1, &objsize); - void *buf = map; - void *temp_obj = NULL; - z_stream stream; + void *buf = map_sha1_file_internal(sha1, &objsize); - if (!buf) { - unsigned char *unpacked; - unsigned long len; - char type[20]; - char hdr[50]; - int hdrlen; - // need to unpack and recompress it by itself - unpacked = read_packed_sha1(sha1, type, &len); - - hdrlen = sprintf(hdr, "%s %lu", type, len) + 1; - - /* Set it up */ - memset(&stream, 0, sizeof(stream)); - deflateInit(&stream, Z_BEST_COMPRESSION); - size = deflateBound(&stream, len + hdrlen); - temp_obj = buf = xmalloc(size); - - /* Compress it */ - stream.next_out = buf; - stream.avail_out = size; - - /* First header.. */ - stream.next_in = (void *)hdr; - stream.avail_in = hdrlen; - while (deflate(&stream, 0) == Z_OK) - /* nothing */; - - /* Then the data itself.. */ - stream.next_in = unpacked; - stream.avail_in = len; - while (deflate(&stream, Z_FINISH) == Z_OK) - /* nothing */; - deflateEnd(&stream); - free(unpacked); - - objsize = stream.total_out; + if (buf) { + retval = write_buffer(fd, buf, objsize); + munmap(buf, objsize); + return retval; } - do { - size = write(fd, buf + posn, objsize - posn); - if (size <= 0) { - if (!size) { - fprintf(stderr, "write closed\n"); - } else { - perror("write "); - } - return -1; - } - posn += size; - } while (posn < objsize); - - if (map) - munmap(map, objsize); - if (temp_obj) - free(temp_obj); - - return 0; + buf = repack_object(sha1, &objsize); + retval = write_buffer(fd, buf, objsize); + free(buf); + return retval; } int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, @@ -1579,7 +1595,8 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, SHA1_Update(&c, discard, sizeof(discard) - stream.avail_out); } while (stream.avail_in && ret == Z_OK); - write(local, buffer, *bufposn - stream.avail_in); + if (write_buffer(local, buffer, *bufposn - stream.avail_in) < 0) + die("unable to write sha1 file"); memmove(buffer, buffer + *bufposn - stream.avail_in, stream.avail_in); *bufposn = stream.avail_in;