Merge branch 'master' into lj/refs

* master: (72 commits)
  runstatus: do not recurse into subdirectories if not needed
  grep: fix --fixed-strings combined with expression.
  grep: free expressions and patterns when done.
  Corrected copy-and-paste thinko in ignore executable bit test case.
  An illustration of rev-list --parents --pretty=raw
  Allow git-checkout when on a non-existant branch.
  gitweb: Decode long title for link tooltips
  git-svn: Fix fetch --no-ignore-externals with GIT_SVN_NO_LIB=1
  Ignore executable bit when adding files if filemode=0.
  Remove empty ref directories that prevent creating a ref.
  Use const for interpolate arguments
  git-archive: update documentation
  Deprecate merge-recursive.py
  gitweb: fix over-eager application of esc_html().
  Allow '(no author)' in git-svn's authors file.
  Allow 'svn fetch' on '(no date)' revisions in Subversion.
  git-repack: allow git-repack to run in subdirectory
  Remove upload-tar and make git-tar-tree a thin wrapper to git-archive
  git-tar-tree: Move code for git-archive --format=tar to archive-tar.c
  git-tar-tree: Remove duplicate git_config() call
  ...
This commit is contained in:
Junio C Hamano 2006-09-27 22:23:12 -07:00
commit 2958d9b5db
70 changed files with 2330 additions and 1611 deletions

3
.gitignore vendored
View File

@ -66,6 +66,7 @@ git-merge-one-file
git-merge-ours git-merge-ours
git-merge-recur git-merge-recur
git-merge-recursive git-merge-recursive
git-merge-recursive-old
git-merge-resolve git-merge-resolve
git-merge-stupid git-merge-stupid
git-mktag git-mktag
@ -125,13 +126,11 @@ git-update-ref
git-update-server-info git-update-server-info
git-upload-archive git-upload-archive
git-upload-pack git-upload-pack
git-upload-tar
git-var git-var
git-verify-pack git-verify-pack
git-verify-tag git-verify-tag
git-whatchanged git-whatchanged
git-write-tree git-write-tree
git-zip-tree
git-core-*/?* git-core-*/?*
gitweb/gitweb.cgi gitweb/gitweb.cgi
test-date test-date

View File

@ -119,6 +119,13 @@ apply.whitespace::
Tells `git-apply` how to handle whitespaces, in the same way Tells `git-apply` how to handle whitespaces, in the same way
as the '--whitespace' option. See gitlink:git-apply[1]. as the '--whitespace' option. See gitlink:git-apply[1].
branch.<name>.remote::
When in branch <name>, it tells `git fetch` which remote to fetch.
branch.<name>.merge::
When in branch <name>, it tells `git fetch` the default remote branch
to be merged.
pager.color:: pager.color::
A boolean to enable/disable colored output when the pager is in A boolean to enable/disable colored output when the pager is in
use (default is true). use (default is true).
@ -267,3 +274,10 @@ whatchanged.difftree::
imap:: imap::
The configuration variables in the 'imap' section are described The configuration variables in the 'imap' section are described
in gitlink:git-imap-send[1]. in gitlink:git-imap-send[1].
receive.denyNonFastforwads::
If set to true, git-receive-pack will deny a ref update which is
not a fast forward. Use this to prevent such an update via a push,
even if that push is forced. This configuration variable is
set when initializing a shared repository.

View File

@ -40,6 +40,7 @@ OPTIONS
<extra>:: <extra>::
This can be any options that the archiver backend understand. This can be any options that the archiver backend understand.
See next section.
--remote=<repo>:: --remote=<repo>::
Instead of making a tar archive from local repository, Instead of making a tar archive from local repository,
@ -52,6 +53,18 @@ path::
If one or more paths are specified, include only these in the If one or more paths are specified, include only these in the
archive, otherwise include all files and subdirectories. archive, otherwise include all files and subdirectories.
BACKEND EXTRA OPTIONS
---------------------
zip
~~~
-0::
Store the files instead of deflating them.
-9::
Highest and slowest compression level. You can specify any
number from 1 to 9 to adjust compression speed and ratio.
CONFIGURATION CONFIGURATION
------------- -------------
By default, file and directories modes are set to 0666 or 0777 in tar By default, file and directories modes are set to 0666 or 0777 in tar

View File

@ -11,6 +11,7 @@ SYNOPSIS
'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all] 'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all]
[--timeout=n] [--init-timeout=n] [--strict-paths] [--timeout=n] [--init-timeout=n] [--strict-paths]
[--base-path=path] [--user-path | --user-path=path] [--base-path=path] [--user-path | --user-path=path]
[--interpolated-path=pathtemplate]
[--enable=service] [--disable=service] [--enable=service] [--disable=service]
[--allow-override=service] [--forbid-override=service] [--allow-override=service] [--forbid-override=service]
[--reuseaddr] [--detach] [--pid-file=file] [--reuseaddr] [--detach] [--pid-file=file]
@ -50,6 +51,12 @@ OPTIONS
'git://example.com/hello.git', `git-daemon` will interpret the path 'git://example.com/hello.git', `git-daemon` will interpret the path
as '/srv/git/hello.git'. as '/srv/git/hello.git'.
--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,
and %D for the absolute path of the named repository.
--export-all:: --export-all::
Allow pulling from all directories that look like GIT repositories Allow pulling from all directories that look like GIT repositories
(have the 'objects' and 'refs' subdirectories), even if they (have the 'objects' and 'refs' subdirectories), even if they
@ -135,6 +142,46 @@ upload-pack::
disable it by setting `daemon.uploadpack` configuration disable it by setting `daemon.uploadpack` configuration
item to `false`. item to `false`.
EXAMPLES
--------
git-daemon as inetd server::
To set up `git-daemon` as an inetd service that handles any
repository under the whitelisted set of directories, /pub/foo
and /pub/bar, place an entry like the following into
/etc/inetd all on one line:
+
------------------------------------------------
git stream tcp nowait nobody /usr/bin/git-daemon
git-daemon --inetd --verbose
--syslog --export-all
/pub/foo /pub/bar
------------------------------------------------
git-daemon as inetd server for virtual hosts::
To set up `git-daemon` as an inetd service that handles
repositories for different virtual hosts, `www.example.com`
and `www.example.org`, place an entry like the following into
`/etc/inetd` all on one line:
+
------------------------------------------------
git stream tcp nowait nobody /usr/bin/git-daemon
git-daemon --inetd --verbose
--syslog --export-all
--interpolated-path=/pub/%H%D
/pub/www.example.org/software
/pub/www.example.com/software
/software
------------------------------------------------
+
In this example, the root-level directory `/pub` will contain
a subdirectory for each virtual host name supported.
Further, both hosts advertise repositories simply as
`git://www.example.com/software/repo.git`. For pre-1.4.0
clients, a symlink from `/software` into the appropriate
default repository could be made as well.
Author Author
------ ------
Written by Linus Torvalds <torvalds@osdl.org>, YOSHIFUJI Hideaki Written by Linus Torvalds <torvalds@osdl.org>, YOSHIFUJI Hideaki

View File

@ -48,6 +48,10 @@ is given:
- 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository - 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository
readable by all users. readable by all users.
By default, the configuration flag receive.denyNonFastforward is enabled
in shared repositories, so that you cannot force a non fast-forwarding push
into it.
-- --

View File

@ -73,6 +73,8 @@ packed and is served via a dumb transport.
There are other real-world examples of using update and There are other real-world examples of using update and
post-update hooks found in the Documentation/howto directory. post-update hooks found in the Documentation/howto directory.
git-receive-pack honours the receive.denyNonFastforwards flag, which
tells it if updates to a ref should be denied if they are not fast-forwards.
OPTIONS OPTIONS
------- -------

View File

@ -20,6 +20,7 @@ SYNOPSIS
[ \--stdin ] [ \--stdin ]
[ \--topo-order ] [ \--topo-order ]
[ \--parents ] [ \--parents ]
[ \--(author|committer|grep)=<pattern> ]
[ [\--objects | \--objects-edge] [ \--unpacked ] ] [ [\--objects | \--objects-edge] [ \--unpacked ] ]
[ \--pretty | \--header ] [ \--pretty | \--header ]
[ \--bisect ] [ \--bisect ]
@ -154,6 +155,16 @@ limiting may be applied.
Limit the commits output to specified time range. Limit the commits output to specified time range.
--author='pattern', --committer='pattern'::
Limit the commits output to ones with author/committer
header lines that match the specified pattern.
--grep='pattern'::
Limit the commits output to ones with log message that
matches the specified pattern.
--remove-empty:: --remove-empty::
Stop when a given path disappears from the tree. Stop when a given path disappears from the tree.

View File

@ -244,6 +244,18 @@ doing.
repo-config key: svn.noignoreexternals repo-config key: svn.noignoreexternals
--ignore-nodate::
Only used with the 'fetch' command.
By default git-svn will crash if it tries to import a revision
from SVN which has '(no date)' listed as the date of the revision.
This is repository corruption on SVN's part, plain and simple.
But sometimes you really need those revisions anyway.
If supplied git-svn will convert '(no date)' entries to the UNIX
epoch (midnight on Jan. 1, 1970). Yes, that's probably very wrong.
SVN was very wrong.
-- --
Basic Examples Basic Examples

View File

@ -12,6 +12,9 @@ SYNOPSIS
DESCRIPTION DESCRIPTION
----------- -----------
THIS COMMAND IS DEPRECATED. Use `git-archive` with `--format=tar`
option instead.
Creates a tar archive containing the tree structure for the named tree. Creates a tar archive containing the tree structure for the named tree.
When <base> is specified it is added as a leading path to the files in the When <base> is specified it is added as a leading path to the files in the
generated tar archive. generated tar archive.

View File

@ -1,39 +0,0 @@
git-upload-tar(1)
=================
NAME
----
git-upload-tar - Send tar archive
SYNOPSIS
--------
'git-upload-tar' <directory>
DESCRIPTION
-----------
Invoked by 'git-tar-tree --remote' and sends a generated tar archive
to the other end over the git protocol.
This command is usually not invoked directly by the end user.
The UI for the protocol is on the 'git-tar-tree' side, and the
program pair is meant to be used to get a tar archive from a
remote repository.
OPTIONS
-------
<directory>::
The repository to get a tar archive from.
Author
------
Written by Junio C Hamano <junio@kernel.org>
Documentation
--------------
Documentation by Junio C Hamano.
GIT
---
Part of the gitlink:git[7] suite

View File

@ -1,67 +0,0 @@
git-zip-tree(1)
===============
NAME
----
git-zip-tree - Creates a ZIP archive of the files in the named tree
SYNOPSIS
--------
'git-zip-tree' [-0|...|-9] <tree-ish> [ <base> ]
DESCRIPTION
-----------
Creates a ZIP archive containing the tree structure for the named tree.
When <base> is specified it is added as a leading path to the files in the
generated ZIP archive.
git-zip-tree behaves differently when given a tree ID versus when given
a commit ID or tag ID. In the first case the current time is used as
modification time of each file in the archive. In the latter case the
commit time as recorded in the referenced commit object is used instead.
Additionally the commit ID is stored as an archive comment.
Currently git-zip-tree can handle only files and directories, symbolic
links are not supported.
OPTIONS
-------
-0::
Store the files instead of deflating them.
-9::
Highest and slowest compression level. You can specify any
number from 1 to 9 to adjust compression speed and ratio.
<tree-ish>::
The tree or commit to produce ZIP archive for. If it is
the object name of a commit object.
<base>::
Leading path to the files in the resulting ZIP archive.
EXAMPLES
--------
git zip-tree v1.4.0 git-1.4.0 >git-1.4.0.zip::
Create a ZIP file for v1.4.0 release.
git zip-tree HEAD:Documentation/ git-docs >docs.zip::
Put everything in the current head's Documentation/ directory
into 'docs.zip', with the prefix 'git-docs/'.
Author
------
Written by Rene Scharfe.
Documentation
--------------
Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite

View File

@ -247,10 +247,6 @@ gitlink:git-upload-pack[1]::
Invoked by 'git-fetch-pack' to push Invoked by 'git-fetch-pack' to push
what are asked for. what are asked for.
gitlink:git-upload-tar[1]::
Invoked by 'git-tar-tree --remote' to return the tar
archive the other end asked for.
High-level commands (porcelain) High-level commands (porcelain)
------------------------------- -------------------------------
@ -270,6 +266,9 @@ gitlink:git-am[1]::
gitlink:git-applymbox[1]:: gitlink:git-applymbox[1]::
Apply patches from a mailbox, original version by Linus. Apply patches from a mailbox, original version by Linus.
gitlink:git-archive[1]::
Creates an archive of files from a named tree.
gitlink:git-bisect[1]:: gitlink:git-bisect[1]::
Find the change that introduced a bug by binary search. Find the change that introduced a bug by binary search.

View File

@ -37,6 +37,10 @@ frequently used options.
Show commits older than a specific date. Show commits older than a specific date.
--all::
Show all branches.
<revs>:: <revs>::
Limit the revisions to show. This can be either a single revision Limit the revisions to show. This can be either a single revision
@ -63,6 +67,11 @@ gitk --since="2 weeks ago" \-- gitk::
The "--" is necessary to avoid confusion with the *branch* named The "--" is necessary to avoid confusion with the *branch* named
'gitk' 'gitk'
gitk --max-count=100 --all -- Makefile::
Show at most 100 changes made to the file 'Makefile'. Instead of only
looking for changes in the current branch look in all branches.
See Also See Also
-------- --------
'qgit(1)':: 'qgit(1)'::

View File

@ -5,8 +5,7 @@ Hooks are little scripts you can place in `$GIT_DIR/hooks`
directory to trigger action at certain points. When directory to trigger action at certain points. When
`git-init-db` is run, a handful example hooks are copied in the `git-init-db` is run, a handful example hooks are copied in the
`hooks` directory of the new repository, but by default they are `hooks` directory of the new repository, but by default they are
all disabled. To enable a hook, make it executable with `chmod all disabled. To enable a hook, make it executable with `chmod +x`.
+x`.
This document describes the currently defined hooks. This document describes the currently defined hooks.
@ -16,16 +15,16 @@ applypatch-msg
This hook is invoked by `git-applypatch` script, which is This hook is invoked by `git-applypatch` script, which is
typically invoked by `git-applymbox`. It takes a single typically invoked by `git-applymbox`. It takes a single
parameter, the name of the file that holds the proposed commit parameter, the name of the file that holds the proposed commit
log message. Exiting with non-zero status causes the log message. Exiting with non-zero status causes
'git-applypatch' to abort before applying the patch. `git-applypatch` to abort before applying the patch.
The hook is allowed to edit the message file in place, and can The hook is allowed to edit the message file in place, and can
be used to normalize the message into some project standard be used to normalize the message into some project standard
format (if the project has one). It can also be used to refuse format (if the project has one). It can also be used to refuse
the commit after inspecting the message file. the commit after inspecting the message file.
The default applypatch-msg hook, when enabled, runs the The default 'applypatch-msg' hook, when enabled, runs the
commit-msg hook, if the latter is enabled. 'commit-msg' hook, if the latter is enabled.
pre-applypatch pre-applypatch
-------------- --------------
@ -39,8 +38,8 @@ after application of the patch not committed.
It can be used to inspect the current working tree and refuse to It can be used to inspect the current working tree and refuse to
make a commit if it does not pass certain test. make a commit if it does not pass certain test.
The default pre-applypatch hook, when enabled, runs the The default 'pre-applypatch' hook, when enabled, runs the
pre-commit hook, if the latter is enabled. 'pre-commit' hook, if the latter is enabled.
post-applypatch post-applypatch
--------------- ---------------
@ -61,9 +60,9 @@ invoked before obtaining the proposed commit log message and
making a commit. Exiting with non-zero status from this script making a commit. Exiting with non-zero status from this script
causes the `git-commit` to abort. causes the `git-commit` to abort.
The default pre-commit hook, when enabled, catches introduction The default 'pre-commit' hook, when enabled, catches introduction
of lines with trailing whitespaces and aborts the commit when of lines with trailing whitespaces and aborts the commit when
a such line is found. such a line is found.
commit-msg commit-msg
---------- ----------
@ -79,8 +78,8 @@ be used to normalize the message into some project standard
format (if the project has one). It can also be used to refuse format (if the project has one). It can also be used to refuse
the commit after inspecting the message file. the commit after inspecting the message file.
The default commit-msg hook, when enabled, detects duplicate The default 'commit-msg' hook, when enabled, detects duplicate
Signed-off-by: lines, and aborts the commit when one is found. "Signed-off-by" lines, and aborts the commit if one is found.
post-commit post-commit
----------- -----------
@ -91,23 +90,24 @@ parameter, and is invoked after a commit is made.
This hook is meant primarily for notification, and cannot affect This hook is meant primarily for notification, and cannot affect
the outcome of `git-commit`. the outcome of `git-commit`.
The default post-commit hook, when enabled, demonstrates how to The default 'post-commit' hook, when enabled, demonstrates how to
send out a commit notification e-mail. send out a commit notification e-mail.
update update
------ ------
This hook is invoked by `git-receive-pack` on the remote repository, This hook is invoked by `git-receive-pack` on the remote repository,
which is happens when a `git push` is done on a local repository. which happens when a `git push` is done on a local repository.
Just before updating the ref on the remote repository, the update hook Just before updating the ref on the remote repository, the update hook
is invoked. Its exit status determines the success or failure of is invoked. Its exit status determines the success or failure of
the ref update. the ref update.
The hook executes once for each ref to be updated, and takes The hook executes once for each ref to be updated, and takes
three parameters: three parameters:
- the name of the ref being updated,
- the old object name stored in the ref, - the name of the ref being updated,
- and the new objectname to be stored in the ref. - the old object name stored in the ref,
- and the new objectname to be stored in the ref.
A zero exit from the update hook allows the ref to be updated. A zero exit from the update hook allows the ref to be updated.
Exiting with a non-zero status prevents `git-receive-pack` Exiting with a non-zero status prevents `git-receive-pack`
@ -126,16 +126,16 @@ Another use suggested on the mailing list is to use this hook to
implement access control which is finer grained than the one implement access control which is finer grained than the one
based on filesystem group. based on filesystem group.
The standard output of this hook is sent to /dev/null; if you The standard output of this hook is sent to `/dev/null`; if you
want to report something to the git-send-pack on the other end, want to report something to the `git-send-pack` on the other end,
you can redirect your output to your stderr. you can redirect your output to your `stderr`.
post-update post-update
----------- -----------
This hook is invoked by `git-receive-pack` on the remote repository, This hook is invoked by `git-receive-pack` on the remote repository,
which is happens when a `git push` is done on a local repository. which happens when a `git push` is done on a local repository.
It executes on the remote repository once after all the refs have It executes on the remote repository once after all the refs have
been updated. been updated.
@ -145,16 +145,16 @@ name of ref that was actually updated.
This hook is meant primarily for notification, and cannot affect This hook is meant primarily for notification, and cannot affect
the outcome of `git-receive-pack`. the outcome of `git-receive-pack`.
The post-update hook can tell what are the heads that were pushed, The 'post-update' hook can tell what are the heads that were pushed,
but it does not know what their original and updated values are, but it does not know what their original and updated values are,
so it is a poor place to do log old..new. so it is a poor place to do log old..new.
The default post-update hook, when enabled, runs When enabled, the default 'post-update' hook runs
`git-update-server-info` to keep the information used by dumb `git-update-server-info` to keep the information used by dumb
transports (e.g., http) up-to-date. If you are publishing transports (e.g., HTTP) up-to-date. If you are publishing
a git repository that is accessible via http, you should a git repository that is accessible via HTTP, you should
probably enable this hook. probably enable this hook.
The standard output of this hook is sent to /dev/null; if you The standard output of this hook is sent to `/dev/null`; if you
want to report something to the git-send-pack on the other end, want to report something to the `git-send-pack` on the other end,
you can redirect your output to your stderr. you can redirect your output to your `stderr`.

View File

@ -81,8 +81,6 @@ all:
# Define NO_ACCURATE_DIFF if your diff program at least sometimes misses # Define NO_ACCURATE_DIFF if your diff program at least sometimes misses
# a missing newline at the end of the file. # a missing newline at the end of the file.
# #
# Define NO_PYTHON if you want to lose all benefits of the recursive merge.
#
# Define COLLISION_CHECK below if you believe that SHA1's # Define COLLISION_CHECK below if you believe that SHA1's
# 1461501637330902918203684832716283019655932542976 hashes do not give you # 1461501637330902918203684832716283019655932542976 hashes do not give you
# sufficient guarantee that no collisions between objects will ever happen. # sufficient guarantee that no collisions between objects will ever happen.
@ -174,7 +172,7 @@ SCRIPT_PERL = \
git-send-email.perl git-svn.perl git-send-email.perl git-svn.perl
SCRIPT_PYTHON = \ SCRIPT_PYTHON = \
git-merge-recursive.py git-merge-recursive-old.py
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
$(patsubst %.perl,%,$(SCRIPT_PERL)) \ $(patsubst %.perl,%,$(SCRIPT_PERL)) \
@ -199,7 +197,7 @@ PROGRAMS = \
git-upload-pack$X git-verify-pack$X \ git-upload-pack$X git-verify-pack$X \
git-pack-redundant$X git-var$X \ git-pack-redundant$X git-var$X \
git-describe$X git-merge-tree$X git-blame$X git-imap-send$X \ git-describe$X git-merge-tree$X git-blame$X git-imap-send$X \
git-merge-recur$X \ git-merge-recursive$X \
$(EXTRA_PROGRAMS) $(EXTRA_PROGRAMS)
# Empty... # Empty...
@ -234,7 +232,7 @@ LIB_FILE=libgit.a
XDIFF_LIB=xdiff/lib.a XDIFF_LIB=xdiff/lib.a
LIB_H = \ LIB_H = \
archive.h blob.h cache.h commit.h csum-file.h delta.h \ archive.h blob.h cache.h commit.h csum-file.h delta.h grep.h \
diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \ diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \ run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h
@ -246,15 +244,17 @@ DIFF_OBJS = \
LIB_OBJS = \ LIB_OBJS = \
blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \ blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
date.o diff-delta.o entry.o exec_cmd.o ident.o lockfile.o \ date.o diff-delta.o entry.o exec_cmd.o ident.o \
interpolate.o \
lockfile.o \
object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \ object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \
quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \ quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \
server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \ server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
tag.o tree.o usage.o config.o environment.o ctype.o copy.o \ tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \ fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
write_or_die.o trace.o list-objects.o \ write_or_die.o trace.o list-objects.o grep.o \
alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \ alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
color.o wt-status.o color.o wt-status.o archive-zip.o archive-tar.o
BUILTIN_OBJS = \ BUILTIN_OBJS = \
builtin-add.o \ builtin-add.o \
@ -299,10 +299,8 @@ BUILTIN_OBJS = \
builtin-update-index.o \ builtin-update-index.o \
builtin-update-ref.o \ builtin-update-ref.o \
builtin-upload-archive.o \ builtin-upload-archive.o \
builtin-upload-tar.o \
builtin-verify-pack.o \ builtin-verify-pack.o \
builtin-write-tree.o \ builtin-write-tree.o \
builtin-zip-tree.o \
builtin-show-ref.o \ builtin-show-ref.o \
builtin-pack-refs.o builtin-pack-refs.o
@ -573,7 +571,8 @@ LIB_OBJS += $(COMPAT_OBJS)
export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
### Build rules ### Build rules
all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi \
git-merge-recur$X
all: all:
$(MAKE) -C templates $(MAKE) -C templates
@ -588,6 +587,9 @@ git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS) GIT-CFLAGS
help.o: common-cmds.h help.o: common-cmds.h
git-merge-recur$X: git-merge-recursive$X
rm -f $@ && ln git-merge-recursive$X $@
$(BUILT_INS): git$X $(BUILT_INS): git$X
rm -f $@ && ln git$X $@ rm -f $@ && ln git$X $@
@ -725,11 +727,6 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
merge-recursive.o path-list.o: path-list.h
git-merge-recur$X: merge-recursive.o path-list.o $(GITLIBS)
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS)
$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
$(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
$(DIFF_OBJS): diffcore.h $(DIFF_OBJS): diffcore.h
@ -890,6 +887,7 @@ check-docs::
case "$$v" in \ case "$$v" in \
git-merge-octopus | git-merge-ours | git-merge-recursive | \ git-merge-octopus | git-merge-ours | git-merge-recursive | \
git-merge-resolve | git-merge-stupid | git-merge-recur | \ git-merge-resolve | git-merge-stupid | git-merge-recur | \
git-merge-recursive-old | \
git-ssh-pull | git-ssh-push ) continue ;; \ git-ssh-pull | git-ssh-push ) continue ;; \
esac ; \ esac ; \
test -f "Documentation/$$v.txt" || \ test -f "Documentation/$$v.txt" || \

