From: Junio C Hamano Date: Sun, 1 Jul 2007 06:49:01 +0000 (-0700) Subject: Merge branch 'fl/config' X-Git-Tag: v1.5.3-rc0~31 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/cd5ada993d6a992eaed1c416f9a2ed6ba9c4cb6d?hp=db1696b8ab1d1bde67e78c773c58d837fa8b5e70 Merge branch 'fl/config' * fl/config: config: add support for --bool and --int while setting values --- diff --git a/Documentation/RelNotes-1.5.3.txt b/Documentation/RelNotes-1.5.3.txt index d111661a7b..ef2f95b3c5 100644 --- a/Documentation/RelNotes-1.5.3.txt +++ b/Documentation/RelNotes-1.5.3.txt @@ -1,4 +1,4 @@ -GIT v1.5.3 Release Notes (draft) +GIT v1.5.3 Release Notes ======================== Updates since v1.5.2 @@ -10,8 +10,23 @@ Updates since v1.5.2 * Thee are a handful pack-objects changes to help you cope better with repositories with pathologically large blobs in them. +* For people who need to import from Perforce, a front-end for + fast-import is in contrib/fast-import/ now. + +* Comes with git-gui 0.8.0. + +* Comes with updated gitk. + * New commands and options. + - "git log" learned a new option '--follow', to follow + renaming history of a single file. + + - "git-filter-branch" is a reborn cg-admin-rewritehist. + + - "git-cvsserver" learned new options (--base-path, --export-all, + --strict-paths) inspired by git-daemon. + - "git-submodule" command helps you manage the projects from the superproject that contain them. @@ -36,9 +51,45 @@ Updates since v1.5.2 - "git repack" can be told to split resulting packs to avoid exceeding limit specified with "--max-pack-size". + - "git fsck" gained --verbose option. This is really really + verbose but it might help you identify exact commit that is + corrupt in your repository. + + - "git format-patch" learned --numbered-files option. This + may be useful for MH users. + + - "git tag -n -l" shows tag annotations while listing tags. + + - "git cvsimport" can optionally use the separate-remote layout. + + - "git blame" can be told to see through commits that changes + whitespaces and indentation levels with "-w" option. + + - "git send-email" can be told not to thread the messages when + sending out more than one patches. + + - "git config" learned NUL terminated output format via -z to + help scripts. + * Updated behavior of existing commands. - - "git push" pretends that you immediately fetched back from + - "git mergetool" chooses its backend more wisely, taking + notice of its environment such as use of X, Gnome/KDE, etc. + + - "gitweb" shows merge commits a lot nicer than before. The + default view uses more compact --cc format, while the UI + allows to choose normal diff with any parent. + + - snapshot files "gitweb" creates from a repository at + $path/$project/.git are more useful. We use $project part + in the filename, which we used to discard. + + - "git cvsimort" creates lightweight tag; there is not any + interesting information we can record in an annotated tag, + and the handcrafted ones the old code created was not + properly formed anyway. + + - "git-push" pretends that you immediately fetched back from the remote by updating corresponding remote tracking branches if you have any. @@ -48,17 +99,25 @@ Updates since v1.5.2 - "git-apply --whitespace=strip" removes blank lines added at the end of the file. - - fetch over git native protocols with -v shows connection + - "git-fetch" over git native protocols with -v shows connection status, and the IP address of the other end, to help diagnosing problems. - - core.legacyheaders is no more, although we still can read - objects created in a new loose object format. + - We used to have core.legacyheaders configuration, when + set to false, allowed git to write loose objects in a format + that mimicks the format used by objects stored in packs. It + turns out that this was not so useful. Although we will + continue to read objects written in that format, we do not + honor that configuration anymore and create loose objects in + the legacy/traditional format. + + - "--find-copies-harder" option to diff family can now be + spelled as "-C -C" for brevity. - "git-mailsplit" (hence "git-am") can read from Maildir formatted mailboxes. - - "git cvsserver" does not barf upon seeing "cvs login" + - "git-cvsserver" does not barf upon seeing "cvs login" request. - "pack-objects" honors "delta" attribute set in @@ -68,10 +127,25 @@ Updates since v1.5.2 - new-workdir script (in contrib) can now be used with a bare repository. + - "git-mergetool" learned to use gvimdiff. + + - "gitview" (in contrib) has a better blame interface. + + - "git log" and friends did not handle a commit log message + that is larger than 16kB; they do now. + + - "--pretty=oneline" output format for "git log" and friends + deals with "malformed" commit log messages that have more + than one lines in the first paragraph better. We used to + show the first line, cutting the title at mid-sentence; we + concatenate them into a single line and treat the result as + "oneline". * Builds - - + - old-style function definitions (most notably, a function + without parameter defined with "func()", not "func(void)") + have been eradicated. * Performance Tweaks @@ -88,6 +162,10 @@ Updates since v1.5.2 the object requested the last time, which exploits the locality of references. + - verifying pack contents done by "git fsck --full" got boost + by carefully choosing the order to verify objects in them. + + Fixes since v1.5.2 ------------------ @@ -96,14 +174,11 @@ this release, unless otherwise noted. * Bugfixes - - .... This has not - been backported to 1.5.2.x series, as it is rather an - intrusive change. - + - "gitweb" had trouble handling non UTF-8 text with older + Encode.pm Perl module. -- exec >/var/tmp/1 -O=v1.5.2-45-ged82edc -O=v1.5.2-172-g1a8b769 +O=v1.5.2.2-603-g7c85173 echo O=`git describe refs/heads/master` git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index ab232c2f68..61a6022ce8 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -49,6 +49,9 @@ branch the `HEAD` at the remote repository actually points at. 'show':: Gives some information about the remote . ++ +With `-n` option, the remote heads are not queried first with +`git ls-remote `; cached information is used instead. 'prune':: @@ -56,6 +59,10 @@ Deletes all stale tracking branches under . These stale branches have already been removed from the remote repository referenced by , but are still locally available in "remotes/". ++ +With `-n` option, the remote heads are not confirmed first with `git +ls-remote `; cached information is used instead. Use with +caution. 'update':: diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index c33a512ffb..28949397ca 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -14,7 +14,8 @@ DESCRIPTION ----------- This script is used to combine all objects that do not currently -reside in a "pack", into a pack. +reside in a "pack", into a pack. It can also be used to re-organise +existing packs into a single, more efficient pack. A pack is a collection of objects, individually compressed, with delta compression applied, stored in a single file, with an @@ -28,11 +29,13 @@ OPTIONS -a:: Instead of incrementally packing the unpacked objects, - pack everything available into a single pack. + pack everything referenced into a single pack. Especially useful when packing a repository that is used for private development and there is no need to worry - about people fetching via dumb file transfer protocols - from it. Use with '-d'. + about people fetching via dumb protocols from it. Use + with '-d'. This will clean up the objects that `git prune` + leaves behind, but `git fsck --full` shows as + dangling. -d:: After packing, if the newly created packs make some diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 1f5d57d42f..293686c31f 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -59,9 +59,11 @@ The --cc option must be repeated for each user you want on the cc list. Only necessary if --compose is also set. If --compose is not set, this will be prompted for. ---no-signed-off-by-cc:: - Do not add emails found in Signed-off-by: or Cc: lines to the - cc list. +--signed-off-by-cc, --no-signed-off-by-cc:: + If this is set, add emails found in Signed-off-by: or Cc: lines to the + cc list. + Default is the value of 'sendemail.signedoffbycc' configuration value; + if that is unspecified, default to --signed-off-by-cc. --quiet:: Make git-send-email less verbose. One line per email should be @@ -82,16 +84,18 @@ The --cc option must be repeated for each user you want on the cc list. Only necessary if --compose is also set. If --compose is not set, this will be prompted for. ---suppress-from:: - Do not add the From: address to the cc: list, if it shows up in a From: - line. +--suppress-from, --no-suppress-from:: + If this is set, do not add the From: address to the cc: list, if it + shows up in a From: line. + Default is the value of 'sendemail.suppressfrom' configuration value; + if that is unspecified, default to --no-supress-from. ---threaded, --no-threaded:: +--thread, --no-thread:: If this is set, the In-Reply-To header will be set on each email sent. - If disabled with "--no-threaded", no emails will have the In-Reply-To + If disabled with "--no-thread", no emails will have the In-Reply-To header set. - Default is the value of the 'sendemail.threaded' configuration value; - if that is unspecified, default to --threaded. + Default is the value of the 'sendemail.thread' configuration value; + if that is unspecified, default to --thread. --dry-run:: Do everything except actually send the emails. diff --git a/Documentation/git.txt b/Documentation/git.txt index 20b5b7bb48..826914837b 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -396,6 +396,16 @@ other 'GIT_PAGER':: This environment variable overrides `$PAGER`. +'GIT_FLUSH':: + If this environment variable is set to "1", then commands such + as git-blame (in incremental mode), git-rev-list, git-log, + git-whatchanged, etc., will force a flush of the output stream + after each commit-oriented record have been flushed. If this + variable is set to "0", the output of these commands will be done + using completely buffered I/O. If this environment variable is + not set, git will choose buffered or record-oriented flushing + based on whether stdout appears to be redirected to a file or not. + 'GIT_TRACE':: If this variable is set to "1", "2" or "true" (comparison is case insensitive), git will print `trace:` messages on diff --git a/Makefile b/Makefile index a98e27aa7e..5d60dc8e12 100644 --- a/Makefile +++ b/Makefile @@ -94,9 +94,9 @@ all:: # Define OLD_ICONV if your library has an old iconv(), where the second # (input buffer pointer) parameter is declared with type (const char **). # -# Define NO_R_TO_GCC if your gcc does not like "-R/path/lib" that -# tells runtime paths to dynamic libraries; "-Wl,-rpath=/path/lib" -# is used instead. +# Define NO_R_TO_GCC_LINKER if your gcc does not like "-R/path/lib" +# that tells runtime paths to dynamic libraries; +# "-Wl,-rpath=/path/lib" is used instead. # # Define USE_NSEC below if you want git to care about sub-second file mtimes # and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and diff --git a/builtin-blame.c b/builtin-blame.c index f7e2c13885..da23a6f9c9 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -1459,6 +1459,7 @@ static void found_guilty_entry(struct blame_entry *ent) printf("boundary\n"); } write_filename_info(suspect->path); + maybe_flush_or_die(stdout, "stdout"); } } diff --git a/builtin-log.c b/builtin-log.c index 073a2a16a3..a4186eac8e 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -589,7 +589,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) get_patch_ids(&rev, &ids, prefix); if (!use_stdout) - realstdout = fdopen(dup(1), "w"); + realstdout = xfdopen(xdup(1), "w"); prepare_revision_walk(&rev); while ((commit = get_revision(&rev)) != NULL) { diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 813aadf596..86db8b03fe 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -100,7 +100,7 @@ static void show_commit(struct commit *commit) printf("%s%c", buf, hdr_termination); free(buf); } - fflush(stdout); + maybe_flush_or_die(stdout, "stdout"); if (commit->parents) { free_commit_list(commit->parents); commit->parents = NULL; diff --git a/builtin-stripspace.c b/builtin-stripspace.c index 62bd4b547b..d8358e28f0 100644 --- a/builtin-stripspace.c +++ b/builtin-stripspace.c @@ -1,58 +1,79 @@ #include "builtin.h" +#include "cache.h" /* - * Remove empty lines from the beginning and end. + * Remove trailing spaces from a line. * - * Turn multiple consecutive empty lines into just one - * empty line. Return true if it is an incomplete line. + * If the line ends with newline, it will be removed too. + * Returns the new length of the string. */ -static int cleanup(char *line) +static int cleanup(char *line, int len) { - int len = strlen(line); + if (len) { + if (line[len - 1] == '\n') + len--; - if (len && line[len-1] == '\n') { - if (len == 1) - return 0; - do { - unsigned char c = line[len-2]; + while (len) { + unsigned char c = line[len - 1]; if (!isspace(c)) break; - line[len-2] = '\n'; len--; - line[len] = 0; - } while (len > 1); - return 0; + } + line[len] = 0; } - return 1; + return len; } -static void stripspace(FILE *in, FILE *out) +/* + * Remove empty lines from the beginning and end + * and also trailing spaces from every line. + * + * Turn multiple consecutive empty lines between paragraphs + * into just one empty line. + * + * If the input has only empty lines and spaces, + * no output will be produced. + * + * Enable skip_comments to skip every line starting with "#". + */ +void stripspace(FILE *in, FILE *out, int skip_comments) { int empties = -1; - int incomplete = 0; - char line[1024]; + int alloc = 1024; + char *line = xmalloc(alloc); + + while (fgets(line, alloc, in)) { + int len = strlen(line); - while (fgets(line, sizeof(line), in)) { - incomplete = cleanup(line); + while (len == alloc - 1 && line[len - 1] != '\n') { + alloc = alloc_nr(alloc); + line = xrealloc(line, alloc); + fgets(line + len, alloc - len, in); + len += strlen(line + len); + } + + if (skip_comments && line[0] == '#') + continue; + len = cleanup(line, len); /* Not just an empty line? */ - if (line[0] != '\n') { + if (len) { if (empties > 0) fputc('\n', out); empties = 0; fputs(line, out); + fputc('\n', out); continue; } if (empties < 0) continue; empties++; } - if (incomplete) - fputc('\n', out); + free(line); } int cmd_stripspace(int argc, const char **argv, const char *prefix) { - stripspace(stdin, stdout); + stripspace(stdin, stdout, 0); return 0; } diff --git a/builtin.h b/builtin.h index da4834c312..661a92f787 100644 --- a/builtin.h +++ b/builtin.h @@ -7,6 +7,7 @@ extern const char git_version_string[]; extern const char git_usage_string[]; extern void help_unknown_cmd(const char *cmd); +extern void stripspace(FILE *in, FILE *out, int skip_comments); extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix); extern void prune_packed_objects(int); diff --git a/cache.h b/cache.h index ed83d92c5a..0525c4ee55 100644 --- a/cache.h +++ b/cache.h @@ -532,6 +532,8 @@ extern char git_default_name[MAX_GITNAME]; extern const char *git_commit_encoding; extern const char *git_log_output_encoding; +/* IO helper functions */ +extern void maybe_flush_or_die(FILE *, const char *); extern int copy_fd(int ifd, int ofd); extern int read_in_full(int fd, void *buf, size_t count); extern int write_in_full(int fd, const void *buf, size_t count); diff --git a/git-compat-util.h b/git-compat-util.h index b2ab3f8256..362e040f52 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -287,6 +287,22 @@ static inline ssize_t xwrite(int fd, const void *buf, size_t len) } } +static inline int xdup(int fd) +{ + int ret = dup(fd); + if (ret < 0) + die("dup failed: %s", strerror(errno)); + return ret; +} + +static inline FILE *xfdopen(int fd, const char *mode) +{ + FILE *stream = fdopen(fd, mode); + if (stream == NULL) + die("Out of memory? fdopen failed: %s", strerror(errno)); + return stream; +} + static inline size_t xsize_t(off_t len) { return (size_t)len; diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 69ccb88dde..ba23eb8eeb 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -1007,7 +1007,7 @@ sub commit { if ($opt_r && $opt_o ne 'HEAD'); system('git-update-ref', 'HEAD', "$orig_branch"); unless ($opt_i) { - system('git checkout'); + system('git checkout -f'); die "checkout failed: $?\n" if $?; } } diff --git a/git-gui/Makefile b/git-gui/Makefile index 3de0de1a23..1bac6fed46 100644 --- a/git-gui/Makefile +++ b/git-gui/Makefile @@ -7,6 +7,8 @@ GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE @$(SHELL_PATH) ./GIT-VERSION-GEN -include GIT-VERSION-FILE +uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not') + SCRIPT_SH = git-gui.sh GITGUI_BUILT_INS = git-citool ALL_PROGRAMS = $(GITGUI_BUILT_INS) $(patsubst %.sh,%,$(SCRIPT_SH)) @@ -29,11 +31,35 @@ ifndef INSTALL INSTALL = install endif +INSTALL_D0 = $(INSTALL) -d -m755 # space is required here +INSTALL_D1 = +INSTALL_R0 = $(INSTALL) -m644 # space is required here +INSTALL_R1 = +INSTALL_X0 = $(INSTALL) -m755 # space is required here +INSTALL_X1 = +INSTALL_L0 = rm -f # space is required here +INSTALL_L1 = && ln # space is required here +INSTALL_L2 = +INSTALL_L3 = + ifndef V - QUIET_GEN = @echo ' ' GEN $@; - QUIET_BUILT_IN = @echo ' ' BUILTIN $@; - QUIET_INDEX = @echo ' ' INDEX $(dir $@); + QUIET = @ + QUIET_GEN = $(QUIET)echo ' ' GEN $@ && + QUIET_BUILT_IN = $(QUIET)echo ' ' BUILTIN $@ && + QUIET_INDEX = $(QUIET)echo ' ' INDEX $(dir $@) && QUIET_2DEVNULL = 2>/dev/null + + INSTALL_D0 = dir= + INSTALL_D1 = && echo ' ' DEST $$dir && $(INSTALL) -d -m755 "$$dir" + INSTALL_R0 = src= + INSTALL_R1 = && echo ' ' INSTALL 644 `basename $$src` && $(INSTALL) -m644 $$src + INSTALL_X0 = src= + INSTALL_X1 = && echo ' ' INSTALL 755 `basename $$src` && $(INSTALL) -m755 $$src + + INSTALL_L0 = dst= + INSTALL_L1 = && src= + INSTALL_L2 = && dst= + INSTALL_L3 = && echo ' ' 'LINK ' `basename "$$dst"` '->' `basename "$$src"` && rm -f "$$dst" && ln "$$src" "$$dst" endif TCL_PATH ?= tclsh @@ -58,11 +84,15 @@ exedir_SQ = $(subst ','\'',$(exedir)) $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh $(QUIET_GEN)rm -f $@ $@+ && \ + GITGUI_RELATIVE= && \ if test '$(exedir_SQ)' = '$(libdir_SQ)'; then \ - GITGUI_RELATIVE=1; \ + if test "$(uname_O)" = Cygwin; \ + then GITGUI_RELATIVE= ; \ + else GITGUI_RELATIVE=1; \ + fi; \ fi && \ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ - -e 's|^exec wish "$$0"|exec $(subst |,'\|',$(TCLTK_PATH_SQ)) "$$0"|' \ + -e 's|^ exec wish "$$0"| exec $(subst |,'\|',$(TCLTK_PATH_SQ)) "$$0"|' \ -e 's/@@GITGUI_VERSION@@/$(GITGUI_VERSION)/g' \ -e 's|@@GITGUI_RELATIVE@@|'$$GITGUI_RELATIVE'|' \ -e $$GITGUI_RELATIVE's|@@GITGUI_LIBDIR@@|$(libdir_SQ)|' \ @@ -109,12 +139,12 @@ GIT-GUI-VARS: .FORCE-GIT-GUI-VARS all:: $(ALL_PROGRAMS) lib/tclIndex install: all - $(INSTALL) -d -m755 '$(DESTDIR_SQ)$(gitexecdir_SQ)' - $(INSTALL) git-gui '$(DESTDIR_SQ)$(gitexecdir_SQ)' - $(foreach p,$(GITGUI_BUILT_INS), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;) - $(INSTALL) -d -m755 '$(DESTDIR_SQ)$(libdir_SQ)' - $(INSTALL) -m644 lib/tclIndex '$(DESTDIR_SQ)$(libdir_SQ)' - $(foreach p,$(ALL_LIBFILES), $(INSTALL) -m644 $p '$(DESTDIR_SQ)$(libdir_SQ)' ;) + $(QUIET)$(INSTALL_D0)'$(DESTDIR_SQ)$(gitexecdir_SQ)' $(INSTALL_D1) + $(QUIET)$(INSTALL_X0)git-gui $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)' + $(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(INSTALL_L0)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L1)'$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' $(INSTALL_L2)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L3) &&) true + $(QUIET)$(INSTALL_D0)'$(DESTDIR_SQ)$(libdir_SQ)' $(INSTALL_D1) + $(QUIET)$(INSTALL_R0)lib/tclIndex $(INSTALL_R1) '$(DESTDIR_SQ)$(libdir_SQ)' + $(QUIET)$(foreach p,$(ALL_LIBFILES), $(INSTALL_R0)$p $(INSTALL_R1) '$(DESTDIR_SQ)$(libdir_SQ)' &&) true dist-version: @mkdir -p $(TARDIR) diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh index 3237f3d596..9df2e47029 100755 --- a/git-gui/git-gui.sh +++ b/git-gui/git-gui.sh @@ -1,6 +1,12 @@ #!/bin/sh # Tcl ignores the next line -*- tcl -*- \ -exec wish "$0" -- "$@" + if test "z$*" = zversion \ + || test "z$*" = z--version; \ + then \ + echo 'git-gui version @@GITGUI_VERSION@@'; \ + exit; \ + fi; \ + exec wish "$0" -- "$@" set appvers {@@GITGUI_VERSION@@} set copyright { @@ -302,11 +308,6 @@ proc tk_optionMenu {w varName args} { ## ## version check -if {{--version} eq $argv || {version} eq $argv} { - puts "git-gui version $appvers" - exit -} - set req_maj 1 set req_min 5 @@ -1580,8 +1581,7 @@ if {[is_MacOSX]} { # -- Tools Menu # - if {[file exists /usr/local/miga/lib/gui-miga] - && [file exists .pvcsrc]} { + if {[is_Cygwin] && [file exists /usr/local/miga/lib/gui-miga]} { proc do_miga {} { global ui_status_value if {![lock_index update]} return diff --git a/git-gui/lib/blame.tcl b/git-gui/lib/blame.tcl index 139171d39e..b523654815 100644 --- a/git-gui/lib/blame.tcl +++ b/git-gui/lib/blame.tcl @@ -272,6 +272,8 @@ constructor new {i_commit i_path} { set cursorW %W tk_popup $w.ctxm %X %Y " + bind $i "[list focus $w_cviewer];break" + bind $i "[list focus $w_cviewer];break" } foreach i [concat $w_columns $w_cviewer] { @@ -287,8 +289,10 @@ constructor new {i_commit i_path} { bind $i {catch {%W yview scroll 1 pages};break} } + bind $w_cviewer "[list focus $w_file];break" + bind $w_cviewer "[list focus $w_file];break" bind $w_cviewer [list focus $w_cviewer] - bind $top [list focus $top] + bind $w_file [list focus $w_file] grid configure $w.header -sticky ew grid configure $w.file_pane -sticky nsew @@ -483,7 +487,11 @@ method _read_file {fd jump} { } ifdeleted { catch {close $fd} } method _exec_blame {cur_w cur_d options cur_s} { - set cmd [list nice git blame] + set cmd [list] + if {![is_Windows] || [is_Cygwin]} { + lappend cmd nice + } + lappend cmd git blame set cmd [concat $cmd $options] lappend cmd --incremental if {$commit eq {}} { diff --git a/git-send-email.perl b/git-send-email.perl index b8b8fe7ee3..87f59fa313 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -64,17 +64,16 @@ sub usage { email sent, rather than to the first email sent. Defaults to on. - --no-signed-off-cc Suppress the automatic addition of email addresses - that appear in Signed-off-by: or Cc: lines to the cc: - list. Note: Using this option is not recommended. + --signed-off-cc Automatically add email addresses that appear in + Signed-off-by: or Cc: lines to the cc: list. Defaults to on. --smtp-server If set, specifies the outgoing SMTP server to use. Defaults to localhost. --suppress-from Suppress sending emails to yourself if your address - appears in a From: line. + appears in a From: line. Defaults to off. - --threaded Specify that the "In-Reply-To:" header should be set on all + --thread Specify that the "In-Reply-To:" header should be set on all emails. Defaults to on. --quiet Make git-send-email less verbose. One line per email @@ -140,9 +139,6 @@ sub format_2822_time { my (@to,@cc,@initial_cc,@bcclist,@xh, $initial_reply_to,$initial_subject,@files,$from,$compose,$time); -# Behavior modification variables -my ($threaded, $chain_reply_to, $quiet, $suppress_from, $no_signed_off_cc, - $dry_run) = (1, 1, 0, 0, 0, 0); my $smtp_server; my $envelope_sender; @@ -157,16 +153,22 @@ sub format_2822_time { $term = new FakeTerm "$@: going non-interactive"; } +# Behavior modification variables +my ($quiet, $dry_run) = (0, 0); + +# Variables with corresponding config settings +my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc); + my %config_settings = ( - "threaded" => \$threaded, - "chainreplyto" => \$chain_reply_to, + "thread" => [\$thread, 1], + "chainreplyto" => [\$chain_reply_to, 1], + "suppressfrom" => [\$suppress_from, 0], + "signedoffcc" => [\$signed_off_cc, 1], ); foreach my $setting (keys %config_settings) { - my $default = $repo->config_bool("sendemail.$setting"); - if (defined $default) { - $config_settings{$setting} = $default ? 1 : 0; - } + my $config = $repo->config_bool("sendemail.$setting"); + ${$config_settings{$setting}->[0]} = (defined $config) ? $config : $config_settings{$setting}->[1]; } @bcclist = $repo->config('sendemail.bcc'); @@ -187,11 +189,11 @@ sub format_2822_time { "smtp-server=s" => \$smtp_server, "compose" => \$compose, "quiet" => \$quiet, - "suppress-from" => \$suppress_from, - "no-signed-off-cc|no-signed-off-by-cc" => \$no_signed_off_cc, + "suppress-from!" => \$suppress_from, + "signed-off-cc|signed-off-by-cc!" => \$signed_off_cc, "dry-run" => \$dry_run, "envelope-sender=s" => \$envelope_sender, - "threaded!" => \$threaded, + "thread!" => \$thread, ); unless ($rc) { @@ -298,7 +300,7 @@ sub expand_aliases { $prompting++; } -if ($threaded && !defined $initial_reply_to && $prompting) { +if ($thread && !defined $initial_reply_to && $prompting) { do { $_= $term->readline("Message-ID to be used as In-Reply-To for the first email? ", $initial_reply_to); @@ -495,7 +497,7 @@ sub send_message Message-Id: $message_id X-Mailer: git-send-email $gitversion "; - if ($threaded && $reply_to) { + if ($thread && $reply_to) { $header .= "In-Reply-To: $reply_to\n"; $header .= "References: $references\n"; @@ -620,7 +622,7 @@ sub send_message } } else { $message .= $_; - if (/^(Signed-off-by|Cc): (.*)$/i && !$no_signed_off_cc) { + if (/^(Signed-off-by|Cc): (.*)$/i && $signed_off_cc) { my $c = $2; chomp $c; push @cc, $c; diff --git a/git-svn.perl b/git-svn.perl index 50128d7285..98687f54d7 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -787,12 +787,12 @@ sub read_repo_config { sub extract_metadata { my $id = shift or return (undef, undef, undef); - my ($url, $rev, $uuid) = ($id =~ /^git-svn-id:\s(\S+?)\@(\d+) + my ($url, $rev, $uuid) = ($id =~ /^\s*git-svn-id:\s+(.*)\@(\d+) \s([a-f\d\-]+)$/x); if (!defined $rev || !$uuid || !$url) { # some of the original repositories I made had # identifiers like this: - ($rev, $uuid) = ($id =~/^git-svn-id:\s(\d+)\@([a-f\d\-]+)/); + ($rev, $uuid) = ($id =~/^\s*git-svn-id:\s(\d+)\@([a-f\d\-]+)/); } return ($url, $rev, $uuid); } @@ -804,20 +804,29 @@ sub cmt_metadata { sub working_head_info { my ($head, $refs) = @_; - my ($fh, $ctx) = command_output_pipe('rev-list', $head); - while (my $hash = <$fh>) { - chomp($hash); - my ($url, $rev, $uuid) = cmt_metadata($hash); + my ($fh, $ctx) = command_output_pipe('log', $head); + my $hash; + my %max; + while (<$fh>) { + if ( m{^commit ($::sha1)$} ) { + unshift @$refs, $hash if $hash and $refs; + $hash = $1; + next; + } + next unless s{^\s*(git-svn-id:)}{$1}; + my ($url, $rev, $uuid) = extract_metadata($_); if (defined $url && defined $rev) { + next if $max{$url} and $max{$url} < $rev; if (my $gs = Git::SVN->find_by_url($url)) { my $c = $gs->rev_db_get($rev); if ($c && $c eq $hash) { close $fh; # break the pipe return ($url, $rev, $uuid, $gs); + } else { + $max{$url} ||= $gs->rev_db_max; } } } - unshift @$refs, $hash if $refs; } command_close_pipe($fh, $ctx); (undef, undef, undef, undef); @@ -1966,16 +1975,19 @@ sub rebuild { return; } print "Rebuilding $db_path ...\n"; - my ($rev_list, $ctx) = command_output_pipe("rev-list", $self->refname); + my ($log, $ctx) = command_output_pipe("log", $self->refname); my $latest; my $full_url = $self->full_url; remove_username($full_url); my $svn_uuid; - while (<$rev_list>) { - chomp; - my $c = $_; - die "Non-SHA1: $c\n" unless $c =~ /^$::sha1$/o; - my ($url, $rev, $uuid) = ::cmt_metadata($c); + my $c; + while (<$log>) { + if ( m{^commit ($::sha1)$} ) { + $c = $1; + next; + } + next unless s{^\s*(git-svn-id:)}{$1}; + my ($url, $rev, $uuid) = ::extract_metadata($_); remove_username($url); # ignore merges (from set-tree) @@ -1993,7 +2005,7 @@ sub rebuild { $self->rev_db_set($rev, $c); print "r$rev = $c\n"; } - command_close_pipe($rev_list, $ctx); + command_close_pipe($log, $ctx); print "Done rebuilding $db_path\n"; } @@ -2925,6 +2937,7 @@ sub new { SVN::Client::get_ssl_server_trust_file_provider(), SVN::Client::get_simple_prompt_provider( \&Git::SVN::Prompt::simple, 2), + SVN::Client::get_ssl_client_cert_file_provider(), SVN::Client::get_ssl_client_cert_prompt_provider( \&Git::SVN::Prompt::ssl_client_cert, 2), SVN::Client::get_ssl_client_cert_pw_prompt_provider( diff --git a/git-tag.sh b/git-tag.sh index c84043902f..1ff5b41e7f 100755 --- a/git-tag.sh +++ b/git-tag.sh @@ -19,28 +19,40 @@ do case "$1" in -a) annotate=1 + shift ;; -s) annotate=1 signed=1 + shift ;; -f) force=1 + shift ;; -n) - case $2 in - -*) LINES=1 # no argument + case "$#,$2" in + 1,* | *,-*) + LINES=1 # no argument ;; *) shift LINES=$(expr "$1" : '\([0-9]*\)') [ -z "$LINES" ] && LINES=1 # 1 line is default when -n is used ;; esac + shift ;; -l) list=1 shift - PATTERN="$1" # select tags by shell pattern, not re + case $# in + 0) PATTERN= + ;; + *) + PATTERN="$1" # select tags by shell pattern, not re + shift + ;; + esac git rev-parse --symbolic --tags | sort | while read TAG do @@ -51,12 +63,16 @@ do [ "$LINES" -le 0 ] && { echo "$TAG"; continue ;} OBJTYPE=$(git cat-file -t "$TAG") case $OBJTYPE in - tag) ANNOTATION=$(git cat-file tag "$TAG" | - sed -e '1,/^$/d' \ - -e '/^-----BEGIN PGP SIGNATURE-----$/Q' ) - printf "%-15s %s\n" "$TAG" "$ANNOTATION" | - sed -e '2,$s/^/ /' \ - -e "${LINES}q" + tag) + ANNOTATION=$(git cat-file tag "$TAG" | + sed -e '1,/^$/d' | + sed -n -e " + /^-----BEGIN PGP SIGNATURE-----\$/q + 2,\$s/^/ / + p + ${LINES}q + ") + printf "%-15s %s\n" "$TAG" "$ANNOTATION" ;; *) echo "$TAG" ;; @@ -70,7 +86,9 @@ do if test "$#" = "0"; then die "error: option -m needs an argument" else + message="$1" message_given=1 + shift fi ;; -F) @@ -81,13 +99,19 @@ do else message="$(cat "$1")" message_given=1 + shift fi ;; -u) annotate=1 signed=1 shift - username="$1" + if test "$#" = "0"; then + die "error: option -u needs an argument" + else + username="$1" + shift + fi ;; -d) shift @@ -122,7 +146,6 @@ do break ;; esac - shift done [ -n "$list" ] && exit 0 diff --git a/git-verify-tag.sh b/git-verify-tag.sh index f2d5597dba..68858b694d 100755 --- a/git-verify-tag.sh +++ b/git-verify-tag.sh @@ -37,8 +37,9 @@ esac trap 'rm -f "$GIT_DIR/.tmp-vtag"' 0 git-cat-file tag "$1" >"$GIT_DIR/.tmp-vtag" || exit 1 - -cat "$GIT_DIR/.tmp-vtag" | -sed '/-----BEGIN PGP/Q' | +sed -n -e ' + /^-----BEGIN PGP SIGNATURE-----$/q + p +' <"$GIT_DIR/.tmp-vtag" | gpg --verify "$GIT_DIR/.tmp-vtag" - || exit 1 rm -f "$GIT_DIR/.tmp-vtag" diff --git a/git.c b/git.c index 29b55a1604..cfec5d70ee 100644 --- a/git.c +++ b/git.c @@ -216,14 +216,54 @@ const char git_version_string[] = GIT_VERSION; */ #define NOT_BARE (1<<2) -static void handle_internal_command(int argc, const char **argv, char **envp) +struct cmd_struct { + const char *cmd; + int (*fn)(int, const char **, const char *); + int option; +}; + +static int run_command(struct cmd_struct *p, int argc, const char **argv) +{ + int status; + struct stat st; + const char *prefix; + + prefix = NULL; + if (p->option & RUN_SETUP) + prefix = setup_git_directory(); + if (p->option & USE_PAGER) + setup_pager(); + if (p->option & NOT_BARE) { + if (is_bare_repository() || is_inside_git_dir()) + die("%s must be run in a work tree", p->cmd); + } + trace_argv_printf(argv, argc, "trace: built-in: git"); + + status = p->fn(argc, argv, prefix); + if (status) + return status; + + /* Somebody closed stdout? */ + if (fstat(fileno(stdout), &st)) + return 0; + /* Ignore write errors for pipes and sockets.. */ + if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) + return 0; + + /* Check for ENOSPC and EIO errors.. */ + if (fflush(stdout)) + die("write failure on standard output: %s", strerror(errno)); + if (ferror(stdout)) + die("unknown write failure on standard output"); + if (fclose(stdout)) + die("close failed on standard output: %s", strerror(errno)); + return 0; +} + +static void handle_internal_command(int argc, const char **argv) { const char *cmd = argv[0]; - static struct cmd_struct { - const char *cmd; - int (*fn)(int, const char **, const char *); - int option; - } commands[] = { + static struct cmd_struct commands[] = { { "add", cmd_add, RUN_SETUP | NOT_BARE }, { "annotate", cmd_annotate, RUN_SETUP | USE_PAGER }, { "apply", cmd_apply }, @@ -307,25 +347,13 @@ static void handle_internal_command(int argc, const char **argv, char **envp) for (i = 0; i < ARRAY_SIZE(commands); i++) { struct cmd_struct *p = commands+i; - const char *prefix; if (strcmp(p->cmd, cmd)) continue; - - prefix = NULL; - if (p->option & RUN_SETUP) - prefix = setup_git_directory(); - if (p->option & USE_PAGER) - setup_pager(); - if ((p->option & NOT_BARE) && - (is_bare_repository() || is_inside_git_dir())) - die("%s must be run in a work tree", cmd); - trace_argv_printf(argv, argc, "trace: built-in: git"); - - exit(p->fn(argc, argv, prefix)); + exit(run_command(p, argc, argv)); } } -int main(int argc, const char **argv, char **envp) +int main(int argc, const char **argv) { const char *cmd = argv[0] ? argv[0] : "git-help"; char *slash = strrchr(cmd, '/'); @@ -358,7 +386,7 @@ int main(int argc, const char **argv, char **envp) if (!prefixcmp(cmd, "git-")) { cmd += 4; argv[0] = cmd; - handle_internal_command(argc, argv, envp); + handle_internal_command(argc, argv); die("cannot handle %s internally", cmd); } @@ -390,7 +418,7 @@ int main(int argc, const char **argv, char **envp) while (1) { /* See if it's an internal command */ - handle_internal_command(argc, argv, envp); + handle_internal_command(argc, argv); /* .. then try the external ones */ execv_git_cmd(argv); diff --git a/gitk b/gitk index 269f9b08a0..2d6a6ef9ce 100755 --- a/gitk +++ b/gitk @@ -6972,7 +6972,7 @@ proc doprefs {} { pack $top.ntag.b $top.ntag.l -side left grid x $top.ntag -sticky w label $top.tabstopl -text "tabstop" -font optionfont - entry $top.tabstop -width 10 -textvariable tabstop + spinbox $top.tabstop -from 1 -to 20 -width 4 -textvariable tabstop grid x $top.tabstopl $top.tabstop -sticky w label $top.cdisp -text "Colors: press to choose" @@ -7004,7 +7004,7 @@ proc doprefs {} { grid x $top.hunksepbut $top.hunksep -sticky w label $top.selbgsep -padx 40 -relief sunk -background $selectbgcolor button $top.selbgbut -text "Select bg" -font optionfont \ - -command [list choosecolor selectbgcolor 0 $top.bg background setselbg] + -command [list choosecolor selectbgcolor 0 $top.selbgsep background setselbg] grid x $top.selbgbut $top.selbgsep -sticky w frame $top.buts diff --git a/log-tree.c b/log-tree.c index 0cf21bc051..ced3f332ef 100644 --- a/log-tree.c +++ b/log-tree.c @@ -408,5 +408,6 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit) shown = 1; } opt->loginfo = NULL; + maybe_flush_or_die(stdout, "stdout"); return shown; } diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh new file mode 100755 index 0000000000..f4294d72d9 --- /dev/null +++ b/t/t0030-stripspace.sh @@ -0,0 +1,355 @@ +#!/bin/sh +# +# Copyright (c) 2007 Carlos Rica +# + +test_description='git-stripspace' + +. ./test-lib.sh + +t40='A quick brown fox jumps over the lazy do' +s40=' ' +sss="$s40$s40$s40$s40$s40$s40$s40$s40$s40$s40" # 400 +ttt="$t40$t40$t40$t40$t40$t40$t40$t40$t40$t40" # 400 + +test_expect_success \ + 'long lines without spaces should be unchanged' ' + echo "$ttt" >expect && + git-stripspace actual && + git diff expect actual && + + echo "$ttt$ttt" >expect && + git-stripspace actual && + git diff expect actual && + + echo "$ttt$ttt$ttt" >expect && + git-stripspace actual && + git diff expect actual && + + echo "$ttt$ttt$ttt$ttt" >expect && + git-stripspace actual && + git diff expect actual +' + +test_expect_success \ + 'lines with spaces at the beginning should be unchanged' ' + echo "$sss$ttt" >expect && + git-stripspace actual && + git diff expect actual && + + echo "$sss$sss$ttt" >expect && + git-stripspace actual && + git diff expect actual && + + echo "$sss$sss$sss$ttt" >expect && + git-stripspace actual && + git diff expect actual +' + +test_expect_success \ + 'lines with intermediate spaces should be unchanged' ' + echo "$ttt$sss$ttt" >expect && + git-stripspace actual && + git diff expect actual && + + echo "$ttt$sss$sss$ttt" >expect && + git-stripspace actual && + git diff expect actual +' + +test_expect_success \ + 'consecutive blank lines should be unified' ' + printf "$ttt\n\n$ttt\n" > expect && + printf "$ttt\n\n\n\n\n$ttt\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt$ttt\n\n$ttt\n" > expect && + printf "$ttt$ttt\n\n\n\n\n$ttt\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt$ttt$ttt\n\n$ttt\n" > expect && + printf "$ttt$ttt$ttt\n\n\n\n\n$ttt\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n\n$ttt\n" > expect && + printf "$ttt\n\n\n\n\n$ttt\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n\n$ttt$ttt\n" > expect && + printf "$ttt\n\n\n\n\n$ttt$ttt\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n\n$ttt$ttt$ttt\n" > expect && + printf "$ttt\n\n\n\n\n$ttt$ttt$ttt\n" | git-stripspace >actual && + git diff expect actual +' + +test_expect_success \ + 'consecutive blank lines at the beginning should be removed' ' + printf "" > expect && + printf "\n" | git-stripspace >actual && + git diff expect actual && + + printf "" > expect && + printf "\n\n\n" | git-stripspace >actual && + git diff expect actual && + + printf "" > expect && + printf "$sss\n$sss\n$sss\n" | git-stripspace >actual && + git diff expect actual && + + printf "" > expect && + printf "$sss$sss\n$sss\n\n" | git-stripspace >actual && + git diff expect actual && + + printf "" > expect && + printf "\n$sss\n$sss$sss\n" | git-stripspace >actual && + git diff expect actual && + + printf "" > expect && + printf "$sss$sss$sss$sss\n\n\n" | git-stripspace >actual && + git diff expect actual && + + printf "" > expect && + printf "\n$sss$sss$sss$sss\n\n" | git-stripspace >actual && + git diff expect actual && + + printf "" > expect && + printf "\n\n$sss$sss$sss$sss\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n" > expect && + printf "\n$ttt\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n" > expect && + printf "\n\n\n$ttt\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt$ttt\n" > expect && + printf "\n\n\n$ttt$ttt\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt$ttt$ttt\n" > expect && + printf "\n\n\n$ttt$ttt$ttt\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt$ttt$ttt$ttt\n" > expect && + printf "\n\n\n$ttt$ttt$ttt$ttt\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n" > expect && + printf "$sss\n$sss\n$sss\n$ttt\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n" > expect && + printf "\n$sss\n$sss$sss\n$ttt\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n" > expect && + printf "$sss$sss\n$sss\n\n$ttt\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n" > expect && + printf "$sss$sss$sss\n\n\n$ttt\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n" > expect && + printf "\n$sss$sss$sss\n\n$ttt\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n" > expect && + printf "\n\n$sss$sss$sss\n$ttt\n" | git-stripspace >actual && + git diff expect actual +' + +test_expect_success \ + 'consecutive blank lines at the end should be removed' ' + printf "$ttt\n" > expect && + printf "$ttt\n\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n" > expect && + printf "$ttt\n\n\n\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt$ttt\n" > expect && + printf "$ttt$ttt\n\n\n\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt$ttt$ttt\n" > expect && + printf "$ttt$ttt$ttt\n\n\n\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt$ttt$ttt$ttt\n" > expect && + printf "$ttt$ttt$ttt$ttt\n\n\n\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n" > expect && + printf "$ttt\n$sss\n$sss\n$sss\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n" > expect && + printf "$ttt\n\n$sss\n$sss$sss\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n" > expect && + printf "$ttt\n$sss$sss\n$sss\n\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n" > expect && + printf "$ttt\n$sss$sss$sss\n\n\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n" > expect && + printf "$ttt\n\n$sss$sss$sss\n\n" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n" > expect && + printf "$ttt\n\n\n$sss$sss$sss\n" | git-stripspace >actual && + git diff expect actual +' + +test_expect_success \ + 'text without newline at end should end with newline' ' + test `printf "$ttt" | git-stripspace | wc -l` -gt 0 && + test `printf "$ttt$ttt" | git-stripspace | wc -l` -gt 0 && + test `printf "$ttt$ttt$ttt" | git-stripspace | wc -l` -gt 0 && + test `printf "$ttt$ttt$ttt$ttt" | git-stripspace | wc -l` -gt 0 +' + +# text plus spaces at the end: + +test_expect_success \ + 'text plus spaces without newline at end should end with newline' ' + test `printf "$ttt$sss" | git-stripspace | wc -l` -gt 0 && + test `printf "$ttt$ttt$sss" | git-stripspace | wc -l` -gt 0 && + test `printf "$ttt$ttt$ttt$sss" | git-stripspace | wc -l` -gt 0 + test `printf "$ttt$sss$sss" | git-stripspace | wc -l` -gt 0 && + test `printf "$ttt$ttt$sss$sss" | git-stripspace | wc -l` -gt 0 && + test `printf "$ttt$sss$sss$sss" | git-stripspace | wc -l` -gt 0 +' + +test_expect_failure \ + 'text plus spaces without newline at end should not show spaces' ' + printf "$ttt$sss" | git-stripspace | grep -q " " || + printf "$ttt$ttt$sss" | git-stripspace | grep -q " " || + printf "$ttt$ttt$ttt$sss" | git-stripspace | grep -q " " || + printf "$ttt$sss$sss" | git-stripspace | grep -q " " || + printf "$ttt$ttt$sss$sss" | git-stripspace | grep -q " " || + printf "$ttt$sss$sss$sss" | git-stripspace | grep -q " " +' + +test_expect_success \ + 'text plus spaces without newline should show the correct lines' ' + printf "$ttt\n" >expect && + printf "$ttt$sss" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n" >expect && + printf "$ttt$sss$sss" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt\n" >expect && + printf "$ttt$sss$sss$sss" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt$ttt\n" >expect && + printf "$ttt$ttt$sss" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt$ttt\n" >expect && + printf "$ttt$ttt$sss$sss" | git-stripspace >actual && + git diff expect actual && + + printf "$ttt$ttt$ttt\n" >expect && + printf "$ttt$ttt$ttt$sss" | git-stripspace >actual && + git diff expect actual +' + +test_expect_failure \ + 'text plus spaces at end should not show spaces' ' + echo "$ttt$sss" | git-stripspace | grep -q " " || + echo "$ttt$ttt$sss" | git-stripspace | grep -q " " || + echo "$ttt$ttt$ttt$sss" | git-stripspace | grep -q " " || + echo "$ttt$sss$sss" | git-stripspace | grep -q " " || + echo "$ttt$ttt$sss$sss" | git-stripspace | grep -q " " || + echo "$ttt$sss$sss$sss" | git-stripspace | grep -q " " +' + +test_expect_success \ + 'text plus spaces at end should be cleaned and newline must remain' ' + echo "$ttt" >expect && + echo "$ttt$sss" | git-stripspace >actual && + git diff expect actual && + + echo "$ttt" >expect && + echo "$ttt$sss$sss" | git-stripspace >actual && + git diff expect actual && + + echo "$ttt" >expect && + echo "$ttt$sss$sss$sss" | git-stripspace >actual && + git diff expect actual && + + echo "$ttt$ttt" >expect && + echo "$ttt$ttt$sss" | git-stripspace >actual && + git diff expect actual && + + echo "$ttt$ttt" >expect && + echo "$ttt$ttt$sss$sss" | git-stripspace >actual && + git diff expect actual && + + echo "$ttt$ttt$ttt" >expect && + echo "$ttt$ttt$ttt$sss" | git-stripspace >actual && + git diff expect actual +' + +# spaces only: + +test_expect_success \ + 'spaces with newline at end should be replaced with empty string' ' + printf "" >expect && + + echo | git-stripspace >actual && + git diff expect actual && + + echo "$sss" | git-stripspace >actual && + git diff expect actual && + + echo "$sss$sss" | git-stripspace >actual && + git diff expect actual && + + echo "$sss$sss$sss" | git-stripspace >actual && + git diff expect actual && + + echo "$sss$sss$sss$sss" | git-stripspace >actual && + git diff expect actual +' + +test_expect_failure \ + 'spaces without newline at end should not show spaces' ' + printf "" | git-stripspace | grep -q " " || + printf "$sss" | git-stripspace | grep -q " " || + printf "$sss$sss" | git-stripspace | grep -q " " || + printf "$sss$sss$sss" | git-stripspace | grep -q " " || + printf "$sss$sss$sss$sss" | git-stripspace | grep -q " " +' + +test_expect_success \ + 'spaces without newline at end should be replaced with empty string' ' + printf "" >expect && + + printf "" | git-stripspace >actual && + git diff expect actual + + printf "$sss$sss" | git-stripspace >actual && + git diff expect actual + + printf "$sss$sss$sss" | git-stripspace >actual && + git diff expect actual + + printf "$sss$sss$sss$sss" | git-stripspace >actual && + git diff expect actual +' + +test_done diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 9443b875e2..a2c11c4639 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -575,25 +575,23 @@ cat > .git/config <<\EOF EOF cat > expect <<\EOF -Key: section.sub=section.val1 -Value: foo=bar -Key: section.sub=section.val2 -Value: foo -bar -Key: section.sub=section.val3 -Value: +section.sub=section.val1 +foo=barQsection.sub=section.val2 +foo +barQsection.sub=section.val3 -Key: section.sub=section.val4 -Value: -Key: section.sub=section.val5 +Qsection.sub=section.val4 +Qsection.sub=section.val5Q EOF -git config --null --list | perl -0ne 'chop;($key,$value)=split(/\n/,$_,2);print "Key: $key\n";print "Value: $value\n" if defined($value)' > result +git config --null --list | tr '[\000]' 'Q' > result +echo >>result test_expect_success '--null --list' 'cmp result expect' -git config --null --get-regexp 'val[0-9]' | perl -0ne 'chop;($key,$value)=split(/\n/,$_,2);print "Key: $key\n";print "Value: $value\n" if defined($value)' > result +git config --null --get-regexp 'val[0-9]' | tr '[\000]' 'Q' > result +echo >>result test_expect_success '--null --get-regexp' 'cmp result expect' diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh new file mode 100755 index 0000000000..5d15449be5 --- /dev/null +++ b/t/t7004-tag.sh @@ -0,0 +1,686 @@ +#!/bin/sh +# +# Copyright (c) 2007 Carlos Rica +# + +test_description='git-tag + +Basic tests for operations with tags.' + +. ./test-lib.sh + +# creating and listing lightweight tags: + +tag_exists () { + git show-ref --quiet --verify refs/tags/"$1" +} + +# todo: git tag -l now returns always zero, when fixed, change this test +test_expect_success 'listing all tags in an empty tree should succeed' \ + 'git tag -l' + +test_expect_success 'listing all tags in an empty tree should output nothing' \ + 'test `git-tag -l | wc -l` -eq 0' + +test_expect_failure 'looking for a tag in an empty tree should fail' \ + 'tag_exists mytag' + +test_expect_success 'creating a tag in an empty tree should fail' ' + ! git-tag mynotag && + ! tag_exists mynotag +' + +test_expect_success 'creating a tag for HEAD in an empty tree should fail' ' + ! git-tag mytaghead HEAD && + ! tag_exists mytaghead +' + +test_expect_success 'creating a tag for an unknown revision should fail' ' + ! git-tag mytagnorev aaaaaaaaaaa && + ! tag_exists mytagnorev +' + +# commit used in the tests, test_tick is also called here to freeze the date: +test_expect_success 'creating a tag using default HEAD should succeed' ' + test_tick && + echo foo >foo && + git add foo && + git commit -m Foo && + git tag mytag +' + +test_expect_success 'listing all tags if one exists should succeed' \ + 'git-tag -l' + +test_expect_success 'listing all tags if one exists should output that tag' \ + 'test `git-tag -l` = mytag' + +# pattern matching: + +test_expect_success 'listing a tag using a matching pattern should succeed' \ + 'git-tag -l mytag' + +test_expect_success \ + 'listing a tag using a matching pattern should output that tag' \ + 'test `git-tag -l mytag` = mytag' + +# todo: git tag -l now returns always zero, when fixed, change this test +test_expect_success \ + 'listing tags using a non-matching pattern should suceed' \ + 'git-tag -l xxx' + +test_expect_success \ + 'listing tags using a non-matching pattern should output nothing' \ + 'test `git-tag -l xxx | wc -l` -eq 0' + +# special cases for creating tags: + +test_expect_failure \ + 'trying to create a tag with the name of one existing should fail' \ + 'git tag mytag' + +test_expect_success \ + 'trying to create a tag with a non-valid name should fail' ' + test `git-tag -l | wc -l` -eq 1 && + ! git tag "" && + ! git tag .othertag && + ! git tag "other tag" && + ! git tag "othertag^" && + ! git tag "other~tag" && + test `git-tag -l | wc -l` -eq 1 +' + +test_expect_success 'creating a tag using HEAD directly should succeed' ' + git tag myhead HEAD && + tag_exists myhead +' + +# deleting tags: + +test_expect_success 'trying to delete an unknown tag should fail' ' + ! tag_exists unknown-tag && + ! git-tag -d unknown-tag +' + +cat >expect < actual && git diff expect actual && + git-tag -d && + git tag -l > actual && git diff expect actual +' + +test_expect_success \ + 'deleting two existing tags in one command should succeed' ' + tag_exists mytag && + tag_exists myhead && + git-tag -d mytag myhead && + ! tag_exists mytag && + ! tag_exists myhead +' + +test_expect_success \ + 'creating a tag with the name of another deleted one should succeed' ' + ! tag_exists mytag && + git-tag mytag && + tag_exists mytag +' + +test_expect_success \ + 'trying to delete two tags, existing and not, should fail in the 2nd' ' + tag_exists mytag && + ! tag_exists myhead && + ! git-tag -d mytag anothertag && + ! tag_exists mytag && + ! tag_exists myhead +' + +test_expect_failure 'trying to delete an already deleted tag should fail' \ + 'git-tag -d mytag' + +# listing various tags with pattern matching: + +cat >expect < actual + git diff expect actual +' + +cat >expect < actual && + git-diff expect actual +' + +cat >expect < actual && + git-diff expect actual +' + +cat >expect < actual && + git-diff expect actual +' + +cat >expect < actual && + git-diff expect actual +' + +cat >expect < actual && + git-diff expect actual +' + +cat >expect < actual && + git-diff expect actual +' + +>expect +test_expect_success \ + 'listing tags using v.* should print nothing because none have v.' ' + git-tag -l "v.*" > actual && + git-diff expect actual +' + +cat >expect < actual && + git-diff expect actual +' + +# creating and verifying lightweight tags: + +test_expect_success \ + 'a non-annotated tag created without parameters should point to HEAD' ' + git-tag non-annotated-tag && + test $(git-cat-file -t non-annotated-tag) = commit && + test $(git-rev-parse non-annotated-tag) = $(git-rev-parse HEAD) +' + +test_expect_failure 'trying to verify an unknown tag should fail' \ + 'git-tag -v unknown-tag' + +test_expect_failure \ + 'trying to verify a non-annotated and non-signed tag should fail' \ + 'git-tag -v non-annotated-tag' + +# creating annotated tags: + +get_tag_msg () { + git cat-file tag "$1" | sed -e "/BEGIN PGP/q" +} + +# run test_tick before committing always gives the time in that timezone +get_tag_header () { +cat < $4 -0700 + +EOF +} + +commit=$(git rev-parse HEAD) +time=$test_tick + +get_tag_header annotated-tag $commit commit $time >expect +echo "A message" >>expect +test_expect_success \ + 'creating an annotated tag with -m message should succeed' ' + git-tag -m "A message" annotated-tag && + get_tag_msg annotated-tag >actual && + git diff expect actual +' + +cat >msgfile <expect +cat msgfile >>expect +test_expect_success \ + 'creating an annotated tag with -F messagefile should succeed' ' + git-tag -F msgfile file-annotated-tag && + get_tag_msg file-annotated-tag >actual && + git diff expect actual +' + +# blank and empty messages: + +get_tag_header empty-annotated-tag $commit commit $time >expect +test_expect_success \ + 'creating a tag with an empty -m message should succeed' ' + git-tag -m "" empty-annotated-tag && + get_tag_msg empty-annotated-tag >actual && + git diff expect actual +' + +>emptyfile +get_tag_header emptyfile-annotated-tag $commit commit $time >expect +test_expect_success \ + 'creating a tag with an empty -F messagefile should succeed' ' + git-tag -F emptyfile emptyfile-annotated-tag && + get_tag_msg emptyfile-annotated-tag >actual && + git diff expect actual +' + +printf '\n\n \n\t\nLeading blank lines\n' >blanksfile +printf '\n\t \t \nRepeated blank lines\n' >>blanksfile +printf '\n\n\nTrailing spaces \t \n' >>blanksfile +printf '\nTrailing blank lines\n\n\t \n\n' >>blanksfile +get_tag_header blanks-annotated-tag $commit commit $time >expect +cat >>expect <actual && + git diff expect actual +' + +get_tag_header blank-annotated-tag $commit commit $time >expect +test_expect_success \ + 'creating a tag with blank -m message with spaces should succeed' ' + git-tag -m " " blank-annotated-tag && + get_tag_msg blank-annotated-tag >actual && + git diff expect actual +' + +echo ' ' >blankfile +echo '' >>blankfile +echo ' ' >>blankfile +get_tag_header blankfile-annotated-tag $commit commit $time >expect +test_expect_success \ + 'creating a tag with blank -F messagefile with spaces should succeed' ' + git-tag -F blankfile blankfile-annotated-tag && + get_tag_msg blankfile-annotated-tag >actual && + git diff expect actual +' + +printf ' ' >blanknonlfile +get_tag_header blanknonlfile-annotated-tag $commit commit $time >expect +test_expect_success \ + 'creating a tag with -F file of spaces and no newline should succeed' ' + git-tag -F blanknonlfile blanknonlfile-annotated-tag && + get_tag_msg blanknonlfile-annotated-tag >actual && + git diff expect actual +' + +# messages with commented lines: + +cat >commentsfile <expect +cat >>expect <actual && + git diff expect actual +' + +get_tag_header comment-annotated-tag $commit commit $time >expect +test_expect_success \ + 'creating a tag with a #comment in the -m message should succeed' ' + git-tag -m "#comment" comment-annotated-tag && + get_tag_msg comment-annotated-tag >actual && + git diff expect actual +' + +echo '#comment' >commentfile +echo '' >>commentfile +echo '####' >>commentfile +get_tag_header commentfile-annotated-tag $commit commit $time >expect +test_expect_success \ + 'creating a tag with #comments in the -F messagefile should succeed' ' + git-tag -F commentfile commentfile-annotated-tag && + get_tag_msg commentfile-annotated-tag >actual && + git diff expect actual +' + +printf '#comment' >commentnonlfile +get_tag_header commentnonlfile-annotated-tag $commit commit $time >expect +test_expect_success \ + 'creating a tag with a file of #comment and no newline should succeed' ' + git-tag -F commentnonlfile commentnonlfile-annotated-tag && + get_tag_msg commentnonlfile-annotated-tag >actual && + git diff expect actual +' + +# trying to verify annotated non-signed tags: + +test_expect_success \ + 'trying to verify an annotated non-signed tag should fail' ' + tag_exists annotated-tag && + ! git-tag -v annotated-tag +' + +test_expect_success \ + 'trying to verify a file-annotated non-signed tag should fail' ' + tag_exists file-annotated-tag && + ! git-tag -v file-annotated-tag +' + +# creating and verifying signed tags: + +gpg --version >/dev/null +if [ $? -eq 127 ]; then + echo "Skipping signed tags tests, because gpg was not found" + test_done + exit +fi + +# key generation info: gpg --homedir t/t7004 --gen-key +# Type DSA and Elgamal, size 2048 bits, no expiration date. +# Name and email: C O Mitter +# No password given, to enable non-interactive operation. + +cp -R ../t7004 ./gpghome +chmod 0700 gpghome +export GNUPGHOME="$(pwd)/gpghome" + +get_tag_header signed-tag $commit commit $time >expect +echo 'A signed tag message' >>expect +echo '-----BEGIN PGP SIGNATURE-----' >>expect +test_expect_success 'creating a signed tag with -m message should succeed' ' + git-tag -s -m "A signed tag message" signed-tag && + get_tag_msg signed-tag >actual && + git-diff expect actual +' + +test_expect_success 'verifying a signed tag should succeed' \ + 'git-tag -v signed-tag' + +test_expect_success 'verifying a forged tag should fail' ' + forged=$(git cat-file tag signed-tag | + sed -e "s/signed-tag/forged-tag/" | + git mktag) && + git tag forged-tag $forged && + ! git-tag -v forged-tag +' + +# blank and empty messages for signed tags: + +get_tag_header empty-signed-tag $commit commit $time >expect +echo '-----BEGIN PGP SIGNATURE-----' >>expect +test_expect_success \ + 'creating a signed tag with an empty -m message should succeed' ' + git-tag -s -m "" empty-signed-tag && + get_tag_msg empty-signed-tag >actual && + git diff expect actual && + git-tag -v empty-signed-tag +' + +>sigemptyfile +get_tag_header emptyfile-signed-tag $commit commit $time >expect +echo '-----BEGIN PGP SIGNATURE-----' >>expect +test_expect_success \ + 'creating a signed tag with an empty -F messagefile should succeed' ' + git-tag -s -F sigemptyfile emptyfile-signed-tag && + get_tag_msg emptyfile-signed-tag >actual && + git diff expect actual && + git-tag -v emptyfile-signed-tag +' + +printf '\n\n \n\t\nLeading blank lines\n' > sigblanksfile +printf '\n\t \t \nRepeated blank lines\n' >>sigblanksfile +printf '\n\n\nTrailing spaces \t \n' >>sigblanksfile +printf '\nTrailing blank lines\n\n\t \n\n' >>sigblanksfile +get_tag_header blanks-signed-tag $commit commit $time >expect +cat >>expect <>expect +test_expect_success \ + 'extra blanks in the message for a signed tag should be removed' ' + git-tag -s -F sigblanksfile blanks-signed-tag && + get_tag_msg blanks-signed-tag >actual && + git diff expect actual && + git-tag -v blanks-signed-tag +' + +get_tag_header blank-signed-tag $commit commit $time >expect +echo '-----BEGIN PGP SIGNATURE-----' >>expect +test_expect_success \ + 'creating a signed tag with a blank -m message should succeed' ' + git-tag -s -m " " blank-signed-tag && + get_tag_msg blank-signed-tag >actual && + git diff expect actual && + git-tag -v blank-signed-tag +' + +echo ' ' >sigblankfile +echo '' >>sigblankfile +echo ' ' >>sigblankfile +get_tag_header blankfile-signed-tag $commit commit $time >expect +echo '-----BEGIN PGP SIGNATURE-----' >>expect +test_expect_success \ + 'creating a signed tag with blank -F file with spaces should succeed' ' + git-tag -s -F sigblankfile blankfile-signed-tag && + get_tag_msg blankfile-signed-tag >actual && + git diff expect actual && + git-tag -v blankfile-signed-tag +' + +printf ' ' >sigblanknonlfile +get_tag_header blanknonlfile-signed-tag $commit commit $time >expect +echo '-----BEGIN PGP SIGNATURE-----' >>expect +test_expect_success \ + 'creating a signed tag with spaces and no newline should succeed' ' + git-tag -s -F sigblanknonlfile blanknonlfile-signed-tag && + get_tag_msg blanknonlfile-signed-tag >actual && + git diff expect actual && + git-tag -v signed-tag +' + +# messages with commented lines for signed tags: + +cat >sigcommentsfile <expect +cat >>expect <>expect +test_expect_success \ + 'creating a signed tag with a -F file with #comments should succeed' ' + git-tag -s -F sigcommentsfile comments-signed-tag && + get_tag_msg comments-signed-tag >actual && + git diff expect actual && + git-tag -v comments-signed-tag +' + +get_tag_header comment-signed-tag $commit commit $time >expect +echo '-----BEGIN PGP SIGNATURE-----' >>expect +test_expect_success \ + 'creating a signed tag with #commented -m message should succeed' ' + git-tag -s -m "#comment" comment-signed-tag && + get_tag_msg comment-signed-tag >actual && + git diff expect actual && + git-tag -v comment-signed-tag +' + +echo '#comment' >sigcommentfile +echo '' >>sigcommentfile +echo '####' >>sigcommentfile +get_tag_header commentfile-signed-tag $commit commit $time >expect +echo '-----BEGIN PGP SIGNATURE-----' >>expect +test_expect_success \ + 'creating a signed tag with #commented -F messagefile should succeed' ' + git-tag -s -F sigcommentfile commentfile-signed-tag && + get_tag_msg commentfile-signed-tag >actual && + git diff expect actual && + git-tag -v commentfile-signed-tag +' + +printf '#comment' >sigcommentnonlfile +get_tag_header commentnonlfile-signed-tag $commit commit $time >expect +echo '-----BEGIN PGP SIGNATURE-----' >>expect +test_expect_success \ + 'creating a signed tag with a #comment and no newline should succeed' ' + git-tag -s -F sigcommentnonlfile commentnonlfile-signed-tag && + get_tag_msg commentnonlfile-signed-tag >actual && + git diff expect actual && + git-tag -v commentnonlfile-signed-tag +' + +# tags pointing to objects different from commits: + +tree=$(git rev-parse HEAD^{tree}) +blob=$(git rev-parse HEAD:foo) +tag=$(git rev-parse signed-tag) + +get_tag_header tree-signed-tag $tree tree $time >expect +echo "A message for a tree" >>expect +echo '-----BEGIN PGP SIGNATURE-----' >>expect +test_expect_success \ + 'creating a signed tag pointing to a tree should succeed' ' + git-tag -s -m "A message for a tree" tree-signed-tag HEAD^{tree} && + get_tag_msg tree-signed-tag >actual && + git diff expect actual +' + +get_tag_header blob-signed-tag $blob blob $time >expect +echo "A message for a blob" >>expect +echo '-----BEGIN PGP SIGNATURE-----' >>expect +test_expect_success \ + 'creating a signed tag pointing to a blob should succeed' ' + git-tag -s -m "A message for a blob" blob-signed-tag HEAD:foo && + get_tag_msg blob-signed-tag >actual && + git diff expect actual +' + +get_tag_header tag-signed-tag $tag tag $time >expect +echo "A message for another tag" >>expect +echo '-----BEGIN PGP SIGNATURE-----' >>expect +test_expect_success \ + 'creating a signed tag pointing to another tag should succeed' ' + git-tag -s -m "A message for another tag" tag-signed-tag signed-tag && + get_tag_msg tag-signed-tag >actual && + git diff expect actual +' + +# try to verify without gpg: + +rm -rf gpghome +test_expect_failure \ + 'verify signed tag fails when public key is not present' \ + 'git-tag -v signed-tag' + +test_done diff --git a/t/t7004/pubring.gpg b/t/t7004/pubring.gpg new file mode 100644 index 0000000000..83855fa4e1 Binary files /dev/null and b/t/t7004/pubring.gpg differ diff --git a/t/t7004/random_seed b/t/t7004/random_seed new file mode 100644 index 0000000000..8fed1339ed Binary files /dev/null and b/t/t7004/random_seed differ diff --git a/t/t7004/secring.gpg b/t/t7004/secring.gpg new file mode 100644 index 0000000000..d831cd9eb3 Binary files /dev/null and b/t/t7004/secring.gpg differ diff --git a/write_or_die.c b/write_or_die.c index 5c4bc8515a..e125e11d3b 100644 --- a/write_or_die.c +++ b/write_or_die.c @@ -1,5 +1,45 @@ #include "cache.h" +/* + * Some cases use stdio, but want to flush after the write + * to get error handling (and to get better interactive + * behaviour - not buffering excessively). + * + * Of course, if the flush happened within the write itself, + * we've already lost the error code, and cannot report it any + * more. So we just ignore that case instead (and hope we get + * the right error code on the flush). + * + * If the file handle is stdout, and stdout is a file, then skip the + * flush entirely since it's not needed. + */ +void maybe_flush_or_die(FILE *f, const char *desc) +{ + static int skip_stdout_flush = -1; + struct stat st; + char *cp; + + if (f == stdout) { + if (skip_stdout_flush < 0) { + cp = getenv("GIT_FLUSH"); + if (cp) + skip_stdout_flush = (atoi(cp) == 0); + else if ((fstat(fileno(stdout), &st) == 0) && + S_ISREG(st.st_mode)) + skip_stdout_flush = 1; + else + skip_stdout_flush = 0; + } + if (skip_stdout_flush && !ferror(f)) + return; + } + if (fflush(f)) { + if (errno == EPIPE) + exit(0); + die("write failure on %s: %s", desc, strerror(errno)); + } +} + int read_in_full(int fd, void *buf, size_t count) { char *p = buf;