From 7791ecbc62b792b3eaa6d722b6dadcea4d0f322d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 23 Oct 2007 13:33:26 -0700 Subject: [PATCH 01/74] revert/cherry-pick: work on merge commits as well Usually you cannot revert a merge because you do not know which side of the merge should be considered the mainline (iow, what change to reverse). With this patch, cherry-pick and revert learn -m (--mainline) option that lets you specify the parent number (starting from 1) of the mainline, so that you can: git revert -m 1 $merge to reverse the changes introduced by the $merge commit relative to its first parent, and: git cherry-pick -m 2 $merge to replay the changes introduced by the $merge commit relative to its second parent. Signed-off-by: Junio C Hamano --- Documentation/git-cherry-pick.txt | 9 ++++++- Documentation/git-revert.txt | 9 ++++++- builtin-revert.c | 42 +++++++++++++++++++++++++------ git-compat-util.h | 13 ++++++++++ 4 files changed, 64 insertions(+), 9 deletions(-) diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt index 76a2edfd9..937c4a792 100644 --- a/Documentation/git-cherry-pick.txt +++ b/Documentation/git-cherry-pick.txt @@ -7,7 +7,7 @@ git-cherry-pick - Apply the change introduced by an existing commit SYNOPSIS -------- -'git-cherry-pick' [--edit] [-n] [-x] +'git-cherry-pick' [--edit] [-n] [-m parent-number] [-x] DESCRIPTION ----------- @@ -44,6 +44,13 @@ OPTIONS described above, and `-r` was to disable it. Now the default is not to do `-x` so this option is a no-op. +-m parent-number|--mainline parent-number:: + Usually you cannot revert a merge because you do not know which + side of the merge should be considered the mainline. This + option specifies the parent number (starting from 1) of + the mainline and allows cherry-pick to replay the change + relative to the specified parent. + -n|--no-commit:: Usually the command automatically creates a commit with a commit log message stating which commit was diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt index 69db49844..3457c4078 100644 --- a/Documentation/git-revert.txt +++ b/Documentation/git-revert.txt @@ -7,7 +7,7 @@ git-revert - Revert an existing commit SYNOPSIS -------- -'git-revert' [--edit | --no-edit] [-n] +'git-revert' [--edit | --no-edit] [-n] [-m parent-number] DESCRIPTION ----------- @@ -27,6 +27,13 @@ OPTIONS message prior committing the revert. This is the default if you run the command from a terminal. +-m parent-number|--mainline parent-number:: + Usually you cannot revert a merge because you do not know which + side of the merge should be considered the mainline. This + option specifies the parent number (starting from 1) of + the mainline and allows revert to reverse the change + relative to the specified parent. + --no-edit:: With this option, `git-revert` will not start the commit message editor. diff --git a/builtin-revert.c b/builtin-revert.c index a655c8ee2..bfed69d7e 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -19,9 +19,9 @@ * Copyright (c) 2005 Junio C Hamano */ -static const char *revert_usage = "git-revert [--edit | --no-edit] [-n] "; +static const char *revert_usage = "git-revert [--edit | --no-edit] [-n] [-m parent-number] "; -static const char *cherry_pick_usage = "git-cherry-pick [--edit] [-n] [-r] [-x] "; +static const char *cherry_pick_usage = "git-cherry-pick [--edit] [-n] [-m parent-number] [-r] [-x] "; static int edit; static int replay; @@ -29,6 +29,7 @@ static enum { REVERT, CHERRY_PICK } action; static int no_commit; static struct commit *commit; static int needed_deref; +static int mainline; static const char *me; @@ -58,6 +59,12 @@ static void parse_options(int argc, const char **argv) else if (!strcmp(arg, "-x") || !strcmp(arg, "--i-really-want-" "to-expose-my-private-commit-object-name")) replay = 0; + else if (!strcmp(arg, "-m") || !strcmp(arg, "--mainline")) { + if (++i >= argc || + strtol_i(argv[i], 10, &mainline) || + mainline <= 0) + usage(usage_str); + } else if (strcmp(arg, "-r")) usage(usage_str); } @@ -234,7 +241,7 @@ static int merge_recursive(const char *base_sha1, static int revert_or_cherry_pick(int argc, const char **argv) { unsigned char head[20]; - struct commit *base, *next; + struct commit *base, *next, *parent; int i; char *oneline, *reencoded_message = NULL; const char *message, *encoding; @@ -269,8 +276,29 @@ static int revert_or_cherry_pick(int argc, const char **argv) if (!commit->parents) die ("Cannot %s a root commit", me); - if (commit->parents->next) - die ("Cannot %s a multi-parent commit.", me); + if (commit->parents->next) { + /* Reverting or cherry-picking a merge commit */ + int cnt; + struct commit_list *p; + + if (!mainline) + die("Commit %s is a merge but no -m option was given.", + sha1_to_hex(commit->object.sha1)); + + for (cnt = 1, p = commit->parents; + cnt != mainline && p; + cnt++) + p = p->next; + if (cnt != mainline || !p) + die("Commit %s does not have parent %d", + sha1_to_hex(commit->object.sha1), mainline); + parent = p->item; + } else if (0 < mainline) + die("Mainline was specified but commit %s is not a merge.", + sha1_to_hex(commit->object.sha1)); + else + parent = commit->parents->item; + if (!(message = commit->buffer)) die ("Cannot get commit message for %s", sha1_to_hex(commit->object.sha1)); @@ -299,14 +327,14 @@ static int revert_or_cherry_pick(int argc, const char **argv) char *oneline_body = strchr(oneline, ' '); base = commit; - next = commit->parents->item; + next = parent; add_to_msg("Revert \""); add_to_msg(oneline_body + 1); add_to_msg("\"\n\nThis reverts commit "); add_to_msg(sha1_to_hex(commit->object.sha1)); add_to_msg(".\n"); } else { - base = commit->parents->item; + base = parent; next = commit; set_author_ident_env(message); add_message_to_msg(message); diff --git a/git-compat-util.h b/git-compat-util.h index 474f1d1ff..7b29d1b90 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -381,4 +381,17 @@ static inline int strtoul_ui(char const *s, int base, unsigned int *result) return 0; } +static inline int strtol_i(char const *s, int base, int *result) +{ + long ul; + char *p; + + errno = 0; + ul = strtol(s, &p, base); + if (errno || *p || p == s || (int) ul != ul) + return -1; + *result = ul; + return 0; +} + #endif From 4593fb84051d39f65cec81958e91056986e4682f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 31 Oct 2007 14:55:17 -0700 Subject: [PATCH 02/74] format-patch -s: add MIME encoding header if signer's name requires so When the body of the commit log message contains a non-ASCII character, format-patch correctly emitted the encoding header to mark the resulting message as such. However, if the original message was fully ASCII, the command line switch "-s" was given to add a new sign-off, and the signer's name was not ASCII only, the resulting message would have contained non-ASCII character but was not marked as such. Signed-off-by: Junio C Hamano --- builtin-branch.c | 2 +- builtin-log.c | 2 +- builtin-rev-list.c | 3 ++- builtin-show-branch.c | 2 +- commit.c | 5 ++--- commit.h | 4 +++- log-tree.c | 15 ++++++++++++++- 7 files changed, 24 insertions(+), 9 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index 3da8b55b8..3e020ccf1 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -276,7 +276,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, commit = lookup_commit(item->sha1); if (commit && !parse_commit(commit)) { pretty_print_commit(CMIT_FMT_ONELINE, commit, - &subject, 0, NULL, NULL, 0); + &subject, 0, NULL, NULL, 0, 0); sub = subject.buf; } printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color), diff --git a/builtin-log.c b/builtin-log.c index e8b982db7..8b2bf632c 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -787,7 +787,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) struct strbuf buf; strbuf_init(&buf, 0); pretty_print_commit(CMIT_FMT_ONELINE, commit, - &buf, 0, NULL, NULL, 0); + &buf, 0, NULL, NULL, 0, 0); printf("%c %s %s\n", sign, sha1_to_hex(commit->object.sha1), buf.buf); strbuf_release(&buf); diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 44393320e..697046723 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -86,7 +86,8 @@ static void show_commit(struct commit *commit) struct strbuf buf; strbuf_init(&buf, 0); pretty_print_commit(revs.commit_format, commit, - &buf, revs.abbrev, NULL, NULL, revs.date_mode); + &buf, revs.abbrev, NULL, NULL, + revs.date_mode, 0); if (buf.len) printf("%s%c", buf.buf, hdr_termination); strbuf_release(&buf); diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 07a0c2316..6dc835d30 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -266,7 +266,7 @@ static void show_one_commit(struct commit *commit, int no_name) strbuf_init(&pretty, 0); if (commit->object.parsed) { pretty_print_commit(CMIT_FMT_ONELINE, commit, - &pretty, 0, NULL, NULL, 0); + &pretty, 0, NULL, NULL, 0, 0); pretty_str = pretty.buf; } if (!prefixcmp(pretty_str, "[PATCH] ")) diff --git a/commit.c b/commit.c index ac24266e9..8262f6ac5 100644 --- a/commit.c +++ b/commit.c @@ -479,7 +479,7 @@ static int get_one_line(const char *msg) } /* High bit set, or ISO-2022-INT */ -static int non_ascii(int ch) +int non_ascii(int ch) { ch = (ch & 0xff); return ((ch & 0x80) || (ch == 0x1b)); @@ -1046,12 +1046,11 @@ static void pp_remainder(enum cmit_fmt fmt, void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, struct strbuf *sb, int abbrev, const char *subject, const char *after_subject, - enum date_mode dmode) + enum date_mode dmode, int plain_non_ascii) { unsigned long beginning_of_body; int indent = 4; const char *msg = commit->buffer; - int plain_non_ascii = 0; char *reencoded; const char *encoding; diff --git a/commit.h b/commit.h index b66150397..13b537293 100644 --- a/commit.h +++ b/commit.h @@ -61,13 +61,15 @@ enum cmit_fmt { CMIT_FMT_UNSPECIFIED, }; +extern int non_ascii(int); extern enum cmit_fmt get_commit_format(const char *arg); extern void format_commit_message(const struct commit *commit, const void *format, struct strbuf *sb); extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*, struct strbuf *, int abbrev, const char *subject, - const char *after_subject, enum date_mode); + const char *after_subject, enum date_mode, + int non_ascii_present); /** Removes the first commit from a list sorted by date, and adds all * of its parents. diff --git a/log-tree.c b/log-tree.c index 3763ce94f..a34beb0b0 100644 --- a/log-tree.c +++ b/log-tree.c @@ -125,6 +125,18 @@ static unsigned int digits_in_number(unsigned int number) return result; } +static int has_non_ascii(const char *s) +{ + int ch; + if (!s) + return 0; + while ((ch = *s++) != '\0') { + if (non_ascii(ch)) + return 1; + } + return 0; +} + void show_log(struct rev_info *opt, const char *sep) { struct strbuf msgbuf; @@ -273,7 +285,8 @@ void show_log(struct rev_info *opt, const char *sep) */ strbuf_init(&msgbuf, 0); pretty_print_commit(opt->commit_format, commit, &msgbuf, - abbrev, subject, extra_headers, opt->date_mode); + abbrev, subject, extra_headers, opt->date_mode, + has_non_ascii(opt->add_signoff)); if (opt->add_signoff) append_signoff(&msgbuf, opt->add_signoff); From 9d3014566302ad0d3d378b0a1653959b3118066d Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 1 Nov 2007 12:38:08 +0100 Subject: [PATCH 03/74] gitweb: Always set 'from_file' and 'to_file' in parse_difftree_raw_line Always set 'from_file' and 'to_file' keys when parsing raw diff output format line, even if filename didn't change (file was not renamed). This allows for simpler code. Previously, you would have written: $diffinfo->{'from_file'} || $diffinfo->{'file'} but now you can just use $diffinfo->{'from_file'} as 'from_file' is always defined. While at it, replace (for merge commits) $diffinfo->{'from_file'}[$i] || $diffinfo->{'to_file'} by defined $diffinfo->{'from_file'}[$i] ? $diffinfo->{'from_file'}[$i] : $diffinfo->{'to_file'}; to have no problems with file named '0'. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 2e0075627..b22f4be15 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1995,7 +1995,7 @@ sub parse_difftree_raw_line { if ($res{'status'} eq 'R' || $res{'status'} eq 'C') { # renamed or copied ($res{'from_file'}, $res{'to_file'}) = map { unquote($_) } split("\t", $7); } else { - $res{'file'} = unquote($7); + $res{'from_file'} = $res{'to_file'} = $res{'file'} = unquote($7); } } # '::100755 100755 100755 60e79ca1b01bc8b057abe17ddab484699a7f5fdb 94067cc5f73388f33722d52ae02f44692bc07490 94067cc5f73388f33722d52ae02f44692bc07490 MR git-gui/git-gui.sh' @@ -2062,7 +2062,10 @@ sub parse_from_to_diffinfo { fill_from_file_info($diffinfo, @parents) unless exists $diffinfo->{'from_file'}; for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) { - $from->{'file'}[$i] = $diffinfo->{'from_file'}[$i] || $diffinfo->{'to_file'}; + $from->{'file'}[$i] = + defined $diffinfo->{'from_file'}[$i] ? + $diffinfo->{'from_file'}[$i] : + $diffinfo->{'to_file'}; if ($diffinfo->{'status'}[$i] ne "A") { # not new (added) file $from->{'href'}[$i] = href(action=>"blob", hash_base=>$parents[$i], @@ -2074,7 +2077,7 @@ sub parse_from_to_diffinfo { } } else { # ordinary (not combined) diff - $from->{'file'} = $diffinfo->{'from_file'} || $diffinfo->{'file'}; + $from->{'file'} = $diffinfo->{'from_file'}; if ($diffinfo->{'status'} ne "A") { # not new (added) file $from->{'href'} = href(action=>"blob", hash_base=>$hash_parent, hash=>$diffinfo->{'from_id'}, @@ -2084,7 +2087,7 @@ sub parse_from_to_diffinfo { } } - $to->{'file'} = $diffinfo->{'to_file'} || $diffinfo->{'file'}; + $to->{'file'} = $diffinfo->{'to_file'}; if (!is_deleted($diffinfo)) { # file exists in result $to->{'href'} = href(action=>"blob", hash_base=>$hash, hash=>$diffinfo->{'to_id'}, @@ -2829,7 +2832,7 @@ sub is_patch_split { my ($diffinfo, $patchinfo) = @_; return defined $diffinfo && defined $patchinfo - && ($diffinfo->{'to_file'} || $diffinfo->{'file'}) eq $patchinfo->{'to_file'}; + && $diffinfo->{'to_file'} eq $patchinfo->{'to_file'}; } @@ -4667,8 +4670,8 @@ sub git_blobdiff { } %diffinfo = parse_difftree_raw_line($difftree[0]); - $file_parent ||= $diffinfo{'from_file'} || $file_name || $diffinfo{'file'}; - $file_name ||= $diffinfo{'to_file'} || $diffinfo{'file'}; + $file_parent ||= $diffinfo{'from_file'} || $file_name; + $file_name ||= $diffinfo{'to_file'}; $hash_parent ||= $diffinfo{'from_id'}; $hash ||= $diffinfo{'to_id'}; From 6aa6f92fda47cc4ee5f599895e8a5a327fb6f9ab Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 1 Nov 2007 12:38:09 +0100 Subject: [PATCH 04/74] gitweb: Add 'status_str' to parse_difftree_raw_line output Add 'status_str' to diffinfo output, which stores status (also for merge commit) as a string. This allows for easy checking if there is given status among all for merge commit, e.g. $diffinfo->{'status_str'} =~ /D/; Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index b22f4be15..abb5a7d39 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1990,7 +1990,7 @@ sub parse_difftree_raw_line { $res{'to_mode'} = $2; $res{'from_id'} = $3; $res{'to_id'} = $4; - $res{'status'} = $5; + $res{'status'} = $res{'status_str'} = $5; $res{'similarity'} = $6; if ($res{'status'} eq 'R' || $res{'status'} eq 'C') { # renamed or copied ($res{'from_file'}, $res{'to_file'}) = map { unquote($_) } split("\t", $7); @@ -2006,6 +2006,7 @@ sub parse_difftree_raw_line { $res{'to_mode'} = pop @{$res{'from_mode'}}; $res{'from_id'} = [ split(' ', $3) ]; $res{'to_id'} = pop @{$res{'from_id'}}; + $res{'status_str'} = $4; $res{'status'} = [ split('', $4) ]; $res{'to_file'} = unquote($5); } @@ -2821,7 +2822,7 @@ sub fill_from_file_info { sub is_deleted { my $diffinfo = shift; - return $diffinfo->{'to_id'} eq ('0' x 40); + return $diffinfo->{'status_str'} =~ /D/; } # does patch correspond to [previous] difftree raw line From fa9aff463da42feea68228ca51685cd9f4403e92 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 1 Nov 2007 14:23:16 +0100 Subject: [PATCH 05/74] gitweb: Remove CGI::Carp::set_programname() call from t9500 gitweb test It does appear to do nothing; gitweb is run as standalone program and not as CGI script in this test. This call caused problems later. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- t/t9500-gitweb-standalone-no-errors.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index f7bad5bb2..1bf0988d9 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -31,7 +31,6 @@ our \$projects_list = ""; our \$export_ok = ""; our \$strict_export = ""; -CGI::Carp::set_programname("gitweb/gitweb.cgi"); EOF cat >.git/description < Date: Thu, 1 Nov 2007 13:06:27 +0100 Subject: [PATCH 06/74] gitweb: Easier adding/changing parameters to current URL Add boolean option '-replay' to href() subroutine, which is used to generate links in gitweb. This option "replays" current URL, overriding it with provided parameters. It means that current value of each CGI parameter is used unless otherwise provided. This change is meant to make it easier to generate links which differ from current page URL only by one parameter, for example the same view but sorted by different column: href(-replay=>1, order=>"age") or view which differs by some option, e.g. in log views href(-replay=>1, extra_options=>"--no-merges") or alternate view of the same object, e.g. in the 'blob' view href(-replay=>1, action=>"blob_plain") Actual use of this functionality is left for later. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index abb5a7d39..c93c546fb 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -611,6 +611,15 @@ (%) ); my %mapping = @mapping; + if ($params{-replay}) { + while (my ($name, $symbol) = each %mapping) { + if (!exists $params{$name}) { + # to allow for multivalued params we use arrayref form + $params{$name} = [ $cgi->param($symbol) ]; + } + } + } + $params{'project'} = $project unless exists $params{'project'}; my ($use_pathinfo) = gitweb_check_feature('pathinfo'); From 7afd77bfc1b6881fd9e476274d3f08b793c292ed Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 1 Nov 2007 13:06:28 +0100 Subject: [PATCH 07/74] gitweb: Use href(-replay=>1, page=>...) to generate pagination links Use href(-replay=>1, page=>$page-1) and href(-replay=>1, page=>$page+1) to generate previous page and next page links. Generate next page link only once. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 44 +++++++++++++++----------------------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index c93c546fb..d2adae31c 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2518,7 +2518,7 @@ sub format_paging_nav { if ($page > 0) { $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>$action, hash=>$hash, page=>$page-1), + $cgi->a({-href => href(-replay=>1, page=>$page-1), -accesskey => "p", -title => "Alt-p"}, "prev"); } else { $paging_nav .= " ⋅ prev"; @@ -2526,7 +2526,7 @@ sub format_paging_nav { if ($nrevs >= (100 * ($page+1)-1)) { $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>$action, hash=>$hash, page=>$page+1), + $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); } else { $paging_nav .= " ⋅ next"; @@ -4448,7 +4448,7 @@ sub git_log { } if ($#commitlist >= 100) { print "
\n"; - print $cgi->a({-href => href(action=>"log", hash=>$hash, page=>$page+1), + print $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); print "
\n"; } @@ -5015,27 +5015,20 @@ sub git_history { file_name=>$file_name)}, "first"); $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, - file_name=>$file_name, page=>$page-1), + $cgi->a({-href => href(-replay=>1, page=>$page-1), -accesskey => "p", -title => "Alt-p"}, "prev"); } else { $paging_nav .= "first"; $paging_nav .= " ⋅ prev"; } - if ($#commitlist >= 100) { - $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, - file_name=>$file_name, page=>$page+1), - -accesskey => "n", -title => "Alt-n"}, "next"); - } else { - $paging_nav .= " ⋅ next"; - } my $next_link = ''; if ($#commitlist >= 100) { $next_link = - $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, - file_name=>$file_name, page=>$page+1), + $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); + $paging_nav .= " ⋅ $next_link"; + } else { + $paging_nav .= " ⋅ next"; } git_header_html(); @@ -5105,30 +5098,23 @@ sub git_search { searchtext=>$searchtext, searchtype=>$searchtype)}, "first"); $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>"search", hash=>$hash, - searchtext=>$searchtext, searchtype=>$searchtype, - page=>$page-1), + $cgi->a({-href => href(-replay=>1, page=>$page-1), -accesskey => "p", -title => "Alt-p"}, "prev"); } else { $paging_nav .= "first"; $paging_nav .= " ⋅ prev"; } + my $next_link = ''; if ($#commitlist >= 100) { - $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>"search", hash=>$hash, - searchtext=>$searchtext, searchtype=>$searchtype, - page=>$page+1), + $next_link = + $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); + $paging_nav .= " ⋅ $next_link"; } else { $paging_nav .= " ⋅ next"; } - my $next_link = ''; + if ($#commitlist >= 100) { - $next_link = - $cgi->a({-href => href(action=>"search", hash=>$hash, - searchtext=>$searchtext, searchtype=>$searchtype, - page=>$page+1), - -accesskey => "n", -title => "Alt-n"}, "next"); } git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav); @@ -5327,7 +5313,7 @@ sub git_shortlog { my $next_link = ''; if ($#commitlist >= 100) { $next_link = - $cgi->a({-href => href(action=>"shortlog", hash=>$hash, page=>$page+1), + $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); } From a3823e5ad75498774a32850f44e8254b0e1c0901 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 1 Nov 2007 13:06:29 +0100 Subject: [PATCH 08/74] gitweb: Use href(-replay=>1, action=>...) to generate alternate views Use href(action=>..., -replay=>1) to generate links to alternate views of current page in the $formats_nav (bottom) part of page_nav navigation bar. This form is used only when all parameters are repeated, and when the replay form is shorter. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index d2adae31c..ecb0113a7 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3911,11 +3911,11 @@ sub git_blame2 { or die_error(undef, "Open git-blame failed"); git_header_html(); my $formats_nav = - $cgi->a({-href => href(action=>"blob", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"blob", -replay=>1)}, "blob") . " | " . - $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, - "history") . + $cgi->a({-href => href(action=>"history", -replay=>1)}, + "history") . " | " . $cgi->a({-href => href(action=>"blame", file_name=>$file_name)}, "HEAD"); @@ -4191,18 +4191,15 @@ sub git_blob { if (defined $file_name) { if ($have_blame) { $formats_nav .= - $cgi->a({-href => href(action=>"blame", hash_base=>$hash_base, - hash=>$hash, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"blame", -replay=>1)}, "blame") . " | "; } $formats_nav .= - $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, - hash=>$hash, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"history", -replay=>1)}, "history") . " | " . - $cgi->a({-href => href(action=>"blob_plain", - hash=>$hash, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"blob_plain", -replay=>1)}, "raw") . " | " . $cgi->a({-href => href(action=>"blob", @@ -4210,7 +4207,8 @@ sub git_blob { "HEAD"); } else { $formats_nav .= - $cgi->a({-href => href(action=>"blob_plain", hash=>$hash)}, "raw"); + $cgi->a({-href => href(action=>"blob_plain", -replay=>1)}, + "raw"); } git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_header_div('commit', esc_html($co{'title'}), $hash_base); @@ -4273,8 +4271,7 @@ sub git_tree { my @views_nav = (); if (defined $file_name) { push @views_nav, - $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, - hash=>$hash, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"history", -replay=>1)}, "history"), $cgi->a({-href => href(action=>"tree", hash_base=>"HEAD", file_name=>$file_name)}, @@ -4742,10 +4739,7 @@ sub git_blobdiff { # header if ($format eq 'html') { my $formats_nav = - $cgi->a({-href => href(action=>"blobdiff_plain", - hash=>$hash, hash_parent=>$hash_parent, - hash_base=>$hash_base, hash_parent_base=>$hash_parent_base, - file_name=>$file_name, file_parent=>$file_parent)}, + $cgi->a({-href => href(action=>"blobdiff_plain", -replay=>1)}, "raw"); git_header_html(undef, $expires); if (defined $hash_base && (my %co = parse_commit($hash_base))) { @@ -4819,8 +4813,7 @@ sub git_commitdiff { my $formats_nav; if ($format eq 'html') { $formats_nav = - $cgi->a({-href => href(action=>"commitdiff_plain", - hash=>$hash, hash_parent=>$hash_parent)}, + $cgi->a({-href => href(action=>"commitdiff_plain", -replay=>1)}, "raw"); if (defined $hash_parent && From 4d00bda2aa9dd8cd6ec4015832b80eb1273d46d7 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 1 Nov 2007 23:26:04 -0400 Subject: [PATCH 09/74] make the pack index version configurable It is a good idea to use pack index version 2 all the time since it has proper protection against propagation of certain pack corruptions when repacking which is not possible with index version 1, as demonstrated in test t5302. Hence this config option. The default is still pack index version 1. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- Documentation/config.txt | 9 +++++++++ builtin-pack-objects.c | 6 ++++++ index-pack.c | 13 +++++++++++++ 3 files changed, 28 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index edf50cd21..0df004ea2 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -661,6 +661,15 @@ pack.threads:: machines. The required amount of memory for the delta search window is however multiplied by the number of threads. +pack.indexVersion:: + Specify the default pack index version. Valid values are 1 for + legacy pack index used by Git versions prior to 1.5.2, and 2 for + the new pack index with capabilities for packs larger than 4 GB + as well as proper protection against the repacking of corrupted + packs. Version 2 is selected and this config option ignored + whenever the corresponding pack is larger than 2 GB. Otherwise + the default is 1. + pull.octopus:: The default merge strategy to use when pulling multiple branches at once. diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 0be539ed7..f4b90c1e4 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1773,6 +1773,12 @@ static int git_pack_config(const char *k, const char *v) #endif return 0; } + if (!strcmp(k, "pack.indexversion")) { + pack_idx_default_version = git_config_int(k, v); + if (pack_idx_default_version > 2) + die("bad pack.indexversion=%d", pack_idx_default_version); + return 0; + } return git_default_config(k, v); } diff --git a/index-pack.c b/index-pack.c index db58e0504..c0bb78a8e 100644 --- a/index-pack.c +++ b/index-pack.c @@ -683,6 +683,17 @@ static void final(const char *final_pack_name, const char *curr_pack_name, } } +static int git_index_pack_config(const char *k, const char *v) +{ + if (!strcmp(k, "pack.indexversion")) { + pack_idx_default_version = git_config_int(k, v); + if (pack_idx_default_version > 2) + die("bad pack.indexversion=%d", pack_idx_default_version); + return 0; + } + return git_default_config(k, v); +} + int main(int argc, char **argv) { int i, fix_thin_pack = 0; @@ -693,6 +704,8 @@ int main(int argc, char **argv) struct pack_idx_entry **idx_objects; unsigned char sha1[20]; + git_config(git_index_pack_config); + for (i = 1; i < argc; i++) { const char *arg = argv[i]; From 79814f425c00129dbdbdc3c99d04af52ccc58254 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 1 Nov 2007 23:43:24 -0400 Subject: [PATCH 10/74] pack-objects: get rid of an ugly cast ... when calling write_idx_file(). Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index f4b90c1e4..d0ca165c9 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -57,7 +57,7 @@ struct object_entry { * nice "minimum seek" order. */ static struct object_entry *objects; -static struct object_entry **written_list; +static struct pack_idx_entry **written_list; static uint32_t nr_objects, nr_alloc, nr_result, nr_written; static int non_empty; @@ -579,7 +579,7 @@ static off_t write_one(struct sha1file *f, e->idx.offset = 0; return 0; } - written_list[nr_written++] = e; + written_list[nr_written++] = &e->idx; /* make sure off_t is sufficiently large not to wrap */ if (offset > offset + size) @@ -607,7 +607,7 @@ static void write_pack_file(void) if (do_progress) start_progress(&progress_state, "Writing %u objects...", "", nr_result); - written_list = xmalloc(nr_objects * sizeof(struct object_entry *)); + written_list = xmalloc(nr_objects * sizeof(*written_list)); do { unsigned char sha1[20]; @@ -654,8 +654,8 @@ static void write_pack_file(void) umask(mode); mode = 0444 & ~mode; - idx_tmp_name = write_idx_file(NULL, - (struct pack_idx_entry **) written_list, nr_written, sha1); + idx_tmp_name = write_idx_file(NULL, written_list, + nr_written, sha1); snprintf(tmpname, sizeof(tmpname), "%s-%s.pack", base_name, sha1_to_hex(sha1)); if (adjust_perm(pack_tmp_name, mode)) @@ -677,7 +677,7 @@ static void write_pack_file(void) /* mark written objects as written to previous pack */ for (j = 0; j < nr_written; j++) { - written_list[j]->idx.offset = (off_t)-1; + written_list[j]->offset = (off_t)-1; } nr_remaining -= nr_written; } while (nr_remaining && i < nr_objects); From f88a545a94cb474d370ef97dd3694d09b6ac90c1 Mon Sep 17 00:00:00 2001 From: Simon Sasburg Date: Thu, 1 Nov 2007 23:57:45 +0100 Subject: [PATCH 11/74] Make mailsplit and mailinfo strip whitespace from the start of the input Signed-off-by: Simon Sasburg Signed-off-by: Junio C Hamano --- builtin-mailinfo.c | 6 ++++++ builtin-mailsplit.c | 6 ++++++ t/t5100/sample.mbox | 3 +++ 3 files changed, 15 insertions(+) diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index fb12248f8..260084797 100644 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@ -915,6 +915,7 @@ static void handle_info(void) static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const char *msg, const char *patch) { + int peek; keep_subject = ks; metainfo_charset = encoding; fin = in; @@ -935,6 +936,11 @@ static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *)); s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *)); + do { + peek = fgetc(in); + } while (isspace(peek)); + ungetc(peek, in); + /* process the email header */ while (read_one_header_line(line, sizeof(line), fin)) check_header(line, sizeof(line), p_hdr_data, 1); diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c index 43fc373a1..74b04706f 100644 --- a/builtin-mailsplit.c +++ b/builtin-mailsplit.c @@ -164,6 +164,7 @@ static int split_mbox(const char *file, const char *dir, int allow_bare, { char name[PATH_MAX]; int ret = -1; + int peek; FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r"); int file_done = 0; @@ -173,6 +174,11 @@ static int split_mbox(const char *file, const char *dir, int allow_bare, goto out; } + do { + peek = fgetc(f); + } while (isspace(peek)); + ungetc(peek, f); + if (fgets(buf, sizeof(buf), f) == NULL) { /* empty stdin is OK */ if (f != stdin) { diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox index b80c981c1..070c1661b 100644 --- a/t/t5100/sample.mbox +++ b/t/t5100/sample.mbox @@ -1,3 +1,6 @@ + + + From nobody Mon Sep 17 00:00:00 2001 From: A U Thor Date: Fri, 9 Jun 2006 00:44:16 -0700 From b43b0a3c5c313619397b2f81d5a4acad2c3cb4a9 Mon Sep 17 00:00:00 2001 From: Chris Pettitt Date: Thu, 1 Nov 2007 20:43:13 -0700 Subject: [PATCH 12/74] git-p4: Add a helper function to parse the full git diff-tree output. Signed-off-by: Chris Pettitt Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 49 ++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index bf33f74b7..c7fc564a5 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -71,6 +71,46 @@ def isP4Exec(kind): a plus sign, it is also executable""" return (re.search(r"(^[cku]?x)|\+.*x", kind) != None) +def diffTreePattern(): + # This is a simple generator for the diff tree regex pattern. This could be + # a class variable if this and parseDiffTreeEntry were a part of a class. + pattern = re.compile(':(\d+) (\d+) (\w+) (\w+) ([A-Z])(\d+)?\t(.*?)((\t(.*))|$)') + while True: + yield pattern + +def parseDiffTreeEntry(entry): + """Parses a single diff tree entry into its component elements. + + See git-diff-tree(1) manpage for details about the format of the diff + output. This method returns a dictionary with the following elements: + + src_mode - The mode of the source file + dst_mode - The mode of the destination file + src_sha1 - The sha1 for the source file + dst_sha1 - The sha1 fr the destination file + status - The one letter status of the diff (i.e. 'A', 'M', 'D', etc) + status_score - The score for the status (applicable for 'C' and 'R' + statuses). This is None if there is no score. + src - The path for the source file. + dst - The path for the destination file. This is only present for + copy or renames. If it is not present, this is None. + + If the pattern is not matched, None is returned.""" + + match = diffTreePattern().next().match(entry) + if match: + return { + 'src_mode': match.group(1), + 'dst_mode': match.group(2), + 'src_sha1': match.group(3), + 'dst_sha1': match.group(4), + 'status': match.group(5), + 'status_score': match.group(6), + 'src': match.group(7), + 'dst': match.group(10) + } + return None + def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): cmd = "p4 -G %s" % cmd if verbose: @@ -494,13 +534,14 @@ class P4Submit(Command): else: print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id)) diffOpts = ("", "-M")[self.detectRename] - diff = read_pipe_lines("git diff-tree -r --name-status %s \"%s^\" \"%s\"" % (diffOpts, id, id)) + diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id)) filesToAdd = set() filesToDelete = set() editedFiles = set() for line in diff: - modifier = line[0] - path = line[1:].strip() + diff = parseDiffTreeEntry(line) + modifier = diff['status'] + path = diff['src'] if modifier == "M": system("p4 edit \"%s\"" % path) editedFiles.add(path) @@ -513,7 +554,7 @@ class P4Submit(Command): if path in filesToAdd: filesToAdd.remove(path) elif modifier == "R": - src, dest = line.strip().split("\t")[1:3] + src, dest = diff['src'], diff['dst'] system("p4 integrate -Dt \"%s\" \"%s\"" % (src, dest)) system("p4 edit \"%s\"" % (dest)) os.unlink(dest) From c65b670e858a0fc010c8b8bb3cbf064bb22e1839 Mon Sep 17 00:00:00 2001 From: Chris Pettitt Date: Thu, 1 Nov 2007 20:43:14 -0700 Subject: [PATCH 13/74] git-p4: Detect changes to executable bit and include them in p4 submit. This changeset takes advantage of the new parseDiffTreeEntry(...) function to detect changes to the execute bit in the git repository. During submit, git-p4 now looks for changes to the executable bit and if it finds them it "reopens" the file in perforce, which allows it to change the file type. The logic for adding the executable bit in perforce is straightforward: the +x modifier can be used. Removing the executable bit in perforce requires that the entire filetype be redefined (there is no way to join remove the bit with a -x modifier, for example). This changeset includes logic to remove the executable bit from the full file type while preserving the base file type and other modifiers. Signed-off-by: Chris Pettitt Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c7fc564a5..c148b5ab7 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -71,6 +71,31 @@ def isP4Exec(kind): a plus sign, it is also executable""" return (re.search(r"(^[cku]?x)|\+.*x", kind) != None) +def setP4ExecBit(file, mode): + # Reopens an already open file and changes the execute bit to match + # the execute bit setting in the passed in mode. + + p4Type = "+x" + + if not isModeExec(mode): + p4Type = getP4OpenedType(file) + p4Type = re.sub('^([cku]?)x(.*)', '\\1\\2', p4Type) + p4Type = re.sub('(.*?\+.*?)x(.*?)', '\\1\\2', p4Type) + if p4Type[-1] == "+": + p4Type = p4Type[0:-1] + + system("p4 reopen -t %s %s" % (p4Type, file)) + +def getP4OpenedType(file): + # Returns the perforce file type for the given file. + + result = read_pipe("p4 opened %s" % file) + match = re.match(".*\((.+)\)$", result) + if match: + return match.group(1) + else: + die("Could not determine file type for %s" % file) + def diffTreePattern(): # This is a simple generator for the diff tree regex pattern. This could be # a class variable if this and parseDiffTreeEntry were a part of a class. @@ -111,6 +136,14 @@ def parseDiffTreeEntry(entry): } return None +def isModeExec(mode): + # Returns True if the given git mode represents an executable file, + # otherwise False. + return mode[-3:] == "755" + +def isModeExecChanged(src_mode, dst_mode): + return isModeExec(src_mode) != isModeExec(dst_mode) + def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): cmd = "p4 -G %s" % cmd if verbose: @@ -538,15 +571,19 @@ class P4Submit(Command): filesToAdd = set() filesToDelete = set() editedFiles = set() + filesToChangeExecBit = {} for line in diff: diff = parseDiffTreeEntry(line) modifier = diff['status'] path = diff['src'] if modifier == "M": system("p4 edit \"%s\"" % path) + if isModeExecChanged(diff['src_mode'], diff['dst_mode']): + filesToChangeExecBit[path] = diff['dst_mode'] editedFiles.add(path) elif modifier == "A": filesToAdd.add(path) + filesToChangeExecBit[path] = diff['dst_mode'] if path in filesToDelete: filesToDelete.remove(path) elif modifier == "D": @@ -557,6 +594,8 @@ class P4Submit(Command): src, dest = diff['src'], diff['dst'] system("p4 integrate -Dt \"%s\" \"%s\"" % (src, dest)) system("p4 edit \"%s\"" % (dest)) + if isModeExecChanged(diff['src_mode'], diff['dst_mode']): + filesToChangeExecBit[dest] = diff['dst_mode'] os.unlink(dest) editedFiles.add(dest) filesToDelete.add(src) @@ -609,6 +648,11 @@ class P4Submit(Command): system("p4 revert \"%s\"" % f) system("p4 delete \"%s\"" % f) + # Set/clear executable bits + for f in filesToChangeExecBit.keys(): + mode = filesToChangeExecBit[f] + setP4ExecBit(f, mode) + logMessage = "" if not self.directSubmit: logMessage = extractLogMessageFromGitCommit(id) From 6232b3438d127def8cc0612e45422347578c6102 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 2 Nov 2007 17:25:24 -0700 Subject: [PATCH 14/74] cherry-pick/revert -m: add tests This adds a new test to check cherry-pick/revert of a merge commit. Signed-off-by: Junio C Hamano --- t/t3502-cherry-pick-merge.sh | 123 +++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100755 t/t3502-cherry-pick-merge.sh diff --git a/t/t3502-cherry-pick-merge.sh b/t/t3502-cherry-pick-merge.sh new file mode 100755 index 000000000..3274c6141 --- /dev/null +++ b/t/t3502-cherry-pick-merge.sh @@ -0,0 +1,123 @@ +#!/bin/sh + +test_description='cherry picking and reverting a merge + + b---c + / / + initial---a + +' + +. ./test-lib.sh + +test_expect_success setup ' + + >A && + >B && + git add A B && + git commit -m "Initial" && + git tag initial && + git branch side && + echo new line >A && + git commit -m "add line to A" A && + git tag a && + git checkout side && + echo new line >B && + git commit -m "add line to B" B && + git tag b && + git checkout master && + git merge side && + git tag c + +' + +test_expect_success 'cherry-pick a non-merge with -m should fail' ' + + git reset --hard && + git checkout a^0 && + ! git cherry-pick -m 1 b && + git diff --exit-code a + +' + +test_expect_success 'cherry pick a merge without -m should fail' ' + + git reset --hard && + git checkout a^0 && + ! git cherry-pick c && + git diff --exit-code a + +' + +test_expect_success 'cherry pick a merge (1)' ' + + git reset --hard && + git checkout a^0 && + git cherry-pick -m 1 c && + git diff --exit-code c + +' + +test_expect_success 'cherry pick a merge (2)' ' + + git reset --hard && + git checkout b^0 && + git cherry-pick -m 2 c && + git diff --exit-code c + +' + +test_expect_success 'cherry pick a merge relative to nonexistent parent should fail' ' + + git reset --hard && + git checkout b^0 && + ! git cherry-pick -m 3 c + +' + +test_expect_success 'revert a non-merge with -m should fail' ' + + git reset --hard && + git checkout c^0 && + ! git revert -m 1 b && + git diff --exit-code c + +' + +test_expect_success 'revert a merge without -m should fail' ' + + git reset --hard && + git checkout c^0 && + ! git revert c && + git diff --exit-code c + +' + +test_expect_success 'revert a merge (1)' ' + + git reset --hard && + git checkout c^0 && + git revert -m 1 c && + git diff --exit-code a + +' + +test_expect_success 'revert a merge (2)' ' + + git reset --hard && + git checkout c^0 && + git revert -m 2 c && + git diff --exit-code b + +' + +test_expect_success 'revert a merge relative to nonexistent parent should fail' ' + + git reset --hard && + git checkout c^0 && + ! git revert -m 3 c && + git diff --exit-code c + +' + +test_done From aacb8f10a70c07dfe1461684f098313f0edb371f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 2 Nov 2007 17:55:31 -0700 Subject: [PATCH 15/74] test format-patch -s: make sure MIME content type is shown as needed Signed-off-by: Junio C Hamano --- t/t4021-format-patch-signer-mime.sh | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100755 t/t4021-format-patch-signer-mime.sh diff --git a/t/t4021-format-patch-signer-mime.sh b/t/t4021-format-patch-signer-mime.sh new file mode 100755 index 000000000..67a70fada --- /dev/null +++ b/t/t4021-format-patch-signer-mime.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +test_description='format-patch -s should force MIME encoding as needed' + +. ./test-lib.sh + +test_expect_success setup ' + + >F && + git add F && + git commit -m initial && + echo new line >F && + + test_tick && + git commit -m "This adds some lines to F" F + +' + +test_expect_success 'format normally' ' + + git format-patch --stdout -1 >output && + ! grep Content-Type output + +' + +test_expect_success 'format with signoff without funny signer name' ' + + git format-patch -s --stdout -1 >output && + ! grep Content-Type output + +' + +test_expect_success 'format with non ASCII signer name' ' + + GIT_COMMITTER_NAME="$B$O$^$N(B $B$U$K$*$&(B" \ + git format-patch -s --stdout -1 >output && + grep Content-Type output + +' + +test_done + From e9c34c233f2e838c334f6e8a807ebc6a988e9509 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 3 Nov 2007 00:41:18 +0100 Subject: [PATCH 16/74] gitweb: Add tests for overriding gitweb config with repo config Make blame view and snapshot support overridable by repository config. Test tree view with both features disabled, and with both features enabled. Test with features enabled also tests multiple formats snapshot support (in tree view). Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- t/t9500-gitweb-standalone-no-errors.sh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index 1bf0988d9..35fff3ddb 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -557,4 +557,27 @@ test_expect_success \ 'gitweb_run "p=.git;a=tree;opt=--no-merges"' test_debug 'cat gitweb.log' +# ---------------------------------------------------------------------- +# gitweb config and repo config + +cat >>gitweb_config.perl < Date: Sat, 3 Nov 2007 00:41:19 +0100 Subject: [PATCH 17/74] gitweb: Read repo config using 'git config -z -l' Change git_get_project_config to run git-config only once per repository, without changing its signature (its calling convention). This means for example that it returns 'true' or 'false' when called with second argument '--bool', and not true or false value. Instead of calling 'git config [] --get gitweb.' once for each config variable, call 'git config -z -l' only once, parsing and saving its output to %config variable. This makes possible to add new per repository configuration without paying cost of forking once per variable checked. We can now allow repository description and repository URLs to be stored in config file without badly affecting gitweb performance. For now only configuration variables for 'gitweb' section are stored. Multiple values for single configuration variable are stored as anonymous array reference; configuration variable with no value is stored as undef. Converting configuration variable values to boolean or integer value are done in Perl. Results differ from git-config in the fact that no conversion error is ever raised. For boolean values no value, 'true' (any case) and 'false' (any case) are considered true, numbers are true if not zero; all other values (even invalid for bool) are considered false. For integer values value suffix of 'k', 'm', or 'g' following decimal number will cause the value to be multiplied by 1024, 1048576, or 1073741824; other values are returned as-is, only whitespace stripped. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 115 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 108 insertions(+), 7 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index ecb0113a7..e94ff9633 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1432,20 +1432,121 @@ sub git_get_type { return $type; } +# repository configuration +our $config_file = ''; +our %config; + +# store multiple values for single key as anonymous array reference +# single values stored directly in the hash, not as [ ] +sub hash_set_multi { + my ($hash, $key, $value) = @_; + + if (!exists $hash->{$key}) { + $hash->{$key} = $value; + } elsif (!ref $hash->{$key}) { + $hash->{$key} = [ $hash->{$key}, $value ]; + } else { + push @{$hash->{$key}}, $value; + } +} + +# return hash of git project configuration +# optionally limited to some section, e.g. 'gitweb' +sub git_parse_project_config { + my $section_regexp = shift; + my %config; + + local $/ = "\0"; + + open my $fh, "-|", git_cmd(), "config", '-z', '-l', + or return; + + while (my $keyval = <$fh>) { + chomp $keyval; + my ($key, $value) = split(/\n/, $keyval, 2); + + hash_set_multi(\%config, $key, $value) + if (!defined $section_regexp || $key =~ /^(?:$section_regexp)\./o); + } + close $fh; + + return %config; +} + +# convert config value to boolean, 'true' or 'false' +# no value, number > 0, 'true' and 'yes' values are true +# rest of values are treated as false (never as error) +sub config_to_bool { + my $val = shift; + + # strip leading and trailing whitespace + $val =~ s/^\s+//; + $val =~ s/\s+$//; + + return (!defined $val || # section.key + ($val =~ /^\d+$/ && $val) || # section.key = 1 + ($val =~ /^(?:true|yes)$/i)); # section.key = true +} + +# convert config value to simple decimal number +# an optional value suffix of 'k', 'm', or 'g' will cause the value +# to be multiplied by 1024, 1048576, or 1073741824 +sub config_to_int { + my $val = shift; + + # strip leading and trailing whitespace + $val =~ s/^\s+//; + $val =~ s/\s+$//; + + if (my ($num, $unit) = ($val =~ /^([0-9]*)([kmg])$/i)) { + $unit = lc($unit); + # unknown unit is treated as 1 + return $num * ($unit eq 'g' ? 1073741824 : + $unit eq 'm' ? 1048576 : + $unit eq 'k' ? 1024 : 1); + } + return $val; +} + +# convert config value to array reference, if needed +sub config_to_multi { + my $val = shift; + + return ref($val) ? $val : [ $val ]; +} + sub git_get_project_config { my ($key, $type) = @_; + # key sanity check return unless ($key); $key =~ s/^gitweb\.//; return if ($key =~ m/\W/); - my @x = (git_cmd(), 'config'); - if (defined $type) { push @x, $type; } - push @x, "--get"; - push @x, "gitweb.$key"; - my $val = qx(@x); - chomp $val; - return ($val); + # type sanity check + if (defined $type) { + $type =~ s/^--//; + $type = undef + unless ($type eq 'bool' || $type eq 'int'); + } + + # get config + if (!defined $config_file || + $config_file ne "$git_dir/config") { + %config = git_parse_project_config('gitweb'); + $config_file = "$git_dir/config"; + } + + # ensure given type + if (!defined $type) { + return $config{"gitweb.$key"}; + } elsif ($type eq 'bool') { + # backward compatibility: 'git config --bool' returns true/false + return config_to_bool($config{"gitweb.$key"}) ? 'true' : 'false'; + } elsif ($type eq 'int') { + return config_to_int($config{"gitweb.$key"}); + } + return $config{"gitweb.$key"}; } # get hash of given path at given ref From 0e121a2cd42d28bc4034feedf8a13c5a91f85bd3 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 3 Nov 2007 00:41:20 +0100 Subject: [PATCH 18/74] gitweb: Use config file for repository description and URLs Allow to use configuration variable gitweb.description for repository description if there is no $GIT_DIR/description file, and multivalued configuration variable gitweb.url for URLs of a project (to clone or fetch from) if there is no $GIT_DIR/cloneurl file. While repository description is shown in the projects list page, so it is better to use file and not config variable for performance, it is I think better to use gitweb.url for URLs (as it is shown only on project summary page). Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index e94ff9633..759dff1cc 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1606,7 +1606,9 @@ sub git_get_path_by_hash { sub git_get_project_description { my $path = shift; - open my $fd, "$projectroot/$path/description" or return undef; + $git_dir = "$projectroot/$path"; + open my $fd, "$projectroot/$path/description" + or return git_get_project_config('description'); my $descr = <$fd>; close $fd; if (defined $descr) { @@ -1618,7 +1620,11 @@ sub git_get_project_description { sub git_get_project_url_list { my $path = shift; - open my $fd, "$projectroot/$path/cloneurl" or return; + $git_dir = "$projectroot/$path"; + open my $fd, "$projectroot/$path/cloneurl" + or return wantarray ? + @{ config_to_multi(git_get_project_config('url')) } : + config_to_multi(git_get_project_config('url')); my @git_project_url_list = map { chomp; $_ } <$fd>; close $fd; From 165f390250874b32ed8158150fe49d574297c9f9 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Sat, 3 Nov 2007 01:32:48 -0400 Subject: [PATCH 19/74] git-fetch: more terse fetch output This makes the fetch output much more terse and prettier on a 80 column display, based on a consensus reached on the mailing list. Here's an example output: Receiving objects: 100% (5439/5439), 1.60 MiB | 636 KiB/s, done. Resolving deltas: 100% (4604/4604), done. From git://git.kernel.org/pub/scm/git/git ! [rejected] html -> origin/html (non fast forward) 136e631..f45e867 maint -> origin/maint (fast forward) 9850e2e..44dd7e0 man -> origin/man (fast forward) 3e4bb08..e3d6d56 master -> origin/master (fast forward) fa3665c..536f64a next -> origin/next (fast forward) + 4f6d9d6...768326f pu -> origin/pu (forced update) * [new branch] todo -> origin/todo Some portions of this patch have been extracted from earlier proposals by Jeff King and Shawn Pearce. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-fetch.c | 114 +++++++++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 54 deletions(-) diff --git a/builtin-fetch.c b/builtin-fetch.c index 003ed76d1..960b1dae8 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -131,12 +131,6 @@ static struct ref *get_ref_map(struct transport *transport, return ref_map; } -static void show_new(enum object_type type, unsigned char *sha1_new) -{ - fprintf(stderr, " %s: %s\n", typename(type), - find_unique_abbrev(sha1_new, DEFAULT_ABBREV)); -} - static int s_update_ref(const char *action, struct ref *ref, int check_old) @@ -157,34 +151,38 @@ static int s_update_ref(const char *action, return 0; } +#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3) + static int update_local_ref(struct ref *ref, - const char *note, - int verbose) + const char *remote, + int verbose, + char *display) { - char oldh[41], newh[41]; struct commit *current = NULL, *updated; enum object_type type; struct branch *current_branch = branch_get(NULL); + const char *pretty_ref = ref->name + ( + !prefixcmp(ref->name, "refs/heads/") ? 11 : + !prefixcmp(ref->name, "refs/tags/") ? 10 : + !prefixcmp(ref->name, "refs/remotes/") ? 13 : + 0); + *display = 0; type = sha1_object_info(ref->new_sha1, NULL); if (type < 0) die("object %s not found", sha1_to_hex(ref->new_sha1)); if (!*ref->name) { /* Not storing */ - if (verbose) { - fprintf(stderr, "* fetched %s\n", note); - show_new(type, ref->new_sha1); - } + if (verbose) + sprintf(display, "* branch %s -> FETCH_HEAD", remote); return 0; } if (!hashcmp(ref->old_sha1, ref->new_sha1)) { - if (verbose) { - fprintf(stderr, "* %s: same as %s\n", - ref->name, note); - show_new(type, ref->new_sha1); - } + if (verbose) + sprintf(display, "= %-*s %s -> %s", SUMMARY_WIDTH, + "[up to date]", remote, pretty_ref); return 0; } @@ -196,63 +194,65 @@ static int update_local_ref(struct ref *ref, * If this is the head, and it's not okay to update * the head, and the old value of the head isn't empty... */ - fprintf(stderr, - " * %s: Cannot fetch into the current branch.\n", - ref->name); + sprintf(display, "! %-*s %s -> %s (can't fetch in current branch)", + SUMMARY_WIDTH, "[rejected]", remote, pretty_ref); return 1; } if (!is_null_sha1(ref->old_sha1) && !prefixcmp(ref->name, "refs/tags/")) { - fprintf(stderr, "* %s: updating with %s\n", - ref->name, note); - show_new(type, ref->new_sha1); + sprintf(display, "- %-*s %s -> %s", + SUMMARY_WIDTH, "[tag update]", remote, pretty_ref); return s_update_ref("updating tag", ref, 0); } current = lookup_commit_reference_gently(ref->old_sha1, 1); updated = lookup_commit_reference_gently(ref->new_sha1, 1); if (!current || !updated) { - char *msg; - if (!strncmp(ref->name, "refs/tags/", 10)) + const char *msg; + const char *what; + if (!strncmp(ref->name, "refs/tags/", 10)) { msg = "storing tag"; - else + what = "[new tag]"; + } + else { msg = "storing head"; - fprintf(stderr, "* %s: storing %s\n", - ref->name, note); - show_new(type, ref->new_sha1); + what = "[new branch]"; + } + + sprintf(display, "* %-*s %s -> %s", + SUMMARY_WIDTH, what, remote, pretty_ref); return s_update_ref(msg, ref, 0); } - strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); - strcpy(newh, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); - if (in_merge_bases(current, &updated, 1)) { - fprintf(stderr, "* %s: fast forward to %s\n", - ref->name, note); - fprintf(stderr, " old..new: %s..%s\n", oldh, newh); + char quickref[83]; + strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); + strcat(quickref, ".."); + strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); + sprintf(display, " %-*s %s -> %s (fast forward)", + SUMMARY_WIDTH, quickref, remote, pretty_ref); return s_update_ref("fast forward", ref, 1); - } - if (!force && !ref->force) { - fprintf(stderr, - "* %s: not updating to non-fast forward %s\n", - ref->name, note); - fprintf(stderr, - " old...new: %s...%s\n", oldh, newh); + } else if (force || ref->force) { + char quickref[84]; + strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); + strcat(quickref, "..."); + strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); + sprintf(display, "+ %-*s %s -> %s (forced update)", + SUMMARY_WIDTH, quickref, remote, pretty_ref); + return s_update_ref("forced-update", ref, 1); + } else { + sprintf(display, "! %-*s %s -> %s (non fast forward)", + SUMMARY_WIDTH, "[rejected]", remote, pretty_ref); return 1; } - fprintf(stderr, - "* %s: forcing update to non-fast forward %s\n", - ref->name, note); - fprintf(stderr, " old...new: %s...%s\n", oldh, newh); - return s_update_ref("forced-update", ref, 1); } static void store_updated_refs(const char *url, struct ref *ref_map) { FILE *fp; struct commit *commit; - int url_len, i, note_len; + int url_len, i, note_len, shown_url = 0; char note[1024]; const char *what, *kind; struct ref *rm; @@ -315,8 +315,17 @@ static void store_updated_refs(const char *url, struct ref *ref_map) rm->merge ? "" : "not-for-merge", note); - if (ref) - update_local_ref(ref, note, verbose); + if (ref) { + update_local_ref(ref, what, verbose, note); + if (*note) { + if (!shown_url) { + fprintf(stderr, "From %.*s\n", + url_len, url); + shown_url = 1; + } + fprintf(stderr, " %s\n", note); + } + } } fclose(fp); } @@ -376,9 +385,6 @@ static struct ref *find_non_local_tags(struct transport *transport, if (!path_list_has_path(&existing_refs, ref_name) && !path_list_has_path(&new_refs, ref_name) && lookup_object(ref->old_sha1)) { - fprintf(stderr, "Auto-following %s\n", - ref_name); - path_list_insert(ref_name, &new_refs); rm = alloc_ref(strlen(ref_name) + 1); From 00ae82895e7f30e52ff12edc1076409c7c53d99e Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 2 Nov 2007 22:39:44 -0400 Subject: [PATCH 20/74] errors: "strict subset" -> "ancestor" The term "ancestor" is a bit more intuitive (and more consistent with the documentation) than the term "strict subset". Also, remove superfluous "ref", capitalize, and add some carriage returns, changing: error: remote 'refs/heads/master' is not a strict subset of local ref 'refs/heads/master'. maybe you are not up-to-date and need to pull first? error: failed to push to 'ssh://linux-nfs.org/~bfields/exports/git.git' to: error: remote 'refs/heads/master' is not an ancestor of local 'refs/heads/master'. Maybe you are not up-to-date and need to pull first? error: failed to push to 'ssh://linux-nfs.org/~bfields/exports/git.git' Signed-off-by: J. Bruce Fields Signed-off-by: Junio C Hamano --- builtin-branch.c | 2 +- http-push.c | 15 ++++++++++----- send-pack.c | 6 +++--- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index d6d5cff6b..fbd90e42d 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -148,7 +148,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) if (!force && !in_merge_bases(rev, &head_rev, 1)) { - error("The branch '%s' is not a strict subset of " + error("The branch '%s' is not an ancestor of " "your current HEAD.\n" "If you are sure you want to delete it, " "run 'git branch -D %s'.", argv[i], argv[i]); diff --git a/http-push.c b/http-push.c index c02a3af63..9314621a1 100644 --- a/http-push.c +++ b/http-push.c @@ -2241,7 +2241,11 @@ static int delete_remote_branch(char *pattern, int force) /* Remote branch must be an ancestor of remote HEAD */ if (!verify_merge_base(head_sha1, remote_ref->old_sha1)) { - return error("The branch '%s' is not a strict subset of your current HEAD.\nIf you are sure you want to delete it, run:\n\t'git http-push -D %s %s'", remote_ref->name, remote->url, pattern); + return error("The branch '%s' is not an ancestor " + "of your current HEAD.\n" + "If you are sure you want to delete it," + " run:\n\t'git http-push -D %s %s'", + remote_ref->name, remote->url, pattern); } } @@ -2417,16 +2421,17 @@ int main(int argc, char **argv) if (!has_sha1_file(ref->old_sha1) || !ref_newer(ref->peer_ref->new_sha1, ref->old_sha1)) { - /* We do not have the remote ref, or + /* + * We do not have the remote ref, or * we know that the remote ref is not * an ancestor of what we are trying to * push. Either way this can be losing * commits at the remote end and likely * we were not up to date to begin with. */ - error("remote '%s' is not a strict " - "subset of local ref '%s'. " - "maybe you are not up-to-date and " + error("remote '%s' is not an ancestor of\n" + "local '%s'.\n" + "Maybe you are not up-to-date and " "need to pull first?", ref->name, ref->peer_ref->name); diff --git a/send-pack.c b/send-pack.c index 5e127a1b7..b74fd454f 100644 --- a/send-pack.c +++ b/send-pack.c @@ -297,9 +297,9 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha * commits at the remote end and likely * we were not up to date to begin with. */ - error("remote '%s' is not a strict " - "subset of local ref '%s'. " - "maybe you are not up-to-date and " + error("remote '%s' is not an ancestor of\n" + " local '%s'.\n" + " Maybe you are not up-to-date and " "need to pull first?", ref->name, ref->peer_ref->name); From a0554224a285b73b87fe1c95dbdff469561b182f Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Sat, 3 Nov 2007 11:55:02 +0000 Subject: [PATCH 21/74] git-cvsimport: really convert underscores in branch names to dots with -u The documentation states for the -u option that underscores in tag and branch names are converted to dots, but this was actually implemented for the tag names only. Kurt Roeckx reported this through http://bugs.debian.org/446495 Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- git-cvsimport.perl | 1 + 1 file changed, 1 insertion(+) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 2954fb846..e4bc2b54f 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -818,6 +818,7 @@ sub commit { $state = 4; } elsif ($state == 4 and s/^Branch:\s+//) { s/\s+$//; + tr/_/\./ if ( $opt_u ); s/[\/]/$opt_s/g; $branch = $_; $state = 5; From b92565dc5c505c3d01f3219fb7f61ebc70630a2c Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Sat, 3 Nov 2007 13:22:53 +0100 Subject: [PATCH 22/74] Delay pager setup in git blame This avoids to launch the pager when git blame fails for any reason. Signed-off-by: Mike Hommey Signed-off-by: Junio C Hamano --- builtin-blame.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin-blame.c b/builtin-blame.c index dc88a953a..e17b03d87 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -2231,9 +2231,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix) argv[unk++] = arg; } - if (!incremental) - setup_pager(); - if (!blame_move_score) blame_move_score = BLAME_DEFAULT_MOVE_SCORE; if (!blame_copy_score) @@ -2427,6 +2424,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix) read_mailmap(&mailmap, ".mailmap", NULL); + if (!incremental) + setup_pager(); + assign_blame(&sb, &revs, opt); if (incremental) From 4b7bbdd14c6b926fbb0dbd203ce02e150b308694 Mon Sep 17 00:00:00 2001 From: Steven Grimm Date: Sat, 3 Nov 2007 19:26:54 -0700 Subject: [PATCH 23/74] builtin-fetch: Add "-q" as a synonym for "--quiet" "-q" is the very first option described in the git-fetch manpage, and it isn't supported. Signed-off-by: Steven Grimm Signed-off-by: Junio C Hamano --- builtin-fetch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-fetch.c b/builtin-fetch.c index 003ed76d1..8e790552c 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -517,7 +517,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) depth = argv[i]; continue; } - if (!strcmp(arg, "--quiet")) { + if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) { quiet = 1; continue; } From aa807bc2663d69d6adb1d823231ba4b6d30d4860 Mon Sep 17 00:00:00 2001 From: Benoit Sigoure Date: Sat, 3 Nov 2007 19:53:34 +0100 Subject: [PATCH 24/74] git-svn: sort the options in the --help message. "git svn --help" gave options in the order they were found in a Perl hash, which meant "randomly" to humans. Signed-off-by: Benoit Sigoure Acked-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-svn.perl b/git-svn.perl index 22bb47b34..4900f57f1 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -252,7 +252,7 @@ sub usage { next if $cmd && $cmd ne $_; next if /^multi-/; # don't show deprecated commands print $fd ' ',pack('A17',$_),$cmd{$_}->[1],"\n"; - foreach (keys %{$cmd{$_}->[2]}) { + foreach (sort keys %{$cmd{$_}->[2]}) { # mixed-case options are for .git/config only next if /[A-Z]/ && /^[a-z]+$/i; # prints out arguments as they should be passed: From ee787400de25ed419f40e70698ba35db475b2d61 Mon Sep 17 00:00:00 2001 From: David D Kilzer Date: Sat, 3 Nov 2007 07:04:52 -0700 Subject: [PATCH 25/74] RelNotes-1.5.3.5: fix typo Signed-off-by: David D Kilzer Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.3.5.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/RelNotes-1.5.3.5.txt b/Documentation/RelNotes-1.5.3.5.txt index 4e46d2c2a..f99a2cd65 100644 --- a/Documentation/RelNotes-1.5.3.5.txt +++ b/Documentation/RelNotes-1.5.3.5.txt @@ -63,8 +63,8 @@ Fixes since v1.5.3.4 * Git segfaulted when reading an invalid .gitattributes file. Fixed. - * post-receive-email example hook fixed was fixed for - non-fast-forward updates. + * post-receive-email example hook was fixed for non-fast-forward + updates. * Documentation updates for supported (but previously undocumented) options of "git-archive" and "git-reflog". From 19391c371cec06da6cca45dbe3c212bd479cc48c Mon Sep 17 00:00:00 2001 From: Heikki Orsila Date: Thu, 1 Nov 2007 16:21:39 +0200 Subject: [PATCH 26/74] git-clone: honor "--" to end argument parsing Signed-off-by: Heikki Orsila --- Documentation/git-clone.txt | 2 +- git-clone.sh | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index cca14d6b5..14e58f386 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -12,7 +12,7 @@ SYNOPSIS 'git-clone' [--template=] [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [-o ] [-u ] [--reference ] - [--depth ] [] + [--depth ] [--] [] DESCRIPTION ----------- diff --git a/git-clone.sh b/git-clone.sh index 0ea3c24f5..3f0069360 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -14,7 +14,7 @@ die() { } usage() { - die "Usage: $0 [--template=] [--reference ] [--bare] [-l [-s]] [-q] [-u ] [--origin ] [--depth ] [-n] []" + die "Usage: $0 [--template=] [--reference ] [--bare] [-l [-s]] [-q] [-u ] [--origin ] [--depth ] [-n] [--] []" } get_repo_base() { @@ -160,6 +160,9 @@ while *,--depth) shift depth="--depth=$1";; + *,--) + shift + break ;; *,-*) usage ;; *) break ;; esac From 2e7a9785c27f00a8f7a06edc1d4c9b2f3fa2eeb9 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 3 Nov 2007 13:12:17 +0000 Subject: [PATCH 27/74] git-reset: do not be confused if there is nothing to reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The purpose of the function update_index_from_diff() (which is the callback function we give do_diff_cache()) is to update those index entries which differ from the given commit. Since do_diff_cache() plays games with the in-memory index, this function discarded the cache and reread it. Then, back in the function read_from_tree() we wrote the index. Of course, this broke down when there were no changes and update_index_from_diff() was not called, and therefore the mangled index was not discarded. The solution is to move the index writing into the function update_index_from_diff(). Noticed by Björn Steinbrink. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-reset.c | 24 ++++++++++++++++++------ t/t7102-reset.sh | 7 +++++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/builtin-reset.c b/builtin-reset.c index e1dc31e0e..5467e36c7 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -113,10 +113,17 @@ static int update_index_refresh(void) return run_command_v_opt(argv_update_index, RUN_GIT_CMD); } +struct update_cb_data { + int index_fd; + struct lock_file *lock; + int exit_code; +}; + static void update_index_from_diff(struct diff_queue_struct *q, struct diff_options *opt, void *data) { int i; + struct update_cb_data *cb = data; /* do_diff_cache() mangled the index */ discard_cache(); @@ -133,29 +140,34 @@ static void update_index_from_diff(struct diff_queue_struct *q, } else remove_file_from_cache(one->path); } + + cb->exit_code = write_cache(cb->index_fd, active_cache, active_nr) || + close(cb->index_fd) || + commit_locked_index(cb->lock); } static int read_from_tree(const char *prefix, const char **argv, unsigned char *tree_sha1) { - struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); - int index_fd; struct diff_options opt; + struct update_cb_data cb; memset(&opt, 0, sizeof(opt)); diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt); opt.output_format = DIFF_FORMAT_CALLBACK; opt.format_callback = update_index_from_diff; + opt.format_callback_data = &cb; - index_fd = hold_locked_index(lock, 1); + cb.lock = xcalloc(1, sizeof(struct lock_file)); + cb.index_fd = hold_locked_index(cb.lock, 1); + cb.exit_code = 0; read_cache(); if (do_diff_cache(tree_sha1, &opt)) return 1; diffcore_std(&opt); diff_flush(&opt); - return write_cache(index_fd, active_cache, active_nr) || - close(index_fd) || - commit_locked_index(lock); + + return cb.exit_code; } static void prepend_reflog_action(const char *action, char *buf, size_t size) diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index f64b1cbf7..cea9afb76 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -402,4 +402,11 @@ test_expect_success 'test resetting the index at give paths' ' ' +test_expect_success 'resetting an unmodified path is a no-op' ' + git reset --hard && + git reset -- file1 && + git diff-files --exit-code && + git diff-index --cached --exit-code HEAD +' + test_done From 562ca192f94496611583688beb3725603ce51f89 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 1 Nov 2007 17:32:04 -0700 Subject: [PATCH 28/74] clean: require -f to do damage by default This makes the clean.requireForce configuration default to true. Too many people are burned by typing "git clean" by mistake when they meant to say "make clean". Signed-off-by: Junio C Hamano --- Documentation/config.txt | 4 ++-- git-clean.sh | 6 +++++- t/t7201-co.sh | 12 ++++++------ t/t7300-clean.sh | 9 +++++++++ 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 7ee97df8a..aeb7961ad 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -338,8 +338,8 @@ branch..merge:: `.` (a period) for branch..remote. clean.requireForce:: - A boolean to make git-clean do nothing unless given -f or -n. Defaults - to false. + A boolean to make git-clean do nothing unless given -f + or -n. Defaults to true. color.branch:: A boolean to enable/disable color in the output of diff --git a/git-clean.sh b/git-clean.sh index 449173818..f4965b839 100755 --- a/git-clean.sh +++ b/git-clean.sh @@ -20,12 +20,16 @@ require_work_tree ignored= ignoredonly= cleandir= -disabled="`git config --bool clean.requireForce`" rmf="rm -f --" rmrf="rm -rf --" rm_refuse="echo Not removing" echo1="echo" +# requireForce used to default to false but now it defaults to true. +# IOW, lack of explicit "clean.requireForce = false" is taken as +# "clean.requireForce = true". +disabled=$(git config --bool clean.requireForce || echo true) + while test $# != 0 do case "$1" in diff --git a/t/t7201-co.sh b/t/t7201-co.sh index ed2e9ee3c..55558aba8 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -77,7 +77,7 @@ test_expect_success "checkout with dirty tree without -m" ' test_expect_success "checkout -m with dirty tree" ' git checkout -f master && - git clean && + git clean -f && fill 0 1 2 3 4 5 6 7 8 >one && git checkout -m side && @@ -99,7 +99,7 @@ test_expect_success "checkout -m with dirty tree" ' test_expect_success "checkout -m with dirty tree, renamed" ' - git checkout -f master && git clean && + git checkout -f master && git clean -f && fill 1 2 3 4 5 7 8 >one && if git checkout renamer @@ -121,7 +121,7 @@ test_expect_success "checkout -m with dirty tree, renamed" ' test_expect_success 'checkout -m with merge conflict' ' - git checkout -f master && git clean && + git checkout -f master && git clean -f && fill 1 T 3 4 5 6 S 8 >one && if git checkout renamer @@ -144,7 +144,7 @@ test_expect_success 'checkout -m with merge conflict' ' test_expect_success 'checkout to detach HEAD' ' - git checkout -f renamer && git clean && + git checkout -f renamer && git clean -f && git checkout renamer^ && H=$(git rev-parse --verify HEAD) && M=$(git show-ref -s --verify refs/heads/master) && @@ -160,7 +160,7 @@ test_expect_success 'checkout to detach HEAD' ' test_expect_success 'checkout to detach HEAD with branchname^' ' - git checkout -f master && git clean && + git checkout -f master && git clean -f && git checkout renamer^ && H=$(git rev-parse --verify HEAD) && M=$(git show-ref -s --verify refs/heads/master) && @@ -176,7 +176,7 @@ test_expect_success 'checkout to detach HEAD with branchname^' ' test_expect_success 'checkout to detach HEAD with HEAD^0' ' - git checkout -f master && git clean && + git checkout -f master && git clean -f && git checkout HEAD^0 && H=$(git rev-parse --verify HEAD) && M=$(git show-ref -s --verify refs/heads/master) && diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index eb0847afe..8697213e6 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@ -7,6 +7,8 @@ test_description='git-clean basic tests' . ./test-lib.sh +git config clean.requireForce no + test_expect_success 'setup' ' mkdir -p src && @@ -139,6 +141,13 @@ test_expect_success 'git-clean -d -X' ' ' +test_expect_success 'clean.requireForce defaults to true' ' + + git config --unset clean.requireForce && + ! git-clean + +' + test_expect_success 'clean.requireForce' ' git config clean.requireForce true && From c2015b3ae0d52ccae33ee00c2b25b8402c66bdf0 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Sun, 4 Nov 2007 21:26:22 +0100 Subject: [PATCH 29/74] Fix an infinite loop in sq_quote_buf(). sq_quote_buf() treats single-quotes and exclamation marks specially, but it incorrectly parsed the input for single-quotes and backslashes. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- quote.c | 2 +- t/t5510-fetch.sh | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/quote.c b/quote.c index 482be05b7..919d0920a 100644 --- a/quote.c +++ b/quote.c @@ -26,7 +26,7 @@ void sq_quote_buf(struct strbuf *dst, const char *src) strbuf_addch(dst, '\''); while (*src) { - size_t len = strcspn(src, "'\\"); + size_t len = strcspn(src, "'!"); strbuf_add(dst, src, len); src += len; while (need_bs_quote(*src)) { diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index d21765714..aad863db7 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -208,4 +208,11 @@ test_expect_success 'fetch with a non-applying branch..merge' ' git fetch blub ' +# the strange name is: a\!'b +test_expect_success 'quoting of a strangely named repo' ' + ! git fetch "a\\!'\''b" > result 2>&1 && + cat result && + grep "fatal: '\''a\\\\!'\''b'\''" result +' + test_done From 93fc05eb9ef505a05d9ce9415177697449afc8ad Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 4 Nov 2007 19:15:06 +0000 Subject: [PATCH 30/74] Split off the pretty print stuff into its own file The file commit.c got quite large, but it does not have to be: the code concerning pretty printing is pretty well contained. In fact, this commit just splits it off into pretty.c, leaving commit.c with just 672 lines. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Makefile | 2 +- commit.c | 718 ------------------------------------------------------ pretty.c | 723 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 724 insertions(+), 719 deletions(-) create mode 100644 pretty.c diff --git a/Makefile b/Makefile index 042f79ef8..cf8ec49d1 100644 --- a/Makefile +++ b/Makefile @@ -299,7 +299,7 @@ DIFF_OBJS = \ LIB_OBJS = \ blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \ date.o diff-delta.o entry.o exec_cmd.o ident.o \ - interpolate.o hash.o \ + pretty.o interpolate.o hash.o \ lockfile.o \ patch-ids.o \ object.o pack-check.o pack-write.o patch-delta.o path.o pkt-line.o \ diff --git a/commit.c b/commit.c index 8262f6ac5..b50926587 100644 --- a/commit.c +++ b/commit.c @@ -3,7 +3,6 @@ #include "commit.h" #include "pkt-line.h" #include "utf8.h" -#include "interpolate.h" #include "diff.h" #include "revision.h" @@ -27,46 +26,6 @@ struct sort_node const char *commit_type = "commit"; -static struct cmt_fmt_map { - const char *n; - size_t cmp_len; - enum cmit_fmt v; -} cmt_fmts[] = { - { "raw", 1, CMIT_FMT_RAW }, - { "medium", 1, CMIT_FMT_MEDIUM }, - { "short", 1, CMIT_FMT_SHORT }, - { "email", 1, CMIT_FMT_EMAIL }, - { "full", 5, CMIT_FMT_FULL }, - { "fuller", 5, CMIT_FMT_FULLER }, - { "oneline", 1, CMIT_FMT_ONELINE }, - { "format:", 7, CMIT_FMT_USERFORMAT}, -}; - -static char *user_format; - -enum cmit_fmt get_commit_format(const char *arg) -{ - int i; - - if (!arg || !*arg) - return CMIT_FMT_DEFAULT; - if (*arg == '=') - arg++; - if (!prefixcmp(arg, "format:")) { - if (user_format) - free(user_format); - user_format = xstrdup(arg + 7); - return CMIT_FMT_USERFORMAT; - } - for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) { - if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) && - !strncmp(arg, cmt_fmts[i].n, strlen(arg))) - return cmt_fmts[i].v; - } - - die("invalid --pretty format: %s", arg); -} - static struct commit *check_commit(struct object *obj, const unsigned char *sha1, int quiet) @@ -460,683 +419,6 @@ void clear_commit_marks(struct commit *commit, unsigned int mark) } } -/* - * Generic support for pretty-printing the header - */ -static int get_one_line(const char *msg) -{ - int ret = 0; - - for (;;) { - char c = *msg++; - if (!c) - break; - ret++; - if (c == '\n') - break; - } - return ret; -} - -/* High bit set, or ISO-2022-INT */ -int non_ascii(int ch) -{ - ch = (ch & 0xff); - return ((ch & 0x80) || (ch == 0x1b)); -} - -static int is_rfc2047_special(char ch) -{ - return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_')); -} - -static void add_rfc2047(struct strbuf *sb, const char *line, int len, - const char *encoding) -{ - int i, last; - - for (i = 0; i < len; i++) { - int ch = line[i]; - if (non_ascii(ch)) - goto needquote; - if ((i + 1 < len) && (ch == '=' && line[i+1] == '?')) - goto needquote; - } - strbuf_add(sb, line, len); - return; - -needquote: - strbuf_grow(sb, len * 3 + strlen(encoding) + 100); - strbuf_addf(sb, "=?%s?q?", encoding); - for (i = last = 0; i < len; i++) { - unsigned ch = line[i] & 0xFF; - /* - * We encode ' ' using '=20' even though rfc2047 - * allows using '_' for readability. Unfortunately, - * many programs do not understand this and just - * leave the underscore in place. - */ - if (is_rfc2047_special(ch) || ch == ' ') { - strbuf_add(sb, line + last, i - last); - strbuf_addf(sb, "=%02X", ch); - last = i + 1; - } - } - strbuf_add(sb, line + last, len - last); - strbuf_addstr(sb, "?="); -} - -static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, - const char *line, enum date_mode dmode, - const char *encoding) -{ - char *date; - int namelen; - unsigned long time; - int tz; - const char *filler = " "; - - if (fmt == CMIT_FMT_ONELINE) - return; - date = strchr(line, '>'); - if (!date) - return; - namelen = ++date - line; - time = strtoul(date, &date, 10); - tz = strtol(date, NULL, 10); - - if (fmt == CMIT_FMT_EMAIL) { - char *name_tail = strchr(line, '<'); - int display_name_length; - if (!name_tail) - return; - while (line < name_tail && isspace(name_tail[-1])) - name_tail--; - display_name_length = name_tail - line; - filler = ""; - strbuf_addstr(sb, "From: "); - add_rfc2047(sb, line, display_name_length, encoding); - strbuf_add(sb, name_tail, namelen - display_name_length); - strbuf_addch(sb, '\n'); - } else { - strbuf_addf(sb, "%s: %.*s%.*s\n", what, - (fmt == CMIT_FMT_FULLER) ? 4 : 0, - filler, namelen, line); - } - switch (fmt) { - case CMIT_FMT_MEDIUM: - strbuf_addf(sb, "Date: %s\n", show_date(time, tz, dmode)); - break; - case CMIT_FMT_EMAIL: - strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822)); - break; - case CMIT_FMT_FULLER: - strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode)); - break; - default: - /* notin' */ - break; - } -} - -static int is_empty_line(const char *line, int *len_p) -{ - int len = *len_p; - while (len && isspace(line[len-1])) - len--; - *len_p = len; - return !len; -} - -static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb, - const struct commit *commit, int abbrev) -{ - struct commit_list *parent = commit->parents; - - if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) || - !parent || !parent->next) - return; - - strbuf_addstr(sb, "Merge:"); - - while (parent) { - struct commit *p = parent->item; - const char *hex = NULL; - const char *dots; - if (abbrev) - hex = find_unique_abbrev(p->object.sha1, abbrev); - if (!hex) - hex = sha1_to_hex(p->object.sha1); - dots = (abbrev && strlen(hex) != 40) ? "..." : ""; - parent = parent->next; - - strbuf_addf(sb, " %s%s", hex, dots); - } - strbuf_addch(sb, '\n'); -} - -static char *get_header(const struct commit *commit, const char *key) -{ - int key_len = strlen(key); - const char *line = commit->buffer; - - for (;;) { - const char *eol = strchr(line, '\n'), *next; - - if (line == eol) - return NULL; - if (!eol) { - eol = line + strlen(line); - next = NULL; - } else - next = eol + 1; - if (eol - line > key_len && - !strncmp(line, key, key_len) && - line[key_len] == ' ') { - return xmemdupz(line + key_len + 1, eol - line - key_len - 1); - } - line = next; - } -} - -static char *replace_encoding_header(char *buf, const char *encoding) -{ - struct strbuf tmp; - size_t start, len; - char *cp = buf; - - /* guess if there is an encoding header before a \n\n */ - while (strncmp(cp, "encoding ", strlen("encoding "))) { - cp = strchr(cp, '\n'); - if (!cp || *++cp == '\n') - return buf; - } - start = cp - buf; - cp = strchr(cp, '\n'); - if (!cp) - return buf; /* should not happen but be defensive */ - len = cp + 1 - (buf + start); - - strbuf_init(&tmp, 0); - strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1); - if (is_encoding_utf8(encoding)) { - /* we have re-coded to UTF-8; drop the header */ - strbuf_remove(&tmp, start, len); - } else { - /* just replaces XXXX in 'encoding XXXX\n' */ - strbuf_splice(&tmp, start + strlen("encoding "), - len - strlen("encoding \n"), - encoding, strlen(encoding)); - } - return strbuf_detach(&tmp, NULL); -} - -static char *logmsg_reencode(const struct commit *commit, - const char *output_encoding) -{ - static const char *utf8 = "utf-8"; - const char *use_encoding; - char *encoding; - char *out; - - if (!*output_encoding) - return NULL; - encoding = get_header(commit, "encoding"); - use_encoding = encoding ? encoding : utf8; - if (!strcmp(use_encoding, output_encoding)) - if (encoding) /* we'll strip encoding header later */ - out = xstrdup(commit->buffer); - else - return NULL; /* nothing to do */ - else - out = reencode_string(commit->buffer, - output_encoding, use_encoding); - if (out) - out = replace_encoding_header(out, output_encoding); - - free(encoding); - return out; -} - -static void fill_person(struct interp *table, const char *msg, int len) -{ - int start, end, tz = 0; - unsigned long date; - char *ep; - - /* parse name */ - for (end = 0; end < len && msg[end] != '<'; end++) - ; /* do nothing */ - start = end + 1; - while (end > 0 && isspace(msg[end - 1])) - end--; - table[0].value = xmemdupz(msg, end); - - if (start >= len) - return; - - /* parse email */ - for (end = start + 1; end < len && msg[end] != '>'; end++) - ; /* do nothing */ - - if (end >= len) - return; - - table[1].value = xmemdupz(msg + start, end - start); - - /* parse date */ - for (start = end + 1; start < len && isspace(msg[start]); start++) - ; /* do nothing */ - if (start >= len) - return; - date = strtoul(msg + start, &ep, 10); - if (msg + start == ep) - return; - - table[5].value = xmemdupz(msg + start, ep - (msg + start)); - - /* parse tz */ - for (start = ep - msg + 1; start < len && isspace(msg[start]); start++) - ; /* do nothing */ - if (start + 1 < len) { - tz = strtoul(msg + start + 1, NULL, 10); - if (msg[start] == '-') - tz = -tz; - } - - interp_set_entry(table, 2, show_date(date, tz, DATE_NORMAL)); - interp_set_entry(table, 3, show_date(date, tz, DATE_RFC2822)); - interp_set_entry(table, 4, show_date(date, tz, DATE_RELATIVE)); - interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601)); -} - -void format_commit_message(const struct commit *commit, - const void *format, struct strbuf *sb) -{ - struct interp table[] = { - { "%H" }, /* commit hash */ - { "%h" }, /* abbreviated commit hash */ - { "%T" }, /* tree hash */ - { "%t" }, /* abbreviated tree hash */ - { "%P" }, /* parent hashes */ - { "%p" }, /* abbreviated parent hashes */ - { "%an" }, /* author name */ - { "%ae" }, /* author email */ - { "%ad" }, /* author date */ - { "%aD" }, /* author date, RFC2822 style */ - { "%ar" }, /* author date, relative */ - { "%at" }, /* author date, UNIX timestamp */ - { "%ai" }, /* author date, ISO 8601 */ - { "%cn" }, /* committer name */ - { "%ce" }, /* committer email */ - { "%cd" }, /* committer date */ - { "%cD" }, /* committer date, RFC2822 style */ - { "%cr" }, /* committer date, relative */ - { "%ct" }, /* committer date, UNIX timestamp */ - { "%ci" }, /* committer date, ISO 8601 */ - { "%e" }, /* encoding */ - { "%s" }, /* subject */ - { "%b" }, /* body */ - { "%Cred" }, /* red */ - { "%Cgreen" }, /* green */ - { "%Cblue" }, /* blue */ - { "%Creset" }, /* reset color */ - { "%n" }, /* newline */ - { "%m" }, /* left/right/bottom */ - }; - enum interp_index { - IHASH = 0, IHASH_ABBREV, - ITREE, ITREE_ABBREV, - IPARENTS, IPARENTS_ABBREV, - IAUTHOR_NAME, IAUTHOR_EMAIL, - IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE, - IAUTHOR_TIMESTAMP, IAUTHOR_ISO8601, - ICOMMITTER_NAME, ICOMMITTER_EMAIL, - ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822, - ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP, - ICOMMITTER_ISO8601, - IENCODING, - ISUBJECT, - IBODY, - IRED, IGREEN, IBLUE, IRESET_COLOR, - INEWLINE, - ILEFT_RIGHT, - }; - struct commit_list *p; - char parents[1024]; - unsigned long len; - int i; - enum { HEADER, SUBJECT, BODY } state; - const char *msg = commit->buffer; - - if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table)) - die("invalid interp table!"); - - /* these are independent of the commit */ - interp_set_entry(table, IRED, "\033[31m"); - interp_set_entry(table, IGREEN, "\033[32m"); - interp_set_entry(table, IBLUE, "\033[34m"); - interp_set_entry(table, IRESET_COLOR, "\033[m"); - interp_set_entry(table, INEWLINE, "\n"); - - /* these depend on the commit */ - if (!commit->object.parsed) - parse_object(commit->object.sha1); - interp_set_entry(table, IHASH, sha1_to_hex(commit->object.sha1)); - interp_set_entry(table, IHASH_ABBREV, - find_unique_abbrev(commit->object.sha1, - DEFAULT_ABBREV)); - interp_set_entry(table, ITREE, sha1_to_hex(commit->tree->object.sha1)); - interp_set_entry(table, ITREE_ABBREV, - find_unique_abbrev(commit->tree->object.sha1, - DEFAULT_ABBREV)); - interp_set_entry(table, ILEFT_RIGHT, - (commit->object.flags & BOUNDARY) - ? "-" - : (commit->object.flags & SYMMETRIC_LEFT) - ? "<" - : ">"); - - parents[1] = 0; - for (i = 0, p = commit->parents; - p && i < sizeof(parents) - 1; - p = p->next) - i += snprintf(parents + i, sizeof(parents) - i - 1, " %s", - sha1_to_hex(p->item->object.sha1)); - interp_set_entry(table, IPARENTS, parents + 1); - - parents[1] = 0; - for (i = 0, p = commit->parents; - p && i < sizeof(parents) - 1; - p = p->next) - i += snprintf(parents + i, sizeof(parents) - i - 1, " %s", - find_unique_abbrev(p->item->object.sha1, - DEFAULT_ABBREV)); - interp_set_entry(table, IPARENTS_ABBREV, parents + 1); - - for (i = 0, state = HEADER; msg[i] && state < BODY; i++) { - int eol; - for (eol = i; msg[eol] && msg[eol] != '\n'; eol++) - ; /* do nothing */ - - if (state == SUBJECT) { - table[ISUBJECT].value = xmemdupz(msg + i, eol - i); - i = eol; - } - if (i == eol) { - state++; - /* strip empty lines */ - while (msg[eol + 1] == '\n') - eol++; - } else if (!prefixcmp(msg + i, "author ")) - fill_person(table + IAUTHOR_NAME, - msg + i + 7, eol - i - 7); - else if (!prefixcmp(msg + i, "committer ")) - fill_person(table + ICOMMITTER_NAME, - msg + i + 10, eol - i - 10); - else if (!prefixcmp(msg + i, "encoding ")) - table[IENCODING].value = - xmemdupz(msg + i + 9, eol - i - 9); - i = eol; - } - if (msg[i]) - table[IBODY].value = xstrdup(msg + i); - - len = interpolate(sb->buf + sb->len, strbuf_avail(sb), - format, table, ARRAY_SIZE(table)); - if (len > strbuf_avail(sb)) { - strbuf_grow(sb, len); - interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1, - format, table, ARRAY_SIZE(table)); - } - strbuf_setlen(sb, sb->len + len); - interp_clear_table(table, ARRAY_SIZE(table)); -} - -static void pp_header(enum cmit_fmt fmt, - int abbrev, - enum date_mode dmode, - const char *encoding, - const struct commit *commit, - const char **msg_p, - struct strbuf *sb) -{ - int parents_shown = 0; - - for (;;) { - const char *line = *msg_p; - int linelen = get_one_line(*msg_p); - - if (!linelen) - return; - *msg_p += linelen; - - if (linelen == 1) - /* End of header */ - return; - - if (fmt == CMIT_FMT_RAW) { - strbuf_add(sb, line, linelen); - continue; - } - - if (!memcmp(line, "parent ", 7)) { - if (linelen != 48) - die("bad parent line in commit"); - continue; - } - - if (!parents_shown) { - struct commit_list *parent; - int num; - for (parent = commit->parents, num = 0; - parent; - parent = parent->next, num++) - ; - /* with enough slop */ - strbuf_grow(sb, num * 50 + 20); - add_merge_info(fmt, sb, commit, abbrev); - parents_shown = 1; - } - - /* - * MEDIUM == DEFAULT shows only author with dates. - * FULL shows both authors but not dates. - * FULLER shows both authors and dates. - */ - if (!memcmp(line, "author ", 7)) { - strbuf_grow(sb, linelen + 80); - add_user_info("Author", fmt, sb, line + 7, dmode, encoding); - } - if (!memcmp(line, "committer ", 10) && - (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) { - strbuf_grow(sb, linelen + 80); - add_user_info("Commit", fmt, sb, line + 10, dmode, encoding); - } - } -} - -static void pp_title_line(enum cmit_fmt fmt, - const char **msg_p, - struct strbuf *sb, - const char *subject, - const char *after_subject, - const char *encoding, - int plain_non_ascii) -{ - struct strbuf title; - - strbuf_init(&title, 80); - - for (;;) { - const char *line = *msg_p; - int linelen = get_one_line(line); - - *msg_p += linelen; - if (!linelen || is_empty_line(line, &linelen)) - break; - - strbuf_grow(&title, linelen + 2); - if (title.len) { - if (fmt == CMIT_FMT_EMAIL) { - strbuf_addch(&title, '\n'); - } - strbuf_addch(&title, ' '); - } - strbuf_add(&title, line, linelen); - } - - strbuf_grow(sb, title.len + 1024); - if (subject) { - strbuf_addstr(sb, subject); - add_rfc2047(sb, title.buf, title.len, encoding); - } else { - strbuf_addbuf(sb, &title); - } - strbuf_addch(sb, '\n'); - - if (plain_non_ascii) { - const char *header_fmt = - "MIME-Version: 1.0\n" - "Content-Type: text/plain; charset=%s\n" - "Content-Transfer-Encoding: 8bit\n"; - strbuf_addf(sb, header_fmt, encoding); - } - if (after_subject) { - strbuf_addstr(sb, after_subject); - } - if (fmt == CMIT_FMT_EMAIL) { - strbuf_addch(sb, '\n'); - } - strbuf_release(&title); -} - -static void pp_remainder(enum cmit_fmt fmt, - const char **msg_p, - struct strbuf *sb, - int indent) -{ - int first = 1; - for (;;) { - const char *line = *msg_p; - int linelen = get_one_line(line); - *msg_p += linelen; - - if (!linelen) - break; - - if (is_empty_line(line, &linelen)) { - if (first) - continue; - if (fmt == CMIT_FMT_SHORT) - break; - } - first = 0; - - strbuf_grow(sb, linelen + indent + 20); - if (indent) { - memset(sb->buf + sb->len, ' ', indent); - strbuf_setlen(sb, sb->len + indent); - } - strbuf_add(sb, line, linelen); - strbuf_addch(sb, '\n'); - } -} - -void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, - struct strbuf *sb, int abbrev, - const char *subject, const char *after_subject, - enum date_mode dmode, int plain_non_ascii) -{ - unsigned long beginning_of_body; - int indent = 4; - const char *msg = commit->buffer; - char *reencoded; - const char *encoding; - - if (fmt == CMIT_FMT_USERFORMAT) { - format_commit_message(commit, user_format, sb); - return; - } - - encoding = (git_log_output_encoding - ? git_log_output_encoding - : git_commit_encoding); - if (!encoding) - encoding = "utf-8"; - reencoded = logmsg_reencode(commit, encoding); - if (reencoded) { - msg = reencoded; - } - - if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) - indent = 0; - - /* After-subject is used to pass in Content-Type: multipart - * MIME header; in that case we do not have to do the - * plaintext content type even if the commit message has - * non 7-bit ASCII character. Otherwise, check if we need - * to say this is not a 7-bit ASCII. - */ - if (fmt == CMIT_FMT_EMAIL && !after_subject) { - int i, ch, in_body; - - for (in_body = i = 0; (ch = msg[i]); i++) { - if (!in_body) { - /* author could be non 7-bit ASCII but - * the log may be so; skip over the - * header part first. - */ - if (ch == '\n' && msg[i+1] == '\n') - in_body = 1; - } - else if (non_ascii(ch)) { - plain_non_ascii = 1; - break; - } - } - } - - pp_header(fmt, abbrev, dmode, encoding, commit, &msg, sb); - if (fmt != CMIT_FMT_ONELINE && !subject) { - strbuf_addch(sb, '\n'); - } - - /* Skip excess blank lines at the beginning of body, if any... */ - for (;;) { - int linelen = get_one_line(msg); - int ll = linelen; - if (!linelen) - break; - if (!is_empty_line(msg, &ll)) - break; - msg += linelen; - } - - /* These formats treat the title line specially. */ - if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) - pp_title_line(fmt, &msg, sb, subject, - after_subject, encoding, plain_non_ascii); - - beginning_of_body = sb->len; - if (fmt != CMIT_FMT_ONELINE) - pp_remainder(fmt, &msg, sb, indent); - strbuf_rtrim(sb); - - /* Make sure there is an EOLN for the non-oneline case */ - if (fmt != CMIT_FMT_ONELINE) - strbuf_addch(sb, '\n'); - - /* - * The caller may append additional body text in e-mail - * format. Make sure we did not strip the blank line - * between the header and the body. - */ - if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body) - strbuf_addch(sb, '\n'); - free(reencoded); -} - struct commit *pop_commit(struct commit_list **stack) { struct commit_list *top = *stack; diff --git a/pretty.c b/pretty.c new file mode 100644 index 000000000..490cede26 --- /dev/null +++ b/pretty.c @@ -0,0 +1,723 @@ +#include "cache.h" +#include "commit.h" +#include "interpolate.h" +#include "utf8.h" +#include "diff.h" +#include "revision.h" + +static struct cmt_fmt_map { + const char *n; + size_t cmp_len; + enum cmit_fmt v; +} cmt_fmts[] = { + { "raw", 1, CMIT_FMT_RAW }, + { "medium", 1, CMIT_FMT_MEDIUM }, + { "short", 1, CMIT_FMT_SHORT }, + { "email", 1, CMIT_FMT_EMAIL }, + { "full", 5, CMIT_FMT_FULL }, + { "fuller", 5, CMIT_FMT_FULLER }, + { "oneline", 1, CMIT_FMT_ONELINE }, + { "format:", 7, CMIT_FMT_USERFORMAT}, +}; + +static char *user_format; + +enum cmit_fmt get_commit_format(const char *arg) +{ + int i; + + if (!arg || !*arg) + return CMIT_FMT_DEFAULT; + if (*arg == '=') + arg++; + if (!prefixcmp(arg, "format:")) { + if (user_format) + free(user_format); + user_format = xstrdup(arg + 7); + return CMIT_FMT_USERFORMAT; + } + for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) { + if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) && + !strncmp(arg, cmt_fmts[i].n, strlen(arg))) + return cmt_fmts[i].v; + } + + die("invalid --pretty format: %s", arg); +} + +/* + * Generic support for pretty-printing the header + */ +static int get_one_line(const char *msg) +{ + int ret = 0; + + for (;;) { + char c = *msg++; + if (!c) + break; + ret++; + if (c == '\n') + break; + } + return ret; +} + +/* High bit set, or ISO-2022-INT */ +int non_ascii(int ch) +{ + ch = (ch & 0xff); + return ((ch & 0x80) || (ch == 0x1b)); +} + +static int is_rfc2047_special(char ch) +{ + return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_')); +} + +static void add_rfc2047(struct strbuf *sb, const char *line, int len, + const char *encoding) +{ + int i, last; + + for (i = 0; i < len; i++) { + int ch = line[i]; + if (non_ascii(ch)) + goto needquote; + if ((i + 1 < len) && (ch == '=' && line[i+1] == '?')) + goto needquote; + } + strbuf_add(sb, line, len); + return; + +needquote: + strbuf_grow(sb, len * 3 + strlen(encoding) + 100); + strbuf_addf(sb, "=?%s?q?", encoding); + for (i = last = 0; i < len; i++) { + unsigned ch = line[i] & 0xFF; + /* + * We encode ' ' using '=20' even though rfc2047 + * allows using '_' for readability. Unfortunately, + * many programs do not understand this and just + * leave the underscore in place. + */ + if (is_rfc2047_special(ch) || ch == ' ') { + strbuf_add(sb, line + last, i - last); + strbuf_addf(sb, "=%02X", ch); + last = i + 1; + } + } + strbuf_add(sb, line + last, len - last); + strbuf_addstr(sb, "?="); +} + +static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, + const char *line, enum date_mode dmode, + const char *encoding) +{ + char *date; + int namelen; + unsigned long time; + int tz; + const char *filler = " "; + + if (fmt == CMIT_FMT_ONELINE) + return; + date = strchr(line, '>'); + if (!date) + return; + namelen = ++date - line; + time = strtoul(date, &date, 10); + tz = strtol(date, NULL, 10); + + if (fmt == CMIT_FMT_EMAIL) { + char *name_tail = strchr(line, '<'); + int display_name_length; + if (!name_tail) + return; + while (line < name_tail && isspace(name_tail[-1])) + name_tail--; + display_name_length = name_tail - line; + filler = ""; + strbuf_addstr(sb, "From: "); + add_rfc2047(sb, line, display_name_length, encoding); + strbuf_add(sb, name_tail, namelen - display_name_length); + strbuf_addch(sb, '\n'); + } else { + strbuf_addf(sb, "%s: %.*s%.*s\n", what, + (fmt == CMIT_FMT_FULLER) ? 4 : 0, + filler, namelen, line); + } + switch (fmt) { + case CMIT_FMT_MEDIUM: + strbuf_addf(sb, "Date: %s\n", show_date(time, tz, dmode)); + break; + case CMIT_FMT_EMAIL: + strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822)); + break; + case CMIT_FMT_FULLER: + strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode)); + break; + default: + /* notin' */ + break; + } +} + +static int is_empty_line(const char *line, int *len_p) +{ + int len = *len_p; + while (len && isspace(line[len-1])) + len--; + *len_p = len; + return !len; +} + +static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb, + const struct commit *commit, int abbrev) +{ + struct commit_list *parent = commit->parents; + + if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) || + !parent || !parent->next) + return; + + strbuf_addstr(sb, "Merge:"); + + while (parent) { + struct commit *p = parent->item; + const char *hex = NULL; + const char *dots; + if (abbrev) + hex = find_unique_abbrev(p->object.sha1, abbrev); + if (!hex) + hex = sha1_to_hex(p->object.sha1); + dots = (abbrev && strlen(hex) != 40) ? "..." : ""; + parent = parent->next; + + strbuf_addf(sb, " %s%s", hex, dots); + } + strbuf_addch(sb, '\n'); +} + +static char *get_header(const struct commit *commit, const char *key) +{ + int key_len = strlen(key); + const char *line = commit->buffer; + + for (;;) { + const char *eol = strchr(line, '\n'), *next; + + if (line == eol) + return NULL; + if (!eol) { + eol = line + strlen(line); + next = NULL; + } else + next = eol + 1; + if (eol - line > key_len && + !strncmp(line, key, key_len) && + line[key_len] == ' ') { + return xmemdupz(line + key_len + 1, eol - line - key_len - 1); + } + line = next; + } +} + +static char *replace_encoding_header(char *buf, const char *encoding) +{ + struct strbuf tmp; + size_t start, len; + char *cp = buf; + + /* guess if there is an encoding header before a \n\n */ + while (strncmp(cp, "encoding ", strlen("encoding "))) { + cp = strchr(cp, '\n'); + if (!cp || *++cp == '\n') + return buf; + } + start = cp - buf; + cp = strchr(cp, '\n'); + if (!cp) + return buf; /* should not happen but be defensive */ + len = cp + 1 - (buf + start); + + strbuf_init(&tmp, 0); + strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1); + if (is_encoding_utf8(encoding)) { + /* we have re-coded to UTF-8; drop the header */ + strbuf_remove(&tmp, start, len); + } else { + /* just replaces XXXX in 'encoding XXXX\n' */ + strbuf_splice(&tmp, start + strlen("encoding "), + len - strlen("encoding \n"), + encoding, strlen(encoding)); + } + return strbuf_detach(&tmp, NULL); +} + +static char *logmsg_reencode(const struct commit *commit, + const char *output_encoding) +{ + static const char *utf8 = "utf-8"; + const char *use_encoding; + char *encoding; + char *out; + + if (!*output_encoding) + return NULL; + encoding = get_header(commit, "encoding"); + use_encoding = encoding ? encoding : utf8; + if (!strcmp(use_encoding, output_encoding)) + if (encoding) /* we'll strip encoding header later */ + out = xstrdup(commit->buffer); + else + return NULL; /* nothing to do */ + else + out = reencode_string(commit->buffer, + output_encoding, use_encoding); + if (out) + out = replace_encoding_header(out, output_encoding); + + free(encoding); + return out; +} + +static void fill_person(struct interp *table, const char *msg, int len) +{ + int start, end, tz = 0; + unsigned long date; + char *ep; + + /* parse name */ + for (end = 0; end < len && msg[end] != '<'; end++) + ; /* do nothing */ + start = end + 1; + while (end > 0 && isspace(msg[end - 1])) + end--; + table[0].value = xmemdupz(msg, end); + + if (start >= len) + return; + + /* parse email */ + for (end = start + 1; end < len && msg[end] != '>'; end++) + ; /* do nothing */ + + if (end >= len) + return; + + table[1].value = xmemdupz(msg + start, end - start); + + /* parse date */ + for (start = end + 1; start < len && isspace(msg[start]); start++) + ; /* do nothing */ + if (start >= len) + return; + date = strtoul(msg + start, &ep, 10); + if (msg + start == ep) + return; + + table[5].value = xmemdupz(msg + start, ep - (msg + start)); + + /* parse tz */ + for (start = ep - msg + 1; start < len && isspace(msg[start]); start++) + ; /* do nothing */ + if (start + 1 < len) { + tz = strtoul(msg + start + 1, NULL, 10); + if (msg[start] == '-') + tz = -tz; + } + + interp_set_entry(table, 2, show_date(date, tz, DATE_NORMAL)); + interp_set_entry(table, 3, show_date(date, tz, DATE_RFC2822)); + interp_set_entry(table, 4, show_date(date, tz, DATE_RELATIVE)); + interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601)); +} + +void format_commit_message(const struct commit *commit, + const void *format, struct strbuf *sb) +{ + struct interp table[] = { + { "%H" }, /* commit hash */ + { "%h" }, /* abbreviated commit hash */ + { "%T" }, /* tree hash */ + { "%t" }, /* abbreviated tree hash */ + { "%P" }, /* parent hashes */ + { "%p" }, /* abbreviated parent hashes */ + { "%an" }, /* author name */ + { "%ae" }, /* author email */ + { "%ad" }, /* author date */ + { "%aD" }, /* author date, RFC2822 style */ + { "%ar" }, /* author date, relative */ + { "%at" }, /* author date, UNIX timestamp */ + { "%ai" }, /* author date, ISO 8601 */ + { "%cn" }, /* committer name */ + { "%ce" }, /* committer email */ + { "%cd" }, /* committer date */ + { "%cD" }, /* committer date, RFC2822 style */ + { "%cr" }, /* committer date, relative */ + { "%ct" }, /* committer date, UNIX timestamp */ + { "%ci" }, /* committer date, ISO 8601 */ + { "%e" }, /* encoding */ + { "%s" }, /* subject */ + { "%b" }, /* body */ + { "%Cred" }, /* red */ + { "%Cgreen" }, /* green */ + { "%Cblue" }, /* blue */ + { "%Creset" }, /* reset color */ + { "%n" }, /* newline */ + { "%m" }, /* left/right/bottom */ + }; + enum interp_index { + IHASH = 0, IHASH_ABBREV, + ITREE, ITREE_ABBREV, + IPARENTS, IPARENTS_ABBREV, + IAUTHOR_NAME, IAUTHOR_EMAIL, + IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE, + IAUTHOR_TIMESTAMP, IAUTHOR_ISO8601, + ICOMMITTER_NAME, ICOMMITTER_EMAIL, + ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822, + ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP, + ICOMMITTER_ISO8601, + IENCODING, + ISUBJECT, + IBODY, + IRED, IGREEN, IBLUE, IRESET_COLOR, + INEWLINE, + ILEFT_RIGHT, + }; + struct commit_list *p; + char parents[1024]; + unsigned long len; + int i; + enum { HEADER, SUBJECT, BODY } state; + const char *msg = commit->buffer; + + if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table)) + die("invalid interp table!"); + + /* these are independent of the commit */ + interp_set_entry(table, IRED, "\033[31m"); + interp_set_entry(table, IGREEN, "\033[32m"); + interp_set_entry(table, IBLUE, "\033[34m"); + interp_set_entry(table, IRESET_COLOR, "\033[m"); + interp_set_entry(table, INEWLINE, "\n"); + + /* these depend on the commit */ + if (!commit->object.parsed) + parse_object(commit->object.sha1); + interp_set_entry(table, IHASH, sha1_to_hex(commit->object.sha1)); + interp_set_entry(table, IHASH_ABBREV, + find_unique_abbrev(commit->object.sha1, + DEFAULT_ABBREV)); + interp_set_entry(table, ITREE, sha1_to_hex(commit->tree->object.sha1)); + interp_set_entry(table, ITREE_ABBREV, + find_unique_abbrev(commit->tree->object.sha1, + DEFAULT_ABBREV)); + interp_set_entry(table, ILEFT_RIGHT, + (commit->object.flags & BOUNDARY) + ? "-" + : (commit->object.flags & SYMMETRIC_LEFT) + ? "<" + : ">"); + + parents[1] = 0; + for (i = 0, p = commit->parents; + p && i < sizeof(parents) - 1; + p = p->next) + i += snprintf(parents + i, sizeof(parents) - i - 1, " %s", + sha1_to_hex(p->item->object.sha1)); + interp_set_entry(table, IPARENTS, parents + 1); + + parents[1] = 0; + for (i = 0, p = commit->parents; + p && i < sizeof(parents) - 1; + p = p->next) + i += snprintf(parents + i, sizeof(parents) - i - 1, " %s", + find_unique_abbrev(p->item->object.sha1, + DEFAULT_ABBREV)); + interp_set_entry(table, IPARENTS_ABBREV, parents + 1); + + for (i = 0, state = HEADER; msg[i] && state < BODY; i++) { + int eol; + for (eol = i; msg[eol] && msg[eol] != '\n'; eol++) + ; /* do nothing */ + + if (state == SUBJECT) { + table[ISUBJECT].value = xmemdupz(msg + i, eol - i); + i = eol; + } + if (i == eol) { + state++; + /* strip empty lines */ + while (msg[eol + 1] == '\n') + eol++; + } else if (!prefixcmp(msg + i, "author ")) + fill_person(table + IAUTHOR_NAME, + msg + i + 7, eol - i - 7); + else if (!prefixcmp(msg + i, "committer ")) + fill_person(table + ICOMMITTER_NAME, + msg + i + 10, eol - i - 10); + else if (!prefixcmp(msg + i, "encoding ")) + table[IENCODING].value = + xmemdupz(msg + i + 9, eol - i - 9); + i = eol; + } + if (msg[i]) + table[IBODY].value = xstrdup(msg + i); + + len = interpolate(sb->buf + sb->len, strbuf_avail(sb), + format, table, ARRAY_SIZE(table)); + if (len > strbuf_avail(sb)) { + strbuf_grow(sb, len); + interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1, + format, table, ARRAY_SIZE(table)); + } + strbuf_setlen(sb, sb->len + len); + interp_clear_table(table, ARRAY_SIZE(table)); +} + +static void pp_header(enum cmit_fmt fmt, + int abbrev, + enum date_mode dmode, + const char *encoding, + const struct commit *commit, + const char **msg_p, + struct strbuf *sb) +{ + int parents_shown = 0; + + for (;;) { + const char *line = *msg_p; + int linelen = get_one_line(*msg_p); + + if (!linelen) + return; + *msg_p += linelen; + + if (linelen == 1) + /* End of header */ + return; + + if (fmt == CMIT_FMT_RAW) { + strbuf_add(sb, line, linelen); + continue; + } + + if (!memcmp(line, "parent ", 7)) { + if (linelen != 48) + die("bad parent line in commit"); + continue; + } + + if (!parents_shown) { + struct commit_list *parent; + int num; + for (parent = commit->parents, num = 0; + parent; + parent = parent->next, num++) + ; + /* with enough slop */ + strbuf_grow(sb, num * 50 + 20); + add_merge_info(fmt, sb, commit, abbrev); + parents_shown = 1; + } + + /* + * MEDIUM == DEFAULT shows only author with dates. + * FULL shows both authors but not dates. + * FULLER shows both authors and dates. + */ + if (!memcmp(line, "author ", 7)) { + strbuf_grow(sb, linelen + 80); + add_user_info("Author", fmt, sb, line + 7, dmode, encoding); + } + if (!memcmp(line, "committer ", 10) && + (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) { + strbuf_grow(sb, linelen + 80); + add_user_info("Commit", fmt, sb, line + 10, dmode, encoding); + } + } +} + +static void pp_title_line(enum cmit_fmt fmt, + const char **msg_p, + struct strbuf *sb, + const char *subject, + const char *after_subject, + const char *encoding, + int plain_non_ascii) +{ + struct strbuf title; + + strbuf_init(&title, 80); + + for (;;) { + const char *line = *msg_p; + int linelen = get_one_line(line); + + *msg_p += linelen; + if (!linelen || is_empty_line(line, &linelen)) + break; + + strbuf_grow(&title, linelen + 2); + if (title.len) { + if (fmt == CMIT_FMT_EMAIL) { + strbuf_addch(&title, '\n'); + } + strbuf_addch(&title, ' '); + } + strbuf_add(&title, line, linelen); + } + + strbuf_grow(sb, title.len + 1024); + if (subject) { + strbuf_addstr(sb, subject); + add_rfc2047(sb, title.buf, title.len, encoding); + } else { + strbuf_addbuf(sb, &title); + } + strbuf_addch(sb, '\n'); + + if (plain_non_ascii) { + const char *header_fmt = + "MIME-Version: 1.0\n" + "Content-Type: text/plain; charset=%s\n" + "Content-Transfer-Encoding: 8bit\n"; + strbuf_addf(sb, header_fmt, encoding); + } + if (after_subject) { + strbuf_addstr(sb, after_subject); + } + if (fmt == CMIT_FMT_EMAIL) { + strbuf_addch(sb, '\n'); + } + strbuf_release(&title); +} + +static void pp_remainder(enum cmit_fmt fmt, + const char **msg_p, + struct strbuf *sb, + int indent) +{ + int first = 1; + for (;;) { + const char *line = *msg_p; + int linelen = get_one_line(line); + *msg_p += linelen; + + if (!linelen) + break; + + if (is_empty_line(line, &linelen)) { + if (first) + continue; + if (fmt == CMIT_FMT_SHORT) + break; + } + first = 0; + + strbuf_grow(sb, linelen + indent + 20); + if (indent) { + memset(sb->buf + sb->len, ' ', indent); + strbuf_setlen(sb, sb->len + indent); + } + strbuf_add(sb, line, linelen); + strbuf_addch(sb, '\n'); + } +} + +void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, + struct strbuf *sb, int abbrev, + const char *subject, const char *after_subject, + enum date_mode dmode, int plain_non_ascii) +{ + unsigned long beginning_of_body; + int indent = 4; + const char *msg = commit->buffer; + char *reencoded; + const char *encoding; + + if (fmt == CMIT_FMT_USERFORMAT) { + format_commit_message(commit, user_format, sb); + return; + } + + encoding = (git_log_output_encoding + ? git_log_output_encoding + : git_commit_encoding); + if (!encoding) + encoding = "utf-8"; + reencoded = logmsg_reencode(commit, encoding); + if (reencoded) { + msg = reencoded; + } + + if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) + indent = 0; + + /* After-subject is used to pass in Content-Type: multipart + * MIME header; in that case we do not have to do the + * plaintext content type even if the commit message has + * non 7-bit ASCII character. Otherwise, check if we need + * to say this is not a 7-bit ASCII. + */ + if (fmt == CMIT_FMT_EMAIL && !after_subject) { + int i, ch, in_body; + + for (in_body = i = 0; (ch = msg[i]); i++) { + if (!in_body) { + /* author could be non 7-bit ASCII but + * the log may be so; skip over the + * header part first. + */ + if (ch == '\n' && msg[i+1] == '\n') + in_body = 1; + } + else if (non_ascii(ch)) { + plain_non_ascii = 1; + break; + } + } + } + + pp_header(fmt, abbrev, dmode, encoding, commit, &msg, sb); + if (fmt != CMIT_FMT_ONELINE && !subject) { + strbuf_addch(sb, '\n'); + } + + /* Skip excess blank lines at the beginning of body, if any... */ + for (;;) { + int linelen = get_one_line(msg); + int ll = linelen; + if (!linelen) + break; + if (!is_empty_line(msg, &ll)) + break; + msg += linelen; + } + + /* These formats treat the title line specially. */ + if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) + pp_title_line(fmt, &msg, sb, subject, + after_subject, encoding, plain_non_ascii); + + beginning_of_body = sb->len; + if (fmt != CMIT_FMT_ONELINE) + pp_remainder(fmt, &msg, sb, indent); + strbuf_rtrim(sb); + + /* Make sure there is an EOLN for the non-oneline case */ + if (fmt != CMIT_FMT_ONELINE) + strbuf_addch(sb, '\n'); + + /* + * The caller may append additional body text in e-mail + * format. Make sure we did not strip the blank line + * between the header and the body. + */ + if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body) + strbuf_addch(sb, '\n'); + free(reencoded); +} From c74d9acf20ba0c69bbd67c5b0bb3bd3c2349cebe Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 5 Nov 2007 03:21:47 -0800 Subject: [PATCH 31/74] git-svn: fix dcommit clobbering when committing a series of diffs Our revision number sent to SVN is set to the last revision we committed if we've made any previous commits in a dcommit invocation. Although our SVN Editor code uses the delta of two (old) trees to generate information to send upstream, it'll still send complete resultant files upstream; even if the tree they're based against is out-of-date. The combination of sending a file that does not include the latest changes, but set with a revision number of a commit we just made will cause SVN to accept the resultant file even if it was generated against an old tree. More trouble was caused when fixing this because we were rebasing uncessarily at times. We used git-diff-tree to check the imported SVN revision against our HEAD, not the last tree we committed to SVN. The unnecessary rebasing caused merge commits upstream to SVN to fail. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 48 +++++++++++++++++-- t/t9106-git-svn-dcommit-clobber-series.sh | 56 +++++++++++++++++++++++ 2 files changed, 100 insertions(+), 4 deletions(-) create mode 100755 t/t9106-git-svn-dcommit-clobber-series.sh diff --git a/git-svn.perl b/git-svn.perl index c015ea858..ec25ea423 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -390,7 +390,8 @@ sub cmd_dcommit { "If these changes depend on each other, re-running ", "without --no-rebase will be required." } - foreach my $d (@$linear_refs) { + while (1) { + my $d = shift @$linear_refs or last; unless (defined $last_rev) { (undef, $last_rev, undef) = cmt_metadata("$d~1"); unless (defined $last_rev) { @@ -423,14 +424,14 @@ sub cmd_dcommit { # we always want to rebase against the current HEAD, # not any head that was passed to us - my @diff = command('diff-tree', 'HEAD', + my @diff = command('diff-tree', $d, $gs->refname, '--'); my @finish; if (@diff) { @finish = rebase_cmd(); - print STDERR "W: HEAD and ", $gs->refname, + print STDERR "W: $d and ", $gs->refname, " differ, using @finish:\n", - "@diff"; + join("\n", @diff), "\n"; } else { print "No changes between current HEAD and ", $gs->refname, @@ -439,6 +440,45 @@ sub cmd_dcommit { @finish = qw/reset --mixed/; } command_noisy(@finish, $gs->refname); + if (@diff) { + @refs = (); + my ($url_, $rev_, $uuid_, $gs_) = + working_head_info($head, \@refs); + my ($linear_refs_, $parents_) = + linearize_history($gs_, \@refs); + if (scalar(@$linear_refs) != + scalar(@$linear_refs_)) { + fatal "# of revisions changed ", + "\nbefore:\n", + join("\n", @$linear_refs), + "\n\nafter:\n", + join("\n", @$linear_refs_), "\n", + 'If you are attempting to commit ', + "merges, try running:\n\t", + 'git rebase --interactive', + '--preserve-merges ', + $gs->refname, + "\nBefore dcommitting"; + } + if ($url_ ne $url) { + fatal "URL mismatch after rebase: ", + "$url_ != $url"; + } + if ($uuid_ ne $uuid) { + fatal "uuid mismatch after rebase: ", + "$uuid_ != $uuid"; + } + # remap parents + my (%p, @l, $i); + for ($i = 0; $i < scalar @$linear_refs; $i++) { + my $new = $linear_refs_->[$i] or next; + $p{$new} = + $parents->{$linear_refs->[$i]}; + push @l, $new; + } + $parents = \%p; + $linear_refs = \@l; + } $last_rev = $cmt_rev; } } diff --git a/t/t9106-git-svn-dcommit-clobber-series.sh b/t/t9106-git-svn-dcommit-clobber-series.sh new file mode 100755 index 000000000..7eff4cdc0 --- /dev/null +++ b/t/t9106-git-svn-dcommit-clobber-series.sh @@ -0,0 +1,56 @@ +#!/bin/sh +# +# Copyright (c) 2007 Eric Wong +test_description='git-svn dcommit clobber series' +. ./lib-git-svn.sh + +test_expect_success 'initialize repo' " + mkdir import && + cd import && + awk 'BEGIN { for (i = 1; i < 64; i++) { print i } }' > file + svn import -m 'initial' . $svnrepo && + cd .. && + git svn init $svnrepo && + git svn fetch && + test -e file + " + +test_expect_success '(supposedly) non-conflicting change from SVN' " + test x\"\`sed -n -e 58p < file\`\" = x58 && + test x\"\`sed -n -e 61p < file\`\" = x61 && + svn co $svnrepo tmp && + cd tmp && + perl -i -p -e 's/^58\$/5588/' file && + perl -i -p -e 's/^61\$/6611/' file && + test x\"\`sed -n -e 58p < file\`\" = x5588 && + test x\"\`sed -n -e 61p < file\`\" = x6611 && + svn commit -m '58 => 5588, 61 => 6611' && + cd .. + " + +test_expect_success 'some unrelated changes to git' " + echo hi > life && + git update-index --add life && + git commit -m hi-life && + echo bye >> life && + git commit -m bye-life life + " + +test_expect_success 'change file but in unrelated area' " + test x\"\`sed -n -e 4p < file\`\" = x4 && + test x\"\`sed -n -e 7p < file\`\" = x7 && + perl -i -p -e 's/^4\$/4444/' file && + perl -i -p -e 's/^7\$/7777/' file && + test x\"\`sed -n -e 4p < file\`\" = x4444 && + test x\"\`sed -n -e 7p < file\`\" = x7777 && + git commit -m '4 => 4444, 7 => 7777' file && + git svn dcommit && + svn up tmp && + cd tmp && + test x\"\`sed -n -e 4p < file\`\" = x4444 && + test x\"\`sed -n -e 7p < file\`\" = x7777 && + test x\"\`sed -n -e 58p < file\`\" = x5588 && + test x\"\`sed -n -e 61p < file\`\" = x6611 + " + +test_done From fb159580a1628947f0a088e24cfe6fe4c81d99d0 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 5 Nov 2007 03:21:48 -0800 Subject: [PATCH 32/74] git-svn: t9114: verify merge commit message in test It's possible that we end up with an incorrect commit message in this test after making changes to fix the clobber bug in dcommit. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- t/t9114-git-svn-dcommit-merge.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/t/t9114-git-svn-dcommit-merge.sh b/t/t9114-git-svn-dcommit-merge.sh index d6ca95508..225060b88 100755 --- a/t/t9114-git-svn-dcommit-merge.sh +++ b/t/t9114-git-svn-dcommit-merge.sh @@ -86,4 +86,9 @@ test_expect_success 'verify post-merge ancestry' " git cat-file commit refs/heads/svn^ | grep '^friend$' " +test_expect_success 'verify merge commit message' " + git rev-list --pretty=raw -1 refs/heads/svn | \ + grep \" Merge branch 'merge' into svn\" + " + test_done From 378c4832ef7271a389913f5533611a06415ac5a6 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Sun, 4 Nov 2007 22:35:37 -0500 Subject: [PATCH 33/74] Use parseopts in builtin-push Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- builtin-push.c | 88 +++++++++++++++++++++----------------------------- 1 file changed, 36 insertions(+), 52 deletions(-) diff --git a/builtin-push.c b/builtin-push.c index 4b39ef385..2c561953f 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -7,8 +7,12 @@ #include "builtin.h" #include "remote.h" #include "transport.h" +#include "parse-options.h" -static const char push_usage[] = "git-push [--all] [--dry-run] [--tags] [--receive-pack=] [--repo=all] [-f | --force] [-v] [ ...]"; +static const char * const push_usage[] = { + "git-push [--all] [--dry-run] [--tags] [--receive-pack=] [--repo=all] [-f | --force] [-v] [ ...]", + NULL, +}; static int thin, verbose; static const char *receivepack; @@ -85,63 +89,43 @@ static int do_push(const char *repo, int flags) int cmd_push(int argc, const char **argv, const char *prefix) { - int i; int flags = 0; + int all = 0; + int dry_run = 0; + int force = 0; + int tags = 0; const char *repo = NULL; /* default repository */ - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; + struct option options[] = { + OPT__VERBOSE(&verbose), + OPT_STRING( 0 , "repo", &repo, "repository", "repository"), + OPT_BOOLEAN( 0 , "all", &all, "push all refs"), + OPT_BOOLEAN( 0 , "tags", &tags, "push tags"), + OPT_BOOLEAN( 0 , "dry-run", &dry_run, "dry run"), + OPT_BOOLEAN('f', "force", &force, "force updates"), + OPT_BOOLEAN( 0 , "thin", &thin, "use thin pack"), + OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", "receive pack program"), + OPT_STRING( 0 , "exec", &receivepack, "receive-pack", "receive pack program"), + OPT_END() + }; - if (arg[0] != '-') { - repo = arg; - i++; - break; - } - if (!strcmp(arg, "-v")) { - verbose=1; - continue; - } - if (!prefixcmp(arg, "--repo=")) { - repo = arg+7; - continue; - } - if (!strcmp(arg, "--all")) { - flags |= TRANSPORT_PUSH_ALL; - continue; - } - if (!strcmp(arg, "--dry-run")) { - flags |= TRANSPORT_PUSH_DRY_RUN; - continue; - } - if (!strcmp(arg, "--tags")) { - add_refspec("refs/tags/*"); - continue; - } - if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) { - flags |= TRANSPORT_PUSH_FORCE; - continue; - } - if (!strcmp(arg, "--thin")) { - thin = 1; - continue; - } - if (!strcmp(arg, "--no-thin")) { - thin = 0; - continue; - } - if (!prefixcmp(arg, "--receive-pack=")) { - receivepack = arg + 15; - continue; - } - if (!prefixcmp(arg, "--exec=")) { - receivepack = arg + 7; - continue; - } - usage(push_usage); + argc = parse_options(argc, argv, options, push_usage, 0); + + if (force) + flags |= TRANSPORT_PUSH_FORCE; + if (dry_run) + flags |= TRANSPORT_PUSH_DRY_RUN; + if (tags) + add_refspec("refs/tags/*"); + if (all) + flags |= TRANSPORT_PUSH_ALL; + + if (argc > 0) { + repo = argv[0]; + set_refspecs(argv + 1, argc - 1); } - set_refspecs(argv + i, argc - i); if ((flags & TRANSPORT_PUSH_ALL) && refspec) - usage(push_usage); + usage_with_options(push_usage, options); return do_push(repo, flags); } From ae3e76c2991ddd5fd72cff22cd39d5d297e63504 Mon Sep 17 00:00:00 2001 From: Shawn Bohrer Date: Sun, 4 Nov 2007 22:28:12 -0600 Subject: [PATCH 34/74] Add more tests for git-clean Signed-off-by: Shawn Bohrer Signed-off-by: Junio C Hamano --- t/t7300-clean.sh | 105 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index eb0847afe..232743618 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@ -37,6 +37,93 @@ test_expect_success 'git-clean' ' ' +test_expect_success 'git-clean src/' ' + + mkdir -p build docs && + touch a.out src/part3.c docs/manual.txt obj.o build/lib.so && + git-clean src/ && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test ! -f src/part3.c && + test -f docs/manual.txt && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git-clean src/ src/' ' + + mkdir -p build docs && + touch a.out src/part3.c docs/manual.txt obj.o build/lib.so && + git-clean src/ src/ && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test ! -f src/part3.c && + test -f docs/manual.txt && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git-clean with prefix' ' + + mkdir -p build docs && + touch a.out src/part3.c docs/manual.txt obj.o build/lib.so && + (cd src/ && git-clean) && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test ! -f src/part3.c && + test -f docs/manual.txt && + test -f obj.o && + test -f build/lib.so + +' +test_expect_success 'git-clean -d with prefix and path' ' + + mkdir -p build docs src/feature && + touch a.out src/part3.c src/feature/file.c docs/manual.txt obj.o build/lib.so && + (cd src/ && git-clean -d feature/) && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test -f src/part3.c && + test ! -f src/feature/file.c && + test -f docs/manual.txt && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git-clean symbolic link' ' + + mkdir -p build docs && + touch a.out src/part3.c docs/manual.txt obj.o build/lib.so && + ln -s docs/manual.txt src/part4.c + git-clean && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test ! -f src/part3.c && + test ! -f src/part4.c && + test -f docs/manual.txt && + test -f obj.o && + test -f build/lib.so + +' + test_expect_success 'git-clean -n' ' mkdir -p build docs && @@ -71,6 +158,24 @@ test_expect_success 'git-clean -d' ' ' +test_expect_success 'git-clean -d src/ examples/' ' + + mkdir -p build docs examples && + touch a.out src/part3.c docs/manual.txt obj.o build/lib.so examples/1.c && + git-clean -d src/ examples/ && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test ! -f src/part3.c && + test ! -f examples/1.c && + test -f docs/manual.txt && + test -f obj.o && + test -f build/lib.so + +' + test_expect_success 'git-clean -x' ' mkdir -p build docs && From 9f12bec4386fc96e5b617268822cbb75e4c76101 Mon Sep 17 00:00:00 2001 From: Brian Gernhardt Date: Sun, 4 Nov 2007 10:31:26 -0500 Subject: [PATCH 35/74] t3502: Disambiguate between file and rev by adding -- On a case insensitive file system, this test fails because git-diff doesn't know if it is asking for the file "A" or the tag "a". Adding "--" at the end of the ambiguous commands allows the test to finish properly. Signed-off-by: Brian Gernhardt Signed-off-by: Junio C Hamano --- t/t3502-cherry-pick-merge.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/t/t3502-cherry-pick-merge.sh b/t/t3502-cherry-pick-merge.sh index 3274c6141..7c92e261f 100755 --- a/t/t3502-cherry-pick-merge.sh +++ b/t/t3502-cherry-pick-merge.sh @@ -36,7 +36,7 @@ test_expect_success 'cherry-pick a non-merge with -m should fail' ' git reset --hard && git checkout a^0 && ! git cherry-pick -m 1 b && - git diff --exit-code a + git diff --exit-code a -- ' @@ -45,7 +45,7 @@ test_expect_success 'cherry pick a merge without -m should fail' ' git reset --hard && git checkout a^0 && ! git cherry-pick c && - git diff --exit-code a + git diff --exit-code a -- ' @@ -98,7 +98,7 @@ test_expect_success 'revert a merge (1)' ' git reset --hard && git checkout c^0 && git revert -m 1 c && - git diff --exit-code a + git diff --exit-code a -- ' @@ -107,7 +107,7 @@ test_expect_success 'revert a merge (2)' ' git reset --hard && git checkout c^0 && git revert -m 2 c && - git diff --exit-code b + git diff --exit-code b -- ' From 0aab4abdd4e6461d61ce6cf1392e1d31c4d1573a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 5 Nov 2007 16:39:00 -0800 Subject: [PATCH 36/74] gc: --prune prunes unreferenced objects. Brandon Casey correctly points out that we repack with -A without --prune and with -a with --prune, so it is not just unreferenced loose objects that are pruned away when the option is given. Signed-off-by: Junio C Hamano --- builtin-gc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-gc.c b/builtin-gc.c index c5bce893a..799c26393 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -175,7 +175,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) char buf[80]; struct option builtin_gc_options[] = { - OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced loose objects"), + OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects"), OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"), OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"), OPT_END() From c67359be45be74e1056d6293c6bb09ee6d00a54a Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Mon, 5 Nov 2007 09:16:22 +0000 Subject: [PATCH 37/74] git-daemon: fix remote port number in log entry The port number in struct sockaddr_in needs to be converted from network byte order to host byte order (on some architectures). Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- daemon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemon.c b/daemon.c index 660e1552d..b8df980dc 100644 --- a/daemon.c +++ b/daemon.c @@ -540,7 +540,7 @@ static int execute(struct sockaddr *addr) if (addr->sa_family == AF_INET) { struct sockaddr_in *sin_addr = (void *) addr; inet_ntop(addr->sa_family, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf)); - port = sin_addr->sin_port; + port = ntohs(sin_addr->sin_port); #ifndef NO_IPV6 } else if (addr && addr->sa_family == AF_INET6) { struct sockaddr_in6 *sin6_addr = (void *) addr; @@ -550,7 +550,7 @@ static int execute(struct sockaddr *addr) inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1); strcat(buf, "]"); - port = sin6_addr->sin6_port; + port = ntohs(sin6_addr->sin6_port); #endif } loginfo("Connection from %s:%d", addrbuf, port); From 9b3beb581216fc89972e926574b9d3b243d5e4f4 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 5 Nov 2007 13:03:22 +0100 Subject: [PATCH 38/74] Some better parse-options documentation. Signed-off-by: Junio C Hamano --- parse-options.h | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/parse-options.h b/parse-options.h index 3a470e5eb..65bce6eaf 100644 --- a/parse-options.h +++ b/parse-options.h @@ -22,6 +22,41 @@ enum parse_opt_option_flags { struct option; typedef int parse_opt_cb(const struct option *, const char *arg, int unset); +/* + * `type`:: + * holds the type of the option, you must have an OPTION_END last in your + * array. + * + * `short_name`:: + * the character to use as a short option name, '\0' if none. + * + * `long_name`:: + * the long option name, without the leading dashes, NULL if none. + * + * `value`:: + * stores pointers to the values to be filled. + * + * `argh`:: + * token to explain the kind of argument this option wants. Keep it + * homogenous across the repository. + * + * `help`:: + * the short help associated to what the option does. + * Must never be NULL (except for OPTION_END). + * OPTION_GROUP uses this pointer to store the group header. + * + * `flags`:: + * mask of parse_opt_option_flags. + * PARSE_OPT_OPTARG: says that the argument is optionnal (not for BOOLEANs) + * PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs + * + * `callback`:: + * pointer to the callback to use for OPTION_CALLBACK. + * + * `defval`:: + * default value to fill (*->value) with for PARSE_OPT_OPTARG. + * CALLBACKS can use it like they want. + */ struct option { enum parse_opt_type type; int short_name; @@ -32,8 +67,6 @@ struct option { int flags; parse_opt_cb *callback; - /* holds default value for PARSE_OPT_OPTARG, - though callbacks can use it like they want */ intptr_t defval; }; From b67a43bb8f4a8ffb64f26b7351c3b0b90239696a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 5 Nov 2007 17:16:47 -0800 Subject: [PATCH 39/74] grep with unmerged index We called flush_grep() every time we saw an unmerged entry in the index. If we happen to find an unmerged entry before we saw more than two paths, we incorrectly declared that the user had too many non-paths options in front. Signed-off-by: Junio C Hamano --- builtin-grep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-grep.c b/builtin-grep.c index c7b45c4d5..185876b0a 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -343,7 +343,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) memcpy(name + 2, ce->name, len + 1); } argv[argc++] = name; - if (argc < MAXARGS && !ce_stage(ce)) + if (argc < MAXARGS) continue; status = flush_grep(opt, argc, nr, argv, &kept); if (0 < status) From d8e21ba8967f43b61fc2fd50dd749ff8f0c6592a Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sun, 4 Nov 2007 15:35:26 +0100 Subject: [PATCH 40/74] Remove a couple of duplicated include Signed-off-by: Marco Costalba Signed-off-by: Junio C Hamano --- compat/inet_ntop.c | 1 - compat/inet_pton.c | 1 - 2 files changed, 2 deletions(-) diff --git a/compat/inet_ntop.c b/compat/inet_ntop.c index 4d7ab9d97..f44498258 100644 --- a/compat/inet_ntop.c +++ b/compat/inet_ntop.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/compat/inet_pton.c b/compat/inet_pton.c index 5704e0d2b..4078fc087 100644 --- a/compat/inet_pton.c +++ b/compat/inet_pton.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include From a1f611d5d03d7849f4d7f47f5bd39b5c44808bb5 Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Sun, 4 Nov 2007 09:02:21 +0100 Subject: [PATCH 41/74] Fix comment in strbuf.h to use correct name strbuf_avail() Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano --- strbuf.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/strbuf.h b/strbuf.h index 9b9e861d3..cd7f295b6 100644 --- a/strbuf.h +++ b/strbuf.h @@ -23,12 +23,12 @@ * that way: * * strbuf_grow(sb, SOME_SIZE); - * // ... here the memory areay starting at sb->buf, and of length - * // sb_avail(sb) is all yours, and you are sure that sb_avail(sb) is at - * // least SOME_SIZE + * ... Here, the memory array starting at sb->buf, and of length + * ... strbuf_avail(sb) is all yours, and you are sure that + * ... strbuf_avail(sb) is at least SOME_SIZE. * strbuf_setlen(sb, sb->len + SOME_OTHER_SIZE); * - * Of course, SOME_OTHER_SIZE must be smaller or equal to sb_avail(sb). + * Of course, SOME_OTHER_SIZE must be smaller or equal to strbuf_avail(sb). * * Doing so is safe, though if it has to be done in many places, adding the * missing API to the strbuf module is the way to go. From 59f0f2f33a420e9a14bb8cef20d38f508f0d098e Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Sat, 3 Nov 2007 12:23:11 +0100 Subject: [PATCH 42/74] Refactor working tree setup Create a setup_work_tree() that can be used from any command requiring a working tree conditionally. Signed-off-by: Mike Hommey Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- cache.h | 1 + git.c | 11 +++-------- setup.c | 9 +++++++++ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/cache.h b/cache.h index bfffa05df..497b9f962 100644 --- a/cache.h +++ b/cache.h @@ -222,6 +222,7 @@ extern const char *get_git_work_tree(void); #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES" extern const char **get_pathspec(const char *prefix, const char **pathspec); +extern void setup_work_tree(void); extern const char *setup_git_directory_gently(int *); extern const char *setup_git_directory(void); extern const char *prefix_path(const char *prefix, int len, const char *path); diff --git a/git.c b/git.c index 4e1058110..eb31c93d0 100644 --- a/git.c +++ b/git.c @@ -249,14 +249,9 @@ static int run_command(struct cmd_struct *p, int argc, const char **argv) prefix = setup_git_directory(); if (p->option & USE_PAGER) setup_pager(); - if (p->option & NEED_WORK_TREE) { - const char *work_tree = get_git_work_tree(); - const char *git_dir = get_git_dir(); - if (!is_absolute_path(git_dir)) - set_git_dir(make_absolute_path(git_dir)); - if (!work_tree || chdir(work_tree)) - die("%s must be run in a work tree", p->cmd); - } + if (p->option & NEED_WORK_TREE) + setup_work_tree(); + trace_argv_printf(argv, argc, "trace: built-in: git"); status = p->fn(argc, argv, prefix); diff --git a/setup.c b/setup.c index 145eca50f..df006d9c4 100644 --- a/setup.c +++ b/setup.c @@ -206,6 +206,15 @@ static const char *set_work_tree(const char *dir) return NULL; } +void setup_work_tree(void) { + const char *work_tree = get_git_work_tree(); + const char *git_dir = get_git_dir(); + if (!is_absolute_path(git_dir)) + set_git_dir(make_absolute_path(git_dir)); + if (!work_tree || chdir(work_tree)) + die("This operation must be run in a work tree"); +} + /* * We cannot decide in this function whether we are in the work tree or * not, since the config can only be read _after_ this function was called. From 7d8ae93292e2cd862359f02879a0c954faf8afa8 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Sat, 3 Nov 2007 12:23:12 +0100 Subject: [PATCH 43/74] Use setup_work_tree() in builtin-ls-files.c Signed-off-by: Mike Hommey Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-ls-files.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/builtin-ls-files.c b/builtin-ls-files.c index b70da1863..e0b856f43 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -525,11 +525,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) break; } - if (require_work_tree && !is_inside_work_tree()) { - const char *work_tree = get_git_work_tree(); - if (!work_tree || chdir(work_tree)) - die("This operation must be run in a work tree"); - } + if (require_work_tree && !is_inside_work_tree()) + setup_work_tree(); pathspec = get_pathspec(prefix, argv + i); From 271bb08735435b859464c22506ba512f003a4191 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Sat, 3 Nov 2007 12:23:13 +0100 Subject: [PATCH 44/74] Don't always require working tree for git-rm This allows to do git rm --cached -r directory, instead of git ls-files -z directory | git update-index --remove -z --stdin. This can be particularly useful for git-filter-branch users. Signed-off-by: Mike Hommey Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-rm.c | 3 +++ git.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/builtin-rm.c b/builtin-rm.c index bca2bd970..a3d25e6a5 100644 --- a/builtin-rm.c +++ b/builtin-rm.c @@ -155,6 +155,9 @@ int cmd_rm(int argc, const char **argv, const char *prefix) if (!argc) usage_with_options(builtin_rm_usage, builtin_rm_options); + if (!index_only) + setup_work_tree(); + pathspec = get_pathspec(prefix, argv); seen = NULL; for (i = 0; pathspec[i] ; i++) diff --git a/git.c b/git.c index eb31c93d0..4a250f7e8 100644 --- a/git.c +++ b/git.c @@ -340,7 +340,7 @@ static void handle_internal_command(int argc, const char **argv) { "rev-list", cmd_rev_list, RUN_SETUP }, { "rev-parse", cmd_rev_parse, RUN_SETUP }, { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE }, - { "rm", cmd_rm, RUN_SETUP | NEED_WORK_TREE }, + { "rm", cmd_rm, RUN_SETUP }, { "runstatus", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE }, { "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER }, { "show-branch", cmd_show_branch, RUN_SETUP }, From 1981820be20538d7a70f86c5ddbe379566fd5ff2 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Sat, 3 Nov 2007 13:22:55 +0100 Subject: [PATCH 45/74] Make git-blame fail when working tree is needed and we're not in one Signed-off-by: Mike Hommey Signed-off-by: Junio C Hamano --- builtin-blame.c | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin-blame.c b/builtin-blame.c index aedc294ea..55a3c0bc5 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -2342,6 +2342,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) * do not default to HEAD, but use the working tree * or "--contents". */ + setup_work_tree(); sb.final = fake_working_tree_commit(path, contents_from); add_pending_object(&revs, &(sb.final->object), ":"); } From 521b53e5c7ee23a137e903d99108f81d44cca5ec Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Sun, 4 Nov 2007 09:37:20 +0000 Subject: [PATCH 46/74] git-reset: add -q option to operate quietly Many git commands have a -q option to suppress output to stdout, let's have it for git-reset too. This was asked for by Joey Hess through http://bugs.debian.org/444933 Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- Documentation/git-reset.txt | 7 +++++-- builtin-reset.c | 14 ++++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index 87afa6f8d..050e4eadb 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -8,8 +8,8 @@ git-reset - Reset current HEAD to the specified state SYNOPSIS -------- [verse] -'git-reset' [--mixed | --soft | --hard] [] -'git-reset' [--mixed] [--] ... +'git-reset' [--mixed | --soft | --hard] [-q] [] +'git-reset' [--mixed] [-q] [--] ... DESCRIPTION ----------- @@ -45,6 +45,9 @@ OPTIONS switched to. Any changes to tracked files in the working tree since are lost. +-q:: + Be quiet, only report errors. + :: Commit to make the current HEAD. diff --git a/builtin-reset.c b/builtin-reset.c index 5467e36c7..9626d4c54 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -18,7 +18,7 @@ #include "tree.h" static const char builtin_reset_usage[] = -"git-reset [--mixed | --soft | --hard] [] [ [--] ...]"; +"git-reset [--mixed | --soft | --hard] [-q] [] [ [--] ...]"; static char *args_to_str(const char **argv) { @@ -185,7 +185,7 @@ static const char *reset_type_names[] = { "mixed", "soft", "hard", NULL }; int cmd_reset(int argc, const char **argv, const char *prefix) { - int i = 1, reset_type = NONE, update_ref_status = 0; + int i = 1, reset_type = NONE, update_ref_status = 0, quiet = 0; const char *rev = "HEAD"; unsigned char sha1[20], *orig = NULL, sha1_orig[20], *old_orig = NULL, sha1_old_orig[20]; @@ -197,7 +197,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) reflog_action = args_to_str(argv); setenv("GIT_REFLOG_ACTION", reflog_action, 0); - if (i < argc) { + while (i < argc) { if (!strcmp(argv[i], "--mixed")) { reset_type = MIXED; i++; @@ -210,6 +210,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix) reset_type = HARD; i++; } + else if (!strcmp(argv[i], "-q")) { + quiet = 1; + i++; + } + else + break; } if (i < argc && argv[i][0] != '-') @@ -270,7 +276,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) switch (reset_type) { case HARD: - if (!update_ref_status) + if (!update_ref_status && !quiet) print_new_head_line(commit); break; case SOFT: /* Nothing else to do. */ From f49439e1e16713d8d531e8a0bbc3b0bd7df5a407 Mon Sep 17 00:00:00 2001 From: David D Kilzer Date: Sun, 4 Nov 2007 04:45:22 -0800 Subject: [PATCH 47/74] RelNotes-1.5.3.5: fix another typo Signed-off-by: David D Kilzer Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.3.5.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/RelNotes-1.5.3.5.txt b/Documentation/RelNotes-1.5.3.5.txt index f99a2cd65..7ff1d5d0d 100644 --- a/Documentation/RelNotes-1.5.3.5.txt +++ b/Documentation/RelNotes-1.5.3.5.txt @@ -90,5 +90,5 @@ Fixes since v1.5.3.4 * "git-send-pack $remote frotz" segfaulted when there is nothing named 'frotz' on the local end. - * "git-rebase -interactive" did not handle its "--strategy" option + * "git-rebase --interactive" did not handle its "--strategy" option properly. From 6aaa65da20a8f0a4c95834f73f0b20d5dc3aa3c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ask=20Bj=C3=B8rn=20Hansen?= Date: Tue, 6 Nov 2007 02:54:01 -0800 Subject: [PATCH 48/74] When exec() fails include the failing command in the error message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn occasionally fails with no details as to what went wrong - this should help debug those situations. Signed-off-by: Ask Bjørn Hansen Signed-off-by: Junio C Hamano --- perl/Git.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/perl/Git.pm b/perl/Git.pm index 3f4080cbf..dca92c8ad 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -812,7 +812,7 @@ sub _cmd_exec { $self->wc_subdir() and chdir($self->wc_subdir()); } _execv_git_cmd(@args); - die "exec failed: $!"; + die qq[exec "@args" failed: $!]; } # Execute the given Git command ($_[0]) with arguments ($_[1..]) From 3891f390eafa4bec95088f16f8f481553c535b78 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 7 Nov 2007 17:20:22 -0500 Subject: [PATCH 49/74] restore fetching with thin-pack capability Broken since commit fa74052922cf39e5a39ad7178d1b13c2da9b4519. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-fetch-pack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index 862652be9..bb1742f1a 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -32,7 +32,7 @@ static const char fetch_pack_usage[] = #define MAX_IN_VAIN 256 static struct commit_list *rev_list; -static int non_common_revs, multi_ack, use_thin_pack, use_sideband; +static int non_common_revs, multi_ack, use_sideband; static void rev_list_push(struct commit *commit, int mark) { @@ -178,7 +178,7 @@ static int find_common(int fd[2], unsigned char *result_sha1, (multi_ack ? " multi_ack" : ""), (use_sideband == 2 ? " side-band-64k" : ""), (use_sideband == 1 ? " side-band" : ""), - (use_thin_pack ? " thin-pack" : ""), + (args.use_thin_pack ? " thin-pack" : ""), (args.no_progress ? " no-progress" : ""), " ofs-delta"); else From b5786c828304c7dcf42742729374d04f30ac1a09 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Tue, 6 Nov 2007 13:48:07 +0000 Subject: [PATCH 50/74] contrib/hooks/post-receive-email: fix typo Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 2aa9bb501..379cedc57 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -30,7 +30,7 @@ # This is the list that all pushes of annotated tags will go to. Leave it # blank to default to the mailinglist field. The announce emails lists the # short log summary of the changes since the last annotated tag. -# hook.envelopesender +# hooks.envelopesender # If set then the -f option is passed to sendmail to allow the envelope sender # address to be set # From 15a2f530111f32d66eb0c25527e540b5e09804ad Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Tue, 6 Nov 2007 13:48:34 +0000 Subject: [PATCH 51/74] contrib/hooks/post-receive-email: reformat to wrap comments at 76 chars Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 199 +++++++++++++++++-------------- 1 file changed, 107 insertions(+), 92 deletions(-) diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 379cedc57..9b9a97777 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -2,24 +2,26 @@ # # Copyright (c) 2007 Andy Parkins # -# An example hook script to mail out commit update information. This hook sends emails -# listing new revisions to the repository introduced by the change being reported. The -# rule is that (for branch updates) each commit will appear on one email and one email -# only. +# An example hook script to mail out commit update information. This hook +# sends emails listing new revisions to the repository introduced by the +# change being reported. The rule is that (for branch updates) each commit +# will appear on one email and one email only. # -# This hook is stored in the contrib/hooks directory. Your distribution will have put -# this somewhere standard. You should make this script executable then link to it in -# the repository you would like to use it in. For example, on debian the hook is stored -# in /usr/share/doc/git-core/contrib/hooks/post-receive-email: +# This hook is stored in the contrib/hooks directory. Your distribution +# will have put this somewhere standard. You should make this script +# executable then link to it in the repository you would like to use it in. +# For example, on debian the hook is stored in +# /usr/share/doc/git-core/contrib/hooks/post-receive-email: # # chmod a+x post-receive-email # cd /path/to/your/repository.git # ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive # -# This hook script assumes it is enabled on the central repository of a project, with -# all users pushing only to it and not between each other. It will still work if you -# don't operate in that style, but it would become possible for the email to be from -# someone other than the person doing the push. +# This hook script assumes it is enabled on the central repository of a +# project, with all users pushing only to it and not between each other. It +# will still work if you don't operate in that style, but it would become +# possible for the email to be from someone other than the person doing the +# push. # # Config # ------ @@ -28,11 +30,11 @@ # emails for every ref update. # hooks.announcelist # This is the list that all pushes of annotated tags will go to. Leave it -# blank to default to the mailinglist field. The announce emails lists the -# short log summary of the changes since the last annotated tag. +# blank to default to the mailinglist field. The announce emails lists +# the short log summary of the changes since the last annotated tag. # hooks.envelopesender -# If set then the -f option is passed to sendmail to allow the envelope sender -# address to be set +# If set then the -f option is passed to sendmail to allow the envelope +# sender address to be set # # Notes # ----- @@ -49,8 +51,8 @@ # this is and calls the appropriate body-generation routine after outputting # the common header # -# Note this function doesn't actually generate any email output, that is taken -# care of by the functions it calls: +# Note this function doesn't actually generate any email output, that is +# taken care of by the functions it calls: # - generate_email_header # - generate_create_XXXX_email # - generate_update_XXXX_email @@ -225,8 +227,9 @@ generate_create_branch_email() echo $LOGBEGIN # This shows all log entries that are not already covered by # another ref - i.e. commits that are now accessible from this - # ref that were previously not accessible (see generate_update_branch_email - # for the explanation of this command) + # ref that were previously not accessible + # (see generate_update_branch_email for the explanation of this + # command) git rev-parse --not --branches | grep -v $(git rev-parse $refname) | git rev-list --pretty --stdin $newrev echo $LOGEND @@ -254,9 +257,10 @@ generate_update_branch_email() # # git-rev-list N ^O ^X ^N # - # So, we need to build up the list more carefully. git-rev-parse will - # generate a list of revs that may be fed into git-rev-list. We can get - # it to make the "--not --all" part and then filter out the "^N" with: + # So, we need to build up the list more carefully. git-rev-parse + # will generate a list of revs that may be fed into git-rev-list. + # We can get it to make the "--not --all" part and then filter out + # the "^N" with: # # git-rev-parse --not --all | grep -v N # @@ -266,16 +270,17 @@ generate_update_branch_email() # git-rev-list N ^O ^X # # This leaves a problem when someone else updates the repository - # while this script is running. Their new value of the ref we're working - # on would be included in the "--not --all" output; and as our $newrev - # would be an ancestor of that commit, it would exclude all of our - # commits. What we really want is to exclude the current value of - # $refname from the --not list, rather than N itself. So: + # while this script is running. Their new value of the ref we're + # working on would be included in the "--not --all" output; and as + # our $newrev would be an ancestor of that commit, it would exclude + # all of our commits. What we really want is to exclude the current + # value of $refname from the --not list, rather than N itself. So: # # git-rev-parse --not --all | grep -v $(git-rev-parse $refname) # - # Get's us to something pretty safe (apart from the small time between - # refname being read, and git-rev-parse running - for that, I give up) + # Get's us to something pretty safe (apart from the small time + # between refname being read, and git-rev-parse running - for that, + # I give up) # # # Next problem, consider this: @@ -283,18 +288,18 @@ generate_update_branch_email() # \ # * --- X --- * --- N ($newrev) # - # That is to say, there is no guarantee that oldrev is a strict subset of - # newrev (it would have required a --force, but that's allowed). So, we - # can't simply say rev-list $oldrev..$newrev. Instead we find the common - # base of the two revs and list from there. + # That is to say, there is no guarantee that oldrev is a strict + # subset of newrev (it would have required a --force, but that's + # allowed). So, we can't simply say rev-list $oldrev..$newrev. + # Instead we find the common base of the two revs and list from + # there. # - # As above, we need to take into account the presence of X; if another - # branch is already in the repository and points at some of the revisions - # that we are about to output - we don't want them. The solution is as - # before: git-rev-parse output filtered. + # As above, we need to take into account the presence of X; if + # another branch is already in the repository and points at some of + # the revisions that we are about to output - we don't want them. + # The solution is as before: git-rev-parse output filtered. # - # Finally, tags: - # 1 --- 2 --- O --- T --- 3 --- 4 --- N + # Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N # # Tags pushed into the repository generate nice shortlog emails that # summarise the commits between them and the previous tag. However, @@ -302,13 +307,14 @@ generate_update_branch_email() # for a branch update. Therefore we still want to output revisions # that have been output on a tag email. # - # Luckily, git-rev-parse includes just the tool. Instead of using "--all" - # we use "--branches"; this has the added benefit that "remotes/" will - # be ignored as well. - - # List all of the revisions that were removed by this update, in a fast forward - # update, this list will be empty, because rev-list O ^N is empty. For a non - # fast forward, O ^N is the list of removed revisions + # Luckily, git-rev-parse includes just the tool. Instead of using + # "--all" we use "--branches"; this has the added benefit that + # "remotes/" will be ignored as well. + + # List all of the revisions that were removed by this update, in a + # fast forward update, this list will be empty, because rev-list O + # ^N is empty. For a non fast forward, O ^N is the list of removed + # revisions fast_forward="" rev="" for rev in $(git rev-list $newrev..$oldrev) @@ -321,10 +327,10 @@ generate_update_branch_email() fi # List all the revisions from baserev to newrev in a kind of - # "table-of-contents"; note this list can include revisions that have - # already had notification emails and is present to show the full detail - # of the change from rolling back the old revision to the base revision and - # then forward to the new revision + # "table-of-contents"; note this list can include revisions that + # have already had notification emails and is present to show the + # full detail of the change from rolling back the old revision to + # the base revision and then forward to the new revision for rev in $(git rev-list $oldrev..$newrev) do revtype=$(git cat-file -t "$rev") @@ -334,19 +340,20 @@ generate_update_branch_email() if [ "$fast_forward" ]; then echo " from $oldrev ($oldrev_type)" else - # 1. Existing revisions were removed. In this case newrev is a - # subset of oldrev - this is the reverse of a fast-forward, - # a rewind - # 2. New revisions were added on top of an old revision, this is - # a rewind and addition. + # 1. Existing revisions were removed. In this case newrev + # is a subset of oldrev - this is the reverse of a + # fast-forward, a rewind + # 2. New revisions were added on top of an old revision, + # this is a rewind and addition. - # (1) certainly happened, (2) possibly. When (2) hasn't happened, - # we set a flag to indicate that no log printout is required. + # (1) certainly happened, (2) possibly. When (2) hasn't + # happened, we set a flag to indicate that no log printout + # is required. echo "" - # Find the common ancestor of the old and new revisions and compare - # it with newrev + # Find the common ancestor of the old and new revisions and + # compare it with newrev baserev=$(git merge-base $oldrev $newrev) rewind_only="" if [ "$baserev" = "$newrev" ]; then @@ -387,21 +394,22 @@ generate_update_branch_email() git rev-parse --not --branches | grep -v $(git rev-parse $refname) | git rev-list --pretty --stdin $oldrev..$newrev - # XXX: Need a way of detecting whether git rev-list actually outputted - # anything, so that we can issue a "no new revisions added by this - # update" message + # XXX: Need a way of detecting whether git rev-list actually + # outputted anything, so that we can issue a "no new + # revisions added by this update" message echo $LOGEND else echo "No new revisions were added by this update." fi - # The diffstat is shown from the old revision to the new revision. This - # is to show the truth of what happened in this change. There's no point - # showing the stat from the base to the new revision because the base - # is effectively a random revision at this point - the user will be - # interested in what this revision changed - including the undoing of - # previous revisions in the case of non-fast forward updates. + # The diffstat is shown from the old revision to the new revision. + # This is to show the truth of what happened in this change. + # There's no point showing the stat from the base to the new + # revision because the base is effectively a random revision at this + # point - the user will be interested in what this revision changed + # - including the undoing of previous revisions in the case of + # non-fast forward updates. echo "" echo "Summary of changes:" git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev @@ -448,7 +456,8 @@ generate_update_atag_email() # generate_atag_email() { - # Use git-for-each-ref to pull out the individual fields from the tag + # Use git-for-each-ref to pull out the individual fields from the + # tag eval $(git for-each-ref --shell --format=' tagobject=%(*objectname) tagtype=%(*objecttype) @@ -459,8 +468,10 @@ generate_atag_email() echo " tagging $tagobject ($tagtype)" case "$tagtype" in commit) + # If the tagged object is a commit, then we assume this is a - # release, and so we calculate which tag this tag is replacing + # release, and so we calculate which tag this tag is + # replacing prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null) if [ -n "$prevtag" ]; then @@ -477,25 +488,27 @@ generate_atag_email() echo "" echo $LOGBEGIN - # Show the content of the tag message; this might contain a change log - # or release notes so is worth displaying. + # Show the content of the tag message; this might contain a change + # log or release notes so is worth displaying. git cat-file tag $newrev | sed -e '1,/^$/d' echo "" case "$tagtype" in commit) - # Only commit tags make sense to have rev-list operations performed - # on them + # Only commit tags make sense to have rev-list operations + # performed on them if [ -n "$prevtag" ]; then # Show changes since the previous release git rev-list --pretty=short "$prevtag..$newrev" | git shortlog else - # No previous tag, show all the changes since time began + # No previous tag, show all the changes since time + # began git rev-list --pretty=short $newrev | git shortlog fi ;; *) - # XXX: Is there anything useful we can do for non-commit objects? + # XXX: Is there anything useful we can do for non-commit + # objects? ;; esac @@ -544,13 +557,14 @@ generate_update_general_email() # generate_general_email() { - # Unannotated tags are more about marking a point than releasing a version; - # therefore we don't do the shortlog summary that we do for annotated tags - # above - we simply show that the point has been marked, and print the log - # message for the marked point for reference purposes + # Unannotated tags are more about marking a point than releasing a + # version; therefore we don't do the shortlog summary that we do for + # annotated tags above - we simply show that the point has been + # marked, and print the log message for the marked point for + # reference purposes # - # Note this section also catches any other reference type (although there - # aren't any) and deals with them in the same way. + # Note this section also catches any other reference type (although + # there aren't any) and deals with them in the same way. echo "" if [ "$newrev_type" = "commit" ]; then @@ -558,10 +572,10 @@ generate_general_email() git show --no-color --root -s $newrev echo $LOGEND else - # What can we do here? The tag marks an object that is not a commit, - # so there is no log for us to display. It's probably not wise to - # output git-cat-file as it could be a binary blob. We'll just say how - # big it is + # What can we do here? The tag marks an object that is not + # a commit, so there is no log for us to display. It's + # probably not wise to output git-cat-file as it could be a + # binary blob. We'll just say how big it is echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long." fi } @@ -604,8 +618,8 @@ if [ -z "$GIT_DIR" ]; then fi projectdesc=$(sed -ne '1p' "$GIT_DIR/description") -# Check if the description is unchanged from it's default, and shorten it to a -# more manageable length if it is +# Check if the description is unchanged from it's default, and shorten it to +# a more manageable length if it is if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null then projectdesc="UNNAMED PROJECT" @@ -616,11 +630,12 @@ announcerecipients=$(git repo-config hooks.announcelist) envelopesender=$(git-repo-config hooks.envelopesender) # --- Main loop -# Allow dual mode: run from the command line just like the update hook, or if -# no arguments are given then run as a hook script +# Allow dual mode: run from the command line just like the update hook, or +# if no arguments are given then run as a hook script if [ -n "$1" -a -n "$2" -a -n "$3" ]; then # Output to the terminal in command line mode - if someone wanted to - # resend an email; they could redirect the output to sendmail themselves + # resend an email; they could redirect the output to sendmail + # themselves PAGER= generate_email $2 $3 $1 else while read oldrev newrev refname From e7509ee388480046a685f885431291f484de66de Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Tue, 6 Nov 2007 13:49:30 +0000 Subject: [PATCH 52/74] contrib/hooks/post-receive-email: make subject prefix configurable Email subjects are prefixed with "[SCM] " by default, make this optionally configurable through the hooks.emailprefix config option. Suggested by martin f krafft through http://bugs.debian.org/428418 Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 9b9a97777..3904c182e 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -35,10 +35,12 @@ # hooks.envelopesender # If set then the -f option is passed to sendmail to allow the envelope # sender address to be set +# hooks.emailprefix +# All emails have their subjects prefixed with this prefix, or "[SCM]" +# if emailprefix is unset, to aid filtering # # Notes # ----- -# All emails have their subjects prefixed with "[SCM]" to aid filtering. # All emails include the headers "X-Git-Refname", "X-Git-Oldrev", # "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and # give information for debugging. @@ -188,7 +190,7 @@ generate_email_header() # Generate header cat <<-EOF To: $recipients - Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe + Subject: ${emailprefix}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe X-Git-Refname: $refname X-Git-Reftype: $refname_type X-Git-Oldrev: $oldrev @@ -604,7 +606,6 @@ send_mail() # ---------------------------- main() # --- Constants -EMAILPREFIX="[SCM] " LOGBEGIN="- Log -----------------------------------------------------------------" LOGEND="-----------------------------------------------------------------------" @@ -628,6 +629,7 @@ fi recipients=$(git repo-config hooks.mailinglist) announcerecipients=$(git repo-config hooks.announcelist) envelopesender=$(git-repo-config hooks.envelopesender) +emailprefix=$(git-repo-config hooks.emailprefix || echo '[SCM] ') # --- Main loop # Allow dual mode: run from the command line just like the update hook, or From 6d0618a820a20846d60b665897fcce600f452eec Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 8 Nov 2007 00:33:19 +0000 Subject: [PATCH 53/74] Add Documentation/CodingGuidelines Even if our code is quite a good documentation for our coding style, some people seem to prefer a document describing it. The part about the shell scripts is clearly just copied from one of Junio's helpful mails, and some parts were added from comments by Junio, Andreas Ericsson and Robin Rosenberg. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Documentation/CodingGuidelines | 112 +++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 Documentation/CodingGuidelines diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines new file mode 100644 index 000000000..3b042db62 --- /dev/null +++ b/Documentation/CodingGuidelines @@ -0,0 +1,112 @@ +Like other projects, we also have some guidelines to keep to the +code. For git in general, three rough rules are: + + - Most importantly, we never say "It's in POSIX; we'll happily + ignore your needs should your system not conform to it." + We live in the real world. + + - However, we often say "Let's stay away from that construct, + it's not even in POSIX". + + - In spite of the above two rules, we sometimes say "Although + this is not in POSIX, it (is so convenient | makes the code + much more readable | has other good characteristics) and + practically all the platforms we care about support it, so + let's use it". + + Again, we live in the real world, and it is sometimes a + judgement call, the decision based more on real world + constraints people face than what the paper standard says. + + +As for more concrete guidelines, just imitate the existing code +(this is a good guideline, no matter which project you are +contributing to). But if you must have a list of rules, +here they are. + +For shell scripts specifically (not exhaustive): + + - We prefer $( ... ) for command substitution; unlike ``, it + properly nests. It should have been the way Bourne spelled + it from day one, but unfortunately isn't. + + - We use ${parameter-word} and its [-=?+] siblings, and their + colon'ed "unset or null" form. + + - We use ${parameter#word} and its [#%] siblings, and their + doubled "longest matching" form. + + - We use Arithmetic Expansion $(( ... )). + + - No "Substring Expansion" ${parameter:offset:length}. + + - No shell arrays. + + - No strlen ${#parameter}. + + - No regexp ${parameter/pattern/string}. + + - We do not use Process Substitution <(list) or >(list). + + - We prefer "test" over "[ ... ]". + + - We do not write the noiseword "function" in front of shell + functions. + +For C programs: + + - We use tabs to indent, and interpret tabs as taking up to + 8 spaces. + + - We try to keep to at most 80 characters per line. + + - When declaring pointers, the star sides with the variable + name, i.e. "char *string", not "char* string" or + "char * string". This makes it easier to understand code + like "char *string, c;". + + - We avoid using braces unnecessarily. I.e. + + if (bla) { + x = 1; + } + + is frowned upon. A gray area is when the statement extends + over a few lines, and/or you have a lengthy comment atop of + it. Also, like in the Linux kernel, if there is a long list + of "else if" statements, it can make sense to add braces to + single line blocks. + + - Try to make your code understandable. You may put comments + in, but comments invariably tend to stale out when the code + they were describing changes. Often splitting a function + into two makes the intention of the code much clearer. + + - Double negation is often harder to understand than no negation + at all. + + - Some clever tricks, like using the !! operator with arithmetic + constructs, can be extremely confusing to others. Avoid them, + unless there is a compelling reason to use them. + + - Use the API. No, really. We have a strbuf (variable length + string), several arrays with the ALLOC_GROW() macro, a + path_list for sorted string lists, a hash map (mapping struct + objects) named "struct decorate", amongst other things. + + - When you come up with an API, document it. + + - The first #include in C files, except in platform specific + compat/ implementations, should be git-compat-util.h or another + header file that includes it, such as cache.h or builtin.h. + + - If you are planning a new command, consider writing it in shell + or perl first, so that changes in semantics can be easily + changed and discussed. Many git commands started out like + that, and a few are still scripts. + + - Avoid introducing a new dependency into git. This means you + usually should stay away from scripting languages not already + used in the git core command set (unless your command is clearly + separate from it, such as an importer to convert random-scm-X + repositories to git). From 609a2289d76fd9a7dddceabc63d1654fe61e0ffb Mon Sep 17 00:00:00 2001 From: David Symonds Date: Wed, 7 Nov 2007 14:24:28 +1100 Subject: [PATCH 54/74] Improve accuracy of check for presence of deflateBound. ZLIB_VERNUM isn't defined in some zlib versions, so this patch does a proper linking test in autoconf to see whether deflateBound exists in zlib. Also, setting NO_DEFLATE_BOUND will also work for folk not using autoconf. Signed-off-by: David Symonds Signed-off-by: Junio C Hamano --- Makefile | 6 ++++++ cache.h | 2 +- config.mak.in | 1 + configure.ac | 20 ++++++++++++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e70e3209d..783cd5bf6 100644 --- a/Makefile +++ b/Makefile @@ -94,6 +94,8 @@ all:: # Define OLD_ICONV if your library has an old iconv(), where the second # (input buffer pointer) parameter is declared with type (const char **). # +# Define NO_DEFLATE_BOUND if your zlib does not have deflateBound. +# # Define NO_R_TO_GCC_LINKER if your gcc does not like "-R/path/lib" # that tells runtime paths to dynamic libraries; # "-Wl,-rpath=/path/lib" is used instead. @@ -637,6 +639,10 @@ ifdef OLD_ICONV BASIC_CFLAGS += -DOLD_ICONV endif +ifdef NO_DEFLATE_BOUND + BASIC_CFLAGS += -DNO_DEFLATE_BOUND +endif + ifdef PPC_SHA1 SHA1_HEADER = "ppc/sha1.h" LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o diff --git a/cache.h b/cache.h index fc195bc47..ddc011d19 100644 --- a/cache.h +++ b/cache.h @@ -6,7 +6,7 @@ #include SHA1_HEADER #include -#if ZLIB_VERNUM < 0x1200 +#if defined(NO_DEFLATE_BOUND) || ZLIB_VERNUM < 0x1200 #define deflateBound(c,s) ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11) #endif diff --git a/config.mak.in b/config.mak.in index a3032e389..776b80565 100644 --- a/config.mak.in +++ b/config.mak.in @@ -38,3 +38,4 @@ NO_STRCASESTR=@NO_STRCASESTR@ NO_STRLCPY=@NO_STRLCPY@ NO_SETENV=@NO_SETENV@ NO_ICONV=@NO_ICONV@ +NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@ diff --git a/configure.ac b/configure.ac index ed7cc895d..ab516db37 100644 --- a/configure.ac +++ b/configure.ac @@ -182,6 +182,26 @@ AC_SUBST(NEEDS_LIBICONV) AC_SUBST(NO_ICONV) test -n "$NEEDS_LIBICONV" && LIBS="$LIBS -liconv" # +# Define NO_DEFLATE_BOUND if deflateBound is missing from zlib. +AC_DEFUN([ZLIBTEST_SRC], [ +#include + +int main(void) +{ + deflateBound(0, 0); + return 0; +} +]) +AC_MSG_CHECKING([for deflateBound in -lz]) +old_LIBS="$LIBS" +LIBS="$LIBS -lz" +AC_LINK_IFELSE(ZLIBTEST_SRC, + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + NO_DEFLATE_BOUND=yes]) +LIBS="$old_LIBS" +AC_SUBST(NO_DEFLATE_BOUND) +# # Define NEEDS_SOCKET if linking with libc is not enough (SunOS, # Patrick Mauritz). AC_CHECK_LIB([c], [socket], From fc8b5f0307d5477cb4a52d3b54ff098f8ea34c8e Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 8 Nov 2007 00:41:22 +0000 Subject: [PATCH 55/74] Deprecate git-lost-found "git fsck" learnt the option "--lost-found" in v1.5.3-rc0~5, to make "git lost-found" obsolete. It is time to deprecate "git lost-found". Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.4.txt | 3 +++ Documentation/git-lost-found.txt | 4 ++++ git-lost-found.sh | 2 ++ 3 files changed, 9 insertions(+) diff --git a/Documentation/RelNotes-1.5.4.txt b/Documentation/RelNotes-1.5.4.txt index 133fa64d2..65dd1b0a2 100644 --- a/Documentation/RelNotes-1.5.4.txt +++ b/Documentation/RelNotes-1.5.4.txt @@ -46,6 +46,9 @@ Updates since v1.5.3 * Various Perforce importer updates. + * git-lost-found was deprecated in favor of git-fsck's --lost-found + option. + Fixes since v1.5.3 ------------------ diff --git a/Documentation/git-lost-found.txt b/Documentation/git-lost-found.txt index bc739117b..7f808fcd7 100644 --- a/Documentation/git-lost-found.txt +++ b/Documentation/git-lost-found.txt @@ -11,6 +11,10 @@ SYNOPSIS DESCRIPTION ----------- + +*NOTE*: this command is deprecated. Use gitlink:git-fsck[1] with +the option '--lost-found' instead. + Finds dangling commits and tags from the object database, and creates refs to them in the .git/lost-found/ directory. Commits and tags that dereference to commits are stored in .git/lost-found/commit, diff --git a/git-lost-found.sh b/git-lost-found.sh index c0b00e0fd..f2ec5d147 100755 --- a/git-lost-found.sh +++ b/git-lost-found.sh @@ -4,6 +4,8 @@ USAGE='' SUBDIRECTORY_OK='Yes' . git-sh-setup +echo "WARNING: '$0' is deprecated in favor of 'git fsck --lost-found'" >&2 + if [ "$#" != "0" ] then usage From 3c307bfbe8f8f9223c9b3cef8fdf2fabd05cda6d Mon Sep 17 00:00:00 2001 From: Ralf Wildenhues Date: Tue, 6 Nov 2007 21:12:45 +0100 Subject: [PATCH 56/74] Fix minor nits in configure.ac Avoid "test -o" as it is only XSI not POSIX, and not portable. Avoid exit(3) in test programs in favor of return, to accommodate for newer Autoconf not providing a declaration for exit. Signed-off-by: Junio C Hamano --- configure.ac | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index ed7cc895d..bd8051766 100644 --- a/configure.ac +++ b/configure.ac @@ -73,7 +73,7 @@ fi \ AC_ARG_WITH([lib], [AS_HELP_STRING([--with-lib=ARG], [ARG specifies alternative name for lib directory])], - [if test "$withval" = "no" -o "$withval" = "yes"; then \ + [if test "$withval" = "no" || test "$withval" = "yes"; then \ AC_MSG_WARN([You should provide name for --with-lib=ARG]); \ else \ GIT_CONF_APPEND_LINE(lib=$withval); \ @@ -245,9 +245,9 @@ AC_RUN_IFELSE( [AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT], [[char buf[64]; if (sprintf(buf, "%lld%hhd%jd%zd%td", (long long int)1, (char)2, (intmax_t)3, (size_t)4, (ptrdiff_t)5) != 5) - exit(1); + return 1; else if (strcmp(buf, "12345")) - exit(2);]])], + return 2;]])], [ac_cv_c_c99_format=yes], [ac_cv_c_c99_format=no]) ]) From 620bb245b945531bb6e08016d5f28caf9e797786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 7 Nov 2007 08:34:12 +0100 Subject: [PATCH 57/74] send-email: apply --suppress-from to S-o-b and cc-cmd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Cc: Ryan Anderson Signed-off-by: Junio C Hamano --- Documentation/git-send-email.txt | 3 +-- git-send-email.perl | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index e38b7021b..659215ac7 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -113,8 +113,7 @@ The --cc option must be repeated for each user you want on the cc list. is not set, this will be prompted for. --suppress-from, --no-suppress-from:: - If this is set, do not add the From: address to the cc: list, if it - shows up in a From: line. + If this is set, do not add the From: address to the cc: list. Default is the value of 'sendemail.suppressfrom' configuration value; if that is unspecified, default to --no-suppress-from. diff --git a/git-send-email.perl b/git-send-email.perl index 96051bc01..f4b8f9651 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -88,8 +88,7 @@ sub usage { --smtp-ssl If set, connects to the SMTP server using SSL. - --suppress-from Suppress sending emails to yourself if your address - appears in a From: line. Defaults to off. + --suppress-from Suppress sending emails to yourself. Defaults to off. --thread Specify that the "In-Reply-To:" header should be set on all emails. Defaults to on. @@ -730,6 +729,7 @@ sub send_message if (/^(Signed-off-by|Cc): (.*)$/i && $signed_off_cc) { my $c = $2; chomp $c; + next if ($c eq $sender and $suppress_from); push @cc, $c; printf("(sob) Adding cc: %s from line '%s'\n", $c, $_) unless $quiet; @@ -745,6 +745,7 @@ sub send_message my $c = $_; $c =~ s/^\s*//g; $c =~ s/\n$//g; + next if ($c eq $sender and $suppress_from); push @cc, $c; printf("(cc-cmd) Adding cc: %s from: '%s'\n", $c, $cc_cmd) unless $quiet; From e1f14cce694602ca879bea03d143add1c18dff52 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Sat, 3 Nov 2007 14:08:05 +0100 Subject: [PATCH 58/74] Small code readability improvement in show_reference() in builtin-tag.c Signed-off-by: Mike Hommey Signed-off-by: Junio C Hamano --- builtin-tag.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/builtin-tag.c b/builtin-tag.c index 66e5a5830..4aca3dc79 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -81,17 +81,16 @@ static int show_reference(const char *refname, const unsigned char *sha1, } printf("%-15s ", refname); - sp = buf = read_sha1_file(sha1, &type, &size); - if (!buf) + buf = read_sha1_file(sha1, &type, &size); + if (!buf || !size) return 0; - if (!size) { + + /* skip header */ + sp = strstr(buf, "\n\n"); + if (!sp) { free(buf); return 0; } - /* skip header */ - while (sp + 1 < buf + size && - !(sp[0] == '\n' && sp[1] == '\n')) - sp++; /* only take up to "lines" lines, and strip the signature */ for (i = 0, sp += 2; i < filter->lines && sp < buf + size && From d50a4bc4e91f03cecc337e5e95dc4c0e0a4054f0 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Tue, 6 Nov 2007 08:54:18 +0000 Subject: [PATCH 59/74] git-mailsplit: with maildirs not only process cur/, but also new/ When saving patches to a maildir with e.g. mutt, the files are put into the new/ subdirectory of the maildir, not cur/. This makes git-am state "Nothing to do.". This patch lets git-mailsplit additional check new/ after reading cur/. This was reported by Joey Hess through http://bugs.debian.org/447396 Signed-off-by: Gerrit Pape Acked-by: Jeff King Acked-by: Alex Riesen Acked-by: Fernando J. Pereda Signed-off-by: Junio C Hamano --- builtin-mailsplit.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c index 43fc373a1..10fa17734 100644 --- a/builtin-mailsplit.c +++ b/builtin-mailsplit.c @@ -101,20 +101,29 @@ static int populate_maildir_list(struct path_list *list, const char *path) { DIR *dir; struct dirent *dent; + char name[PATH_MAX]; + char *subs[] = { "cur", "new", NULL }; + char **sub; + + for (sub = subs; *sub; ++sub) { + snprintf(name, sizeof(name), "%s/%s", path, *sub); + if ((dir = opendir(name)) == NULL) { + if (errno == ENOENT) + continue; + error("cannot opendir %s (%s)", name, strerror(errno)); + return -1; + } - if ((dir = opendir(path)) == NULL) { - error("cannot opendir %s (%s)", path, strerror(errno)); - return -1; - } + while ((dent = readdir(dir)) != NULL) { + if (dent->d_name[0] == '.') + continue; + snprintf(name, sizeof(name), "%s/%s", *sub, dent->d_name); + path_list_insert(name, list); + } - while ((dent = readdir(dir)) != NULL) { - if (dent->d_name[0] == '.') - continue; - path_list_insert(dent->d_name, list); + closedir(dir); } - closedir(dir); - return 0; } @@ -122,19 +131,17 @@ static int split_maildir(const char *maildir, const char *dir, int nr_prec, int skip) { char file[PATH_MAX]; - char curdir[PATH_MAX]; char name[PATH_MAX]; int ret = -1; int i; struct path_list list = {NULL, 0, 0, 1}; - snprintf(curdir, sizeof(curdir), "%s/cur", maildir); - if (populate_maildir_list(&list, curdir) < 0) + if (populate_maildir_list(&list, maildir) < 0) goto out; for (i = 0; i < list.nr; i++) { FILE *f; - snprintf(file, sizeof(file), "%s/%s", curdir, list.items[i].path); + snprintf(file, sizeof(file), "%s/%s", maildir, list.items[i].path); f = fopen(file, "r"); if (!f) { error("cannot open mail %s (%s)", file, strerror(errno)); @@ -152,10 +159,9 @@ static int split_maildir(const char *maildir, const char *dir, fclose(f); } - path_list_clear(&list, 1); - ret = skip; out: + path_list_clear(&list, 1); return ret; } From 1756fed9ee849c687f92cac30d86d2955d8c94c4 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Thu, 8 Nov 2007 14:02:00 +0000 Subject: [PATCH 60/74] hooks--update: fix test for properly set up project description file The update hook template intends to abort if the project description file hasn't been adjusted or is empty. This patch fixes the check for 'being adjusted'. Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- templates/hooks--update | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/hooks--update b/templates/hooks--update index d8c76264b..7e4d6270a 100644 --- a/templates/hooks--update +++ b/templates/hooks--update @@ -34,8 +34,8 @@ fi allowunannotated=$(git-repo-config --bool hooks.allowunannotated) # check for no description -projectdesc=$(sed -e '1p' "$GIT_DIR/description") -if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb" ]; then +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb." ]; then echo "*** Project description file hasn't been set" >&2 exit 1 fi From ad7638b2edbad05d5f5581ec76d49094d8ba9237 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Thu, 8 Nov 2007 09:47:39 +0000 Subject: [PATCH 61/74] hooks--update: decline deleting tags or branches by default, add config options Decline deleting tags or branches through git push : by default, support config options hooks.allowdeletetag, hooks.allowdeletebranch to override this per repository. Before this patch the update hook interpreted deleting a tag, no matter if annotated or not, through git push : as unannotated tag, and declined it by default, but with an unappropriate error message: $ git push origin :atag deleting 'refs/tags/atag' *** The un-annotated tag, atag, is not allowed in this repository *** Use 'git tag [ -a | -s ]' for tags you want to propagate. ng refs/tags/atag hook declined error: hooks/update exited with error code 1 error: hook declined to update refs/tags/atag error: failed to push to 'monolith:/git/qm/test-repo' Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- templates/hooks--update | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/templates/hooks--update b/templates/hooks--update index 7e4d6270a..bd93dd197 100644 --- a/templates/hooks--update +++ b/templates/hooks--update @@ -10,6 +10,12 @@ # hooks.allowunannotated # This boolean sets whether unannotated tags will be allowed into the # repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. # # --- Command line @@ -32,6 +38,8 @@ fi # --- Config allowunannotated=$(git-repo-config --bool hooks.allowunannotated) +allowdeletebranch=$(git-repo-config --bool hooks.allowdeletebranch) +allowdeletetag=$(git-repo-config --bool hooks.allowdeletetag) # check for no description projectdesc=$(sed -e '1q' "$GIT_DIR/description") @@ -41,9 +49,9 @@ if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file t fi # --- Check types -# if $newrev is 0000...0000, it's a commit to delete a branch +# if $newrev is 0000...0000, it's a commit to delete a ref. if [ "$newrev" = "0000000000000000000000000000000000000000" ]; then - newrev_type=commit + newrev_type=delete else newrev_type=$(git-cat-file -t $newrev) fi @@ -58,15 +66,36 @@ case "$refname","$newrev_type" in exit 1 fi ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; refs/tags/*,tag) # annotated tag ;; refs/heads/*,commit) # branch ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; refs/remotes/*,commit) # tracking branch ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; *) # Anything else (is there anything else?) echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 From 5c355059c379a55366bed573d4e65dba4b5a0565 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Thu, 8 Nov 2007 12:11:57 +0000 Subject: [PATCH 62/74] contrib/hooks/post-receive-email: remove cruft, $committer is not used Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 4 ---- 1 file changed, 4 deletions(-) diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 3904c182e..7511ea079 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -156,10 +156,6 @@ generate_email() fi # Email parameters - # The committer will be obtained from the latest existing rev; so - # for a deletion it will be the oldrev, for the others, then newrev - committer=$(git show --pretty=full -s $rev | sed -ne "s/^Commit: //p" | - sed -ne 's/\(.*\) /dev/null) From f3fa1838024dc0f3741d2f9654af92f6a1bb34bd Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 8 Nov 2007 15:35:32 -0800 Subject: [PATCH 63/74] Style: place opening brace of a function definition at column 1 Signed-off-by: Junio C Hamano --- bundle.c | 3 ++- daemon.c | 6 ++++-- dir.c | 3 ++- help.c | 3 ++- quote.c | 3 ++- setup.c | 3 ++- transport.c | 6 ++++-- utf8.c | 3 ++- 8 files changed, 20 insertions(+), 10 deletions(-) diff --git a/bundle.c b/bundle.c index 0869fcf02..e4d60cde6 100644 --- a/bundle.c +++ b/bundle.c @@ -23,7 +23,8 @@ static void add_to_ref_list(const unsigned char *sha1, const char *name, } /* returns an fd */ -int read_bundle_header(const char *path, struct bundle_header *header) { +int read_bundle_header(const char *path, struct bundle_header *header) +{ char buffer[1024]; int fd; long fpos; diff --git a/daemon.c b/daemon.c index b8df980dc..41a60af62 100644 --- a/daemon.c +++ b/daemon.c @@ -406,7 +406,8 @@ static struct daemon_service daemon_service[] = { { "receive-pack", "receivepack", receive_pack, 0, 1 }, }; -static void enable_service(const char *name, int ena) { +static void enable_service(const char *name, int ena) +{ int i; for (i = 0; i < ARRAY_SIZE(daemon_service); i++) { if (!strcmp(daemon_service[i].name, name)) { @@ -417,7 +418,8 @@ static void enable_service(const char *name, int ena) { die("No such service %s", name); } -static void make_service_overridable(const char *name, int ena) { +static void make_service_overridable(const char *name, int ena) +{ int i; for (i = 0; i < ARRAY_SIZE(daemon_service); i++) { if (!strcmp(daemon_service[i].name, name)) { diff --git a/dir.c b/dir.c index 5bcc764c9..01790ab27 100644 --- a/dir.c +++ b/dir.c @@ -298,7 +298,8 @@ int excluded(struct dir_struct *dir, const char *pathname) return 0; } -static struct dir_entry *dir_entry_new(const char *pathname, int len) { +static struct dir_entry *dir_entry_new(const char *pathname, int len) +{ struct dir_entry *ent; ent = xmalloc(sizeof(*ent) + len + 1); diff --git a/help.c b/help.c index 855aeef92..8217d9778 100644 --- a/help.c +++ b/help.c @@ -79,7 +79,8 @@ static void uniq(struct cmdnames *cmds) cmds->cnt = j; } -static void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) { +static void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) +{ int ci, cj, ei; int cmp; diff --git a/quote.c b/quote.c index 919d0920a..04557833a 100644 --- a/quote.c +++ b/quote.c @@ -131,7 +131,8 @@ static signed char const sq_lookup[256] = { /* 0x80 */ /* set to 0 */ }; -static inline int sq_must_quote(char c) { +static inline int sq_must_quote(char c) +{ return sq_lookup[(unsigned char)c] + quote_path_fully > 0; } diff --git a/setup.c b/setup.c index df006d9c4..1e2c55d07 100644 --- a/setup.c +++ b/setup.c @@ -206,7 +206,8 @@ static const char *set_work_tree(const char *dir) return NULL; } -void setup_work_tree(void) { +void setup_work_tree(void) +{ const char *work_tree = get_git_work_tree(); const char *git_dir = get_git_dir(); if (!is_absolute_path(git_dir)) diff --git a/transport.c b/transport.c index d44fe7cee..fa5cfbb09 100644 --- a/transport.c +++ b/transport.c @@ -380,7 +380,8 @@ static int disconnect_walker(struct transport *transport) } #ifndef NO_CURL -static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) { +static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) +{ const char **argv; int argc; int err; @@ -646,7 +647,8 @@ static int fetch_refs_via_pack(struct transport *transport, return 0; } -static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) { +static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) +{ struct git_transport_data *data = transport->data; const char **argv; char *rem; diff --git a/utf8.c b/utf8.c index 4efef6faf..8095a71d3 100644 --- a/utf8.c +++ b/utf8.c @@ -11,7 +11,8 @@ struct interval { }; /* auxiliary function for binary search in interval table */ -static int bisearch(ucs_char_t ucs, const struct interval *table, int max) { +static int bisearch(ucs_char_t ucs, const struct interval *table, int max) +{ int min = 0; int mid; From b5e960b108b48b3ebd92a3c1b22b13a9edd127dc Mon Sep 17 00:00:00 2001 From: Ralf Wildenhues Date: Thu, 8 Nov 2007 22:47:36 +0100 Subject: [PATCH 64/74] Avoid a few unportable, needlessly nested "...`...". Signed-off-by: Ralf Wildenhues Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 2 +- git-request-pull.sh | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index f28c3df20..d65df2cb8 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -361,7 +361,7 @@ do -s|--strategy) case "$#,$1" in *,*=*) - STRATEGY="-s `expr "z$1" : 'z-[^=]*=\(.*\)'`" ;; + STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;; 1,*) usage ;; *) diff --git a/git-request-pull.sh b/git-request-pull.sh index a99243067..95ad66630 100755 --- a/git-request-pull.sh +++ b/git-request-pull.sh @@ -24,13 +24,13 @@ headrev=`git rev-parse --verify "$head"^0` || exit merge_base=`git merge-base $baserev $headrev` || die "fatal: No commits in common between $base and $head" -url="`get_remote_url "$url"`" -branch=`git peek-remote "$url" \ +url=$(get_remote_url "$url") +branch=$(git peek-remote "$url" \ | sed -n -e "/^$headrev refs.heads./{ s/^.* refs.heads.// p q - }"` + }") if [ -z "$branch" ]; then echo "warn: No branch of $url is at:" >&2 git log --max-count=1 --pretty='format:warn: %h: %s' $headrev >&2 From 4f2e94c0f7685f9c58e67b0651147684efb0f57e Mon Sep 17 00:00:00 2001 From: Ralf Wildenhues Date: Thu, 8 Nov 2007 22:48:49 +0100 Subject: [PATCH 65/74] Fix sed string regex escaping in module_name. When escaping a string to be used as a sed regex, it is important to only escape active characters. Escaping other characters is undefined according to POSIX, and in practice leads to issues with extensions such as GNU sed's \+. Signed-off-by: Ralf Wildenhues 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 673aa27a4..b91d62632 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -47,7 +47,7 @@ get_repo_base() { module_name() { # Do we have "submodule..path = $1" defined in .gitmodules file? - re=$(printf '%s' "$1" | sed -e 's/\([^a-zA-Z0-9_]\)/\\\1/g') + re=$(printf '%s' "$1" | sed -e 's/[].[^$\\*]/\\&/g') name=$( GIT_CONFIG=.gitmodules \ git config --get-regexp '^submodule\..*\.path$' | sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' ) From fbbdbfc3ccff209136c9f94fed8321e5d3d0ca85 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 8 Nov 2007 16:24:00 -0800 Subject: [PATCH 66/74] refresh_index_quietly(): express "optional" nature of index writing better The point of the part of the code this patch touches is that if we modified the active_cache, we try to write it out and make it the index file for later users to use by calling "commit_locked_index", but we do not really care about the failure from this sequence because it is done purely as an optimization. The original code called three functions primarily for their side effects but as condition of an if statement, which is admittedly a bad style. Incidentally, it squelches an "empty if body" warning from gcc. Signed-off-by: Junio C Hamano --- builtin-diff.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/builtin-diff.c b/builtin-diff.c index f77352b40..f557d2192 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -200,15 +200,11 @@ static void refresh_index_quietly(void) discard_cache(); read_cache(); refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED); - if (active_cache_changed) { - if (write_cache(fd, active_cache, active_nr) || - close(fd) || - commit_locked_index(lock_file)) - ; /* - * silently ignore it -- we haven't mucked - * with the real index. - */ - } + + if (active_cache_changed && + !write_cache(fd, active_cache, active_nr) && !close(fd)) + commit_locked_index(lock_file); + rollback_lock_file(lock_file); } From d349a03e7401625a9bb92f403f36923472e498c6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 8 Nov 2007 16:41:56 -0800 Subject: [PATCH 67/74] Makefile: add missing dependency on wt-status.h Signed-off-by: Junio C Hamano --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index e70e3209d..2331e45ad 100644 --- a/Makefile +++ b/Makefile @@ -894,6 +894,7 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS) $(LIB_OBJS) $(BUILTIN_OBJS) fetch.o: $(LIB_H) $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) $(DIFF_OBJS): diffcore.h +builtin-revert.o builtin-runstatus.o wt-status.o: wt-status.h $(LIB_FILE): $(LIB_OBJS) $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) From d9c8344b467bb97b8dca8d811c99d4735eca88f2 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Fri, 9 Nov 2007 00:41:39 +0100 Subject: [PATCH 68/74] stop t1400 hiding errors in tests The last rm in the test was lacking an "&&" before it, which caused the errors in the commands be silently hidden. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- t/t1400-update-ref.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index ce045b2a5..a90824ba8 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -205,7 +205,7 @@ test_expect_success \ echo $h_TEST >.git/MERGE_HEAD && GIT_AUTHOR_DATE="2005-05-26 23:45" \ GIT_COMMITTER_DATE="2005-05-26 23:45" git-commit -F M && - h_MERGED=$(git rev-parse --verify HEAD) + h_MERGED=$(git rev-parse --verify HEAD) && rm -f M' cat >expect < Date: Fri, 9 Nov 2007 00:21:42 +0100 Subject: [PATCH 69/74] instaweb: Minor cleanups and fixes for potential problems Fix path quoting and test of empty values that some shells do not like. Remove duplicate check and setting of $browser. Signed-off-by: Jonas Fonseca Signed-off-by: Junio C Hamano --- git-instaweb.sh | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/git-instaweb.sh b/git-instaweb.sh index 41ff08f8e..2ca487d7d 100755 --- a/git-instaweb.sh +++ b/git-instaweb.sh @@ -15,7 +15,7 @@ browser="`git config --get instaweb.browser`" port=`git config --get instaweb.port` module_path="`git config --get instaweb.modulepath`" -conf=$GIT_DIR/gitweb/httpd.conf +conf="$GIT_DIR/gitweb/httpd.conf" # Defaults: @@ -32,7 +32,7 @@ start_httpd () { httpd_only="`echo $httpd | cut -f1 -d' '`" if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null;; esac then - $httpd $fqgitdir/gitweb/httpd.conf + $httpd "$fqgitdir/gitweb/httpd.conf" else # many httpds are installed in /usr/sbin or /usr/local/sbin # these days and those are not in most users $PATHs @@ -146,14 +146,14 @@ server.pid-file = "$fqgitdir/pid" cgi.assign = ( ".cgi" => "" ) mimetype.assign = ( ".css" => "text/css" ) EOF - test "$local" = true && echo 'server.bind = "127.0.0.1"' >> "$conf" + test x"$local" = xtrue && echo 'server.bind = "127.0.0.1"' >> "$conf" } apache2_conf () { test -z "$module_path" && module_path=/usr/lib/apache2/modules mkdir -p "$GIT_DIR/gitweb/logs" bind= - test "$local" = true && bind='127.0.0.1:' + test x"$local" = xtrue && bind='127.0.0.1:' echo 'text/css css' > $fqgitdir/mime.types cat > "$conf" < Date: Thu, 8 Nov 2007 19:40:25 +0300 Subject: [PATCH 70/74] SubmittingPatches: improve the 'Patch:' section of the checklist There were 2 items "send patch to..." but having different set of addresses to send patch to. Merge them together and move the resulting item to the end of checklist. Signed-off-by: Sergei Organov Signed-off-by: Junio C Hamano --- Documentation/SubmittingPatches | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 61635bf04..83bf54c7a 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -20,9 +20,6 @@ Checklist (and a short version for the impatient): Patch: - use "git format-patch -M" to create the patch - - send your patch to . If you use - git-send-email(1), please test it first by sending - email to yourself. - do not PGP sign your patch - do not attach your patch, but read in the mail body, unless you cannot teach your mailer to @@ -31,13 +28,15 @@ Checklist (and a short version for the impatient): corrupt whitespaces. - provide additional information (which is unsuitable for the commit message) between the "---" and the diffstat - - send the patch to the list (git@vger.kernel.org) and the - maintainer (gitster@pobox.com). - if you change, add, or remove a command line option or make some other user interface change, the associated documentation should be updated as well. - if your name is not writable in ASCII, make sure that you send off a message in the correct encoding. + - send the patch to the list (git@vger.kernel.org) and the + maintainer (gitster@pobox.com). If you use + git-send-email(1), please test it first by sending + email to yourself. Long version: From cb6c162fba3fe3e1333b48c6062c18108c41fb82 Mon Sep 17 00:00:00 2001 From: Benoit Sigoure Date: Thu, 8 Nov 2007 19:56:28 +0100 Subject: [PATCH 71/74] git-send-email: Change the prompt for the subject of the initial message. I never understood what this prompt was asking for until I read the actual source code. I think this wording is much more understandable. Signed-off-by: Benoit Sigoure Signed-off-by: Junio C Hamano --- git-send-email.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-send-email.perl b/git-send-email.perl index 9547cc37a..8760cf88a 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -303,7 +303,7 @@ sub expand_aliases { if (!defined $initial_subject && $compose) { do { - $_ = $term->readline("What subject should the emails start with? ", + $_ = $term->readline("What subject should the initial email start with? ", $initial_subject); } while (!defined $_); $initial_subject = $_; From b9217c09386e5313ac6a54cc91cf03149514154b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 9 Nov 2007 00:17:26 -0800 Subject: [PATCH 72/74] Start preparing for 1.5.3.6 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.3.6.txt | 21 +++++++++++++++++++++ RelNotes | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 Documentation/RelNotes-1.5.3.6.txt diff --git a/Documentation/RelNotes-1.5.3.6.txt b/Documentation/RelNotes-1.5.3.6.txt new file mode 100644 index 000000000..06e44f773 --- /dev/null +++ b/Documentation/RelNotes-1.5.3.6.txt @@ -0,0 +1,21 @@ +GIT v1.5.3.6 Release Notes +========================== + +Fixes since v1.5.3.5 +-------------------- + + * git-cvsexportcommit handles root commits better; + + * git-svn dcommit used to clobber when sending a series of + patches; + + * git-grep sometimes refused to work when your index was + unmerged; + + * Quite a lot of documentation clarifications. + +-- +exec >/var/tmp/1 +O=v1.5.3.5-32-gcb6c162 +echo O=`git describe refs/heads/maint` +git shortlog --no-merges $O..refs/heads/maint diff --git a/RelNotes b/RelNotes index a1ee57eb5..07ff9722c 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes-1.5.3.5.txt \ No newline at end of file +Documentation/RelNotes-1.5.3.6.txt \ No newline at end of file From 063036af0832710cfcb1380cc4f9c4597b98cc26 Mon Sep 17 00:00:00 2001 From: Ralf Wildenhues Date: Thu, 8 Nov 2007 22:48:24 +0100 Subject: [PATCH 73/74] git-bisect.sh: Fix sed script to work with AIX and BSD sed. \n is not portable in a s/// replacement string, only in the regex part. backslash-newline helps. Signed-off-by: Ralf Wildenhues Signed-off-by: Junio C Hamano --- git-bisect.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git-bisect.sh b/git-bisect.sh index b74f44df6..1ed44e56a 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -275,7 +275,8 @@ exit_if_skipped_commits () { if expr "$_tried" : ".*[|].*" > /dev/null ; then echo "There are only 'skip'ped commit left to test." echo "The first bad commit could be any of:" - echo "$_tried" | sed -e 's/[|]/\n/g' + echo "$_tried" | sed -e 's/[|]/\ +/g' echo "We cannot bisect more!" exit 2 fi From c238dad407aec1ccf6e364f9c95e10e7f84eac8f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 9 Nov 2007 00:32:38 -0800 Subject: [PATCH 74/74] Update draft release notes for 1.5.4 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.4.txt | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/Documentation/RelNotes-1.5.4.txt b/Documentation/RelNotes-1.5.4.txt index 65dd1b0a2..93fb9c914 100644 --- a/Documentation/RelNotes-1.5.4.txt +++ b/Documentation/RelNotes-1.5.4.txt @@ -6,7 +6,10 @@ Updates since v1.5.3 * Comes with much improved gitk. - * git-reset is now built-in. + * "progress display" from many commands are a lot nicer to the + eye. Transfer commands show throughput data. + + * git-reset is now built-in and its output can be squelched with -q. * git-send-email can optionally talk over ssmtp and use SMTP-AUTH. @@ -49,6 +52,25 @@ Updates since v1.5.3 * git-lost-found was deprecated in favor of git-fsck's --lost-found option. + * git-svnimport was removed in favor of git-svn. + + * git-bisect learned "skip" action to mark untestable commits. + + * rename detection diff family, while detecting exact matches, + has been greatly optimized. + + * Example update and post-receive hooks have been improved. + + * In addition there are quite a few internal clean-ups. Notably + + - many fork/exec have been replaced with run-command API, + brought from the msysgit effort. + + - introduction and more use of the option parser API. + + - enhancement and more use of the strbuf API. + + Fixes since v1.5.3 ------------------ @@ -57,6 +79,6 @@ this release, unless otherwise noted. -- exec >/var/tmp/1 -O=v1.5.3.4-450-g952a9e5 +O=v1.5.3.5-618-g5d4138a echo O=`git describe refs/heads/master` git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint