mirror of
https://github.com/git/git.git
synced 2024-11-27 20:14:30 +08:00
Merge branch 'master' into lj/refs
* master: (99 commits) lock_ref_sha1_basic does not remove empty directories on BSD git-push: .git/remotes/ file does not require SP after colon git-mv: invalidate the removed path properly in cache-tree Makefile: install and clean merge-recur, still. GIT 1.4.3-rc1 gitweb: tree view: hash_base and hash are now context sensitive git-diff -B output fix. fetch: Reset remote refs list each time fetch_main is called Remove -fPIC which was only needed for Git.xs 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() ...
This commit is contained in:
commit
ff989b8d46
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
--port::
|
||||
Listen on an alternative port.
|
||||
--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=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
|
||||
|
@ -54,7 +54,8 @@ OPTIONS
|
||||
|
||||
--get::
|
||||
Get the value for a given key (optionally filtered by a regex
|
||||
matching the value).
|
||||
matching the value). Returns error code 1 if the key was not
|
||||
found and error code 2 if multiple key values were found.
|
||||
|
||||
--get-all::
|
||||
Like get, but does not fail if the number of values for the key
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
GVF=GIT-VERSION-FILE
|
||||
DEF_VER=v1.4.2.GIT
|
||||
DEF_VER=v1.4.3.GIT
|
||||
|
||||
LF='
|
||||
'
|
||||
|
13
INSTALL
13
INSTALL
@ -38,6 +38,19 @@ Issues of note:
|
||||
has been actively developed since 1997, and people have moved over to
|
||||
graphical file managers.
|
||||
|
||||
- You can use git after building but without installing if you
|
||||
wanted to. Various git commands need to find other git
|
||||
commands and scripts to do their work, so you would need to
|
||||
arrange a few environment variables to tell them that their
|
||||
friends will be found in your built source area instead of at
|
||||
their standard installation area. Something like this works
|
||||
for me:
|
||||
|
||||
GIT_EXEC_PATH=`pwd`
|
||||
PATH=`pwd`:$PATH
|
||||
GITPERLLIB=`pwd`/perl/blib/lib
|
||||
export GIT_EXEC_PATH PATH GITPERLLIB
|
||||
|
||||
- Git is reasonably self-sufficient, but does depend on a few external
|
||||
programs and libraries:
|
||||
|
||||
|
125
Makefile
125
Makefile
@ -1,11 +1,6 @@
|
||||
# The default target of this Makefile is...
|
||||
all:
|
||||
|
||||
# Define MOZILLA_SHA1 environment variable when running make to make use of
|
||||
# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
|
||||
# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
|
||||
# choice) has very fast version optimized for i586.
|
||||
#
|
||||
# Define NO_OPENSSL environment variable if you do not have OpenSSL.
|
||||
# This also implies MOZILLA_SHA1.
|
||||
#
|
||||
@ -60,6 +55,11 @@ all:
|
||||
# Define ARM_SHA1 environment variable when running make to make use of
|
||||
# a bundled SHA1 routine optimized for ARM.
|
||||
#
|
||||
# Define MOZILLA_SHA1 environment variable when running make to make use of
|
||||
# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
|
||||
# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
|
||||
# choice) has very fast version optimized for i586.
|
||||
#
|
||||
# Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin).
|
||||
#
|
||||
# Define NEEDS_LIBICONV if linking with libc is not enough (Darwin).
|
||||
@ -84,13 +84,13 @@ all:
|
||||
# Define COLLISION_CHECK below if you believe that SHA1's
|
||||
# 1461501637330902918203684832716283019655932542976 hashes do not give you
|
||||
# sufficient guarantee that no collisions between objects will ever happen.
|
||||
|
||||
#
|
||||
# Define USE_NSEC below if you want git to care about sub-second file mtimes
|
||||
# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
|
||||
# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
|
||||
# randomly break unless your underlying filesystem supports those sub-second
|
||||
# times (my ext3 doesn't).
|
||||
|
||||
#
|
||||
# Define USE_STDEV below if you want git to care about the underlying device
|
||||
# change being considered an inode change from the update-cache perspective.
|
||||
|
||||
@ -149,6 +149,12 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
|
||||
|
||||
### --- END CONFIGURATION SECTION ---
|
||||
|
||||
# Those must not be GNU-specific; they are shared with perl/ which may
|
||||
# be built by a different compiler. (Note that this is an artifact now
|
||||
# but it still might be nice to keep that distinction.)
|
||||
BASIC_CFLAGS =
|
||||
BASIC_LDFLAGS =
|
||||
|
||||
SCRIPT_SH = \
|
||||
git-bisect.sh git-branch.sh git-checkout.sh \
|
||||
git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \
|
||||
@ -209,7 +215,8 @@ BUILT_INS = \
|
||||
$(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
|
||||
|
||||
# what 'all' will build and 'install' will install, in gitexecdir
|
||||
ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
|
||||
ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) \
|
||||
git-merge-recur$X
|
||||
|
||||
# Backward compatibility -- to be removed after 1.0
|
||||
PROGRAMS += git-ssh-pull$X git-ssh-push$X
|
||||
@ -305,7 +312,7 @@ BUILTIN_OBJS = \
|
||||
builtin-pack-refs.o
|
||||
|
||||
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
|
||||
LIBS = $(GITLIBS) -lz
|
||||
EXTLIBS = -lz
|
||||
|
||||
#
|
||||
# Platform specific tweaks
|
||||
@ -327,14 +334,14 @@ ifeq ($(uname_S),Darwin)
|
||||
NO_STRLCPY = YesPlease
|
||||
ifndef NO_FINK
|
||||
ifeq ($(shell test -d /sw/lib && echo y),y)
|
||||
ALL_CFLAGS += -I/sw/include
|
||||
ALL_LDFLAGS += -L/sw/lib
|
||||
BASIC_CFLAGS += -I/sw/include
|
||||
BASIC_LDFLAGS += -L/sw/lib
|
||||
endif
|
||||
endif
|
||||
ifndef NO_DARWIN_PORTS
|
||||
ifeq ($(shell test -d /opt/local/lib && echo y),y)
|
||||
ALL_CFLAGS += -I/opt/local/include
|
||||
ALL_LDFLAGS += -L/opt/local/lib
|
||||
BASIC_CFLAGS += -I/opt/local/include
|
||||
BASIC_LDFLAGS += -L/opt/local/lib
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@ -356,7 +363,7 @@ ifeq ($(uname_S),SunOS)
|
||||
endif
|
||||
INSTALL = ginstall
|
||||
TAR = gtar
|
||||
ALL_CFLAGS += -D__EXTENSIONS__
|
||||
BASIC_CFLAGS += -D__EXTENSIONS__
|
||||
endif
|
||||
ifeq ($(uname_O),Cygwin)
|
||||
NO_D_TYPE_IN_DIRENT = YesPlease
|
||||
@ -374,21 +381,22 @@ ifeq ($(uname_O),Cygwin)
|
||||
endif
|
||||
ifeq ($(uname_S),FreeBSD)
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
ALL_CFLAGS += -I/usr/local/include
|
||||
ALL_LDFLAGS += -L/usr/local/lib
|
||||
BASIC_CFLAGS += -I/usr/local/include
|
||||
BASIC_LDFLAGS += -L/usr/local/lib
|
||||
endif
|
||||
ifeq ($(uname_S),OpenBSD)
|
||||
NO_STRCASESTR = YesPlease
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
ALL_CFLAGS += -I/usr/local/include
|
||||
ALL_LDFLAGS += -L/usr/local/lib
|
||||
BASIC_CFLAGS += -I/usr/local/include
|
||||
BASIC_LDFLAGS += -L/usr/local/lib
|
||||
endif
|
||||
ifeq ($(uname_S),NetBSD)
|
||||
ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
endif
|
||||
ALL_CFLAGS += -I/usr/pkg/include
|
||||
ALL_LDFLAGS += -L/usr/pkg/lib -Wl,-rpath,/usr/pkg/lib
|
||||
BASIC_CFLAGS += -I/usr/pkg/include
|
||||
BASIC_LDFLAGS += -L/usr/pkg/lib
|
||||
ALL_LDFLAGS += -Wl,-rpath,/usr/pkg/lib
|
||||
endif
|
||||
ifeq ($(uname_S),AIX)
|
||||
NO_STRCASESTR=YesPlease
|
||||
@ -402,9 +410,9 @@ ifeq ($(uname_S),IRIX64)
|
||||
NO_STRLCPY = YesPlease
|
||||
NO_SOCKADDR_STORAGE=YesPlease
|
||||
SHELL_PATH=/usr/gnu/bin/bash
|
||||
ALL_CFLAGS += -DPATH_MAX=1024
|
||||
BASIC_CFLAGS += -DPATH_MAX=1024
|
||||
# for now, build 32-bit version
|
||||
ALL_LDFLAGS += -L/usr/lib32
|
||||
BASIC_LDFLAGS += -L/usr/lib32
|
||||
endif
|
||||
ifneq (,$(findstring arm,$(uname_M)))
|
||||
ARM_SHA1 = YesPlease
|
||||
@ -426,7 +434,7 @@ endif
|
||||
ifndef NO_CURL
|
||||
ifdef CURLDIR
|
||||
# This is still problematic -- gcc does not always want -R.
|
||||
ALL_CFLAGS += -I$(CURLDIR)/include
|
||||
BASIC_CFLAGS += -I$(CURLDIR)/include
|
||||
CURL_LIBCURL = -L$(CURLDIR)/lib -R$(CURLDIR)/lib -lcurl
|
||||
else
|
||||
CURL_LIBCURL = -lcurl
|
||||
@ -447,13 +455,13 @@ ifndef NO_OPENSSL
|
||||
OPENSSL_LIBSSL = -lssl
|
||||
ifdef OPENSSLDIR
|
||||
# Again this may be problematic -- gcc does not always want -R.
|
||||
ALL_CFLAGS += -I$(OPENSSLDIR)/include
|
||||
BASIC_CFLAGS += -I$(OPENSSLDIR)/include
|
||||
OPENSSL_LINK = -L$(OPENSSLDIR)/lib -R$(OPENSSLDIR)/lib
|
||||
else
|
||||
OPENSSL_LINK =
|
||||
endif
|
||||
else
|
||||
ALL_CFLAGS += -DNO_OPENSSL
|
||||
BASIC_CFLAGS += -DNO_OPENSSL
|
||||
MOZILLA_SHA1 = 1
|
||||
OPENSSL_LIBSSL =
|
||||
endif
|
||||
@ -465,32 +473,32 @@ endif
|
||||
ifdef NEEDS_LIBICONV
|
||||
ifdef ICONVDIR
|
||||
# Again this may be problematic -- gcc does not always want -R.
|
||||
ALL_CFLAGS += -I$(ICONVDIR)/include
|
||||
BASIC_CFLAGS += -I$(ICONVDIR)/include
|
||||
ICONV_LINK = -L$(ICONVDIR)/lib -R$(ICONVDIR)/lib
|
||||
else
|
||||
ICONV_LINK =
|
||||
endif
|
||||
LIBS += $(ICONV_LINK) -liconv
|
||||
EXTLIBS += $(ICONV_LINK) -liconv
|
||||
endif
|
||||
ifdef NEEDS_SOCKET
|
||||
LIBS += -lsocket
|
||||
EXTLIBS += -lsocket
|
||||
SIMPLE_LIB += -lsocket
|
||||
endif
|
||||
ifdef NEEDS_NSL
|
||||
LIBS += -lnsl
|
||||
EXTLIBS += -lnsl
|
||||
SIMPLE_LIB += -lnsl
|
||||
endif
|
||||
ifdef NO_D_TYPE_IN_DIRENT
|
||||
ALL_CFLAGS += -DNO_D_TYPE_IN_DIRENT
|
||||
BASIC_CFLAGS += -DNO_D_TYPE_IN_DIRENT
|
||||
endif
|
||||
ifdef NO_D_INO_IN_DIRENT
|
||||
ALL_CFLAGS += -DNO_D_INO_IN_DIRENT
|
||||
BASIC_CFLAGS += -DNO_D_INO_IN_DIRENT
|
||||
endif
|
||||
ifdef NO_C99_FORMAT
|
||||
ALL_CFLAGS += -DNO_C99_FORMAT
|
||||
endif
|
||||
ifdef NO_SYMLINK_HEAD
|
||||
ALL_CFLAGS += -DNO_SYMLINK_HEAD
|
||||
BASIC_CFLAGS += -DNO_SYMLINK_HEAD
|
||||
endif
|
||||
ifdef NO_STRCASESTR
|
||||
COMPAT_CFLAGS += -DNO_STRCASESTR
|
||||
@ -513,21 +521,24 @@ ifdef NO_MMAP
|
||||
COMPAT_OBJS += compat/mmap.o
|
||||
endif
|
||||
ifdef NO_IPV6
|
||||
ALL_CFLAGS += -DNO_IPV6
|
||||
BASIC_CFLAGS += -DNO_IPV6
|
||||
endif
|
||||
ifdef NO_SOCKADDR_STORAGE
|
||||
ifdef NO_IPV6
|
||||
ALL_CFLAGS += -Dsockaddr_storage=sockaddr_in
|
||||
BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in
|
||||
else
|
||||
ALL_CFLAGS += -Dsockaddr_storage=sockaddr_in6
|
||||
BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in6
|
||||
endif
|
||||
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
|
||||
BASIC_CFLAGS += -DNO_ICONV
|
||||
endif
|
||||
|
||||
ifdef PPC_SHA1
|
||||
@ -543,12 +554,12 @@ ifdef MOZILLA_SHA1
|
||||
LIB_OBJS += mozilla-sha1/sha1.o
|
||||
else
|
||||
SHA1_HEADER = <openssl/sha.h>
|
||||
LIBS += $(LIB_4_CRYPTO)
|
||||
EXTLIBS += $(LIB_4_CRYPTO)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
ifdef NO_ACCURATE_DIFF
|
||||
ALL_CFLAGS += -DNO_ACCURATE_DIFF
|
||||
BASIC_CFLAGS += -DNO_ACCURATE_DIFF
|
||||
endif
|
||||
|
||||
# Shell quote (do not use $(call) to accommodate ancient setups);
|
||||
@ -566,15 +577,23 @@ PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
|
||||
PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
|
||||
GIT_PYTHON_DIR_SQ = $(subst ','\'',$(GIT_PYTHON_DIR))
|
||||
|
||||
ALL_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' $(COMPAT_CFLAGS)
|
||||
LIBS = $(GITLIBS) $(EXTLIBS)
|
||||
|
||||
BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' $(COMPAT_CFLAGS)
|
||||
LIB_OBJS += $(COMPAT_OBJS)
|
||||
|
||||
ALL_CFLAGS += $(BASIC_CFLAGS)
|
||||
ALL_LDFLAGS += $(BASIC_LDFLAGS)
|
||||
|
||||
export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
|
||||
|
||||
|
||||
### Build rules
|
||||
|
||||
all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi \
|
||||
git-merge-recur$X
|
||||
all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi
|
||||
|
||||
all:
|
||||
all: perl/Makefile
|
||||
$(MAKE) -C perl
|
||||
$(MAKE) -C templates
|
||||
|
||||
strip: $(PROGRAMS) git$X
|
||||
@ -608,9 +627,18 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
|
||||
chmod +x $@+
|
||||
mv $@+ $@
|
||||
|
||||
$(patsubst %.perl,%,$(SCRIPT_PERL)) : % : %.perl
|
||||
$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/Makefile
|
||||
$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
|
||||
rm -f $@ $@+
|
||||
sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
|
||||
INSTLIBDIR=`$(MAKE) -C perl -s --no-print-directory instlibdir` && \
|
||||
sed -e '1{' \
|
||||
-e ' s|#!.*perl|#!$(PERL_PATH_SQ)|' \
|
||||
-e ' h' \
|
||||
-e ' s=.*=use lib (split(/:/, $$ENV{GITPERLLIB} || "@@INSTLIBDIR@@"));=' \
|
||||
-e ' H' \
|
||||
-e ' x' \
|
||||
-e '}' \
|
||||
-e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
|
||||
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
|
||||
$@.perl >$@+
|
||||
chmod +x $@+
|
||||
@ -740,6 +768,10 @@ $(XDIFF_LIB): $(XDIFF_OBJS)
|
||||
rm -f $@ && $(AR) rcs $@ $(XDIFF_OBJS)
|
||||
|
||||
|
||||
perl/Makefile: perl/Git.pm perl/Makefile.PL GIT-CFLAGS
|
||||
(cd perl && $(PERL_PATH) Makefile.PL \
|
||||
PREFIX='$(prefix_SQ)')
|
||||
|
||||
doc:
|
||||
$(MAKE) -C Documentation all
|
||||
|
||||
@ -802,6 +834,7 @@ install: all
|
||||
$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
|
||||
$(INSTALL) git$X gitk '$(DESTDIR_SQ)$(bindir_SQ)'
|
||||
$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
|
||||
$(MAKE) -C perl install
|
||||
$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'
|
||||
$(INSTALL) $(PYMODULES) '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'
|
||||
if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \
|
||||
@ -872,7 +905,9 @@ clean:
|
||||
rm -f $(htmldocs).tar.gz $(manpages).tar.gz
|
||||
rm -f gitweb/gitweb.cgi
|
||||
$(MAKE) -C Documentation/ clean
|
||||
$(MAKE) -C templates clean
|
||||
[ ! -f perl/Makefile ] || $(MAKE) -C perl/ clean || $(MAKE) -C perl/ clean
|
||||
rm -f perl/ppport.h perl/Makefile.old
|
||||
$(MAKE) -C templates/ clean
|
||||
$(MAKE) -C t/ clean
|
||||
rm -f GIT-VERSION-FILE GIT-CFLAGS
|
||||
|
||||
|
@ -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?");
|
||||
|
@ -278,6 +278,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
|
||||
for (i = 0; i < deleted.nr; i++) {
|
||||
const char *path = deleted.items[i].path;
|
||||
remove_file_from_cache(path);
|
||||
cache_tree_invalidate_path(active_cache_tree, path);
|
||||
}
|
||||
|
||||
if (active_cache_changed) {
|
||||
|
@ -78,12 +78,12 @@ static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
|
||||
int is_refspec;
|
||||
char *s, *p;
|
||||
|
||||
if (!strncmp("URL: ", buffer, 5)) {
|
||||
if (!strncmp("URL:", buffer, 4)) {
|
||||
is_refspec = 0;
|
||||
s = buffer + 5;
|
||||
} else if (!strncmp("Push: ", buffer, 6)) {
|
||||
s = buffer + 4;
|
||||
} else if (!strncmp("Push:", buffer, 5)) {
|
||||
is_refspec = 1;
|
||||
s = buffer + 6;
|
||||
s = buffer + 5;
|
||||
} else
|
||||
continue;
|
||||
|
||||
|
@ -119,7 +119,7 @@ static int get_value(const char* key_, const char* regex_)
|
||||
if (do_all)
|
||||
ret = !seen;
|
||||
else
|
||||
ret = (seen == 1) ? 0 : 1;
|
||||
ret = (seen == 1) ? 0 : seen > 1 ? 2 : 1;
|
||||
|
||||
free_strings:
|
||||
free(repo_config);
|
||||
|
220
compat/inet_pton.c
Normal file
220
compat/inet_pton.c
Normal 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 */
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
# @configure_input@
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@
|
||||
AR = @AR@
|
||||
TAR = @TAR@
|
||||
#INSTALL = @INSTALL@ # needs install-sh or install.sh in sources
|
||||
|
@ -94,7 +94,7 @@ AC_SUBST(PYTHON_PATH)
|
||||
## Checks for programs.
|
||||
AC_MSG_NOTICE([CHECKS for programs])
|
||||
#
|
||||
AC_PROG_CC
|
||||
AC_PROG_CC([cc gcc])
|
||||
#AC_PROG_INSTALL # needs install-sh or install.sh in sources
|
||||
AC_CHECK_TOOL(AR, ar, :)
|
||||
AC_CHECK_PROGS(TAR, [gtar tar])
|
||||
|
324
contrib/completion/git-completion.bash
Executable file
324
contrib/completion/git-completion.bash
Executable 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
179
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);
|
||||
}
|
||||
|
63
date.c
63
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))
|
||||
|
168
diff.c
168
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++;
|
||||
}
|
||||
|
||||
|
3
diff.h
3
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;
|
||||
|
@ -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?"
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
@ -257,6 +257,7 @@ fi
|
||||
fetch_main () {
|
||||
reflist="$1"
|
||||
refs=
|
||||
rref=
|
||||
|
||||
for ref in $reflist
|
||||
do
|
||||
@ -289,6 +290,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 +305,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 ||
|
||||
|
@ -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"
|
||||
;;
|
||||
|
@ -21,6 +21,7 @@ use warnings;
|
||||
use Term::ReadLine;
|
||||
use Getopt::Long;
|
||||
use Data::Dumper;
|
||||
use Git;
|
||||
|
||||
package FakeTerm;
|
||||
sub new {
|
||||
@ -92,6 +93,7 @@ my $smtp_server;
|
||||
# Example reply to:
|
||||
#$initial_reply_to = ''; #<20050203173208.GA23964@foobar.com>';
|
||||
|
||||
my $repo = Git->repository();
|
||||
my $term = eval {
|
||||
new Term::ReadLine 'git-send-email';
|
||||
};
|
||||
@ -132,33 +134,12 @@ foreach my $entry (@bcclist) {
|
||||
|
||||
# Now, let's fill any that aren't set in with defaults:
|
||||
|
||||
sub gitvar {
|
||||
my ($var) = @_;
|
||||
my $fh;
|
||||
my $pid = open($fh, '-|');
|
||||
die "$!" unless defined $pid;
|
||||
if (!$pid) {
|
||||
exec('git-var', $var) or die "$!";
|
||||
}
|
||||
my ($val) = <$fh>;
|
||||
close $fh or die "$!";
|
||||
chomp($val);
|
||||
return $val;
|
||||
}
|
||||
|
||||
sub gitvar_ident {
|
||||
my ($name) = @_;
|
||||
my $val = gitvar($name);
|
||||
my @field = split(/\s+/, $val);
|
||||
return join(' ', @field[0...(@field-3)]);
|
||||
}
|
||||
|
||||
my ($author) = gitvar_ident('GIT_AUTHOR_IDENT');
|
||||
my ($committer) = gitvar_ident('GIT_COMMITTER_IDENT');
|
||||
my ($author) = $repo->ident_person('author');
|
||||
my ($committer) = $repo->ident_person('committer');
|
||||
|
||||
my %aliases;
|
||||
chomp(my @alias_files = `git-repo-config --get-all sendemail.aliasesfile`);
|
||||
chomp(my $aliasfiletype = `git-repo-config sendemail.aliasfiletype`);
|
||||
my @alias_files = $repo->config('sendemail.aliasesfile');
|
||||
my $aliasfiletype = $repo->config('sendemail.aliasfiletype');
|
||||
my %parse_alias = (
|
||||
# multiline formats can be supported in the future
|
||||
mutt => sub { my $fh = shift; while (<$fh>) {
|
||||
@ -183,7 +164,7 @@ my %parse_alias = (
|
||||
}}}
|
||||
);
|
||||
|
||||
if (@alias_files && defined $parse_alias{$aliasfiletype}) {
|
||||
if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) {
|
||||
foreach my $file (@alias_files) {
|
||||
open my $fh, '<', $file or die "opening $file: $!\n";
|
||||
$parse_alias{$aliasfiletype}->($fh);
|
||||
@ -425,10 +406,7 @@ sub send_message
|
||||
my $date = format_2822_time($time++);
|
||||
my $gitversion = '@@GIT_VERSION@@';
|
||||
if ($gitversion =~ m/..GIT_VERSION../) {
|
||||
$gitversion = `git --version`;
|
||||
chomp $gitversion;
|
||||
# keep only what's after the last space
|
||||
$gitversion =~ s/^.* //;
|
||||
$gitversion = Git::version();
|
||||
}
|
||||
|
||||
my $header = "From: $from
|
||||
|
@ -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;
|
||||
|
23
git.spec.in
23
git.spec.in
@ -9,7 +9,7 @@ URL: http://kernel.org/pub/software/scm/git/
|
||||
Source: http://kernel.org/pub/software/scm/git/%{name}-%{version}.tar.gz
|
||||
BuildRequires: zlib-devel >= 1.2, openssl-devel, curl-devel, expat-devel %{!?_without_docs:, xmlto, asciidoc > 6.0.3}
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
||||
Requires: git-core, git-svn, git-cvs, git-arch, git-email, gitk
|
||||
Requires: git-core, git-svn, git-cvs, git-arch, git-email, gitk, perl-Git
|
||||
|
||||
%description
|
||||
This is a stupid (but extremely fast) directory content manager. It
|
||||
@ -70,6 +70,16 @@ Requires: git-core = %{version}-%{release}, tk >= 8.4
|
||||
%description -n gitk
|
||||
Git revision tree visualiser ('gitk')
|
||||
|
||||
%package -n perl-Git
|
||||
Summary: Perl interface to Git
|
||||
Group: Development/Libraries
|
||||
Requires: git-core = %{version}-%{release}
|
||||
Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version))
|
||||
BuildRequires: perl(Error)
|
||||
|
||||
%description -n perl-Git
|
||||
Perl interface to Git
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
@ -80,12 +90,18 @@ make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" WITH_OWN_SUBPROCESS_PY=YesPlease \
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
make %{_smp_mflags} DESTDIR=$RPM_BUILD_ROOT WITH_OWN_SUBPROCESS_PY=YesPlease \
|
||||
prefix=%{_prefix} mandir=%{_mandir} \
|
||||
prefix=%{_prefix} mandir=%{_mandir} INSTALLDIRS=vendor \
|
||||
install %{!?_without_docs: install-doc}
|
||||
find $RPM_BUILD_ROOT -type f -name .packlist -exec rm -f {} ';'
|
||||
find $RPM_BUILD_ROOT -type f -name '*.bs' -empty -exec rm -f {} ';'
|
||||
find $RPM_BUILD_ROOT -type f -name perllocal.pod -exec rm -f {} ';'
|
||||
|
||||
(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "arch|svn|cvs|email|gitk" | sed -e s@^$RPM_BUILD_ROOT@@) > bin-man-doc-files
|
||||
(find $RPM_BUILD_ROOT%{perl_vendorarch} -type f | sed -e s@^$RPM_BUILD_ROOT@@) >> perl-files
|
||||
%if %{!?_without_docs:1}0
|
||||
(find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "arch|svn|git-cvs|email|gitk" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files
|
||||
%else
|
||||
rm -rf $RPM_BUILD_ROOT%{_mandir}
|
||||
%endif
|
||||
|
||||
%clean
|
||||
@ -129,6 +145,9 @@ rm -rf $RPM_BUILD_ROOT
|
||||
%{!?_without_docs: %{_mandir}/man1/*gitk*.1*}
|
||||
%{!?_without_docs: %doc Documentation/*gitk*.html }
|
||||
|
||||
%files -n perl-Git -f perl-files
|
||||
%defattr(-,root,root)
|
||||
|
||||
%files core -f bin-man-doc-files
|
||||
%defattr(-,root,root)
|
||||
%{_datadir}/git-core/
|
||||
|
@ -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');
|
||||
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');
|
||||
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;
|
||||
|
||||
# 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}$/) {
|
||||
return $input;
|
||||
}
|
||||
if ($input =~ m/(^|\/)(|\.|\.\.)($|\/)/) {
|
||||
return undef;
|
||||
}
|
||||
if ($input =~ m/[^a-zA-Z0-9_\x80-\xff\ \t\.\/\-\+\#\~\%]/) {
|
||||
# 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;
|
||||
|
12
http.c
12
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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
4
perl/.gitignore
vendored
Normal file
4
perl/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
Makefile
|
||||
blib
|
||||
blibdirs
|
||||
pm_to_blib
|
837
perl/Git.pm
Normal file
837
perl/Git.pm
Normal file
@ -0,0 +1,837 @@
|
||||
=head1 NAME
|
||||
|
||||
Git - Perl interface to the Git version control system
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
package Git;
|
||||
|
||||
use strict;
|
||||
|
||||
|
||||
BEGIN {
|
||||
|
||||
our ($VERSION, @ISA, @EXPORT, @EXPORT_OK);
|
||||
|
||||
# Totally unstable API.
|
||||
$VERSION = '0.01';
|
||||
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Git;
|
||||
|
||||
my $version = Git::command_oneline('version');
|
||||
|
||||
git_cmd_try { Git::command_noisy('update-server-info') }
|
||||
'%s failed w/ code %d';
|
||||
|
||||
my $repo = Git->repository (Directory => '/srv/git/cogito.git');
|
||||
|
||||
|
||||
my @revs = $repo->command('rev-list', '--since=last monday', '--all');
|
||||
|
||||
my ($fh, $c) = $repo->command_output_pipe('rev-list', '--since=last monday', '--all');
|
||||
my $lastrev = <$fh>; chomp $lastrev;
|
||||
$repo->command_close_pipe($fh, $c);
|
||||
|
||||
my $lastrev = $repo->command_oneline( [ 'rev-list', '--all' ],
|
||||
STDERR => 0 );
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
require Exporter;
|
||||
|
||||
@ISA = qw(Exporter);
|
||||
|
||||
@EXPORT = qw(git_cmd_try);
|
||||
|
||||
# Methods which can be called as standalone functions as well:
|
||||
@EXPORT_OK = qw(command command_oneline command_noisy
|
||||
command_output_pipe command_input_pipe command_close_pipe
|
||||
version exec_path hash_object git_cmd_try);
|
||||
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module provides Perl scripts easy way to interface the Git version control
|
||||
system. The modules have an easy and well-tested way to call arbitrary Git
|
||||
commands; in the future, the interface will also provide specialized methods
|
||||
for doing easily operations which are not totally trivial to do over
|
||||
the generic command interface.
|
||||
|
||||
While some commands can be executed outside of any context (e.g. 'version'
|
||||
or 'init-db'), most operations require a repository context, which in practice
|
||||
means getting an instance of the Git object using the repository() constructor.
|
||||
(In the future, we will also get a new_repository() constructor.) All commands
|
||||
called as methods of the object are then executed in the context of the
|
||||
repository.
|
||||
|
||||
Part of the "repository state" is also information about path to the attached
|
||||
working copy (unless you work with a bare repository). You can also navigate
|
||||
inside of the working copy using the C<wc_chdir()> method. (Note that
|
||||
the repository object is self-contained and will not change working directory
|
||||
of your process.)
|
||||
|
||||
TODO: In the future, we might also do
|
||||
|
||||
my $remoterepo = $repo->remote_repository (Name => 'cogito', Branch => 'master');
|
||||
$remoterepo ||= Git->remote_repository ('http://git.or.cz/cogito.git/');
|
||||
my @refs = $remoterepo->refs();
|
||||
|
||||
Currently, the module merely wraps calls to external Git tools. In the future,
|
||||
it will provide a much faster way to interact with Git by linking directly
|
||||
to libgit. This should be completely opaque to the user, though (performance
|
||||
increate nonwithstanding).
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
use Carp qw(carp croak); # but croak is bad - throw instead
|
||||
use Error qw(:try);
|
||||
use Cwd qw(abs_path);
|
||||
|
||||
}
|
||||
|
||||
|
||||
=head1 CONSTRUCTORS
|
||||
|
||||
=over 4
|
||||
|
||||
=item repository ( OPTIONS )
|
||||
|
||||
=item repository ( DIRECTORY )
|
||||
|
||||
=item repository ()
|
||||
|
||||
Construct a new repository object.
|
||||
C<OPTIONS> are passed in a hash like fashion, using key and value pairs.
|
||||
Possible options are:
|
||||
|
||||
B<Repository> - Path to the Git repository.
|
||||
|
||||
B<WorkingCopy> - Path to the associated working copy; not strictly required
|
||||
as many commands will happily crunch on a bare repository.
|
||||
|
||||
B<WorkingSubdir> - Subdirectory in the working copy to work inside.
|
||||
Just left undefined if you do not want to limit the scope of operations.
|
||||
|
||||
B<Directory> - Path to the Git working directory in its usual setup.
|
||||
The C<.git> directory is searched in the directory and all the parent
|
||||
directories; if found, C<WorkingCopy> is set to the directory containing
|
||||
it and C<Repository> to the C<.git> directory itself. If no C<.git>
|
||||
directory was found, the C<Directory> is assumed to be a bare repository,
|
||||
C<Repository> is set to point at it and C<WorkingCopy> is left undefined.
|
||||
If the C<$GIT_DIR> environment variable is set, things behave as expected
|
||||
as well.
|
||||
|
||||
You should not use both C<Directory> and either of C<Repository> and
|
||||
C<WorkingCopy> - the results of that are undefined.
|
||||
|
||||
Alternatively, a directory path may be passed as a single scalar argument
|
||||
to the constructor; it is equivalent to setting only the C<Directory> option
|
||||
field.
|
||||
|
||||
Calling the constructor with no options whatsoever is equivalent to
|
||||
calling it with C<< Directory => '.' >>. In general, if you are building
|
||||
a standard porcelain command, simply doing C<< Git->repository() >> should
|
||||
do the right thing and setup the object to reflect exactly where the user
|
||||
is right now.
|
||||
|
||||
=cut
|
||||
|
||||
sub repository {
|
||||
my $class = shift;
|
||||
my @args = @_;
|
||||
my %opts = ();
|
||||
my $self;
|
||||
|
||||
if (defined $args[0]) {
|
||||
if ($#args % 2 != 1) {
|
||||
# Not a hash.
|
||||
$#args == 0 or throw Error::Simple("bad usage");
|
||||
%opts = ( Directory => $args[0] );
|
||||
} else {
|
||||
%opts = @args;
|
||||
}
|
||||
}
|
||||
|
||||
if (not defined $opts{Repository} and not defined $opts{WorkingCopy}) {
|
||||
$opts{Directory} ||= '.';
|
||||
}
|
||||
|
||||
if ($opts{Directory}) {
|
||||
-d $opts{Directory} or throw Error::Simple("Directory not found: $!");
|
||||
|
||||
my $search = Git->repository(WorkingCopy => $opts{Directory});
|
||||
my $dir;
|
||||
try {
|
||||
$dir = $search->command_oneline(['rev-parse', '--git-dir'],
|
||||
STDERR => 0);
|
||||
} catch Git::Error::Command with {
|
||||
$dir = undef;
|
||||
};
|
||||
|
||||
if ($dir) {
|
||||
$dir =~ m#^/# or $dir = $opts{Directory} . '/' . $dir;
|
||||
$opts{Repository} = $dir;
|
||||
|
||||
# If --git-dir went ok, this shouldn't die either.
|
||||
my $prefix = $search->command_oneline('rev-parse', '--show-prefix');
|
||||
$dir = abs_path($opts{Directory}) . '/';
|
||||
if ($prefix) {
|
||||
if (substr($dir, -length($prefix)) ne $prefix) {
|
||||
throw Error::Simple("rev-parse confused me - $dir does not have trailing $prefix");
|
||||
}
|
||||
substr($dir, -length($prefix)) = '';
|
||||
}
|
||||
$opts{WorkingCopy} = $dir;
|
||||
$opts{WorkingSubdir} = $prefix;
|
||||
|
||||
} else {
|
||||
# A bare repository? Let's see...
|
||||
$dir = $opts{Directory};
|
||||
|
||||
unless (-d "$dir/refs" and -d "$dir/objects" and -e "$dir/HEAD") {
|
||||
# Mimick git-rev-parse --git-dir error message:
|
||||
throw Error::Simple('fatal: Not a git repository');
|
||||
}
|
||||
my $search = Git->repository(Repository => $dir);
|
||||
try {
|
||||
$search->command('symbolic-ref', 'HEAD');
|
||||
} catch Git::Error::Command with {
|
||||
# Mimick git-rev-parse --git-dir error message:
|
||||
throw Error::Simple('fatal: Not a git repository');
|
||||
}
|
||||
|
||||
$opts{Repository} = abs_path($dir);
|
||||
}
|
||||
|
||||
delete $opts{Directory};
|
||||
}
|
||||
|
||||
$self = { opts => \%opts };
|
||||
bless $self, $class;
|
||||
}
|
||||
|
||||
|
||||
=back
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over 4
|
||||
|
||||
=item command ( COMMAND [, ARGUMENTS... ] )
|
||||
|
||||
=item command ( [ COMMAND, ARGUMENTS... ], { Opt => Val ... } )
|
||||
|
||||
Execute the given Git C<COMMAND> (specify it without the 'git-'
|
||||
prefix), optionally with the specified extra C<ARGUMENTS>.
|
||||
|
||||
The second more elaborate form can be used if you want to further adjust
|
||||
the command execution. Currently, only one option is supported:
|
||||
|
||||
B<STDERR> - How to deal with the command's error output. By default (C<undef>)
|
||||
it is delivered to the caller's C<STDERR>. A false value (0 or '') will cause
|
||||
it to be thrown away. If you want to process it, you can get it in a filehandle
|
||||
you specify, but you must be extremely careful; if the error output is not
|
||||
very short and you want to read it in the same process as where you called
|
||||
C<command()>, you are set up for a nice deadlock!
|
||||
|
||||
The method can be called without any instance or on a specified Git repository
|
||||
(in that case the command will be run in the repository context).
|
||||
|
||||
In scalar context, it returns all the command output in a single string
|
||||
(verbatim).
|
||||
|
||||
In array context, it returns an array containing lines printed to the
|
||||
command's stdout (without trailing newlines).
|
||||
|
||||
In both cases, the command's stdin and stderr are the same as the caller's.
|
||||
|
||||
=cut
|
||||
|
||||
sub command {
|
||||
my ($fh, $ctx) = command_output_pipe(@_);
|
||||
|
||||
if (not defined wantarray) {
|
||||
# Nothing to pepper the possible exception with.
|
||||
_cmd_close($fh, $ctx);
|
||||
|
||||
} elsif (not wantarray) {
|
||||
local $/;
|
||||
my $text = <$fh>;
|
||||
try {
|
||||
_cmd_close($fh, $ctx);
|
||||
} catch Git::Error::Command with {
|
||||
# Pepper with the output:
|
||||
my $E = shift;
|
||||
$E->{'-outputref'} = \$text;
|
||||
throw $E;
|
||||
};
|
||||
return $text;
|
||||
|
||||
} else {
|
||||
my @lines = <$fh>;
|
||||
chomp @lines;
|
||||
try {
|
||||
_cmd_close($fh, $ctx);
|
||||
} catch Git::Error::Command with {
|
||||
my $E = shift;
|
||||
$E->{'-outputref'} = \@lines;
|
||||
throw $E;
|
||||
};
|
||||
return @lines;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
=item command_oneline ( COMMAND [, ARGUMENTS... ] )
|
||||
|
||||
=item command_oneline ( [ COMMAND, ARGUMENTS... ], { Opt => Val ... } )
|
||||
|
||||
Execute the given C<COMMAND> in the same way as command()
|
||||
does but always return a scalar string containing the first line
|
||||
of the command's standard output.
|
||||
|
||||
=cut
|
||||
|
||||
sub command_oneline {
|
||||
my ($fh, $ctx) = command_output_pipe(@_);
|
||||
|
||||
my $line = <$fh>;
|
||||
defined $line and chomp $line;
|
||||
try {
|
||||
_cmd_close($fh, $ctx);
|
||||
} catch Git::Error::Command with {
|
||||
# Pepper with the output:
|
||||
my $E = shift;
|
||||
$E->{'-outputref'} = \$line;
|
||||
throw $E;
|
||||
};
|
||||
return $line;
|
||||
}
|
||||
|
||||
|
||||
=item command_output_pipe ( COMMAND [, ARGUMENTS... ] )
|
||||
|
||||
=item command_output_pipe ( [ COMMAND, ARGUMENTS... ], { Opt => Val ... } )
|
||||
|
||||
Execute the given C<COMMAND> in the same way as command()
|
||||
does but return a pipe filehandle from which the command output can be
|
||||
read.
|
||||
|
||||
The function can return C<($pipe, $ctx)> in array context.
|
||||
See C<command_close_pipe()> for details.
|
||||
|
||||
=cut
|
||||
|
||||
sub command_output_pipe {
|
||||
_command_common_pipe('-|', @_);
|
||||
}
|
||||
|
||||
|
||||
=item command_input_pipe ( COMMAND [, ARGUMENTS... ] )
|
||||
|
||||
=item command_input_pipe ( [ COMMAND, ARGUMENTS... ], { Opt => Val ... } )
|
||||
|
||||
Execute the given C<COMMAND> in the same way as command_output_pipe()
|
||||
does but return an input pipe filehandle instead; the command output
|
||||
is not captured.
|
||||
|
||||
The function can return C<($pipe, $ctx)> in array context.
|
||||
See C<command_close_pipe()> for details.
|
||||
|
||||
=cut
|
||||
|
||||
sub command_input_pipe {
|
||||
_command_common_pipe('|-', @_);
|
||||
}
|
||||
|
||||
|
||||
=item command_close_pipe ( PIPE [, CTX ] )
|
||||
|
||||
Close the C<PIPE> as returned from C<command_*_pipe()>, checking
|
||||
whether the command finished successfuly. The optional C<CTX> argument
|
||||
is required if you want to see the command name in the error message,
|
||||
and it is the second value returned by C<command_*_pipe()> when
|
||||
called in array context. The call idiom is:
|
||||
|
||||
my ($fh, $ctx) = $r->command_output_pipe('status');
|
||||
while (<$fh>) { ... }
|
||||
$r->command_close_pipe($fh, $ctx);
|
||||
|
||||
Note that you should not rely on whatever actually is in C<CTX>;
|
||||
currently it is simply the command name but in future the context might
|
||||
have more complicated structure.
|
||||
|
||||
=cut
|
||||
|
||||
sub command_close_pipe {
|
||||
my ($self, $fh, $ctx) = _maybe_self(@_);
|
||||
$ctx ||= '<unknown>';
|
||||
_cmd_close($fh, $ctx);
|
||||
}
|
||||
|
||||
|
||||
=item command_noisy ( COMMAND [, ARGUMENTS... ] )
|
||||
|
||||
Execute the given C<COMMAND> in the same way as command() does but do not
|
||||
capture the command output - the standard output is not redirected and goes
|
||||
to the standard output of the caller application.
|
||||
|
||||
While the method is called command_noisy(), you might want to as well use
|
||||
it for the most silent Git commands which you know will never pollute your
|
||||
stdout but you want to avoid the overhead of the pipe setup when calling them.
|
||||
|
||||
The function returns only after the command has finished running.
|
||||
|
||||
=cut
|
||||
|
||||
sub command_noisy {
|
||||
my ($self, $cmd, @args) = _maybe_self(@_);
|
||||
_check_valid_cmd($cmd);
|
||||
|
||||
my $pid = fork;
|
||||
if (not defined $pid) {
|
||||
throw Error::Simple("fork failed: $!");
|
||||
} elsif ($pid == 0) {
|
||||
_cmd_exec($self, $cmd, @args);
|
||||
}
|
||||
if (waitpid($pid, 0) > 0 and $?>>8 != 0) {
|
||||
throw Git::Error::Command(join(' ', $cmd, @args), $? >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
=item version ()
|
||||
|
||||
Return the Git version in use.
|
||||
|
||||
=cut
|
||||
|
||||
sub version {
|
||||
my $verstr = command_oneline('--version');
|
||||
$verstr =~ s/^git version //;
|
||||
$verstr;
|
||||
}
|
||||
|
||||
|
||||
=item exec_path ()
|
||||
|
||||
Return path to the Git sub-command executables (the same as
|
||||
C<git --exec-path>). Useful mostly only internally.
|
||||
|
||||
=cut
|
||||
|
||||
sub exec_path { command_oneline('--exec-path') }
|
||||
|
||||
|
||||
=item repo_path ()
|
||||
|
||||
Return path to the git repository. Must be called on a repository instance.
|
||||
|
||||
=cut
|
||||
|
||||
sub repo_path { $_[0]->{opts}->{Repository} }
|
||||
|
||||
|
||||
=item wc_path ()
|
||||
|
||||
Return path to the working copy. Must be called on a repository instance.
|
||||
|
||||
=cut
|
||||
|
||||
sub wc_path { $_[0]->{opts}->{WorkingCopy} }
|
||||
|
||||
|
||||
=item wc_subdir ()
|
||||
|
||||
Return path to the subdirectory inside of a working copy. Must be called
|
||||
on a repository instance.
|
||||
|
||||
=cut
|
||||
|
||||
sub wc_subdir { $_[0]->{opts}->{WorkingSubdir} ||= '' }
|
||||
|
||||
|
||||
=item wc_chdir ( SUBDIR )
|
||||
|
||||
Change the working copy subdirectory to work within. The C<SUBDIR> is
|
||||
relative to the working copy root directory (not the current subdirectory).
|
||||
Must be called on a repository instance attached to a working copy
|
||||
and the directory must exist.
|
||||
|
||||
=cut
|
||||
|
||||
sub wc_chdir {
|
||||
my ($self, $subdir) = @_;
|
||||
$self->wc_path()
|
||||
or throw Error::Simple("bare repository");
|
||||
|
||||
-d $self->wc_path().'/'.$subdir
|
||||
or throw Error::Simple("subdir not found: $!");
|
||||
# Of course we will not "hold" the subdirectory so anyone
|
||||
# can delete it now and we will never know. But at least we tried.
|
||||
|
||||
$self->{opts}->{WorkingSubdir} = $subdir;
|
||||
}
|
||||
|
||||
|
||||
=item config ( VARIABLE )
|
||||
|
||||
Retrieve the configuration C<VARIABLE> in the same manner as C<repo-config>
|
||||
does. In scalar context requires the variable to be set only one time
|
||||
(exception is thrown otherwise), in array context returns allows the
|
||||
variable to be set multiple times and returns all the values.
|
||||
|
||||
Must be called on a repository instance.
|
||||
|
||||
This currently wraps command('repo-config') so it is not so fast.
|
||||
|
||||
=cut
|
||||
|
||||
sub config {
|
||||
my ($self, $var) = @_;
|
||||
$self->repo_path()
|
||||
or throw Error::Simple("not a repository");
|
||||
|
||||
try {
|
||||
if (wantarray) {
|
||||
return $self->command('repo-config', '--get-all', $var);
|
||||
} else {
|
||||
return $self->command_oneline('repo-config', '--get', $var);
|
||||
}
|
||||
} catch Git::Error::Command with {
|
||||
my $E = shift;
|
||||
if ($E->value() == 1) {
|
||||
# Key not found.
|
||||
return undef;
|
||||
} else {
|
||||
throw $E;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
=item ident ( TYPE | IDENTSTR )
|
||||
|
||||
=item ident_person ( TYPE | IDENTSTR | IDENTARRAY )
|
||||
|
||||
This suite of functions retrieves and parses ident information, as stored
|
||||
in the commit and tag objects or produced by C<var GIT_type_IDENT> (thus
|
||||
C<TYPE> can be either I<author> or I<committer>; case is insignificant).
|
||||
|
||||
The C<ident> method retrieves the ident information from C<git-var>
|
||||
and either returns it as a scalar string or as an array with the fields parsed.
|
||||
Alternatively, it can take a prepared ident string (e.g. from the commit
|
||||
object) and just parse it.
|
||||
|
||||
C<ident_person> returns the person part of the ident - name and email;
|
||||
it can take the same arguments as C<ident> or the array returned by C<ident>.
|
||||
|
||||
The synopsis is like:
|
||||
|
||||
my ($name, $email, $time_tz) = ident('author');
|
||||
"$name <$email>" eq ident_person('author');
|
||||
"$name <$email>" eq ident_person($name);
|
||||
$time_tz =~ /^\d+ [+-]\d{4}$/;
|
||||
|
||||
Both methods must be called on a repository instance.
|
||||
|
||||
=cut
|
||||
|
||||
sub ident {
|
||||
my ($self, $type) = @_;
|
||||
my $identstr;
|
||||
if (lc $type eq lc 'committer' or lc $type eq lc 'author') {
|
||||
$identstr = $self->command_oneline('var', 'GIT_'.uc($type).'_IDENT');
|
||||
} else {
|
||||
$identstr = $type;
|
||||
}
|
||||
if (wantarray) {
|
||||
return $identstr =~ /^(.*) <(.*)> (\d+ [+-]\d{4})$/;
|
||||
} else {
|
||||
return $identstr;
|
||||
}
|
||||
}
|
||||
|
||||
sub ident_person {
|
||||
my ($self, @ident) = @_;
|
||||
$#ident == 0 and @ident = $self->ident($ident[0]);
|
||||
return "$ident[0] <$ident[1]>";
|
||||
}
|
||||
|
||||
|
||||
=item hash_object ( TYPE, FILENAME )
|
||||
|
||||
Compute the SHA1 object id of the given C<FILENAME> (or data waiting in
|
||||
C<FILEHANDLE>) considering it is of the C<TYPE> object type (C<blob>,
|
||||
C<commit>, C<tree>).
|
||||
|
||||
The method can be called without any instance or on a specified Git repository,
|
||||
it makes zero difference.
|
||||
|
||||
The function returns the SHA1 hash.
|
||||
|
||||
=cut
|
||||
|
||||
# TODO: Support for passing FILEHANDLE instead of FILENAME
|
||||
sub hash_object {
|
||||
my ($self, $type, $file) = _maybe_self(@_);
|
||||
command_oneline('hash-object', '-t', $type, $file);
|
||||
}
|
||||
|
||||
|
||||
|
||||
=back
|
||||
|
||||
=head1 ERROR HANDLING
|
||||
|
||||
All functions are supposed to throw Perl exceptions in case of errors.
|
||||
See the L<Error> module on how to catch those. Most exceptions are mere
|
||||
L<Error::Simple> instances.
|
||||
|
||||
However, the C<command()>, C<command_oneline()> and C<command_noisy()>
|
||||
functions suite can throw C<Git::Error::Command> exceptions as well: those are
|
||||
thrown when the external command returns an error code and contain the error
|
||||
code as well as access to the captured command's output. The exception class
|
||||
provides the usual C<stringify> and C<value> (command's exit code) methods and
|
||||
in addition also a C<cmd_output> method that returns either an array or a
|
||||
string with the captured command output (depending on the original function
|
||||
call context; C<command_noisy()> returns C<undef>) and $<cmdline> which
|
||||
returns the command and its arguments (but without proper quoting).
|
||||
|
||||
Note that the C<command_*_pipe()> functions cannot throw this exception since
|
||||
it has no idea whether the command failed or not. You will only find out
|
||||
at the time you C<close> the pipe; if you want to have that automated,
|
||||
use C<command_close_pipe()>, which can throw the exception.
|
||||
|
||||
=cut
|
||||
|
||||
{
|
||||
package Git::Error::Command;
|
||||
|
||||
@Git::Error::Command::ISA = qw(Error);
|
||||
|
||||
sub new {
|
||||
my $self = shift;
|
||||
my $cmdline = '' . shift;
|
||||
my $value = 0 + shift;
|
||||
my $outputref = shift;
|
||||
my(@args) = ();
|
||||
|
||||
local $Error::Depth = $Error::Depth + 1;
|
||||
|
||||
push(@args, '-cmdline', $cmdline);
|
||||
push(@args, '-value', $value);
|
||||
push(@args, '-outputref', $outputref);
|
||||
|
||||
$self->SUPER::new(-text => 'command returned error', @args);
|
||||
}
|
||||
|
||||
sub stringify {
|
||||
my $self = shift;
|
||||
my $text = $self->SUPER::stringify;
|
||||
$self->cmdline() . ': ' . $text . ': ' . $self->value() . "\n";
|
||||
}
|
||||
|
||||
sub cmdline {
|
||||
my $self = shift;
|
||||
$self->{'-cmdline'};
|
||||
}
|
||||
|
||||
sub cmd_output {
|
||||
my $self = shift;
|
||||
my $ref = $self->{'-outputref'};
|
||||
defined $ref or undef;
|
||||
if (ref $ref eq 'ARRAY') {
|
||||
return @$ref;
|
||||
} else { # SCALAR
|
||||
return $$ref;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
=over 4
|
||||
|
||||
=item git_cmd_try { CODE } ERRMSG
|
||||
|
||||
This magical statement will automatically catch any C<Git::Error::Command>
|
||||
exceptions thrown by C<CODE> and make your program die with C<ERRMSG>
|
||||
on its lips; the message will have %s substituted for the command line
|
||||
and %d for the exit status. This statement is useful mostly for producing
|
||||
more user-friendly error messages.
|
||||
|
||||
In case of no exception caught the statement returns C<CODE>'s return value.
|
||||
|
||||
Note that this is the only auto-exported function.
|
||||
|
||||
=cut
|
||||
|
||||
sub git_cmd_try(&$) {
|
||||
my ($code, $errmsg) = @_;
|
||||
my @result;
|
||||
my $err;
|
||||
my $array = wantarray;
|
||||
try {
|
||||
if ($array) {
|
||||
@result = &$code;
|
||||
} else {
|
||||
$result[0] = &$code;
|
||||
}
|
||||
} catch Git::Error::Command with {
|
||||
my $E = shift;
|
||||
$err = $errmsg;
|
||||
$err =~ s/\%s/$E->cmdline()/ge;
|
||||
$err =~ s/\%d/$E->value()/ge;
|
||||
# We can't croak here since Error.pm would mangle
|
||||
# that to Error::Simple.
|
||||
};
|
||||
$err and croak $err;
|
||||
return $array ? @result : $result[0];
|
||||
}
|
||||
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2006 by Petr Baudis E<lt>pasky@suse.czE<gt>.
|
||||
|
||||
This module is free software; it may be used, copied, modified
|
||||
and distributed under the terms of the GNU General Public Licence,
|
||||
either version 2, or (at your option) any later version.
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
# Take raw method argument list and return ($obj, @args) in case
|
||||
# the method was called upon an instance and (undef, @args) if
|
||||
# it was called directly.
|
||||
sub _maybe_self {
|
||||
# This breaks inheritance. Oh well.
|
||||
ref $_[0] eq 'Git' ? @_ : (undef, @_);
|
||||
}
|
||||
|
||||
# Check if the command id is something reasonable.
|
||||
sub _check_valid_cmd {
|
||||
my ($cmd) = @_;
|
||||
$cmd =~ /^[a-z0-9A-Z_-]+$/ or throw Error::Simple("bad command: $cmd");
|
||||
}
|
||||
|
||||
# Common backend for the pipe creators.
|
||||
sub _command_common_pipe {
|
||||
my $direction = shift;
|
||||
my ($self, @p) = _maybe_self(@_);
|
||||
my (%opts, $cmd, @args);
|
||||
if (ref $p[0]) {
|
||||
($cmd, @args) = @{shift @p};
|
||||
%opts = ref $p[0] ? %{$p[0]} : @p;
|
||||
} else {
|
||||
($cmd, @args) = @p;
|
||||
}
|
||||
_check_valid_cmd($cmd);
|
||||
|
||||
my $fh;
|
||||
if ($^O eq '##INSERT_ACTIVESTATE_STRING_HERE##') {
|
||||
# ActiveState Perl
|
||||
#defined $opts{STDERR} and
|
||||
# warn 'ignoring STDERR option - running w/ ActiveState';
|
||||
$direction eq '-|' or
|
||||
die 'input pipe for ActiveState not implemented';
|
||||
tie ($fh, 'Git::activestate_pipe', $cmd, @args);
|
||||
|
||||
} else {
|
||||
my $pid = open($fh, $direction);
|
||||
if (not defined $pid) {
|
||||
throw Error::Simple("open failed: $!");
|
||||
} elsif ($pid == 0) {
|
||||
if (defined $opts{STDERR}) {
|
||||
close STDERR;
|
||||
}
|
||||
if ($opts{STDERR}) {
|
||||
open (STDERR, '>&', $opts{STDERR})
|
||||
or die "dup failed: $!";
|
||||
}
|
||||
_cmd_exec($self, $cmd, @args);
|
||||
}
|
||||
}
|
||||
return wantarray ? ($fh, join(' ', $cmd, @args)) : $fh;
|
||||
}
|
||||
|
||||
# When already in the subprocess, set up the appropriate state
|
||||
# for the given repository and execute the git command.
|
||||
sub _cmd_exec {
|
||||
my ($self, @args) = @_;
|
||||
if ($self) {
|
||||
$self->repo_path() and $ENV{'GIT_DIR'} = $self->repo_path();
|
||||
$self->wc_path() and chdir($self->wc_path());
|
||||
$self->wc_subdir() and chdir($self->wc_subdir());
|
||||
}
|
||||
_execv_git_cmd(@args);
|
||||
die "exec failed: $!";
|
||||
}
|
||||
|
||||
# Execute the given Git command ($_[0]) with arguments ($_[1..])
|
||||
# by searching for it at proper places.
|
||||
sub _execv_git_cmd { exec('git', @_); }
|
||||
|
||||
# Close pipe to a subprocess.
|
||||
sub _cmd_close {
|
||||
my ($fh, $ctx) = @_;
|
||||
if (not close $fh) {
|
||||
if ($!) {
|
||||
# It's just close, no point in fatalities
|
||||
carp "error closing pipe: $!";
|
||||
} elsif ($? >> 8) {
|
||||
# The caller should pepper this.
|
||||
throw Git::Error::Command($ctx, $? >> 8);
|
||||
}
|
||||
# else we might e.g. closed a live stream; the command
|
||||
# dying of SIGPIPE would drive us here.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub DESTROY { }
|
||||
|
||||
|
||||
# Pipe implementation for ActiveState Perl.
|
||||
|
||||
package Git::activestate_pipe;
|
||||
use strict;
|
||||
|
||||
sub TIEHANDLE {
|
||||
my ($class, @params) = @_;
|
||||
# FIXME: This is probably horrible idea and the thing will explode
|
||||
# at the moment you give it arguments that require some quoting,
|
||||
# but I have no ActiveState clue... --pasky
|
||||
my $cmdline = join " ", @params;
|
||||
my @data = qx{$cmdline};
|
||||
bless { i => 0, data => \@data }, $class;
|
||||
}
|
||||
|
||||
sub READLINE {
|
||||
my $self = shift;
|
||||
if ($self->{i} >= scalar @{$self->{data}}) {
|
||||
return undef;
|
||||
}
|
||||
return $self->{'data'}->[ $self->{i}++ ];
|
||||
}
|
||||
|
||||
sub CLOSE {
|
||||
my $self = shift;
|
||||
delete $self->{data};
|
||||
delete $self->{i};
|
||||
}
|
||||
|
||||
sub EOF {
|
||||
my $self = shift;
|
||||
return ($self->{i} >= scalar @{$self->{data}});
|
||||
}
|
||||
|
||||
|
||||
1; # Famous last words
|
28
perl/Makefile.PL
Normal file
28
perl/Makefile.PL
Normal file
@ -0,0 +1,28 @@
|
||||
use ExtUtils::MakeMaker;
|
||||
|
||||
sub MY::postamble {
|
||||
return <<'MAKE_FRAG';
|
||||
instlibdir:
|
||||
@echo '$(INSTALLSITELIB)'
|
||||
|
||||
MAKE_FRAG
|
||||
}
|
||||
|
||||
my %pm = ('Git.pm' => '$(INST_LIBDIR)/Git.pm');
|
||||
|
||||
# We come with our own bundled Error.pm. It's not in the set of default
|
||||
# Perl modules so install it if it's not available on the system yet.
|
||||
eval { require Error };
|
||||
if ($@) {
|
||||
$pm{'private-Error.pm'} = '$(INST_LIBDIR)/Error.pm';
|
||||
}
|
||||
|
||||
my %extra;
|
||||
$extra{DESTDIR} = $ENV{DESTDIR} if $ENV{DESTDIR};
|
||||
|
||||
WriteMakefile(
|
||||
NAME => 'Git',
|
||||
VERSION_FROM => 'Git.pm',
|
||||
PM => \%pm,
|
||||
%extra
|
||||
);
|
827
perl/private-Error.pm
Normal file
827
perl/private-Error.pm
Normal file
@ -0,0 +1,827 @@
|
||||
# Error.pm
|
||||
#
|
||||
# Copyright (c) 1997-8 Graham Barr <gbarr@ti.com>. All rights reserved.
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the same terms as Perl itself.
|
||||
#
|
||||
# Based on my original Error.pm, and Exceptions.pm by Peter Seibel
|
||||
# <peter@weblogic.com> and adapted by Jesse Glick <jglick@sig.bsh.com>.
|
||||
#
|
||||
# but modified ***significantly***
|
||||
|
||||
package Error;
|
||||
|
||||
use strict;
|
||||
use vars qw($VERSION);
|
||||
use 5.004;
|
||||
|
||||
$VERSION = "0.15009";
|
||||
|
||||
use overload (
|
||||
'""' => 'stringify',
|
||||
'0+' => 'value',
|
||||
'bool' => sub { return 1; },
|
||||
'fallback' => 1
|
||||
);
|
||||
|
||||
$Error::Depth = 0; # Depth to pass to caller()
|
||||
$Error::Debug = 0; # Generate verbose stack traces
|
||||
@Error::STACK = (); # Clause stack for try
|
||||
$Error::THROWN = undef; # last error thrown, a workaround until die $ref works
|
||||
|
||||
my $LAST; # Last error created
|
||||
my %ERROR; # Last error associated with package
|
||||
|
||||
sub throw_Error_Simple
|
||||
{
|
||||
my $args = shift;
|
||||
return Error::Simple->new($args->{'text'});
|
||||
}
|
||||
|
||||
$Error::ObjectifyCallback = \&throw_Error_Simple;
|
||||
|
||||
|
||||
# Exported subs are defined in Error::subs
|
||||
|
||||
sub import {
|
||||
shift;
|
||||
local $Exporter::ExportLevel = $Exporter::ExportLevel + 1;
|
||||
Error::subs->import(@_);
|
||||
}
|
||||
|
||||
# I really want to use last for the name of this method, but it is a keyword
|
||||
# which prevent the syntax last Error
|
||||
|
||||
sub prior {
|
||||
shift; # ignore
|
||||
|
||||
return $LAST unless @_;
|
||||
|
||||
my $pkg = shift;
|
||||
return exists $ERROR{$pkg} ? $ERROR{$pkg} : undef
|
||||
unless ref($pkg);
|
||||
|
||||
my $obj = $pkg;
|
||||
my $err = undef;
|
||||
if($obj->isa('HASH')) {
|
||||
$err = $obj->{'__Error__'}
|
||||
if exists $obj->{'__Error__'};
|
||||
}
|
||||
elsif($obj->isa('GLOB')) {
|
||||
$err = ${*$obj}{'__Error__'}
|
||||
if exists ${*$obj}{'__Error__'};
|
||||
}
|
||||
|
||||
$err;
|
||||
}
|
||||
|
||||
sub flush {
|
||||
shift; #ignore
|
||||
|
||||
unless (@_) {
|
||||
$LAST = undef;
|
||||
return;
|
||||
}
|
||||
|
||||
my $pkg = shift;
|
||||
return unless ref($pkg);
|
||||
|
||||
undef $ERROR{$pkg} if defined $ERROR{$pkg};
|
||||
}
|
||||
|
||||
# Return as much information as possible about where the error
|
||||
# happened. The -stacktrace element only exists if $Error::DEBUG
|
||||
# was set when the error was created
|
||||
|
||||
sub stacktrace {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{'-stacktrace'}
|
||||
if exists $self->{'-stacktrace'};
|
||||
|
||||
my $text = exists $self->{'-text'} ? $self->{'-text'} : "Died";
|
||||
|
||||
$text .= sprintf(" at %s line %d.\n", $self->file, $self->line)
|
||||
unless($text =~ /\n$/s);
|
||||
|
||||
$text;
|
||||
}
|
||||
|
||||
# Allow error propagation, ie
|
||||
#
|
||||
# $ber->encode(...) or
|
||||
# return Error->prior($ber)->associate($ldap);
|
||||
|
||||
sub associate {
|
||||
my $err = shift;
|
||||
my $obj = shift;
|
||||
|
||||
return unless ref($obj);
|
||||
|
||||
if($obj->isa('HASH')) {
|
||||
$obj->{'__Error__'} = $err;
|
||||
}
|
||||
elsif($obj->isa('GLOB')) {
|
||||
${*$obj}{'__Error__'} = $err;
|
||||
}
|
||||
$obj = ref($obj);
|
||||
$ERROR{ ref($obj) } = $err;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub new {
|
||||
my $self = shift;
|
||||
my($pkg,$file,$line) = caller($Error::Depth);
|
||||
|
||||
my $err = bless {
|
||||
'-package' => $pkg,
|
||||
'-file' => $file,
|
||||
'-line' => $line,
|
||||
@_
|
||||
}, $self;
|
||||
|
||||
$err->associate($err->{'-object'})
|
||||
if(exists $err->{'-object'});
|
||||
|
||||
# To always create a stacktrace would be very inefficient, so
|
||||
# we only do it if $Error::Debug is set
|
||||
|
||||
if($Error::Debug) {
|
||||
require Carp;
|
||||
local $Carp::CarpLevel = $Error::Depth;
|
||||
my $text = defined($err->{'-text'}) ? $err->{'-text'} : "Error";
|
||||
my $trace = Carp::longmess($text);
|
||||
# Remove try calls from the trace
|
||||
$trace =~ s/(\n\s+\S+__ANON__[^\n]+)?\n\s+eval[^\n]+\n\s+Error::subs::try[^\n]+(?=\n)//sog;
|
||||
$trace =~ s/(\n\s+\S+__ANON__[^\n]+)?\n\s+eval[^\n]+\n\s+Error::subs::run_clauses[^\n]+\n\s+Error::subs::try[^\n]+(?=\n)//sog;
|
||||
$err->{'-stacktrace'} = $trace
|
||||
}
|
||||
|
||||
$@ = $LAST = $ERROR{$pkg} = $err;
|
||||
}
|
||||
|
||||
# Throw an error. this contains some very gory code.
|
||||
|
||||
sub throw {
|
||||
my $self = shift;
|
||||
local $Error::Depth = $Error::Depth + 1;
|
||||
|
||||
# if we are not rethrow-ing then create the object to throw
|
||||
$self = $self->new(@_) unless ref($self);
|
||||
|
||||
die $Error::THROWN = $self;
|
||||
}
|
||||
|
||||
# syntactic sugar for
|
||||
#
|
||||
# die with Error( ... );
|
||||
|
||||
sub with {
|
||||
my $self = shift;
|
||||
local $Error::Depth = $Error::Depth + 1;
|
||||
|
||||
$self->new(@_);
|
||||
}
|
||||
|
||||
# syntactic sugar for
|
||||
#
|
||||
# record Error( ... ) and return;
|
||||
|
||||
sub record {
|
||||
my $self = shift;
|
||||
local $Error::Depth = $Error::Depth + 1;
|
||||
|
||||
$self->new(@_);
|
||||
}
|
||||
|
||||
# catch clause for
|
||||
#
|
||||
# try { ... } catch CLASS with { ... }
|
||||
|
||||
sub catch {
|
||||
my $pkg = shift;
|
||||
my $code = shift;
|
||||
my $clauses = shift || {};
|
||||
my $catch = $clauses->{'catch'} ||= [];
|
||||
|
||||
unshift @$catch, $pkg, $code;
|
||||
|
||||
$clauses;
|
||||
}
|
||||
|
||||
# Object query methods
|
||||
|
||||
sub object {
|
||||
my $self = shift;
|
||||
exists $self->{'-object'} ? $self->{'-object'} : undef;
|
||||
}
|
||||
|
||||
sub file {
|
||||
my $self = shift;
|
||||
exists $self->{'-file'} ? $self->{'-file'} : undef;
|
||||
}
|
||||
|
||||
sub line {
|
||||
my $self = shift;
|
||||
exists $self->{'-line'} ? $self->{'-line'} : undef;
|
||||
}
|
||||
|
||||
sub text {
|
||||
my $self = shift;
|
||||
exists $self->{'-text'} ? $self->{'-text'} : undef;
|
||||
}
|
||||
|
||||
# overload methods
|
||||
|
||||
sub stringify {
|
||||
my $self = shift;
|
||||
defined $self->{'-text'} ? $self->{'-text'} : "Died";
|
||||
}
|
||||
|
||||
sub value {
|
||||
my $self = shift;
|
||||
exists $self->{'-value'} ? $self->{'-value'} : undef;
|
||||
}
|
||||
|
||||
package Error::Simple;
|
||||
|
||||
@Error::Simple::ISA = qw(Error);
|
||||
|
||||
sub new {
|
||||
my $self = shift;
|
||||
my $text = "" . shift;
|
||||
my $value = shift;
|
||||
my(@args) = ();
|
||||
|
||||
local $Error::Depth = $Error::Depth + 1;
|
||||
|
||||
@args = ( -file => $1, -line => $2)
|
||||
if($text =~ s/\s+at\s+(\S+)\s+line\s+(\d+)(?:,\s*<[^>]*>\s+line\s+\d+)?\.?\n?$//s);
|
||||
push(@args, '-value', 0 + $value)
|
||||
if defined($value);
|
||||
|
||||
$self->SUPER::new(-text => $text, @args);
|
||||
}
|
||||
|
||||
sub stringify {
|
||||
my $self = shift;
|
||||
my $text = $self->SUPER::stringify;
|
||||
$text .= sprintf(" at %s line %d.\n", $self->file, $self->line)
|
||||
unless($text =~ /\n$/s);
|
||||
$text;
|
||||
}
|
||||
|
||||
##########################################################################
|
||||
##########################################################################
|
||||
|
||||
# Inspired by code from Jesse Glick <jglick@sig.bsh.com> and
|
||||
# Peter Seibel <peter@weblogic.com>
|
||||
|
||||
package Error::subs;
|
||||
|
||||
use Exporter ();
|
||||
use vars qw(@EXPORT_OK @ISA %EXPORT_TAGS);
|
||||
|
||||
@EXPORT_OK = qw(try with finally except otherwise);
|
||||
%EXPORT_TAGS = (try => \@EXPORT_OK);
|
||||
|
||||
@ISA = qw(Exporter);
|
||||
|
||||
|
||||
sub blessed {
|
||||
my $item = shift;
|
||||
local $@; # don't kill an outer $@
|
||||
ref $item and eval { $item->can('can') };
|
||||
}
|
||||
|
||||
|
||||
sub run_clauses ($$$\@) {
|
||||
my($clauses,$err,$wantarray,$result) = @_;
|
||||
my $code = undef;
|
||||
|
||||
$err = $Error::ObjectifyCallback->({'text' =>$err}) unless ref($err);
|
||||
|
||||
CATCH: {
|
||||
|
||||
# catch
|
||||
my $catch;
|
||||
if(defined($catch = $clauses->{'catch'})) {
|
||||
my $i = 0;
|
||||
|
||||
CATCHLOOP:
|
||||
for( ; $i < @$catch ; $i += 2) {
|
||||
my $pkg = $catch->[$i];
|
||||
unless(defined $pkg) {
|
||||
#except
|
||||
splice(@$catch,$i,2,$catch->[$i+1]->());
|
||||
$i -= 2;
|
||||
next CATCHLOOP;
|
||||
}
|
||||
elsif(blessed($err) && $err->isa($pkg)) {
|
||||
$code = $catch->[$i+1];
|
||||
while(1) {
|
||||
my $more = 0;
|
||||
local($Error::THROWN);
|
||||
my $ok = eval {
|
||||
if($wantarray) {
|
||||
@{$result} = $code->($err,\$more);
|
||||
}
|
||||
elsif(defined($wantarray)) {
|
||||
@{$result} = ();
|
||||
$result->[0] = $code->($err,\$more);
|
||||
}
|
||||
else {
|
||||
$code->($err,\$more);
|
||||
}
|
||||
1;
|
||||
};
|
||||
if( $ok ) {
|
||||
next CATCHLOOP if $more;
|
||||
undef $err;
|
||||
}
|
||||
else {
|
||||
$err = defined($Error::THROWN)
|
||||
? $Error::THROWN : $@;
|
||||
$err = $Error::ObjectifyCallback->({'text' =>$err})
|
||||
unless ref($err);
|
||||
}
|
||||
last CATCH;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# otherwise
|
||||
my $owise;
|
||||
if(defined($owise = $clauses->{'otherwise'})) {
|
||||
my $code = $clauses->{'otherwise'};
|
||||
my $more = 0;
|
||||
my $ok = eval {
|
||||
if($wantarray) {
|
||||
@{$result} = $code->($err,\$more);
|
||||
}
|
||||
elsif(defined($wantarray)) {
|
||||
@{$result} = ();
|
||||
$result->[0] = $code->($err,\$more);
|
||||
}
|
||||
else {
|
||||
$code->($err,\$more);
|
||||
}
|
||||
1;
|
||||
};
|
||||
if( $ok ) {
|
||||
undef $err;
|
||||
}
|
||||
else {
|
||||
$err = defined($Error::THROWN)
|
||||
? $Error::THROWN : $@;
|
||||
|
||||
$err = $Error::ObjectifyCallback->({'text' =>$err})
|
||||
unless ref($err);
|
||||
}
|
||||
}
|
||||
}
|
||||
$err;
|
||||
}
|
||||
|
||||
sub try (&;$) {
|
||||
my $try = shift;
|
||||
my $clauses = @_ ? shift : {};
|
||||
my $ok = 0;
|
||||
my $err = undef;
|
||||
my @result = ();
|
||||
|
||||
unshift @Error::STACK, $clauses;
|
||||
|
||||
my $wantarray = wantarray();
|
||||
|
||||
do {
|
||||
local $Error::THROWN = undef;
|
||||
local $@ = undef;
|
||||
|
||||
$ok = eval {
|
||||
if($wantarray) {
|
||||
@result = $try->();
|
||||
}
|
||||
elsif(defined $wantarray) {
|
||||
$result[0] = $try->();
|
||||
}
|
||||
else {
|
||||
$try->();
|
||||
}
|
||||
1;
|
||||
};
|
||||
|
||||
$err = defined($Error::THROWN) ? $Error::THROWN : $@
|
||||
unless $ok;
|
||||
};
|
||||
|
||||
shift @Error::STACK;
|
||||
|
||||
$err = run_clauses($clauses,$err,wantarray,@result)
|
||||
unless($ok);
|
||||
|
||||
$clauses->{'finally'}->()
|
||||
if(defined($clauses->{'finally'}));
|
||||
|
||||
if (defined($err))
|
||||
{
|
||||
if (blessed($err) && $err->can('throw'))
|
||||
{
|
||||
throw $err;
|
||||
}
|
||||
else
|
||||
{
|
||||
die $err;
|
||||
}
|
||||
}
|
||||
|
||||
wantarray ? @result : $result[0];
|
||||
}
|
||||
|
||||
# Each clause adds a sub to the list of clauses. The finally clause is
|
||||
# always the last, and the otherwise clause is always added just before
|
||||
# the finally clause.
|
||||
#
|
||||
# All clauses, except the finally clause, add a sub which takes one argument
|
||||
# this argument will be the error being thrown. The sub will return a code ref
|
||||
# if that clause can handle that error, otherwise undef is returned.
|
||||
#
|
||||
# The otherwise clause adds a sub which unconditionally returns the users
|
||||
# code reference, this is why it is forced to be last.
|
||||
#
|
||||
# The catch clause is defined in Error.pm, as the syntax causes it to
|
||||
# be called as a method
|
||||
|
||||
sub with (&;$) {
|
||||
@_
|
||||
}
|
||||
|
||||
sub finally (&) {
|
||||
my $code = shift;
|
||||
my $clauses = { 'finally' => $code };
|
||||
$clauses;
|
||||
}
|
||||
|
||||
# The except clause is a block which returns a hashref or a list of
|
||||
# key-value pairs, where the keys are the classes and the values are subs.
|
||||
|
||||
sub except (&;$) {
|
||||
my $code = shift;
|
||||
my $clauses = shift || {};
|
||||
my $catch = $clauses->{'catch'} ||= [];
|
||||
|
||||
my $sub = sub {
|
||||
my $ref;
|
||||
my(@array) = $code->($_[0]);
|
||||
if(@array == 1 && ref($array[0])) {
|
||||
$ref = $array[0];
|
||||
$ref = [ %$ref ]
|
||||
if(UNIVERSAL::isa($ref,'HASH'));
|
||||
}
|
||||
else {
|
||||
$ref = \@array;
|
||||
}
|
||||
@$ref
|
||||
};
|
||||
|
||||
unshift @{$catch}, undef, $sub;
|
||||
|
||||
$clauses;
|
||||
}
|
||||
|
||||
sub otherwise (&;$) {
|
||||
my $code = shift;
|
||||
my $clauses = shift || {};
|
||||
|
||||
if(exists $clauses->{'otherwise'}) {
|
||||
require Carp;
|
||||
Carp::croak("Multiple otherwise clauses");
|
||||
}
|
||||
|
||||
$clauses->{'otherwise'} = $code;
|
||||
|
||||
$clauses;
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Error - Error/exception handling in an OO-ish way
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Error qw(:try);
|
||||
|
||||
throw Error::Simple( "A simple error");
|
||||
|
||||
sub xyz {
|
||||
...
|
||||
record Error::Simple("A simple error")
|
||||
and return;
|
||||
}
|
||||
|
||||
unlink($file) or throw Error::Simple("$file: $!",$!);
|
||||
|
||||
try {
|
||||
do_some_stuff();
|
||||
die "error!" if $condition;
|
||||
throw Error::Simple -text => "Oops!" if $other_condition;
|
||||
}
|
||||
catch Error::IO with {
|
||||
my $E = shift;
|
||||
print STDERR "File ", $E->{'-file'}, " had a problem\n";
|
||||
}
|
||||
except {
|
||||
my $E = shift;
|
||||
my $general_handler=sub {send_message $E->{-description}};
|
||||
return {
|
||||
UserException1 => $general_handler,
|
||||
UserException2 => $general_handler
|
||||
};
|
||||
}
|
||||
otherwise {
|
||||
print STDERR "Well I don't know what to say\n";
|
||||
}
|
||||
finally {
|
||||
close_the_garage_door_already(); # Should be reliable
|
||||
}; # Don't forget the trailing ; or you might be surprised
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The C<Error> package provides two interfaces. Firstly C<Error> provides
|
||||
a procedural interface to exception handling. Secondly C<Error> is a
|
||||
base class for errors/exceptions that can either be thrown, for
|
||||
subsequent catch, or can simply be recorded.
|
||||
|
||||
Errors in the class C<Error> should not be thrown directly, but the
|
||||
user should throw errors from a sub-class of C<Error>.
|
||||
|
||||
=head1 PROCEDURAL INTERFACE
|
||||
|
||||
C<Error> exports subroutines to perform exception handling. These will
|
||||
be exported if the C<:try> tag is used in the C<use> line.
|
||||
|
||||
=over 4
|
||||
|
||||
=item try BLOCK CLAUSES
|
||||
|
||||
C<try> is the main subroutine called by the user. All other subroutines
|
||||
exported are clauses to the try subroutine.
|
||||
|
||||
The BLOCK will be evaluated and, if no error is throw, try will return
|
||||
the result of the block.
|
||||
|
||||
C<CLAUSES> are the subroutines below, which describe what to do in the
|
||||
event of an error being thrown within BLOCK.
|
||||
|
||||
=item catch CLASS with BLOCK
|
||||
|
||||
This clauses will cause all errors that satisfy C<$err-E<gt>isa(CLASS)>
|
||||
to be caught and handled by evaluating C<BLOCK>.
|
||||
|
||||
C<BLOCK> will be passed two arguments. The first will be the error
|
||||
being thrown. The second is a reference to a scalar variable. If this
|
||||
variable is set by the catch block then, on return from the catch
|
||||
block, try will continue processing as if the catch block was never
|
||||
found.
|
||||
|
||||
To propagate the error the catch block may call C<$err-E<gt>throw>
|
||||
|
||||
If the scalar reference by the second argument is not set, and the
|
||||
error is not thrown. Then the current try block will return with the
|
||||
result from the catch block.
|
||||
|
||||
=item except BLOCK
|
||||
|
||||
When C<try> is looking for a handler, if an except clause is found
|
||||
C<BLOCK> is evaluated. The return value from this block should be a
|
||||
HASHREF or a list of key-value pairs, where the keys are class names
|
||||
and the values are CODE references for the handler of errors of that
|
||||
type.
|
||||
|
||||
=item otherwise BLOCK
|
||||
|
||||
Catch any error by executing the code in C<BLOCK>
|
||||
|
||||
When evaluated C<BLOCK> will be passed one argument, which will be the
|
||||
error being processed.
|
||||
|
||||
Only one otherwise block may be specified per try block
|
||||
|
||||
=item finally BLOCK
|
||||
|
||||
Execute the code in C<BLOCK> either after the code in the try block has
|
||||
successfully completed, or if the try block throws an error then
|
||||
C<BLOCK> will be executed after the handler has completed.
|
||||
|
||||
If the handler throws an error then the error will be caught, the
|
||||
finally block will be executed and the error will be re-thrown.
|
||||
|
||||
Only one finally block may be specified per try block
|
||||
|
||||
=back
|
||||
|
||||
=head1 CLASS INTERFACE
|
||||
|
||||
=head2 CONSTRUCTORS
|
||||
|
||||
The C<Error> object is implemented as a HASH. This HASH is initialized
|
||||
with the arguments that are passed to it's constructor. The elements
|
||||
that are used by, or are retrievable by the C<Error> class are listed
|
||||
below, other classes may add to these.
|
||||
|
||||
-file
|
||||
-line
|
||||
-text
|
||||
-value
|
||||
-object
|
||||
|
||||
If C<-file> or C<-line> are not specified in the constructor arguments
|
||||
then these will be initialized with the file name and line number where
|
||||
the constructor was called from.
|
||||
|
||||
If the error is associated with an object then the object should be
|
||||
passed as the C<-object> argument. This will allow the C<Error> package
|
||||
to associate the error with the object.
|
||||
|
||||
The C<Error> package remembers the last error created, and also the
|
||||
last error associated with a package. This could either be the last
|
||||
error created by a sub in that package, or the last error which passed
|
||||
an object blessed into that package as the C<-object> argument.
|
||||
|
||||
=over 4
|
||||
|
||||
=item throw ( [ ARGS ] )
|
||||
|
||||
Create a new C<Error> object and throw an error, which will be caught
|
||||
by a surrounding C<try> block, if there is one. Otherwise it will cause
|
||||
the program to exit.
|
||||
|
||||
C<throw> may also be called on an existing error to re-throw it.
|
||||
|
||||
=item with ( [ ARGS ] )
|
||||
|
||||
Create a new C<Error> object and returns it. This is defined for
|
||||
syntactic sugar, eg
|
||||
|
||||
die with Some::Error ( ... );
|
||||
|
||||
=item record ( [ ARGS ] )
|
||||
|
||||
Create a new C<Error> object and returns it. This is defined for
|
||||
syntactic sugar, eg
|
||||
|
||||
record Some::Error ( ... )
|
||||
and return;
|
||||
|
||||
=back
|
||||
|
||||
=head2 STATIC METHODS
|
||||
|
||||
=over 4
|
||||
|
||||
=item prior ( [ PACKAGE ] )
|
||||
|
||||
Return the last error created, or the last error associated with
|
||||
C<PACKAGE>
|
||||
|
||||
=item flush ( [ PACKAGE ] )
|
||||
|
||||
Flush the last error created, or the last error associated with
|
||||
C<PACKAGE>.It is necessary to clear the error stack before exiting the
|
||||
package or uncaught errors generated using C<record> will be reported.
|
||||
|
||||
$Error->flush;
|
||||
|
||||
=cut
|
||||
|
||||
=back
|
||||
|
||||
=head2 OBJECT METHODS
|
||||
|
||||
=over 4
|
||||
|
||||
=item stacktrace
|
||||
|
||||
If the variable C<$Error::Debug> was non-zero when the error was
|
||||
created, then C<stacktrace> returns a string created by calling
|
||||
C<Carp::longmess>. If the variable was zero the C<stacktrace> returns
|
||||
the text of the error appended with the filename and line number of
|
||||
where the error was created, providing the text does not end with a
|
||||
newline.
|
||||
|
||||
=item object
|
||||
|
||||
The object this error was associated with
|
||||
|
||||
=item file
|
||||
|
||||
The file where the constructor of this error was called from
|
||||
|
||||
=item line
|
||||
|
||||
The line where the constructor of this error was called from
|
||||
|
||||
=item text
|
||||
|
||||
The text of the error
|
||||
|
||||
=back
|
||||
|
||||
=head2 OVERLOAD METHODS
|
||||
|
||||
=over 4
|
||||
|
||||
=item stringify
|
||||
|
||||
A method that converts the object into a string. This method may simply
|
||||
return the same as the C<text> method, or it may append more
|
||||
information. For example the file name and line number.
|
||||
|
||||
By default this method returns the C<-text> argument that was passed to
|
||||
the constructor, or the string C<"Died"> if none was given.
|
||||
|
||||
=item value
|
||||
|
||||
A method that will return a value that can be associated with the
|
||||
error. For example if an error was created due to the failure of a
|
||||
system call, then this may return the numeric value of C<$!> at the
|
||||
time.
|
||||
|
||||
By default this method returns the C<-value> argument that was passed
|
||||
to the constructor.
|
||||
|
||||
=back
|
||||
|
||||
=head1 PRE-DEFINED ERROR CLASSES
|
||||
|
||||
=over 4
|
||||
|
||||
=item Error::Simple
|
||||
|
||||
This class can be used to hold simple error strings and values. It's
|
||||
constructor takes two arguments. The first is a text value, the second
|
||||
is a numeric value. These values are what will be returned by the
|
||||
overload methods.
|
||||
|
||||
If the text value ends with C<at file line 1> as $@ strings do, then
|
||||
this infomation will be used to set the C<-file> and C<-line> arguments
|
||||
of the error object.
|
||||
|
||||
This class is used internally if an eval'd block die's with an error
|
||||
that is a plain string. (Unless C<$Error::ObjectifyCallback> is modified)
|
||||
|
||||
=back
|
||||
|
||||
=head1 $Error::ObjectifyCallback
|
||||
|
||||
This variable holds a reference to a subroutine that converts errors that
|
||||
are plain strings to objects. It is used by Error.pm to convert textual
|
||||
errors to objects, and can be overrided by the user.
|
||||
|
||||
It accepts a single argument which is a hash reference to named parameters.
|
||||
Currently the only named parameter passed is C<'text'> which is the text
|
||||
of the error, but others may be available in the future.
|
||||
|
||||
For example the following code will cause Error.pm to throw objects of the
|
||||
class MyError::Bar by default:
|
||||
|
||||
sub throw_MyError_Bar
|
||||
{
|
||||
my $args = shift;
|
||||
my $err = MyError::Bar->new();
|
||||
$err->{'MyBarText'} = $args->{'text'};
|
||||
return $err;
|
||||
}
|
||||
|
||||
{
|
||||
local $Error::ObjectifyCallback = \&throw_MyError_Bar;
|
||||
|
||||
# Error handling here.
|
||||
}
|
||||
|
||||
=head1 KNOWN BUGS
|
||||
|
||||
None, but that does not mean there are not any.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Graham Barr <gbarr@pobox.com>
|
||||
|
||||
The code that inspired me to write this was originally written by
|
||||
Peter Seibel <peter@weblogic.com> and adapted by Jesse Glick
|
||||
<jglick@sig.bsh.com>.
|
||||
|
||||
=head1 MAINTAINER
|
||||
|
||||
Shlomi Fish <shlomif@iglu.org.il>
|
||||
|
||||
=head1 PAST MAINTAINERS
|
||||
|
||||
Arun Kumar U <u_arunkumar@yahoo.com>
|
||||
|
||||
=cut
|
6
refs.c
6
refs.c
@ -234,6 +234,12 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
|
||||
}
|
||||
}
|
||||
|
||||
/* Is it a directory? */
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
errno = EISDIR;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Anything else, just open it and try to use it as
|
||||
* a ref
|
||||
|
@ -86,4 +86,23 @@ test_expect_success \
|
||||
'move into "."' \
|
||||
'git-mv path1/path2/ .'
|
||||
|
||||
test_expect_success "Michael Cassar's test case" '
|
||||
rm -fr .git papers partA &&
|
||||
git init-db &&
|
||||
mkdir -p papers/unsorted papers/all-papers partA &&
|
||||
echo a > papers/unsorted/Thesis.pdf &&
|
||||
echo b > partA/outline.txt &&
|
||||
echo c > papers/unsorted/_another &&
|
||||
git add papers partA &&
|
||||
T1=`git write-tree` &&
|
||||
|
||||
git mv papers/unsorted/Thesis.pdf papers/all-papers/moo-blah.pdf &&
|
||||
|
||||
T=`git write-tree` &&
|
||||
git ls-tree -r $T | grep partA/outline.txt || {
|
||||
git ls-tree -r $T
|
||||
(exit 1)
|
||||
}
|
||||
'
|
||||
|
||||
test_done
|
||||
|
@ -218,6 +218,8 @@ PYTHON=`sed -e '1{
|
||||
PYTHONPATH=$(pwd)/../compat
|
||||
export PYTHONPATH
|
||||
}
|
||||
GITPERLLIB=$(pwd)/../perl/blib/lib:$(pwd)/../perl/blib/arch/auto/Git
|
||||
export GITPERLLIB
|
||||
test -d ../templates/blt || {
|
||||
error "You haven't built things yet, have you?"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user