325
archive-tar.c Normal file
View File

@ -0,0 +1,325 @@
/*
* Copyright (c) 2005, 2006 Rene Scharfe
*/
#include <time.h>
#include "cache.h"
#include "commit.h"
#include "strbuf.h"
#include "tar.h"
#include "builtin.h"
#include "archive.h"
#define RECORDSIZE (512)
#define BLOCKSIZE (RECORDSIZE * 20)
static char block[BLOCKSIZE];
static unsigned long offset;
static time_t archive_time;
static int tar_umask;
static int verbose;
/* writes out the whole block, but only if it is full */
static void write_if_needed(void)
{
if (offset == BLOCKSIZE) {
write_or_die(1, block, BLOCKSIZE);
offset = 0;
}
}
/*
* queues up writes, so that all our write(2) calls write exactly one
* full block; pads writes to RECORDSIZE
*/
static void write_blocked(const void *data, unsigned long size)
{
const char *buf = data;
unsigned long tail;
if (offset) {
unsigned long chunk = BLOCKSIZE - offset;
if (size < chunk)
chunk = size;
memcpy(block + offset, buf, chunk);
size -= chunk;
offset += chunk;
buf += chunk;
write_if_needed();
}
while (size >= BLOCKSIZE) {
write_or_die(1, buf, BLOCKSIZE);
size -= BLOCKSIZE;
buf += BLOCKSIZE;
}
if (size) {
memcpy(block + offset, buf, size);
offset += size;
}
tail = offset % RECORDSIZE;
if (tail) {
memset(block + offset, 0, RECORDSIZE - tail);
offset += RECORDSIZE - tail;
}
write_if_needed();
}
/*
* The end of tar archives is marked by 2*512 nul bytes and after that
* follows the rest of the block (if any).
*/
static void write_trailer(void)
{
int tail = BLOCKSIZE - offset;
memset(block + offset, 0, tail);
write_or_die(1, block, BLOCKSIZE);
if (tail < 2 * RECORDSIZE) {
memset(block, 0, offset);
write_or_die(1, block, BLOCKSIZE);
}
}
static void strbuf_append_string(struct strbuf *sb, const char *s)
{
int slen = strlen(s);
int total = sb->len + slen;
if (total > sb->alloc) {
sb->buf = xrealloc(sb->buf, total);
sb->alloc = total;
}
memcpy(sb->buf + sb->len, s, slen);
sb->len = total;
}
/*
* pax extended header records have the format "%u %s=%s\n". %u contains
* the size of the whole string (including the %u), the first %s is the
* keyword, the second one is the value. This function constructs such a
* string and appends it to a struct strbuf.
*/
static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,
const char *value, unsigned int valuelen)
{
char *p;
int len, total, tmp;
/* "%u %s=%s\n" */
len = 1 + 1 + strlen(keyword) + 1 + valuelen + 1;
for (tmp = len; tmp > 9; tmp /= 10)
len++;
total = sb->len + len;
if (total > sb->alloc) {
sb->buf = xrealloc(sb->buf, total);
sb->alloc = total;
}
p = sb->buf;
p += sprintf(p, "%u %s=", len, keyword);
memcpy(p, value, valuelen);
p += valuelen;
*p = '\n';
sb->len = total;
}
static unsigned int ustar_header_chksum(const struct ustar_header *header)
{
char *p = (char *)header;
unsigned int chksum = 0;
while (p < header->chksum)
chksum += *p++;
chksum += sizeof(header->chksum) * ' ';
p += sizeof(header->chksum);
while (p < (char *)header + sizeof(struct ustar_header))
chksum += *p++;
return chksum;
}
static int get_path_prefix(const struct strbuf *path, int maxlen)
{
int i = path->len;
if (i > maxlen)
i = maxlen;
do {
i--;
} while (i > 0 && path->buf[i] != '/');
return i;
}
static void write_entry(const unsigned char *sha1, struct strbuf *path,
unsigned int mode, void *buffer, unsigned long size)
{
struct ustar_header header;
struct strbuf ext_header;
memset(&header, 0, sizeof(header));
ext_header.buf = NULL;
ext_header.len = ext_header.alloc = 0;
if (!sha1) {
*header.typeflag = TYPEFLAG_GLOBAL_HEADER;
mode = 0100666;
strcpy(header.name, "pax_global_header");
} else if (!path) {
*header.typeflag = TYPEFLAG_EXT_HEADER;
mode = 0100666;
sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
} else {
if (verbose)
fprintf(stderr, "%.*s\n", path->len, path->buf);
if (S_ISDIR(mode)) {
*header.typeflag = TYPEFLAG_DIR;
mode = (mode | 0777) & ~tar_umask;
} else if (S_ISLNK(mode)) {
*header.typeflag = TYPEFLAG_LNK;
mode |= 0777;
} else if (S_ISREG(mode)) {
*header.typeflag = TYPEFLAG_REG;
mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask;
} else {
error("unsupported file mode: 0%o (SHA1: %s)",
mode, sha1_to_hex(sha1));
return;
}
if (path->len > sizeof(header.name)) {
int plen = get_path_prefix(path, sizeof(header.prefix));
int rest = path->len - plen - 1;
if (plen > 0 && rest <= sizeof(header.name)) {
memcpy(header.prefix, path->buf, plen);
memcpy(header.name, path->buf + plen + 1, rest);
} else {
sprintf(header.name, "%s.data",
sha1_to_hex(sha1));
strbuf_append_ext_header(&ext_header, "path",
path->buf, path->len);
}
} else
memcpy(header.name, path->buf, path->len);
}
if (S_ISLNK(mode) && buffer) {
if (size > sizeof(header.linkname)) {
sprintf(header.linkname, "see %s.paxheader",
sha1_to_hex(sha1));
strbuf_append_ext_header(&ext_header, "linkpath",
buffer, size);
} else
memcpy(header.linkname, buffer, size);
}
sprintf(header.mode, "%07o", mode & 07777);
sprintf(header.size, "%011lo", S_ISREG(mode) ? size : 0);
sprintf(header.mtime, "%011lo", archive_time);
/* XXX: should we provide more meaningful info here? */
sprintf(header.uid, "%07o", 0);
sprintf(header.gid, "%07o", 0);
strlcpy(header.uname, "git", sizeof(header.uname));
strlcpy(header.gname, "git", sizeof(header.gname));
sprintf(header.devmajor, "%07o", 0);
sprintf(header.devminor, "%07o", 0);
memcpy(header.magic, "ustar", 6);
memcpy(header.version, "00", 2);
sprintf(header.chksum, "%07o", ustar_header_chksum(&header));
if (ext_header.len > 0) {
write_entry(sha1, NULL, 0, ext_header.buf, ext_header.len);
free(ext_header.buf);
}
write_blocked(&header, sizeof(header));
if (S_ISREG(mode) && buffer && size > 0)
write_blocked(buffer, size);
}
static void write_global_extended_header(const unsigned char *sha1)
{
struct strbuf ext_header;
ext_header.buf = NULL;
ext_header.len = ext_header.alloc = 0;
strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40);
write_entry(NULL, NULL, 0, ext_header.buf, ext_header.len);
free(ext_header.buf);
}
static int git_tar_config(const char *var, const char *value)
{
if (!strcmp(var, "tar.umask")) {
if (!strcmp(value, "user")) {
tar_umask = umask(0);
umask(tar_umask);
} else {
tar_umask = git_config_int(var, value);
}
return 0;
}
return git_default_config(var, value);
}
static int write_tar_entry(const unsigned char *sha1,
const char *base, int baselen,
const char *filename, unsigned mode, int stage)
{
static struct strbuf path;
int filenamelen = strlen(filename);
void *buffer;
char type[20];
unsigned long size;
if (!path.alloc) {
path.buf = xmalloc(PATH_MAX);
path.alloc = PATH_MAX;
path.len = path.eof = 0;
}
if (path.alloc < baselen + filenamelen) {
free(path.buf);
path.buf = xmalloc(baselen + filenamelen);
path.alloc = baselen + filenamelen;
}
memcpy(path.buf, base, baselen);
memcpy(path.buf + baselen, filename, filenamelen);
path.len = baselen + filenamelen;
if (S_ISDIR(mode)) {
strbuf_append_string(&path, "/");
buffer = NULL;
size = 0;
} else {
buffer = read_sha1_file(sha1, type, &size);
if (!buffer)
die("cannot read %s", sha1_to_hex(sha1));
}
write_entry(sha1, &path, mode, buffer, size);
free(buffer);
return READ_TREE_RECURSIVE;
}
int write_tar_archive(struct archiver_args *args)
{
int plen = args->base ? strlen(args->base) : 0;
git_config(git_tar_config);
archive_time = args->time;
verbose = args->verbose;
if (args->commit_sha1)
write_global_extended_header(args->commit_sha1);
if (args->base && plen > 0 && args->base[plen - 1] == '/') {
char *base = xstrdup(args->base);
int baselen = strlen(base);
while (baselen > 0 && base[baselen - 1] == '/')
base[--baselen] = '\0';
write_tar_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
free(base);
}
read_tree_recursive(args->tree, args->base, plen, 0,
args->pathspec, write_tar_entry);
write_trailer();
return 0;
}

View File

@ -10,9 +10,6 @@
#include "builtin.h" #include "builtin.h"
#include "archive.h" #include "archive.h"
static const char zip_tree_usage[] =
"git-zip-tree [-0|...|-9] <tree-ish> [ <base> ]";
static int verbose; static int verbose;
static int zip_date; static int zip_date;
static int zip_time; static int zip_time;
@ -294,68 +291,6 @@ static void dos_time(time_t *time, int *dos_date, int *dos_time)
*dos_time = t->tm_sec / 2 + t->tm_min * 32 + t->tm_hour * 2048; *dos_time = t->tm_sec / 2 + t->tm_min * 32 + t->tm_hour * 2048;
} }
int cmd_zip_tree(int argc, const char **argv, const char *prefix)
{
unsigned char sha1[20];
struct tree *tree;
struct commit *commit;
time_t archive_time;
char *base;
int baselen;
git_config(git_default_config);
if (argc > 1 && argv[1][0] == '-') {
if (isdigit(argv[1][1]) && argv[1][2] == '\0') {
zlib_compression_level = argv[1][1] - '0';
argc--;
argv++;
}
}
switch (argc) {
case 3:
base = xstrdup(argv[2]);
baselen = strlen(base);
break;
case 2:
base = xstrdup("");
baselen = 0;
break;
default:
usage(zip_tree_usage);
}
if (get_sha1(argv[1], sha1))
die("Not a valid object name %s", argv[1]);
commit = lookup_commit_reference_gently(sha1, 1);
archive_time = commit ? commit->date : time(NULL);
dos_time(&archive_time, &zip_date, &zip_time);
zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);
zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
tree = parse_tree_indirect(sha1);
if (!tree)
die("not a tree object");
if (baselen > 0) {
write_zip_entry(tree->object.sha1, "", 0, base, 040777, 0);
base = xrealloc(base, baselen + 1);
base[baselen] = '/';
baselen++;
base[baselen] = '\0';
}
read_tree_recursive(tree, base, baselen, 0, NULL, write_zip_entry);
write_zip_trailer(commit ? commit->object.sha1 : NULL);
free(zip_dir);
free(base);
return 0;
}
int write_zip_archive(struct archiver_args *args) int write_zip_archive(struct archiver_args *args)
{ {
int plen = strlen(args->base); int plen = strlen(args->base);

View File

@ -854,6 +854,49 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
return -1; return -1;
} }
static void check_whitespace(const char *line, int len)
{
const char *err = "Adds trailing whitespace";
int seen_space = 0;
int i;
/*
* We know len is at least two, since we have a '+' and we
* checked that the last character was a '\n' before calling
* this function. That is, an addition of an empty line would
* check the '+' here. Sneaky...
*/
if (isspace(line[len-2]))
goto error;
/*
* Make sure that there is no space followed by a tab in
* indentation.
*/
err = "Space in indent is followed by a tab";
for (i = 1; i < len; i++) {
if (line[i] == '\t') {
if (seen_space)
goto error;
}
else if (line[i] == ' ')
seen_space = 1;
else
break;
}
return;
error:
whitespace_error++;
if (squelch_whitespace_errors &&
squelch_whitespace_errors < whitespace_error)
;
else
fprintf(stderr, "%s.\n%s:%d:%.*s\n",
err, patch_input_file, linenr, len-2, line+1);
}
/* /*
* Parse a unified diff. Note that this really needs to parse each * Parse a unified diff. Note that this really needs to parse each
* fragment separately, since the only way to know the difference * fragment separately, since the only way to know the difference
@ -904,25 +947,8 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
trailing = 0; trailing = 0;
break; break;
case '+': case '+':
/* if (new_whitespace != nowarn_whitespace)
* We know len is at least two, since we have a '+' and check_whitespace(line, len);
* we checked that the last character was a '\n' above.
* That is, an addition of an empty line would check
* the '+' here. Sneaky...
*/
if ((new_whitespace != nowarn_whitespace) &&
isspace(line[len-2])) {
whitespace_error++;
if (squelch_whitespace_errors &&
squelch_whitespace_errors <
whitespace_error)
;
else {
fprintf(stderr, "Adds trailing whitespace.\n%s:%d:%.*s\n",
patch_input_file,
linenr, len-2, line+1);
}
}
added++; added++;
newlines--; newlines--;
trailing = 0; trailing = 0;
@ -1494,22 +1520,68 @@ static int apply_line(char *output, const char *patch, int plen)
{ {
/* plen is number of bytes to be copied from patch, /* plen is number of bytes to be copied from patch,
* starting at patch+1 (patch[0] is '+'). Typically * starting at patch+1 (patch[0] is '+'). Typically
* patch[plen] is '\n'. * patch[plen] is '\n', unless this is the incomplete
* last line.
*/ */
int i;
int add_nl_to_tail = 0; int add_nl_to_tail = 0;
if ((new_whitespace == strip_whitespace) && int fixed = 0;
1 < plen && isspace(patch[plen-1])) { int last_tab_in_indent = -1;
int last_space_in_indent = -1;
int need_fix_leading_space = 0;
char *buf;
if ((new_whitespace != strip_whitespace) || !whitespace_error) {
memcpy(output, patch + 1, plen);
return plen;
}
if (1 < plen && isspace(patch[plen-1])) {
if (patch[plen] == '\n') if (patch[plen] == '\n')
add_nl_to_tail = 1; add_nl_to_tail = 1;
plen--; plen--;
while (0 < plen && isspace(patch[plen])) while (0 < plen && isspace(patch[plen]))
plen--; plen--;
applied_after_stripping++; fixed = 1;
} }
memcpy(output, patch + 1, plen);
for (i = 1; i < plen; i++) {
char ch = patch[i];
if (ch == '\t') {
last_tab_in_indent = i;
if (0 <= last_space_in_indent)
need_fix_leading_space = 1;
}
else if (ch == ' ')
last_space_in_indent = i;
else
break;
}
buf = output;
if (need_fix_leading_space) {
/* between patch[1..last_tab_in_indent] strip the
* funny spaces, updating them to tab as needed.
*/
for (i = 1; i < last_tab_in_indent; i++, plen--) {
char ch = patch[i];
if (ch != ' ')
*output++ = ch;
else if ((i % 8) == 0)
*output++ = '\t';
}
fixed = 1;
i = last_tab_in_indent;
}
else
i = 1;
memcpy(output, patch + i, plen);
if (add_nl_to_tail) if (add_nl_to_tail)
output[plen++] = '\n'; output[plen++] = '\n';
return plen; if (fixed)
applied_after_stripping++;
return output + plen - buf;
} }
static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, int inaccurate_eof) static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, int inaccurate_eof)

View File

@ -11,6 +11,7 @@
#include "tree-walk.h" #include "tree-walk.h"
#include "builtin.h" #include "builtin.h"
#include <regex.h> #include <regex.h>
#include "grep.h"
#include <fnmatch.h> #include <fnmatch.h>
#include <sys/wait.h> #include <sys/wait.h>
@ -82,498 +83,6 @@ static int pathspec_matches(const char **paths, const char *name)
return 0; return 0;
} }
enum grep_pat_token {
GREP_PATTERN,
GREP_AND,
GREP_OPEN_PAREN,
GREP_CLOSE_PAREN,
GREP_NOT,
GREP_OR,
};
struct grep_pat {
struct grep_pat *next;
const char *origin;
int no;
enum grep_pat_token token;
const char *pattern;
regex_t regexp;
};
enum grep_expr_node {
GREP_NODE_ATOM,
GREP_NODE_NOT,
GREP_NODE_AND,
GREP_NODE_OR,
};
struct grep_expr {
enum grep_expr_node node;
union {
struct grep_pat *atom;
struct grep_expr *unary;
struct {
struct grep_expr *left;
struct grep_expr *right;
} binary;
} u;
};
struct grep_opt {
struct grep_pat *pattern_list;
struct grep_pat **pattern_tail;
struct grep_expr *pattern_expression;
int prefix_length;
regex_t regexp;
unsigned linenum:1;
unsigned invert:1;
unsigned name_only:1;
unsigned unmatch_name_only:1;
unsigned count:1;
unsigned word_regexp:1;
unsigned fixed:1;
#define GREP_BINARY_DEFAULT 0
#define GREP_BINARY_NOMATCH 1
#define GREP_BINARY_TEXT 2
unsigned binary:2;
unsigned extended:1;
unsigned relative:1;
unsigned pathname:1;
int regflags;
unsigned pre_context;
unsigned post_context;
};
static void add_pattern(struct grep_opt *opt, const char *pat,
const char *origin, int no, enum grep_pat_token t)
{
struct grep_pat *p = xcalloc(1, sizeof(*p));
p->pattern = pat;
p->origin = origin;
p->no = no;
p->token = t;
*opt->pattern_tail = p;
opt->pattern_tail = &p->next;
p->next = NULL;
}
static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
{
int err = regcomp(&p->regexp, p->pattern, opt->regflags);
if (err) {
char errbuf[1024];
char where[1024];
if (p->no)
sprintf(where, "In '%s' at %d, ",
p->origin, p->no);
else if (p->origin)
sprintf(where, "%s, ", p->origin);
else
where[0] = 0;
regerror(err, &p->regexp, errbuf, 1024);
regfree(&p->regexp);
die("%s'%s': %s", where, p->pattern, errbuf);
}
}
static struct grep_expr *compile_pattern_expr(struct grep_pat **);
static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
{
struct grep_pat *p;
struct grep_expr *x;
p = *list;
switch (p->token) {
case GREP_PATTERN: /* atom */
x = xcalloc(1, sizeof (struct grep_expr));
x->node = GREP_NODE_ATOM;
x->u.atom = p;
*list = p->next;
return x;
case GREP_OPEN_PAREN:
*list = p->next;
x = compile_pattern_expr(list);
if (!x)
return NULL;
if (!*list || (*list)->token != GREP_CLOSE_PAREN)
die("unmatched parenthesis");
*list = (*list)->next;
return x;
default:
return NULL;
}
}
static struct grep_expr *compile_pattern_not(struct grep_pat **list)
{
struct grep_pat *p;
struct grep_expr *x;
p = *list;
switch (p->token) {
case GREP_NOT:
if (!p->next)
die("--not not followed by pattern expression");
*list = p->next;
x = xcalloc(1, sizeof (struct grep_expr));
x->node = GREP_NODE_NOT;
x->u.unary = compile_pattern_not(list);
if (!x->u.unary)
die("--not followed by non pattern expression");
return x;
default:
return compile_pattern_atom(list);
}
}
static struct grep_expr *compile_pattern_and(struct grep_pat **list)
{
struct grep_pat *p;
struct grep_expr *x, *y, *z;
x = compile_pattern_not(list);
p = *list;
if (p && p->token == GREP_AND) {
if (!p->next)
die("--and not followed by pattern expression");
*list = p->next;
y = compile_pattern_and(list);
if (!y)
die("--and not followed by pattern expression");
z = xcalloc(1, sizeof (struct grep_expr));
z->node = GREP_NODE_AND;
z->u.binary.left = x;
z->u.binary.right = y;
return z;
}
return x;
}
static struct grep_expr *compile_pattern_or(struct grep_pat **list)
{
struct grep_pat *p;
struct grep_expr *x, *y, *z;
x = compile_pattern_and(list);
p = *list;
if (x && p && p->token != GREP_CLOSE_PAREN) {
y = compile_pattern_or(list);
if (!y)
die("not a pattern expression %s", p->pattern);
z = xcalloc(1, sizeof (struct grep_expr));
z->node = GREP_NODE_OR;
z->u.binary.left = x;
z->u.binary.right = y;
return z;
}
return x;
}
static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
{
return compile_pattern_or(list);
}
static void compile_patterns(struct grep_opt *opt)
{
struct grep_pat *p;
/* First compile regexps */
for (p = opt->pattern_list; p; p = p->next) {
if (p->token == GREP_PATTERN)
compile_regexp(p, opt);
else
opt->extended = 1;
}
if (!opt->extended)
return;
/* Then bundle them up in an expression.
* A classic recursive descent parser would do.
*/
p = opt->pattern_list;
opt->pattern_expression = compile_pattern_expr(&p);
if (p)
die("incomplete pattern expression: %s", p->pattern);
}
static char *end_of_line(char *cp, unsigned long *left)
{
unsigned long l = *left;
while (l && *cp != '\n') {
l--;
cp++;
}
*left = l;
return cp;
}
static int word_char(char ch)
{
return isalnum(ch) || ch == '_';
}
static void show_line(struct grep_opt *opt, const char *bol, const char *eol,
const char *name, unsigned lno, char sign)
{
if (opt->pathname)
printf("%s%c", name, sign);
if (opt->linenum)
printf("%d%c", lno, sign);
printf("%.*s\n", (int)(eol-bol), bol);
}
/*
* NEEDSWORK: share code with diff.c
*/
#define FIRST_FEW_BYTES 8000
static int buffer_is_binary(const char *ptr, unsigned long size)
{
if (FIRST_FEW_BYTES < size)
size = FIRST_FEW_BYTES;
return !!memchr(ptr, 0, size);
}
static int fixmatch(const char *pattern, char *line, regmatch_t *match)
{
char *hit = strstr(line, pattern);
if (!hit) {
match->rm_so = match->rm_eo = -1;
return REG_NOMATCH;
}
else {
match->rm_so = hit - line;
match->rm_eo = match->rm_so + strlen(pattern);
return 0;
}
}
static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol)
{
int hit = 0;
int at_true_bol = 1;
regmatch_t pmatch[10];
again:
if (!opt->fixed) {
regex_t *exp = &p->regexp;
hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
pmatch, 0);
}
else {
hit = !fixmatch(p->pattern, bol, pmatch);
}
if (hit && opt->word_regexp) {
if ((pmatch[0].rm_so < 0) ||
(eol - bol) <= pmatch[0].rm_so ||
(pmatch[0].rm_eo < 0) ||
(eol - bol) < pmatch[0].rm_eo)
die("regexp returned nonsense");
/* Match beginning must be either beginning of the
* line, or at word boundary (i.e. the last char must
* not be a word char). Similarly, match end must be
* either end of the line, or at word boundary
* (i.e. the next char must not be a word char).
*/
if ( ((pmatch[0].rm_so == 0 && at_true_bol) ||
!word_char(bol[pmatch[0].rm_so-1])) &&
((pmatch[0].rm_eo == (eol-bol)) ||
!word_char(bol[pmatch[0].rm_eo])) )
;
else
hit = 0;
if (!hit && pmatch[0].rm_so + bol + 1 < eol) {
/* There could be more than one match on the
* line, and the first match might not be
* strict word match. But later ones could be!
*/
bol = pmatch[0].rm_so + bol + 1;
at_true_bol = 0;
goto again;
}
}
return hit;
}
static int match_expr_eval(struct grep_opt *opt,
struct grep_expr *x,
char *bol, char *eol)
{
switch (x->node) {
case GREP_NODE_ATOM:
return match_one_pattern(opt, x->u.atom, bol, eol);
break;
case GREP_NODE_NOT:
return !match_expr_eval(opt, x->u.unary, bol, eol);
case GREP_NODE_AND:
return (match_expr_eval(opt, x->u.binary.left, bol, eol) &&
match_expr_eval(opt, x->u.binary.right, bol, eol));
case GREP_NODE_OR:
return (match_expr_eval(opt, x->u.binary.left, bol, eol) ||
match_expr_eval(opt, x->u.binary.right, bol, eol));
}
die("Unexpected node type (internal error) %d\n", x->node);
}
static int match_expr(struct grep_opt *opt, char *bol, char *eol)
{
struct grep_expr *x = opt->pattern_expression;
return match_expr_eval(opt, x, bol, eol);
}
static int match_line(struct grep_opt *opt, char *bol, char *eol)
{
struct grep_pat *p;
if (opt->extended)
return match_expr(opt, bol, eol);
for (p = opt->pattern_list; p; p = p->next) {
if (match_one_pattern(opt, p, bol, eol))
return 1;
}
return 0;
}
static int grep_buffer(struct grep_opt *opt, const char *name,
char *buf, unsigned long size)
{
char *bol = buf;
unsigned long left = size;
unsigned lno = 1;
struct pre_context_line {
char *bol;
char *eol;
} *prev = NULL, *pcl;
unsigned last_hit = 0;
unsigned last_shown = 0;
int binary_match_only = 0;
const char *hunk_mark = "";
unsigned count = 0;
if (buffer_is_binary(buf, size)) {
switch (opt->binary) {
case GREP_BINARY_DEFAULT:
binary_match_only = 1;
break;
case GREP_BINARY_NOMATCH:
return 0; /* Assume unmatch */
break;
default:
break;
}
}
if (opt->pre_context)
prev = xcalloc(opt->pre_context, sizeof(*prev));
if (opt->pre_context || opt->post_context)
hunk_mark = "--\n";
while (left) {
char *eol, ch;
int hit = 0;
eol = end_of_line(bol, &left);
ch = *eol;
*eol = 0;
hit = match_line(opt, bol, eol);
/* "grep -v -e foo -e bla" should list lines
* that do not have either, so inversion should
* be done outside.
*/
if (opt->invert)
hit = !hit;
if (opt->unmatch_name_only) {
if (hit)
return 0;
goto next_line;
}
if (hit) {
count++;
if (binary_match_only) {
printf("Binary file %s matches\n", name);
return 1;
}
if (opt->name_only) {
printf("%s\n", name);
return 1;
}
/* Hit at this line. If we haven't shown the
* pre-context lines, we would need to show them.
* When asked to do "count", this still show
* the context which is nonsense, but the user
* deserves to get that ;-).
*/
if (opt->pre_context) {
unsigned from;
if (opt->pre_context < lno)
from = lno - opt->pre_context;
else
from = 1;
if (from <= last_shown)
from = last_shown + 1;
if (last_shown && from != last_shown + 1)
printf(hunk_mark);
while (from < lno) {
pcl = &prev[lno-from-1];
show_line(opt, pcl->bol, pcl->eol,
name, from, '-');
from++;
}
last_shown = lno-1;
}
if (last_shown && lno != last_shown + 1)
printf(hunk_mark);
if (!opt->count)
show_line(opt, bol, eol, name, lno, ':');
last_shown = last_hit = lno;
}
else if (last_hit &&
lno <= last_hit + opt->post_context) {
/* If the last hit is within the post context,
* we need to show this line.
*/
if (last_shown && lno != last_shown + 1)
printf(hunk_mark);
show_line(opt, bol, eol, name, lno, '-');
last_shown = lno;
}
if (opt->pre_context) {
memmove(prev+1, prev,
(opt->pre_context-1) * sizeof(*prev));
prev->bol = bol;
prev->eol = eol;
}
next_line:
*eol = ch;
bol = eol + 1;
if (!left)
break;
left--;
lno++;
}
if (opt->unmatch_name_only) {
/* We did not see any hit, so we want to show this */
printf("%s\n", name);
return 1;
}
/* NEEDSWORK:
* The real "grep -c foo *.c" gives many "bar.c:0" lines,
* which feels mostly useless but sometimes useful. Maybe
* make it another option? For now suppress them.
*/
if (opt->count && count)
printf("%s:%u\n", name, count);
return !!last_hit;
}
static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name, int tree_name_len) static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name, int tree_name_len)
{ {
unsigned long size; unsigned long size;
@ -816,6 +325,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
else else
hit |= grep_file(opt, ce->name); hit |= grep_file(opt, ce->name);
} }
free_grep_patterns(opt);
return hit; return hit;
} }
@ -1055,8 +565,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
/* ignore empty line like grep does */ /* ignore empty line like grep does */
if (!buf[0]) if (!buf[0])
continue; continue;
add_pattern(&opt, xstrdup(buf), argv[1], ++lno, append_grep_pattern(&opt, xstrdup(buf),
GREP_PATTERN); argv[1], ++lno,
GREP_PATTERN);
} }
fclose(patterns); fclose(patterns);
argv++; argv++;
@ -1064,27 +575,32 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
continue; continue;
} }
if (!strcmp("--not", arg)) { if (!strcmp("--not", arg)) {
add_pattern(&opt, arg, "command line", 0, GREP_NOT); append_grep_pattern(&opt, arg, "command line", 0,
GREP_NOT);
continue; continue;
} }
if (!strcmp("--and", arg)) { if (!strcmp("--and", arg)) {
add_pattern(&opt, arg, "command line", 0, GREP_AND); append_grep_pattern(&opt, arg, "command line", 0,
GREP_AND);
continue; continue;
} }
if (!strcmp("--or", arg)) if (!strcmp("--or", arg))
continue; /* no-op */ continue; /* no-op */
if (!strcmp("(", arg)) { if (!strcmp("(", arg)) {
add_pattern(&opt, arg, "command line", 0, GREP_OPEN_PAREN); append_grep_pattern(&opt, arg, "command line", 0,
GREP_OPEN_PAREN);
continue; continue;
} }
if (!strcmp(")", arg)) { if (!strcmp(")", arg)) {
add_pattern(&opt, arg, "command line", 0, GREP_CLOSE_PAREN); append_grep_pattern(&opt, arg, "command line", 0,
GREP_CLOSE_PAREN);
continue; continue;
} }
if (!strcmp("-e", arg)) { if (!strcmp("-e", arg)) {
if (1 < argc) { if (1 < argc) {
add_pattern(&opt, argv[1], "-e option", 0, append_grep_pattern(&opt, argv[1],
GREP_PATTERN); "-e option", 0,
GREP_PATTERN);
argv++; argv++;
argc--; argc--;
continue; continue;
@ -1106,8 +622,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
/* First unrecognized non-option token */ /* First unrecognized non-option token */
if (!opt.pattern_list) { if (!opt.pattern_list) {
add_pattern(&opt, arg, "command line", 0, append_grep_pattern(&opt, arg, "command line", 0,
GREP_PATTERN); GREP_PATTERN);
break; break;
} }
else { else {
@ -1124,8 +640,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
die("no pattern given."); die("no pattern given.");
if ((opt.regflags != REG_NEWLINE) && opt.fixed) if ((opt.regflags != REG_NEWLINE) && opt.fixed)
die("cannot mix --fixed-strings and regexp"); die("cannot mix --fixed-strings and regexp");
if (!opt.fixed) compile_grep_patterns(&opt);
compile_patterns(&opt);
/* Check revs and then paths */ /* Check revs and then paths */
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
@ -1180,5 +695,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
if (grep_object(&opt, paths, real_obj, list.objects[i].name)) if (grep_object(&opt, paths, real_obj, list.objects[i].name))
hit = 1; hit = 1;
} }
free_grep_patterns(&opt);
return !hit; return !hit;
} }

View File

@ -311,6 +311,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
*/ */
sprintf(buf, "%d", shared_repository); sprintf(buf, "%d", shared_repository);
git_config_set("core.sharedrepository", buf); git_config_set("core.sharedrepository", buf);
git_config_set("receive.denyNonFastforwards", "true");
} }
return 0; return 0;

