aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <junkio@cox.net>2006-10-01 00:34:58 -0700
committerJunio C Hamano <junkio@cox.net>2006-10-01 00:34:58 -0700
commit12f9b8a3152ff9fa48464187a83f7c6279fe5094 (patch)
tree8eac7b25900e69c2b332f39ad720698982de66c7
parent2eaf22242f61b13c38c87cbb0e84c84974c52d66 (diff)
parentba0ac36ec5708820e670731001f7ab35351c6c48 (diff)
downloadgit-12f9b8a3152ff9fa48464187a83f7c6279fe5094.tar.gz
git-12f9b8a3152ff9fa48464187a83f7c6279fe5094.tar.xz
Merge branch 'sb/fetch' into jc/refs-and-fetch
* sb/fetch: (41 commits) merge and resolve: Output short hashes and .. in "Updating ..." fetch: Misc output cleanup gitweb: tree view: hash_base and hash are now context sensitive fetch: Reset remote refs list each time fetch_main is called Fix approxidate() to understand 12:34 AM/PM are 00:34 and 12:34 git-diff -B output fix. Make cvsexportcommit remove files. diff --stat: ensure at least one '-' for deletions, and one '+' for additions diff --stat=width[,name-width]: allow custom diffstat output width. gitweb: History: blob and tree are first, then commitdiff, etc gitweb: Remove redundant "commit" from history http/ftp: optionally ask curl to not use EPSV command gitweb: Don't use quotemeta on internally generated strings gitweb: Add snapshot to shortlog gitweb: Factor out gitweb_have_snapshot() gitweb: Remove redundant "commit" link from shortlog gitweb: "alternate" starts with shade (i.e. 1) git-format-patch: fix bug using -o in subdirectories do not discard constness in interp_set_entry value argument Fix approxidate() to understand more extended numbers ...
-rw-r--r--Documentation/config.txt6
-rw-r--r--Documentation/diff-options.txt7
-rw-r--r--Documentation/git-daemon.txt43
-rw-r--r--Makefile3
-rw-r--r--builtin-log.c3
-rw-r--r--compat/inet_pton.c220
-rwxr-xr-xcontrib/completion/git-completion.bash324
-rw-r--r--daemon.c179
-rw-r--r--date.c63
-rw-r--r--diff.c168
-rw-r--r--diff.h3
-rwxr-xr-xgit-clone.sh4
-rwxr-xr-xgit-cvsexportcommit.perl2
-rwxr-xr-xgit-fetch.sh48
-rwxr-xr-xgit-ls-remote.sh4
-rwxr-xr-xgit-merge.sh2
-rwxr-xr-xgit-resolve.sh2
-rwxr-xr-xgit-svnimport.perl12
-rwxr-xr-xgitweb/gitweb.perl323
-rw-r--r--http.c12
-rw-r--r--interpolate.c26
-rw-r--r--interpolate.h3
22 files changed, 1202 insertions, 255 deletions
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 98c1f3e2e..84e38911e 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -202,6 +202,12 @@ http.lowSpeedLimit, http.lowSpeedTime::
Can be overridden by the 'GIT_HTTP_LOW_SPEED_LIMIT' and
'GIT_HTTP_LOW_SPEED_TIME' environment variables.
+http.noEPSV::
+ A boolean which disables using of EPSV ftp command by curl.
+ This can helpful with some "poor" ftp servers which doesn't
+ support EPSV mode. Can be overridden by the 'GIT_CURL_FTP_NO_EPSV'
+ environment variable. Default is false (curl will use EPSV).
+
i18n.commitEncoding::
Character encoding the commit messages are stored in; git itself
does not care per se, but this information is necessary e.g. when
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index b5d976359..7b7b9e8ce 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -10,8 +10,11 @@
--patch-with-raw::
Synonym for "-p --raw".
---stat::
- Generate a diffstat.
+--stat[=width[,name-width]]::
+ Generate a diffstat. You can override the default
+ output width for 80-column terminal by "--stat=width".
+ The width of the filename part can be controlled by
+ giving another width to it separated by a comma.
--summary::
Output a condensed summary of extended header information
diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index 51d7c94d7..d562232e5 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -8,14 +8,15 @@ git-daemon - A really simple server for git repositories
SYNOPSIS
--------
[verse]
-'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all]
+'git-daemon' [--verbose] [--syslog] [--export-all]
[--timeout=n] [--init-timeout=n] [--strict-paths]
[--base-path=path] [--user-path | --user-path=path]
[--interpolated-path=pathtemplate]
+ [--reuseaddr] [--detach] [--pid-file=file]
[--enable=service] [--disable=service]
[--allow-override=service] [--forbid-override=service]
- [--reuseaddr] [--detach] [--pid-file=file]
- [--user=user [--group=group]] [directory...]
+ [--inetd | [--listen=host_or_ipaddr] [--port=n] [--user=user [--group=group]]
+ [directory...]
DESCRIPTION
-----------
@@ -54,8 +55,12 @@ OPTIONS
--interpolated-path=pathtemplate::
To support virtual hosting, an interpolated path template can be
used to dynamically construct alternate paths. The template
- supports %H for the target hostname as supplied by the client,
+ supports %H for the target hostname as supplied by the client but
+ converted to all lowercase, %CH for the canonical hostname,
+ %IP for the server's IP address, %P for the port number,
and %D for the absolute path of the named repository.
+ After interpolation, the path is validated against the directory
+ whitelist.
--export-all::
Allow pulling from all directories that look like GIT repositories
@@ -64,9 +69,17 @@ OPTIONS
--inetd::
Have the server run as an inetd service. Implies --syslog.
+ Incompatible with --port, --listen, --user and --group options.
+
+--listen=host_or_ipaddr::
+ Listen on an a specific IP address or hostname. IP addresses can
+ be either an IPv4 address or an IPV6 address if supported. If IPv6
+ is not supported, then --listen=hostname is also not supported and
+ --listen must be given an IPv4 address.
+ Incompatible with '--inetd' option.
---port::
- Listen on an alternative port.
+--port=n::
+ Listen on an alternative port. Incompatible with '--inetd' option.
--init-timeout::
Timeout between the moment the connection is established and the
@@ -182,6 +195,24 @@ clients, a symlink from `/software` into the appropriate
default repository could be made as well.
+git-daemon as regular daemon for virtual hosts::
+ To set up `git-daemon` as a regular, non-inetd service that
+ handles repositories for multiple virtual hosts based on
+ their IP addresses, start the daemon like this:
++
+------------------------------------------------
+ git-daemon --verbose --export-all
+ --interpolated-path=/pub/%IP/%D
+ /pub/192.168.1.200/software
+ /pub/10.10.220.23/software
+------------------------------------------------
++
+In this example, the root-level directory `/pub` will contain
+a subdirectory for each virtual host IP address supported.
+Repositories can still be accessed by hostname though, assuming
+they correspond to these IP addresses.
+
+
Author
------
Written by Linus Torvalds <torvalds@osdl.org>, YOSHIFUJI Hideaki
diff --git a/Makefile b/Makefile
index 65fb8284f..efcf3fb29 100644
--- a/Makefile
+++ b/Makefile
@@ -525,6 +525,9 @@ endif
ifdef NO_INET_NTOP
LIB_OBJS += compat/inet_ntop.o
endif
+ifdef NO_INET_PTON
+ LIB_OBJS += compat/inet_pton.o
+endif
ifdef NO_ICONV
ALL_CFLAGS += -DNO_ICONV
diff --git a/builtin-log.c b/builtin-log.c
index fbc58bbca..9d1ceae44 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -348,6 +348,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (!rev.diffopt.output_format)
rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;
+ if (!output_directory)
+ output_directory = prefix;
+
if (output_directory) {
if (use_stdout)
die("standard output, or directory, which one?");
diff --git a/compat/inet_pton.c b/compat/inet_pton.c
new file mode 100644
index 000000000..5704e0d2b
--- /dev/null
+++ b/compat/inet_pton.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 1996-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifndef NS_INT16SZ
+#define NS_INT16SZ 2
+#endif
+
+#ifndef NS_INADDRSZ
+#define NS_INADDRSZ 4
+#endif
+
+#ifndef NS_IN6ADDRSZ
+#define NS_IN6ADDRSZ 16
+#endif
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static int inet_pton4(const char *src, unsigned char *dst);
+static int inet_pton6(const char *src, unsigned char *dst);
+
+/* int
+ * inet_pton4(src, dst)
+ * like inet_aton() but without all the hexadecimal and shorthand.
+ * return:
+ * 1 if `src' is a valid dotted quad, else 0.
+ * notice:
+ * does not touch `dst' unless it's returning 1.
+ * author:
+ * Paul Vixie, 1996.
+ */
+static int
+inet_pton4(const char *src, unsigned char *dst)
+{
+ static const char digits[] = "0123456789";
+ int saw_digit, octets, ch;
+ unsigned char tmp[NS_INADDRSZ], *tp;
+
+ saw_digit = 0;
+ octets = 0;
+ *(tp = tmp) = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr(digits, ch)) != NULL) {
+ unsigned int new = *tp * 10 + (pch - digits);
+
+ if (new > 255)
+ return (0);
+ *tp = new;
+ if (! saw_digit) {
+ if (++octets > 4)
+ return (0);
+ saw_digit = 1;
+ }
+ } else if (ch == '.' && saw_digit) {
+ if (octets == 4)
+ return (0);
+ *++tp = 0;
+ saw_digit = 0;
+ } else
+ return (0);
+ }
+ if (octets < 4)
+ return (0);
+ memcpy(dst, tmp, NS_INADDRSZ);
+ return (1);
+}
+
+/* int
+ * inet_pton6(src, dst)
+ * convert presentation level address to network order binary form.
+ * return:
+ * 1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ * (1) does not touch `dst' unless it's returning 1.
+ * (2) :: in a full address is silently ignored.
+ * credit:
+ * inspired by Mark Andrews.
+ * author:
+ * Paul Vixie, 1996.
+ */
+
+#ifndef NO_IPV6
+static int
+inet_pton6(const char *src, unsigned char *dst)
+{
+ static const char xdigits_l[] = "0123456789abcdef",
+ xdigits_u[] = "0123456789ABCDEF";
+ unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
+ const char *xdigits, *curtok;
+ int ch, saw_xdigit;
+ unsigned int val;
+
+ memset((tp = tmp), '\0', NS_IN6ADDRSZ);
+ endp = tp + NS_IN6ADDRSZ;
+ colonp = NULL;
+ /* Leading :: requires some special handling. */
+ if (*src == ':')
+ if (*++src != ':')
+ return (0);
+ curtok = src;
+ saw_xdigit = 0;
+ val = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+ pch = strchr((xdigits = xdigits_u), ch);
+ if (pch != NULL) {
+ val <<= 4;
+ val |= (pch - xdigits);
+ if (val > 0xffff)
+ return (0);
+ saw_xdigit = 1;
+ continue;
+ }
+ if (ch == ':') {
+ curtok = src;
+ if (!saw_xdigit) {
+ if (colonp)
+ return (0);
+ colonp = tp;
+ continue;
+ }
+ if (tp + NS_INT16SZ > endp)
+ return (0);
+ *tp++ = (unsigned char) (val >> 8) & 0xff;
+ *tp++ = (unsigned char) val & 0xff;
+ saw_xdigit = 0;
+ val = 0;
+ continue;
+ }
+ if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
+ inet_pton4(curtok, tp) > 0) {
+ tp += NS_INADDRSZ;
+ saw_xdigit = 0;
+ break; /* '\0' was seen by inet_pton4(). */
+ }
+ return (0);
+ }
+ if (saw_xdigit) {
+ if (tp + NS_INT16SZ > endp)
+ return (0);
+ *tp++ = (unsigned char) (val >> 8) & 0xff;
+ *tp++ = (unsigned char) val & 0xff;
+ }
+ if (colonp != NULL) {
+ /*
+ * Since some memmove()'s erroneously fail to handle
+ * overlapping regions, we'll do the shift by hand.
+ */
+ const int n = tp - colonp;
+ int i;
+
+ for (i = 1; i <= n; i++) {
+ endp[- i] = colonp[n - i];
+ colonp[n - i] = 0;
+ }
+ tp = endp;
+ }
+ if (tp != endp)
+ return (0);
+ memcpy(dst, tmp, NS_IN6ADDRSZ);
+ return (1);
+}
+#endif
+
+/* int
+ * isc_net_pton(af, src, dst)
+ * convert from presentation format (which usually means ASCII printable)
+ * to network format (which is usually some kind of binary format).
+ * return:
+ * 1 if the address was valid for the specified address family
+ * 0 if the address wasn't valid (`dst' is untouched in this case)
+ * -1 if some other error occurred (`dst' is untouched in this case, too)
+ * author:
+ * Paul Vixie, 1996.
+ */
+int
+inet_pton(int af, const char *src, void *dst)
+{
+ switch (af) {
+ case AF_INET:
+ return (inet_pton4(src, dst));
+#ifndef NO_IPV6
+ case AF_INET6:
+ return (inet_pton6(src, dst));
+#endif
+ default:
+ errno = EAFNOSUPPORT;
+ return (-1);
+ }
+ /* NOTREACHED */
+}
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
new file mode 100755
index 000000000..d9cb17d0b
--- /dev/null
+++ b/contrib/completion/git-completion.bash
@@ -0,0 +1,324 @@
+#
+# bash completion support for core Git.
+#
+# Copyright (C) 2006 Shawn Pearce
+# Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/).
+#
+# The contained completion routines provide support for completing:
+#
+# *) local and remote branch names
+# *) local and remote tag names
+# *) .git/remotes file names
+# *) git 'subcommands'
+# *) tree paths within 'ref:path/to/file' expressions
+#
+# To use these routines:
+#
+# 1) Copy this file to somewhere (e.g. ~/.git-completion.sh).
+# 2) Added the following line to your .bashrc:
+# source ~/.git-completion.sh
+#
+
+__git_refs ()
+{
+ local cmd i is_hash=y
+ if [ -d "$1" ]; then
+ cmd=git-peek-remote
+ else
+ cmd=git-ls-remote
+ fi
+ for i in $($cmd "$1" 2>/dev/null); do
+ case "$is_hash,$i" in
+ y,*) is_hash=n ;;
+ n,*^{}) is_hash=y ;;
+ n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;;
+ n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
+ n,*) is_hash=y; echo "$i" ;;
+ esac
+ done
+}
+
+__git_refs2 ()
+{
+ local cmd i is_hash=y
+ if [ -d "$1" ]; then
+ cmd=git-peek-remote
+ else
+ cmd=git-ls-remote
+ fi
+ for i in $($cmd "$1" 2>/dev/null); do
+ case "$is_hash,$i" in
+ y,*) is_hash=n ;;
+ n,*^{}) is_hash=y ;;
+ n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}:${i#refs/tags/}" ;;
+ n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}:${i#refs/heads/}" ;;
+ n,*) is_hash=y; echo "$i:$i" ;;
+ esac
+ done
+}
+
+__git_remotes ()
+{
+ local i REVERTGLOB=$(shopt -p nullglob)
+ shopt -s nullglob
+ for i in .git/remotes/*; do
+ echo ${i#.git/remotes/}
+ done
+ $REVERTGLOB
+}
+
+__git_complete_file ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ case "$cur" in
+ ?*:*)
+ local pfx ls ref="$(echo "$cur" | sed 's,:.*$,,')"
+ cur="$(echo "$cur" | sed 's,^.*:,,')"
+ case "$cur" in
+ ?*/*)
+ pfx="$(echo "$cur" | sed 's,/[^/]*$,,')"
+ cur="$(echo "$cur" | sed 's,^.*/,,')"
+ ls="$ref:$pfx"
+ pfx="$pfx/"
+ ;;
+ *)
+ ls="$ref"
+ ;;
+ esac
+ COMPREPLY=($(compgen -P "$pfx" \
+ -W "$(git-ls-tree "$ls" \
+ | sed '/^100... blob /s,^.* ,,
+ /^040000 tree /{
+ s,^.* ,,
+ s,$,/,
+ }
+ s/^.* //')" \
+ -- "$cur"))
+ ;;
+ *)
+ COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur"))
+ ;;
+ esac
+}
+
+_git_branch ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ COMPREPLY=($(compgen -W "-l -f -d -D $(__git_refs .)" -- "$cur"))
+}
+
+_git_cat_file ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ case "${COMP_WORDS[0]},$COMP_CWORD" in
+ git-cat-file*,1)
+ COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur"))
+ ;;
+ git,2)
+ COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur"))
+ ;;
+ *)
+ __git_complete_file
+ ;;
+ esac
+}
+
+_git_checkout ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ COMPREPLY=($(compgen -W "-l -b $(__git_refs .)" -- "$cur"))
+}
+
+_git_diff ()
+{
+ __git_complete_file
+}
+
+_git_diff_tree ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ COMPREPLY=($(compgen -W "-r -p -M $(__git_refs .)" -- "$cur"))
+}
+
+_git_fetch ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+
+ case "${COMP_WORDS[0]},$COMP_CWORD" in
+ git-fetch*,1)
+ COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
+ ;;
+ git,2)
+ COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
+ ;;
+ *)
+ case "$cur" in
+ *:*)
+ cur=$(echo "$cur" | sed 's/^.*://')
+ COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur"))
+ ;;
+ *)
+ local remote
+ case "${COMP_WORDS[0]}" in
+ git-fetch) remote="${COMP_WORDS[1]}" ;;
+ git) remote="${COMP_WORDS[2]}" ;;
+ esac
+ COMPREPLY=($(compgen -W "$(__git_refs2 "$remote")" -- "$cur"))
+ ;;
+ esac
+ ;;
+ esac
+}
+
+_git_ls_remote ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
+}
+
+_git_ls_tree ()
+{
+ __git_complete_file
+}
+
+_git_log ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ case "$cur" in
+ *..*)
+ local pfx=$(echo "$cur" | sed 's/\.\..*$/../')
+ cur=$(echo "$cur" | sed 's/^.*\.\.//')
+ COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs .)" -- "$cur"))
+ ;;
+ *)
+ COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur"))
+ ;;
+ esac
+}
+
+_git_merge_base ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur"))
+}
+
+_git_pull ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+
+ case "${COMP_WORDS[0]},$COMP_CWORD" in
+ git-pull*,1)
+ COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
+ ;;
+ git,2)
+ COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
+ ;;
+ *)
+ local remote
+ case "${COMP_WORDS[0]}" in
+ git-pull) remote="${COMP_WORDS[1]}" ;;
+ git) remote="${COMP_WORDS[2]}" ;;
+ esac
+ COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur"))
+ ;;
+ esac
+}
+
+_git_push ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+
+ case "${COMP_WORDS[0]},$COMP_CWORD" in
+ git-push*,1)
+ COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
+ ;;
+ git,2)
+ COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
+ ;;
+ *)
+ case "$cur" in
+ *:*)
+ local remote
+ case "${COMP_WORDS[0]}" in
+ git-push) remote="${COMP_WORDS[1]}" ;;
+ git) remote="${COMP_WORDS[2]}" ;;
+ esac
+ cur=$(echo "$cur" | sed 's/^.*://')
+ COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur"))
+ ;;
+ *)
+ COMPREPLY=($(compgen -W "$(__git_refs2 .)" -- "$cur"))
+ ;;
+ esac
+ ;;
+ esac
+}
+
+_git_show ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur"))
+}
+
+_git ()
+{
+ if [ $COMP_CWORD = 1 ]; then
+ COMPREPLY=($(compgen \
+ -W "--version $(git help -a|egrep '^ ')" \
+ -- "${COMP_WORDS[COMP_CWORD]}"))
+ else
+ case "${COMP_WORDS[1]}" in
+ branch) _git_branch ;;
+ cat-file) _git_cat_file ;;
+ checkout) _git_checkout ;;
+ diff) _git_diff ;;
+ diff-tree) _git_diff_tree ;;
+ fetch) _git_fetch ;;
+ log) _git_log ;;
+ ls-remote) _git_ls_remote ;;
+ ls-tree) _git_ls_tree ;;
+ pull) _git_pull ;;
+ push) _git_push ;;
+ show) _git_show ;;
+ show-branch) _git_log ;;
+ whatchanged) _git_log ;;
+ *) COMPREPLY=() ;;
+ esac
+ fi
+}
+
+_gitk ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ COMPREPLY=($(compgen -W "--all $(__git_refs .)" -- "$cur"))
+}
+
+complete -o default -o nospace -F _git git
+complete -o default -F _gitk gitk
+complete -o default -F _git_branch git-branch
+complete -o default -o nospace -F _git_cat_file git-cat-file
+complete -o default -F _git_checkout git-checkout
+complete -o default -o nospace -F _git_diff git-diff
+complete -o default -F _git_diff_tree git-diff-tree
+complete -o default -o nospace -F _git_fetch git-fetch
+complete -o default -o nospace -F _git_log git-log
+complete -o default -F _git_ls_remote git-ls-remote
+complete -o default -o nospace -F _git_ls_tree git-ls-tree
+complete -o default -F _git_merge_base git-merge-base
+complete -o default -o nospace -F _git_pull git-pull
+complete -o default -o nospace -F _git_push git-push
+complete -o default -F _git_show git-show
+complete -o default -o nospace -F _git_log git-whatchanged
+
+# The following are necessary only for Cygwin, and only are needed
+# when the user has tab-completed the executable name and consequently
+# included the '.exe' suffix.
+#
+complete -o default -o nospace -F _git_cat_file git-cat-file.exe
+complete -o default -o nospace -F _git_diff git-diff.exe
+complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe
+complete -o default -o nospace -F _git_log git-log.exe
+complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe
+complete -o default -F _git_merge_base git-merge-base.exe
+complete -o default -o nospace -F _git_push git-push.exe
+complete -o default -o nospace -F _git_log git-whatchanged.exe
diff --git a/daemon.c b/daemon.c
index eb4f3f1e9..ad8492873 100644
--- a/daemon.c
+++ b/daemon.c
@@ -9,23 +9,30 @@
#include <syslog.h>
#include <pwd.h>
#include <grp.h>
+#include <limits.h>
#include "pkt-line.h"
#include "cache.h"
#include "exec_cmd.h"
#include "interpolate.h"
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 256
+#endif
+
static int log_syslog;
static int verbose;
static int reuseaddr;
static const char daemon_usage[] =
-"git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n"
+"git-daemon [--verbose] [--syslog] [--export-all]\n"
" [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
" [--base-path=path] [--user-path | --user-path=path]\n"
" [--interpolated-path=path]\n"
" [--reuseaddr] [--detach] [--pid-file=file]\n"
" [--[enable|disable|allow-override|forbid-override]=service]\n"
-" [--user=user [[--group=group]] [directory...]";
+" [--inetd | [--listen=host_or_ipaddr] [--port=n]\n"
+" [--user=user [--group=group]]\n"
+" [directory...]";
/* List of acceptable pathname prefixes */
static char **ok_paths;
@@ -56,13 +63,19 @@ static unsigned int init_timeout;
* Feel free to make dynamic as needed.
*/
#define INTERP_SLOT_HOST (0)
-#define INTERP_SLOT_DIR (1)
-#define INTERP_SLOT_PERCENT (2)
+#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},
};
@@ -396,7 +409,11 @@ static void make_service_overridable(const char *name, int ena) {
die("No such service %s", name);
}
-static void parse_extra_args(char *extra_args, int buflen)
+/*
+ * Separate the "extra args" information as supplied by the client connection.
+ * Any resulting data is squirrelled away in the given interpolation table.
+ */
+static void parse_extra_args(struct interp *table, char *extra_args, int buflen)
{
char *val;
int vallen;
@@ -408,16 +425,88 @@ static void parse_extra_args(char *extra_args, int buflen)
val = extra_args + 5;
vallen = strlen(val) + 1;
if (*val) {
- char *save = xmalloc(vallen);
- interp_table[INTERP_SLOT_HOST].value = save;
- strlcpy(save, val, vallen);
+ /* Split <host>:<port> at colon. */
+ char *host = val;
+ char *port = strrchr(host, ':');
+ if (port) {
+ *port = 0;
+ port++;
+ interp_set_entry(table, INTERP_SLOT_PORT, port);
+ }
+ interp_set_entry(table, INTERP_SLOT_HOST, host);
}
+
/* On to the next one */
extra_args = val + vallen;
}
}
}
+void fill_in_extra_table_entries(struct interp *itable)
+{
+ char *hp;
+
+ /*
+ * Replace literal host with lowercase-ized hostname.
+ */
+ hp = interp_table[INTERP_SLOT_HOST].value;
+ for ( ; *hp; hp++)
+ *hp = tolower(*hp);
+
+ /*
+ * Locate canonical hostname and its IP address.
+ */
+#ifndef NO_IPV6
+ {
+ struct addrinfo hints;
+ struct addrinfo *ai, *ai0;
+ int gai;
+ static char addrbuf[HOST_NAME_MAX + 1];
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+
+ gai = getaddrinfo(interp_table[INTERP_SLOT_HOST].value, 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);
+ break;
+ }
+ freeaddrinfo(ai0);
+ }
+ }
+#else
+ {
+ struct hostent *hent;
+ struct sockaddr_in sa;
+ char **ap;
+ static char addrbuf[HOST_NAME_MAX + 1];
+
+ hent = gethostbyname(interp_table[INTERP_SLOT_HOST].value);
+
+ ap = hent->h_addr_list;
+ memset(&sa, 0, sizeof sa);
+ sa.sin_family = hent->h_addrtype;
+ sa.sin_port = htons(0);
+ memcpy(&sa.sin_addr, *ap, hent->h_length);
+
+ 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);
+ }
+#endif
+}
+
+
static int execute(struct sockaddr *addr)
{
static char line[1000];
@@ -458,8 +547,16 @@ static int execute(struct sockaddr *addr)
if (len && line[len-1] == '\n')
line[--len] = 0;
- if (len != pktlen)
- parse_extra_args(line + len + 1, pktlen - len - 1);
+ /*
+ * 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, "%");
+
+ if (len != pktlen) {
+ parse_extra_args(interp_table, line + len + 1, pktlen - len - 1);
+ fill_in_extra_table_entries(interp_table);
+ }
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
struct daemon_service *s = &(daemon_service[i]);
@@ -467,7 +564,12 @@ static int execute(struct sockaddr *addr)
if (!strncmp("git-", line, 4) &&
!strncmp(s->name, line + 4, namelen) &&
line[namelen + 4] == ' ') {
- interp_table[INTERP_SLOT_DIR].value = line+namelen+5;
+ /*
+ * 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);
}
}
@@ -663,23 +765,22 @@ static int set_reuse_addr(int sockfd)
#ifndef NO_IPV6
-static int socksetup(int port, int **socklist_p)
+static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
{
int socknum = 0, *socklist = NULL;
int maxfd = -1;
char pbuf[NI_MAXSERV];
-
struct addrinfo hints, *ai0, *ai;
int gai;
- sprintf(pbuf, "%d", port);
+ sprintf(pbuf, "%d", listen_port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
- gai = getaddrinfo(NULL, pbuf, &hints, &ai0);
+ gai = getaddrinfo(listen_addr, pbuf, &hints, &ai0);
if (gai)
die("getaddrinfo() failed: %s\n", gai_strerror(gai));
@@ -733,20 +834,27 @@ static int socksetup(int port, int **socklist_p)
#else /* NO_IPV6 */
-static int socksetup(int port, int **socklist_p)
+static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
{
struct sockaddr_in sin;
int sockfd;
+ memset(&sin, 0, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(listen_port);
+
+ if (listen_addr) {
+ /* Well, host better be an IP address here. */
+ if (inet_pton(AF_INET, listen_addr, &sin.sin_addr.s_addr) <= 0)
+ return 0;
+ } else {
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ }
+
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
return 0;
- memset(&sin, 0, sizeof sin);
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = htonl(INADDR_ANY);
- sin.sin_port = htons(port);
-
if (set_reuse_addr(sockfd)) {
close(sockfd);
return 0;
@@ -855,13 +963,14 @@ static void store_pid(const char *path)
fclose(f);
}
-static int serve(int port, struct passwd *pass, gid_t gid)
+static int serve(char *listen_addr, int listen_port, struct passwd *pass, gid_t gid)
{
int socknum, *socklist;
- socknum = socksetup(port, &socklist);
+ socknum = socksetup(listen_addr, listen_port, &socklist);
if (socknum == 0)
- die("unable to allocate any listen sockets on port %u", port);
+ die("unable to allocate any listen sockets on host %s port %u",
+ listen_addr, listen_port);
if (pass && gid &&
(initgroups(pass->pw_name, gid) || setgid (gid) ||
@@ -873,7 +982,8 @@ static int serve(int port, struct passwd *pass, gid_t gid)
int main(int argc, char **argv)
{
- int port = DEFAULT_GIT_PORT;
+ int listen_port = 0;
+ char *listen_addr = NULL;
int inetd_mode = 0;
const char *pid_file = NULL, *user_name = NULL, *group_name = NULL;
int detach = 0;
@@ -890,12 +1000,20 @@ int main(int argc, char **argv)
for (i = 1; i < argc; i++) {
char *arg = argv[i];
+ if (!strncmp(arg, "--listen=", 9)) {
+ char *p = arg + 9;
+ char *ph = listen_addr = xmalloc(strlen(arg + 9) + 1);
+ while (*p)
+ *ph++ = tolower(*p++);
+ *ph = 0;
+ continue;
+ }
if (!strncmp(arg, "--port=", 7)) {
char *end;
unsigned long n;
n = strtoul(arg+7, &end, 0);
if (arg[7] && !*end) {
- port = n;
+ listen_port = n;
continue;
}
}
@@ -995,6 +1113,11 @@ int main(int argc, char **argv)
if (inetd_mode && (group_name || user_name))
die("--user and --group are incompatible with --inetd");
+ if (inetd_mode && (listen_port || listen_addr))
+ die("--listen= and --port= are incompatible with --inetd");
+ else if (listen_port == 0)
+ listen_port = DEFAULT_GIT_PORT;
+
if (group_name && !user_name)
die("--group supplied without --user");
@@ -1043,5 +1166,5 @@ int main(int argc, char **argv)
if (pid_file)
store_pid(pid_file);
- return serve(port, pass, gid);
+ return serve(listen_addr, listen_port, pass, gid);
}
diff --git a/date.c b/date.c
index e387dcd39..182592273 100644
--- a/date.c
+++ b/date.c
@@ -256,8 +256,12 @@ static int match_alpha(const char *date, struct tm *tm, int *offset)
}
if (match_string(date, "PM") == 2) {
- if (tm->tm_hour > 0 && tm->tm_hour < 12)
- tm->tm_hour += 12;
+ tm->tm_hour = (tm->tm_hour % 12) + 12;
+ return 2;
+ }
+
+ if (match_string(date, "AM") == 2) {
+ tm->tm_hour = (tm->tm_hour % 12) + 0;
return 2;
}
@@ -598,6 +602,34 @@ static void date_tea(struct tm *tm, int *num)
date_time(tm, 17);
}
+static void date_pm(struct tm *tm, int *num)
+{
+ int hour, n = *num;
+ *num = 0;
+
+ hour = tm->tm_hour;
+ if (n) {
+ hour = n;
+ tm->tm_min = 0;
+ tm->tm_sec = 0;
+ }
+ tm->tm_hour = (hour % 12) + 12;
+}
+
+static void date_am(struct tm *tm, int *num)
+{
+ int hour, n = *num;
+ *num = 0;
+
+ hour = tm->tm_hour;
+ if (n) {
+ hour = n;
+ tm->tm_min = 0;
+ tm->tm_sec = 0;
+ }
+ tm->tm_hour = (hour % 12);
+}
+
static const struct special {
const char *name;
void (*fn)(struct tm *, int *);
@@ -606,6 +638,8 @@ static const struct special {
{ "noon", date_noon },
{ "midnight", date_midnight },
{ "tea", date_tea },
+ { "PM", date_pm },
+ { "AM", date_am },
{ NULL }
};
@@ -712,6 +746,27 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)
return end;
}
+static const char *approxidate_digit(const char *date, struct tm *tm, int *num)
+{
+ char *end;
+ unsigned long number = strtoul(date, &end, 10);
+
+ switch (*end) {
+ case ':':
+ case '.':
+ case '/':
+ case '-':
+ if (isdigit(end[1])) {
+ int match = match_multi_number(number, *end, date, end, tm);
+ if (match)
+ return date + match;
+ }
+ }
+
+ *num = number;
+ return end;
+}
+
unsigned long approxidate(const char *date)
{
int number = 0;
@@ -731,9 +786,7 @@ unsigned long approxidate(const char *date)
break;
date++;
if (isdigit(c)) {
- char *end;
- number = strtoul(date-1, &end, 10);
- date = end;
+ date = approxidate_digit(date-1, &tm, &number);
continue;
}
if (isalpha(c))
diff --git a/diff.c b/diff.c
index 2464238cc..fb8243261 100644
--- a/diff.c
+++ b/diff.c
@@ -208,7 +208,7 @@ static void emit_rewrite_diff(const char *name_a,
diff_populate_filespec(two, 0);
lc_a = count_lines(one->data, one->size);
lc_b = count_lines(two->data, two->size);
- printf("--- %s\n+++ %s\n@@ -", name_a, name_b);
+ printf("--- a/%s\n+++ b/%s\n@@ -", name_a, name_b);
print_line_count(lc_a);
printf(" +");
print_line_count(lc_b);
@@ -635,21 +635,76 @@ static void diffstat_consume(void *priv, char *line, unsigned long len)
x->deleted++;
}
-static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
-static const char minuses[]= "----------------------------------------------------------------------";
const char mime_boundary_leader[] = "------------";
-static void show_stats(struct diffstat_t* data)
+static int scale_linear(int it, int width, int max_change)
+{
+ /*
+ * make sure that at least one '-' is printed if there were deletions,
+ * and likewise for '+'.
+ */
+ if (max_change < 2)
+ return it;
+ return ((it - 1) * (width - 1) + max_change - 1) / (max_change - 1);
+}
+
+static void show_name(const char *prefix, const char *name, int len,
+ const char *reset, const char *set)
+{
+ printf(" %s%s%-*s%s |", set, prefix, len, name, reset);
+}
+
+static void show_graph(char ch, int cnt, const char *set, const char *reset)
+{
+ if (cnt <= 0)
+ return;
+ printf("%s", set);
+ while (cnt--)
+ putchar(ch);
+ printf("%s", reset);
+}
+
+static void show_stats(struct diffstat_t* data, struct diff_options *options)
{
int i, len, add, del, total, adds = 0, dels = 0;
- int max, max_change = 0, max_len = 0;
+ int max_change = 0, max_len = 0;
int total_files = data->nr;
+ int width, name_width;
+ const char *reset, *set, *add_c, *del_c;
if (data->nr == 0)
return;
+ width = options->stat_width ? options->stat_width : 80;
+ name_width = options->stat_name_width ? options->stat_name_width : 50;
+
+ /* Sanity: give at least 5 columns to the graph,
+ * but leave at least 10 columns for the name.
+ */
+ if (width < name_width + 15) {
+ if (name_width <= 25)
+ width = name_width + 15;
+ else
+ name_width = width - 15;
+ }
+
+ /* Find the longest filename and max number of changes */
+ reset = diff_get_color(options->color_diff, DIFF_RESET);
+ set = diff_get_color(options->color_diff, DIFF_PLAIN);
+ add_c = diff_get_color(options->color_diff, DIFF_FILE_NEW);
+ del_c = diff_get_color(options->color_diff, DIFF_FILE_OLD);
+
for (i = 0; i < data->nr; i++) {
struct diffstat_file *file = data->files[i];
+ int change = file->added + file->deleted;
+
+ len = quote_c_style(file->name, NULL, NULL, 0);
+ if (len) {
+ char *qname = xmalloc(len + 1);
+ quote_c_style(file->name, qname, NULL, 0);
+ free(file->name);
+ file->name = qname;
+ }
len = strlen(file->name);
if (max_len < len)
@@ -657,54 +712,53 @@ static void show_stats(struct diffstat_t* data)
if (file->is_binary || file->is_unmerged)
continue;
- if (max_change < file->added + file->deleted)
- max_change = file->added + file->deleted;
+ if (max_change < change)
+ max_change = change;
}
+ /* Compute the width of the graph part;
+ * 10 is for one blank at the beginning of the line plus
+ * " | count " between the name and the graph.
+ *
+ * From here on, name_width is the width of the name area,
+ * and width is the width of the graph area.
+ */
+ name_width = (name_width < max_len) ? name_width : max_len;
+ if (width < (name_width + 10) + max_change)
+ width = width - (name_width + 10);
+ else
+ width = max_change;
+
for (i = 0; i < data->nr; i++) {
const char *prefix = "";
char *name = data->files[i]->name;
int added = data->files[i]->added;
int deleted = data->files[i]->deleted;
-
- if (0 < (len = quote_c_style(name, NULL, NULL, 0))) {
- char *qname = xmalloc(len + 1);
- quote_c_style(name, qname, NULL, 0);
- free(name);
- data->files[i]->name = name = qname;
- }
+ int name_len;
/*
* "scale" the filename
*/
- len = strlen(name);
- max = max_len;
- if (max > 50)
- max = 50;
- if (len > max) {
+ len = name_width;
+ name_len = strlen(name);
+ if (name_width < name_len) {
char *slash;
prefix = "...";
- max -= 3;
- name += len - max;
+ len -= 3;
+ name += name_len - len;
slash = strchr(name, '/');
if (slash)
name = slash;
}
- len = max;
-
- /*
- * scale the add/delete
- */
- max = max_change;
- if (max + len > 70)
- max = 70 - len;
if (data->files[i]->is_binary) {
- printf(" %s%-*s | Bin\n", prefix, len, name);
+ show_name(prefix, name, len, reset, set);
+ printf(" Bin\n");
goto free_diffstat_file;
}
else if (data->files[i]->is_unmerged) {
- printf(" %s%-*s | Unmerged\n", prefix, len, name);
+ show_name(prefix, name, len, reset, set);
+ printf(" Unmerged\n");
goto free_diffstat_file;
}
else if (!data->files[i]->is_renamed &&
@@ -713,27 +767,32 @@ static void show_stats(struct diffstat_t* data)
goto free_diffstat_file;
}
+ /*
+ * scale the add/delete
+ */
add = added;
del = deleted;
total = add + del;
adds += add;
dels += del;
- if (max_change > 0) {
- total = (total * max + max_change / 2) / max_change;
- add = (add * max + max_change / 2) / max_change;
- del = total - add;
+ if (width <= max_change) {
+ add = scale_linear(add, width, max_change);
+ del = scale_linear(del, width, max_change);
+ total = add + del;
}
- printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
- len, name, added + deleted,
- add, pluses, del, minuses);
+ show_name(prefix, name, len, reset, set);
+ printf("%5d ", added + deleted);
+ show_graph('+', add, add_c, reset);
+ show_graph('-', del, del_c, reset);
+ putchar('\n');
free_diffstat_file:
free(data->files[i]->name);
free(data->files[i]);
}
free(data->files);
- printf(" %d files changed, %d insertions(+), %d deletions(-)\n",
- total_files, adds, dels);
+ printf("%s %d files changed, %d insertions(+), %d deletions(-)%s\n",
+ set, total_files, adds, dels, reset);
}
struct checkdiff_t {
@@ -1769,8 +1828,33 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
else if (!strcmp(arg, "--patch-with-raw")) {
options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW;
}
- else if (!strcmp(arg, "--stat"))
+ else if (!strncmp(arg, "--stat", 6)) {
+ char *end;
+ int width = options->stat_width;
+ int name_width = options->stat_name_width;
+ arg += 6;
+ end = (char *)arg;
+
+ switch (*arg) {
+ case '-':
+ if (!strncmp(arg, "-width=", 7))
+ width = strtoul(arg + 7, &end, 10);
+ else if (!strncmp(arg, "-name-width=", 12))
+ name_width = strtoul(arg + 12, &end, 10);
+ break;
+ case '=':
+ width = strtoul(arg+1, &end, 10);
+ if (*end == ',')
+ name_width = strtoul(end+1, &end, 10);
+ }
+
+ /* Important! This checks all the error cases! */
+ if (*end)
+ return 0;
options->output_format |= DIFF_FORMAT_DIFFSTAT;
+ options->stat_name_width = name_width;
+ options->stat_width = width;
+ }
else if (!strcmp(arg, "--check"))
options->output_format |= DIFF_FORMAT_CHECKDIFF;
else if (!strcmp(arg, "--summary"))
@@ -2528,7 +2612,7 @@ void diff_flush(struct diff_options *options)
if (check_pair_status(p))
diff_flush_stat(p, options, &diffstat);
}
- show_stats(&diffstat);
+ show_stats(&diffstat, options);
separator++;
}
diff --git a/diff.h b/diff.h
index 3435fe7b3..b48c9914e 100644
--- a/diff.h
+++ b/diff.h
@@ -69,6 +69,9 @@ struct diff_options {
const char *stat_sep;
long xdl_opts;
+ int stat_width;
+ int stat_name_width;
+
int nr_paths;
const char **paths;
int *pathlens;
diff --git a/git-clone.sh b/git-clone.sh
index e1b3bf382..3998c55ce 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -31,6 +31,10 @@ clone_dumb_http () {
cd "$2" &&
clone_tmp="$GIT_DIR/clone-tmp" &&
mkdir -p "$clone_tmp" || exit 1
+ if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \
+ "`git-repo-config --bool http.noEPSV`" = true ]; then
+ curl_extra_args="${curl_extra_args} --disable-epsv"
+ fi
http_fetch "$1/info/refs" "$clone_tmp/refs" || {
echo >&2 "Cannot get remote repository information.
Perhaps git-update-server-info needs to be run there?"
diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl
index 99b3dc392..5e23851f8 100755
--- a/git-cvsexportcommit.perl
+++ b/git-cvsexportcommit.perl
@@ -135,7 +135,7 @@ foreach my $f (@files) {
if ($fields[4] eq 'M') {
push @mfiles, $fields[5];
}
- if ($fields[4] eq 'R') {
+ if ($fields[4] eq 'D') {
push @dfiles, $fields[5];
}
}
diff --git a/git-fetch.sh b/git-fetch.sh
index 50ad101e8..85e96a1cc 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -129,22 +129,25 @@ append_fetch_head () {
then
headc_=$(git-rev-parse --verify "$head_^0") || exit
echo "$headc_ $not_for_merge_ $note_" >>"$GIT_DIR/FETCH_HEAD"
- [ "$verbose" ] && echo >&2 "* committish: $head_"
- [ "$verbose" ] && echo >&2 " $note_"
else
echo "$head_ not-for-merge $note_" >>"$GIT_DIR/FETCH_HEAD"
- [ "$verbose" ] && echo >&2 "* non-commit: $head_"
- [ "$verbose" ] && echo >&2 " $note_"
- fi
- if test "$local_name_" != ""
- then
- # We are storing the head locally. Make sure that it is
- # a fast forward (aka "reverse push").
- fast_forward_local "$local_name_" "$head_" "$note_"
fi
+
+ update_local_ref "$local_name_" "$head_" "$note_"
}
-fast_forward_local () {
+update_local_ref () {
+ # If we are storing the head locally make sure that it is
+ # a fast forward (aka "reverse push").
+
+ label_=$(git-cat-file -t $2)
+ newshort_=$(git-rev-parse --short $2)
+ if test -z "$1" ; then
+ [ "$verbose" ] && echo >&2 "* fetched $3"
+ [ "$verbose" ] && echo >&2 " $label_: $newshort_"
+ return 0
+ fi
+ oldshort_=$(git-rev-parse --short "$1" 2>/dev/null)
mkdir -p "$(dirname "$GIT_DIR/$1")"
case "$1" in
refs/tags/*)
@@ -154,13 +157,16 @@ fast_forward_local () {
then
if now_=$(cat "$GIT_DIR/$1") && test "$now_" = "$2"
then
- [ "$verbose" ] && echo >&2 "* $1: same as $3" ||:
+ [ "$verbose" ] && echo >&2 "* $1: same as $3"
+ [ "$verbose" ] && echo >&2 " $label_: $newshort_" ||:
else
echo >&2 "* $1: updating with $3"
+ echo >&2 " $label_: $newshort_"
git-update-ref -m "$rloga: updating tag" "$1" "$2"
fi
else
echo >&2 "* $1: storing $3"
+ echo >&2 " $label_: $newshort_"
git-update-ref -m "$rloga: storing tag" "$1" "$2"
fi
;;
@@ -178,31 +184,34 @@ fast_forward_local () {
if test -n "$verbose"
then
echo >&2 "* $1: same as $3"
+ echo >&2 " $label_: $newshort_"
fi
;;
*,$local)
echo >&2 "* $1: fast forward to $3"
- echo >&2 " from $local to $2"
+ echo >&2 " old..new: $oldshort_..$newshort_"
git-update-ref -m "$rloga: fast-forward" "$1" "$2" "$local"
;;
*)
false
;;
esac || {
- echo >&2 "* $1: does not fast forward to $3;"
case ",$force,$single_force," in
*,t,*)
- echo >&2 " forcing update."
+ echo >&2 "* $1: forcing update to non-fast forward $3"
+ echo >&2 " old...new: $oldshort_...$newshort_"
git-update-ref -m "$rloga: forced-update" "$1" "$2" "$local"
;;
*)
- echo >&2 " not updating."
+ echo >&2 "* $1: not updating to non-fast forward $3"
+ echo >&2 " old...new: $oldshort_...$newshort_"
exit 1
;;
esac
}
else
echo >&2 "* $1: storing $3"
+ echo >&2 " $label_: $newshort_"
git-update-ref -m "$rloga: storing head" "$1" "$2"
fi
;;
@@ -257,6 +266,7 @@ fi
fetch_main () {
reflist="$1"
refs=
+ rref=
for ref in $reflist
do
@@ -289,6 +299,10 @@ fetch_main () {
if [ -n "$GIT_SSL_NO_VERIFY" ]; then
curl_extra_args="-k"
fi
+ if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \
+ "`git-repo-config --bool http.noEPSV`" = true ]; then
+ noepsv_opt="--disable-epsv"
+ fi
max_depth=5
depth=0
head="ref: $remote_name"
@@ -300,7 +314,7 @@ fetch_main () {
$u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg;
print "$u";
' "$head")
- head=$(curl -nsfL $curl_extra_args "$remote/$remote_name_quoted")
+ head=$(curl -nsfL $curl_extra_args $noepsv_opt "$remote/$remote_name_quoted")
depth=$( expr \( $depth + 1 \) )
done
expr "z$head" : "z$_x40\$" >/dev/null ||
diff --git a/git-ls-remote.sh b/git-ls-remote.sh
index 2c0b52122..0f88953f2 100755
--- a/git-ls-remote.sh
+++ b/git-ls-remote.sh
@@ -53,6 +53,10 @@ http://* | https://* | ftp://* )
if [ -n "$GIT_SSL_NO_VERIFY" ]; then
curl_extra_args="-k"
fi
+ if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \
+ "`git-repo-config --bool http.noEPSV`" = true ]; then
+ curl_extra_args="${curl_extra_args} --disable-epsv"
+ fi
curl -nsf $curl_extra_args --header "Pragma: no-cache" "$peek_repo/info/refs" ||
echo "failed slurping"
;;
diff --git a/git-merge.sh b/git-merge.sh
index 5b34b4de9..49c46d55d 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -197,7 +197,7 @@ f,*)
;;
?,1,"$head",*)
# Again the most common case of merging one remote.
- echo "Updating from $head to $1"
+ echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $1)"
git-update-index --refresh 2>/dev/null
new_head=$(git-rev-parse --verify "$1^0") &&
git-read-tree -u -v -m $head "$new_head" &&
diff --git a/git-resolve.sh b/git-resolve.sh
index 729ec65dc..36b90e384 100755
--- a/git-resolve.sh
+++ b/git-resolve.sh
@@ -46,7 +46,7 @@ case "$common" in
exit 0
;;
"$head")
- echo "Updating from $head to $merge"
+ echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $merge)"
git-read-tree -u -m $head $merge || exit 1
git-update-ref -m "resolve $merge_name: Fast forward" \
HEAD "$merge" "$head"
diff --git a/git-svnimport.perl b/git-svnimport.perl
index ed628974d..988514e29 100755
--- a/git-svnimport.perl
+++ b/git-svnimport.perl
@@ -31,7 +31,7 @@ $SIG{'PIPE'}="IGNORE";
$ENV{'TZ'}="UTC";
our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T,
- $opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D,$opt_S);
+ $opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D,$opt_S,$opt_F);
sub usage() {
print STDERR <<END;
@@ -39,12 +39,12 @@ Usage: ${\basename $0} # fetch/update GIT from SVN
[-o branch-for-HEAD] [-h] [-v] [-l max_rev]
[-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname]
[-d|-D] [-i] [-u] [-r] [-I ignorefilename] [-s start_chg]
- [-m] [-M regex] [-A author_file] [-S] [SVN_URL]
+ [-m] [-M regex] [-A author_file] [-S] [-F] [SVN_URL]
END
exit(1);
}
-getopts("A:b:C:dDhiI:l:mM:o:rs:t:T:Suv") or usage();
+getopts("A:b:C:dDFhiI:l:mM:o:rs:t:T:Suv") or usage();
usage if $opt_h;
my $tag_name = $opt_t || "tags";
@@ -548,8 +548,12 @@ sub commit {
$committer_name = $committer_email = $author;
}
- if ($opt_S && $message =~ /Signed-off-by:\s+(.*?)\s+<(.*)>\s*\n/) {
+ if ($opt_F && $message =~ /From:\s+(.*?)\s+<(.*)>\s*\n/) {
($author_name, $author_email) = ($1, $2);
+ print "Author from From: $1 <$2>\n" if ($opt_v);;
+ } elsif ($opt_S && $message =~ /Signed-off-by:\s+(.*?)\s+<(.*)>\s*\n/) {
+ ($author_name, $author_email) = ($1, $2);
+ print "Author from Signed-off-by: $1 <$2>\n" if ($opt_v);;
} else {
$author_name = $committer_name;
$author_email = $committer_email;
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 597d29f22..44991b153 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -106,7 +106,7 @@ our %feature = (
sub gitweb_check_feature {
my ($name) = @_;
- return undef unless exists $feature{$name};
+ return unless exists $feature{$name};
my ($sub, $override, @defaults) = (
$feature{$name}{'sub'},
$feature{$name}{'override'},
@@ -155,6 +155,13 @@ sub feature_snapshot {
return ($ctype, $suffix, $command);
}
+sub gitweb_have_snapshot {
+ my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
+ my $have_snapshot = (defined $ctype && defined $suffix);
+
+ return $have_snapshot;
+}
+
# To enable system wide have in $GITWEB_CONFIG
# $feature{'pickaxe'}{'default'} = [1];
# To have project specific config enable override in $GITWEB_CONFIG
@@ -200,9 +207,10 @@ if (defined $action) {
}
}
+# parameters which are pathnames
our $project = $cgi->param('p');
if (defined $project) {
- if (!validate_input($project) ||
+ if (!validate_pathname($project) ||
!(-d "$projectroot/$project") ||
!(-e "$projectroot/$project/HEAD") ||
($export_ok && !(-e "$projectroot/$project/$export_ok")) ||
@@ -212,38 +220,50 @@ if (defined $project) {
}
}
-# We have to handle those containing any characters:
our $file_name = $cgi->param('f');
+if (defined $file_name) {
+ if (!validate_pathname($file_name)) {
+ die_error(undef, "Invalid file parameter");
+ }
+}
+
our $file_parent = $cgi->param('fp');
+if (defined $file_parent) {
+ if (!validate_pathname($file_parent)) {
+ die_error(undef, "Invalid file parent parameter");
+ }
+}
+# parameters which are refnames
our $hash = $cgi->param('h');
if (defined $hash) {
- if (!validate_input($hash)) {
+ if (!validate_refname($hash)) {
die_error(undef, "Invalid hash parameter");
}
}
our $hash_parent = $cgi->param('hp');
if (defined $hash_parent) {
- if (!validate_input($hash_parent)) {
+ if (!validate_refname($hash_parent)) {
die_error(undef, "Invalid hash parent parameter");
}
}
our $hash_base = $cgi->param('hb');
if (defined $hash_base) {
- if (!validate_input($hash_base)) {
+ if (!validate_refname($hash_base)) {
die_error(undef, "Invalid hash base parameter");
}
}
our $hash_parent_base = $cgi->param('hpb');
if (defined $hash_parent_base) {
- if (!validate_input($hash_parent_base)) {
+ if (!validate_refname($hash_parent_base)) {
die_error(undef, "Invalid hash parent base parameter");
}
}
+# other parameters
our $page = $cgi->param('pg');
if (defined $page) {
if ($page =~ m/[^0-9]/) {
@@ -273,7 +293,7 @@ sub evaluate_path_info {
$project =~ s,/*[^/]*$,,;
}
# validate project
- $project = validate_input($project);
+ $project = validate_pathname($project);
if (!$project ||
($export_ok && !-e "$projectroot/$project/$export_ok") ||
($strict_export && !project_in_list($project))) {
@@ -294,12 +314,12 @@ sub evaluate_path_info {
} else {
$action ||= "blob_plain";
}
- $hash_base ||= validate_input($refname);
- $file_name ||= $pathname;
+ $hash_base ||= validate_refname($refname);
+ $file_name ||= validate_pathname($pathname);
} elsif (defined $refname) {
# we got "project.git/branch"
$action ||= "shortlog";
- $hash ||= validate_input($refname);
+ $hash ||= validate_refname($refname);
}
}
evaluate_path_info();
@@ -387,16 +407,34 @@ sub href(%) {
## ======================================================================
## validation, quoting/unquoting and escaping
-sub validate_input {
- my $input = shift;
+sub validate_pathname {
+ my $input = shift || return undef;
- if ($input =~ m/^[0-9a-fA-F]{40}$/) {
- return $input;
+ # no '.' or '..' as elements of path, i.e. no '.' nor '..'
+ # at the beginning, at the end, and between slashes.
+ # also this catches doubled slashes
+ if ($input =~ m!(^|/)(|\.|\.\.)(/|$)!) {
+ return undef;
}
- if ($input =~ m/(^|\/)(|\.|\.\.)($|\/)/) {
+ # no null characters
+ if ($input =~ m!\0!) {
return undef;
}
- if ($input =~ m/[^a-zA-Z0-9_\x80-\xff\ \t\.\/\-\+\#\~\%]/) {
+ return $input;
+}
+
+sub validate_refname {
+ my $input = shift || return undef;
+
+ # textual hashes are O.K.
+ if ($input =~ m/^[0-9a-fA-F]{40}$/) {
+ return $input;
+ }
+ # it must be correct pathname
+ $input = validate_pathname($input)
+ or return undef;
+ # restrictions on ref name according to git-check-ref-format
+ if ($input =~ m!(/\.|\.\.|[\000-\040\177 ~^:?*\[]|/$)!) {
return undef;
}
return $input;
@@ -412,6 +450,15 @@ sub esc_param {
return $str;
}
+# quote unsafe chars in whole URL, so some charactrs cannot be quoted
+sub esc_url {
+ my $str = shift;
+ $str =~ s/([^A-Za-z0-9\-_.~();\/;?:@&=])/sprintf("%%%02X", ord($1))/eg;
+ $str =~ s/\+/%2B/g;
+ $str =~ s/ /\+/g;
+ return $str;
+}
+
# replace invalid utf8 character with SUBSTITUTION sequence
sub esc_html {
my $str = shift;
@@ -710,7 +757,7 @@ sub git_get_hash_by_path {
my $path = shift || return undef;
my $type = shift;
- my $tree = $base;
+ $path =~ s,/+$,,;
open my $fd, "-|", git_cmd(), "ls-tree", $base, "--", $path
or die_error(undef, "Open git-ls-tree failed");
@@ -781,7 +828,7 @@ sub git_get_projects_list {
# 'git%2Fgit.git Linus+Torvalds'
# 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
# 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman'
- open my ($fd), $projects_list or return undef;
+ open my ($fd), $projects_list or return;
while (my $line = <$fd>) {
chomp $line;
my ($path, $owner) = split ' ', $line;
@@ -1328,7 +1375,7 @@ EOF
"<a href=\"http://www.kernel.org/pub/software/scm/git/docs/\" title=\"git documentation\">" .
"<img src=\"$logo\" width=\"72\" height=\"27\" alt=\"git\" style=\"float:right; border-width:0px;\"/>" .
"</a>\n";
- print $cgi->a({-href => esc_param($home_link)}, $home_link_str) . " / ";
+ print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / ";
if (defined $project) {
print $cgi->a({-href => href(action=>"summary")}, esc_html($project));
if (defined $action) {
@@ -1600,48 +1647,45 @@ sub git_print_tree_entry {
my %base_key = ();
$base_key{hash_base} = $hash_base if defined $hash_base;
+ # The format of a table row is: mode list link. Where mode is
+ # the mode of the entry, list is the name of the entry, an href,
+ # and link is the action links of the entry.
+
print "<td class=\"mode\">" . mode_str($t->{'mode'}) . "</td>\n";
if ($t->{'type'} eq "blob") {
print "<td class=\"list\">" .
- $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
- file_name=>"$basedir$t->{'name'}", %base_key),
- -class => "list"}, esc_html($t->{'name'})) .
- "</td>\n" .
- "<td class=\"link\">" .
- $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
- file_name=>"$basedir$t->{'name'}", %base_key)},
- "blob");
+ $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
+ file_name=>"$basedir$t->{'name'}", %base_key),
+ -class => "list"}, esc_html($t->{'name'})) . "</td>\n";
+ print "<td class=\"link\">";
if ($have_blame) {
- print " | " .
- $cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'},
- file_name=>"$basedir$t->{'name'}", %base_key)},
- "blame");
+ print $cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'},
+ file_name=>"$basedir$t->{'name'}", %base_key)},
+ "blame");
}
if (defined $hash_base) {
- print " | " .
- $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
+ if ($have_blame) {
+ print " | ";
+ }
+ print $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}")},
"history");
}
print " | " .
- $cgi->a({-href => href(action=>"blob_plain",
- hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}")},
- "raw") .
- "</td>\n";
+ $cgi->a({-href => href(action=>"blob_plain", hash_base=>$hash_base,
+ file_name=>"$basedir$t->{'name'}")},
+ "raw");
+ print "</td>\n";
} elsif ($t->{'type'} eq "tree") {
- print "<td class=\"list\">" .
- $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
+ print "<td class=\"list\">";
+ print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
file_name=>"$basedir$t->{'name'}", %base_key)},
- esc_html($t->{'name'})) .
- "</td>\n" .
- "<td class=\"link\">" .
- $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
- file_name=>"$basedir$t->{'name'}", %base_key)},
- "tree");
+ esc_html($t->{'name'}));
+ print "</td>\n";
+ print "<td class=\"link\">";
if (defined $hash_base) {
- print " | " .
- $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
+ print $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
file_name=>"$basedir$t->{'name'}")},
"history");
}
@@ -1662,7 +1706,7 @@ sub git_difftree_body {
print "</div>\n";
print "<table class=\"diff_tree\">\n";
- my $alternate = 0;
+ my $alternate = 1;
my $patchno = 0;
foreach my $line (@{$difftree}) {
my %diff = parse_difftree_raw_line($line);
@@ -1695,47 +1739,42 @@ sub git_difftree_body {
my $mode_chng = "<span class=\"file_status new\">[new $to_file_type";
$mode_chng .= " with mode: $to_mode_str" if $to_mode_str;
$mode_chng .= "]</span>";
- print "<td>" .
- $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
+ print "<td>";
+ print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
hash_base=>$hash, file_name=>$diff{'file'}),
- -class => "list"}, esc_html($diff{'file'})) .
- "</td>\n" .
- "<td>$mode_chng</td>\n" .
- "<td class=\"link\">" .
- $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
- hash_base=>$hash, file_name=>$diff{'file'})},
- "blob");
+ -class => "list"}, esc_html($diff{'file'}));
+ print "</td>\n";
+ print "<td>$mode_chng</td>\n";
+ print "<td class=\"link\">";
if ($action eq 'commitdiff') {
# link to patch
$patchno++;
- print " | " .
- $cgi->a({-href => "#patch$patchno"}, "patch");
+ print $cgi->a({-href => "#patch$patchno"}, "patch");
}
print "</td>\n";
} elsif ($diff{'status'} eq "D") { # deleted
my $mode_chng = "<span class=\"file_status deleted\">[deleted $from_file_type]</span>";
- print "<td>" .
- $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
+ print "<td>";
+ print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
hash_base=>$parent, file_name=>$diff{'file'}),
- -class => "list"}, esc_html($diff{'file'})) .
- "</td>\n" .
- "<td>$mode_chng</td>\n" .
- "<td class=\"link\">" .
- $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
- hash_base=>$parent, file_name=>$diff{'file'})},
- "blob") .
- " | ";
+ -class => "list"}, esc_html($diff{'file'}));
+ print "</td>\n";
+ print "<td>$mode_chng</td>\n";
+ print "<td class=\"link\">";
if ($action eq 'commitdiff') {
# link to patch
$patchno++;
- print " | " .
- $cgi->a({-href => "#patch$patchno"}, "patch");
+ print $cgi->a({-href => "#patch$patchno"}, "patch");
+ print " | ";
}
+ print $cgi->a({-href => href(action=>"blame", hash_base=>$parent,
+ file_name=>$diff{'file'})},
+ "blame") . " | ";
print $cgi->a({-href => href(action=>"history", hash_base=>$parent,
- file_name=>$diff{'file'})},
- "history") .
- "</td>\n";
+ file_name=>$diff{'file'})},
+ "history");
+ print "</td>\n";
} elsif ($diff{'status'} eq "M" || $diff{'status'} eq "T") { # modified, or type changed
my $mode_chnge = "";
@@ -1754,42 +1793,32 @@ sub git_difftree_body {
$mode_chnge .= "]</span>\n";
}
print "<td>";
- if ($diff{'to_id'} ne $diff{'from_id'}) { # modified
- print $cgi->a({-href => href(action=>"blobdiff",
- hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
- hash_base=>$hash, hash_parent_base=>$parent,
- file_name=>$diff{'file'}),
- -class => "list"}, esc_html($diff{'file'}));
- } else { # only mode changed
- print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
- hash_base=>$hash, file_name=>$diff{'file'}),
- -class => "list"}, esc_html($diff{'file'}));
- }
- print "</td>\n" .
- "<td>$mode_chnge</td>\n" .
- "<td class=\"link\">" .
- $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
- hash_base=>$hash, file_name=>$diff{'file'})},
- "blob");
+ print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
+ hash_base=>$hash, file_name=>$diff{'file'}),
+ -class => "list"}, esc_html($diff{'file'}));
+ print "</td>\n";
+ print "<td>$mode_chnge</td>\n";
+ print "<td class=\"link\">";
if ($diff{'to_id'} ne $diff{'from_id'}) { # modified
if ($action eq 'commitdiff') {
# link to patch
$patchno++;
- print " | " .
- $cgi->a({-href => "#patch$patchno"}, "patch");
+ print $cgi->a({-href => "#patch$patchno"}, "patch");
} else {
- print " | " .
- $cgi->a({-href => href(action=>"blobdiff",
- hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
- hash_base=>$hash, hash_parent_base=>$parent,
- file_name=>$diff{'file'})},
- "diff");
+ print $cgi->a({-href => href(action=>"blobdiff",
+ hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
+ hash_base=>$hash, hash_parent_base=>$parent,
+ file_name=>$diff{'file'})},
+ "diff");
}
+ print " | ";
}
- print " | " .
- $cgi->a({-href => href(action=>"history",
- hash_base=>$hash, file_name=>$diff{'file'})},
- "history");
+ print $cgi->a({-href => href(action=>"blame", hash_base=>$hash,
+ file_name=>$diff{'file'})},
+ "blame") . " | ";
+ print $cgi->a({-href => href(action=>"history", hash_base=>$hash,
+ file_name=>$diff{'file'})},
+ "history");
print "</td>\n";
} elsif ($diff{'status'} eq "R" || $diff{'status'} eq "C") { # renamed or copied
@@ -1809,25 +1838,27 @@ sub git_difftree_body {
hash=>$diff{'from_id'}, file_name=>$diff{'from_file'}),
-class => "list"}, esc_html($diff{'from_file'})) .
" with " . (int $diff{'similarity'}) . "% similarity$mode_chng]</span></td>\n" .
- "<td class=\"link\">" .
- $cgi->a({-href => href(action=>"blob", hash_base=>$hash,
- hash=>$diff{'to_id'}, file_name=>$diff{'to_file'})},
- "blob");
+ "<td class=\"link\">";
if ($diff{'to_id'} ne $diff{'from_id'}) {
if ($action eq 'commitdiff') {
# link to patch
$patchno++;
- print " | " .
- $cgi->a({-href => "#patch$patchno"}, "patch");
+ print $cgi->a({-href => "#patch$patchno"}, "patch");
} else {
- print " | " .
- $cgi->a({-href => href(action=>"blobdiff",
- hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
- hash_base=>$hash, hash_parent_base=>$parent,
- file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})},
- "diff");
+ print $cgi->a({-href => href(action=>"blobdiff",
+ hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
+ hash_base=>$hash, hash_parent_base=>$parent,
+ file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})},
+ "diff");
}
+ print " | ";
}
+ print $cgi->a({-href => href(action=>"blame", hash_base=>$parent,
+ file_name=>$diff{'from_file'})},
+ "blame") . " | ";
+ print $cgi->a({-href => href(action=>"history", hash_base=>$parent,
+ file_name=>$diff{'from_file'})},
+ "history");
print "</td>\n";
} # we should not encounter Unmerged (U) or Unknown (X) status
@@ -1969,7 +2000,7 @@ sub git_shortlog_body {
$to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);
print "<table class=\"shortlog\" cellspacing=\"0\">\n";
- my $alternate = 0;
+ my $alternate = 1;
for (my $i = $from; $i <= $to; $i++) {
my $commit = $revlist->[$i];
#my $ref = defined $refs ? format_ref_marker($refs, $commit) : '';
@@ -1989,9 +2020,9 @@ sub git_shortlog_body {
href(action=>"commit", hash=>$commit), $ref);
print "</td>\n" .
"<td class=\"link\">" .
- $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " .
$cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " .
- $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree");
+ $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree") . " | " .
+ $cgi->a({-href => href(action=>"snapshot", hash=>$commit)}, "snapshot");
print "</td>\n" .
"</tr>\n";
}
@@ -2011,7 +2042,7 @@ sub git_history_body {
$to = $#{$revlist} unless (defined $to && $to <= $#{$revlist});
print "<table class=\"history\" cellspacing=\"0\">\n";
- my $alternate = 0;
+ my $alternate = 1;
for (my $i = $from; $i <= $to; $i++) {
if ($revlist->[$i] !~ m/^([0-9a-fA-F]{40})/) {
next;
@@ -2040,9 +2071,8 @@ sub git_history_body {
href(action=>"commit", hash=>$commit), $ref);
print "</td>\n" .
"<td class=\"link\">" .
- $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " .
- $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " .
- $cgi->a({-href => href(action=>$ftype, hash_base=>$commit, file_name=>$file_name)}, $ftype);
+ $cgi->a({-href => href(action=>$ftype, hash_base=>$commit, file_name=>$file_name)}, $ftype) . " | " .
+ $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff");
if ($ftype eq 'blob') {
my $blob_current = git_get_hash_by_path($hash_base, $file_name);
@@ -2075,7 +2105,7 @@ sub git_tags_body {
$to = $#{$taglist} if (!defined $to || $#{$taglist} < $to);
print "<table class=\"tags\" cellspacing=\"0\">\n";
- my $alternate = 0;
+ my $alternate = 1;
for (my $i = $from; $i <= $to; $i++) {
my $entry = $taglist->[$i];
my %tag = %$entry;
@@ -2135,7 +2165,7 @@ sub git_heads_body {
$to = $#{$headlist} if (!defined $to || $#{$headlist} < $to);
print "<table class=\"heads\" cellspacing=\"0\">\n";
- my $alternate = 0;
+ my $alternate = 1;
for (my $i = $from; $i <= $to; $i++) {
my $entry = $headlist->[$i];
my %tag = %$entry;
@@ -2251,7 +2281,7 @@ sub git_project_list {
}
print "<th></th>\n" .
"</tr>\n";
- my $alternate = 0;
+ my $alternate = 1;
foreach my $pr (@projects) {
if ($alternate) {
print "<tr class=\"dark\">\n";
@@ -2283,7 +2313,7 @@ sub git_project_index {
print $cgi->header(
-type => 'text/plain',
-charset => 'utf-8',
- -content_disposition => qq(inline; filename="index.aux"));
+ -content_disposition => 'inline; filename="index.aux"');
foreach my $pr (@projects) {
if (!exists $pr->{'owner'}) {
@@ -2629,7 +2659,7 @@ sub git_blob_plain {
print $cgi->header(
-type => "$type",
-expires=>$expires,
- -content_disposition => "inline; filename=\"$save_as\"");
+ -content_disposition => 'inline; filename="' . "$save_as" . '"');
undef $/;
binmode STDOUT, ':raw';
print <$fd>;
@@ -2713,17 +2743,16 @@ sub git_blob {
}
sub git_tree {
- my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
- my $have_snapshot = (defined $ctype && defined $suffix);
+ my $have_snapshot = gitweb_have_snapshot();
+ if (!defined $hash_base) {
+ $hash_base = "HEAD";
+ }
if (!defined $hash) {
- $hash = git_get_head_hash($project);
if (defined $file_name) {
- my $base = $hash_base || $hash;
- $hash = git_get_hash_by_path($base, $file_name, "tree");
- }
- if (!defined $hash_base) {
- $hash_base = $hash;
+ $hash = git_get_hash_by_path($hash_base, $file_name, "tree");
+ } else {
+ $hash = $hash_base;
}
}
$/ = "\0";
@@ -2769,7 +2798,7 @@ sub git_tree {
git_print_page_path($file_name, 'tree', $hash_base);
print "<div class=\"page_body\">\n";
print "<table cellspacing=\"0\">\n";
- my $alternate = 0;
+ my $alternate = 1;
foreach my $line (@entries) {
my %t = parse_ls_tree_line($line, -z => 1);
@@ -2790,7 +2819,6 @@ sub git_tree {
}
sub git_snapshot {
-
my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
my $have_snapshot = (defined $ctype && defined $suffix);
if (!$have_snapshot) {
@@ -2803,10 +2831,11 @@ sub git_snapshot {
my $filename = basename($project) . "-$hash.tar.$suffix";
- print $cgi->header(-type => 'application/x-tar',
- -content_encoding => $ctype,
- -content_disposition => "inline; filename=\"$filename\"",
- -status => '200 OK');
+ print $cgi->header(
+ -type => 'application/x-tar',
+ -content_encoding => $ctype,
+ -content_disposition => 'inline; filename="' . "$filename" . '"',
+ -status => '200 OK');
my $git_command = git_cmd_str();
open my $fd, "-|", "$git_command tar-tree $hash \'$project\' | $command" or
@@ -2899,12 +2928,10 @@ sub git_commit {
my $refs = git_get_references();
my $ref = format_ref_marker($refs, $co{'id'});
- my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
- my $have_snapshot = (defined $ctype && defined $suffix);
+ my $have_snapshot = gitweb_have_snapshot();
my @views_nav = ();
if (defined $file_name && defined $co{'parent'}) {
- my $parent = $co{'parent'};
push @views_nav,
$cgi->a({-href => href(action=>"blame", hash_parent=>$parent, file_name=>$file_name)},
"blame");
@@ -3116,7 +3143,7 @@ sub git_blobdiff {
-type => 'text/plain',
-charset => 'utf-8',
-expires => $expires,
- -content_disposition => qq(inline; filename=") . quotemeta($file_name) . qq(.patch"));
+ -content_disposition => 'inline; filename="' . "$file_name" . '.patch"');
print "X-Git-Url: " . $cgi->self_url() . "\n\n";
@@ -3219,7 +3246,7 @@ sub git_commitdiff {
-type => 'text/plain',
-charset => 'utf-8',
-expires => $expires,
- -content_disposition => qq(inline; filename="$filename"));
+ -content_disposition => 'inline; filename="' . "$filename" . '"');
my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'});
print <<TEXT;
From: $co{'author'}
@@ -3364,7 +3391,7 @@ sub git_search {
git_print_header_div('commit', esc_html($co{'title'}), $hash);
print "<table cellspacing=\"0\">\n";
- my $alternate = 0;
+ my $alternate = 1;
if ($commit_search) {
$/ = "\0";
open my $fd, "-|", git_cmd(), "rev-list", "--header", "--parents", $hash or next;
diff --git a/http.c b/http.c
index 6c1937b67..576740fef 100644
--- a/http.c
+++ b/http.c
@@ -23,6 +23,7 @@ char *ssl_capath = NULL;
char *ssl_cainfo = NULL;
long curl_low_speed_limit = -1;
long curl_low_speed_time = -1;
+int curl_ftp_no_epsv = 0;
struct curl_slist *pragma_header;
@@ -155,6 +156,11 @@ static int http_options(const char *var, const char *value)
return 0;
}
+ if (!strcmp("http.noepsv", var)) {
+ curl_ftp_no_epsv = git_config_bool(var, value);
+ return 0;
+ }
+
/* Fall back on the default ones */
return git_default_config(var, value);
}
@@ -196,6 +202,9 @@ static CURL* get_curl_handle(void)
curl_easy_setopt(result, CURLOPT_USERAGENT, GIT_USER_AGENT);
+ if (curl_ftp_no_epsv)
+ curl_easy_setopt(result, CURLOPT_FTP_USE_EPSV, 0);
+
return result;
}
@@ -251,6 +260,9 @@ void http_init(void)
max_requests = DEFAULT_MAX_REQUESTS;
#endif
+ if (getenv("GIT_CURL_FTP_NO_EPSV"))
+ curl_ftp_no_epsv = 1;
+
#ifndef NO_CURL_EASY_DUPHANDLE
curl_default = get_curl_handle();
#endif
diff --git a/interpolate.c b/interpolate.c
index 4570c123d..5d9d1889f 100644
--- a/interpolate.c
+++ b/interpolate.c
@@ -4,9 +4,35 @@
#include <string.h>
+#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;
+
+ if (oldval)
+ 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,
diff --git a/interpolate.h b/interpolate.h
index d16f9244f..190a180b5 100644
--- a/interpolate.h
+++ b/interpolate.h
@@ -16,6 +16,9 @@ struct interp {
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 int interpolate(char *result, int reslen,
const char *orig,
const struct interp *interps, int ninterps);