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
  ...
This commit is contained in:
Junio C Hamano 2006-10-01 00:34:58 -07:00
commit 12f9b8a315
22 changed files with 1203 additions and 256 deletions

View File

@ -202,6 +202,12 @@ http.lowSpeedLimit, http.lowSpeedTime::
Can be overridden by the 'GIT_HTTP_LOW_SPEED_LIMIT' and Can be overridden by the 'GIT_HTTP_LOW_SPEED_LIMIT' and
'GIT_HTTP_LOW_SPEED_TIME' environment variables. '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:: i18n.commitEncoding::
Character encoding the commit messages are stored in; git itself Character encoding the commit messages are stored in; git itself
does not care per se, but this information is necessary e.g. when does not care per se, but this information is necessary e.g. when

View File

@ -10,8 +10,11 @@
--patch-with-raw:: --patch-with-raw::
Synonym for "-p --raw". Synonym for "-p --raw".
--stat:: --stat[=width[,name-width]]::
Generate a diffstat. 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:: --summary::
Output a condensed summary of extended header information Output a condensed summary of extended header information

View File

@ -8,14 +8,15 @@ git-daemon - A really simple server for git repositories
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all] 'git-daemon' [--verbose] [--syslog] [--export-all]
[--timeout=n] [--init-timeout=n] [--strict-paths] [--timeout=n] [--init-timeout=n] [--strict-paths]
[--base-path=path] [--user-path | --user-path=path] [--base-path=path] [--user-path | --user-path=path]
[--interpolated-path=pathtemplate] [--interpolated-path=pathtemplate]
[--reuseaddr] [--detach] [--pid-file=file]
[--enable=service] [--disable=service] [--enable=service] [--disable=service]
[--allow-override=service] [--forbid-override=service] [--allow-override=service] [--forbid-override=service]
[--reuseaddr] [--detach] [--pid-file=file] [--inetd | [--listen=host_or_ipaddr] [--port=n] [--user=user [--group=group]]
[--user=user [--group=group]] [directory...] [directory...]
DESCRIPTION DESCRIPTION
----------- -----------
@ -54,8 +55,12 @@ OPTIONS
--interpolated-path=pathtemplate:: --interpolated-path=pathtemplate::
To support virtual hosting, an interpolated path template can be To support virtual hosting, an interpolated path template can be
used to dynamically construct alternate paths. The template 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. and %D for the absolute path of the named repository.
After interpolation, the path is validated against the directory
whitelist.
--export-all:: --export-all::
Allow pulling from all directories that look like GIT repositories Allow pulling from all directories that look like GIT repositories
@ -64,9 +69,17 @@ OPTIONS
--inetd:: --inetd::
Have the server run as an inetd service. Implies --syslog. Have the server run as an inetd service. Implies --syslog.
Incompatible with --port, --listen, --user and --group options.
--port:: --listen=host_or_ipaddr::
Listen on an alternative port. 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=n::
Listen on an alternative port. Incompatible with '--inetd' option.
--init-timeout:: --init-timeout::
Timeout between the moment the connection is established and the 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. 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 Author
------ ------
Written by Linus Torvalds <torvalds@osdl.org>, YOSHIFUJI Hideaki Written by Linus Torvalds <torvalds@osdl.org>, YOSHIFUJI Hideaki

View File

@ -525,6 +525,9 @@ endif
ifdef NO_INET_NTOP ifdef NO_INET_NTOP
LIB_OBJS += compat/inet_ntop.o LIB_OBJS += compat/inet_ntop.o
endif endif
ifdef NO_INET_PTON
LIB_OBJS += compat/inet_pton.o
endif
ifdef NO_ICONV ifdef NO_ICONV
ALL_CFLAGS += -DNO_ICONV ALL_CFLAGS += -DNO_ICONV

View File

@ -348,6 +348,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (!rev.diffopt.output_format) if (!rev.diffopt.output_format)
rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH; rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;
if (!output_directory)
output_directory = prefix;
if (output_directory) { if (output_directory) {
if (use_stdout) if (use_stdout)
die("standard output, or directory, which one?"); die("standard output, or directory, which one?");

220
compat/inet_pton.c Normal file
View File

@ -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 */
}

View File

@ -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

179
daemon.c
View File

@ -9,23 +9,30 @@
#include <syslog.h> #include <syslog.h>
#include <pwd.h> #include <pwd.h>
#include <grp.h> #include <grp.h>
#include <limits.h>
#include "pkt-line.h" #include "pkt-line.h"
#include "cache.h" #include "cache.h"
#include "exec_cmd.h" #include "exec_cmd.h"
#include "interpolate.h" #include "interpolate.h"
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 256
#endif
static int log_syslog; static int log_syslog;
static int verbose; static int verbose;
static int reuseaddr; static int reuseaddr;
static const char daemon_usage[] = 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" " [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
" [--base-path=path] [--user-path | --user-path=path]\n" " [--base-path=path] [--user-path | --user-path=path]\n"
" [--interpolated-path=path]\n" " [--interpolated-path=path]\n"
" [--reuseaddr] [--detach] [--pid-file=file]\n" " [--reuseaddr] [--detach] [--pid-file=file]\n"
" [--[enable|disable|allow-override|forbid-override]=service]\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 */ /* List of acceptable pathname prefixes */
static char **ok_paths; static char **ok_paths;
@ -56,13 +63,19 @@ static unsigned int init_timeout;
* Feel free to make dynamic as needed. * Feel free to make dynamic as needed.
*/ */
#define INTERP_SLOT_HOST (0) #define INTERP_SLOT_HOST (0)
#define INTERP_SLOT_DIR (1) #define INTERP_SLOT_CANON_HOST (1)
#define INTERP_SLOT_PERCENT (2) #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[] = { static struct interp interp_table[] = {
{ "%H", 0}, { "%H", 0},
{ "%CH", 0},
{ "%IP", 0},
{ "%P", 0},
{ "%D", 0}, { "%D", 0},
{ "%%", "%"}, { "%%", 0},
}; };
@ -396,7 +409,11 @@ static void make_service_overridable(const char *name, int ena) {
die("No such service %s", name); 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; char *val;
int vallen; int vallen;
@ -408,16 +425,88 @@ static void parse_extra_args(char *extra_args, int buflen)
val = extra_args + 5; val = extra_args + 5;
vallen = strlen(val) + 1; vallen = strlen(val) + 1;
if (*val) { if (*val) {
char *save = xmalloc(vallen); /* Split <host>:<port> at colon. */
interp_table[INTERP_SLOT_HOST].value = save; char *host = val;
strlcpy(save, val, vallen); 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 */ /* On to the next one */
extra_args = val + vallen; 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 int execute(struct sockaddr *addr)
{ {
static char line[1000]; static char line[1000];
@ -458,8 +547,16 @@ static int execute(struct sockaddr *addr)
if (len && line[len-1] == '\n') if (len && line[len-1] == '\n')
line[--len] = 0; 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++) { for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
struct daemon_service *s = &(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) && if (!strncmp("git-", line, 4) &&
!strncmp(s->name, line + 4, namelen) && !strncmp(s->name, line + 4, namelen) &&
line[namelen + 4] == ' ') { 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); return run_service(interp_table, s);
} }
} }
@ -663,23 +765,22 @@ static int set_reuse_addr(int sockfd)
#ifndef NO_IPV6 #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 socknum = 0, *socklist = NULL;
int maxfd = -1; int maxfd = -1;
char pbuf[NI_MAXSERV]; char pbuf[NI_MAXSERV];
struct addrinfo hints, *ai0, *ai; struct addrinfo hints, *ai0, *ai;
int gai; int gai;
sprintf(pbuf, "%d", port); sprintf(pbuf, "%d", listen_port);
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP; hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE; hints.ai_flags = AI_PASSIVE;
gai = getaddrinfo(NULL, pbuf, &hints, &ai0); gai = getaddrinfo(listen_addr, pbuf, &hints, &ai0);
if (gai) if (gai)
die("getaddrinfo() failed: %s\n", gai_strerror(gai)); die("getaddrinfo() failed: %s\n", gai_strerror(gai));
@ -733,20 +834,27 @@ static int socksetup(int port, int **socklist_p)
#else /* NO_IPV6 */ #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; struct sockaddr_in sin;
int sockfd; 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); sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) if (sockfd < 0)
return 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)) { if (set_reuse_addr(sockfd)) {
close(sockfd); close(sockfd);
return 0; return 0;
@ -855,13 +963,14 @@ static void store_pid(const char *path)
fclose(f); 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; int socknum, *socklist;
socknum = socksetup(port, &socklist); socknum = socksetup(listen_addr, listen_port, &socklist);
if (socknum == 0) 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 && if (pass && gid &&
(initgroups(pass->pw_name, gid) || setgid (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 main(int argc, char **argv)
{ {
int port = DEFAULT_GIT_PORT; int listen_port = 0;
char *listen_addr = NULL;
int inetd_mode = 0; int inetd_mode = 0;
const char *pid_file = NULL, *user_name = NULL, *group_name = NULL; const char *pid_file = NULL, *user_name = NULL, *group_name = NULL;
int detach = 0; int detach = 0;
@ -890,12 +1000,20 @@ int main(int argc, char **argv)
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
char *arg = argv[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)) { if (!strncmp(arg, "--port=", 7)) {
char *end; char *end;
unsigned long n; unsigned long n;
n = strtoul(arg+7, &end, 0); n = strtoul(arg+7, &end, 0);
if (arg[7] && !*end) { if (arg[7] && !*end) {
port = n; listen_port = n;
continue; continue;
} }
} }
@ -995,6 +1113,11 @@ int main(int argc, char **argv)
if (inetd_mode && (group_name || user_name)) if (inetd_mode && (group_name || user_name))
die("--user and --group are incompatible with --inetd"); 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) if (group_name && !user_name)
die("--group supplied without --user"); die("--group supplied without --user");
@ -1043,5 +1166,5 @@ int main(int argc, char **argv)
if (pid_file) if (pid_file)
store_pid(pid_file); store_pid(pid_file);
return serve(port, pass, gid); return serve(listen_addr, listen_port, pass, gid);
} }

63
date.c
View File

@ -256,8 +256,12 @@ static int match_alpha(const char *date, struct tm *tm, int *offset)
} }
if (match_string(date, "PM") == 2) { if (match_string(date, "PM") == 2) {
if (tm->tm_hour > 0 && tm->tm_hour < 12) tm->tm_hour = (tm->tm_hour % 12) + 12;
tm->tm_hour += 12; return 2;
}
if (match_string(date, "AM") == 2) {
tm->tm_hour = (tm->tm_hour % 12) + 0;
return 2; return 2;
} }
@ -598,6 +602,34 @@ static void date_tea(struct tm *tm, int *num)
date_time(tm, 17); 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 { static const struct special {
const char *name; const char *name;
void (*fn)(struct tm *, int *); void (*fn)(struct tm *, int *);
@ -606,6 +638,8 @@ static const struct special {
{ "noon", date_noon }, { "noon", date_noon },
{ "midnight", date_midnight }, { "midnight", date_midnight },
{ "tea", date_tea }, { "tea", date_tea },
{ "PM", date_pm },
{ "AM", date_am },
{ NULL } { NULL }
}; };
@ -712,6 +746,27 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)
return end; 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) unsigned long approxidate(const char *date)
{ {
int number = 0; int number = 0;
@ -731,9 +786,7 @@ unsigned long approxidate(const char *date)
break; break;
date++; date++;
if (isdigit(c)) { if (isdigit(c)) {
char *end; date = approxidate_digit(date-1, &tm, &number);
number = strtoul(date-1, &end, 10);
date = end;
continue; continue;
} }
if (isalpha(c)) if (isalpha(c))

168
diff.c
View File

@ -208,7 +208,7 @@ static void emit_rewrite_diff(const char *name_a,
diff_populate_filespec(two, 0); diff_populate_filespec(two, 0);
lc_a = count_lines(one->data, one->size); lc_a = count_lines(one->data, one->size);
lc_b = count_lines(two->data, two->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); print_line_count(lc_a);
printf(" +"); printf(" +");
print_line_count(lc_b); print_line_count(lc_b);
@ -635,21 +635,76 @@ static void diffstat_consume(void *priv, char *line, unsigned long len)
x->deleted++; x->deleted++;
} }
static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
static const char minuses[]= "----------------------------------------------------------------------";
const char mime_boundary_leader[] = "------------"; 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 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 total_files = data->nr;
int width, name_width;
const char *reset, *set, *add_c, *del_c;
if (data->nr == 0) if (data->nr == 0)
return; 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++) { for (i = 0; i < data->nr; i++) {
struct diffstat_file *file = data->files[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); len = strlen(file->name);
if (max_len < len) if (max_len < len)
@ -657,54 +712,53 @@ static void show_stats(struct diffstat_t* data)
if (file->is_binary || file->is_unmerged) if (file->is_binary || file->is_unmerged)
continue; continue;
if (max_change < file->added + file->deleted) if (max_change < change)
max_change = file->added + file->deleted; 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++) { for (i = 0; i < data->nr; i++) {
const char *prefix = ""; const char *prefix = "";
char *name = data->files[i]->name; char *name = data->files[i]->name;
int added = data->files[i]->added; int added = data->files[i]->added;
int deleted = data->files[i]->deleted; int deleted = data->files[i]->deleted;
int name_len;
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;
}
/* /*
* "scale" the filename * "scale" the filename
*/ */
len = strlen(name); len = name_width;
max = max_len; name_len = strlen(name);
if (max > 50) if (name_width < name_len) {
max = 50;
if (len > max) {
char *slash; char *slash;
prefix = "..."; prefix = "...";
max -= 3; len -= 3;
name += len - max; name += name_len - len;
slash = strchr(name, '/'); slash = strchr(name, '/');
if (slash) if (slash)
name = 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) { 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; goto free_diffstat_file;
} }
else if (data->files[i]->is_unmerged) { 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; goto free_diffstat_file;
} }
else if (!data->files[i]->is_renamed && else if (!data->files[i]->is_renamed &&
@ -713,27 +767,32 @@ static void show_stats(struct diffstat_t* data)
goto free_diffstat_file; goto free_diffstat_file;
} }
/*
* scale the add/delete
*/
add = added; add = added;
del = deleted; del = deleted;
total = add + del; total = add + del;
adds += add; adds += add;
dels += del; dels += del;
if (max_change > 0) { if (width <= max_change) {
total = (total * max + max_change / 2) / max_change; add = scale_linear(add, width, max_change);
add = (add * max + max_change / 2) / max_change; del = scale_linear(del, width, max_change);
del = total - add; total = add + del;
} }
printf(" %s%-*s |%5d %.*s%.*s\n", prefix, show_name(prefix, name, len, reset, set);
len, name, added + deleted, printf("%5d ", added + deleted);
add, pluses, del, minuses); show_graph('+', add, add_c, reset);
show_graph('-', del, del_c, reset);
putchar('\n');
free_diffstat_file: free_diffstat_file:
free(data->files[i]->name); free(data->files[i]->name);
free(data->files[i]); free(data->files[i]);
} }
free(data->files); free(data->files);
printf(" %d files changed, %d insertions(+), %d deletions(-)\n", printf("%s %d files changed, %d insertions(+), %d deletions(-)%s\n",
total_files, adds, dels); set, total_files, adds, dels, reset);
} }
struct checkdiff_t { 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")) { else if (!strcmp(arg, "--patch-with-raw")) {
options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_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->output_format |= DIFF_FORMAT_DIFFSTAT;
options->stat_name_width = name_width;
options->stat_width = width;
}
else if (!strcmp(arg, "--check")) else if (!strcmp(arg, "--check"))
options->output_format |= DIFF_FORMAT_CHECKDIFF; options->output_format |= DIFF_FORMAT_CHECKDIFF;
else if (!strcmp(arg, "--summary")) else if (!strcmp(arg, "--summary"))
@ -2528,7 +2612,7 @@ void diff_flush(struct diff_options *options)
if (check_pair_status(p)) if (check_pair_status(p))
diff_flush_stat(p, options, &diffstat); diff_flush_stat(p, options, &diffstat);
} }
show_stats(&diffstat); show_stats(&diffstat, options);
separator++; separator++;
} }