View File

@ -451,17 +451,6 @@ static int read_one_header_line(char *line, int sz, FILE *in)
return ofs; return ofs;
} }
static unsigned hexval(int c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return ~0;
}
static int decode_q_segment(char *in, char *ot, char *ep, int rfc2047) static int decode_q_segment(char *in, char *ot, char *ep, int rfc2047)
{ {
int c; int c;

View File

@ -597,15 +597,15 @@ static int add_object_entry(const unsigned char *sha1, unsigned hash, int exclud
if (!exclude) { if (!exclude) {
for (p = packed_git; p; p = p->next) { for (p = packed_git; p; p = p->next) {
struct pack_entry e; unsigned long offset = find_pack_entry_one(sha1, p);
if (find_pack_entry_one(sha1, &e, p)) { if (offset) {
if (incremental) if (incremental)
return 0; return 0;
if (local && !p->pack_local) if (local && !p->pack_local)
return 0; return 0;
if (!found_pack) { if (!found_pack) {
found_offset = e.offset; found_offset = offset;
found_pack = e.p; found_pack = p;
} }
} }
} }

View File

@ -269,7 +269,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
revs.diff) revs.diff)
usage(rev_list_usage); usage(rev_list_usage);
save_commit_buffer = revs.verbose_header; save_commit_buffer = revs.verbose_header || revs.grep_filter;
track_object_refs = 0; track_object_refs = 0;
if (bisect_list) if (bisect_list)
revs.limited = 1; revs.limited = 1;

View File

@ -4,413 +4,68 @@
#include <time.h> #include <time.h>
#include "cache.h" #include "cache.h"
#include "commit.h" #include "commit.h"
#include "strbuf.h"
#include "tar.h" #include "tar.h"
#include "builtin.h" #include "builtin.h"
#include "pkt-line.h" #include "quote.h"
#include "archive.h"
#define RECORDSIZE (512)
#define BLOCKSIZE (RECORDSIZE * 20)
static const char tar_tree_usage[] = static const char tar_tree_usage[] =
"git-tar-tree [--remote=<repo>] <tree-ish> [basedir]"; "git-tar-tree [--remote=<repo>] <tree-ish> [basedir]\n"
"*** Note that this command is now deprecated; use git-archive instead.";
static char block[BLOCKSIZE];
static unsigned long offset;
static time_t archive_time;
static int tar_umask;
static int verbose;
/* writes out the whole block, but only if it is full */
static void write_if_needed(void)
{
if (offset == BLOCKSIZE) {
write_or_die(1, block, BLOCKSIZE);
offset = 0;
}
}
/*
* queues up writes, so that all our write(2) calls write exactly one
* full block; pads writes to RECORDSIZE
*/
static void write_blocked(const void *data, unsigned long size)
{
const char *buf = data;
unsigned long tail;
if (offset) {
unsigned long chunk = BLOCKSIZE - offset;
if (size < chunk)
chunk = size;
memcpy(block + offset, buf, chunk);
size -= chunk;
offset += chunk;
buf += chunk;
write_if_needed();
}
while (size >= BLOCKSIZE) {
write_or_die(1, buf, BLOCKSIZE);
size -= BLOCKSIZE;
buf += BLOCKSIZE;
}
if (size) {
memcpy(block + offset, buf, size);
offset += size;
}
tail = offset % RECORDSIZE;
if (tail) {
memset(block + offset, 0, RECORDSIZE - tail);
offset += RECORDSIZE - tail;
}
write_if_needed();
}
/*
* The end of tar archives is marked by 2*512 nul bytes and after that
* follows the rest of the block (if any).
*/
static void write_trailer(void)
{
int tail = BLOCKSIZE - offset;
memset(block + offset, 0, tail);
write_or_die(1, block, BLOCKSIZE);
if (tail < 2 * RECORDSIZE) {
memset(block, 0, offset);
write_or_die(1, block, BLOCKSIZE);
}
}
static void strbuf_append_string(struct strbuf *sb, const char *s)
{
int slen = strlen(s);
int total = sb->len + slen;
if (total > sb->alloc) {
sb->buf = xrealloc(sb->buf, total);
sb->alloc = total;
}
memcpy(sb->buf + sb->len, s, slen);
sb->len = total;
}
/*
* pax extended header records have the format "%u %s=%s\n". %u contains
* the size of the whole string (including the %u), the first %s is the
* keyword, the second one is the value. This function constructs such a
* string and appends it to a struct strbuf.
*/
static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,
const char *value, unsigned int valuelen)
{
char *p;
int len, total, tmp;
/* "%u %s=%s\n" */
len = 1 + 1 + strlen(keyword) + 1 + valuelen + 1;
for (tmp = len; tmp > 9; tmp /= 10)
len++;
total = sb->len + len;
if (total > sb->alloc) {
sb->buf = xrealloc(sb->buf, total);
sb->alloc = total;
}
p = sb->buf;
p += sprintf(p, "%u %s=", len, keyword);
memcpy(p, value, valuelen);
p += valuelen;
*p = '\n';
sb->len = total;
}
static unsigned int ustar_header_chksum(const struct ustar_header *header)
{
char *p = (char *)header;
unsigned int chksum = 0;
while (p < header->chksum)
chksum += *p++;
chksum += sizeof(header->chksum) * ' ';
p += sizeof(header->chksum);
while (p < (char *)header + sizeof(struct ustar_header))
chksum += *p++;
return chksum;
}
static int get_path_prefix(const struct strbuf *path, int maxlen)
{
int i = path->len;
if (i > maxlen)
i = maxlen;
do {
i--;
} while (i > 0 && path->buf[i] != '/');
return i;
}
static void write_entry(const unsigned char *sha1, struct strbuf *path,
unsigned int mode, void *buffer, unsigned long size)
{
struct ustar_header header;
struct strbuf ext_header;
memset(&header, 0, sizeof(header));
ext_header.buf = NULL;
ext_header.len = ext_header.alloc = 0;
if (!sha1) {
*header.typeflag = TYPEFLAG_GLOBAL_HEADER;
mode = 0100666;
strcpy(header.name, "pax_global_header");
} else if (!path) {
*header.typeflag = TYPEFLAG_EXT_HEADER;
mode = 0100666;
sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
} else {
if (verbose)
fprintf(stderr, "%.*s\n", path->len, path->buf);
if (S_ISDIR(mode)) {
*header.typeflag = TYPEFLAG_DIR;
mode = (mode | 0777) & ~tar_umask;
} else if (S_ISLNK(mode)) {
*header.typeflag = TYPEFLAG_LNK;
mode |= 0777;
} else if (S_ISREG(mode)) {
*header.typeflag = TYPEFLAG_REG;
mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask;
} else {
error("unsupported file mode: 0%o (SHA1: %s)",
mode, sha1_to_hex(sha1));
return;
}
if (path->len > sizeof(header.name)) {
int plen = get_path_prefix(path, sizeof(header.prefix));
int rest = path->len - plen - 1;
if (plen > 0 && rest <= sizeof(header.name)) {
memcpy(header.prefix, path->buf, plen);
memcpy(header.name, path->buf + plen + 1, rest);
} else {
sprintf(header.name, "%s.data",
sha1_to_hex(sha1));
strbuf_append_ext_header(&ext_header, "path",
path->buf, path->len);
}
} else
memcpy(header.name, path->buf, path->len);
}
if (S_ISLNK(mode) && buffer) {
if (size > sizeof(header.linkname)) {
sprintf(header.linkname, "see %s.paxheader",
sha1_to_hex(sha1));
strbuf_append_ext_header(&ext_header, "linkpath",
buffer, size);
} else
memcpy(header.linkname, buffer, size);
}
sprintf(header.mode, "%07o", mode & 07777);
sprintf(header.size, "%011lo", S_ISREG(mode) ? size : 0);
sprintf(header.mtime, "%011lo", archive_time);
/* XXX: should we provide more meaningful info here? */
sprintf(header.uid, "%07o", 0);
sprintf(header.gid, "%07o", 0);
strlcpy(header.uname, "git", sizeof(header.uname));
strlcpy(header.gname, "git", sizeof(header.gname));
sprintf(header.devmajor, "%07o", 0);
sprintf(header.devminor, "%07o", 0);
memcpy(header.magic, "ustar", 6);
memcpy(header.version, "00", 2);
sprintf(header.chksum, "%07o", ustar_header_chksum(&header));
if (ext_header.len > 0) {
write_entry(sha1, NULL, 0, ext_header.buf, ext_header.len);
free(ext_header.buf);
}
write_blocked(&header, sizeof(header));
if (S_ISREG(mode) && buffer && size > 0)
write_blocked(buffer, size);
}
static void write_global_extended_header(const unsigned char *sha1)
{
struct strbuf ext_header;
ext_header.buf = NULL;
ext_header.len = ext_header.alloc = 0;
strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40);
write_entry(NULL, NULL, 0, ext_header.buf, ext_header.len);
free(ext_header.buf);
}
static int git_tar_config(const char *var, const char *value)
{
if (!strcmp(var, "tar.umask")) {
if (!strcmp(value, "user")) {
tar_umask = umask(0);
umask(tar_umask);
} else {
tar_umask = git_config_int(var, value);
}
return 0;
}
return git_default_config(var, value);
}
static int generate_tar(int argc, const char **argv, const char *prefix)
{
struct archiver_args args;
int result;
char *base = NULL;
git_config(git_tar_config);
memset(&args, 0, sizeof(args));
if (argc != 2 && argc != 3)
usage(tar_tree_usage);
if (argc == 3) {
int baselen = strlen(argv[2]);
base = xmalloc(baselen + 2);
memcpy(base, argv[2], baselen);
base[baselen] = '/';
base[baselen + 1] = '\0';
}
args.base = base;
parse_treeish_arg(argv + 1, &args, NULL);
result = write_tar_archive(&args);
free(base);
return result;
}
static int write_tar_entry(const unsigned char *sha1,
const char *base, int baselen,
const char *filename, unsigned mode, int stage)
{
static struct strbuf path;
int filenamelen = strlen(filename);
void *buffer;
char type[20];
unsigned long size;
if (!path.alloc) {
path.buf = xmalloc(PATH_MAX);
path.alloc = PATH_MAX;
path.len = path.eof = 0;
}
if (path.alloc < baselen + filenamelen) {
free(path.buf);
path.buf = xmalloc(baselen + filenamelen);
path.alloc = baselen + filenamelen;
}
memcpy(path.buf, base, baselen);
memcpy(path.buf + baselen, filename, filenamelen);
path.len = baselen + filenamelen;
if (S_ISDIR(mode)) {
strbuf_append_string(&path, "/");
buffer = NULL;
size = 0;
} else {
buffer = read_sha1_file(sha1, type, &size);
if (!buffer)
die("cannot read %s", sha1_to_hex(sha1));
}
write_entry(sha1, &path, mode, buffer, size);
free(buffer);
return READ_TREE_RECURSIVE;
}
int write_tar_archive(struct archiver_args *args)
{
int plen = args->base ? strlen(args->base) : 0;
git_config(git_tar_config);
archive_time = args->time;
verbose = args->verbose;
if (args->commit_sha1)
write_global_extended_header(args->commit_sha1);
if (args->base && plen > 0 && args->base[plen - 1] == '/') {
char *base = xstrdup(args->base);
int baselen = strlen(base);
while (baselen > 0 && base[baselen - 1] == '/')
base[--baselen] = '\0';
write_tar_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
free(base);
}
read_tree_recursive(args->tree, args->base, plen, 0,
args->pathspec, write_tar_entry);
write_trailer();
return 0;
}
static const char *exec = "git-upload-tar";
static int remote_tar(int argc, const char **argv)
{
int fd[2], ret, len;
pid_t pid;
char buf[1024];
char *url;
if (argc < 3 || 4 < argc)
usage(tar_tree_usage);
/* --remote=<repo> */
url = xstrdup(argv[1]+9);
pid = git_connect(fd, url, exec);
if (pid < 0)
return 1;
packet_write(fd[1], "want %s\n", argv[2]);
if (argv[3])
packet_write(fd[1], "base %s\n", argv[3]);
packet_flush(fd[1]);
len = packet_read_line(fd[0], buf, sizeof(buf));
if (!len)
die("git-tar-tree: expected ACK/NAK, got EOF");
if (buf[len-1] == '\n')
buf[--len] = 0;
if (strcmp(buf, "ACK")) {
if (5 < len && !strncmp(buf, "NACK ", 5))
die("git-tar-tree: NACK %s", buf + 5);
die("git-tar-tree: protocol error");
}
/* expect a flush */
len = packet_read_line(fd[0], buf, sizeof(buf));
if (len)
die("git-tar-tree: expected a flush");
/* Now, start reading from fd[0] and spit it out to stdout */
ret = copy_fd(fd[0], 1);
close(fd[0]);
ret |= finish_connect(pid);
return !!ret;
}
int cmd_tar_tree(int argc, const char **argv, const char *prefix) int cmd_tar_tree(int argc, const char **argv, const char *prefix)
{ {
if (argc < 2) /*
* git-tar-tree is now a wrapper around git-archive --format=tar
*
* $0 --remote=<repo> arg... ==>
* git-archive --format=tar --remote=<repo> arg...
* $0 tree-ish ==>
* git-archive --format=tar tree-ish
* $0 tree-ish basedir ==>
* git-archive --format-tar --prefix=basedir tree-ish
*/
int i;
const char **nargv = xcalloc(sizeof(*nargv), argc + 2);
char *basedir_arg;
int nargc = 0;
nargv[nargc++] = "git-archive";
nargv[nargc++] = "--format=tar";
if (2 <= argc && !strncmp("--remote=", argv[1], 9)) {
nargv[nargc++] = argv[1];
argv++;
argc--;
}
switch (argc) {
default:
usage(tar_tree_usage); usage(tar_tree_usage);
if (!strncmp("--remote=", argv[1], 9)) break;
return remote_tar(argc, argv); case 3:
return generate_tar(argc, argv, prefix); /* base-path */
basedir_arg = xmalloc(strlen(argv[2]) + 11);
sprintf(basedir_arg, "--prefix=%s/", argv[2]);
nargv[nargc++] = basedir_arg;
/* fallthru */
case 2:
/* tree-ish */
nargv[nargc++] = argv[1];
}
nargv[nargc] = NULL;
fprintf(stderr,
"*** git-tar-tree is now deprecated.\n"
"*** Running git-archive instead.\n***");
for (i = 0; i < nargc; i++) {
fputc(' ', stderr);
sq_quote_print(stderr, nargv[i]);
}
fputc('\n', stderr);
return cmd_archive(nargc, nargv, prefix);
} }
/* ustar header + extended global header content */ /* ustar header + extended global header content */
#define RECORDSIZE (512)
#define HEADERSIZE (2 * RECORDSIZE) #define HEADERSIZE (2 * RECORDSIZE)
int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix) int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)

View File

@ -112,11 +112,13 @@ static int add_file_to_cache(const char *path)
ce->ce_mode = create_ce_mode(st.st_mode); ce->ce_mode = create_ce_mode(st.st_mode);
if (!trust_executable_bit) { if (!trust_executable_bit) {
/* If there is an existing entry, pick the mode bits /* If there is an existing entry, pick the mode bits
* from it. * from it, otherwise force to 644.
*/ */
int pos = cache_name_pos(path, namelen); int pos = cache_name_pos(path, namelen);
if (0 <= pos) if (0 <= pos)
ce->ce_mode = active_cache[pos]->ce_mode; ce->ce_mode = active_cache[pos]->ce_mode;
else
ce->ce_mode = create_ce_mode(S_IFREG | 0644);
} }
if (index_path(ce->sha1, path, &st, !info_only)) if (index_path(ce->sha1, path, &st, !info_only))

View File

@ -2,13 +2,13 @@
* Copyright (c) 2006 Franck Bui-Huu * Copyright (c) 2006 Franck Bui-Huu
*/ */
#include <time.h> #include <time.h>
#include <sys/wait.h>
#include <sys/poll.h>
#include "cache.h" #include "cache.h"
#include "builtin.h" #include "builtin.h"
#include "archive.h" #include "archive.h"
#include "pkt-line.h" #include "pkt-line.h"
#include "sideband.h" #include "sideband.h"
#include <sys/wait.h>
#include <sys/poll.h>
static const char upload_archive_usage[] = static const char upload_archive_usage[] =
"git-upload-archive <repo>"; "git-upload-archive <repo>";

View File

