From caf0c3d692a5a4639e48499711271cb279ecd0dc Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Tue, 11 Nov 2008 00:53:59 +0100 Subject: [PATCH 01/63] git send-email: make the message file name more specific. This helps editors choosing their syntax hilighting properly. Also make the file live under the git directory. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- git-send-email.perl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/git-send-email.perl b/git-send-email.perl index 94ca5c89ad..aaace02fa6 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -124,9 +124,6 @@ my $auth; sub unique_email_list(@); sub cleanup_compose_files(); -# Constants (essentially) -my $compose_filename = ".msg.$$"; - # Variables we fill in automatically, or via prompting: my (@to,@cc,@initial_cc,@bcclist,@xh, $initial_reply_to,$initial_subject,@files,$author,$sender,$smtp_authpass,$compose,$time); @@ -149,6 +146,7 @@ if ($@) { # Behavior modification variables my ($quiet, $dry_run) = (0, 0); +my $compose_filename = $repo->repo_path() . "/.gitsendemail.msg.$$"; # Variables with corresponding config settings my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd); From 5df9fcf695a0ba85abfeed68efb3b1c5890068d6 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Tue, 11 Nov 2008 00:54:00 +0100 Subject: [PATCH 02/63] git send-email: interpret unknown files as revision lists Filter out all the arguments git-send-email doesn't like to a git format-patch command, that dumps its content to a safe directory. Barf when a file/revision conflict occurs, allow it to be overriden --[no-]format-patch. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- Documentation/git-send-email.txt | 8 +++++- git-send-email.perl | 47 ++++++++++++++++++++++++++++---- t/t9001-send-email.sh | 8 ++++++ 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 82f505686e..0beaad45bf 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -8,7 +8,7 @@ git-send-email - Send a collection of patches as emails SYNOPSIS -------- -'git send-email' [options] [... file|directory] +'git send-email' [options] ... DESCRIPTION @@ -183,6 +183,12 @@ Administering --[no-]validate:: Perform sanity checks on patches. Currently, validation means the following: + +--[no-]format-patch:: + When an argument may be understood either as a reference or as a file name, + choose to understand it as a format-patch argument ('--format-patch') + or as a file name ('--no-format-patch'). By default, when such a conflict + occurs, git send-email will fail. + -- * Warn of patches that contain lines longer than 998 characters; this diff --git a/git-send-email.perl b/git-send-email.perl index aaace02fa6..6f5a613898 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -22,8 +22,12 @@ use Term::ReadLine; use Getopt::Long; use Data::Dumper; use Term::ANSIColor; +use File::Temp qw/ tempdir /; +use Error qw(:try); use Git; +Getopt::Long::Configure qw/ pass_through /; + package FakeTerm; sub new { my ($class, $reason) = @_; @@ -38,7 +42,7 @@ package main; sub usage { print <... +git send-email [options] Composing: --from * Email From: @@ -73,6 +77,8 @@ git send-email [options] ... --quiet * Output one line of info per email. --dry-run * Don't actually send the emails. --[no-]validate * Perform patch sanity checks. Default on. + --[no-]format-patch * understand any non optional arguments as + `git format-patch` ones. EOT exit(1); @@ -146,6 +152,7 @@ if ($@) { # Behavior modification variables my ($quiet, $dry_run) = (0, 0); +my $format_patch; my $compose_filename = $repo->repo_path() . "/.gitsendemail.msg.$$"; # Variables with corresponding config settings @@ -229,6 +236,7 @@ my $rc = GetOptions("sender|from=s" => \$sender, "envelope-sender=s" => \$envelope_sender, "thread!" => \$thread, "validate!" => \$validate, + "format-patch!" => \$format_patch, ); unless ($rc) { @@ -363,23 +371,52 @@ if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) { ($sender) = expand_aliases($sender) if defined $sender; +# returns 1 if the conflict must be solved using it as a format-patch argument +sub check_file_rev_conflict($) { + my $f = shift; + try { + $repo->command('rev-parse', '--verify', '--quiet', $f); + if (defined($format_patch)) { + print "foo\n"; + return $format_patch; + } + die(<command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts); +} + if ($validate) { foreach my $f (@files) { unless (-p $f) { diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 561ae7d0a6..617e97d963 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -292,4 +292,12 @@ test_expect_success '--compose adds MIME for utf8 subject' ' grep "^Subject: =?utf-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1 ' +test_expect_success 'detects ambiguous reference/file conflict' ' + echo master > master && + git add master && + git commit -m"add master" && + test_must_fail git send-email --dry-run master 2>errors && + grep disambiguate errors +' + test_done From 8fd5bb7f44b7192a564ebe4f9db376af9fe148ec Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Tue, 11 Nov 2008 00:54:01 +0100 Subject: [PATCH 03/63] git send-email: add --annotate option This allows to review every patch (and fix various aspects of them, or comment them) in an editor just before being sent. Combined to the fact that git send-email can now process revision lists, this makes git send-email and efficient way to review and send patches interactively. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- Documentation/git-send-email.txt | 11 +++++++++++ git-send-email.perl | 26 ++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 0beaad45bf..66d5f4cddc 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -37,6 +37,11 @@ The --bcc option must be repeated for each user you want on the bcc list. + The --cc option must be repeated for each user you want on the cc list. +--annotate:: + Review each patch you're about to send in an editor. The setting + 'sendemail.multiedit' defines if this will spawn one editor per patch + or one for all of them at once. + --compose:: Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an introductory message for the patch series. @@ -210,6 +215,12 @@ sendemail.aliasfiletype:: Format of the file(s) specified in sendemail.aliasesfile. Must be one of 'mutt', 'mailrc', 'pine', or 'gnus'. +sendemail.multiedit:: + If true (default), a single editor instance will be spawned to edit + files you have to edit (patches when '--annotate' is used, and the + summary when '--compose' is used). If false, files will be edited one + after the other, spawning a new editor each time. + Author ------ diff --git a/git-send-email.perl b/git-send-email.perl index 6f5a613898..ccb3b1816c 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -51,6 +51,7 @@ git send-email [options] --bcc * Email Bcc: --subject * Email "Subject:" --in-reply-to * Email "In-Reply-To:" + --annotate * Review each patch that will be sent in an editor. --compose * Open an editor for introduction. Sending: @@ -132,7 +133,8 @@ sub cleanup_compose_files(); # Variables we fill in automatically, or via prompting: my (@to,@cc,@initial_cc,@bcclist,@xh, - $initial_reply_to,$initial_subject,@files,$author,$sender,$smtp_authpass,$compose,$time); + $initial_reply_to,$initial_subject,@files, + $author,$sender,$smtp_authpass,$annotate,$compose,$time); my $envelope_sender; @@ -155,6 +157,17 @@ my ($quiet, $dry_run) = (0, 0); my $format_patch; my $compose_filename = $repo->repo_path() . "/.gitsendemail.msg.$$"; +# Handle interactive edition of files. +my $multiedit; +my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi"; +sub do_edit { + if (defined($multiedit) && !$multiedit) { + map { system('sh', '-c', $editor.' "$@"', $editor, $_); } @_; + } else { + system('sh', '-c', $editor.' "$@"', $editor, @_); + } +} + # Variables with corresponding config settings my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd); my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption); @@ -184,6 +197,7 @@ my %config_settings = ( "aliasesfile" => \@alias_files, "suppresscc" => \@suppress_cc, "envelopesender" => \$envelope_sender, + "multiedit" => \$multiedit, ); # Handle Uncouth Termination @@ -226,6 +240,7 @@ my $rc = GetOptions("sender|from=s" => \$sender, "smtp-ssl" => sub { $smtp_encryption = 'ssl' }, "smtp-encryption=s" => \$smtp_encryption, "identity=s" => \$identity, + "annotate" => \$annotate, "compose" => \$compose, "quiet" => \$quiet, "cc-cmd=s" => \$cc_cmd, @@ -532,7 +547,12 @@ EOT close(C); my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi"; - system('sh', '-c', $editor.' "$@"', $editor, $compose_filename); + + if ($annotate) { + do_edit($compose_filename, @files); + } else { + do_edit($compose_filename); + } open(C2,">",$compose_filename . ".final") or die "Failed to open $compose_filename.final : " . $!; @@ -581,6 +601,8 @@ EOT } @files = ($compose_filename . ".final", @files); +} elsif ($annotate) { + do_edit(@files); } # Variables we set as part of the loop over files From beece9dab8e26c98062351536ce0d871a066790e Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Tue, 11 Nov 2008 00:54:02 +0100 Subject: [PATCH 04/63] git send-email: ask less questions when --compose is used. When --compose is used, we can grab the From/Subject/In-Reply-To from the edited summary, let it be so and don't ask the user silly questions. The summary templates gets quite revamped, and includes the list of patches subjects that are going to be sent with this batch. When having a body full of empty lines, the summary isn't sent. Document that in the git-send-email manpage fully. Note: It doesn't deal with To/Cc/Bcc yet. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- Documentation/git-send-email.txt | 9 ++ git-send-email.perl | 187 +++++++++++++++++++------------ 2 files changed, 123 insertions(+), 73 deletions(-) diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 66d5f4cddc..acf8bf41d6 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -45,6 +45,15 @@ The --cc option must be repeated for each user you want on the cc list. --compose:: Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an introductory message for the patch series. ++ +When compose is in used, git send-email gets less interactive will use the +values of the headers you set there. If the body of the email (what you type +after the headers and a blank line) only contains blank (or GIT: prefixed) +lines, the summary won't be sent, but git-send-email will still use the +Headers values if you don't removed them. ++ +If it wasn't able to see a header in the summary it will ask you about it +interactively after quitting your editor. --from:: Specify the sender of the emails. This will default to diff --git a/git-send-email.perl b/git-send-email.perl index ccb3b1816c..9039cfde06 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -162,9 +162,17 @@ my $multiedit; my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi"; sub do_edit { if (defined($multiedit) && !$multiedit) { - map { system('sh', '-c', $editor.' "$@"', $editor, $_); } @_; + map { + system('sh', '-c', $editor.' "$@"', $editor, $_); + if (($? & 127) || ($? >> 8)) { + die("the editor exited uncleanly, aborting everything"); + } + } @_; } else { system('sh', '-c', $editor.' "$@"', $editor, @_); + if (($? & 127) || ($? >> 8)) { + die("the editor exited uncleanly, aborting everything"); + } } } @@ -450,6 +458,108 @@ if (@files) { usage(); } +sub get_patch_subject($) { + my $fn = shift; + open (my $fh, '<', $fn); + while (my $line = <$fh>) { + next unless ($line =~ /^Subject: (.*)$/); + close $fh; + return "GIT: $1\n"; + } + close $fh; + die "No subject line in $fn ?"; +} + +if ($compose) { + # Note that this does not need to be secure, but we will make a small + # effort to have it be unique + open(C,">",$compose_filename) + or die "Failed to open for writing $compose_filename: $!"; + + + my $tpl_sender = $sender || $repoauthor || $repocommitter || ''; + my $tpl_subject = $initial_subject || ''; + my $tpl_reply_to = $initial_reply_to || ''; + + print C <",$compose_filename . ".final") + or die "Failed to open $compose_filename.final : " . $!; + + open(C,"<",$compose_filename) + or die "Failed to open $compose_filename : " . $!; + + my $need_8bit_cte = file_has_nonascii($compose_filename); + my $in_body = 0; + my $summary_empty = 1; + while() { + next if m/^GIT: /; + if ($in_body) { + $summary_empty = 0 unless (/^\n$/); + } elsif (/^\n$/) { + $in_body = 1; + if ($need_8bit_cte) { + print C2 "MIME-Version: 1.0\n", + "Content-Type: text/plain; ", + "charset=utf-8\n", + "Content-Transfer-Encoding: 8bit\n"; + } + } elsif (/^MIME-Version:/i) { + $need_8bit_cte = 0; + } elsif (/^Subject:\s*(.+)\s*$/i) { + $initial_subject = $1; + my $subject = $initial_subject; + $_ = "Subject: " . + ($subject =~ /[^[:ascii:]]/ ? + quote_rfc2047($subject) : + $subject) . + "\n"; + } elsif (/^In-Reply-To:\s*(.+)\s*$/i) { + $initial_reply_to = $1; + next; + } elsif (/^From:\s*(.+)\s*$/i) { + $sender = $1; + next; + } elsif (/^(?:To|Cc|Bcc):/i) { + print "To/Cc/Bcc fields are not interpreted yet, they have been ignored\n"; + next; + } + print C2 $_; + } + close(C); + close(C2); + + if ($summary_empty) { + print "Summary email is empty, skipping it\n"; + $compose = -1; + } +} elsif ($annotate) { + do_edit(@files); +} + my $prompting = 0; if (!defined $sender) { $sender = $repoauthor || $repocommitter || ''; @@ -494,17 +604,6 @@ sub expand_aliases { @initial_cc = expand_aliases(@initial_cc); @bcclist = expand_aliases(@bcclist); -if (!defined $initial_subject && $compose) { - while (1) { - $_ = $term->readline("What subject should the initial email start with? ", $initial_subject); - last if defined $_; - print "\n"; - } - - $initial_subject = $_; - $prompting++; -} - if ($thread && !defined $initial_reply_to && $prompting) { while (1) { $_= $term->readline("Message-ID to be used as In-Reply-To for the first email? ", $initial_reply_to); @@ -531,64 +630,6 @@ if (!defined $smtp_server) { } if ($compose) { - # Note that this does not need to be secure, but we will make a small - # effort to have it be unique - open(C,">",$compose_filename) - or die "Failed to open for writing $compose_filename: $!"; - print C "From $sender # This line is ignored.\n"; - printf C "Subject: %s\n\n", $initial_subject; - printf C <",$compose_filename . ".final") - or die "Failed to open $compose_filename.final : " . $!; - - open(C,"<",$compose_filename) - or die "Failed to open $compose_filename : " . $!; - - my $need_8bit_cte = file_has_nonascii($compose_filename); - my $in_body = 0; - while() { - next if m/^GIT: /; - if (!$in_body && /^\n$/) { - $in_body = 1; - if ($need_8bit_cte) { - print C2 "MIME-Version: 1.0\n", - "Content-Type: text/plain; ", - "charset=utf-8\n", - "Content-Transfer-Encoding: 8bit\n"; - } - } - if (!$in_body && /^MIME-Version:/i) { - $need_8bit_cte = 0; - } - if (!$in_body && /^Subject: ?(.*)/i) { - my $subject = $1; - $_ = "Subject: " . - ($subject =~ /[^[:ascii:]]/ ? - quote_rfc2047($subject) : - $subject) . - "\n"; - } - print C2 $_; - } - close(C); - close(C2); - while (1) { $_ = $term->readline("Send this email? (y|n) "); last if defined $_; @@ -600,9 +641,9 @@ EOT exit(0); } - @files = ($compose_filename . ".final", @files); -} elsif ($annotate) { - do_edit(@files); + if ($compose > 0) { + @files = ($compose_filename . ".final", @files); + } } # Variables we set as part of the loop over files From 7f87aff22c0232a5ce327ea3d2923776936c97f4 Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Sat, 15 Nov 2008 01:14:24 +0100 Subject: [PATCH 05/63] Teach/Fix pull/fetch -q/-v options Implement git-pull --quiet and git-pull --verbose by adding the options to git-pull and fixing verbosity handling in git-fetch. Signed-off-by: Junio C Hamano --- Documentation/merge-options.txt | 8 +++++ builtin-fetch.c | 19 +++++------ builtin-merge.c | 21 ++++++++---- git-pull.sh | 10 ++++-- parse-options.c | 22 ++++++++++++ parse-options.h | 6 ++++ t/t5521-pull-options.sh | 60 +++++++++++++++++++++++++++++++++ 7 files changed, 126 insertions(+), 20 deletions(-) create mode 100755 t/t5521-pull-options.sh diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt index 007909a82f..427cdefd01 100644 --- a/Documentation/merge-options.txt +++ b/Documentation/merge-options.txt @@ -1,3 +1,11 @@ +-q:: +--quiet:: + Operate quietly. + +-v:: +--verbose:: + Be verbose. + --stat:: Show a diffstat at the end of the merge. The diffstat is also controlled by the configuration option merge.stat. diff --git a/builtin-fetch.c b/builtin-fetch.c index f151cfa2fd..7568163af2 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -22,7 +22,7 @@ enum { TAGS_SET = 2 }; -static int append, force, keep, update_head_ok, verbose, quiet; +static int append, force, keep, update_head_ok, verbosity; static int tags = TAGS_DEFAULT; static const char *depth; static const char *upload_pack; @@ -30,8 +30,7 @@ static struct strbuf default_rla = STRBUF_INIT; static struct transport *transport; static struct option builtin_fetch_options[] = { - OPT__QUIET(&quiet), - OPT__VERBOSE(&verbose), + OPT__VERBOSITY(&verbosity), OPT_BOOLEAN('a', "append", &append, "append to .git/FETCH_HEAD instead of overwriting"), OPT_STRING(0, "upload-pack", &upload_pack, "PATH", @@ -192,7 +191,6 @@ static int s_update_ref(const char *action, static int update_local_ref(struct ref *ref, const char *remote, - int verbose, char *display) { struct commit *current = NULL, *updated; @@ -210,7 +208,7 @@ static int update_local_ref(struct ref *ref, die("object %s not found", sha1_to_hex(ref->new_sha1)); if (!hashcmp(ref->old_sha1, ref->new_sha1)) { - if (verbose) + if (verbosity > 0) sprintf(display, "= %-*s %-*s -> %s", SUMMARY_WIDTH, "[up to date]", REFCOL_WIDTH, remote, pretty_ref); @@ -366,18 +364,19 @@ static int store_updated_refs(const char *url, const char *remote_name, note); if (ref) - rc |= update_local_ref(ref, what, verbose, note); + rc |= update_local_ref(ref, what, note); else sprintf(note, "* %-*s %-*s -> FETCH_HEAD", SUMMARY_WIDTH, *kind ? kind : "branch", REFCOL_WIDTH, *what ? what : "HEAD"); if (*note) { - if (!shown_url) { + if (verbosity >= 0 && !shown_url) { fprintf(stderr, "From %.*s\n", url_len, url); shown_url = 1; } - fprintf(stderr, " %s\n", note); + if (verbosity >= 0) + fprintf(stderr, " %s\n", note); } } fclose(fp); @@ -637,9 +636,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) remote = remote_get(argv[0]); transport = transport_get(remote, remote->url[0]); - if (verbose >= 2) + if (verbosity >= 2) transport->verbose = 1; - if (quiet) + if (verbosity < 0) transport->verbose = -1; if (upload_pack) set_option(TRANS_OPT_UPLOADPACK, upload_pack); diff --git a/builtin-merge.c b/builtin-merge.c index 5e7910bd8d..7c2b90c70b 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -50,6 +50,7 @@ static unsigned char head[20], stash[20]; static struct strategy **use_strategies; static size_t use_strategies_nr, use_strategies_alloc; static const char *branch; +static int verbosity; static struct strategy all_strategy[] = { { "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL }, @@ -171,6 +172,7 @@ static struct option builtin_merge_options[] = { OPT_CALLBACK('m', "message", &merge_msg, "message", "message to be used for the merge commit (if any)", option_parse_message), + OPT__VERBOSITY(&verbosity), OPT_END() }; @@ -250,7 +252,8 @@ static void restore_state(void) /* This is called when no merge was necessary. */ static void finish_up_to_date(const char *msg) { - printf("%s%s\n", squash ? " (nothing to squash)" : "", msg); + if (verbosity >= 0) + printf("%s%s\n", squash ? " (nothing to squash)" : "", msg); drop_save(); } @@ -331,14 +334,15 @@ static void finish(const unsigned char *new_head, const char *msg) if (!msg) strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION")); else { - printf("%s\n", msg); + if (verbosity >= 0) + printf("%s\n", msg); strbuf_addf(&reflog_message, "%s: %s", getenv("GIT_REFLOG_ACTION"), msg); } if (squash) { squash_message(); } else { - if (!merge_msg.len) + if (verbosity >= 0 && !merge_msg.len) printf("No merge message -- not updating HEAD\n"); else { const char *argv_gc_auto[] = { "gc", "--auto", NULL }; @@ -872,6 +876,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, builtin_merge_options, builtin_merge_usage, 0); + if (verbosity < 0) + show_diffstat = 0; if (squash) { if (!allow_fast_forward) @@ -1013,10 +1019,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix) strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV)); - printf("Updating %s..%s\n", - hex, - find_unique_abbrev(remoteheads->item->object.sha1, - DEFAULT_ABBREV)); + if (verbosity >= 0) + printf("Updating %s..%s\n", + hex, + find_unique_abbrev(remoteheads->item->object.sha1, + DEFAULT_ABBREV)); strbuf_addstr(&msg, "Fast forward"); if (have_message) strbuf_addstr(&msg, diff --git a/git-pull.sh b/git-pull.sh index 664fe34419..8866f2a1e2 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -16,13 +16,17 @@ cd_to_toplevel test -z "$(git ls-files -u)" || die "You are in the middle of a conflicted merge." -strategy_args= no_stat= no_commit= squash= no_ff= log_arg= +strategy_args= no_stat= no_commit= squash= no_ff= log_arg= verbosity= curr_branch=$(git symbolic-ref -q HEAD) curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||") rebase=$(git config --bool branch.$curr_branch_short.rebase) while : do case "$1" in + -q|--quiet) + verbosity=-q ;; + -v|--verbose) + verbosity=-v ;; -n|--no-stat|--no-summary) no_stat=-n ;; --stat|--summary) @@ -121,7 +125,7 @@ test true = "$rebase" && { "refs/remotes/$origin/$reflist" 2>/dev/null)" } orig_head=$(git rev-parse --verify HEAD 2>/dev/null) -git fetch --update-head-ok "$@" || exit 1 +git fetch $verbosity --update-head-ok "$@" || exit 1 curr_head=$(git rev-parse --verify HEAD 2>/dev/null) if test -n "$orig_head" && test "$curr_head" != "$orig_head" @@ -182,4 +186,4 @@ test true = "$rebase" && exec git-rebase $strategy_args --onto $merge_head \ ${oldremoteref:-$merge_head} exec git-merge $no_stat $no_commit $squash $no_ff $log_arg $strategy_args \ - "$merge_name" HEAD $merge_head + "$merge_name" HEAD $merge_head $verbosity diff --git a/parse-options.c b/parse-options.c index fd08bb425c..9eb55cc8b5 100644 --- a/parse-options.c +++ b/parse-options.c @@ -484,6 +484,28 @@ int parse_opt_approxidate_cb(const struct option *opt, const char *arg, return 0; } +int parse_opt_verbosity_cb(const struct option *opt, const char *arg, + int unset) +{ + int *target = opt->value; + + if (unset) + /* --no-quiet, --no-verbose */ + *target = 0; + else if (opt->short_name == 'v') { + if (*target >= 0) + (*target)++; + else + *target = 1; + } else { + if (*target <= 0) + (*target)--; + else + *target = -1; + } + return 0; +} + /* * This should really be OPTION_FILENAME type as a part of * parse_options that take prefix to do this while parsing. diff --git a/parse-options.h b/parse-options.h index 5199950c00..034162ec69 100644 --- a/parse-options.h +++ b/parse-options.h @@ -150,9 +150,15 @@ extern int parse_options_end(struct parse_opt_ctx_t *ctx); /*----- some often used options -----*/ extern int parse_opt_abbrev_cb(const struct option *, const char *, int); extern int parse_opt_approxidate_cb(const struct option *, const char *, int); +extern int parse_opt_verbosity_cb(const struct option *, const char *, int); #define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose") #define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet") +#define OPT__VERBOSITY(var) \ + { OPTION_CALLBACK, 'v', "verbose", (var), NULL, "be more verbose", \ + PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \ + { OPTION_CALLBACK, 'q', "quiet", (var), NULL, "be more quiet", \ + PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 } #define OPT__DRY_RUN(var) OPT_BOOLEAN('n', "dry-run", (var), "dry run") #define OPT__ABBREV(var) \ { OPTION_CALLBACK, 0, "abbrev", (var), "n", \ diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh new file mode 100755 index 0000000000..83e2e8ab80 --- /dev/null +++ b/t/t5521-pull-options.sh @@ -0,0 +1,60 @@ +#!/bin/sh + +test_description='pull options' + +. ./test-lib.sh + +D=`pwd` + +test_expect_success 'setup' ' + mkdir parent && + (cd parent && git init && + echo one >file && git add file && + git commit -m one) +' + +cd "$D" + +test_expect_success 'git pull -q' ' + mkdir clonedq && + cd clonedq && + git pull -q "$D/parent" >out 2>err && + test ! -s out +' + +cd "$D" + +test_expect_success 'git pull' ' + mkdir cloned && + cd cloned && + git pull "$D/parent" >out 2>err && + test -s out +' +cd "$D" + +test_expect_success 'git pull -v' ' + mkdir clonedv && + cd clonedv && + git pull -v "$D/parent" >out 2>err && + test -s out +' + +cd "$D" + +test_expect_success 'git pull -v -q' ' + mkdir clonedvq && + cd clonedvq && + git pull -v -q "$D/parent" >out 2>err && + test ! -s out +' + +cd "$D" + +test_expect_success 'git pull -q -v' ' + mkdir clonedqv && + cd clonedqv && + git pull -q -v "$D/parent" >out 2>err && + test -s out +' + +test_done From 671c9b7e315db89081cc69f83a8f405e4aca37bc Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 13 Nov 2008 16:36:30 -0800 Subject: [PATCH 06/63] Add cache preload facility This can do the lstat() storm in parallel, giving potentially much improved performance for cold-cache cases or things like NFS that have weak metadata caching. Just use "read_cache_preload()" instead of "read_cache()" to force an optimistic preload of the index stat data. The function takes a pathspec as its argument, allowing us to preload only the relevant portion of the index. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- Documentation/config.txt | 9 ++++ Makefile | 1 + builtin-commit.c | 8 ++-- builtin-diff-files.c | 4 +- builtin-diff.c | 8 ++-- cache.h | 3 ++ config.c | 5 +++ environment.c | 3 ++ preload-index.c | 91 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 122 insertions(+), 10 deletions(-) create mode 100644 preload-index.c diff --git a/Documentation/config.txt b/Documentation/config.txt index 32dcd643d2..0d96e763ac 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -413,6 +413,15 @@ data writes properly, but can be useful for filesystems that do not use journalling (traditional UNIX filesystems) or that only journal metadata and not file contents (OS X's HFS+, or Linux ext3 with "data=writeback"). +core.preloadindex:: + Enable parallel index preload for operations like 'git diff' ++ +This can speed up operations like 'git diff' and 'git status' especially +on filesystems like NFS that have weak caching semantics and thus +relatively high IO latencies. With this set to 'true', git will do the +index comparison to the filesystem data in parallel, allowing +overlapping IO's. + alias.*:: Command aliases for the linkgit:git[1] command wrapper - e.g. after defining "alias.last = cat-file commit HEAD", the invocation diff --git a/Makefile b/Makefile index 35adafa011..acac0ae5d9 100644 --- a/Makefile +++ b/Makefile @@ -496,6 +496,7 @@ LIB_OBJS += write_or_die.o LIB_OBJS += ws.o LIB_OBJS += wt-status.o LIB_OBJS += xdiff-interface.o +LIB_OBJS += preload-index.o BUILTIN_OBJS += builtin-add.o BUILTIN_OBJS += builtin-annotate.o diff --git a/builtin-commit.c b/builtin-commit.c index 591d16b91e..1677e6b45f 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -225,18 +225,18 @@ static char *prepare_index(int argc, const char **argv, const char *prefix) if (interactive) { interactive_add(argc, argv, prefix); - if (read_cache() < 0) + if (read_cache_preload(NULL) < 0) die("index file corrupt"); commit_style = COMMIT_AS_IS; return get_index_file(); } - if (read_cache() < 0) - die("index file corrupt"); - if (*argv) pathspec = get_pathspec(prefix, argv); + if (read_cache_preload(pathspec) < 0) + die("index file corrupt"); + /* * Non partial, non as-is commit. * diff --git a/builtin-diff-files.c b/builtin-diff-files.c index 2b578c714d..5b64011de8 100644 --- a/builtin-diff-files.c +++ b/builtin-diff-files.c @@ -59,8 +59,8 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) (rev.diffopt.output_format & DIFF_FORMAT_PATCH)) rev.combine_merges = rev.dense_combined_merges = 1; - if (read_cache() < 0) { - perror("read_cache"); + if (read_cache_preload(rev.diffopt.paths) < 0) { + perror("read_cache_preload"); return -1; } result = run_diff_files(&rev, options); diff --git a/builtin-diff.c b/builtin-diff.c index 7ceceeb6ff..1447c1d6ba 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -134,8 +134,8 @@ static int builtin_diff_index(struct rev_info *revs, revs->max_count != -1 || revs->min_age != -1 || revs->max_age != -1) usage(builtin_diff_usage); - if (read_cache() < 0) { - perror("read_cache"); + if (read_cache_preload(revs->diffopt.paths) < 0) { + perror("read_cache_preload"); return -1; } return run_diff_index(revs, cached); @@ -234,8 +234,8 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv revs->combine_merges = revs->dense_combined_merges = 1; setup_work_tree(); - if (read_cache() < 0) { - perror("read_cache"); + if (read_cache_preload(revs->diffopt.paths) < 0) { + perror("read_cache_preload"); return -1; } result = run_diff_files(revs, options); diff --git a/cache.h b/cache.h index 3b5f0c4c00..685a8666b6 100644 --- a/cache.h +++ b/cache.h @@ -262,6 +262,7 @@ static inline void remove_name_hash(struct cache_entry *ce) #define read_cache() read_index(&the_index) #define read_cache_from(path) read_index_from(&the_index, (path)) +#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec)) #define is_cache_unborn() is_index_unborn(&the_index) #define read_cache_unmerged() read_index_unmerged(&the_index) #define write_cache(newfd, cache, entries) write_index(&the_index, (newfd)) @@ -368,6 +369,7 @@ extern int init_db(const char *template_dir, unsigned int flags); /* Initialize and use the cache information */ extern int read_index(struct index_state *); +extern int read_index_preload(struct index_state *, const char **pathspec); extern int read_index_from(struct index_state *, const char *path); extern int is_index_unborn(struct index_state *); extern int read_index_unmerged(struct index_state *); @@ -458,6 +460,7 @@ extern size_t packed_git_limit; extern size_t delta_base_cache_limit; extern int auto_crlf; extern int fsync_object_files; +extern int core_preload_index; enum safe_crlf { SAFE_CRLF_FALSE = 0, diff --git a/config.c b/config.c index 67cc1dcad0..d2fc8f5f22 100644 --- a/config.c +++ b/config.c @@ -490,6 +490,11 @@ static int git_default_core_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "core.preloadindex")) { + core_preload_index = git_config_bool(var, value); + return 0; + } + /* Add other config variables here and to Documentation/config.txt. */ return 0; } diff --git a/environment.c b/environment.c index bb96ac0a71..e278bce0ea 100644 --- a/environment.c +++ b/environment.c @@ -43,6 +43,9 @@ unsigned whitespace_rule_cfg = WS_DEFAULT_RULE; enum branch_track git_branch_track = BRANCH_TRACK_REMOTE; enum rebase_setup_type autorebase = AUTOREBASE_NEVER; +/* Parallel index stat data preload? */ +int core_preload_index = 0; + /* This is set by setup_git_dir_gently() and/or git_default_config() */ char *git_work_tree_cfg; static char *work_tree; diff --git a/preload-index.c b/preload-index.c new file mode 100644 index 0000000000..6253578c96 --- /dev/null +++ b/preload-index.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2008 Linus Torvalds + */ +#include "cache.h" +#include + +/* + * Mostly randomly chosen maximum thread counts: we + * cap the parallelism to 20 threads, and we want + * to have at least 500 lstat's per thread for it to + * be worth starting a thread. + */ +#define MAX_PARALLEL (20) +#define THREAD_COST (500) + +struct thread_data { + pthread_t pthread; + struct index_state *index; + const char **pathspec; + int offset, nr; +}; + +static void *preload_thread(void *_data) +{ + int nr; + struct thread_data *p = _data; + struct index_state *index = p->index; + struct cache_entry **cep = index->cache + p->offset; + + nr = p->nr; + if (nr + p->offset > index->cache_nr) + nr = index->cache_nr - p->offset; + + do { + struct cache_entry *ce = *cep++; + struct stat st; + + if (ce_stage(ce)) + continue; + if (ce_uptodate(ce)) + continue; + if (!ce_path_match(ce, p->pathspec)) + continue; + if (lstat(ce->name, &st)) + continue; + if (ie_match_stat(index, ce, &st, 0)) + continue; + ce_mark_uptodate(ce); + } while (--nr > 0); + return NULL; +} + +static void preload_index(struct index_state *index, const char **pathspec) +{ + int threads, i, work, offset; + struct thread_data data[MAX_PARALLEL]; + + if (!core_preload_index) + return; + + threads = index->cache_nr / THREAD_COST; + if (threads < 2) + return; + if (threads > MAX_PARALLEL) + threads = MAX_PARALLEL; + offset = 0; + work = (index->cache_nr + threads - 1) / threads; + for (i = 0; i < threads; i++) { + struct thread_data *p = data+i; + p->index = index; + p->pathspec = pathspec; + p->offset = offset; + p->nr = work; + offset += work; + if (pthread_create(&p->pthread, NULL, preload_thread, p)) + die("unable to create threaded lstat"); + } + for (i = 0; i < threads; i++) { + struct thread_data *p = data+i; + if (pthread_join(p->pthread, NULL)) + die("unable to join threaded lstat"); + } +} + +int read_index_preload(struct index_state *index, const char **pathspec) +{ + int retval = read_index(index); + + preload_index(index, pathspec); + return retval; +} From 3eb91bfc0d2e0c8c67bd4ec62523deb849919dd9 Mon Sep 17 00:00:00 2001 From: Stefan Naewe Date: Mon, 17 Nov 2008 09:57:19 +0100 Subject: [PATCH 07/63] request-pull: make usage string match manpage The usage string of 'git request-pull' differs from he manpage which gives the correct 'synopsis'. Signed-off-by: Stefan Naewe Signed-off-by: Junio C Hamano --- git-request-pull.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/git-request-pull.sh b/git-request-pull.sh index 073a314c80..a2cf5b8215 100755 --- a/git-request-pull.sh +++ b/git-request-pull.sh @@ -4,9 +4,9 @@ # This file is licensed under the GPL v2, or a later version # at the discretion of Linus Torvalds. -USAGE=' []' -LONG_USAGE='Summarizes the changes since to the standard output, -and includes in the message generated.' +USAGE=' []' +LONG_USAGE='Summarizes the changes between two commits to the standard output, +and includes the given URL in the generated summary.' SUBDIRECTORY_OK='Yes' OPTIONS_SPEC= . git-sh-setup From 357af14fc491748c874cd8791a5bcf5e1a426ee8 Mon Sep 17 00:00:00 2001 From: Cheng Renquan Date: Mon, 17 Nov 2008 19:15:49 +0800 Subject: [PATCH 08/63] git-remote: match usage string with the manual pages Signed-off-by: Cheng Renquan Signed-off-by: Junio C Hamano --- builtin-remote.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/builtin-remote.c b/builtin-remote.c index 71696b50d3..d032f25358 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -8,12 +8,12 @@ #include "refs.h" static const char * const builtin_remote_usage[] = { - "git remote", - "git remote add ", + "git remote [-v | --verbose]", + "git remote add [-t ] [-m ] [-f] [--mirror] ", "git remote rename ", "git remote rm ", - "git remote show ", - "git remote prune ", + "git remote show [-n] ", + "git remote prune [-n | --dry-run] ", "git remote update [group]", NULL }; From 83b767360a183abf29cb0cc0fd647fb683cff616 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 17 Nov 2008 07:54:07 -0800 Subject: [PATCH 09/63] builtin-remote.c: plug a small memory leak in get_one_remote_for_updates() We know that the string pointed at by remote->name won't change. It can be borrowed as the key in the string_list without copying. Other parts of existing code such as get_one_entry() already rely on this fact. Noticed by Cheng Renquan. Signed-off-by: Junio C Hamano --- builtin-remote.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-remote.c b/builtin-remote.c index d032f25358..14774e36c4 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -770,7 +770,7 @@ static int get_one_remote_for_update(struct remote *remote, void *priv) { struct string_list *list = priv; if (!remote->skip_default_update) - string_list_append(xstrdup(remote->name), list); + string_list_append(remote->name, list); return 0; } From b3d9888792f220f4c1e72953874484a0cf0575fe Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 17 Nov 2008 16:42:47 +0100 Subject: [PATCH 10/63] Documentation: user-manual: add information about "git help" at the beginning Talking about "git help" is useful because it has a few more features (like when using it without arguments or with "-a") and it may work on non unix like platforms. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- Documentation/user-manual.txt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 645d752c5c..c0d8caf46d 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -18,12 +18,22 @@ People needing to do actual development will also want to read Further chapters cover more specialized topics. Comprehensive reference documentation is available through the man -pages. For a command such as "git clone ", just use +pages, or linkgit:git-help[1] command. For example, for the command +"git clone ", you can either use: ------------------------------------------------ $ man git-clone ------------------------------------------------ +or: + +------------------------------------------------ +$ git help clone +------------------------------------------------ + +With the latter, you can use the manual viewer of your choice; see +linkgit:git-help[1] for more information. + See also <> for a brief overview of git commands, without any explanation. From 6e702c245803e3cdf2cad3f1f74aa4d894aa165a Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 17 Nov 2008 16:43:04 +0100 Subject: [PATCH 11/63] Documentation: tutorial: add information about "git help" at the beginning Talking about "git help" is useful because it has a few more features (like when using it without arguments or with "-a") and it may work on non unix like platforms. Also add a few links to git-help(1) in "See also" sections. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- Documentation/gitcore-tutorial.txt | 1 + Documentation/gittutorial-2.txt | 1 + Documentation/gittutorial.txt | 10 ++++++++++ 3 files changed, 12 insertions(+) diff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt index 61fc5d7be8..96bf353d13 100644 --- a/Documentation/gitcore-tutorial.txt +++ b/Documentation/gitcore-tutorial.txt @@ -1693,6 +1693,7 @@ SEE ALSO linkgit:gittutorial[7], linkgit:gittutorial-2[7], linkgit:gitcvs-migration[7], +linkgit:git-help[1], link:everyday.html[Everyday git], link:user-manual.html[The Git User's Manual] diff --git a/Documentation/gittutorial-2.txt b/Documentation/gittutorial-2.txt index bab0f34b45..a057b50b2b 100644 --- a/Documentation/gittutorial-2.txt +++ b/Documentation/gittutorial-2.txt @@ -425,6 +425,7 @@ linkgit:gittutorial[7], linkgit:gitcvs-migration[7], linkgit:gitcore-tutorial[7], linkgit:gitglossary[7], +linkgit:git-help[1], link:everyday.html[Everyday git], link:user-manual.html[The Git User's Manual] diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt index 384972cb9b..7892244ef1 100644 --- a/Documentation/gittutorial.txt +++ b/Documentation/gittutorial.txt @@ -26,6 +26,15 @@ First, note that you can get documentation for a command such as $ man git-log ------------------------------------------------ +or: + +------------------------------------------------ +$ git help log +------------------------------------------------ + +With the latter, you can use the manual viewer of your choice; see +linkgit:git-help[1] for more information. + It is a good idea to introduce yourself to git with your name and public email address before doing any operation. The easiest way to do so is: @@ -653,6 +662,7 @@ linkgit:gittutorial-2[7], linkgit:gitcvs-migration[7], linkgit:gitcore-tutorial[7], linkgit:gitglossary[7], +linkgit:git-help[1], link:everyday.html[Everyday git], link:user-manual.html[The Git User's Manual] From 7c4ea599b0d44e46c4f96bf955b62d96126b53ff Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 17 Nov 2008 09:01:20 -0800 Subject: [PATCH 12/63] Fix index preloading for racy dirty case In the threaded index preloading case, we must be sure to always use the CE_MATCH_RACY_IS_DIRTY flag when calling ie_match_stat(), in order to make sure that we only ever look at the stat() data, and don't try to do anything fancy. Because most of git internals are not thread-safe, and must not be called in parallel. Otherwise, what happens is that if the timestamps indicate that an entry _might_ be dirty, we might start actually comparing filesystem data with the object database. And we mustn't do that, because that would involve looking up and creating the object structure, and that whole code sequence with read_sha1_file() where we look up and add objects to the hashes is definitely not thread-safe. Nor do we want to add locking, because the whole point of the preload was to be simple and not affect anything else. With CE_MATCH_RACY_IS_DIRTY, we get what we wanted, and we'll just leave the hard cases well alone, to be done later in the much simpler serial case. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- preload-index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/preload-index.c b/preload-index.c index 6253578c96..a6855837a4 100644 --- a/preload-index.c +++ b/preload-index.c @@ -43,7 +43,7 @@ static void *preload_thread(void *_data) continue; if (lstat(ce->name, &st)) continue; - if (ie_match_stat(index, ce, &st, 0)) + if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY)) continue; ce_mark_uptodate(ce); } while (--nr > 0); From dbbd56f1031992593f129168006b6c2f8bae13ec Mon Sep 17 00:00:00 2001 From: Cheng Renquan Date: Tue, 18 Nov 2008 19:04:02 +0800 Subject: [PATCH 13/63] git-remote: add verbose mode to git remote update Pass the verbose mode parameter to the underlying fetch command. $ ./git remote -v update Updating origin From git://git.kernel.org/pub/scm/git/git = [up to date] html -> origin/html = [up to date] maint -> origin/maint = [up to date] man -> origin/man = [up to date] master -> origin/master = [up to date] next -> origin/next = [up to date] pu -> origin/pu = [up to date] todo -> origin/todo Signed-off-by: Cheng Renquan Signed-off-by: Junio C Hamano --- builtin-remote.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/builtin-remote.c b/builtin-remote.c index 14774e36c4..abc8dd8389 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -14,7 +14,7 @@ static const char * const builtin_remote_usage[] = { "git remote rm ", "git remote show [-n] ", "git remote prune [-n | --dry-run] ", - "git remote update [group]", + "git remote [-v | --verbose] update [group]", NULL }; @@ -42,7 +42,11 @@ static int opt_parse_track(const struct option *opt, const char *arg, int not) static int fetch_remote(const char *name) { - const char *argv[] = { "fetch", name, NULL }; + const char *argv[] = { "fetch", name, NULL, NULL }; + if (verbose) { + argv[1] = "-v"; + argv[2] = name; + } printf("Updating %s\n", name); if (run_command_v_opt(argv, RUN_GIT_CMD)) return error("Could not fetch %s", name); From 3c59c50d76cd479caf14cab73fdb09b68597d5e5 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Mon, 17 Nov 2008 21:48:35 +0100 Subject: [PATCH 14/63] builtin-branch: use strbuf in delete_branches() In case the length of branch name is greather then PATH_MAX-7, we write to unallocated memory otherwise. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- builtin-branch.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index 2b3613fea2..b9149b78f4 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -97,7 +97,6 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) unsigned char sha1[20]; char *name = NULL; const char *fmt, *remote; - char section[PATH_MAX]; int i; int ret = 0; @@ -165,11 +164,12 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) argv[i]); ret = 1; } else { + struct strbuf buf = STRBUF_INIT; printf("Deleted %sbranch %s.\n", remote, argv[i]); - snprintf(section, sizeof(section), "branch.%s", - argv[i]); - if (git_config_rename_section(section, NULL) < 0) + strbuf_addf(&buf, "branch.%s", argv[i]); + if (git_config_rename_section(buf.buf, NULL) < 0) warning("Update of config-file failed"); + strbuf_release(&buf); } } From d3f9f9a92eca0dd6379d836a083e823cc5e3eeb6 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Mon, 17 Nov 2008 21:48:36 +0100 Subject: [PATCH 15/63] builtin-branch: use strbuf in fill_tracking_info() This is just about using the API, though in case of ~ 10^100 commits, this would fix the problem of writing to unallocated memory as well. ;-) Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- builtin-branch.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index b9149b78f4..c8a8e2a2f4 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -279,7 +279,7 @@ static int ref_cmp(const void *r1, const void *r2) return strcmp(c1->name, c2->name); } -static void fill_tracking_info(char *stat, const char *branch_name) +static void fill_tracking_info(struct strbuf *stat, const char *branch_name) { int ours, theirs; struct branch *branch = branch_get(branch_name); @@ -287,11 +287,11 @@ static void fill_tracking_info(char *stat, const char *branch_name) if (!stat_tracking_info(branch, &ours, &theirs) || (!ours && !theirs)) return; if (!ours) - sprintf(stat, "[behind %d] ", theirs); + strbuf_addf(stat, "[behind %d] ", theirs); else if (!theirs) - sprintf(stat, "[ahead %d] ", ours); + strbuf_addf(stat, "[ahead %d] ", ours); else - sprintf(stat, "[ahead %d, behind %d] ", ours, theirs); + strbuf_addf(stat, "[ahead %d, behind %d] ", ours, theirs); } static int matches_merge_filter(struct commit *commit) @@ -334,11 +334,8 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, } if (verbose) { - struct strbuf subject = STRBUF_INIT; + struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT; const char *sub = " **** invalid ref ****"; - char stat[128]; - - stat[0] = '\0'; commit = item->commit; if (commit && !parse_commit(commit)) { @@ -348,13 +345,14 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, } if (item->kind == REF_LOCAL_BRANCH) - fill_tracking_info(stat, item->name); + fill_tracking_info(&stat, item->name); printf("%c %s%-*s%s %s %s%s\n", c, branch_get_color(color), maxwidth, item->name, branch_get_color(COLOR_BRANCH_RESET), find_unique_abbrev(item->commit->object.sha1, abbrev), - stat, sub); + stat.buf, sub); + strbuf_release(&stat); strbuf_release(&subject); } else { printf("%c %s%s%s\n", c, branch_get_color(color), item->name, From da3f78a29cafee487c107a81c442213fa41463f7 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Mon, 17 Nov 2008 21:48:37 +0100 Subject: [PATCH 16/63] builtin-branch: use strbuf in rename_branch() In case the length of branch name is greather then PATH_MAX-11, we write to unallocated memory otherwise. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- builtin-branch.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index c8a8e2a2f4..494cbac005 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -424,42 +424,45 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str static void rename_branch(const char *oldname, const char *newname, int force) { - char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100]; + struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT; unsigned char sha1[20]; - char oldsection[PATH_MAX], newsection[PATH_MAX]; + struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT; if (!oldname) die("cannot rename the current branch while not on any."); - if (snprintf(oldref, sizeof(oldref), "refs/heads/%s", oldname) > sizeof(oldref)) - die("Old branchname too long"); + strbuf_addf(&oldref, "refs/heads/%s", oldname); - if (check_ref_format(oldref)) - die("Invalid branch name: %s", oldref); + if (check_ref_format(oldref.buf)) + die("Invalid branch name: %s", oldref.buf); - if (snprintf(newref, sizeof(newref), "refs/heads/%s", newname) > sizeof(newref)) - die("New branchname too long"); + strbuf_addf(&newref, "refs/heads/%s", newname); - if (check_ref_format(newref)) - die("Invalid branch name: %s", newref); + if (check_ref_format(newref.buf)) + die("Invalid branch name: %s", newref.buf); - if (resolve_ref(newref, sha1, 1, NULL) && !force) + if (resolve_ref(newref.buf, sha1, 1, NULL) && !force) die("A branch named '%s' already exists.", newname); - snprintf(logmsg, sizeof(logmsg), "Branch: renamed %s to %s", - oldref, newref); + strbuf_addf(&logmsg, "Branch: renamed %s to %s", + oldref.buf, newref.buf); - if (rename_ref(oldref, newref, logmsg)) + if (rename_ref(oldref.buf, newref.buf, logmsg.buf)) die("Branch rename failed"); + strbuf_release(&logmsg); /* no need to pass logmsg here as HEAD didn't really move */ - if (!strcmp(oldname, head) && create_symref("HEAD", newref, NULL)) + if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL)) die("Branch renamed to %s, but HEAD is not updated!", newname); - snprintf(oldsection, sizeof(oldsection), "branch.%s", oldref + 11); - snprintf(newsection, sizeof(newsection), "branch.%s", newref + 11); - if (git_config_rename_section(oldsection, newsection) < 0) + strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11); + strbuf_release(&oldref); + strbuf_addf(&newsection, "branch.%s", newref.buf + 11); + strbuf_release(&newref); + if (git_config_rename_section(oldsection.buf, newsection.buf) < 0) die("Branch is renamed, but update of config-file failed"); + strbuf_release(&oldsection); + strbuf_release(&newsection); } static int opt_parse_with_commit(const struct option *opt, const char *arg, int unset) From c2e6385da53bd3a517332ba2c8d2bd09bdb05396 Mon Sep 17 00:00:00 2001 From: Mark Burton Date: Mon, 17 Nov 2008 21:03:59 +0000 Subject: [PATCH 17/63] Documentation: rev-list-options.txt: added --branches, --tags & --remotes. Added simple descriptions of these options (based on description of --all). Signed-off-by: Mark Burton Signed-off-by: Junio C Hamano --- Documentation/rev-list-options.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 6d7cf6d51f..b9f6e4d1b7 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -222,6 +222,21 @@ endif::git-rev-list[] Pretend as if all the refs in `$GIT_DIR/refs/` are listed on the command line as ''. +--branches:: + + Pretend as if all the refs in `$GIT_DIR/refs/heads` are listed + on the command line as ''. + +--tags:: + + Pretend as if all the refs in `$GIT_DIR/refs/tags` are listed + on the command line as ''. + +--remotes:: + + Pretend as if all the refs in `$GIT_DIR/refs/remotes` are listed + on the command line as ''. + ifdef::git-rev-list[] --stdin:: From c6576f912fc34193a02d7ec587484f7c2ce3fe77 Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Mon, 17 Nov 2008 23:09:30 +0100 Subject: [PATCH 18/63] Retain multiple -q/-v occurrences in git pull To support counting -q/-v options in git pull retain them by concatenating. Signed-off-by: Tuncer Ayaz Signed-off-by: Junio C Hamano --- git-pull.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-pull.sh b/git-pull.sh index 8866f2a1e2..1cac898a24 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -24,9 +24,9 @@ while : do case "$1" in -q|--quiet) - verbosity=-q ;; + verbosity="$verbosity -q" ;; -v|--verbose) - verbosity=-v ;; + verbosity="$verbosity -v" ;; -n|--no-stat|--no-summary) no_stat=-n ;; --stat|--summary) From 6fc4a7e546d5e2b0ce545f73b5c1829887db2462 Mon Sep 17 00:00:00 2001 From: Mark Burton Date: Tue, 18 Nov 2008 22:33:44 +0000 Subject: [PATCH 19/63] git-commit.txt - mention that files listed on the command line must be known to git. Signed-off-by: Mark Burton Signed-off-by: Junio C Hamano --- Documentation/git-commit.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 2e62165fa9..a1ce9a8bf7 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -29,7 +29,8 @@ The content to be added can be specified in several ways: 3. by listing files as arguments to the 'commit' command, in which case the commit will ignore changes staged in the index, and instead - record the current content of the listed files; + record the current content of the listed files (which must already + be known to git); 4. by using the -a switch with the 'commit' command to automatically "add" changes from all known files (i.e. all files that are already From 36d2078ff19c849aef94f045bf2b4fb73a5098e8 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 1 Nov 2008 20:34:33 +0100 Subject: [PATCH 20/63] git.el: Improve error handling for commits. Display all errors happening in the various subcommands of the commit sequence, and abort on any error. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 46 ++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index c1cf1cbcc0..d357b543e2 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -208,6 +208,15 @@ and returns the process output as a string, or nil if the git failed." (and (eq 0 (apply #' git-call-process-env t env args)) (buffer-string)))) +(defun git-call-process-string-display-error (&rest args) + "Wrapper for call-process that displays error message and returns +the process output as a string, or nil if the git command failed." + (with-temp-buffer + (if (eq 0 (apply #'git-call-process-env (list t t) nil args)) + (buffer-string) + (display-message-or-buffer (current-buffer)) + nil))) + (defun git-run-process-region (buffer start end program args) "Run a git process with a buffer region as input." (let ((output-buffer (current-buffer)) @@ -391,14 +400,16 @@ and returns the process output as a string, or nil if the git failed." (defun git-read-tree (tree &optional index-file) "Read a tree into the index file." - (apply #'git-call-process-env nil - (if index-file `(("GIT_INDEX_FILE" . ,index-file)) nil) - "read-tree" (if tree (list tree)))) + (let ((process-environment + (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) process-environment))) + (apply 'git-call-process-display-error "read-tree" (if tree (list tree))))) (defun git-write-tree (&optional index-file) "Call git-write-tree and return the resulting tree SHA1 as a string." - (git-get-string-sha1 - (git-call-process-env-string (and index-file `(("GIT_INDEX_FILE" . ,index-file))) "write-tree"))) + (let ((process-environment + (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) process-environment))) + (git-get-string-sha1 + (git-call-process-string-display-error "write-tree")))) (defun git-commit-tree (buffer tree head) "Call git-commit-tree with buffer as input and return the resulting commit SHA1." @@ -824,19 +835,18 @@ Return the list of files that haven't been handled." (defun git-update-index (index-file files) "Run git-update-index on a list of files." - (let ((env (and index-file `(("GIT_INDEX_FILE" . ,index-file)))) + (let ((process-environment (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) + process-environment)) added deleted modified) (dolist (info files) (case (git-fileinfo->state info) ('added (push info added)) ('deleted (push info deleted)) ('modified (push info modified)))) - (when added - (apply #'git-call-process-env nil env "update-index" "--add" "--" (git-get-filenames added))) - (when deleted - (apply #'git-call-process-env nil env "update-index" "--remove" "--" (git-get-filenames deleted))) - (when modified - (apply #'git-call-process-env nil env "update-index" "--" (git-get-filenames modified))))) + (and + (or (not added) (apply #'git-call-process-display-error "update-index" "--add" "--" (git-get-filenames added))) + (or (not deleted) (apply #'git-call-process-display-error "update-index" "--remove" "--" (git-get-filenames deleted))) + (or (not modified) (apply #'git-call-process-display-error "update-index" "--" (git-get-filenames modified)))))) (defun git-run-pre-commit-hook () "Run the pre-commit hook if any." @@ -862,17 +872,19 @@ Return the list of files that haven't been handled." (message "You cannot commit unmerged files, resolve them first.") (unwind-protect (let ((files (git-marked-files-state 'added 'deleted 'modified)) - head head-tree) + head tree head-tree) (unless (git-empty-db-p) (setq head (git-rev-parse "HEAD") head-tree (git-rev-parse "HEAD^{tree}"))) (if files (progn (message "Running git commit...") - (git-read-tree head-tree index-file) - (git-update-index nil files) ;update both the default index - (git-update-index index-file files) ;and the temporary one - (let ((tree (git-write-tree index-file))) + (when + (and + (git-read-tree head-tree index-file) + (git-update-index nil files) ;update both the default index + (git-update-index index-file files) ;and the temporary one + (setq tree (git-write-tree index-file))) (if (or (not (string-equal tree head-tree)) (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? ")) (let ((commit (git-commit-tree buffer tree head))) From 9ddf6d7c105e58d76ea6762d8cc8eebdf463903e Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 1 Nov 2008 20:42:39 +0100 Subject: [PATCH 21/63] git.el: Remove the env parameter in git-call-process and git-call-process-string. All callers that need to change the environment now set process-environment themselves. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 54 +++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index d357b543e2..d28b45e558 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -183,11 +183,9 @@ if there is already one that displays the same directory." "Build a list of NAME=VALUE strings from a list of environment strings." (mapcar (lambda (entry) (concat (car entry) "=" (cdr entry))) env)) -(defun git-call-process-env (buffer env &rest args) +(defun git-call-process (buffer &rest args) "Wrapper for call-process that sets environment strings." - (let ((process-environment (append (git-get-env-strings env) - process-environment))) - (apply #'call-process "git" nil buffer nil args))) + (apply #'call-process "git" nil buffer nil args)) (defun git-call-process-display-error (&rest args) "Wrapper for call-process that displays error messages." @@ -197,22 +195,22 @@ if there is already one that displays the same directory." (let ((default-directory dir) (buffer-read-only nil)) (erase-buffer) - (eq 0 (apply 'call-process "git" nil (list buffer t) nil args)))))) + (eq 0 (apply #'git-call-process (list buffer t) args)))))) (unless ok (display-message-or-buffer buffer)) ok)) -(defun git-call-process-env-string (env &rest args) - "Wrapper for call-process that sets environment strings, -and returns the process output as a string, or nil if the git failed." +(defun git-call-process-string (&rest args) + "Wrapper for call-process that returns the process output as a string, +or nil if the git command failed." (with-temp-buffer - (and (eq 0 (apply #' git-call-process-env t env args)) + (and (eq 0 (apply #'git-call-process t args)) (buffer-string)))) (defun git-call-process-string-display-error (&rest args) "Wrapper for call-process that displays error message and returns the process output as a string, or nil if the git command failed." (with-temp-buffer - (if (eq 0 (apply #'git-call-process-env (list t t) nil args)) + (if (eq 0 (apply #'git-call-process (list t t) args)) (buffer-string) (display-message-or-buffer (current-buffer)) nil))) @@ -235,7 +233,7 @@ the process output as a string, or nil if the git command failed." (let ((default-directory dir) (buffer-read-only nil)) (erase-buffer) - (apply #'git-call-process-env buffer nil args))) + (apply #'git-call-process buffer args))) (message "Running git %s...done" (car args)) buffer)) @@ -336,7 +334,7 @@ the process output as a string, or nil if the git command failed." (let ((cdup (with-output-to-string (with-current-buffer standard-output (cd dir) - (unless (eq 0 (call-process "git" nil t nil "rev-parse" "--show-cdup")) + (unless (eq 0 (git-call-process t "rev-parse" "--show-cdup")) (error "cannot find top-level git tree for %s." dir)))))) (expand-file-name (concat (file-name-as-directory dir) (car (split-string cdup "\n")))))) @@ -357,7 +355,7 @@ the process output as a string, or nil if the git command failed." (sort-lines nil (point-min) (point-max)) (save-buffer)) (when created - (git-call-process-env nil nil "update-index" "--add" "--" (file-relative-name ignore-name))) + (git-call-process nil "update-index" "--add" "--" (file-relative-name ignore-name))) (git-update-status-files (list (file-relative-name ignore-name)) 'unknown))) ; propertize definition for XEmacs, stolen from erc-compat @@ -376,16 +374,16 @@ the process output as a string, or nil if the git command failed." (defun git-rev-parse (rev) "Parse a revision name and return its SHA1." (git-get-string-sha1 - (git-call-process-env-string nil "rev-parse" rev))) + (git-call-process-string "rev-parse" rev))) (defun git-config (key) "Retrieve the value associated to KEY in the git repository config file." - (let ((str (git-call-process-env-string nil "config" key))) + (let ((str (git-call-process-string "config" key))) (and str (car (split-string str "\n"))))) (defun git-symbolic-ref (ref) "Wrapper for the git-symbolic-ref command." - (let ((str (git-call-process-env-string nil "symbolic-ref" ref))) + (let ((str (git-call-process-string "symbolic-ref" ref))) (and str (car (split-string str "\n"))))) (defun git-update-ref (ref newval &optional oldval reason) @@ -463,7 +461,7 @@ the process output as a string, or nil if the git command failed." (defun git-empty-db-p () "Check if the git db is empty (no commit done yet)." - (not (eq 0 (call-process "git" nil nil nil "rev-parse" "--verify" "HEAD")))) + (not (eq 0 (git-call-process nil "rev-parse" "--verify" "HEAD")))) (defun git-get-merge-heads () "Retrieve the merge heads from the MERGE_HEAD file if present." @@ -479,7 +477,7 @@ the process output as a string, or nil if the git command failed." (defun git-get-commit-description (commit) "Get a one-line description of COMMIT." (let ((coding-system-for-read (git-get-logoutput-coding-system))) - (let ((descr (git-call-process-env-string nil "log" "--max-count=1" "--pretty=oneline" commit))) + (let ((descr (git-call-process-string "log" "--max-count=1" "--pretty=oneline" commit))) (if (and descr (string-match "\\`\\([0-9a-f]\\{40\\}\\) *\\(.*\\)$" descr)) (concat (substring (match-string 1 descr) 0 10) " - " (match-string 2 descr)) descr)))) @@ -655,7 +653,7 @@ Return the list of files that haven't been handled." (let ((remaining (copy-sequence files)) infolist) (with-temp-buffer - (apply #'git-call-process-env t nil "diff-index" "-z" "-M" "HEAD" "--" files) + (apply #'git-call-process t "diff-index" "-z" "-M" "HEAD" "--" files) (goto-char (point-min)) (while (re-search-forward ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMUT]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0" @@ -688,7 +686,7 @@ Return the list of files that haven't been handled." Return the list of files that haven't been handled." (let (infolist) (with-temp-buffer - (apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files)) + (apply #'git-call-process t "ls-files" "-z" (append options (list "--") files)) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*?\\)\\(/?\\)\0" nil t 1) (let ((name (match-string 1))) @@ -705,7 +703,7 @@ Return the list of files that haven't been handled." (let ((remaining (copy-sequence files)) infolist) (with-temp-buffer - (apply #'git-call-process-env t nil "ls-files" "-z" "-s" "-c" "--" files) + (apply #'git-call-process t "ls-files" "-z" "-s" "-c" "--" files) (goto-char (point-min)) (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t) (let* ((new-perm (string-to-number (match-string 1) 8)) @@ -719,7 +717,7 @@ Return the list of files that haven't been handled." (defun git-run-ls-unmerged (status files) "Run git-ls-files -u on FILES and parse the results into STATUS." (with-temp-buffer - (apply #'git-call-process-env t nil "ls-files" "-z" "-u" "--" files) + (apply #'git-call-process t "ls-files" "-z" "-u" "--" files) (goto-char (point-min)) (let (unmerged-files) (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t) @@ -893,8 +891,8 @@ Return the list of files that haven't been handled." (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) (with-current-buffer buffer (erase-buffer)) (git-update-status-files (git-get-filenames files) 'uptodate) - (git-call-process-env nil nil "rerere") - (git-call-process-env nil nil "gc" "--auto") + (git-call-process nil "rerere") + (git-call-process nil "gc" "--auto") (git-refresh-files) (git-refresh-ewoc-hf git-status) (message "Committed %s." commit) @@ -1311,7 +1309,7 @@ Return the list of files that haven't been handled." (let (author-name author-email subject date msg) (with-temp-buffer (let ((coding-system (git-get-logoutput-coding-system))) - (git-call-process-env t nil "log" "-1" "--pretty=medium" commit) + (git-call-process t "log" "-1" "--pretty=medium" commit) (goto-char (point-min)) (when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t) (setq author-name (match-string 1)) @@ -1331,7 +1329,7 @@ Return the list of files that haven't been handled." "Retrieve the list of files modified by COMMIT." (let (files) (with-temp-buffer - (git-call-process-env t nil "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit) + (git-call-process t "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) (push (match-string 1) files))) @@ -1395,7 +1393,7 @@ amended version of it." (cur-name (and pos (git-fileinfo->name (ewoc-data pos))))) (unless status (error "Not in git-status buffer.")) (message "Refreshing git status...") - (git-call-process-env nil nil "update-index" "--refresh") + (git-call-process nil "update-index" "--refresh") (git-clear-status status) (git-update-status-files nil) ; restore file marks @@ -1588,7 +1586,7 @@ Meant to be used in `after-save-hook'." (let ((filename (file-relative-name file dir))) ; skip files located inside the .git directory (unless (string-match "^\\.git/" filename) - (git-call-process-env nil nil "add" "--refresh" "--" filename) + (git-call-process nil "add" "--refresh" "--" filename) (git-update-status-files (list filename) 'uptodate))))))) (defun git-help () From 6fb204266cc985284b554e9f9f1894ec8360b2f5 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 2 Aug 2008 18:04:31 +0200 Subject: [PATCH 22/63] git.el: Simplify handling of merge heads in the commit log-edit buffer. Use a single Merge: header instead of one Parent: header for each parent, and don't list the current HEAD as a merged head. Support symbolic references too. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index d28b45e558..53ada39b43 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -173,7 +173,7 @@ if there is already one that displays the same directory." (defconst git-log-msg-separator "--- log message follows this line ---") (defvar git-log-edit-font-lock-keywords - `(("^\\(Author:\\|Date:\\|Parent:\\|Signed-off-by:\\)\\(.*\\)$" + `(("^\\(Author:\\|Date:\\|Merge:\\|Signed-off-by:\\)\\(.*\\)$" (1 font-lock-keyword-face) (2 font-lock-function-name-face)) (,(concat "^\\(" (regexp-quote git-log-msg-separator) "\\)$") @@ -433,11 +433,11 @@ the process output as a string, or nil if the git command failed." (when (re-search-forward "^Date: +\\(.*\\)$" nil t) (setq author-date (match-string 1))) (goto-char (point-min)) - (while (re-search-forward "^Parent: +\\([0-9a-f]+\\)" nil t) - (unless (string-equal head (match-string 1)) - (setq subject "commit (merge): ") + (when (re-search-forward "^Merge: +\\(.*\\)" nil t) + (setq subject "commit (merge): ") + (dolist (parent (split-string (match-string 1) " +" t)) (push "-p" args) - (push (match-string 1) args)))) + (push parent args)))) (setq log-start (point-min))) (setq log-end (point-max)) (goto-char log-start) @@ -1253,9 +1253,8 @@ Return the list of files that haven't been handled." (or author-email committer-email) (if date (format "Date: %s\n" date) "") (if merge-heads - (format "Parent: %s\n%s\n" - (git-rev-parse "HEAD") - (mapconcat (lambda (str) (concat "Parent: " str)) merge-heads "\n")) + (format "Merge: %s\n" + (mapconcat 'identity merge-heads " ")) "")) 'face 'git-header-face) (propertize git-log-msg-separator 'face 'git-separator-face) From ef5133df7c9d0304ce2fc437abc74c220f49dabd Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 2 Aug 2008 18:05:58 +0200 Subject: [PATCH 23/63] git.el: Properly handle merge commits in git-amend-commit. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 53ada39b43..207ff0ba27 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1235,11 +1235,10 @@ Return the list of files that haven't been handled." (goto-char (point-max)) (insert sign-off "\n")))) -(defun git-setup-log-buffer (buffer &optional author-name author-email subject date msg) +(defun git-setup-log-buffer (buffer &optional merge-heads author-name author-email subject date msg) "Setup the log buffer for a commit." (unless git-status (error "Not in git-status buffer.")) - (let ((merge-heads (git-get-merge-heads)) - (dir default-directory) + (let ((dir default-directory) (committer-name (git-get-committer-name)) (committer-email (git-get-committer-email)) (sign-off git-append-signed-off-by)) @@ -1294,7 +1293,7 @@ Return the list of files that haven't been handled." (goto-char (point-min)) (when (re-search-forward "^Date: \\(.*\\)$" nil t) (setq date (match-string 1))))) - (git-setup-log-buffer buffer author-name author-email subject date)) + (git-setup-log-buffer buffer (git-get-merge-heads) author-name author-email subject date)) (if (boundp 'log-edit-diff-function) (log-edit 'git-do-commit nil '((log-edit-listfun . git-log-edit-files) (log-edit-diff-function . git-log-edit-diff)) buffer) @@ -1305,11 +1304,13 @@ Return the list of files that haven't been handled." (defun git-setup-commit-buffer (commit) "Setup the commit buffer with the contents of COMMIT." - (let (author-name author-email subject date msg) + (let (parents author-name author-email subject date msg) (with-temp-buffer (let ((coding-system (git-get-logoutput-coding-system))) - (git-call-process t "log" "-1" "--pretty=medium" commit) + (git-call-process t "log" "-1" "--pretty=medium" "--abbrev=40" commit) (goto-char (point-min)) + (when (re-search-forward "^Merge: *\\(.*\\)$" nil t) + (setq parents (cdr (split-string (match-string 1) " +")))) (when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t) (setq author-name (match-string 1)) (setq author-email (match-string 2))) @@ -1321,14 +1322,14 @@ Return the list of files that haven't been handled." (setq subject (pop msg)) (while (and msg (zerop (length (car msg))) (pop msg))))) (git-setup-log-buffer (get-buffer-create "*git-commit*") - author-name author-email subject date + parents author-name author-email subject date (mapconcat #'identity msg "\n")))) (defun git-get-commit-files (commit) "Retrieve the list of files modified by COMMIT." (let (files) (with-temp-buffer - (git-call-process t "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit) + (git-call-process t "diff-tree" "-m" "-r" "-z" "--name-only" "--no-commit-id" commit) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) (push (match-string 1) files))) From db18a182a28f254fa514179dcd49ceb3d95cd9d8 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 2 Aug 2008 20:35:20 +0200 Subject: [PATCH 24/63] git.el: Fix git-amend-commit to support amending an initial commit. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 207ff0ba27..2dd97eeb54 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -389,11 +389,12 @@ the process output as a string, or nil if the git command failed." (defun git-update-ref (ref newval &optional oldval reason) "Update a reference by calling git-update-ref." (let ((args (and oldval (list oldval)))) - (push newval args) + (when newval (push newval args)) (push ref args) (when reason (push reason args) (push "-m" args)) + (unless newval (push "-d" args)) (apply 'git-call-process-display-error "update-ref" args))) (defun git-read-tree (tree &optional index-file) @@ -1329,7 +1330,7 @@ Return the list of files that haven't been handled." "Retrieve the list of files modified by COMMIT." (let (files) (with-temp-buffer - (git-call-process t "diff-tree" "-m" "-r" "-z" "--name-only" "--no-commit-id" commit) + (git-call-process t "diff-tree" "-m" "-r" "-z" "--name-only" "--no-commit-id" "--root" commit) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) (push (match-string 1) files))) @@ -1343,7 +1344,10 @@ amended version of it." (when (git-empty-db-p) (error "No commit to amend.")) (let* ((commit (git-rev-parse "HEAD")) (files (git-get-commit-files commit))) - (when (git-call-process-display-error "reset" "--soft" "HEAD^") + (when (if (git-rev-parse "HEAD^") + (git-call-process-display-error "reset" "--soft" "HEAD^") + (and (git-update-ref "ORIG_HEAD" commit) + (git-update-ref "HEAD" nil commit))) (git-update-status-files (copy-sequence files) 'uptodate) (git-mark-files git-status files) (git-refresh-files) From 433ee03f9748a8a5d0e0495bbf4b054ae922103b Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 2 Aug 2008 20:35:52 +0200 Subject: [PATCH 25/63] git.el: Never clear the status buffer, only update the files. This makes it unnecessary to save/restore the file marks. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 169 ++++++++++++++++++++++--------------------- 1 file changed, 85 insertions(+), 84 deletions(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 2dd97eeb54..67c5275992 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -356,7 +356,7 @@ the process output as a string, or nil if the git command failed." (save-buffer)) (when created (git-call-process nil "update-index" "--add" "--" (file-relative-name ignore-name))) - (git-update-status-files (list (file-relative-name ignore-name)) 'unknown))) + (git-update-status-files (list (file-relative-name ignore-name))))) ; propertize definition for XEmacs, stolen from erc-compat (eval-when-compile @@ -497,14 +497,11 @@ the process output as a string, or nil if the git command failed." old-perm new-perm ;; permission flags rename-state ;; rename or copy state orig-name ;; original name for renames or copies + needs-update ;; whether file needs to be updated needs-refresh) ;; whether file needs to be refreshed (defvar git-status nil) -(defun git-clear-status (status) - "Remove everything from the status list." - (ewoc-filter status (lambda (info) nil))) - (defun git-set-fileinfo-state (info state) "Set the state of a file info." (unless (eq (git-fileinfo->state info) state) @@ -512,6 +509,7 @@ the process output as a string, or nil if the git command failed." (git-fileinfo->new-perm info) (git-fileinfo->old-perm info) (git-fileinfo->rename-state info) nil (git-fileinfo->orig-name info) nil + (git-fileinfo->needs-update info) nil (git-fileinfo->needs-refresh info) t))) (defun git-status-filenames-map (status func files &rest args) @@ -521,10 +519,11 @@ the process output as a string, or nil if the git command failed." (let ((file (pop files)) (node (ewoc-nth status 0))) (while (and file node) - (let ((info (ewoc-data node))) - (if (string-lessp (git-fileinfo->name info) file) + (let* ((info (ewoc-data node)) + (name (git-fileinfo->name info))) + (if (string-lessp name file) (setq node (ewoc-next status node)) - (if (string-equal (git-fileinfo->name info) file) + (if (string-equal name file) (apply func info args)) (setq file (pop files)))))))) @@ -622,37 +621,50 @@ the process output as a string, or nil if the git command failed." (git-file-type-as-string old-perm new-perm) (git-rename-as-string info))))) -(defun git-insert-info-list (status infolist) - "Insert a list of file infos in the status buffer, replacing existing ones if any." - (setq infolist (sort infolist - (lambda (info1 info2) - (string-lessp (git-fileinfo->name info1) - (git-fileinfo->name info2))))) - (let ((info (pop infolist)) - (node (ewoc-nth status 0))) +(defun git-update-node-fileinfo (node info) + "Update the fileinfo of the specified node. The names are assumed to match already." + (let ((data (ewoc-data node))) + (setf + ;; preserve the marked flag + (git-fileinfo->marked info) (git-fileinfo->marked data) + (git-fileinfo->needs-update data) nil) + (when (not (equal info data)) + (setf (git-fileinfo->needs-refresh info) t + (ewoc-data node) info)))) + +(defun git-insert-info-list (status infolist files) + "Insert a sorted list of file infos in the status buffer, replacing existing ones if any." + (let* ((info (pop infolist)) + (node (ewoc-nth status 0)) + (name (and info (git-fileinfo->name info))) + remaining) (while info - (cond ((not node) - (setq node (ewoc-enter-last status info)) - (setq info (pop infolist))) - ((string-lessp (git-fileinfo->name (ewoc-data node)) - (git-fileinfo->name info)) - (setq node (ewoc-next status node))) - ((string-equal (git-fileinfo->name (ewoc-data node)) - (git-fileinfo->name info)) - ;; preserve the marked flag - (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node))) - (setf (git-fileinfo->needs-refresh info) t) - (setf (ewoc-data node) info) - (setq info (pop infolist))) - (t - (setq node (ewoc-enter-before status node info)) - (setq info (pop infolist))))))) + (let ((nodename (and node (git-fileinfo->name (ewoc-data node))))) + (while (and files (string-lessp (car files) name)) + (push (pop files) remaining)) + (when (and files (string-equal (car files) name)) + (setq files (cdr files))) + (cond ((not nodename) + (setq node (ewoc-enter-last status info)) + (setq info (pop infolist)) + (setq name (and info (git-fileinfo->name info)))) + ((string-lessp nodename name) + (setq node (ewoc-next status node))) + ((string-equal nodename name) + ;; preserve the marked flag + (git-update-node-fileinfo node info) + (setq info (pop infolist)) + (setq name (and info (git-fileinfo->name info)))) + (t + (setq node (ewoc-enter-before status node info)) + (setq info (pop infolist)) + (setq name (and info (git-fileinfo->name info))))))) + (nconc (nreverse remaining) files))) (defun git-run-diff-index (status files) "Run git-diff-index on FILES and parse the results into STATUS. Return the list of files that haven't been handled." - (let ((remaining (copy-sequence files)) - infolist) + (let (infolist) (with-temp-buffer (apply #'git-call-process t "diff-index" "-z" "-M" "HEAD" "--" files) (goto-char (point-min)) @@ -669,11 +681,12 @@ Return the list of files that haven't been handled." (push (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) infolist) (push (git-create-fileinfo 'deleted name 0 0 'rename new-name) infolist) (push (git-create-fileinfo 'added new-name old-perm new-perm 'rename name) infolist)) - (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist)) - (setq remaining (delete name remaining)) - (when new-name (setq remaining (delete new-name remaining)))))) - (git-insert-info-list status infolist) - remaining)) + (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist))))) + (setq infolist (sort (nreverse infolist) + (lambda (info1 info2) + (string-lessp (git-fileinfo->name info1) + (git-fileinfo->name info2))))) + (git-insert-info-list status infolist files))) (defun git-find-status-file (status file) "Find a given file in the status ewoc and return its node." @@ -693,16 +706,14 @@ Return the list of files that haven't been handled." (let ((name (match-string 1))) (push (git-create-fileinfo default-state name 0 (if (string-equal "/" (match-string 2)) (lsh ?\110 9) 0)) - infolist) - (setq files (delete name files))))) - (git-insert-info-list status infolist) - files)) + infolist)))) + (setq infolist (nreverse infolist)) ;; assume it is sorted already + (git-insert-info-list status infolist files))) (defun git-run-ls-files-cached (status files default-state) "Run git-ls-files -c on FILES and parse the results into STATUS. Return the list of files that haven't been handled." - (let ((remaining (copy-sequence files)) - infolist) + (let (infolist) (with-temp-buffer (apply #'git-call-process t "ls-files" "-z" "-s" "-c" "--" files) (goto-char (point-min)) @@ -710,10 +721,9 @@ Return the list of files that haven't been handled." (let* ((new-perm (string-to-number (match-string 1) 8)) (old-perm (if (eq default-state 'added) 0 new-perm)) (name (match-string 2))) - (push (git-create-fileinfo default-state name old-perm new-perm) infolist) - (setq remaining (delete name remaining))))) - (git-insert-info-list status infolist) - remaining)) + (push (git-create-fileinfo default-state name old-perm new-perm) infolist)))) + (setq infolist (nreverse infolist)) ;; assume it is sorted already + (git-insert-info-list status infolist files))) (defun git-run-ls-unmerged (status files) "Run git-ls-files -u on FILES and parse the results into STATUS." @@ -742,11 +752,17 @@ Return the list of files that haven't been handled." (concat "--exclude-per-directory=" git-per-dir-ignore-file) (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) -(defun git-update-status-files (files &optional default-state) +(defun git-update-status-files (&optional files) "Update the status of FILES from the index." (unless git-status (error "Not in git-status buffer.")) - (when (or git-show-uptodate files) - (git-run-ls-files-cached git-status files 'uptodate)) + ;; set the needs-update flag on existing files + (if (setq files (sort files #'string-lessp)) + (git-status-filenames-map + git-status (lambda (info) (setf (git-fileinfo->needs-update info) t)) files) + (ewoc-map (lambda (info) (setf (git-fileinfo->needs-update info) t) nil) git-status) + (git-call-process nil "update-index" "--refresh") + (when git-show-uptodate + (git-run-ls-files-cached git-status nil 'uptodate))) (let* ((remaining-files (if (git-empty-db-p) ; we need some special handling for an empty db (git-run-ls-files-cached git-status files 'added) @@ -756,7 +772,11 @@ Return the list of files that haven't been handled." (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'unknown "-o"))) (when (or remaining-files (and git-show-ignored (not files))) (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'ignored "-o" "-i"))) - (git-set-filenames-state git-status remaining-files default-state) + (unless files + (setq remaining-files (git-get-filenames (ewoc-collect git-status #'git-fileinfo->needs-update)))) + (when remaining-files + (setq remaining-files (git-run-ls-files-cached git-status remaining-files 'uptodate))) + (git-set-filenames-state git-status remaining-files nil) (git-refresh-files) (git-refresh-ewoc-hf git-status))) @@ -891,11 +911,9 @@ Return the list of files that haven't been handled." (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) (with-current-buffer buffer (erase-buffer)) - (git-update-status-files (git-get-filenames files) 'uptodate) + (git-update-status-files (git-get-filenames files)) (git-call-process nil "rerere") (git-call-process nil "gc" "--auto") - (git-refresh-files) - (git-refresh-ewoc-hf git-status) (message "Committed %s." commit) (git-run-hook "post-commit" nil))) (message "Commit aborted.")))) @@ -1009,7 +1027,7 @@ Return the list of files that haven't been handled." (unless files (push (file-relative-name (read-file-name "File to add: " nil nil t)) files)) (when (apply 'git-call-process-display-error "update-index" "--add" "--" files) - (git-update-status-files files 'uptodate) + (git-update-status-files files) (git-success-message "Added" files)))) (defun git-ignore-file () @@ -1019,7 +1037,7 @@ Return the list of files that haven't been handled." (unless files (push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files)) (dolist (f files) (git-append-to-ignore f)) - (git-update-status-files files 'ignored) + (git-update-status-files files) (git-success-message "Ignored" files))) (defun git-remove-file () @@ -1037,7 +1055,7 @@ Return the list of files that haven't been handled." (delete-directory name) (delete-file name)))) (when (apply 'git-call-process-display-error "update-index" "--remove" "--" files) - (git-update-status-files files nil) + (git-update-status-files files) (git-success-message "Removed" files))) (message "Aborting")))) @@ -1065,7 +1083,7 @@ Return the list of files that haven't been handled." (apply 'git-call-process-display-error "update-index" "--force-remove" "--" added)) (or (not modified) (apply 'git-call-process-display-error "checkout" "HEAD" modified))))) - (git-update-status-files (append added modified) 'uptodate) + (git-update-status-files (append added modified)) (when ok (dolist (file modified) (let ((buffer (get-file-buffer file))) @@ -1078,7 +1096,7 @@ Return the list of files that haven't been handled." (let ((files (git-get-filenames (git-marked-files-state 'unmerged)))) (when files (when (apply 'git-call-process-display-error "update-index" "--" files) - (git-update-status-files files 'uptodate) + (git-update-status-files files) (git-success-message "Resolved" files))))) (defun git-remove-handled () @@ -1348,7 +1366,7 @@ amended version of it." (git-call-process-display-error "reset" "--soft" "HEAD^") (and (git-update-ref "ORIG_HEAD" commit) (git-update-ref "HEAD" nil commit))) - (git-update-status-files (copy-sequence files) 'uptodate) + (git-update-status-files (copy-sequence files)) (git-mark-files git-status files) (git-refresh-files) (git-setup-commit-buffer commit) @@ -1391,27 +1409,10 @@ amended version of it." (defun git-refresh-status () "Refresh the git status buffer." (interactive) - (let* ((status git-status) - (pos (ewoc-locate status)) - (marked-files (git-get-filenames (ewoc-collect status (lambda (info) (git-fileinfo->marked info))))) - (cur-name (and pos (git-fileinfo->name (ewoc-data pos))))) - (unless status (error "Not in git-status buffer.")) - (message "Refreshing git status...") - (git-call-process nil "update-index" "--refresh") - (git-clear-status status) - (git-update-status-files nil) - ; restore file marks - (when marked-files - (git-status-filenames-map status - (lambda (info) - (setf (git-fileinfo->marked info) t) - (setf (git-fileinfo->needs-refresh info) t)) - marked-files) - (git-refresh-files)) - ; move point to the current file name if any - (message "Refreshing git status...done") - (let ((node (and cur-name (git-find-status-file status cur-name)))) - (when node (ewoc-goto-node status node))))) + (unless git-status (error "Not in git-status buffer.")) + (message "Refreshing git status...") + (git-update-status-files) + (message "Refreshing git status...done")) (defun git-status-quit () "Quit git-status mode." @@ -1591,7 +1592,7 @@ Meant to be used in `after-save-hook'." ; skip files located inside the .git directory (unless (string-match "^\\.git/" filename) (git-call-process nil "add" "--refresh" "--" filename) - (git-update-status-files (list filename) 'uptodate))))))) + (git-update-status-files (list filename)))))))) (defun git-help () "Display help for Git mode." From b0a53e9e56d0a501aebc99d3614be413e91613f6 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Mon, 4 Aug 2008 09:30:42 +0200 Subject: [PATCH 26/63] git.el: Add an insert file command. This allows to insert a file in the buffer no matter what its state is, making it possible for instance to remove an up-to-date file. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 67c5275992..6119c31d05 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1019,6 +1019,11 @@ Return the list of files that haven't been handled." (setq node (ewoc-prev git-status node))) (ewoc-goto-node git-status last))) +(defun git-insert-file (file) + "Insert file(s) into the git-status buffer." + (interactive "fInsert file: ") + (git-update-status-files (list (file-relative-name file)))) + (defun git-add-file () "Add marked file(s) to the index cache." (interactive) @@ -1449,6 +1454,7 @@ amended version of it." (define-key map "\r" 'git-find-file) (define-key map "g" 'git-refresh-status) (define-key map "i" 'git-ignore-file) + (define-key map "I" 'git-insert-file) (define-key map "l" 'git-log-file) (define-key map "m" 'git-mark-file) (define-key map "M" 'git-mark-all) @@ -1505,6 +1511,7 @@ amended version of it." ["Revert File" git-revert-file t] ["Ignore File" git-ignore-file t] ["Remove File" git-remove-file t] + ["Insert File" git-insert-file t] "--------" ["Find File" git-find-file t] ["View File" git-view-file t] From c4e8b72f228b20d3ed6cfba0586364ea8ca431af Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 1 Nov 2008 20:14:10 +0100 Subject: [PATCH 27/63] git.el: Add possibility to mark files directly in git-update-status-files. This avoids the need to go through the list twice, which helps performance on large file lists. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 6119c31d05..9e9101b17e 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -752,7 +752,7 @@ Return the list of files that haven't been handled." (concat "--exclude-per-directory=" git-per-dir-ignore-file) (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) -(defun git-update-status-files (&optional files) +(defun git-update-status-files (&optional files mark-files) "Update the status of FILES from the index." (unless git-status (error "Not in git-status buffer.")) ;; set the needs-update flag on existing files @@ -777,12 +777,12 @@ Return the list of files that haven't been handled." (when remaining-files (setq remaining-files (git-run-ls-files-cached git-status remaining-files 'uptodate))) (git-set-filenames-state git-status remaining-files nil) + (when mark-files (git-mark-files git-status files)) (git-refresh-files) (git-refresh-ewoc-hf git-status))) (defun git-mark-files (status files) "Mark all the specified FILES, and unmark the others." - (setq files (sort files #'string-lessp)) (let ((file (and files (pop files))) (node (ewoc-nth status 0))) (while node @@ -1371,9 +1371,7 @@ amended version of it." (git-call-process-display-error "reset" "--soft" "HEAD^") (and (git-update-ref "ORIG_HEAD" commit) (git-update-ref "HEAD" nil commit))) - (git-update-status-files (copy-sequence files)) - (git-mark-files git-status files) - (git-refresh-files) + (git-update-status-files files t) (git-setup-commit-buffer commit) (git-commit-file)))) From 1905a8666a676f7070ba15c6f56f98bb1da20f7b Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 7 Nov 2008 14:28:09 +0100 Subject: [PATCH 28/63] git.el: Allow to commit even if there are no marked files. This can be useful to commit a merge that didn't result in any changes. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 9e9101b17e..09e8bae3a4 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -895,29 +895,26 @@ Return the list of files that haven't been handled." (unless (git-empty-db-p) (setq head (git-rev-parse "HEAD") head-tree (git-rev-parse "HEAD^{tree}"))) - (if files - (progn - (message "Running git commit...") - (when - (and - (git-read-tree head-tree index-file) - (git-update-index nil files) ;update both the default index - (git-update-index index-file files) ;and the temporary one - (setq tree (git-write-tree index-file))) - (if (or (not (string-equal tree head-tree)) - (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? ")) - (let ((commit (git-commit-tree buffer tree head))) - (when commit - (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) - (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) - (with-current-buffer buffer (erase-buffer)) - (git-update-status-files (git-get-filenames files)) - (git-call-process nil "rerere") - (git-call-process nil "gc" "--auto") - (message "Committed %s." commit) - (git-run-hook "post-commit" nil))) - (message "Commit aborted.")))) - (message "No files to commit."))) + (message "Running git commit...") + (when + (and + (git-read-tree head-tree index-file) + (git-update-index nil files) ;update both the default index + (git-update-index index-file files) ;and the temporary one + (setq tree (git-write-tree index-file))) + (if (or (not (string-equal tree head-tree)) + (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? ")) + (let ((commit (git-commit-tree buffer tree head))) + (when commit + (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) + (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) + (with-current-buffer buffer (erase-buffer)) + (git-update-status-files (git-get-filenames files)) + (git-call-process nil "rerere") + (git-call-process nil "gc" "--auto") + (message "Committed %s." commit) + (git-run-hook "post-commit" nil))) + (message "Commit aborted.")))) (delete-file index-file)))))) From 13c6bcd49f8151438aa3302b8764c6f8d42441e2 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 18 Nov 2008 19:53:26 +0100 Subject: [PATCH 29/63] Fix deletion of last character in levenshtein distance Without this change, "git tags" will not suggest "git tag" (it will only suggest "git status"), and "git statusx" will not suggest anything. Signed-off-by: Samuel Tardieu Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- levenshtein.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/levenshtein.c b/levenshtein.c index db52f2c205..98fea723d4 100644 --- a/levenshtein.c +++ b/levenshtein.c @@ -25,7 +25,7 @@ int levenshtein(const char *string1, const char *string2, row2[j + 1] > row0[j - 1] + w) row2[j + 1] = row0[j - 1] + w; /* deletion */ - if (j + 1 < len2 && row2[j + 1] > row1[j + 1] + d) + if (row2[j + 1] > row1[j + 1] + d) row2[j + 1] = row1[j + 1] + d; /* insertion */ if (row2[j + 1] > row2[j] + a) From 850fb6ff81a151887043b7edd10681640b0e91c1 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 20 Nov 2008 14:27:27 +0100 Subject: [PATCH 30/63] Document levenshtein.c Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- levenshtein.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/levenshtein.c b/levenshtein.c index 98fea723d4..a32f4cdc45 100644 --- a/levenshtein.c +++ b/levenshtein.c @@ -1,6 +1,43 @@ #include "cache.h" #include "levenshtein.h" +/* + * This function implements the Damerau-Levenshtein algorithm to + * calculate a distance between strings. + * + * Basically, it says how many letters need to be swapped, substituted, + * deleted from, or added to string1, at least, to get string2. + * + * The idea is to build a distance matrix for the substrings of both + * strings. To avoid a large space complexity, only the last three rows + * are kept in memory (if swaps had the same or higher cost as one deletion + * plus one insertion, only two rows would be needed). + * + * At any stage, "i + 1" denotes the length of the current substring of + * string1 that the distance is calculated for. + * + * row2 holds the current row, row1 the previous row (i.e. for the substring + * of string1 of length "i"), and row0 the row before that. + * + * In other words, at the start of the big loop, row2[j + 1] contains the + * Damerau-Levenshtein distance between the substring of string1 of length + * "i" and the substring of string2 of length "j + 1". + * + * All the big loop does is determine the partial minimum-cost paths. + * + * It does so by calculating the costs of the path ending in characters + * i (in string1) and j (in string2), respectively, given that the last + * operation is a substition, a swap, a deletion, or an insertion. + * + * This implementation allows the costs to be weighted: + * + * - w (as in "sWap") + * - s (as in "Substitution") + * - a (for insertion, AKA "Add") + * - d (as in "Deletion") + * + * Note that this algorithm calculates a distance _iff_ d == a. + */ int levenshtein(const char *string1, const char *string2, int w, int s, int a, int d) { From 632f70178784291bd2974e07fdcd2b8e8608f252 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Wed, 19 Nov 2008 17:25:27 +0100 Subject: [PATCH 31/63] compat/mingw.c: Teach mingw_rename() to replace read-only files On POSIX, rename() can replace files that are not writable. On Windows, however, read-only files cannot be replaced without additional efforts: We have to make the destination writable first. Since the situations where the destination is read-only are rare, we do not make the destination writable on every invocation, but only if the first try to rename a file failed with an "access denied" error. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- compat/mingw.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/compat/mingw.c b/compat/mingw.c index 772cad510d..45733f9e04 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -865,6 +865,8 @@ int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz) #undef rename int mingw_rename(const char *pold, const char *pnew) { + DWORD attrs; + /* * Try native rename() first to get errno right. * It is based on MoveFile(), which cannot overwrite existing files. @@ -876,12 +878,19 @@ int mingw_rename(const char *pold, const char *pnew) if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING)) return 0; /* TODO: translate more errors */ - if (GetLastError() == ERROR_ACCESS_DENIED) { - DWORD attrs = GetFileAttributes(pnew); - if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY)) { + if (GetLastError() == ERROR_ACCESS_DENIED && + (attrs = GetFileAttributes(pnew)) != INVALID_FILE_ATTRIBUTES) { + if (attrs & FILE_ATTRIBUTE_DIRECTORY) { errno = EISDIR; return -1; } + if ((attrs & FILE_ATTRIBUTE_READONLY) && + SetFileAttributes(pnew, attrs & ~FILE_ATTRIBUTE_READONLY)) { + if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING)) + return 0; + /* revert file attributes on failure */ + SetFileAttributes(pnew, attrs); + } } errno = EACCES; return -1; From f755bb996ba0540d668104d0e2d4e03bb71b560b Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Wed, 19 Nov 2008 12:14:05 +0100 Subject: [PATCH 32/63] Fix handle leak in sha1_file/unpack_objects if there were damaged object data In the case of bad packed object CRC, unuse_pack wasn't called after check_pack_crc which calls use_pack. Signed-off-by: Alex Riesen Acked-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- sha1_file.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sha1_file.c b/sha1_file.c index 75a748a644..0106e2ce5c 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1749,6 +1749,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset, error("bad packed object CRC for %s", sha1_to_hex(sha1)); mark_bad_packed_object(p, sha1); + unuse_pack(&w_curs); return NULL; } } From 3b91c30b763d972c5e133507ccc4b749dc2c6df5 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Wed, 19 Nov 2008 12:14:50 +0100 Subject: [PATCH 33/63] Fix t4030-diff-textconv.sh Avoid passing cygwin pathnames to Perl. Some Perls have problems using them Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- t/t4030-diff-textconv.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh index 03ba26a0de..0b76e7c97a 100755 --- a/t/t4030-diff-textconv.sh +++ b/t/t4030-diff-textconv.sh @@ -21,7 +21,7 @@ EOF cat >hexdump <<'EOF' #!/bin/sh -perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' "$1" +perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1" EOF chmod +x hexdump From 37a7744ffe31af785571858f7341d588a6c66784 Mon Sep 17 00:00:00 2001 From: Bryan Drewery Date: Wed, 19 Nov 2008 23:11:42 -0600 Subject: [PATCH 34/63] Fix misleading wording for git-cherry-pick Documentation for -n implies that -x is normally used, however this is no longer true. Signed-off-by: Bryan Drewery Signed-off-by: Junio C Hamano --- Documentation/git-cherry-pick.txt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt index 837fb08b79..b764130d26 100644 --- a/Documentation/git-cherry-pick.txt +++ b/Documentation/git-cherry-pick.txt @@ -55,13 +55,12 @@ OPTIONS -n:: --no-commit:: - Usually the command automatically creates a commit with - a commit log message stating which commit was - cherry-picked. This flag applies the change necessary - to cherry-pick the named commit to your working tree - and the index, but does not make the commit. In addition, - when this option is used, your index does not have to match - the HEAD commit. The cherry-pick is done against the + Usually the command automatically creates a commit. + This flag applies the change necessary to cherry-pick + the named commit to your working tree and the index, + but does not make the commit. In addition, when this + option is used, your index does not have to match the + HEAD commit. The cherry-pick is done against the beginning state of your index. + This is useful when cherry-picking more than one commits' From cbacbf4e55ba53d35567578d8c97fdea6b52e16a Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 20 Nov 2008 13:56:28 -0500 Subject: [PATCH 35/63] sha1_file: avoid bogus "file exists" error message This avoids the following misleading error message: error: unable to create temporary sha1 filename ./objects/15: File exists mkstemp can fail for many reasons, one of which, ENOENT, can occur if the directory for the temp file doesn't exist. create_tmpfile tried to handle this case by always trying to mkdir the directory, even if it already existed. This caused errno to be clobbered, so one cannot tell why mkstemp really failed, and it truncated the buffer to just the directory name, resulting in the strange error message shown above. Note that in both occasions that I've seen this failure, it has not been due to a missing directory, or bad permissions, but some other, unknown mkstemp failure mode that did not occur when I ran git again. This code could perhaps be made more robust by retrying mkstemp, in case it was a transient failure. Signed-off-by: Joey Hess Signed-off-by: Junio C Hamano --- sha1_file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sha1_file.c b/sha1_file.c index 0106e2ce5c..36dad7261b 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2289,7 +2289,7 @@ static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename) memcpy(buffer, filename, dirlen); strcpy(buffer + dirlen, "tmp_obj_XXXXXX"); fd = mkstemp(buffer); - if (fd < 0 && dirlen) { + if (fd < 0 && dirlen && errno == ENOENT) { /* Make sure the directory exists */ memcpy(buffer, filename, dirlen); buffer[dirlen-1] = 0; From 283b9532831fc0a86c40ac89e1e713b28dabb241 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sat, 22 Nov 2008 19:22:48 +0100 Subject: [PATCH 36/63] Add new testcase to show fast-export does not always exports all tags Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- t/t9301-fast-export.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/t/t9301-fast-export.sh b/t/t9301-fast-export.sh index c19b4a2bab..7607a12dbb 100755 --- a/t/t9301-fast-export.sh +++ b/t/t9301-fast-export.sh @@ -231,4 +231,12 @@ test_expect_success 'fast-export -C -C | fast-import' ' ' +test_expect_failure 'fast-export | fast-import when master is tagged' ' + + git tag -m msg last && + git fast-export -C -C --signed-tags=strip --all > output && + test $(grep -c "^tag " output) = 3 + +' + test_done From 2075ffb58e3a2d46d9e7606010f7f943f2295376 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 23 Nov 2008 12:55:54 +0100 Subject: [PATCH 37/63] fast-export: use an unsorted string list for extra_refs The list extra_refs contains tags and the objects referenced by them, so that they can be handled at the end. When a tag references a commit, that commit is added to the list using the same name. Also, the function handle_tags_and_duplicates() relies on the order the items were added to extra_refs, so clearly we do not want to use a sorted list here. Noticed by Miklos Vajna. Signed-off-by: Johannes Schindelin Tested-by: Miklos Vajna Signed-off-by: Junio C Hamano --- builtin-fast-export.c | 4 ++-- t/t9301-fast-export.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin-fast-export.c b/builtin-fast-export.c index 7c93eb878d..7d5d57ad75 100644 --- a/builtin-fast-export.c +++ b/builtin-fast-export.c @@ -354,7 +354,7 @@ static void get_tags_and_duplicates(struct object_array *pending, case OBJ_TAG: tag = (struct tag *)e->item; while (tag && tag->object.type == OBJ_TAG) { - string_list_insert(full_name, extra_refs)->util = tag; + string_list_append(full_name, extra_refs)->util = tag; tag = (struct tag *)tag->tagged; } if (!tag) @@ -374,7 +374,7 @@ static void get_tags_and_duplicates(struct object_array *pending, } if (commit->util) /* more than one name for the same object */ - string_list_insert(full_name, extra_refs)->util = commit; + string_list_append(full_name, extra_refs)->util = commit; else commit->util = full_name; } diff --git a/t/t9301-fast-export.sh b/t/t9301-fast-export.sh index 7607a12dbb..638c858dc7 100755 --- a/t/t9301-fast-export.sh +++ b/t/t9301-fast-export.sh @@ -231,7 +231,7 @@ test_expect_success 'fast-export -C -C | fast-import' ' ' -test_expect_failure 'fast-export | fast-import when master is tagged' ' +test_expect_success 'fast-export | fast-import when master is tagged' ' git tag -m msg last && git fast-export -C -C --signed-tags=strip --all > output && From 9b864e730bda0e028a85387fa568429724582f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sun, 23 Nov 2008 00:09:30 +0100 Subject: [PATCH 38/63] add strbuf_expand_dict_cb(), a helper for simple cases The new callback function strbuf_expand_dict_cb() can be used together with strbuf_expand() if there is only a small number of placeholders for static replacement texts. It expects its dictionary as an array of placeholder+value pairs as context parameter, terminated by an entry with the placeholder member set to NULL. The new helper is intended to aid converting the remaining calls of interpolate(). strbuf_expand() is smaller, more flexible and can be used to go faster than interpolate(), so it should replace the latter. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- Documentation/technical/api-strbuf.txt | 7 +++++++ strbuf.c | 16 ++++++++++++++++ strbuf.h | 5 +++++ 3 files changed, 28 insertions(+) diff --git a/Documentation/technical/api-strbuf.txt b/Documentation/technical/api-strbuf.txt index a9668e5f2d..a8ee2fe6a1 100644 --- a/Documentation/technical/api-strbuf.txt +++ b/Documentation/technical/api-strbuf.txt @@ -205,6 +205,13 @@ In order to facilitate caching and to make it possible to give parameters to the callback, `strbuf_expand()` passes a context pointer, which can be used by the programmer of the callback as she sees fit. +`strbuf_expand_dict_cb`:: + + Used as callback for `strbuf_expand()`, expects an array of + struct strbuf_expand_dict_entry as context, i.e. pairs of + placeholder and replacement string. The array needs to be + terminated by an entry with placeholder set to NULL. + `strbuf_addf`:: Add a formatted string to the buffer. diff --git a/strbuf.c b/strbuf.c index 720737d856..13be67e4d3 100644 --- a/strbuf.c +++ b/strbuf.c @@ -237,6 +237,22 @@ void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, } } +size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, + void *context) +{ + struct strbuf_expand_dict_entry *e = context; + size_t len; + + for (; e->placeholder && (len = strlen(e->placeholder)); e++) { + if (!strncmp(placeholder, e->placeholder, len)) { + if (e->value) + strbuf_addstr(sb, e->value); + return len; + } + } + return 0; +} + size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f) { size_t res; diff --git a/strbuf.h b/strbuf.h index eba7ba423a..b1670d9945 100644 --- a/strbuf.h +++ b/strbuf.h @@ -111,6 +111,11 @@ extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len); typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context); extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context); +struct strbuf_expand_dict_entry { + const char *placeholder; + const char *value; +}; +extern size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, void *context); __attribute__((format(printf,2,3))) extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...); From ced621b2c15c3d3dd65dd1d7eec984f8546a4673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sun, 23 Nov 2008 00:13:00 +0100 Subject: [PATCH 39/63] merge-recursive: use strbuf_expand() instead of interpolate() Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- ll-merge.c | 21 +++++++++------------ merge-recursive.c | 1 - 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/ll-merge.c b/ll-merge.c index 4a716146f6..fa2ca5250c 100644 --- a/ll-merge.c +++ b/ll-merge.c @@ -8,7 +8,6 @@ #include "attr.h" #include "xdiff-interface.h" #include "run-command.h" -#include "interpolate.h" #include "ll-merge.h" struct ll_merge_driver; @@ -169,11 +168,12 @@ static int ll_ext_merge(const struct ll_merge_driver *fn, int virtual_ancestor) { char temp[3][50]; - char cmdbuf[2048]; - struct interp table[] = { - { "%O" }, - { "%A" }, - { "%B" }, + struct strbuf cmd = STRBUF_INIT; + struct strbuf_expand_dict_entry dict[] = { + { "O", temp[0] }, + { "A", temp[1] }, + { "B", temp[2] }, + { NULL } }; struct child_process child; const char *args[20]; @@ -189,17 +189,13 @@ static int ll_ext_merge(const struct ll_merge_driver *fn, create_temp(src1, temp[1]); create_temp(src2, temp[2]); - interp_set_entry(table, 0, temp[0]); - interp_set_entry(table, 1, temp[1]); - interp_set_entry(table, 2, temp[2]); - - interpolate(cmdbuf, sizeof(cmdbuf), fn->cmdline, table, 3); + strbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict); memset(&child, 0, sizeof(child)); child.argv = args; args[0] = "sh"; args[1] = "-c"; - args[2] = cmdbuf; + args[2] = cmd.buf; args[3] = NULL; status = run_command(&child); @@ -224,6 +220,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn, bad: for (i = 0; i < 3; i++) unlink(temp[i]); + strbuf_release(&cmd); return status; } diff --git a/merge-recursive.c b/merge-recursive.c index 7472d3ecc9..0e988f2a00 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -16,7 +16,6 @@ #include "string-list.h" #include "xdiff-interface.h" #include "ll-merge.h" -#include "interpolate.h" #include "attr.h" #include "merge-recursive.h" #include "dir.h" From 9d7ca667466b9e1a8160d20cce7c06d09213ab6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sun, 23 Nov 2008 00:15:01 +0100 Subject: [PATCH 40/63] daemon: use strbuf_expand() instead of interpolate() Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- daemon.c | 109 +++++++++++++++++++++++++++---------------------------- 1 file changed, 54 insertions(+), 55 deletions(-) diff --git a/daemon.c b/daemon.c index b9ba44c582..64f60c431b 100644 --- a/daemon.c +++ b/daemon.c @@ -1,7 +1,6 @@ #include "cache.h" #include "pkt-line.h" #include "exec_cmd.h" -#include "interpolate.h" #include @@ -54,26 +53,11 @@ static const char *user_path; static unsigned int timeout; static unsigned int init_timeout; -/* - * Static table for now. Ugh. - * Feel free to make dynamic as needed. - */ -#define INTERP_SLOT_HOST (0) -#define INTERP_SLOT_CANON_HOST (1) -#define INTERP_SLOT_IP (2) -#define INTERP_SLOT_PORT (3) -#define INTERP_SLOT_DIR (4) -#define INTERP_SLOT_PERCENT (5) - -static struct interp interp_table[] = { - { "%H", 0}, - { "%CH", 0}, - { "%IP", 0}, - { "%P", 0}, - { "%D", 0}, - { "%%", 0}, -}; - +static char *hostname; +static char *canon_hostname; +static char *ip_address; +static char *tcp_port; +static char *directory; static void logreport(int priority, const char *err, va_list params) { @@ -163,7 +147,7 @@ static int avoid_alias(char *p) } } -static char *path_ok(struct interp *itable) +static char *path_ok(void) { static char rpath[PATH_MAX]; static char interp_path[PATH_MAX]; @@ -171,7 +155,7 @@ static char *path_ok(struct interp *itable) char *path; char *dir; - dir = itable[INTERP_SLOT_DIR].value; + dir = directory; if (avoid_alias(dir)) { logerror("'%s': aliased", dir); @@ -201,14 +185,27 @@ static char *path_ok(struct interp *itable) } } else if (interpolated_path && saw_extended_args) { + struct strbuf expanded_path = STRBUF_INIT; + struct strbuf_expand_dict_entry dict[] = { + { "H", hostname }, + { "CH", canon_hostname }, + { "IP", ip_address }, + { "P", tcp_port }, + { "D", directory }, + { "%", "%" }, + { NULL } + }; + if (*dir != '/') { /* Allow only absolute */ logerror("'%s': Non-absolute path denied (interpolated-path active)", dir); return NULL; } - interpolate(interp_path, PATH_MAX, interpolated_path, - interp_table, ARRAY_SIZE(interp_table)); + strbuf_expand(&expanded_path, interpolated_path, + strbuf_expand_dict_cb, &dict); + strlcpy(interp_path, expanded_path.buf, PATH_MAX); + strbuf_release(&expanded_path); loginfo("Interpolated dir '%s'", interp_path); dir = interp_path; @@ -233,7 +230,7 @@ static char *path_ok(struct interp *itable) * prefixing the base path */ if (base_path && base_path_relaxed && !retried_path) { - dir = itable[INTERP_SLOT_DIR].value; + dir = directory; retried_path = 1; continue; } @@ -299,14 +296,12 @@ static int git_daemon_config(const char *var, const char *value, void *cb) return 0; } -static int run_service(struct interp *itable, struct daemon_service *service) +static int run_service(struct daemon_service *service) { const char *path; int enabled = service->enabled; - loginfo("Request %s for '%s'", - service->name, - itable[INTERP_SLOT_DIR].value); + loginfo("Request %s for '%s'", service->name, directory); if (!enabled && !service->overridable) { logerror("'%s': service not enabled.", service->name); @@ -314,7 +309,7 @@ static int run_service(struct interp *itable, struct daemon_service *service) return -1; } - if (!(path = path_ok(itable))) + if (!(path = path_ok())) return -1; /* @@ -413,9 +408,8 @@ static void make_service_overridable(const char *name, int ena) /* * Separate the "extra args" information as supplied by the client connection. - * Any resulting data is squirreled away in the given interpolation table. */ -static void parse_extra_args(struct interp *table, char *extra_args, int buflen) +static void parse_extra_args(char *extra_args, int buflen) { char *val; int vallen; @@ -433,9 +427,11 @@ static void parse_extra_args(struct interp *table, char *extra_args, int buflen) if (port) { *port = 0; port++; - interp_set_entry(table, INTERP_SLOT_PORT, port); + free(tcp_port); + tcp_port = xstrdup(port); } - interp_set_entry(table, INTERP_SLOT_HOST, host); + free(hostname); + hostname = xstrdup(host); } /* On to the next one */ @@ -444,14 +440,14 @@ static void parse_extra_args(struct interp *table, char *extra_args, int buflen) } } -static void fill_in_extra_table_entries(struct interp *itable) +static void fill_in_extra_table_entries(void) { char *hp; /* * Replace literal host with lowercase-ized hostname. */ - hp = interp_table[INTERP_SLOT_HOST].value; + hp = hostname; if (!hp) return; for ( ; *hp; hp++) @@ -470,17 +466,17 @@ static void fill_in_extra_table_entries(struct interp *itable) memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_CANONNAME; - gai = getaddrinfo(interp_table[INTERP_SLOT_HOST].value, 0, &hints, &ai0); + gai = getaddrinfo(hostname, 0, &hints, &ai0); if (!gai) { for (ai = ai0; ai; ai = ai->ai_next) { struct sockaddr_in *sin_addr = (void *)ai->ai_addr; inet_ntop(AF_INET, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf)); - interp_set_entry(interp_table, - INTERP_SLOT_CANON_HOST, ai->ai_canonname); - interp_set_entry(interp_table, - INTERP_SLOT_IP, addrbuf); + free(canon_hostname); + canon_hostname = xstrdup(ai->ai_canonname); + free(ip_address); + ip_address = xstrdup(addrbuf); break; } freeaddrinfo(ai0); @@ -493,7 +489,7 @@ static void fill_in_extra_table_entries(struct interp *itable) char **ap; static char addrbuf[HOST_NAME_MAX + 1]; - hent = gethostbyname(interp_table[INTERP_SLOT_HOST].value); + hent = gethostbyname(hostname); ap = hent->h_addr_list; memset(&sa, 0, sizeof sa); @@ -504,8 +500,10 @@ static void fill_in_extra_table_entries(struct interp *itable) inet_ntop(hent->h_addrtype, &sa.sin_addr, addrbuf, sizeof(addrbuf)); - interp_set_entry(interp_table, INTERP_SLOT_CANON_HOST, hent->h_name); - interp_set_entry(interp_table, INTERP_SLOT_IP, addrbuf); + free(canon_hostname); + canon_hostname = xstrdup(hent->h_name); + free(ip_address); + ip_address = xstrdup(addrbuf); } #endif } @@ -557,15 +555,16 @@ static int execute(struct sockaddr *addr) pktlen--; } - /* - * Initialize the path interpolation table for this connection. - */ - interp_clear_table(interp_table, ARRAY_SIZE(interp_table)); - interp_set_entry(interp_table, INTERP_SLOT_PERCENT, "%"); + free(hostname); + free(canon_hostname); + free(ip_address); + free(tcp_port); + free(directory); + hostname = canon_hostname = ip_address = tcp_port = directory = NULL; if (len != pktlen) { - parse_extra_args(interp_table, line + len + 1, pktlen - len - 1); - fill_in_extra_table_entries(interp_table); + parse_extra_args(line + len + 1, pktlen - len - 1); + fill_in_extra_table_entries(); } for (i = 0; i < ARRAY_SIZE(daemon_service); i++) { @@ -578,9 +577,9 @@ static int execute(struct sockaddr *addr) * Note: The directory here is probably context sensitive, * and might depend on the actual service being performed. */ - interp_set_entry(interp_table, - INTERP_SLOT_DIR, line + namelen + 5); - return run_service(interp_table, s); + free(directory); + directory = xstrdup(line + namelen + 5); + return run_service(s); } } From d433ed0bb49b947e3bb05d6474cf328c75ffa57d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sun, 23 Nov 2008 00:19:09 +0100 Subject: [PATCH 41/63] daemon: inline fill_in_extra_table_entries() Having fill_in_extra_table_entries() as a separate function has no advantage -- a function with no parameters and return values might as well be an anonymous block of code. Its name still refers to the table of interpolate() which has been removed earlier, so it's better to inline it at its only call site. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- daemon.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/daemon.c b/daemon.c index 64f60c431b..fbf61ca4d5 100644 --- a/daemon.c +++ b/daemon.c @@ -414,6 +414,7 @@ static void parse_extra_args(char *extra_args, int buflen) char *val; int vallen; char *end = extra_args + buflen; + char *hp; while (extra_args < end && *extra_args) { saw_extended_args = 1; @@ -438,11 +439,6 @@ static void parse_extra_args(char *extra_args, int buflen) extra_args = val + vallen; } } -} - -static void fill_in_extra_table_entries(void) -{ - char *hp; /* * Replace literal host with lowercase-ized hostname. @@ -562,10 +558,8 @@ static int execute(struct sockaddr *addr) free(directory); hostname = canon_hostname = ip_address = tcp_port = directory = NULL; - if (len != pktlen) { + if (len != pktlen) parse_extra_args(line + len + 1, pktlen - len - 1); - fill_in_extra_table_entries(); - } for (i = 0; i < ARRAY_SIZE(daemon_service); i++) { struct daemon_service *s = &(daemon_service[i]); From a47551c3828f81ec88d5b0b3d05887f1a7a4233a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sun, 23 Nov 2008 00:21:52 +0100 Subject: [PATCH 42/63] daemon: deglobalize variable 'directory' Remove the global variable 'directory' and pass it as a parameter of the two functions that use it instead, (almost) restoring their interface to how it was before 49ba83fb67d9e447b86953965ce5f949c6a93b81. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- daemon.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/daemon.c b/daemon.c index fbf61ca4d5..1cef3098d2 100644 --- a/daemon.c +++ b/daemon.c @@ -57,7 +57,6 @@ static char *hostname; static char *canon_hostname; static char *ip_address; static char *tcp_port; -static char *directory; static void logreport(int priority, const char *err, va_list params) { @@ -147,7 +146,7 @@ static int avoid_alias(char *p) } } -static char *path_ok(void) +static char *path_ok(char *directory) { static char rpath[PATH_MAX]; static char interp_path[PATH_MAX]; @@ -296,12 +295,12 @@ static int git_daemon_config(const char *var, const char *value, void *cb) return 0; } -static int run_service(struct daemon_service *service) +static int run_service(char *dir, struct daemon_service *service) { const char *path; int enabled = service->enabled; - loginfo("Request %s for '%s'", service->name, directory); + loginfo("Request %s for '%s'", service->name, dir); if (!enabled && !service->overridable) { logerror("'%s': service not enabled.", service->name); @@ -309,7 +308,7 @@ static int run_service(struct daemon_service *service) return -1; } - if (!(path = path_ok())) + if (!(path = path_ok(dir))) return -1; /* @@ -555,8 +554,7 @@ static int execute(struct sockaddr *addr) free(canon_hostname); free(ip_address); free(tcp_port); - free(directory); - hostname = canon_hostname = ip_address = tcp_port = directory = NULL; + hostname = canon_hostname = ip_address = tcp_port = NULL; if (len != pktlen) parse_extra_args(line + len + 1, pktlen - len - 1); @@ -571,9 +569,7 @@ static int execute(struct sockaddr *addr) * Note: The directory here is probably context sensitive, * and might depend on the actual service being performed. */ - free(directory); - directory = xstrdup(line + namelen + 5); - return run_service(s); + return run_service(line + namelen + 5, s); } } From 7de1950cb28cee7d6b1e9cdaf22f30ddcdc5bd01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sun, 23 Nov 2008 00:16:59 +0100 Subject: [PATCH 43/63] remove the unused files interpolate.c and interpolate.h Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- Makefile | 1 - interpolate.c | 103 -------------------------------------------------- interpolate.h | 26 ------------- 3 files changed, 130 deletions(-) delete mode 100644 interpolate.c delete mode 100644 interpolate.h diff --git a/Makefile b/Makefile index 35adafa011..54e0745246 100644 --- a/Makefile +++ b/Makefile @@ -437,7 +437,6 @@ LIB_OBJS += grep.o LIB_OBJS += hash.o LIB_OBJS += help.o LIB_OBJS += ident.o -LIB_OBJS += interpolate.o LIB_OBJS += levenshtein.o LIB_OBJS += list-objects.o LIB_OBJS += ll-merge.o diff --git a/interpolate.c b/interpolate.c deleted file mode 100644 index 7f03bd99c5..0000000000 --- a/interpolate.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2006 Jon Loeliger - */ - -#include "git-compat-util.h" -#include "interpolate.h" - - -void interp_set_entry(struct interp *table, int slot, const char *value) -{ - char *oldval = table[slot].value; - char *newval = NULL; - - free(oldval); - - if (value) - newval = xstrdup(value); - - table[slot].value = newval; -} - - -void interp_clear_table(struct interp *table, int ninterps) -{ - int i; - - for (i = 0; i < ninterps; i++) { - interp_set_entry(table, i, NULL); - } -} - - -/* - * Convert a NUL-terminated string in buffer orig - * into the supplied buffer, result, whose length is reslen, - * performing substitutions on %-named sub-strings from - * the table, interps, with ninterps entries. - * - * Example interps: - * { - * { "%H", "example.org"}, - * { "%port", "123"}, - * { "%%", "%"}, - * } - * - * Returns the length of the substituted string (not including the final \0). - * Like with snprintf, if the result is >= reslen, then it overflowed. - */ - -unsigned long interpolate(char *result, unsigned long reslen, - const char *orig, - const struct interp *interps, int ninterps) -{ - const char *src = orig; - char *dest = result; - unsigned long newlen = 0; - const char *name, *value; - unsigned long namelen, valuelen; - int i; - char c; - - while ((c = *src)) { - if (c == '%') { - /* Try to match an interpolation string. */ - for (i = 0; i < ninterps; i++) { - name = interps[i].name; - namelen = strlen(name); - if (strncmp(src, name, namelen) == 0) - break; - } - - /* Check for valid interpolation. */ - if (i < ninterps) { - value = interps[i].value; - if (!value) { - src += namelen; - continue; - } - - valuelen = strlen(value); - if (newlen + valuelen < reslen) { - /* Substitute. */ - memcpy(dest, value, valuelen); - dest += valuelen; - } - newlen += valuelen; - src += namelen; - continue; - } - } - /* Straight copy one non-interpolation character. */ - if (newlen + 1 < reslen) - *dest++ = *src; - src++; - newlen++; - } - - /* XXX: the previous loop always keep room for the ending NUL, - we just need to check if there was room for a NUL in the first place */ - if (reslen > 0) - *dest = '\0'; - return newlen; -} diff --git a/interpolate.h b/interpolate.h deleted file mode 100644 index 77407e67dc..0000000000 --- a/interpolate.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2006 Jon Loeliger - */ - -#ifndef INTERPOLATE_H -#define INTERPOLATE_H - -/* - * Convert a NUL-terminated string in buffer orig, - * performing substitutions on %-named sub-strings from - * the interpretation table. - */ - -struct interp { - const char *name; - char *value; -}; - -extern void interp_set_entry(struct interp *table, int slot, const char *value); -extern void interp_clear_table(struct interp *table, int ninterps); - -extern unsigned long interpolate(char *result, unsigned long reslen, - const char *orig, - const struct interp *interps, int ninterps); - -#endif /* INTERPOLATE_H */ From 32716a2c6c238113c9a5af116e5ab6f3f4ecde8d Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Fri, 21 Nov 2008 01:44:59 +0100 Subject: [PATCH 44/63] builtin-clone: use strbuf in guess_dir_name() Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- builtin-clone.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin-clone.c b/builtin-clone.c index 8e1a1d3995..275b690b3c 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -134,9 +134,9 @@ static char *guess_dir_name(const char *repo, int is_bundle, int is_bare) } if (is_bare) { - char *result = xmalloc(end - start + 5); - sprintf(result, "%.*s.git", (int)(end - start), start); - return result; + struct strbuf result = STRBUF_INIT; + strbuf_addf(&result, "%.*s.git", (int)(end - start), start); + return strbuf_detach(&result, 0); } return xstrndup(start, end - start); From b9e125e07e349275b16d177b8ee6bad140da31ab Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Fri, 21 Nov 2008 01:45:00 +0100 Subject: [PATCH 45/63] builtin-clone: use strbuf in clone_local() and copy_or_link_directory() Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- builtin-clone.c | 58 ++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/builtin-clone.c b/builtin-clone.c index 275b690b3c..c590d4a179 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -183,36 +183,38 @@ static void setup_reference(const char *repo) free(ref_git_copy); } -static void copy_or_link_directory(char *src, char *dest) +static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest) { struct dirent *de; struct stat buf; int src_len, dest_len; DIR *dir; - dir = opendir(src); + dir = opendir(src->buf); if (!dir) - die("failed to open %s\n", src); + die("failed to open %s\n", src->buf); - if (mkdir(dest, 0777)) { + if (mkdir(dest->buf, 0777)) { if (errno != EEXIST) - die("failed to create directory %s\n", dest); - else if (stat(dest, &buf)) - die("failed to stat %s\n", dest); + die("failed to create directory %s\n", dest->buf); + else if (stat(dest->buf, &buf)) + die("failed to stat %s\n", dest->buf); else if (!S_ISDIR(buf.st_mode)) - die("%s exists and is not a directory\n", dest); + die("%s exists and is not a directory\n", dest->buf); } - src_len = strlen(src); - src[src_len] = '/'; - dest_len = strlen(dest); - dest[dest_len] = '/'; + strbuf_addch(src, '/'); + src_len = src->len; + strbuf_addch(dest, '/'); + dest_len = dest->len; while ((de = readdir(dir)) != NULL) { - strcpy(src + src_len + 1, de->d_name); - strcpy(dest + dest_len + 1, de->d_name); - if (stat(src, &buf)) { - warning ("failed to stat %s\n", src); + strbuf_setlen(src, src_len); + strbuf_addstr(src, de->d_name); + strbuf_setlen(dest, dest_len); + strbuf_addstr(dest, de->d_name); + if (stat(src->buf, &buf)) { + warning ("failed to stat %s\n", src->buf); continue; } if (S_ISDIR(buf.st_mode)) { @@ -221,17 +223,17 @@ static void copy_or_link_directory(char *src, char *dest) continue; } - if (unlink(dest) && errno != ENOENT) - die("failed to unlink %s\n", dest); + if (unlink(dest->buf) && errno != ENOENT) + die("failed to unlink %s\n", dest->buf); if (!option_no_hardlinks) { - if (!link(src, dest)) + if (!link(src->buf, dest->buf)) continue; if (option_local) - die("failed to create link %s\n", dest); + die("failed to create link %s\n", dest->buf); option_no_hardlinks = 1; } - if (copy_file(dest, src, 0666)) - die("failed to copy file to %s\n", dest); + if (copy_file(dest->buf, src->buf, 0666)) + die("failed to copy file to %s\n", dest->buf); } closedir(dir); } @@ -240,17 +242,19 @@ static const struct ref *clone_local(const char *src_repo, const char *dest_repo) { const struct ref *ret; - char src[PATH_MAX]; - char dest[PATH_MAX]; + struct strbuf src = STRBUF_INIT; + struct strbuf dest = STRBUF_INIT; struct remote *remote; struct transport *transport; if (option_shared) add_to_alternates_file(src_repo); else { - snprintf(src, PATH_MAX, "%s/objects", src_repo); - snprintf(dest, PATH_MAX, "%s/objects", dest_repo); - copy_or_link_directory(src, dest); + strbuf_addf(&src, "%s/objects", src_repo); + strbuf_addf(&dest, "%s/objects", dest_repo); + copy_or_link_directory(&src, &dest); + strbuf_release(&src); + strbuf_release(&dest); } remote = remote_get(src_repo); From b5ff37ac6ba4543ab3bbaf99d0641ca260567ff2 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Fri, 21 Nov 2008 01:45:01 +0100 Subject: [PATCH 46/63] builtin_clone: use strbuf in cmd_clone() Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- builtin-clone.c | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/builtin-clone.c b/builtin-clone.c index c590d4a179..2feac9c5cb 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -358,8 +358,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) const char *repo_name, *repo, *work_tree, *git_dir; char *path, *dir; const struct ref *refs, *head_points_at, *remote_head, *mapped_refs; - char branch_top[256], key[256], value[256]; - struct strbuf reflog_msg = STRBUF_INIT; + struct strbuf key = STRBUF_INIT, value = STRBUF_INIT; + struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT; struct transport *transport = NULL; char *src_ref_prefix = "refs/heads/"; @@ -463,35 +463,36 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (option_bare) { if (option_mirror) src_ref_prefix = "refs/"; - strcpy(branch_top, src_ref_prefix); + strbuf_addstr(&branch_top, src_ref_prefix); git_config_set("core.bare", "true"); } else { - snprintf(branch_top, sizeof(branch_top), - "refs/remotes/%s/", option_origin); + strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin); } if (option_mirror || !option_bare) { /* Configure the remote */ if (option_mirror) { - snprintf(key, sizeof(key), - "remote.%s.mirror", option_origin); - git_config_set(key, "true"); + strbuf_addf(&key, "remote.%s.mirror", option_origin); + git_config_set(key.buf, "true"); + strbuf_reset(&key); } - snprintf(key, sizeof(key), "remote.%s.url", option_origin); - git_config_set(key, repo); + strbuf_addf(&key, "remote.%s.url", option_origin); + git_config_set(key.buf, repo); + strbuf_reset(&key); - snprintf(key, sizeof(key), "remote.%s.fetch", option_origin); - snprintf(value, sizeof(value), - "+%s*:%s*", src_ref_prefix, branch_top); - git_config_set_multivar(key, value, "^$", 0); + strbuf_addf(&key, "remote.%s.fetch", option_origin); + strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf); + git_config_set_multivar(key.buf, value.buf, "^$", 0); + strbuf_reset(&key); + strbuf_reset(&value); } refspec.force = 0; refspec.pattern = 1; refspec.src = src_ref_prefix; - refspec.dst = branch_top; + refspec.dst = branch_top.buf; if (path && !is_bundle) refs = clone_local(path, git_dir); @@ -545,7 +546,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) head_points_at->old_sha1, NULL, 0, DIE_ON_ERR); - strbuf_addstr(&head_ref, branch_top); + strbuf_addstr(&head_ref, branch_top.buf); strbuf_addstr(&head_ref, "HEAD"); /* Remote branch link */ @@ -553,10 +554,11 @@ int cmd_clone(int argc, const char **argv, const char *prefix) head_points_at->peer_ref->name, reflog_msg.buf); - snprintf(key, sizeof(key), "branch.%s.remote", head); - git_config_set(key, option_origin); - snprintf(key, sizeof(key), "branch.%s.merge", head); - git_config_set(key, head_points_at->name); + strbuf_addf(&key, "branch.%s.remote", head); + git_config_set(key.buf, option_origin); + strbuf_reset(&key); + strbuf_addf(&key, "branch.%s.merge", head); + git_config_set(key.buf, head_points_at->name); } } else if (remote_head) { /* Source had detached HEAD pointing somewhere. */ @@ -606,6 +608,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix) } strbuf_release(&reflog_msg); + strbuf_release(&branch_top); + strbuf_release(&key); + strbuf_release(&value); junk_pid = 0; return 0; } From ee2314f59a4eb8451008d4468fd96bb4e40763dc Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 23 Nov 2008 22:02:49 +0100 Subject: [PATCH 47/63] bisect: teach "skip" to accept special arguments like "A..B" The current "git bisect skip" syntax is "git bisect skip [...]" so it's already possible to skip a range of revisions using something like: $ git bisect skip $(git rev-list A..B) where A and B are the bounds of the range we want to skip. This patch teaches "git bisect skip" to accept: $ git bisect skip A..B as an abbreviation for the former command. This is done by checking each argument to see if it contains two dots one after the other ('..'), and by expending it using "git rev-list" if that is the case. Note that this patch will not make "git bisect skip" accept all that "git rev-list" accepts, as things like "^A B" for exemple will not work. But things like "A B..C D E F.. ..G H...I" should work as expected. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- git-bisect.sh | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/git-bisect.sh b/git-bisect.sh index 0d0e278c92..6706bc1e7c 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -191,6 +191,21 @@ check_expected_revs() { done } +bisect_skip() { + all='' + for arg in "$@" + do + case "$arg" in + *..*) + revs=$(git rev-list "$arg") || die "Bad rev input: $arg" ;; + *) + revs="'$arg'" ;; + esac + all="$all $revs" + done + bisect_state 'skip' $all +} + bisect_state() { bisect_autostart state=$1 @@ -630,8 +645,10 @@ case "$#" in git bisect -h ;; start) bisect_start "$@" ;; - bad|good|skip) + bad|good) bisect_state "$cmd" "$@" ;; + skip) + bisect_skip "$@" ;; next) # Not sure we want "next" at the UI level anymore. bisect_next "$@" ;; From 73c427eb99a9bb8a3ade40809194405ddb1fd733 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 25 Nov 2008 18:55:00 -0800 Subject: [PATCH 48/63] send-email: Fix Pine address book parsing See: http://www.washington.edu/pine/tech-notes/low-level.html Entries with a fcc or comment field after the address weren't parsed correctly. Continuation lines, identified by leading spaces, were also not handled. Distribution lists which had ( ) around a list of addresses did not have the parenthesis removed. Signed-off-by: Trent Piepho Signed-off-by: Junio C Hamano --- git-send-email.perl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/git-send-email.perl b/git-send-email.perl index 94ca5c89ad..007e2c6ee1 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -345,10 +345,13 @@ my %parse_alias = ( # spaces delimit multiple addresses $aliases{$1} = [ split(/\s+/, $2) ]; }}}, - pine => sub { my $fh = shift; while (<$fh>) { - if (/^(\S+)\t.*\t(.*)$/) { + pine => sub { my $fh = shift; my $f='\t[^\t]*'; + for (my $x = ''; defined($x); $x = $_) { + chomp $x; + $x .= $1 while(defined($_ = <$fh>) && /^ +(.*)$/); + $x =~ /^(\S+)$f\t\(?([^\t]+?)\)?(:?$f){0,2}$/ or next; $aliases{$1} = [ split(/\s*,\s*/, $2) ]; - }}}, + }}, gnus => sub { my $fh = shift; while (<$fh>) { if (/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) { $aliases{$1} = [ $2 ]; From 61af494ca4e63cdc484ea091617ab5f3974f2f9c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 26 Nov 2008 09:58:41 -0800 Subject: [PATCH 49/63] Teach "git diff" to honour --[no-]ext-diff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original intention of 72909be (Add diff-option --ext-diff, 2007-06-30) was to optionally allow the use of external diff viewer in "git log" family (while keeping them disabled by default). It exposed the "allow external diff" bit to the UI, but forgot to adjust the "git diff" codepath that was set up to always allow use of the external diff viewer. Noticed by Nazri Ramliy; tests by René Scharfe squashed in. Signed-off-by: Junio C Hamano --- builtin-diff.c | 5 ++++- t/t4020-diff-external.sh | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/builtin-diff.c b/builtin-diff.c index d5fe775fc1..26cf678591 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -290,6 +290,9 @@ int cmd_diff(int argc, const char **argv, const char *prefix) /* Otherwise, we are doing the usual "git" diff */ rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index; + /* Default to let external be used */ + DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL); + if (nongit) die("Not a git repository"); argc = setup_revisions(argc, argv, &rev, NULL); @@ -298,7 +301,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) if (diff_setup_done(&rev.diffopt) < 0) die("diff_setup_done failed"); } - DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL); + DIFF_OPT_SET(&rev.diffopt, RECURSIVE); /* diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh index 637b4e19d5..22ef7d44b0 100755 --- a/t/t4020-diff-external.sh +++ b/t/t4020-diff-external.sh @@ -43,6 +43,13 @@ test_expect_success 'GIT_EXTERNAL_DIFF environment should apply only to diff' ' ' +test_expect_success 'GIT_EXTERNAL_DIFF environment and --no-ext-diff' ' + + GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff | + grep "^diff --git a/file b/file" + +' + test_expect_success 'diff attribute' ' git config diff.parrot.command echo && @@ -68,6 +75,13 @@ test_expect_success 'diff attribute should apply only to diff' ' ' +test_expect_success 'diff attribute and --no-ext-diff' ' + + git diff --no-ext-diff | + grep "^diff --git a/file b/file" + +' + test_expect_success 'diff attribute' ' git config --unset diff.parrot.command && @@ -94,6 +108,13 @@ test_expect_success 'diff attribute should apply only to diff' ' ' +test_expect_success 'diff attribute and --no-ext-diff' ' + + git diff --no-ext-diff | + grep "^diff --git a/file b/file" + +' + test_expect_success 'no diff with -diff' ' echo >.gitattributes "file -diff" && git diff | grep Binary From 26d6cc555db0144961bcb3537312cc5a7b6d84d1 Mon Sep 17 00:00:00 2001 From: Marcel Koeppen Date: Wed, 26 Nov 2008 17:51:01 +0100 Subject: [PATCH 50/63] t9129-git-svn-i18n-commitencoding: Make compare_svn_head_with() compatible with OSX sed The sed call used in compare_svn_head_with() uses the + quantifier, which is not supported in the OSX version of sed. It is replaced by the equivalent \{1,\}. Signed-off-by: Marcel Koeppen Signed-off-by: Junio C Hamano --- t/t9129-git-svn-i18n-commitencoding.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh index 2848e46e38..938b7fe4b4 100755 --- a/t/t9129-git-svn-i18n-commitencoding.sh +++ b/t/t9129-git-svn-i18n-commitencoding.sh @@ -16,7 +16,7 @@ compare_git_head_with () { compare_svn_head_with () { LC_ALL=en_US.UTF-8 svn log --limit 1 `git svn info --url` | \ - sed -e 1,3d -e "/^-\+\$/d" >current && + sed -e 1,3d -e "/^-\{1,\}\$/d" >current && test_cmp current "$1" } From c095a1db3095f26092ee2d2a9c606b8b656936af Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Wed, 26 Nov 2008 14:27:10 +0100 Subject: [PATCH 51/63] xdiff-interface.c: remove 10 duplicated lines Remove an accidentally duplicated sequence of 10 lines. This happens to plug a leak, too. Signed-off-by: Junio C Hamano --- xdiff-interface.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/xdiff-interface.c b/xdiff-interface.c index e8ef46d10d..d782f06d99 100644 --- a/xdiff-interface.c +++ b/xdiff-interface.c @@ -254,16 +254,6 @@ static long ff_regexp(const char *line, long len, line_buffer = xstrndup(line, len); /* make NUL terminated */ - /* Exclude terminating newline (and cr) from matching */ - if (len > 0 && line[len-1] == '\n') { - if (len > 1 && line[len-2] == '\r') - len -= 2; - else - len--; - } - - line_buffer = xstrndup(line, len); /* make NUL terminated */ - for (i = 0; i < regs->nr; i++) { struct ff_reg *reg = regs->array + i; if (!regexec(®->re, line_buffer, 2, pmatch, 0)) { @@ -338,4 +328,3 @@ int git_xmerge_config(const char *var, const char *value, void *cb) } return git_default_config(var, value, cb); } - From b0f34c3d6719903e280545e73ae89f6c2ebe533f Mon Sep 17 00:00:00 2001 From: Matt McCutchen Date: Wed, 26 Nov 2008 03:26:50 -0500 Subject: [PATCH 52/63] config.txt: alphabetize configuration sections I figured the sections might as well be in some order, so I chose alphabetical but with "core" at the beginning. This should help people add new variables in the right places. Signed-off-by: Matt McCutchen Signed-off-by: Junio C Hamano --- Documentation/config.txt | 96 ++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 87b028fbc1..113d9d1438 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -561,9 +561,6 @@ color.status.:: to red). The values of these variables may be specified as in color.branch.. -commit.template:: - Specify a file to use as the template for new commit messages. - color.ui:: When set to `always`, always use colors in all git commands which are capable of colored output. When false (or `never`), never. When @@ -571,6 +568,9 @@ color.ui:: terminal. When more specific variables of color.* are set, they always take precedence over this setting. Defaults to false. +commit.template:: + Specify a file to use as the template for new commit messages. + diff.autorefreshindex:: When using 'git-diff' to compare with work tree files, do not consider stat-only change as changed. @@ -682,18 +682,6 @@ gc.rerereunresolved:: kept for this many days when 'git-rerere gc' is run. The default is 15 days. See linkgit:git-rerere[1]. -rerere.autoupdate:: - When set to true, `git-rerere` updates the index with the - resulting contents after it cleanly resolves conflicts using - previously recorded resolution. Defaults to false. - -rerere.enabled:: - Activate recording of resolved conflicts, so that identical - conflict hunks can be resolved automatically, should they - be encountered again. linkgit:git-rerere[1] command is by - default enabled if you create `rr-cache` directory under - `$GIT_DIR`, but can be disabled by setting this option to false. - gitcvs.enabled:: Whether the CVS server interface is enabled for this repository. See linkgit:git-cvsserver[1]. @@ -852,6 +840,10 @@ i18n.logOutputEncoding:: Character encoding the commit messages are converted to when running 'git-log' and friends. +imap:: + The configuration variables in the 'imap' section are described + in linkgit:git-imap-send[1]. + instaweb.browser:: Specify the program that will be used to browse your working repository in gitweb. See linkgit:git-instaweb[1]. @@ -887,8 +879,6 @@ man.viewer:: Specify the programs that may be used to display help in the 'man' format. See linkgit:git-help[1]. -include::merge-config.txt[] - man..cmd:: Specify the command to invoke the specified man viewer. The specified command is evaluated in shell with the man page @@ -898,6 +888,8 @@ man..path:: Override the path for the given tool that may be used to display help in the 'man' format. See linkgit:git-help[1]. +include::merge-config.txt[] + mergetool..path:: Override the path for the given tool. This is useful in case your tool is not in the PATH. @@ -1006,6 +998,28 @@ pull.octopus:: pull.twohead:: The default merge strategy to use when pulling a single branch. +receive.fsckObjects:: + If it is set to true, git-receive-pack will check all received + objects. It will abort in the case of a malformed object or a + broken link. The result of an abort are only dangling objects. + Defaults to false. + +receive.unpackLimit:: + If the number of objects received in a push is below this + limit then the objects will be unpacked into loose object + files. However if the number of received objects equals or + exceeds this limit then the received pack will be stored as + a pack, after adding any missing delta bases. Storing the + pack from a push can make the push operation complete faster, + especially on slow filesystems. If not set, the value of + `transfer.unpackLimit` is used instead. + +receive.denyNonFastForwards:: + If set to true, git-receive-pack will deny a ref update which is + not a fast forward. Use this to prevent such an update via a push, + even if that push is forced. This configuration variable is + set when initializing a shared repository. + remote..url:: The URL of a remote repository. See linkgit:git-fetch[1] or linkgit:git-push[1]. @@ -1055,6 +1069,18 @@ repack.usedeltabaseoffset:: "false" and repack. Access from old git versions over the native protocol are unaffected by this option. +rerere.autoupdate:: + When set to true, `git-rerere` updates the index with the + resulting contents after it cleanly resolves conflicts using + previously recorded resolution. Defaults to false. + +rerere.enabled:: + Activate recording of resolved conflicts, so that identical + conflict hunks can be resolved automatically, should they + be encountered again. linkgit:git-rerere[1] command is by + default enabled if you create `rr-cache` directory under + `$GIT_DIR`, but can be disabled by setting this option to false. + showbranch.default:: The default set of branches for linkgit:git-show-branch[1]. See linkgit:git-show-branch[1]. @@ -1091,6 +1117,11 @@ tar.umask:: archiving user's umask will be used instead. See umask(2) and linkgit:git-archive[1]. +transfer.unpackLimit:: + When `fetch.unpackLimit` or `receive.unpackLimit` are + not set, the value of this variable is used instead. + The default value is 100. + url..insteadOf:: Any URL that starts with this value will be rewritten to start, instead, with . In cases where some site serves a @@ -1119,37 +1150,6 @@ user.signingkey:: unchanged to gpg's --local-user parameter, so you may specify a key using any method that gpg supports. -imap:: - The configuration variables in the 'imap' section are described - in linkgit:git-imap-send[1]. - -receive.fsckObjects:: - If it is set to true, git-receive-pack will check all received - objects. It will abort in the case of a malformed object or a - broken link. The result of an abort are only dangling objects. - Defaults to false. - -receive.unpackLimit:: - If the number of objects received in a push is below this - limit then the objects will be unpacked into loose object - files. However if the number of received objects equals or - exceeds this limit then the received pack will be stored as - a pack, after adding any missing delta bases. Storing the - pack from a push can make the push operation complete faster, - especially on slow filesystems. If not set, the value of - `transfer.unpackLimit` is used instead. - -receive.denyNonFastForwards:: - If set to true, git-receive-pack will deny a ref update which is - not a fast forward. Use this to prevent such an update via a push, - even if that push is forced. This configuration variable is - set when initializing a shared repository. - -transfer.unpackLimit:: - When `fetch.unpackLimit` or `receive.unpackLimit` are - not set, the value of this variable is used instead. - The default value is 100. - web.browser:: Specify a web browser that may be used by some commands. Currently only linkgit:git-instaweb[1] and linkgit:git-help[1] From a0178ae2cfd79a1da5d7ddfacadb1e41560bb464 Mon Sep 17 00:00:00 2001 From: Ralf Wildenhues Date: Thu, 27 Nov 2008 08:32:01 +0100 Subject: [PATCH 53/63] Fix typos in the documentation. Signed-off-by: Ralf Wildenhues Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.0.4.txt | 2 +- Documentation/git-commit.txt | 2 +- Documentation/git-svn.txt | 2 +- Documentation/user-manual.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/RelNotes-1.6.0.4.txt b/Documentation/RelNotes-1.6.0.4.txt index fba3f30a89..d522661d31 100644 --- a/Documentation/RelNotes-1.6.0.4.txt +++ b/Documentation/RelNotes-1.6.0.4.txt @@ -30,7 +30,7 @@ Fixes since v1.6.0.3 * 'git status' incorrectly reported a submodule directory as an untracked directory. -* 'git svn' used deprecated 'git-foo' form of subcommand invocaition. +* 'git svn' used deprecated 'git-foo' form of subcommand invocation. * 'git update-ref -d' to remove a reference did not honor --no-deref option. diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 79be4f1c00..77604d0216 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -92,7 +92,7 @@ OPTIONS -s:: --signoff:: - Add Signed-off-by line by the commiter at the end of the commit + Add Signed-off-by line by the committer at the end of the commit log message. -n:: diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index e160b99bdb..5d6d30f764 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -109,7 +109,7 @@ COMMANDS This works similarly to `svn update` or 'git-pull' except that it preserves linear history with 'git-rebase' instead of -'git-merge' for ease of dcommiting with 'git-svn'. +'git-merge' for ease of dcommitting with 'git-svn'. This accepts all options that 'git-svn fetch' and 'git-rebase' accept. However, '--fetch-all' only fetches from the current diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 08d1310bf5..39c0167e1e 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -536,7 +536,7 @@ $ git bisect skip ------------------------------------------------- In this case, though, git may not eventually be able to tell the first -bad one between some first skipped commits and a latter bad commit. +bad one between some first skipped commits and a later bad commit. There are also ways to automate the bisecting process if you have a test script that can tell a good from a bad commit. See From 76bac89036abd73a4a32fd494a34dcc3340fef1a Mon Sep 17 00:00:00 2001 From: Ralf Wildenhues Date: Thu, 27 Nov 2008 08:32:01 +0100 Subject: [PATCH 54/63] Fix typos in the documentation. Signed-off-by: Ralf Wildenhues Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.1.txt | 6 +++--- Documentation/config.txt | 2 +- Documentation/git-add.txt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/RelNotes-1.6.1.txt b/Documentation/RelNotes-1.6.1.txt index 7fdf83f604..848541a5ad 100644 --- a/Documentation/RelNotes-1.6.1.txt +++ b/Documentation/RelNotes-1.6.1.txt @@ -55,9 +55,9 @@ on. to a non-zero value to accept the suggestion when git can uniquely guess. -* The packfile machinery hopefully is more robust when dealilng with +* The packfile machinery hopefully is more robust when dealing with corrupt packs if redundant objects involved in the corruption are - available elsehwere. + available elsewhere. * "git add -N path..." adds the named paths as an empty blob, so that subsequent "git diff" will show a diff as if they are creation events. @@ -157,7 +157,7 @@ on. * "git log" learned "--source" to show what ref each commit was reached from. -* "git log" also learned "--simplify-by-decration" to show the +* "git log" also learned "--simplify-by-decoration" to show the birds-eye-view of the topology of the history. * "git log --pretty=format:" learned "%d" format element that inserts diff --git a/Documentation/config.txt b/Documentation/config.txt index 220b937198..8fee253114 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -820,7 +820,7 @@ gui.fastcopyblame:: repositories at the expense of less thorough copy detection. gui.copyblamethreshold:: - Specifies the theshold to use in 'git gui blame' original location + Specifies the threshold to use in 'git gui blame' original location detection, measured in alphanumeric characters. See the linkgit:git-blame[1] manual for more information on copy detection. diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 6fc20b0baf..7c129cb24f 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -98,7 +98,7 @@ OPTIONS Record only the fact that the path will be added later. An entry for the path is placed in the index with no content. This is useful for, among other things, showing the unstaged content of - such files with 'git diff' and commiting them with 'git commit + such files with 'git diff' and committing them with 'git commit -a'. --refresh:: From 3d51c853df5730212f704a526340a5a059dffeda Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Wed, 26 Nov 2008 13:52:15 -0500 Subject: [PATCH 55/63] git-p4: fix keyword-expansion regex This text: my $dir = $File::Find::dir; return if ($dir !~ m,$options->{dirpat}$,); was improperly converted to: my $dir = $File$dir !~ m,$options->{dirpat}$,); by the keyword identifier expansion code. Add a \n to make sure the regex doesn't go across end-of-line boundaries. Signed-off-by: Pete Wyckoff Acked-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 46136d49bf..2b122d3f51 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -963,7 +963,7 @@ class P4Sync(Command): if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'): text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text) elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'): - text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', text) + text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text) contents[stat['depotFile']] = text From 8d8163f377829d5f61f6053bd55fdcecaf360d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Thu, 27 Nov 2008 14:35:38 +0100 Subject: [PATCH 56/63] bash: remove dashed command leftovers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 5a625b07 (bash: remove fetch, push, pull dashed form leftovers, 2008-10-03) did that already, but there were still some git-cmd left here and there. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 41 +++++--------------------- 1 file changed, 8 insertions(+), 33 deletions(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 39a1ce5a39..bec09bdd62 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -650,21 +650,12 @@ _git_branch () _git_bundle () { - local mycword="$COMP_CWORD" - case "${COMP_WORDS[0]}" in - git) - local cmd="${COMP_WORDS[2]}" - mycword="$((mycword-1))" - ;; - git-bundle*) - local cmd="${COMP_WORDS[1]}" - ;; - esac - case "$mycword" in - 1) + local cmd="${COMP_WORDS[2]}" + case "$COMP_CWORD" in + 2) __gitcomp "create list-heads verify unbundle" ;; - 2) + 3) # looking for a file ;; *) @@ -812,12 +803,7 @@ _git_fetch () __gitcomp "$(__git_refs)" "$pfx" "${cur#*:}" ;; *) - local remote - case "${COMP_WORDS[0]}" in - git-fetch) remote="${COMP_WORDS[1]}" ;; - git) remote="${COMP_WORDS[2]}" ;; - esac - __gitcomp "$(__git_refs2 "$remote")" + __gitcomp "$(__git_refs2 "${COMP_WORDS[2]}")" ;; esac fi @@ -1060,12 +1046,7 @@ _git_pull () if [ "$COMP_CWORD" = 2 ]; then __gitcomp "$(__git_remotes)" else - local remote - case "${COMP_WORDS[0]}" in - git-pull) remote="${COMP_WORDS[1]}" ;; - git) remote="${COMP_WORDS[2]}" ;; - esac - __gitcomp "$(__git_refs "$remote")" + __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" fi } @@ -1078,19 +1059,13 @@ _git_push () else case "$cur" in *:*) - local remote - case "${COMP_WORDS[0]}" in - git-push) remote="${COMP_WORDS[1]}" ;; - git) remote="${COMP_WORDS[2]}" ;; - esac - local pfx="" case "$COMP_WORDBREAKS" in *:*) : great ;; *) pfx="${cur%%:*}:" ;; esac - __gitcomp "$(__git_refs "$remote")" "$pfx" "${cur#*:}" + __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" "$pfx" "${cur#*:}" ;; +*) __gitcomp "$(__git_refs)" + "${cur#+}" @@ -1591,7 +1566,7 @@ _git_tag () -m|-F) COMPREPLY=() ;; - -*|tag|git-tag) + -*|tag) if [ $f = 1 ]; then __gitcomp "$(__git_tags)" else From 608efb875f89a946d5cb37b2dd4077132618e0e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 28 Nov 2008 01:46:38 +0100 Subject: [PATCH 57/63] bash: complete full refs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes it's handy to complete full refs, e.g. the user has some refs outside of refs/{heads,remotes,tags} or the user wants to complete some git command's special refs (like 'git show refs/bisect/bad'). To do that, we check whether the ref to be completed starts with 'refs/' or is 'refs' (to reduce the risk of matching 'refs-'). If it does, then we offer full refs for completion; otherwise everything works as usual. This way the impact on the common case is fairly small (hopefully not many users have branches or tags starting with 'refs'), and in the special case the cost of typing out 'refs' is bearable. While at it, also remove the unused 'cmd' variable from '__git_refs'. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index de193ba7c1..5fb34c49dc 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -188,11 +188,22 @@ __git_tags () __git_refs () { - local cmd i is_hash=y dir="$(__gitdir "$1")" + local i is_hash=y dir="$(__gitdir "$1")" + local cur="${COMP_WORDS[COMP_CWORD]}" format refs if [ -d "$dir" ]; then - if [ -e "$dir/HEAD" ]; then echo HEAD; fi - git --git-dir="$dir" for-each-ref --format='%(refname:short)' \ - refs/tags refs/heads refs/remotes + case "$cur" in + refs|refs/*) + format="refname" + refs="${cur%/*}" + ;; + *) + if [ -e "$dir/HEAD" ]; then echo HEAD; fi + format="refname:short" + refs="refs/tags refs/heads refs/remotes" + ;; + esac + git --git-dir="$dir" for-each-ref --format="%($format)" \ + $refs return fi for i in $(git ls-remote "$dir" 2>/dev/null); do From c07838371b116251b6c4bc62a2ba64109baf74f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Thu, 27 Nov 2008 14:35:53 +0100 Subject: [PATCH 58/63] bash: offer refs instead of filenames for 'git revert' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The completion script for 'git revert' currently offers options and filenames. However, 'git revert' doesn't take any filenames from the command line, but a single commit. Therefore, it's more sane to offer refs instead. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index bec09bdd62..554a03ff4f 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1344,7 +1344,7 @@ _git_revert () return ;; esac - COMPREPLY=() + __gitcomp "$(__git_refs)" } _git_rm () From 1510dbe380a24fdf303a3c0594752cfdc968cb12 Mon Sep 17 00:00:00 2001 From: Matt McCutchen Date: Mon, 24 Nov 2008 01:55:22 -0500 Subject: [PATCH 59/63] git checkout: don't warn about unborn branch if -f is already passed I think it's unnecessary to warn that the checkout has been forced due to an unborn current branch if -f has been explicitly passed. For one project, I am using git-new-workdir to create workdirs from a bare repository whose HEAD is set to an unborn branch, and this warning started to irritate me. Signed-off-by: Matt McCutchen Signed-off-by: Junio C Hamano --- builtin-checkout.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-checkout.c b/builtin-checkout.c index 25845cdd5e..c107fd643a 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -421,7 +421,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new) if (!opts->quiet && !old.path && old.commit && new->commit != old.commit) describe_detached_head("Previous HEAD position was", old.commit); - if (!old.commit) { + if (!old.commit && !opts->force) { if (!opts->quiet) { fprintf(stderr, "warning: You appear to be on a branch yet to be born.\n"); fprintf(stderr, "warning: Forcing checkout of %s.\n", new->name); From 65117abc040d95ef9877c3b14a24f4bc6aeaf4cb Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 20 Nov 2008 13:56:28 -0500 Subject: [PATCH 60/63] sha1_file: avoid bogus "file exists" error message This avoids the following misleading error message: error: unable to create temporary sha1 filename ./objects/15: File exists mkstemp can fail for many reasons, one of which, ENOENT, can occur if the directory for the temp file doesn't exist. create_tmpfile tried to handle this case by always trying to mkdir the directory, even if it already existed. This caused errno to be clobbered, so one cannot tell why mkstemp really failed, and it truncated the buffer to just the directory name, resulting in the strange error message shown above. Note that in both occasions that I've seen this failure, it has not been due to a missing directory, or bad permissions, but some other, unknown mkstemp failure mode that did not occur when I ran git again. This code could perhaps be made more robust by retrying mkstemp, in case it was a transient failure. Signed-off-by: Joey Hess Signed-off-by: Junio C Hamano --- sha1_file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sha1_file.c b/sha1_file.c index 12fc767ee5..1d7f3b6ce8 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2220,7 +2220,7 @@ static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename) memcpy(buffer, filename, dirlen); strcpy(buffer + dirlen, "tmp_obj_XXXXXX"); fd = mkstemp(buffer); - if (fd < 0 && dirlen) { + if (fd < 0 && dirlen && errno == ENOENT) { /* Make sure the directory exists */ memcpy(buffer, filename, dirlen); buffer[dirlen-1] = 0; From 35243577ab460d0b97b97948928d47f71dc8e46a Mon Sep 17 00:00:00 2001 From: Sam Vilain Date: Fri, 14 Nov 2008 20:19:34 +1300 Subject: [PATCH 61/63] sha1_file.c: resolve confusion EACCES vs EPERM An earlier commit 916d081 (Nicer error messages in case saving an object to db goes wrong, 2006-11-09) confused EACCES with EPERM, the latter of which is an unlikely error from mkstemp(). Signed-off-by: Sam Vilain --- sha1_file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sha1_file.c b/sha1_file.c index 1d7f3b6ce8..4e05429aba 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2246,7 +2246,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen, filename = sha1_file_name(sha1); fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename); if (fd < 0) { - if (errno == EPERM) + if (errno == EACCES) return error("insufficient permission for adding an object to repository database %s\n", get_object_directory()); else return error("unable to create temporary sha1 filename %s: %s\n", tmpfile, strerror(errno)); From 16d258332e35dad58c0b22cd4d5c0e63ebb25328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 30 Nov 2008 17:54:31 +0700 Subject: [PATCH 62/63] generate-cmdlist.sh: avoid selecting synopsis at wrong place MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In "common" man pages there is luckily no "NAME" anywhere except at beginning of documents. If there is another "NAME", sed could mis-select it and lead to common-cmds.h corruption. So better nail it at beginning of line, which would reduce corruption chance. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- generate-cmdlist.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh index a2913c2a2c..75c68d948f 100755 --- a/generate-cmdlist.sh +++ b/generate-cmdlist.sh @@ -14,7 +14,7 @@ sort | while read cmd do sed -n ' - /NAME/,/git-'"$cmd"'/H + /^NAME/,/git-'"$cmd"'/H ${ x s/.*git-'"$cmd"' - \(.*\)/ {"'"$cmd"'", "\1"},/ From 5359fde8a4df60f9a2898dcfd2b4fcda02df4708 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 30 Nov 2008 18:33:20 -0800 Subject: [PATCH 63/63] Update draft release notes to 1.6.0.5 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.0.5.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Documentation/RelNotes-1.6.0.5.txt b/Documentation/RelNotes-1.6.0.5.txt index 62f95e6bad..1dc101c6a3 100644 --- a/Documentation/RelNotes-1.6.0.5.txt +++ b/Documentation/RelNotes-1.6.0.5.txt @@ -10,12 +10,34 @@ Fixes since v1.6.0.4 * 'git checkout' from an un-checked-out state did not allow switching out of the current branch. +* 'git diff' always allowed GIT_EXTERNAL_DIFF and --no-ext-diff was no-op for + the command. + +* 'git fast-export' did not export all tags. + +* 'git ls-files --with-tree=' did not work with options other + than -c, most notably with -m. + * 'git pack-objects' did not make its best effort to honor --max-pack-size option when a single first object already busted the given limit and placed many objects in a single pack. +* 'git-p4' fast import frontend was too eager to trigger its keyword expansion + logic, even on a keyword-looking string that does not have closing '$' on the + same line. + +* 'git push $there' when the remote $there is defined in $GIT_DIR/branches/$there + behaves more like what cg-push from Cogito used to work. + +* 'git tag' did not complain when given mutually incompatible set of options. + * 'make check' cannot be run without sparse; people may have meant to say 'make test' instead, so suggest that. * Many unsafe call to sprintf() style varargs functions are corrected. +* Also contains quite a few documentation updates. + +-- +O=v1.6.0.4-39-g27f6496 +