3
diff.h
View File

@ -69,6 +69,9 @@ struct diff_options {
const char *stat_sep; const char *stat_sep;
long xdl_opts; long xdl_opts;
int stat_width;
int stat_name_width;
int nr_paths; int nr_paths;
const char **paths; const char **paths;
int *pathlens; int *pathlens;

View File

@ -31,6 +31,10 @@ clone_dumb_http () {
cd "$2" && cd "$2" &&
clone_tmp="$GIT_DIR/clone-tmp" && clone_tmp="$GIT_DIR/clone-tmp" &&
mkdir -p "$clone_tmp" || exit 1 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" || { http_fetch "$1/info/refs" "$clone_tmp/refs" || {
echo >&2 "Cannot get remote repository information. echo >&2 "Cannot get remote repository information.
Perhaps git-update-server-info needs to be run there?" Perhaps git-update-server-info needs to be run there?"

View File

@ -135,7 +135,7 @@ foreach my $f (@files) {
if ($fields[4] eq 'M') { if ($fields[4] eq 'M') {
push @mfiles, $fields[5]; push @mfiles, $fields[5];
} }
if ($fields[4] eq 'R') { if ($fields[4] eq 'D') {
push @dfiles, $fields[5]; push @dfiles, $fields[5];
} }
} }

View File

@ -129,22 +129,25 @@ append_fetch_head () {
then then
headc_=$(git-rev-parse --verify "$head_^0") || exit headc_=$(git-rev-parse --verify "$head_^0") || exit
echo "$headc_ $not_for_merge_ $note_" >>"$GIT_DIR/FETCH_HEAD" echo "$headc_ $not_for_merge_ $note_" >>"$GIT_DIR/FETCH_HEAD"
[ "$verbose" ] && echo >&2 "* committish: $head_"
[ "$verbose" ] && echo >&2 " $note_"
else else
echo "$head_ not-for-merge $note_" >>"$GIT_DIR/FETCH_HEAD" 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 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")" mkdir -p "$(dirname "$GIT_DIR/$1")"
case "$1" in case "$1" in
refs/tags/*) refs/tags/*)
@ -154,13 +157,16 @@ fast_forward_local () {
then then
if now_=$(cat "$GIT_DIR/$1") && test "$now_" = "$2" if now_=$(cat "$GIT_DIR/$1") && test "$now_" = "$2"
then then
[ "$verbose" ] && echo >&2 "* $1: same as $3" ||: [ "$verbose" ] && echo >&2 "* $1: same as $3"
[ "$verbose" ] && echo >&2 " $label_: $newshort_" ||:
else else
echo >&2 "* $1: updating with $3" echo >&2 "* $1: updating with $3"
echo >&2 " $label_: $newshort_"
git-update-ref -m "$rloga: updating tag" "$1" "$2" git-update-ref -m "$rloga: updating tag" "$1" "$2"
fi fi
else else
echo >&2 "* $1: storing $3" echo >&2 "* $1: storing $3"
echo >&2 " $label_: $newshort_"
git-update-ref -m "$rloga: storing tag" "$1" "$2" git-update-ref -m "$rloga: storing tag" "$1" "$2"
fi fi
;; ;;
@ -178,31 +184,34 @@ fast_forward_local () {
if test -n "$verbose" if test -n "$verbose"
then then
echo >&2 "* $1: same as $3" echo >&2 "* $1: same as $3"
echo >&2 " $label_: $newshort_"
fi fi
;; ;;
*,$local) *,$local)
echo >&2 "* $1: fast forward to $3" 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" git-update-ref -m "$rloga: fast-forward" "$1" "$2" "$local"
;; ;;
*) *)
false false
;; ;;
esac || { esac || {
echo >&2 "* $1: does not fast forward to $3;"
case ",$force,$single_force," in case ",$force,$single_force," in
*,t,*) *,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" 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 exit 1
;; ;;
esac esac
} }
else else
echo >&2 "* $1: storing $3" echo >&2 "* $1: storing $3"
echo >&2 " $label_: $newshort_"
git-update-ref -m "$rloga: storing head" "$1" "$2" git-update-ref -m "$rloga: storing head" "$1" "$2"
fi fi
;; ;;
@ -257,6 +266,7 @@ fi
fetch_main () { fetch_main () {
reflist="$1" reflist="$1"
refs= refs=
rref=
for ref in $reflist for ref in $reflist
do do
@ -289,6 +299,10 @@ fetch_main () {
if [ -n "$GIT_SSL_NO_VERIFY" ]; then if [ -n "$GIT_SSL_NO_VERIFY" ]; then
curl_extra_args="-k" curl_extra_args="-k"
fi 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 max_depth=5
depth=0 depth=0
head="ref: $remote_name" head="ref: $remote_name"
@ -300,7 +314,7 @@ fetch_main () {
$u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg; $u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg;
print "$u"; print "$u";
' "$head") ' "$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 \) ) depth=$( expr \( $depth + 1 \) )
done done
expr "z$head" : "z$_x40\$" >/dev/null || expr "z$head" : "z$_x40\$" >/dev/null ||

View File

@ -53,6 +53,10 @@ http://* | https://* | ftp://* )
if [ -n "$GIT_SSL_NO_VERIFY" ]; then if [ -n "$GIT_SSL_NO_VERIFY" ]; then
curl_extra_args="-k" curl_extra_args="-k"
fi 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" || curl -nsf $curl_extra_args --header "Pragma: no-cache" "$peek_repo/info/refs" ||
echo "failed slurping" echo "failed slurping"
;; ;;

View File

@ -197,7 +197,7 @@ f,*)
;; ;;
?,1,"$head",*) ?,1,"$head",*)
# Again the most common case of merging one remote. # 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 git-update-index --refresh 2>/dev/null
new_head=$(git-rev-parse --verify "$1^0") && new_head=$(git-rev-parse --verify "$1^0") &&
git-read-tree -u -v -m $head "$new_head" && git-read-tree -u -v -m $head "$new_head" &&

View File

@ -46,7 +46,7 @@ case "$common" in
exit 0 exit 0
;; ;;
"$head") "$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-read-tree -u -m $head $merge || exit 1
git-update-ref -m "resolve $merge_name: Fast forward" \ git-update-ref -m "resolve $merge_name: Fast forward" \
HEAD "$merge" "$head" HEAD "$merge" "$head"

View File

@ -31,7 +31,7 @@ $SIG{'PIPE'}="IGNORE";
$ENV{'TZ'}="UTC"; $ENV{'TZ'}="UTC";
our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T, 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() { sub usage() {
print STDERR <<END; print STDERR <<END;
@ -39,12 +39,12 @@ Usage: ${\basename $0} # fetch/update GIT from SVN
[-o branch-for-HEAD] [-h] [-v] [-l max_rev] [-o branch-for-HEAD] [-h] [-v] [-l max_rev]
[-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname] [-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname]
[-d|-D] [-i] [-u] [-r] [-I ignorefilename] [-s start_chg] [-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 END
exit(1); 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; usage if $opt_h;
my $tag_name = $opt_t || "tags"; my $tag_name = $opt_t || "tags";
@ -548,8 +548,12 @@ sub commit {
$committer_name = $committer_email = $author; $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); ($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 { } else {
$author_name = $committer_name; $author_name = $committer_name;
$author_email = $committer_email; $author_email = $committer_email;

View File

@ -106,7 +106,7 @@ our %feature = (
sub gitweb_check_feature { sub gitweb_check_feature {
my ($name) = @_; my ($name) = @_;
return undef unless exists $feature{$name}; return unless exists $feature{$name};
my ($sub, $override, @defaults) = ( my ($sub, $override, @defaults) = (
$feature{$name}{'sub'}, $feature{$name}{'sub'},
$feature{$name}{'override'}, $feature{$name}{'override'},
@ -155,6 +155,13 @@ sub feature_snapshot {
return ($ctype, $suffix, $command); 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 # To enable system wide have in $GITWEB_CONFIG
# $feature{'pickaxe'}{'default'} = [1]; # $feature{'pickaxe'}{'default'} = [1];
# To have project specific config enable override in $GITWEB_CONFIG # 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'); our $project = $cgi->param('p');
if (defined $project) { if (defined $project) {
if (!validate_input($project) || if (!validate_pathname($project) ||
!(-d "$projectroot/$project") || !(-d "$projectroot/$project") ||
!(-e "$projectroot/$project/HEAD") || !(-e "$projectroot/$project/HEAD") ||
($export_ok && !(-e "$projectroot/$project/$export_ok")) || ($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'); our $file_name = $cgi->param('f');
our $file_parent = $cgi->param('fp'); 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'); our $hash = $cgi->param('h');
if (defined $hash) { if (defined $hash) {
if (!validate_input($hash)) { if (!validate_refname($hash)) {
die_error(undef, "Invalid hash parameter"); die_error(undef, "Invalid hash parameter");
} }
} }
our $hash_parent = $cgi->param('hp'); our $hash_parent = $cgi->param('hp');
if (defined $hash_parent) { if (defined $hash_parent) {
if (!validate_input($hash_parent)) { if (!validate_refname($hash_parent)) {
die_error(undef, "Invalid hash parent parameter"); die_error(undef, "Invalid hash parent parameter");
} }
} }
our $hash_base = $cgi->param('hb'); our $hash_base = $cgi->param('hb');
if (defined $hash_base) { if (defined $hash_base) {
if (!validate_input($hash_base)) { if (!validate_refname($hash_base)) {
die_error(undef, "Invalid hash base parameter"); die_error(undef, "Invalid hash base parameter");
} }
} }
our $hash_parent_base = $cgi->param('hpb'); our $hash_parent_base = $cgi->param('hpb');
if (defined $hash_parent_base) { 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"); die_error(undef, "Invalid hash parent base parameter");
} }
} }
# other parameters
our $page = $cgi->param('pg'); our $page = $cgi->param('pg');
if (defined $page) { if (defined $page) {
if ($page =~ m/[^0-9]/) { if ($page =~ m/[^0-9]/) {
@ -273,7 +293,7 @@ sub evaluate_path_info {
$project =~ s,/*[^/]*$,,; $project =~ s,/*[^/]*$,,;
} }
# validate project # validate project
$project = validate_input($project); $project = validate_pathname($project);
if (!$project || if (!$project ||
($export_ok && !-e "$projectroot/$project/$export_ok") || ($export_ok && !-e "$projectroot/$project/$export_ok") ||
($strict_export && !project_in_list($project))) { ($strict_export && !project_in_list($project))) {
@ -294,12 +314,12 @@ sub evaluate_path_info {
} else { } else {
$action ||= "blob_plain"; $action ||= "blob_plain";
} }
$hash_base ||= validate_input($refname); $hash_base ||= validate_refname($refname);
$file_name ||= $pathname; $file_name ||= validate_pathname($pathname);
} elsif (defined $refname) { } elsif (defined $refname) {
# we got "project.git/branch" # we got "project.git/branch"
$action ||= "shortlog"; $action ||= "shortlog";
$hash ||= validate_input($refname); $hash ||= validate_refname($refname);
} }
} }
evaluate_path_info(); evaluate_path_info();
@ -387,16 +407,34 @@ sub href(%) {
## ====================================================================== ## ======================================================================
## validation, quoting/unquoting and escaping ## validation, quoting/unquoting and escaping
sub validate_input { sub validate_pathname {
my $input = shift; my $input = shift || return undef;
# 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;
}
# no null characters
if ($input =~ m!\0!) {
return undef;
}
return $input;
}
sub validate_refname {
my $input = shift || return undef;
# textual hashes are O.K.
if ($input =~ m/^[0-9a-fA-F]{40}$/) { if ($input =~ m/^[0-9a-fA-F]{40}$/) {
return $input; return $input;
} }
if ($input =~ m/(^|\/)(|\.|\.\.)($|\/)/) { # it must be correct pathname
return undef; $input = validate_pathname($input)
} or return undef;
if ($input =~ m/[^a-zA-Z0-9_\x80-\xff\ \t\.\/\-\+\#\~\%]/) { # restrictions on ref name according to git-check-ref-format
if ($input =~ m!(/\.|\.\.|[\000-\040\177 ~^:?*\[]|/$)!) {
return undef; return undef;
} }
return $input; return $input;
@ -412,6 +450,15 @@ sub esc_param {
return $str; 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 # replace invalid utf8 character with SUBSTITUTION sequence
sub esc_html { sub esc_html {
my $str = shift; my $str = shift;
@ -710,7 +757,7 @@ sub git_get_hash_by_path {
my $path = shift || return undef; my $path = shift || return undef;
my $type = shift; my $type = shift;
my $tree = $base; $path =~ s,/+$,,;
open my $fd, "-|", git_cmd(), "ls-tree", $base, "--", $path open my $fd, "-|", git_cmd(), "ls-tree", $base, "--", $path
or die_error(undef, "Open git-ls-tree failed"); or die_error(undef, "Open git-ls-tree failed");
@ -781,7 +828,7 @@ sub git_get_projects_list {
# 'git%2Fgit.git Linus+Torvalds' # 'git%2Fgit.git Linus+Torvalds'
# 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin' # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
# 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman' # '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>) { while (my $line = <$fd>) {
chomp $line; chomp $line;
my ($path, $owner) = split ' ', $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\">" . "<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;\"/>" . "<img src=\"$logo\" width=\"72\" height=\"27\" alt=\"git\" style=\"float:right; border-width:0px;\"/>" .
"</a>\n"; "</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) { if (defined $project) {
print $cgi->a({-href => href(action=>"summary")}, esc_html($project)); print $cgi->a({-href => href(action=>"summary")}, esc_html($project));
if (defined $action) { if (defined $action) {
@ -1600,48 +1647,45 @@ sub git_print_tree_entry {
my %base_key = (); my %base_key = ();
$base_key{hash_base} = $hash_base if defined $hash_base; $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"; print "<td class=\"mode\">" . mode_str($t->{'mode'}) . "</td>\n";
if ($t->{'type'} eq "blob") { if ($t->{'type'} eq "blob") {
print "<td class=\"list\">" . print "<td class=\"list\">" .
$cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'}, $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
file_name=>"$basedir$t->{'name'}", %base_key), file_name=>"$basedir$t->{'name'}", %base_key),
-class => "list"}, esc_html($t->{'name'})) . -class => "list"}, esc_html($t->{'name'})) . "</td>\n";
"</td>\n" . print "<td class=\"link\">";
"<td class=\"link\">" .
$cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
file_name=>"$basedir$t->{'name'}", %base_key)},
"blob");
if ($have_blame) { if ($have_blame) {
print " | " . print $cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'},
$cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}", %base_key)},
file_name=>"$basedir$t->{'name'}", %base_key)}, "blame");
"blame");
} }
if (defined $hash_base) { if (defined $hash_base) {
print " | " . if ($have_blame) {
$cgi->a({-href => href(action=>"history", hash_base=>$hash_base, print " | ";
}
print $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}")}, hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}")},
"history"); "history");
} }
print " | " . print " | " .
$cgi->a({-href => href(action=>"blob_plain", $cgi->a({-href => href(action=>"blob_plain", hash_base=>$hash_base,
hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}")}, file_name=>"$basedir$t->{'name'}")},
"raw") . "raw");
"</td>\n"; print "</td>\n";
} elsif ($t->{'type'} eq "tree") { } elsif ($t->{'type'} eq "tree") {
print "<td class=\"list\">" . print "<td class=\"list\">";
$cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'}, print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
file_name=>"$basedir$t->{'name'}", %base_key)}, file_name=>"$basedir$t->{'name'}", %base_key)},
esc_html($t->{'name'})) . esc_html($t->{'name'}));
"</td>\n" . print "</td>\n";
"<td class=\"link\">" . print "<td class=\"link\">";
$cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
file_name=>"$basedir$t->{'name'}", %base_key)},
"tree");
if (defined $hash_base) { if (defined $hash_base) {
print " | " . print $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
$cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
file_name=>"$basedir$t->{'name'}")}, file_name=>"$basedir$t->{'name'}")},
"history"); "history");
} }
@ -1662,7 +1706,7 @@ sub git_difftree_body {
print "</div>\n"; print "</div>\n";
print "<table class=\"diff_tree\">\n"; print "<table class=\"diff_tree\">\n";
my $alternate = 0; my $alternate = 1;
my $patchno = 0; my $patchno = 0;
foreach my $line (@{$difftree}) { foreach my $line (@{$difftree}) {
my %diff = parse_difftree_raw_line($line); 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"; 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 .= " with mode: $to_mode_str" if $to_mode_str;
$mode_chng .= "]</span>"; $mode_chng .= "]</span>";
print "<td>" . print "<td>";
$cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'}, print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
hash_base=>$hash, file_name=>$diff{'file'}), hash_base=>$hash, file_name=>$diff{'file'}),
-class => "list"}, esc_html($diff{'file'})) . -class => "list"}, esc_html($diff{'file'}));
"</td>\n" . print "</td>\n";
"<td>$mode_chng</td>\n" . print "<td>$mode_chng</td>\n";
"<td class=\"link\">" . print "<td class=\"link\">";
$cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
hash_base=>$hash, file_name=>$diff{'file'})},
"blob");
if ($action eq 'commitdiff') { if ($action eq 'commitdiff') {
# link to patch # link to patch
$patchno++; $patchno++;
print " | " . print $cgi->a({-href => "#patch$patchno"}, "patch");
$cgi->a({-href => "#patch$patchno"}, "patch");
} }
print "</td>\n"; print "</td>\n";
} elsif ($diff{'status'} eq "D") { # deleted } elsif ($diff{'status'} eq "D") { # deleted
my $mode_chng = "<span class=\"file_status deleted\">[deleted $from_file_type]</span>"; my $mode_chng = "<span class=\"file_status deleted\">[deleted $from_file_type]</span>";
print "<td>" . print "<td>";
$cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'}, print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
hash_base=>$parent, file_name=>$diff{'file'}), hash_base=>$parent, file_name=>$diff{'file'}),
-class => "list"}, esc_html($diff{'file'})) . -class => "list"}, esc_html($diff{'file'}));
"</td>\n" . print "</td>\n";
"<td>$mode_chng</td>\n" . print "<td>$mode_chng</td>\n";
"<td class=\"link\">" . print "<td class=\"link\">";
$cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
hash_base=>$parent, file_name=>$diff{'file'})},
"blob") .
" | ";
if ($action eq 'commitdiff') { if ($action eq 'commitdiff') {
# link to patch # link to patch
$patchno++; $patchno++;
print " | " . print $cgi->a({-href => "#patch$patchno"}, "patch");
$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, print $cgi->a({-href => href(action=>"history", hash_base=>$parent,
file_name=>$diff{'file'})}, file_name=>$diff{'file'})},
"history") . "history");
"</td>\n"; print "</td>\n";
} elsif ($diff{'status'} eq "M" || $diff{'status'} eq "T") { # modified, or type changed } elsif ($diff{'status'} eq "M" || $diff{'status'} eq "T") { # modified, or type changed
my $mode_chnge = ""; my $mode_chnge = "";
@ -1754,42 +1793,32 @@ sub git_difftree_body {
$mode_chnge .= "]</span>\n"; $mode_chnge .= "]</span>\n";
} }
print "<td>"; print "<td>";
if ($diff{'to_id'} ne $diff{'from_id'}) { # modified print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
print $cgi->a({-href => href(action=>"blobdiff", hash_base=>$hash, file_name=>$diff{'file'}),
hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, -class => "list"}, esc_html($diff{'file'}));
hash_base=>$hash, hash_parent_base=>$parent, print "</td>\n";
file_name=>$diff{'file'}), print "<td>$mode_chnge</td>\n";
-class => "list"}, esc_html($diff{'file'})); print "<td class=\"link\">";
} 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");
if ($diff{'to_id'} ne $diff{'from_id'}) { # modified if ($diff{'to_id'} ne $diff{'from_id'}) { # modified
if ($action eq 'commitdiff') { if ($action eq 'commitdiff') {
# link to patch # link to patch
$patchno++; $patchno++;
print " | " . print $cgi->a({-href => "#patch$patchno"}, "patch");
$cgi->a({-href => "#patch$patchno"}, "patch");
} else { } else {
print " | " . print $cgi->a({-href => href(action=>"blobdiff",
$cgi->a({-href => href(action=>"blobdiff", hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, hash_base=>$hash, hash_parent_base=>$parent,
hash_base=>$hash, hash_parent_base=>$parent, file_name=>$diff{'file'})},
file_name=>$diff{'file'})}, "diff");
"diff");
} }
print " | ";
} }
print " | " . print $cgi->a({-href => href(action=>"blame", hash_base=>$hash,
$cgi->a({-href => href(action=>"history", file_name=>$diff{'file'})},
hash_base=>$hash, file_name=>$diff{'file'})}, "blame") . " | ";
"history"); print $cgi->a({-href => href(action=>"history", hash_base=>$hash,
file_name=>$diff{'file'})},
"history");
print "</td>\n"; print "</td>\n";
} elsif ($diff{'status'} eq "R" || $diff{'status'} eq "C") { # renamed or copied } 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'}), hash=>$diff{'from_id'}, file_name=>$diff{'from_file'}),
-class => "list"}, esc_html($diff{'from_file'})) . -class => "list"}, esc_html($diff{'from_file'})) .
" with " . (int $diff{'similarity'}) . "% similarity$mode_chng]</span></td>\n" . " with " . (int $diff{'similarity'}) . "% similarity$mode_chng]</span></td>\n" .
"<td class=\"link\">" . "<td class=\"link\">";
$cgi->a({-href => href(action=>"blob", hash_base=>$hash,
hash=>$diff{'to_id'}, file_name=>$diff{'to_file'})},
"blob");
if ($diff{'to_id'} ne $diff{'from_id'}) { if ($diff{'to_id'} ne $diff{'from_id'}) {
if ($action eq 'commitdiff') { if ($action eq 'commitdiff') {
# link to patch # link to patch
$patchno++; $patchno++;
print " | " . print $cgi->a({-href => "#patch$patchno"}, "patch");
$cgi->a({-href => "#patch$patchno"}, "patch");
} else { } else {
print " | " . print $cgi->a({-href => href(action=>"blobdiff",
$cgi->a({-href => href(action=>"blobdiff", hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, hash_base=>$hash, hash_parent_base=>$parent,
hash_base=>$hash, hash_parent_base=>$parent, file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})},
file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})}, "diff");
"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"; print "</td>\n";
} # we should not encounter Unmerged (U) or Unknown (X) status } # 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); $to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);
print "<table class=\"shortlog\" cellspacing=\"0\">\n"; print "<table class=\"shortlog\" cellspacing=\"0\">\n";
my $alternate = 0; my $alternate = 1;
for (my $i = $from; $i <= $to; $i++) { for (my $i = $from; $i <= $to; $i++) {
my $commit = $revlist->[$i]; my $commit = $revlist->[$i];
#my $ref = defined $refs ? format_ref_marker($refs, $commit) : ''; #my $ref = defined $refs ? format_ref_marker($refs, $commit) : '';
@ -1989,9 +2020,9 @@ sub git_shortlog_body {
href(action=>"commit", hash=>$commit), $ref); href(action=>"commit", hash=>$commit), $ref);
print "</td>\n" . print "</td>\n" .
"<td class=\"link\">" . "<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=>"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" . print "</td>\n" .
"</tr>\n"; "</tr>\n";
} }
@ -2011,7 +2042,7 @@ sub git_history_body {
$to = $#{$revlist} unless (defined $to && $to <= $#{$revlist}); $to = $#{$revlist} unless (defined $to && $to <= $#{$revlist});
print "<table class=\"history\" cellspacing=\"0\">\n"; print "<table class=\"history\" cellspacing=\"0\">\n";
my $alternate = 0; my $alternate = 1;
for (my $i = $from; $i <= $to; $i++) { for (my $i = $from; $i <= $to; $i++) {
if ($revlist->[$i] !~ m/^([0-9a-fA-F]{40})/) { if ($revlist->[$i] !~ m/^([0-9a-fA-F]{40})/) {
next; next;
@ -2040,9 +2071,8 @@ sub git_history_body {
href(action=>"commit", hash=>$commit), $ref); href(action=>"commit", hash=>$commit), $ref);
print "</td>\n" . print "</td>\n" .
"<td class=\"link\">" . "<td class=\"link\">" .
$cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " . $cgi->a({-href => href(action=>$ftype, hash_base=>$commit, file_name=>$file_name)}, $ftype) . " | " .
$cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " . $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff");
$cgi->a({-href => href(action=>$ftype, hash_base=>$commit, file_name=>$file_name)}, $ftype);
if ($ftype eq 'blob') { if ($ftype eq 'blob') {
my $blob_current = git_get_hash_by_path($hash_base, $file_name); 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); $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to);
print "<table class=\"tags\" cellspacing=\"0\">\n"; print "<table class=\"tags\" cellspacing=\"0\">\n";
my $alternate = 0; my $alternate = 1;
for (my $i = $from; $i <= $to; $i++) { for (my $i = $from; $i <= $to; $i++) {
my $entry = $taglist->[$i]; my $entry = $taglist->[$i];
my %tag = %$entry; my %tag = %$entry;
@ -2135,7 +2165,7 @@ sub git_heads_body {
$to = $#{$headlist} if (!defined $to || $#{$headlist} < $to); $to = $#{$headlist} if (!defined $to || $#{$headlist} < $to);
print "<table class=\"heads\" cellspacing=\"0\">\n"; print "<table class=\"heads\" cellspacing=\"0\">\n";
my $alternate = 0; my $alternate = 1;
for (my $i = $from; $i <= $to; $i++) { for (my $i = $from; $i <= $to; $i++) {
my $entry = $headlist->[$i]; my $entry = $headlist->[$i];
my %tag = %$entry; my %tag = %$entry;
@ -2251,7 +2281,7 @@ sub git_project_list {
} }
print "<th></th>\n" . print "<th></th>\n" .
"</tr>\n"; "</tr>\n";
my $alternate = 0; my $alternate = 1;
foreach my $pr (@projects) { foreach my $pr (@projects) {
if ($alternate) { if ($alternate) {
print "<tr class=\"dark\">\n"; print "<tr class=\"dark\">\n";
@ -2283,7 +2313,7 @@ sub git_project_index {
print $cgi->header( print $cgi->header(
-type => 'text/plain', -type => 'text/plain',
-charset => 'utf-8', -charset => 'utf-8',
-content_disposition => qq(inline; filename="index.aux")); -content_disposition => 'inline; filename="index.aux"');
foreach my $pr (@projects) { foreach my $pr (@projects) {
if (!exists $pr->{'owner'}) { if (!exists $pr->{'owner'}) {
@ -2629,7 +2659,7 @@ sub git_blob_plain {
print $cgi->header( print $cgi->header(
-type => "$type", -type => "$type",
-expires=>$expires, -expires=>$expires,
-content_disposition => "inline; filename=\"$save_as\""); -content_disposition => 'inline; filename="' . "$save_as" . '"');
undef $/; undef $/;
binmode STDOUT, ':raw'; binmode STDOUT, ':raw';
print <$fd>; print <$fd>;
@ -2713,17 +2743,16 @@ sub git_blob {
} }
sub git_tree { sub git_tree {
my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot'); my $have_snapshot = gitweb_have_snapshot();
my $have_snapshot = (defined $ctype && defined $suffix);
if (!defined $hash_base) {
$hash_base = "HEAD";
}
if (!defined $hash) { if (!defined $hash) {
$hash = git_get_head_hash($project);
if (defined $file_name) { if (defined $file_name) {
my $base = $hash_base || $hash; $hash = git_get_hash_by_path($hash_base, $file_name, "tree");
$hash = git_get_hash_by_path($base, $file_name, "tree"); } else {
} $hash = $hash_base;
if (!defined $hash_base) {
$hash_base = $hash;
} }
} }
$/ = "\0"; $/ = "\0";
@ -2769,7 +2798,7 @@ sub git_tree {
git_print_page_path($file_name, 'tree', $hash_base); git_print_page_path($file_name, 'tree', $hash_base);
print "<div class=\"page_body\">\n"; print "<div class=\"page_body\">\n";
print "<table cellspacing=\"0\">\n"; print "<table cellspacing=\"0\">\n";
my $alternate = 0; my $alternate = 1;
foreach my $line (@entries) { foreach my $line (@entries) {
my %t = parse_ls_tree_line($line, -z => 1); my %t = parse_ls_tree_line($line, -z => 1);
@ -2790,7 +2819,6 @@ sub git_tree {
} }
sub git_snapshot { sub git_snapshot {
my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot'); my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
my $have_snapshot = (defined $ctype && defined $suffix); my $have_snapshot = (defined $ctype && defined $suffix);
if (!$have_snapshot) { if (!$have_snapshot) {
@ -2803,10 +2831,11 @@ sub git_snapshot {
my $filename = basename($project) . "-$hash.tar.$suffix"; my $filename = basename($project) . "-$hash.tar.$suffix";
print $cgi->header(-type => 'application/x-tar', print $cgi->header(
-content_encoding => $ctype, -type => 'application/x-tar',
-content_disposition => "inline; filename=\"$filename\"", -content_encoding => $ctype,
-status => '200 OK'); -content_disposition => 'inline; filename="' . "$filename" . '"',
-status => '200 OK');
my $git_command = git_cmd_str(); my $git_command = git_cmd_str();
open my $fd, "-|", "$git_command tar-tree $hash \'$project\' | $command" or open my $fd, "-|", "$git_command tar-tree $hash \'$project\' | $command" or
@ -2899,12 +2928,10 @@ sub git_commit {
my $refs = git_get_references(); my $refs = git_get_references();
my $ref = format_ref_marker($refs, $co{'id'}); my $ref = format_ref_marker($refs, $co{'id'});
my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot'); my $have_snapshot = gitweb_have_snapshot();
my $have_snapshot = (defined $ctype && defined $suffix);
my @views_nav = (); my @views_nav = ();
if (defined $file_name && defined $co{'parent'}) { if (defined $file_name && defined $co{'parent'}) {
my $parent = $co{'parent'};
push @views_nav, push @views_nav,
$cgi->a({-href => href(action=>"blame", hash_parent=>$parent, file_name=>$file_name)}, $cgi->a({-href => href(action=>"blame", hash_parent=>$parent, file_name=>$file_name)},
"blame"); "blame");
@ -3116,7 +3143,7 @@ sub git_blobdiff {
-type => 'text/plain', -type => 'text/plain',
-charset => 'utf-8', -charset => 'utf-8',
-expires => $expires, -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"; print "X-Git-Url: " . $cgi->self_url() . "\n\n";
@ -3219,7 +3246,7 @@ sub git_commitdiff {
-type => 'text/plain', -type => 'text/plain',
-charset => 'utf-8', -charset => 'utf-8',
-expires => $expires, -expires => $expires,
-content_disposition => qq(inline; filename="$filename")); -content_disposition => 'inline; filename="' . "$filename" . '"');
my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'}); my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'});
print <<TEXT; print <<TEXT;
From: $co{'author'} From: $co{'author'}
@ -3364,7 +3391,7 @@ sub git_search {
git_print_header_div('commit', esc_html($co{'title'}), $hash); git_print_header_div('commit', esc_html($co{'title'}), $hash);
print "<table cellspacing=\"0\">\n"; print "<table cellspacing=\"0\">\n";
my $alternate = 0; my $alternate = 1;
if ($commit_search) { if ($commit_search) {
$/ = "\0"; $/ = "\0";
open my $fd, "-|", git_cmd(), "rev-list", "--header", "--parents", $hash or next; open my $fd, "-|", git_cmd(), "rev-list", "--header", "--parents", $hash or next;

12
http.c
View File

@ -23,6 +23,7 @@ char *ssl_capath = NULL;
char *ssl_cainfo = NULL; char *ssl_cainfo = NULL;
long curl_low_speed_limit = -1; long curl_low_speed_limit = -1;
long curl_low_speed_time = -1; long curl_low_speed_time = -1;
int curl_ftp_no_epsv = 0;
struct curl_slist *pragma_header; struct curl_slist *pragma_header;
@ -155,6 +156,11 @@ static int http_options(const char *var, const char *value)
return 0; return 0;
} }
if (!strcmp("http.noepsv", var)) {
curl_ftp_no_epsv = git_config_bool(var, value);
return 0;
}
/* Fall back on the default ones */ /* Fall back on the default ones */
return git_default_config(var, value); 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); 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; return result;
} }
@ -251,6 +260,9 @@ void http_init(void)
max_requests = DEFAULT_MAX_REQUESTS; max_requests = DEFAULT_MAX_REQUESTS;
#endif #endif
if (getenv("GIT_CURL_FTP_NO_EPSV"))
curl_ftp_no_epsv = 1;
#ifndef NO_CURL_EASY_DUPHANDLE #ifndef NO_CURL_EASY_DUPHANDLE
curl_default = get_curl_handle(); curl_default = get_curl_handle();
#endif #endif

View File

@ -4,9 +4,35 @@
#include <string.h> #include <string.h>
#include "git-compat-util.h"
#include "interpolate.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 * Convert a NUL-terminated string in buffer orig
* into the supplied buffer, result, whose length is reslen, * into the supplied buffer, result, whose length is reslen,

View File

@ -16,6 +16,9 @@ struct interp {
char *value; 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, extern int interpolate(char *result, int reslen,
const char *orig, const char *orig,
const struct interp *interps, int ninterps); const struct interp *interps, int ninterps);