@ -1,74 +0,0 @@
/*
* Copyright (c) 2006 Junio C Hamano
*/
#include "cache.h"
#include "pkt-line.h"
#include "exec_cmd.h"
#include "builtin.h"
static const char upload_tar_usage[] = "git-upload-tar <repo>";
static int nak(const char *reason)
{
packet_write(1, "NACK %s\n", reason);
packet_flush(1);
return 1;
}
int cmd_upload_tar(int argc, const char **argv, const char *prefix)
{
int len;
const char *dir = argv[1];
char buf[8192];
unsigned char sha1[20];
char *base = NULL;
char hex[41];
int ac;
const char *av[4];
if (argc != 2)
usage(upload_tar_usage);
if (strlen(dir) < sizeof(buf)-1)
strcpy(buf, dir); /* enter-repo smudges its argument */
else
packet_write(1, "NACK insanely long repository name %s\n", dir);
if (!enter_repo(buf, 0)) {
packet_write(1, "NACK not a git archive %s\n", dir);
packet_flush(1);
return 1;
}
len = packet_read_line(0, buf, sizeof(buf));
if (len < 5 || strncmp("want ", buf, 5))
return nak("expected want");
if (buf[len-1] == '\n')
buf[--len] = 0;
if (get_sha1(buf + 5, sha1))
return nak("expected sha1");
strcpy(hex, sha1_to_hex(sha1));
len = packet_read_line(0, buf, sizeof(buf));
if (len) {
if (len < 5 || strncmp("base ", buf, 5))
return nak("expected (optional) base");
if (buf[len-1] == '\n')
buf[--len] = 0;
base = xstrdup(buf + 5);
len = packet_read_line(0, buf, sizeof(buf));
}
if (len)
return nak("expected flush");
packet_write(1, "ACK\n");
packet_flush(1);
ac = 0;
av[ac++] = "tar-tree";
av[ac++] = hex;
if (base)
av[ac++] = base;
av[ac++] = NULL;
execv_git_cmd(av);
/* should it return that is an error */
return 1;
}

View File

@ -54,7 +54,6 @@ extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
extern int cmd_stripspace(int argc, const char **argv, const char *prefix); extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix); extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
extern int cmd_tar_tree(int argc, const char **argv, const char *prefix); extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
extern int cmd_zip_tree(int argc, const char **argv, const char *prefix);
extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix); extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
extern int cmd_update_index(int argc, const char **argv, const char *prefix); extern int cmd_update_index(int argc, const char **argv, const char *prefix);
extern int cmd_update_ref(int argc, const char **argv, const char *prefix); extern int cmd_update_ref(int argc, const char **argv, const char *prefix);

13
cache.h
View File

@ -189,6 +189,7 @@ extern int prefer_symlink_refs;
extern int log_all_ref_updates; extern int log_all_ref_updates;
extern int warn_ambiguous_refs; extern int warn_ambiguous_refs;
extern int shared_repository; extern int shared_repository;
extern int deny_non_fast_forwards;
extern const char *apply_default_whitespace; extern const char *apply_default_whitespace;
extern int zlib_compression_level; extern int zlib_compression_level;
@ -279,6 +280,12 @@ enum object_type {
OBJ_BAD, OBJ_BAD,
}; };
extern signed char hexval_table[256];
static inline unsigned int hexval(unsigned int c)
{
return hexval_table[c];
}
/* Convert to/from hex/sha1 representation */ /* Convert to/from hex/sha1 representation */
#define MINIMUM_ABBREV 4 #define MINIMUM_ABBREV 4
#define DEFAULT_ABBREV 7 #define DEFAULT_ABBREV 7
@ -384,10 +391,10 @@ extern void unuse_packed_git(struct packed_git *);
extern struct packed_git *add_packed_git(char *, int, int); extern struct packed_git *add_packed_git(char *, int, int);
extern int num_packed_objects(const struct packed_git *p); extern int num_packed_objects(const struct packed_git *p);
extern int nth_packed_object_sha1(const struct packed_git *, int, unsigned char*); extern int nth_packed_object_sha1(const struct packed_git *, int, unsigned char*);
extern int find_pack_entry_one(const unsigned char *, struct pack_entry *, struct packed_git *); extern unsigned long find_pack_entry_one(const unsigned char *, struct packed_git *);
extern void *unpack_entry_gently(struct pack_entry *, char *, unsigned long *); extern void *unpack_entry_gently(struct packed_git *, unsigned long, char *, unsigned long *);
extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep); extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
extern void packed_object_info_detail(struct pack_entry *, char *, unsigned long *, unsigned long *, unsigned int *, unsigned char *); extern void packed_object_info_detail(struct packed_git *, unsigned long, char *, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
/* Dumb servers support */ /* Dumb servers support */
extern int update_server_info(int); extern int update_server_info(int);

View File

@ -75,7 +75,6 @@ GIT_ARG_SET_PATH(shell)
# Define PERL_PATH to provide path to Perl. # Define PERL_PATH to provide path to Perl.
GIT_ARG_SET_PATH(perl) GIT_ARG_SET_PATH(perl)
# #
# Define NO_PYTHON if you want to lose all benefits of the recursive merge.
# Define PYTHON_PATH to provide path to Python. # Define PYTHON_PATH to provide path to Python.
AC_ARG_WITH(python,[AS_HELP_STRING([--with-python=PATH], [provide PATH to python]) AC_ARG_WITH(python,[AS_HELP_STRING([--with-python=PATH], [provide PATH to python])
AS_HELP_STRING([--without-python], [don't use python scripts])], AS_HELP_STRING([--without-python], [don't use python scripts])],
@ -100,7 +99,6 @@ AC_PROG_CC
AC_CHECK_TOOL(AR, ar, :) AC_CHECK_TOOL(AR, ar, :)
AC_CHECK_PROGS(TAR, [gtar tar]) AC_CHECK_PROGS(TAR, [gtar tar])
# #
# Define NO_PYTHON if you want to lose all benefits of the recursive merge.
# Define PYTHON_PATH to provide path to Python. # Define PYTHON_PATH to provide path to Python.
if test -z "$NO_PYTHON"; then if test -z "$NO_PYTHON"; then
if test -z "$PYTHON_PATH"; then if test -z "$PYTHON_PATH"; then

View File

@ -12,6 +12,7 @@
#include "pkt-line.h" #include "pkt-line.h"
#include "cache.h" #include "cache.h"
#include "exec_cmd.h" #include "exec_cmd.h"
#include "interpolate.h"
static int log_syslog; static int log_syslog;
static int verbose; static int verbose;
@ -21,6 +22,7 @@ static const char daemon_usage[] =
"git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n" "git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n"
" [--timeout=n] [--init-timeout=n] [--strict-paths]\n" " [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
" [--base-path=path] [--user-path | --user-path=path]\n" " [--base-path=path] [--user-path | --user-path=path]\n"
" [--interpolated-path=path]\n"
" [--reuseaddr] [--detach] [--pid-file=file]\n" " [--reuseaddr] [--detach] [--pid-file=file]\n"
" [--[enable|disable|allow-override|forbid-override]=service]\n" " [--[enable|disable|allow-override|forbid-override]=service]\n"
" [--user=user [[--group=group]] [directory...]"; " [--user=user [[--group=group]] [directory...]";
@ -34,6 +36,10 @@ static int export_all_trees;
/* Take all paths relative to this one if non-NULL */ /* Take all paths relative to this one if non-NULL */
static char *base_path; static char *base_path;
static char *interpolated_path;
/* Flag indicating client sent extra args. */
static int saw_extended_args;
/* If defined, ~user notation is allowed and the string is inserted /* If defined, ~user notation is allowed and the string is inserted
* after ~user/. E.g. a request to git://host/~alice/frotz would * after ~user/. E.g. a request to git://host/~alice/frotz would
@ -45,6 +51,21 @@ static const char *user_path;
static unsigned int timeout; static unsigned int timeout;
static unsigned int init_timeout; static unsigned int init_timeout;
/*
* Static table for now. Ugh.
* Feel free to make dynamic as needed.
*/
#define INTERP_SLOT_HOST (0)
#define INTERP_SLOT_DIR (1)
#define INTERP_SLOT_PERCENT (2)
static struct interp interp_table[] = {
{ "%H", 0},
{ "%D", 0},
{ "%%", "%"},
};
static void logreport(int priority, const char *err, va_list params) static void logreport(int priority, const char *err, va_list params)
{ {
/* We should do a single write so that it is atomic and output /* We should do a single write so that it is atomic and output
@ -152,10 +173,14 @@ static int avoid_alias(char *p)
} }
} }
static char *path_ok(char *dir) static char *path_ok(struct interp *itable)
{ {
static char rpath[PATH_MAX]; static char rpath[PATH_MAX];
static char interp_path[PATH_MAX];
char *path; char *path;
char *dir;
dir = itable[INTERP_SLOT_DIR].value;
if (avoid_alias(dir)) { if (avoid_alias(dir)) {
logerror("'%s': aliased", dir); logerror("'%s': aliased", dir);
@ -184,16 +209,27 @@ static char *path_ok(char *dir)
dir = rpath; dir = rpath;
} }
} }
else if (interpolated_path && saw_extended_args) {
if (*dir != '/') {
/* Allow only absolute */
logerror("'%s': Non-absolute path denied (interpolated-path active)", dir);
return NULL;
}
interpolate(interp_path, PATH_MAX, interpolated_path,
interp_table, ARRAY_SIZE(interp_table));
loginfo("Interpolated dir '%s'", interp_path);
dir = interp_path;
}
else if (base_path) { else if (base_path) {
if (*dir != '/') { if (*dir != '/') {
/* Allow only absolute */ /* Allow only absolute */
logerror("'%s': Non-absolute path denied (base-path active)", dir); logerror("'%s': Non-absolute path denied (base-path active)", dir);
return NULL; return NULL;
} }
else { snprintf(rpath, PATH_MAX, "%s%s", base_path, dir);
snprintf(rpath, PATH_MAX, "%s%s", base_path, dir); dir = rpath;
dir = rpath;
}
} }
path = enter_repo(dir, strict_paths); path = enter_repo(dir, strict_paths);
@ -257,12 +293,14 @@ static int git_daemon_config(const char *var, const char *value)
return 0; return 0;
} }
static int run_service(char *dir, struct daemon_service *service) static int run_service(struct interp *itable, struct daemon_service *service)
{ {
const char *path; const char *path;
int enabled = service->enabled; int enabled = service->enabled;
loginfo("Request %s for '%s'", service->name, dir); loginfo("Request %s for '%s'",
service->name,
itable[INTERP_SLOT_DIR].value);
if (!enabled && !service->overridable) { if (!enabled && !service->overridable) {
logerror("'%s': service not enabled.", service->name); logerror("'%s': service not enabled.", service->name);
@ -270,7 +308,7 @@ static int run_service(char *dir, struct daemon_service *service)
return -1; return -1;
} }
if (!(path = path_ok(dir))) if (!(path = path_ok(itable)))
return -1; return -1;
/* /*
@ -358,6 +396,28 @@ static void make_service_overridable(const char *name, int ena) {
die("No such service %s", name); die("No such service %s", name);
} }
static void parse_extra_args(char *extra_args, int buflen)
{
char *val;
int vallen;
char *end = extra_args + buflen;
while (extra_args < end && *extra_args) {
saw_extended_args = 1;
if (strncasecmp("host=", extra_args, 5) == 0) {
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);
}
/* On to the next one */
extra_args = val + vallen;
}
}
}
static int execute(struct sockaddr *addr) static int execute(struct sockaddr *addr)
{ {
static char line[1000]; static char line[1000];
@ -398,13 +458,18 @@ static int execute(struct sockaddr *addr)
if (len && line[len-1] == '\n') if (len && line[len-1] == '\n')
line[--len] = 0; line[--len] = 0;
if (len != pktlen)
parse_extra_args(line + len + 1, pktlen - len - 1);
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) { for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
struct daemon_service *s = &(daemon_service[i]); struct daemon_service *s = &(daemon_service[i]);
int namelen = strlen(s->name); int namelen = strlen(s->name);
if (!strncmp("git-", line, 4) && if (!strncmp("git-", line, 4) &&
!strncmp(s->name, line + 4, namelen) && !strncmp(s->name, line + 4, namelen) &&
line[namelen + 4] == ' ') line[namelen + 4] == ' ') {
return run_service(line + namelen + 5, s); interp_table[INTERP_SLOT_DIR].value = line+namelen+5;
return run_service(interp_table, s);
}
} }
logerror("Protocol error: '%s'", line); logerror("Protocol error: '%s'", line);
@ -867,6 +932,10 @@ int main(int argc, char **argv)
base_path = arg+12; base_path = arg+12;
continue; continue;
} }
if (!strncmp(arg, "--interpolated-path=", 20)) {
interpolated_path = arg+20;
continue;
}
if (!strcmp(arg, "--reuseaddr")) { if (!strcmp(arg, "--reuseaddr")) {
reuseaddr = 1; reuseaddr = 1;
continue; continue;

View File

@ -308,8 +308,8 @@ create_delta(const struct delta_index *index,
continue; continue;
if (ref_size > top - src) if (ref_size > top - src)
ref_size = top - src; ref_size = top - src;
if (ref_size > 0x10000) if (ref_size > 0xffffff)
ref_size = 0x10000; ref_size = 0xffffff;
if (ref_size <= msize) if (ref_size <= msize)
break; break;
while (ref_size-- && *src++ == *ref) while (ref_size-- && *src++ == *ref)
@ -318,6 +318,8 @@ create_delta(const struct delta_index *index,
/* this is our best match so far */ /* this is our best match so far */
msize = ref - entry->ptr; msize = ref - entry->ptr;
moff = entry->ptr - ref_data; moff = entry->ptr - ref_data;
if (msize >= 0x10000)
break; /* this is good enough */
} }
} }
@ -381,6 +383,8 @@ create_delta(const struct delta_index *index,
if (msize & 0xff) { out[outpos++] = msize; i |= 0x10; } if (msize & 0xff) { out[outpos++] = msize; i |= 0x10; }
msize >>= 8; msize >>= 8;
if (msize & 0xff) { out[outpos++] = msize; i |= 0x20; } if (msize & 0xff) { out[outpos++] = msize; i |= 0x20; }
msize >>= 8;
if (msize & 0xff) { out[outpos++] = msize; i |= 0x40; }
*op = i; *op = i;
} }

172
diff.c
View File

@ -20,12 +20,13 @@ static int diff_use_color_default;
static char diff_colors[][COLOR_MAXLEN] = { static char diff_colors[][COLOR_MAXLEN] = {
"\033[m", /* reset */ "\033[m", /* reset */
"", /* normal */ "", /* PLAIN (normal) */
"\033[1m", /* bold */ "\033[1m", /* METAINFO (bold) */
"\033[36m", /* cyan */ "\033[36m", /* FRAGINFO (cyan) */
"\033[31m", /* red */ "\033[31m", /* OLD (red) */
"\033[32m", /* green */ "\033[32m", /* NEW (green) */
"\033[33m" /* yellow */ "\033[33m", /* COMMIT (yellow) */
"\033[41m", /* WHITESPACE (red background) */
}; };
static int parse_diff_color_slot(const char *var, int ofs) static int parse_diff_color_slot(const char *var, int ofs)
@ -42,6 +43,8 @@ static int parse_diff_color_slot(const char *var, int ofs)
return DIFF_FILE_NEW; return DIFF_FILE_NEW;
if (!strcasecmp(var+ofs, "commit")) if (!strcasecmp(var+ofs, "commit"))
return DIFF_COMMIT; return DIFF_COMMIT;
if (!strcasecmp(var+ofs, "whitespace"))
return DIFF_WHITESPACE;
die("bad config variable '%s'", var); die("bad config variable '%s'", var);
} }
@ -383,9 +386,89 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix)
return ""; return "";
} }
static void emit_line(const char *set, const char *reset, const char *line, int len)
{
if (len > 0 && line[len-1] == '\n')
len--;
fputs(set, stdout);
fwrite(line, len, 1, stdout);
puts(reset);
}
static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len)
{
int col0 = ecbdata->nparents;
int last_tab_in_indent = -1;
int last_space_in_indent = -1;
int i;
int tail = len;
int need_highlight_leading_space = 0;
const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
if (!*ws) {
emit_line(set, reset, line, len);
return;
}
/* The line is a newly added line. Does it have funny leading
* whitespaces? In indent, SP should never precede a TAB.
*/
for (i = col0; i < len; i++) {
if (line[i] == '\t') {
last_tab_in_indent = i;
if (0 <= last_space_in_indent)
need_highlight_leading_space = 1;
}
else if (line[i] == ' ')
last_space_in_indent = i;
else
break;
}
fputs(set, stdout);
fwrite(line, col0, 1, stdout);
fputs(reset, stdout);
if (((i == len) || line[i] == '\n') && i != col0) {
/* The whole line was indent */
emit_line(ws, reset, line + col0, len - col0);
return;
}
i = col0;
if (need_highlight_leading_space) {
while (i < last_tab_in_indent) {
if (line[i] == ' ') {
fputs(ws, stdout);
putchar(' ');
fputs(reset, stdout);
}
else
putchar(line[i]);
i++;
}
}
tail = len - 1;
if (line[tail] == '\n' && i < tail)
tail--;
while (i < tail) {
if (!isspace(line[tail]))
break;
tail--;
}
if ((i < tail && line[tail + 1] != '\n')) {
/* This has whitespace between tail+1..len */
fputs(set, stdout);
fwrite(line + i, tail - i + 1, 1, stdout);
fputs(reset, stdout);
emit_line(ws, reset, line + tail + 1, len - tail - 1);
}
else
emit_line(set, reset, line + i, len - i);
}
static void fn_out_consume(void *priv, char *line, unsigned long len) static void fn_out_consume(void *priv, char *line, unsigned long len)
{ {
int i; int i;
int color;
struct emit_callback *ecbdata = priv; struct emit_callback *ecbdata = priv;
const char *set = diff_get_color(ecbdata->color_diff, DIFF_METAINFO); const char *set = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET); const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
@ -403,45 +486,52 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
; ;
if (2 <= i && i < len && line[i] == ' ') { if (2 <= i && i < len && line[i] == ' ') {
ecbdata->nparents = i - 1; ecbdata->nparents = i - 1;
set = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO); emit_line(diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO),
reset, line, len);
return;
} }
else if (len < ecbdata->nparents)
if (len < ecbdata->nparents) {
set = reset; set = reset;
else { emit_line(reset, reset, line, len);
int nparents = ecbdata->nparents; return;
int color = DIFF_PLAIN;
if (ecbdata->diff_words && nparents != 1)
/* fall back to normal diff */
free_diff_words_data(ecbdata);
if (ecbdata->diff_words) {
if (line[0] == '-') {
diff_words_append(line, len,
&ecbdata->diff_words->minus);
return;
} else if (line[0] == '+') {
diff_words_append(line, len,
&ecbdata->diff_words->plus);
return;
}
if (ecbdata->diff_words->minus.text.size ||
ecbdata->diff_words->plus.text.size)
diff_words_show(ecbdata->diff_words);
line++;
len--;
} else
for (i = 0; i < nparents && len; i++) {
if (line[i] == '-')
color = DIFF_FILE_OLD;
else if (line[i] == '+')
color = DIFF_FILE_NEW;
}
set = diff_get_color(ecbdata->color_diff, color);
} }
if (len > 0 && line[len-1] == '\n')
color = DIFF_PLAIN;
if (ecbdata->diff_words && ecbdata->nparents != 1)
/* fall back to normal diff */
free_diff_words_data(ecbdata);
if (ecbdata->diff_words) {
if (line[0] == '-') {
diff_words_append(line, len,
&ecbdata->diff_words->minus);
return;
} else if (line[0] == '+') {
diff_words_append(line, len,
&ecbdata->diff_words->plus);
return;
}
if (ecbdata->diff_words->minus.text.size ||
ecbdata->diff_words->plus.text.size)
diff_words_show(ecbdata->diff_words);
line++;
len--; len--;
fputs (set, stdout); emit_line(set, reset, line, len);
fwrite (line, len, 1, stdout); return;
puts (reset); }
for (i = 0; i < ecbdata->nparents && len; i++) {
if (line[i] == '-')
color = DIFF_FILE_OLD;
else if (line[i] == '+')
color = DIFF_FILE_NEW;
}
if (color != DIFF_FILE_NEW) {
emit_line(diff_get_color(ecbdata->color_diff, color),
reset, line, len);
return;
}
emit_add_line(reset, ecbdata, line, len);
} }
static char *pprint_rename(const char *a, const char *b) static char *pprint_rename(const char *a, const char *b)

1
diff.h
View File

@ -86,6 +86,7 @@ enum color_diff {
DIFF_FILE_OLD = 4, DIFF_FILE_OLD = 4,
DIFF_FILE_NEW = 5, DIFF_FILE_NEW = 5,
DIFF_COMMIT = 6, DIFF_COMMIT = 6,
DIFF_WHITESPACE = 7,
}; };
const char *diff_get_color(int diff_use_color, enum color_diff ix); const char *diff_get_color(int diff_use_color, enum color_diff ix);

27
dir.c
View File

@ -283,7 +283,7 @@ static int dir_exists(const char *dirname, int len)
* Also, we ignore the name ".git" (even if it is not a directory). * Also, we ignore the name ".git" (even if it is not a directory).
* That likely will not change. * That likely will not change.
*/ */
static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen) static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen, int check_only)
{ {
DIR *fdir = opendir(path); DIR *fdir = opendir(path);
int contents = 0; int contents = 0;
@ -314,7 +314,6 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
switch (DTYPE(de)) { switch (DTYPE(de)) {
struct stat st; struct stat st;
int subdir, rewind_base;
default: default:
continue; continue;
case DT_UNKNOWN: case DT_UNKNOWN:
@ -328,26 +327,30 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
case DT_DIR: case DT_DIR:
memcpy(fullname + baselen + len, "/", 2); memcpy(fullname + baselen + len, "/", 2);
len++; len++;
rewind_base = dir->nr;
subdir = read_directory_recursive(dir, fullname, fullname,
baselen + len);
if (dir->show_other_directories && if (dir->show_other_directories &&
(subdir || !dir->hide_empty_directories) &&
!dir_exists(fullname, baselen + len)) { !dir_exists(fullname, baselen + len)) {
/* Rewind the read subdirectory */ if (dir->hide_empty_directories &&
while (dir->nr > rewind_base) !read_directory_recursive(dir,
free(dir->entries[--dir->nr]); fullname, fullname,
baselen + len, 1))
continue;
break; break;
} }
contents += subdir;
contents += read_directory_recursive(dir,
fullname, fullname, baselen + len, 0);
continue; continue;
case DT_REG: case DT_REG:
case DT_LNK: case DT_LNK:
break; break;
} }
add_name(dir, fullname, baselen + len);
contents++; contents++;
if (check_only)
goto exit_early;
else
add_name(dir, fullname, baselen + len);
} }
exit_early:
closedir(fdir); closedir(fdir);
pop_exclude_per_directory(dir, exclude_stk); pop_exclude_per_directory(dir, exclude_stk);
@ -393,7 +396,7 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i
} }
} }
read_directory_recursive(dir, path, base, baselen); read_directory_recursive(dir, path, base, baselen, 0);
qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name); qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
return dir->nr; return dir->nr;
} }

View File

@ -20,6 +20,7 @@ int warn_ambiguous_refs = 1;
int repository_format_version; int repository_format_version;
char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8"; char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
int shared_repository = PERM_UMASK; int shared_repository = PERM_UMASK;
int deny_non_fast_forwards = 0;
const char *apply_default_whitespace; const char *apply_default_whitespace;
int zlib_compression_level = Z_DEFAULT_COMPRESSION; int zlib_compression_level = Z_DEFAULT_COMPRESSION;
int pager_in_use; int pager_in_use;

View File

@ -111,6 +111,16 @@ rev=$(git-rev-parse --verify "$head") || exit
git-check-ref-format "heads/$branchname" || git-check-ref-format "heads/$branchname" ||
die "we do not like '$branchname' as a branch name." die "we do not like '$branchname' as a branch name."
if [ -d "$GIT_DIR/refs/heads/$branchname" ]
then
for refdir in `cd "$GIT_DIR" && \
find "refs/heads/$branchname" -type d | sort -r`
do
rmdir "$GIT_DIR/$refdir" || \
die "Could not delete '$refdir', there may still be a ref there."
done
fi
prev='' prev=''
if git-show-ref --verify --quiet -- "refs/heads/$branchname" if git-show-ref --verify --quiet -- "refs/heads/$branchname"
then then

View File

@ -4,8 +4,8 @@ USAGE='[-f] [-b <new_branch>] [-m] [<branch>] [<paths>...]'
SUBDIRECTORY_OK=Sometimes SUBDIRECTORY_OK=Sometimes
. git-sh-setup . git-sh-setup
old=$(git-rev-parse HEAD)
old_name=HEAD old_name=HEAD
old=$(git-rev-parse --verify $old_name 2>/dev/null)
new= new=
new_name= new_name=
force= force=
@ -140,6 +140,13 @@ fi
die "git checkout: to checkout the requested commit you need to specify die "git checkout: to checkout the requested commit you need to specify
a name for a new branch which is created and switched to" a name for a new branch which is created and switched to"
if [ "X$old" = X ]
then
echo "warning: You do not appear to currently be on a branch." >&2
echo "warning: Forcing checkout of $new_name." >&2
force=1
fi
if [ "$force" ] if [ "$force" ]
then then
git-read-tree --reset -u $new git-read-tree --reset -u $new

