зеркало из https://github.com/microsoft/git.git
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:
Коммит
2958d9b5db
|
@ -66,6 +66,7 @@ git-merge-one-file
|
|||
git-merge-ours
|
||||
git-merge-recur
|
||||
git-merge-recursive
|
||||
git-merge-recursive-old
|
||||
git-merge-resolve
|
||||
git-merge-stupid
|
||||
git-mktag
|
||||
|
@ -125,13 +126,11 @@ git-update-ref
|
|||
git-update-server-info
|
||||
git-upload-archive
|
||||
git-upload-pack
|
||||
git-upload-tar
|
||||
git-var
|
||||
git-verify-pack
|
||||
git-verify-tag
|
||||
git-whatchanged
|
||||
git-write-tree
|
||||
git-zip-tree
|
||||
git-core-*/?*
|
||||
gitweb/gitweb.cgi
|
||||
test-date
|
||||
|
|
|
@ -119,6 +119,13 @@ apply.whitespace::
|
|||
Tells `git-apply` how to handle whitespaces, in the same way
|
||||
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::
|
||||
A boolean to enable/disable colored output when the pager is in
|
||||
use (default is true).
|
||||
|
@ -267,3 +274,10 @@ whatchanged.difftree::
|
|||
imap::
|
||||
The configuration variables in the 'imap' section are described
|
||||
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.
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ OPTIONS
|
|||
|
||||
<extra>::
|
||||
This can be any options that the archiver backend understand.
|
||||
See next section.
|
||||
|
||||
--remote=<repo>::
|
||||
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
|
||||
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
|
||||
-------------
|
||||
By default, file and directories modes are set to 0666 or 0777 in tar
|
||||
|
|
|
@ -11,6 +11,7 @@ SYNOPSIS
|
|||
'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all]
|
||||
[--timeout=n] [--init-timeout=n] [--strict-paths]
|
||||
[--base-path=path] [--user-path | --user-path=path]
|
||||
[--interpolated-path=pathtemplate]
|
||||
[--enable=service] [--disable=service]
|
||||
[--allow-override=service] [--forbid-override=service]
|
||||
[--reuseaddr] [--detach] [--pid-file=file]
|
||||
|
@ -50,6 +51,12 @@ OPTIONS
|
|||
'git://example.com/hello.git', `git-daemon` will interpret the path
|
||||
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::
|
||||
Allow pulling from all directories that look like GIT repositories
|
||||
(have the 'objects' and 'refs' subdirectories), even if they
|
||||
|
@ -135,6 +142,46 @@ upload-pack::
|
|||
disable it by setting `daemon.uploadpack` configuration
|
||||
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
|
||||
------
|
||||
Written by Linus Torvalds <torvalds@osdl.org>, YOSHIFUJI Hideaki
|
||||
|
|
|
@ -48,6 +48,10 @@ is given:
|
|||
- 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository
|
||||
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.
|
||||
|
||||
--
|
||||
|
||||
|
||||
|
|
|
@ -73,6 +73,8 @@ packed and is served via a dumb transport.
|
|||
There are other real-world examples of using update and
|
||||
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
|
||||
-------
|
||||
|
|
|
@ -20,6 +20,7 @@ SYNOPSIS
|
|||
[ \--stdin ]
|
||||
[ \--topo-order ]
|
||||
[ \--parents ]
|
||||
[ \--(author|committer|grep)=<pattern> ]
|
||||
[ [\--objects | \--objects-edge] [ \--unpacked ] ]
|
||||
[ \--pretty | \--header ]
|
||||
[ \--bisect ]
|
||||
|
@ -154,6 +155,16 @@ limiting may be applied.
|
|||
|
||||
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::
|
||||
|
||||
Stop when a given path disappears from the tree.
|
||||
|
|
|
@ -244,6 +244,18 @@ doing.
|
|||
|
||||
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
|
||||
|
|
|
@ -12,6 +12,9 @@ SYNOPSIS
|
|||
|
||||
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.
|
||||
When <base> is specified it is added as a leading path to the files in the
|
||||
generated tar archive.
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -247,10 +247,6 @@ gitlink:git-upload-pack[1]::
|
|||
Invoked by 'git-fetch-pack' to push
|
||||
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)
|
||||
-------------------------------
|
||||
|
@ -270,6 +266,9 @@ gitlink:git-am[1]::
|
|||
gitlink:git-applymbox[1]::
|
||||
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]::
|
||||
Find the change that introduced a bug by binary search.
|
||||
|
||||
|
|
|
@ -37,6 +37,10 @@ frequently used options.
|
|||
|
||||
Show commits older than a specific date.
|
||||
|
||||
--all::
|
||||
|
||||
Show all branches.
|
||||
|
||||
<revs>::
|
||||
|
||||
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
|
||||
'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
|
||||
--------
|
||||
'qgit(1)'::
|
||||
|
|
|
@ -5,8 +5,7 @@ Hooks are little scripts you can place in `$GIT_DIR/hooks`
|
|||
directory to trigger action at certain points. When
|
||||
`git-init-db` is run, a handful example hooks are copied in the
|
||||
`hooks` directory of the new repository, but by default they are
|
||||
all disabled. To enable a hook, make it executable with `chmod
|
||||
+x`.
|
||||
all disabled. To enable a hook, make it executable with `chmod +x`.
|
||||
|
||||
This document describes the currently defined hooks.
|
||||
|
||||
|
@ -16,16 +15,16 @@ applypatch-msg
|
|||
This hook is invoked by `git-applypatch` script, which is
|
||||
typically invoked by `git-applymbox`. It takes a single
|
||||
parameter, the name of the file that holds the proposed commit
|
||||
log message. Exiting with non-zero status causes the
|
||||
'git-applypatch' to abort before applying the patch.
|
||||
log message. Exiting with non-zero status causes
|
||||
`git-applypatch` to abort before applying the patch.
|
||||
|
||||
The hook is allowed to edit the message file in place, and can
|
||||
be used to normalize the message into some project standard
|
||||
format (if the project has one). It can also be used to refuse
|
||||
the commit after inspecting the message file.
|
||||
|
||||
The default applypatch-msg hook, when enabled, runs the
|
||||
commit-msg hook, if the latter is enabled.
|
||||
The default 'applypatch-msg' hook, when enabled, runs the
|
||||
'commit-msg' hook, if the latter is enabled.
|
||||
|
||||
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
|
||||
make a commit if it does not pass certain test.
|
||||
|
||||
The default pre-applypatch hook, when enabled, runs the
|
||||
pre-commit hook, if the latter is enabled.
|
||||
The default 'pre-applypatch' hook, when enabled, runs the
|
||||
'pre-commit' hook, if the latter is enabled.
|
||||
|
||||
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
|
||||
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
|
||||
a such line is found.
|
||||
such a line is found.
|
||||
|
||||
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
|
||||
the commit after inspecting the message file.
|
||||
|
||||
The default commit-msg hook, when enabled, detects duplicate
|
||||
Signed-off-by: lines, and aborts the commit when one is found.
|
||||
The default 'commit-msg' hook, when enabled, detects duplicate
|
||||
"Signed-off-by" lines, and aborts the commit if one is found.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
update
|
||||
------
|
||||
|
||||
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
|
||||
is invoked. Its exit status determines the success or failure of
|
||||
the ref update.
|
||||
|
||||
The hook executes once for each ref to be updated, and takes
|
||||
three parameters:
|
||||
- the name of the ref being updated,
|
||||
- the old object name stored in the ref,
|
||||
- and the new objectname to be stored in the ref.
|
||||
|
||||
- the name of the ref being updated,
|
||||
- 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.
|
||||
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
|
||||
based on filesystem group.
|
||||
|
||||
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,
|
||||
you can redirect your output to your stderr.
|
||||
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,
|
||||
you can redirect your output to your `stderr`.
|
||||
|
||||
|
||||
post-update
|
||||
-----------
|
||||
|
||||
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
|
||||
been updated.
|
||||
|
||||
|
@ -145,16 +145,16 @@ name of ref that was actually updated.
|
|||
This hook is meant primarily for notification, and cannot affect
|
||||
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,
|
||||
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
|
||||
transports (e.g., http) up-to-date. If you are publishing
|
||||
a git repository that is accessible via http, you should
|
||||
transports (e.g., HTTP) up-to-date. If you are publishing
|
||||
a git repository that is accessible via HTTP, you should
|
||||
probably enable this hook.
|
||||
|
||||
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,
|
||||
you can redirect your output to your stderr.
|
||||
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,
|
||||
you can redirect your output to your `stderr`.
|
||||
|
|
30
Makefile
30
Makefile
|
@ -81,8 +81,6 @@ all:
|
|||
# Define NO_ACCURATE_DIFF if your diff program at least sometimes misses
|
||||
# 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
|
||||
# 1461501637330902918203684832716283019655932542976 hashes do not give you
|
||||
# sufficient guarantee that no collisions between objects will ever happen.
|
||||
|
@ -174,7 +172,7 @@ SCRIPT_PERL = \
|
|||
git-send-email.perl git-svn.perl
|
||||
|
||||
SCRIPT_PYTHON = \
|
||||
git-merge-recursive.py
|
||||
git-merge-recursive-old.py
|
||||
|
||||
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
|
||||
$(patsubst %.perl,%,$(SCRIPT_PERL)) \
|
||||
|
@ -199,7 +197,7 @@ PROGRAMS = \
|
|||
git-upload-pack$X git-verify-pack$X \
|
||||
git-pack-redundant$X git-var$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)
|
||||
|
||||
# Empty...
|
||||
|
@ -234,7 +232,7 @@ LIB_FILE=libgit.a
|
|||
XDIFF_LIB=xdiff/lib.a
|
||||
|
||||
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 \
|
||||
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
|
||||
|
@ -246,15 +244,17 @@ DIFF_OBJS = \
|
|||
|
||||
LIB_OBJS = \
|
||||
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 \
|
||||
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 \
|
||||
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 \
|
||||
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) \
|
||||
color.o wt-status.o
|
||||
color.o wt-status.o archive-zip.o archive-tar.o
|
||||
|
||||
BUILTIN_OBJS = \
|
||||
builtin-add.o \
|
||||
|
@ -299,10 +299,8 @@ BUILTIN_OBJS = \
|
|||
builtin-update-index.o \
|
||||
builtin-update-ref.o \
|
||||
builtin-upload-archive.o \
|
||||
builtin-upload-tar.o \
|
||||
builtin-verify-pack.o \
|
||||
builtin-write-tree.o \
|
||||
builtin-zip-tree.o \
|
||||
builtin-show-ref.o \
|
||||
builtin-pack-refs.o
|
||||
|
||||
|
@ -573,7 +571,8 @@ LIB_OBJS += $(COMPAT_OBJS)
|
|||
export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
|
||||
### 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:
|
||||
$(MAKE) -C templates
|
||||
|
@ -588,6 +587,9 @@ git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS) GIT-CFLAGS
|
|||
|
||||
help.o: common-cmds.h
|
||||
|
||||
git-merge-recur$X: git-merge-recursive$X
|
||||
rm -f $@ && ln git-merge-recursive$X $@
|
||||
|
||||
$(BUILT_INS): 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,$^) \
|
||||
$(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)
|
||||
$(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
|
||||
$(DIFF_OBJS): diffcore.h
|
||||
|
@ -890,6 +887,7 @@ check-docs::
|
|||
case "$$v" in \
|
||||
git-merge-octopus | git-merge-ours | git-merge-recursive | \
|
||||
git-merge-resolve | git-merge-stupid | git-merge-recur | \
|
||||
git-merge-recursive-old | \
|
||||
git-ssh-pull | git-ssh-push ) continue ;; \
|
||||
esac ; \
|
||||
test -f "Documentation/$$v.txt" || \
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -10,9 +10,6 @@
|
|||
#include "builtin.h"
|
||||
#include "archive.h"
|
||||
|
||||
static const char zip_tree_usage[] =
|
||||
"git-zip-tree [-0|...|-9] <tree-ish> [ <base> ]";
|
||||
|
||||
static int verbose;
|
||||
static int zip_date;
|
||||
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;
|
||||
}
|
||||
|
||||
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 plen = strlen(args->base);
|
122
builtin-apply.c
122
builtin-apply.c
|
@ -854,6 +854,49 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
|
|||
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
|
||||
* 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;
|
||||
break;
|
||||
case '+':
|
||||
/*
|
||||
* We know len is at least two, since we have a '+' and
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
if (new_whitespace != nowarn_whitespace)
|
||||
check_whitespace(line, len);
|
||||
added++;
|
||||
newlines--;
|
||||
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,
|
||||
* 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;
|
||||
if ((new_whitespace == strip_whitespace) &&
|
||||
1 < plen && isspace(patch[plen-1])) {
|
||||
int fixed = 0;
|
||||
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')
|
||||
add_nl_to_tail = 1;
|
||||
plen--;
|
||||
while (0 < plen && isspace(patch[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)
|
||||
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)
|
||||
|
|
524
builtin-grep.c
524
builtin-grep.c
|
@ -11,6 +11,7 @@
|
|||
#include "tree-walk.h"
|
||||
#include "builtin.h"
|
||||
#include <regex.h>
|
||||
#include "grep.h"
|
||||
#include <fnmatch.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
|
@ -82,498 +83,6 @@ static int pathspec_matches(const char **paths, const char *name)
|
|||
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)
|
||||
{
|
||||
unsigned long size;
|
||||
|
@ -816,6 +325,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
|
|||
else
|
||||
hit |= grep_file(opt, ce->name);
|
||||
}
|
||||
free_grep_patterns(opt);
|
||||
return hit;
|
||||
}
|
||||
|
||||
|
@ -1055,8 +565,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||
/* ignore empty line like grep does */
|
||||
if (!buf[0])
|
||||
continue;
|
||||
add_pattern(&opt, xstrdup(buf), argv[1], ++lno,
|
||||
GREP_PATTERN);
|
||||
append_grep_pattern(&opt, xstrdup(buf),
|
||||
argv[1], ++lno,
|
||||
GREP_PATTERN);
|
||||
}
|
||||
fclose(patterns);
|
||||
argv++;
|
||||
|
@ -1064,27 +575,32 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||
continue;
|
||||
}
|
||||
if (!strcmp("--not", arg)) {
|
||||
add_pattern(&opt, arg, "command line", 0, GREP_NOT);
|
||||
append_grep_pattern(&opt, arg, "command line", 0,
|
||||
GREP_NOT);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--and", arg)) {
|
||||
add_pattern(&opt, arg, "command line", 0, GREP_AND);
|
||||
append_grep_pattern(&opt, arg, "command line", 0,
|
||||
GREP_AND);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--or", arg))
|
||||
continue; /* no-op */
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (!strcmp("-e", arg)) {
|
||||
if (1 < argc) {
|
||||
add_pattern(&opt, argv[1], "-e option", 0,
|
||||
GREP_PATTERN);
|
||||
append_grep_pattern(&opt, argv[1],
|
||||
"-e option", 0,
|
||||
GREP_PATTERN);
|
||||
argv++;
|
||||
argc--;
|
||||
continue;
|
||||
|
@ -1106,8 +622,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||
|
||||
/* First unrecognized non-option token */
|
||||
if (!opt.pattern_list) {
|
||||
add_pattern(&opt, arg, "command line", 0,
|
||||
GREP_PATTERN);
|
||||
append_grep_pattern(&opt, arg, "command line", 0,
|
||||
GREP_PATTERN);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
|
@ -1124,8 +640,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||
die("no pattern given.");
|
||||
if ((opt.regflags != REG_NEWLINE) && opt.fixed)
|
||||
die("cannot mix --fixed-strings and regexp");
|
||||
if (!opt.fixed)
|
||||
compile_patterns(&opt);
|
||||
compile_grep_patterns(&opt);
|
||||
|
||||
/* Check revs and then paths */
|
||||
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))
|
||||
hit = 1;
|
||||
}
|
||||
free_grep_patterns(&opt);
|
||||
return !hit;
|
||||
}
|
||||
|
|
|
@ -311,6 +311,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
|
|||
*/
|
||||
sprintf(buf, "%d", shared_repository);
|
||||
git_config_set("core.sharedrepository", buf);
|
||||
git_config_set("receive.denyNonFastforwards", "true");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -451,17 +451,6 @@ static int read_one_header_line(char *line, int sz, FILE *in)
|
|||
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)
|
||||
{
|
||||
int c;
|
||||
|
|
|
@ -597,15 +597,15 @@ static int add_object_entry(const unsigned char *sha1, unsigned hash, int exclud
|
|||
|
||||
if (!exclude) {
|
||||
for (p = packed_git; p; p = p->next) {
|
||||
struct pack_entry e;
|
||||
if (find_pack_entry_one(sha1, &e, p)) {
|
||||
unsigned long offset = find_pack_entry_one(sha1, p);
|
||||
if (offset) {
|
||||
if (incremental)
|
||||
return 0;
|
||||
if (local && !p->pack_local)
|
||||
return 0;
|
||||
if (!found_pack) {
|
||||
found_offset = e.offset;
|
||||
found_pack = e.p;
|
||||
found_offset = offset;
|
||||
found_pack = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -269,7 +269,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
|||
revs.diff)
|
||||
usage(rev_list_usage);
|
||||
|
||||
save_commit_buffer = revs.verbose_header;
|
||||
save_commit_buffer = revs.verbose_header || revs.grep_filter;
|
||||
track_object_refs = 0;
|
||||
if (bisect_list)
|
||||
revs.limited = 1;
|
||||
|
|
|
@ -4,413 +4,68 @@
|
|||
#include <time.h>
|
||||
#include "cache.h"
|
||||
#include "commit.h"
|
||||
#include "strbuf.h"
|
||||
#include "tar.h"
|
||||
#include "builtin.h"
|
||||
#include "pkt-line.h"
|
||||
#include "archive.h"
|
||||
|
||||
#define RECORDSIZE (512)
|
||||
#define BLOCKSIZE (RECORDSIZE * 20)
|
||||
#include "quote.h"
|
||||
|
||||
static const char tar_tree_usage[] =
|
||||
"git-tar-tree [--remote=<repo>] <tree-ish> [basedir]";
|
||||
|
||||
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;
|
||||
}
|
||||
"git-tar-tree [--remote=<repo>] <tree-ish> [basedir]\n"
|
||||
"*** Note that this command is now deprecated; use git-archive instead.";
|
||||
|
||||
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);
|
||||
if (!strncmp("--remote=", argv[1], 9))
|
||||
return remote_tar(argc, argv);
|
||||
return generate_tar(argc, argv, prefix);
|
||||
break;
|
||||
case 3:
|
||||
/* 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 */
|
||||
#define RECORDSIZE (512)
|
||||
#define HEADERSIZE (2 * RECORDSIZE)
|
||||
|
||||
int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
|
||||
|
|
|
@ -112,11 +112,13 @@ static int add_file_to_cache(const char *path)
|
|||
ce->ce_mode = create_ce_mode(st.st_mode);
|
||||
if (!trust_executable_bit) {
|
||||
/* 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);
|
||||
if (0 <= pos)
|
||||
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))
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
* Copyright (c) 2006 Franck Bui-Huu
|
||||
*/
|
||||
#include <time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/poll.h>
|
||||
#include "cache.h"
|
||||
#include "builtin.h"
|
||||
#include "archive.h"
|
||||
#include "pkt-line.h"
|
||||
#include "sideband.h"
|
||||
#include <sys/wait.h>
|
||||
#include <sys/poll.h>
|
||||
|
||||
static const char upload_archive_usage[] =
|
||||
"git-upload-archive <repo>";
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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_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_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_update_index(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
|
||||
|
|
13
cache.h
13
cache.h
|
@ -189,6 +189,7 @@ extern int prefer_symlink_refs;
|
|||
extern int log_all_ref_updates;
|
||||
extern int warn_ambiguous_refs;
|
||||
extern int shared_repository;
|
||||
extern int deny_non_fast_forwards;
|
||||
extern const char *apply_default_whitespace;
|
||||
extern int zlib_compression_level;
|
||||
|
||||
|
@ -279,6 +280,12 @@ enum object_type {
|
|||
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 */
|
||||
#define MINIMUM_ABBREV 4
|
||||
#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 int num_packed_objects(const struct packed_git *p);
|
||||
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 void *unpack_entry_gently(struct pack_entry *, char *, unsigned long *);
|
||||
extern unsigned long find_pack_entry_one(const unsigned char *, struct packed_git *);
|
||||
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 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 */
|
||||
extern int update_server_info(int);
|
||||
|
|
|
@ -75,7 +75,6 @@ GIT_ARG_SET_PATH(shell)
|
|||
# Define PERL_PATH to provide path to 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.
|
||||
AC_ARG_WITH(python,[AS_HELP_STRING([--with-python=PATH], [provide PATH to python])
|
||||
AS_HELP_STRING([--without-python], [don't use python scripts])],
|
||||
|
@ -100,7 +99,6 @@ AC_PROG_CC
|
|||
AC_CHECK_TOOL(AR, ar, :)
|
||||
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.
|
||||
if test -z "$NO_PYTHON"; then
|
||||
if test -z "$PYTHON_PATH"; then
|
||||
|
|
89
daemon.c
89
daemon.c
|
@ -12,6 +12,7 @@
|
|||
#include "pkt-line.h"
|
||||
#include "cache.h"
|
||||
#include "exec_cmd.h"
|
||||
#include "interpolate.h"
|
||||
|
||||
static int log_syslog;
|
||||
static int verbose;
|
||||
|
@ -21,6 +22,7 @@ static const char daemon_usage[] =
|
|||
"git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n"
|
||||
" [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
|
||||
" [--base-path=path] [--user-path | --user-path=path]\n"
|
||||
" [--interpolated-path=path]\n"
|
||||
" [--reuseaddr] [--detach] [--pid-file=file]\n"
|
||||
" [--[enable|disable|allow-override|forbid-override]=service]\n"
|
||||
" [--user=user [[--group=group]] [directory...]";
|
||||
|
@ -34,6 +36,10 @@ static int export_all_trees;
|
|||
|
||||
/* Take all paths relative to this one if non-NULL */
|
||||
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
|
||||
* 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 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)
|
||||
{
|
||||
/* 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 interp_path[PATH_MAX];
|
||||
char *path;
|
||||
char *dir;
|
||||
|
||||
dir = itable[INTERP_SLOT_DIR].value;
|
||||
|
||||
if (avoid_alias(dir)) {
|
||||
logerror("'%s': aliased", dir);
|
||||
|
@ -184,16 +209,27 @@ static char *path_ok(char *dir)
|
|||
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) {
|
||||
if (*dir != '/') {
|
||||
/* Allow only absolute */
|
||||
logerror("'%s': Non-absolute path denied (base-path active)", dir);
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
snprintf(rpath, PATH_MAX, "%s%s", base_path, dir);
|
||||
dir = rpath;
|
||||
}
|
||||
snprintf(rpath, PATH_MAX, "%s%s", base_path, dir);
|
||||
dir = rpath;
|
||||
}
|
||||
|
||||
path = enter_repo(dir, strict_paths);
|
||||
|
@ -257,12 +293,14 @@ static int git_daemon_config(const char *var, const char *value)
|
|||
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;
|
||||
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) {
|
||||
logerror("'%s': service not enabled.", service->name);
|
||||
|
@ -270,7 +308,7 @@ static int run_service(char *dir, struct daemon_service *service)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (!(path = path_ok(dir)))
|
||||
if (!(path = path_ok(itable)))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
|
@ -358,6 +396,28 @@ static void make_service_overridable(const char *name, int ena) {
|
|||
die("No such service %s", name);
|
||||
}
|
||||
|
||||
static void parse_extra_args(char *extra_args, int buflen)
|
||||
{
|
||||
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 char line[1000];
|
||||
|
@ -398,13 +458,18 @@ static int execute(struct sockaddr *addr)
|
|||
if (len && line[len-1] == '\n')
|
||||
line[--len] = 0;
|
||||
|
||||
if (len != pktlen)
|
||||
parse_extra_args(line + len + 1, pktlen - len - 1);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
|
||||
struct daemon_service *s = &(daemon_service[i]);
|
||||
int namelen = strlen(s->name);
|
||||
if (!strncmp("git-", line, 4) &&
|
||||
!strncmp(s->name, line + 4, namelen) &&
|
||||
line[namelen + 4] == ' ')
|
||||
return run_service(line + namelen + 5, s);
|
||||
line[namelen + 4] == ' ') {
|
||||
interp_table[INTERP_SLOT_DIR].value = line+namelen+5;
|
||||
return run_service(interp_table, s);
|
||||
}
|
||||
}
|
||||
|
||||
logerror("Protocol error: '%s'", line);
|
||||
|
@ -867,6 +932,10 @@ int main(int argc, char **argv)
|
|||
base_path = arg+12;
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(arg, "--interpolated-path=", 20)) {
|
||||
interpolated_path = arg+20;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--reuseaddr")) {
|
||||
reuseaddr = 1;
|
||||
continue;
|
||||
|
|
|
@ -308,8 +308,8 @@ create_delta(const struct delta_index *index,
|
|||
continue;
|
||||
if (ref_size > top - src)
|
||||
ref_size = top - src;
|
||||
if (ref_size > 0x10000)
|
||||
ref_size = 0x10000;
|
||||
if (ref_size > 0xffffff)
|
||||
ref_size = 0xffffff;
|
||||
if (ref_size <= msize)
|
||||
break;
|
||||
while (ref_size-- && *src++ == *ref)
|
||||
|
@ -318,6 +318,8 @@ create_delta(const struct delta_index *index,
|
|||
/* this is our best match so far */
|
||||
msize = ref - entry->ptr;
|
||||
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; }
|
||||
msize >>= 8;
|
||||
if (msize & 0xff) { out[outpos++] = msize; i |= 0x20; }
|
||||
msize >>= 8;
|
||||
if (msize & 0xff) { out[outpos++] = msize; i |= 0x40; }
|
||||
|
||||
*op = i;
|
||||
}
|
||||
|
|
172
diff.c
172
diff.c
|
@ -20,12 +20,13 @@ static int diff_use_color_default;
|
|||
|
||||
static char diff_colors[][COLOR_MAXLEN] = {
|
||||
"\033[m", /* reset */
|
||||
"", /* normal */
|
||||
"\033[1m", /* bold */
|
||||
"\033[36m", /* cyan */
|
||||
"\033[31m", /* red */
|
||||
"\033[32m", /* green */
|
||||
"\033[33m" /* yellow */
|
||||
"", /* PLAIN (normal) */
|
||||
"\033[1m", /* METAINFO (bold) */
|
||||
"\033[36m", /* FRAGINFO (cyan) */
|
||||
"\033[31m", /* OLD (red) */
|
||||
"\033[32m", /* NEW (green) */
|
||||
"\033[33m", /* COMMIT (yellow) */
|
||||
"\033[41m", /* WHITESPACE (red background) */
|
||||
};
|
||||
|
||||
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;
|
||||
if (!strcasecmp(var+ofs, "commit"))
|
||||
return DIFF_COMMIT;
|
||||
if (!strcasecmp(var+ofs, "whitespace"))
|
||||
return DIFF_WHITESPACE;
|
||||
die("bad config variable '%s'", var);
|
||||
}
|
||||
|
||||
|
@ -383,9 +386,89 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix)
|
|||
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)
|
||||
{
|
||||
int i;
|
||||
int color;
|
||||
struct emit_callback *ecbdata = priv;
|
||||
const char *set = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
|
||||
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] == ' ') {
|
||||
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;
|
||||
else {
|
||||
int nparents = ecbdata->nparents;
|
||||
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);
|
||||
emit_line(reset, reset, line, len);
|
||||
return;
|
||||
}
|
||||
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--;
|
||||
fputs (set, stdout);
|
||||
fwrite (line, len, 1, stdout);
|
||||
puts (reset);
|
||||
emit_line(set, reset, line, len);
|
||||
return;
|
||||
}
|
||||
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)
|
||||
|
|
1
diff.h
1
diff.h
|
@ -86,6 +86,7 @@ enum color_diff {
|
|||
DIFF_FILE_OLD = 4,
|
||||
DIFF_FILE_NEW = 5,
|
||||
DIFF_COMMIT = 6,
|
||||
DIFF_WHITESPACE = 7,
|
||||
};
|
||||
const char *diff_get_color(int diff_use_color, enum color_diff ix);
|
||||
|
||||
|
|
27
dir.c
27
dir.c
|
@ -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).
|
||||
* 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);
|
||||
int contents = 0;
|
||||
|
@ -314,7 +314,6 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
|
|||
|
||||
switch (DTYPE(de)) {
|
||||
struct stat st;
|
||||
int subdir, rewind_base;
|
||||
default:
|
||||
continue;
|
||||
case DT_UNKNOWN:
|
||||
|
@ -328,26 +327,30 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
|
|||
case DT_DIR:
|
||||
memcpy(fullname + baselen + len, "/", 2);
|
||||
len++;
|
||||
rewind_base = dir->nr;
|
||||
subdir = read_directory_recursive(dir, fullname, fullname,
|
||||
baselen + len);
|
||||
if (dir->show_other_directories &&
|
||||
(subdir || !dir->hide_empty_directories) &&
|
||||
!dir_exists(fullname, baselen + len)) {
|
||||
/* Rewind the read subdirectory */
|
||||
while (dir->nr > rewind_base)
|
||||
free(dir->entries[--dir->nr]);
|
||||
if (dir->hide_empty_directories &&
|
||||
!read_directory_recursive(dir,
|
||||
fullname, fullname,
|
||||
baselen + len, 1))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
contents += subdir;
|
||||
|
||||
contents += read_directory_recursive(dir,
|
||||
fullname, fullname, baselen + len, 0);
|
||||
continue;
|
||||
case DT_REG:
|
||||
case DT_LNK:
|
||||
break;
|
||||
}
|
||||
add_name(dir, fullname, baselen + len);
|
||||
contents++;
|
||||
if (check_only)
|
||||
goto exit_early;
|
||||
else
|
||||
add_name(dir, fullname, baselen + len);
|
||||
}
|
||||
exit_early:
|
||||
closedir(fdir);
|
||||
|
||||
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);
|
||||
return dir->nr;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ int warn_ambiguous_refs = 1;
|
|||
int repository_format_version;
|
||||
char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
|
||||
int shared_repository = PERM_UMASK;
|
||||
int deny_non_fast_forwards = 0;
|
||||
const char *apply_default_whitespace;
|
||||
int zlib_compression_level = Z_DEFAULT_COMPRESSION;
|
||||
int pager_in_use;
|
||||
|
|
|
@ -111,6 +111,16 @@ rev=$(git-rev-parse --verify "$head") || exit
|
|||
git-check-ref-format "heads/$branchname" ||
|
||||
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=''
|
||||
if git-show-ref --verify --quiet -- "refs/heads/$branchname"
|
||||
then
|
||||
|
|
|
@ -4,8 +4,8 @@ USAGE='[-f] [-b <new_branch>] [-m] [<branch>] [<paths>...]'
|
|||
SUBDIRECTORY_OK=Sometimes
|
||||
. git-sh-setup
|
||||
|
||||
old=$(git-rev-parse HEAD)
|
||||
old_name=HEAD
|
||||
old=$(git-rev-parse --verify $old_name 2>/dev/null)
|
||||
new=
|
||||
new_name=
|
||||
force=
|
||||
|
@ -140,6 +140,13 @@ fi
|
|||
die "git checkout: to checkout the requested commit you need to specify
|
||||
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" ]
|
||||
then
|
||||
git-read-tree --reset -u $new
|
||||
|
|
|
@ -68,11 +68,10 @@ done
|
|||
|
||||
case "$#" in
|
||||
0)
|
||||
test -f "$GIT_DIR/branches/origin" ||
|
||||
test -f "$GIT_DIR/remotes/origin" ||
|
||||
git-repo-config --get remote.origin.url >/dev/null ||
|
||||
die "Where do you want to fetch from today?"
|
||||
set origin ;;
|
||||
origin=$(get_default_remote)
|
||||
test -n "$(get_remote_url ${origin})" ||
|
||||
die "Where do you want to fetch from today?"
|
||||
set x $origin ; shift ;;
|
||||
esac
|
||||
|
||||
remote_nick="$1"
|
||||
|
|
16
git-merge.sh
16
git-merge.sh
|
@ -9,21 +9,15 @@ USAGE='[-n] [--no-commit] [--squash] [-s <strategy>]... <merge-message> <head> <
|
|||
LF='
|
||||
'
|
||||
|
||||
all_strategies='recursive recur octopus resolve stupid ours'
|
||||
case "${GIT_USE_RECUR_FOR_RECURSIVE}" in
|
||||
'')
|
||||
default_twohead_strategies=recursive ;;
|
||||
?*)
|
||||
default_twohead_strategies=recur ;;
|
||||
esac
|
||||
all_strategies='recur recursive recursive-old octopus resolve stupid ours'
|
||||
default_twohead_strategies='recursive'
|
||||
default_octopus_strategies='octopus'
|
||||
no_trivial_merge_strategies='ours'
|
||||
use_strategies=
|
||||
|
||||
index_merge=t
|
||||
if test "@@NO_PYTHON@@"; then
|
||||
all_strategies='recur resolve octopus stupid ours'
|
||||
default_twohead_strategies='resolve'
|
||||
all_strategies='recur recursive resolve octopus stupid ours'
|
||||
fi
|
||||
|
||||
dropsave() {
|
||||
|
@ -122,10 +116,6 @@ do
|
|||
strategy="$2"
|
||||
shift ;;
|
||||
esac
|
||||
case "$strategy,${GIT_USE_RECUR_FOR_RECURSIVE}" in
|
||||
recursive,?*)
|
||||
strategy=recur ;;
|
||||
esac
|
||||
case " $all_strategies " in
|
||||
*" $strategy "*)
|
||||
use_strategies="$use_strategies$strategy " ;;
|
||||
|
|
|
@ -68,6 +68,12 @@ get_remote_url () {
|
|||
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 () {
|
||||
data_source=$(get_data_source "$1")
|
||||
case "$data_source" in
|
||||
|
@ -86,9 +92,22 @@ get_remote_default_refs_for_push () {
|
|||
|
||||
# Subroutine to canonicalize remote:local notation.
|
||||
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.
|
||||
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
|
||||
do
|
||||
force=
|
||||
|
@ -101,6 +120,18 @@ canon_refs_list_for_fetch () {
|
|||
expr "z$ref" : 'z.*:' >/dev/null || ref="${ref}:"
|
||||
remote=$(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
|
||||
'') remote=HEAD ;;
|
||||
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"
|
||||
fi
|
||||
echo "${dot_prefix}${force}${remote}:${local}"
|
||||
dot_prefix=.
|
||||
done
|
||||
}
|
||||
|
||||
|
@ -131,7 +161,7 @@ get_remote_default_refs_for_fetch () {
|
|||
'' | config-partial | branches-partial)
|
||||
echo "HEAD:" ;;
|
||||
config)
|
||||
canon_refs_list_for_fetch \
|
||||
canon_refs_list_for_fetch -d "$1" \
|
||||
$(git-repo-config --get-all "remote.$1.fetch") ;;
|
||||
branches)
|
||||
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"
|
||||
;;
|
||||
remotes)
|
||||
# This prefixes the second and later default refspecs
|
||||
# with a '.', to signal git-fetch to mark them
|
||||
# not-for-merge.
|
||||
canon_refs_list_for_fetch $(sed -ne '/^Pull: */{
|
||||
canon_refs_list_for_fetch -d "$1" $(sed -ne '/^Pull: */{
|
||||
s///p
|
||||
}' "$GIT_DIR/remotes/$1")
|
||||
;;
|
||||
|
|
|
@ -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\".
|
||||
"
|
||||
unset newbase
|
||||
case "${GIT_USE_RECUR_FOR_RECURSIVE}" in
|
||||
'')
|
||||
strategy=recursive ;;
|
||||
?*)
|
||||
strategy=recur ;;
|
||||
esac
|
||||
|
||||
strategy=recursive
|
||||
do_merge=
|
||||
dotest=$GIT_DIR/.dotest-merge
|
||||
prec=4
|
||||
|
@ -206,11 +200,6 @@ do
|
|||
shift
|
||||
done
|
||||
|
||||
case "$strategy,${GIT_USE_RECUR_FOR_RECURSIVE}" in
|
||||
recursive,?*)
|
||||
strategy=recur ;;
|
||||
esac
|
||||
|
||||
# Make sure we do not have .dotest
|
||||
if test -z "$do_merge"
|
||||
then
|
||||
|
@ -303,11 +292,11 @@ then
|
|||
exit $?
|
||||
fi
|
||||
|
||||
if test "@@NO_PYTHON@@" && test "$strategy" = "recursive"
|
||||
if test "@@NO_PYTHON@@" && test "$strategy" = "recursive-old"
|
||||
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
|
||||
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.'
|
||||
|
||||
fi
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#
|
||||
|
||||
USAGE='[-a] [-d] [-f] [-l] [-n] [-q]'
|
||||
SUBDIRECTORY_OK='Yes'
|
||||
. git-sh-setup
|
||||
|
||||
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...
|
||||
case ",$all_into_one," in
|
||||
,,)
|
||||
rev_list='--unpacked'
|
||||
pack_objects='--incremental'
|
||||
args='--unpacked --incremental'
|
||||
;;
|
||||
,t,)
|
||||
rev_list=
|
||||
pack_objects=
|
||||
args=
|
||||
|
||||
# Redundancy check in all-into-one case is trivial.
|
||||
existing=`test -d "$PACKDIR" && cd "$PACKDIR" && \
|
||||
|
@ -45,11 +44,8 @@ case ",$all_into_one," in
|
|||
;;
|
||||
esac
|
||||
|
||||
pack_objects="$pack_objects $local $quiet $no_reuse_delta$extra"
|
||||
name=$( { git-rev-list --objects --all $rev_list ||
|
||||
echo "git-rev-list died with exit code $?"
|
||||
} |
|
||||
git-pack-objects --non-empty $pack_objects "$PACKTMP") ||
|
||||
args="$args $local $quiet $no_reuse_delta$extra"
|
||||
name=$(git-pack-objects --non-empty --all $args </dev/null "$PACKTMP") ||
|
||||
exit 1
|
||||
if [ -z "$name" ]; then
|
||||
echo Nothing new to pack.
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
# 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>'
|
||||
. git-sh-setup
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
|
|||
$_template, $_shared, $_no_default_regex, $_no_graft_copy,
|
||||
$_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,
|
||||
$_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 ($_svn_co_url_revs, $_svn_pg_peg_revs);
|
||||
my @repo_path_split_cache;
|
||||
|
@ -65,6 +65,7 @@ my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
|
|||
'repack:i' => \$_repack,
|
||||
'no-metadata' => \$_no_metadata,
|
||||
'quiet|q' => \$_q,
|
||||
'ignore-nodate' => \$_ignore_nodate,
|
||||
'repack-flags|repack-args|repack-opts=s' => \$_repack_flags);
|
||||
|
||||
my ($_trunk, $_tags, $_branches);
|
||||
|
@ -1246,6 +1247,7 @@ sub assert_svn_wc_clean {
|
|||
}
|
||||
my @status = grep(!/^Performing status on external/,(`svn status`));
|
||||
@status = grep(!/^\s*$/,@status);
|
||||
@status = grep(!/^X/,@status) if $_no_ignore_ext;
|
||||
if (scalar @status) {
|
||||
print STDERR "Tree ($SVN_WC) is not clean:\n";
|
||||
print STDERR $_ foreach @status;
|
||||
|
@ -1734,6 +1736,8 @@ sub next_log_entry {
|
|||
my $rev = $1;
|
||||
my ($author, $date, $lines) = split(/\s*\|\s*/, $_, 3);
|
||||
($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 =~
|
||||
/(\d{4})\-(\d\d)\-(\d\d)\s
|
||||
(\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";
|
||||
while (<$authors>) {
|
||||
chomp;
|
||||
next unless /^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/;
|
||||
next unless /^(\S+?|\(no author\))\s*=\s*(.+?)\s*<(.+)>\s*$/;
|
||||
my ($user, $name, $email) = ($1, $2, $3);
|
||||
$users{$user} = [$name, $email];
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ $SIG{'PIPE'}="IGNORE";
|
|||
$ENV{'TZ'}="UTC";
|
||||
|
||||
our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T,
|
||||
$opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D);
|
||||
$opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D,$opt_S);
|
||||
|
||||
sub usage() {
|
||||
print STDERR <<END;
|
||||
|
@ -39,12 +39,12 @@ Usage: ${\basename $0} # fetch/update GIT from SVN
|
|||
[-o branch-for-HEAD] [-h] [-v] [-l max_rev]
|
||||
[-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname]
|
||||
[-d|-D] [-i] [-u] [-r] [-I ignorefilename] [-s start_chg]
|
||||
[-m] [-M regex] [-A author_file] [SVN_URL]
|
||||
[-m] [-M regex] [-A author_file] [-S] [SVN_URL]
|
||||
END
|
||||
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;
|
||||
|
||||
my $tag_name = $opt_t || "tags";
|
||||
|
@ -531,21 +531,30 @@ sub copy_path($$$$$$$$) {
|
|||
|
||||
sub commit {
|
||||
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);
|
||||
|
||||
if (not defined $author or $author eq "") {
|
||||
$author_name = $author_email = "unknown";
|
||||
$committer_name = $committer_email = "unknown";
|
||||
} elsif (defined $users_file) {
|
||||
die "User $author is not listed in $users_file\n"
|
||||
unless exists $users{$author};
|
||||
($author_name,$author_email) = @{$users{$author}};
|
||||
($committer_name,$committer_email) = @{$users{$author}};
|
||||
} elsif ($author =~ /^(.*?)\s+<(.*)>$/) {
|
||||
($author_name, $author_email) = ($1, $2);
|
||||
($committer_name, $committer_email) = ($1, $2);
|
||||
} else {
|
||||
$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);
|
||||
|
||||
my $tag;
|
||||
|
@ -772,8 +781,8 @@ sub commit {
|
|||
"GIT_AUTHOR_NAME=$author_name",
|
||||
"GIT_AUTHOR_EMAIL=$author_email",
|
||||
"GIT_AUTHOR_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)),
|
||||
"GIT_COMMITTER_NAME=$author_name",
|
||||
"GIT_COMMITTER_EMAIL=$author_email",
|
||||
"GIT_COMMITTER_NAME=$committer_name",
|
||||
"GIT_COMMITTER_EMAIL=$committer_email",
|
||||
"GIT_COMMITTER_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)),
|
||||
"git-commit-tree", $tree,@par);
|
||||
die "Cannot exec git-commit-tree: $!\n";
|
||||
|
@ -825,7 +834,7 @@ sub commit {
|
|||
print $out ("object $cid\n".
|
||||
"type commit\n".
|
||||
"tag $dest\n".
|
||||
"tagger $author_name <$author_email>\n") and
|
||||
"tagger $committer_name <$committer_email>\n") and
|
||||
close($out)
|
||||
or die "Cannot create tag object $dest: $!\n";
|
||||
|
||||
|
|
2
git.c
2
git.c
|
@ -260,12 +260,10 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
|
|||
{ "stripspace", cmd_stripspace },
|
||||
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
|
||||
{ "tar-tree", cmd_tar_tree, RUN_SETUP },
|
||||
{ "zip-tree", cmd_zip_tree, RUN_SETUP },
|
||||
{ "unpack-objects", cmd_unpack_objects, RUN_SETUP },
|
||||
{ "update-index", cmd_update_index, RUN_SETUP },
|
||||
{ "update-ref", cmd_update_ref, RUN_SETUP },
|
||||
{ "upload-archive", cmd_upload_archive },
|
||||
{ "upload-tar", cmd_upload_tar },
|
||||
{ "version", cmd_version },
|
||||
{ "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
|
||||
{ "write-tree", cmd_write_tree, RUN_SETUP },
|
||||
|
|
|
@ -212,19 +212,9 @@ if (defined $project) {
|
|||
}
|
||||
}
|
||||
|
||||
# We have to handle those containing any characters:
|
||||
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');
|
||||
if (defined $file_parent) {
|
||||
if (!validate_input($file_parent)) {
|
||||
die_error(undef, "Invalid file parent parameter");
|
||||
}
|
||||
}
|
||||
|
||||
our $hash = $cgi->param('h');
|
||||
if (defined $hash) {
|
||||
|
@ -274,13 +264,16 @@ sub evaluate_path_info {
|
|||
return if defined $project;
|
||||
my $path_info = $ENV{"PATH_INFO"};
|
||||
return if !$path_info;
|
||||
$path_info =~ s,(^/|/$),,gs;
|
||||
$path_info = validate_input($path_info);
|
||||
$path_info =~ s,^/+,,;
|
||||
return if !$path_info;
|
||||
# find which part of PATH_INFO is project
|
||||
$project = $path_info;
|
||||
$project =~ s,/+$,,;
|
||||
while ($project && !-e "$projectroot/$project/HEAD") {
|
||||
$project =~ s,/*[^/]*$,,;
|
||||
}
|
||||
# validate project
|
||||
$project = validate_input($project);
|
||||
if (!$project ||
|
||||
($export_ok && !-e "$projectroot/$project/$export_ok") ||
|
||||
($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
|
||||
return if $action;
|
||||
if ($path_info =~ m,^$project/([^/]+)/(.+)$,) {
|
||||
# we got "project.git/branch/filename"
|
||||
$action ||= "blob_plain";
|
||||
$hash_base ||= validate_input($1);
|
||||
$file_name ||= validate_input($2);
|
||||
} elsif ($path_info =~ m,^$project/([^/]+)$,) {
|
||||
$path_info =~ s,^$project/*,,;
|
||||
my ($refname, $pathname) = split(/:/, $path_info, 2);
|
||||
if (defined $pathname) {
|
||||
# we got "project.git/branch:filename" or "project.git/branch:dir/"
|
||||
# we could use git_get_type(branch:pathname), but it needs $git_dir
|
||||
$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"
|
||||
$action ||= "shortlog";
|
||||
$hash ||= validate_input($1);
|
||||
$hash ||= validate_input($refname);
|
||||
}
|
||||
}
|
||||
evaluate_path_info();
|
||||
|
@ -341,6 +343,10 @@ if (defined $project) {
|
|||
if (!defined($actions{$action})) {
|
||||
die_error(undef, "Unknown action");
|
||||
}
|
||||
if ($action !~ m/^(opml|project_list|project_index)$/ &&
|
||||
!$project) {
|
||||
die_error(undef, "Project needed");
|
||||
}
|
||||
$actions{$action}->();
|
||||
exit;
|
||||
|
||||
|
@ -400,7 +406,7 @@ sub validate_input {
|
|||
# correct, but quoted slashes look too horrible in bookmarks
|
||||
sub esc_param {
|
||||
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/ /\+/g;
|
||||
return $str;
|
||||
|
@ -611,7 +617,7 @@ sub format_subject_html {
|
|||
|
||||
if (length($short) < length($long)) {
|
||||
return $cgi->a({-href => $href, -class => "list subject",
|
||||
-title => $long},
|
||||
-title => decode("utf8", $long, Encode::FB_DEFAULT)},
|
||||
esc_html($short) . $extra);
|
||||
} else {
|
||||
return $cgi->a({-href => $href, -class => "list subject"},
|
||||
|
@ -702,6 +708,7 @@ sub git_get_project_config {
|
|||
sub git_get_hash_by_path {
|
||||
my $base = shift;
|
||||
my $path = shift || return undef;
|
||||
my $type = shift;
|
||||
|
||||
my $tree = $base;
|
||||
|
||||
|
@ -712,6 +719,10 @@ sub git_get_hash_by_path {
|
|||
|
||||
#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
|
||||
$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/;
|
||||
if (defined $type && $type ne $2) {
|
||||
# type doesn't match
|
||||
return undef;
|
||||
}
|
||||
return $3;
|
||||
}
|
||||
|
||||
|
@ -731,7 +742,7 @@ sub git_get_project_description {
|
|||
sub git_get_project_url_list {
|
||||
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>;
|
||||
close $fd;
|
||||
|
||||
|
@ -828,16 +839,10 @@ sub git_get_project_owner {
|
|||
sub git_get_references {
|
||||
my $type = shift || "";
|
||||
my %refs;
|
||||
my $fd;
|
||||
# 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11
|
||||
# c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11^{}
|
||||
if (-f "$projectroot/$project/info/refs") {
|
||||
open $fd, "$projectroot/$project/info/refs"
|
||||
or return;
|
||||
} else {
|
||||
open $fd, "-|", git_cmd(), "ls-remote", "."
|
||||
or return;
|
||||
}
|
||||
open my $fd, "-|", $GIT, "peek-remote", "$projectroot/$project/"
|
||||
or return;
|
||||
|
||||
while (my $line = <$fd>) {
|
||||
chomp $line;
|
||||
|
@ -1125,7 +1130,8 @@ sub parse_ls_tree_line ($;%) {
|
|||
## parse to array of hashes functions
|
||||
|
||||
sub git_get_refs_list {
|
||||
my $ref_dir = shift;
|
||||
my $type = shift || "";
|
||||
my %refs;
|
||||
my @reflist;
|
||||
|
||||
my @refs;
|
||||
|
@ -1133,14 +1139,21 @@ sub git_get_refs_list {
|
|||
or return;
|
||||
while (my $line = <$fd>) {
|
||||
chomp $line;
|
||||
if ($line =~ m/^([0-9a-fA-F]{40})\t$ref_dir\/?([^\^]+)$/) {
|
||||
push @refs, { hash => $1, name => $2 };
|
||||
} elsif ($line =~ m/^[0-9a-fA-F]{40}\t$ref_dir\/?(.*)\^\{\}$/ &&
|
||||
$1 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";
|
||||
if ($line =~ m/^([0-9a-fA-F]{40})\trefs\/($type\/?([^\^]+))(\^\{\})?$/) {
|
||||
if (defined $refs{$1}) {
|
||||
push @{$refs{$1}}, $2;
|
||||
} else {
|
||||
$refs{$1} = [ $2 ];
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -1156,7 +1169,7 @@ sub git_get_refs_list {
|
|||
}
|
||||
# sort refs by age
|
||||
@reflist = sort {$b->{'epoch'} <=> $a->{'epoch'}} @reflist;
|
||||
return \@reflist;
|
||||
return (\@reflist, \%refs);
|
||||
}
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
|
@ -1197,7 +1210,7 @@ sub mimetype_guess_file {
|
|||
}
|
||||
close(MIME);
|
||||
|
||||
$filename =~ /\.(.*?)$/;
|
||||
$filename =~ /\.([^.]*)$/;
|
||||
return $mimemap{$1};
|
||||
}
|
||||
|
||||
|
@ -1259,7 +1272,7 @@ sub git_header_html {
|
|||
if (defined $action) {
|
||||
$title .= "/$action";
|
||||
if (defined $file_name) {
|
||||
$title .= " - $file_name";
|
||||
$title .= " - " . esc_html($file_name);
|
||||
if ($action eq "tree" && $file_name !~ m|/$|) {
|
||||
$title .= "/";
|
||||
}
|
||||
|
@ -1496,12 +1509,15 @@ sub git_print_page_path {
|
|||
my $fullname = '';
|
||||
|
||||
print "<div class=\"page_path\">";
|
||||
print $cgi->a({-href => href(action=>"tree", hash_base=>$hb),
|
||||
-title => 'tree root'}, "[$project]");
|
||||
print " / ";
|
||||
foreach my $dir (@dirname) {
|
||||
$fullname .= $dir . '/';
|
||||
$fullname .= ($fullname ? '/' : '') . $dir;
|
||||
print $cgi->a({-href => href(action=>"tree", file_name=>$fullname,
|
||||
hash_base=>$hb),
|
||||
-title => $fullname}, esc_html($dir));
|
||||
print "/";
|
||||
print " / ";
|
||||
}
|
||||
if (defined $type && $type eq 'blob') {
|
||||
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,
|
||||
hash_base=>$hb),
|
||||
-title => $name}, esc_html($basename));
|
||||
print "/";
|
||||
} else {
|
||||
print esc_html($basename);
|
||||
}
|
||||
|
@ -1950,9 +1965,6 @@ sub git_shortlog_body {
|
|||
# uses global variable $project
|
||||
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;
|
||||
$to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);
|
||||
|
||||
|
@ -1978,10 +1990,8 @@ sub git_shortlog_body {
|
|||
print "</td>\n" .
|
||||
"<td class=\"link\">" .
|
||||
$cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " .
|
||||
$cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff");
|
||||
if ($have_snapshot) {
|
||||
print " | " . $cgi->a({-href => href(action=>"snapshot", hash=>$commit)}, "snapshot");
|
||||
}
|
||||
$cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " .
|
||||
$cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree");
|
||||
print "</td>\n" .
|
||||
"</tr>\n";
|
||||
}
|
||||
|
@ -2120,14 +2130,14 @@ sub git_tags_body {
|
|||
|
||||
sub git_heads_body {
|
||||
# uses global variable $project
|
||||
my ($taglist, $head, $from, $to, $extra) = @_;
|
||||
my ($headlist, $head, $from, $to, $extra) = @_;
|
||||
$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";
|
||||
my $alternate = 0;
|
||||
for (my $i = $from; $i <= $to; $i++) {
|
||||
my $entry = $taglist->[$i];
|
||||
my $entry = $headlist->[$i];
|
||||
my %tag = %$entry;
|
||||
my $curr = $tag{'id'} eq $head;
|
||||
if ($alternate) {
|
||||
|
@ -2143,7 +2153,8 @@ sub git_heads_body {
|
|||
"</td>\n" .
|
||||
"<td class=\"link\">" .
|
||||
$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" .
|
||||
"</tr>";
|
||||
}
|
||||
|
@ -2257,7 +2268,8 @@ sub git_project_list {
|
|||
"<td class=\"link\">" .
|
||||
$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=>"log")}, "log") .
|
||||
$cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") . " | " .
|
||||
$cgi->a({-href => href(project=>$pr->{'path'}, action=>"tree")}, "tree") .
|
||||
"</td>\n" .
|
||||
"</tr>\n";
|
||||
}
|
||||
|
@ -2297,7 +2309,19 @@ sub git_summary {
|
|||
|
||||
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_print_page_nav('summary','', $head);
|
||||
|
||||
|
@ -2327,17 +2351,15 @@ sub git_summary {
|
|||
git_shortlog_body(\@revlist, 0, 15, $refs,
|
||||
$cgi->a({-href => href(action=>"shortlog")}, "..."));
|
||||
|
||||
my $taglist = git_get_refs_list("refs/tags");
|
||||
if (defined @$taglist) {
|
||||
if (@taglist) {
|
||||
git_print_header_div('tags');
|
||||
git_tags_body($taglist, 0, 15,
|
||||
git_tags_body(\@taglist, 0, 15,
|
||||
$cgi->a({-href => href(action=>"tags")}, "..."));
|
||||
}
|
||||
|
||||
my $headlist = git_get_refs_list("refs/heads");
|
||||
if (defined @$headlist) {
|
||||
if (@headlist) {
|
||||
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")}, "..."));
|
||||
}
|
||||
|
||||
|
@ -2398,15 +2420,18 @@ sub git_blame2 {
|
|||
if ($ftype !~ "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");
|
||||
git_header_html();
|
||||
my $formats_nav =
|
||||
$cgi->a({-href => href(action=>"blob", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)},
|
||||
"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)},
|
||||
"head");
|
||||
"HEAD");
|
||||
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_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)},
|
||||
"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)},
|
||||
"head");
|
||||
"HEAD");
|
||||
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_page_path($file_name, 'blob', $hash_base);
|
||||
|
@ -2548,8 +2576,8 @@ sub git_tags {
|
|||
git_print_page_nav('','', $head,undef,$head);
|
||||
git_print_header_div('summary', $project);
|
||||
|
||||
my $taglist = git_get_refs_list("refs/tags");
|
||||
if (defined @$taglist) {
|
||||
my ($taglist) = git_get_refs_list("tags");
|
||||
if (@$taglist) {
|
||||
git_tags_body($taglist);
|
||||
}
|
||||
git_footer_html();
|
||||
|
@ -2561,9 +2589,9 @@ sub git_heads {
|
|||
git_print_page_nav('','', $head,undef,$head);
|
||||
git_print_header_div('summary', $project);
|
||||
|
||||
my $taglist = git_get_refs_list("refs/heads");
|
||||
if (defined @$taglist) {
|
||||
git_heads_body($taglist, $head);
|
||||
my ($headlist) = git_get_refs_list("heads");
|
||||
if (@$headlist) {
|
||||
git_heads_body($headlist, $head);
|
||||
}
|
||||
git_footer_html();
|
||||
}
|
||||
|
@ -2646,16 +2674,20 @@ sub git_blob {
|
|||
" | ";
|
||||
}
|
||||
$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",
|
||||
hash=>$hash, file_name=>$file_name)},
|
||||
"plain") .
|
||||
"raw") .
|
||||
" | " .
|
||||
$cgi->a({-href => href(action=>"blob",
|
||||
hash_base=>"HEAD", file_name=>$file_name)},
|
||||
"head");
|
||||
"HEAD");
|
||||
} else {
|
||||
$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_header_div('commit', esc_html($co{'title'}), $hash_base);
|
||||
|
@ -2681,6 +2713,9 @@ sub git_blob {
|
|||
}
|
||||
|
||||
sub git_tree {
|
||||
my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
|
||||
my $have_snapshot = (defined $ctype && defined $suffix);
|
||||
|
||||
if (!defined $hash) {
|
||||
$hash = git_get_head_hash($project);
|
||||
if (defined $file_name) {
|
||||
|
@ -2704,7 +2739,23 @@ sub git_tree {
|
|||
my $base = "";
|
||||
my ($have_blame) = gitweb_check_feature('blame');
|
||||
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);
|
||||
} else {
|
||||
undef $hash_base;
|
||||
|
@ -2809,6 +2860,8 @@ sub git_log {
|
|||
$cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") .
|
||||
" | " .
|
||||
$cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") .
|
||||
" | " .
|
||||
$cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree") .
|
||||
"<br/>\n" .
|
||||
"</div>\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 $have_snapshot = (defined $ctype && defined $suffix);
|
||||
|
||||
my $formats_nav = '';
|
||||
my @views_nav = ();
|
||||
if (defined $file_name && defined $co{'parent'}) {
|
||||
my $parent = $co{'parent'};
|
||||
$formats_nav .=
|
||||
push @views_nav,
|
||||
$cgi->a({-href => href(action=>"blame", hash_parent=>$parent, file_name=>$file_name)},
|
||||
"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_print_page_nav('commit', defined $co{'parent'} ? '' : 'commitdiff',
|
||||
$hash, $co{'tree'}, $hash,
|
||||
$formats_nav);
|
||||
join (' | ', @views_nav));
|
||||
|
||||
if (defined $co{'parent'}) {
|
||||
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_base=>$hash_base, hash_parent_base=>$hash_parent_base,
|
||||
file_name=>$file_name, file_parent=>$file_parent)},
|
||||
"plain");
|
||||
"raw");
|
||||
git_header_html(undef, $expires);
|
||||
if (defined $hash_base && (my %co = parse_commit($hash_base))) {
|
||||
git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
|
||||
|
@ -3058,7 +3116,7 @@ sub git_blobdiff {
|
|||
-type => 'text/plain',
|
||||
-charset => 'utf-8',
|
||||
-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";
|
||||
|
||||
|
@ -3078,8 +3136,8 @@ sub git_blobdiff {
|
|||
|
||||
} else {
|
||||
while (my $line = <$fd>) {
|
||||
$line =~ s!a/($hash|$hash_parent)!a/$diffinfo{'from_file'}!g;
|
||||
$line =~ s!b/($hash|$hash_parent)!b/$diffinfo{'to_file'}!g;
|
||||
$line =~ s!a/($hash|$hash_parent)!'a/'.esc_html($diffinfo{'from_file'})!eg;
|
||||
$line =~ s!b/($hash|$hash_parent)!'b/'.esc_html($diffinfo{'to_file'})!eg;
|
||||
|
||||
print $line;
|
||||
|
||||
|
@ -3141,7 +3199,7 @@ sub git_commitdiff {
|
|||
my $formats_nav =
|
||||
$cgi->a({-href => href(action=>"commitdiff_plain",
|
||||
hash=>$hash, hash_parent=>$hash_parent)},
|
||||
"plain");
|
||||
"raw");
|
||||
|
||||
git_header_html(undef, $expires);
|
||||
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(.*)$/)) {
|
||||
next;
|
||||
}
|
||||
my $file = validate_input(unquote($7));
|
||||
my $file = esc_html(unquote($7));
|
||||
$file = decode("utf8", $file, Encode::FB_DEFAULT);
|
||||
print "$file<br/>\n";
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
4
http.h
|
@ -22,6 +22,10 @@
|
|||
#define NO_CURL_EASY_DUPHANDLE
|
||||
#endif
|
||||
|
||||
#if LIBCURL_VERSION_NUM < 0x070a03
|
||||
#define CURLE_HTTP_RETURNED_ERROR CURLE_HTTP_NOT_FOUND
|
||||
#endif
|
||||
|
||||
struct slot_results
|
||||
{
|
||||
CURLcode curl_result;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
19
pack-check.c
19
pack-check.c
|
@ -42,16 +42,16 @@ static int verify_packfile(struct packed_git *p)
|
|||
*/
|
||||
for (i = err = 0; i < nr_objects; i++) {
|
||||
unsigned char sha1[20];
|
||||
struct pack_entry e;
|
||||
void *data;
|
||||
char type[20];
|
||||
unsigned long size;
|
||||
unsigned long size, offset;
|
||||
|
||||
if (nth_packed_object_sha1(p, i, sha1))
|
||||
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");
|
||||
data = unpack_entry_gently(&e, type, &size);
|
||||
data = unpack_entry_gently(p, offset, type, &size);
|
||||
if (!data) {
|
||||
err = error("cannot unpack %s from %s",
|
||||
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++) {
|
||||
unsigned char sha1[20], base_sha1[20];
|
||||
struct pack_entry e;
|
||||
char type[20];
|
||||
unsigned long size;
|
||||
unsigned long store_size;
|
||||
unsigned long offset;
|
||||
unsigned int delta_chain_length;
|
||||
|
||||
if (nth_packed_object_sha1(p, i, sha1))
|
||||
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");
|
||||
|
||||
packed_object_info_detail(&e, type, &size, &store_size,
|
||||
packed_object_info_detail(p, offset, type, &size, &store_size,
|
||||
&delta_chain_length,
|
||||
base_sha1);
|
||||
printf("%s ", sha1_to_hex(sha1));
|
||||
if (!delta_chain_length)
|
||||
printf("%-6s %lu %u\n", type, size, e.offset);
|
||||
printf("%-6s %lu %lu\n", type, size, offset);
|
||||
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));
|
||||
if (delta_chain_length < MAX_CHAIN)
|
||||
chain_histogram[delta_chain_length]++;
|
||||
|
|
2
pack.h
2
pack.h
|
@ -7,7 +7,7 @@
|
|||
* Packed object header
|
||||
*/
|
||||
#define PACK_SIGNATURE 0x5041434b /* "PACK" */
|
||||
#define PACK_VERSION 2
|
||||
#define PACK_VERSION 3
|
||||
#define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3))
|
||||
struct pack_header {
|
||||
unsigned int hdr_signature;
|
||||
|
|
|
@ -347,11 +347,13 @@ int add_file_to_index(const char *path, int verbose)
|
|||
ce->ce_mode = create_ce_mode(st.st_mode);
|
||||
if (!trust_executable_bit) {
|
||||
/* 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);
|
||||
if (pos >= 0)
|
||||
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))
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#include "refs.h"
|
||||
#include "pkt-line.h"
|
||||
#include "run-command.h"
|
||||
#include "commit.h"
|
||||
#include "object.h"
|
||||
|
||||
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, "
|
||||
"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)) {
|
||||
cmd->error_string = "hook declined";
|
||||
return error("hook declined to update %s", name);
|
||||
|
|
69
revision.c
69
revision.c
|
@ -6,6 +6,8 @@
|
|||
#include "diff.h"
|
||||
#include "refs.h"
|
||||
#include "revision.h"
|
||||
#include <regex.h>
|
||||
#include "grep.h"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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);
|
||||
if (opts > 0) {
|
||||
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)
|
||||
die("diff_setup_done failed");
|
||||
|
||||
if (revs->grep_filter)
|
||||
compile_grep_patterns(revs->grep_filter);
|
||||
|
||||
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_list *list = revs->commits;
|
||||
|
@ -1105,6 +1172,8 @@ struct commit *get_revision(struct rev_info *revs)
|
|||
if (revs->no_merges &&
|
||||
commit->parents && commit->parents->next)
|
||||
continue;
|
||||
if (!commit_match(commit, revs))
|
||||
continue;
|
||||
if (revs->prune_fn && revs->dense) {
|
||||
/* Commit without changes? */
|
||||
if (!(commit->object.flags & TREECHANGE)) {
|
||||
|
|
|
@ -71,6 +71,9 @@ struct rev_info {
|
|||
const char *add_signoff;
|
||||
const char *extra_headers;
|
||||
|
||||
/* Filter by commit log message */
|
||||
struct grep_opt *grep_filter;
|
||||
|
||||
/* special limits */
|
||||
int max_count;
|
||||
unsigned long max_age;
|
||||
|
|
2
setup.c
2
setup.c
|
@ -244,6 +244,8 @@ int check_repository_format_version(const char *var, const char *value)
|
|||
repository_format_version = git_config_int(var, value);
|
||||
else if (strcmp(var, "core.sharedrepository") == 0)
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
257
sha1_file.c
257
sha1_file.c
|
@ -26,44 +26,40 @@ const unsigned char null_sha1[20];
|
|||
|
||||
static unsigned int sha1_file_open_flag = O_NOATIME;
|
||||
|
||||
static inline unsigned int hexval(unsigned int c)
|
||||
{
|
||||
static signed char val[256] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 00-07 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 08-0f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 10-17 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 18-1f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 20-27 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 28-2f */
|
||||
0, 1, 2, 3, 4, 5, 6, 7, /* 30-37 */
|
||||
8, 9, -1, -1, -1, -1, -1, -1, /* 38-3f */
|
||||
-1, 10, 11, 12, 13, 14, 15, -1, /* 40-47 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 48-4f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 50-57 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 58-5f */
|
||||
-1, 10, 11, 12, 13, 14, 15, -1, /* 60-67 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 68-67 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 70-77 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 78-7f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 80-87 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 88-8f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 90-97 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 98-9f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* a0-a7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* a8-af */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* b0-b7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* b8-bf */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* c0-c7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* c8-cf */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* d0-d7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* d8-df */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* e0-e7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* e8-ef */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* f0-f7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* f8-ff */
|
||||
};
|
||||
return val[c];
|
||||
}
|
||||
signed char hexval_table[256] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 00-07 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 08-0f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 10-17 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 18-1f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 20-27 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 28-2f */
|
||||
0, 1, 2, 3, 4, 5, 6, 7, /* 30-37 */
|
||||
8, 9, -1, -1, -1, -1, -1, -1, /* 38-3f */
|
||||
-1, 10, 11, 12, 13, 14, 15, -1, /* 40-47 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 48-4f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 50-57 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 58-5f */
|
||||
-1, 10, 11, 12, 13, 14, 15, -1, /* 60-67 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 68-67 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 70-77 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 78-7f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 80-87 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 88-8f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 90-97 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 98-9f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* a0-a7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* a8-af */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* b0-b7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* b8-bf */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* c0-c7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* c8-cf */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* d0-d7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* d8-df */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* e0-e7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* e8-ef */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* f0-f7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* f8-ff */
|
||||
};
|
||||
|
||||
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 */
|
||||
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);
|
||||
|
||||
static int packed_delta_info(unsigned char *base_sha1,
|
||||
unsigned long delta_size,
|
||||
unsigned long left,
|
||||
static int packed_delta_info(struct packed_git *p,
|
||||
unsigned long offset,
|
||||
char *type,
|
||||
unsigned long *sizep,
|
||||
struct packed_git *p)
|
||||
unsigned long *sizep)
|
||||
{
|
||||
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");
|
||||
|
||||
/* 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",
|
||||
sha1_to_hex(base_sha1));
|
||||
offset += 20;
|
||||
|
||||
/* We choose to only get the type of the base object and
|
||||
* ignore potentially corrupt pack file that expects the delta
|
||||
* based on a base with a wrong size. This saves tons of
|
||||
* inflate() calls.
|
||||
*/
|
||||
|
||||
if (packed_object_info(&base_ent, type, NULL))
|
||||
if (packed_object_info(p, base_offset, type, NULL))
|
||||
die("cannot get info for delta-pack base");
|
||||
|
||||
if (sizep) {
|
||||
|
@ -926,8 +921,8 @@ static int packed_delta_info(unsigned char *base_sha1,
|
|||
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
|
||||
data = stream.next_in = base_sha1 + 20;
|
||||
stream.avail_in = left - 20;
|
||||
stream.next_in = (unsigned char *) p->pack_base + offset;
|
||||
stream.avail_in = p->pack_size - offset;
|
||||
stream.next_out = 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;
|
||||
}
|
||||
|
||||
void packed_object_info_detail(struct pack_entry *e,
|
||||
void packed_object_info_detail(struct packed_git *p,
|
||||
unsigned long offset,
|
||||
char *type,
|
||||
unsigned long *size,
|
||||
unsigned long *store_size,
|
||||
unsigned int *delta_chain_length,
|
||||
unsigned char *base_sha1)
|
||||
{
|
||||
struct packed_git *p = e->p;
|
||||
unsigned long offset;
|
||||
unsigned char *pack;
|
||||
unsigned long val;
|
||||
unsigned char *next_sha1;
|
||||
enum object_type kind;
|
||||
|
||||
offset = unpack_object_header(p, e->offset, &kind, size);
|
||||
pack = (unsigned char *) p->pack_base + offset;
|
||||
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;
|
||||
*delta_chain_length = 0;
|
||||
offset = unpack_object_header(p, offset, &kind, size);
|
||||
|
||||
find_pack_entry_one(pack, &base_ent, p);
|
||||
offset = unpack_object_header(p, base_ent.offset,
|
||||
&kind, &junk);
|
||||
pack = (unsigned char *) p->pack_base + offset;
|
||||
chain_length++;
|
||||
} while (kind == OBJ_DELTA);
|
||||
*delta_chain_length = chain_length;
|
||||
for (;;) {
|
||||
switch (kind) {
|
||||
default:
|
||||
die("corrupted pack file %s containing object of kind %d",
|
||||
p->pack_name, kind);
|
||||
case OBJ_COMMIT:
|
||||
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)
|
||||
{
|
||||
struct packed_git *p = entry->p;
|
||||
unsigned long offset, size, left;
|
||||
unsigned char *pack;
|
||||
unsigned long size;
|
||||
enum object_type kind;
|
||||
int retval;
|
||||
|
||||
if (use_packed_git(p))
|
||||
die("cannot map packed file");
|
||||
offset = unpack_object_header(p, offset, &kind, &size);
|
||||
|
||||
offset = unpack_object_header(p, entry->offset, &kind, &size);
|
||||
pack = (unsigned char *) p->pack_base + offset;
|
||||
left = p->pack_size - offset;
|
||||
if (kind == OBJ_DELTA)
|
||||
return packed_delta_info(p, offset, type, sizep);
|
||||
|
||||
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_TREE:
|
||||
case OBJ_BLOB:
|
||||
|
@ -1070,7 +1050,6 @@ static int packed_object_info(struct pack_entry *entry,
|
|||
}
|
||||
if (sizep)
|
||||
*sizep = size;
|
||||
unuse_packed_git(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1107,25 +1086,26 @@ static void *unpack_delta_entry(struct packed_git *p,
|
|||
char *type,
|
||||
unsigned long *sizep)
|
||||
{
|
||||
struct pack_entry base_ent;
|
||||
void *delta_data, *result, *base;
|
||||
unsigned long result_size, base_size;
|
||||
unsigned char* base_sha1;
|
||||
unsigned long result_size, base_size, base_offset;
|
||||
unsigned char *base_sha1;
|
||||
|
||||
if ((offset + 20) >= p->pack_size)
|
||||
if (p->pack_size < offset + 20)
|
||||
die("truncated pack file");
|
||||
|
||||
/* The base entry _must_ be in the same pack */
|
||||
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",
|
||||
sha1_to_hex(base_sha1));
|
||||
base = unpack_entry_gently(&base_ent, type, &base_size);
|
||||
if (!base)
|
||||
die("failed to read delta-pack base object %s",
|
||||
sha1_to_hex(base_sha1));
|
||||
offset += 20;
|
||||
|
||||
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,
|
||||
delta_data, delta_size,
|
||||
&result_size);
|
||||
|
@ -1145,7 +1125,7 @@ static void *unpack_entry(struct pack_entry *entry,
|
|||
|
||||
if (use_packed_git(p))
|
||||
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);
|
||||
if (!retval)
|
||||
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 */
|
||||
void *unpack_entry_gently(struct pack_entry *entry,
|
||||
void *unpack_entry_gently(struct packed_git *p, unsigned long offset,
|
||||
char *type, unsigned long *sizep)
|
||||
{
|
||||
struct packed_git *p = entry->p;
|
||||
unsigned long offset, size;
|
||||
unsigned long size;
|
||||
enum object_type kind;
|
||||
|
||||
offset = unpack_object_header(p, entry->offset, &kind, &size);
|
||||
offset = unpack_object_header(p, offset, &kind, &size);
|
||||
switch (kind) {
|
||||
case OBJ_DELTA:
|
||||
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;
|
||||
}
|
||||
|
||||
int find_pack_entry_one(const unsigned char *sha1,
|
||||
struct pack_entry *e, struct packed_git *p)
|
||||
unsigned long find_pack_entry_one(const unsigned char *sha1,
|
||||
struct packed_git *p)
|
||||
{
|
||||
unsigned int *level1_ofs = p->index_base;
|
||||
int hi = ntohl(level1_ofs[*sha1]);
|
||||
|
@ -1203,12 +1182,8 @@ int find_pack_entry_one(const unsigned char *sha1,
|
|||
do {
|
||||
int mi = (lo + hi) / 2;
|
||||
int cmp = hashcmp((unsigned char *)index + (24 * mi) + 4, sha1);
|
||||
if (!cmp) {
|
||||
e->offset = ntohl(*((unsigned int *) ((char *) index + (24 * mi))));
|
||||
hashcpy(e->sha1, sha1);
|
||||
e->p = p;
|
||||
return 1;
|
||||
}
|
||||
if (!cmp)
|
||||
return ntohl(*((unsigned int *) ((char *) index + (24 * mi))));
|
||||
if (cmp > 0)
|
||||
hi = mi;
|
||||
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)
|
||||
{
|
||||
struct packed_git *p;
|
||||
unsigned long offset;
|
||||
|
||||
prepare_packed_git();
|
||||
|
||||
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)
|
||||
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 0;
|
||||
}
|
||||
|
@ -1241,10 +1223,9 @@ struct packed_git *find_sha1_pack(const unsigned char *sha1,
|
|||
struct packed_git *packs)
|
||||
{
|
||||
struct packed_git *p;
|
||||
struct pack_entry e;
|
||||
|
||||
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 NULL;
|
||||
|
@ -1263,12 +1244,16 @@ int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep
|
|||
if (!map) {
|
||||
struct pack_entry e;
|
||||
|
||||
if (find_pack_entry(sha1, &e, NULL))
|
||||
return packed_object_info(&e, type, sizep);
|
||||
reprepare_packed_git();
|
||||
if (find_pack_entry(sha1, &e, NULL))
|
||||
return packed_object_info(&e, type, sizep);
|
||||
return error("unable to find %s", sha1_to_hex(sha1));
|
||||
if (!find_pack_entry(sha1, &e, NULL)) {
|
||||
reprepare_packed_git();
|
||||
if (!find_pack_entry(sha1, &e, NULL))
|
||||
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)
|
||||
status = error("unable to unpack %s header",
|
||||
|
|
26
sha1_name.c
26
sha1_name.c
|
@ -431,6 +431,26 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
|
|||
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)
|
||||
{
|
||||
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);
|
||||
if (!ret)
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -61,4 +61,16 @@ test_expect_success \
|
|||
test -f .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
|
||||
|
|
|
@ -19,4 +19,26 @@ test_expect_success \
|
|||
'Test that "git-add -- -q" works' \
|
||||
'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
|
||||
|
|
|
@ -64,4 +64,18 @@ test_expect_success \
|
|||
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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -31,6 +31,15 @@ test_expect_success setup '
|
|||
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" '
|
||||
|
||||
fill 0 1 2 3 4 5 >one &&
|
||||
|
|
|
@ -34,7 +34,7 @@ export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
|
|||
export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
|
||||
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)
|
||||
echo "* warning: Some tests will not work if GIT_TRACE" \
|
||||
"is set as to trace on STDERR ! *"
|
||||
|
@ -211,7 +211,7 @@ export PATH GIT_EXEC_PATH
|
|||
PYTHON=`sed -e '1{
|
||||
s/^#!//
|
||||
q
|
||||
}' ../git-merge-recursive` || {
|
||||
}' ../git-merge-recursive-old` || {
|
||||
error "You haven't built things yet, have you?"
|
||||
}
|
||||
"$PYTHON" -c 'import subprocess' 2>/dev/null || {
|
||||
|
|
Загрузка…
Ссылка в новой задаче