From 46d164b0cd1d5d254047d7573c53e368e42bf5e5 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 22 Mar 2009 19:14:01 -0700 Subject: [PATCH 01/55] pretty.c: add %f format specifier to format_commit_message() This specifier represents the sanitized and filename friendly subject line of a commit. No checks are made against the length of the string, so users may need to trim the result to the desired length if using as a filename. This is commonly used by format-patch to massage commit subjects into filenames and output patches to files. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- Documentation/pretty-formats.txt | 1 + pretty.c | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index 5c6e678aa3..2a845b1e57 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -121,6 +121,7 @@ The placeholders are: - '%d': ref names, like the --decorate option of linkgit:git-log[1] - '%e': encoding - '%s': subject +- '%f': sanitized subject line, suitable for a filename - '%b': body - '%Cred': switch color to red - '%Cgreen': switch color to green diff --git a/pretty.c b/pretty.c index efa70245f1..c57cef47c9 100644 --- a/pretty.c +++ b/pretty.c @@ -493,6 +493,38 @@ static void parse_commit_header(struct format_commit_context *context) context->commit_header_parsed = 1; } +static int istitlechar(char c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || c == '.' || c == '_'; +} + +static void format_sanitized_subject(struct strbuf *sb, const char *msg) +{ + size_t trimlen; + int space = 2; + + for (; *msg && *msg != '\n'; msg++) { + if (istitlechar(*msg)) { + if (space == 1) + strbuf_addch(sb, '-'); + space = 0; + strbuf_addch(sb, *msg); + if (*msg == '.') + while (*(msg+1) == '.') + msg++; + } else + space |= 1; + } + + /* trim any trailing '.' or '-' characters */ + trimlen = 0; + while (sb->buf[sb->len - 1 - trimlen] == '.' + || sb->buf[sb->len - 1 - trimlen] == '-') + trimlen++; + strbuf_remove(sb, sb->len - trimlen, trimlen); +} + const char *format_subject(struct strbuf *sb, const char *msg, const char *line_separator) { @@ -683,6 +715,9 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder, case 's': /* subject */ format_subject(sb, msg + c->subject_off, " "); return 1; + case 'f': /* sanitized subject */ + format_sanitized_subject(sb, msg + c->subject_off); + return 1; case 'b': /* body */ strbuf_addstr(sb, msg + c->body_off); return 1; From 6df514af9dfb44f104baa9b581e91de22af89b8d Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 22 Mar 2009 19:14:02 -0700 Subject: [PATCH 02/55] format-patch: construct patch filename in one function reopen_stdout() usually takes the oneline subject of a commit, appends the patch suffix, prepends the output directory (if any) and then reopens stdout as the resulting file. Now the patch filename (the oneline subject and the patch suffix) is created in get_patch_filename() and passed to reopen_stdout() which prepends the output directory and reopens stdout as that file. The original function to get the oneline description, get_oneline_for_filename(), has been renamed to get_patch_filename() to reflect its new functionality. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- builtin-log.c | 98 +++++++++++++++++---------------------------------- 1 file changed, 33 insertions(+), 65 deletions(-) diff --git a/builtin-log.c b/builtin-log.c index c7a5772594..193de3ff2e 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -419,12 +419,6 @@ int cmd_log(int argc, const char **argv, const char *prefix) /* format-patch */ #define FORMAT_PATCH_NAME_MAX 64 -static int istitlechar(char c) -{ - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || c == '.' || c == '_'; -} - static const char *fmt_patch_suffix = ".patch"; static int numbered = 0; static int auto_number = 1; @@ -519,61 +513,33 @@ static int git_format_config(const char *var, const char *value, void *cb) } -static const char *get_oneline_for_filename(struct commit *commit, - int keep_subject) +static void get_patch_filename(struct commit *commit, int nr, + const char *suffix, struct strbuf *buf) { - static char filename[PATH_MAX]; - char *sol; - int len = 0; - int suffix_len = strlen(fmt_patch_suffix) + 1; + int suffix_len = strlen(suffix) + 1; + int start_len = buf->len; - sol = strstr(commit->buffer, "\n\n"); - if (!sol) - filename[0] = '\0'; - else { - int j, space = 0; - - sol += 2; - /* strip [PATCH] or [PATCH blabla] */ - if (!keep_subject && !prefixcmp(sol, "[PATCH")) { - char *eos = strchr(sol + 6, ']'); - if (eos) { - while (isspace(*eos)) - eos++; - sol = eos; - } - } - - for (j = 0; - j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 && - len < sizeof(filename) - suffix_len && - sol[j] && sol[j] != '\n'; - j++) { - if (istitlechar(sol[j])) { - if (space) { - filename[len++] = '-'; - 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--; - filename[len] = '\0'; + strbuf_addf(buf, commit ? "%04d-" : "%d", nr); + if (commit) { + format_commit_message(commit, "%f", buf, DATE_NORMAL); + /* + * Replace characters at the end with the suffix if the + * filename is too long + */ + if (buf->len + suffix_len > FORMAT_PATCH_NAME_MAX + start_len) + strbuf_splice(buf, + start_len + FORMAT_PATCH_NAME_MAX - suffix_len, + suffix_len, suffix, suffix_len); + else + strbuf_addstr(buf, suffix); } - return filename; } static FILE *realstdout = NULL; static const char *output_directory = NULL; static int outdir_offset; -static int reopen_stdout(const char *oneline, int nr, struct rev_info *rev) +static int reopen_stdout(const char *oneline, struct rev_info *rev) { char filename[PATH_MAX]; int len = 0; @@ -589,14 +555,7 @@ static int reopen_stdout(const char *oneline, int nr, struct rev_info *rev) filename[len++] = '/'; } - if (!oneline) - len += sprintf(filename + len, "%d", nr); - else { - len += sprintf(filename + len, "%04d-", nr); - len += snprintf(filename + len, sizeof(filename) - len - 1 - - suffix_len, "%s", oneline); - strcpy(filename + len, fmt_patch_suffix); - } + strncpy(filename + len, oneline, PATH_MAX - len); if (!DIFF_OPT_TST(&rev->diffopt, QUIET)) fprintf(realstdout, "%s\n", filename + outdir_offset); @@ -684,12 +643,17 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, const char *encoding = "utf-8"; struct diff_options opts; int need_8bit_cte = 0; + char filename[PATH_MAX]; if (rev->commit_format != CMIT_FMT_EMAIL) die("Cover letter needs email format"); - if (!use_stdout && reopen_stdout(numbered_files ? - NULL : "cover-letter", 0, rev)) + if (numbered_files) + sprintf(filename, "0"); + else + sprintf(filename, "%04d-cover-letter%s", 0, fmt_patch_suffix); + + if (!use_stdout && reopen_stdout(filename, rev)) return; head_sha1 = sha1_to_hex(head->object.sha1); @@ -802,6 +766,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) struct patch_ids ids; char *add_signoff = NULL; struct strbuf buf = STRBUF_INIT; + struct strbuf patch_filename = STRBUF_INIT; git_config(git_format_config, NULL); init_revisions(&rev, prefix); @@ -1104,10 +1069,12 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) } gen_message_id(&rev, sha1_to_hex(commit->object.sha1)); } - if (!use_stdout && reopen_stdout(numbered_files ? NULL : - get_oneline_for_filename(commit, keep_subject), - rev.nr, &rev)) + + get_patch_filename(numbered_files ? NULL : commit, rev.nr, + fmt_patch_suffix, &patch_filename); + if (!use_stdout && reopen_stdout(patch_filename.buf, &rev)) die("Failed to create output files"); + strbuf_setlen(&patch_filename, 0); shown = log_tree_commit(&rev, commit); free(commit->buffer); commit->buffer = NULL; @@ -1131,6 +1098,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (!use_stdout) fclose(stdout); } + strbuf_release(&patch_filename); free(list); if (ignore_if_in_upstream) free_patch_ids(&ids); From cd2ef591c8e753fe5295ac3c6f1dee481f00a185 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 22 Mar 2009 19:14:03 -0700 Subject: [PATCH 03/55] format-patch: pass a commit to reopen_stdout() We use the commit to generate the patch filename in reopen_stdout() before we redirect stdout. The cover letter codepath creates a dummy commit with the desired subject line 'cover letter'. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- builtin-log.c | 67 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/builtin-log.c b/builtin-log.c index 193de3ff2e..6a27ce6958 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -539,30 +539,29 @@ static FILE *realstdout = NULL; static const char *output_directory = NULL; static int outdir_offset; -static int reopen_stdout(const char *oneline, struct rev_info *rev) +static int reopen_stdout(struct commit *commit, struct rev_info *rev) { - char filename[PATH_MAX]; - int len = 0; + struct strbuf filename = STRBUF_INIT; int suffix_len = strlen(fmt_patch_suffix) + 1; if (output_directory) { - len = snprintf(filename, sizeof(filename), "%s", - output_directory); - if (len >= - sizeof(filename) - FORMAT_PATCH_NAME_MAX - suffix_len) + strbuf_addstr(&filename, output_directory); + if (filename.len >= + PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len) return error("name of output directory is too long"); - if (filename[len - 1] != '/') - filename[len++] = '/'; + if (filename.buf[filename.len - 1] != '/') + strbuf_addch(&filename, '/'); } - strncpy(filename + len, oneline, PATH_MAX - len); + get_patch_filename(commit, rev->nr, fmt_patch_suffix, &filename); if (!DIFF_OPT_TST(&rev->diffopt, QUIET)) - fprintf(realstdout, "%s\n", filename + outdir_offset); + fprintf(realstdout, "%s\n", filename.buf + outdir_offset); - if (freopen(filename, "w", stdout) == NULL) - return error("Cannot open patch file %s",filename); + if (freopen(filename.buf, "w", stdout) == NULL) + return error("Cannot open patch file %s", filename.buf); + strbuf_release(&filename); return 0; } @@ -643,26 +642,42 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, const char *encoding = "utf-8"; struct diff_options opts; int need_8bit_cte = 0; - char filename[PATH_MAX]; + struct commit *commit = NULL; if (rev->commit_format != CMIT_FMT_EMAIL) die("Cover letter needs email format"); - if (numbered_files) - sprintf(filename, "0"); - else - sprintf(filename, "%04d-cover-letter%s", 0, fmt_patch_suffix); + committer = git_committer_info(0); + head_sha1 = sha1_to_hex(head->object.sha1); - if (!use_stdout && reopen_stdout(filename, rev)) + if (!numbered_files) { + /* + * We fake a commit for the cover letter so we get the filename + * desired. + */ + commit = xcalloc(1, sizeof(*commit)); + commit->buffer = xmalloc(400); + snprintf(commit->buffer, 400, + "tree 0000000000000000000000000000000000000000\n" + "parent %s\n" + "author %s\n" + "committer %s\n\n" + "cover letter\n", + head_sha1, committer, committer); + } + + if (!use_stdout && reopen_stdout(commit, rev)) return; - head_sha1 = sha1_to_hex(head->object.sha1); + if (commit) { + + free(commit->buffer); + free(commit); + } log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers, &need_8bit_cte); - committer = git_committer_info(0); - msg = body; pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822, encoding); @@ -766,7 +781,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) struct patch_ids ids; char *add_signoff = NULL; struct strbuf buf = STRBUF_INIT; - struct strbuf patch_filename = STRBUF_INIT; git_config(git_format_config, NULL); init_revisions(&rev, prefix); @@ -1070,11 +1084,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) gen_message_id(&rev, sha1_to_hex(commit->object.sha1)); } - get_patch_filename(numbered_files ? NULL : commit, rev.nr, - fmt_patch_suffix, &patch_filename); - if (!use_stdout && reopen_stdout(patch_filename.buf, &rev)) + if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit, + &rev)) die("Failed to create output files"); - strbuf_setlen(&patch_filename, 0); shown = log_tree_commit(&rev, commit); free(commit->buffer); commit->buffer = NULL; @@ -1098,7 +1110,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (!use_stdout) fclose(stdout); } - strbuf_release(&patch_filename); free(list); if (ignore_if_in_upstream) free_patch_ids(&ids); From 6fa8e6278b210bfa56fcb54ed38d2b485350e7c6 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 22 Mar 2009 19:14:04 -0700 Subject: [PATCH 04/55] format-patch: move get_patch_filename() into log-tree Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- builtin-log.c | 24 ------------------------ log-tree.c | 22 ++++++++++++++++++++++ log-tree.h | 4 ++++ 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/builtin-log.c b/builtin-log.c index 6a27ce6958..4f438db4c1 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -417,7 +417,6 @@ int cmd_log(int argc, const char **argv, const char *prefix) } /* format-patch */ -#define FORMAT_PATCH_NAME_MAX 64 static const char *fmt_patch_suffix = ".patch"; static int numbered = 0; @@ -512,29 +511,6 @@ static int git_format_config(const char *var, const char *value, void *cb) return git_log_config(var, value, cb); } - -static void get_patch_filename(struct commit *commit, int nr, - const char *suffix, struct strbuf *buf) -{ - int suffix_len = strlen(suffix) + 1; - int start_len = buf->len; - - strbuf_addf(buf, commit ? "%04d-" : "%d", nr); - if (commit) { - format_commit_message(commit, "%f", buf, DATE_NORMAL); - /* - * Replace characters at the end with the suffix if the - * filename is too long - */ - if (buf->len + suffix_len > FORMAT_PATCH_NAME_MAX + start_len) - strbuf_splice(buf, - start_len + FORMAT_PATCH_NAME_MAX - suffix_len, - suffix_len, suffix, suffix_len); - else - strbuf_addstr(buf, suffix); - } -} - static FILE *realstdout = NULL; static const char *output_directory = NULL; static int outdir_offset; diff --git a/log-tree.c b/log-tree.c index 9565c184db..aee9995531 100644 --- a/log-tree.c +++ b/log-tree.c @@ -179,6 +179,28 @@ static int has_non_ascii(const char *s) return 0; } +void get_patch_filename(struct commit *commit, int nr, const char *suffix, + struct strbuf *buf) +{ + int suffix_len = strlen(suffix) + 1; + int start_len = buf->len; + + strbuf_addf(buf, commit ? "%04d-" : "%d", nr); + if (commit) { + format_commit_message(commit, "%f", buf, DATE_NORMAL); + /* + * Replace characters at the end with the suffix if the + * filename is too long + */ + if (buf->len + suffix_len > FORMAT_PATCH_NAME_MAX + start_len) + strbuf_splice(buf, + start_len + FORMAT_PATCH_NAME_MAX - suffix_len, + suffix_len, suffix, suffix_len); + else + strbuf_addstr(buf, suffix); + } +} + void log_write_email_headers(struct rev_info *opt, const char *name, const char **subject_p, const char **extra_headers_p, diff --git a/log-tree.h b/log-tree.h index f2a90084ae..78dc5be76f 100644 --- a/log-tree.h +++ b/log-tree.h @@ -19,4 +19,8 @@ void log_write_email_headers(struct rev_info *opt, const char *name, int *need_8bit_cte_p); void load_ref_decorations(void); +#define FORMAT_PATCH_NAME_MAX 64 +void get_patch_filename(struct commit *commit, int nr, const char *suffix, + struct strbuf *buf); + #endif From 108dab2811701c20d6d6e8d9fe8af88e41d65d77 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 22 Mar 2009 19:14:05 -0700 Subject: [PATCH 05/55] format-patch: --attach/inline uses filename instead of SHA1 Currently when format-patch is used with --attach or --inline the patch attachment has the SHA1 of the commit for its filename. This replaces the SHA1 with the filename used by format-patch when outputting to files. Fix tests relying on the SHA1 output and add a test showing how the --suffix option affects the attachment filename output. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- builtin-log.c | 8 +-- log-tree.c | 18 +++--- log-tree.h | 2 +- revision.h | 2 + t/t4013-diff-various.sh | 1 + ...tach_--stdout_--suffix=.diff_initial..side | 61 +++++++++++++++++++ ...at-patch_--attach_--stdout_initial..master | 12 ++-- ...t-patch_--attach_--stdout_initial..master^ | 8 +-- ...rmat-patch_--attach_--stdout_initial..side | 4 +- ..._--subject-prefix=TESTCASE_initial..master | 12 ++-- ...at-patch_--inline_--stdout_initial..master | 12 ++-- ...t-patch_--inline_--stdout_initial..master^ | 8 +-- ...-patch_--inline_--stdout_initial..master^^ | 4 +- ...rmat-patch_--inline_--stdout_initial..side | 4 +- 14 files changed, 112 insertions(+), 44 deletions(-) create mode 100644 t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side diff --git a/builtin-log.c b/builtin-log.c index 4f438db4c1..3e3cbc11fd 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -607,7 +607,6 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, int nr, struct commit **list, struct commit *head) { const char *committer; - char *head_sha1; const char *subject_start = NULL; const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n"; const char *msg; @@ -624,7 +623,6 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, die("Cover letter needs email format"); committer = git_committer_info(0); - head_sha1 = sha1_to_hex(head->object.sha1); if (!numbered_files) { /* @@ -639,7 +637,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, "author %s\n" "committer %s\n\n" "cover letter\n", - head_sha1, committer, committer); + sha1_to_hex(head->object.sha1), committer, committer); } if (!use_stdout && reopen_stdout(commit, rev)) @@ -651,7 +649,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, free(commit); } - log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers, + log_write_email_headers(rev, head, &subject_start, &extra_headers, &need_8bit_cte); msg = body; @@ -1011,6 +1009,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) const char *msgid = clean_message_id(in_reply_to); string_list_append(msgid, rev.ref_message_ids); } + rev.numbered_files = numbered_files; + rev.patch_suffix = fmt_patch_suffix; if (cover_letter) { if (thread) gen_message_id(&rev, "cover"); diff --git a/log-tree.c b/log-tree.c index aee9995531..56a3488592 100644 --- a/log-tree.c +++ b/log-tree.c @@ -201,13 +201,14 @@ void get_patch_filename(struct commit *commit, int nr, const char *suffix, } } -void log_write_email_headers(struct rev_info *opt, const char *name, +void log_write_email_headers(struct rev_info *opt, struct commit *commit, const char **subject_p, const char **extra_headers_p, int *need_8bit_cte_p) { const char *subject = NULL; const char *extra_headers = opt->extra_headers; + const char *name = sha1_to_hex(commit->object.sha1); *need_8bit_cte_p = 0; /* unknown */ if (opt->total > 0) { @@ -246,6 +247,7 @@ void log_write_email_headers(struct rev_info *opt, const char *name, if (opt->mime_boundary) { static char subject_buffer[1024]; static char buffer[1024]; + struct strbuf filename = STRBUF_INIT; *need_8bit_cte_p = -1; /* NEVER */ snprintf(subject_buffer, sizeof(subject_buffer) - 1, "%s" @@ -264,18 +266,21 @@ void log_write_email_headers(struct rev_info *opt, const char *name, mime_boundary_leader, opt->mime_boundary); extra_headers = subject_buffer; + get_patch_filename(opt->numbered_files ? NULL : commit, opt->nr, + opt->patch_suffix, &filename); snprintf(buffer, sizeof(buffer) - 1, "\n--%s%s\n" "Content-Type: text/x-patch;" - " name=\"%s.diff\"\n" + " name=\"%s\"\n" "Content-Transfer-Encoding: 8bit\n" "Content-Disposition: %s;" - " filename=\"%s.diff\"\n\n", + " filename=\"%s\"\n\n", mime_boundary_leader, opt->mime_boundary, - name, + filename.buf, opt->no_inline ? "attachment" : "inline", - name); + filename.buf); opt->diffopt.stat_sep = buffer; + strbuf_release(&filename); } *subject_p = subject; *extra_headers_p = extra_headers; @@ -355,8 +360,7 @@ void show_log(struct rev_info *opt) */ if (opt->commit_format == CMIT_FMT_EMAIL) { - log_write_email_headers(opt, sha1_to_hex(commit->object.sha1), - &subject, &extra_headers, + log_write_email_headers(opt, commit, &subject, &extra_headers, &need_8bit_cte); } else if (opt->commit_format != CMIT_FMT_USERFORMAT) { fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout); diff --git a/log-tree.h b/log-tree.h index 78dc5be76f..20b5caf1aa 100644 --- a/log-tree.h +++ b/log-tree.h @@ -13,7 +13,7 @@ int log_tree_commit(struct rev_info *, struct commit *); int log_tree_opt_parse(struct rev_info *, const char **, int); void show_log(struct rev_info *opt); void show_decorations(struct rev_info *opt, struct commit *commit); -void log_write_email_headers(struct rev_info *opt, const char *name, +void log_write_email_headers(struct rev_info *opt, struct commit *commit, const char **subject_p, const char **extra_headers_p, int *need_8bit_cte_p); diff --git a/revision.h b/revision.h index ad123d78c5..5259ed5192 100644 --- a/revision.h +++ b/revision.h @@ -86,6 +86,8 @@ struct rev_info { struct log_info *loginfo; int nr, total; const char *mime_boundary; + const char *patch_suffix; + int numbered_files; char *message_id; struct string_list *ref_message_ids; const char *add_signoff; diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index 426e64e828..6592a4f29d 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -246,6 +246,7 @@ format-patch --stdout initial..master format-patch --stdout --no-numbered initial..master format-patch --stdout --numbered initial..master format-patch --attach --stdout initial..side +format-patch --attach --stdout --suffix=.diff initial..side format-patch --attach --stdout initial..master^ format-patch --attach --stdout initial..master format-patch --inline --stdout initial..side diff --git a/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side b/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side new file mode 100644 index 0000000000..52116d3ead --- /dev/null +++ b/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side @@ -0,0 +1,61 @@ +$ git format-patch --attach --stdout --suffix=.diff initial..side +From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001 +From: A U Thor +Date: Mon, 26 Jun 2006 00:03:00 +0000 +Subject: [PATCH] Side +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n" + +This is a multi-part message in MIME format. +--------------g-i-t--v-e-r-s-i-o-n +Content-Type: text/plain; charset=UTF-8; format=fixed +Content-Transfer-Encoding: 8bit + +--- + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ + 3 files changed, 9 insertions(+), 0 deletions(-) + create mode 100644 file3 + + +--------------g-i-t--v-e-r-s-i-o-n +Content-Type: text/x-patch; name="0001-Side.diff" +Content-Transfer-Encoding: 8bit +Content-Disposition: attachment; filename="0001-Side.diff" + +diff --git a/dir/sub b/dir/sub +index 35d242b..7289e35 100644 +--- a/dir/sub ++++ b/dir/sub +@@ -1,2 +1,4 @@ + A + B ++1 ++2 +diff --git a/file0 b/file0 +index 01e79c3..f4615da 100644 +--- a/file0 ++++ b/file0 +@@ -1,3 +1,6 @@ + 1 + 2 + 3 ++A ++B ++C +diff --git a/file3 b/file3 +new file mode 100644 +index 0000000..7289e35 +--- /dev/null ++++ b/file3 +@@ -0,0 +1,4 @@ ++A ++B ++1 ++2 + +--------------g-i-t--v-e-r-s-i-o-n-- + + +$ diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master b/t/t4013/diff.format-patch_--attach_--stdout_initial..master index e5ab74437e..ce49bd676e 100644 --- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master +++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master @@ -22,9 +22,9 @@ This is the second commit. --------------g-i-t--v-e-r-s-i-o-n -Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" +Content-Type: text/x-patch; name="0001-Second.patch" Content-Transfer-Encoding: 8bit -Content-Disposition: attachment; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" +Content-Disposition: attachment; filename="0001-Second.patch" diff --git a/dir/sub b/dir/sub index 35d242b..8422d40 100644 @@ -80,9 +80,9 @@ Content-Transfer-Encoding: 8bit --------------g-i-t--v-e-r-s-i-o-n -Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff" +Content-Type: text/x-patch; name="0002-Third.patch" Content-Transfer-Encoding: 8bit -Content-Disposition: attachment; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff" +Content-Disposition: attachment; filename="0002-Third.patch" diff --git a/dir/sub b/dir/sub index 8422d40..cead32e 100644 @@ -129,9 +129,9 @@ Content-Transfer-Encoding: 8bit --------------g-i-t--v-e-r-s-i-o-n -Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff" +Content-Type: text/x-patch; name="0003-Side.patch" Content-Transfer-Encoding: 8bit -Content-Disposition: attachment; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff" +Content-Disposition: attachment; filename="0003-Side.patch" diff --git a/dir/sub b/dir/sub index 35d242b..7289e35 100644 diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ index 2c71d20d37..5f1b23863b 100644 --- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ +++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ @@ -22,9 +22,9 @@ This is the second commit. --------------g-i-t--v-e-r-s-i-o-n -Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" +Content-Type: text/x-patch; name="0001-Second.patch" Content-Transfer-Encoding: 8bit -Content-Disposition: attachment; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" +Content-Disposition: attachment; filename="0001-Second.patch" diff --git a/dir/sub b/dir/sub index 35d242b..8422d40 100644 @@ -80,9 +80,9 @@ Content-Transfer-Encoding: 8bit --------------g-i-t--v-e-r-s-i-o-n -Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff" +Content-Type: text/x-patch; name="0002-Third.patch" Content-Transfer-Encoding: 8bit -Content-Disposition: attachment; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff" +Content-Disposition: attachment; filename="0002-Third.patch" diff --git a/dir/sub b/dir/sub index 8422d40..cead32e 100644 diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..side b/t/t4013/diff.format-patch_--attach_--stdout_initial..side index 38f790290a..4a2364abc2 100644 --- a/t/t4013/diff.format-patch_--attach_--stdout_initial..side +++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..side @@ -20,9 +20,9 @@ Content-Transfer-Encoding: 8bit --------------g-i-t--v-e-r-s-i-o-n -Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff" +Content-Type: text/x-patch; name="0001-Side.patch" Content-Transfer-Encoding: 8bit -Content-Disposition: attachment; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff" +Content-Disposition: attachment; filename="0001-Side.patch" diff --git a/dir/sub b/dir/sub index 35d242b..7289e35 100644 diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master index 58f8a7b7d6..ca3f60bf0e 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master +++ b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master @@ -22,9 +22,9 @@ This is the second commit. --------------g-i-t--v-e-r-s-i-o-n -Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" +Content-Type: text/x-patch; name="0001-Second.patch" Content-Transfer-Encoding: 8bit -Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" +Content-Disposition: inline; filename="0001-Second.patch" diff --git a/dir/sub b/dir/sub index 35d242b..8422d40 100644 @@ -80,9 +80,9 @@ Content-Transfer-Encoding: 8bit --------------g-i-t--v-e-r-s-i-o-n -Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff" +Content-Type: text/x-patch; name="0002-Third.patch" Content-Transfer-Encoding: 8bit -Content-Disposition: inline; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff" +Content-Disposition: inline; filename="0002-Third.patch" diff --git a/dir/sub b/dir/sub index 8422d40..cead32e 100644 @@ -129,9 +129,9 @@ Content-Transfer-Encoding: 8bit --------------g-i-t--v-e-r-s-i-o-n -Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff" +Content-Type: text/x-patch; name="0003-Side.patch" Content-Transfer-Encoding: 8bit -Content-Disposition: inline; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff" +Content-Disposition: inline; filename="0003-Side.patch" diff --git a/dir/sub b/dir/sub index 35d242b..7289e35 100644 diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_initial..master index 9e7bbdffa2..08f23014bc 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master +++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master @@ -22,9 +22,9 @@ This is the second commit. --------------g-i-t--v-e-r-s-i-o-n -Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" +Content-Type: text/x-patch; name="0001-Second.patch" Content-Transfer-Encoding: 8bit -Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" +Content-Disposition: inline; filename="0001-Second.patch" diff --git a/dir/sub b/dir/sub index 35d242b..8422d40 100644 @@ -80,9 +80,9 @@ Content-Transfer-Encoding: 8bit --------------g-i-t--v-e-r-s-i-o-n -Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff" +Content-Type: text/x-patch; name="0002-Third.patch" Content-Transfer-Encoding: 8bit -Content-Disposition: inline; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff" +Content-Disposition: inline; filename="0002-Third.patch" diff --git a/dir/sub b/dir/sub index 8422d40..cead32e 100644 @@ -129,9 +129,9 @@ Content-Transfer-Encoding: 8bit --------------g-i-t--v-e-r-s-i-o-n -Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff" +Content-Type: text/x-patch; name="0003-Side.patch" Content-Transfer-Encoding: 8bit -Content-Disposition: inline; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff" +Content-Disposition: inline; filename="0003-Side.patch" diff --git a/dir/sub b/dir/sub index 35d242b..7289e35 100644 diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ index f881f644cc..07f1230d31 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ +++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ @@ -22,9 +22,9 @@ This is the second commit. --------------g-i-t--v-e-r-s-i-o-n -Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" +Content-Type: text/x-patch; name="0001-Second.patch" Content-Transfer-Encoding: 8bit -Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" +Content-Disposition: inline; filename="0001-Second.patch" diff --git a/dir/sub b/dir/sub index 35d242b..8422d40 100644 @@ -80,9 +80,9 @@ Content-Transfer-Encoding: 8bit --------------g-i-t--v-e-r-s-i-o-n -Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff" +Content-Type: text/x-patch; name="0002-Third.patch" Content-Transfer-Encoding: 8bit -Content-Disposition: inline; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff" +Content-Disposition: inline; filename="0002-Third.patch" diff --git a/dir/sub b/dir/sub index 8422d40..cead32e 100644 diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ index 4f258b8858..29e00ab8af 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ +++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ @@ -22,9 +22,9 @@ This is the second commit. --------------g-i-t--v-e-r-s-i-o-n -Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" +Content-Type: text/x-patch; name="0001-Second.patch" Content-Transfer-Encoding: 8bit -Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" +Content-Disposition: inline; filename="0001-Second.patch" diff --git a/dir/sub b/dir/sub index 35d242b..8422d40 100644 diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..side b/t/t4013/diff.format-patch_--inline_--stdout_initial..side index e86dce69a3..67633d424a 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_initial..side +++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..side @@ -20,9 +20,9 @@ Content-Transfer-Encoding: 8bit --------------g-i-t--v-e-r-s-i-o-n -Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff" +Content-Type: text/x-patch; name="0001-Side.patch" Content-Transfer-Encoding: 8bit -Content-Disposition: inline; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff" +Content-Disposition: inline; filename="0001-Side.patch" diff --git a/dir/sub b/dir/sub index 35d242b..7289e35 100644 From 747e25050bfef8f3a7c882954b654cf6d97fc63e Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 22 Mar 2009 19:14:06 -0700 Subject: [PATCH 06/55] format-patch: --numbered-files and --stdout aren't mutually exclusive For example: git format-patch --numbered-files --stdout --attach HEAD~~ will create two messages with files 1 and 2 attached respectively. There is no effect when using --numbered-files and --stdout together without an --attach or --inline, the --numbered-files option will be ignored. Add a test to show this. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- t/t4013-diff-various.sh | 2 +- ..._--stdout_--numbered-files_initial..master | 170 ++++++++++++++++++ 2 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index 6592a4f29d..8b33321f8c 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -251,7 +251,7 @@ format-patch --attach --stdout initial..master^ format-patch --attach --stdout initial..master format-patch --inline --stdout initial..side format-patch --inline --stdout initial..master^ -format-patch --inline --stdout initial..master +format-patch --inline --stdout --numbered-files initial..master format-patch --inline --stdout initial..master format-patch --inline --stdout --subject-prefix=TESTCASE initial..master config format.subjectprefix DIFFERENT_PREFIX diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master new file mode 100644 index 0000000000..43b81eba54 --- /dev/null +++ b/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master @@ -0,0 +1,170 @@ +$ git format-patch --inline --stdout --numbered-files initial..master +From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001 +From: A U Thor +Date: Mon, 26 Jun 2006 00:01:00 +0000 +Subject: [PATCH 1/3] Second +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n" + +This is a multi-part message in MIME format. +--------------g-i-t--v-e-r-s-i-o-n +Content-Type: text/plain; charset=UTF-8; format=fixed +Content-Transfer-Encoding: 8bit + + +This is the second commit. +--- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- + 3 files changed, 5 insertions(+), 3 deletions(-) + delete mode 100644 file2 + + +--------------g-i-t--v-e-r-s-i-o-n +Content-Type: text/x-patch; name="1" +Content-Transfer-Encoding: 8bit +Content-Disposition: inline; filename="1" + +diff --git a/dir/sub b/dir/sub +index 35d242b..8422d40 100644 +--- a/dir/sub ++++ b/dir/sub +@@ -1,2 +1,4 @@ + A + B ++C ++D +diff --git a/file0 b/file0 +index 01e79c3..b414108 100644 +--- a/file0 ++++ b/file0 +@@ -1,3 +1,6 @@ + 1 + 2 + 3 ++4 ++5 ++6 +diff --git a/file2 b/file2 +deleted file mode 100644 +index 01e79c3..0000000 +--- a/file2 ++++ /dev/null +@@ -1,3 +0,0 @@ +-1 +-2 +-3 + +--------------g-i-t--v-e-r-s-i-o-n-- + + + +From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001 +From: A U Thor +Date: Mon, 26 Jun 2006 00:02:00 +0000 +Subject: [PATCH 2/3] Third +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n" + +This is a multi-part message in MIME format. +--------------g-i-t--v-e-r-s-i-o-n +Content-Type: text/plain; charset=UTF-8; format=fixed +Content-Transfer-Encoding: 8bit + +--- + dir/sub | 2 ++ + file1 | 3 +++ + 2 files changed, 5 insertions(+), 0 deletions(-) + create mode 100644 file1 + + +--------------g-i-t--v-e-r-s-i-o-n +Content-Type: text/x-patch; name="2" +Content-Transfer-Encoding: 8bit +Content-Disposition: inline; filename="2" + +diff --git a/dir/sub b/dir/sub +index 8422d40..cead32e 100644 +--- a/dir/sub ++++ b/dir/sub +@@ -2,3 +2,5 @@ A + B + C + D ++E ++F +diff --git a/file1 b/file1 +new file mode 100644 +index 0000000..b1e6722 +--- /dev/null ++++ b/file1 +@@ -0,0 +1,3 @@ ++A ++B ++C + +--------------g-i-t--v-e-r-s-i-o-n-- + + + +From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001 +From: A U Thor +Date: Mon, 26 Jun 2006 00:03:00 +0000 +Subject: [PATCH 3/3] Side +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n" + +This is a multi-part message in MIME format. +--------------g-i-t--v-e-r-s-i-o-n +Content-Type: text/plain; charset=UTF-8; format=fixed +Content-Transfer-Encoding: 8bit + +--- + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ + 3 files changed, 9 insertions(+), 0 deletions(-) + create mode 100644 file3 + + +--------------g-i-t--v-e-r-s-i-o-n +Content-Type: text/x-patch; name="3" +Content-Transfer-Encoding: 8bit +Content-Disposition: inline; filename="3" + +diff --git a/dir/sub b/dir/sub +index 35d242b..7289e35 100644 +--- a/dir/sub ++++ b/dir/sub +@@ -1,2 +1,4 @@ + A + B ++1 ++2 +diff --git a/file0 b/file0 +index 01e79c3..f4615da 100644 +--- a/file0 ++++ b/file0 +@@ -1,3 +1,6 @@ + 1 + 2 + 3 ++A ++B ++C +diff --git a/file3 b/file3 +new file mode 100644 +index 0000000..7289e35 +--- /dev/null ++++ b/file3 +@@ -0,0 +1,4 @@ ++A ++B ++1 ++2 + +--------------g-i-t--v-e-r-s-i-o-n-- + + +$ From 431b1969fcde69959a23355fba6894fb69c8fa0c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 21 Mar 2009 12:51:34 -0700 Subject: [PATCH 07/55] Rename interpret/substitute nth_last_branch functions These allow you to say "git checkout @{-2}" to switch to the branch two "branch switching" ago by pretending as if you typed the name of that branch. As it is likely that we will be introducing more short-hands to write the name of a branch without writing it explicitly, rename the functions from "nth_last_branch" to more generic "branch_name", to prepare for different semantics. Signed-off-by: Junio C Hamano --- branch.c | 2 +- builtin-branch.c | 2 +- builtin-checkout.c | 2 +- builtin-merge.c | 2 +- cache.h | 2 +- sha1_name.c | 12 ++++++------ 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/branch.c b/branch.c index 5f889fee6b..313bcf1634 100644 --- a/branch.c +++ b/branch.c @@ -137,7 +137,7 @@ void create_branch(const char *head, int len; len = strlen(name); - if (interpret_nth_last_branch(name, &ref) != len) { + if (interpret_branch_name(name, &ref) != len) { strbuf_reset(&ref); strbuf_add(&ref, name, len); } diff --git a/builtin-branch.c b/builtin-branch.c index 14d4b917e5..cacd7daae7 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -123,7 +123,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) for (i = 0; i < argc; i++, strbuf_release(&bname)) { int len = strlen(argv[i]); - if (interpret_nth_last_branch(argv[i], &bname) != len) + if (interpret_branch_name(argv[i], &bname) != len) strbuf_add(&bname, argv[i], len); if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) { diff --git a/builtin-checkout.c b/builtin-checkout.c index 9fdfc58d1a..a8d9d97da2 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -355,7 +355,7 @@ static void setup_branch_path(struct branch_info *branch) struct strbuf buf = STRBUF_INIT; int ret; - if ((ret = interpret_nth_last_branch(branch->name, &buf)) + if ((ret = interpret_branch_name(branch->name, &buf)) && ret == strlen(branch->name)) { branch->name = xstrdup(buf.buf); strbuf_splice(&buf, 0, 0, "refs/heads/", 11); diff --git a/builtin-merge.c b/builtin-merge.c index 4c119359e7..e94ea7c35f 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -361,7 +361,7 @@ static void merge_name(const char *remote, struct strbuf *msg) int len, early; len = strlen(remote); - if (interpret_nth_last_branch(remote, &bname) == len) + if (interpret_branch_name(remote, &bname) == len) remote = bname.buf; memset(branch_head, 0, sizeof(branch_head)); diff --git a/cache.h b/cache.h index 39789ee94a..d28fd74880 100644 --- a/cache.h +++ b/cache.h @@ -671,7 +671,7 @@ extern int read_ref(const char *filename, unsigned char *sha1); extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *); extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); -extern int interpret_nth_last_branch(const char *str, struct strbuf *); +extern int interpret_branch_name(const char *str, struct strbuf *); extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules); extern const char *ref_rev_parse_rules[]; diff --git a/sha1_name.c b/sha1_name.c index 2f75179f4c..904bcd96a5 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -242,10 +242,10 @@ static int ambiguous_path(const char *path, int len) * *string and *len will only be substituted, and *string returned (for * later free()ing) if the string passed in is of the form @{-}. */ -static char *substitute_nth_last_branch(const char **string, int *len) +static char *substitute_branch_name(const char **string, int *len) { struct strbuf buf = STRBUF_INIT; - int ret = interpret_nth_last_branch(*string, &buf); + int ret = interpret_branch_name(*string, &buf); if (ret == *len) { size_t size; @@ -259,7 +259,7 @@ static char *substitute_nth_last_branch(const char **string, int *len) int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref) { - char *last_branch = substitute_nth_last_branch(&str, &len); + char *last_branch = substitute_branch_name(&str, &len); const char **p, *r; int refs_found = 0; @@ -288,7 +288,7 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref) int dwim_log(const char *str, int len, unsigned char *sha1, char **log) { - char *last_branch = substitute_nth_last_branch(&str, &len); + char *last_branch = substitute_branch_name(&str, &len); const char **p; int logs_found = 0; @@ -355,7 +355,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) struct strbuf buf = STRBUF_INIT; int ret; /* try the @{-N} syntax for n-th checkout */ - ret = interpret_nth_last_branch(str+at, &buf); + ret = interpret_branch_name(str+at, &buf); if (ret > 0) { /* substitute this branch name and restart */ return get_sha1_1(buf.buf, buf.len, sha1); @@ -750,7 +750,7 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1, * If the input was ok but there are not N branch switches in the * reflog, it returns 0. */ -int interpret_nth_last_branch(const char *name, struct strbuf *buf) +int interpret_branch_name(const char *name, struct strbuf *buf) { long nth; int i, retval; From a552de75eb01f78046feaf7dc88e5e4833624ad5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 21 Mar 2009 13:17:30 -0700 Subject: [PATCH 08/55] strbuf_branchname(): a wrapper for branch name shorthands The function takes a user-supplied string that is supposed to be a branch name, and puts it in a strbuf after expanding possible shorthand notation. A handful of open coded sequence to do this in the existing code have been changed to use this helper function. Signed-off-by: Junio C Hamano --- branch.c | 7 +------ builtin-branch.c | 6 +----- builtin-checkout.c | 11 +++-------- builtin-merge.c | 5 ++--- strbuf.c | 9 +++++++++ strbuf.h | 2 ++ 6 files changed, 18 insertions(+), 22 deletions(-) diff --git a/branch.c b/branch.c index 313bcf1634..558f092701 100644 --- a/branch.c +++ b/branch.c @@ -134,13 +134,8 @@ void create_branch(const char *head, char *real_ref, msg[PATH_MAX + 20]; struct strbuf ref = STRBUF_INIT; int forcing = 0; - int len; - len = strlen(name); - if (interpret_branch_name(name, &ref) != len) { - strbuf_reset(&ref); - strbuf_add(&ref, name, len); - } + strbuf_branchname(&ref, name); strbuf_splice(&ref, 0, 0, "refs/heads/", 11); if (check_ref_format(ref.buf)) diff --git a/builtin-branch.c b/builtin-branch.c index cacd7daae7..7452db13c8 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -121,11 +121,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) die("Couldn't look up commit object for HEAD"); } for (i = 0; i < argc; i++, strbuf_release(&bname)) { - int len = strlen(argv[i]); - - if (interpret_branch_name(argv[i], &bname) != len) - strbuf_add(&bname, argv[i], len); - + strbuf_branchname(&bname, argv[i]); if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) { error("Cannot delete the branch '%s' " "which you are currently on.", bname.buf); diff --git a/builtin-checkout.c b/builtin-checkout.c index a8d9d97da2..b2680466d8 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -353,16 +353,11 @@ struct branch_info { static void setup_branch_path(struct branch_info *branch) { struct strbuf buf = STRBUF_INIT; - int ret; - if ((ret = interpret_branch_name(branch->name, &buf)) - && ret == strlen(branch->name)) { + strbuf_branchname(&buf, branch->name); + if (strcmp(buf.buf, branch->name)) branch->name = xstrdup(buf.buf); - strbuf_splice(&buf, 0, 0, "refs/heads/", 11); - } else { - strbuf_addstr(&buf, "refs/heads/"); - strbuf_addstr(&buf, branch->name); - } + strbuf_splice(&buf, 0, 0, "refs/heads/", 11); branch->path = strbuf_detach(&buf, NULL); } diff --git a/builtin-merge.c b/builtin-merge.c index e94ea7c35f..6a51823a55 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -360,9 +360,8 @@ static void merge_name(const char *remote, struct strbuf *msg) const char *ptr; int len, early; - len = strlen(remote); - if (interpret_branch_name(remote, &bname) == len) - remote = bname.buf; + strbuf_branchname(&bname, remote); + remote = bname.buf; memset(branch_head, 0, sizeof(branch_head)); remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT); diff --git a/strbuf.c b/strbuf.c index bfbd81632e..a60b0ad67d 100644 --- a/strbuf.c +++ b/strbuf.c @@ -357,3 +357,12 @@ int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint) return len; } + +int strbuf_branchname(struct strbuf *sb, const char *name) +{ + int len = strlen(name); + if (interpret_branch_name(name, sb) == len) + return 0; + strbuf_add(sb, name, len); + return len; +} diff --git a/strbuf.h b/strbuf.h index 89bd36e15a..68923e1908 100644 --- a/strbuf.h +++ b/strbuf.h @@ -131,4 +131,6 @@ extern int strbuf_getline(struct strbuf *, FILE *, int); extern void stripspace(struct strbuf *buf, int skip_comments); extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env); +extern int strbuf_branchname(struct strbuf *sb, const char *name); + #endif /* STRBUF_H */ From a31dca0393fefae894b7a155ae24000107bcc383 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 21 Mar 2009 14:19:53 -0700 Subject: [PATCH 09/55] check-ref-format --branch: give Porcelain a way to grok branch shorthand The command may not be the best place to add this new feature, but $ git check-ref-format --branch "@{-1}" allows Porcelains to figure out what branch you were on before the last branch switching. Signed-off-by: Junio C Hamano --- Documentation/git-check-ref-format.txt | 12 ++++++++++++ builtin-check-ref-format.c | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt index 034223cc5a..51579f6776 100644 --- a/Documentation/git-check-ref-format.txt +++ b/Documentation/git-check-ref-format.txt @@ -7,7 +7,9 @@ git-check-ref-format - Make sure ref name is well formed SYNOPSIS -------- +[verse] 'git check-ref-format' +'git check-ref-format' [--branch] DESCRIPTION ----------- @@ -49,6 +51,16 @@ refname expressions (see linkgit:git-rev-parse[1]). Namely: It may also be used to select a specific object such as with 'git-cat-file': "git cat-file blob v1.3.3:refs.c". +With the `--branch` option, it expands a branch name shorthand and +prints the name of the branch the shorthand refers to. + +EXAMPLE +------- + +git check-ref-format --branch @{-1}:: + +Print the name of the previous branch. + GIT --- diff --git a/builtin-check-ref-format.c b/builtin-check-ref-format.c index 701de439ae..39db6cbe47 100644 --- a/builtin-check-ref-format.c +++ b/builtin-check-ref-format.c @@ -5,9 +5,19 @@ #include "cache.h" #include "refs.h" #include "builtin.h" +#include "strbuf.h" int cmd_check_ref_format(int argc, const char **argv, const char *prefix) { + if (argc == 3 && !strcmp(argv[1], "--branch")) { + struct strbuf sb = STRBUF_INIT; + strbuf_branchname(&sb, argv[2]); + strbuf_splice(&sb, 0, 0, "refs/heads/", 11); + if (check_ref_format(sb.buf)) + die("'%s' is not a valid branch name", argv[2]); + printf("%s\n", sb.buf + 11); + exit(0); + } if (argc != 2) usage("git check-ref-format refname"); return !!check_ref_format(argv[1]); From 03d3aada5a2a68a7acdb6286fd72155f01626e41 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 21 Mar 2009 13:23:27 -0700 Subject: [PATCH 10/55] Fix branch -m @{-1} newname The command is supposed to rename the branch we were on before switched from to a new name, but was not aware of the short-hand notation we added recently. Signed-off-by: Junio C Hamano --- builtin-branch.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index 7452db13c8..0df82bf96d 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -468,18 +468,18 @@ static void rename_branch(const char *oldname, const char *newname, int force) if (!oldname) die("cannot rename the current branch while not on any."); - strbuf_addf(&oldref, "refs/heads/%s", oldname); - + strbuf_branchname(&oldref, oldname); + strbuf_splice(&oldref, 0, 0, "refs/heads/", 11); if (check_ref_format(oldref.buf)) - die("Invalid branch name: %s", oldref.buf); - - strbuf_addf(&newref, "refs/heads/%s", newname); + die("Invalid branch name: '%s'", oldname); + strbuf_branchname(&newref, newname); + strbuf_splice(&newref, 0, 0, "refs/heads/", 11); if (check_ref_format(newref.buf)) - die("Invalid branch name: %s", newref.buf); + die("Invalid branch name: '%s'", newname); if (resolve_ref(newref.buf, sha1, 1, NULL) && !force) - die("A branch named '%s' already exists.", newname); + die("A branch named '%s' already exists.", newref.buf + 11); strbuf_addf(&logmsg, "Branch: renamed %s to %s", oldref.buf, newref.buf); From a2fab531bbb00ff64335906e22854365be2eb5c7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 21 Mar 2009 14:35:51 -0700 Subject: [PATCH 11/55] strbuf_check_branch_ref(): a helper to check a refname for a branch This allows a common calling sequence strbuf_branchname(&ref, name); strbuf_splice(&ref, 0, 0, "refs/heads/", 11); if (check_ref_format(ref.buf)) die(...); to be refactored into if (strbuf_check_branch_ref(&ref, name)) die(...); Signed-off-by: Junio C Hamano --- branch.c | 5 +---- builtin-branch.c | 8 ++------ builtin-check-ref-format.c | 5 ++--- builtin-checkout.c | 7 +++---- strbuf.c | 8 ++++++++ strbuf.h | 1 + 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/branch.c b/branch.c index 558f092701..62030af4b5 100644 --- a/branch.c +++ b/branch.c @@ -135,10 +135,7 @@ void create_branch(const char *head, struct strbuf ref = STRBUF_INIT; int forcing = 0; - strbuf_branchname(&ref, name); - strbuf_splice(&ref, 0, 0, "refs/heads/", 11); - - if (check_ref_format(ref.buf)) + if (strbuf_check_branch_ref(&ref, name)) die("'%s' is not a valid branch name.", name); if (resolve_ref(ref.buf, sha1, 1, NULL)) { diff --git a/builtin-branch.c b/builtin-branch.c index 0df82bf96d..afeed68cfd 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -468,14 +468,10 @@ static void rename_branch(const char *oldname, const char *newname, int force) if (!oldname) die("cannot rename the current branch while not on any."); - strbuf_branchname(&oldref, oldname); - strbuf_splice(&oldref, 0, 0, "refs/heads/", 11); - if (check_ref_format(oldref.buf)) + if (strbuf_check_branch_ref(&oldref, oldname)) die("Invalid branch name: '%s'", oldname); - strbuf_branchname(&newref, newname); - strbuf_splice(&newref, 0, 0, "refs/heads/", 11); - if (check_ref_format(newref.buf)) + if (strbuf_check_branch_ref(&newref, newname)) die("Invalid branch name: '%s'", newname); if (resolve_ref(newref.buf, sha1, 1, NULL) && !force) diff --git a/builtin-check-ref-format.c b/builtin-check-ref-format.c index 39db6cbe47..f9381e07ea 100644 --- a/builtin-check-ref-format.c +++ b/builtin-check-ref-format.c @@ -11,9 +11,8 @@ int cmd_check_ref_format(int argc, const char **argv, const char *prefix) { if (argc == 3 && !strcmp(argv[1], "--branch")) { struct strbuf sb = STRBUF_INIT; - strbuf_branchname(&sb, argv[2]); - strbuf_splice(&sb, 0, 0, "refs/heads/", 11); - if (check_ref_format(sb.buf)) + + if (strbuf_check_branch_ref(&sb, argv[2])) die("'%s' is not a valid branch name", argv[2]); printf("%s\n", sb.buf + 11); exit(0); diff --git a/builtin-checkout.c b/builtin-checkout.c index b2680466d8..66df0c072c 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -733,12 +733,11 @@ no_reference: if (opts.new_branch) { struct strbuf buf = STRBUF_INIT; - strbuf_addstr(&buf, "refs/heads/"); - strbuf_addstr(&buf, opts.new_branch); + if (strbuf_check_branch_ref(&buf, opts.new_branch)) + die("git checkout: we do not like '%s' as a branch name.", + opts.new_branch); if (!get_sha1(buf.buf, rev)) die("git checkout: branch %s already exists", opts.new_branch); - if (check_ref_format(buf.buf)) - die("git checkout: we do not like '%s' as a branch name.", opts.new_branch); strbuf_release(&buf); } diff --git a/strbuf.c b/strbuf.c index a60b0ad67d..a88496030b 100644 --- a/strbuf.c +++ b/strbuf.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "refs.h" int prefixcmp(const char *str, const char *prefix) { @@ -366,3 +367,10 @@ int strbuf_branchname(struct strbuf *sb, const char *name) strbuf_add(sb, name, len); return len; } + +int strbuf_check_branch_ref(struct strbuf *sb, const char *name) +{ + strbuf_branchname(sb, name); + strbuf_splice(sb, 0, 0, "refs/heads/", 11); + return check_ref_format(sb->buf); +} diff --git a/strbuf.h b/strbuf.h index 68923e1908..9ee908a3ec 100644 --- a/strbuf.h +++ b/strbuf.h @@ -132,5 +132,6 @@ extern void stripspace(struct strbuf *buf, int skip_comments); extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env); extern int strbuf_branchname(struct strbuf *sb, const char *name); +extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name); #endif /* STRBUF_H */ From cbdffe4093be77bbb1408e54eead7865dd3bc33f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 21 Mar 2009 13:27:31 -0700 Subject: [PATCH 12/55] check_ref_format(): tighten refname rules This changes the rules for refnames to forbid: (1) a refname that contains "@{" in it. Some people and foreign SCM converter may have named their branches as frotz@24 and we still want to keep supporting it. However, "git branch frotz@{24}" is a disaster. It cannot even checked out because "git checkout frotz@{24}" will interpret it as "detach the HEAD at twenty-fourth reflog entry of the frotz branch". (2) a refname that ends with a dot. We already reject a path component that begins with a dot, primarily to avoid ambiguous range interpretation. If we allowed ".B" as a valid ref, it is unclear if "A...B" means "in dot-B but not in A" or "either in A or B but not in both". But for this to be complete, we need also to forbid "A." to avoid "in B but not in A-dot". This was not a problem in the original range notation, but we should have added this restriction when three-dot notation was introduced. Unlike "no dot at the beginning of any path component" rule, this rule does not have to be "no dot at the end of any path component", because you cannot abbreviate the tail end away, similar to you can say "dot-B" to mean "refs/heads/dot-B". For these reasons, it is not likely people created branches with these names on purpose, but we have allowed such names to be used for quite some time, and it is possible that people created such branches by mistake or by accident. To help people with branches with such unfortunate names to recover, we still allow "branch -d 'bad.'" to delete such branches, and also allow "branch -m bad. good" to rename them. Signed-off-by: Junio C Hamano --- Documentation/git-check-ref-format.txt | 6 +++++- builtin-branch.c | 16 ++++++++++++++-- refs.c | 13 +++++++++---- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt index 51579f6776..d23fd219da 100644 --- a/Documentation/git-check-ref-format.txt +++ b/Documentation/git-check-ref-format.txt @@ -32,7 +32,9 @@ imposes the following rules on how refs are named: caret `{caret}`, colon `:`, question-mark `?`, asterisk `*`, or open bracket `[` anywhere; -. It cannot end with a slash `/`. +. They cannot end with a slash `/` nor a dot `.`. + +. They cannot contain a sequence `@{`. These rules makes it easy for shell script based tools to parse refnames, pathname expansion by the shell when a refname is used @@ -51,6 +53,8 @@ refname expressions (see linkgit:git-rev-parse[1]). Namely: It may also be used to select a specific object such as with 'git-cat-file': "git cat-file blob v1.3.3:refs.c". +. at-open-brace `@{` is used as a notation to access a reflog entry. + With the `--branch` option, it expands a branch name shorthand and prints the name of the branch the shorthand refers to. diff --git a/builtin-branch.c b/builtin-branch.c index afeed68cfd..330e0c3f16 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -464,12 +464,21 @@ static void rename_branch(const char *oldname, const char *newname, int force) struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT; unsigned char sha1[20]; struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT; + int recovery = 0; if (!oldname) die("cannot rename the current branch while not on any."); - if (strbuf_check_branch_ref(&oldref, oldname)) - die("Invalid branch name: '%s'", oldname); + if (strbuf_check_branch_ref(&oldref, oldname)) { + /* + * Bad name --- this could be an attempt to rename a + * ref that we used to allow to be created by accident. + */ + if (resolve_ref(oldref.buf, sha1, 1, NULL)) + recovery = 1; + else + die("Invalid branch name: '%s'", oldname); + } if (strbuf_check_branch_ref(&newref, newname)) die("Invalid branch name: '%s'", newname); @@ -484,6 +493,9 @@ static void rename_branch(const char *oldname, const char *newname, int force) die("Branch rename failed"); strbuf_release(&logmsg); + if (recovery) + warning("Renamed a misnamed branch '%s' away", oldref.buf + 11); + /* no need to pass logmsg here as HEAD didn't really move */ if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL)) die("Branch renamed to %s, but HEAD is not updated!", newname); diff --git a/refs.c b/refs.c index 8d3c502a15..e355489e51 100644 --- a/refs.c +++ b/refs.c @@ -693,7 +693,7 @@ static inline int bad_ref_char(int ch) int check_ref_format(const char *ref) { - int ch, level, bad_type; + int ch, level, bad_type, last; int ret = CHECK_REF_FORMAT_OK; const char *cp = ref; @@ -717,19 +717,24 @@ int check_ref_format(const char *ref) return CHECK_REF_FORMAT_ERROR; } + last = ch; /* scan the rest of the path component */ while ((ch = *cp++) != 0) { bad_type = bad_ref_char(ch); - if (bad_type) { + if (bad_type) return CHECK_REF_FORMAT_ERROR; - } if (ch == '/') break; - if (ch == '.' && *cp == '.') + if (last == '.' && ch == '.') return CHECK_REF_FORMAT_ERROR; + if (last == '@' && ch == '{') + return CHECK_REF_FORMAT_ERROR; + last = ch; } level++; if (!ch) { + if (ref <= cp - 2 && cp[-2] == '.') + return CHECK_REF_FORMAT_ERROR; if (level < 2) return CHECK_REF_FORMAT_ONELEVEL; return ret; From 3e262b95c50991de12cc5e180b72256561606a19 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 24 Mar 2009 16:31:01 -0700 Subject: [PATCH 13/55] Don't permit ref/branch names to end with ".lock" We already skip over loose refs under $GIT_DIR/refs if the name ends with ".lock", so creating a branch named "foo.lock" will not appear in the output of "git branch", "git for-each-ref", nor will its commit be considered reachable by "git rev-list --all". In the latter case this is especially evil, as it may cause repository corruption when objects reachable only through such a ref are deleted by "git prune". It should be reasonably safe to deny use of ".lock" as a ref suffix. In prior versions of Git such branches would be "phantom branches"; you can create it, but you can't see it in "git branch" output. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- Documentation/git-check-ref-format.txt | 2 ++ refs.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt index d23fd219da..9b707a7030 100644 --- a/Documentation/git-check-ref-format.txt +++ b/Documentation/git-check-ref-format.txt @@ -34,6 +34,8 @@ imposes the following rules on how refs are named: . They cannot end with a slash `/` nor a dot `.`. +. They cannot end with the sequence `.lock`. + . They cannot contain a sequence `@{`. These rules makes it easy for shell script based tools to parse diff --git a/refs.c b/refs.c index e355489e51..f3fdcbd202 100644 --- a/refs.c +++ b/refs.c @@ -676,6 +676,7 @@ int for_each_rawref(each_ref_fn fn, void *cb_data) * - it has double dots "..", or * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or * - it ends with a "/". + * - it ends with ".lock" */ static inline int bad_ref_char(int ch) @@ -737,6 +738,8 @@ int check_ref_format(const char *ref) return CHECK_REF_FORMAT_ERROR; if (level < 2) return CHECK_REF_FORMAT_ONELEVEL; + if (has_extension(ref, ".lock")) + return CHECK_REF_FORMAT_ERROR; return ret; } } From 57dac0bfe43a68d55e16def58b8ed305e0676d3f Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Thu, 26 Mar 2009 21:53:24 +0100 Subject: [PATCH 14/55] Test for local branches being followed with --track According to the documentation, it is perfectly okay to follow local branches using the --track option. Introduce a test which checks whether they behave the same. Currently one test fails. Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- t/t6040-tracking-info.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh index ba9060190d..2a2b6b63d5 100755 --- a/t/t6040-tracking-info.sh +++ b/t/t6040-tracking-info.sh @@ -29,7 +29,9 @@ test_expect_success setup ' git checkout -b b4 origin && advance e && advance f - ) + ) && + git checkout -b follower --track master && + advance g ' script='s/^..\(b.\)[ 0-9a-f]*\[\([^]]*\)\].*/\1 \2/p' @@ -56,6 +58,12 @@ test_expect_success 'checkout' ' grep "have 1 and 1 different" actual ' +test_expect_failure 'checkout with local tracked branch' ' + git checkout master && + git checkout follower >actual + grep "is ahead of" actual +' + test_expect_success 'status' ' ( cd test && From b09b868f7fee689483d00bea3d52c0f14a80386c Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Fri, 27 Mar 2009 01:13:01 +0100 Subject: [PATCH 15/55] log-tree: fix patch filename computation in "git format-patch" When using "git format-patch", "get_patch_filename" in "log-tree.c" calls "strbuf_splice" that could die with the following message: "`pos + len' is too far after the end of the buffer" if you have: buf->len < start_len + FORMAT_PATCH_NAME_MAX but: buf->len + suffix_len > start_len + FORMAT_PATCH_NAME_MAX This patch tries to get rid of that bug. [jc: w/ simplified logic] Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- log-tree.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/log-tree.c b/log-tree.c index 56a3488592..5bd29e6994 100644 --- a/log-tree.c +++ b/log-tree.c @@ -187,17 +187,12 @@ void get_patch_filename(struct commit *commit, int nr, const char *suffix, strbuf_addf(buf, commit ? "%04d-" : "%d", nr); if (commit) { + int max_len = start_len + FORMAT_PATCH_NAME_MAX - suffix_len; + format_commit_message(commit, "%f", buf, DATE_NORMAL); - /* - * Replace characters at the end with the suffix if the - * filename is too long - */ - if (buf->len + suffix_len > FORMAT_PATCH_NAME_MAX + start_len) - strbuf_splice(buf, - start_len + FORMAT_PATCH_NAME_MAX - suffix_len, - suffix_len, suffix, suffix_len); - else - strbuf_addstr(buf, suffix); + if (max_len < buf->len) + strbuf_setlen(buf, max_len); + strbuf_addstr(buf, suffix); } } From 5a688fe4706462dfa0a7932ef0c82c335c47e9ab Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 25 Mar 2009 16:19:36 -0700 Subject: [PATCH 16/55] "core.sharedrepository = 0mode" should set, not loosen This fixes the behaviour of octal notation to how it is defined in the documentation, while keeping the traditional "loosen only" semantics intact for "group" and "everybody". Three main points of this patch are: - For an explicit octal notation, the internal shared_repository variable is set to a negative value, so that we can tell "group" (which is to "OR" in 0660) and 0660 (which is to "SET" to 0660); - git-init did not set shared_repository variable early enough to affect the initial creation of many files, notably copied templates and the configuration. We set it very early when a command-line option specifies a custom value. - Many codepaths create files inside $GIT_DIR by various ways that all involve mkstemp(), and then call move_temp_to_file() to rename it to its final destination. We can add adjust_shared_perm() call here; for the traditional "loosen-only", this would be a no-op for many codepaths because the mode is already loose enough, but with the new behaviour it makes a difference. Signed-off-by: Junio C Hamano --- builtin-init-db.c | 12 ++++++++++-- path.c | 34 ++++++++++++++++++++-------------- setup.c | 4 ++-- sha1_file.c | 8 +++++++- t/t1301-shared-repo.sh | 37 +++++++++++++++++++++++++++++++++++++ 5 files changed, 76 insertions(+), 19 deletions(-) diff --git a/builtin-init-db.c b/builtin-init-db.c index ee3911f8ee..8199e5d4d5 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -195,6 +195,8 @@ static int create_default_files(const char *template_path) git_config(git_default_config, NULL); is_bare_repository_cfg = init_is_bare_repository; + + /* reading existing config may have overwrote it */ if (init_shared_repository != -1) shared_repository = init_shared_repository; @@ -313,12 +315,15 @@ int init_db(const char *template_dir, unsigned int flags) * and compatibility values for PERM_GROUP and * PERM_EVERYBODY. */ - if (shared_repository == PERM_GROUP) + if (shared_repository < 0) + /* force to the mode value */ + sprintf(buf, "0%o", -shared_repository); + else if (shared_repository == PERM_GROUP) sprintf(buf, "%d", OLD_PERM_GROUP); else if (shared_repository == PERM_EVERYBODY) sprintf(buf, "%d", OLD_PERM_EVERYBODY); else - sprintf(buf, "0%o", shared_repository); + die("oops"); git_config_set("core.sharedrepository", buf); git_config_set("receive.denyNonFastforwards", "true"); } @@ -398,6 +403,9 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) usage(init_db_usage); } + if (init_shared_repository != -1) + shared_repository = init_shared_repository; + /* * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR * without --bare. Catch the error early. diff --git a/path.c b/path.c index e332b504a6..42898e0fb1 100644 --- a/path.c +++ b/path.c @@ -314,33 +314,39 @@ char *enter_repo(char *path, int strict) int adjust_shared_perm(const char *path) { struct stat st; - int mode; + int mode, tweak, shared; if (!shared_repository) return 0; if (lstat(path, &st) < 0) return -1; mode = st.st_mode; + if (shared_repository < 0) + shared = -shared_repository; + else + shared = shared_repository; + tweak = shared; - if (shared_repository) { - int tweak = shared_repository; - if (!(mode & S_IWUSR)) - tweak &= ~0222; + if (!(mode & S_IWUSR)) + tweak &= ~0222; + if (mode & S_IXUSR) + /* Copy read bits to execute bits */ + tweak |= (tweak & 0444) >> 2; + if (shared_repository < 0) + mode = (mode & ~0777) | tweak; + else mode |= tweak; - } else { - /* Preserve old PERM_UMASK behaviour */ - if (mode & S_IWUSR) - mode |= S_IWGRP; - } if (S_ISDIR(mode)) { - mode |= FORCE_DIR_SET_GID; - /* Copy read bits to execute bits */ - mode |= (shared_repository & 0444) >> 2; + mode |= (shared & 0444) >> 2; + mode |= FORCE_DIR_SET_GID; } - if ((mode & st.st_mode) != mode && chmod(path, mode) < 0) + if (((shared_repository < 0 + ? (st.st_mode & (FORCE_DIR_SET_GID | 0777)) + : (st.st_mode & mode)) != mode) && + chmod(path, mode) < 0) return -2; return 0; } diff --git a/setup.c b/setup.c index 6c2deda184..ebd60de9ce 100644 --- a/setup.c +++ b/setup.c @@ -434,7 +434,7 @@ int git_config_perm(const char *var, const char *value) /* * Treat values 0, 1 and 2 as compatibility cases, otherwise it is - * a chmod value. + * a chmod value to restrict to. */ switch (i) { case PERM_UMASK: /* 0 */ @@ -456,7 +456,7 @@ int git_config_perm(const char *var, const char *value) * Mask filemode value. Others can not get write permission. * x flags for directories are handled separately. */ - return i & 0666; + return -(i & 0666); } int check_repository_format_version(const char *var, const char *value, void *cb) diff --git a/sha1_file.c b/sha1_file.c index a07aa4e5c4..45987bdea8 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2243,11 +2243,15 @@ static void write_sha1_file_prepare(const void *buf, unsigned long len, } /* - * Move the just written object into its final resting place + * Move the just written object into its final resting place. + * NEEDSWORK: this should be renamed to finalize_temp_file() as + * "moving" is only a part of what it does, when no patch between + * master to pu changes the call sites of this function. */ int move_temp_to_file(const char *tmpfile, const char *filename) { int ret = 0; + if (link(tmpfile, filename)) ret = errno; @@ -2275,6 +2279,8 @@ int move_temp_to_file(const char *tmpfile, const char *filename) /* FIXME!!! Collision check here ? */ } + if (adjust_shared_perm(filename)) + return error("unable to set permission to '%s'", filename); return 0; } diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh index 653362ba22..d459854e71 100755 --- a/t/t1301-shared-repo.sh +++ b/t/t1301-shared-repo.sh @@ -126,4 +126,41 @@ test_expect_success 'git reflog expire honors core.sharedRepository' ' esac ' +test_expect_success 'forced modes' ' + mkdir -p templates/hooks && + echo update-server-info >templates/hooks/post-update && + chmod +x templates/hooks/post-update && + echo : >random-file && + mkdir new && + ( + cd new && + umask 002 && + git init --shared=0660 --template=../templates && + >frotz && + git add frotz && + git commit -a -m initial && + git repack + ) && + find new/.git -print | + xargs ls -ld >actual && + + # Everything must be unaccessible to others + test -z "$(sed -n -e "/^.......---/d" actual)" && + + # All directories must have 2770 + test -z "$(sed -n -e "/^drwxrws---/d" -e "/^d/p" actual)" && + + # post-update hook must be 0770 + test -z "$(sed -n -e "/post-update/{ + /^-rwxrwx---/d + p + }" actual)" && + + # All files inside objects must be 0440 + test -z "$(sed -n -e "/objects\//{ + /^d/d + /^-r--r-----/d + }" actual)" +' + test_done From fb8b193670b0c11d118185332efc899d6d01d5f4 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Thu, 26 Mar 2009 16:16:47 +0100 Subject: [PATCH 17/55] Move chmod(foo, 0444) into move_temp_to_file() When writing out a loose object or a pack (index), move_temp_to_file() is called to finalize the resulting file. These files (loose files and packs) should all have permission mode 0444 (modulo adjust_shared_perm()). Therefore, instead of doing chmod(foo, 0444) explicitly from each callsite (or even forgetting to chmod() at all), do the chmod() call from within move_temp_to_file(). Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- fast-import.c | 3 --- http-push.c | 1 - http-walker.c | 1 - index-pack.c | 7 +++---- sha1_file.c | 3 +-- 5 files changed, 4 insertions(+), 11 deletions(-) diff --git a/fast-import.c b/fast-import.c index 3748ddf48d..d5fc042bbf 100644 --- a/fast-import.c +++ b/fast-import.c @@ -902,9 +902,6 @@ static char *keep_pack(char *curr_index_name) static const char *keep_msg = "fast-import"; int keep_fd; - chmod(pack_data->pack_name, 0444); - chmod(curr_index_name, 0444); - keep_fd = odb_pack_keep(name, sizeof(name), pack_data->sha1); if (keep_fd < 0) die("cannot create keep file"); diff --git a/http-push.c b/http-push.c index 30d2d34041..968b6b0662 100644 --- a/http-push.c +++ b/http-push.c @@ -748,7 +748,6 @@ static void finish_request(struct transfer_request *request) aborted = 1; } } else if (request->state == RUN_FETCH_LOOSE) { - fchmod(request->local_fileno, 0444); close(request->local_fileno); request->local_fileno = -1; if (request->curl_result != CURLE_OK && diff --git a/http-walker.c b/http-walker.c index 0dbad3c888..c5a3ea3b31 100644 --- a/http-walker.c +++ b/http-walker.c @@ -231,7 +231,6 @@ static void finish_object_request(struct object_request *obj_req) { struct stat st; - fchmod(obj_req->local, 0444); close(obj_req->local); obj_req->local = -1; if (obj_req->http_code == 416) { diff --git a/index-pack.c b/index-pack.c index 7fee872533..5dfe03ee6c 100644 --- a/index-pack.c +++ b/index-pack.c @@ -823,8 +823,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name, } if (move_temp_to_file(curr_pack_name, final_pack_name)) die("cannot store pack file"); - } - if (from_stdin) + } else if (from_stdin) chmod(final_pack_name, 0444); if (final_index_name != curr_index_name) { @@ -835,8 +834,8 @@ static void final(const char *final_pack_name, const char *curr_pack_name, } if (move_temp_to_file(curr_index_name, final_index_name)) die("cannot store index file"); - } - chmod(final_index_name, 0444); + } else + chmod(final_index_name, 0444); if (!from_stdin) { printf("%s\n", sha1_to_hex(sha1)); diff --git a/sha1_file.c b/sha1_file.c index 45987bdea8..3bd20e715b 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2279,7 +2279,7 @@ int move_temp_to_file(const char *tmpfile, const char *filename) /* FIXME!!! Collision check here ? */ } - if (adjust_shared_perm(filename)) + if (chmod(filename, 0444) || adjust_shared_perm(filename)) return error("unable to set permission to '%s'", filename); return 0; } @@ -2305,7 +2305,6 @@ static void close_sha1_file(int fd) { if (fsync_object_files) fsync_or_die(fd, "sha1 file"); - fchmod(fd, 0444); if (close(fd) != 0) die("error when closing sha1 file (%s)", strerror(errno)); } From 3be1f18e1b15c28ac6c750ff1a42576fd981d0f5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 27 Mar 2009 23:14:39 -0700 Subject: [PATCH 18/55] move_temp_to_file(): do not forget to chmod() in "Coda hack" codepath Now move_temp_to_file() is responsible for doing everything that is necessary to turn a tempfile in $GIT_DIR into its final form, it must make sure "Coda hack" codepath correctly makes the file read-only. Signed-off-by: Junio C Hamano --- sha1_file.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sha1_file.c b/sha1_file.c index 3bd20e715b..6f278593e5 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2263,12 +2263,12 @@ int move_temp_to_file(const char *tmpfile, const char *filename) * * The same holds for FAT formatted media. * - * When this succeeds, we just return 0. We have nothing + * When this succeeds, we just return. We have nothing * left to unlink. */ if (ret && ret != EEXIST) { if (!rename(tmpfile, filename)) - return 0; + goto out; ret = errno; } unlink(tmpfile); @@ -2279,6 +2279,7 @@ int move_temp_to_file(const char *tmpfile, const char *filename) /* FIXME!!! Collision check here ? */ } +out: if (chmod(filename, 0444) || adjust_shared_perm(filename)) return error("unable to set permission to '%s'", filename); return 0; From 17e61b82887fb71800b0fcd39ffe89ddf4d2492e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 27 Mar 2009 23:21:00 -0700 Subject: [PATCH 19/55] set_shared_perm(): sometimes we know what the final mode bits should look like adjust_shared_perm() first obtains the mode bits from lstat(2), expecting to find what the result of applying user's umask is, and then tweaks it as necessary. When the file to be adjusted is created with mkstemp(3), however, the mode thusly obtained does not have anything to do with user's umask, and we would need to start from 0444 in such a case and there is no point running lstat(2) for such a path. This introduces a new API set_shared_perm() to bypass the lstat(2) and instead force setting the mode bits to the desired value directly. adjust_shared_perm() becomes a thin wrapper to the function. Signed-off-by: Junio C Hamano --- cache.h | 3 ++- path.c | 25 ++++++++++++++++--------- sha1_file.c | 2 +- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/cache.h b/cache.h index 189151de25..e283bbe173 100644 --- a/cache.h +++ b/cache.h @@ -613,7 +613,8 @@ enum sharedrepo { PERM_EVERYBODY = 0664, }; int git_config_perm(const char *var, const char *value); -int adjust_shared_perm(const char *path); +int set_shared_perm(const char *path, int mode); +#define adjust_shared_perm(path) set_shared_perm((path), 0) int safe_create_leading_directories(char *path); int safe_create_leading_directories_const(const char *path); char *enter_repo(char *path, int strict); diff --git a/path.c b/path.c index 42898e0fb1..8a0a6741fd 100644 --- a/path.c +++ b/path.c @@ -311,16 +311,23 @@ char *enter_repo(char *path, int strict) return NULL; } -int adjust_shared_perm(const char *path) +int set_shared_perm(const char *path, int mode) { struct stat st; - int mode, tweak, shared; + int tweak, shared, orig_mode; - if (!shared_repository) + if (!shared_repository) { + if (mode) + return chmod(path, mode & ~S_IFMT); return 0; - if (lstat(path, &st) < 0) - return -1; - mode = st.st_mode; + } + if (!mode) { + if (lstat(path, &st) < 0) + return -1; + mode = st.st_mode; + orig_mode = mode; + } else + orig_mode = 0; if (shared_repository < 0) shared = -shared_repository; else @@ -344,9 +351,9 @@ int adjust_shared_perm(const char *path) } if (((shared_repository < 0 - ? (st.st_mode & (FORCE_DIR_SET_GID | 0777)) - : (st.st_mode & mode)) != mode) && - chmod(path, mode) < 0) + ? (orig_mode & (FORCE_DIR_SET_GID | 0777)) + : (orig_mode & mode)) != mode) && + chmod(path, (mode & ~S_IFMT)) < 0) return -2; return 0; } diff --git a/sha1_file.c b/sha1_file.c index 6f278593e5..d978abf43d 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2280,7 +2280,7 @@ int move_temp_to_file(const char *tmpfile, const char *filename) } out: - if (chmod(filename, 0444) || adjust_shared_perm(filename)) + if (set_shared_perm(filename, (S_IFREG|0444))) return error("unable to set permission to '%s'", filename); return 0; } From 871d21d42e0f782b7cb111beec8c252e9aa627ff Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 31 Mar 2009 16:24:38 -0700 Subject: [PATCH 20/55] format_sanitized_subject: Don't trim past initial length of strbuf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the subject line is '...' the strbuf will be accessed before the first dot is added; potentially changing the strbuf passed into the function or accessing sb->buf[-1] if it was originally empty. Reported-by: René Scharfe Signed-off-by: Junio C Hamano --- pretty.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pretty.c b/pretty.c index c57cef47c9..a0ef356558 100644 --- a/pretty.c +++ b/pretty.c @@ -502,6 +502,7 @@ static int istitlechar(char c) static void format_sanitized_subject(struct strbuf *sb, const char *msg) { size_t trimlen; + size_t start_len = sb->len; int space = 2; for (; *msg && *msg != '\n'; msg++) { @@ -519,8 +520,9 @@ static void format_sanitized_subject(struct strbuf *sb, const char *msg) /* trim any trailing '.' or '-' characters */ trimlen = 0; - while (sb->buf[sb->len - 1 - trimlen] == '.' - || sb->buf[sb->len - 1 - trimlen] == '-') + while (sb->len - trimlen > start_len && + (sb->buf[sb->len - 1 - trimlen] == '.' + || sb->buf[sb->len - 1 - trimlen] == '-')) trimlen++; strbuf_remove(sb, sb->len - trimlen, trimlen); } From 1b89eaa4bef44ef84f2af611d5db8727e3be266c Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 31 Mar 2009 16:36:00 -0400 Subject: [PATCH 21/55] t1301: loosen test for forced modes One of the aspects of the test checked explicitly for the g+s bit to be set on created directories. However, this is only the means to an end (the "end" being having the correct group set). And in fact, on systems where DIR_HAS_BSD_GROUP_SEMANTICS is set, we do not even need to use this "means" at all, causing the test to fail. This patch removes that part of the test. In an ideal world it would be replaced by a test to check that the group was properly assigned, but that is difficult to automate because it requires the user running the test suite be a member of multiple groups. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t1301-shared-repo.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh index d459854e71..3c8a2373ac 100755 --- a/t/t1301-shared-repo.sh +++ b/t/t1301-shared-repo.sh @@ -147,8 +147,8 @@ test_expect_success 'forced modes' ' # Everything must be unaccessible to others test -z "$(sed -n -e "/^.......---/d" actual)" && - # All directories must have 2770 - test -z "$(sed -n -e "/^drwxrws---/d" -e "/^d/p" actual)" && + # All directories must have either 2770 or 770 + test -z "$(sed -n -e "/^drwxrw[sx]---/d" -e "/^d/p" actual)" && # post-update hook must be 0770 test -z "$(sed -n -e "/post-update/{ From 666e07e69703c3930a60fbb1a74ed9923d293f16 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 31 Mar 2009 09:45:22 -0700 Subject: [PATCH 22/55] Clean up reflog unreachability pruning decision This clarifies the pruning rules for unreachable commits by having a separate helpder function for the unreachability decision. It's preparation for actual bigger changes to come to speed up the decision when the reachability calculations become a bottleneck. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-reflog.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/builtin-reflog.c b/builtin-reflog.c index d95f515f2e..a07960ff5e 100644 --- a/builtin-reflog.c +++ b/builtin-reflog.c @@ -209,6 +209,31 @@ static int keep_entry(struct commit **it, unsigned char *sha1) return 1; } +static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1) +{ + /* + * We may or may not have the commit yet - if not, look it + * up using the supplied sha1. + */ + if (!commit) { + if (is_null_sha1(sha1)) + return 0; + + commit = lookup_commit_reference_gently(sha1, 1); + + /* Not a commit -- keep it */ + if (!commit) + return 0; + } + + /* Reachable from the current ref? Don't prune. */ + if (in_merge_bases(commit, &cb->ref_commit, 1)) + return 0; + + /* We can't reach it - prune it. */ + return 1; +} + static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, const char *email, unsigned long timestamp, int tz, const char *message, void *cb_data) @@ -230,12 +255,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, if (timestamp < cb->cmd->expire_unreachable) { if (!cb->ref_commit) goto prune; - if (!old && !is_null_sha1(osha1)) - old = lookup_commit_reference_gently(osha1, 1); - if (!new && !is_null_sha1(nsha1)) - new = lookup_commit_reference_gently(nsha1, 1); - if ((old && !in_merge_bases(old, &cb->ref_commit, 1)) || - (new && !in_merge_bases(new, &cb->ref_commit, 1))) + if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1)) goto prune; } From 494fbfe87ade4658cb5c3a061a5be5d6f6496607 Mon Sep 17 00:00:00 2001 From: Junio Hamano Date: Mon, 30 Mar 2009 21:34:14 -0700 Subject: [PATCH 23/55] Speed up reflog pruning of unreachable commits Instead of doing the (potentially very expensive) "in_merge_base()" check for each commit that might be pruned if it is unreachable, do a preparatory reachability graph of the commit space, so that the common case of being reachable can be tested directly. [ Cleaned up a bit and tweaked to actually work. - Linus ] Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-reflog.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/builtin-reflog.c b/builtin-reflog.c index a07960ff5e..249ad2a311 100644 --- a/builtin-reflog.c +++ b/builtin-reflog.c @@ -52,6 +52,7 @@ struct collect_reflog_cb { #define INCOMPLETE (1u<<10) #define STUDYING (1u<<11) +#define REACHABLE (1u<<12) static int tree_is_complete(const unsigned char *sha1) { @@ -227,6 +228,8 @@ static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsig } /* Reachable from the current ref? Don't prune. */ + if (commit->object.flags & REACHABLE) + return 0; if (in_merge_bases(commit, &cb->ref_commit, 1)) return 0; @@ -234,6 +237,43 @@ static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsig return 1; } +static void mark_reachable(struct commit *commit, unsigned long expire_limit) +{ + /* + * We need to compute if commit on either side of an reflog + * entry is reachable from the tip of the ref for all entries. + * Mark commits that are reachable from the tip down to the + * time threashold first; we know a commit marked thusly is + * reachable from the tip without running in_merge_bases() + * at all. + */ + struct commit_list *pending = NULL; + + commit_list_insert(commit, &pending); + while (pending) { + struct commit_list *entry = pending; + struct commit_list *parent; + pending = entry->next; + commit = entry->item; + free(entry); + if (commit->object.flags & REACHABLE) + continue; + if (parse_commit(commit)) + continue; + commit->object.flags |= REACHABLE; + if (commit->date < expire_limit) + continue; + parent = commit->parents; + while (parent) { + commit = parent->item; + parent = parent->next; + if (commit->object.flags & REACHABLE) + continue; + commit_list_insert(commit, &pending); + } + } +} + static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, const char *email, unsigned long timestamp, int tz, const char *message, void *cb_data) @@ -308,7 +348,11 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, cb.ref_commit = lookup_commit_reference_gently(sha1, 1); cb.ref = ref; cb.cmd = cmd; + if (cb.ref_commit) + mark_reachable(cb.ref_commit, cmd->expire_total); for_each_reflog_ent(ref, expire_reflog_ent, &cb); + if (cb.ref_commit) + clear_commit_marks(cb.ref_commit, REACHABLE); finish: if (cb.newlog) { if (fclose(cb.newlog)) { From 5e6e2b487e76066d03b4f5809dc6b44b1234519a Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Wed, 1 Apr 2009 23:42:49 +0200 Subject: [PATCH 24/55] Make local branches behave like remote branches when --tracked This makes sure that local branches, when followed using --track, behave the same as remote ones (e.g. differences being reported by git status and git checkout). This fixes 1 known failure. The fix is done within branch_get(): The first natural candidate, namely remote_find_tracking(), does not have all the necessary info because in general there is no remote struct for '.', and we don't want one because it would show up in other places as well. branch_get(), on the other hand, has access to merge_names[] (in addition to merge[]) and therefore can set up the followed branch easily. Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- remote.c | 7 +++++-- t/t6040-tracking-info.sh | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/remote.c b/remote.c index e5d6b100d7..30d7fdec83 100644 --- a/remote.c +++ b/remote.c @@ -1170,8 +1170,9 @@ struct branch *branch_get(const char *name) for (i = 0; i < ret->merge_nr; i++) { ret->merge[i] = xcalloc(1, sizeof(**ret->merge)); ret->merge[i]->src = xstrdup(ret->merge_name[i]); - remote_find_tracking(ret->remote, - ret->merge[i]); + if (remote_find_tracking(ret->remote, ret->merge[i]) + && !strcmp(ret->remote_name, ".")) + ret->merge[i]->dst = xstrdup(ret->merge_name[i]); } } } @@ -1450,6 +1451,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb) base = branch->merge[0]->dst; if (!prefixcmp(base, "refs/remotes/")) { base += strlen("refs/remotes/"); + } else if (!prefixcmp(base, "refs/heads/")) { + base += strlen("refs/heads/"); } if (!num_theirs) strbuf_addf(sb, "Your branch is ahead of '%s' " diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh index 2a2b6b63d5..3d6db4d386 100755 --- a/t/t6040-tracking-info.sh +++ b/t/t6040-tracking-info.sh @@ -58,7 +58,7 @@ test_expect_success 'checkout' ' grep "have 1 and 1 different" actual ' -test_expect_failure 'checkout with local tracked branch' ' +test_expect_success 'checkout with local tracked branch' ' git checkout master && git checkout follower >actual grep "is ahead of" actual From f0946cb826b9b6c01976860fdd9dfd498ce0ec5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Tue, 31 Mar 2009 17:05:01 +0200 Subject: [PATCH 25/55] tree_entry_interesting: a pathspec only matches at directory boundary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously the code did a simple prefix match, which means that a path in a directory "frotz/" would have matched with pathspec "f". Signed-off-by: Björn Steinbrink Signed-off-by: Junio C Hamano --- t/t4010-diff-pathspec.sh | 8 ++++++++ tree-diff.c | 12 +++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh index ad3d9e4845..4c4c8b1570 100755 --- a/t/t4010-diff-pathspec.sh +++ b/t/t4010-diff-pathspec.sh @@ -62,4 +62,12 @@ test_expect_success \ 'git diff-index --cached $tree -- file0/ >current && compare_diff_raw current expected' +test_expect_success 'diff-tree pathspec' ' + tree2=$(git write-tree) && + echo "$tree2" && + git diff-tree -r --name-only $tree $tree2 -- pa path1/a >current && + >expected && + test_cmp expected current +' + test_done diff --git a/tree-diff.c b/tree-diff.c index 9f67af6c1f..b05d0f4355 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -118,10 +118,16 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int continue; /* - * The base is a subdirectory of a path which - * was specified, so all of them are interesting. + * If the base is a subdirectory of a path which + * was specified, all of them are interesting. */ - return 2; + if (!matchlen || + base[matchlen] == '/' || + match[matchlen - 1] == '/') + return 2; + + /* Just a random prefix match */ + continue; } /* Does the base match? */ From 8092bfb6c23776d72ce4b38a3b517c3753c3b9fe Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 1 Apr 2009 19:34:03 -0700 Subject: [PATCH 26/55] match_tree_entry(): a pathspec only matches at directory boundaries Previously the code did a simple prefix match, which means that a path in a directory "frotz/" would have matched with pathspec "f". Signed-off-by: Junio C Hamano --- t/t3101-ls-tree-dirname.sh | 6 ++++++ tree.c | 8 ++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh index 4dd7d12bac..51cb4a30f5 100755 --- a/t/t3101-ls-tree-dirname.sh +++ b/t/t3101-ls-tree-dirname.sh @@ -135,4 +135,10 @@ test_expect_success \ EOF test_output' +test_expect_success 'ls-tree filter is leading path match' ' + git ls-tree $tree pa path3/a >current && + >expected && + test_output +' + test_done diff --git a/tree.c b/tree.c index 03e782a9ca..d82a047e55 100644 --- a/tree.c +++ b/tree.c @@ -60,8 +60,12 @@ static int match_tree_entry(const char *base, int baselen, const char *path, uns /* If it doesn't match, move along... */ if (strncmp(base, match, matchlen)) continue; - /* The base is a subdirectory of a path which was specified. */ - return 1; + /* pathspecs match only at the directory boundaries */ + if (!matchlen || + base[matchlen] == '/' || + match[matchlen - 1] == '/') + return 1; + continue; } /* Does the base match? */ From 3944ba0cb0ef5119dc9d1708c572855fca88fc43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Wed, 1 Apr 2009 19:48:24 +0300 Subject: [PATCH 27/55] Allow curl to rewind the read buffers When using multi-pass authentication methods, the curl library may need to rewind the read buffers (depending on how much already has been fed to the server) used for providing data to HTTP PUT, POST or PROPFIND, and in order to allow the library to do so, we need to tell it how by providing either an ioctl callback or a seek callback. This patch adds an ioctl callback, which should be usable on older curl versions (since 7.12.3) than the seek callback (introduced in curl 7.18.0). Some HTTP servers (such as Apache) give an 401 error reply immediately after receiving the headers (so no data has been read from the read buffers, and thus no rewinding is needed), but other servers (such as Lighttpd) only replies after the whole request has been sent and all data has been read from the read buffers, making rewinding necessary. Signed-off-by: Martin Storsjo Signed-off-by: Junio C Hamano --- http-push.c | 24 ++++++++++++++++++++++++ http.c | 19 +++++++++++++++++++ http.h | 7 +++++++ 3 files changed, 50 insertions(+) diff --git a/http-push.c b/http-push.c index 6ce5a1d550..7dc0dd4ec7 100644 --- a/http-push.c +++ b/http-push.c @@ -567,6 +567,10 @@ static void start_put(struct transfer_request *request) curl_easy_setopt(slot->curl, CURLOPT_INFILE, &request->buffer); curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, request->buffer.buf.len); curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); +#ifndef NO_CURL_IOCTL + curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer); + curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &request->buffer); +#endif curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null); curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT); curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1); @@ -1267,6 +1271,10 @@ static struct remote_lock *lock_remote(const char *path, long timeout) curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer); curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len); curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); +#ifndef NO_CURL_IOCTL + curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer); + curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer); +#endif curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); curl_easy_setopt(slot->curl, CURLOPT_URL, url); @@ -1508,6 +1516,10 @@ static void remote_ls(const char *path, int flags, curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer); curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len); curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); +#ifndef NO_CURL_IOCTL + curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer); + curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer); +#endif curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); curl_easy_setopt(slot->curl, CURLOPT_URL, url); @@ -1584,6 +1596,10 @@ static int locking_available(void) curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer); curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len); curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); +#ifndef NO_CURL_IOCTL + curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer); + curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer); +#endif curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); curl_easy_setopt(slot->curl, CURLOPT_URL, repo->url); @@ -1766,6 +1782,10 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock) curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer); curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len); curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); +#ifndef NO_CURL_IOCTL + curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer); + curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer); +#endif curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null); curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); @@ -1910,6 +1930,10 @@ static void update_remote_info_refs(struct remote_lock *lock) curl_easy_setopt(slot->curl, CURLOPT_INFILE, &buffer); curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, buffer.buf.len); curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); +#ifndef NO_CURL_IOCTL + curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer); + curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &buffer); +#endif curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null); curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); diff --git a/http.c b/http.c index 2fc55d671e..2e3d6493ef 100644 --- a/http.c +++ b/http.c @@ -44,6 +44,25 @@ size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *buffer_) return size; } +#ifndef NO_CURL_IOCTL +curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp) +{ + struct buffer *buffer = clientp; + + switch (cmd) { + case CURLIOCMD_NOP: + return CURLIOE_OK; + + case CURLIOCMD_RESTARTREAD: + buffer->posn = 0; + return CURLIOE_OK; + + default: + return CURLIOE_UNKNOWNCMD; + } +} +#endif + size_t fwrite_buffer(const void *ptr, size_t eltsize, size_t nmemb, void *buffer_) { size_t size = eltsize * nmemb; diff --git a/http.h b/http.h index 905b4629a4..26abebed1f 100644 --- a/http.h +++ b/http.h @@ -37,6 +37,10 @@ #define CURLE_HTTP_RETURNED_ERROR CURLE_HTTP_NOT_FOUND #endif +#if LIBCURL_VERSION_NUM < 0x070c03 +#define NO_CURL_IOCTL +#endif + struct slot_results { CURLcode curl_result; @@ -67,6 +71,9 @@ struct buffer extern size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *strbuf); extern size_t fwrite_buffer(const void *ptr, size_t eltsize, size_t nmemb, void *strbuf); extern size_t fwrite_null(const void *ptr, size_t eltsize, size_t nmemb, void *strbuf); +#ifndef NO_CURL_IOCTL +extern curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp); +#endif /* Slot lifecycle functions */ extern struct active_request_slot *get_active_slot(void); From bef3894847118ab8c8668ddc90fa238ae9e05baa Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 2 Apr 2009 15:30:25 +0200 Subject: [PATCH 28/55] Fix 'git checkout ' to update the index While 'git checkout ' should not update the submodule's working directory, it should update the index. This is in line with how submodules are handled in the rest of Git. While at it, test 'git reset [] ', too. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-checkout.c | 3 --- t/t2013-checkout-submodule.sh | 42 +++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) create mode 100755 t/t2013-checkout-submodule.sh diff --git a/builtin-checkout.c b/builtin-checkout.c index 20b34ce6e1..c0abe1ca77 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -53,9 +53,6 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen, int len; struct cache_entry *ce; - if (S_ISGITLINK(mode)) - return 0; - if (S_ISDIR(mode)) return READ_TREE_RECURSIVE; diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh new file mode 100755 index 0000000000..fda3f0af7e --- /dev/null +++ b/t/t2013-checkout-submodule.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +test_description='checkout can handle submodules' + +. ./test-lib.sh + +test_expect_success 'setup' ' + mkdir submodule && + (cd submodule && + git init && + test_commit first) && + git add submodule && + test_tick && + git commit -m superproject && + (cd submodule && + test_commit second) && + git add submodule && + test_tick && + git commit -m updated.superproject +' + +test_expect_success '"reset " updates the index' ' + git update-index --refresh && + git diff-files --quiet && + git diff-index --quiet --cached HEAD && + test_must_fail git reset HEAD^ submodule && + test_must_fail git diff-files --quiet && + git reset submodule && + git diff-files --quiet +' + +test_expect_success '"checkout " updates the index only' ' + git update-index --refresh && + git diff-files --quiet && + git diff-index --quiet --cached HEAD && + git checkout HEAD^ submodule && + test_must_fail git diff-files --quiet && + git checkout HEAD submodule && + git diff-files --quiet +' + +test_done From 0da43a685aa061f55ed19ea30e1d6220020059a6 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Sat, 4 Apr 2009 23:23:21 -0400 Subject: [PATCH 29/55] send-email: fix nasty bug in ask() function Commit 6e18251 (send-email: refactor and ensure prompting doesn't loop forever) introduced an ask function, which unfortunately had a nasty bug. This caused it not to accept anything but the default reply to the "Who should the emails appear to be from?" prompt, and nothing but ctrl-d to the "Who should the emails be sent to?" and "Message-ID to be used as In-Reply-To for the first email?" prompts. This commit corrects the issues and adds a test to confirm the fix. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- git-send-email.perl | 4 ++-- t/t9001-send-email.sh | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/git-send-email.perl b/git-send-email.perl index 6bbdfec849..172b53c2d5 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -608,7 +608,7 @@ EOT sub ask { my ($prompt, %arg) = @_; - my $valid_re = $arg{valid_re} || ""; # "" matches anything + my $valid_re = $arg{valid_re}; my $default = $arg{default}; my $resp; my $i = 0; @@ -624,7 +624,7 @@ sub ask { if ($resp eq '' and defined $default) { return $default; } - if ($resp =~ /$valid_re/) { + if (!defined $valid_re or $resp =~ /$valid_re/) { return $resp; } } diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 192b97b2d6..3c90c4fc1c 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -130,6 +130,19 @@ test_expect_success 'Show all headers' ' test_cmp expected-show-all-headers actual-show-all-headers ' +test_expect_success 'Prompting works' ' + clean_fake_sendmail && + (echo "Example " + echo "to@example.com" + echo "" + ) | GIT_SEND_EMAIL_NOTTY=1 git send-email \ + --smtp-server="$(pwd)/fake.sendmail" \ + $patches \ + 2>errors && + grep "^From: Example $" msgtxt1 && + grep "^To: to@example.com$" msgtxt1 +' + z8=zzzzzzzz z64=$z8$z8$z8$z8$z8$z8$z8$z8 z512=$z64$z64$z64$z64$z64$z64$z64$z64 From 89a56bfbd3fd855cb0c2a381520e6255de41a55e Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Sun, 5 Apr 2009 04:15:16 +0200 Subject: [PATCH 30/55] add --html-path to get the location of installed HTML docs This can be used in GUIs to open installed HTML documentation in the browser. Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- Documentation/git.txt | 6 +++++- Makefile | 1 + contrib/completion/git-completion.bash | 1 + git.c | 5 ++++- perl/Git.pm | 12 +++++++++++- 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Documentation/git.txt b/Documentation/git.txt index 7513c57c6a..2ce5e6b451 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -9,7 +9,7 @@ git - the stupid content tracker SYNOPSIS -------- [verse] -'git' [--version] [--exec-path[=GIT_EXEC_PATH]] +'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS] @@ -178,6 +178,10 @@ help ...`. environment variable. If no path is given, 'git' will print the current setting and then exit. +--html-path:: + Print the path to wherever your git HTML documentation is installed + and exit. + -p:: --paginate:: Pipe all output into 'less' (or if set, $PAGER). diff --git a/Makefile b/Makefile index 7867eaccdb..bcf7cbb3fb 100644 --- a/Makefile +++ b/Makefile @@ -1191,6 +1191,7 @@ strip: $(PROGRAMS) git$X git.o: git.c common-cmds.h GIT-CFLAGS $(QUIET_CC)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \ + '-DGIT_HTML_PATH="$(htmldir_SQ)"' \ $(ALL_CFLAGS) -c $(filter %.c,$^) git$X: git.o $(BUILTIN_OBJS) $(GITLIBS) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e72ce2428d..fdc5a24b27 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1870,6 +1870,7 @@ _git () --bare --version --exec-path + --html-path --work-tree= --help " diff --git a/git.c b/git.c index c2b181ed78..ff72e22bec 100644 --- a/git.c +++ b/git.c @@ -5,7 +5,7 @@ #include "run-command.h" const char git_usage_string[] = - "git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]"; + "git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]"; const char git_more_info_string[] = "See 'git help COMMAND' for more information on a specific command."; @@ -75,6 +75,9 @@ static int handle_options(const char*** argv, int* argc, int* envchanged) puts(git_exec_path()); exit(0); } + } else if (!strcmp(cmd, "--html-path")) { + puts(system_path(GIT_HTML_PATH)); + exit(0); } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) { use_pager = 1; } else if (!strcmp(cmd, "--no-pager")) { diff --git a/perl/Git.pm b/perl/Git.pm index 7d7f2b1d36..291ff5b53c 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -56,7 +56,7 @@ require Exporter; @EXPORT_OK = qw(command command_oneline command_noisy command_output_pipe command_input_pipe command_close_pipe command_bidi_pipe command_close_bidi_pipe - version exec_path hash_object git_cmd_try + version exec_path html_path hash_object git_cmd_try remote_refs temp_acquire temp_release temp_reset temp_path); @@ -492,6 +492,16 @@ C). Useful mostly only internally. sub exec_path { command_oneline('--exec-path') } +=item html_path () + +Return path to the Git html documentation (the same as +C). Useful mostly only internally. + +=cut + +sub html_path { command_oneline('--html-path') } + + =item repo_path () Return path to the git repository. Must be called on a repository instance. From e1dc49bcdefd362e129c9ee3a85527b518f9b49d Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sat, 4 Apr 2009 11:59:55 -0500 Subject: [PATCH 31/55] git-repack: use non-dashed update-server-info Signed-off-by: Dan McGee Signed-off-by: Junio C Hamano --- git-repack.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-repack.sh b/git-repack.sh index be6db5e805..00c597e97c 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -181,5 +181,5 @@ fi case "$no_update_info" in t) : ;; -*) git-update-server-info ;; +*) git update-server-info ;; esac From f3a186ffade15f793ea17713a10e10ec4f26ff11 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 4 Apr 2009 22:02:26 +0200 Subject: [PATCH 32/55] bisect: improve error message when branch checkout fails In "git-bisect.sh" the "git checkout" command is only used to change the current branch, but it is used like this: git checkout "$branch" which will output the following misleading error message when it fails: error: pathspec 'foo' did not match any file(s) known to git. This patch change the way we use "git checkout" like this: git checkout "$branch" -- so that we will get the following error message: fatal: invalid reference: foo which is better. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- git-bisect.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index e313bdea70..df0ae63b4e 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -77,7 +77,7 @@ bisect_start() { then # Reset to the rev from where we started. start_head=$(cat "$GIT_DIR/BISECT_START") - git checkout "$start_head" || exit + git checkout "$start_head" -- || exit else # Get rev from where we start. case "$head" in @@ -370,7 +370,7 @@ bisect_checkout() { _msg="$2" echo "Bisecting: $_msg" mark_expected_rev "$_rev" - git checkout -q "$_rev" || exit + git checkout -q "$_rev" -- || exit git show-branch "$_rev" } @@ -549,7 +549,7 @@ bisect_reset() { *) usage ;; esac - git checkout "$branch" && bisect_clean_state + git checkout "$branch" -- && bisect_clean_state } bisect_clean_state() { From f37ae35e73dbc492bd3ca24badb309817ecaaa67 Mon Sep 17 00:00:00 2001 From: Clemens Buchacher Date: Sun, 5 Apr 2009 02:46:58 +0200 Subject: [PATCH 33/55] add tests for merging with submodules Signed-off-by: Clemens Buchacher Signed-off-by: Junio C Hamano --- t/t7405-submodule-merge.sh | 74 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100755 t/t7405-submodule-merge.sh diff --git a/t/t7405-submodule-merge.sh b/t/t7405-submodule-merge.sh new file mode 100755 index 0000000000..9778ad49cf --- /dev/null +++ b/t/t7405-submodule-merge.sh @@ -0,0 +1,74 @@ +#!/bin/sh + +test_description='merging with submodules' + +. ./test-lib.sh + +# +# history +# +# a --- c +# / \ / +# root X +# \ / \ +# b --- d +# + +test_expect_success setup ' + + mkdir sub && + (cd sub && + git init && + echo original > file && + git add file && + test_tick && + git commit -m sub-root) && + git add sub && + test_tick && + git commit -m root && + + git checkout -b a master && + (cd sub && + echo A > file && + git add file && + test_tick && + git commit -m sub-a) && + git add sub && + test_tick && + git commit -m a && + + git checkout -b b master && + (cd sub && + echo B > file && + git add file && + test_tick && + git commit -m sub-b) && + git add sub && + test_tick && + git commit -m b + + git checkout -b c a && + git merge -s ours b && + + git checkout -b d b && + git merge -s ours a +' + +test_expect_failure 'merging with modify/modify conflict' ' + + git checkout -b test1 a && + test_must_fail git merge b && + test -f .git/MERGE_MSG && + git diff + +' + +test_expect_failure 'merging with a modify/modify conflict between merge bases' ' + + git reset --hard HEAD && + git checkout -b test2 c && + git merge d + +' + +test_done From 0eb6574c24241b1e54be1ddff60287544faaf8d8 Mon Sep 17 00:00:00 2001 From: Clemens Buchacher Date: Sun, 5 Apr 2009 02:46:59 +0200 Subject: [PATCH 34/55] update cache for conflicting submodule entries When merging merge bases during a recursive merge we do not want to leave any unmerged entries. Otherwise we cannot create a temporary tree for the recursive merge to work with. We failed to do so in case of a submodule conflict between merge bases, causing a NULL pointer dereference in the next step of the recursive merge. Signed-off-by: Clemens Buchacher Signed-off-by: Junio C Hamano --- merge-recursive.c | 5 +++-- t/t7405-submodule-merge.sh | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/merge-recursive.c b/merge-recursive.c index ee853b990d..3618c94bd2 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -1123,10 +1123,11 @@ static int process_entry(struct merge_options *o, clean_merge = mfi.clean; if (mfi.clean) update_file(o, 1, mfi.sha, mfi.mode, path); - else if (S_ISGITLINK(mfi.mode)) + else if (S_ISGITLINK(mfi.mode)) { output(o, 1, "CONFLICT (submodule): Merge conflict in %s " "- needs %s", path, sha1_to_hex(b.sha1)); - else { + update_file(o, 0, mfi.sha, mfi.mode, path); + } else { output(o, 1, "CONFLICT (%s): Merge conflict in %s", reason, path); diff --git a/t/t7405-submodule-merge.sh b/t/t7405-submodule-merge.sh index 9778ad49cf..aa6c44ce4f 100755 --- a/t/t7405-submodule-merge.sh +++ b/t/t7405-submodule-merge.sh @@ -63,7 +63,7 @@ test_expect_failure 'merging with modify/modify conflict' ' ' -test_expect_failure 'merging with a modify/modify conflict between merge bases' ' +test_expect_success 'merging with a modify/modify conflict between merge bases' ' git reset --hard HEAD && git checkout -b test2 c && From 39d8e271f42e976a61f08a4f7bc2047a682ac532 Mon Sep 17 00:00:00 2001 From: Clemens Buchacher Date: Sun, 5 Apr 2009 02:47:00 +0200 Subject: [PATCH 35/55] simplify output of conflicting merge This simplifies the code without changing the semantics and removes the unhelpful "needs $sha1" part of the conflicting submodule message. Signed-off-by: Clemens Buchacher Signed-off-by: Junio C Hamano --- merge-recursive.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/merge-recursive.c b/merge-recursive.c index 3618c94bd2..9bf5cc7175 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -1121,22 +1121,13 @@ static int process_entry(struct merge_options *o, o->branch1, o->branch2); clean_merge = mfi.clean; - if (mfi.clean) - update_file(o, 1, mfi.sha, mfi.mode, path); - else if (S_ISGITLINK(mfi.mode)) { - output(o, 1, "CONFLICT (submodule): Merge conflict in %s " - "- needs %s", path, sha1_to_hex(b.sha1)); - update_file(o, 0, mfi.sha, mfi.mode, path); - } else { + if (!mfi.clean) { + if (S_ISGITLINK(mfi.mode)) + reason = "submodule"; output(o, 1, "CONFLICT (%s): Merge conflict in %s", reason, path); - - if (o->call_depth) - update_file(o, 0, mfi.sha, mfi.mode, path); - else - update_file_flags(o, mfi.sha, mfi.mode, path, - 0 /* update_cache */, 1 /* update_working_directory */); } + update_file(o, mfi.clean, mfi.sha, mfi.mode, path); } else if (!o_sha && !a_sha && !b_sha) { /* * this entry was deleted altogether. a_mode == 0 means From 4f6a32f8af9cceaf0c8ccf6d00d2f100dab5a6db Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 3 Apr 2009 15:28:56 -0400 Subject: [PATCH 36/55] commit: abort commit if interactive add failed Previously we ignored the result of calling add_interactive, which meant that if an error occurred we simply committed whatever happened to be in the index. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-commit.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builtin-commit.c b/builtin-commit.c index 6cbdd55f16..fde7b891d9 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -224,7 +224,8 @@ static char *prepare_index(int argc, const char **argv, const char *prefix) const char **pathspec = NULL; if (interactive) { - interactive_add(argc, argv, prefix); + if (interactive_add(argc, argv, prefix) != 0) + die("interactive add failed"); if (read_cache() < 0) die("index file corrupt"); commit_style = COMMIT_AS_IS; From 5dba35912474770d0df45ed801d78c4c9ed5e949 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 3 Apr 2009 15:31:10 -0400 Subject: [PATCH 37/55] tests: remove exit after test_done call test_done always exits, so this line is never executed. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/lib-git-svn.sh | 4 ---- t/lib-httpd.sh | 3 --- t/t4004-diff-rename-symlink.sh | 1 - t/t4011-diff-symlink.sh | 1 - t/t4023-diff-rename-typechange.sh | 1 - t/t4114-apply-typechange.sh | 1 - t/t4115-apply-symlink.sh | 1 - t/t4122-apply-symlink-inside.sh | 1 - t/t5503-tagfollow.sh | 1 - t/t5522-pull-symlink.sh | 1 - t/t5540-http-push.sh | 1 - t/t7005-editor.sh | 1 - t/t9200-git-cvsexportcommit.sh | 1 - t/t9400-git-cvsserver-server.sh | 2 -- t/t9401-git-cvsserver-crlf.sh | 2 -- t/t9500-gitweb-standalone-no-errors.sh | 1 - t/t9600-cvsimport.sh | 3 --- 17 files changed, 26 deletions(-) diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh index de384e6ac3..cdd7ccdd2a 100644 --- a/t/lib-git-svn.sh +++ b/t/lib-git-svn.sh @@ -7,7 +7,6 @@ if test -n "$NO_SVN_TESTS" then say 'skipping git svn tests, NO_SVN_TESTS defined' test_done - exit fi GIT_DIR=$PWD/.git @@ -19,7 +18,6 @@ if test $? -ne 1 then say 'skipping git svn tests, svn not found' test_done - exit fi svnrepo=$PWD/svnrepo @@ -43,7 +41,6 @@ then fi say "$err" test_done - exit fi rawsvnrepo="$svnrepo" @@ -144,7 +141,6 @@ require_svnserve () { then say 'skipping svnserve test. (set $SVNSERVE_PORT to enable)' test_done - exit fi } diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index 589aaf8214..cde659d14a 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -8,7 +8,6 @@ then say "skipping test, network testing disabled by default" say "(define GIT_TEST_HTTPD to enable)" test_done - exit fi HTTPD_PARA="" @@ -36,7 +35,6 @@ if ! test -x "$LIB_HTTPD_PATH" then say "skipping test, no web server found at '$LIB_HTTPD_PATH'" test_done - exit fi HTTPD_VERSION=`$LIB_HTTPD_PATH -v | \ @@ -50,7 +48,6 @@ then then say "skipping test, at least Apache version 2 is required" test_done - exit fi LIB_HTTPD_MODULE_PATH="$DEFAULT_HTTPD_MODULE_PATH" diff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh index 3db74443f8..a4da1196a9 100755 --- a/t/t4004-diff-rename-symlink.sh +++ b/t/t4004-diff-rename-symlink.sh @@ -16,7 +16,6 @@ if ! test_have_prereq SYMLINKS then say 'Symbolic links not supported, skipping tests.' test_done - exit fi test_expect_success \ diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh index 3a81309967..d7e327cc5b 100755 --- a/t/t4011-diff-symlink.sh +++ b/t/t4011-diff-symlink.sh @@ -13,7 +13,6 @@ if ! test_have_prereq SYMLINKS then say 'Symbolic links not supported, skipping tests.' test_done - exit fi cat > expected << EOF diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh index 5099862eba..9bdf6596d8 100755 --- a/t/t4023-diff-rename-typechange.sh +++ b/t/t4023-diff-rename-typechange.sh @@ -8,7 +8,6 @@ if ! test_have_prereq SYMLINKS then say 'Symbolic links not supported, skipping tests.' test_done - exit fi test_expect_success setup ' diff --git a/t/t4114-apply-typechange.sh b/t/t4114-apply-typechange.sh index 7dc35dea38..99ec13dd53 100755 --- a/t/t4114-apply-typechange.sh +++ b/t/t4114-apply-typechange.sh @@ -13,7 +13,6 @@ if ! test_have_prereq SYMLINKS then say 'Symbolic links not supported, skipping tests.' test_done - exit fi test_expect_success 'setup repository and commits' ' diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh index 1a3aea34ce..b852e58980 100755 --- a/t/t4115-apply-symlink.sh +++ b/t/t4115-apply-symlink.sh @@ -13,7 +13,6 @@ if ! test_have_prereq SYMLINKS then say 'Symbolic links not supported, skipping tests.' test_done - exit fi test_expect_success setup ' diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh index 8aad20bfcc..0d3c1d5dd5 100755 --- a/t/t4122-apply-symlink-inside.sh +++ b/t/t4122-apply-symlink-inside.sh @@ -7,7 +7,6 @@ if ! test_have_prereq SYMLINKS then say 'Symbolic links not supported, skipping tests.' test_done - exit fi lecho () { diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh index e75ccbcaeb..d5db75d826 100755 --- a/t/t5503-tagfollow.sh +++ b/t/t5503-tagfollow.sh @@ -8,7 +8,6 @@ case $(uname -s) in *MINGW*) say "GIT_DEBUG_SEND_PACK not supported - skipping tests" test_done - exit esac # End state of the repository: diff --git a/t/t5522-pull-symlink.sh b/t/t5522-pull-symlink.sh index d887eb6c1a..86bbd7d024 100755 --- a/t/t5522-pull-symlink.sh +++ b/t/t5522-pull-symlink.sh @@ -8,7 +8,6 @@ if ! test_have_prereq SYMLINKS then say 'Symbolic links not supported, skipping tests.' test_done - exit fi # The scenario we are building: diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh index c46592f03d..5fe479e1c2 100755 --- a/t/t5540-http-push.sh +++ b/t/t5540-http-push.sh @@ -17,7 +17,6 @@ if git http-push > /dev/null 2>&1 || [ $? -eq 128 ] then say "skipping test, USE_CURL_MULTI is not defined" test_done - exit fi . "$TEST_DIRECTORY"/lib-httpd.sh diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh index e83bc8fd89..b647957d75 100755 --- a/t/t7005-editor.sh +++ b/t/t7005-editor.sh @@ -92,7 +92,6 @@ if ! echo 'echo space > "$1"' > "e space.sh" then say "Skipping; FS does not support spaces in filenames" test_done - exit fi test_expect_success 'editor with a space' ' diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh index 995f60771a..36656923ac 100755 --- a/t/t9200-git-cvsexportcommit.sh +++ b/t/t9200-git-cvsexportcommit.sh @@ -11,7 +11,6 @@ if test $? -ne 1 then say 'skipping git cvsexportcommit tests, cvs not found' test_done - exit fi CVSROOT=$(pwd)/cvsroot diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh index 466240cd41..39185db6c9 100755 --- a/t/t9400-git-cvsserver-server.sh +++ b/t/t9400-git-cvsserver-server.sh @@ -15,12 +15,10 @@ if test $? -ne 1 then say 'skipping git-cvsserver tests, cvs not found' test_done - exit fi perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { say 'skipping git-cvsserver tests, Perl SQLite interface unavailable' test_done - exit } unset GIT_DIR GIT_CONFIG diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh index 8882230134..12e0e50822 100755 --- a/t/t9401-git-cvsserver-crlf.sh +++ b/t/t9401-git-cvsserver-crlf.sh @@ -51,12 +51,10 @@ if test $? -ne 1 then say 'skipping git-cvsserver tests, cvs not found' test_done - exit fi perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { say 'skipping git-cvsserver tests, Perl SQLite interface unavailable' test_done - exit } unset GIT_DIR GIT_CONFIG diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index 9ec5030a91..0bd332c493 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -68,7 +68,6 @@ gitweb_run () { perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || { say 'skipping gitweb tests, perl version is too old' test_done - exit } gitweb_init diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh index d2379e7f62..33eb51938d 100755 --- a/t/t9600-cvsimport.sh +++ b/t/t9600-cvsimport.sh @@ -14,7 +14,6 @@ if ! type cvs >/dev/null 2>&1 then say 'skipping cvsimport tests, cvs not found' test_done - exit fi cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'` @@ -24,12 +23,10 @@ case "$cvsps_version" in '') say 'skipping cvsimport tests, cvsps not found' test_done - exit ;; *) say 'skipping cvsimport tests, unsupported cvsps version' test_done - exit ;; esac From 38b7ccbe8c6615c709a4a69071c46593a1494952 Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Sat, 4 Apr 2009 12:35:22 +0200 Subject: [PATCH 38/55] doc/git-pack-refs: fix two grammar issues Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- Documentation/git-pack-refs.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/git-pack-refs.txt b/Documentation/git-pack-refs.txt index a5244d35f4..1ee99c208c 100644 --- a/Documentation/git-pack-refs.txt +++ b/Documentation/git-pack-refs.txt @@ -26,7 +26,7 @@ problem by stashing the refs in a single file, traditional `$GIT_DIR/refs` hierarchy, it is looked up in this file and used if found. -Subsequent updates to branches always creates new file under +Subsequent updates to branches always create new files under `$GIT_DIR/refs` hierarchy. A recommended practice to deal with a repository with too many @@ -35,7 +35,7 @@ occasionally run `git pack-refs \--prune`. Tags are by definition stationary and are not expected to change. Branch heads will be packed with the initial `pack-refs --all`, but only the currently active branch heads will become unpacked, -and next `pack-refs` (without `--all`) will leave them +and the next `pack-refs` (without `--all`) will leave them unpacked. From 835a3eea3e18695e29b1d4c3adca37d1caaa16ef Mon Sep 17 00:00:00 2001 From: Julien Danjou Date: Tue, 31 Mar 2009 17:50:12 +0200 Subject: [PATCH 39/55] git submodule: fix usage line Actually, you have to set the -b option after the add command. Signed-off-by: Julien Danjou Signed-off-by: Junio C Hamano --- git-submodule.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-submodule.sh b/git-submodule.sh index 0a27232b90..7c2e060ae7 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -5,7 +5,7 @@ # Copyright (c) 2007 Lars Hjemli USAGE="[--quiet] [--cached] \ -[add [-b branch] ]|[status|init|update [-i|--init] [-N|--no-fetch]|summary [-n|--summary-limit ] []] \ +[add [-b branch] ]|[status|init|update [-i|--init] [-N|--no-fetch]|summary [-n|--summary-limit ] []] \ [--] [...]|[foreach ]|[sync [--] [...]]" OPTIONS_SPEC= . git-sh-setup From 43acdf243ee8a8fa876bdd6659026fe5ed2d4c24 Mon Sep 17 00:00:00 2001 From: Todd Zullinger Date: Sun, 5 Apr 2009 12:33:38 -0400 Subject: [PATCH 40/55] bash completion: Update 'git am' options This adds --committer-date-is-author-date, --ignore-date, and --no-utf8 options. The --binary option is removed, as it was made a no-op by cb3a160. The option list is also sorted alphabetically. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e72ce2428d..d3d8203171 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -646,7 +646,8 @@ _git_am () ;; --*) __gitcomp " - --signoff --utf8 --binary --3way --interactive + --3way --committer-date-is-author-date --ignore-date + --interactive --keep --no-utf8 --signoff --utf8 --whitespace= " return From 1d1876e9300c56f399ea2976c5831674cd9818b1 Mon Sep 17 00:00:00 2001 From: Heiko Voigt Date: Wed, 1 Apr 2009 19:51:54 +0200 Subject: [PATCH 41/55] Add configuration variable for sign-off to format-patch If you regularly create patches which require a Signed-off: line you may want to make it your default to add that line. It also helps you not to forget to add the -s/--signoff switch. Signed-off-by: Heiko Voigt Signed-off-by: Junio C Hamano --- Documentation/config.txt | 7 +++++++ Documentation/git-format-patch.txt | 1 + builtin-log.c | 23 ++++++++++++++++------- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index ad22cb875e..27cb7f1408 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -715,6 +715,13 @@ format.thread:: A true boolean value is the same as `shallow`, and a false value disables threading. +format.signoff:: + A boolean value which lets you enable the `-s/--signoff` option of + format-patch by default. *Note:* Adding the Signed-off-by: line to a + patch should be a conscious act and means that you certify you have + the rights to submit this work under the same open source license. + Please see the 'SubmittingPatches' document for further discussion. + gc.aggressiveWindow:: The window size parameter used in the delta compression algorithm used by 'git-gc --aggressive'. This defaults diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index db3bdb5657..eb2fbcff1a 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -205,6 +205,7 @@ more than one. numbered = auto cc = attach [ = mime-boundary-string ] + signoff = true ------------ diff --git a/builtin-log.c b/builtin-log.c index 27bc0dce23..eb2c0541b5 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -465,6 +465,7 @@ static void add_header(const char *value) #define THREAD_SHALLOW 1 #define THREAD_DEEP 2 static int thread = 0; +static int do_signoff = 0; static int git_format_config(const char *var, const char *value, void *cb) { @@ -514,6 +515,10 @@ static int git_format_config(const char *var, const char *value, void *cb) thread = git_config_bool(var, value) && THREAD_SHALLOW; return 0; } + if (!strcmp(var, "format.signoff")) { + do_signoff = git_config_bool(var, value); + return 0; + } return git_log_config(var, value, cb); } @@ -865,13 +870,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) } else if (!strcmp(argv[i], "--signoff") || !strcmp(argv[i], "-s")) { - const char *committer; - const char *endpos; - committer = git_committer_info(IDENT_ERROR_ON_NO_NAME); - endpos = strchr(committer, '>'); - if (!endpos) - die("bogus committer info %s", committer); - add_signoff = xmemdupz(committer, endpos - committer + 1); + do_signoff = 1; } else if (!strcmp(argv[i], "--attach")) { rev.mime_boundary = git_version_string; @@ -925,6 +924,16 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) } argc = j; + if (do_signoff) { + const char *committer; + const char *endpos; + committer = git_committer_info(IDENT_ERROR_ON_NO_NAME); + endpos = strchr(committer, '>'); + if (!endpos) + die("bogus committer info %s", committer); + add_signoff = xmemdupz(committer, endpos - committer + 1); + } + for (i = 0; i < extra_hdr_nr; i++) { strbuf_addstr(&buf, extra_hdr[i]); strbuf_addch(&buf, '\n'); From 5c9c990341bad6b4087c2dd99d28a6cb910749df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Santi=20B=C3=A9jar?= Date: Mon, 30 Mar 2009 12:11:42 +0200 Subject: [PATCH 42/55] Documentation: branch.*.merge can also affect 'git-push' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Santi Béjar Signed-off-by: Junio C Hamano --- Documentation/config.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 27cb7f1408..3afd124749 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -480,7 +480,7 @@ branch..remote:: branch..merge:: Defines, together with branch..remote, the upstream branch for the given branch. It tells 'git-fetch'/'git-pull' which - branch to merge from. + branch to merge and can also affect 'git-push' (see push.default). When in branch , it tells 'git-fetch' the default refspec to be marked for merging in FETCH_HEAD. The value is handled like the remote part of a refspec, and must match a From 6127c0864798111d3524da2bea6188b0048d9fcf Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Sat, 4 Apr 2009 12:38:23 +0300 Subject: [PATCH 43/55] user-manual: remove some git-foo usage Also, use `git foo` when it make sense. Signed-off-by: Felipe Contreras Signed-off-by: Junio C Hamano --- Documentation/user-manual.txt | 148 +++++++++++++++++----------------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index e33b29b1dd..1bc4b7037a 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -188,7 +188,7 @@ As you can see, a commit shows who made the latest change, what they did, and why. Every commit has a 40-hexdigit id, sometimes called the "object name" or the -"SHA1 id", shown on the first line of the "git-show" output. You can usually +"SHA1 id", shown on the first line of the "git show" output. You can usually refer to a commit by a shorter name, such as a tag or a branch name, but this longer name can also be useful. Most importantly, it is a globally unique name for this commit: so if you tell somebody else the object name (for @@ -307,7 +307,7 @@ ref: refs/heads/master Examining an old version without creating a new branch ------------------------------------------------------ -The git-checkout command normally expects a branch head, but will also +The `git checkout` command normally expects a branch head, but will also accept an arbitrary commit; for example, you can check out the commit referenced by a tag: @@ -400,7 +400,7 @@ references with the same shorthand name, see the "SPECIFYING REVISIONS" section of linkgit:git-rev-parse[1]. [[Updating-a-repository-With-git-fetch]] -Updating a repository with git-fetch +Updating a repository with git fetch ------------------------------------ Eventually the developer cloned from will do additional work in her @@ -427,7 +427,7 @@ $ git fetch linux-nfs ------------------------------------------------- New remote-tracking branches will be stored under the shorthand name -that you gave "git-remote add", in this case linux-nfs: +that you gave "git remote add", in this case linux-nfs: ------------------------------------------------- $ git branch -r @@ -516,7 +516,7 @@ $ git bisect reset to return you to the branch you were on before. -Note that the version which git-bisect checks out for you at each +Note that the version which `git bisect` checks out for you at each point is just a suggestion, and you're free to try a different version if you think it would be a good idea. For example, occasionally you may land on a commit that broke something unrelated; @@ -592,11 +592,11 @@ In addition to HEAD, there are several other special names for commits: Merges (to be discussed later), as well as operations such as -git-reset, which change the currently checked-out commit, generally +`git reset`, which change the currently checked-out commit, generally set ORIG_HEAD to the value HEAD had before the current operation. -The git-fetch operation always stores the head of the last fetched -branch in FETCH_HEAD. For example, if you run git fetch without +The `git fetch` operation always stores the head of the last fetched +branch in FETCH_HEAD. For example, if you run `git fetch` without specifying a local branch as the target of the operation ------------------------------------------------- @@ -1073,9 +1073,9 @@ $ git diff shows the difference between the working tree and the index file. -Note that "git-add" always adds just the current contents of a file +Note that "git add" always adds just the current contents of a file to the index; further changes to the same file will be ignored unless -you run git-add on the file again. +you run `git add` on the file again. When you're ready, just run @@ -1136,7 +1136,7 @@ Ignoring files A project will often generate files that you do 'not' want to track with git. This typically includes files generated by a build process or temporary backup files made by your editor. Of course, 'not' tracking files with git -is just a matter of 'not' calling `git-add` on them. But it quickly becomes +is just a matter of 'not' calling `git add` on them. But it quickly becomes annoying to have these untracked files lying around; e.g. they make `git add .` practically useless, and they keep showing up in the output of `git status`. @@ -1349,7 +1349,7 @@ $ git add file.txt ------------------------------------------------- the different stages of that file will be "collapsed", after which -git-diff will (by default) no longer show diffs for that file. +`git diff` will (by default) no longer show diffs for that file. [[undoing-a-merge]] Undoing a merge @@ -1446,7 +1446,7 @@ Fixing a mistake by rewriting history If the problematic commit is the most recent commit, and you have not yet made that commit public, then you may just -<>. +<>. Alternatively, you can edit the working directory and update the index to fix your @@ -1474,7 +1474,7 @@ Checking out an old version of a file In the process of undoing a previous bad change, you may find it useful to check out an older version of a particular file using -linkgit:git-checkout[1]. We've used git-checkout before to switch +linkgit:git-checkout[1]. We've used `git checkout` before to switch branches, but it has quite different behavior if it is given a path name: the command @@ -1542,7 +1542,7 @@ $ git gc ------------------------------------------------- to recompress the archive. This can be very time-consuming, so -you may prefer to run git-gc when you are not doing other work. +you may prefer to run `git gc` when you are not doing other work. [[ensuring-reliability]] @@ -1634,7 +1634,7 @@ In some situations the reflog may not be able to save you. For example, suppose you delete a branch, then realize you need the history it contained. The reflog is also deleted; however, if you have not yet pruned the repository, then you may still be able to find the lost -commits in the dangling objects that git-fsck reports. See +commits in the dangling objects that `git fsck` reports. See <> for the details. ------------------------------------------------- @@ -1676,7 +1676,7 @@ Sharing development with others =============================== [[getting-updates-With-git-pull]] -Getting updates with git-pull +Getting updates with git pull ----------------------------- After you clone a repository and make a few changes of your own, you @@ -1722,7 +1722,7 @@ repository that you pulled from. <>; instead, your branch will just be updated to point to the latest commit from the upstream branch.) -The git-pull command can also be given "." as the "remote" repository, +The `git pull` command can also be given "." as the "remote" repository, in which case it just merges in a branch from the current repository; so the commands @@ -1795,7 +1795,7 @@ Public git repositories Another way to submit changes to a project is to tell the maintainer of that project to pull the changes from your repository using linkgit:git-pull[1]. In the section "<>" we described this as a way to get +Getting updates with `git pull`>>" we described this as a way to get updates from the "main" repository, but it works just as well in the other direction. @@ -1847,7 +1847,7 @@ Setting up a public repository ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Assume your personal repository is in the directory ~/proj. We -first create a new clone of the repository and tell git-daemon that it +first create a new clone of the repository and tell `git daemon` that it is meant to be public: ------------------------------------------------- @@ -1878,10 +1878,10 @@ repository>>", below. Otherwise, all you need to do is start linkgit:git-daemon[1]; it will listen on port 9418. By default, it will allow access to any directory that looks like a git directory and contains the magic file -git-daemon-export-ok. Passing some directory paths as git-daemon +git-daemon-export-ok. Passing some directory paths as `git daemon` arguments will further restrict the exports to those paths. -You can also run git-daemon as an inetd service; see the +You can also run `git daemon` as an inetd service; see the linkgit:git-daemon[1] man page for details. (See especially the examples section.) @@ -1942,7 +1942,7 @@ or just $ git push ssh://yourserver.com/~you/proj.git master ------------------------------------------------- -As with git-fetch, git-push will complain if this does not result in a +As with `git fetch`, `git push` will complain if this does not result in a <>; see the following section for details on handling this case. @@ -1952,7 +1952,7 @@ repository that has a checked-out working tree, but the working tree will not be updated by the push. This may lead to unexpected results if the branch you push to is the currently checked-out branch! -As with git-fetch, you may also set up configuration options to +As with `git fetch`, you may also set up configuration options to save typing; so, for example, after ------------------------------------------------- @@ -1988,13 +1988,13 @@ error: failed to push to 'ssh://yourserver.com/~you/proj.git' This can happen, for example, if you: - - use `git-reset --hard` to remove already-published commits, or - - use `git-commit --amend` to replace already-published commits + - use `git reset --hard` to remove already-published commits, or + - use `git commit --amend` to replace already-published commits (as in <>), or - - use `git-rebase` to rebase any already-published commits (as + - use `git rebase` to rebase any already-published commits (as in <>). -You may force git-push to perform the update anyway by preceding the +You may force `git push` to perform the update anyway by preceding the branch name with a plus sign: ------------------------------------------------- @@ -2036,7 +2036,7 @@ advantages over the central shared repository: - Git's ability to quickly import and merge patches allows a single maintainer to process incoming changes even at very - high rates. And when that becomes too much, git-pull provides + high rates. And when that becomes too much, `git pull` provides an easy way for that maintainer to delegate this job to other maintainers while still allowing optional review of incoming changes. @@ -2404,7 +2404,7 @@ use them, and then explain some of the problems that can arise because you are rewriting history. [[using-git-rebase]] -Keeping a patch series up to date using git-rebase +Keeping a patch series up to date using git rebase -------------------------------------------------- Suppose that you create a branch "mywork" on a remote-tracking branch @@ -2468,9 +2468,9 @@ patches to the new mywork. The result will look like: ................................................ In the process, it may discover conflicts. In that case it will stop -and allow you to fix the conflicts; after fixing conflicts, use "git-add" +and allow you to fix the conflicts; after fixing conflicts, use `git add` to update the index with those contents, and then, instead of -running git-commit, just run +running `git commit`, just run ------------------------------------------------- $ git rebase --continue @@ -2508,7 +2508,7 @@ with $ git tag bad mywork~5 ------------------------------------------------- -(Either gitk or git-log may be useful for finding the commit.) +(Either gitk or `git log` may be useful for finding the commit.) Then check out that commit, edit it, and rebase the rest of the series on top of it (note that we could check out the commit on a temporary @@ -2549,12 +2549,12 @@ $ gitk origin..mywork & and browse through the list of patches in the mywork branch using gitk, applying them (possibly in a different order) to mywork-new using -cherry-pick, and possibly modifying them as you go using `commit --amend`. +cherry-pick, and possibly modifying them as you go using `git commit --amend`. The linkgit:git-gui[1] command may also help as it allows you to individually select diff hunks for inclusion in the index (by right-clicking on the diff hunk and choosing "Stage Hunk for Commit"). -Another technique is to use git-format-patch to create a series of +Another technique is to use `git format-patch` to create a series of patches, then reset the state to before the patches: ------------------------------------------------- @@ -2662,7 +2662,7 @@ you know is that D is bad, that Z is good, and that linkgit:git-bisect[1] identifies C as the culprit, how will you figure out that the problem is due to this change in semantics? -When the result of a git-bisect is a non-merge commit, you should +When the result of a `git bisect` is a non-merge commit, you should normally be able to discover the problem by examining just that commit. Developers can make this easy by breaking their changes into small self-contained commits. That won't help in the case above, however, @@ -2725,7 +2725,7 @@ master branch. In more detail: git fetch and fast-forwards --------------------------- -In the previous example, when updating an existing branch, "git-fetch" +In the previous example, when updating an existing branch, "git fetch" checks to make sure that the most recent commit on the remote branch is a descendant of the most recent commit on your copy of the branch before updating your copy of the branch to point at the new @@ -2751,7 +2751,7 @@ resulting in a situation like: o--o--o <-- new head of the branch ................................................ -In this case, "git-fetch" will fail, and print out a warning. +In this case, "git fetch" will fail, and print out a warning. In that case, you can still force git to update to the new head, as described in the following section. However, note that in the @@ -2760,7 +2760,7 @@ unless you've already created a reference of your own pointing to them. [[forcing-fetch]] -Forcing git-fetch to do non-fast-forward updates +Forcing git fetch to do non-fast-forward updates ------------------------------------------------ If git fetch fails because the new head of a branch is not a @@ -3131,7 +3131,7 @@ $ git prune to remove any of the "loose" objects that are now contained in the pack. This will also remove any unreferenced objects (which may be -created when, for example, you use "git-reset" to remove a commit). +created when, for example, you use "git reset" to remove a commit). You can verify that the loose objects are gone by looking at the .git/objects directory or by running @@ -3160,7 +3160,7 @@ branch still exists, as does everything it pointed to. The branch pointer itself just doesn't, since you replaced it with another one. There are also other situations that cause dangling objects. For -example, a "dangling blob" may arise because you did a "git-add" of a +example, a "dangling blob" may arise because you did a "git add" of a file, but then, before you actually committed it and made it part of the bigger picture, you changed something else in that file and committed that *updated* thing--the old state that you added originally ends up @@ -3210,7 +3210,7 @@ Usually, dangling blobs and trees aren't very interesting. They're almost always the result of either being a half-way mergebase (the blob will often even have the conflict markers from a merge in it, if you have had conflicting merges that you fixed up by hand), or simply -because you interrupted a "git-fetch" with ^C or something like that, +because you interrupted a "git fetch" with ^C or something like that, leaving _some_ of the new objects in the object database, but just dangling and useless. @@ -3225,9 +3225,9 @@ and they'll be gone. But you should only run "git prune" on a quiescent repository--it's kind of like doing a filesystem fsck recovery: you don't want to do that while the filesystem is mounted. -(The same is true of "git-fsck" itself, btw, but since -git-fsck never actually *changes* the repository, it just reports -on what it found, git-fsck itself is never "dangerous" to run. +(The same is true of "git fsck" itself, btw, but since +`git fsck` never actually *changes* the repository, it just reports +on what it found, `git fsck` itself is never 'dangerous' to run. Running it while somebody is actually changing the repository can cause confusing and scary messages, but it won't actually do anything bad. In contrast, running "git prune" while somebody is actively changing the @@ -3489,14 +3489,14 @@ done NOTE: Do not use local URLs here if you plan to publish your superproject! -See what files `git-submodule` created: +See what files `git submodule` created: ------------------------------------------------- $ ls -a . .. .git .gitmodules a b c d ------------------------------------------------- -The `git-submodule add ` command does a couple of things: +The `git submodule add ` command does a couple of things: - It clones the submodule from to the given under the current directory and by default checks out the master branch. @@ -3542,7 +3542,7 @@ init` to add the submodule repository URLs to `.git/config`: $ git submodule init ------------------------------------------------- -Now use `git-submodule update` to clone the repositories and check out the +Now use `git submodule update` to clone the repositories and check out the commits specified in the superproject: ------------------------------------------------- @@ -3552,8 +3552,8 @@ $ ls -a . .. .git a.txt ------------------------------------------------- -One major difference between `git-submodule update` and `git-submodule add` is -that `git-submodule update` checks out a specific commit, rather than the tip +One major difference between `git submodule update` and `git submodule add` is +that `git submodule update` checks out a specific commit, rather than the tip of a branch. It's like checking out a tag: the head is detached, so you're not working on a branch. @@ -3769,7 +3769,7 @@ You update your working directory from the index by "checking out" files. This is not a very common operation, since normally you'd just keep your files updated, and rather than write to your working directory, you'd tell the index files about the changes in your -working directory (i.e. `git-update-index`). +working directory (i.e. `git update-index`). However, if you decide to jump to a new version, or check out somebody else's version, or just restore a previous tree, you'd populate your @@ -3782,7 +3782,7 @@ $ git checkout-index filename or, if you want to check out all of the index, use `-a`. -NOTE! git-checkout-index normally refuses to overwrite old files, so +NOTE! `git checkout-index` normally refuses to overwrite old files, so if you have an old version of the tree already checked out, you will need to use the "-f" flag ('before' the "-a" flag or the filename) to 'force' the checkout. @@ -3820,7 +3820,7 @@ $ git commit-tree -p [-p ..] and then giving the reason for the commit on stdin (either through redirection from a pipe or file, or by just typing it at the tty). -git-commit-tree will return the name of the object that represents +`git commit-tree` will return the name of the object that represents that commit, and you should save it away for later use. Normally, you'd commit a new `HEAD` state, and while git doesn't care where you save the note about that state, in practice we tend to just write the @@ -3889,7 +3889,7 @@ $ git cat-file blob|tree|commit|tag to show its contents. NOTE! Trees have binary content, and as a result there is a special helper for showing that content, called -`git-ls-tree`, which turns the binary content into a more easily +`git ls-tree`, which turns the binary content into a more easily readable form. It's especially instructive to look at "commit" objects, since those @@ -3984,7 +3984,7 @@ came from: stage 1 corresponds to `$orig` tree, stage 2 `HEAD` tree, and stage3 `$target` tree. Earlier we said that trivial merges are done inside -`git-read-tree -m`. For example, if the file did not change +`git read-tree -m`. For example, if the file did not change from `$orig` to `HEAD` nor `$target`, or if the file changed from `$orig` to `HEAD` and `$orig` to `$target` the same way, obviously the final outcome is what is in `HEAD`. What the @@ -4011,20 +4011,20 @@ $ mv -f hello.c~2 hello.c $ git update-index hello.c ------------------------------------------------- -When a path is in the "unmerged" state, running `git-update-index` for +When a path is in the "unmerged" state, running `git update-index` for that path tells git to mark the path resolved. The above is the description of a git merge at the lowest level, to help you understand what conceptually happens under the hood. -In practice, nobody, not even git itself, runs `git-cat-file` three times -for this. There is a `git-merge-index` program that extracts the +In practice, nobody, not even git itself, runs `git cat-file` three times +for this. There is a `git merge-index` program that extracts the stages to temporary files and calls a "merge" script on it: ------------------------------------------------- $ git merge-index git-merge-one-file hello.c ------------------------------------------------- -and that is what higher level `git-merge -s resolve` is implemented with. +and that is what higher level `git merge -s resolve` is implemented with. [[hacking-git]] Hacking git @@ -4061,7 +4061,7 @@ size> {plus} {plus} . The structured objects can further have their structure and connectivity to other objects verified. This is generally done with -the `git-fsck` program, which generates a full dependency graph +the `git fsck` program, which generates a full dependency graph of all objects, and verifies their internal consistency (in addition to just verifying their superficial consistency through the hash). @@ -4120,7 +4120,7 @@ functions like `get_sha1_basic()` or the likes. This is just to get you into the groove for the most libified part of Git: the revision walker. -Basically, the initial version of `git-log` was a shell script: +Basically, the initial version of `git log` was a shell script: ---------------------------------------------------------------- $ git-rev-list --pretty $(git-rev-parse --default HEAD "$@") | \ @@ -4129,20 +4129,20 @@ $ git-rev-list --pretty $(git-rev-parse --default HEAD "$@") | \ What does this mean? -`git-rev-list` is the original version of the revision walker, which +`git rev-list` is the original version of the revision walker, which _always_ printed a list of revisions to stdout. It is still functional, and needs to, since most new Git programs start out as scripts using -`git-rev-list`. +`git rev-list`. -`git-rev-parse` is not as important any more; it was only used to filter out +`git rev-parse` is not as important any more; it was only used to filter out options that were relevant for the different plumbing commands that were called by the script. -Most of what `git-rev-list` did is contained in `revision.c` and +Most of what `git rev-list` did is contained in `revision.c` and `revision.h`. It wraps the options in a struct named `rev_info`, which controls how and what revisions are walked, and more. -The original job of `git-rev-parse` is now taken by the function +The original job of `git rev-parse` is now taken by the function `setup_revisions()`, which parses the revisions and the common command line options for the revision walker. This information is stored in the struct `rev_info` for later consumption. You can do your own command line option @@ -4155,7 +4155,7 @@ just have a look at the first implementation of `cmd_log()`; call `git show v1.3.0{tilde}155^2{tilde}4` and scroll down to that function (note that you no longer need to call `setup_pager()` directly). -Nowadays, `git-log` is a builtin, which means that it is _contained_ in the +Nowadays, `git log` is a builtin, which means that it is _contained_ in the command `git`. The source side of a builtin is - a function called `cmd_`, typically defined in `builtin-.c`, @@ -4171,7 +4171,7 @@ since they share quite a bit of code. In that case, the commands which are _not_ named like the `.c` file in which they live have to be listed in `BUILT_INS` in the `Makefile`. -`git-log` looks more complicated in C than it does in the original script, +`git log` looks more complicated in C than it does in the original script, but that allows for a much greater flexibility and performance. Here again it is a good point to take a pause. @@ -4182,9 +4182,9 @@ the organization of Git (after you know the basic concepts). So, think about something which you are interested in, say, "how can I access a blob just knowing the object name of it?". The first step is to find a Git command with which you can do it. In this example, it is either -`git-show` or `git-cat-file`. +`git show` or `git cat-file`. -For the sake of clarity, let's stay with `git-cat-file`, because it +For the sake of clarity, let's stay with `git cat-file`, because it - is plumbing, and @@ -4198,7 +4198,7 @@ it does. ------------------------------------------------------------------ git_config(git_default_config); if (argc != 3) - usage("git-cat-file [-t|-s|-e|-p|] "); + usage("git cat-file [-t|-s|-e|-p|] "); if (get_sha1(argv[2], sha1)) die("Not a valid object name %s", argv[2]); ------------------------------------------------------------------ @@ -4243,10 +4243,10 @@ To find out how the result can be used, just read on in `cmd_cat_file()`: ----------------------------------- Sometimes, you do not know where to look for a feature. In many such cases, -it helps to search through the output of `git log`, and then `git-show` the +it helps to search through the output of `git log`, and then `git show` the corresponding commit. -Example: If you know that there was some test case for `git-bundle`, but +Example: If you know that there was some test case for `git bundle`, but do not remember where it was (yes, you _could_ `git grep bundle t/`, but that does not illustrate the point!): @@ -4530,7 +4530,7 @@ The basic requirements: - Whenever possible, section headings should clearly describe the task they explain how to do, in language that requires no more knowledge than necessary: for example, "importing patches into a project" rather - than "the git-am command" + than "the `git am` command" Think about how to create a clear chapter dependency graph that will allow people to get to important topics without necessarily reading From a3df1e464e110776120e37f467cc0ad3b957a80e Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Sat, 4 Apr 2009 12:38:24 +0300 Subject: [PATCH 44/55] docbook: change css style A handful of random personal preference: - Force sans-serif for the text. - Quote code sample literal inside a single-quote pair. - Show emphasis in blue-green italics. - Do not use itarlics for term definition, but show them in navy. Signed-off-by: Felipe Contreras Signed-off-by: Junio C Hamano --- Documentation/docbook-xsl.css | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Documentation/docbook-xsl.css b/Documentation/docbook-xsl.css index b878b385c6..e11c8f053a 100644 --- a/Documentation/docbook-xsl.css +++ b/Documentation/docbook-xsl.css @@ -16,6 +16,7 @@ body blockquote { html body { margin: 1em 5% 1em 5%; line-height: 1.2; + font-family: sans-serif; } body div { @@ -128,6 +129,15 @@ body pre { tt.literal, code.literal { color: navy; + font-family: sans-serif; +} + +code.literal:before { content: "'"; } +code.literal:after { content: "'"; } + +em { + font-style: italic; + color: #064; } div.literallayout p { @@ -137,7 +147,6 @@ div.literallayout p { div.literallayout { font-family: monospace; -# margin: 0.5em 10% 0.5em 1em; margin: 0em; color: navy; border: 1px solid silver; @@ -187,7 +196,8 @@ dt { } dt span.term { - font-style: italic; + font-style: normal; + color: navy; } div.variablelist dd p { From a6e5ef7d9cc34199fb6618e1fbb57f3615d002f6 Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Sat, 4 Apr 2009 12:38:27 +0300 Subject: [PATCH 45/55] user-manual: the name of the hash function is SHA-1, not sha1 Signed-off-by: Felipe Contreras Signed-off-by: Junio C Hamano --- Documentation/user-manual.txt | 50 +++++++++++++++++------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 1bc4b7037a..dbbeb7e7c7 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -188,7 +188,7 @@ As you can see, a commit shows who made the latest change, what they did, and why. Every commit has a 40-hexdigit id, sometimes called the "object name" or the -"SHA1 id", shown on the first line of the "git show" output. You can usually +"SHA-1 id", shown on the first line of the "git show" output. You can usually refer to a commit by a shorter name, such as a tag or a branch name, but this longer name can also be useful. Most importantly, it is a globally unique name for this commit: so if you tell somebody else the object name (for @@ -320,7 +320,7 @@ If you want to create a new branch from this checkout, you may do so HEAD is now at 427abfa... Linux v2.6.17 ------------------------------------------------ -The HEAD then refers to the SHA1 of the commit instead of to a branch, +The HEAD then refers to the SHA-1 of the commit instead of to a branch, and git branch shows that you are no longer on a branch: ------------------------------------------------ @@ -739,7 +739,7 @@ $ git log --pretty=oneline origin..mybranch | wc -l ------------------------------------------------- Alternatively, you may often see this sort of thing done with the -lower-level command linkgit:git-rev-list[1], which just lists the SHA1's +lower-level command linkgit:git-rev-list[1], which just lists the SHA-1's of all the given commits: ------------------------------------------------- @@ -2865,8 +2865,8 @@ The Object Database We already saw in <> that all commits are stored under a 40-digit "object name". In fact, all the information needed to represent the history of a project is stored in objects with such names. -In each case the name is calculated by taking the SHA1 hash of the -contents of the object. The SHA1 hash is a cryptographic hash function. +In each case the name is calculated by taking the SHA-1 hash of the +contents of the object. The SHA-1 hash is a cryptographic hash function. What that means to us is that it is impossible to find two different objects with the same name. This has a number of advantages; among others: @@ -2877,10 +2877,10 @@ others: same content stored in two repositories will always be stored under the same name. - Git can detect errors when it reads an object, by checking that the - object's name is still the SHA1 hash of its contents. + object's name is still the SHA-1 hash of its contents. (See <> for the details of the object formatting and -SHA1 calculation.) +SHA-1 calculation.) There are four different types of objects: "blob", "tree", "commit", and "tag". @@ -2926,9 +2926,9 @@ committer Junio C Hamano 1187591163 -0700 As you can see, a commit is defined by: -- a tree: The SHA1 name of a tree object (as defined below), representing +- a tree: The SHA-1 name of a tree object (as defined below), representing the contents of a directory at a certain point in time. -- parent(s): The SHA1 name of some number of commits which represent the +- parent(s): The SHA-1 name of some number of commits which represent the immediately previous step(s) in the history of the project. The example above has one parent; merge commits may have more than one. A commit with no parents is called a "root" commit, and @@ -2977,13 +2977,13 @@ $ git ls-tree fb3a8bdd0ce ------------------------------------------------ As you can see, a tree object contains a list of entries, each with a -mode, object type, SHA1 name, and name, sorted by name. It represents +mode, object type, SHA-1 name, and name, sorted by name. It represents the contents of a single directory tree. The object type may be a blob, representing the contents of a file, or another tree, representing the contents of a subdirectory. Since trees -and blobs, like all other objects, are named by the SHA1 hash of their -contents, two trees have the same SHA1 name if and only if their +and blobs, like all other objects, are named by the SHA-1 hash of their +contents, two trees have the same SHA-1 name if and only if their contents (including, recursively, the contents of all subdirectories) are identical. This allows git to quickly determine the differences between two related tree objects, since it can ignore any entries with @@ -3029,15 +3029,15 @@ currently checked out. Trust ~~~~~ -If you receive the SHA1 name of a blob from one source, and its contents +If you receive the SHA-1 name of a blob from one source, and its contents from another (possibly untrusted) source, you can still trust that those -contents are correct as long as the SHA1 name agrees. This is because -the SHA1 is designed so that it is infeasible to find different contents +contents are correct as long as the SHA-1 name agrees. This is because +the SHA-1 is designed so that it is infeasible to find different contents that produce the same hash. -Similarly, you need only trust the SHA1 name of a top-level tree object +Similarly, you need only trust the SHA-1 name of a top-level tree object to trust the contents of the entire directory that it refers to, and if -you receive the SHA1 name of a commit from a trusted source, then you +you receive the SHA-1 name of a commit from a trusted source, then you can easily verify the entire history of commits reachable through parents of that commit, and all of those contents of the trees referred to by those commits. @@ -3049,7 +3049,7 @@ that you trust that commit, and the immutability of the history of commits tells others that they can trust the whole history. In other words, you can easily validate a whole archive by just -sending out a single email that tells the people the name (SHA1 hash) +sending out a single email that tells the people the name (SHA-1 hash) of the top commit, and digitally sign that email using something like GPG/PGP. @@ -3090,7 +3090,7 @@ How git stores objects efficiently: pack files ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Newly created objects are initially created in a file named after the -object's SHA1 hash (stored in .git/objects). +object's SHA-1 hash (stored in .git/objects). Unfortunately this system becomes inefficient once a project has a lot of objects. Try this on an old project: @@ -3297,7 +3297,7 @@ $ git hash-object -w somedirectory/myfile ------------------------------------------------ which will create and store a blob object with the contents of -somedirectory/myfile, and output the sha1 of that object. if you're +somedirectory/myfile, and output the SHA-1 of that object. if you're extremely lucky it might be 4b9458b3786228369c63936db65827de3cc06200, in which case you've guessed right, and the corruption is fixed! @@ -3359,7 +3359,7 @@ The index ----------- The index is a binary file (generally kept in .git/index) containing a -sorted list of path names, each with permissions and the SHA1 of a blob +sorted list of path names, each with permissions and the SHA-1 of a blob object; linkgit:git-ls-files[1] can show you the contents of the index: ------------------------------------------------- @@ -3754,7 +3754,7 @@ unsaved state that you might want to restore later!) your current index. Normal operation is just ------------------------------------------------- -$ git read-tree +$ git read-tree ------------------------------------------------- and your index file will now be equivalent to the tree that you saved @@ -3978,7 +3978,7 @@ $ git ls-files --unmerged ------------------------------------------------ Each line of the `git ls-files --unmerged` output begins with -the blob mode bits, blob SHA1, 'stage number', and the +the blob mode bits, blob SHA-1, 'stage number', and the filename. The 'stage number' is git's way to say which tree it came from: stage 1 corresponds to `$orig` tree, stage 2 `HEAD` tree, and stage3 `$target` tree. @@ -4045,12 +4045,12 @@ objects). There are currently four different object types: "blob", Regardless of object type, all objects share the following characteristics: they are all deflated with zlib, and have a header that not only specifies their type, but also provides size information -about the data in the object. It's worth noting that the SHA1 hash +about the data in the object. It's worth noting that the SHA-1 hash that is used to name the object is the hash of the original data plus this header, so `sha1sum` 'file' does not match the object name for 'file'. (Historical note: in the dawn of the age of git the hash -was the sha1 of the 'compressed' object.) +was the SHA-1 of the 'compressed' object.) As a result, the general consistency of an object can always be tested independently of the contents or the type of the object: all objects can From ce8936c342a15a08029c71fdc92e283e3624bc18 Mon Sep 17 00:00:00 2001 From: Matthieu Moy Date: Mon, 6 Apr 2009 22:45:21 +0200 Subject: [PATCH 46/55] git-checkout.txt: fix incorrect statement about HEAD and index The command "git checkout" checks out from the index by default, not HEAD (the introducing comment were correct, but the detailled explanation added below were not). Signed-off-by: Matthieu Moy Signed-off-by: Junio C Hamano --- Documentation/git-checkout.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 5aa69c0e12..883047982a 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -145,8 +145,8 @@ $ git checkout hello.c <3> ------------ + <1> switch branch -<2> take out a file out of other commit -<3> restore hello.c from HEAD of current branch +<2> take a file out of another commit +<3> restore hello.c from the index + If you have an unfortunate branch that is named `hello.c`, this step would be confused as an instruction to switch to that branch. From 39470cf961e584ddb505f51bb9c452ebcfd5c569 Mon Sep 17 00:00:00 2001 From: Matthieu Moy Date: Tue, 7 Apr 2009 14:43:53 +0200 Subject: [PATCH 47/55] git-checkout.txt: clarify that applies when no path is given. Otherwise, the sentence "Defaults to HEAD." can be mis-read to mean that "git checkout -- hello.c" checks-out from HEAD. Signed-off-by: Matthieu Moy Signed-off-by: Junio C Hamano --- Documentation/git-checkout.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 883047982a..19510de151 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -85,9 +85,13 @@ should result in deletion of the path). :: Name for the new branch. +:: + Tree to checkout from (when paths are given). If not specified, + the index will be used. + :: - Branch to checkout; may be any object ID that resolves to a - commit. Defaults to HEAD. + Branch to checkout (when no paths are given); may be any object + ID that resolves to a commit. Defaults to HEAD. + When this parameter names a non-branch (but still a valid commit object), your HEAD becomes 'detached'. From 20ff3ec28e1dc7b653bfdc7643cb55045d7913fc Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Mon, 6 Apr 2009 11:03:36 -0400 Subject: [PATCH 48/55] Documentation: clarify .gitattributes search Use the term "toplevel of the work tree" in gitattributes.txt and gitignore.txt to define the limits of the search for those files. Signed-off-by: Jason Merrill Signed-off-by: Junio C Hamano --- Documentation/gitattributes.txt | 6 +++--- Documentation/gitignore.txt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 37fff208ff..c4aebc4351 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -59,9 +59,9 @@ attribute. When deciding what attributes are assigned to a path, git consults `$GIT_DIR/info/attributes` file (which has the highest precedence), `.gitattributes` file in the same directory as the -path in question, and its parent directories (the further the -directory that contains `.gitattributes` is from the path in -question, the lower its precedence). +path in question, and its parent directories up to the toplevel of the +work tree (the further the directory that contains `.gitattributes` +is from the path in question, the lower its precedence). If you wish to affect only a single repository (i.e., to assign attributes to files that are particular to one user's workflow), then diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt index 59321a2e82..7df3cef46f 100644 --- a/Documentation/gitignore.txt +++ b/Documentation/gitignore.txt @@ -31,8 +31,8 @@ precedence, the last matching pattern decides the outcome): * Patterns read from a `.gitignore` file in the same directory as the path, or in any parent directory, with patterns in the - higher level files (up to the root) being overridden by those in - lower level files down to the directory containing the file. + higher level files (up to the toplevel of the work tree) being overridden + by those in lower level files down to the directory containing the file. These patterns match relative to the location of the `.gitignore` file. A project normally includes such `.gitignore` files in its repository, containing patterns for From 4c0fe0af68fdfc2d328992b8cadc953159f0ea30 Mon Sep 17 00:00:00 2001 From: Matthieu Moy Date: Tue, 7 Apr 2009 19:24:32 +0200 Subject: [PATCH 49/55] git-send-email.txt: clarify which options take an argument. Signed-off-by: Matthieu Moy Signed-off-by: Junio C Hamano --- Documentation/git-send-email.txt | 34 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 10dfd667b2..0b1f183ce8 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -39,13 +39,13 @@ OPTIONS Composing ~~~~~~~~~ ---bcc:: +--bcc=
:: Specify a "Bcc:" value for each email. Default is the value of 'sendemail.bcc'. + The --bcc option must be repeated for each user you want on the bcc list. ---cc:: +--cc=
:: Specify a starting "Cc:" value for each email. Default is the value of 'sendemail.cc'. + @@ -68,24 +68,24 @@ and In-Reply-To headers will be used unless they are removed. + Missing From or In-Reply-To headers will be prompted for. ---from:: +--from=
:: Specify the sender of the emails. This will default to the value GIT_COMMITTER_IDENT, as returned by "git var -l". The user will still be prompted to confirm this entry. ---in-reply-to:: +--in-reply-to=:: Specify the contents of the first In-Reply-To header. Subsequent emails will refer to the previous email instead of this if --chain-reply-to is set (the default) Only necessary if --compose is also set. If --compose is not set, this will be prompted for. ---subject:: +--subject=:: Specify the initial subject of the email thread. Only necessary if --compose is also set. If --compose is not set, this will be prompted for. ---to:: +--to=
:: Specify the primary recipient of the emails generated. Generally, this will be the upstream maintainer of the project involved. Default is the value of the 'sendemail.to' configuration value; if that is unspecified, @@ -97,7 +97,7 @@ The --to option must be repeated for each user you want on the to list. Sending ~~~~~~~ ---envelope-sender:: +--envelope-sender=
:: Specify the envelope sender used to send the emails. This is useful if your default address is not the address that is subscribed to a list. If you use the sendmail binary, you must have @@ -105,12 +105,12 @@ Sending the 'sendemail.envelopesender' configuration variable; if that is unspecified, choosing the envelope sender is left to your MTA. ---smtp-encryption:: +--smtp-encryption=:: Specify the encryption to use, either 'ssl' or 'tls'. Any other value reverts to plain SMTP. Default is the value of 'sendemail.smtpencryption'. ---smtp-pass:: +--smtp-pass[=]:: Password for SMTP-AUTH. The argument is optional: If no argument is specified, then the empty string is used as the password. Default is the value of 'sendemail.smtppass', @@ -122,7 +122,7 @@ or on the command line. If a username has been specified (with specified (with '--smtp-pass' or 'sendemail.smtppass'), then the user is prompted for a password while the input is masked for privacy. ---smtp-server:: +--smtp-server=:: If set, specifies the outgoing SMTP server to use (e.g. `smtp.example.com` or a raw IP address). Alternatively it can specify a full pathname of a sendmail-like program instead; @@ -132,7 +132,7 @@ user is prompted for a password while the input is masked for privacy. `/usr/lib/sendmail` if such program is available, or `localhost` otherwise. ---smtp-server-port:: +--smtp-server-port=:: Specifies a port different from the default port (SMTP servers typically listen to smtp port 25 and ssmtp port 465). This can be set with 'sendemail.smtpserverport'. @@ -140,7 +140,7 @@ user is prompted for a password while the input is masked for privacy. --smtp-ssl:: Legacy alias for '--smtp-encryption ssl'. ---smtp-user:: +--smtp-user=:: Username for SMTP-AUTH. Default is the value of 'sendemail.smtpuser'; if a username is not specified (with '--smtp-user' or 'sendemail.smtpuser'), then authentication is not attempted. @@ -149,13 +149,13 @@ user is prompted for a password while the input is masked for privacy. Automating ~~~~~~~~~~ ---cc-cmd:: +--cc-cmd=:: Specify a command to execute once per patch file which should generate patch file specific "Cc:" entries. Output of this command must be single email address per line. Default is the value of 'sendemail.cccmd' configuration value. ---[no-]chain-reply-to:: +--[no-]chain-reply-to=:: If this is set, each email will be sent as a reply to the previous email sent. If disabled with "--no-chain-reply-to", all emails after the first will be sent as replies to the first email sent. When using @@ -163,7 +163,7 @@ Automating entire patch series. Default is the value of the 'sendemail.chainreplyto' configuration value; if that is unspecified, default to --chain-reply-to. ---identity:: +--identity=:: A configuration identity. When given, causes values in the 'sendemail.' subsection to take precedence over values in the 'sendemail' section. The default identity is @@ -174,7 +174,7 @@ Automating cc list. Default is the value of 'sendemail.signedoffbycc' configuration value; if that is unspecified, default to --signed-off-by-cc. ---suppress-cc:: +--suppress-cc=:: Specify an additional category of recipients to suppress the auto-cc of: + @@ -211,7 +211,7 @@ specified, as well as 'body' if --no-signed-off-cc is specified. Administering ~~~~~~~~~~~~~ ---confirm:: +--confirm=:: Confirm just before sending: + -- From 982962ce24c7d614d94f40de54cfcfdf71dcb0d2 Mon Sep 17 00:00:00 2001 From: Matthieu Moy Date: Tue, 7 Apr 2009 19:24:33 +0200 Subject: [PATCH 50/55] git-rev-list.txt: make ascii markup uniform with other pages. Other pages use --option=, not --option='argument', do the same here. Signed-off-by: Matthieu Moy Signed-off-by: Junio C Hamano --- Documentation/rev-list-options.txt | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 7dd237c2f6..11eec941df 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -140,38 +140,38 @@ limiting may be applied. -- -n 'number':: ---max-count='number':: +--max-count=:: Limit the number of commits output. ---skip='number':: +--skip=:: Skip 'number' commits before starting to show the commit output. ---since='date':: ---after='date':: +--since=:: +--after=:: Show commits more recent than a specific date. ---until='date':: ---before='date':: +--until=:: +--before=:: Show commits older than a specific date. ifdef::git-rev-list[] ---max-age='timestamp':: ---min-age='timestamp':: +--max-age=:: +--min-age=:: Limit the commits output to specified time range. endif::git-rev-list[] ---author='pattern':: ---committer='pattern':: +--author=:: +--committer=:: Limit the commits output to ones with author/committer header lines that match the specified pattern (regular expression). ---grep='pattern':: +--grep=:: Limit the commits output to ones with log message that matches the specified pattern (regular expression). From 714fddf2fc50cd5abb11d75837e6340edb9601f6 Mon Sep 17 00:00:00 2001 From: Jari Aalto Date: Sat, 21 Mar 2009 11:00:54 +0200 Subject: [PATCH 51/55] Change double quotes to single quotes in message Most of the time when we give branch name in the message, we quote it inside a pair of single-quotes. git-checkout uses double-quotes; this patch corrects the inconsistency. Signed-off-by: Jari Aalto Signed-off-by: Junio C Hamano --- builtin-checkout.c | 6 +++--- t/t7201-co.sh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/builtin-checkout.c b/builtin-checkout.c index 20b34ce6e1..ed0f318c0c 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -501,10 +501,10 @@ static void update_refs_for_switch(struct checkout_opts *opts, create_symref("HEAD", new->path, msg.buf); if (!opts->quiet) { if (old->path && !strcmp(new->path, old->path)) - fprintf(stderr, "Already on \"%s\"\n", + fprintf(stderr, "Already on '%s'\n", new->name); else - fprintf(stderr, "Switched to%s branch \"%s\"\n", + fprintf(stderr, "Switched to%s branch '%s'\n", opts->new_branch ? " a new" : "", new->name); } @@ -513,7 +513,7 @@ static void update_refs_for_switch(struct checkout_opts *opts, REF_NODEREF, DIE_ON_ERR); if (!opts->quiet) { if (old->path) - fprintf(stderr, "Note: moving to \"%s\" which isn't a local branch\nIf you want to create a new branch from this checkout, you may do so\n(now or later) by using -b with the checkout command again. Example:\n git checkout -b \n", new->name); + fprintf(stderr, "Note: moving to '%s' which isn't a local branch\nIf you want to create a new branch from this checkout, you may do so\n(now or later) by using -b with the checkout command again. Example:\n git checkout -b \n", new->name); describe_detached_head("HEAD is now at", new->commit); } } diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 0e21632f19..bdb808af1a 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -171,7 +171,7 @@ test_expect_success 'checkout to detach HEAD' ' git checkout -f renamer && git clean -f && git checkout renamer^ 2>messages && (cat >messages.expect < From 61e6108d94353d932d397e4be498c7e5293723eb Mon Sep 17 00:00:00 2001 From: Matthieu Moy Date: Wed, 8 Apr 2009 09:24:03 +0200 Subject: [PATCH 52/55] git-pull.sh: better warning message for "git pull" on detached head. Otherwise, git complains about not finding a branch to pull from in 'branch..merge', which is hardly understandable. While we're there, reword the sentences slightly. Signed-off-by: Matthieu Moy Signed-off-by: Junio C Hamano --- git-pull.sh | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/git-pull.sh b/git-pull.sh index 25adddfddf..c892059605 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -90,23 +90,31 @@ error_on_no_merge_candidates () { curr_branch=${curr_branch#refs/heads/} - echo "You asked me to pull without telling me which branch you" - echo "want to merge with, and 'branch.${curr_branch}.merge' in" - echo "your configuration file does not tell me either. Please" - echo "name which branch you want to merge on the command line and" - echo "try again (e.g. 'git pull ')." - echo "See git-pull(1) for details on the refspec." - echo - echo "If you often merge with the same branch, you may want to" - echo "configure the following variables in your configuration" - echo "file:" - echo - echo " branch.${curr_branch}.remote = " - echo " branch.${curr_branch}.merge = " - echo " remote..url = " - echo " remote..fetch = " - echo - echo "See git-config(1) for details." + if [ -z "$curr_branch" ]; then + echo "You are not currently on a branch, so I cannot use any" + echo "'branch..merge' in your configuration file." + echo "Please specify which branch you want to merge on the command" + echo "line and try again (e.g. 'git pull ')." + echo "See git-pull(1) for details." + else + echo "You asked me to pull without telling me which branch you" + echo "want to merge with, and 'branch.${curr_branch}.merge' in" + echo "your configuration file does not tell me either. Please" + echo "specify which branch you want to merge on the command line and" + echo "try again (e.g. 'git pull ')." + echo "See git-pull(1) for details." + echo + echo "If you often merge with the same branch, you may want to" + echo "configure the following variables in your configuration" + echo "file:" + echo + echo " branch.${curr_branch}.remote = " + echo " branch.${curr_branch}.merge = " + echo " remote..url = " + echo " remote..fetch = " + echo + echo "See git-config(1) for details." + fi exit 1 } From de551d472ed65fa570d9456e8498348fe4c8e7d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Wed, 8 Apr 2009 13:28:54 +0200 Subject: [PATCH 53/55] process_{tree,blob}: Remove useless xstrdup calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The name of the processed object was duplicated for passing it to add_object(), but that already calls path_name, which allocates a new string anyway. So the memory allocated by the xstrdup calls just went nowhere, leaking memory. This reduces the RSS usage for a "rev-list --all --objects" by about 10% on the gentoo repo (fully packed) as well as linux-2.6.git: gentoo: | old | new ----------------|------------------------------- RSS | 1537284 | 1388408 VSZ | 1816852 | 1667952 time elapsed | 1:49.62 | 1:48.99 min. page faults| 417178 | 379919 linux-2.6.git: | old | new ----------------|------------------------------- RSS | 324452 | 292996 VSZ | 491792 | 460376 time elapsed | 0:14.53 | 0:14.28 min. page faults| 89360 | 81613 Signed-off-by: Björn Steinbrink Signed-off-by: Junio C Hamano --- list-objects.c | 2 -- reachable.c | 1 - 2 files changed, 3 deletions(-) diff --git a/list-objects.c b/list-objects.c index c8b8375e49..dd243c7c66 100644 --- a/list-objects.c +++ b/list-objects.c @@ -23,7 +23,6 @@ static void process_blob(struct rev_info *revs, if (obj->flags & (UNINTERESTING | SEEN)) return; obj->flags |= SEEN; - name = xstrdup(name); add_object(obj, p, path, name); } @@ -78,7 +77,6 @@ static void process_tree(struct rev_info *revs, if (parse_tree(tree) < 0) die("bad tree object %s", sha1_to_hex(obj->sha1)); obj->flags |= SEEN; - name = xstrdup(name); add_object(obj, p, path, name); me.up = path; me.elem = name; diff --git a/reachable.c b/reachable.c index 3b1c18ff9b..b515fa2de3 100644 --- a/reachable.c +++ b/reachable.c @@ -48,7 +48,6 @@ static void process_tree(struct tree *tree, obj->flags |= SEEN; if (parse_tree(tree) < 0) die("bad tree object %s", sha1_to_hex(obj->sha1)); - name = xstrdup(name); add_object(obj, p, path, name); me.up = path; me.elem = name; From db12d97542762cdb54d332ea047122f1071132b5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 8 Apr 2009 23:40:33 -0700 Subject: [PATCH 54/55] Start 1.6.2.3 preparation Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.2.3.txt | 28 ++++++++++++++++++++++++++++ RelNotes | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 Documentation/RelNotes-1.6.2.3.txt diff --git a/Documentation/RelNotes-1.6.2.3.txt b/Documentation/RelNotes-1.6.2.3.txt new file mode 100644 index 0000000000..6560593fd5 --- /dev/null +++ b/Documentation/RelNotes-1.6.2.3.txt @@ -0,0 +1,28 @@ +GIT v1.6.2.3 Release Notes +========================== + +Fixes since v1.6.2.2 +-------------------- + +* Setting an octal mode value to core.sharedrepository configuration to + restrict access to the repository to group members did not work as + advertised. + +* A fairly large and trivial memory leak while rev-list shows list of + reachable objects has been identified and plugged. + +* "git-commit --interactive" did not abort when underlying "git-add -i" + signaled a failure. + +* git-repack (invoked from git-gc) did not work as nicely as it should in + a repository that borrows objects from neighbours via alternates + mechanism especially when some packs are marked with the ".keep" flag + to prevent them from being repacked. + +Many small documentation updates are included as well. + +--- +exec >/var/tmp/1 +echo O=$(git describe maint) +O=v1.6.2.2-41-gbff82d0 +git shortlog --no-merges $O..maint diff --git a/RelNotes b/RelNotes index 96e77da7c0..e90dc8ee42 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes-1.6.2.2.txt \ No newline at end of file +Documentation/RelNotes-1.6.2.3.txt \ No newline at end of file From e37347bba651f051998f23a3701b555f1a194557 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 9 Apr 2009 00:04:17 -0700 Subject: [PATCH 55/55] Update draft release notes to 1.6.3 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.3.txt | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/Documentation/RelNotes-1.6.3.txt b/Documentation/RelNotes-1.6.3.txt index 517c783b5d..9aa143b199 100644 --- a/Documentation/RelNotes-1.6.3.txt +++ b/Documentation/RelNotes-1.6.3.txt @@ -40,6 +40,9 @@ Updates since v1.6.2 * many uses of lstat(2) in the codepath for "git checkout" have been optimized out. +* pruning reflog entries that are unreachable from the tip of the ref + during "git reflog prune" (hence "git gc") was very inefficient. + (usability, bells and whistles) * rsync:/path/to/repo can be used to run git over rsync for local @@ -56,7 +59,7 @@ Updates since v1.6.2 spelled as "--format=