View File

@ -68,11 +68,10 @@ done
case "$#" in case "$#" in
0) 0)
test -f "$GIT_DIR/branches/origin" || origin=$(get_default_remote)
test -f "$GIT_DIR/remotes/origin" || test -n "$(get_remote_url ${origin})" ||
git-repo-config --get remote.origin.url >/dev/null || die "Where do you want to fetch from today?"
die "Where do you want to fetch from today?" set x $origin ; shift ;;
set origin ;;
esac esac
remote_nick="$1" remote_nick="$1"

View File

@ -9,21 +9,15 @@ USAGE='[-n] [--no-commit] [--squash] [-s <strategy>]... <merge-message> <head> <
LF=' LF='
' '
all_strategies='recursive recur octopus resolve stupid ours' all_strategies='recur recursive recursive-old octopus resolve stupid ours'
case "${GIT_USE_RECUR_FOR_RECURSIVE}" in default_twohead_strategies='recursive'
'')
default_twohead_strategies=recursive ;;
?*)
default_twohead_strategies=recur ;;
esac
default_octopus_strategies='octopus' default_octopus_strategies='octopus'
no_trivial_merge_strategies='ours' no_trivial_merge_strategies='ours'
use_strategies= use_strategies=
index_merge=t index_merge=t
if test "@@NO_PYTHON@@"; then if test "@@NO_PYTHON@@"; then
all_strategies='recur resolve octopus stupid ours' all_strategies='recur recursive resolve octopus stupid ours'
default_twohead_strategies='resolve'
fi fi
dropsave() { dropsave() {
@ -122,10 +116,6 @@ do
strategy="$2" strategy="$2"
shift ;; shift ;;
esac esac
case "$strategy,${GIT_USE_RECUR_FOR_RECURSIVE}" in
recursive,?*)
strategy=recur ;;
esac
case " $all_strategies " in case " $all_strategies " in
*" $strategy "*) *" $strategy "*)
use_strategies="$use_strategies$strategy " ;; use_strategies="$use_strategies$strategy " ;;

View File

@ -68,6 +68,12 @@ get_remote_url () {
esac esac
} }
get_default_remote () {
curr_branch=$(git-symbolic-ref HEAD | sed -e 's|^refs/heads/||')
origin=$(git-repo-config --get "branch.$curr_branch.remote")
echo ${origin:-origin}
}
get_remote_default_refs_for_push () { get_remote_default_refs_for_push () {
data_source=$(get_data_source "$1") data_source=$(get_data_source "$1")
case "$data_source" in case "$data_source" in
@ -86,9 +92,22 @@ get_remote_default_refs_for_push () {
# Subroutine to canonicalize remote:local notation. # Subroutine to canonicalize remote:local notation.
canon_refs_list_for_fetch () { canon_refs_list_for_fetch () {
# Leave only the first one alone; add prefix . to the rest # If called from get_remote_default_refs_for_fetch
# leave the branches in branch.${curr_branch}.merge alone,
# or the first one otherwise; add prefix . to the rest
# to prevent the secondary branches to be merged by default. # to prevent the secondary branches to be merged by default.
dot_prefix= merge_branches=
if test "$1" = "-d"
then
shift ; remote="$1" ; shift
if test "$remote" = "$(get_default_remote)"
then
curr_branch=$(git-symbolic-ref HEAD | \
sed -e 's|^refs/heads/||')
merge_branches=$(git-repo-config \
--get-all "branch.${curr_branch}.merge")
fi
fi
for ref for ref
do do
force= force=
@ -101,6 +120,18 @@ canon_refs_list_for_fetch () {
expr "z$ref" : 'z.*:' >/dev/null || ref="${ref}:" expr "z$ref" : 'z.*:' >/dev/null || ref="${ref}:"
remote=$(expr "z$ref" : 'z\([^:]*\):') remote=$(expr "z$ref" : 'z\([^:]*\):')
local=$(expr "z$ref" : 'z[^:]*:\(.*\)') local=$(expr "z$ref" : 'z[^:]*:\(.*\)')
dot_prefix=.
if test -z "$merge_branches"
then
merge_branches=$remote
dot_prefix=
else
for merge_branch in $merge_branches
do
[ "$remote" = "$merge_branch" ] &&
dot_prefix= && break
done
fi
case "$remote" in case "$remote" in
'') remote=HEAD ;; '') remote=HEAD ;;
refs/heads/* | refs/tags/* | refs/remotes/*) ;; refs/heads/* | refs/tags/* | refs/remotes/*) ;;
@ -120,7 +151,6 @@ canon_refs_list_for_fetch () {
die "* refusing to create funny ref '$local_ref_name' locally" die "* refusing to create funny ref '$local_ref_name' locally"
fi fi
echo "${dot_prefix}${force}${remote}:${local}" echo "${dot_prefix}${force}${remote}:${local}"
dot_prefix=.
done done
} }
@ -131,7 +161,7 @@ get_remote_default_refs_for_fetch () {
'' | config-partial | branches-partial) '' | config-partial | branches-partial)
echo "HEAD:" ;; echo "HEAD:" ;;
config) config)
canon_refs_list_for_fetch \ canon_refs_list_for_fetch -d "$1" \
$(git-repo-config --get-all "remote.$1.fetch") ;; $(git-repo-config --get-all "remote.$1.fetch") ;;
branches) branches)
remote_branch=$(sed -ne '/#/s/.*#//p' "$GIT_DIR/branches/$1") remote_branch=$(sed -ne '/#/s/.*#//p' "$GIT_DIR/branches/$1")
@ -139,10 +169,7 @@ get_remote_default_refs_for_fetch () {
echo "refs/heads/${remote_branch}:refs/heads/$1" echo "refs/heads/${remote_branch}:refs/heads/$1"
;; ;;
remotes) remotes)
# This prefixes the second and later default refspecs canon_refs_list_for_fetch -d "$1" $(sed -ne '/^Pull: */{
# with a '.', to signal git-fetch to mark them
# not-for-merge.
canon_refs_list_for_fetch $(sed -ne '/^Pull: */{
s///p s///p
}' "$GIT_DIR/remotes/$1") }' "$GIT_DIR/remotes/$1")
;; ;;

View File

@ -35,13 +35,7 @@ If you would prefer to skip this patch, instead run \"git rebase --skip\".
To restore the original branch and stop rebasing run \"git rebase --abort\". To restore the original branch and stop rebasing run \"git rebase --abort\".
" "
unset newbase unset newbase
case "${GIT_USE_RECUR_FOR_RECURSIVE}" in strategy=recursive
'')
strategy=recursive ;;
?*)
strategy=recur ;;
esac
do_merge= do_merge=
dotest=$GIT_DIR/.dotest-merge dotest=$GIT_DIR/.dotest-merge
prec=4 prec=4
@ -206,11 +200,6 @@ do
shift shift
done done
case "$strategy,${GIT_USE_RECUR_FOR_RECURSIVE}" in
recursive,?*)
strategy=recur ;;
esac
# Make sure we do not have .dotest # Make sure we do not have .dotest
if test -z "$do_merge" if test -z "$do_merge"
then then
@ -303,11 +292,11 @@ then
exit $? exit $?
fi fi
if test "@@NO_PYTHON@@" && test "$strategy" = "recursive" if test "@@NO_PYTHON@@" && test "$strategy" = "recursive-old"
then then
die 'The recursive merge strategy currently relies on Python, die 'The recursive-old merge strategy is written in Python,
which this installation of git was not configured with. Please consider which this installation of git was not configured with. Please consider
a different merge strategy (e.g. octopus, resolve, stupid, ours) a different merge strategy (e.g. recursive, resolve, or stupid)
or install Python and git with Python support.' or install Python and git with Python support.'
fi fi

View File

@ -4,6 +4,7 @@
# #
USAGE='[-a] [-d] [-f] [-l] [-n] [-q]' USAGE='[-a] [-d] [-f] [-l] [-n] [-q]'
SUBDIRECTORY_OK='Yes'
. git-sh-setup . git-sh-setup
no_update_info= all_into_one= remove_redundant= no_update_info= all_into_one= remove_redundant=
@ -32,12 +33,10 @@ trap 'rm -f "$PACKTMP"-*' 0 1 2 3 15
# There will be more repacking strategies to come... # There will be more repacking strategies to come...
case ",$all_into_one," in case ",$all_into_one," in
,,) ,,)
rev_list='--unpacked' args='--unpacked --incremental'
pack_objects='--incremental'
;; ;;
,t,) ,t,)
rev_list= args=
pack_objects=
# Redundancy check in all-into-one case is trivial. # Redundancy check in all-into-one case is trivial.
existing=`test -d "$PACKDIR" && cd "$PACKDIR" && \ existing=`test -d "$PACKDIR" && cd "$PACKDIR" && \
@ -45,11 +44,8 @@ case ",$all_into_one," in
;; ;;
esac esac
pack_objects="$pack_objects $local $quiet $no_reuse_delta$extra" args="$args $local $quiet $no_reuse_delta$extra"
name=$( { git-rev-list --objects --all $rev_list || name=$(git-pack-objects --non-empty --all $args </dev/null "$PACKTMP") ||
echo "git-rev-list died with exit code $?"
} |
git-pack-objects --non-empty $pack_objects "$PACKTMP") ||
exit 1 exit 1
if [ -z "$name" ]; then if [ -z "$name" ]; then
echo Nothing new to pack. echo Nothing new to pack.

View File

@ -5,6 +5,10 @@
# Resolve two trees. # Resolve two trees.
# #
echo 'WARNING: This command is DEPRECATED and will be removed very soon.' >&2
echo 'WARNING: Please use git-merge or git-pull instead.' >&2
sleep 2
USAGE='<head> <remote> <merge-message>' USAGE='<head> <remote> <merge-message>'
. git-sh-setup . git-sh-setup

View File

@ -52,7 +52,7 @@ my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
$_template, $_shared, $_no_default_regex, $_no_graft_copy, $_template, $_shared, $_no_default_regex, $_no_graft_copy,
$_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit, $_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,
$_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m, $_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m,
$_merge, $_strategy, $_dry_run); $_merge, $_strategy, $_dry_run, $_ignore_nodate);
my (@_branch_from, %tree_map, %users, %rusers, %equiv); my (@_branch_from, %tree_map, %users, %rusers, %equiv);
my ($_svn_co_url_revs, $_svn_pg_peg_revs); my ($_svn_co_url_revs, $_svn_pg_peg_revs);
my @repo_path_split_cache; my @repo_path_split_cache;
@ -65,6 +65,7 @@ my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
'repack:i' => \$_repack, 'repack:i' => \$_repack,
'no-metadata' => \$_no_metadata, 'no-metadata' => \$_no_metadata,
'quiet|q' => \$_q, 'quiet|q' => \$_q,
'ignore-nodate' => \$_ignore_nodate,
'repack-flags|repack-args|repack-opts=s' => \$_repack_flags); 'repack-flags|repack-args|repack-opts=s' => \$_repack_flags);
my ($_trunk, $_tags, $_branches); my ($_trunk, $_tags, $_branches);
@ -1246,6 +1247,7 @@ sub assert_svn_wc_clean {
} }
my @status = grep(!/^Performing status on external/,(`svn status`)); my @status = grep(!/^Performing status on external/,(`svn status`));
@status = grep(!/^\s*$/,@status); @status = grep(!/^\s*$/,@status);
@status = grep(!/^X/,@status) if $_no_ignore_ext;
if (scalar @status) { if (scalar @status) {
print STDERR "Tree ($SVN_WC) is not clean:\n"; print STDERR "Tree ($SVN_WC) is not clean:\n";
print STDERR $_ foreach @status; print STDERR $_ foreach @status;
@ -1734,6 +1736,8 @@ sub next_log_entry {
my $rev = $1; my $rev = $1;
my ($author, $date, $lines) = split(/\s*\|\s*/, $_, 3); my ($author, $date, $lines) = split(/\s*\|\s*/, $_, 3);
($lines) = ($lines =~ /(\d+)/); ($lines) = ($lines =~ /(\d+)/);
$date = '1970-01-01 00:00:00 +0000'
if ($_ignore_nodate && $date eq '(no date)');
my ($Y,$m,$d,$H,$M,$S,$tz) = ($date =~ my ($Y,$m,$d,$H,$M,$S,$tz) = ($date =~
/(\d{4})\-(\d\d)\-(\d\d)\s /(\d{4})\-(\d\d)\-(\d\d)\s
(\d\d)\:(\d\d)\:(\d\d)\s([\-\+]\d+)/x) (\d\d)\:(\d\d)\:(\d\d)\s([\-\+]\d+)/x)
@ -2168,7 +2172,7 @@ sub load_authors {
open my $authors, '<', $_authors or die "Can't open $_authors $!\n"; open my $authors, '<', $_authors or die "Can't open $_authors $!\n";
while (<$authors>) { while (<$authors>) {
chomp; chomp;
next unless /^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/; next unless /^(\S+?|\(no author\))\s*=\s*(.+?)\s*<(.+)>\s*$/;
my ($user, $name, $email) = ($1, $2, $3); my ($user, $name, $email) = ($1, $2, $3);
$users{$user} = [$name, $email]; $users{$user} = [$name, $email];
} }

View File

@ -31,7 +31,7 @@ $SIG{'PIPE'}="IGNORE";
$ENV{'TZ'}="UTC"; $ENV{'TZ'}="UTC";
our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T, our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T,
$opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D); $opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D,$opt_S);
sub usage() { sub usage() {
print STDERR <<END; print STDERR <<END;
@ -39,12 +39,12 @@ Usage: ${\basename $0} # fetch/update GIT from SVN
[-o branch-for-HEAD] [-h] [-v] [-l max_rev] [-o branch-for-HEAD] [-h] [-v] [-l max_rev]
[-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname] [-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname]
[-d|-D] [-i] [-u] [-r] [-I ignorefilename] [-s start_chg] [-d|-D] [-i] [-u] [-r] [-I ignorefilename] [-s start_chg]
[-m] [-M regex] [-A author_file] [SVN_URL] [-m] [-M regex] [-A author_file] [-S] [SVN_URL]
END END
exit(1); exit(1);
} }
getopts("A:b:C:dDhiI:l:mM:o:rs:t:T:uv") or usage(); getopts("A:b:C:dDhiI:l:mM:o:rs:t:T:Suv") or usage();
usage if $opt_h; usage if $opt_h;
my $tag_name = $opt_t || "tags"; my $tag_name = $opt_t || "tags";
@ -531,21 +531,30 @@ sub copy_path($$$$$$$$) {
sub commit { sub commit {
my($branch, $changed_paths, $revision, $author, $date, $message) = @_; my($branch, $changed_paths, $revision, $author, $date, $message) = @_;
my($author_name,$author_email,$dest); my($committer_name,$committer_email,$dest);
my($author_name,$author_email);
my(@old,@new,@parents); my(@old,@new,@parents);
if (not defined $author or $author eq "") { if (not defined $author or $author eq "") {
$author_name = $author_email = "unknown"; $committer_name = $committer_email = "unknown";
} elsif (defined $users_file) { } elsif (defined $users_file) {
die "User $author is not listed in $users_file\n" die "User $author is not listed in $users_file\n"
unless exists $users{$author}; unless exists $users{$author};
($author_name,$author_email) = @{$users{$author}}; ($committer_name,$committer_email) = @{$users{$author}};
} elsif ($author =~ /^(.*?)\s+<(.*)>$/) { } elsif ($author =~ /^(.*?)\s+<(.*)>$/) {
($author_name, $author_email) = ($1, $2); ($committer_name, $committer_email) = ($1, $2);
} else { } else {
$author =~ s/^<(.*)>$/$1/; $author =~ s/^<(.*)>$/$1/;
$author_name = $author_email = $author; $committer_name = $committer_email = $author;
} }
if ($opt_S && $message =~ /Signed-off-by:\s+(.*?)\s+<(.*)>\s*\n/) {
($author_name, $author_email) = ($1, $2);
} else {
$author_name = $committer_name;
$author_email = $committer_email;
}
$date = pdate($date); $date = pdate($date);
my $tag; my $tag;
@ -772,8 +781,8 @@ sub commit {
"GIT_AUTHOR_NAME=$author_name", "GIT_AUTHOR_NAME=$author_name",
"GIT_AUTHOR_EMAIL=$author_email", "GIT_AUTHOR_EMAIL=$author_email",
"GIT_AUTHOR_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)), "GIT_AUTHOR_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)),
"GIT_COMMITTER_NAME=$author_name", "GIT_COMMITTER_NAME=$committer_name",
"GIT_COMMITTER_EMAIL=$author_email", "GIT_COMMITTER_EMAIL=$committer_email",
"GIT_COMMITTER_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)), "GIT_COMMITTER_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)),
"git-commit-tree", $tree,@par); "git-commit-tree", $tree,@par);
die "Cannot exec git-commit-tree: $!\n"; die "Cannot exec git-commit-tree: $!\n";
@ -825,7 +834,7 @@ sub commit {
print $out ("object $cid\n". print $out ("object $cid\n".
"type commit\n". "type commit\n".
"tag $dest\n". "tag $dest\n".
"tagger $author_name <$author_email>\n") and "tagger $committer_name <$committer_email>\n") and
close($out) close($out)
or die "Cannot create tag object $dest: $!\n"; or die "Cannot create tag object $dest: $!\n";

2
git.c
View File

@ -260,12 +260,10 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "stripspace", cmd_stripspace }, { "stripspace", cmd_stripspace },
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP }, { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
{ "tar-tree", cmd_tar_tree, RUN_SETUP }, { "tar-tree", cmd_tar_tree, RUN_SETUP },
{ "zip-tree", cmd_zip_tree, RUN_SETUP },
{ "unpack-objects", cmd_unpack_objects, RUN_SETUP }, { "unpack-objects", cmd_unpack_objects, RUN_SETUP },
{ "update-index", cmd_update_index, RUN_SETUP }, { "update-index", cmd_update_index, RUN_SETUP },
{ "update-ref", cmd_update_ref, RUN_SETUP }, { "update-ref", cmd_update_ref, RUN_SETUP },
{ "upload-archive", cmd_upload_archive }, { "upload-archive", cmd_upload_archive },
{ "upload-tar", cmd_upload_tar },
{ "version", cmd_version }, { "version", cmd_version },
{ "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER }, { "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
{ "write-tree", cmd_write_tree, RUN_SETUP }, { "write-tree", cmd_write_tree, RUN_SETUP },

View File

@ -212,19 +212,9 @@ if (defined $project) {
} }
} }
# We have to handle those containing any characters:
our $file_name = $cgi->param('f'); our $file_name = $cgi->param('f');
if (defined $file_name) {
if (!validate_input($file_name)) {
die_error(undef, "Invalid file parameter");
}
}
our $file_parent = $cgi->param('fp'); our $file_parent = $cgi->param('fp');
if (defined $file_parent) {
if (!validate_input($file_parent)) {
die_error(undef, "Invalid file parent parameter");
}
}
our $hash = $cgi->param('h'); our $hash = $cgi->param('h');
if (defined $hash) { if (defined $hash) {
@ -274,13 +264,16 @@ sub evaluate_path_info {
return if defined $project; return if defined $project;
my $path_info = $ENV{"PATH_INFO"}; my $path_info = $ENV{"PATH_INFO"};
return if !$path_info; return if !$path_info;
$path_info =~ s,(^/|/$),,gs; $path_info =~ s,^/+,,;
$path_info = validate_input($path_info);
return if !$path_info; return if !$path_info;
# find which part of PATH_INFO is project
$project = $path_info; $project = $path_info;
$project =~ s,/+$,,;
while ($project && !-e "$projectroot/$project/HEAD") { while ($project && !-e "$projectroot/$project/HEAD") {
$project =~ s,/*[^/]*$,,; $project =~ s,/*[^/]*$,,;
} }
# validate project
$project = validate_input($project);
if (!$project || if (!$project ||
($export_ok && !-e "$projectroot/$project/$export_ok") || ($export_ok && !-e "$projectroot/$project/$export_ok") ||
($strict_export && !project_in_list($project))) { ($strict_export && !project_in_list($project))) {
@ -289,15 +282,24 @@ sub evaluate_path_info {
} }
# do not change any parameters if an action is given using the query string # do not change any parameters if an action is given using the query string
return if $action; return if $action;
if ($path_info =~ m,^$project/([^/]+)/(.+)$,) { $path_info =~ s,^$project/*,,;
# we got "project.git/branch/filename" my ($refname, $pathname) = split(/:/, $path_info, 2);
$action ||= "blob_plain"; if (defined $pathname) {
$hash_base ||= validate_input($1); # we got "project.git/branch:filename" or "project.git/branch:dir/"
$file_name ||= validate_input($2); # we could use git_get_type(branch:pathname), but it needs $git_dir
} elsif ($path_info =~ m,^$project/([^/]+)$,) { $pathname =~ s,^/+,,;
if (!$pathname || substr($pathname, -1) eq "/") {
$action ||= "tree";
$pathname =~ s,/$,,;
} else {
$action ||= "blob_plain";
}
$hash_base ||= validate_input($refname);
$file_name ||= $pathname;
} elsif (defined $refname) {
# we got "project.git/branch" # we got "project.git/branch"
$action ||= "shortlog"; $action ||= "shortlog";
$hash ||= validate_input($1); $hash ||= validate_input($refname);
} }
} }
evaluate_path_info(); evaluate_path_info();
@ -341,6 +343,10 @@ if (defined $project) {
if (!defined($actions{$action})) { if (!defined($actions{$action})) {
die_error(undef, "Unknown action"); die_error(undef, "Unknown action");
} }
if ($action !~ m/^(opml|project_list|project_index)$/ &&
!$project) {
die_error(undef, "Project needed");
}
$actions{$action}->(); $actions{$action}->();
exit; exit;
@ -400,7 +406,7 @@ sub validate_input {
# correct, but quoted slashes look too horrible in bookmarks # correct, but quoted slashes look too horrible in bookmarks
sub esc_param { sub esc_param {
my $str = shift; my $str = shift;
$str =~ s/([^A-Za-z0-9\-_.~();\/;?:@&=])/sprintf("%%%02X", ord($1))/eg; $str =~ s/([^A-Za-z0-9\-_.~()\/:@])/sprintf("%%%02X", ord($1))/eg;
$str =~ s/\+/%2B/g; $str =~ s/\+/%2B/g;
$str =~ s/ /\+/g; $str =~ s/ /\+/g;
return $str; return $str;
@ -611,7 +617,7 @@ sub format_subject_html {
if (length($short) < length($long)) { if (length($short) < length($long)) {
return $cgi->a({-href => $href, -class => "list subject", return $cgi->a({-href => $href, -class => "list subject",
-title => $long}, -title => decode("utf8", $long, Encode::FB_DEFAULT)},
esc_html($short) . $extra); esc_html($short) . $extra);
} else { } else {
return $cgi->a({-href => $href, -class => "list subject"}, return $cgi->a({-href => $href, -class => "list subject"},
@ -702,6 +708,7 @@ sub git_get_project_config {
sub git_get_hash_by_path { sub git_get_hash_by_path {
my $base = shift; my $base = shift;
my $path = shift || return undef; my $path = shift || return undef;
my $type = shift;
my $tree = $base; my $tree = $base;
@ -712,6 +719,10 @@ sub git_get_hash_by_path {
#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/; $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/;
if (defined $type && $type ne $2) {
# type doesn't match
return undef;
}
return $3; return $3;
} }
@ -731,7 +742,7 @@ sub git_get_project_description {
sub git_get_project_url_list { sub git_get_project_url_list {
my $path = shift; my $path = shift;
open my $fd, "$projectroot/$path/cloneurl" or return undef; open my $fd, "$projectroot/$path/cloneurl" or return;
my @git_project_url_list = map { chomp; $_ } <$fd>; my @git_project_url_list = map { chomp; $_ } <$fd>;
close $fd; close $fd;
@ -828,16 +839,10 @@ sub git_get_project_owner {
sub git_get_references { sub git_get_references {
my $type = shift || ""; my $type = shift || "";
my %refs; my %refs;
my $fd;
# 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11 # 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11
# c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11^{} # c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11^{}
if (-f "$projectroot/$project/info/refs") { open my $fd, "-|", $GIT, "peek-remote", "$projectroot/$project/"
open $fd, "$projectroot/$project/info/refs" or return;
or return;
} else {
open $fd, "-|", git_cmd(), "ls-remote", "."
or return;
}
while (my $line = <$fd>) { while (my $line = <$fd>) {
chomp $line; chomp $line;
@ -1125,7 +1130,8 @@ sub parse_ls_tree_line ($;%) {
## parse to array of hashes functions ## parse to array of hashes functions
sub git_get_refs_list { sub git_get_refs_list {
my $ref_dir = shift; my $type = shift || "";
my %refs;
my @reflist; my @reflist;
my @refs; my @refs;
@ -1133,14 +1139,21 @@ sub git_get_refs_list {
or return; or return;
while (my $line = <$fd>) { while (my $line = <$fd>) {
chomp $line; chomp $line;
if ($line =~ m/^([0-9a-fA-F]{40})\t$ref_dir\/?([^\^]+)$/) { if ($line =~ m/^([0-9a-fA-F]{40})\trefs\/($type\/?([^\^]+))(\^\{\})?$/) {
push @refs, { hash => $1, name => $2 }; if (defined $refs{$1}) {
} elsif ($line =~ m/^[0-9a-fA-F]{40}\t$ref_dir\/?(.*)\^\{\}$/ && push @{$refs{$1}}, $2;
$1 eq $refs[-1]{'name'}) { } else {
# most likely a tag is followed by its peeled $refs{$1} = [ $2 ];
# (deref) one, and when that happens we know the }
# previous one was of type 'tag'.
$refs[-1]{'type'} = "tag"; if (! $4) { # unpeeled, direct reference
push @refs, { hash => $1, name => $3 }; # without type
} elsif ($3 eq $refs[-1]{'name'}) {
# most likely a tag is followed by its peeled
# (deref) one, and when that happens we know the
# previous one was of type 'tag'.
$refs[-1]{'type'} = "tag";
}
} }
} }
close $fd; close $fd;
@ -1156,7 +1169,7 @@ sub git_get_refs_list {
} }
# sort refs by age # sort refs by age
@reflist = sort {$b->{'epoch'} <=> $a->{'epoch'}} @reflist; @reflist = sort {$b->{'epoch'} <=> $a->{'epoch'}} @reflist;
return \@reflist; return (\@reflist, \%refs);
} }
## ---------------------------------------------------------------------- ## ----------------------------------------------------------------------
@ -1197,7 +1210,7 @@ sub mimetype_guess_file {
} }
close(MIME); close(MIME);
$filename =~ /\.(.*?)$/; $filename =~ /\.([^.]*)$/;
return $mimemap{$1}; return $mimemap{$1};
} }
@ -1259,7 +1272,7 @@ sub git_header_html {
if (defined $action) { if (defined $action) {
$title .= "/$action"; $title .= "/$action";
if (defined $file_name) { if (defined $file_name) {
$title .= " - $file_name"; $title .= " - " . esc_html($file_name);
if ($action eq "tree" && $file_name !~ m|/$|) { if ($action eq "tree" && $file_name !~ m|/$|) {
$title .= "/"; $title .= "/";
} }
@ -1496,12 +1509,15 @@ sub git_print_page_path {
my $fullname = ''; my $fullname = '';
print "<div class=\"page_path\">"; print "<div class=\"page_path\">";
print $cgi->a({-href => href(action=>"tree", hash_base=>$hb),
-title => 'tree root'}, "[$project]");
print " / ";
foreach my $dir (@dirname) { foreach my $dir (@dirname) {
$fullname .= $dir . '/'; $fullname .= ($fullname ? '/' : '') . $dir;
print $cgi->a({-href => href(action=>"tree", file_name=>$fullname, print $cgi->a({-href => href(action=>"tree", file_name=>$fullname,
hash_base=>$hb), hash_base=>$hb),
-title => $fullname}, esc_html($dir)); -title => $fullname}, esc_html($dir));
print "/"; print " / ";
} }
if (defined $type && $type eq 'blob') { if (defined $type && $type eq 'blob') {
print $cgi->a({-href => href(action=>"blob_plain", file_name=>$file_name, print $cgi->a({-href => href(action=>"blob_plain", file_name=>$file_name,
@ -1511,7 +1527,6 @@ sub git_print_page_path {
print $cgi->a({-href => href(action=>"tree", file_name=>$file_name, print $cgi->a({-href => href(action=>"tree", file_name=>$file_name,
hash_base=>$hb), hash_base=>$hb),
-title => $name}, esc_html($basename)); -title => $name}, esc_html($basename));
print "/";
} else { } else {
print esc_html($basename); print esc_html($basename);
} }
@ -1950,9 +1965,6 @@ sub git_shortlog_body {
# uses global variable $project # uses global variable $project
my ($revlist, $from, $to, $refs, $extra) = @_; my ($revlist, $from, $to, $refs, $extra) = @_;
my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
my $have_snapshot = (defined $ctype && defined $suffix);
$from = 0 unless defined $from; $from = 0 unless defined $from;
$to = $#{$revlist} if (!defined $to || $#{$revlist} < $to); $to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);
@ -1978,10 +1990,8 @@ sub git_shortlog_body {
print "</td>\n" . print "</td>\n" .
"<td class=\"link\">" . "<td class=\"link\">" .
$cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " . $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " .
$cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff"); $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " .
if ($have_snapshot) { $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree");
print " | " . $cgi->a({-href => href(action=>"snapshot", hash=>$commit)}, "snapshot");
}
print "</td>\n" . print "</td>\n" .
"</tr>\n"; "</tr>\n";
} }
@ -2120,14 +2130,14 @@ sub git_tags_body {
sub git_heads_body { sub git_heads_body {
# uses global variable $project # uses global variable $project
my ($taglist, $head, $from, $to, $extra) = @_; my ($headlist, $head, $from, $to, $extra) = @_;
$from = 0 unless defined $from; $from = 0 unless defined $from;
$to = $#{$taglist} if (!defined $to || $#{$taglist} < $to); $to = $#{$headlist} if (!defined $to || $#{$headlist} < $to);
print "<table class=\"heads\" cellspacing=\"0\">\n"; print "<table class=\"heads\" cellspacing=\"0\">\n";
my $alternate = 0; my $alternate = 0;
for (my $i = $from; $i <= $to; $i++) { for (my $i = $from; $i <= $to; $i++) {
my $entry = $taglist->[$i]; my $entry = $headlist->[$i];
my %tag = %$entry; my %tag = %$entry;
my $curr = $tag{'id'} eq $head; my $curr = $tag{'id'} eq $head;
if ($alternate) { if ($alternate) {
@ -2143,7 +2153,8 @@ sub git_heads_body {
"</td>\n" . "</td>\n" .
"<td class=\"link\">" . "<td class=\"link\">" .
$cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'})}, "shortlog") . " | " . $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'})}, "shortlog") . " | " .
$cgi->a({-href => href(action=>"log", hash=>$tag{'name'})}, "log") . $cgi->a({-href => href(action=>"log", hash=>$tag{'name'})}, "log") . " | " .
$cgi->a({-href => href(action=>"tree", hash=>$tag{'name'}, hash_base=>$tag{'name'})}, "tree") .
"</td>\n" . "</td>\n" .
"</tr>"; "</tr>";
} }
@ -2257,7 +2268,8 @@ sub git_project_list {
"<td class=\"link\">" . "<td class=\"link\">" .
$cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary") . " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary") . " | " .
$cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " .
$cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") . " | " .
$cgi->a({-href => href(project=>$pr->{'path'}, action=>"tree")}, "tree") .
"</td>\n" . "</td>\n" .
"</tr>\n"; "</tr>\n";
} }
@ -2297,7 +2309,19 @@ sub git_summary {
my $owner = git_get_project_owner($project); my $owner = git_get_project_owner($project);
my $refs = git_get_references(); my ($reflist, $refs) = git_get_refs_list();
my @taglist;
my @headlist;
foreach my $ref (@$reflist) {
if ($ref->{'name'} =~ s!^heads/!!) {
push @headlist, $ref;
} else {
$ref->{'name'} =~ s!^tags/!!;
push @taglist, $ref;
}
}
git_header_html(); git_header_html();
git_print_page_nav('summary','', $head); git_print_page_nav('summary','', $head);
@ -2327,17 +2351,15 @@ sub git_summary {
git_shortlog_body(\@revlist, 0, 15, $refs, git_shortlog_body(\@revlist, 0, 15, $refs,
$cgi->a({-href => href(action=>"shortlog")}, "...")); $cgi->a({-href => href(action=>"shortlog")}, "..."));
my $taglist = git_get_refs_list("refs/tags"); if (@taglist) {
if (defined @$taglist) {
git_print_header_div('tags'); git_print_header_div('tags');
git_tags_body($taglist, 0, 15, git_tags_body(\@taglist, 0, 15,
$cgi->a({-href => href(action=>"tags")}, "...")); $cgi->a({-href => href(action=>"tags")}, "..."));
} }
my $headlist = git_get_refs_list("refs/heads"); if (@headlist) {
if (defined @$headlist) {
git_print_header_div('heads'); git_print_header_div('heads');
git_heads_body($headlist, $head, 0, 15, git_heads_body(\@headlist, $head, 0, 15,
$cgi->a({-href => href(action=>"heads")}, "...")); $cgi->a({-href => href(action=>"heads")}, "..."));
} }
@ -2398,15 +2420,18 @@ sub git_blame2 {
if ($ftype !~ "blob") { if ($ftype !~ "blob") {
die_error("400 Bad Request", "Object is not a blob"); die_error("400 Bad Request", "Object is not a blob");
} }
open ($fd, "-|", git_cmd(), "blame", '-l', $file_name, $hash_base) open ($fd, "-|", git_cmd(), "blame", '-l', '--', $file_name, $hash_base)
or die_error(undef, "Open git-blame failed"); or die_error(undef, "Open git-blame failed");
git_header_html(); git_header_html();
my $formats_nav = my $formats_nav =
$cgi->a({-href => href(action=>"blob", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, $cgi->a({-href => href(action=>"blob", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)},
"blob") . "blob") .
" | " . " | " .
$cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)},
"history") .
" | " .
$cgi->a({-href => href(action=>"blame", file_name=>$file_name)}, $cgi->a({-href => href(action=>"blame", file_name=>$file_name)},
"head"); "HEAD");
git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
git_print_header_div('commit', esc_html($co{'title'}), $hash_base); git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
git_print_page_path($file_name, $ftype, $hash_base); git_print_page_path($file_name, $ftype, $hash_base);
@ -2471,8 +2496,11 @@ sub git_blame {
$cgi->a({-href => href(action=>"blob", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, $cgi->a({-href => href(action=>"blob", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)},
"blob") . "blob") .
" | " . " | " .
$cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)},
"history") .
" | " .
$cgi->a({-href => href(action=>"blame", file_name=>$file_name)}, $cgi->a({-href => href(action=>"blame", file_name=>$file_name)},
"head"); "HEAD");
git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
git_print_header_div('commit', esc_html($co{'title'}), $hash_base); git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
git_print_page_path($file_name, 'blob', $hash_base); git_print_page_path($file_name, 'blob', $hash_base);
@ -2548,8 +2576,8 @@ sub git_tags {
git_print_page_nav('','', $head,undef,$head); git_print_page_nav('','', $head,undef,$head);
git_print_header_div('summary', $project); git_print_header_div('summary', $project);
my $taglist = git_get_refs_list("refs/tags"); my ($taglist) = git_get_refs_list("tags");
if (defined @$taglist) { if (@$taglist) {
git_tags_body($taglist); git_tags_body($taglist);
} }
git_footer_html(); git_footer_html();
@ -2561,9 +2589,9 @@ sub git_heads {
git_print_page_nav('','', $head,undef,$head); git_print_page_nav('','', $head,undef,$head);
git_print_header_div('summary', $project); git_print_header_div('summary', $project);
my $taglist = git_get_refs_list("refs/heads"); my ($headlist) = git_get_refs_list("heads");
if (defined @$taglist) { if (@$headlist) {
git_heads_body($taglist, $head); git_heads_body($headlist, $head);
} }
git_footer_html(); git_footer_html();
} }
@ -2646,16 +2674,20 @@ sub git_blob {
" | "; " | ";
} }
$formats_nav .= $formats_nav .=
$cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
hash=>$hash, file_name=>$file_name)},
"history") .
" | " .
$cgi->a({-href => href(action=>"blob_plain", $cgi->a({-href => href(action=>"blob_plain",
hash=>$hash, file_name=>$file_name)}, hash=>$hash, file_name=>$file_name)},
"plain") . "raw") .
" | " . " | " .
$cgi->a({-href => href(action=>"blob", $cgi->a({-href => href(action=>"blob",
hash_base=>"HEAD", file_name=>$file_name)}, hash_base=>"HEAD", file_name=>$file_name)},
"head"); "HEAD");
} else { } else {
$formats_nav .= $formats_nav .=
$cgi->a({-href => href(action=>"blob_plain", hash=>$hash)}, "plain"); $cgi->a({-href => href(action=>"blob_plain", hash=>$hash)}, "raw");
} }
git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
git_print_header_div('commit', esc_html($co{'title'}), $hash_base); git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
@ -2681,6 +2713,9 @@ sub git_blob {
} }
sub git_tree { sub git_tree {
my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
my $have_snapshot = (defined $ctype && defined $suffix);
if (!defined $hash) { if (!defined $hash) {
$hash = git_get_head_hash($project); $hash = git_get_head_hash($project);
if (defined $file_name) { if (defined $file_name) {
@ -2704,7 +2739,23 @@ sub git_tree {
my $base = ""; my $base = "";
my ($have_blame) = gitweb_check_feature('blame'); my ($have_blame) = gitweb_check_feature('blame');
if (defined $hash_base && (my %co = parse_commit($hash_base))) { if (defined $hash_base && (my %co = parse_commit($hash_base))) {
git_print_page_nav('tree','', $hash_base); my @views_nav = ();
if (defined $file_name) {
push @views_nav,
$cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
hash=>$hash, file_name=>$file_name)},
"history"),
$cgi->a({-href => href(action=>"tree",
hash_base=>"HEAD", file_name=>$file_name)},
"HEAD"),
}
if ($have_snapshot) {
# FIXME: Should be available when we have no hash base as well.
push @views_nav,
$cgi->a({-href => href(action=>"snapshot", hash=>$hash)},
"snapshot");
}
git_print_page_nav('tree','', $hash_base, undef, undef, join(' | ', @views_nav));
git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base); git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base);
} else { } else {
undef $hash_base; undef $hash_base;
@ -2809,6 +2860,8 @@ sub git_log {
$cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") .
" | " . " | " .
$cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") .
" | " .
$cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree") .
"<br/>\n" . "<br/>\n" .
"</div>\n" . "</div>\n" .
"<i>" . esc_html($co{'author_name'}) . " [$ad{'rfc2822'}]</i><br/>\n" . "<i>" . esc_html($co{'author_name'}) . " [$ad{'rfc2822'}]</i><br/>\n" .
@ -2849,17 +2902,22 @@ sub git_commit {
my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot'); my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
my $have_snapshot = (defined $ctype && defined $suffix); my $have_snapshot = (defined $ctype && defined $suffix);
my $formats_nav = ''; my @views_nav = ();
if (defined $file_name && defined $co{'parent'}) { if (defined $file_name && defined $co{'parent'}) {
my $parent = $co{'parent'}; my $parent = $co{'parent'};
$formats_nav .= push @views_nav,
$cgi->a({-href => href(action=>"blame", hash_parent=>$parent, file_name=>$file_name)}, $cgi->a({-href => href(action=>"blame", hash_parent=>$parent, file_name=>$file_name)},
"blame"); "blame");
} }
if (defined $co{'parent'}) {
push @views_nav,
$cgi->a({-href => href(action=>"shortlog", hash=>$hash)}, "shortlog"),
$cgi->a({-href => href(action=>"log", hash=>$hash)}, "log");
}
git_header_html(undef, $expires); git_header_html(undef, $expires);
git_print_page_nav('commit', defined $co{'parent'} ? '' : 'commitdiff', git_print_page_nav('commit', defined $co{'parent'} ? '' : 'commitdiff',
$hash, $co{'tree'}, $hash, $hash, $co{'tree'}, $hash,
$formats_nav); join (' | ', @views_nav));
if (defined $co{'parent'}) { if (defined $co{'parent'}) {
git_print_header_div('commitdiff', esc_html($co{'title'}) . $ref, $hash); git_print_header_div('commitdiff', esc_html($co{'title'}) . $ref, $hash);
@ -3038,7 +3096,7 @@ sub git_blobdiff {
hash=>$hash, hash_parent=>$hash_parent, hash=>$hash, hash_parent=>$hash_parent,
hash_base=>$hash_base, hash_parent_base=>$hash_parent_base, hash_base=>$hash_base, hash_parent_base=>$hash_parent_base,
file_name=>$file_name, file_parent=>$file_parent)}, file_name=>$file_name, file_parent=>$file_parent)},
"plain"); "raw");
git_header_html(undef, $expires); git_header_html(undef, $expires);
if (defined $hash_base && (my %co = parse_commit($hash_base))) { if (defined $hash_base && (my %co = parse_commit($hash_base))) {
git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
@ -3058,7 +3116,7 @@ sub git_blobdiff {
-type => 'text/plain', -type => 'text/plain',
-charset => 'utf-8', -charset => 'utf-8',
-expires => $expires, -expires => $expires,
-content_disposition => qq(inline; filename="${file_name}.patch")); -content_disposition => qq(inline; filename=") . quotemeta($file_name) . qq(.patch"));
print "X-Git-Url: " . $cgi->self_url() . "\n\n"; print "X-Git-Url: " . $cgi->self_url() . "\n\n";
@ -3078,8 +3136,8 @@ sub git_blobdiff {
} else { } else {
while (my $line = <$fd>) { while (my $line = <$fd>) {
$line =~ s!a/($hash|$hash_parent)!a/$diffinfo{'from_file'}!g; $line =~ s!a/($hash|$hash_parent)!'a/'.esc_html($diffinfo{'from_file'})!eg;
$line =~ s!b/($hash|$hash_parent)!b/$diffinfo{'to_file'}!g; $line =~ s!b/($hash|$hash_parent)!'b/'.esc_html($diffinfo{'to_file'})!eg;
print $line; print $line;
@ -3141,7 +3199,7 @@ sub git_commitdiff {
my $formats_nav = my $formats_nav =
$cgi->a({-href => href(action=>"commitdiff_plain", $cgi->a({-href => href(action=>"commitdiff_plain",
hash=>$hash, hash_parent=>$hash_parent)}, hash=>$hash, hash_parent=>$hash_parent)},
"plain"); "raw");
git_header_html(undef, $expires); git_header_html(undef, $expires);
git_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav); git_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav);
@ -3508,7 +3566,7 @@ XML
if (!($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/)) { if (!($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/)) {
next; next;
} }
my $file = validate_input(unquote($7)); my $file = esc_html(unquote($7));
$file = decode("utf8", $file, Encode::FB_DEFAULT); $file = decode("utf8", $file, Encode::FB_DEFAULT);
print "$file<br/>\n"; print "$file<br/>\n";
} }

498
grep.c Normal file
View File

@ -0,0 +1,498 @@
#include "cache.h"
#include <regex.h>
#include "grep.h"
void append_grep_pattern(struct grep_opt *opt, const char *pat,
const char *origin, int no, enum grep_pat_token t)
{
struct grep_pat *p = xcalloc(1, sizeof(*p));
p->pattern = pat;
p->origin = origin;
p->no = no;
p->token = t;
*opt->pattern_tail = p;
opt->pattern_tail = &p->next;
p->next = NULL;
}
static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
{
int err = regcomp(&p->regexp, p->pattern, opt->regflags);
if (err) {
char errbuf[1024];
char where[1024];
if (p->no)
sprintf(where, "In '%s' at %d, ",
p->origin, p->no);
else if (p->origin)
sprintf(where, "%s, ", p->origin);
else
where[0] = 0;
regerror(err, &p->regexp, errbuf, 1024);
regfree(&p->regexp);
die("%s'%s': %s", where, p->pattern, errbuf);
}
}
static struct grep_expr *compile_pattern_expr(struct grep_pat **);
static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
{
struct grep_pat *p;
struct grep_expr *x;
p = *list;
switch (p->token) {
case GREP_PATTERN: /* atom */
case GREP_PATTERN_HEAD:
case GREP_PATTERN_BODY:
x = xcalloc(1, sizeof (struct grep_expr));
x->node = GREP_NODE_ATOM;
x->u.atom = p;
*list = p->next;
return x;
case GREP_OPEN_PAREN:
*list = p->next;
x = compile_pattern_expr(list);
if (!x)
return NULL;
if (!*list || (*list)->token != GREP_CLOSE_PAREN)
die("unmatched parenthesis");
*list = (*list)->next;
return x;
default:
return NULL;
}
}
static struct grep_expr *compile_pattern_not(struct grep_pat **list)
{
struct grep_pat *p;
struct grep_expr *x;
p = *list;
switch (p->token) {
case GREP_NOT:
if (!p->next)
die("--not not followed by pattern expression");
*list = p->next;
x = xcalloc(1, sizeof (struct grep_expr));
x->node = GREP_NODE_NOT;
x->u.unary = compile_pattern_not(list);
if (!x->u.unary)
die("--not followed by non pattern expression");
return x;
default:
return compile_pattern_atom(list);
}
}
static struct grep_expr *compile_pattern_and(struct grep_pat **list)
{
struct grep_pat *p;
struct grep_expr *x, *y, *z;
x = compile_pattern_not(list);
p = *list;
if (p && p->token == GREP_AND) {
if (!p->next)
die("--and not followed by pattern expression");
*list = p->next;
y = compile_pattern_and(list);
if (!y)
die("--and not followed by pattern expression");
z = xcalloc(1, sizeof (struct grep_expr));
z->node = GREP_NODE_AND;
z->u.binary.left = x;
z->u.binary.right = y;
return z;
}
return x;
}
static struct grep_expr *compile_pattern_or(struct grep_pat **list)
{
struct grep_pat *p;
struct grep_expr *x, *y, *z;
x = compile_pattern_and(list);
p = *list;
if (x && p && p->token != GREP_CLOSE_PAREN) {
y = compile_pattern_or(list);
if (!y)
die("not a pattern expression %s", p->pattern);
z = xcalloc(1, sizeof (struct grep_expr));
z->node = GREP_NODE_OR;
z->u.binary.left = x;
z->u.binary.right = y;
return z;
}
return x;
}
static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
{
return compile_pattern_or(list);
}
void compile_grep_patterns(struct grep_opt *opt)
{
struct grep_pat *p;
for (p = opt->pattern_list; p; p = p->next) {
switch (p->token) {
case GREP_PATTERN: /* atom */
case GREP_PATTERN_HEAD:
case GREP_PATTERN_BODY:
if (!opt->fixed)
compile_regexp(p, opt);
break;
default:
opt->extended = 1;
break;
}
}
if (!opt->extended)
return;
/* Then bundle them up in an expression.
* A classic recursive descent parser would do.
*/
p = opt->pattern_list;
opt->pattern_expression = compile_pattern_expr(&p);
if (p)
die("incomplete pattern expression: %s", p->pattern);
}
static void free_pattern_expr(struct grep_expr *x)
{
switch (x->node) {
case GREP_NODE_ATOM:
break;
case GREP_NODE_NOT:
free_pattern_expr(x->u.unary);
break;
case GREP_NODE_AND:
case GREP_NODE_OR:
free_pattern_expr(x->u.binary.left);
free_pattern_expr(x->u.binary.right);
break;
}
free(x);
}
void free_grep_patterns(struct grep_opt *opt)
{
struct grep_pat *p, *n;
for (p = opt->pattern_list; p; p = n) {
n = p->next;
switch (p->token) {
case GREP_PATTERN: /* atom */
case GREP_PATTERN_HEAD:
case GREP_PATTERN_BODY:
regfree(&p->regexp);
break;
default:
break;
}
free(p);
}
if (!opt->extended)
return;
free_pattern_expr(opt->pattern_expression);
}
static char *end_of_line(char *cp, unsigned long *left)
{
unsigned long l = *left;
while (l && *cp != '\n') {
l--;
cp++;
}
*left = l;
return cp;
}
static int word_char(char ch)
{
return isalnum(ch) || ch == '_';
}
static void show_line(struct grep_opt *opt, const char *bol, const char *eol,
const char *name, unsigned lno, char sign)
{
if (opt->pathname)
printf("%s%c", name, sign);
if (opt->linenum)
printf("%d%c", lno, sign);
printf("%.*s\n", (int)(eol-bol), bol);
}
/*
* NEEDSWORK: share code with diff.c
*/
#define FIRST_FEW_BYTES 8000
static int buffer_is_binary(const char *ptr, unsigned long size)
{
if (FIRST_FEW_BYTES < size)
size = FIRST_FEW_BYTES;
return !!memchr(ptr, 0, size);
}
static int fixmatch(const char *pattern, char *line, regmatch_t *match)
{
char *hit = strstr(line, pattern);
if (!hit) {
match->rm_so = match->rm_eo = -1;
return REG_NOMATCH;
}
else {
match->rm_so = hit - line;
match->rm_eo = match->rm_so + strlen(pattern);
return 0;
}
}
static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol, enum grep_context ctx)
{
int hit = 0;
int at_true_bol = 1;
regmatch_t pmatch[10];
if ((p->token != GREP_PATTERN) &&
((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD)))
return 0;
again:
if (!opt->fixed) {
regex_t *exp = &p->regexp;
hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
pmatch, 0);
}
else {
hit = !fixmatch(p->pattern, bol, pmatch);
}
if (hit && opt->word_regexp) {
if ((pmatch[0].rm_so < 0) ||
(eol - bol) <= pmatch[0].rm_so ||
(pmatch[0].rm_eo < 0) ||
(eol - bol) < pmatch[0].rm_eo)
die("regexp returned nonsense");
/* Match beginning must be either beginning of the
* line, or at word boundary (i.e. the last char must
* not be a word char). Similarly, match end must be
* either end of the line, or at word boundary
* (i.e. the next char must not be a word char).
*/
if ( ((pmatch[0].rm_so == 0 && at_true_bol) ||
!word_char(bol[pmatch[0].rm_so-1])) &&
((pmatch[0].rm_eo == (eol-bol)) ||
!word_char(bol[pmatch[0].rm_eo])) )
;
else
hit = 0;
if (!hit && pmatch[0].rm_so + bol + 1 < eol) {
/* There could be more than one match on the
* line, and the first match might not be
* strict word match. But later ones could be!
*/
bol = pmatch[0].rm_so + bol + 1;
at_true_bol = 0;
goto again;
}
}
return hit;
}
static int match_expr_eval(struct grep_opt *opt,
struct grep_expr *x,
char *bol, char *eol,
enum grep_context ctx)
{
switch (x->node) {
case GREP_NODE_ATOM:
return match_one_pattern(opt, x->u.atom, bol, eol, ctx);
break;
case GREP_NODE_NOT:
return !match_expr_eval(opt, x->u.unary, bol, eol, ctx);
case GREP_NODE_AND:
return (match_expr_eval(opt, x->u.binary.left, bol, eol, ctx) &&
match_expr_eval(opt, x->u.binary.right, bol, eol, ctx));
case GREP_NODE_OR:
return (match_expr_eval(opt, x->u.binary.left, bol, eol, ctx) ||
match_expr_eval(opt, x->u.binary.right, bol, eol, ctx));
}
die("Unexpected node type (internal error) %d\n", x->node);
}
static int match_expr(struct grep_opt *opt, char *bol, char *eol,
enum grep_context ctx)
{
struct grep_expr *x = opt->pattern_expression;
return match_expr_eval(opt, x, bol, eol, ctx);
}
static int match_line(struct grep_opt *opt, char *bol, char *eol,
enum grep_context ctx)
{
struct grep_pat *p;
if (opt->extended)
return match_expr(opt, bol, eol, ctx);
for (p = opt->pattern_list; p; p = p->next) {
if (match_one_pattern(opt, p, bol, eol, ctx))
return 1;
}
return 0;
}
int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size)
{
char *bol = buf;
unsigned long left = size;
unsigned lno = 1;
struct pre_context_line {
char *bol;
char *eol;
} *prev = NULL, *pcl;
unsigned last_hit = 0;
unsigned last_shown = 0;
int binary_match_only = 0;
const char *hunk_mark = "";
unsigned count = 0;
enum grep_context ctx = GREP_CONTEXT_HEAD;
if (buffer_is_binary(buf, size)) {
switch (opt->binary) {
case GREP_BINARY_DEFAULT:
binary_match_only = 1;
break;
case GREP_BINARY_NOMATCH:
return 0; /* Assume unmatch */
break;
default:
break;
}
}
if (opt->pre_context)
prev = xcalloc(opt->pre_context, sizeof(*prev));
if (opt->pre_context || opt->post_context)
hunk_mark = "--\n";
while (left) {
char *eol, ch;
int hit = 0;
eol = end_of_line(bol, &left);
ch = *eol;
*eol = 0;
if ((ctx == GREP_CONTEXT_HEAD) && (eol == bol))
ctx = GREP_CONTEXT_BODY;
hit = match_line(opt, bol, eol, ctx);
*eol = ch;
/* "grep -v -e foo -e bla" should list lines
* that do not have either, so inversion should
* be done outside.
*/
if (opt->invert)
hit = !hit;
if (opt->unmatch_name_only) {
if (hit)
return 0;
goto next_line;
}
if (hit) {
count++;
if (opt->status_only)
return 1;
if (binary_match_only) {
printf("Binary file %s matches\n", name);
return 1;
}
if (opt->name_only) {
printf("%s\n", name);
return 1;
}
/* Hit at this line. If we haven't shown the
* pre-context lines, we would need to show them.
* When asked to do "count", this still show
* the context which is nonsense, but the user
* deserves to get that ;-).
*/
if (opt->pre_context) {
unsigned from;
if (opt->pre_context < lno)
from = lno - opt->pre_context;
else
from = 1;
if (from <= last_shown)
from = last_shown + 1;
if (last_shown && from != last_shown + 1)
printf(hunk_mark);
while (from < lno) {
pcl = &prev[lno-from-1];
show_line(opt, pcl->bol, pcl->eol,
name, from, '-');
from++;
}
last_shown = lno-1;
}
if (last_shown && lno != last_shown + 1)
printf(hunk_mark);
if (!opt->count)
show_line(opt, bol, eol, name, lno, ':');
last_shown = last_hit = lno;
}
else if (last_hit &&
lno <= last_hit + opt->post_context) {
/* If the last hit is within the post context,
* we need to show this line.
*/
if (last_shown && lno != last_shown + 1)
printf(hunk_mark);
show_line(opt, bol, eol, name, lno, '-');
last_shown = lno;
}
if (opt->pre_context) {
memmove(prev+1, prev,
(opt->pre_context-1) * sizeof(*prev));
prev->bol = bol;
prev->eol = eol;
}
next_line:
bol = eol + 1;
if (!left)
break;
left--;
lno++;
}
free(prev);
if (opt->status_only)
return 0;
if (opt->unmatch_name_only) {
/* We did not see any hit, so we want to show this */
printf("%s\n", name);
return 1;
}
/* NEEDSWORK:
* The real "grep -c foo *.c" gives many "bar.c:0" lines,
* which feels mostly useless but sometimes useful. Maybe
* make it another option? For now suppress them.
*/
if (opt->count && count)
printf("%s:%u\n", name, count);
return !!last_hit;
}

79
grep.h Normal file
View File

@ -0,0 +1,79 @@
#ifndef GREP_H
#define GREP_H
enum grep_pat_token {
GREP_PATTERN,
GREP_PATTERN_HEAD,
GREP_PATTERN_BODY,
GREP_AND,
GREP_OPEN_PAREN,
GREP_CLOSE_PAREN,
GREP_NOT,
GREP_OR,
};
enum grep_context {
GREP_CONTEXT_HEAD,
GREP_CONTEXT_BODY,
};
struct grep_pat {
struct grep_pat *next;
const char *origin;
int no;
enum grep_pat_token token;
const char *pattern;
regex_t regexp;
};
enum grep_expr_node {
GREP_NODE_ATOM,
GREP_NODE_NOT,
GREP_NODE_AND,
GREP_NODE_OR,
};
struct grep_expr {
enum grep_expr_node node;
union {
struct grep_pat *atom;
struct grep_expr *unary;
struct {
struct grep_expr *left;
struct grep_expr *right;
} binary;
} u;
};
struct grep_opt {
struct grep_pat *pattern_list;
struct grep_pat **pattern_tail;
struct grep_expr *pattern_expression;
int prefix_length;
regex_t regexp;
unsigned linenum:1;
unsigned invert:1;
unsigned status_only:1;
unsigned name_only:1;
unsigned unmatch_name_only:1;
unsigned count:1;
unsigned word_regexp:1;
unsigned fixed:1;
#define GREP_BINARY_DEFAULT 0
#define GREP_BINARY_NOMATCH 1
#define GREP_BINARY_TEXT 2
unsigned binary:2;
unsigned extended:1;
unsigned relative:1;
unsigned pathname:1;
int regflags;
unsigned pre_context;
unsigned post_context;
};
extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
extern void compile_grep_patterns(struct grep_opt *opt);
extern void free_grep_patterns(struct grep_opt *opt);
extern int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size);
#endif

4
http.h
View File

@ -22,6 +22,10 @@
#define NO_CURL_EASY_DUPHANDLE #define NO_CURL_EASY_DUPHANDLE
#endif #endif
#if LIBCURL_VERSION_NUM < 0x070a03
#define CURLE_HTTP_RETURNED_ERROR CURLE_HTTP_NOT_FOUND
#endif
struct slot_results struct slot_results
{ {
CURLcode curl_result; CURLcode curl_result;

82
interpolate.c Normal file
View File

@ -0,0 +1,82 @@
/*
* Copyright 2006 Jon Loeliger
*/
#include <string.h>
#include "interpolate.h"
/*
* Convert a NUL-terminated string in buffer orig
* into the supplied buffer, result, whose length is reslen,
* performing substitutions on %-named sub-strings from
* the table, interps, with ninterps entries.
*
* Example interps:
* {
* { "%H", "example.org"},
* { "%port", "123"},
* { "%%", "%"},
* }
*
* Returns 1 on a successful substitution pass that fits in result,
* Returns 0 on a failed or overflowing substitution pass.
*/
int interpolate(char *result, int reslen,
const char *orig,
const struct interp *interps, int ninterps)
{
const char *src = orig;
char *dest = result;
int newlen = 0;
char *name, *value;
int namelen, valuelen;
int i;
char c;
memset(result, 0, reslen);
while ((c = *src) && newlen < reslen - 1) {
if (c == '%') {
/* Try to match an interpolation string. */
for (i = 0; i < ninterps; i++) {
name = interps[i].name;
namelen = strlen(name);
if (strncmp(src, name, namelen) == 0) {
break;
}
}
/* Check for valid interpolation. */
if (i < ninterps) {
value = interps[i].value;
valuelen = strlen(value);
if (newlen + valuelen < reslen - 1) {
/* Substitute. */
strncpy(dest, value, valuelen);
newlen += valuelen;
dest += valuelen;
src += namelen;
} else {
/* Something's not fitting. */
return 0;
}
} else {
/* Skip bogus interpolation. */
*dest++ = *src++;
newlen++;
}
} else {
/* Straight copy one non-interpolation character. */
*dest++ = *src++;
newlen++;
}
}
return newlen < reslen - 1;
}

23
interpolate.h Normal file
View File

@ -0,0 +1,23 @@
/*
* Copyright 2006 Jon Loeliger
*/
#ifndef INTERPOLATE_H
#define INTERPOLATE_H
/*
* Convert a NUL-terminated string in buffer orig,
* performing substitutions on %-named sub-strings from
* the interpretation table.
*/
struct interp {
char *name;
char *value;
};
extern int interpolate(char *result, int reslen,
const char *orig,
const struct interp *interps, int ninterps);
#endif /* INTERPOLATE_H */

View File

@ -42,16 +42,16 @@ static int verify_packfile(struct packed_git *p)
*/ */
for (i = err = 0; i < nr_objects; i++) { for (i = err = 0; i < nr_objects; i++) {
unsigned char sha1[20]; unsigned char sha1[20];
struct pack_entry e;
void *data; void *data;
char type[20]; char type[20];
unsigned long size; unsigned long size, offset;
if (nth_packed_object_sha1(p, i, sha1)) if (nth_packed_object_sha1(p, i, sha1))
die("internal error pack-check nth-packed-object"); die("internal error pack-check nth-packed-object");
if (!find_pack_entry_one(sha1, &e, p)) offset = find_pack_entry_one(sha1, p);
if (!offset)
die("internal error pack-check find-pack-entry-one"); die("internal error pack-check find-pack-entry-one");
data = unpack_entry_gently(&e, type, &size); data = unpack_entry_gently(p, offset, type, &size);
if (!data) { if (!data) {
err = error("cannot unpack %s from %s", err = error("cannot unpack %s from %s",
sha1_to_hex(sha1), p->pack_name); sha1_to_hex(sha1), p->pack_name);
@ -84,25 +84,26 @@ static void show_pack_info(struct packed_git *p)
for (i = 0; i < nr_objects; i++) { for (i = 0; i < nr_objects; i++) {
unsigned char sha1[20], base_sha1[20]; unsigned char sha1[20], base_sha1[20];
struct pack_entry e;
char type[20]; char type[20];
unsigned long size; unsigned long size;
unsigned long store_size; unsigned long store_size;
unsigned long offset;
unsigned int delta_chain_length; unsigned int delta_chain_length;
if (nth_packed_object_sha1(p, i, sha1)) if (nth_packed_object_sha1(p, i, sha1))
die("internal error pack-check nth-packed-object"); die("internal error pack-check nth-packed-object");
if (!find_pack_entry_one(sha1, &e, p)) offset = find_pack_entry_one(sha1, p);
if (!offset)
die("internal error pack-check find-pack-entry-one"); die("internal error pack-check find-pack-entry-one");
packed_object_info_detail(&e, type, &size, &store_size, packed_object_info_detail(p, offset, type, &size, &store_size,
&delta_chain_length, &delta_chain_length,
base_sha1); base_sha1);
printf("%s ", sha1_to_hex(sha1)); printf("%s ", sha1_to_hex(sha1));
if (!delta_chain_length) if (!delta_chain_length)
printf("%-6s %lu %u\n", type, size, e.offset); printf("%-6s %lu %lu\n", type, size, offset);
else { else {
printf("%-6s %lu %u %u %s\n", type, size, e.offset, printf("%-6s %lu %lu %u %s\n", type, size, offset,
delta_chain_length, sha1_to_hex(base_sha1)); delta_chain_length, sha1_to_hex(base_sha1));
if (delta_chain_length < MAX_CHAIN) if (delta_chain_length < MAX_CHAIN)
chain_histogram[delta_chain_length]++; chain_histogram[delta_chain_length]++;

2
pack.h
View File

@ -7,7 +7,7 @@
* Packed object header * Packed object header
*/ */
#define PACK_SIGNATURE 0x5041434b /* "PACK" */ #define PACK_SIGNATURE 0x5041434b /* "PACK" */
#define PACK_VERSION 2 #define PACK_VERSION 3
#define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3)) #define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3))
struct pack_header { struct pack_header {
unsigned int hdr_signature; unsigned int hdr_signature;

View File

@ -347,11 +347,13 @@ int add_file_to_index(const char *path, int verbose)
ce->ce_mode = create_ce_mode(st.st_mode); ce->ce_mode = create_ce_mode(st.st_mode);
if (!trust_executable_bit) { if (!trust_executable_bit) {
/* If there is an existing entry, pick the mode bits /* If there is an existing entry, pick the mode bits
* from it. * from it, otherwise force to 644.
*/ */
int pos = cache_name_pos(path, namelen); int pos = cache_name_pos(path, namelen);
if (pos >= 0) if (pos >= 0)
ce->ce_mode = active_cache[pos]->ce_mode; ce->ce_mode = active_cache[pos]->ce_mode;
else
ce->ce_mode = create_ce_mode(S_IFREG | 0644);
} }
if (index_path(ce->sha1, path, &st, 1)) if (index_path(ce->sha1, path, &st, 1))

View File

@ -2,6 +2,8 @@
#include "refs.h" #include "refs.h"
#include "pkt-line.h" #include "pkt-line.h"
#include "run-command.h" #include "run-command.h"
#include "commit.h"
#include "object.h"
static const char receive_pack_usage[] = "git-receive-pack <git-dir>"; static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
@ -94,6 +96,21 @@ static int update(struct command *cmd)
return error("unpack should have generated %s, " return error("unpack should have generated %s, "
"but I can't find it!", new_hex); "but I can't find it!", new_hex);
} }
if (deny_non_fast_forwards && !is_null_sha1(old_sha1)) {
struct commit *old_commit, *new_commit;
struct commit_list *bases, *ent;
old_commit = (struct commit *)parse_object(old_sha1);
new_commit = (struct commit *)parse_object(new_sha1);
bases = get_merge_bases(old_commit, new_commit, 1);
for (ent = bases; ent; ent = ent->next)
if (!hashcmp(old_sha1, ent->item->object.sha1))
break;
free_commit_list(bases);
if (!ent)
return error("denying non-fast forward;"
" you should pull first");
}
if (run_update_hook(name, old_hex, new_hex)) { if (run_update_hook(name, old_hex, new_hex)) {
cmd->error_string = "hook declined"; cmd->error_string = "hook declined";
return error("hook declined to update %s", name); return error("hook declined to update %s", name);

View File

@ -6,6 +6,8 @@
#include "diff.h" #include "diff.h"
#include "refs.h" #include "refs.h"
#include "revision.h" #include "revision.h"
#include <regex.h>
#include "grep.h"
static char *path_name(struct name_path *path, const char *name) static char *path_name(struct name_path *path, const char *name)
{ {
@ -672,6 +674,42 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
return 0; return 0;
} }
static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token what)
{
if (!revs->grep_filter) {
struct grep_opt *opt = xcalloc(1, sizeof(*opt));
opt->status_only = 1;
opt->pattern_tail = &(opt->pattern_list);
opt->regflags = REG_NEWLINE;
revs->grep_filter = opt;
}
append_grep_pattern(revs->grep_filter, ptn,
"command line", 0, what);
}
static void add_header_grep(struct rev_info *revs, const char *field, const char *pattern)
{
char *pat;
const char *prefix;
int patlen, fldlen;
fldlen = strlen(field);
patlen = strlen(pattern);
pat = xmalloc(patlen + fldlen + 10);
prefix = ".*";
if (*pattern == '^') {
prefix = "";
pattern++;
}
sprintf(pat, "^%s %s%s", field, prefix, pattern);
add_grep(revs, pat, GREP_PATTERN_HEAD);
}
static void add_message_grep(struct rev_info *revs, const char *pattern)
{
add_grep(revs, pattern, GREP_PATTERN_BODY);
}
static void add_ignore_packed(struct rev_info *revs, const char *name) static void add_ignore_packed(struct rev_info *revs, const char *name)
{ {
int num = ++revs->num_ignore_packed; int num = ++revs->num_ignore_packed;
@ -913,6 +951,23 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
revs->relative_date = 1; revs->relative_date = 1;
continue; continue;
} }
/*
* Grepping the commit log
*/
if (!strncmp(arg, "--author=", 9)) {
add_header_grep(revs, "author", arg+9);
continue;
}
if (!strncmp(arg, "--committer=", 12)) {
add_header_grep(revs, "committer", arg+12);
continue;
}
if (!strncmp(arg, "--grep=", 7)) {
add_message_grep(revs, arg+7);
continue;
}
opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i); opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
if (opts > 0) { if (opts > 0) {
revs->diff = 1; revs->diff = 1;
@ -973,6 +1028,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
if (diff_setup_done(&revs->diffopt) < 0) if (diff_setup_done(&revs->diffopt) < 0)
die("diff_setup_done failed"); die("diff_setup_done failed");
if (revs->grep_filter)
compile_grep_patterns(revs->grep_filter);
return left; return left;
} }
@ -1045,6 +1103,15 @@ static void mark_boundary_to_show(struct commit *commit)
} }
} }
static int commit_match(struct commit *commit, struct rev_info *opt)
{
if (!opt->grep_filter)
return 1;
return grep_buffer(opt->grep_filter,
NULL, /* we say nothing, not even filename */
commit->buffer, strlen(commit->buffer));
}
struct commit *get_revision(struct rev_info *revs) struct commit *get_revision(struct rev_info *revs)
{ {
struct commit_list *list = revs->commits; struct commit_list *list = revs->commits;
@ -1105,6 +1172,8 @@ struct commit *get_revision(struct rev_info *revs)
if (revs->no_merges && if (revs->no_merges &&
commit->parents && commit->parents->next) commit->parents && commit->parents->next)
continue; continue;
if (!commit_match(commit, revs))
continue;
if (revs->prune_fn && revs->dense) { if (revs->prune_fn && revs->dense) {
/* Commit without changes? */ /* Commit without changes? */
if (!(commit->object.flags & TREECHANGE)) { if (!(commit->object.flags & TREECHANGE)) {

View File

@ -71,6 +71,9 @@ struct rev_info {
const char *add_signoff; const char *add_signoff;
const char *extra_headers; const char *extra_headers;
/* Filter by commit log message */
struct grep_opt *grep_filter;
/* special limits */ /* special limits */
int max_count; int max_count;
unsigned long max_age; unsigned long max_age;

View File

@ -244,6 +244,8 @@ int check_repository_format_version(const char *var, const char *value)
repository_format_version = git_config_int(var, value); repository_format_version = git_config_int(var, value);
else if (strcmp(var, "core.sharedrepository") == 0) else if (strcmp(var, "core.sharedrepository") == 0)
shared_repository = git_config_perm(var, value); shared_repository = git_config_perm(var, value);
else if (strcmp(var, "receive.denynonfastforwards") == 0)
deny_non_fast_forwards = git_config_bool(var, value);
return 0; return 0;
} }

View File

@ -26,44 +26,40 @@ const unsigned char null_sha1[20];
static unsigned int sha1_file_open_flag = O_NOATIME; static unsigned int sha1_file_open_flag = O_NOATIME;
static inline unsigned int hexval(unsigned int c) signed char hexval_table[256] = {
{ -1, -1, -1, -1, -1, -1, -1, -1, /* 00-07 */
static signed char val[256] = { -1, -1, -1, -1, -1, -1, -1, -1, /* 08-0f */
-1, -1, -1, -1, -1, -1, -1, -1, /* 00-07 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 10-17 */
-1, -1, -1, -1, -1, -1, -1, -1, /* 08-0f */ -1, -1, -1, -1, -1, -1, -1, -1, /* 18-1f */
-1, -1, -1, -1, -1, -1, -1, -1, /* 10-17 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 20-27 */
-1, -1, -1, -1, -1, -1, -1, -1, /* 18-1f */ -1, -1, -1, -1, -1, -1, -1, -1, /* 28-2f */
-1, -1, -1, -1, -1, -1, -1, -1, /* 20-27 */ 0, 1, 2, 3, 4, 5, 6, 7, /* 30-37 */
-1, -1, -1, -1, -1, -1, -1, -1, /* 28-2f */ 8, 9, -1, -1, -1, -1, -1, -1, /* 38-3f */
0, 1, 2, 3, 4, 5, 6, 7, /* 30-37 */ -1, 10, 11, 12, 13, 14, 15, -1, /* 40-47 */
8, 9, -1, -1, -1, -1, -1, -1, /* 38-3f */ -1, -1, -1, -1, -1, -1, -1, -1, /* 48-4f */
-1, 10, 11, 12, 13, 14, 15, -1, /* 40-47 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 50-57 */
-1, -1, -1, -1, -1, -1, -1, -1, /* 48-4f */ -1, -1, -1, -1, -1, -1, -1, -1, /* 58-5f */
-1, -1, -1, -1, -1, -1, -1, -1, /* 50-57 */ -1, 10, 11, 12, 13, 14, 15, -1, /* 60-67 */
-1, -1, -1, -1, -1, -1, -1, -1, /* 58-5f */ -1, -1, -1, -1, -1, -1, -1, -1, /* 68-67 */
-1, 10, 11, 12, 13, 14, 15, -1, /* 60-67 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 70-77 */
-1, -1, -1, -1, -1, -1, -1, -1, /* 68-67 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 78-7f */
-1, -1, -1, -1, -1, -1, -1, -1, /* 70-77 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 80-87 */
-1, -1, -1, -1, -1, -1, -1, -1, /* 78-7f */ -1, -1, -1, -1, -1, -1, -1, -1, /* 88-8f */
-1, -1, -1, -1, -1, -1, -1, -1, /* 80-87 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 90-97 */
-1, -1, -1, -1, -1, -1, -1, -1, /* 88-8f */ -1, -1, -1, -1, -1, -1, -1, -1, /* 98-9f */
-1, -1, -1, -1, -1, -1, -1, -1, /* 90-97 */ -1, -1, -1, -1, -1, -1, -1, -1, /* a0-a7 */
-1, -1, -1, -1, -1, -1, -1, -1, /* 98-9f */ -1, -1, -1, -1, -1, -1, -1, -1, /* a8-af */
-1, -1, -1, -1, -1, -1, -1, -1, /* a0-a7 */ -1, -1, -1, -1, -1, -1, -1, -1, /* b0-b7 */
-1, -1, -1, -1, -1, -1, -1, -1, /* a8-af */ -1, -1, -1, -1, -1, -1, -1, -1, /* b8-bf */
-1, -1, -1, -1, -1, -1, -1, -1, /* b0-b7 */ -1, -1, -1, -1, -1, -1, -1, -1, /* c0-c7 */
-1, -1, -1, -1, -1, -1, -1, -1, /* b8-bf */ -1, -1, -1, -1, -1, -1, -1, -1, /* c8-cf */
-1, -1, -1, -1, -1, -1, -1, -1, /* c0-c7 */ -1, -1, -1, -1, -1, -1, -1, -1, /* d0-d7 */
-1, -1, -1, -1, -1, -1, -1, -1, /* c8-cf */ -1, -1, -1, -1, -1, -1, -1, -1, /* d8-df */
-1, -1, -1, -1, -1, -1, -1, -1, /* d0-d7 */ -1, -1, -1, -1, -1, -1, -1, -1, /* e0-e7 */
-1, -1, -1, -1, -1, -1, -1, -1, /* d8-df */ -1, -1, -1, -1, -1, -1, -1, -1, /* e8-ef */
-1, -1, -1, -1, -1, -1, -1, -1, /* e0-e7 */ -1, -1, -1, -1, -1, -1, -1, -1, /* f0-f7 */
-1, -1, -1, -1, -1, -1, -1, -1, /* e8-ef */ -1, -1, -1, -1, -1, -1, -1, -1, /* f8-ff */
-1, -1, -1, -1, -1, -1, -1, -1, /* f0-f7 */ };
-1, -1, -1, -1, -1, -1, -1, -1, /* f8-ff */
};
return val[c];
}
int get_sha1_hex(const char *hex, unsigned char *sha1) int get_sha1_hex(const char *hex, unsigned char *sha1)
{ {
@ -888,33 +884,32 @@ void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned l
} }
/* forward declaration for a mutually recursive function */ /* forward declaration for a mutually recursive function */
static int packed_object_info(struct pack_entry *entry, static int packed_object_info(struct packed_git *p, unsigned long offset,
char *type, unsigned long *sizep); char *type, unsigned long *sizep);
static int packed_delta_info(unsigned char *base_sha1, static int packed_delta_info(struct packed_git *p,
unsigned long delta_size, unsigned long offset,
unsigned long left,
char *type, char *type,
unsigned long *sizep, unsigned long *sizep)
struct packed_git *p)
{ {
struct pack_entry base_ent; unsigned long base_offset;
unsigned char *base_sha1 = (unsigned char *) p->pack_base + offset;
if (left < 20) if (p->pack_size < offset + 20)
die("truncated pack file"); die("truncated pack file");
/* The base entry _must_ be in the same pack */ /* The base entry _must_ be in the same pack */
if (!find_pack_entry_one(base_sha1, &base_ent, p)) base_offset = find_pack_entry_one(base_sha1, p);
if (!base_offset)
die("failed to find delta-pack base object %s", die("failed to find delta-pack base object %s",
sha1_to_hex(base_sha1)); sha1_to_hex(base_sha1));
offset += 20;
/* We choose to only get the type of the base object and /* We choose to only get the type of the base object and
* ignore potentially corrupt pack file that expects the delta * ignore potentially corrupt pack file that expects the delta
* based on a base with a wrong size. This saves tons of * based on a base with a wrong size. This saves tons of
* inflate() calls. * inflate() calls.
*/ */
if (packed_object_info(p, base_offset, type, NULL))
if (packed_object_info(&base_ent, type, NULL))
die("cannot get info for delta-pack base"); die("cannot get info for delta-pack base");
if (sizep) { if (sizep) {
@ -926,8 +921,8 @@ static int packed_delta_info(unsigned char *base_sha1,
memset(&stream, 0, sizeof(stream)); memset(&stream, 0, sizeof(stream));
data = stream.next_in = base_sha1 + 20; stream.next_in = (unsigned char *) p->pack_base + offset;
stream.avail_in = left - 20; stream.avail_in = p->pack_size - offset;
stream.next_out = delta_head; stream.next_out = delta_head;
stream.avail_out = sizeof(delta_head); stream.avail_out = sizeof(delta_head);
@ -989,75 +984,60 @@ int check_reuse_pack_delta(struct packed_git *p, unsigned long offset,
return status; return status;
} }
void packed_object_info_detail(struct pack_entry *e, void packed_object_info_detail(struct packed_git *p,
unsigned long offset,
char *type, char *type,
unsigned long *size, unsigned long *size,
unsigned long *store_size, unsigned long *store_size,
unsigned int *delta_chain_length, unsigned int *delta_chain_length,
unsigned char *base_sha1) unsigned char *base_sha1)
{ {
struct packed_git *p = e->p; unsigned long val;
unsigned long offset; unsigned char *next_sha1;
unsigned char *pack;
enum object_type kind; enum object_type kind;
offset = unpack_object_header(p, e->offset, &kind, size); *delta_chain_length = 0;
pack = (unsigned char *) p->pack_base + offset; offset = unpack_object_header(p, offset, &kind, size);
if (kind != OBJ_DELTA)
*delta_chain_length = 0;
else {
unsigned int chain_length = 0;
if (p->pack_size <= offset + 20)
die("pack file %s records an incomplete delta base",
p->pack_name);
hashcpy(base_sha1, pack);
do {
struct pack_entry base_ent;
unsigned long junk;
find_pack_entry_one(pack, &base_ent, p); for (;;) {
offset = unpack_object_header(p, base_ent.offset, switch (kind) {
&kind, &junk); default:
pack = (unsigned char *) p->pack_base + offset; die("corrupted pack file %s containing object of kind %d",
chain_length++; p->pack_name, kind);
} while (kind == OBJ_DELTA); case OBJ_COMMIT:
*delta_chain_length = chain_length; case OBJ_TREE:
case OBJ_BLOB:
case OBJ_TAG:
strcpy(type, type_names[kind]);
*store_size = 0; /* notyet */
return;
case OBJ_DELTA:
if (p->pack_size <= offset + 20)
die("pack file %s records an incomplete delta base",
p->pack_name);
next_sha1 = (unsigned char *) p->pack_base + offset;
if (*delta_chain_length == 0)
hashcpy(base_sha1, next_sha1);
offset = find_pack_entry_one(next_sha1, p);
break;
}
offset = unpack_object_header(p, offset, &kind, &val);
(*delta_chain_length)++;
} }
switch (kind) {
case OBJ_COMMIT:
case OBJ_TREE:
case OBJ_BLOB:
case OBJ_TAG:
strcpy(type, type_names[kind]);
break;
default:
die("corrupted pack file %s containing object of kind %d",
p->pack_name, kind);
}
*store_size = 0; /* notyet */
} }
static int packed_object_info(struct pack_entry *entry, static int packed_object_info(struct packed_git *p, unsigned long offset,
char *type, unsigned long *sizep) char *type, unsigned long *sizep)
{ {
struct packed_git *p = entry->p; unsigned long size;
unsigned long offset, size, left;
unsigned char *pack;
enum object_type kind; enum object_type kind;
int retval;
if (use_packed_git(p)) offset = unpack_object_header(p, offset, &kind, &size);
die("cannot map packed file");
offset = unpack_object_header(p, entry->offset, &kind, &size); if (kind == OBJ_DELTA)
pack = (unsigned char *) p->pack_base + offset; return packed_delta_info(p, offset, type, sizep);
left = p->pack_size - offset;
switch (kind) { switch (kind) {
case OBJ_DELTA:
retval = packed_delta_info(pack, size, left, type, sizep, p);
unuse_packed_git(p);
return retval;
case OBJ_COMMIT: case OBJ_COMMIT:
case OBJ_TREE: case OBJ_TREE:
case OBJ_BLOB: case OBJ_BLOB:
@ -1070,7 +1050,6 @@ static int packed_object_info(struct pack_entry *entry,
} }
if (sizep) if (sizep)
*sizep = size; *sizep = size;
unuse_packed_git(p);
return 0; return 0;
} }
@ -1107,25 +1086,26 @@ static void *unpack_delta_entry(struct packed_git *p,
char *type, char *type,
unsigned long *sizep) unsigned long *sizep)
{ {
struct pack_entry base_ent;
void *delta_data, *result, *base; void *delta_data, *result, *base;
unsigned long result_size, base_size; unsigned long result_size, base_size, base_offset;
unsigned char* base_sha1; unsigned char *base_sha1;
if ((offset + 20) >= p->pack_size) if (p->pack_size < offset + 20)
die("truncated pack file"); die("truncated pack file");
/* The base entry _must_ be in the same pack */ /* The base entry _must_ be in the same pack */
base_sha1 = (unsigned char*)p->pack_base + offset; base_sha1 = (unsigned char*)p->pack_base + offset;
if (!find_pack_entry_one(base_sha1, &base_ent, p)) base_offset = find_pack_entry_one(base_sha1, p);
if (!base_offset)
die("failed to find delta-pack base object %s", die("failed to find delta-pack base object %s",
sha1_to_hex(base_sha1)); sha1_to_hex(base_sha1));
base = unpack_entry_gently(&base_ent, type, &base_size); offset += 20;
if (!base)
die("failed to read delta-pack base object %s",
sha1_to_hex(base_sha1));
delta_data = unpack_compressed_entry(p, offset + 20, delta_size); base = unpack_entry_gently(p, base_offset, type, &base_size);
if (!base)
die("failed to read delta base object at %lu from %s",
base_offset, p->pack_name);
delta_data = unpack_compressed_entry(p, offset, delta_size);
result = patch_delta(base, base_size, result = patch_delta(base, base_size,
delta_data, delta_size, delta_data, delta_size,
&result_size); &result_size);
@ -1145,7 +1125,7 @@ static void *unpack_entry(struct pack_entry *entry,
if (use_packed_git(p)) if (use_packed_git(p))
die("cannot map packed file"); die("cannot map packed file");
retval = unpack_entry_gently(entry, type, sizep); retval = unpack_entry_gently(p, entry->offset, type, sizep);
unuse_packed_git(p); unuse_packed_git(p);
if (!retval) if (!retval)
die("corrupted pack file %s", p->pack_name); die("corrupted pack file %s", p->pack_name);
@ -1153,14 +1133,13 @@ static void *unpack_entry(struct pack_entry *entry,
} }
/* The caller is responsible for use_packed_git()/unuse_packed_git() pair */ /* The caller is responsible for use_packed_git()/unuse_packed_git() pair */
void *unpack_entry_gently(struct pack_entry *entry, void *unpack_entry_gently(struct packed_git *p, unsigned long offset,
char *type, unsigned long *sizep) char *type, unsigned long *sizep)
{ {
struct packed_git *p = entry->p; unsigned long size;
unsigned long offset, size;
enum object_type kind; enum object_type kind;
offset = unpack_object_header(p, entry->offset, &kind, &size); offset = unpack_object_header(p, offset, &kind, &size);
switch (kind) { switch (kind) {
case OBJ_DELTA: case OBJ_DELTA:
return unpack_delta_entry(p, offset, size, type, sizep); return unpack_delta_entry(p, offset, size, type, sizep);
@ -1192,8 +1171,8 @@ int nth_packed_object_sha1(const struct packed_git *p, int n,
return 0; return 0;
} }
int find_pack_entry_one(const unsigned char *sha1, unsigned long find_pack_entry_one(const unsigned char *sha1,
struct pack_entry *e, struct packed_git *p) struct packed_git *p)
{ {
unsigned int *level1_ofs = p->index_base; unsigned int *level1_ofs = p->index_base;
int hi = ntohl(level1_ofs[*sha1]); int hi = ntohl(level1_ofs[*sha1]);
@ -1203,12 +1182,8 @@ int find_pack_entry_one(const unsigned char *sha1,
do { do {
int mi = (lo + hi) / 2; int mi = (lo + hi) / 2;
int cmp = hashcmp((unsigned char *)index + (24 * mi) + 4, sha1); int cmp = hashcmp((unsigned char *)index + (24 * mi) + 4, sha1);
if (!cmp) { if (!cmp)
e->offset = ntohl(*((unsigned int *) ((char *) index + (24 * mi)))); return ntohl(*((unsigned int *) ((char *) index + (24 * mi))));
hashcpy(e->sha1, sha1);
e->p = p;
return 1;
}
if (cmp > 0) if (cmp > 0)
hi = mi; hi = mi;
else else
@ -1220,6 +1195,8 @@ int find_pack_entry_one(const unsigned char *sha1,
static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed) static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed)
{ {
struct packed_git *p; struct packed_git *p;
unsigned long offset;
prepare_packed_git(); prepare_packed_git();
for (p = packed_git; p; p = p->next) { for (p = packed_git; p; p = p->next) {
@ -1231,8 +1208,13 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, cons
if (*ig) if (*ig)
continue; continue;
} }
if (find_pack_entry_one(sha1, e, p)) offset = find_pack_entry_one(sha1, p);
if (offset) {
e->offset = offset;
e->p = p;
hashcpy(e->sha1, sha1);
return 1; return 1;
}
} }
return 0; return 0;
} }
@ -1241,10 +1223,9 @@ struct packed_git *find_sha1_pack(const unsigned char *sha1,
struct packed_git *packs) struct packed_git *packs)
{ {
struct packed_git *p; struct packed_git *p;
struct pack_entry e;
for (p = packs; p; p = p->next) { for (p = packs; p; p = p->next) {
if (find_pack_entry_one(sha1, &e, p)) if (find_pack_entry_one(sha1, p))
return p; return p;
} }
return NULL; return NULL;
@ -1263,12 +1244,16 @@ int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep
if (!map) { if (!map) {
struct pack_entry e; struct pack_entry e;
if (find_pack_entry(sha1, &e, NULL)) if (!find_pack_entry(sha1, &e, NULL)) {
return packed_object_info(&e, type, sizep); reprepare_packed_git();
reprepare_packed_git(); if (!find_pack_entry(sha1, &e, NULL))
if (find_pack_entry(sha1, &e, NULL)) return error("unable to find %s", sha1_to_hex(sha1));
return packed_object_info(&e, type, sizep); }
return error("unable to find %s", sha1_to_hex(sha1)); if (use_packed_git(e.p))
die("cannot map packed file");
status = packed_object_info(e.p, e.offset, type, sizep);
unuse_packed_git(e.p);
return status;
} }
if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0) if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
status = error("unable to unpack %s header", status = error("unable to unpack %s header",

View File

@ -431,6 +431,26 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
return 0; return 0;
} }
static int get_describe_name(const char *name, int len, unsigned char *sha1)
{
const char *cp;
for (cp = name + len - 1; name + 2 <= cp; cp--) {
char ch = *cp;
if (hexval(ch) & ~0377) {
/* We must be looking at g in "SOMETHING-g"
* for it to be describe output.
*/
if (ch == 'g' && cp[-1] == '-') {
cp++;
len -= cp - name;
return get_short_sha1(cp, len, sha1, 1);
}
}
}
return -1;
}
static int get_sha1_1(const char *name, int len, unsigned char *sha1) static int get_sha1_1(const char *name, int len, unsigned char *sha1)
{ {
int ret, has_suffix; int ret, has_suffix;
@ -472,6 +492,12 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
ret = get_sha1_basic(name, len, sha1); ret = get_sha1_basic(name, len, sha1);
if (!ret) if (!ret)
return 0; return 0;
/* It could be describe output that is "SOMETHING-gXXXX" */
ret = get_describe_name(name, len, sha1);
if (!ret)
return 0;
return get_short_sha1(name, len, sha1, 0); return get_short_sha1(name, len, sha1, 0);
} }

View File

@ -61,4 +61,16 @@ test_expect_success \
test -f .git/logs/refs/heads/g/h/i && test -f .git/logs/refs/heads/g/h/i &&
diff expect .git/logs/refs/heads/g/h/i' diff expect .git/logs/refs/heads/g/h/i'
test_expect_success \
'git branch j/k should work after branch j has been deleted' \
'git-branch j &&
git-branch -d j &&
git-branch j/k'
test_expect_success \
'git branch l should work after branch l/m has been deleted' \
'git-branch l/m &&
git-branch -d l/m &&
git-branch l'
test_done test_done

View File

@ -19,4 +19,26 @@ test_expect_success \
'Test that "git-add -- -q" works' \ 'Test that "git-add -- -q" works' \
'touch -- -q && git-add -- -q' 'touch -- -q && git-add -- -q'
test_expect_success \
'git-add: Test that executable bit is not used if core.filemode=0' \
'git repo-config core.filemode 0 &&
echo foo >xfoo1 &&
chmod 755 xfoo1 &&
git-add xfoo1 &&
case "`git-ls-files --stage xfoo1`" in
100644" "*xfoo1) echo ok;;
*) echo fail; git-ls-files --stage xfoo1; exit 1;;
esac'
test_expect_success \
'git-update-index --add: Test that executable bit is not used...' \
'git repo-config core.filemode 0 &&
echo foo >xfoo2 &&
chmod 755 xfoo2 &&
git-update-index --add xfoo2 &&
case "`git-ls-files --stage xfoo2`" in
100644" "*xfoo2) echo ok;;
*) echo fail; git-ls-files --stage xfoo2; exit 1;;
esac'
test_done test_done

View File

@ -64,4 +64,18 @@ test_expect_success \
cmp victim/.git/refs/heads/master .git/refs/heads/master cmp victim/.git/refs/heads/master .git/refs/heads/master
' '
unset GIT_CONFIG GIT_CONFIG_LOCAL
HOME=`pwd`/no-such-directory
export HOME ;# this way we force the victim/.git/config to be used.
test_expect_success \
'pushing with --force should be denied with denyNonFastforwards' '
cd victim &&
git-repo-config receive.denyNonFastforwards true &&
cd .. &&
git-update-ref refs/heads/master master^ &&
git-send-pack --force ./victim/.git/ master &&
! diff -u .git/refs/heads/master victim/.git/refs/heads/master
'
test_done test_done

69
t/t5510-fetch.sh Executable file
View File

@ -0,0 +1,69 @@
#!/bin/sh
# Copyright (c) 2006, Junio C Hamano.
test_description='Per branch config variables affects "git fetch".
'
. ./test-lib.sh
D=`pwd`
test_expect_success setup '
echo >file original &&
git add file &&
git commit -a -m original'
test_expect_success "clone and setup child repos" '
git clone . one &&
cd one &&
echo >file updated by one &&
git commit -a -m "updated by one" &&
cd .. &&
git clone . two &&
cd two &&
git repo-config branch.master.remote one &&
{
echo "URL: ../one/.git/"
echo "Pull: refs/heads/master:refs/heads/one"
} >.git/remotes/one
cd .. &&
git clone . three &&
cd three &&
git repo-config branch.master.remote two &&
git repo-config branch.master.merge refs/heads/one &&
{
echo "URL: ../two/.git/"
echo "Pull: refs/heads/master:refs/heads/two"
echo "Pull: refs/heads/one:refs/heads/one"
} >.git/remotes/two
'
test_expect_success "fetch test" '
cd "$D" &&
echo >file updated by origin &&
git commit -a -m "updated by origin" &&
cd two &&
git fetch &&
test -f .git/refs/heads/one &&
mine=`git rev-parse refs/heads/one` &&
his=`cd ../one && git rev-parse refs/heads/master` &&
test "z$mine" = "z$his"
'
test_expect_success "fetch test for-merge" '
cd "$D" &&
cd three &&
git fetch &&
test -f .git/refs/heads/two &&
test -f .git/refs/heads/one &&
master_in_two=`cd ../two && git rev-parse master` &&
one_in_two=`cd ../two && git rev-parse one` &&
{
echo "$master_in_two not-for-merge"
echo "$one_in_two "
} >expected &&
cut -f -2 .git/FETCH_HEAD >actual &&
diff expected actual'
test_done

113
t/t6001-rev-list-graft.sh Executable file
View File

@ -0,0 +1,113 @@
#!/bin/sh
test_description='Revision traversal vs grafts and path limiter'
. ./test-lib.sh
test_expect_success setup '
mkdir subdir &&
echo >fileA fileA &&
echo >subdir/fileB fileB &&
git add fileA subdir/fileB &&
git commit -a -m "Initial in one history." &&
A0=`git rev-parse --verify HEAD` &&
echo >fileA fileA modified &&
git commit -a -m "Second in one history." &&
A1=`git rev-parse --verify HEAD` &&
echo >subdir/fileB fileB modified &&
git commit -a -m "Third in one history." &&
A2=`git rev-parse --verify HEAD` &&
rm -f .git/refs/heads/master .git/index &&
echo >fileA fileA again &&
echo >subdir/fileB fileB again &&
git add fileA subdir/fileB &&
git commit -a -m "Initial in alternate history." &&
B0=`git rev-parse --verify HEAD` &&
echo >fileA fileA modified in alternate history &&
git commit -a -m "Second in alternate history." &&
B1=`git rev-parse --verify HEAD` &&
echo >subdir/fileB fileB modified in alternate history &&
git commit -a -m "Third in alternate history." &&
B2=`git rev-parse --verify HEAD` &&
: done
'
check () {
type=$1
shift
arg=
which=arg
rm -f test.expect
for a
do
if test "z$a" = z--
then
which=expect
child=
continue
fi
if test "$which" = arg
then
arg="$arg$a "
continue
fi
if test "$type" = basic
then
echo "$a"
else
if test "z$child" != z
then
echo "$child $a"
fi
child="$a"
fi
done >test.expect
if test "$type" != basic && test "z$child" != z
then
echo >>test.expect $child
fi
if test $type = basic
then
git rev-list $arg >test.actual
elif test $type = parents
then
git rev-list --parents $arg >test.actual
elif test $type = parents-raw
then
git rev-list --parents --pretty=raw $arg |
sed -n -e 's/^commit //p' >test.actual
fi
diff test.expect test.actual
}
for type in basic parents parents-raw
do
test_expect_success 'without grafts' "
rm -f .git/info/grafts
check $type $B2 -- $B2 $B1 $B0
"
test_expect_success 'with grafts' "
echo '$B0 $A2' >.git/info/grafts
check $type $B2 -- $B2 $B1 $B0 $A2 $A1 $A0
"
test_expect_success 'without grafts, with pathlimit' "
rm -f .git/info/grafts
check $type $B2 subdir -- $B2 $B0
"
test_expect_success 'with grafts, with pathlimit' "
echo '$B0 $A2' >.git/info/grafts
check $type $B2 subdir -- $B2 $B0 $A2 $A0
"
done
test_done

View File

@ -31,6 +31,15 @@ test_expect_success setup '
git checkout master git checkout master
' '
test_expect_success "checkout from non-existing branch" '
git checkout -b delete-me master &&
rm .git/refs/heads/delete-me &&
test refs/heads/delete-me = "$(git symbolic-ref HEAD)" &&
git checkout master &&
test refs/heads/master = "$(git symbolic-ref HEAD)"
'
test_expect_success "checkout with dirty tree without -m" ' test_expect_success "checkout with dirty tree without -m" '
fill 0 1 2 3 4 5 >one && fill 0 1 2 3 4 5 >one &&

View File

@ -34,7 +34,7 @@ export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
export EDITOR VISUAL export EDITOR VISUAL
case $(echo $GIT_TRACE |tr [A-Z] [a-z]) in case $(echo $GIT_TRACE |tr "[A-Z]" "[a-z]") in
1|2|true) 1|2|true)
echo "* warning: Some tests will not work if GIT_TRACE" \ echo "* warning: Some tests will not work if GIT_TRACE" \
"is set as to trace on STDERR ! *" "is set as to trace on STDERR ! *"
@ -211,7 +211,7 @@ export PATH GIT_EXEC_PATH
PYTHON=`sed -e '1{ PYTHON=`sed -e '1{
s/^#!// s/^#!//
q q
}' ../git-merge-recursive` || { }' ../git-merge-recursive-old` || {
error "You haven't built things yet, have you?" error "You haven't built things yet, have you?"
} }
"$PYTHON" -c 'import subprocess' 2>/dev/null || { "$PYTHON" -c 'import subprocess' 2>/dev/null || {