summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Documentation/diff-options.txt3
-rw-r--r--Documentation/git-apply.txt46
-rw-r--r--Documentation/git-blame.txt29
-rw-r--r--Documentation/git-daemon.txt14
-rw-r--r--Documentation/git-grep.txt8
-rw-r--r--Documentation/git-ls-remote.txt18
-rw-r--r--Documentation/git-svn.txt248
-rw-r--r--Documentation/git-update-index.txt4
-rw-r--r--Documentation/git-zip-tree.txt67
-rw-r--r--Documentation/git.txt6
-rw-r--r--Documentation/gitk.txt60
-rw-r--r--Documentation/technical/racy-git.txt193
-rw-r--r--INSTALL2
-rw-r--r--Makefile139
-rwxr-xr-xTEST14
-rw-r--r--blame.c17
-rw-r--r--builtin-apply.c443
-rw-r--r--builtin-cat-file.c40
-rw-r--r--builtin-checkout-index.c (renamed from checkout-index.c)29
-rw-r--r--builtin-commit-tree.c2
-rw-r--r--builtin-count-objects.c (renamed from builtin-count.c)0
-rw-r--r--builtin-diff-files.c7
-rw-r--r--builtin-diff-stages.c2
-rw-r--r--builtin-diff.c16
-rw-r--r--builtin-fmt-merge-msg.c6
-rw-r--r--builtin-grep.c123
-rw-r--r--builtin-log.c4
-rw-r--r--builtin-ls-files.c27
-rw-r--r--builtin-ls-tree.c6
-rw-r--r--builtin-mailinfo.c6
-rw-r--r--builtin-mv.c133
-rw-r--r--builtin-name-rev.c (renamed from name-rev.c)6
-rw-r--r--builtin-pack-objects.c (renamed from pack-objects.c)62
-rw-r--r--builtin-prune.c2
-rw-r--r--builtin-push.c24
-rw-r--r--builtin-read-tree.c4
-rw-r--r--builtin-repo-config.c16
-rw-r--r--builtin-rev-list.c6
-rw-r--r--builtin-rev-parse.c10
-rw-r--r--builtin-show-branch.c10
-rw-r--r--builtin-symbolic-ref.c (renamed from symbolic-ref.c)4
-rw-r--r--builtin-tar-tree.c31
-rw-r--r--builtin-unpack-objects.c (renamed from unpack-objects.c)15
-rw-r--r--builtin-update-index.c14
-rw-r--r--builtin-update-ref.c2
-rw-r--r--builtin-verify-pack.c (renamed from verify-pack.c)15
-rw-r--r--builtin-write-tree.c4
-rw-r--r--builtin-zip-tree.c353
-rw-r--r--builtin.h81
-rw-r--r--cache-tree.c6
-rw-r--r--cache.h23
-rw-r--r--check-racy.c28
-rw-r--r--combine-diff.c41
-rw-r--r--commit.c24
-rw-r--r--config.mak.in16
-rw-r--r--configure.ac240
-rw-r--r--connect.c23
-rw-r--r--contrib/emacs/vc-git.el2
-rw-r--r--contrib/gitview/gitview.txt56
-rw-r--r--convert-objects.c8
-rw-r--r--csum-file.c6
-rw-r--r--daemon.c76
-rw-r--r--date.c69
-rw-r--r--describe.c10
-rw-r--r--diff-delta.c4
-rw-r--r--diff-lib.c7
-rw-r--r--diff.c227
-rw-r--r--diff.h3
-rw-r--r--diffcore-break.c2
-rw-r--r--diffcore-rename.c2
-rw-r--r--dir.c6
-rw-r--r--dump-cache-tree.c2
-rw-r--r--entry.c4
-rw-r--r--environment.c17
-rw-r--r--exec_cmd.c2
-rw-r--r--fetch-clone.c3
-rw-r--r--fetch-pack.c8
-rw-r--r--fetch.c2
-rw-r--r--fsck-objects.c23
-rwxr-xr-xgit-cherry.sh3
-rw-r--r--git-compat-util.h3
-rwxr-xr-xgit-reset.sh3
-rwxr-xr-xgit-send-email.perl2
-rwxr-xr-xgit-svn.perl40
-rw-r--r--git.c101
-rw-r--r--gitweb/README30
-rw-r--r--gitweb/git-logo.pngbin0 -> 208 bytes
-rw-r--r--gitweb/gitweb.css42
-rwxr-xr-xgitweb/gitweb.perl (renamed from gitweb/gitweb.cgi)3080
-rw-r--r--help.c (renamed from builtin-help.c)2
-rw-r--r--http-fetch.c16
-rw-r--r--http-push.c76
-rw-r--r--index-pack.c16
-rw-r--r--local-fetch.c8
-rw-r--r--log-tree.c44
-rw-r--r--merge-base.c2
-rw-r--r--merge-index.c5
-rw-r--r--merge-tree.c2
-rw-r--r--mktree.c5
-rw-r--r--object.c4
-rw-r--r--pack-check.c6
-rw-r--r--pack-redundant.c18
-rw-r--r--pager.c4
-rw-r--r--patch-id.c2
-rw-r--r--read-cache.c56
-rw-r--r--receive-pack.c10
-rw-r--r--refs.c10
-rw-r--r--revision.c2
-rw-r--r--run-command.c8
-rw-r--r--send-pack.c17
-rw-r--r--sha1_file.c222
-rw-r--r--sha1_name.c32
-rw-r--r--ssh-fetch.c10
-rw-r--r--ssh-upload.c4
-rwxr-xr-xt/t4116-apply-reverse.sh85
-rwxr-xr-xt/t4117-apply-reject.sh157
-rwxr-xr-xt/t7001-mv.sh8
-rwxr-xr-xt/t7002-grep.sh31
-rw-r--r--templates/hooks--commit-msg6
-rw-r--r--tree-diff.c15
-rw-r--r--tree-walk.c4
-rw-r--r--tree.c5
-rw-r--r--unpack-trees.c6
-rw-r--r--upload-pack.c116
-rw-r--r--write_or_die.c20
-rw-r--r--xdiff-interface.c12
127 files changed, 4799 insertions, 2865 deletions
diff --git a/.gitignore b/.gitignore
index 2299e36..3da0e5e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -126,6 +126,7 @@ git-verify-tag
git-whatchanged
git-write-tree
git-core-*/?*
+gitweb/gitweb.cgi
test-date
test-delta
test-dump-cache-tree
@@ -141,7 +142,7 @@ config.mak
autom4te.cache
config.log
config.status
-config.mak.in
config.mak.autogen
+config.mak.append
configure
git-blame
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 47ba9a4..b5d9763 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -36,6 +36,9 @@
Turn off colored diff, even when the configuration file
gives the default to color output.
+--color-words::
+ Show colored word diff, i.e. color words which have changed.
+
--no-renames::
Turn off rename detection, even when the configuration
file gives the default to do so.
diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
index 2ff7494..c76cfff 100644
--- a/Documentation/git-apply.txt
+++ b/Documentation/git-apply.txt
@@ -10,9 +10,10 @@ SYNOPSIS
--------
[verse]
'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] [--apply]
- [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM]
- [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>]
- [<patch>...]
+ [--no-add] [--index-info] [--allow-binary-replacement | --binary]
+ [-R | --reverse] [--reject] [-z] [-pNUM] [-CNUM] [--inaccurate-eof]
+ [--whitespace=<nowarn|warn|error|error-all|strip>] [--exclude=PATH]
+ [--cached] [--verbose] [<patch>...]
DESCRIPTION
-----------
@@ -55,6 +56,11 @@ OPTIONS
up-to-date, it is flagged as an error. This flag also
causes the index file to be updated.
+--cached::
+ Apply a patch without touching the working tree. Instead, take the
+ cached data, apply the patch, and store the result in the index,
+ without using the working tree. This implies '--index'.
+
--index-info::
Newer git-diff output has embedded 'index information'
for each blob to help identify the original version that
@@ -62,6 +68,16 @@ OPTIONS
the original version of the blob is available locally,
outputs information about them to the standard output.
+-R, --reverse::
+ Apply the patch in reverse.
+
+--reject::
+ For atomicity, gitlink:git-apply[1] by default fails the whole patch and
+ does not touch the working tree when some of the hunks
+ do not apply. This option makes it apply
+ the parts of the patch that are applicable, and leave the
+ rejected hunks in corresponding *.rej files.
+
-z::
When showing the index information, do not munge paths,
but use NUL terminated machine readable format. Without
@@ -80,8 +96,8 @@ OPTIONS
ever ignored.
--apply::
- If you use any of the options marked ``Turns off
- "apply"'' above, git-apply reads and outputs the
+ If you use any of the options marked "Turns off
+ 'apply'" above, gitlink:git-apply[1] reads and outputs the
information you asked without actually applying the
patch. Give this flag after those flags to also apply
the patch.
@@ -93,7 +109,7 @@ OPTIONS
the result with this option, which would apply the
deletion part but not addition part.
---allow-binary-replacement::
+--allow-binary-replacement, --binary::
When applying a patch, which is a git-enhanced patch
that was prepared to record the pre- and post-image object
name in full, and the path being patched exactly matches
@@ -104,13 +120,18 @@ OPTIONS
result. This allows binary files to be patched in a
very limited way.
+--exclude=<path-pattern>::
+ Don't apply changes to files matching the given path pattern. This can
+ be useful when importing patchsets, where you want to exclude certain
+ files or directories.
+
--whitespace=<option>::
When applying a patch, detect a new or modified line
that ends with trailing whitespaces (this includes a
line that solely consists of whitespaces). By default,
the command outputs warning messages and applies the
patch.
- When `git-apply` is used for statistics and not applying a
+ When gitlink:git-apply[1] is used for statistics and not applying a
patch, it defaults to `nowarn`.
You can use different `<option>` to control this
behavior:
@@ -124,6 +145,17 @@ OPTIONS
* `strip` outputs warnings for a few such errors, strips out the
trailing whitespaces and applies the patch.
+--inacurate-eof::
+ Under certain circumstances, some versions of diff do not correctly
+ detect a missing new-line at the end of the file. As a result, patches
+ created by such diff programs do not record incomplete lines
+ correctly. This option adds support for applying such patches by
+ working around this bug.
+
+--verbose::
+ Report progress to stderr. By default, only a message about the
+ current patch being applied will be printed. This option will cause
+ additional information to be reported.
Configuration
-------------
diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt
index bfed945..e1f8944 100644
--- a/Documentation/git-blame.txt
+++ b/Documentation/git-blame.txt
@@ -3,21 +3,38 @@ git-blame(1)
NAME
----
-git-blame - Blame file lines on commits
+git-blame - Show what revision and author last modified each line of a file
SYNOPSIS
--------
-git-blame file [options] file [revision]
+'git-blame' [-c] [-l] [-t] [-S <revs-file>] [--] <file> [<rev>]
DESCRIPTION
-----------
-Annotates each line in the given file with information from the commit
-which introduced the line. Start annotation from the given revision.
+
+Annotates each line in the given file with information from the revision which
+last modified the line. Optionally, start annotating from the given revision.
+
+This report doesn't tell you anything about lines which have been deleted or
+replaced; you need to use a tool such as gitlink:git-diff[1] or the "pickaxe"
+interface briefly mentioned in the following paragraph.
+
+Apart from supporting file annotation, git also supports searching the
+development history for when a code snippet occured in a change. This makes it
+possible to track when a code snippet was added to a file, moved or copied
+between files, and eventually deleted or replaced. It works by searching for
+a text string in the diff. A small example:
+
+-----------------------------------------------------------------------------
+$ git log --pretty=oneline -S'blame_usage'
+5040f17eba15504bad66b14a645bddd9b015ebb7 blame -S <ancestry-file>
+ea4c7f9bf69e781dd0cd88d2bccb2bf5cc15c9a7 git-blame: Make the output
+-----------------------------------------------------------------------------
OPTIONS
-------
-c, --compatibility::
- Use the same output mode as git-annotate (Default: off).
+ Use the same output mode as gitlink:git-annotate[1] (Default: off).
-l, --long::
Show long rev (Default: off).
@@ -26,7 +43,7 @@ OPTIONS
Show raw timestamp (Default: off).
-S, --rev-file <revs-file>::
- Use revs from revs-file instead of calling git-rev-list.
+ Use revs from revs-file instead of calling gitlink:git-rev-list[1].
-h, --help::
Show help message.
diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index 0f7d274..17619a3 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -11,7 +11,8 @@ 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]
- [--reuseaddr] [--detach] [--pid-file=file] [directory...]
+ [--reuseaddr] [--detach] [--pid-file=file]
+ [--user=user [--group=group]] [directory...]
DESCRIPTION
-----------
@@ -93,6 +94,17 @@ OPTIONS
--pid-file=file::
Save the process id in 'file'.
+--user=user, --group=group::
+ Change daemon's uid and gid before entering the service loop.
+ When only `--user` is given without `--group`, the
+ primary group ID for the user is used. The values of
+ the option are given to `getpwnam(3)` and `getgrnam(3)`
+ and numeric IDs are not supported.
++
+Giving these options is an error when used with `--inetd`; use
+the facility of inet daemon to achieve the same before spawning
+`git-daemon` if needed.
+
<directory>::
A directory to add to the whitelist of allowed directories. Unless
--strict-paths is specified this will also include subdirectories
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index dc76833..7545dd9 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -11,7 +11,7 @@ SYNOPSIS
[verse]
'git-grep' [--cached]
[-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp]
- [-v | --invert-match]
+ [-v | --invert-match] [--full-name]
[-E | --extended-regexp] [-G | --basic-regexp] [-F | --fixed-strings]
[-n] [-l | --files-with-matches] [-L | --files-without-match]
[-c | --count]
@@ -47,6 +47,12 @@ OPTIONS
-v | --invert-match::
Select non-matching lines.
+--full-name::
+ When run from a subdirectory, the command usually
+ outputs paths relative to the current directory. This
+ option forces paths to be output relative to the project
+ top directory.
+
-E | --extended-regexp | -G | --basic-regexp::
Use POSIX extended/basic regexp for patterns. Default
is to use basic regexp.
diff --git a/Documentation/git-ls-remote.txt b/Documentation/git-ls-remote.txt
index ae4c1a2..c8a4c5a 100644
--- a/Documentation/git-ls-remote.txt
+++ b/Documentation/git-ls-remote.txt
@@ -3,16 +3,19 @@ git-ls-remote(1)
NAME
----
-git-ls-remote - Look at references other repository has
+git-ls-remote - List references in a remote repository
SYNOPSIS
--------
-'git-ls-remote' [--heads] [--tags] <repository> <refs>...
+[verse]
+'git-ls-remote' [--heads] [--tags] [-u <exec> | --upload-pack <exec>]
+ <repository> <refs>...
DESCRIPTION
-----------
-Displays the references other repository has.
+Displays references available in a remote repository along with the associated
+commit IDs.
OPTIONS
@@ -23,9 +26,16 @@ OPTIONS
both, references stored in refs/heads and refs/tags are
displayed.
+-u <exec>, --upload-pack=<exec>::
+ Specify the full path of gitlink:git-upload-pack[1] on the remote
+ host. This allows listing references from repositories accessed via
+ SSH and where the SSH deamon does not use the PATH configured by the
+ user. Also see the '--exec' option for gitlink:git-peek-remote[1].
+
<repository>::
Location of the repository. The shorthand defined in
- $GIT_DIR/branches/ can be used.
+ $GIT_DIR/branches/ can be used. Use "." (dot) to list references in
+ the local repository.
<refs>...::
When unspecified, all references, after filtering done
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 7d86809..b7b63f7 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -12,10 +12,8 @@ SYNOPSIS
DESCRIPTION
-----------
git-svn is a simple conduit for changesets between a single Subversion
-branch and git.
-
-git-svn is not to be confused with git-svnimport. The were designed
-with very different goals in mind.
+branch and git. It is not to be confused with gitlink:git-svnimport[1].
+They were designed with very different goals in mind.
git-svn is designed for an individual developer who wants a
bidirectional flow of changesets between a single branch in Subversion
@@ -34,26 +32,38 @@ git-svnimport is designed for.
COMMANDS
--------
-init::
+--
+
+'init'::
Creates an empty git repository with additional metadata
directories for git-svn. The Subversion URL must be specified
as a command-line argument.
-fetch::
- Fetch unfetched revisions from the Subversion URL we are
- tracking. refs/remotes/git-svn will be updated to the
- latest revision.
+'fetch'::
+
+Fetch unfetched revisions from the Subversion URL we are
+tracking. refs/remotes/git-svn will be updated to the
+latest revision.
+
+Note: You should never attempt to modify the remotes/git-svn
+branch outside of git-svn. Instead, create a branch from
+remotes/git-svn and work on that branch. Use the 'commit'
+command (see below) to write git commits back to
+remotes/git-svn.
- Note: You should never attempt to modify the remotes/git-svn
- branch outside of git-svn. Instead, create a branch from
- remotes/git-svn and work on that branch. Use the 'commit'
- command (see below) to write git commits back to
- remotes/git-svn.
+See '<<fetch-args,Additional Fetch Arguments>>' if you are interested in
+manually joining branches on commit.
- See 'Additional Fetch Arguments' if you are interested in
- manually joining branches on commit.
+'dcommit'::
+ Commit all diffs from the current HEAD directly to the SVN
+ repository, and then rebase or reset (depending on whether or
+ not there is a diff between SVN and HEAD). It is recommended
+ that you run git-svn fetch and rebase (not pull) your commits
+ against the latest changes in the SVN repository.
+ This is advantageous over 'commit' (below) because it produces
+ cleaner, more linear history.
-commit::
+'commit'::
Commit specified commit or tree objects to SVN. This relies on
your imported fetch data being up-to-date. This makes
absolutely no attempts to do patching when committing to SVN, it
@@ -61,9 +71,9 @@ commit::
commit. All merging is assumed to have taken place
independently of git-svn functions.
-rebuild::
+'rebuild'::
Not a part of daily usage, but this is a useful command if
- you've just cloned a repository (using git-clone) that was
+ you've just cloned a repository (using gitlink:git-clone[1]) that was
tracked with git-svn. Unfortunately, git-clone does not clone
git-svn metadata and the svn working tree that git-svn uses for
its operations. This rebuilds the metadata so git-svn can
@@ -71,130 +81,170 @@ rebuild::
specified at the command-line if the directory/repository you're
tracking has moved or changed protocols.
-show-ignore::
+'show-ignore'::
Recursively finds and lists the svn:ignore property on
directories. The output is suitable for appending to
the $GIT_DIR/info/exclude file.
+--
+
OPTIONS
-------
+--
+
-r <ARG>::
--revision <ARG>::
- Only used with the 'fetch' command.
- Takes any valid -r<argument> svn would accept and passes it
- directly to svn. -r<ARG1>:<ARG2> ranges and "{" DATE "}" syntax
- is also supported. This is passed directly to svn, see svn
- documentation for more details.
+Only used with the 'fetch' command.
+
+Takes any valid -r<argument> svn would accept and passes it
+directly to svn. -r<ARG1>:<ARG2> ranges and "{" DATE "}" syntax
+is also supported. This is passed directly to svn, see svn
+documentation for more details.
- This can allow you to make partial mirrors when running fetch.
+This can allow you to make partial mirrors when running fetch.
-::
--stdin::
- Only used with the 'commit' command.
- Read a list of commits from stdin and commit them in reverse
- order. Only the leading sha1 is read from each line, so
- git-rev-list --pretty=oneline output can be used.
+Only used with the 'commit' command.
+
+Read a list of commits from stdin and commit them in reverse
+order. Only the leading sha1 is read from each line, so
+git-rev-list --pretty=oneline output can be used.
--rmdir::
- Only used with the 'commit' command.
- Remove directories from the SVN tree if there are no files left
- behind. SVN can version empty directories, and they are not
- removed by default if there are no files left in them. git
- cannot version empty directories. Enabling this flag will make
- the commit to SVN act like git.
+Only used with the 'commit' command.
+
+Remove directories from the SVN tree if there are no files left
+behind. SVN can version empty directories, and they are not
+removed by default if there are no files left in them. git
+cannot version empty directories. Enabling this flag will make
+the commit to SVN act like git.
- repo-config key: svn.rmdir
+repo-config key: svn.rmdir
-e::
--edit::
- Only used with the 'commit' command.
- Edit the commit message before committing to SVN. This is off by
- default for objects that are commits, and forced on when committing
- tree objects.
+Only used with the 'commit' command.
- repo-config key: svn.edit
+Edit the commit message before committing to SVN. This is off by
+default for objects that are commits, and forced on when committing
+tree objects.
+
+repo-config key: svn.edit
-l<num>::
--find-copies-harder::
- Both of these are only used with the 'commit' command.
- They are both passed directly to git-diff-tree see
- git-diff-tree(1) for more information.
+Both of these are only used with the 'commit' command.
+
+They are both passed directly to git-diff-tree see
+gitlink:git-diff-tree[1] for more information.
- repo-config key: svn.l
- repo-config key: svn.findcopiesharder
+[verse]
+repo-config key: svn.l
+repo-config key: svn.findcopiesharder
-A<filename>::
--authors-file=<filename>::
- Syntax is compatible with the files used by git-svnimport and
- git-cvsimport:
+Syntax is compatible with the files used by git-svnimport and
+git-cvsimport:
------------------------------------------------------------------------
-loginname = Joe User <user@example.com>
+ loginname = Joe User <user@example.com>
------------------------------------------------------------------------
- If this option is specified and git-svn encounters an SVN
- committer name that does not exist in the authors-file, git-svn
- will abort operation. The user will then have to add the
- appropriate entry. Re-running the previous git-svn command
- after the authors-file is modified should continue operation.
+If this option is specified and git-svn encounters an SVN
+committer name that does not exist in the authors-file, git-svn
+will abort operation. The user will then have to add the
+appropriate entry. Re-running the previous git-svn command
+after the authors-file is modified should continue operation.
+
+repo-config key: svn.authors-file
+
+-m::
+--merge::
+-s<strategy>::
+--strategy=<strategy>::
+
+These are only used with the 'dcommit' command.
- repo-config key: svn.authors-file
+Passed directly to git-rebase when using 'dcommit' if a
+'git-reset' cannot be used (see dcommit).
+
+-n::
+--dry-run::
+
+This is only used with the 'dcommit' command.
+
+Print out the series of git arguments that would show
+which diffs would be committed to SVN.
+
+--
ADVANCED OPTIONS
----------------
+--
+
-b<refname>::
--branch <refname>::
- Used with 'fetch' or 'commit'.
+Used with 'fetch' or 'commit'.
- This can be used to join arbitrary git branches to remotes/git-svn
- on new commits where the tree object is equivalent.
+This can be used to join arbitrary git branches to remotes/git-svn
+on new commits where the tree object is equivalent.
- When used with different GIT_SVN_ID values, tags and branches in
- SVN can be tracked this way, as can some merges where the heads
- end up having completely equivalent content. This can even be
- used to track branches across multiple SVN _repositories_.
+When used with different GIT_SVN_ID values, tags and branches in
+SVN can be tracked this way, as can some merges where the heads
+end up having completely equivalent content. This can even be
+used to track branches across multiple SVN _repositories_.
- This option may be specified multiple times, once for each
- branch.
+This option may be specified multiple times, once for each
+branch.
- repo-config key: svn.branch
+repo-config key: svn.branch
-i<GIT_SVN_ID>::
--id <GIT_SVN_ID>::
- This sets GIT_SVN_ID (instead of using the environment). See
- the section on "Tracking Multiple Repositories or Branches" for
- more information on using GIT_SVN_ID.
+
+This sets GIT_SVN_ID (instead of using the environment). See the
+section on
+'<<tracking-multiple-repos,Tracking Multiple Repositories or Branches>>'
+for more information on using GIT_SVN_ID.
+
+--
COMPATIBILITY OPTIONS
---------------------
+--
+
--upgrade::
- Only used with the 'rebuild' command.
+Only used with the 'rebuild' command.
- Run this if you used an old version of git-svn that used
- "git-svn-HEAD" instead of "remotes/git-svn" as the branch
- for tracking the remote.
+Run this if you used an old version of git-svn that used
+"git-svn-HEAD" instead of "remotes/git-svn" as the branch
+for tracking the remote.
--no-ignore-externals::
- Only used with the 'fetch' and 'rebuild' command.
+Only used with the 'fetch' and 'rebuild' command.
+
+By default, git-svn passes --ignore-externals to svn to avoid
+fetching svn:external trees into git. Pass this flag to enable
+externals tracking directly via git.
- By default, git-svn passes --ignore-externals to svn to avoid
- fetching svn:external trees into git. Pass this flag to enable
- externals tracking directly via git.
+Versions of svn that do not support --ignore-externals are
+automatically detected and this flag will be automatically
+enabled for them.
- Versions of svn that do not support --ignore-externals are
- automatically detected and this flag will be automatically
- enabled for them.
+Otherwise, do not enable this flag unless you know what you're
+doing.
- Otherwise, do not enable this flag unless you know what you're
- doing.
+repo-config key: svn.noignoreexternals
- repo-config key: svn.noignoreexternals
+--
Basic Examples
~~~~~~~~~~~~~~
@@ -212,12 +262,26 @@ Tracking and contributing to an Subversion managed-project:
git-svn commit <tree-ish> [<tree-ish_2> ...]
# Commit all the git commits from my-branch that don't exist in SVN:
git-svn commit remotes/git-svn..my-branch
-# Something is committed to SVN, pull the latest into your branch:
- git-svn fetch && git pull . remotes/git-svn
+# Something is committed to SVN, rebase the latest into your branch:
+ git-svn fetch && git rebase remotes/git-svn
# Append svn:ignore settings to the default git exclude file:
git-svn show-ignore >> .git/info/exclude
------------------------------------------------------------------------
+REBASE VS. PULL
+---------------
+
+Originally, git-svn recommended that the remotes/git-svn branch be
+pulled from. This is because the author favored 'git-svn commit B'
+to commit a single head rather than the 'git-svn commit A..B' notation
+to commit multiple commits.
+
+If you use 'git-svn commit A..B' to commit several diffs and you do not
+have the latest remotes/git-svn merged into my-branch, you should use
+'git rebase' to update your work branch instead of 'git pull'. 'pull'
+can cause non-linear history to be flattened when committing into SVN,
+which can lead to merge commits reversing previous commits in SVN.
+
DESIGN PHILOSOPHY
-----------------
Merge tracking in Subversion is lacking and doing branched development
@@ -226,6 +290,7 @@ any automated merge/branch tracking on the Subversion side and leaves it
entirely up to the user on the git side. It's simply not worth it to do
a useful translation when the original signal is weak.
+[[tracking-multiple-repos]]
TRACKING MULTIPLE REPOSITORIES OR BRANCHES
------------------------------------------
This is for advanced users, most users should ignore this section.
@@ -241,6 +306,7 @@ invocation. The interface branch will be remotes/$GIT_SVN_ID, instead of
remotes/git-svn. Any remotes/$GIT_SVN_ID branch should never be modified
by the user outside of git-svn commands.
+[[fetch-args]]
ADDITIONAL FETCH ARGUMENTS
--------------------------
This is for advanced users, most users should ignore this section.
@@ -251,11 +317,15 @@ optionally be specified in the form of sha1 hex sums at the
command-line. Unfetched SVN revisions may also be tied to particular
git commits with the following syntax:
+------------------------------------------------
svn_revision_number=git_commit_sha1
+------------------------------------------------
-This allows you to tie unfetched SVN revision 375 to your current HEAD::
+This allows you to tie unfetched SVN revision 375 to your current HEAD:
- `git-svn fetch 375=$(git-rev-parse HEAD)`
+------------------------------------------------
+ git-svn fetch 375=$(git-rev-parse HEAD)
+------------------------------------------------
Advanced Example: Tracking a Reorganized Repository
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -310,6 +380,10 @@ the possible corner cases (git doesn't do it, either). Renamed and
copied files are fully supported if they're similar enough for git to
detect them.
+SEE ALSO
+--------
+gitlink:git-rebase[1]
+
Author
------
Written by Eric Wong <normalperson@yhbt.net>.
diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt
index 3ae6e74..41bb7e1 100644
--- a/Documentation/git-update-index.txt
+++ b/Documentation/git-update-index.txt
@@ -15,7 +15,7 @@ SYNOPSIS
[--cacheinfo <mode> <object> <file>]\*
[--chmod=(+|-)x]
[--assume-unchanged | --no-assume-unchanged]
- [--really-refresh] [--unresolve] [--again]
+ [--really-refresh] [--unresolve] [--again | -g]
[--info-only] [--index-info]
[-z] [--stdin]
[--verbose]
@@ -80,7 +80,7 @@ OPTIONS
filesystem that has very slow lstat(2) system call
(e.g. cifs).
---again::
+--again, -g::
Runs `git-update-index` itself on the paths whose index
entries are different from those from the `HEAD` commit.
diff --git a/Documentation/git-zip-tree.txt b/Documentation/git-zip-tree.txt
new file mode 100644
index 0000000..2e9d981
--- /dev/null
+++ b/Documentation/git-zip-tree.txt
@@ -0,0 +1,67 @@
+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
+
diff --git a/Documentation/git.txt b/Documentation/git.txt
index bcf187a..a9c87e3 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -8,8 +8,9 @@ git - the stupid content tracker
SYNOPSIS
--------
+[verse]
'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate]
- [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]
+ [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]
DESCRIPTION
-----------
@@ -633,6 +634,9 @@ git Diffs
other
~~~~~
+'GIT_PAGER'::
+ This environment variable overrides `$PAGER`.
+
'GIT_TRACE'::
If this variable is set git will print `trace:` messages on
stderr telling about alias expansion, built-in command
diff --git a/Documentation/gitk.txt b/Documentation/gitk.txt
index cb482bf..23be005 100644
--- a/Documentation/gitk.txt
+++ b/Documentation/gitk.txt
@@ -3,26 +3,52 @@ gitk(1)
NAME
----
-gitk - Some git command not yet documented.
-
+gitk - git repository browser
SYNOPSIS
--------
-'gitk' [ --option ] <args>...
+'gitk' [<option>...] [<revs>] [--] [<path>...]
DESCRIPTION
-----------
-Does something not yet documented.
+Displays changes in a repository or a selected set of commits. This includes
+visualizing the commit graph, showing information related to each commit, and
+the files in the trees of each revision.
+Historically, gitk was the first repository browser. It's written in tcl/tk
+and started off in a separate repository but was later merged into the main
+git repository.
OPTIONS
-------
---option::
- Some option not yet documented.
+To control which revisions to shown, the command takes options applicable to
+the gitlink:git-rev-list[1] command. This manual page describes only the most
+frequently used options.
+
+-n <number>, --max-count=<number>::
+
+ Limits the number of commits to show.
+
+--since=<date>::
+
+ Show commits more recent than a specific date.
+
+--until=<date>::
+
+ Show commits older than a specific date.
-<args>...::
- Some argument not yet documented.
+<revs>::
+ Limit the revisions to show. This can be either a single revision
+ meaning show from the given revision and back, or it can be a range in
+ the form "'<from>'..'<to>'" to show all revisions between '<from>' and
+ back to '<to>'. Note, more advanced revision selection can be applied.
+
+<path>::
+
+ Limit commits to the ones touching files in the given paths. Note, to
+ avoid ambiguity wrt. revision names use "--" to separate the paths
+ from any preceeding options.
Examples
--------
@@ -37,13 +63,27 @@ gitk --since="2 weeks ago" \-- gitk::
The "--" is necessary to avoid confusion with the *branch* named
'gitk'
+See Also
+--------
+'qgit(1)'::
+ A repository browser written in C++ using Qt.
+
+'gitview(1)'::
+ A repository browser written in Python using Gtk. It's based on
+ 'bzrk(1)' and distributed in the contrib area of the git repository.
+
+'tig(1)'::
+ A minimal repository browser and git tool output highlighter written
+ in C using Ncurses.
+
Author
------
-Written by Paul Mackerras <paulus@samba.org>
+Written by Paul Mackerras <paulus@samba.org>.
Documentation
--------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
+Documentation by Junio C Hamano, Jonas Fonseca, and the git-list
+<git@vger.kernel.org>.
GIT
---
diff --git a/Documentation/technical/racy-git.txt b/Documentation/technical/racy-git.txt
new file mode 100644
index 0000000..7597d04
--- /dev/null
+++ b/Documentation/technical/racy-git.txt
@@ -0,0 +1,193 @@
+Use of index and Racy git problem
+=================================
+
+Background
+----------
+
+The index is one of the most important data structure in git.
+It represents a virtual working tree state by recording list of
+paths and their object names and serves as a staging area to
+write out the next tree object to be committed. The state is
+"virtual" in the sense that it does not necessarily have to, and
+often does not, match the files in the working tree.
+
+There are cases git needs to examine the differences between the
+virtual working tree state in the index and the files in the
+working tree. The most obvious case is when the user asks `git
+diff` (or its low level implementation, `git diff-files`) or
+`git-ls-files --modified`. In addition, git internally checks
+if the files in the working tree is different from what are
+recorded in the index to avoid stomping on local changes in them
+during patch application, switching branches, and merging.
+
+In order to speed up this comparison between the files in the
+working tree and the index entries, the index entries record the
+information obtained from the filesystem via `lstat(2)` system
+call when they were last updated. When checking if they differ,
+git first runs `lstat(2)` on the files and compare the result
+with this information (this is what was originally done by the
+`ce_match_stat()` function, which the current code does in
+`ce_match_stat_basic()` function). If some of these "cached
+stat information" fields do not match, git can tell that the
+files are modified without even looking at their contents.
+
+Note: not all members in `struct stat` obtained via `lstat(2)`
+are used for this comparison. For example, `st_atime` obviously
+is not useful. Currently, git compares the file type (regular
+files vs symbolic links) and executable bits (only for regular
+files) from `st_mode` member, `st_mtime` and `st_ctime`
+timestamps, `st_uid`, `st_gid`, `st_ino`, and `st_size` members.
+With a `USE_STDEV` compile-time option, `st_dev` is also
+compared, but this is not enabled by default because this member
+is not stable on network filesystems. With `USE_NSEC`
+compile-time option, `st_mtim.tv_nsec` and `st_ctim.tv_nsec`
+members are also compared, but this is not enabled by default
+because the value of this member becomes meaningless once the
+inode is evicted from the inode cache on filesystems that do not
+store it on disk.
+
+
+Racy git
+--------
+
+There is one slight problem with the optimization based on the
+cached stat information. Consider this sequence:
+
+ $ git update-index 'foo'
+ : modify 'foo' in-place without changing its size
+
+The first `update-index` computes the object name of the
+contents of file `foo` and updates the index entry for `foo`
+along with the `struct stat` information. If the modification
+that follows it happens very fast so that the file's `st_mtime`
+timestamp does not change, after this sequence, the cached stat
+information the index entry records still exactly match what you
+can obtain from the filesystem, but the file `foo` is modified.
+This way, git can incorrectly think files in the working tree
+are unmodified even though they actually are. This is called
+the "racy git" problem (discovered by Pasky), and the entries
+that appear clean when they may not be because of this problem
+are called "racily clean".
+
+To avoid this problem, git does two things:
+
+. When the cached stat information says the file has not been
+ modified, and the `st_mtime` is the same as (or newer than)
+ the timestamp of the index file itself (which is the time `git
+ update-index foo` finished running in the above example), it
+ also compares the contents with the object registered in the
+ index entry to make sure they match.
+
+. When the index file is updated that contains racily clean
+ entries, cached `st_size` information is truncated to zero
+ before writing a new version of the index file.
+
+Because the index file itself is written after collecting all
+the stat information from updated paths, `st_mtime` timestamp of
+it is usually the same as or newer than any of the paths the
+index contains. And no matter how quick the modification that
+follows `git update-index foo` finishes, the resulting
+`st_mtime` timestamp on `foo` cannot get the timestamp earlier
+than the index file. Therefore, index entries that can be
+racily clean are limited to the ones that have the same
+timestamp as the index file itself.
+
+The callers that want to check if an index entry matches the
+corresponding file in the working tree continue to call
+`ce_match_stat()`, but with this change, `ce_match_stat()` uses
+`ce_modified_check_fs()` to see if racily clean ones are
+actually clean after comparing the cached stat information using
+`ce_match_stat_basic()`.
+
+The problem the latter solves is this sequence:
+
+ $ git update-index 'foo'
+ : modify 'foo' in-place without changing its size
+ : wait for enough time
+ $ git update-index 'bar'
+
+Without the latter, the timestamp of the index file gets a newer
+value, and falsely clean entry `foo` would not be caught by the
+timestamp comparison check done with the former logic anymore.
+The latter makes sure that the cached stat information for `foo`
+would never match with the file in the working tree, so later
+checks by `ce_match_stat_basic()` would report the index entry
+does not match the file and git does not have to fall back on more
+expensive `ce_modified_check_fs()`.
+
+
+Runtime penalty
+---------------
+
+The runtime penalty of falling back to `ce_modified_check_fs()`
+from `ce_match_stat()` can be very expensive when there are many
+racily clean entries. An obvious way to artificially create
+this situation is to give the same timestamp to all the files in
+the working tree in a large project, run `git update-index` on
+them, and give the same timestamp to the index file:
+
+ $ date >.datestamp
+ $ git ls-files | xargs touch -r .datestamp
+ $ git ls-files | git update-index --stdin
+ $ touch -r .datestamp .git/index
+
+This will make all index entries racily clean. The linux-2.6
+project, for example, there are over 20,000 files in the working
+tree. On my Athron 64X2 3800+, after the above:
+
+ $ /usr/bin/time git diff-files
+ 1.68user 0.54system 0:02.22elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
+ 0inputs+0outputs (0major+67111minor)pagefaults 0swaps
+ $ git update-index MAINTAINERS
+ $ /usr/bin/time git diff-files
+ 0.02user 0.12system 0:00.14elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
+ 0inputs+0outputs (0major+935minor)pagefaults 0swaps
+
+Running `git update-index` in the middle checked the racily
+clean entries, and left the cached `st_mtime` for all the paths
+intact because they were actually clean (so this step took about
+the same amount of time as the first `git diff-files`). After
+that, they are not racily clean anymore but are truly clean, so
+the second invocation of `git diff-files` fully took advantage
+of the cached stat information.
+
+
+Avoiding runtime penalty
+------------------------
+
+In order to avoid the above runtime penalty, the recent "master"
+branch (post 1.4.2) has a code that makes sure the index file
+gets timestamp newer than the youngest files in the index when
+there are many young files with the same timestamp as the
+resulting index file would otherwise would have by waiting
+before finishing writing the index file out.
+
+I suspect that in practice the situation where many paths in the
+index are all racily clean is quite rare. The only code paths
+that can record recent timestamp for large number of paths I
+know of are:
+
+. Initial `git add .` of a large project.
+
+. `git checkout` of a large project from an empty index into an
+ unpopulated working tree.
+
+Note: switching branches with `git checkout` keeps the cached
+stat information of existing working tree files that are the
+same between the current branch and the new branch, which are
+all older than the resulting index file, and they will not
+become racily clean. Only the files that are actually checked
+out can become racily clean.
+
+In a large project where raciness avoidance cost really matters,
+however, the initial computation of all object names in the
+index takes more than one second, and the index file is written
+out after all that happens. Therefore the timestamp of the
+index file will be more than one seconds later than the the
+youngest file in the working tree. This means that in these
+cases there actually will not be any racily clean entry in
+the resulting index.
+
+So in summary I think we should not worry about avoiding the
+runtime penalty and get rid of the "wait before finishing
+writing" code out.
diff --git a/INSTALL b/INSTALL
index ba9778c..fa9bf74 100644
--- a/INSTALL
+++ b/INSTALL
@@ -16,7 +16,7 @@ install" would not work.
Alternatively you can use autoconf generated ./configure script to
set up install paths (via config.mak.autogen), so you can write instead
- $ autoconf ;# as yourself if ./configure doesn't exist yet
+ $ make configure ;# as yourself
$ ./configure --prefix=/usr ;# as yourself
$ make all doc ;# as yourself
# make install install-doc ;# as root
diff --git a/Makefile b/Makefile
index 1c327c1..a608476 100644
--- a/Makefile
+++ b/Makefile
@@ -27,7 +27,7 @@ all:
# Define NO_C99_FORMAT if your formatted IO functions (printf/scanf et.al.)
# do not support the 'size specifiers' introduced by C99, namely ll, hh,
# j, z, t. (representing long long int, char, intmax_t, size_t, ptrdiff_t).
-# some c compilers supported these specifiers prior to C99 as an extension.
+# some C compilers supported these specifiers prior to C99 as an extension.
#
# Define NO_STRCASESTR if you don't have strcasestr.
#
@@ -121,6 +121,17 @@ template_dir = $(prefix)/share/git-core/templates/
GIT_PYTHON_DIR = $(prefix)/share/git-core/python
# DESTDIR=
+# default configuration for gitweb
+GITWEB_CONFIG = gitweb_config.perl
+GITWEB_HOME_LINK_STR = projects
+GITWEB_SITENAME =
+GITWEB_PROJECTROOT = /pub/git
+GITWEB_BASE_URL =
+GITWEB_LIST =
+GITWEB_HOMETEXT = indextext.html
+GITWEB_CSS = gitweb.css
+GITWEB_LOGO = git-logo.png
+
export prefix bindir gitexecdir template_dir GIT_PYTHON_DIR
CC = gcc
@@ -173,33 +184,28 @@ SIMPLE_PROGRAMS = \
# ... and all the rest that could be moved out of bindir to gitexecdir
PROGRAMS = \
- git-checkout-index$X \
git-convert-objects$X git-fetch-pack$X git-fsck-objects$X \
git-hash-object$X git-index-pack$X git-local-fetch$X \
git-merge-base$X \
- git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \
+ git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \
git-peek-remote$X git-receive-pack$X \
git-send-pack$X git-shell$X \
git-show-index$X git-ssh-fetch$X \
git-ssh-upload$X git-unpack-file$X \
- git-unpack-objects$X git-update-server-info$X \
+ git-update-server-info$X \
git-upload-pack$X git-verify-pack$X \
- git-symbolic-ref$X \
- git-name-rev$X git-pack-redundant$X git-var$X \
+ git-pack-redundant$X git-var$X \
git-describe$X git-merge-tree$X git-blame$X git-imap-send$X \
- git-merge-recur$X
-
-BUILT_INS = git-log$X git-whatchanged$X git-show$X git-update-ref$X \
- git-count-objects$X git-diff$X git-push$X git-mailsplit$X \
- git-grep$X git-add$X git-rm$X git-rev-list$X git-stripspace$X \
- git-check-ref-format$X git-rev-parse$X git-mailinfo$X \
- git-init-db$X git-tar-tree$X git-upload-tar$X git-format-patch$X \
- git-ls-files$X git-ls-tree$X git-get-tar-commit-id$X \
- git-read-tree$X git-commit-tree$X git-write-tree$X \
- git-apply$X git-show-branch$X git-diff-files$X git-update-index$X \
- git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X \
- git-fmt-merge-msg$X git-prune$X git-mv$X git-prune-packed$X \
- git-repo-config$X
+ git-merge-recur$X \
+ $(EXTRA_PROGRAMS)
+
+# Empty...
+EXTRA_PROGRAMS =
+
+BUILT_INS = \
+ git-format-patch$X git-show$X git-whatchanged$X \
+ git-get-tar-commit-id$X \
+ $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
# what 'all' will build and 'install' will install, in gitexecdir
ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
@@ -228,7 +234,7 @@ LIB_H = \
blob.h cache.h commit.h csum-file.h delta.h \
diff.h object.h pack.h pkt-line.h quote.h refs.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
+ tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h
DIFF_OBJS = \
diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@ -243,20 +249,52 @@ LIB_OBJS = \
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 \
- alloc.o merge-file.o path-list.o unpack-trees.o $(DIFF_OBJS)
+ write_or_die.o \
+ alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS)
BUILTIN_OBJS = \
- builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \
- builtin-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.o \
- builtin-rm.o builtin-init-db.o builtin-rev-parse.o \
- builtin-tar-tree.o builtin-upload-tar.o builtin-update-index.o \
- builtin-ls-files.o builtin-ls-tree.o builtin-write-tree.o \
- builtin-read-tree.o builtin-commit-tree.o builtin-mailinfo.o \
- builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
- builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \
- builtin-cat-file.o builtin-mailsplit.o builtin-stripspace.o \
- builtin-update-ref.o builtin-fmt-merge-msg.o builtin-prune.o \
- builtin-mv.o builtin-prune-packed.o builtin-repo-config.o
+ builtin-add.o \
+ builtin-apply.o \
+ builtin-cat-file.o \
+ builtin-checkout-index.o \
+ builtin-check-ref-format.o \
+ builtin-commit-tree.o \
+ builtin-count-objects.o \
+ builtin-diff.o \
+ builtin-diff-files.o \
+ builtin-diff-index.o \
+ builtin-diff-stages.o \
+ builtin-diff-tree.o \
+ builtin-fmt-merge-msg.o \
+ builtin-grep.o \
+ builtin-init-db.o \
+ builtin-log.o \
+ builtin-ls-files.o \
+ builtin-ls-tree.o \
+ builtin-mailinfo.o \
+ builtin-mailsplit.o \
+ builtin-mv.o \
+ builtin-name-rev.o \
+ builtin-pack-objects.o \
+ builtin-prune.o \
+ builtin-prune-packed.o \
+ builtin-push.o \
+ builtin-read-tree.o \
+ builtin-repo-config.o \
+ builtin-rev-list.o \
+ builtin-rev-parse.o \
+ builtin-rm.o \
+ builtin-show-branch.o \
+ builtin-stripspace.o \
+ builtin-symbolic-ref.o \
+ builtin-tar-tree.o \
+ builtin-unpack-objects.o \
+ builtin-update-index.o \
+ builtin-update-ref.o \
+ builtin-upload-tar.o \
+ builtin-verify-pack.o \
+ builtin-write-tree.o \
+ builtin-zip-tree.o
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
LIBS = $(GITLIBS) -lz
@@ -297,15 +335,16 @@ ifeq ($(uname_S),SunOS)
NEEDS_NSL = YesPlease
SHELL_PATH = /bin/bash
NO_STRCASESTR = YesPlease
- NO_STRLCPY = YesPlease
ifeq ($(uname_R),5.8)
NEEDS_LIBICONV = YesPlease
NO_UNSETENV = YesPlease
NO_SETENV = YesPlease
+ NO_C99_FORMAT = YesPlease
endif
ifeq ($(uname_R),5.9)
NO_UNSETENV = YesPlease
NO_SETENV = YesPlease
+ NO_C99_FORMAT = YesPlease
endif
INSTALL = ginstall
TAR = gtar
@@ -524,7 +563,7 @@ LIB_OBJS += $(COMPAT_OBJS)
export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
### Build rules
-all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk
+all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi
all:
$(MAKE) -C templates
@@ -537,7 +576,7 @@ git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS) GIT-CFLAGS
$(ALL_CFLAGS) -o $@ $(filter %.c,$^) \
$(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
-builtin-help.o: common-cmds.h
+help.o: common-cmds.h
$(BUILT_INS): git$X
rm -f $@ && ln git$X $@
@@ -582,6 +621,24 @@ git-status: git-commit
cp $< $@+
mv $@+ $@
+gitweb/gitweb.cgi: gitweb/gitweb.perl
+ rm -f $@ $@+
+ sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
+ -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
+ -e 's|++GIT_BINDIR++|$(bindir)|g' \
+ -e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \
+ -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
+ -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
+ -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
+ -e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \
+ -e 's|++GITWEB_LIST++|$(GITWEB_LIST)|g' \
+ -e 's|++GITWEB_HOMETEXT++|$(GITWEB_HOMETEXT)|g' \
+ -e 's|++GITWEB_CSS++|$(GITWEB_CSS)|g' \
+ -e 's|++GITWEB_LOGO++|$(GITWEB_LOGO)|g' \
+ $< >$@+
+ chmod +x $@+
+ mv $@+ $@
+
git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
rm -f $@ $@+
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
@@ -592,10 +649,17 @@ git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
-e '/@@GITWEB_CGI@@/d' \
-e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \
-e '/@@GITWEB_CSS@@/d' \
- $@.sh | sed "s|/usr/bin/git|$(bindir)/git|" > $@+
+ $@.sh > $@+
chmod +x $@+
mv $@+ $@
+configure: configure.ac
+ rm -f $@ $<+
+ sed -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+ $< > $<+
+ autoconf -o $@ $<+
+ rm -f $<+
+
# These can record GIT_VERSION
git$X git.spec \
$(patsubst %.sh,%,$(SCRIPT_SH)) \
@@ -792,10 +856,11 @@ clean:
rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X
rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
rm -rf autom4te.cache
- rm -f config.log config.mak.autogen configure config.status config.cache
+ rm -f configure config.log config.mak.autogen config.mak.append config.status config.cache
rm -rf $(GIT_TARNAME) .doc-tmp-dir
rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
rm -f $(htmldocs).tar.gz $(manpages).tar.gz
+ rm -f gitweb/gitweb.cgi
$(MAKE) -C Documentation/ clean
$(MAKE) -C templates clean
$(MAKE) -C t/ clean
diff --git a/TEST b/TEST
deleted file mode 100755
index 7286e2a..0000000
--- a/TEST
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/sh -x
-
-cd t || exit
-GIT_USE_RECUR_FOR_RECURSIVE=LetsTryIt
-export GIT_USE_RECUR_FOR_RECURSIVE
-
-./t3400-rebase.sh "$@" &&
-./t6020-merge-df.sh "$@" &&
-./t3401-rebase-partial.sh "$@" &&
-./t6021-merge-criss-cross.sh "$@" &&
-./t3402-rebase-merge.sh "$@" &&
-./t6022-merge-rename.sh "$@" &&
-./t6010-merge-base.sh "$@" &&
-:
diff --git a/blame.c b/blame.c
index 7099b53..8968046 100644
--- a/blame.c
+++ b/blame.c
@@ -56,9 +56,9 @@ struct patch {
static void get_blob(struct commit *commit);
/* Only used for statistics */
-static int num_get_patch = 0;
-static int num_commits = 0;
-static int patch_time = 0;
+static int num_get_patch;
+static int num_commits;
+static int patch_time;
struct blame_diff_state {
struct xdiff_emit_state xm;
@@ -165,7 +165,7 @@ static int get_blob_sha1(struct tree *t, const char *pathname,
blame_file = pathname;
pathspec[0] = pathname;
pathspec[1] = NULL;
- memset(blob_sha1, 0, sizeof(blob_sha1));
+ hashclr(blob_sha1);
read_tree_recursive(t, "", 0, 0, pathspec, get_blob_sha1_internal);
for (i = 0; i < 20; i++) {
@@ -176,7 +176,7 @@ static int get_blob_sha1(struct tree *t, const char *pathname,
if (i == 20)
return -1;
- memcpy(sha1, blob_sha1, 20);
+ hashcpy(sha1, blob_sha1);
return 0;
}
@@ -191,7 +191,7 @@ static int get_blob_sha1_internal(const unsigned char *sha1, const char *base,
strcmp(blame_file + baselen, pathname))
return -1;
- memcpy(blob_sha1, sha1, 20);
+ hashcpy(blob_sha1, sha1);
return -1;
}
@@ -351,10 +351,7 @@ static int fill_util_info(struct commit *commit)
assert(util);
assert(util->pathname);
- if (get_blob_sha1(commit->tree, util->pathname, util->sha1))
- return 1;
- else
- return 0;
+ return !!get_blob_sha1(commit->tree, util->pathname, util->sha1);
}
static void alloc_line_map(struct commit *commit)
diff --git a/builtin-apply.c b/builtin-apply.c
index 9cf477c..b47ccac 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -28,21 +28,24 @@ static int prefix_length = -1;
static int newfd = -1;
static int p_value = 1;
-static int allow_binary_replacement = 0;
-static int check_index = 0;
-static int write_index = 0;
-static int cached = 0;
-static int diffstat = 0;
-static int numstat = 0;
-static int summary = 0;
-static int check = 0;
+static int allow_binary_replacement;
+static int check_index;
+static int write_index;
+static int cached;
+static int diffstat;
+static int numstat;
+static int summary;
+static int check;
static int apply = 1;
-static int no_add = 0;
-static int show_index_info = 0;
+static int apply_in_reverse;
+static int apply_with_reject;
+static int apply_verbosely;
+static int no_add;
+static int show_index_info;
static int line_termination = '\n';
static unsigned long p_context = -1;
static const char apply_usage[] =
-"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
+"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
static enum whitespace_eol {
nowarn_whitespace,
@@ -50,10 +53,10 @@ static enum whitespace_eol {
error_on_whitespace,
strip_whitespace,
} new_whitespace = warn_on_whitespace;
-static int whitespace_error = 0;
+static int whitespace_error;
static int squelch_whitespace_errors = 5;
-static int applied_after_stripping = 0;
-static const char *patch_input_file = NULL;
+static int applied_after_stripping;
+static const char *patch_input_file;
static void parse_whitespace_option(const char *option)
{
@@ -108,21 +111,37 @@ static int max_change, max_len;
*/
static int linenr = 1;
+/*
+ * This represents one "hunk" from a patch, starting with
+ * "@@ -oldpos,oldlines +newpos,newlines @@" marker. The
+ * patch text is pointed at by patch, and its byte length
+ * is stored in size. leading and trailing are the number
+ * of context lines.
+ */
struct fragment {
unsigned long leading, trailing;
unsigned long oldpos, oldlines;
unsigned long newpos, newlines;
const char *patch;
int size;
+ int rejected;
struct fragment *next;
};
+/*
+ * When dealing with a binary patch, we reuse "leading" field
+ * to store the type of the binary hunk, either deflated "delta"
+ * or deflated "literal".
+ */
+#define binary_patch_method leading
+#define BINARY_DELTA_DEFLATED 1
+#define BINARY_LITERAL_DEFLATED 2
+
struct patch {
char *new_name, *old_name, *def_name;
unsigned int old_mode, new_mode;
- int is_rename, is_copy, is_new, is_delete, is_binary, is_reverse;
-#define BINARY_DELTA_DEFLATED 1
-#define BINARY_LITERAL_DEFLATED 2
+ int is_rename, is_copy, is_new, is_delete, is_binary;
+ int rejected;
unsigned long deflate_origlen;
int lines_added, lines_deleted;
int score;
@@ -135,6 +154,24 @@ struct patch {
struct patch *next;
};
+static void say_patch_name(FILE *output, const char *pre, struct patch *patch, const char *post)
+{
+ fputs(pre, output);
+ if (patch->old_name && patch->new_name &&
+ strcmp(patch->old_name, patch->new_name)) {
+ write_name_quoted(NULL, 0, patch->old_name, 1, output);
+ fputs(" => ", output);
+ write_name_quoted(NULL, 0, patch->new_name, 1, output);
+ }
+ else {
+ const char *n = patch->new_name;
+ if (!n)
+ n = patch->old_name;
+ write_name_quoted(NULL, 0, n, 1, output);
+ }
+ fputs(post, output);
+}
+
#define CHUNKSIZE (8192)
#define SLOP (16)
@@ -591,9 +628,7 @@ static char *git_header_name(char *line, int llen)
* form.
*/
for (len = 0 ; ; len++) {
- char c = name[len];
-
- switch (c) {
+ switch (name[len]) {
default:
continue;
case '\n':
@@ -978,51 +1013,82 @@ static inline int metadata_changes(struct patch *patch)
patch->old_mode != patch->new_mode);
}
-static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
+static char *inflate_it(const void *data, unsigned long size,
+ unsigned long inflated_size)
{
- /* We have read "GIT binary patch\n"; what follows is a line
- * that says the patch method (currently, either "deflated
- * literal" or "deflated delta") and the length of data before
- * deflating; a sequence of 'length-byte' followed by base-85
- * encoded data follows.
+ z_stream stream;
+ void *out;
+ int st;
+
+ memset(&stream, 0, sizeof(stream));
+
+ stream.next_in = (unsigned char *)data;
+ stream.avail_in = size;
+ stream.next_out = out = xmalloc(inflated_size);
+ stream.avail_out = inflated_size;
+ inflateInit(&stream);
+ st = inflate(&stream, Z_FINISH);
+ if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
+ free(out);
+ return NULL;
+ }
+ return out;
+}
+
+static struct fragment *parse_binary_hunk(char **buf_p,
+ unsigned long *sz_p,
+ int *status_p,
+ int *used_p)
+{
+ /* Expect a line that begins with binary patch method ("literal"
+ * or "delta"), followed by the length of data before deflating.
+ * a sequence of 'length-byte' followed by base-85 encoded data
+ * should follow, terminated by a newline.
*
* Each 5-byte sequence of base-85 encodes up to 4 bytes,
* and we would limit the patch line to 66 characters,
* so one line can fit up to 13 groups that would decode
* to 52 bytes max. The length byte 'A'-'Z' corresponds
* to 1-26 bytes, and 'a'-'z' corresponds to 27-52 bytes.
- * The end of binary is signaled with an empty line.
*/
int llen, used;
- struct fragment *fragment;
+ unsigned long size = *sz_p;
+ char *buffer = *buf_p;
+ int patch_method;
+ unsigned long origlen;
char *data = NULL;
+ int hunk_size = 0;
+ struct fragment *frag;
- patch->fragments = fragment = xcalloc(1, sizeof(*fragment));
-
- /* Grab the type of patch */
llen = linelen(buffer, size);
used = llen;
- linenr++;
+
+ *status_p = 0;
if (!strncmp(buffer, "delta ", 6)) {
- patch->is_binary = BINARY_DELTA_DEFLATED;
- patch->deflate_origlen = strtoul(buffer + 6, NULL, 10);
+ patch_method = BINARY_DELTA_DEFLATED;
+ origlen = strtoul(buffer + 6, NULL, 10);
}
else if (!strncmp(buffer, "literal ", 8)) {
- patch->is_binary = BINARY_LITERAL_DEFLATED;
- patch->deflate_origlen = strtoul(buffer + 8, NULL, 10);
+ patch_method = BINARY_LITERAL_DEFLATED;
+ origlen = strtoul(buffer + 8, NULL, 10);
}
else
- return error("unrecognized binary patch at line %d: %.*s",
- linenr-1, llen-1, buffer);
+ return NULL;
+
+ linenr++;
buffer += llen;
while (1) {
int byte_length, max_byte_length, newsize;
llen = linelen(buffer, size);
used += llen;
linenr++;
- if (llen == 1)
+ if (llen == 1) {
+ /* consume the blank line */
+ buffer++;
+ size--;
break;
+ }
/* Minimum line is "A00000\n" which is 7-byte long,
* and the line length must be multiple of 5 plus 2.
*/
@@ -1043,21 +1109,79 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
if (max_byte_length < byte_length ||
byte_length <= max_byte_length - 4)
goto corrupt;
- newsize = fragment->size + byte_length;
+ newsize = hunk_size + byte_length;
data = xrealloc(data, newsize);
- if (decode_85(data + fragment->size,
- buffer + 1,
- byte_length))
+ if (decode_85(data + hunk_size, buffer + 1, byte_length))
goto corrupt;
- fragment->size = newsize;
+ hunk_size = newsize;
buffer += llen;
size -= llen;
}
- fragment->patch = data;
- return used;
+
+ frag = xcalloc(1, sizeof(*frag));
+ frag->patch = inflate_it(data, hunk_size, origlen);
+ if (!frag->patch)
+ goto corrupt;
+ free(data);
+ frag->size = origlen;
+ *buf_p = buffer;
+ *sz_p = size;
+ *used_p = used;
+ frag->binary_patch_method = patch_method;
+ return frag;
+
corrupt:
- return error("corrupt binary patch at line %d: %.*s",
- linenr-1, llen-1, buffer);
+ if (data)
+ free(data);
+ *status_p = -1;
+ error("corrupt binary patch at line %d: %.*s",
+ linenr-1, llen-1, buffer);
+ return NULL;
+}
+
+static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
+{
+ /* We have read "GIT binary patch\n"; what follows is a line
+ * that says the patch method (currently, either "literal" or
+ * "delta") and the length of data before deflating; a
+ * sequence of 'length-byte' followed by base-85 encoded data
+ * follows.
+ *
+ * When a binary patch is reversible, there is another binary
+ * hunk in the same format, starting with patch method (either
+ * "literal" or "delta") with the length of data, and a sequence
+ * of length-byte + base-85 encoded data, terminated with another
+ * empty line. This data, when applied to the postimage, produces
+ * the preimage.
+ */
+ struct fragment *forward;
+ struct fragment *reverse;
+ int status;
+ int used, used_1;
+
+ forward = parse_binary_hunk(&buffer, &size, &status, &used);
+ if (!forward && !status)
+ /* there has to be one hunk (forward hunk) */
+ return error("unrecognized binary patch at line %d", linenr-1);
+ if (status)
+ /* otherwise we already gave an error message */
+ return status;
+
+ reverse = parse_binary_hunk(&buffer, &size, &status, &used_1);
+ if (reverse)
+ used += used_1;
+ else if (status) {
+ /* not having reverse hunk is not an error, but having
+ * a corrupt reverse hunk is.
+ */
+ free((void*) forward->patch);
+ free(forward);
+ return status;
+ }
+ forward->next = reverse;
+ patch->fragments = forward;
+ patch->is_binary = 1;
+ return used;
}
static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
@@ -1143,7 +1267,6 @@ static void reverse_patches(struct patch *p)
swap(frag->newpos, frag->oldpos);
swap(frag->newlines, frag->oldlines);
}
- p->is_reverse = !p->is_reverse;
}
}
@@ -1363,8 +1486,7 @@ static int apply_line(char *output, const char *patch, int plen)
return plen;
}
-static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
- int reverse, int inaccurate_eof)
+static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, int inaccurate_eof)
{
int match_beginning, match_end;
char *buf = desc->buffer;
@@ -1396,7 +1518,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
if (len < size && patch[len] == '\\')
plen--;
first = *patch;
- if (reverse) {
+ if (apply_in_reverse) {
if (first == '-')
first = '+';
else if (first == '+')
@@ -1446,7 +1568,8 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
lines = 0;
pos = frag->newpos;
for (;;) {
- offset = find_offset(buf, desc->size, oldlines, oldsize, pos, &lines);
+ offset = find_offset(buf, desc->size,
+ oldlines, oldsize, pos, &lines);
if (match_end && offset + oldsize != desc->size)
offset = -1;
if (match_beginning && offset)
@@ -1459,8 +1582,10 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
/* Warn if it was necessary to reduce the number
* of context lines.
*/
- if ((leading != frag->leading) || (trailing != frag->trailing))
- fprintf(stderr, "Context reduced to (%ld/%ld) to apply fragment at %d\n",
+ if ((leading != frag->leading) ||
+ (trailing != frag->trailing))
+ fprintf(stderr, "Context reduced to (%ld/%ld)"
+ " to apply fragment at %d\n",
leading, trailing, pos + lines);
if (size > alloc) {
@@ -1470,7 +1595,9 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
desc->buffer = buf;
}
desc->size = size;
- memmove(buf + offset + newsize, buf + offset + oldsize, size - offset - newsize);
+ memmove(buf + offset + newsize,
+ buf + offset + oldsize,
+ size - offset - newsize);
memcpy(buf + offset, newlines, newsize);
offset = 0;
@@ -1506,28 +1633,6 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
return offset;
}
-static char *inflate_it(const void *data, unsigned long size,
- unsigned long inflated_size)
-{
- z_stream stream;
- void *out;
- int st;
-
- memset(&stream, 0, sizeof(stream));
-
- stream.next_in = (unsigned char *)data;
- stream.avail_in = size;
- stream.next_out = out = xmalloc(inflated_size);
- stream.avail_out = inflated_size;
- inflateInit(&stream);
- st = inflate(&stream, Z_FINISH);
- if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
- free(out);
- return NULL;
- }
- return out;
-}
-
static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
{
unsigned long dst_size;
@@ -1535,30 +1640,29 @@ static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
void *data;
void *result;
- /* Binary patch is irreversible */
- if (patch->is_reverse)
- return error("cannot reverse-apply a binary patch to '%s'",
- patch->new_name
- ? patch->new_name : patch->old_name);
-
- data = inflate_it(fragment->patch, fragment->size,
- patch->deflate_origlen);
- if (!data)
- return error("corrupt patch data");
- switch (patch->is_binary) {
+ /* Binary patch is irreversible without the optional second hunk */
+ if (apply_in_reverse) {
+ if (!fragment->next)
+ return error("cannot reverse-apply a binary patch "
+ "without the reverse hunk to '%s'",
+ patch->new_name
+ ? patch->new_name : patch->old_name);
+ fragment = fragment->next;
+ }
+ data = (void*) fragment->patch;
+ switch (fragment->binary_patch_method) {
case BINARY_DELTA_DEFLATED:
result = patch_delta(desc->buffer, desc->size,
data,
- patch->deflate_origlen,
+ fragment->size,
&dst_size);
free(desc->buffer);
desc->buffer = result;
- free(data);
break;
case BINARY_LITERAL_DEFLATED:
free(desc->buffer);
desc->buffer = data;
- dst_size = patch->deflate_origlen;
+ dst_size = fragment->size;
break;
}
if (!desc->buffer)
@@ -1609,7 +1713,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
}
get_sha1_hex(patch->new_sha1_prefix, sha1);
- if (!memcmp(sha1, null_sha1, 20)) {
+ if (is_null_sha1(sha1)) {
free(desc->buffer);
desc->alloc = desc->size = 0;
desc->buffer = NULL;
@@ -1642,7 +1746,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
write_sha1_file_prepare(desc->buffer, desc->size, blob_type,
sha1, hdr, &hdrlen);
if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
- return error("binary patch to '%s' creates incorrect result", name);
+ return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)", name, patch->new_sha1_prefix, sha1_to_hex(sha1));
}
return 0;
@@ -1657,10 +1761,12 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
return apply_binary(desc, patch);
while (frag) {
- if (apply_one_fragment(desc, frag, patch->is_reverse,
- patch->inaccurate_eof) < 0)
- return error("patch failed: %s:%ld",
- name, frag->oldpos);
+ if (apply_one_fragment(desc, frag, patch->inaccurate_eof)) {
+ error("patch failed: %s:%ld", name, frag->oldpos);
+ if (!apply_with_reject)
+ return -1;
+ frag->rejected = 1;
+ }
frag = frag->next;
}
return 0;
@@ -1696,8 +1802,9 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
desc.size = size;
desc.alloc = alloc;
desc.buffer = buf;
+
if (apply_fragments(&desc, patch) < 0)
- return -1;
+ return -1; /* note with --reject this succeeds. */
/* NUL terminate the result */
if (desc.alloc <= desc.size)
@@ -1722,6 +1829,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
struct cache_entry *ce = NULL;
int ok_if_exists;
+ patch->rejected = 1; /* we will drop this after we succeed */
if (old_name) {
int changed = 0;
int stat_ret = 0;
@@ -1827,24 +1935,23 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
if (apply_data(patch, &st, ce) < 0)
return error("%s: patch does not apply", name);
+ patch->rejected = 0;
return 0;
}
static int check_patch_list(struct patch *patch)
{
struct patch *prev_patch = NULL;
- int error = 0;
+ int err = 0;
for (prev_patch = NULL; patch ; patch = patch->next) {
- error |= check_patch(patch, prev_patch);
+ if (apply_verbosely)
+ say_patch_name(stderr,
+ "Checking patch ", patch, "...\n");
+ err |= check_patch(patch, prev_patch);
prev_patch = patch;
}
- return error;
-}
-
-static inline int is_null_sha1(const unsigned char *sha1)
-{
- return !memcmp(sha1, null_sha1, 20);
+ return err;
}
static void show_index_list(struct patch *list)
@@ -2150,23 +2257,99 @@ static void write_out_one_result(struct patch *patch, int phase)
if (phase == 0)
remove_file(patch);
if (phase == 1)
- create_file(patch);
+ create_file(patch);
+}
+
+static int write_out_one_reject(struct patch *patch)
+{
+ FILE *rej;
+ char namebuf[PATH_MAX];
+ struct fragment *frag;
+ int cnt = 0;
+
+ for (cnt = 0, frag = patch->fragments; frag; frag = frag->next) {
+ if (!frag->rejected)
+ continue;
+ cnt++;
+ }
+
+ if (!cnt) {
+ if (apply_verbosely)
+ say_patch_name(stderr,
+ "Applied patch ", patch, " cleanly.\n");
+ return 0;
+ }
+
+ /* This should not happen, because a removal patch that leaves
+ * contents are marked "rejected" at the patch level.
+ */
+ if (!patch->new_name)
+ die("internal error");
+
+ /* Say this even without --verbose */
+ say_patch_name(stderr, "Applying patch ", patch, " with");
+ fprintf(stderr, " %d rejects...\n", cnt);
+
+ cnt = strlen(patch->new_name);
+ if (ARRAY_SIZE(namebuf) <= cnt + 5) {
+ cnt = ARRAY_SIZE(namebuf) - 5;
+ fprintf(stderr,
+ "warning: truncating .rej filename to %.*s.rej",
+ cnt - 1, patch->new_name);
+ }
+ memcpy(namebuf, patch->new_name, cnt);
+ memcpy(namebuf + cnt, ".rej", 5);
+
+ rej = fopen(namebuf, "w");
+ if (!rej)
+ return error("cannot open %s: %s", namebuf, strerror(errno));
+
+ /* Normal git tools never deal with .rej, so do not pretend
+ * this is a git patch by saying --git nor give extended
+ * headers. While at it, maybe please "kompare" that wants
+ * the trailing TAB and some garbage at the end of line ;-).
+ */
+ fprintf(rej, "diff a/%s b/%s\t(rejected hunks)\n",
+ patch->new_name, patch->new_name);
+ for (cnt = 1, frag = patch->fragments;
+ frag;
+ cnt++, frag = frag->next) {
+ if (!frag->rejected) {
+ fprintf(stderr, "Hunk #%d applied cleanly.\n", cnt);
+ continue;
+ }
+ fprintf(stderr, "Rejected hunk #%d.\n", cnt);
+ fprintf(rej, "%.*s", frag->size, frag->patch);
+ if (frag->patch[frag->size-1] != '\n')
+ fputc('\n', rej);
+ }
+ fclose(rej);
+ return -1;
}
-static void write_out_results(struct patch *list, int skipped_patch)
+static int write_out_results(struct patch *list, int skipped_patch)
{
int phase;
+ int errs = 0;
+ struct patch *l;
if (!list && !skipped_patch)
- die("No changes");
+ return error("No changes");
for (phase = 0; phase < 2; phase++) {
- struct patch *l = list;
+ l = list;
while (l) {
- write_out_one_result(l, phase);
+ if (l->rejected)
+ errs = 1;
+ else {
+ write_out_one_result(l, phase);
+ if (phase == 1 && write_out_one_reject(l))
+ errs = 1;
+ }
l = l->next;
}
}
+ return errs;
}
static struct lock_file lock_file;
@@ -2194,8 +2377,7 @@ static int use_patch(struct patch *p)
return 1;
}
-static int apply_patch(int fd, const char *filename,
- int reverse, int inaccurate_eof)
+static int apply_patch(int fd, const char *filename, int inaccurate_eof)
{
unsigned long offset, size;
char *buffer = read_patch_file(fd, &size);
@@ -2215,7 +2397,7 @@ static int apply_patch(int fd, const char *filename,
nr = parse_chunk(buffer + offset, size, patch);
if (nr < 0)
break;
- if (reverse)
+ if (apply_in_reverse)
reverse_patches(patch);
if (use_patch(patch)) {
patch_stats(patch);
@@ -2242,11 +2424,13 @@ static int apply_patch(int fd, const char *filename,
die("unable to read index file");
}
- if ((check || apply) && check_patch_list(list) < 0)
+ if ((check || apply) &&
+ check_patch_list(list) < 0 &&
+ !apply_with_reject)
exit(1);
- if (apply)
- write_out_results(list, skipped_patch);
+ if (apply && write_out_results(list, skipped_patch))
+ exit(1);
if (show_index_info)
show_index_list(list);
@@ -2278,8 +2462,8 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
{
int i;
int read_stdin = 1;
- int reverse = 0;
int inaccurate_eof = 0;
+ int errs = 0;
const char *whitespace_option = NULL;
@@ -2289,7 +2473,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
int fd;
if (!strcmp(arg, "-")) {
- apply_patch(0, "<stdin>", reverse, inaccurate_eof);
+ errs |= apply_patch(0, "<stdin>", inaccurate_eof);
read_stdin = 0;
continue;
}
@@ -2367,7 +2551,15 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
continue;
}
if (!strcmp(arg, "-R") || !strcmp(arg, "--reverse")) {
- reverse = 1;
+ apply_in_reverse = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--reject")) {
+ apply = apply_with_reject = apply_verbosely = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--verbose")) {
+ apply_verbosely = 1;
continue;
}
if (!strcmp(arg, "--inaccurate-eof")) {
@@ -2390,18 +2582,19 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
usage(apply_usage);
read_stdin = 0;
set_default_whitespace_mode(whitespace_option);
- apply_patch(fd, arg, reverse, inaccurate_eof);
+ errs |= apply_patch(fd, arg, inaccurate_eof);
close(fd);
}
set_default_whitespace_mode(whitespace_option);
if (read_stdin)
- apply_patch(0, "<stdin>", reverse, inaccurate_eof);
+ errs |= apply_patch(0, "<stdin>", inaccurate_eof);
if (whitespace_error) {
if (squelch_whitespace_errors &&
squelch_whitespace_errors < whitespace_error) {
int squelched =
whitespace_error - squelch_whitespace_errors;
- fprintf(stderr, "warning: squelched %d whitespace error%s\n",
+ fprintf(stderr, "warning: squelched %d "
+ "whitespace error%s\n",
squelched,
squelched == 1 ? "" : "s");
}
@@ -2429,5 +2622,5 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
die("Unable to write new index file");
}
- return 0;
+ return !!errs;
}
diff --git a/builtin-cat-file.c b/builtin-cat-file.c
index 814fb07..6c16bfa 100644
--- a/builtin-cat-file.c
+++ b/builtin-cat-file.c
@@ -9,24 +9,7 @@
#include "tree.h"
#include "builtin.h"
-static void flush_buffer(const char *buf, unsigned long size)
-{
- while (size > 0) {
- long ret = xwrite(1, buf, size);
- if (ret < 0) {
- /* Ignore epipe */
- if (errno == EPIPE)
- break;
- die("git-cat-file: %s", strerror(errno));
- } else if (!ret) {
- die("git-cat-file: disk full?");
- }
- size -= ret;
- buf += ret;
- }
-}
-
-static int pprint_tag(const unsigned char *sha1, const char *buf, unsigned long size)
+static void pprint_tag(const unsigned char *sha1, const char *buf, unsigned long size)
{
/* the parser in tag.c is useless here. */
const char *endp = buf + size;
@@ -42,7 +25,7 @@ static int pprint_tag(const unsigned char *sha1, const char *buf, unsigned long
/* Found the tagger line. Copy out the contents
* of the buffer so far.
*/
- flush_buffer(buf, cp - buf);
+ write_or_die(1, buf, cp - buf);
/*
* Do something intelligent, like pretty-printing
@@ -61,18 +44,18 @@ static int pprint_tag(const unsigned char *sha1, const char *buf, unsigned long
sp++;
if (sp == cp) {
/* give up */
- flush_buffer(tagger,
+ write_or_die(1, tagger,
cp - tagger);
break;
}
while (sp < cp &&
!('0' <= *sp && *sp <= '9'))
sp++;
- flush_buffer(tagger, sp - tagger);
+ write_or_die(1, tagger, sp - tagger);
date = strtoul(sp, &ep, 10);
tz = strtol(ep, NULL, 10);
- sp = show_date(date, tz);
- flush_buffer(sp, strlen(sp));
+ sp = show_date(date, tz, 0);
+ write_or_die(1, sp, strlen(sp));
xwrite(1, "\n", 1);
break;
}
@@ -90,8 +73,7 @@ static int pprint_tag(const unsigned char *sha1, const char *buf, unsigned long
* remainder as is.
*/
if (cp < endp)
- flush_buffer(cp, endp - cp);
- return 0;
+ write_or_die(1, cp, endp - cp);
}
int cmd_cat_file(int argc, const char **argv, const char *prefix)
@@ -145,8 +127,10 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
buf = read_sha1_file(sha1, type, &size);
if (!buf)
die("Cannot read object %s", argv[2]);
- if (!strcmp(type, tag_type))
- return pprint_tag(sha1, buf, size);
+ if (!strcmp(type, tag_type)) {
+ pprint_tag(sha1, buf, size);
+ return 0;
+ }
/* otherwise just spit out the data */
break;
@@ -161,6 +145,6 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
if (!buf)
die("git-cat-file %s: bad file", argv[2]);
- flush_buffer(buf, size);
+ write_or_die(1, buf, size);
return 0;
}
diff --git a/checkout-index.c b/builtin-checkout-index.c
index dfb1c44..b097c88 100644
--- a/checkout-index.c
+++ b/builtin-checkout-index.c
@@ -42,16 +42,14 @@
#include "cache-tree.h"
#define CHECKOUT_ALL 4
-static const char *prefix;
-static int prefix_length;
static int line_termination = '\n';
static int checkout_stage; /* default to checkout stage0 */
static int to_tempfile;
-static char topath[4][MAXPATHLEN+1];
+static char topath[4][PATH_MAX + 1];
static struct checkout state;
-static void write_tempfile_record (const char *name)
+static void write_tempfile_record(const char *name, int prefix_length)
{
int i;
@@ -77,7 +75,7 @@ static void write_tempfile_record (const char *name)
}
}
-static int checkout_file(const char *name)
+static int checkout_file(const char *name, int prefix_length)
{
int namelen = strlen(name);
int pos = cache_name_pos(name, namelen);
@@ -106,7 +104,7 @@ static int checkout_file(const char *name)
if (did_checkout) {
if (to_tempfile)
- write_tempfile_record(name);
+ write_tempfile_record(name, prefix_length);
return errs > 0 ? -1 : 0;
}
@@ -124,7 +122,7 @@ static int checkout_file(const char *name)
return -1;
}
-static int checkout_all(void)
+static void checkout_all(const char *prefix, int prefix_length)
{
int i, errs = 0;
struct cache_entry* last_ce = NULL;
@@ -141,7 +139,7 @@ static int checkout_all(void)
if (last_ce && to_tempfile) {
if (ce_namelen(last_ce) != ce_namelen(ce)
|| memcmp(last_ce->name, ce->name, ce_namelen(ce)))
- write_tempfile_record(last_ce->name);
+ write_tempfile_record(last_ce->name, prefix_length);
}
if (checkout_entry(ce, &state,
to_tempfile ? topath[ce_stage(ce)] : NULL) < 0)
@@ -149,13 +147,12 @@ static int checkout_all(void)
last_ce = ce;
}
if (last_ce && to_tempfile)
- write_tempfile_record(last_ce->name);
+ write_tempfile_record(last_ce->name, prefix_length);
if (errs)
/* we have already done our error reporting.
* exit with the same code as die().
*/
exit(128);
- return 0;
}
static const char checkout_cache_usage[] =
@@ -163,16 +160,16 @@ static const char checkout_cache_usage[] =
static struct lock_file lock_file;
-int main(int argc, char **argv)
+int cmd_checkout_index(int argc, const char **argv, const char *prefix)
{
int i;
int newfd = -1;
int all = 0;
int read_from_stdin = 0;
+ int prefix_length;
- state.base_dir = "";
- prefix = setup_git_directory();
git_config(git_default_config);
+ state.base_dir = "";
prefix_length = prefix ? strlen(prefix) : 0;
if (read_cache() < 0) {
@@ -270,7 +267,7 @@ int main(int argc, char **argv)
if (read_from_stdin)
die("git-checkout-index: don't mix '--stdin' and explicit filenames");
p = prefix_path(prefix, prefix_length, arg);
- checkout_file(p);
+ checkout_file(p, prefix_length);
if (p < arg || p > arg + strlen(arg))
free((char*)p);
}
@@ -292,7 +289,7 @@ int main(int argc, char **argv)
else
path_name = buf.buf;
p = prefix_path(prefix, prefix_length, path_name);
- checkout_file(p);
+ checkout_file(p, prefix_length);
if (p < path_name || p > path_name + strlen(path_name))
free((char *)p);
if (path_name != buf.buf)
@@ -301,7 +298,7 @@ int main(int argc, char **argv)
}
if (all)
- checkout_all();
+ checkout_all(prefix, prefix_length);
if (0 <= newfd &&
(write_cache(newfd, active_cache, active_nr) ||
diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c
index 9c98796..e2e690a 100644
--- a/builtin-commit-tree.c
+++ b/builtin-commit-tree.c
@@ -69,7 +69,7 @@ static int new_parent(int idx)
int i;
unsigned char *sha1 = parent_sha1[idx];
for (i = 0; i < idx; i++) {
- if (!memcmp(parent_sha1[i], sha1, 20)) {
+ if (!hashcmp(parent_sha1[i], sha1)) {
error("duplicate parent %s ignored", sha1_to_hex(sha1));
return 0;
}
diff --git a/builtin-count.c b/builtin-count-objects.c
index 1d3729a..1d3729a 100644
--- a/builtin-count.c
+++ b/builtin-count-objects.c
diff --git a/builtin-diff-files.c b/builtin-diff-files.c
index ac13db7..5d4a5c5 100644
--- a/builtin-diff-files.c
+++ b/builtin-diff-files.c
@@ -47,12 +47,5 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
if (rev.pending.nr ||
rev.min_age != -1 || rev.max_age != -1)
usage(diff_files_usage);
- /*
- * Backward compatibility wart - "diff-files -s" used to
- * defeat the common diff option "-s" which asked for
- * DIFF_FORMAT_NO_OUTPUT.
- */
- if (rev.diffopt.output_format == DIFF_FORMAT_NO_OUTPUT)
- rev.diffopt.output_format = DIFF_FORMAT_RAW;
return run_diff_files(&rev, silent);
}
diff --git a/builtin-diff-stages.c b/builtin-diff-stages.c
index 5960e08..70bb898 100644
--- a/builtin-diff-stages.c
+++ b/builtin-diff-stages.c
@@ -46,7 +46,7 @@ static void diff_stages(int stage1, int stage2, const char **pathspec)
else if (!two)
diff_addremove(&diff_options, '-', ntohl(one->ce_mode),
one->sha1, name, NULL);
- else if (memcmp(one->sha1, two->sha1, 20) ||
+ else if (hashcmp(one->sha1, two->sha1) ||
(one->ce_mode != two->ce_mode) ||
diff_options.find_copies_harder)
diff_change(&diff_options,
diff --git a/builtin-diff.c b/builtin-diff.c
index a090e29..a659020 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -56,13 +56,6 @@ static int builtin_diff_files(struct rev_info *revs,
if (revs->max_count < 0 &&
(revs->diffopt.output_format & DIFF_FORMAT_PATCH))
revs->combine_merges = revs->dense_combined_merges = 1;
- /*
- * Backward compatibility wart - "diff-files -s" used to
- * defeat the common diff option "-s" which asked for
- * DIFF_FORMAT_NO_OUTPUT.
- */
- if (revs->diffopt.output_format == DIFF_FORMAT_NO_OUTPUT)
- revs->diffopt.output_format = DIFF_FORMAT_RAW;
return run_diff_files(revs, silent);
}
@@ -75,9 +68,8 @@ static void stuff_change(struct diff_options *opt,
{
struct diff_filespec *one, *two;
- if (memcmp(null_sha1, old_sha1, 20) &&
- memcmp(null_sha1, new_sha1, 20) &&
- !memcmp(old_sha1, new_sha1, 20))
+ if (!is_null_sha1(old_sha1) && !is_null_sha1(new_sha1) &&
+ !hashcmp(old_sha1, new_sha1))
return;
if (opt->reverse_diff) {
@@ -200,7 +192,7 @@ static int builtin_diff_combined(struct rev_info *revs,
parent = xmalloc(ents * sizeof(*parent));
/* Again, the revs are all reverse */
for (i = 0; i < ents; i++)
- memcpy(parent + i, ent[ents - 1 - i].item->sha1, 20);
+ hashcpy((unsigned char*)parent + i, ent[ents - 1 - i].item->sha1);
diff_tree_combined(parent[0], parent + 1, ents - 1,
revs->dense_combined_merges, revs);
return 0;
@@ -298,7 +290,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
if (obj->type == OBJ_BLOB) {
if (2 <= blobs)
die("more than two blobs given: '%s'", name);
- memcpy(blob[blobs].sha1, obj->sha1, 20);
+ hashcpy(blob[blobs].sha1, obj->sha1);
blob[blobs].name = name;
blobs++;
continue;
diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c
index 485ede7..a5ed8db 100644
--- a/builtin-fmt-merge-msg.c
+++ b/builtin-fmt-merge-msg.c
@@ -8,7 +8,7 @@
static const char *fmt_merge_msg_usage =
"git-fmt-merge-msg [--summary] [--no-summary] [--file <file>]";
-static int merge_summary = 0;
+static int merge_summary;
static int fmt_merge_msg_config(const char *key, const char *value)
{
@@ -27,8 +27,8 @@ static void append_to_list(struct list *list, char *value, void *payload)
{
if (list->nr == list->alloc) {
list->alloc += 32;
- list->list = realloc(list->list, sizeof(char *) * list->alloc);
- list->payload = realloc(list->payload,
+ list->list = xrealloc(list->list, sizeof(char *) * list->alloc);
+ list->payload = xrealloc(list->payload,
sizeof(char *) * list->alloc);
}
list->payload[list->nr] = payload;
diff --git a/builtin-grep.c b/builtin-grep.c
index 93b7e07..8213ce2 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -123,6 +123,7 @@ 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;
@@ -136,6 +137,7 @@ struct grep_opt {
#define GREP_BINARY_TEXT 2
unsigned binary:2;
unsigned extended:1;
+ unsigned relative:1;
int regflags;
unsigned pre_context;
unsigned post_context;
@@ -173,61 +175,12 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
}
}
-#if DEBUG
-static inline void indent(int in)
-{
- int i;
- for (i = 0; i < in; i++) putchar(' ');
-}
-
-static void dump_pattern_exp(struct grep_expr *x, int in)
-{
- switch (x->node) {
- case GREP_NODE_ATOM:
- indent(in);
- puts(x->u.atom->pattern);
- break;
- case GREP_NODE_NOT:
- indent(in);
- puts("--not");
- dump_pattern_exp(x->u.unary, in+1);
- break;
- case GREP_NODE_AND:
- dump_pattern_exp(x->u.binary.left, in+1);
- indent(in);
- puts("--and");
- dump_pattern_exp(x->u.binary.right, in+1);
- break;
- case GREP_NODE_OR:
- dump_pattern_exp(x->u.binary.left, in+1);
- indent(in);
- puts("--or");
- dump_pattern_exp(x->u.binary.right, in+1);
- break;
- }
-}
-
-static void looking_at(const char *msg, struct grep_pat **list)
-{
- struct grep_pat *p = *list;
- fprintf(stderr, "%s: looking at ", msg);
- if (!p)
- fprintf(stderr, "empty\n");
- else
- fprintf(stderr, "<%s>\n", p->pattern);
-}
-#else
-#define looking_at(a,b) do {} while(0)
-#endif
-
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;
- looking_at("atom", list);
-
p = *list;
switch (p->token) {
case GREP_PATTERN: /* atom */
@@ -255,8 +208,6 @@ static struct grep_expr *compile_pattern_not(struct grep_pat **list)
struct grep_pat *p;
struct grep_expr *x;
- looking_at("not", list);
-
p = *list;
switch (p->token) {
case GREP_NOT:
@@ -279,8 +230,6 @@ static struct grep_expr *compile_pattern_and(struct grep_pat **list)
struct grep_pat *p;
struct grep_expr *x, *y, *z;
- looking_at("and", list);
-
x = compile_pattern_not(list);
p = *list;
if (p && p->token == GREP_AND) {
@@ -304,8 +253,6 @@ static struct grep_expr *compile_pattern_or(struct grep_pat **list)
struct grep_pat *p;
struct grep_expr *x, *y, *z;
- looking_at("or", list);
-
x = compile_pattern_and(list);
p = *list;
if (x && p && p->token != GREP_CLOSE_PAREN) {
@@ -323,8 +270,6 @@ static struct grep_expr *compile_pattern_or(struct grep_pat **list)
static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
{
- looking_at("expr", list);
-
return compile_pattern_or(list);
}
@@ -348,9 +293,6 @@ static void compile_patterns(struct grep_opt *opt)
*/
p = opt->pattern_list;
opt->pattern_expression = compile_pattern_expr(&p);
-#if DEBUG
- dump_pattern_exp(opt->pattern_expression, 0);
-#endif
if (p)
die("incomplete pattern expression: %s", p->pattern);
}
@@ -388,9 +330,7 @@ static int buffer_is_binary(const char *ptr, unsigned long size)
{
if (FIRST_FEW_BYTES < size)
size = FIRST_FEW_BYTES;
- if (memchr(ptr, 0, size))
- return 1;
- return 0;
+ return !!memchr(ptr, 0, size);
}
static int fixmatch(const char *pattern, char *line, regmatch_t *match)
@@ -632,19 +572,40 @@ static int grep_buffer(struct grep_opt *opt, const char *name,
return !!last_hit;
}
-static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name)
+static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name, int tree_name_len)
{
unsigned long size;
char *data;
char type[20];
+ char *to_free = NULL;
int hit;
+
data = read_sha1_file(sha1, type, &size);
if (!data) {
error("'%s': unable to read %s", name, sha1_to_hex(sha1));
return 0;
}
+ if (opt->relative && opt->prefix_length) {
+ static char name_buf[PATH_MAX];
+ char *cp;
+ int name_len = strlen(name) - opt->prefix_length + 1;
+
+ if (!tree_name_len)
+ name += opt->prefix_length;
+ else {
+ if (ARRAY_SIZE(name_buf) <= name_len)
+ cp = to_free = xmalloc(name_len);
+ else
+ cp = name_buf;
+ memcpy(cp, name, tree_name_len);
+ strcpy(cp + tree_name_len,
+ name + tree_name_len + opt->prefix_length);
+ name = cp;
+ }
+ }
hit = grep_buffer(opt, name, data, size);
free(data);
+ free(to_free);
return hit;
}
@@ -674,6 +635,8 @@ static int grep_file(struct grep_opt *opt, const char *filename)
return 0;
}
close(i);
+ if (opt->relative && opt->prefix_length)
+ filename += opt->prefix_length;
i = grep_buffer(opt, filename, data, st.st_size);
free(data);
return i;
@@ -720,7 +683,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
char *argptr = randarg;
struct grep_pat *p;
- if (opt->extended)
+ if (opt->extended || (opt->relative && opt->prefix_length))
return -1;
len = nr = 0;
push_arg("grep");
@@ -845,7 +808,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
if (!pathspec_matches(paths, ce->name))
continue;
if (cached)
- hit |= grep_sha1(opt, ce->sha1, ce->name);
+ hit |= grep_sha1(opt, ce->sha1, ce->name, 0);
else
hit |= grep_file(opt, ce->name);
}
@@ -860,11 +823,12 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
int hit = 0;
struct name_entry entry;
char *down;
- char *path_buf = xmalloc(PATH_MAX + strlen(tree_name) + 100);
+ int tn_len = strlen(tree_name);
+ char *path_buf = xmalloc(PATH_MAX + tn_len + 100);
- if (tree_name[0]) {
- int offset = sprintf(path_buf, "%s:", tree_name);
- down = path_buf + offset;
+ if (tn_len) {
+ tn_len = sprintf(path_buf, "%s:", tree_name);
+ down = path_buf + tn_len;
strcat(down, base);
}
else {
@@ -886,7 +850,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
if (!pathspec_matches(paths, down))
;
else if (S_ISREG(entry.mode))
- hit |= grep_sha1(opt, entry.sha1, path_buf);
+ hit |= grep_sha1(opt, entry.sha1, path_buf, tn_len);
else if (S_ISDIR(entry.mode)) {
char type[20];
struct tree_desc sub;
@@ -907,7 +871,7 @@ static int grep_object(struct grep_opt *opt, const char **paths,
struct object *obj, const char *name)
{
if (obj->type == OBJ_BLOB)
- return grep_sha1(opt, obj->sha1, name);
+ return grep_sha1(opt, obj->sha1, name, 0);
if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
struct tree_desc tree;
void *data;
@@ -945,6 +909,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
int i;
memset(&opt, 0, sizeof(opt));
+ opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
+ opt.relative = 1;
opt.pattern_tail = &opt.pattern_list;
opt.regflags = REG_NEWLINE;
@@ -1118,6 +1084,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
}
die(emsg_missing_argument, arg);
}
+ if (!strcmp("--full-name", arg)) {
+ opt.relative = 0;
+ continue;
+ }
if (!strcmp("--", arg)) {
/* later processing wants to have this at argv[1] */
argv--;
@@ -1176,8 +1146,15 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
verify_filename(prefix, argv[j]);
}
- if (i < argc)
+ if (i < argc) {
paths = get_pathspec(prefix, argv + i);
+ if (opt.prefix_length && opt.relative) {
+ /* Make sure we do not get outside of paths */
+ for (i = 0; paths[i]; i++)
+ if (strncmp(prefix, paths[i], opt.prefix_length))
+ die("git-grep: cannot generate relative filenames containing '..'");
+ }
+ }
else if (prefix) {
paths = xcalloc(2, sizeof(const char *));
paths[0] = prefix;
diff --git a/builtin-log.c b/builtin-log.c
index 691cf3a..fbc58bb 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -101,7 +101,7 @@ static int git_format_config(const char *var, const char *value)
if (!strcmp(var, "format.headers")) {
int len = strlen(value);
extra_headers_size += len + 1;
- extra_headers = realloc(extra_headers, extra_headers_size);
+ extra_headers = xrealloc(extra_headers, extra_headers_size);
extra_headers[extra_headers_size - len - 1] = 0;
strcat(extra_headers, value);
return 0;
@@ -381,7 +381,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
continue;
nr++;
- list = realloc(list, nr * sizeof(list[0]));
+ list = xrealloc(list, nr * sizeof(list[0]));
list[nr - 1] = commit;
}
total = nr;
diff --git a/builtin-ls-files.c b/builtin-ls-files.c
index 11386c4..ad8c41e 100644
--- a/builtin-ls-files.c
+++ b/builtin-ls-files.c
@@ -12,21 +12,22 @@
#include "dir.h"
#include "builtin.h"
-static int abbrev = 0;
-static int show_deleted = 0;
-static int show_cached = 0;
-static int show_others = 0;
-static int show_stage = 0;
-static int show_unmerged = 0;
-static int show_modified = 0;
-static int show_killed = 0;
-static int show_valid_bit = 0;
+static int abbrev;
+static int show_deleted;
+static int show_cached;
+static int show_others;
+static int show_stage;
+static int show_unmerged;
+static int show_modified;
+static int show_killed;
+static int show_valid_bit;
static int line_terminator = '\n';
-static int prefix_len = 0, prefix_offset = 0;
-static const char **pathspec = NULL;
-static int error_unmatch = 0;
-static char *ps_matched = NULL;
+static int prefix_len;
+static int prefix_offset;
+static const char **pathspec;
+static int error_unmatch;
+static char *ps_matched;
static const char *tag_cached = "";
static const char *tag_unmerged = "";
diff --git a/builtin-ls-tree.c b/builtin-ls-tree.c
index 261147f..201defd 100644
--- a/builtin-ls-tree.c
+++ b/builtin-ls-tree.c
@@ -14,10 +14,10 @@ static int line_termination = '\n';
#define LS_TREE_ONLY 2
#define LS_SHOW_TREES 4
#define LS_NAME_ONLY 8
-static int abbrev = 0;
-static int ls_options = 0;
+static int abbrev;
+static int ls_options;
static const char **pathspec;
-static int chomp_prefix = 0;
+static int chomp_prefix;
static const char *ls_tree_prefix;
static const char ls_tree_usage[] =
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index 24a4fc6..0c65f93 100644
--- a/builtin-mailinfo.c
+++ b/builtin-mailinfo.c
@@ -16,8 +16,8 @@
static FILE *cmitmsg, *patchfile, *fin, *fout;
-static int keep_subject = 0;
-static const char *metainfo_charset = NULL;
+static int keep_subject;
+static const char *metainfo_charset;
static char line[1000];
static char date[1000];
static char name[1000];
@@ -31,7 +31,7 @@ static char charset[256];
static char multipart_boundary[1000];
static int multipart_boundary_len;
-static int patch_lines = 0;
+static int patch_lines;
static char *sanity_check(char *name, char *email)
{
diff --git a/builtin-mv.c b/builtin-mv.c
index a731f8d..4d21d88 100644
--- a/builtin-mv.c
+++ b/builtin-mv.c
@@ -17,12 +17,19 @@ static const char builtin_mv_usage[] =
static const char **copy_pathspec(const char *prefix, const char **pathspec,
int count, int base_name)
{
+ int i;
const char **result = xmalloc((count + 1) * sizeof(const char *));
memcpy(result, pathspec, count * sizeof(const char *));
result[count] = NULL;
- if (base_name) {
- int i;
- for (i = 0; i < count; i++) {
+ for (i = 0; i < count; i++) {
+ int length = strlen(result[i]);
+ if (length > 0 && result[i][length - 1] == '/') {
+ char *without_slash = xmalloc(length);
+ memcpy(without_slash, result[i], length - 1);
+ without_slash[length - 1] = '\0';
+ result[i] = without_slash;
+ }
+ if (base_name) {
const char *last_slash = strrchr(result[i], '/');
if (last_slash)
result[i] = last_slash + 1;
@@ -107,7 +114,10 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
modes = xcalloc(count, sizeof(enum update_mode));
dest_path = copy_pathspec(prefix, argv + argc - 1, 1, 0);
- if (!lstat(dest_path[0], &st) &&
+ if (dest_path[0][0] == '\0')
+ /* special case: "." was normalized to "" */
+ destination = copy_pathspec(dest_path[0], argv + i, count, 1);
+ else if (!lstat(dest_path[0], &st) &&
S_ISDIR(st.st_mode)) {
dest_path[0] = add_slash(dest_path[0]);
destination = copy_pathspec(dest_path[0], argv + i, count, 1);
@@ -119,72 +129,71 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
/* Checking */
for (i = 0; i < count; i++) {
+ const char *src = source[i], *dst = destination[i];
+ int length, src_is_dir;
const char *bad = NULL;
if (show_only)
- printf("Checking rename of '%s' to '%s'\n",
- source[i], destination[i]);
+ printf("Checking rename of '%s' to '%s'\n", src, dst);
- if (lstat(source[i], &st) < 0)
+ length = strlen(src);
+ if (lstat(src, &st) < 0)
bad = "bad source";
-
- if (S_ISDIR(st.st_mode)) {
- const char *dir = source[i], *dest_dir = destination[i];
- int first, last, len = strlen(dir);
-
- if (lstat(dest_dir, &st) == 0) {
- bad = "cannot move directory over file";
- goto next;
- }
+ else if (!strncmp(src, dst, length) &&
+ (dst[length] == 0 || dst[length] == '/')) {
+ bad = "can not move directory into itself";
+ } else if ((src_is_dir = S_ISDIR(st.st_mode))
+ && lstat(dst, &st) == 0)
+ bad = "cannot move directory over file";
+ else if (src_is_dir) {
+ int first, last;
modes[i] = WORKING_DIRECTORY;
- first = cache_name_pos(source[i], len);
+ first = cache_name_pos(src, length);
if (first >= 0)
- die ("Huh? %s/ is in index?", dir);
+ die ("Huh? %s/ is in index?", src);
first = -1 - first;
for (last = first; last < active_nr; last++) {
const char *path = active_cache[last]->name;
- if (strncmp(path, dir, len) || path[len] != '/')
+ if (strncmp(path, src, length)
+ || path[length] != '/')
break;
}
if (last - first < 1)
bad = "source directory is empty";
- else if (!bad) {
- int j, dst_len = strlen(dest_dir);
+ else {
+ int j, dst_len;
if (last - first > 0) {
- source = realloc(source,
+ source = xrealloc(source,
(count + last - first)
* sizeof(char *));
- destination = realloc(destination,
+ destination = xrealloc(destination,
(count + last - first)
* sizeof(char *));
- modes = realloc(modes,
+ modes = xrealloc(modes,
(count + last - first)
* sizeof(enum update_mode));
}
- dest_dir = add_slash(dest_dir);
+ dst = add_slash(dst);
+ dst_len = strlen(dst) - 1;
for (j = 0; j < last - first; j++) {
const char *path =
active_cache[first + j]->name;
source[count + j] = path;
destination[count + j] =
- prefix_path(dest_dir, dst_len,
- path + len);
+ prefix_path(dst, dst_len,
+ path + length);
modes[count + j] = INDEX;
}
count += last - first;
}
-
- goto next;
- }
-
- if (!bad && lstat(destination[i], &st) == 0) {
+ } else if (lstat(dst, &st) == 0) {
bad = "destination exists";
if (force) {
/*
@@ -196,28 +205,17 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
" will overwrite!\n",
bad);
bad = NULL;
- path_list_insert(destination[i],
- &overwritten);
+ path_list_insert(dst, &overwritten);
} else
bad = "Cannot overwrite";
}
- }
-
- if (!bad &&
- !strncmp(destination[i], source[i], strlen(source[i])))
- bad = "can not move directory into itself";
-
- if (!bad && cache_name_pos(source[i], strlen(source[i])) < 0)
+ } else if (cache_name_pos(src, length) < 0)
bad = "not under version control";
+ else if (path_list_has_path(&src_for_dst, dst))
+ bad = "multiple sources for the same target";
+ else
+ path_list_insert(dst, &src_for_dst);
- if (!bad) {
- if (path_list_has_path(&src_for_dst, destination[i]))
- bad = "multiple sources for the same target";
- else
- path_list_insert(destination[i], &src_for_dst);
- }
-
-next:
if (bad) {
if (ignore_errors) {
if (--count > 0) {
@@ -229,33 +227,32 @@ next:
}
} else
die ("%s, source=%s, destination=%s",
- bad, source[i], destination[i]);
+ bad, src, dst);
}
}
for (i = 0; i < count; i++) {
+ const char *src = source[i], *dst = destination[i];
+ enum update_mode mode = modes[i];
if (show_only || verbose)
- printf("Renaming %s to %s\n",
- source[i], destination[i]);
- if (!show_only && modes[i] != INDEX &&
- rename(source[i], destination[i]) < 0 &&
- !ignore_errors)
- die ("renaming %s failed: %s",
- source[i], strerror(errno));
-
- if (modes[i] == WORKING_DIRECTORY)
+ printf("Renaming %s to %s\n", src, dst);
+ if (!show_only && mode != INDEX &&
+ rename(src, dst) < 0 && !ignore_errors)
+ die ("renaming %s failed: %s", src, strerror(errno));
+
+ if (mode == WORKING_DIRECTORY)
continue;
- if (cache_name_pos(source[i], strlen(source[i])) >= 0) {
- path_list_insert(source[i], &deleted);
+ if (cache_name_pos(src, strlen(src)) >= 0) {
+ path_list_insert(src, &deleted);
/* destination can be a directory with 1 file inside */
- if (path_list_has_path(&overwritten, destination[i]))
- path_list_insert(destination[i], &changed);
+ if (path_list_has_path(&overwritten, dst))
+ path_list_insert(dst, &changed);
else
- path_list_insert(destination[i], &added);
+ path_list_insert(dst, &added);
} else
- path_list_insert(destination[i], &added);
+ path_list_insert(dst, &added);
}
if (show_only) {
@@ -265,10 +262,10 @@ next:
} else {
for (i = 0; i < changed.nr; i++) {
const char *path = changed.items[i].path;
- int i = cache_name_pos(path, strlen(path));
- struct cache_entry *ce = active_cache[i];
+ int j = cache_name_pos(path, strlen(path));
+ struct cache_entry *ce = active_cache[j];
- if (i < 0)
+ if (j < 0)
die ("Huh? Cache entry for %s unknown?", path);
refresh_cache_entry(ce, 0);
}
diff --git a/name-rev.c b/builtin-name-rev.c
index f92f14e..d44e782 100644
--- a/name-rev.c
+++ b/builtin-name-rev.c
@@ -1,4 +1,5 @@
#include <stdlib.h>
+#include "builtin.h"
#include "cache.h"
#include "commit.h"
#include "tag.h"
@@ -74,7 +75,7 @@ copy_data:
}
}
-static int tags_only = 0;
+static int tags_only;
static int name_ref(const char *path, const unsigned char *sha1)
{
@@ -126,12 +127,11 @@ static const char* get_rev_name(struct object *o)
return buffer;
}
-int main(int argc, char **argv)
+int cmd_name_rev(int argc, const char **argv, const char *prefix)
{
struct object_array revs = { 0, 0, NULL };
int as_is = 0, all = 0, transform_stdin = 0;
- setup_git_directory();
git_config(git_default_config);
if (argc < 2)
diff --git a/pack-objects.c b/builtin-pack-objects.c
index 861c7f0..46f524d 100644
--- a/pack-objects.c
+++ b/builtin-pack-objects.c
@@ -1,3 +1,4 @@
+#include "builtin.h"
#include "cache.h"
#include "object.h"
#include "blob.h"
@@ -52,17 +53,17 @@ struct object_entry {
*/
static unsigned char object_list_sha1[20];
-static int non_empty = 0;
-static int no_reuse_delta = 0;
-static int local = 0;
-static int incremental = 0;
+static int non_empty;
+static int no_reuse_delta;
+static int local;
+static int incremental;
static struct object_entry **sorted_by_sha, **sorted_by_type;
-static struct object_entry *objects = NULL;
-static int nr_objects = 0, nr_alloc = 0, nr_result = 0;
+static struct object_entry *objects;
+static int nr_objects, nr_alloc, nr_result;
static const char *base_name;
static unsigned char pack_file_sha1[20];
static int progress = 1;
-static volatile sig_atomic_t progress_update = 0;
+static volatile sig_atomic_t progress_update;
static int window = 10;
/*
@@ -71,8 +72,8 @@ static int window = 10;
* sorted_by_sha is also possible but this was easier to code and faster.
* This hashtable is built after all the objects are seen.
*/
-static int *object_ix = NULL;
-static int object_ix_hashsz = 0;
+static int *object_ix;
+static int object_ix_hashsz;
/*
* Pack index for existing packs give us easy access to the offsets into
@@ -89,15 +90,15 @@ struct pack_revindex {
struct packed_git *p;
unsigned long *revindex;
} *pack_revindex = NULL;
-static int pack_revindex_hashsz = 0;
+static int pack_revindex_hashsz;
/*
* stats
*/
-static int written = 0;
-static int written_delta = 0;
-static int reused = 0;
-static int reused_delta = 0;
+static int written;
+static int written_delta;
+static int reused;
+static int reused_delta;
static int pack_revindex_ix(struct packed_git *p)
{
@@ -269,6 +270,22 @@ static unsigned long write_object(struct sha1file *f,
* and we do not need to deltify it.
*/
+ if (!entry->in_pack && !entry->delta) {
+ unsigned char *map;
+ unsigned long mapsize;
+ map = map_sha1_file(entry->sha1, &mapsize);
+ if (map && !legacy_loose_object(map)) {
+ /* We can copy straight into the pack file */
+ sha1write(f, map, mapsize);
+ munmap(map, mapsize);
+ written++;
+ reused++;
+ return mapsize;
+ }
+ if (map)
+ munmap(map, mapsize);
+ }
+
if (! to_reuse) {
buf = read_sha1_file(entry->sha1, type, &size);
if (!buf)
@@ -424,7 +441,7 @@ static int locate_object_entry_hash(const unsigned char *sha1)
memcpy(&ui, sha1, sizeof(unsigned int));
i = ui % object_ix_hashsz;
while (0 < object_ix[i]) {
- if (!memcmp(sha1, objects[object_ix[i]-1].sha1, 20))
+ if (!hashcmp(sha1, objects[object_ix[i] - 1].sha1))
return i;
if (++i == object_ix_hashsz)
i = 0;
@@ -517,7 +534,7 @@ static int add_object_entry(const unsigned char *sha1, unsigned hash, int exclud
entry = objects + idx;
nr_objects = idx + 1;
memset(entry, 0, sizeof(*entry));
- memcpy(entry->sha1, sha1, 20);
+ hashcpy(entry->sha1, sha1);
entry->hash = hash;
if (object_ix_hashsz * 3 <= nr_objects * 4)
@@ -590,7 +607,7 @@ static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1)
*/
for (neigh = 0; neigh < 8; neigh++) {
ent = pbase_tree_cache[my_ix];
- if (ent && !memcmp(ent->sha1, sha1, 20)) {
+ if (ent && !hashcmp(ent->sha1, sha1)) {
ent->ref++;
return ent;
}
@@ -632,7 +649,7 @@ static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1)
free(ent->tree_data);
nent = ent;
}
- memcpy(nent->sha1, sha1, 20);
+ hashcpy(nent->sha1, sha1);
nent->tree_data = data;
nent->tree_size = size;
nent->ref = 1;
@@ -772,7 +789,7 @@ static void add_preferred_base(unsigned char *sha1)
return;
for (it = pbase_tree; it; it = it->next) {
- if (!memcmp(it->pcache.sha1, tree_sha1, 20)) {
+ if (!hashcmp(it->pcache.sha1, tree_sha1)) {
free(data);
return;
}
@@ -782,7 +799,7 @@ static void add_preferred_base(unsigned char *sha1)
it->next = pbase_tree;
pbase_tree = it;
- memcpy(it->pcache.sha1, tree_sha1, 20);
+ hashcpy(it->pcache.sha1, tree_sha1);
it->pcache.tree_data = data;
it->pcache.tree_size = size;
}
@@ -914,7 +931,7 @@ static struct object_entry **create_sorted_list(entry_sort_t sort)
static int sha1_sort(const struct object_entry *a, const struct object_entry *b)
{
- return memcmp(a->sha1, b->sha1, 20);
+ return hashcmp(a->sha1, b->sha1);
}
static struct object_entry **create_final_object_list(void)
@@ -1226,7 +1243,7 @@ static int git_pack_config(const char *k, const char *v)
return git_default_config(k, v);
}
-int main(int argc, char **argv)
+int cmd_pack_objects(int argc, const char **argv, const char *prefix)
{
SHA_CTX ctx;
char line[40 + 1 + PATH_MAX + 2];
@@ -1235,7 +1252,6 @@ int main(int argc, char **argv)
int num_preferred_base = 0;
int i;
- setup_git_directory();
git_config(git_pack_config);
progress = isatty(2);
diff --git a/builtin-prune.c b/builtin-prune.c
index 89ec7f1..fc885ce 100644
--- a/builtin-prune.c
+++ b/builtin-prune.c
@@ -11,7 +11,7 @@
#include "cache-tree.h"
static const char prune_usage[] = "git-prune [-n]";
-static int show_only = 0;
+static int show_only;
static struct rev_info revs;
static int prune_object(char *path, const char *filename, const unsigned char *sha1)
diff --git a/builtin-push.c b/builtin-push.c
index 53bc378..ada8338 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -10,14 +10,14 @@
static const char push_usage[] = "git-push [--all] [--tags] [-f | --force] <repository> [<refspec>...]";
-static int all = 0, tags = 0, force = 0, thin = 1;
-static const char *execute = NULL;
+static int all, tags, force, thin = 1;
+static const char *execute;
#define BUF_SIZE (2084)
static char buffer[BUF_SIZE];
-static const char **refspec = NULL;
-static int refspec_nr = 0;
+static const char **refspec;
+static int refspec_nr;
static void add_refspec(const char *ref)
{
@@ -32,10 +32,8 @@ static int expand_one_ref(const char *ref, const unsigned char *sha1)
/* Ignore the "refs/" at the beginning of the refname */
ref += 5;
- if (strncmp(ref, "tags/", 5))
- return 0;
-
- add_refspec(strdup(ref));
+ if (!strncmp(ref, "tags/", 5))
+ add_refspec(strdup(ref));
return 0;
}
@@ -234,7 +232,7 @@ static int do_push(const char *repo)
common_argc = argc;
for (i = 0; i < n; i++) {
- int error;
+ int err;
int dest_argc = common_argc;
int dest_refspec_nr = refspec_nr;
const char **dest_refspec = refspec;
@@ -250,10 +248,10 @@ static int do_push(const char *repo)
while (dest_refspec_nr--)
argv[dest_argc++] = *dest_refspec++;
argv[dest_argc] = NULL;
- error = run_command_v(argc, argv);
- if (!error)
+ err = run_command_v(argc, argv);
+ if (!err)
continue;
- switch (error) {
+ switch (err) {
case -ERR_RUN_COMMAND_FORK:
die("unable to fork for %s", sender);
case -ERR_RUN_COMMAND_EXEC:
@@ -264,7 +262,7 @@ static int do_push(const char *repo)
case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
die("%s died with strange error", sender);
default:
- return -error;
+ return -err;
}
}
return 0;
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 8da8acb..c1867d2 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -12,7 +12,7 @@
#include "unpack-trees.h"
#include "builtin.h"
-static struct object_list *trees = NULL;
+static struct object_list *trees;
static int list_tree(unsigned char *sha1)
{
@@ -53,7 +53,7 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
struct name_entry entry;
int cnt;
- memcpy(it->sha1, tree->object.sha1, 20);
+ hashcpy(it->sha1, tree->object.sha1);
desc.buf = tree->buffer;
desc.size = tree->size;
cnt = 0;
diff --git a/builtin-repo-config.c b/builtin-repo-config.c
index c821e22..c416480 100644
--- a/builtin-repo-config.c
+++ b/builtin-repo-config.c
@@ -5,14 +5,14 @@
static const char git_config_set_usage[] =
"git-repo-config [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --unset | --unset-all] name [value [value_regex]] | --list";
-static char* key = NULL;
-static regex_t* key_regexp = NULL;
-static regex_t* regexp = NULL;
-static int show_keys = 0;
-static int use_key_regexp = 0;
-static int do_all = 0;
-static int do_not_match = 0;
-static int seen = 0;
+static char *key;
+static regex_t *key_regexp;
+static regex_t *regexp;
+static int show_keys;
+static int use_key_regexp;
+static int do_all;
+static int do_not_match;
+static int seen;
static enum { T_RAW, T_INT, T_BOOL } type = T_RAW;
static int show_all_config(const char *key_, const char *value_)
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index 0dee173..bc48a3e 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -39,9 +39,9 @@ static const char rev_list_usage[] =
static struct rev_info revs;
-static int bisect_list = 0;
-static int show_timestamp = 0;
-static int hdr_termination = 0;
+static int bisect_list;
+static int show_timestamp;
+static int hdr_termination;
static const char *header_prefix;
static void show_commit(struct commit *commit)
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index aca4a36..fd3ccc8 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -15,16 +15,16 @@
#define DO_NONFLAGS 8
static int filter = ~0;
-static const char *def = NULL;
+static const char *def;
#define NORMAL 0
#define REVERSED 1
static int show_type = NORMAL;
-static int symbolic = 0;
-static int abbrev = 0;
-static int output_sq = 0;
+static int symbolic;
+static int abbrev;
+static int output_sq;
-static int revs_count = 0;
+static int revs_count;
/*
* Some arguments are relevant "revision" arguments,
diff --git a/builtin-show-branch.c b/builtin-show-branch.c
index 2a1b848..18786f8 100644
--- a/builtin-show-branch.c
+++ b/builtin-show-branch.c
@@ -8,9 +8,9 @@
static const char show_branch_usage[] =
"git-show-branch [--sparse] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...]";
-static int default_num = 0;
-static int default_alloc = 0;
-static const char **default_arg = NULL;
+static int default_num;
+static int default_alloc;
+static const char **default_arg;
#define UNINTERESTING 01
@@ -378,7 +378,7 @@ static int append_head_ref(const char *refname, const unsigned char *sha1)
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (get_sha1(refname + ofs, tmp) || memcmp(tmp, sha1, 20))
+ if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))
ofs = 5;
return append_ref(refname + ofs, sha1);
}
@@ -442,7 +442,7 @@ static int rev_is_head(char *head_path, int headlen, char *name,
{
int namelen;
if ((!head_path[0]) ||
- (head_sha1 && sha1 && memcmp(head_sha1, sha1, 20)))
+ (head_sha1 && sha1 && hashcmp(head_sha1, sha1)))
return 0;
namelen = strlen(name);
if ((headlen < namelen) ||
diff --git a/symbolic-ref.c b/builtin-symbolic-ref.c
index 193c87c..b4ec6f2 100644
--- a/symbolic-ref.c
+++ b/builtin-symbolic-ref.c
@@ -1,3 +1,4 @@
+#include "builtin.h"
#include "cache.h"
static const char git_symbolic_ref_usage[] =
@@ -17,9 +18,8 @@ static void check_symref(const char *HEAD)
die("No such ref: %s", HEAD);
}
-int main(int argc, const char **argv)
+int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
{
- setup_git_directory();
git_config(git_default_config);
switch (argc) {
case 2:
diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c
index 215892b..61a4135 100644
--- a/builtin-tar-tree.c
+++ b/builtin-tar-tree.c
@@ -14,7 +14,7 @@
#define BLOCKSIZE (RECORDSIZE * 20)
static const char tar_tree_usage[] =
-"git-tar-tree [--remote=<repo>] <ent> [basedir]";
+"git-tar-tree [--remote=<repo>] <tree-ish> [basedir]";
static char block[BLOCKSIZE];
static unsigned long offset;
@@ -22,30 +22,11 @@ static unsigned long offset;
static time_t archive_time;
static int tar_umask;
-/* tries hard to write, either succeeds or dies in the attempt */
-static void reliable_write(const void *data, unsigned long size)
-{
- const char *buf = data;
-
- while (size > 0) {
- long ret = xwrite(1, buf, size);
- if (ret < 0) {
- if (errno == EPIPE)
- exit(0);
- die("git-tar-tree: %s", strerror(errno));
- } else if (!ret) {
- die("git-tar-tree: disk full?");
- }
- size -= ret;
- buf += ret;
- }
-}
-
/* writes out the whole block, but only if it is full */
static void write_if_needed(void)
{
if (offset == BLOCKSIZE) {
- reliable_write(block, BLOCKSIZE);
+ write_or_die(1, block, BLOCKSIZE);
offset = 0;
}
}
@@ -70,7 +51,7 @@ static void write_blocked(const void *data, unsigned long size)
write_if_needed();
}
while (size >= BLOCKSIZE) {
- reliable_write(buf, BLOCKSIZE);
+ write_or_die(1, buf, BLOCKSIZE);
size -= BLOCKSIZE;
buf += BLOCKSIZE;
}
@@ -94,10 +75,10 @@ static void write_trailer(void)
{
int tail = BLOCKSIZE - offset;
memset(block + offset, 0, tail);
- reliable_write(block, BLOCKSIZE);
+ write_or_die(1, block, BLOCKSIZE);
if (tail < 2 * RECORDSIZE) {
memset(block, 0, offset);
- reliable_write(block, BLOCKSIZE);
+ write_or_die(1, block, BLOCKSIZE);
}
}
@@ -294,7 +275,7 @@ static void traverse_tree(struct tree_desc *tree, struct strbuf *path)
}
}
-int git_tar_config(const char *var, const char *value)
+static int git_tar_config(const char *var, const char *value)
{
if (!strcmp(var, "tar.umask")) {
if (!strcmp(value, "user")) {
diff --git a/unpack-objects.c b/builtin-unpack-objects.c
index 48c1ee7..ca0ebc2 100644
--- a/unpack-objects.c
+++ b/builtin-unpack-objects.c
@@ -1,3 +1,4 @@
+#include "builtin.h"
#include "cache.h"
#include "object.h"
#include "delta.h"
@@ -94,7 +95,7 @@ static void add_delta_to_list(unsigned char *base_sha1, void *delta, unsigned lo
{
struct delta_info *info = xmalloc(sizeof(*info));
- memcpy(info->base_sha1, base_sha1, 20);
+ hashcpy(info->base_sha1, base_sha1);
info->size = size;
info->delta = delta;
info->next = delta_list;
@@ -112,7 +113,7 @@ static void write_object(void *buf, unsigned long size, const char *type)
}
static int resolve_delta(const char *type,
- void *base, unsigned long base_size,
+ void *base, unsigned long base_size,
void *delta, unsigned long delta_size)
{
void *result;
@@ -135,7 +136,7 @@ static void added_object(unsigned char *sha1, const char *type, void *data, unsi
struct delta_info *info;
while ((info = *p) != NULL) {
- if (!memcmp(info->base_sha1, sha1, 20)) {
+ if (!hashcmp(info->base_sha1, sha1)) {
*p = info->next;
p = &delta_list;
resolve_delta(type, data, size, info->delta, info->size);
@@ -172,7 +173,7 @@ static int unpack_delta_entry(unsigned long delta_size)
unsigned char base_sha1[20];
int result;
- memcpy(base_sha1, fill(20), 20);
+ hashcpy(base_sha1, fill(20));
use(20);
delta_data = get_data(delta_size);
@@ -260,12 +261,12 @@ static void unpack_all(void)
die("unresolved deltas left after unpacking");
}
-int main(int argc, char **argv)
+int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
{
int i;
unsigned char sha1[20];
- setup_git_directory();
+ git_config(git_default_config);
quiet = !isatty(2);
@@ -291,7 +292,7 @@ int main(int argc, char **argv)
unpack_all();
SHA1_Update(&ctx, buffer, offset);
SHA1_Final(sha1, &ctx);
- if (memcmp(fill(20), sha1, 20))
+ if (hashcmp(fill(20), sha1))
die("final sha1 did not match");
use(20);
diff --git a/builtin-update-index.c b/builtin-update-index.c
index d2556f3..0620e77 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -23,7 +23,7 @@ static int allow_replace;
static int info_only;
static int force_remove;
static int verbose;
-static int mark_valid_only = 0;
+static int mark_valid_only;
#define MARK_VALID 1
#define UNMARK_VALID 2
@@ -142,7 +142,7 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
size = cache_entry_size(len);
ce = xcalloc(1, size);
- memcpy(ce->sha1, sha1, 20);
+ hashcpy(ce->sha1, sha1);
memcpy(ce->name, path, len);
ce->ce_flags = create_ce_flags(len, stage);
ce->ce_mode = create_ce_mode(mode);
@@ -306,7 +306,7 @@ static void read_index_info(int line_termination)
}
static const char update_index_usage[] =
-"git-update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again] [--ignore-missing] [-z] [--verbose] [--] <file>...";
+"git-update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again | -g] [--ignore-missing] [-z] [--verbose] [--] <file>...";
static unsigned char head_sha1[20];
static unsigned char merge_head_sha1[20];
@@ -333,7 +333,7 @@ static struct cache_entry *read_one_ent(const char *which,
size = cache_entry_size(namelen);
ce = xcalloc(1, size);
- memcpy(ce->sha1, sha1, 20);
+ hashcpy(ce->sha1, sha1);
memcpy(ce->name, path, namelen);
ce->ce_flags = create_ce_flags(namelen, stage);
ce->ce_mode = create_ce_mode(mode);
@@ -378,7 +378,7 @@ static int unresolve_one(const char *path)
ret = -1;
goto free_return;
}
- if (!memcmp(ce_2->sha1, ce_3->sha1, 20) &&
+ if (!hashcmp(ce_2->sha1, ce_3->sha1) &&
ce_2->ce_mode == ce_3->ce_mode) {
fprintf(stderr, "%s: identical in both, skipping.\n",
path);
@@ -460,7 +460,7 @@ static int do_reupdate(int ac, const char **av,
old = read_one_ent(NULL, head_sha1,
ce->name, ce_namelen(ce), 0);
if (old && ce->ce_mode == old->ce_mode &&
- !memcmp(ce->sha1, old->sha1, 20)) {
+ !hashcmp(ce->sha1, old->sha1)) {
free(old);
continue; /* unchanged */
}
@@ -595,7 +595,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
active_cache_changed = 0;
goto finish;
}
- if (!strcmp(path, "--again")) {
+ if (!strcmp(path, "--again") || !strcmp(path, "-g")) {
has_errors = do_reupdate(argc - i, argv + i,
prefix, prefix_length);
if (has_errors)
diff --git a/builtin-update-ref.c b/builtin-update-ref.c
index 5bd7182..90a3da5 100644
--- a/builtin-update-ref.c
+++ b/builtin-update-ref.c
@@ -44,7 +44,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
if (get_sha1(value, sha1))
die("%s: not a valid SHA1", value);
- memset(oldsha1, 0, 20);
+ hashclr(oldsha1);
if (oldval && get_sha1(oldval, oldsha1))
die("%s: not a valid old SHA1", oldval);
diff --git a/verify-pack.c b/builtin-verify-pack.c
index 357970d..7d39d9b 100644
--- a/verify-pack.c
+++ b/builtin-verify-pack.c
@@ -1,3 +1,4 @@
+#include "builtin.h"
#include "cache.h"
#include "pack.h"
@@ -47,28 +48,28 @@ static int verify_one_pack(const char *path, int verbose)
static const char verify_pack_usage[] = "git-verify-pack [-v] <pack>...";
-int main(int ac, char **av)
+int cmd_verify_pack(int argc, const char **argv, const char *prefix)
{
int err = 0;
int verbose = 0;
int no_more_options = 0;
int nothing_done = 1;
- while (1 < ac) {
- if (!no_more_options && av[1][0] == '-') {
- if (!strcmp("-v", av[1]))
+ while (1 < argc) {
+ if (!no_more_options && argv[1][0] == '-') {
+ if (!strcmp("-v", argv[1]))
verbose = 1;
- else if (!strcmp("--", av[1]))
+ else if (!strcmp("--", argv[1]))
no_more_options = 1;
else
usage(verify_pack_usage);
}
else {
- if (verify_one_pack(av[1], verbose))
+ if (verify_one_pack(argv[1], verbose))
err = 1;
nothing_done = 0;
}
- ac--; av++;
+ argc--; argv++;
}
if (nothing_done)
diff --git a/builtin-write-tree.c b/builtin-write-tree.c
index ca06149..50670dc 100644
--- a/builtin-write-tree.c
+++ b/builtin-write-tree.c
@@ -50,10 +50,10 @@ int write_tree(unsigned char *sha1, int missing_ok, const char *prefix)
if (prefix) {
struct cache_tree *subtree =
cache_tree_find(active_cache_tree, prefix);
- memcpy(sha1, subtree->sha1, 20);
+ hashcpy(sha1, subtree->sha1);
}
else
- memcpy(sha1, active_cache_tree->sha1, 20);
+ hashcpy(sha1, active_cache_tree->sha1);
rollback_lock_file(lock_file);
diff --git a/builtin-zip-tree.c b/builtin-zip-tree.c
new file mode 100644
index 0000000..a5b834d
--- /dev/null
+++ b/builtin-zip-tree.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2006 Rene Scharfe
+ */
+#include <time.h>
+#include "cache.h"
+#include "commit.h"
+#include "blob.h"
+#include "tree.h"
+#include "quote.h"
+#include "builtin.h"
+
+static const char zip_tree_usage[] =
+"git-zip-tree [-0|...|-9] <tree-ish> [ <base> ]";
+
+static int zip_date;
+static int zip_time;
+
+static unsigned char *zip_dir;
+static unsigned int zip_dir_size;
+
+static unsigned int zip_offset;
+static unsigned int zip_dir_offset;
+static unsigned int zip_dir_entries;
+
+#define ZIP_DIRECTORY_MIN_SIZE (1024 * 1024)
+
+struct zip_local_header {
+ unsigned char magic[4];
+ unsigned char version[2];
+ unsigned char flags[2];
+ unsigned char compression_method[2];
+ unsigned char mtime[2];
+ unsigned char mdate[2];
+ unsigned char crc32[4];
+ unsigned char compressed_size[4];
+ unsigned char size[4];
+ unsigned char filename_length[2];
+ unsigned char extra_length[2];
+};
+
+struct zip_dir_header {
+ unsigned char magic[4];
+ unsigned char creator_version[2];
+ unsigned char version[2];
+ unsigned char flags[2];
+ unsigned char compression_method[2];
+ unsigned char mtime[2];
+ unsigned char mdate[2];
+ unsigned char crc32[4];
+ unsigned char compressed_size[4];
+ unsigned char size[4];
+ unsigned char filename_length[2];
+ unsigned char extra_length[2];
+ unsigned char comment_length[2];
+ unsigned char disk[2];
+ unsigned char attr1[2];
+ unsigned char attr2[4];
+ unsigned char offset[4];
+};
+
+struct zip_dir_trailer {
+ unsigned char magic[4];
+ unsigned char disk[2];
+ unsigned char directory_start_disk[2];
+ unsigned char entries_on_this_disk[2];
+ unsigned char entries[2];
+ unsigned char size[4];
+ unsigned char offset[4];
+ unsigned char comment_length[2];
+};
+
+static void copy_le16(unsigned char *dest, unsigned int n)
+{
+ dest[0] = 0xff & n;
+ dest[1] = 0xff & (n >> 010);
+}
+
+static void copy_le32(unsigned char *dest, unsigned int n)
+{
+ dest[0] = 0xff & n;
+ dest[1] = 0xff & (n >> 010);
+ dest[2] = 0xff & (n >> 020);
+ dest[3] = 0xff & (n >> 030);
+}
+
+static void *zlib_deflate(void *data, unsigned long size,
+ unsigned long *compressed_size)
+{
+ z_stream stream;
+ unsigned long maxsize;
+ void *buffer;
+ int result;
+
+ memset(&stream, 0, sizeof(stream));
+ deflateInit(&stream, zlib_compression_level);
+ maxsize = deflateBound(&stream, size);
+ buffer = xmalloc(maxsize);
+
+ stream.next_in = data;
+ stream.avail_in = size;
+ stream.next_out = buffer;
+ stream.avail_out = maxsize;
+
+ do {
+ result = deflate(&stream, Z_FINISH);
+ } while (result == Z_OK);
+
+ if (result != Z_STREAM_END) {
+ free(buffer);
+ return NULL;
+ }
+
+ deflateEnd(&stream);
+ *compressed_size = stream.total_out;
+
+ return buffer;
+}
+
+static char *construct_path(const char *base, int baselen,
+ const char *filename, int isdir, int *pathlen)
+{
+ int filenamelen = strlen(filename);
+ int len = baselen + filenamelen;
+ char *path, *p;
+
+ if (isdir)
+ len++;
+ p = path = xmalloc(len + 1);
+
+ memcpy(p, base, baselen);
+ p += baselen;
+ memcpy(p, filename, filenamelen);
+ p += filenamelen;
+ if (isdir)
+ *p++ = '/';
+ *p = '\0';
+
+ *pathlen = len;
+
+ return path;
+}
+
+static int write_zip_entry(const unsigned char *sha1,
+ const char *base, int baselen,
+ const char *filename, unsigned mode, int stage)
+{
+ struct zip_local_header header;
+ struct zip_dir_header dirent;
+ unsigned long compressed_size;
+ unsigned long uncompressed_size;
+ unsigned long crc;
+ unsigned long direntsize;
+ unsigned long size;
+ int method;
+ int result = -1;
+ int pathlen;
+ unsigned char *out;
+ char *path;
+ char type[20];
+ void *buffer = NULL;
+ void *deflated = NULL;
+
+ crc = crc32(0, Z_NULL, 0);
+
+ path = construct_path(base, baselen, filename, S_ISDIR(mode), &pathlen);
+ if (pathlen > 0xffff) {
+ error("path too long (%d chars, SHA1: %s): %s", pathlen,
+ sha1_to_hex(sha1), path);
+ goto out;
+ }
+
+ if (S_ISDIR(mode)) {
+ method = 0;
+ result = READ_TREE_RECURSIVE;
+ out = NULL;
+ uncompressed_size = 0;
+ compressed_size = 0;
+ } else if (S_ISREG(mode)) {
+ method = zlib_compression_level == 0 ? 0 : 8;
+ result = 0;
+ buffer = read_sha1_file(sha1, type, &size);
+ if (!buffer)
+ die("cannot read %s", sha1_to_hex(sha1));
+ crc = crc32(crc, buffer, size);
+ out = buffer;
+ uncompressed_size = size;
+ compressed_size = size;
+ } else {
+ error("unsupported file mode: 0%o (SHA1: %s)", mode,
+ sha1_to_hex(sha1));
+ goto out;
+ }
+
+ if (method == 8) {
+ deflated = zlib_deflate(buffer, size, &compressed_size);
+ if (deflated && compressed_size - 6 < size) {
+ /* ZLIB --> raw compressed data (see RFC 1950) */
+ /* CMF and FLG ... */
+ out = (unsigned char *)deflated + 2;
+ compressed_size -= 6; /* ... and ADLER32 */
+ } else {
+ method = 0;
+ compressed_size = size;
+ }
+ }
+
+ /* make sure we have enough free space in the dictionary */
+ direntsize = sizeof(struct zip_dir_header) + pathlen;
+ while (zip_dir_size < zip_dir_offset + direntsize) {
+ zip_dir_size += ZIP_DIRECTORY_MIN_SIZE;
+ zip_dir = xrealloc(zip_dir, zip_dir_size);
+ }
+
+ copy_le32(dirent.magic, 0x02014b50);
+ copy_le16(dirent.creator_version, 0);
+ copy_le16(dirent.version, 20);
+ copy_le16(dirent.flags, 0);
+ copy_le16(dirent.compression_method, method);
+ copy_le16(dirent.mtime, zip_time);
+ copy_le16(dirent.mdate, zip_date);
+ copy_le32(dirent.crc32, crc);
+ copy_le32(dirent.compressed_size, compressed_size);
+ copy_le32(dirent.size, uncompressed_size);
+ copy_le16(dirent.filename_length, pathlen);
+ copy_le16(dirent.extra_length, 0);
+ copy_le16(dirent.comment_length, 0);
+ copy_le16(dirent.disk, 0);
+ copy_le16(dirent.attr1, 0);
+ copy_le32(dirent.attr2, 0);
+ copy_le32(dirent.offset, zip_offset);
+ memcpy(zip_dir + zip_dir_offset, &dirent, sizeof(struct zip_dir_header));
+ zip_dir_offset += sizeof(struct zip_dir_header);
+ memcpy(zip_dir + zip_dir_offset, path, pathlen);
+ zip_dir_offset += pathlen;
+ zip_dir_entries++;
+
+ copy_le32(header.magic, 0x04034b50);
+ copy_le16(header.version, 20);
+ copy_le16(header.flags, 0);
+ copy_le16(header.compression_method, method);
+ copy_le16(header.mtime, zip_time);
+ copy_le16(header.mdate, zip_date);
+ copy_le32(header.crc32, crc);
+ copy_le32(header.compressed_size, compressed_size);
+ copy_le32(header.size, uncompressed_size);
+ copy_le16(header.filename_length, pathlen);
+ copy_le16(header.extra_length, 0);
+ write_or_die(1, &header, sizeof(struct zip_local_header));
+ zip_offset += sizeof(struct zip_local_header);
+ write_or_die(1, path, pathlen);
+ zip_offset += pathlen;
+ if (compressed_size > 0) {
+ write_or_die(1, out, compressed_size);
+ zip_offset += compressed_size;
+ }
+
+out:
+ free(buffer);
+ free(deflated);
+ free(path);
+
+ return result;
+}
+
+static void write_zip_trailer(const unsigned char *sha1)
+{
+ struct zip_dir_trailer trailer;
+
+ copy_le32(trailer.magic, 0x06054b50);
+ copy_le16(trailer.disk, 0);
+ copy_le16(trailer.directory_start_disk, 0);
+ copy_le16(trailer.entries_on_this_disk, zip_dir_entries);
+ copy_le16(trailer.entries, zip_dir_entries);
+ copy_le32(trailer.size, zip_dir_offset);
+ copy_le32(trailer.offset, zip_offset);
+ copy_le16(trailer.comment_length, sha1 ? 40 : 0);
+
+ write_or_die(1, zip_dir, zip_dir_offset);
+ write_or_die(1, &trailer, sizeof(struct zip_dir_trailer));
+ if (sha1)
+ write_or_die(1, sha1_to_hex(sha1), 40);
+}
+
+static void dos_time(time_t *time, int *dos_date, int *dos_time)
+{
+ struct tm *t = localtime(time);
+
+ *dos_date = t->tm_mday + (t->tm_mon + 1) * 32 +
+ (t->tm_year + 1900 - 1980) * 512;
+ *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 = strdup(argv[2]);
+ baselen = strlen(base);
+ break;
+ case 2:
+ base = strdup("");
+ 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;
+}
diff --git a/builtin.h b/builtin.h
index 26ebcaf..25431d7 100644
--- a/builtin.h
+++ b/builtin.h
@@ -8,57 +8,58 @@ extern const char git_version_string[];
extern const char git_usage_string[];
extern void help_unknown_cmd(const char *cmd);
+extern int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const char *msg, const char *patch);
+extern int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip);
+extern void stripspace(FILE *in, FILE *out);
+extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
-extern int cmd_help(int argc, const char **argv, const char *prefix);
-extern int cmd_version(int argc, const char **argv, const char *prefix);
-
-extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
-extern int cmd_show(int argc, const char **argv, const char *prefix);
-extern int cmd_log(int argc, const char **argv, const char *prefix);
-extern int cmd_diff(int argc, const char **argv, const char *prefix);
-extern int cmd_format_patch(int argc, const char **argv, const char *prefix);
-extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
-
-extern int cmd_prune(int argc, const char **argv, const char *prefix);
-extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
-
-extern int cmd_push(int argc, const char **argv, const char *prefix);
-extern int cmd_grep(int argc, const char **argv, const char *prefix);
-extern int cmd_rm(int argc, const char **argv, const char *prefix);
extern int cmd_add(int argc, const char **argv, const char *prefix);
-extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
+extern int cmd_apply(int argc, const char **argv, const char *prefix);
+extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
+extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
-extern int cmd_init_db(int argc, const char **argv, const char *prefix);
-extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_upload_tar(int argc, const char **argv, const char *prefix);
-extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
-extern int cmd_ls_files(int argc, const char **argv, const char *prefix);
-extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_apply(int argc, const char **argv, const char *prefix);
-extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
+extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
extern int cmd_diff_index(int argc, const char **argv, const char *prefix);
+extern int cmd_diff(int argc, const char **argv, const char *prefix);
extern int cmd_diff_stages(int argc, const char **argv, const char *prefix);
extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
-extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
-extern int cmd_update_index(int argc, const char **argv, const char *prefix);
-extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
+extern int cmd_format_patch(int argc, const char **argv, const char *prefix);
+extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
+extern int cmd_grep(int argc, const char **argv, const char *prefix);
+extern int cmd_help(int argc, const char **argv, const char *prefix);
+extern int cmd_init_db(int argc, const char **argv, const char *prefix);
+extern int cmd_log(int argc, const char **argv, const char *prefix);
+extern int cmd_ls_files(int argc, const char **argv, const char *prefix);
+extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
+extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
extern int cmd_mv(int argc, const char **argv, const char *prefix);
+extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
+extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
+extern int cmd_prune(int argc, const char **argv, const char *prefix);
+extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
+extern int cmd_push(int argc, const char **argv, const char *prefix);
+extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
extern int cmd_repo_config(int argc, const char **argv, const char *prefix);
-
+extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
+extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
+extern int cmd_rm(int argc, const char **argv, const char *prefix);
+extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
+extern int cmd_show(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);
+extern int cmd_upload_tar(int argc, const char **argv, const char *prefix);
+extern int cmd_version(int argc, const char **argv, const char *prefix);
+extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
extern int cmd_write_tree(int argc, const char **argv, const char *prefix);
-extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
+extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);
-extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
-extern int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip);
-
-extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
-extern int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const char *msg, const char *patch);
-
-extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
-extern void stripspace(FILE *in, FILE *out);
#endif
diff --git a/cache-tree.c b/cache-tree.c
index d9f7e1e..323c68a 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -335,7 +335,7 @@ static int update_one(struct cache_tree *it,
offset += sprintf(buffer + offset,
"%o %.*s", mode, entlen, path + baselen);
buffer[offset++] = 0;
- memcpy(buffer + offset, sha1, 20);
+ hashcpy((unsigned char*)buffer + offset, sha1);
offset += 20;
#if DEBUG
@@ -412,7 +412,7 @@ static void *write_one(struct cache_tree *it,
#endif
if (0 <= it->entry_count) {
- memcpy(buffer + *offset, it->sha1, 20);
+ hashcpy((unsigned char*)buffer + *offset, it->sha1);
*offset += 20;
}
for (i = 0; i < it->subtree_nr; i++) {
@@ -478,7 +478,7 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)
if (0 <= it->entry_count) {
if (size < 20)
goto free_return;
- memcpy(it->sha1, buf, 20);
+ hashcpy(it->sha1, (unsigned char*)buf);
buf += 20;
size -= 20;
}
diff --git a/cache.h b/cache.h
index 9f90df4..03d9dd0 100644
--- a/cache.h
+++ b/cache.h
@@ -123,7 +123,7 @@ extern int cache_errno;
#define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
#define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
-extern char *get_git_dir(void);
+extern const char *get_git_dir(void);
extern char *get_object_directory(void);
extern char *get_refs_directory(void);
extern char *get_index_file(void);
@@ -211,6 +211,22 @@ extern char *sha1_pack_name(const unsigned char *sha1);
extern char *sha1_pack_index_name(const unsigned char *sha1);
extern const char *find_unique_abbrev(const unsigned char *sha1, int);
extern const unsigned char null_sha1[20];
+static inline int is_null_sha1(const unsigned char *sha1)
+{
+ return !memcmp(sha1, null_sha1, 20);
+}
+static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
+{
+ return memcmp(sha1, sha2, 20);
+}
+static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
+{
+ memcpy(sha_dst, sha_src, 20);
+}
+static inline void hashclr(unsigned char *hash)
+{
+ memset(hash, 0, 20);
+}
int git_mkstemp(char *path, size_t n, const char *template);
@@ -245,6 +261,8 @@ extern int move_temp_to_file(const char *tmpfile, char *filename);
extern int has_sha1_pack(const unsigned char *sha1);
extern int has_sha1_file(const unsigned char *sha1);
+extern void *map_sha1_file(const unsigned char *sha1, unsigned long *);
+extern int legacy_loose_object(unsigned char *);
extern int has_pack_file(const unsigned char *sha1);
extern int has_pack_index(const unsigned char *sha1);
@@ -269,7 +287,7 @@ extern void *read_object_with_reference(const unsigned char *sha1,
unsigned long *size,
unsigned char *sha1_ret);
-const char *show_date(unsigned long time, int timezone);
+const char *show_date(unsigned long time, int timezone, int relative);
const char *show_rfc2822_date(unsigned long time, int timezone);
int parse_date(const char *date, char *buf, int bufsize);
void datestamp(char *buf, int bufsize);
@@ -379,6 +397,7 @@ extern char git_default_name[MAX_GITNAME];
extern char git_commit_encoding[MAX_ENCODING_LENGTH];
extern int copy_fd(int ifd, int ofd);
+extern void write_or_die(int fd, const void *buf, size_t count);
/* Finish off pack transfer receiving end */
extern int receive_unpack_pack(int fd[2], const char *me, int quiet, int);
diff --git a/check-racy.c b/check-racy.c
new file mode 100644
index 0000000..d6a08b4
--- /dev/null
+++ b/check-racy.c
@@ -0,0 +1,28 @@
+#include "cache.h"
+
+int main(int ac, char **av)
+{
+ int i;
+ int dirty, clean, racy;
+
+ dirty = clean = racy = 0;
+ read_cache();
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ struct stat st;
+
+ if (lstat(ce->name, &st)) {
+ error("lstat(%s): %s", ce->name, strerror(errno));
+ continue;
+ }
+
+ if (ce_match_stat(ce, &st, 0))
+ dirty++;
+ else if (ce_match_stat(ce, &st, 2))
+ racy++;
+ else
+ clean++;
+ }
+ printf("dirty %d, clean %d, racy %d\n", dirty, clean, racy);
+ return 0;
+}
diff --git a/combine-diff.c b/combine-diff.c
index ba8baca..46d9121 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -7,13 +7,6 @@
#include "xdiff-interface.h"
#include "log-tree.h"
-static int uninteresting(struct diff_filepair *p)
-{
- if (diff_unmodified_pair(p))
- return 1;
- return 0;
-}
-
static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent)
{
struct diff_queue_struct *q = &diff_queued_diff;
@@ -25,7 +18,7 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
for (i = 0; i < q->nr; i++) {
int len;
const char *path;
- if (uninteresting(q->queue[i]))
+ if (diff_unmodified_pair(q->queue[i]))
continue;
path = q->queue[i]->two->path;
len = strlen(path);
@@ -38,9 +31,9 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
memset(p->parent, 0,
sizeof(p->parent[0]) * num_parent);
- memcpy(p->sha1, q->queue[i]->two->sha1, 20);
+ hashcpy(p->sha1, q->queue[i]->two->sha1);
p->mode = q->queue[i]->two->mode;
- memcpy(p->parent[n].sha1, q->queue[i]->one->sha1, 20);
+ hashcpy(p->parent[n].sha1, q->queue[i]->one->sha1);
p->parent[n].mode = q->queue[i]->one->mode;
p->parent[n].status = q->queue[i]->status;
*tail = p;
@@ -57,14 +50,13 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
const char *path;
int len;
- if (uninteresting(q->queue[i]))
+ if (diff_unmodified_pair(q->queue[i]))
continue;
path = q->queue[i]->two->path;
len = strlen(path);
if (len == p->len && !memcmp(path, p->path, len)) {
found = 1;
- memcpy(p->parent[n].sha1,
- q->queue[i]->one->sha1, 20);
+ hashcpy(p->parent[n].sha1, q->queue[i]->one->sha1);
p->parent[n].mode = q->queue[i]->one->mode;
p->parent[n].status = q->queue[i]->status;
break;
@@ -101,7 +93,7 @@ static char *grab_blob(const unsigned char *sha1, unsigned long *size)
{
char *blob;
char type[20];
- if (!memcmp(sha1, null_sha1, 20)) {
+ if (is_null_sha1(sha1)) {
/* deleted blob */
*size = 0;
return xcalloc(1, 1);
@@ -609,16 +601,16 @@ static void dump_quoted_path(const char *prefix, const char *path,
printf("%s\n", c_reset);
}
-static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
- int dense, struct rev_info *rev)
+static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
+ int dense, struct rev_info *rev)
{
struct diff_options *opt = &rev->diffopt;
unsigned long result_size, cnt, lno;
char *result, *cp;
struct sline *sline; /* survived lines */
int mode_differs = 0;
- int i, show_hunks, shown_header = 0;
- int working_tree_file = !memcmp(elem->sha1, null_sha1, 20);
+ int i, show_hunks;
+ int working_tree_file = is_null_sha1(elem->sha1);
int abbrev = opt->full_index ? 40 : DEFAULT_ABBREV;
mmfile_t result_file;
@@ -695,8 +687,8 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
for (i = 0; i < num_parent; i++) {
int j;
for (j = 0; j < i; j++) {
- if (!memcmp(elem->parent[i].sha1,
- elem->parent[j].sha1, 20)) {
+ if (!hashcmp(elem->parent[i].sha1,
+ elem->parent[j].sha1)) {
reuse_combine_diff(sline, cnt, i, j);
break;
}
@@ -769,7 +761,6 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
}
free(sline[0].p_lno);
free(sline);
- return shown_header;
}
#define COLONS "::::::::::::::::::::::::::::::::"
@@ -837,11 +828,10 @@ void show_combined_diff(struct combine_diff_path *p,
return;
if (opt->output_format & (DIFF_FORMAT_RAW |
DIFF_FORMAT_NAME |
- DIFF_FORMAT_NAME_STATUS)) {
+ DIFF_FORMAT_NAME_STATUS))
show_raw_diff(p, num_parent, rev);
- } else if (opt->output_format & DIFF_FORMAT_PATCH) {
+ else if (opt->output_format & DIFF_FORMAT_PATCH)
show_patch_diff(p, num_parent, dense, rev);
- }
}
void diff_tree_combined(const unsigned char *sha1,
@@ -936,6 +926,7 @@ void diff_tree_combined_merge(const unsigned char *sha1,
for (parents = commit->parents, num_parent = 0;
parents;
parents = parents->next, num_parent++)
- memcpy(parent + num_parent, parents->item->object.sha1, 20);
+ hashcpy((unsigned char*)(parent + num_parent),
+ parents->item->object.sha1);
diff_tree_combined(sha1, parent, num_parent, dense, rev);
}
diff --git a/commit.c b/commit.c
index 77f0ca1..c3ff9b4 100644
--- a/commit.c
+++ b/commit.c
@@ -7,15 +7,15 @@ int save_commit_buffer = 1;
struct sort_node
{
/*
- * the number of children of the associated commit
- * that also occur in the list being sorted.
- */
+ * the number of children of the associated commit
+ * that also occur in the list being sorted.
+ */
unsigned int indegree;
/*
- * reference to original list item that we will re-use
- * on output.
- */
+ * reference to original list item that we will re-use
+ * on output.
+ */
struct commit_list * list_item;
};
@@ -123,7 +123,7 @@ static int commit_graft_pos(const unsigned char *sha1)
while (lo < hi) {
int mi = (lo + hi) / 2;
struct commit_graft *graft = commit_graft[mi];
- int cmp = memcmp(sha1, graft->sha1, 20);
+ int cmp = hashcmp(sha1, graft->sha1);
if (!cmp)
return mi;
if (cmp < 0)
@@ -507,14 +507,14 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
}
switch (fmt) {
case CMIT_FMT_MEDIUM:
- ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz));
+ ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz, 0));
break;
case CMIT_FMT_EMAIL:
ret += sprintf(buf + ret, "Date: %s\n",
show_rfc2822_date(time, tz));
break;
case CMIT_FMT_FULLER:
- ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz));
+ ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz, 0));
break;
default:
/* notin' */
@@ -727,10 +727,10 @@ struct commit *pop_commit(struct commit_list **stack)
int count_parents(struct commit * commit)
{
- int count = 0;
+ int count;
struct commit_list * parents = commit->parents;
- for (count=0;parents; parents=parents->next,count++)
- ;
+ for (count = 0; parents; parents = parents->next,count++)
+ ;
return count;
}
diff --git a/config.mak.in b/config.mak.in
index 04f508a..369e611 100644
--- a/config.mak.in
+++ b/config.mak.in
@@ -22,3 +22,19 @@ VPATH = @srcdir@
export exec_prefix mandir
export srcdir VPATH
+NO_PYTHON=@NO_PYTHON@
+NEEDS_SSL_WITH_CRYPTO=@NEEDS_SSL_WITH_CRYPTO@
+NO_OPENSSL=@NO_OPENSSL@
+NO_CURL=@NO_CURL@
+NO_EXPAT=@NO_EXPAT@
+NEEDS_LIBICONV=@NEEDS_LIBICONV@
+NEEDS_SOCKET=@NEEDS_SOCKET@
+NO_D_INO_IN_DIRENT=@NO_D_INO_IN_DIRENT@
+NO_D_TYPE_IN_DIRENT=@NO_D_TYPE_IN_DIRENT@
+NO_SOCKADDR_STORAGE=@NO_SOCKADDR_STORAGE@
+NO_IPV6=@NO_IPV6@
+NO_C99_FORMAT=@NO_C99_FORMAT@
+NO_STRCASESTR=@NO_STRCASESTR@
+NO_STRLCPY=@NO_STRLCPY@
+NO_SETENV=@NO_SETENV@
+
diff --git a/configure.ac b/configure.ac
index a9c88c6..36f9cd9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
-AC_INIT([git], [1.4.2], [git@vger.kernel.org])
+AC_INIT([git], [@@GIT_VERSION@@], [git@vger.kernel.org])
AC_CONFIG_SRCDIR([git.c])
@@ -19,6 +19,77 @@ echo "# ${config_append}. Generated by configure." > "${config_append}"
# Append LINE to file ${config_append}
AC_DEFUN([GIT_CONF_APPEND_LINE],
[echo "$1" >> "${config_append}"])# GIT_CONF_APPEND_LINE
+#
+# GIT_ARG_SET_PATH(PROGRAM)
+# -------------------------
+# Provide --with-PROGRAM=PATH option to set PATH to PROGRAM
+AC_DEFUN([GIT_ARG_SET_PATH],
+[AC_ARG_WITH([$1],
+ [AS_HELP_STRING([--with-$1=PATH],
+ [provide PATH to $1])],
+ [GIT_CONF_APPEND_PATH($1)],[])
+])# GIT_ARG_SET_PATH
+#
+# GIT_CONF_APPEND_PATH(PROGRAM)
+# ------------------------------
+# Parse --with-PROGRAM=PATH option to set PROGRAM_PATH=PATH
+# Used by GIT_ARG_SET_PATH(PROGRAM)
+AC_DEFUN([GIT_CONF_APPEND_PATH],
+[PROGRAM=m4_toupper($1); \
+if test "$withval" = "no"; then \
+ AC_MSG_ERROR([You cannot use git without $1]); \
+else \
+ if test "$withval" = "yes"; then \
+ AC_MSG_WARN([You should provide path for --with-$1=PATH]); \
+ else \
+ GIT_CONF_APPEND_LINE(${PROGRAM}_PATH=$withval); \
+ fi; \
+fi; \
+]) # GIT_CONF_APPEND_PATH
+#
+# GIT_PARSE_WITH(PACKAGE)
+# -----------------------
+# For use in AC_ARG_WITH action-if-found, for packages default ON.
+# * Set NO_PACKAGE=YesPlease for --without-PACKAGE
+# * Set PACKAGEDIR=PATH for --with-PACKAGE=PATH
+# * Unset NO_PACKAGE for --with-PACKAGE without ARG
+AC_DEFUN([GIT_PARSE_WITH],
+[PACKAGE=m4_toupper($1); \
+if test "$withval" = "no"; then \
+ m4_toupper(NO_$1)=YesPlease; \
+elif test "$withval" = "yes"; then \
+ m4_toupper(NO_$1)=; \
+else \
+ m4_toupper(NO_$1)=; \
+ GIT_CONF_APPEND_LINE(${PACKAGE}DIR=$withval); \
+fi \
+])# GIT_PARSE_WITH
+
+
+## Site configuration related to programs (before tests)
+## --with-PACKAGE[=ARG] and --without-PACKAGE
+#
+# Define SHELL_PATH to provide path to shell.
+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])],
+ [if test "$withval" = "no"; then \
+ NO_PYTHON=YesPlease; \
+ elif test "$withval" = "yes"; then \
+ NO_PYTHON=; \
+ else \
+ NO_PYTHON=; \
+ PYTHON_PATH=$withval; \
+ fi; \
+ ])
+AC_SUBST(NO_PYTHON)
+AC_SUBST(PYTHON_PATH)
## Checks for programs.
@@ -30,6 +101,16 @@ 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
+ AC_PATH_PROGS(PYTHON_PATH, [python python2.4 python2.3 python2])
+ fi
+ if test -n "$PYTHON_PATH"; then
+ GIT_CONF_APPEND_LINE([PYTHON_PATH=@PYTHON_PATH@])
+ NO_PYTHON=""
+ fi
+fi
## Checks for libraries.
@@ -37,32 +118,43 @@ AC_MSG_NOTICE([CHECKS for libraries])
#
# Define NO_OPENSSL environment variable if you do not have OpenSSL.
# Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin).
-AC_CHECK_LIB([ssl], [SHA1_Init],[],
-[AC_CHECK_LIB([crypto], [SHA1_INIT],
- [GIT_CONF_APPEND_LINE(NEEDS_SSL_WITH_CRYPTO=YesPlease)],
- [GIT_CONF_APPEND_LINE(NO_OPENSSL=YesPlease)])])
+AC_CHECK_LIB([crypto], [SHA1_Init],
+[NEEDS_SSL_WITH_CRYPTO=],
+[AC_CHECK_LIB([ssl], [SHA1_Init],
+ [NEEDS_SSL_WITH_CRYPTO=YesPlease
+ NEEDS_SSL_WITH_CRYPTO=],
+ [NO_OPENSSL=YesPlease])])
+AC_SUBST(NEEDS_SSL_WITH_CRYPTO)
+AC_SUBST(NO_OPENSSL)
#
# Define NO_CURL if you do not have curl installed. git-http-pull and
# git-http-push are not built, and you cannot use http:// and https://
# transports.
-AC_CHECK_LIB([curl], [curl_global_init],[],
-[GIT_CONF_APPEND_LINE(NO_CURL=YesPlease)])
+AC_CHECK_LIB([curl], [curl_global_init],
+[NO_CURL=],
+[NO_CURL=YesPlease])
+AC_SUBST(NO_CURL)
#
# Define NO_EXPAT if you do not have expat installed. git-http-push is
# not built, and you cannot push using http:// and https:// transports.
-AC_CHECK_LIB([expat], [XML_ParserCreate],[],
-[GIT_CONF_APPEND_LINE(NO_EXPAT=YesPlease)])
+AC_CHECK_LIB([expat], [XML_ParserCreate],
+[NO_EXPAT=],
+[NO_EXPAT=YesPlease])
+AC_SUBST(NO_EXPAT)
#
# Define NEEDS_LIBICONV if linking with libc is not enough (Darwin).
-AC_CHECK_LIB([c], [iconv],[],
-[AC_CHECK_LIB([iconv],[iconv],
- [GIT_CONF_APPEND_LINE(NEEDS_LIBICONV=YesPlease)],[])])
+AC_CHECK_LIB([c], [iconv],
+[NEEDS_LIBICONV=],
+[NEEDS_LIBICONV=YesPlease])
+AC_SUBST(NEEDS_LIBICONV)
#
# Define NEEDS_SOCKET if linking with libc is not enough (SunOS,
# Patrick Mauritz).
-AC_CHECK_LIB([c], [socket],[],
-[AC_CHECK_LIB([socket],[socket],
- [GIT_CONF_APPEND_LINE(NEEDS_SOCKET=YesPlease)],[])])
+AC_CHECK_LIB([c], [socket],
+[NEEDS_SOCKET=],
+[NEEDS_SOCKET=YesPlease])
+AC_SUBST(NEEDS_SOCKET)
+test -n "$NEEDS_SOCKET" && LIBS="$LIBS -lsocket"
## Checks for header files.
@@ -72,21 +164,65 @@ AC_CHECK_LIB([c], [socket],[],
AC_MSG_NOTICE([CHECKS for typedefs, structures, and compiler characteristics])
#
# Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
-AC_CHECK_MEMBER(struct dirent.d_ino,[],
-[GIT_CONF_APPEND_LINE(NO_D_INO_IN_DIRENT=YesPlease)],
+AC_CHECK_MEMBER(struct dirent.d_ino,
+[NO_D_INO_IN_DIRENT=],
+[NO_D_INO_IN_DIRENT=YesPlease],
[#include <dirent.h>])
+AC_SUBST(NO_D_INO_IN_DIRENT)
#
# Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks
# d_type in struct dirent (latest Cygwin -- will be fixed soonish).
-AC_CHECK_MEMBER(struct dirent.d_type,[],
-[GIT_CONF_APPEND_LINE(NO_D_TYPE_IN_DIRENT=YesPlease)],
+AC_CHECK_MEMBER(struct dirent.d_type,
+[NO_D_TYPE_IN_DIRENT=],
+[NO_D_TYPE_IN_DIRENT=YesPlease],
[#include <dirent.h>])
+AC_SUBST(NO_D_TYPE_IN_DIRENT)
#
# Define NO_SOCKADDR_STORAGE if your platform does not have struct
# sockaddr_storage.
-AC_CHECK_TYPE(struct sockaddr_storage,[],
-[GIT_CONF_APPEND_LINE(NO_SOCKADDR_STORAGE=YesPlease)],
-[#include <netinet/in.h>])
+AC_CHECK_TYPE(struct sockaddr_storage,
+[NO_SOCKADDR_STORAGE=],
+[NO_SOCKADDR_STORAGE=YesPlease],[
+#include <sys/types.h>
+#include <sys/socket.h>
+])
+AC_SUBST(NO_SOCKADDR_STORAGE)
+#
+# Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
+AC_CHECK_TYPE([struct addrinfo],[
+ AC_CHECK_FUNC([getaddrinfo],
+ [NO_IPV6=],
+ [NO_IPV6=YesPlease])
+],[NO_IPV6=YesPlease],[
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+])
+AC_SUBST(NO_IPV6)
+#
+# Define NO_C99_FORMAT if your formatted IO functions (printf/scanf et.al.)
+# do not support the 'size specifiers' introduced by C99, namely ll, hh,
+# j, z, t. (representing long long int, char, intmax_t, size_t, ptrdiff_t).
+# some C compilers supported these specifiers prior to C99 as an extension.
+AC_CACHE_CHECK(whether formatted IO functions support C99 size specifiers,
+ ac_cv_c_c99_format,
+[# Actually git uses only %z (%zu) in alloc.c, and %t (%td) in mktag.c
+AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
+ [[char buf[64];
+ if (sprintf(buf, "%lld%hhd%jd%zd%td", (long long int)1, (char)2, (intmax_t)3, (size_t)4, (ptrdiff_t)5) != 5)
+ exit(1);
+ else if (strcmp(buf, "12345"))
+ exit(2);]])],
+ [ac_cv_c_c99_format=yes],
+ [ac_cv_c_c99_format=no])
+])
+if test $ac_cv_c_c99_format = no; then
+ NO_C99_FORMAT=YesPlease
+else
+ NO_C99_FORMAT=
+fi
+AC_SUBST(NO_C99_FORMAT)
## Checks for library functions.
@@ -94,21 +230,25 @@ AC_CHECK_TYPE(struct sockaddr_storage,[],
AC_MSG_NOTICE([CHECKS for library functions])
#
# Define NO_STRCASESTR if you don't have strcasestr.
-AC_CHECK_FUNC(strcasestr,[],
-[GIT_CONF_APPEND_LINE(NO_STRCASESTR=YesPlease)])
+AC_CHECK_FUNC(strcasestr,
+[NO_STRCASESTR=],
+[NO_STRCASESTR=YesPlease])
+AC_SUBST(NO_STRCASESTR)
#
# Define NO_STRLCPY if you don't have strlcpy.
-AC_CHECK_FUNC(strlcpy,[],
-[GIT_CONF_APPEND_LINE(NO_STRLCPY=YesPlease)])
+AC_CHECK_FUNC(strlcpy,
+[NO_STRLCPY=],
+[NO_STRLCPY=YesPlease])
+AC_SUBST(NO_STRLCPY)
#
# Define NO_SETENV if you don't have setenv in the C library.
-AC_CHECK_FUNC(setenv,[],
-[GIT_CONF_APPEND_LINE(NO_SETENV=YesPlease)])
+AC_CHECK_FUNC(setenv,
+[NO_SETENV=],
+[NO_SETENV=YesPlease])
+AC_SUBST(NO_SETENV)
#
# Define NO_MMAP if you want to avoid mmap.
#
-# Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
-#
# Define NO_ICONV if your libc does not properly support iconv.
@@ -125,9 +265,11 @@ AC_CHECK_FUNC(setenv,[],
# a missing newline at the end of the file.
-## Site configuration
+## Site configuration (override autodetection)
## --with-PACKAGE[=ARG] and --without-PACKAGE
-# Define NO_SVN_TESTS if you want to skip time-consuming SVN interopability
+AC_MSG_NOTICE([CHECKS for site configuration])
+#
+# Define NO_SVN_TESTS if you want to skip time-consuming SVN interoperability
# tests. These tests take up a significant amount of the total test time
# but are not needed unless you plan to talk to SVN repos.
#
@@ -145,21 +287,51 @@ AC_CHECK_FUNC(setenv,[],
# Define NO_OPENSSL environment variable if you do not have OpenSSL.
# This also implies MOZILLA_SHA1.
#
+# Define OPENSSLDIR=/foo/bar if your openssl header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+AC_ARG_WITH(openssl,
+AS_HELP_STRING([--with-openssl],[use OpenSSL library (default is YES)])
+AS_HELP_STRING([], [ARG can be prefix for openssl library and headers]),\
+GIT_PARSE_WITH(openssl))
+#
# Define NO_CURL if you do not have curl installed. git-http-pull and
# git-http-push are not built, and you cannot use http:// and https://
# transports.
#
# Define CURLDIR=/foo/bar if your curl header and library files are in
# /foo/bar/include and /foo/bar/lib directories.
+AC_ARG_WITH(curl,
+AS_HELP_STRING([--with-curl],[support http(s):// transports (default is YES)])
+AS_HELP_STRING([], [ARG can be also prefix for curl library and headers]),
+GIT_PARSE_WITH(curl))
#
# Define NO_EXPAT if you do not have expat installed. git-http-push is
# not built, and you cannot push using http:// and https:// transports.
#
-# Define NO_MMAP if you want to avoid mmap.
+# Define EXPATDIR=/foo/bar if your expat header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+AC_ARG_WITH(expat,
+AS_HELP_STRING([--with-expat],
+[support git-push using http:// and https:// transports via WebDAV (default is YES)])
+AS_HELP_STRING([], [ARG can be also prefix for expat library and headers]),
+GIT_PARSE_WITH(expat))
+#
+# Define NO_FINK if you are building on Darwin/Mac OS X, have Fink
+# installed in /sw, but don't want GIT to link against any libraries
+# installed there. If defined you may specify your own (or Fink's)
+# include directories and library directories by defining CFLAGS
+# and LDFLAGS appropriately.
#
-# Define NO_PYTHON if you want to loose all benefits of the recursive merge.
+# Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X,
+# have DarwinPorts installed in /opt/local, but don't want GIT to
+# link against any libraries installed there. If defined you may
+# specify your own (or DarwinPort's) include directories and
+# library directories by defining CFLAGS and LDFLAGS appropriately.
#
+# Define NO_MMAP if you want to avoid mmap.
+
## --enable-FEATURE[=ARG] and --disable-FEATURE
+#
# 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.
diff --git a/connect.c b/connect.c
index 4422a0d..e501ccc 100644
--- a/connect.c
+++ b/connect.c
@@ -10,7 +10,7 @@
#include <netdb.h>
#include <signal.h>
-static char *server_capabilities = NULL;
+static char *server_capabilities;
static int check_ref(const char *name, int len, unsigned int flags)
{
@@ -77,7 +77,7 @@ struct ref **get_remote_heads(int in, struct ref **list,
if (nr_match && !path_match(name, nr_match, match))
continue;
ref = xcalloc(1, sizeof(*ref) + len - 40);
- memcpy(ref->old_sha1, old_sha1, 20);
+ hashcpy(ref->old_sha1, old_sha1);
memcpy(ref->name, buffer + 41, len - 40);
*list = ref;
list = &ref->next;
@@ -208,7 +208,7 @@ static struct ref *try_explicit_object_name(const char *name)
len = strlen(name) + 1;
ref = xcalloc(1, sizeof(*ref) + len);
memcpy(ref->name, name, len);
- memcpy(ref->new_sha1, sha1, 20);
+ hashcpy(ref->new_sha1, sha1);
return ref;
}
@@ -318,7 +318,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
int len = strlen(src->name) + 1;
dst_peer = xcalloc(1, sizeof(*dst_peer) + len);
memcpy(dst_peer->name, src->name, len);
- memcpy(dst_peer->new_sha1, src->new_sha1, 20);
+ hashcpy(dst_peer->new_sha1, src->new_sha1);
link_dst_tail(dst_peer, dst_tail);
}
dst_peer->peer_ref = src;
@@ -493,8 +493,8 @@ static void git_tcp_connect(int fd[2], char *host)
}
-static char *git_proxy_command = NULL;
-static const char *rhost_name = NULL;
+static char *git_proxy_command;
+static const char *rhost_name;
static int rhost_len;
static int git_proxy_command_options(const char *var, const char *value)
@@ -737,14 +737,9 @@ int git_connect(int fd[2], char *url, const char *prog)
int finish_connect(pid_t pid)
{
- int ret;
-
- for (;;) {
- ret = waitpid(pid, NULL, 0);
- if (!ret)
- break;
+ while (waitpid(pid, NULL, 0) < 0) {
if (errno != EINTR)
- break;
+ return -1;
}
- return ret;
+ return 0;
}
diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el
index 3f6ed69..4a8f790 100644
--- a/contrib/emacs/vc-git.el
+++ b/contrib/emacs/vc-git.el
@@ -54,7 +54,7 @@
(let* ((dir (file-name-directory file))
(name (file-relative-name file dir)))
(when dir (cd dir))
- (and (eq 0 (call-process "git" nil '(t nil) nil "ls-files" "-c" "-z" "--" name))
+ (and (ignore-errors (eq 0 (call-process "git" nil '(t nil) nil "ls-files" "-c" "-z" "--" name)))
(let ((str (buffer-string)))
(and (> (length str) (length name))
(string= (substring str 0 (1+ (length name))) (concat name "\0"))))))))
diff --git a/contrib/gitview/gitview.txt b/contrib/gitview/gitview.txt
index 6924df2..77c29de 100644
--- a/contrib/gitview/gitview.txt
+++ b/contrib/gitview/gitview.txt
@@ -7,40 +7,50 @@ gitview - A GTK based repository browser for git
SYNOPSIS
--------
-'gitview' [options] [args]
+'gitview' [options] [args]
DESCRIPTION
---------
-Dependencies
+Dependencies:
* Python 2.4
* PyGTK 2.8 or later
* PyCairo 1.0 or later
OPTIONS
-------
- --without-diff
- If the user doesn't want to list the commit diffs in the main window. This may speed up the repository browsing.
-
- <args>
- All the valid option for git-rev-list(1)
- Key Bindings:
- F4:
- To maximize the window
- F5:
- To reread references.
- F11:
- Full screen
- F12:
- Leave full screen
+-------
+--without-diff::
+
+ If the user doesn't want to list the commit diffs in the main window.
+ This may speed up the repository browsing.
+
+<args>::
+
+ All the valid option for gitlink:git-rev-list[1].
+
+Key Bindings
+------------
+F4::
+ To maximize the window
+
+F5::
+ To reread references.
+
+F11::
+ Full screen
+
+F12::
+ Leave full screen
EXAMPLES
-------
- gitview v2.6.12.. include/scsi drivers/scsi
- Show as the changes since version v2.6.12 that changed any file in the include/scsi
- or drivers/scsi subdirectories
+--------
+
+gitview v2.6.12.. include/scsi drivers/scsi::
+
+ Show as the changes since version v2.6.12 that changed any file in the
+ include/scsi or drivers/scsi subdirectories
- gitview --since=2.weeks.ago
- Show the changes during the last two weeks
+gitview --since=2.weeks.ago::
+ Show the changes during the last two weeks
diff --git a/convert-objects.c b/convert-objects.c
index 168771e..631678b 100644
--- a/convert-objects.c
+++ b/convert-objects.c
@@ -23,7 +23,7 @@ static struct entry * convert_entry(unsigned char *sha1);
static struct entry *insert_new(unsigned char *sha1, int pos)
{
struct entry *new = xcalloc(1, sizeof(struct entry));
- memcpy(new->old_sha1, sha1, 20);
+ hashcpy(new->old_sha1, sha1);
memmove(convert + pos + 1, convert + pos, (nr_convert - pos) * sizeof(struct entry *));
convert[pos] = new;
nr_convert++;
@@ -39,7 +39,7 @@ static struct entry *lookup_entry(unsigned char *sha1)
while (low < high) {
int next = (low + high) / 2;
struct entry *n = convert[next];
- int cmp = memcmp(sha1, n->old_sha1, 20);
+ int cmp = hashcmp(sha1, n->old_sha1);
if (!cmp)
return n;
if (cmp < 0) {
@@ -54,7 +54,7 @@ static struct entry *lookup_entry(unsigned char *sha1)
static void convert_binary_sha1(void *buffer)
{
struct entry *entry = convert_entry(buffer);
- memcpy(buffer, entry->new_sha1, 20);
+ hashcpy(buffer, entry->new_sha1);
}
static void convert_ascii_sha1(void *buffer)
@@ -104,7 +104,7 @@ static int write_subdirectory(void *buffer, unsigned long size, const char *base
if (!slash) {
newlen += sprintf(new + newlen, "%o %s", mode, path);
new[newlen++] = '\0';
- memcpy(new + newlen, (char *) buffer + len - 20, 20);
+ hashcpy((unsigned char*)new + newlen, (unsigned char *) buffer + len - 20);
newlen += 20;
used += len;
diff --git a/csum-file.c b/csum-file.c
index 6a7b40f..b7174c6 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -10,7 +10,7 @@
#include "cache.h"
#include "csum-file.h"
-static int sha1flush(struct sha1file *f, unsigned int count)
+static void sha1flush(struct sha1file *f, unsigned int count)
{
void *buf = f->buffer;
@@ -21,7 +21,7 @@ static int sha1flush(struct sha1file *f, unsigned int count)
count -= ret;
if (count)
continue;
- return 0;
+ return;
}
if (!ret)
die("sha1 file '%s' write error. Out of diskspace", f->name);
@@ -38,7 +38,7 @@ int sha1close(struct sha1file *f, unsigned char *result, int update)
}
SHA1_Final(f->buffer, &f->ctx);
if (result)
- memcpy(result, f->buffer, 20);
+ hashcpy(result, f->buffer);
if (update)
sha1flush(f, 20);
if (close(f->fd))
diff --git a/daemon.c b/daemon.c
index 810837f..66ec830 100644
--- a/daemon.c
+++ b/daemon.c
@@ -7,6 +7,8 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <syslog.h>
+#include <pwd.h>
+#include <grp.h>
#include "pkt-line.h"
#include "cache.h"
#include "exec_cmd.h"
@@ -19,27 +21,28 @@ 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"
-" [--reuseaddr] [--detach] [--pid-file=file] [directory...]";
+" [--reuseaddr] [--detach] [--pid-file=file]\n"
+" [--user=user [[--group=group]] [directory...]";
/* List of acceptable pathname prefixes */
-static char **ok_paths = NULL;
-static int strict_paths = 0;
+static char **ok_paths;
+static int strict_paths;
/* If this is set, git-daemon-export-ok is not required */
-static int export_all_trees = 0;
+static int export_all_trees;
/* Take all paths relative to this one if non-NULL */
-static char *base_path = NULL;
+static char *base_path;
/* If defined, ~user notation is allowed and the string is inserted
* after ~user/. E.g. a request to git://host/~alice/frotz would
* go to /home/alice/pub_git/frotz with --user-path=pub_git.
*/
-static const char *user_path = NULL;
+static const char *user_path;
/* Timeout, and initial timeout */
-static unsigned int timeout = 0;
-static unsigned int init_timeout = 0;
+static unsigned int timeout;
+static unsigned int init_timeout;
static void logreport(int priority, const char *err, va_list params)
{
@@ -333,12 +336,12 @@ static int execute(struct sockaddr *addr)
static int max_connections = 25;
/* These are updated by the signal handler */
-static volatile unsigned int children_reaped = 0;
+static volatile unsigned int children_reaped;
static pid_t dead_child[MAX_CHILDREN];
/* These are updated by the main loop */
-static unsigned int children_spawned = 0;
-static unsigned int children_deleted = 0;
+static unsigned int children_spawned;
+static unsigned int children_deleted;
static struct child {
pid_t pid;
@@ -526,7 +529,6 @@ static int socksetup(int port, int **socklist_p)
for (ai = ai0; ai; ai = ai->ai_next) {
int sockfd;
- int *newlist;
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sockfd < 0)
@@ -560,11 +562,7 @@ static int socksetup(int port, int **socklist_p)
continue; /* not fatal */
}
- newlist = realloc(socklist, sizeof(int) * (socknum + 1));
- if (!newlist)
- die("memory allocation failed: %s", strerror(errno));
-
- socklist = newlist;
+ socklist = xrealloc(socklist, sizeof(int) * (socknum + 1));
socklist[socknum++] = sockfd;
if (maxfd < sockfd)
@@ -701,7 +699,7 @@ static void store_pid(const char *path)
fclose(f);
}
-static int serve(int port)
+static int serve(int port, struct passwd *pass, gid_t gid)
{
int socknum, *socklist;
@@ -709,6 +707,11 @@ static int serve(int port)
if (socknum == 0)
die("unable to allocate any listen sockets on port %u", port);
+ if (pass && gid &&
+ (initgroups(pass->pw_name, gid) || setgid (gid) ||
+ setuid(pass->pw_uid)))
+ die("cannot drop privileges");
+
return service_loop(socknum, socklist);
}
@@ -716,8 +719,11 @@ int main(int argc, char **argv)
{
int port = DEFAULT_GIT_PORT;
int inetd_mode = 0;
- const char *pid_file = NULL;
+ const char *pid_file = NULL, *user_name = NULL, *group_name = NULL;
int detach = 0;
+ struct passwd *pass = NULL;
+ struct group *group;
+ gid_t gid = 0;
int i;
/* Without this we cannot rely on waitpid() to tell
@@ -791,6 +797,14 @@ int main(int argc, char **argv)
log_syslog = 1;
continue;
}
+ if (!strncmp(arg, "--user=", 7)) {
+ user_name = arg + 7;
+ continue;
+ }
+ if (!strncmp(arg, "--group=", 8)) {
+ group_name = arg + 8;
+ continue;
+ }
if (!strcmp(arg, "--")) {
ok_paths = &argv[i+1];
break;
@@ -802,6 +816,28 @@ int main(int argc, char **argv)
usage(daemon_usage);
}
+ if (inetd_mode && (group_name || user_name))
+ die("--user and --group are incompatible with --inetd");
+
+ if (group_name && !user_name)
+ die("--group supplied without --user");
+
+ if (user_name) {
+ pass = getpwnam(user_name);
+ if (!pass)
+ die("user not found - %s", user_name);
+
+ if (!group_name)
+ gid = pass->pw_gid;
+ else {
+ group = getgrnam(group_name);
+ if (!group)
+ die("group not found - %s", group_name);
+
+ gid = group->gr_gid;
+ }
+ }
+
if (log_syslog) {
openlog("git-daemon", 0, LOG_DAEMON);
set_die_routine(daemon_die);
@@ -831,5 +867,5 @@ int main(int argc, char **argv)
if (pid_file)
store_pid(pid_file);
- return serve(port);
+ return serve(port, pass, gid);
}
diff --git a/date.c b/date.c
index 66be23a..e387dcd 100644
--- a/date.c
+++ b/date.c
@@ -37,6 +37,16 @@ static const char *weekday_names[] = {
"Sundays", "Mondays", "Tuesdays", "Wednesdays", "Thursdays", "Fridays", "Saturdays"
};
+static time_t gm_time_t(unsigned long time, int tz)
+{
+ int minutes;
+
+ minutes = tz < 0 ? -tz : tz;
+ minutes = (minutes / 100)*60 + (minutes % 100);
+ minutes = tz < 0 ? -minutes : minutes;
+ return time + minutes * 60;
+}
+
/*
* The "tz" thing is passed in as this strange "decimal parse of tz"
* thing, which means that tz -0100 is passed in as the integer -100,
@@ -44,21 +54,58 @@ static const char *weekday_names[] = {
*/
static struct tm *time_to_tm(unsigned long time, int tz)
{
- time_t t;
- int minutes;
-
- minutes = tz < 0 ? -tz : tz;
- minutes = (minutes / 100)*60 + (minutes % 100);
- minutes = tz < 0 ? -minutes : minutes;
- t = time + minutes * 60;
+ time_t t = gm_time_t(time, tz);
return gmtime(&t);
}
-const char *show_date(unsigned long time, int tz)
+const char *show_date(unsigned long time, int tz, int relative)
{
struct tm *tm;
static char timebuf[200];
+ if (relative) {
+ unsigned long diff;
+ time_t t = gm_time_t(time, tz);
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ if (now.tv_sec < t)
+ return "in the future";
+ diff = now.tv_sec - t;
+ if (diff < 90) {
+ snprintf(timebuf, sizeof(timebuf), "%lu seconds ago", diff);
+ return timebuf;
+ }
+ /* Turn it into minutes */
+ diff = (diff + 30) / 60;
+ if (diff < 90) {
+ snprintf(timebuf, sizeof(timebuf), "%lu minutes ago", diff);
+ return timebuf;
+ }
+ /* Turn it into hours */
+ diff = (diff + 30) / 60;
+ if (diff < 36) {
+ snprintf(timebuf, sizeof(timebuf), "%lu hours ago", diff);
+ return timebuf;
+ }
+ /* We deal with number of days from here on */
+ diff = (diff + 12) / 24;
+ if (diff < 14) {
+ snprintf(timebuf, sizeof(timebuf), "%lu days ago", diff);
+ return timebuf;
+ }
+ /* Say weeks for the past 10 weeks or so */
+ if (diff < 70) {
+ snprintf(timebuf, sizeof(timebuf), "%lu weeks ago", (diff + 3) / 7);
+ return timebuf;
+ }
+ /* Say months for the past 12 months or so */
+ if (diff < 360) {
+ snprintf(timebuf, sizeof(timebuf), "%lu months ago", (diff + 15) / 30);
+ return timebuf;
+ }
+ /* Else fall back on absolute format.. */
+ }
+
tm = time_to_tm(time, tz);
if (!tm)
return NULL;
@@ -584,10 +631,10 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)
const struct typelen *tl;
const struct special *s;
const char *end = date;
- int n = 1, i;
+ int i;
- while (isalpha(*++end))
- n++;
+ while (isalpha(*++end));
+ ;
for (i = 0; i < 12; i++) {
int match = match_string(date, month_names[i]);
diff --git a/describe.c b/describe.c
index 324ca89..5dd8b2e 100644
--- a/describe.c
+++ b/describe.c
@@ -8,12 +8,12 @@
static const char describe_usage[] =
"git-describe [--all] [--tags] [--abbrev=<n>] <committish>*";
-static int all = 0; /* Default to annotated tags only */
-static int tags = 0; /* But allow any tags if --tags is specified */
+static int all; /* Default to annotated tags only */
+static int tags; /* But allow any tags if --tags is specified */
static int abbrev = DEFAULT_ABBREV;
-static int names = 0, allocs = 0;
+static int names, allocs;
static struct commit_name {
const struct commit *commit;
int prio; /* annotated tag = 2, tag = 1, head = 0 */
@@ -42,7 +42,7 @@ static void add_to_known_names(const char *path,
struct commit_name *name = xmalloc(sizeof(struct commit_name) + len);
name->commit = commit;
- name->prio = prio;
+ name->prio = prio;
memcpy(name->path, path, len);
idx = names;
if (idx >= allocs) {
@@ -154,7 +154,7 @@ int main(int argc, char **argv)
tags = 1;
else if (!strncmp(arg, "--abbrev=", 9)) {
abbrev = strtoul(arg + 9, NULL, 10);
- if (abbrev < MINIMUM_ABBREV || 40 <= abbrev)
+ if (abbrev < MINIMUM_ABBREV || 40 < abbrev)
abbrev = DEFAULT_ABBREV;
}
else
diff --git a/diff-delta.c b/diff-delta.c
index 7da9205..fa16d06 100644
--- a/diff-delta.c
+++ b/diff-delta.c
@@ -152,7 +152,7 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
initialization in create_delta(). */
entries = (bufsize - 1) / RABIN_WINDOW;
hsize = entries / 4;
- for (i = 4; (1 << i) < hsize && i < 31; i++);
+ for (i = 4; (1u << i) < hsize && i < 31; i++);
hsize = 1 << i;
hmask = hsize - 1;
@@ -392,7 +392,7 @@ create_delta(const struct delta_index *index,
outsize = max_size + MAX_OP_SIZE + 1;
if (max_size && outpos > max_size)
break;
- out = realloc(out, outsize);
+ out = xrealloc(out, outsize);
if (!out) {
free(tmp);
return NULL;
diff --git a/diff-lib.c b/diff-lib.c
index 116b5a9..9edfa92 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -48,7 +48,7 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
memcpy(dpath->path, ce->name, path_len);
dpath->path[path_len] = '\0';
dpath->mode = 0;
- memset(dpath->sha1, 0, 20);
+ hashclr(dpath->sha1);
memset(&(dpath->parent[0]), 0,
sizeof(struct combine_diff_parent)*5);
@@ -66,8 +66,7 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
if (2 <= stage) {
int mode = ntohl(nce->ce_mode);
num_compare_stages++;
- memcpy(dpath->parent[stage-2].sha1,
- nce->sha1, 20);
+ hashcpy(dpath->parent[stage-2].sha1, nce->sha1);
dpath->parent[stage-2].mode =
canon_mode(mode);
dpath->parent[stage-2].status =
@@ -215,7 +214,7 @@ static int show_modified(struct rev_info *revs,
}
oldmode = old->ce_mode;
- if (mode == oldmode && !memcmp(sha1, old->sha1, 20) &&
+ if (mode == oldmode && !hashcmp(sha1, old->sha1) &&
!revs->diffopt.find_copies_harder)
return 0;
diff --git a/diff.c b/diff.c
index b3b1781..70699fd 100644
--- a/diff.c
+++ b/diff.c
@@ -13,9 +13,9 @@
static int use_size_cache;
-static int diff_detect_rename_default = 0;
+static int diff_detect_rename_default;
static int diff_rename_limit_default = -1;
-static int diff_use_color_default = 0;
+static int diff_use_color_default;
/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
static char diff_colors[][24] = {
@@ -358,12 +358,152 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
return 0;
}
+struct diff_words_buffer {
+ mmfile_t text;
+ long alloc;
+ long current; /* output pointer */
+ int suppressed_newline;
+};
+
+static void diff_words_append(char *line, unsigned long len,
+ struct diff_words_buffer *buffer)
+{
+ if (buffer->text.size + len > buffer->alloc) {
+ buffer->alloc = (buffer->text.size + len) * 3 / 2;
+ buffer->text.ptr = xrealloc(buffer->text.ptr, buffer->alloc);
+ }
+ line++;
+ len--;
+ memcpy(buffer->text.ptr + buffer->text.size, line, len);
+ buffer->text.size += len;
+}
+
+struct diff_words_data {
+ struct xdiff_emit_state xm;
+ struct diff_words_buffer minus, plus;
+};
+
+static void print_word(struct diff_words_buffer *buffer, int len, int color,
+ int suppress_newline)
+{
+ const char *ptr;
+ int eol = 0;
+
+ if (len == 0)
+ return;
+
+ ptr = buffer->text.ptr + buffer->current;
+ buffer->current += len;
+
+ if (ptr[len - 1] == '\n') {
+ eol = 1;
+ len--;
+ }
+
+ fputs(diff_get_color(1, color), stdout);
+ fwrite(ptr, len, 1, stdout);
+ fputs(diff_get_color(1, DIFF_RESET), stdout);
+
+ if (eol) {
+ if (suppress_newline)
+ buffer->suppressed_newline = 1;
+ else
+ putchar('\n');
+ }
+}
+
+static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
+{
+ struct diff_words_data *diff_words = priv;
+
+ if (diff_words->minus.suppressed_newline) {
+ if (line[0] != '+')
+ putchar('\n');
+ diff_words->minus.suppressed_newline = 0;
+ }
+
+ len--;
+ switch (line[0]) {
+ case '-':
+ print_word(&diff_words->minus, len, DIFF_FILE_OLD, 1);
+ break;
+ case '+':
+ print_word(&diff_words->plus, len, DIFF_FILE_NEW, 0);
+ break;
+ case ' ':
+ print_word(&diff_words->plus, len, DIFF_PLAIN, 0);
+ diff_words->minus.current += len;
+ break;
+ }
+}
+
+/* this executes the word diff on the accumulated buffers */
+static void diff_words_show(struct diff_words_data *diff_words)
+{
+ xpparam_t xpp;
+ xdemitconf_t xecfg;
+ xdemitcb_t ecb;
+ mmfile_t minus, plus;
+ int i;
+
+ minus.size = diff_words->minus.text.size;
+ minus.ptr = xmalloc(minus.size);
+ memcpy(minus.ptr, diff_words->minus.text.ptr, minus.size);
+ for (i = 0; i < minus.size; i++)
+ if (isspace(minus.ptr[i]))
+ minus.ptr[i] = '\n';
+ diff_words->minus.current = 0;
+
+ plus.size = diff_words->plus.text.size;
+ plus.ptr = xmalloc(plus.size);
+ memcpy(plus.ptr, diff_words->plus.text.ptr, plus.size);
+ for (i = 0; i < plus.size; i++)
+ if (isspace(plus.ptr[i]))
+ plus.ptr[i] = '\n';
+ diff_words->plus.current = 0;
+
+ xpp.flags = XDF_NEED_MINIMAL;
+ xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
+ xecfg.flags = 0;
+ ecb.outf = xdiff_outf;
+ ecb.priv = diff_words;
+ diff_words->xm.consume = fn_out_diff_words_aux;
+ xdl_diff(&minus, &plus, &xpp, &xecfg, &ecb);
+
+ free(minus.ptr);
+ free(plus.ptr);
+ diff_words->minus.text.size = diff_words->plus.text.size = 0;
+
+ if (diff_words->minus.suppressed_newline) {
+ putchar('\n');
+ diff_words->minus.suppressed_newline = 0;
+ }
+}
+
struct emit_callback {
struct xdiff_emit_state xm;
int nparents, color_diff;
const char **label_path;
+ struct diff_words_data *diff_words;
};
+static void free_diff_words_data(struct emit_callback *ecbdata)
+{
+ if (ecbdata->diff_words) {
+ /* flush buffers */
+ if (ecbdata->diff_words->minus.text.size ||
+ ecbdata->diff_words->plus.text.size)
+ diff_words_show(ecbdata->diff_words);
+
+ if (ecbdata->diff_words->minus.text.ptr)
+ free (ecbdata->diff_words->minus.text.ptr);
+ if (ecbdata->diff_words->plus.text.ptr)
+ free (ecbdata->diff_words->plus.text.ptr);
+ free(ecbdata->diff_words);
+ ecbdata->diff_words = NULL;
+ }
+}
+
const char *diff_get_color(int diff_use_color, enum color_diff ix)
{
if (diff_use_color)
@@ -398,12 +538,31 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
else {
int nparents = ecbdata->nparents;
int color = DIFF_PLAIN;
- for (i = 0; i < nparents && len; i++) {
- if (line[i] == '-')
- color = DIFF_FILE_OLD;
- else if (line[i] == '+')
- color = DIFF_FILE_NEW;
- }
+ if (ecbdata->diff_words && nparents != 1)
+ /* fall back to normal diff */
+ free_diff_words_data(ecbdata);
+ if (ecbdata->diff_words) {
+ if (line[0] == '-') {
+ diff_words_append(line, len,
+ &ecbdata->diff_words->minus);
+ return;
+ } else if (line[0] == '+') {
+ diff_words_append(line, len,
+ &ecbdata->diff_words->plus);
+ return;
+ }
+ if (ecbdata->diff_words->minus.text.size ||
+ ecbdata->diff_words->plus.text.size)
+ diff_words_show(ecbdata->diff_words);
+ line++;
+ len--;
+ } else
+ for (i = 0; i < nparents && len; i++) {
+ if (line[i] == '-')
+ color = DIFF_FILE_OLD;
+ else if (line[i] == '+')
+ color = DIFF_FILE_NEW;
+ }
set = diff_get_color(ecbdata->color_diff, color);
}
if (len > 0 && line[len-1] == '\n')
@@ -679,7 +838,7 @@ static unsigned char *deflate_it(char *data,
return deflated;
}
-static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
+static void emit_binary_diff_body(mmfile_t *one, mmfile_t *two)
{
void *cp;
void *delta;
@@ -690,7 +849,6 @@ static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
unsigned long deflate_size;
unsigned long data_size;
- printf("GIT binary patch\n");
/* We could do deflated delta, or we could do just deflated two,
* whichever is smaller.
*/
@@ -739,15 +897,20 @@ static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
free(data);
}
+static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
+{
+ printf("GIT binary patch\n");
+ emit_binary_diff_body(one, two);
+ emit_binary_diff_body(two, one);
+}
+
#define FIRST_FEW_BYTES 8000
static int mmfile_is_binary(mmfile_t *mf)
{
long sz = mf->size;
if (FIRST_FEW_BYTES < sz)
sz = FIRST_FEW_BYTES;
- if (memchr(mf->ptr, 0, sz))
- return 1;
- return 0;
+ return !!memchr(mf->ptr, 0, sz);
}
static void builtin_diff(const char *name_a,
@@ -836,7 +999,12 @@ static void builtin_diff(const char *name_a,
ecb.outf = xdiff_outf;
ecb.priv = &ecbdata;
ecbdata.xm.consume = fn_out_consume;
+ if (o->color_diff_words)
+ ecbdata.diff_words =
+ xcalloc(1, sizeof(struct diff_words_data));
xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+ if (o->color_diff_words)
+ free_diff_words_data(&ecbdata);
}
free_ab_and_return:
@@ -939,8 +1107,8 @@ void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
{
if (mode) {
spec->mode = canon_mode(mode);
- memcpy(spec->sha1, sha1, 20);
- spec->sha1_valid = !!memcmp(sha1, null_sha1, 20);
+ hashcpy(spec->sha1, sha1);
+ spec->sha1_valid = !is_null_sha1(sha1);
}
}
@@ -978,7 +1146,7 @@ static int work_tree_matches(const char *name, const unsigned char *sha1)
if ((lstat(name, &st) < 0) ||
!S_ISREG(st.st_mode) || /* careful! */
ce_match_stat(ce, &st, 0) ||
- memcmp(sha1, ce->sha1, 20))
+ hashcmp(sha1, ce->sha1))
return 0;
/* we return 1 only when we can stat, it is a regular file,
* stat information matches, and sha1 recorded in the cache
@@ -1006,7 +1174,7 @@ static struct sha1_size_cache *locate_size_cache(unsigned char *sha1,
while (last > first) {
int cmp, next = (last + first) >> 1;
e = sha1_size_cache[next];
- cmp = memcmp(e->sha1, sha1, 20);
+ cmp = hashcmp(e->sha1, sha1);
if (!cmp)
return e;
if (cmp < 0) {
@@ -1032,7 +1200,7 @@ static struct sha1_size_cache *locate_size_cache(unsigned char *sha1,
sizeof(*sha1_size_cache));
e = xmalloc(sizeof(struct sha1_size_cache));
sha1_size_cache[first] = e;
- memcpy(e->sha1, sha1, 20);
+ hashcpy(e->sha1, sha1);
e->size = size;
return e;
}
@@ -1354,7 +1522,7 @@ static void diff_fill_sha1_info(struct diff_filespec *one)
}
}
else
- memset(one->sha1, 0, 20);
+ hashclr(one->sha1);
}
static void run_diff(struct diff_filepair *p, struct diff_options *o)
@@ -1417,7 +1585,7 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
;
}
- if (memcmp(one->sha1, two->sha1, 20)) {
+ if (hashcmp(one->sha1, two->sha1)) {
int abbrev = o->full_index ? 40 : DEFAULT_ABBREV;
len += snprintf(msg + len, sizeof(msg) - len,
@@ -1515,6 +1683,19 @@ void diff_setup(struct diff_options *options)
int diff_setup_done(struct diff_options *options)
{
+ int count = 0;
+
+ if (options->output_format & DIFF_FORMAT_NAME)
+ count++;
+ if (options->output_format & DIFF_FORMAT_NAME_STATUS)
+ count++;
+ if (options->output_format & DIFF_FORMAT_CHECKDIFF)
+ count++;
+ if (options->output_format & DIFF_FORMAT_NO_OUTPUT)
+ count++;
+ if (count > 1)
+ die("--name-only, --name-status, --check and -s are mutually exclusive");
+
if (options->find_copies_harder)
options->detect_rename = DIFF_DETECT_COPY;
@@ -1697,6 +1878,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
options->xdl_opts |= XDF_IGNORE_WHITESPACE;
else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change"))
options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
+ else if (!strcmp(arg, "--color-words"))
+ options->color_diff = options->color_diff_words = 1;
else if (!strcmp(arg, "--no-renames"))
options->detect_rename = 0;
else
@@ -1921,7 +2104,7 @@ int diff_unmodified_pair(struct diff_filepair *p)
* dealing with a change.
*/
if (one->sha1_valid && two->sha1_valid &&
- !memcmp(one->sha1, two->sha1, sizeof(one->sha1)))
+ !hashcmp(one->sha1, two->sha1))
return 1; /* no change */
if (!one->sha1_valid && !two->sha1_valid)
return 1; /* both look at the same file on the filesystem. */
@@ -2060,7 +2243,7 @@ static void diff_resolve_rename_copy(void)
if (!p->status)
p->status = DIFF_STATUS_RENAMED;
}
- else if (memcmp(p->one->sha1, p->two->sha1, 20) ||
+ else if (hashcmp(p->one->sha1, p->two->sha1) ||
p->one->mode != p->two->mode)
p->status = DIFF_STATUS_MODIFIED;
else {
diff --git a/diff.h b/diff.h
index 2cced53..b007240 100644
--- a/diff.h
+++ b/diff.h
@@ -46,7 +46,8 @@ struct diff_options {
full_index:1,
silent_on_remove:1,
find_copies_harder:1,
- color_diff:1;
+ color_diff:1,
+ color_diff_words:1;
int context;
int break_opt;
int detect_rename;
diff --git a/diffcore-break.c b/diffcore-break.c
index ed0e14c..acb18db 100644
--- a/diffcore-break.c
+++ b/diffcore-break.c
@@ -56,7 +56,7 @@ static int should_break(struct diff_filespec *src,
return 0; /* leave symlink rename alone */
if (src->sha1_valid && dst->sha1_valid &&
- !memcmp(src->sha1, dst->sha1, 20))
+ !hashcmp(src->sha1, dst->sha1))
return 0; /* they are the same */
if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 0ec488a..ef23901 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -101,7 +101,7 @@ static int is_exact_match(struct diff_filespec *src,
int contents_too)
{
if (src->sha1_valid && dst->sha1_valid &&
- !memcmp(src->sha1, dst->sha1, 20))
+ !hashcmp(src->sha1, dst->sha1))
return 1;
if (!contents_too)
return 0;
diff --git a/dir.c b/dir.c
index 092d077..d53d48f 100644
--- a/dir.c
+++ b/dir.c
@@ -101,8 +101,8 @@ void add_exclude(const char *string, const char *base,
x->baselen = baselen;
if (which->nr == which->alloc) {
which->alloc = alloc_nr(which->alloc);
- which->excludes = realloc(which->excludes,
- which->alloc * sizeof(x));
+ which->excludes = xrealloc(which->excludes,
+ which->alloc * sizeof(x));
}
which->excludes[which->nr++] = x;
}
@@ -293,7 +293,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
if (fdir) {
int exclude_stk;
struct dirent *de;
- char fullname[MAXPATHLEN + 1];
+ char fullname[PATH_MAX + 1];
memcpy(fullname, base, baselen);
exclude_stk = push_exclude_per_directory(dir, base, baselen);
diff --git a/dump-cache-tree.c b/dump-cache-tree.c
index 1ccaf51..1f73f1e 100644
--- a/dump-cache-tree.c
+++ b/dump-cache-tree.c
@@ -33,7 +33,7 @@ static int dump_cache_tree(struct cache_tree *it,
}
else {
dump_one(it, pfx, "");
- if (memcmp(it->sha1, ref->sha1, 20) ||
+ if (hashcmp(it->sha1, ref->sha1) ||
ref->entry_count != it->entry_count ||
ref->subtree_nr != it->subtree_nr) {
dump_one(ref, pfx, "#(ref) ");
diff --git a/entry.c b/entry.c
index 793724f..b2ea0ef 100644
--- a/entry.c
+++ b/entry.c
@@ -135,7 +135,7 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat
int checkout_entry(struct cache_entry *ce, struct checkout *state, char *topath)
{
- static char path[MAXPATHLEN+1];
+ static char path[PATH_MAX + 1];
struct stat st;
int len = state->base_dir_len;
@@ -172,5 +172,3 @@ int checkout_entry(struct cache_entry *ce, struct checkout *state, char *topath)
create_directories(path, state);
return write_entry(ce, path, state, 0);
}
-
-
diff --git a/environment.c b/environment.c
index 87162b2..5fae9ac 100644
--- a/environment.c
+++ b/environment.c
@@ -13,20 +13,21 @@ char git_default_email[MAX_GITNAME];
char git_default_name[MAX_GITNAME];
int use_legacy_headers = 1;
int trust_executable_bit = 1;
-int assume_unchanged = 0;
-int prefer_symlink_refs = 0;
-int log_all_ref_updates = 0;
+int assume_unchanged;
+int prefer_symlink_refs;
+int log_all_ref_updates;
int warn_ambiguous_refs = 1;
-int repository_format_version = 0;
+int repository_format_version;
char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
int shared_repository = PERM_UMASK;
-const char *apply_default_whitespace = NULL;
+const char *apply_default_whitespace;
int zlib_compression_level = Z_DEFAULT_COMPRESSION;
int pager_in_use;
int pager_use_color = 1;
-static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir,
- *git_graft_file;
+static const char *git_dir;
+static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
+
static void setup_git_env(void)
{
git_dir = getenv(GIT_DIR_ENVIRONMENT);
@@ -49,7 +50,7 @@ static void setup_git_env(void)
git_graft_file = strdup(git_path("info/grafts"));
}
-char *get_git_dir(void)
+const char *get_git_dir(void)
{
if (!git_dir)
setup_git_env();
diff --git a/exec_cmd.c b/exec_cmd.c
index 62f51fc..e30936d 100644
--- a/exec_cmd.c
+++ b/exec_cmd.c
@@ -5,7 +5,7 @@
extern char **environ;
static const char *builtin_exec_path = GIT_EXEC_PATH;
-static const char *current_exec_path = NULL;
+static const char *current_exec_path;
void git_set_exec_path(const char *exec_path)
{
diff --git a/fetch-clone.c b/fetch-clone.c
index 5e84c46..c5cf477 100644
--- a/fetch-clone.c
+++ b/fetch-clone.c
@@ -44,9 +44,8 @@ static int finish_pack(const char *pack_tmp_name, const char *me)
for (;;) {
int status, code;
- int retval = waitpid(pid, &status, 0);
- if (retval < 0) {
+ if (waitpid(pid, &status, 0) < 0) {
if (errno == EINTR)
continue;
error("waitpid failed (%s)", strerror(errno));
diff --git a/fetch-pack.c b/fetch-pack.c
index b7824db..377fede 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -24,8 +24,8 @@ static const char *exec = "git-upload-pack";
*/
#define MAX_IN_VAIN 256
-static struct commit_list *rev_list = NULL;
-static int non_common_revs = 0, multi_ack = 0, use_thin_pack = 0, use_sideband;
+static struct commit_list *rev_list;
+static int non_common_revs, multi_ack, use_thin_pack, use_sideband;
static void rev_list_push(struct commit *commit, int mark)
{
@@ -250,7 +250,7 @@ done:
return retval;
}
-static struct commit_list *complete = NULL;
+static struct commit_list *complete;
static int mark_complete(const char *path, const unsigned char *sha1)
{
@@ -404,7 +404,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
continue;
}
- memcpy(ref->new_sha1, local, 20);
+ hashcpy(ref->new_sha1, local);
if (!verbose)
continue;
fprintf(stderr,
diff --git a/fetch.c b/fetch.c
index aeb6bf2..ef60b04 100644
--- a/fetch.c
+++ b/fetch.c
@@ -84,7 +84,7 @@ static int process_commit(struct commit *commit)
if (commit->object.flags & COMPLETE)
return 0;
- memcpy(current_commit_sha1, commit->object.sha1, 20);
+ hashcpy(current_commit_sha1, commit->object.sha1);
pull_say("walk %s\n", sha1_to_hex(commit->object.sha1));
diff --git a/fsck-objects.c b/fsck-objects.c
index e167f41..ae0ec8d 100644
--- a/fsck-objects.c
+++ b/fsck-objects.c
@@ -14,12 +14,12 @@
#define REACHABLE 0x0001
#define SEEN 0x0002
-static int show_root = 0;
-static int show_tags = 0;
-static int show_unreachable = 0;
-static int check_full = 0;
-static int check_strict = 0;
-static int keep_cache_objects = 0;
+static int show_root;
+static int show_tags;
+static int show_unreachable;
+static int check_full;
+static int check_strict;
+static int keep_cache_objects;
static unsigned char head_sha1[20];
#ifdef NO_D_INO_IN_DIRENT
@@ -356,7 +356,7 @@ static void add_sha1_list(unsigned char *sha1, unsigned long ino)
int nr;
entry->ino = ino;
- memcpy(entry->sha1, sha1, 20);
+ hashcpy(entry->sha1, sha1);
nr = sha1_list.nr;
if (nr == MAX_SHA1_ENTRIES) {
fsck_sha1_list();
@@ -366,13 +366,13 @@ static void add_sha1_list(unsigned char *sha1, unsigned long ino)
sha1_list.nr = ++nr;
}
-static int fsck_dir(int i, char *path)
+static void fsck_dir(int i, char *path)
{
DIR *dir = opendir(path);
struct dirent *de;
if (!dir)
- return 0;
+ return;
while ((de = readdir(dir)) != NULL) {
char name[100];
@@ -398,10 +398,9 @@ static int fsck_dir(int i, char *path)
fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
}
closedir(dir);
- return 0;
}
-static int default_refs = 0;
+static int default_refs;
static int fsck_handle_ref(const char *refname, const unsigned char *sha1)
{
@@ -453,7 +452,7 @@ static int fsck_head_link(void)
if (strncmp(git_refs_heads_master + pfxlen, "refs/heads/", 11))
return error("HEAD points to something strange (%s)",
git_refs_heads_master + pfxlen);
- if (!memcmp(null_sha1, sha1, 20))
+ if (is_null_sha1(sha1))
return error("HEAD: not a valid git pointer");
return 0;
}
diff --git a/git-cherry.sh b/git-cherry.sh
index f0e8831..8832573 100755
--- a/git-cherry.sh
+++ b/git-cherry.sh
@@ -51,9 +51,6 @@ patch=$tmp-patch
mkdir $patch
trap "rm -rf $tmp-*" 0 1 2 3 15
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
-
for c in $inup
do
git-diff-tree -p $c
diff --git a/git-compat-util.h b/git-compat-util.h
index b2e18954..91f2b0d 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -172,7 +172,4 @@ static inline int sane_case(int x, int high)
return x;
}
-#ifndef MAXPATHLEN
-#define MAXPATHLEN 256
-#endif
#endif
diff --git a/git-reset.sh b/git-reset.sh
index 36fc8ce..3133b5b 100755
--- a/git-reset.sh
+++ b/git-reset.sh
@@ -3,9 +3,6 @@
USAGE='[--mixed | --soft | --hard] [<commit-ish>]'
. git-sh-setup
-tmp=${GIT_DIR}/reset.$$
-trap 'rm -f $tmp-*' 0 1 2 3 15
-
update=
reset_type=--mixed
case "$1" in
diff --git a/git-send-email.perl b/git-send-email.perl
index a83c7e9..746c525 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -510,7 +510,7 @@ foreach my $t (@files) {
if ($2 eq $from) {
next if ($suppress_from);
}
- else {
+ elsif ($1 eq 'From') {
$author_not_sender = $2;
}
printf("(mbox) Adding cc: %s from line '%s'\n",
diff --git a/git-svn.perl b/git-svn.perl
index 0d58bb9..0290850 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -51,7 +51,8 @@ my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
$_message, $_file, $_follow_parent, $_no_metadata,
$_template, $_shared, $_no_default_regex, $_no_graft_copy,
$_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,
- $_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m);
+ $_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m,
+ $_merge, $_strategy, $_dry_run);
my (@_branch_from, %tree_map, %users, %rusers, %equiv);
my ($_svn_co_url_revs, $_svn_pg_peg_revs);
my @repo_path_split_cache;
@@ -118,6 +119,11 @@ my %cmd = (
{ 'message|m=s' => \$_message,
'file|F=s' => \$_file,
%cmt_opts } ],
+ dcommit => [ \&dcommit, 'Commit several diffs to merge with upstream',
+ { 'merge|m|M' => \$_merge,
+ 'strategy|s=s' => \$_strategy,
+ 'dry-run|n' => \$_dry_run,
+ %cmt_opts } ],
);
my $cmd;
@@ -500,6 +506,8 @@ sub commit_lib {
my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : ();
my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$";
+ my $repo;
+ ($repo, $SVN_PATH) = repo_path_split($SVN_URL);
set_svn_commit_env();
foreach my $c (@revs) {
my $log_msg = get_commit_message($c, $commit_msg);
@@ -508,6 +516,8 @@ sub commit_lib {
# can't track down... (it's probably in the SVN code)
defined(my $pid = open my $fh, '-|') or croak $!;
if (!$pid) {
+ $SVN_LOG = libsvn_connect($repo);
+ $SVN = libsvn_connect($repo);
my $ed = SVN::Git::Editor->new(
{ r => $r_last,
ra => $SVN,
@@ -557,6 +567,33 @@ sub commit_lib {
unlink $commit_msg;
}
+sub dcommit {
+ my $gs = "refs/remotes/$GIT_SVN";
+ chomp(my @refs = safe_qx(qw/git-rev-list --no-merges/, "$gs..HEAD"));
+ foreach my $d (reverse @refs) {
+ if ($_dry_run) {
+ print "diff-tree $d~1 $d\n";
+ } else {
+ commit_diff("$d~1", $d);
+ }
+ }
+ return if $_dry_run;
+ fetch();
+ my @diff = safe_qx(qw/git-diff-tree HEAD/, $gs);
+ my @finish;
+ if (@diff) {
+ @finish = qw/rebase/;
+ push @finish, qw/--merge/ if $_merge;
+ push @finish, "--strategy=$_strategy" if $_strategy;
+ print STDERR "W: HEAD and $gs differ, using @finish:\n", @diff;
+ } else {
+ print "No changes between current HEAD and $gs\n",
+ "Hard resetting to the latest $gs\n";
+ @finish = qw/reset --hard/;
+ }
+ sys('git', @finish, $gs);
+}
+
sub show_ignore {
$SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
$_use_lib ? show_ignore_lib() : show_ignore_cmd();
@@ -782,6 +819,7 @@ sub commit_diff {
} else {
$ed->close_edit;
}
+ $_message = $_file = undef;
}
########################### utility functions #########################
diff --git a/git.c b/git.c
index 18ba14a..bd07289 100644
--- a/git.c
+++ b/git.c
@@ -92,7 +92,7 @@ static int handle_options(const char*** argv, int* argc)
}
static const char *alias_command;
-static char *alias_string = NULL;
+static char *alias_string;
static int git_alias_config(const char *var, const char *value)
{
@@ -120,7 +120,7 @@ static int split_cmdline(char *cmdline, const char ***argv)
; /* skip */
if (count >= size) {
size += 16;
- *argv = realloc(*argv, sizeof(char*) * size);
+ *argv = xrealloc(*argv, sizeof(char*) * size);
}
(*argv)[count++] = cmdline + dst;
} else if(!quoted && (c == '\'' || c == '"')) {
@@ -191,8 +191,8 @@ static int handle_alias(int *argcp, const char ***argv)
fflush(stderr);
}
- new_argv = realloc(new_argv, sizeof(char*) *
- (count + *argcp + 1));
+ new_argv = xrealloc(new_argv, sizeof(char*) *
+ (count + *argcp + 1));
/* insert after command name */
memcpy(new_argv + count, *argv + 1, sizeof(char*) * *argcp);
new_argv[count+*argcp] = NULL;
@@ -213,8 +213,8 @@ static int handle_alias(int *argcp, const char ***argv)
const char git_version_string[] = GIT_VERSION;
-#define NEEDS_PREFIX 1
-#define USE_PAGER 2
+#define RUN_SETUP (1<<0)
+#define USE_PAGER (1<<1)
static void handle_internal_command(int argc, const char **argv, char **envp)
{
@@ -224,47 +224,54 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
int (*fn)(int, const char **, const char *);
int option;
} commands[] = {
- { "version", cmd_version },
- { "help", cmd_help },
- { "log", cmd_log, NEEDS_PREFIX | USE_PAGER },
- { "whatchanged", cmd_whatchanged, NEEDS_PREFIX | USE_PAGER },
- { "show", cmd_show, NEEDS_PREFIX | USE_PAGER },
- { "push", cmd_push, NEEDS_PREFIX },
- { "format-patch", cmd_format_patch, NEEDS_PREFIX },
+ { "add", cmd_add, RUN_SETUP },
+ { "apply", cmd_apply },
+ { "cat-file", cmd_cat_file, RUN_SETUP },
+ { "checkout-index", cmd_checkout_index, RUN_SETUP },
+ { "check-ref-format", cmd_check_ref_format },
+ { "commit-tree", cmd_commit_tree, RUN_SETUP },
{ "count-objects", cmd_count_objects },
- { "diff", cmd_diff, NEEDS_PREFIX },
- { "grep", cmd_grep, NEEDS_PREFIX },
- { "rm", cmd_rm, NEEDS_PREFIX },
- { "add", cmd_add, NEEDS_PREFIX },
- { "rev-list", cmd_rev_list, NEEDS_PREFIX },
- { "init-db", cmd_init_db },
+ { "diff", cmd_diff, RUN_SETUP },
+ { "diff-files", cmd_diff_files, RUN_SETUP },
+ { "diff-index", cmd_diff_index, RUN_SETUP },
+ { "diff-stages", cmd_diff_stages, RUN_SETUP },
+ { "diff-tree", cmd_diff_tree, RUN_SETUP },
+ { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
+ { "format-patch", cmd_format_patch, RUN_SETUP },
{ "get-tar-commit-id", cmd_get_tar_commit_id },
- { "upload-tar", cmd_upload_tar },
- { "check-ref-format", cmd_check_ref_format },
- { "ls-files", cmd_ls_files, NEEDS_PREFIX },
- { "ls-tree", cmd_ls_tree, NEEDS_PREFIX },
- { "tar-tree", cmd_tar_tree, NEEDS_PREFIX },
- { "read-tree", cmd_read_tree, NEEDS_PREFIX },
- { "commit-tree", cmd_commit_tree, NEEDS_PREFIX },
- { "apply", cmd_apply },
- { "show-branch", cmd_show_branch, NEEDS_PREFIX },
- { "diff-files", cmd_diff_files, NEEDS_PREFIX },
- { "diff-index", cmd_diff_index, NEEDS_PREFIX },
- { "diff-stages", cmd_diff_stages, NEEDS_PREFIX },
- { "diff-tree", cmd_diff_tree, NEEDS_PREFIX },
- { "cat-file", cmd_cat_file, NEEDS_PREFIX },
- { "rev-parse", cmd_rev_parse, NEEDS_PREFIX },
- { "write-tree", cmd_write_tree, NEEDS_PREFIX },
- { "mailsplit", cmd_mailsplit },
+ { "grep", cmd_grep, RUN_SETUP },
+ { "help", cmd_help },
+ { "init-db", cmd_init_db },
+ { "log", cmd_log, RUN_SETUP | USE_PAGER },
+ { "ls-files", cmd_ls_files, RUN_SETUP },
+ { "ls-tree", cmd_ls_tree, RUN_SETUP },
{ "mailinfo", cmd_mailinfo },
- { "stripspace", cmd_stripspace },
- { "update-index", cmd_update_index, NEEDS_PREFIX },
- { "update-ref", cmd_update_ref, NEEDS_PREFIX },
- { "fmt-merge-msg", cmd_fmt_merge_msg, NEEDS_PREFIX },
- { "prune", cmd_prune, NEEDS_PREFIX },
- { "mv", cmd_mv, NEEDS_PREFIX },
- { "prune-packed", cmd_prune_packed, NEEDS_PREFIX },
+ { "mailsplit", cmd_mailsplit },
+ { "mv", cmd_mv, RUN_SETUP },
+ { "name-rev", cmd_name_rev, RUN_SETUP },
+ { "pack-objects", cmd_pack_objects, RUN_SETUP },
+ { "prune", cmd_prune, RUN_SETUP },
+ { "prune-packed", cmd_prune_packed, RUN_SETUP },
+ { "push", cmd_push, RUN_SETUP },
+ { "read-tree", cmd_read_tree, RUN_SETUP },
{ "repo-config", cmd_repo_config },
+ { "rev-list", cmd_rev_list, RUN_SETUP },
+ { "rev-parse", cmd_rev_parse, RUN_SETUP },
+ { "rm", cmd_rm, RUN_SETUP },
+ { "show-branch", cmd_show_branch, RUN_SETUP },
+ { "show", cmd_show, RUN_SETUP | USE_PAGER },
+ { "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-tar", cmd_upload_tar },
+ { "version", cmd_version },
+ { "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
+ { "write-tree", cmd_write_tree, RUN_SETUP },
+ { "verify-pack", cmd_verify_pack },
};
int i;
@@ -281,16 +288,16 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
continue;
prefix = NULL;
- if (p->option & NEEDS_PREFIX)
+ if (p->option & RUN_SETUP)
prefix = setup_git_directory();
if (p->option & USE_PAGER)
setup_pager();
if (getenv("GIT_TRACE")) {
- int i;
+ int j;
fprintf(stderr, "trace: built-in: git");
- for (i = 0; i < argc; ++i) {
+ for (j = 0; j < argc; ++j) {
fputc(' ', stderr);
- sq_quote_print(stderr, argv[i]);
+ sq_quote_print(stderr, argv[j]);
}
putc('\n', stderr);
fflush(stderr);
diff --git a/gitweb/README b/gitweb/README
index 8d67276..27c6dac 100644
--- a/gitweb/README
+++ b/gitweb/README
@@ -5,5 +5,33 @@ The one working on:
From the git version 1.4.0 gitweb is bundled with git.
-Any comment/question/concern to:
+
+How to configure gitweb for your local system:
+
+You can specify the following configuration variables when building GIT:
+ * GITWEB_SITENAME
+ Shown in the title of all generated pages, defaults to the servers name.
+ * GITWEB_PROJECTROOT
+ The root directory for all projects shown by gitweb.
+ * GITWEB_LIST
+ points to a directory to scan for projects (defaults to project root)
+ or to a file for explicit listing of projects.
+ * GITWEB_HOMETEXT
+ points to an .html file which is included on the gitweb project
+ overview page.
+ * GITWEB_CSS
+ Points to the location where you put gitweb.css on your web server.
+ * GITWEB_LOGO
+ Points to the location where you put git-logo.png on your web server.
+ * GITWEB_CONFIG
+ This file will be loaded using 'require'. If the environment
+ $GITWEB_CONFIG is set when gitweb.cgi is executed the file in the
+ environment variable will be loaded instead of the file
+ specified when gitweb.cgi was created.
+
+Originally written by:
Kay Sievers <kay.sievers@vrfy.org>
+
+Any comment/question/concern to:
+ Git mailing list <git@vger.kernel.org>
+
diff --git a/gitweb/git-logo.png b/gitweb/git-logo.png
new file mode 100644
index 0000000..16ae8d5
--- /dev/null
+++ b/gitweb/git-logo.png
Binary files differ
diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
index fffdb13..9013895 100644
--- a/gitweb/gitweb.css
+++ b/gitweb/gitweb.css
@@ -117,9 +117,14 @@ div.list_head {
a.list {
text-decoration: none;
+ font-weight: bold;
color: #000000;
}
+table.tags a.list {
+ font-weight: normal;
+}
+
a.list:hover {
text-decoration: underline;
color: #880000;
@@ -171,6 +176,10 @@ tr.dark {
background-color: #f6f6f0;
}
+tr.dark2 {
+ background-color: #f6f6f0;
+}
+
tr.dark:hover {
background-color: #edece6;
}
@@ -181,12 +190,16 @@ td {
vertical-align: top;
}
-td.link {
+td.link, td.selflink {
padding: 2px 5px;
font-family: sans-serif;
font-size: 10px;
}
+td.selflink {
+ padding-right: 0px;
+}
+
td.sha1 {
font-family: monospace;
}
@@ -196,6 +209,10 @@ td.error {
background-color: yellow;
}
+td.current_head {
+ text-decoration: underline;
+}
+
table.diff_tree span.file_status.new {
color: #008000;
}
@@ -209,6 +226,10 @@ table.diff_tree span.file_status.mode_chnge {
color: #777777;
}
+table.diff_tree span.file_status.copied {
+ color: #70a070;
+}
+
/* age2: 60*60*24*2 <= age */
table.project_list td.age2, table.blame td.age2 {
font-style: italic;
@@ -309,15 +330,30 @@ a.rss_logo:hover {
background-color: #ee5500;
}
-span.tag {
+span.refs span {
padding: 0px 4px;
font-size: 10px;
font-weight: normal;
- background-color: #ffffaa;
border: 1px solid;
+ background-color: #ffaaff;
+ border-color: #ffccff #ff00ee #ff00ee #ffccff;
+}
+
+span.refs span.ref {
+ background-color: #aaaaff;
+ border-color: #ccccff #0033cc #0033cc #ccccff;
+}
+
+span.refs span.tag {
+ background-color: #ffffaa;
border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
}
+span.refs span.head {
+ background-color: #aaffaa;
+ border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
+}
+
span.atnight {
color: #cc0000;
}
diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.perl
index e5fca63..966c54a 100755
--- a/gitweb/gitweb.cgi
+++ b/gitweb/gitweb.perl
@@ -14,47 +14,49 @@ use CGI::Util qw(unescape);
use CGI::Carp qw(fatalsToBrowser);
use Encode;
use Fcntl ':mode';
+use File::Find qw();
binmode STDOUT, ':utf8';
our $cgi = new CGI;
-our $version = "267";
+our $version = "++GIT_VERSION++";
our $my_url = $cgi->url();
our $my_uri = $cgi->url(-absolute => 1);
-our $rss_link = "";
# core git executable to use
# this can just be "git" if your webserver has a sensible PATH
-our $GIT = "/usr/bin/git";
+our $GIT = "++GIT_BINDIR++/git";
# absolute fs-path which will be prepended to the project path
#our $projectroot = "/pub/scm";
-our $projectroot = "/home/kay/public_html/pub/scm";
-
-# version of the core git binary
-our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown";
+our $projectroot = "++GITWEB_PROJECTROOT++";
# location for temporary files needed for diffs
our $git_temp = "/tmp/gitweb";
-if (! -d $git_temp) {
- mkdir($git_temp, 0700) || die_error("Couldn't mkdir $git_temp");
-}
# target of the home link on top of all pages
our $home_link = $my_uri;
+# string of the home link on top of all pages
+our $home_link_str = "++GITWEB_HOME_LINK_STR++";
+
# name of your site or organization to appear in page titles
# replace this with something more descriptive for clearer bookmarks
-our $site_name = $ENV{'SERVER_NAME'} || "Untitled";
+our $site_name = "++GITWEB_SITENAME++" || $ENV{'SERVER_NAME'} || "Untitled";
# html text to include at home page
-our $home_text = "indextext.html";
+our $home_text = "++GITWEB_HOMETEXT++";
# URI of default stylesheet
-our $stylesheet = "gitweb.css";
+our $stylesheet = "++GITWEB_CSS++";
+# URI of GIT logo
+our $logo = "++GITWEB_LOGO++";
# source of projects list
-#our $projects_list = $projectroot;
-our $projects_list = "index/index.aux";
+our $projects_list = "++GITWEB_LIST++";
+
+# list of git base URLs used for URL to where fetch project from,
+# i.e. full URL is "$git_base_url/$project"
+our @git_base_url_list = ("++GITWEB_BASE_URL++");
# default blob_plain mimetype and default charset for text/plain blob
our $default_blob_plain_mimetype = 'text/plain';
@@ -64,47 +66,46 @@ our $default_text_plain_charset = undef;
# (relative to the current git repository)
our $mimetypes_file = undef;
+our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
+require $GITWEB_CONFIG if -e $GITWEB_CONFIG;
+
+# version of the core git binary
+our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown";
+
+$projects_list ||= $projectroot;
+if (! -d $git_temp) {
+ mkdir($git_temp, 0700) || die_error(undef, "Couldn't mkdir $git_temp");
+}
+
+# ======================================================================
# input validation and dispatch
our $action = $cgi->param('a');
if (defined $action) {
if ($action =~ m/[^0-9a-zA-Z\.\-_]/) {
- undef $action;
- die_error(undef, "Invalid action parameter.");
+ die_error(undef, "Invalid action parameter");
}
- if ($action eq "git-logo.png") {
- git_logo();
- exit;
- } elsif ($action eq "opml") {
+ # action which does not check rest of parameters
+ if ($action eq "opml") {
git_opml();
exit;
}
}
-our $order = $cgi->param('o');
-if (defined $order) {
- if ($order =~ m/[^0-9a-zA-Z_]/) {
- undef $order;
- die_error(undef, "Invalid order parameter.");
- }
-}
-
our $project = ($cgi->param('p') || $ENV{'PATH_INFO'});
if (defined $project) {
- $project =~ s|^/||; $project =~ s|/$||;
- $project = validate_input($project);
- if (!defined($project)) {
- die_error(undef, "Invalid project parameter.");
+ $project =~ s|^/||;
+ $project =~ s|/$||;
+}
+if (defined $project && $project) {
+ if (!validate_input($project)) {
+ die_error(undef, "Invalid project parameter");
}
if (!(-d "$projectroot/$project")) {
- undef $project;
- die_error(undef, "No such directory.");
+ die_error(undef, "No such directory");
}
if (!(-e "$projectroot/$project/HEAD")) {
- undef $project;
- die_error(undef, "No such project.");
+ die_error(undef, "No such project");
}
- $rss_link = "<link rel=\"alternate\" title=\"" . esc_param($project) . " log\" href=\"" .
- "$my_uri?" . esc_param("p=$project;a=rss") . "\" type=\"application/rss+xml\"/>";
$ENV{'GIT_DIR'} = "$projectroot/$project";
} else {
git_project_list();
@@ -113,53 +114,106 @@ if (defined $project) {
our $file_name = $cgi->param('f');
if (defined $file_name) {
- $file_name = validate_input($file_name);
- if (!defined($file_name)) {
- die_error(undef, "Invalid file parameter.");
+ if (!validate_input($file_name)) {
+ die_error(undef, "Invalid file parameter");
}
}
our $hash = $cgi->param('h');
if (defined $hash) {
- $hash = validate_input($hash);
- if (!defined($hash)) {
- die_error(undef, "Invalid hash parameter.");
+ if (!validate_input($hash)) {
+ die_error(undef, "Invalid hash parameter");
}
}
our $hash_parent = $cgi->param('hp');
if (defined $hash_parent) {
- $hash_parent = validate_input($hash_parent);
- if (!defined($hash_parent)) {
- die_error(undef, "Invalid hash parent parameter.");
+ if (!validate_input($hash_parent)) {
+ die_error(undef, "Invalid hash parent parameter");
}
}
our $hash_base = $cgi->param('hb');
if (defined $hash_base) {
- $hash_base = validate_input($hash_base);
- if (!defined($hash_base)) {
- die_error(undef, "Invalid hash base parameter.");
+ if (!validate_input($hash_base)) {
+ die_error(undef, "Invalid hash base parameter");
}
}
our $page = $cgi->param('pg');
if (defined $page) {
if ($page =~ m/[^0-9]$/) {
- undef $page;
- die_error(undef, "Invalid page parameter.");
+ die_error(undef, "Invalid page parameter");
}
}
our $searchtext = $cgi->param('s');
if (defined $searchtext) {
if ($searchtext =~ m/[^a-zA-Z0-9_\.\/\-\+\:\@ ]/) {
- undef $searchtext;
- die_error(undef, "Invalid search parameter.");
+ die_error(undef, "Invalid search parameter");
}
$searchtext = quotemeta $searchtext;
}
+# dispatch
+my %actions = (
+ "blame" => \&git_blame2,
+ "blobdiff" => \&git_blobdiff,
+ "blobdiff_plain" => \&git_blobdiff_plain,
+ "blob" => \&git_blob,
+ "blob_plain" => \&git_blob_plain,
+ "commitdiff" => \&git_commitdiff,
+ "commitdiff_plain" => \&git_commitdiff_plain,
+ "commit" => \&git_commit,
+ "heads" => \&git_heads,
+ "history" => \&git_history,
+ "log" => \&git_log,
+ "rss" => \&git_rss,
+ "search" => \&git_search,
+ "shortlog" => \&git_shortlog,
+ "summary" => \&git_summary,
+ "tag" => \&git_tag,
+ "tags" => \&git_tags,
+ "tree" => \&git_tree,
+);
+
+$action = 'summary' if (!defined($action));
+if (!defined($actions{$action})) {
+ die_error(undef, "Unknown action");
+}
+$actions{$action}->();
+exit;
+
+## ======================================================================
+## action links
+
+sub href(%) {
+ my %mapping = (
+ action => "a",
+ project => "p",
+ file_name => "f",
+ hash => "h",
+ hash_parent => "hp",
+ hash_base => "hb",
+ page => "pg",
+ searchtext => "s",
+ );
+
+ my %params = @_;
+ $params{"project"} ||= $project;
+
+ my $href = "$my_uri?";
+ $href .= esc_param( join(";",
+ map { "$mapping{$_}=$params{$_}" } keys %params
+ ) );
+
+ return $href;
+}
+
+
+## ======================================================================
+## validation, quoting/unquoting and escaping
+
sub validate_input {
my $input = shift;
@@ -175,66 +229,6 @@ sub validate_input {
return $input;
}
-if (!defined $action || $action eq "summary") {
- git_summary();
- exit;
-} elsif ($action eq "heads") {
- git_heads();
- exit;
-} elsif ($action eq "tags") {
- git_tags();
- exit;
-} elsif ($action eq "blob") {
- git_blob();
- exit;
-} elsif ($action eq "blob_plain") {
- git_blob_plain();
- exit;
-} elsif ($action eq "tree") {
- git_tree();
- exit;
-} elsif ($action eq "rss") {
- git_rss();
- exit;
-} elsif ($action eq "commit") {
- git_commit();
- exit;
-} elsif ($action eq "log") {
- git_log();
- exit;
-} elsif ($action eq "blobdiff") {
- git_blobdiff();
- exit;
-} elsif ($action eq "blobdiff_plain") {
- git_blobdiff_plain();
- exit;
-} elsif ($action eq "commitdiff") {
- git_commitdiff();
- exit;
-} elsif ($action eq "commitdiff_plain") {
- git_commitdiff_plain();
- exit;
-} elsif ($action eq "history") {
- git_history();
- exit;
-} elsif ($action eq "search") {
- git_search();
- exit;
-} elsif ($action eq "shortlog") {
- git_shortlog();
- exit;
-} elsif ($action eq "tag") {
- git_tag();
- exit;
-} elsif ($action eq "blame") {
- git_blame2();
- exit;
-} else {
- undef $action;
- die_error(undef, "Unknown action.");
- exit;
-}
-
# quote unsafe chars, but keep the slash, even when it's not
# correct, but quoted slashes look too horrible in bookmarks
sub esc_param {
@@ -250,6 +244,7 @@ sub esc_html {
my $str = shift;
$str = decode("utf8", $str, Encode::FB_DEFAULT);
$str = escapeHTML($str);
+ $str =~ s/\014/^L/g; # escape FORM FEED (FF) character (e.g. in COPYING file)
return $str;
}
@@ -263,6 +258,43 @@ sub unquote {
return $str;
}
+# escape tabs (convert tabs to spaces)
+sub untabify {
+ my $line = shift;
+
+ while ((my $pos = index($line, "\t")) != -1) {
+ if (my $count = (8 - ($pos % 8))) {
+ my $spaces = ' ' x $count;
+ $line =~ s/\t/$spaces/;
+ }
+ }
+
+ return $line;
+}
+
+## ----------------------------------------------------------------------
+## HTML aware string manipulation
+
+sub chop_str {
+ my $str = shift;
+ my $len = shift;
+ my $add_len = shift || 10;
+
+ # allow only $len chars, but don't cut a word if it would fit in $add_len
+ # if it doesn't fit, cut it if it's still longer than the dots we would add
+ $str =~ m/^(.{0,$len}[^ \/\-_:\.@]{0,$add_len})(.*)/;
+ my $body = $1;
+ my $tail = $2;
+ if (length($tail) > 4) {
+ $tail = " ...";
+ $body =~ s/&[^;]*$//; # remove chopped character entities
+ }
+ return "$body$tail";
+}
+
+## ----------------------------------------------------------------------
+## functions returning short strings
+
# CSS class for given age value (in seconds)
sub age_class {
my $age = shift;
@@ -276,126 +308,141 @@ sub age_class {
}
}
-sub git_header_html {
- my $status = shift || "200 OK";
- my $expires = shift;
+# convert age in seconds to "nn units ago" string
+sub age_string {
+ my $age = shift;
+ my $age_str;
- my $title = "$site_name git";
- if (defined $project) {
- $title .= " - $project";
- if (defined $action) {
- $title .= "/$action";
- if (defined $file_name) {
- $title .= " - $file_name";
- if ($action eq "tree" && $file_name !~ m|/$|) {
- $title .= "/";
- }
- }
- }
- }
- my $content_type;
- # require explicit support from the UA if we are to send the page as
- # 'application/xhtml+xml', otherwise send it as plain old 'text/html'.
- # we have to do this because MSIE sometimes globs '*/*', pretending to
- # support xhtml+xml but choking when it gets what it asked for.
- if ($cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ && $cgi->Accept('application/xhtml+xml') != 0) {
- $content_type = 'application/xhtml+xml';
+ if ($age > 60*60*24*365*2) {
+ $age_str = (int $age/60/60/24/365);
+ $age_str .= " years ago";
+ } elsif ($age > 60*60*24*(365/12)*2) {
+ $age_str = int $age/60/60/24/(365/12);
+ $age_str .= " months ago";
+ } elsif ($age > 60*60*24*7*2) {
+ $age_str = int $age/60/60/24/7;
+ $age_str .= " weeks ago";
+ } elsif ($age > 60*60*24*2) {
+ $age_str = int $age/60/60/24;
+ $age_str .= " days ago";
+ } elsif ($age > 60*60*2) {
+ $age_str = int $age/60/60;
+ $age_str .= " hours ago";
+ } elsif ($age > 60*2) {
+ $age_str = int $age/60;
+ $age_str .= " min ago";
+ } elsif ($age > 2) {
+ $age_str = int $age;
+ $age_str .= " sec ago";
} else {
- $content_type = 'text/html';
+ $age_str .= " right now";
}
- print $cgi->header(-type=>$content_type, -charset => 'utf-8', -status=> $status, -expires => $expires);
- print <<EOF;
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
-<!-- git web interface v$version, (C) 2005-2006, Kay Sievers <kay.sievers\@vrfy.org>, Christian Gierke -->
-<!-- git core binaries version $git_version -->
-<head>
-<meta http-equiv="content-type" content="$content_type; charset=utf-8"/>
-<meta name="robots" content="index, nofollow"/>
-<title>$title</title>
-<link rel="stylesheet" type="text/css" href="$stylesheet"/>
-$rss_link
-</head>
-<body>
-EOF
- print "<div class=\"page_header\">\n" .
- "<a href=\"http://www.kernel.org/pub/software/scm/git/docs/\" title=\"git documentation\">" .
- "<img src=\"$my_uri?" . esc_param("a=git-logo.png") . "\" width=\"72\" height=\"27\" alt=\"git\" style=\"float:right; border-width:0px;\"/>" .
- "</a>\n";
- print $cgi->a({-href => esc_param($home_link)}, "projects") . " / ";
- if (defined $project) {
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, esc_html($project));
- if (defined $action) {
- print " / $action";
- }
- print "\n";
- if (!defined $searchtext) {
- $searchtext = "";
- }
- my $search_hash;
- if (defined $hash_base) {
- $search_hash = $hash_base;
- } elsif (defined $hash) {
- $search_hash = $hash;
+ return $age_str;
+}
+
+# convert file mode in octal to symbolic file mode string
+sub mode_str {
+ my $mode = oct shift;
+
+ if (S_ISDIR($mode & S_IFMT)) {
+ return 'drwxr-xr-x';
+ } elsif (S_ISLNK($mode)) {
+ return 'lrwxrwxrwx';
+ } elsif (S_ISREG($mode)) {
+ # git cares only about the executable bit
+ if ($mode & S_IXUSR) {
+ return '-rwxr-xr-x';
} else {
- $search_hash = "HEAD";
- }
- $cgi->param("a", "search");
- $cgi->param("h", $search_hash);
- print $cgi->startform(-method => "get", -action => $my_uri) .
- "<div class=\"search\">\n" .
- $cgi->hidden(-name => "p") . "\n" .
- $cgi->hidden(-name => "a") . "\n" .
- $cgi->hidden(-name => "h") . "\n" .
- $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
- "</div>" .
- $cgi->end_form() . "\n";
+ return '-rw-r--r--';
+ };
+ } else {
+ return '----------';
}
- print "</div>\n";
}
-sub git_footer_html {
- print "<div class=\"page_footer\">\n";
- if (defined $project) {
- my $descr = git_read_description($project);
- if (defined $descr) {
- print "<div class=\"page_footer_text\">" . esc_html($descr) . "</div>\n";
- }
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=rss"), -class => "rss_logo"}, "RSS") . "\n";
+# convert file mode in octal to file type string
+sub file_type {
+ my $mode = oct shift;
+
+ if (S_ISDIR($mode & S_IFMT)) {
+ return "directory";
+ } elsif (S_ISLNK($mode)) {
+ return "symlink";
+ } elsif (S_ISREG($mode)) {
+ return "file";
} else {
- print $cgi->a({-href => "$my_uri?" . esc_param("a=opml"), -class => "rss_logo"}, "OPML") . "\n";
+ return "unknown";
}
- print "</div>\n" .
- "</body>\n" .
- "</html>";
}
-sub die_error {
- my $status = shift || "403 Forbidden";
- my $error = shift || "Malformed query, file missing or permission denied";
+## ----------------------------------------------------------------------
+## functions returning short HTML fragments, or transforming HTML fragments
+## which don't beling to other sections
- git_header_html($status);
- print "<div class=\"page_body\">\n" .
- "<br/><br/>\n" .
- "$status - $error\n" .
- "<br/>\n" .
- "</div>\n";
- git_footer_html();
- exit;
+# format line of commit message or tag comment
+sub format_log_line_html {
+ my $line = shift;
+
+ $line = esc_html($line);
+ $line =~ s/ /&nbsp;/g;
+ if ($line =~ m/([0-9a-fA-F]{40})/) {
+ my $hash_text = $1;
+ if (git_get_type($hash_text) eq "commit") {
+ my $link = $cgi->a({-class => "text", -href => href(action=>"commit", hash=>$hash_text)}, $hash_text);
+ $line =~ s/$hash_text/$link/;
+ }
+ }
+ return $line;
}
-sub git_get_type {
- my $hash = shift;
+# format marker of refs pointing to given object
+sub format_ref_marker {
+ my ($refs, $id) = @_;
+ my $markers = '';
- open my $fd, "-|", "$GIT cat-file -t $hash" or return;
- my $type = <$fd>;
- close $fd or return;
- chomp $type;
- return $type;
+ if (defined $refs->{$id}) {
+ foreach my $ref (@{$refs->{$id}}) {
+ my ($type, $name) = qw();
+ # e.g. tags/v2.6.11 or heads/next
+ if ($ref =~ m!^(.*?)s?/(.*)$!) {
+ $type = $1;
+ $name = $2;
+ } else {
+ $type = "ref";
+ $name = $ref;
+ }
+
+ $markers .= " <span class=\"$type\">" . esc_html($name) . "</span>";
+ }
+ }
+
+ if ($markers) {
+ return ' <span class="refs">'. $markers . '</span>';
+ } else {
+ return "";
+ }
}
-sub git_read_head {
+# format, perhaps shortened and with markers, title line
+sub format_subject_html {
+ my ($long, $short, $href, $extra) = @_;
+ $extra = '' unless defined($extra);
+
+ if (length($short) < length($long)) {
+ return $cgi->a({-href => $href, -class => "list",
+ -title => $long},
+ esc_html($short) . $extra);
+ } else {
+ return $cgi->a({-href => $href, -class => "list"},
+ esc_html($long) . $extra);
+ }
+}
+
+## ----------------------------------------------------------------------
+## git utility subroutines, invoking git commands
+
+# get HEAD ref of given project as hash
+sub git_get_head_hash {
my $project = shift;
my $oENV = $ENV{'GIT_DIR'};
my $retval = undef;
@@ -413,7 +460,58 @@ sub git_read_head {
return $retval;
}
-sub git_read_hash {
+# get type of given object
+sub git_get_type {
+ my $hash = shift;
+
+ open my $fd, "-|", $GIT, "cat-file", '-t', $hash or return;
+ my $type = <$fd>;
+ close $fd or return;
+ chomp $type;
+ return $type;
+}
+
+sub git_get_project_config {
+ my $key = shift;
+
+ return unless ($key);
+ $key =~ s/^gitweb\.//;
+ return if ($key =~ m/\W/);
+
+ my $val = qx($GIT repo-config --get gitweb.$key);
+ return ($val);
+}
+
+sub git_get_project_config_bool {
+ my $val = git_get_project_config (@_);
+ if ($val and $val =~ m/true|yes|on/) {
+ return (1);
+ }
+ return; # implicit false
+}
+
+# get hash of given path at given ref
+sub git_get_hash_by_path {
+ my $base = shift;
+ my $path = shift || return undef;
+
+ my $tree = $base;
+
+ open my $fd, "-|", $GIT, "ls-tree", $base, "--", $path
+ or die_error(undef, "Open git-ls-tree failed");
+ my $line = <$fd>;
+ close $fd or return undef;
+
+ #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
+ $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/;
+ return $3;
+}
+
+## ......................................................................
+## git utility functions, directly accessing git repository
+
+# assumes that PATH is not symref
+sub git_get_hash_by_ref {
my $path = shift;
open my $fd, "$projectroot/$path" or return undef;
@@ -425,7 +523,7 @@ sub git_read_hash {
}
}
-sub git_read_description {
+sub git_get_project_description {
my $path = shift;
open my $fd, "$projectroot/$path/description" or return undef;
@@ -435,12 +533,153 @@ sub git_read_description {
return $descr;
}
-sub git_read_tag {
+sub git_get_project_url_list {
+ my $path = shift;
+
+ open my $fd, "$projectroot/$path/cloneurl" or return undef;
+ my @git_project_url_list = map { chomp; $_ } <$fd>;
+ close $fd;
+
+ return wantarray ? @git_project_url_list : \@git_project_url_list;
+}
+
+sub git_get_projects_list {
+ my @list;
+
+ if (-d $projects_list) {
+ # search in directory
+ my $dir = $projects_list;
+ opendir my ($dh), $dir or return undef;
+ while (my $dir = readdir($dh)) {
+ if (-e "$projectroot/$dir/HEAD") {
+ my $pr = {
+ path => $dir,
+ };
+ push @list, $pr
+ }
+ }
+ closedir($dh);
+ } elsif (-f $projects_list) {
+ # read from file(url-encoded):
+ # 'git%2Fgit.git Linus+Torvalds'
+ # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
+ # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman'
+ open my ($fd), $projects_list or return undef;
+ while (my $line = <$fd>) {
+ chomp $line;
+ my ($path, $owner) = split ' ', $line;
+ $path = unescape($path);
+ $owner = unescape($owner);
+ if (!defined $path) {
+ next;
+ }
+ if (-e "$projectroot/$path/HEAD") {
+ my $pr = {
+ path => $path,
+ owner => decode("utf8", $owner, Encode::FB_DEFAULT),
+ };
+ push @list, $pr
+ }
+ }
+ close $fd;
+ }
+ @list = sort {$a->{'path'} cmp $b->{'path'}} @list;
+ return @list;
+}
+
+sub git_get_project_owner {
+ my $project = shift;
+ my $owner;
+
+ return undef unless $project;
+
+ # read from file (url-encoded):
+ # 'git%2Fgit.git Linus+Torvalds'
+ # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
+ # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman'
+ if (-f $projects_list) {
+ open (my $fd , $projects_list);
+ while (my $line = <$fd>) {
+ chomp $line;
+ my ($pr, $ow) = split ' ', $line;
+ $pr = unescape($pr);
+ $ow = unescape($ow);
+ if ($pr eq $project) {
+ $owner = decode("utf8", $ow, Encode::FB_DEFAULT);
+ last;
+ }
+ }
+ close $fd;
+ }
+ if (!defined $owner) {
+ $owner = get_file_owner("$projectroot/$project");
+ }
+
+ return $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, "ls-remote", "."
+ or return;
+ }
+
+ while (my $line = <$fd>) {
+ chomp $line;
+ if ($line =~ m/^([0-9a-fA-F]{40})\trefs\/($type\/?[^\^]+)/) {
+ if (defined $refs{$1}) {
+ push @{$refs{$1}}, $2;
+ } else {
+ $refs{$1} = [ $2 ];
+ }
+ }
+ }
+ close $fd or return;
+ return \%refs;
+}
+
+## ----------------------------------------------------------------------
+## parse to hash functions
+
+sub parse_date {
+ my $epoch = shift;
+ my $tz = shift || "-0000";
+
+ my %date;
+ my @months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
+ my @days = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
+ my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($epoch);
+ $date{'hour'} = $hour;
+ $date{'minute'} = $min;
+ $date{'mday'} = $mday;
+ $date{'day'} = $days[$wday];
+ $date{'month'} = $months[$mon];
+ $date{'rfc2822'} = sprintf "%s, %d %s %4d %02d:%02d:%02d +0000", $days[$wday], $mday, $months[$mon], 1900+$year, $hour ,$min, $sec;
+ $date{'mday-time'} = sprintf "%d %s %02d:%02d", $mday, $months[$mon], $hour ,$min;
+
+ $tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/;
+ my $local = $epoch + ((int $1 + ($2/60)) * 3600);
+ ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($local);
+ $date{'hour_local'} = $hour;
+ $date{'minute_local'} = $min;
+ $date{'tz_local'} = $tz;
+ return %date;
+}
+
+sub parse_tag {
my $tag_id = shift;
my %tag;
my @comment;
- open my $fd, "-|", "$GIT cat-file tag $tag_id" or return;
+ open my $fd, "-|", $GIT, "cat-file", "tag", $tag_id or return;
$tag{'id'} = $tag_id;
while (my $line = <$fd>) {
chomp $line;
@@ -470,38 +709,7 @@ sub git_read_tag {
return %tag
}
-sub age_string {
- my $age = shift;
- my $age_str;
-
- if ($age > 60*60*24*365*2) {
- $age_str = (int $age/60/60/24/365);
- $age_str .= " years ago";
- } elsif ($age > 60*60*24*(365/12)*2) {
- $age_str = int $age/60/60/24/(365/12);
- $age_str .= " months ago";
- } elsif ($age > 60*60*24*7*2) {
- $age_str = int $age/60/60/24/7;
- $age_str .= " weeks ago";
- } elsif ($age > 60*60*24*2) {
- $age_str = int $age/60/60/24;
- $age_str .= " days ago";
- } elsif ($age > 60*60*2) {
- $age_str = int $age/60/60;
- $age_str .= " hours ago";
- } elsif ($age > 60*2) {
- $age_str = int $age/60;
- $age_str .= " min ago";
- } elsif ($age > 2) {
- $age_str = int $age;
- $age_str .= " sec ago";
- } else {
- $age_str .= " right now";
- }
- return $age_str;
-}
-
-sub git_read_commit {
+sub parse_commit {
my $commit_id = shift;
my $commit_text = shift;
@@ -512,7 +720,7 @@ sub git_read_commit {
@commit_lines = @$commit_text;
} else {
$/ = "\0";
- open my $fd, "-|", "$GIT rev-list --header --parents --max-count=1 $commit_id" or return;
+ open my $fd, "-|", $GIT, "rev-list", "--header", "--parents", "--max-count=1", $commit_id or return;
@commit_lines = split '\n', <$fd>;
close $fd or return;
$/ = "\n";
@@ -595,6 +803,716 @@ sub git_read_commit {
return %co;
}
+# parse ref from ref_file, given by ref_id, with given type
+sub parse_ref {
+ my $ref_file = shift;
+ my $ref_id = shift;
+ my $type = shift || git_get_type($ref_id);
+ my %ref_item;
+
+ $ref_item{'type'} = $type;
+ $ref_item{'id'} = $ref_id;
+ $ref_item{'epoch'} = 0;
+ $ref_item{'age'} = "unknown";
+ if ($type eq "tag") {
+ my %tag = parse_tag($ref_id);
+ $ref_item{'comment'} = $tag{'comment'};
+ if ($tag{'type'} eq "commit") {
+ my %co = parse_commit($tag{'object'});
+ $ref_item{'epoch'} = $co{'committer_epoch'};
+ $ref_item{'age'} = $co{'age_string'};
+ } elsif (defined($tag{'epoch'})) {
+ my $age = time - $tag{'epoch'};
+ $ref_item{'epoch'} = $tag{'epoch'};
+ $ref_item{'age'} = age_string($age);
+ }
+ $ref_item{'reftype'} = $tag{'type'};
+ $ref_item{'name'} = $tag{'name'};
+ $ref_item{'refid'} = $tag{'object'};
+ } elsif ($type eq "commit"){
+ my %co = parse_commit($ref_id);
+ $ref_item{'reftype'} = "commit";
+ $ref_item{'name'} = $ref_file;
+ $ref_item{'title'} = $co{'title'};
+ $ref_item{'refid'} = $ref_id;
+ $ref_item{'epoch'} = $co{'committer_epoch'};
+ $ref_item{'age'} = $co{'age_string'};
+ } else {
+ $ref_item{'reftype'} = $type;
+ $ref_item{'name'} = $ref_file;
+ $ref_item{'refid'} = $ref_id;
+ }
+
+ return %ref_item;
+}
+
+## ......................................................................
+## parse to array of hashes functions
+
+sub git_get_refs_list {
+ my $ref_dir = shift;
+ my @reflist;
+
+ my @refs;
+ my $pfxlen = length("$projectroot/$project/$ref_dir");
+ File::Find::find(sub {
+ return if (/^\./);
+ if (-f $_) {
+ push @refs, substr($File::Find::name, $pfxlen + 1);
+ }
+ }, "$projectroot/$project/$ref_dir");
+
+ foreach my $ref_file (@refs) {
+ my $ref_id = git_get_hash_by_ref("$project/$ref_dir/$ref_file");
+ my $type = git_get_type($ref_id) || next;
+ my %ref_item = parse_ref($ref_file, $ref_id, $type);
+
+ push @reflist, \%ref_item;
+ }
+ # sort refs by age
+ @reflist = sort {$b->{'epoch'} <=> $a->{'epoch'}} @reflist;
+ return \@reflist;
+}
+
+## ----------------------------------------------------------------------
+## filesystem-related functions
+
+sub get_file_owner {
+ my $path = shift;
+
+ my ($dev, $ino, $mode, $nlink, $st_uid, $st_gid, $rdev, $size) = stat($path);
+ my ($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell) = getpwuid($st_uid);
+ if (!defined $gcos) {
+ return undef;
+ }
+ my $owner = $gcos;
+ $owner =~ s/[,;].*$//;
+ return decode("utf8", $owner, Encode::FB_DEFAULT);
+}
+
+## ......................................................................
+## mimetype related functions
+
+sub mimetype_guess_file {
+ my $filename = shift;
+ my $mimemap = shift;
+ -r $mimemap or return undef;
+
+ my %mimemap;
+ open(MIME, $mimemap) or return undef;
+ while (<MIME>) {
+ next if m/^#/; # skip comments
+ my ($mime, $exts) = split(/\t+/);
+ if (defined $exts) {
+ my @exts = split(/\s+/, $exts);
+ foreach my $ext (@exts) {
+ $mimemap{$ext} = $mime;
+ }
+ }
+ }
+ close(MIME);
+
+ $filename =~ /\.(.*?)$/;
+ return $mimemap{$1};
+}
+
+sub mimetype_guess {
+ my $filename = shift;
+ my $mime;
+ $filename =~ /\./ or return undef;
+
+ if ($mimetypes_file) {
+ my $file = $mimetypes_file;
+ if ($file !~ m!^/!) { # if it is relative path
+ # it is relative to project
+ $file = "$projectroot/$project/$file";
+ }
+ $mime = mimetype_guess_file($filename, $file);
+ }
+ $mime ||= mimetype_guess_file($filename, '/etc/mime.types');
+ return $mime;
+}
+
+sub blob_mimetype {
+ my $fd = shift;
+ my $filename = shift;
+
+ if ($filename) {
+ my $mime = mimetype_guess($filename);
+ $mime and return $mime;
+ }
+
+ # just in case
+ return $default_blob_plain_mimetype unless $fd;
+
+ if (-T $fd) {
+ return 'text/plain' .
+ ($default_text_plain_charset ? '; charset='.$default_text_plain_charset : '');
+ } elsif (! $filename) {
+ return 'application/octet-stream';
+ } elsif ($filename =~ m/\.png$/i) {
+ return 'image/png';
+ } elsif ($filename =~ m/\.gif$/i) {
+ return 'image/gif';
+ } elsif ($filename =~ m/\.jpe?g$/i) {
+ return 'image/jpeg';
+ } else {
+ return 'application/octet-stream';
+ }
+}
+
+## ======================================================================
+## functions printing HTML: header, footer, error page
+
+sub git_header_html {
+ my $status = shift || "200 OK";
+ my $expires = shift;
+
+ my $title = "$site_name git";
+ if (defined $project) {
+ $title .= " - $project";
+ if (defined $action) {
+ $title .= "/$action";
+ if (defined $file_name) {
+ $title .= " - $file_name";
+ if ($action eq "tree" && $file_name !~ m|/$|) {
+ $title .= "/";
+ }
+ }
+ }
+ }
+ my $content_type;
+ # require explicit support from the UA if we are to send the page as
+ # 'application/xhtml+xml', otherwise send it as plain old 'text/html'.
+ # we have to do this because MSIE sometimes globs '*/*', pretending to
+ # support xhtml+xml but choking when it gets what it asked for.
+ if (defined $cgi->http('HTTP_ACCEPT') && $cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ && $cgi->Accept('application/xhtml+xml') != 0) {
+ $content_type = 'application/xhtml+xml';
+ } else {
+ $content_type = 'text/html';
+ }
+ print $cgi->header(-type=>$content_type, -charset => 'utf-8', -status=> $status, -expires => $expires);
+ print <<EOF;
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
+<!-- git web interface version $version, (C) 2005-2006, Kay Sievers <kay.sievers\@vrfy.org>, Christian Gierke -->
+<!-- git core binaries version $git_version -->
+<head>
+<meta http-equiv="content-type" content="$content_type; charset=utf-8"/>
+<meta name="generator" content="gitweb/$version git/$git_version"/>
+<meta name="robots" content="index, nofollow"/>
+<title>$title</title>
+<link rel="stylesheet" type="text/css" href="$stylesheet"/>
+EOF
+ if (defined $project) {
+ printf('<link rel="alternate" title="%s log" '.
+ 'href="%s" type="application/rss+xml"/>'."\n",
+ esc_param($project), href(action=>"rss"));
+ }
+
+ print "</head>\n" .
+ "<body>\n" .
+ "<div class=\"page_header\">\n" .
+ "<a href=\"http://www.kernel.org/pub/software/scm/git/docs/\" title=\"git documentation\">" .
+ "<img src=\"$logo\" width=\"72\" height=\"27\" alt=\"git\" style=\"float:right; border-width:0px;\"/>" .
+ "</a>\n";
+ print $cgi->a({-href => esc_param($home_link)}, $home_link_str) . " / ";
+ if (defined $project) {
+ print $cgi->a({-href => href(action=>"summary")}, esc_html($project));
+ if (defined $action) {
+ print " / $action";
+ }
+ print "\n";
+ if (!defined $searchtext) {
+ $searchtext = "";
+ }
+ my $search_hash;
+ if (defined $hash_base) {
+ $search_hash = $hash_base;
+ } elsif (defined $hash) {
+ $search_hash = $hash;
+ } else {
+ $search_hash = "HEAD";
+ }
+ $cgi->param("a", "search");
+ $cgi->param("h", $search_hash);
+ print $cgi->startform(-method => "get", -action => $my_uri) .
+ "<div class=\"search\">\n" .
+ $cgi->hidden(-name => "p") . "\n" .
+ $cgi->hidden(-name => "a") . "\n" .
+ $cgi->hidden(-name => "h") . "\n" .
+ $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
+ "</div>" .
+ $cgi->end_form() . "\n";
+ }
+ print "</div>\n";
+}
+
+sub git_footer_html {
+ print "<div class=\"page_footer\">\n";
+ if (defined $project) {
+ my $descr = git_get_project_description($project);
+ if (defined $descr) {
+ print "<div class=\"page_footer_text\">" . esc_html($descr) . "</div>\n";
+ }
+ print $cgi->a({-href => href(action=>"rss"), -class => "rss_logo"}, "RSS") . "\n";
+ } else {
+ print $cgi->a({-href => "$my_uri?" . esc_param("a=opml"), -class => "rss_logo"}, "OPML") . "\n";
+ }
+ print "</div>\n" .
+ "</body>\n" .
+ "</html>";
+}
+
+sub die_error {
+ my $status = shift || "403 Forbidden";
+ my $error = shift || "Malformed query, file missing or permission denied";
+
+ git_header_html($status);
+ print "<div class=\"page_body\">\n" .
+ "<br/><br/>\n" .
+ "$status - $error\n" .
+ "<br/>\n" .
+ "</div>\n";
+ git_footer_html();
+ exit;
+}
+
+## ----------------------------------------------------------------------
+## functions printing or outputting HTML: navigation
+
+sub git_print_page_nav {
+ my ($current, $suppress, $head, $treehead, $treebase, $extra) = @_;
+ $extra = '' if !defined $extra; # pager or formats
+
+ my @navs = qw(summary shortlog log commit commitdiff tree);
+ if ($suppress) {
+ @navs = grep { $_ ne $suppress } @navs;
+ }
+
+ my %arg = map { $_ => {action=>$_} } @navs;
+ if (defined $head) {
+ for (qw(commit commitdiff)) {
+ $arg{$_}{hash} = $head;
+ }
+ if ($current =~ m/^(tree | log | shortlog | commit | commitdiff | search)$/x) {
+ for (qw(shortlog log)) {
+ $arg{$_}{hash} = $head;
+ }
+ }
+ }
+ $arg{tree}{hash} = $treehead if defined $treehead;
+ $arg{tree}{hash_base} = $treebase if defined $treebase;
+
+ print "<div class=\"page_nav\">\n" .
+ (join " | ",
+ map { $_ eq $current ?
+ $_ : $cgi->a({-href => href(%{$arg{$_}})}, "$_")
+ } @navs);
+ print "<br/>\n$extra<br/>\n" .
+ "</div>\n";
+}
+
+sub format_paging_nav {
+ my ($action, $hash, $head, $page, $nrevs) = @_;
+ my $paging_nav;
+
+
+ if ($hash ne $head || $page) {
+ $paging_nav .= $cgi->a({-href => href(action=>$action)}, "HEAD");
+ } else {
+ $paging_nav .= "HEAD";
+ }
+
+ if ($page > 0) {
+ $paging_nav .= " &sdot; " .
+ $cgi->a({-href => href(action=>$action, hash=>$hash, page=>$page-1),
+ -accesskey => "p", -title => "Alt-p"}, "prev");
+ } else {
+ $paging_nav .= " &sdot; prev";
+ }
+
+ if ($nrevs >= (100 * ($page+1)-1)) {
+ $paging_nav .= " &sdot; " .
+ $cgi->a({-href => href(action=>$action, hash=>$hash, page=>$page+1),
+ -accesskey => "n", -title => "Alt-n"}, "next");
+ } else {
+ $paging_nav .= " &sdot; next";
+ }
+
+ return $paging_nav;
+}
+
+## ......................................................................
+## functions printing or outputting HTML: div
+
+sub git_print_header_div {
+ my ($action, $title, $hash, $hash_base) = @_;
+ my %args = ();
+
+ $args{action} = $action;
+ $args{hash} = $hash if $hash;
+ $args{hash_base} = $hash_base if $hash_base;
+
+ print "<div class=\"header\">\n" .
+ $cgi->a({-href => href(%args), -class => "title"},
+ $title ? $title : $action) .
+ "\n</div>\n";
+}
+
+sub git_print_page_path {
+ my $name = shift;
+ my $type = shift;
+
+ if (!defined $name) {
+ print "<div class=\"page_path\"><b>/</b></div>\n";
+ } elsif (defined $type && $type eq 'blob') {
+ print "<div class=\"page_path\"><b>" .
+ $cgi->a({-href => href(action=>"blob_plain", file_name=>$file_name)}, esc_html($name)) . "</b><br/></div>\n";
+ } else {
+ print "<div class=\"page_path\"><b>" . esc_html($name) . "</b><br/></div>\n";
+ }
+}
+
+## ......................................................................
+## functions printing large fragments of HTML
+
+sub git_difftree_body {
+ my ($difftree, $parent) = @_;
+
+ print "<div class=\"list_head\">\n";
+ if ($#{$difftree} > 10) {
+ print(($#{$difftree} + 1) . " files changed:\n");
+ }
+ print "</div>\n";
+
+ print "<table class=\"diff_tree\">\n";
+ my $alternate = 0;
+ foreach my $line (@{$difftree}) {
+ # ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M ls-files.c'
+ # ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M rev-tree.c'
+ 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 $from_mode = $1;
+ my $to_mode = $2;
+ my $from_id = $3;
+ my $to_id = $4;
+ my $status = $5;
+ my $similarity = $6; # score
+ my $file = validate_input(unquote($7));
+
+ if ($alternate) {
+ print "<tr class=\"dark\">\n";
+ } else {
+ print "<tr class=\"light\">\n";
+ }
+ $alternate ^= 1;
+
+ if ($status eq "A") { # created
+ my $mode_chng = "";
+ if (S_ISREG(oct $to_mode)) {
+ $mode_chng = sprintf(" with mode: %04o", (oct $to_mode) & 0777);
+ }
+ print "<td>" .
+ $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$file),
+ -class => "list"}, esc_html($file)) .
+ "</td>\n" .
+ "<td><span class=\"file_status new\">[new " . file_type($to_mode) . "$mode_chng]</span></td>\n" .
+ "<td class=\"link\">" .
+ $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$file)}, "blob") .
+ "</td>\n";
+
+ } elsif ($status eq "D") { # deleted
+ print "<td>" .
+ $cgi->a({-href => href(action=>"blob", hash=>$from_id, hash_base=>$parent, file_name=>$file),
+ -class => "list"}, esc_html($file)) . "</td>\n" .
+ "<td><span class=\"file_status deleted\">[deleted " . file_type($from_mode). "]</span></td>\n" .
+ "<td class=\"link\">" .
+ $cgi->a({-href => href(action=>"blob", hash=>$from_id, hash_base=>$parent, file_name=>$file)}, "blob") . " | " .
+ $cgi->a({-href => href(action=>"history", hash_base=>$parent, file_name=>$file)}, "history") .
+ "</td>\n"
+
+ } elsif ($status eq "M" || $status eq "T") { # modified, or type changed
+ my $mode_chnge = "";
+ if ($from_mode != $to_mode) {
+ $mode_chnge = " <span class=\"file_status mode_chnge\">[changed";
+ if (((oct $from_mode) & S_IFMT) != ((oct $to_mode) & S_IFMT)) {
+ $mode_chnge .= " from " . file_type($from_mode) . " to " . file_type($to_mode);
+ }
+ if (((oct $from_mode) & 0777) != ((oct $to_mode) & 0777)) {
+ if (S_ISREG($from_mode) && S_ISREG($to_mode)) {
+ $mode_chnge .= sprintf(" mode: %04o->%04o", (oct $from_mode) & 0777, (oct $to_mode) & 0777);
+ } elsif (S_ISREG($to_mode)) {
+ $mode_chnge .= sprintf(" mode: %04o", (oct $to_mode) & 0777);
+ }
+ }
+ $mode_chnge .= "]</span>\n";
+ }
+ print "<td>";
+ if ($to_id ne $from_id) { # modified
+ print $cgi->a({-href => href(action=>"blobdiff", hash=>$to_id, hash_parent=>$from_id, hash_base=>$hash, file_name=>$file),
+ -class => "list"}, esc_html($file));
+ } else { # mode changed
+ print $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$file),
+ -class => "list"}, esc_html($file));
+ }
+ print "</td>\n" .
+ "<td>$mode_chnge</td>\n" .
+ "<td class=\"link\">" .
+ $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$file)}, "blob");
+ if ($to_id ne $from_id) { # modified
+ print $cgi->a({-href => href(action=>"blobdiff", hash=>$to_id, hash_parent=>$from_id, hash_base=>$hash, file_name=>$file)}, "diff");
+ }
+ print " | " . $cgi->a({-href => href(action=>"history", hash_base=>$hash, file_name=>$file)}, "history") . "\n";
+ print "</td>\n";
+
+ } elsif ($status eq "R") { # renamed
+ my ($from_file, $to_file) = split "\t", $file;
+ my $mode_chng = "";
+ if ($from_mode != $to_mode) {
+ $mode_chng = sprintf(", mode: %04o", (oct $to_mode) & 0777);
+ }
+ print "<td>" .
+ $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$to_file),
+ -class => "list"}, esc_html($to_file)) . "</td>\n" .
+ "<td><span class=\"file_status moved\">[moved from " .
+ $cgi->a({-href => href(action=>"blob", hash=>$from_id, hash_base=>$parent, file_name=>$from_file),
+ -class => "list"}, esc_html($from_file)) .
+ " with " . (int $similarity) . "% similarity$mode_chng]</span></td>\n" .
+ "<td class=\"link\">" .
+ $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$to_file)}, "blob");
+ if ($to_id ne $from_id) {
+ print " | " .
+ $cgi->a({-href => "$my_uri?" .
+ esc_param("p=$project;a=blobdiff;h=$to_id;hp=$from_id;hb=$hash;f=$to_file;fp=$from_file")}, "diff");
+ }
+ print "</td>\n";
+
+ } elsif ($status eq "C") { # copied
+ my ($from_file, $to_file) = split "\t", $file;
+ my $mode_chng = "";
+ if ($from_mode != $to_mode) {
+ $mode_chng = sprintf(", mode: %04o", (oct $to_mode) & 0777);
+ }
+ print "<td>" .
+ $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$to_file),
+ -class => "list"}, esc_html($to_file)) . "</td>\n" .
+ "<td><span class=\"file_status copied\">[copied from " .
+ $cgi->a({-href => href(action=>"blob", hash=>$from_id, hash_base=>$parent, file_name=>$from_file),
+ -class => "list"}, esc_html($from_file)) .
+ " with " . (int $similarity) . "% similarity$mode_chng]</span></td>\n" .
+ "<td class=\"link\">" .
+ $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$to_file)}, "blob");
+ if ($to_id ne $from_id) {
+ print " | " .
+ $cgi->a({-href => "$my_uri?" .
+ esc_param("p=$project;a=blobdiff;h=$to_id;hp=$from_id;hb=$hash;f=$to_file;fp=$from_file")}, "diff");
+ }
+ print "</td>\n";
+ } # we should not encounter Unmerged (U) or Unknown (X) status
+ print "</tr>\n";
+ }
+ print "</table>\n";
+}
+
+sub git_shortlog_body {
+ # uses global variable $project
+ my ($revlist, $from, $to, $refs, $extra) = @_;
+ $from = 0 unless defined $from;
+ $to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);
+
+ print "<table class=\"shortlog\" cellspacing=\"0\">\n";
+ my $alternate = 0;
+ for (my $i = $from; $i <= $to; $i++) {
+ my $commit = $revlist->[$i];
+ #my $ref = defined $refs ? format_ref_marker($refs, $commit) : '';
+ my $ref = format_ref_marker($refs, $commit);
+ my %co = parse_commit($commit);
+ if ($alternate) {
+ print "<tr class=\"dark\">\n";
+ } else {
+ print "<tr class=\"light\">\n";
+ }
+ $alternate ^= 1;
+ # git_summary() used print "<td><i>$co{'age_string'}</i></td>\n" .
+ print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+ "<td><i>" . esc_html(chop_str($co{'author_name'}, 10)) . "</i></td>\n" .
+ "<td>";
+ print format_subject_html($co{'title'}, $co{'title_short'}, href(action=>"commit", hash=>$commit), $ref);
+ print "</td>\n" .
+ "<td class=\"link\">" .
+ $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " .
+ $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") .
+ "</td>\n" .
+ "</tr>\n";
+ }
+ if (defined $extra) {
+ print "<tr>\n" .
+ "<td colspan=\"4\">$extra</td>\n" .
+ "</tr>\n";
+ }
+ print "</table>\n";
+}
+
+sub git_history_body {
+ # Warning: assumes constant type (blob or tree) during history
+ my ($fd, $refs, $hash_base, $ftype, $extra) = @_;
+
+ print "<table class=\"history\" cellspacing=\"0\">\n";
+ my $alternate = 0;
+ while (my $line = <$fd>) {
+ if ($line !~ m/^([0-9a-fA-F]{40})/) {
+ next;
+ }
+
+ my $commit = $1;
+ my %co = parse_commit($commit);
+ if (!%co) {
+ next;
+ }
+
+ my $ref = format_ref_marker($refs, $commit);
+
+ if ($alternate) {
+ print "<tr class=\"dark\">\n";
+ } else {
+ print "<tr class=\"light\">\n";
+ }
+ $alternate ^= 1;
+ print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+ # shortlog uses chop_str($co{'author_name'}, 10)
+ "<td><i>" . esc_html(chop_str($co{'author_name'}, 15, 3)) . "</i></td>\n" .
+ "<td>";
+ # originally git_history used chop_str($co{'title'}, 50)
+ print format_subject_html($co{'title'}, $co{'title_short'}, href(action=>"commit", hash=>$commit), $ref);
+ print "</td>\n" .
+ "<td class=\"link\">" .
+ $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " .
+ $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " .
+ $cgi->a({-href => href(action=>$ftype, hash_base=>$commit, file_name=>$file_name)}, $ftype);
+
+ if ($ftype eq 'blob') {
+ my $blob_current = git_get_hash_by_path($hash_base, $file_name);
+ my $blob_parent = git_get_hash_by_path($commit, $file_name);
+ if (defined $blob_current && defined $blob_parent &&
+ $blob_current ne $blob_parent) {
+ print " | " .
+ $cgi->a({-href => href(action=>"blobdiff", hash=>$blob_current, hash_parent=>$blob_parent, hash_base=>$commit, file_name=>$file_name)},
+ "diff to current");
+ }
+ }
+ print "</td>\n" .
+ "</tr>\n";
+ }
+ if (defined $extra) {
+ print "<tr>\n" .
+ "<td colspan=\"4\">$extra</td>\n" .
+ "</tr>\n";
+ }
+ print "</table>\n";
+}
+
+sub git_tags_body {
+ # uses global variable $project
+ my ($taglist, $from, $to, $extra) = @_;
+ $from = 0 unless defined $from;
+ $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to);
+
+ print "<table class=\"tags\" cellspacing=\"0\">\n";
+ my $alternate = 0;
+ for (my $i = $from; $i <= $to; $i++) {
+ my $entry = $taglist->[$i];
+ my %tag = %$entry;
+ my $comment_lines = $tag{'comment'};
+ my $comment = shift @$comment_lines;
+ my $comment_short;
+ if (defined $comment) {
+ $comment_short = chop_str($comment, 30, 5);
+ }
+ if ($alternate) {
+ print "<tr class=\"dark\">\n";
+ } else {
+ print "<tr class=\"light\">\n";
+ }
+ $alternate ^= 1;
+ print "<td><i>$tag{'age'}</i></td>\n" .
+ "<td>" .
+ $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'}),
+ -class => "list"}, "<b>" . esc_html($tag{'name'}) . "</b>") .
+ "</td>\n" .
+ "<td>";
+ if (defined $comment) {
+ print format_subject_html($comment, $comment_short, href(action=>"tag", hash=>$tag{'id'}));
+ }
+ print "</td>\n" .
+ "<td class=\"selflink\">";
+ if ($tag{'type'} eq "tag") {
+ print $cgi->a({-href => href(action=>"tag", hash=>$tag{'id'})}, "tag");
+ } else {
+ print "&nbsp;";
+ }
+ print "</td>\n" .
+ "<td class=\"link\">" . " | " .
+ $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'})}, $tag{'reftype'});
+ if ($tag{'reftype'} eq "commit") {
+ print " | " . $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'})}, "shortlog") .
+ " | " . $cgi->a({-href => href(action=>"log", hash=>$tag{'refid'})}, "log");
+ } elsif ($tag{'reftype'} eq "blob") {
+ print " | " . $cgi->a({-href => href(action=>"blob_plain", hash=>$tag{'refid'})}, "raw");
+ }
+ print "</td>\n" .
+ "</tr>";
+ }
+ if (defined $extra) {
+ print "<tr>\n" .
+ "<td colspan=\"5\">$extra</td>\n" .
+ "</tr>\n";
+ }
+ print "</table>\n";
+}
+
+sub git_heads_body {
+ # uses global variable $project
+ my ($taglist, $head, $from, $to, $extra) = @_;
+ $from = 0 unless defined $from;
+ $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to);
+
+ print "<table class=\"heads\" cellspacing=\"0\">\n";
+ my $alternate = 0;
+ for (my $i = $from; $i <= $to; $i++) {
+ my $entry = $taglist->[$i];
+ my %tag = %$entry;
+ my $curr = $tag{'id'} eq $head;
+ if ($alternate) {
+ print "<tr class=\"dark\">\n";
+ } else {
+ print "<tr class=\"light\">\n";
+ }
+ $alternate ^= 1;
+ print "<td><i>$tag{'age'}</i></td>\n" .
+ ($tag{'id'} eq $head ? "<td class=\"current_head\">" : "<td>") .
+ $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'}),
+ -class => "list"}, "<b>" . esc_html($tag{'name'}) . "</b>") .
+ "</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") .
+ "</td>\n" .
+ "</tr>";
+ }
+ if (defined $extra) {
+ print "<tr>\n" .
+ "<td colspan=\"3\">$extra</td>\n" .
+ "</tr>\n";
+ }
+ print "</table>\n";
+}
+
+## ----------------------------------------------------------------------
+## functions printing large fragments, format as one of arguments
+
sub git_diff_print {
my $from = shift;
my $from_name = shift;
@@ -610,7 +1528,7 @@ sub git_diff_print {
if (defined $from) {
$from_tmp = "$git_temp/gitweb_" . $$ . "_from";
open my $fd2, "> $from_tmp";
- open my $fd, "-|", "$GIT cat-file blob $from";
+ open my $fd, "-|", $GIT, "cat-file", "blob", $from;
my @file = <$fd>;
print $fd2 @file;
close $fd2;
@@ -621,7 +1539,7 @@ sub git_diff_print {
if (defined $to) {
$to_tmp = "$git_temp/gitweb_" . $$ . "_to";
open my $fd2, "> $to_tmp";
- open my $fd, "-|", "$GIT cat-file blob $to";
+ open my $fd, "-|", $GIT, "cat-file", "blob", $to;
my @file = <$fd>;
print $fd2 @file;
close $fd2;
@@ -635,7 +1553,7 @@ sub git_diff_print {
$/ = "\n";
} else {
while (my $line = <$fd>) {
- chomp($line);
+ chomp $line;
my $char = substr($line, 0, 1);
my $diff_class = "";
if ($char eq '+') {
@@ -648,12 +1566,7 @@ sub git_diff_print {
# skip errors
next;
}
- while ((my $pos = index($line, "\t")) != -1) {
- if (my $count = (8 - (($pos-1) % 8))) {
- my $spaces = ' ' x $count;
- $line =~ s/\t/$spaces/;
- }
- }
+ $line = untabify($line);
print "<div class=\"diff$diff_class\">" . esc_html($line) . "</div>\n";
}
}
@@ -667,210 +1580,35 @@ sub git_diff_print {
}
}
-sub mode_str {
- my $mode = oct shift;
-
- if (S_ISDIR($mode & S_IFMT)) {
- return 'drwxr-xr-x';
- } elsif (S_ISLNK($mode)) {
- return 'lrwxrwxrwx';
- } elsif (S_ISREG($mode)) {
- # git cares only about the executable bit
- if ($mode & S_IXUSR) {
- return '-rwxr-xr-x';
- } else {
- return '-rw-r--r--';
- };
- } else {
- return '----------';
- }
-}
-
-sub chop_str {
- my $str = shift;
- my $len = shift;
- my $add_len = shift || 10;
-
- # allow only $len chars, but don't cut a word if it would fit in $add_len
- # if it doesn't fit, cut it if it's still longer than the dots we would add
- $str =~ m/^(.{0,$len}[^ \/\-_:\.@]{0,$add_len})(.*)/;
- my $body = $1;
- my $tail = $2;
- if (length($tail) > 4) {
- $tail = " ...";
- }
- return "$body$tail";
-}
-
-sub file_type {
- my $mode = oct shift;
-
- if (S_ISDIR($mode & S_IFMT)) {
- return "directory";
- } elsif (S_ISLNK($mode)) {
- return "symlink";
- } elsif (S_ISREG($mode)) {
- return "file";
- } else {
- return "unknown";
- }
-}
-
-sub format_log_line_html {
- my $line = shift;
-
- $line = esc_html($line);
- $line =~ s/ /&nbsp;/g;
- if ($line =~ m/([0-9a-fA-F]{40})/) {
- my $hash_text = $1;
- if (git_get_type($hash_text) eq "commit") {
- my $link = $cgi->a({-class => "text", -href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_text")}, $hash_text);
- $line =~ s/$hash_text/$link/;
- }
- }
- return $line;
-}
-
-sub date_str {
- my $epoch = shift;
- my $tz = shift || "-0000";
-
- my %date;
- my @months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
- my @days = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
- my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($epoch);
- $date{'hour'} = $hour;
- $date{'minute'} = $min;
- $date{'mday'} = $mday;
- $date{'day'} = $days[$wday];
- $date{'month'} = $months[$mon];
- $date{'rfc2822'} = sprintf "%s, %d %s %4d %02d:%02d:%02d +0000", $days[$wday], $mday, $months[$mon], 1900+$year, $hour ,$min, $sec;
- $date{'mday-time'} = sprintf "%d %s %02d:%02d", $mday, $months[$mon], $hour ,$min;
-
- $tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/;
- my $local = $epoch + ((int $1 + ($2/60)) * 3600);
- ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($local);
- $date{'hour_local'} = $hour;
- $date{'minute_local'} = $min;
- $date{'tz_local'} = $tz;
- return %date;
-}
-
-# git-logo (cached in browser for one day)
-sub git_logo {
- binmode STDOUT, ':raw';
- print $cgi->header(-type => 'image/png', -expires => '+1d');
- # cat git-logo.png | hexdump -e '16/1 " %02x" "\n"' | sed 's/ /\\x/g'
- print "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" .
- "\x00\x00\x00\x48\x00\x00\x00\x1b\x04\x03\x00\x00\x00\x2d\xd9\xd4" .
- "\x2d\x00\x00\x00\x18\x50\x4c\x54\x45\xff\xff\xff\x60\x60\x5d\xb0" .
- "\xaf\xaa\x00\x80\x00\xce\xcd\xc7\xc0\x00\x00\xe8\xe8\xe6\xf7\xf7" .
- "\xf6\x95\x0c\xa7\x47\x00\x00\x00\x73\x49\x44\x41\x54\x28\xcf\x63" .
- "\x48\x67\x20\x04\x4a\x5c\x18\x0a\x08\x2a\x62\x53\x61\x20\x02\x08" .
- "\x0d\x69\x45\xac\xa1\xa1\x01\x30\x0c\x93\x60\x36\x26\x52\x91\xb1" .
- "\x01\x11\xd6\xe1\x55\x64\x6c\x6c\xcc\x6c\x6c\x0c\xa2\x0c\x70\x2a" .
- "\x62\x06\x2a\xc1\x62\x1d\xb3\x01\x02\x53\xa4\x08\xe8\x00\x03\x18" .
- "\x26\x56\x11\xd4\xe1\x20\x97\x1b\xe0\xb4\x0e\x35\x24\x71\x29\x82" .
- "\x99\x30\xb8\x93\x0a\x11\xb9\x45\x88\xc1\x8d\xa0\xa2\x44\x21\x06" .
- "\x27\x41\x82\x40\x85\xc1\x45\x89\x20\x70\x01\x00\xa4\x3d\x21\xc5" .
- "\x12\x1c\x9a\xfe\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82";
-}
-
-sub get_file_owner {
- my $path = shift;
-
- my ($dev, $ino, $mode, $nlink, $st_uid, $st_gid, $rdev, $size) = stat($path);
- my ($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell) = getpwuid($st_uid);
- if (!defined $gcos) {
- return undef;
- }
- my $owner = $gcos;
- $owner =~ s/[,;].*$//;
- return decode("utf8", $owner, Encode::FB_DEFAULT);
-}
-
-sub git_read_projects {
- my @list;
-
- if (-d $projects_list) {
- # search in directory
- my $dir = $projects_list;
- opendir my ($dh), $dir or return undef;
- while (my $dir = readdir($dh)) {
- if (-e "$projectroot/$dir/HEAD") {
- my $pr = {
- path => $dir,
- };
- push @list, $pr
- }
- }
- closedir($dh);
- } elsif (-f $projects_list) {
- # read from file(url-encoded):
- # 'git%2Fgit.git Linus+Torvalds'
- # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
- # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman'
- open my ($fd), $projects_list or return undef;
- while (my $line = <$fd>) {
- chomp $line;
- my ($path, $owner) = split ' ', $line;
- $path = unescape($path);
- $owner = unescape($owner);
- if (!defined $path) {
- next;
- }
- if (-e "$projectroot/$path/HEAD") {
- my $pr = {
- path => $path,
- owner => decode("utf8", $owner, Encode::FB_DEFAULT),
- };
- push @list, $pr
- }
- }
- close $fd;
- }
- @list = sort {$a->{'path'} cmp $b->{'path'}} @list;
- return @list;
-}
-
-sub git_get_project_config {
- my $key = shift;
-
- return unless ($key);
- $key =~ s/^gitweb\.//;
- return if ($key =~ m/\W/);
- my $val = qx($GIT repo-config --get gitweb.$key);
- return ($val);
-}
+## ======================================================================
+## ======================================================================
+## actions
-sub git_get_project_config_bool {
- my $val = git_get_project_config (@_);
- if ($val and $val =~ m/true|yes|on/) {
- return (1);
+sub git_project_list {
+ my $order = $cgi->param('o');
+ if (defined $order && $order !~ m/project|descr|owner|age/) {
+ die_error(undef, "Unknown order parameter");
}
- return; # implicit false
-}
-sub git_project_list {
- my @list = git_read_projects();
+ my @list = git_get_projects_list();
my @projects;
if (!@list) {
- die_error(undef, "No project found.");
+ die_error(undef, "No projects found");
}
foreach my $pr (@list) {
- my $head = git_read_head($pr->{'path'});
+ my $head = git_get_head_hash($pr->{'path'});
if (!defined $head) {
next;
}
$ENV{'GIT_DIR'} = "$projectroot/$pr->{'path'}";
- my %co = git_read_commit($head);
+ my %co = parse_commit($head);
if (!%co) {
next;
}
$pr->{'commit'} = \%co;
if (!defined $pr->{'descr'}) {
- my $descr = git_read_description($pr->{'path'}) || "";
+ my $descr = git_get_project_description($pr->{'path'}) || "";
$pr->{'descr'} = chop_str($descr, 25, 5);
}
if (!defined $pr->{'owner'}) {
@@ -878,6 +1616,7 @@ sub git_project_list {
}
push @projects, $pr;
}
+
git_header_html();
if (-f $home_text) {
print "<div class=\"index_include\">\n";
@@ -888,29 +1627,42 @@ sub git_project_list {
}
print "<table class=\"project_list\">\n" .
"<tr>\n";
- if (!defined($order) || (defined($order) && ($order eq "project"))) {
+ $order ||= "project";
+ if ($order eq "project") {
@projects = sort {$a->{'path'} cmp $b->{'path'}} @projects;
print "<th>Project</th>\n";
} else {
- print "<th>" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=project")}, "Project") . "</th>\n";
+ print "<th>" .
+ $cgi->a({-href => "$my_uri?" . esc_param("o=project"),
+ -class => "header"}, "Project") .
+ "</th>\n";
}
- if (defined($order) && ($order eq "descr")) {
+ if ($order eq "descr") {
@projects = sort {$a->{'descr'} cmp $b->{'descr'}} @projects;
print "<th>Description</th>\n";
} else {
- print "<th>" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=descr")}, "Description") . "</th>\n";
+ print "<th>" .
+ $cgi->a({-href => "$my_uri?" . esc_param("o=descr"),
+ -class => "header"}, "Description") .
+ "</th>\n";
}
- if (defined($order) && ($order eq "owner")) {
+ if ($order eq "owner") {
@projects = sort {$a->{'owner'} cmp $b->{'owner'}} @projects;
print "<th>Owner</th>\n";
} else {
- print "<th>" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=owner")}, "Owner") . "</th>\n";
+ print "<th>" .
+ $cgi->a({-href => "$my_uri?" . esc_param("o=owner"),
+ -class => "header"}, "Owner") .
+ "</th>\n";
}
- if (defined($order) && ($order eq "age")) {
+ if ($order eq "age") {
@projects = sort {$a->{'commit'}{'age'} <=> $b->{'commit'}{'age'}} @projects;
print "<th>Last Change</th>\n";
} else {
- print "<th>" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=age")}, "Last Change") . "</th>\n";
+ print "<th>" .
+ $cgi->a({-href => "$my_uri?" . esc_param("o=age"),
+ -class => "header"}, "Last Change") .
+ "</th>\n";
}
print "<th></th>\n" .
"</tr>\n";
@@ -922,14 +1674,16 @@ sub git_project_list {
print "<tr class=\"light\">\n";
}
$alternate ^= 1;
- print "<td>" . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary"), -class => "list"}, esc_html($pr->{'path'})) . "</td>\n" .
- "<td>$pr->{'descr'}</td>\n" .
+ print "<td>" . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary"),
+ -class => "list"}, esc_html($pr->{'path'})) . "</td>\n" .
+ "<td>" . esc_html($pr->{'descr'}) . "</td>\n" .
"<td><i>" . chop_str($pr->{'owner'}, 15) . "</i></td>\n";
- print "<td class=\"". age_class($pr->{'commit'}{'age'}) . "\">" . $pr->{'commit'}{'age_string'} . "</td>\n" .
+ print "<td class=\"". age_class($pr->{'commit'}{'age'}) . "\">" .
+ $pr->{'commit'}{'age_string'} . "</td>\n" .
"<td class=\"link\">" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary")}, "summary") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=shortlog")}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=log")}, "log") .
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary")}, "summary") . " | " .
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=shortlog")}, "shortlog") . " | " .
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=log")}, "log") .
"</td>\n" .
"</tr>\n";
}
@@ -937,307 +1691,75 @@ sub git_project_list {
git_footer_html();
}
-sub read_info_ref {
- my $type = shift || "";
- my %refs;
- # 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11
- # c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11^{}
- open my $fd, "$projectroot/$project/info/refs" or return;
- while (my $line = <$fd>) {
- chomp($line);
- if ($line =~ m/^([0-9a-fA-F]{40})\t.*$type\/([^\^]+)/) {
- if (defined $refs{$1}) {
- $refs{$1} .= " / $2";
- } else {
- $refs{$1} = $2;
- }
- }
- }
- close $fd or return;
- return \%refs;
-}
-
-sub git_read_refs {
- my $ref_dir = shift;
- my @reflist;
-
- my @refs;
- opendir my $dh, "$projectroot/$project/$ref_dir";
- while (my $dir = readdir($dh)) {
- if ($dir =~ m/^\./) {
- next;
- }
- if (-d "$projectroot/$project/$ref_dir/$dir") {
- opendir my $dh2, "$projectroot/$project/$ref_dir/$dir";
- my @subdirs = grep !m/^\./, readdir $dh2;
- closedir($dh2);
- foreach my $subdir (@subdirs) {
- push @refs, "$dir/$subdir"
- }
- next;
- }
- push @refs, $dir;
- }
- closedir($dh);
- foreach my $ref_file (@refs) {
- my $ref_id = git_read_hash("$project/$ref_dir/$ref_file");
- my $type = git_get_type($ref_id) || next;
- my %ref_item;
- my %co;
- $ref_item{'type'} = $type;
- $ref_item{'id'} = $ref_id;
- $ref_item{'epoch'} = 0;
- $ref_item{'age'} = "unknown";
- if ($type eq "tag") {
- my %tag = git_read_tag($ref_id);
- $ref_item{'comment'} = $tag{'comment'};
- if ($tag{'type'} eq "commit") {
- %co = git_read_commit($tag{'object'});
- $ref_item{'epoch'} = $co{'committer_epoch'};
- $ref_item{'age'} = $co{'age_string'};
- } elsif (defined($tag{'epoch'})) {
- my $age = time - $tag{'epoch'};
- $ref_item{'epoch'} = $tag{'epoch'};
- $ref_item{'age'} = age_string($age);
- }
- $ref_item{'reftype'} = $tag{'type'};
- $ref_item{'name'} = $tag{'name'};
- $ref_item{'refid'} = $tag{'object'};
- } elsif ($type eq "commit"){
- %co = git_read_commit($ref_id);
- $ref_item{'reftype'} = "commit";
- $ref_item{'name'} = $ref_file;
- $ref_item{'title'} = $co{'title'};
- $ref_item{'refid'} = $ref_id;
- $ref_item{'epoch'} = $co{'committer_epoch'};
- $ref_item{'age'} = $co{'age_string'};
- }
-
- push @reflist, \%ref_item;
- }
- # sort tags by age
- @reflist = sort {$b->{'epoch'} <=> $a->{'epoch'}} @reflist;
- return \@reflist;
-}
-
sub git_summary {
- my $descr = git_read_description($project) || "none";
- my $head = git_read_head($project);
- my %co = git_read_commit($head);
- my %cd = date_str($co{'committer_epoch'}, $co{'committer_tz'});
+ my $descr = git_get_project_description($project) || "none";
+ my $head = git_get_head_hash($project);
+ my %co = parse_commit($head);
+ my %cd = parse_date($co{'committer_epoch'}, $co{'committer_tz'});
- my $owner;
- if (-f $projects_list) {
- open (my $fd , $projects_list);
- while (my $line = <$fd>) {
- chomp $line;
- my ($pr, $ow) = split ' ', $line;
- $pr = unescape($pr);
- $ow = unescape($ow);
- if ($pr eq $project) {
- $owner = decode("utf8", $ow, Encode::FB_DEFAULT);
- last;
- }
- }
- close $fd;
- }
- if (!defined $owner) {
- $owner = get_file_owner("$projectroot/$project");
- }
+ my $owner = git_get_project_owner($project);
- my $refs = read_info_ref();
+ my $refs = git_get_references();
git_header_html();
- print "<div class=\"page_nav\">\n" .
- "summary".
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$head")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$head")}, "commitdiff") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree")}, "tree") .
- "<br/><br/>\n" .
- "</div>\n";
+ git_print_page_nav('summary','', $head);
+
print "<div class=\"title\">&nbsp;</div>\n";
print "<table cellspacing=\"0\">\n" .
"<tr><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
"<tr><td>owner</td><td>$owner</td></tr>\n" .
- "<tr><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n" .
- "</table>\n";
- open my $fd, "-|", "$GIT rev-list --max-count=17 " . git_read_head($project) or die_error(undef, "Open failed.");
- my (@revlist) = map { chomp; $_ } <$fd>;
- close $fd;
- print "<div>\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog"), -class => "title"}, "shortlog") .
- "</div>\n";
- my $i = 16;
- print "<table cellspacing=\"0\">\n";
- my $alternate = 0;
- foreach my $commit (@revlist) {
- my %co = git_read_commit($commit);
- my %ad = date_str($co{'author_epoch'});
- if ($alternate) {
- print "<tr class=\"dark\">\n";
- } else {
- print "<tr class=\"light\">\n";
- }
- $alternate ^= 1;
- if ($i-- > 0) {
- my $ref = "";
- if (defined $refs->{$commit}) {
- $ref = " <span class=\"tag\">" . esc_html($refs->{$commit}) . "</span>";
- }
- print "<td><i>$co{'age_string'}</i></td>\n" .
- "<td><i>" . esc_html(chop_str($co{'author_name'}, 10)) . "</i></td>\n" .
- "<td>";
- if (length($co{'title_short'}) < length($co{'title'})) {
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list", -title => "$co{'title'}"},
- "<b>" . esc_html($co{'title_short'}) . "$ref</b>");
- } else {
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list"},
- "<b>" . esc_html($co{'title'}) . "$ref</b>");
- }
- print "</td>\n" .
- "<td class=\"link\">" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") .
- "</td>\n" .
- "</tr>";
- } else {
- print "<td>" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "...") . "</td>\n" .
- "</tr>";
- last;
- }
+ "<tr><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n";
+ # use per project git URL list in $projectroot/$project/cloneurl
+ # or make project git URL from git base URL and project name
+ my $url_tag = "URL";
+ my @url_list = git_get_project_url_list($project);
+ @url_list = map { "$_/$project" } @git_base_url_list unless @url_list;
+ foreach my $git_url (@url_list) {
+ next unless $git_url;
+ print "<tr><td>$url_tag</td><td>$git_url</td></tr>\n";
+ $url_tag = "";
}
- print "</table\n>";
+ print "</table>\n";
+
+ open my $fd, "-|", $GIT, "rev-list", "--max-count=17", git_get_head_hash($project)
+ or die_error(undef, "Open git-rev-list failed");
+ my @revlist = map { chomp; $_ } <$fd>;
+ close $fd;
+ git_print_header_div('shortlog');
+ git_shortlog_body(\@revlist, 0, 15, $refs,
+ $cgi->a({-href => href(action=>"shortlog")}, "..."));
- my $taglist = git_read_refs("refs/tags");
+ my $taglist = git_get_refs_list("refs/tags");
if (defined @$taglist) {
- print "<div>\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tags"), -class => "title"}, "tags") .
- "</div>\n";
- my $i = 16;
- print "<table cellspacing=\"0\">\n";
- my $alternate = 0;
- foreach my $entry (@$taglist) {
- my %tag = %$entry;
- my $comment_lines = $tag{'comment'};
- my $comment = shift @$comment_lines;
- if (defined($comment)) {
- $comment = chop_str($comment, 30, 5);
- }
- if ($alternate) {
- print "<tr class=\"dark\">\n";
- } else {
- print "<tr class=\"light\">\n";
- }
- $alternate ^= 1;
- if ($i-- > 0) {
- print "<td><i>$tag{'age'}</i></td>\n" .
- "<td>" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}"), -class => "list"},
- "<b>" . esc_html($tag{'name'}) . "</b>") .
- "</td>\n" .
- "<td>";
- if (defined($comment)) {
- print $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, esc_html($comment));
- }
- print "</td>\n" .
- "<td class=\"link\">";
- if ($tag{'type'} eq "tag") {
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag") . " | ";
- }
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'});
- if ($tag{'reftype'} eq "commit") {
- print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log");
- }
- print "</td>\n" .
- "</tr>";
- } else {
- print "<td>" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tags")}, "...") . "</td>\n" .
- "</tr>";
- last;
- }
- }
- print "</table\n>";
+ git_print_header_div('tags');
+ git_tags_body($taglist, 0, 15,
+ $cgi->a({-href => href(action=>"tags")}, "..."));
}
- my $headlist = git_read_refs("refs/heads");
+ my $headlist = git_get_refs_list("refs/heads");
if (defined @$headlist) {
- print "<div>\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=heads"), -class => "title"}, "heads") .
- "</div>\n";
- my $i = 16;
- print "<table cellspacing=\"0\">\n";
- my $alternate = 0;
- foreach my $entry (@$headlist) {
- my %tag = %$entry;
- if ($alternate) {
- print "<tr class=\"dark\">\n";
- } else {
- print "<tr class=\"light\">\n";
- }
- $alternate ^= 1;
- if ($i-- > 0) {
- print "<td><i>$tag{'age'}</i></td>\n" .
- "<td>" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}"), -class => "list"},
- "<b>" . esc_html($tag{'name'}) . "</b>") .
- "</td>\n" .
- "<td class=\"link\">" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'name'}")}, "log") .
- "</td>\n" .
- "</tr>";
- } else {
- print "<td>" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=heads")}, "...") . "</td>\n" .
- "</tr>";
- last;
- }
- }
- print "</table\n>";
+ git_print_header_div('heads');
+ git_heads_body($headlist, $head, 0, 15,
+ $cgi->a({-href => href(action=>"heads")}, "..."));
}
- git_footer_html();
-}
-sub git_print_page_path {
- my $name = shift;
- my $type = shift;
-
- if (!defined $name) {
- print "<div class=\"page_path\"><b>/</b></div>\n";
- } elsif ($type =~ "blob") {
- print "<div class=\"page_path\"><b>" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;f=$file_name")}, esc_html($name)) . "</b><br/></div>\n";
- } else {
- print "<div class=\"page_path\"><b>" . esc_html($name) . "</b><br/></div>\n";
- }
+ git_footer_html();
}
sub git_tag {
- my $head = git_read_head($project);
+ my $head = git_get_head_hash($project);
git_header_html();
- print "<div class=\"page_nav\">\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$head")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$head")}, "commitdiff") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;hb=$head")}, "tree") . "<br/>\n" .
- "<br/>\n" .
- "</div>\n";
- my %tag = git_read_tag($hash);
- print "<div>\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash"), -class => "title"}, esc_html($tag{'name'})) . "\n" .
- "</div>\n";
+ git_print_page_nav('','', $head,undef,$head);
+ my %tag = parse_tag($hash);
+ git_print_header_div('commit', esc_html($tag{'name'}), $hash);
print "<div class=\"title_text\">\n" .
"<table cellspacing=\"0\">\n" .
"<tr>\n" .
"<td>object</td>\n" .
- "<td>" . $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=$tag{'type'};h=$tag{'object'}")}, $tag{'object'}) . "</td>\n" .
- "<td class=\"link\">" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'type'};h=$tag{'object'}")}, $tag{'type'}) . "</td>\n" .
+ "<td>" . $cgi->a({-class => "list", -href => href(action=>$tag{'type'}, hash=>$tag{'object'})}, $tag{'object'}) . "</td>\n" .
+ "<td class=\"link\">" . $cgi->a({-href => href(action=>$tag{'type'}, hash=>$tag{'object'})}, $tag{'type'}) . "</td>\n" .
"</tr>\n";
if (defined($tag{'author'})) {
- my %ad = date_str($tag{'epoch'}, $tag{'tz'});
+ my %ad = parse_date($tag{'epoch'}, $tag{'tz'});
print "<tr><td>author</td><td>" . esc_html($tag{'author'}) . "</td></tr>\n";
print "<tr><td></td><td>" . $ad{'rfc2822'} . sprintf(" (%02d:%02d %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'}) . "</td></tr>\n";
}
@@ -1255,11 +1777,11 @@ sub git_tag {
sub git_blame2 {
my $fd;
my $ftype;
- die_error(undef, "Permission denied.") if (!git_get_project_config_bool ('blame'));
+ die_error(undef, "Permission denied") if (!git_get_project_config_bool ('blame'));
die_error('404 Not Found', "File name not defined") if (!$file_name);
- $hash_base ||= git_read_head($project);
- die_error(undef, "Reading commit failed") unless ($hash_base);
- my %co = git_read_commit($hash_base)
+ $hash_base ||= git_get_head_hash($project);
+ die_error(undef, "Couldn't find base commit") unless ($hash_base);
+ my %co = parse_commit($hash_base)
or die_error(undef, "Reading commit failed");
if (!defined $hash) {
$hash = git_get_hash_by_path($hash_base, $file_name, "blob")
@@ -1267,26 +1789,18 @@ sub git_blame2 {
}
$ftype = git_get_type($hash);
if ($ftype !~ "blob") {
- die_error("400 Bad Request", "object is not a blob");
+ die_error("400 Bad Request", "Object is not a blob");
}
open ($fd, "-|", $GIT, "blame", '-l', $file_name, $hash_base)
- or die_error(undef, "Open failed");
+ or die_error(undef, "Open git-blame failed");
git_header_html();
- print "<div class=\"page_nav\">\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") . "<br/>\n";
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;f=$file_name")}, "head") . "<br/>\n";
- print "</div>\n".
- "<div>" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) .
- "</div>\n";
+ my $formats_nav =
+ $cgi->a({-href => href(action=>"blobl", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, "blob") .
+ " | " . $cgi->a({-href => href(action=>"blame", file_name=>$file_name)}, "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);
- my @rev_color = (qw(light dark));
+ my @rev_color = (qw(light2 dark2));
my $num_colors = scalar(@rev_color);
my $current_color = 0;
my $last_rev;
@@ -1308,7 +1822,7 @@ sub git_blame2 {
}
print "<tr class=\"$rev_color[$current_color]\">\n";
print "<td class=\"sha1\">" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$full_rev;f=$file_name")}, esc_html($rev)) . "</td>\n";
+ $cgi->a({-href => href(action=>"commit", hash=>$full_rev, file_name=>$file_name)}, esc_html($rev)) . "</td>\n";
print "<td class=\"linenr\"><a id=\"l$lineno\" href=\"#l$lineno\" class=\"linenr\">" . esc_html($lineno) . "</a></td>\n";
print "<td class=\"pre\">" . esc_html($data) . "</td>\n";
print "</tr>\n";
@@ -1321,33 +1835,25 @@ sub git_blame2 {
sub git_blame {
my $fd;
- die_error('403 Permission denied', "Permission denied.") if (!git_get_project_config_bool ('blame'));
- die_error('404 Not Found', "What file will it be, master?") if (!$file_name);
- $hash_base ||= git_read_head($project);
- die_error(undef, "Reading commit failed.") unless ($hash_base);
- my %co = git_read_commit($hash_base)
- or die_error(undef, "Reading commit failed.");
+ die_error('403 Permission denied', "Permission denied") if (!git_get_project_config_bool ('blame'));
+ die_error('404 Not Found', "File name not defined") if (!$file_name);
+ $hash_base ||= git_get_head_hash($project);
+ die_error(undef, "Couldn't find base commit") unless ($hash_base);
+ my %co = parse_commit($hash_base)
+ or die_error(undef, "Reading commit failed");
if (!defined $hash) {
$hash = git_get_hash_by_path($hash_base, $file_name, "blob")
- or die_error(undef, "Error lookup file.");
+ or die_error(undef, "Error lookup file");
}
open ($fd, "-|", $GIT, "annotate", '-l', '-t', '-r', $file_name, $hash_base)
- or die_error(undef, "Open failed.");
+ or die_error(undef, "Open git-annotate failed");
git_header_html();
- print "<div class=\"page_nav\">\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") . "<br/>\n";
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;f=$file_name")}, "head") . "<br/>\n";
- print "</div>\n".
- "<div>" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) .
- "</div>\n";
- git_print_page_path($file_name);
+ my $formats_nav =
+ $cgi->a({-href => href(action=>"blobl", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, "blob") .
+ " | " . $cgi->a({-href => href(action=>"blame", file_name=>$file_name)}, "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');
print "<div class=\"page_body\">\n";
print <<HTML;
<table class="blame">
@@ -1376,7 +1882,7 @@ HTML
chomp $line;
$line_class_num = ($line_class_num + 1) % $line_class_len;
- if ($line =~ m/^([0-9a-fA-F]{40})\t\(\s*([^\t]+)\t(\d+) \+\d\d\d\d\t(\d+)\)(.*)$/) {
+ if ($line =~ m/^([0-9a-fA-F]{40})\t\(\s*([^\t]+)\t(\d+) [+-]\d\d\d\d\t(\d+)\)(.*)$/) {
$long_rev = $1;
$author = $2;
$time = $3;
@@ -1393,18 +1899,13 @@ HTML
$age_class = age_class($age);
$author = esc_html ($author);
$author =~ s/ /&nbsp;/g;
- # escape tabs
- while ((my $pos = index($data, "\t")) != -1) {
- if (my $count = (8 - ($pos % 8))) {
- my $spaces = ' ' x $count;
- $data =~ s/\t/$spaces/;
- }
- }
+
+ $data = untabify($data);
$data = esc_html ($data);
print <<HTML;
<tr class="$line_class[$line_class_num]">
- <td class="sha1"><a href="$my_uri?${\esc_param ("p=$project;a=commit;h=$long_rev")}" class="text">$short_rev..</a></td>
+ <td class="sha1"><a href="${\href (action=>"commit", hash=>$long_rev)}" class="text">$short_rev..</a></td>
<td class="$age_class">$age_str</td>
<td>$author</td>
<td class="linenr"><a id="$lineno" href="#$lineno" class="linenr">$lineno</a></td>
@@ -1419,211 +1920,46 @@ HTML
}
sub git_tags {
- my $head = git_read_head($project);
+ my $head = git_get_head_hash($project);
git_header_html();
- print "<div class=\"page_nav\">\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$head")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$head")}, "commitdiff") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;hb=$head")}, "tree") . "<br/>\n" .
- "<br/>\n" .
- "</div>\n";
- my $taglist = git_read_refs("refs/tags");
- print "<div>\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary"), -class => "title"}, "&nbsp;") .
- "</div>\n";
- print "<table cellspacing=\"0\">\n";
- my $alternate = 0;
+ git_print_page_nav('','', $head,undef,$head);
+ git_print_header_div('summary', $project);
+
+ my $taglist = git_get_refs_list("refs/tags");
if (defined @$taglist) {
- foreach my $entry (@$taglist) {
- my %tag = %$entry;
- my $comment_lines = $tag{'comment'};
- my $comment = shift @$comment_lines;
- if (defined($comment)) {
- $comment = chop_str($comment, 30, 5);
- }
- if ($alternate) {
- print "<tr class=\"dark\">\n";
- } else {
- print "<tr class=\"light\">\n";
- }
- $alternate ^= 1;
- print "<td><i>$tag{'age'}</i></td>\n" .
- "<td>" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}"), -class => "list"},
- "<b>" . esc_html($tag{'name'}) . "</b>") .
- "</td>\n" .
- "<td>";
- if (defined($comment)) {
- print $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, $comment);
- }
- print "</td>\n" .
- "<td class=\"link\">";
- if ($tag{'type'} eq "tag") {
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag") . " | ";
- }
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'});
- if ($tag{'reftype'} eq "commit") {
- print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log");
- }
- print "</td>\n" .
- "</tr>";
- }
+ git_tags_body($taglist);
}
- print "</table\n>";
git_footer_html();
}
sub git_heads {
- my $head = git_read_head($project);
+ my $head = git_get_head_hash($project);
git_header_html();
- print "<div class=\"page_nav\">\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$head")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$head")}, "commitdiff") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;hb=$head")}, "tree") . "<br/>\n" .
- "<br/>\n" .
- "</div>\n";
- my $taglist = git_read_refs("refs/heads");
- print "<div>\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary"), -class => "title"}, "&nbsp;") .
- "</div>\n";
- print "<table cellspacing=\"0\">\n";
- my $alternate = 0;
+ git_print_page_nav('','', $head,undef,$head);
+ git_print_header_div('summary', $project);
+
+ my $taglist = git_get_refs_list("refs/heads");
if (defined @$taglist) {
- foreach my $entry (@$taglist) {
- my %tag = %$entry;
- if ($alternate) {
- print "<tr class=\"dark\">\n";
- } else {
- print "<tr class=\"light\">\n";
- }
- $alternate ^= 1;
- print "<td><i>$tag{'age'}</i></td>\n" .
- "<td>" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}"), -class => "list"}, "<b>" . esc_html($tag{'name'}) . "</b>") .
- "</td>\n" .
- "<td class=\"link\">" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'name'}")}, "log") .
- "</td>\n" .
- "</tr>";
- }
+ git_heads_body($taglist, $head);
}
- print "</table\n>";
git_footer_html();
}
-sub git_get_hash_by_path {
- my $base = shift;
- my $path = shift || return undef;
-
- my $tree = $base;
- my @parts = split '/', $path;
- while (my $part = shift @parts) {
- open my $fd, "-|", "$GIT ls-tree $tree" or die_error(undef, "Open git-ls-tree failed.");
- my (@entries) = map { chomp; $_ } <$fd>;
- close $fd or return undef;
- foreach my $line (@entries) {
- #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
- $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/;
- my $t_mode = $1;
- my $t_type = $2;
- my $t_hash = $3;
- my $t_name = validate_input(unquote($4));
- if ($t_name eq $part) {
- if (!(@parts)) {
- return $t_hash;
- }
- if ($t_type eq "tree") {
- $tree = $t_hash;
- }
- last;
- }
- }
- }
-}
-
-sub mimetype_guess_file {
- my $filename = shift;
- my $mimemap = shift;
- -r $mimemap or return undef;
-
- my %mimemap;
- open(MIME, $mimemap) or return undef;
- while (<MIME>) {
- my ($mime, $exts) = split(/\t+/);
- my @exts = split(/\s+/, $exts);
- foreach my $ext (@exts) {
- $mimemap{$ext} = $mime;
- }
- }
- close(MIME);
-
- $filename =~ /\.(.*?)$/;
- return $mimemap{$1};
-}
-
-sub mimetype_guess {
- my $filename = shift;
- my $mime;
- $filename =~ /\./ or return undef;
-
- if ($mimetypes_file) {
- my $file = $mimetypes_file;
- #$file =~ m#^/# or $file = "$projectroot/$path/$file";
- $mime = mimetype_guess_file($filename, $file);
- }
- $mime ||= mimetype_guess_file($filename, '/etc/mime.types');
- return $mime;
-}
-
-sub git_blob_plain_mimetype {
- my $fd = shift;
- my $filename = shift;
-
- if ($filename) {
- my $mime = mimetype_guess($filename);
- $mime and return $mime;
- }
-
- # just in case
- return $default_blob_plain_mimetype unless $fd;
-
- if (-T $fd) {
- return 'text/plain' .
- ($default_text_plain_charset ? '; charset='.$default_text_plain_charset : '');
- } elsif (! $filename) {
- return 'application/octet-stream';
- } elsif ($filename =~ m/\.png$/i) {
- return 'image/png';
- } elsif ($filename =~ m/\.gif$/i) {
- return 'image/gif';
- } elsif ($filename =~ m/\.jpe?g$/i) {
- return 'image/jpeg';
- } else {
- return 'application/octet-stream';
- }
-}
-
sub git_blob_plain {
if (!defined $hash) {
- if (defined $file_name) {
- my $base = $hash_base || git_read_head($project);
- $hash = git_get_hash_by_path($base, $file_name, "blob") || die_error(undef, "Error lookup file.");
- } else {
- die_error(undef, "No file name defined.");
- }
- }
+ if (defined $file_name) {
+ my $base = $hash_base || git_get_head_hash($project);
+ $hash = git_get_hash_by_path($base, $file_name, "blob")
+ or die_error(undef, "Error lookup file");
+ } else {
+ die_error(undef, "No file name defined");
+ }
+ }
my $type = shift;
- open my $fd, "-|", "$GIT cat-file blob $hash" or die_error("Couldn't cat $file_name, $hash");
+ open my $fd, "-|", $GIT, "cat-file", "blob", $hash
+ or die_error(undef, "Couldn't cat $file_name, $hash");
- $type ||= git_blob_plain_mimetype($fd, $file_name);
+ $type ||= blob_mimetype($fd, $file_name);
# save as filename, even when no $file_name is given
my $save_as = "$hash";
@@ -1644,42 +1980,37 @@ sub git_blob_plain {
sub git_blob {
if (!defined $hash) {
- if (defined $file_name) {
- my $base = $hash_base || git_read_head($project);
- $hash = git_get_hash_by_path($base, $file_name, "blob") || die_error(undef, "Error lookup file.");
- } else {
- die_error(undef, "No file name defined.");
- }
- }
+ if (defined $file_name) {
+ my $base = $hash_base || git_get_head_hash($project);
+ $hash = git_get_hash_by_path($base, $file_name, "blob")
+ or die_error(undef, "Error lookup file");
+ } else {
+ die_error(undef, "No file name defined");
+ }
+ }
my $have_blame = git_get_project_config_bool ('blame');
- open my $fd, "-|", "$GIT cat-file blob $hash" or die_error(undef, "Open failed.");
- my $mimetype = git_blob_plain_mimetype($fd, $file_name);
+ open my $fd, "-|", $GIT, "cat-file", "blob", $hash
+ or die_error(undef, "Couldn't cat $file_name, $hash");
+ my $mimetype = blob_mimetype($fd, $file_name);
if ($mimetype !~ m/^text\//) {
close $fd;
return git_blob_plain($mimetype);
}
git_header_html();
- if (defined $hash_base && (my %co = git_read_commit($hash_base))) {
- print "<div class=\"page_nav\">\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") . "<br/>\n";
+ my $formats_nav = '';
+ if (defined $hash_base && (my %co = parse_commit($hash_base))) {
if (defined $file_name) {
if ($have_blame) {
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;h=$hash;hb=$hash_base;f=$file_name")}, "blame") . " | ";
+ $formats_nav .= $cgi->a({-href => href(action=>"blame", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, "blame") . " | ";
}
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$hash;f=$file_name")}, "plain") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;hb=HEAD;f=$file_name")}, "head") . "<br/>\n";
+ $formats_nav .=
+ $cgi->a({-href => href(action=>"blob_plain", hash=>$hash, file_name=>$file_name)}, "plain") .
+ " | " . $cgi->a({-href => href(action=>"blob", hash_base=>"HEAD", file_name=>$file_name)}, "head");
} else {
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$hash")}, "plain") . "<br/>\n";
+ $formats_nav .= $cgi->a({-href => href(action=>"blob_plain", hash=>$hash)}, "plain");
}
- print "</div>\n".
- "<div>" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) .
- "</div>\n";
+ git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
+ git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
} else {
print "<div class=\"page_nav\">\n" .
"<br/><br/></div>\n" .
@@ -1691,12 +2022,7 @@ sub git_blob {
while (my $line = <$fd>) {
chomp $line;
$nr++;
- while ((my $pos = index($line, "\t")) != -1) {
- if (my $count = (8 - ($pos % 8))) {
- my $spaces = ' ' x $count;
- $line =~ s/\t/$spaces/;
- }
- }
+ $line = untabify($line);
printf "<div class=\"pre\"><a id=\"l%i\" href=\"#l%i\" class=\"linenr\">%4i</a> %s</div>\n", $nr, $nr, $nr, esc_html($line);
}
close $fd or print "Reading blob failed.\n";
@@ -1706,7 +2032,7 @@ sub git_blob {
sub git_tree {
if (!defined $hash) {
- $hash = git_read_head($project);
+ $hash = git_get_head_hash($project);
if (defined $file_name) {
my $base = $hash_base || $hash;
$hash = git_get_hash_by_path($base, $file_name, "tree");
@@ -1716,33 +2042,22 @@ sub git_tree {
}
}
$/ = "\0";
- open my $fd, "-|", "$GIT ls-tree -z $hash" or die_error(undef, "Open git-ls-tree failed.");
- chomp (my (@entries) = <$fd>);
- close $fd or die_error(undef, "Reading tree failed.");
+ open my $fd, "-|", $GIT, "ls-tree", '-z', $hash
+ or die_error(undef, "Open git-ls-tree failed");
+ my @entries = map { chomp; $_ } <$fd>;
+ close $fd or die_error(undef, "Reading tree failed");
$/ = "\n";
- my $refs = read_info_ref();
- my $ref = "";
- if (defined $refs->{$hash_base}) {
- $ref = " <span class=\"tag\">" . esc_html($refs->{$hash_base}) . "</span>";
- }
+ my $refs = git_get_references();
+ my $ref = format_ref_marker($refs, $hash_base);
git_header_html();
my $base_key = "";
my $base = "";
- if (defined $hash_base && (my %co = git_read_commit($hash_base))) {
+ my $have_blame = git_get_project_config_bool ('blame');
+ if (defined $hash_base && (my %co = parse_commit($hash_base))) {
$base_key = ";hb=$hash_base";
- print "<div class=\"page_nav\">\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash_base")}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash_base")}, "log") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") .
- " | tree" .
- "<br/><br/>\n" .
- "</div>\n";
- print "<div>\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'}) . $ref) . "\n" .
- "</div>\n";
+ git_print_page_nav('tree','', $hash_base);
+ git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base);
} else {
print "<div class=\"page_nav\">\n";
print "<br/><br/></div>\n";
@@ -1751,7 +2066,7 @@ sub git_tree {
if (defined $file_name) {
$base = esc_html("$file_name/");
}
- git_print_page_path($file_name);
+ git_print_page_path($file_name, 'tree');
print "<div class=\"page_body\">\n";
print "<table cellspacing=\"0\">\n";
my $alternate = 0;
@@ -1774,9 +2089,11 @@ sub git_tree {
$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$t_hash$base_key;f=$base$t_name"), -class => "list"}, esc_html($t_name)) .
"</td>\n" .
"<td class=\"link\">" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$t_hash$base_key;f=$base$t_name")}, "blob") .
-# " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;h=$t_hash$base_key;f=$base$t_name")}, "blame") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$t_hash;hb=$hash_base;f=$base$t_name")}, "history") .
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$t_hash$base_key;f=$base$t_name")}, "blob");
+ if ($have_blame) {
+ print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;h=$t_hash$base_key;f=$base$t_name")}, "blame");
+ }
+ print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$t_hash;hb=$hash_base;f=$base$t_name")}, "history") .
" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$t_hash;f=$base$t_name")}, "raw") .
"</td>\n";
} elsif ($t_type eq "tree") {
@@ -1795,163 +2112,47 @@ sub git_tree {
git_footer_html();
}
-sub git_rss {
- # http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
- open my $fd, "-|", "$GIT rev-list --max-count=150 " . git_read_head($project) or die_error(undef, "Open failed.");
- my (@revlist) = map { chomp; $_ } <$fd>;
- close $fd or die_error(undef, "Reading rev-list failed.");
- print $cgi->header(-type => 'text/xml', -charset => 'utf-8');
- print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".
- "<rss version=\"2.0\" xmlns:content=\"http://purl.org/rss/1.0/modules/content/\">\n";
- print "<channel>\n";
- print "<title>$project</title>\n".
- "<link>" . esc_html("$my_url?p=$project;a=summary") . "</link>\n".
- "<description>$project log</description>\n".
- "<language>en</language>\n";
-
- for (my $i = 0; $i <= $#revlist; $i++) {
- my $commit = $revlist[$i];
- my %co = git_read_commit($commit);
- # we read 150, we always show 30 and the ones more recent than 48 hours
- if (($i >= 20) && ((time - $co{'committer_epoch'}) > 48*60*60)) {
- last;
- }
- my %cd = date_str($co{'committer_epoch'});
- open $fd, "-|", "$GIT diff-tree -r $co{'parent'} $co{'id'}" or next;
- my @difftree = map { chomp; $_ } <$fd>;
- close $fd or next;
- print "<item>\n" .
- "<title>" .
- sprintf("%d %s %02d:%02d", $cd{'mday'}, $cd{'month'}, $cd{'hour'}, $cd{'minute'}) . " - " . esc_html($co{'title'}) .
- "</title>\n" .
- "<author>" . esc_html($co{'author'}) . "</author>\n" .
- "<pubDate>$cd{'rfc2822'}</pubDate>\n" .
- "<guid isPermaLink=\"true\">" . esc_html("$my_url?p=$project;a=commit;h=$commit") . "</guid>\n" .
- "<link>" . esc_html("$my_url?p=$project;a=commit;h=$commit") . "</link>\n" .
- "<description>" . esc_html($co{'title'}) . "</description>\n" .
- "<content:encoded>" .
- "<![CDATA[\n";
- my $comment = $co{'comment'};
- foreach my $line (@$comment) {
- $line = decode("utf8", $line, Encode::FB_DEFAULT);
- print "$line<br/>\n";
- }
- print "<br/>\n";
- foreach my $line (@difftree) {
- 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));
- $file = decode("utf8", $file, Encode::FB_DEFAULT);
- print "$file<br/>\n";
- }
- print "]]>\n" .
- "</content:encoded>\n" .
- "</item>\n";
- }
- print "</channel></rss>";
-}
-
-sub git_opml {
- my @list = git_read_projects();
-
- print $cgi->header(-type => 'text/xml', -charset => 'utf-8');
- print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".
- "<opml version=\"1.0\">\n".
- "<head>".
- " <title>$site_name Git OPML Export</title>\n".
- "</head>\n".
- "<body>\n".
- "<outline text=\"git RSS feeds\">\n";
-
- foreach my $pr (@list) {
- my %proj = %$pr;
- my $head = git_read_head($proj{'path'});
- if (!defined $head) {
- next;
- }
- $ENV{'GIT_DIR'} = "$projectroot/$proj{'path'}";
- my %co = git_read_commit($head);
- if (!%co) {
- next;
- }
-
- my $path = esc_html(chop_str($proj{'path'}, 25, 5));
- my $rss = "$my_url?p=$proj{'path'};a=rss";
- my $html = "$my_url?p=$proj{'path'};a=summary";
- print "<outline type=\"rss\" text=\"$path\" title=\"$path\" xmlUrl=\"$rss\" htmlUrl=\"$html\"/>\n";
- }
- print "</outline>\n".
- "</body>\n".
- "</opml>\n";
-}
-
sub git_log {
- my $head = git_read_head($project);
+ my $head = git_get_head_hash($project);
if (!defined $hash) {
$hash = $head;
}
if (!defined $page) {
$page = 0;
}
- my $refs = read_info_ref();
- git_header_html();
- print "<div class=\"page_nav\">\n";
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash")}, "shortlog") .
- " | log" .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash")}, "commitdiff") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$hash;hb=$hash")}, "tree") . "<br/>\n";
+ my $refs = git_get_references();
my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
- open my $fd, "-|", "$GIT rev-list $limit $hash" or die_error(undef, "Open failed.");
- my (@revlist) = map { chomp; $_ } <$fd>;
+ open my $fd, "-|", $GIT, "rev-list", $limit, $hash
+ or die_error(undef, "Open git-rev-list failed");
+ my @revlist = map { chomp; $_ } <$fd>;
close $fd;
- if ($hash ne $head || $page) {
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "HEAD");
- } else {
- print "HEAD";
- }
- if ($page > 0) {
- print " &sdot; " .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash;pg=" . ($page-1)), -accesskey => "p", -title => "Alt-p"}, "prev");
- } else {
- print " &sdot; prev";
- }
- if ($#revlist >= (100 * ($page+1)-1)) {
- print " &sdot; " .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash;pg=" . ($page+1)), -accesskey => "n", -title => "Alt-n"}, "next");
- } else {
- print " &sdot; next";
- }
- print "<br/>\n" .
- "</div>\n";
+ my $paging_nav = format_paging_nav('log', $hash, $head, $page, $#revlist);
+
+ git_header_html();
+ git_print_page_nav('log','', $hash,undef,undef, $paging_nav);
+
if (!@revlist) {
- print "<div>\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary"), -class => "title"}, "&nbsp;") .
- "</div>\n";
- my %co = git_read_commit($hash);
+ my %co = parse_commit($hash);
+
+ git_print_header_div('summary', $project);
print "<div class=\"page_body\"> Last change $co{'age_string'}.<br/><br/></div>\n";
}
for (my $i = ($page * 100); $i <= $#revlist; $i++) {
my $commit = $revlist[$i];
- my $ref = "";
- if (defined $refs->{$commit}) {
- $ref = " <span class=\"tag\">" . esc_html($refs->{$commit}) . "</span>";
- }
- my %co = git_read_commit($commit);
+ my $ref = format_ref_marker($refs, $commit);
+ my %co = parse_commit($commit);
next if !%co;
- my %ad = date_str($co{'author_epoch'});
- print "<div>\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "title"},
- "<span class=\"age\">$co{'age_string'}</span>" . esc_html($co{'title'}) . $ref) . "\n";
- print "</div>\n";
+ my %ad = parse_date($co{'author_epoch'});
+ git_print_header_div('commit',
+ "<span class=\"age\">$co{'age_string'}</span>" .
+ esc_html($co{'title'}) . $ref,
+ $commit);
print "<div class=\"title_text\">\n" .
"<div class=\"log_link\">\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") .
+ $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") .
+ " | " . $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") .
"<br/>\n" .
"</div>\n" .
"<i>" . esc_html($co{'author_name'}) . " [$ad{'rfc2822'}]</i><br/>\n" .
@@ -1982,59 +2183,43 @@ sub git_log {
}
sub git_commit {
- my %co = git_read_commit($hash);
+ my %co = parse_commit($hash);
if (!%co) {
- die_error(undef, "Unknown commit object.");
+ die_error(undef, "Unknown commit object");
}
- my %ad = date_str($co{'author_epoch'}, $co{'author_tz'});
- my %cd = date_str($co{'committer_epoch'}, $co{'committer_tz'});
+ my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'});
+ my %cd = parse_date($co{'committer_epoch'}, $co{'committer_tz'});
- my @difftree;
- my $root = "";
my $parent = $co{'parent'};
if (!defined $parent) {
- $root = " --root";
- $parent = "";
+ $parent = "--root";
}
- open my $fd, "-|", "$GIT diff-tree -r -M $root $parent $hash" or die_error(undef, "Open failed.");
- @difftree = map { chomp; $_ } <$fd>;
- close $fd or die_error(undef, "Reading diff-tree failed.");
+ open my $fd, "-|", $GIT, "diff-tree", '-r', '-M', $parent, $hash
+ or die_error(undef, "Open git-diff-tree failed");
+ my @difftree = map { chomp; $_ } <$fd>;
+ close $fd or die_error(undef, "Reading git-diff-tree failed");
# non-textual hash id's can be cached
my $expires;
if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
$expires = "+1d";
}
- my $refs = read_info_ref();
- my $ref = "";
- if (defined $refs->{$co{'id'}}) {
- $ref = " <span class=\"tag\">" . esc_html($refs->{$co{'id'}}) . "</span>";
- }
- git_header_html(undef, $expires);
- print "<div class=\"page_nav\">\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash")}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash")}, "log") .
- " | commit";
- if (defined $co{'parent'}) {
- print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash")}, "commitdiff");
- }
- print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") . "\n" .
- "<br/>\n";
+ my $refs = git_get_references();
+ my $ref = format_ref_marker($refs, $co{'id'});
+ my $formats_nav = '';
if (defined $file_name && defined $co{'parent'}) {
my $parent = $co{'parent'};
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;hb=$parent;f=$file_name")}, "blame") . "\n";
+ $formats_nav .= $cgi->a({-href => href(action=>"blame", hash_parent=>$parent, file_name=>$file_name)}, "blame");
}
- print "<br/></div>\n";
+ git_header_html(undef, $expires);
+ git_print_page_nav('commit', defined $co{'parent'} ? '' : 'commitdiff',
+ $hash, $co{'tree'}, $hash,
+ $formats_nav);
if (defined $co{'parent'}) {
- print "<div>\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash"), -class => "title"}, esc_html($co{'title'}) . $ref) . "\n" .
- "</div>\n";
+ git_print_header_div('commitdiff', esc_html($co{'title'}) . $ref, $hash);
} else {
- print "<div>\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash"), -class => "title"}, esc_html($co{'title'})) . "\n" .
- "</div>\n";
+ git_print_header_div('tree', esc_html($co{'title'}) . $ref, $co{'tree'}, $hash);
}
print "<div class=\"title_text\">\n" .
"<table cellspacing=\"0\">\n";
@@ -2054,19 +2239,19 @@ sub git_commit {
print "<tr>" .
"<td>tree</td>" .
"<td class=\"sha1\">" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash"), class => "list"}, $co{'tree'}) .
+ $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$hash), class => "list"}, $co{'tree'}) .
"</td>" .
- "<td class=\"link\">" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") .
+ "<td class=\"link\">" . $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$hash)}, "tree") .
"</td>" .
"</tr>\n";
my $parents = $co{'parents'};
foreach my $par (@$parents) {
print "<tr>" .
"<td>parent</td>" .
- "<td class=\"sha1\">" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$par"), class => "list"}, $par) . "</td>" .
+ "<td class=\"sha1\">" . $cgi->a({-href => href(action=>"commit", hash=>$par), class => "list"}, $par) . "</td>" .
"<td class=\"link\">" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$par")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash;hp=$par")}, "commitdiff") .
+ $cgi->a({-href => href(action=>"commit", hash=>$par)}, "commit") .
+ " | " . $cgi->a({-href => href(action=>"commitdiff", hash=>$hash, hash_parent=>$par)}, "commitdiff") .
"</td>" .
"</tr>\n";
}
@@ -2095,121 +2280,20 @@ sub git_commit {
}
}
print "</div>\n";
- print "<div class=\"list_head\">\n";
- if ($#difftree > 10) {
- print(($#difftree + 1) . " files changed:\n");
- }
- print "</div>\n";
- print "<table class=\"diff_tree\">\n";
- my $alternate = 0;
- foreach my $line (@difftree) {
- # ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M ls-files.c'
- # ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M rev-tree.c'
- 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 $from_mode = $1;
- my $to_mode = $2;
- my $from_id = $3;
- my $to_id = $4;
- my $status = $5;
- my $similarity = $6;
- my $file = validate_input(unquote($7));
- if ($alternate) {
- print "<tr class=\"dark\">\n";
- } else {
- print "<tr class=\"light\">\n";
- }
- $alternate ^= 1;
- if ($status eq "A") {
- my $mode_chng = "";
- if (S_ISREG(oct $to_mode)) {
- $mode_chng = sprintf(" with mode: %04o", (oct $to_mode) & 0777);
- }
- print "<td>" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file)) . "</td>\n" .
- "<td><span class=\"file_status new\">[new " . file_type($to_mode) . "$mode_chng]</span></td>\n" .
- "<td class=\"link\">" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, "blob") . "</td>\n";
- } elsif ($status eq "D") {
- print "<td>" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file)) . "</td>\n" .
- "<td><span class=\"file_status deleted\">[deleted " . file_type($from_mode). "]</span></td>\n" .
- "<td class=\"link\">" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, "blob") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash;f=$file")}, "history") .
- "</td>\n"
- } elsif ($status eq "M" || $status eq "T") {
- my $mode_chnge = "";
- if ($from_mode != $to_mode) {
- $mode_chnge = " <span class=\"file_status mode_chnge\">[changed";
- if (((oct $from_mode) & S_IFMT) != ((oct $to_mode) & S_IFMT)) {
- $mode_chnge .= " from " . file_type($from_mode) . " to " . file_type($to_mode);
- }
- if (((oct $from_mode) & 0777) != ((oct $to_mode) & 0777)) {
- if (S_ISREG($from_mode) && S_ISREG($to_mode)) {
- $mode_chnge .= sprintf(" mode: %04o->%04o", (oct $from_mode) & 0777, (oct $to_mode) & 0777);
- } elsif (S_ISREG($to_mode)) {
- $mode_chnge .= sprintf(" mode: %04o", (oct $to_mode) & 0777);
- }
- }
- $mode_chnge .= "]</span>\n";
- }
- print "<td>";
- if ($to_id ne $from_id) {
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$to_id;hp=$from_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file));
- } else {
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file));
- }
- print "</td>\n" .
- "<td>$mode_chnge</td>\n" .
- "<td class=\"link\">";
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, "blob");
- if ($to_id ne $from_id) {
- print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$to_id;hp=$from_id;hb=$hash;f=$file")}, "diff");
- }
- print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash;f=$file")}, "history") . "\n";
- print "</td>\n";
- } elsif ($status eq "R") {
- my ($from_file, $to_file) = split "\t", $file;
- my $mode_chng = "";
- if ($from_mode != $to_mode) {
- $mode_chng = sprintf(", mode: %04o", (oct $to_mode) & 0777);
- }
- print "<td>" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$to_file"), -class => "list"}, esc_html($to_file)) . "</td>\n" .
- "<td><span class=\"file_status moved\">[moved from " .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$from_file"), -class => "list"}, esc_html($from_file)) .
- " with " . (int $similarity) . "% similarity$mode_chng]</span></td>\n" .
- "<td class=\"link\">" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$to_file")}, "blob");
- if ($to_id ne $from_id) {
- print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$to_id;hp=$from_id;hb=$hash;f=$to_file")}, "diff");
- }
- print "</td>\n";
- }
- print "</tr>\n";
- }
- print "</table>\n";
+
+ git_difftree_body(\@difftree, $parent);
+
git_footer_html();
}
sub git_blobdiff {
mkdir($git_temp, 0700);
git_header_html();
- if (defined $hash_base && (my %co = git_read_commit($hash_base))) {
- print "<div class=\"page_nav\">\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") .
- "<br/>\n";
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff_plain;h=$hash;hp=$hash_parent")}, "plain") .
- "</div>\n";
- print "<div>\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . "\n" .
- "</div>\n";
+ if (defined $hash_base && (my %co = parse_commit($hash_base))) {
+ my $formats_nav =
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff_plain;h=$hash;hp=$hash_parent")}, "plain");
+ git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
+ git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
} else {
print "<div class=\"page_nav\">\n" .
"<br/><br/></div>\n" .
@@ -2235,40 +2319,30 @@ sub git_blobdiff_plain {
sub git_commitdiff {
mkdir($git_temp, 0700);
- my %co = git_read_commit($hash);
+ my %co = parse_commit($hash);
if (!%co) {
- die_error(undef, "Unknown commit object.");
+ die_error(undef, "Unknown commit object");
}
if (!defined $hash_parent) {
- $hash_parent = $co{'parent'};
+ $hash_parent = $co{'parent'} || '--root';
}
- open my $fd, "-|", "$GIT diff-tree -r $hash_parent $hash" or die_error(undef, "Open failed.");
- my (@difftree) = map { chomp; $_ } <$fd>;
- close $fd or die_error(undef, "Reading diff-tree failed.");
+ open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash
+ or die_error(undef, "Open git-diff-tree failed");
+ my @difftree = map { chomp; $_ } <$fd>;
+ close $fd or die_error(undef, "Reading git-diff-tree failed");
# non-textual hash id's can be cached
my $expires;
if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
$expires = "+1d";
}
- my $refs = read_info_ref();
- my $ref = "";
- if (defined $refs->{$co{'id'}}) {
- $ref = " <span class=\"tag\">" . esc_html($refs->{$co{'id'}}) . "</span>";
- }
+ my $refs = git_get_references();
+ my $ref = format_ref_marker($refs, $co{'id'});
+ my $formats_nav =
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff_plain;h=$hash;hp=$hash_parent")}, "plain");
git_header_html(undef, $expires);
- print "<div class=\"page_nav\">\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash")}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash")}, "log") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash")}, "commit") .
- " | commitdiff" .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") . "<br/>\n";
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff_plain;h=$hash;hp=$hash_parent")}, "plain") . "\n" .
- "</div>\n";
- print "<div>\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash"), -class => "title"}, esc_html($co{'title'}) . $ref) . "\n" .
- "</div>\n";
+ git_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav);
+ git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash);
print "<div class=\"page_body\">\n";
my $comment = $co{'comment'};
my $empty = 0;
@@ -2297,7 +2371,9 @@ sub git_commitdiff {
foreach my $line (@difftree) {
# ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M ls-files.c'
# ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M rev-tree.c'
- $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/;
+ if ($line !~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/) {
+ next;
+ }
my $from_mode = $1;
my $to_mode = $2;
my $from_id = $3;
@@ -2305,21 +2381,23 @@ sub git_commitdiff {
my $status = $5;
my $file = validate_input(unquote($6));
if ($status eq "A") {
- print "<div class=\"diff_info\">" . file_type($to_mode) . ":" .
+ print "<div class=\"diff_info\">" . file_type($to_mode) . ":" .
$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, $to_id) . "(new)" .
"</div>\n";
git_diff_print(undef, "/dev/null", $to_id, "b/$file");
} elsif ($status eq "D") {
print "<div class=\"diff_info\">" . file_type($from_mode) . ":" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, $from_id) . "(deleted)" .
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash_parent;f=$file")}, $from_id) . "(deleted)" .
"</div>\n";
git_diff_print($from_id, "a/$file", undef, "/dev/null");
} elsif ($status eq "M") {
if ($from_id ne $to_id) {
print "<div class=\"diff_info\">" .
- file_type($from_mode) . ":" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, $from_id) .
+ file_type($from_mode) . ":" .
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash_parent;f=$file")}, $from_id) .
" -> " .
- file_type($to_mode) . ":" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, $to_id);
+ file_type($to_mode) . ":" .
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, $to_id);
print "</div>\n";
git_diff_print($from_id, "a/$file", $to_id, "b/$file");
}
@@ -2332,15 +2410,23 @@ sub git_commitdiff {
sub git_commitdiff_plain {
mkdir($git_temp, 0700);
- open my $fd, "-|", "$GIT diff-tree -r $hash_parent $hash" or die_error(undef, "Open failed.");
- my (@difftree) = map { chomp; $_ } <$fd>;
- close $fd or die_error(undef, "Reading diff-tree failed.");
+ my %co = parse_commit($hash);
+ if (!%co) {
+ die_error(undef, "Unknown commit object");
+ }
+ if (!defined $hash_parent) {
+ $hash_parent = $co{'parent'} || '--root';
+ }
+ open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash
+ or die_error(undef, "Open git-diff-tree failed");
+ my @difftree = map { chomp; $_ } <$fd>;
+ close $fd or die_error(undef, "Reading diff-tree failed");
# try to figure out the next tag after this commit
my $tagname;
- my $refs = read_info_ref("tags");
- open $fd, "-|", "$GIT rev-list HEAD";
- chomp (my (@commits) = <$fd>);
+ my $refs = git_get_references("tags");
+ open $fd, "-|", $GIT, "rev-list", "HEAD";
+ my @commits = map { chomp; $_ } <$fd>;
close $fd;
foreach my $commit (@commits) {
if (defined $refs->{$commit}) {
@@ -2352,14 +2438,13 @@ sub git_commitdiff_plain {
}
print $cgi->header(-type => "text/plain", -charset => 'utf-8', '-content-disposition' => "inline; filename=\"git-$hash.patch\"");
- my %co = git_read_commit($hash);
- my %ad = date_str($co{'author_epoch'}, $co{'author_tz'});
+ my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'});
my $comment = $co{'comment'};
print "From: $co{'author'}\n" .
"Date: $ad{'rfc2822'} ($ad{'tz_local'})\n".
"Subject: $co{'title'}\n";
if (defined $tagname) {
- print "X-Git-Tag: $tagname\n";
+ print "X-Git-Tag: $tagname\n";
}
print "X-Git-Url: $my_url?p=$project;a=commitdiff;h=$hash\n" .
"\n";
@@ -2370,7 +2455,9 @@ sub git_commitdiff_plain {
print "---\n\n";
foreach my $line (@difftree) {
- $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/;
+ if ($line !~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/) {
+ next;
+ }
my $from_id = $3;
my $to_id = $4;
my $status = $5;
@@ -2387,27 +2474,17 @@ sub git_commitdiff_plain {
sub git_history {
if (!defined $hash_base) {
- $hash_base = git_read_head($project);
+ $hash_base = git_get_head_hash($project);
}
my $ftype;
- my %co = git_read_commit($hash_base);
+ my %co = parse_commit($hash_base);
if (!%co) {
- die_error(undef, "Unknown commit object.");
+ die_error(undef, "Unknown commit object");
}
- my $refs = read_info_ref();
+ my $refs = git_get_references();
git_header_html();
- print "<div class=\"page_nav\">\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") .
- "<br/><br/>\n" .
- "</div>\n";
- print "<div>\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . "\n" .
- "</div>\n";
+ git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base);
+ git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
if (!defined $hash && defined $file_name) {
$hash = git_get_hash_by_path($hash_base, $file_name);
}
@@ -2417,60 +2494,23 @@ sub git_history {
git_print_page_path($file_name, $ftype);
open my $fd, "-|",
- "$GIT rev-list --full-history $hash_base -- \'$file_name\'";
- print "<table cellspacing=\"0\">\n";
- my $alternate = 0;
- while (my $line = <$fd>) {
- if ($line =~ m/^([0-9a-fA-F]{40})/){
- my $commit = $1;
- my %co = git_read_commit($commit);
- if (!%co) {
- next;
- }
- my $ref = "";
- if (defined $refs->{$commit}) {
- $ref = " <span class=\"tag\">" . esc_html($refs->{$commit}) . "</span>";
- }
- if ($alternate) {
- print "<tr class=\"dark\">\n";
- } else {
- print "<tr class=\"light\">\n";
- }
- $alternate ^= 1;
- print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
- "<td><i>" . esc_html(chop_str($co{'author_name'}, 15, 3)) . "</i></td>\n" .
- "<td>" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list"}, "<b>" .
- esc_html(chop_str($co{'title'}, 50)) . "$ref</b>") . "</td>\n" .
- "<td class=\"link\">" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;hb=$commit;f=$file_name")}, "blob");
- my $blob = git_get_hash_by_path($hash_base, $file_name);
- my $blob_parent = git_get_hash_by_path($commit, $file_name);
- if (defined $blob && defined $blob_parent && $blob ne $blob_parent) {
- print " | " .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$blob;hp=$blob_parent;hb=$commit;f=$file_name")},
- "diff to current");
- }
- print "</td>\n" .
- "</tr>\n";
- }
- }
- print "</table>\n";
+ $GIT, "rev-list", "--full-history", $hash_base, "--", $file_name;
+ git_history_body($fd, $refs, $hash_base, $ftype);
+
close $fd;
git_footer_html();
}
sub git_search {
if (!defined $searchtext) {
- die_error("", "Text field empty.");
+ die_error(undef, "Text field empty");
}
if (!defined $hash) {
- $hash = git_read_head($project);
+ $hash = git_get_head_hash($project);
}
- my %co = git_read_commit($hash);
+ my %co = parse_commit($hash);
if (!%co) {
- die_error(undef, "Unknown commit object.");
+ die_error(undef, "Unknown commit object");
}
# pickaxe may take all resources of your box and run for several minutes
# with every query - so decide by yourself how public you make this feature :)
@@ -2487,24 +2527,14 @@ sub git_search {
$pickaxe_search = 1;
}
git_header_html();
- print "<div class=\"page_nav\">\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary;h=$hash")}, "summary") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash")}, "log") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash")}, "commitdiff") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") .
- "<br/><br/>\n" .
- "</div>\n";
+ git_print_page_nav('','', $hash,$co{'tree'},$hash);
+ git_print_header_div('commit', esc_html($co{'title'}), $hash);
- print "<div>\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash"), -class => "title"}, esc_html($co{'title'})) . "\n" .
- "</div>\n";
print "<table cellspacing=\"0\">\n";
my $alternate = 0;
if ($commit_search) {
$/ = "\0";
- open my $fd, "-|", "$GIT rev-list --header --parents $hash" or next;
+ open my $fd, "-|", $GIT, "rev-list", "--header", "--parents", $hash or next;
while (my $commit_text = <$fd>) {
if (!grep m/$searchtext/i, $commit_text) {
next;
@@ -2516,7 +2546,7 @@ sub git_search {
next;
}
my @commit_lines = split "\n", $commit_text;
- my %co = git_read_commit(undef, \@commit_lines);
+ my %co = parse_commit(undef, \@commit_lines);
if (!%co) {
next;
}
@@ -2597,7 +2627,7 @@ sub git_search {
print "</td>\n" .
"</tr>\n";
}
- %co = git_read_commit($1);
+ %co = parse_commit($1);
}
}
close $fd;
@@ -2607,90 +2637,130 @@ sub git_search {
}
sub git_shortlog {
- my $head = git_read_head($project);
+ my $head = git_get_head_hash($project);
if (!defined $hash) {
$hash = $head;
}
if (!defined $page) {
$page = 0;
}
- my $refs = read_info_ref();
- git_header_html();
- print "<div class=\"page_nav\">\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
- " | shortlog" .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash")}, "log") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash")}, "commitdiff") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$hash;hb=$hash")}, "tree") . "<br/>\n";
+ my $refs = git_get_references();
my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
- open my $fd, "-|", "$GIT rev-list $limit $hash" or die_error(undef, "Open failed.");
- my (@revlist) = map { chomp; $_ } <$fd>;
+ open my $fd, "-|", $GIT, "rev-list", $limit, $hash
+ or die_error(undef, "Open git-rev-list failed");
+ my @revlist = map { chomp; $_ } <$fd>;
close $fd;
- if ($hash ne $head || $page) {
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "HEAD");
- } else {
- print "HEAD";
- }
- if ($page > 0) {
- print " &sdot; " .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page-1)), -accesskey => "p", -title => "Alt-p"}, "prev");
- } else {
- print " &sdot; prev";
- }
+ my $paging_nav = format_paging_nav('shortlog', $hash, $head, $page, $#revlist);
+ my $next_link = '';
if ($#revlist >= (100 * ($page+1)-1)) {
- print " &sdot; " .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page+1)), -accesskey => "n", -title => "Alt-n"}, "next");
- } else {
- print " &sdot; next";
+ $next_link =
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page+1)),
+ -title => "Alt-n"}, "next");
}
- print "<br/>\n" .
- "</div>\n";
- print "<div>\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary"), -class => "title"}, "&nbsp;") .
- "</div>\n";
- print "<table cellspacing=\"0\">\n";
- my $alternate = 0;
- for (my $i = ($page * 100); $i <= $#revlist; $i++) {
+
+
+ git_header_html();
+ git_print_page_nav('shortlog','', $hash,$hash,$hash, $paging_nav);
+ git_print_header_div('summary', $project);
+
+ git_shortlog_body(\@revlist, ($page * 100), $#revlist, $refs, $next_link);
+
+ git_footer_html();
+}
+
+## ......................................................................
+## feeds (RSS, OPML)
+
+sub git_rss {
+ # http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
+ open my $fd, "-|", $GIT, "rev-list", "--max-count=150", git_get_head_hash($project)
+ or die_error(undef, "Open git-rev-list failed");
+ my @revlist = map { chomp; $_ } <$fd>;
+ close $fd or die_error(undef, "Reading git-rev-list failed");
+ print $cgi->header(-type => 'text/xml', -charset => 'utf-8');
+ print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".
+ "<rss version=\"2.0\" xmlns:content=\"http://purl.org/rss/1.0/modules/content/\">\n";
+ print "<channel>\n";
+ print "<title>$project</title>\n".
+ "<link>" . esc_html("$my_url?p=$project;a=summary") . "</link>\n".
+ "<description>$project log</description>\n".
+ "<language>en</language>\n";
+
+ for (my $i = 0; $i <= $#revlist; $i++) {
my $commit = $revlist[$i];
- my $ref = "";
- if (defined $refs->{$commit}) {
- $ref = " <span class=\"tag\">" . esc_html($refs->{$commit}) . "</span>";
+ my %co = parse_commit($commit);
+ # we read 150, we always show 30 and the ones more recent than 48 hours
+ if (($i >= 20) && ((time - $co{'committer_epoch'}) > 48*60*60)) {
+ last;
}
- my %co = git_read_commit($commit);
- my %ad = date_str($co{'author_epoch'});
- if ($alternate) {
- print "<tr class=\"dark\">\n";
- } else {
- print "<tr class=\"light\">\n";
+ my %cd = parse_date($co{'committer_epoch'});
+ open $fd, "-|", $GIT, "diff-tree", '-r', $co{'parent'}, $co{'id'} or next;
+ my @difftree = map { chomp; $_ } <$fd>;
+ close $fd or next;
+ print "<item>\n" .
+ "<title>" .
+ sprintf("%d %s %02d:%02d", $cd{'mday'}, $cd{'month'}, $cd{'hour'}, $cd{'minute'}) . " - " . esc_html($co{'title'}) .
+ "</title>\n" .
+ "<author>" . esc_html($co{'author'}) . "</author>\n" .
+ "<pubDate>$cd{'rfc2822'}</pubDate>\n" .
+ "<guid isPermaLink=\"true\">" . esc_html("$my_url?p=$project;a=commit;h=$commit") . "</guid>\n" .
+ "<link>" . esc_html("$my_url?p=$project;a=commit;h=$commit") . "</link>\n" .
+ "<description>" . esc_html($co{'title'}) . "</description>\n" .
+ "<content:encoded>" .
+ "<![CDATA[\n";
+ my $comment = $co{'comment'};
+ foreach my $line (@$comment) {
+ $line = decode("utf8", $line, Encode::FB_DEFAULT);
+ print "$line<br/>\n";
}
- $alternate ^= 1;
- print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
- "<td><i>" . esc_html(chop_str($co{'author_name'}, 10)) . "</i></td>\n" .
- "<td>";
- if (length($co{'title_short'}) < length($co{'title'})) {
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list", -title => "$co{'title'}"},
- "<b>" . esc_html($co{'title_short'}) . "$ref</b>");
- } else {
- print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list"},
- "<b>" . esc_html($co{'title_short'}) . "$ref</b>");
+ print "<br/>\n";
+ foreach my $line (@difftree) {
+ 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));
+ $file = decode("utf8", $file, Encode::FB_DEFAULT);
+ print "$file<br/>\n";
}
- print "</td>\n" .
- "<td class=\"link\">" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") .
- "</td>\n" .
- "</tr>";
+ print "]]>\n" .
+ "</content:encoded>\n" .
+ "</item>\n";
}
- if ($#revlist >= (100 * ($page+1)-1)) {
- print "<tr>\n" .
- "<td>" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page+1)), -title => "Alt-n"}, "next") .
- "</td>\n" .
- "</tr>\n";
+ print "</channel></rss>";
+}
+
+sub git_opml {
+ my @list = git_get_projects_list();
+
+ print $cgi->header(-type => 'text/xml', -charset => 'utf-8');
+ print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".
+ "<opml version=\"1.0\">\n".
+ "<head>".
+ " <title>$site_name Git OPML Export</title>\n".
+ "</head>\n".
+ "<body>\n".
+ "<outline text=\"git RSS feeds\">\n";
+
+ foreach my $pr (@list) {
+ my %proj = %$pr;
+ my $head = git_get_head_hash($proj{'path'});
+ if (!defined $head) {
+ next;
+ }
+ $ENV{'GIT_DIR'} = "$projectroot/$proj{'path'}";
+ my %co = parse_commit($head);
+ if (!%co) {
+ next;
+ }
+
+ my $path = esc_html(chop_str($proj{'path'}, 25, 5));
+ my $rss = "$my_url?p=$proj{'path'};a=rss";
+ my $html = "$my_url?p=$proj{'path'};a=summary";
+ print "<outline type=\"rss\" text=\"$path\" title=\"$path\" xmlUrl=\"$rss\" htmlUrl=\"$html\"/>\n";
}
- print "</table\n>";
- git_footer_html();
+ print "</outline>\n".
+ "</body>\n".
+ "</opml>\n";
}
diff --git a/builtin-help.c b/help.c
index 6484cb9..9ecdefd 100644
--- a/builtin-help.c
+++ b/help.c
@@ -14,7 +14,7 @@
static int term_columns(void)
{
char *col_string = getenv("COLUMNS");
- int n_cols = 0;
+ int n_cols;
if (col_string && (n_cols = atoi(col_string)) > 0)
return n_cols;
diff --git a/http-fetch.c b/http-fetch.c
index de5fc44..7619b33 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -36,10 +36,10 @@ enum XML_Status {
#define PREV_BUF_SIZE 4096
#define RANGE_HEADER_SIZE 30
-static int commits_on_stdin = 0;
+static int commits_on_stdin;
static int got_alternates = -1;
-static int corrupt_object_found = 0;
+static int corrupt_object_found;
static struct curl_slist *no_pragma_header;
@@ -52,7 +52,7 @@ struct alt_base
struct alt_base *next;
};
-static struct alt_base *alt = NULL;
+static struct alt_base *alt;
enum object_request_state {
WAITING,
@@ -114,7 +114,7 @@ struct remote_ls_ctx
};
#endif
-static struct object_request *object_queue_head = NULL;
+static struct object_request *object_queue_head;
static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
void *data)
@@ -301,7 +301,7 @@ static void finish_object_request(struct object_request *obj_req)
unlink(obj_req->tmpfile);
return;
}
- if (memcmp(obj_req->sha1, obj_req->real_sha1, 20)) {
+ if (hashcmp(obj_req->sha1, obj_req->real_sha1)) {
unlink(obj_req->tmpfile);
return;
}
@@ -393,7 +393,7 @@ void prefetch(unsigned char *sha1)
char *filename = sha1_file_name(sha1);
newreq = xmalloc(sizeof(*newreq));
- memcpy(newreq->sha1, sha1, 20);
+ hashcpy(newreq->sha1, sha1);
newreq->repo = alt;
newreq->url = NULL;
newreq->local = -1;
@@ -1070,7 +1070,7 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1)
int ret = 0;
struct object_request *obj_req = object_queue_head;
- while (obj_req != NULL && memcmp(obj_req->sha1, sha1, 20))
+ while (obj_req != NULL && hashcmp(obj_req->sha1, sha1))
obj_req = obj_req->next;
if (obj_req == NULL)
return error("Couldn't find request for %s in the queue", hex);
@@ -1109,7 +1109,7 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1)
} else if (obj_req->zret != Z_STREAM_END) {
corrupt_object_found++;
ret = error("File %s (%s) corrupt", hex, obj_req->url);
- } else if (memcmp(obj_req->sha1, obj_req->real_sha1, 20)) {
+ } else if (hashcmp(obj_req->sha1, obj_req->real_sha1)) {
ret = error("File %s has bad hash", hex);
} else if (obj_req->rename < 0) {
ret = error("unable to write sha1 filename %s",
diff --git a/http-push.c b/http-push.c
index d45733e..04cb238 100644
--- a/http-push.c
+++ b/http-push.c
@@ -70,18 +70,18 @@ enum XML_Status {
/* We allow "recursive" symbolic refs. Only within reason, though */
#define MAXDEPTH 5
-static int pushing = 0;
-static int aborted = 0;
+static int pushing;
+static int aborted;
static signed char remote_dir_exists[256];
static struct curl_slist *no_pragma_header;
static struct curl_slist *default_headers;
-static int push_verbosely = 0;
-static int push_all = 0;
-static int force_all = 0;
+static int push_verbosely;
+static int push_all;
+static int force_all;
-static struct object_list *objects = NULL;
+static struct object_list *objects;
struct repo
{
@@ -94,7 +94,7 @@ struct repo
struct remote_lock *locks;
};
-static struct repo *remote = NULL;
+static struct repo *remote;
enum transfer_state {
NEED_FETCH,
@@ -134,7 +134,7 @@ struct transfer_request
struct transfer_request *next;
};
-static struct transfer_request *request_queue_head = NULL;
+static struct transfer_request *request_queue_head;
struct xml_ctx
{
@@ -745,7 +745,7 @@ static void finish_request(struct transfer_request *request)
SHA1_Final(request->real_sha1, &request->c);
if (request->zret != Z_STREAM_END) {
unlink(request->tmpfile);
- } else if (memcmp(request->obj->sha1, request->real_sha1, 20)) {
+ } else if (hashcmp(request->obj->sha1, request->real_sha1)) {
unlink(request->tmpfile);
} else {
request->rename =
@@ -1700,7 +1700,7 @@ static int locking_available(void)
return lock_flags;
}
-struct object_list **add_one_object(struct object *obj, struct object_list **p)
+static struct object_list **add_one_object(struct object *obj, struct object_list **p)
{
struct object_list *entry = xmalloc(sizeof(struct object_list));
entry->item = obj;
@@ -1874,7 +1874,7 @@ static int one_local_ref(const char *refname, const unsigned char *sha1)
struct ref *ref;
int len = strlen(refname) + 1;
ref = xcalloc(1, sizeof(*ref) + len);
- memcpy(ref->new_sha1, sha1, 20);
+ hashcpy(ref->new_sha1, sha1);
memcpy(ref->name, refname, len);
*local_tail = ref;
local_tail = &ref->next;
@@ -1909,7 +1909,7 @@ static void one_remote_ref(char *refname)
}
ref = xcalloc(1, sizeof(*ref) + len);
- memcpy(ref->old_sha1, remote_sha1, 20);
+ hashcpy(ref->old_sha1, remote_sha1);
memcpy(ref->name, refname, len);
*remote_tail = ref;
remote_tail = &ref->next;
@@ -2164,7 +2164,7 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
if (*symref != NULL)
free(*symref);
*symref = NULL;
- memset(sha1, 0, 20);
+ hashclr(sha1);
if (buffer.posn == 0)
return;
@@ -2182,49 +2182,11 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
static int verify_merge_base(unsigned char *head_sha1, unsigned char *branch_sha1)
{
- int pipe_fd[2];
- pid_t merge_base_pid;
- char line[PATH_MAX + 20];
- unsigned char merge_sha1[20];
- int verified = 0;
-
- if (pipe(pipe_fd) < 0)
- die("Verify merge base: pipe failed");
-
- merge_base_pid = fork();
- if (!merge_base_pid) {
- static const char *args[] = {
- "merge-base",
- "-a",
- NULL,
- NULL,
- NULL
- };
- args[2] = strdup(sha1_to_hex(head_sha1));
- args[3] = sha1_to_hex(branch_sha1);
-
- dup2(pipe_fd[1], 1);
- close(pipe_fd[0]);
- close(pipe_fd[1]);
- execv_git_cmd(args);
- die("merge-base setup failed");
- }
- if (merge_base_pid < 0)
- die("merge-base fork failed");
-
- dup2(pipe_fd[0], 0);
- close(pipe_fd[0]);
- close(pipe_fd[1]);
- while (fgets(line, sizeof(line), stdin) != NULL) {
- if (get_sha1_hex(line, merge_sha1))
- die("expected sha1, got garbage:\n %s", line);
- if (!memcmp(branch_sha1, merge_sha1, 20)) {
- verified = 1;
- break;
- }
- }
+ struct commit *head = lookup_commit(head_sha1);
+ struct commit *branch = lookup_commit(branch_sha1);
+ struct commit_list *merge_bases = get_merge_bases(head, branch, 1);
- return verified;
+ return (merge_bases && !merge_bases->next && merge_bases->item == branch);
}
static int delete_remote_branch(char *pattern, int force)
@@ -2454,7 +2416,7 @@ int main(int argc, char **argv)
if (!ref->peer_ref)
continue;
- if (!memcmp(ref->old_sha1, ref->peer_ref->new_sha1, 20)) {
+ if (!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
if (push_verbosely || 1)
fprintf(stderr, "'%s': up-to-date\n", ref->name);
continue;
@@ -2483,7 +2445,7 @@ int main(int argc, char **argv)
continue;
}
}
- memcpy(ref->new_sha1, ref->peer_ref->new_sha1, 20);
+ hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
if (is_zero_sha1(ref->new_sha1)) {
error("cannot happen anymore");
rc = -3;
diff --git a/index-pack.c b/index-pack.c
index b20659c..80bc6cb 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -82,7 +82,7 @@ static void parse_pack_header(void)
SHA1_Init(&ctx);
SHA1_Update(&ctx, pack_base, pack_size - 20);
SHA1_Final(sha1, &ctx);
- if (memcmp(sha1, pack_base + pack_size - 20, 20))
+ if (hashcmp(sha1, pack_base + pack_size - 20))
die("packfile '%s' SHA1 mismatch", pack_name);
}
@@ -161,7 +161,7 @@ static void *unpack_raw_entry(unsigned long offset,
case OBJ_DELTA:
if (pos + 20 >= pack_limit)
bad_object(offset, "object extends past end of pack");
- memcpy(delta_base, pack_base + pos, 20);
+ hashcpy(delta_base, pack_base + pos);
pos += 20;
/* fallthru */
case OBJ_COMMIT:
@@ -189,7 +189,7 @@ static int find_delta(const unsigned char *base_sha1)
struct delta_entry *delta = &deltas[next];
int cmp;
- cmp = memcmp(base_sha1, delta->base_sha1, 20);
+ cmp = hashcmp(base_sha1, delta->base_sha1);
if (!cmp)
return next;
if (cmp < 0) {
@@ -210,9 +210,9 @@ static int find_deltas_based_on_sha1(const unsigned char *base_sha1,
if (first < 0)
return -1;
- while (first > 0 && !memcmp(deltas[first-1].base_sha1, base_sha1, 20))
+ while (first > 0 && !hashcmp(deltas[first - 1].base_sha1, base_sha1))
--first;
- while (last < end && !memcmp(deltas[last+1].base_sha1, base_sha1, 20))
+ while (last < end && !hashcmp(deltas[last + 1].base_sha1, base_sha1))
++last;
*first_index = first;
*last_index = last;
@@ -278,7 +278,7 @@ static int compare_delta_entry(const void *a, const void *b)
{
const struct delta_entry *delta_a = a;
const struct delta_entry *delta_b = b;
- return memcmp(delta_a->base_sha1, delta_b->base_sha1, 20);
+ return hashcmp(delta_a->base_sha1, delta_b->base_sha1);
}
static void parse_pack_objects(void)
@@ -304,7 +304,7 @@ static void parse_pack_objects(void)
if (obj->type == OBJ_DELTA) {
struct delta_entry *delta = &deltas[nr_deltas++];
delta->obj = obj;
- memcpy(delta->base_sha1, base_sha1, 20);
+ hashcpy(delta->base_sha1, base_sha1);
} else
sha1_object(data, data_size, obj->type, obj->sha1);
free(data);
@@ -350,7 +350,7 @@ static int sha1_compare(const void *_a, const void *_b)
{
struct object_entry *a = *(struct object_entry **)_a;
struct object_entry *b = *(struct object_entry **)_b;
- return memcmp(a->sha1, b->sha1, 20);
+ return hashcmp(a->sha1, b->sha1);
}
static void write_index_file(const char *index_name, unsigned char *sha1)
diff --git a/local-fetch.c b/local-fetch.c
index 7d01845..7b6875c 100644
--- a/local-fetch.c
+++ b/local-fetch.c
@@ -5,10 +5,10 @@
#include "commit.h"
#include "fetch.h"
-static int use_link = 0;
-static int use_symlink = 0;
+static int use_link;
+static int use_symlink;
static int use_filecopy = 1;
-static int commits_on_stdin = 0;
+static int commits_on_stdin;
static const char *path; /* "Remote" git repository */
@@ -16,7 +16,7 @@ void prefetch(unsigned char *sha1)
{
}
-static struct packed_git *packs = NULL;
+static struct packed_git *packs;
static void setup_index(unsigned char *sha1)
{
diff --git a/log-tree.c b/log-tree.c
index 05ede0c..031af88 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -19,7 +19,7 @@ static int append_signoff(char *buf, int buf_sz, int at, const char *signoff)
char *cp = buf;
/* Do we have enough space to add it? */
- if (buf_sz - at <= strlen(signed_off_by) + signoff_len + 2)
+ if (buf_sz - at <= strlen(signed_off_by) + signoff_len + 3)
return at;
/* First see if we already have the sign-off by the signer */
@@ -34,6 +34,48 @@ static int append_signoff(char *buf, int buf_sz, int at, const char *signoff)
return at; /* we already have him */
}
+ /* Does the last line already end with "^[-A-Za-z]+: [^@]+@"?
+ * If not, add a blank line to separate the message from
+ * the run of Signed-off-by: and Acked-by: lines.
+ */
+ {
+ char ch;
+ int seen_colon, seen_at, seen_name, seen_head, not_signoff;
+ seen_colon = 0;
+ seen_at = 0;
+ seen_name = 0;
+ seen_head = 0;
+ not_signoff = 0;
+ cp = buf + at;
+ while (buf <= --cp && (ch = *cp) == '\n')
+ ;
+ while (!not_signoff && buf <= cp && (ch = *cp--) != '\n') {
+ if (!seen_at) {
+ if (ch == '@')
+ seen_at = 1;
+ continue;
+ }
+ if (!seen_colon) {
+ if (ch == '@')
+ not_signoff = 1;
+ else if (ch == ':')
+ seen_colon = 1;
+ else
+ seen_name = 1;
+ continue;
+ }
+ if (('A' <= ch && ch <= 'Z') ||
+ ('a' <= ch && ch <= 'z') ||
+ ch == '-') {
+ seen_head = 1;
+ continue;
+ }
+ not_signoff = 1;
+ }
+ if (not_signoff || !seen_head || !seen_name)
+ buf[at++] = '\n';
+ }
+
strcpy(buf + at, signed_off_by);
at += strlen(signed_off_by);
strcpy(buf + at, signoff);
diff --git a/merge-base.c b/merge-base.c
index 59f723f..009caf8 100644
--- a/merge-base.c
+++ b/merge-base.c
@@ -2,7 +2,7 @@
#include "cache.h"
#include "commit.h"
-static int show_all = 0;
+static int show_all;
static int merge_base(struct commit *rev1, struct commit *rev2)
{
diff --git a/merge-index.c b/merge-index.c
index 0498a6f..646d090 100644
--- a/merge-index.c
+++ b/merge-index.c
@@ -4,14 +4,15 @@
#include "cache.h"
-static const char *pgm = NULL;
+static const char *pgm;
static const char *arguments[8];
static int one_shot, quiet;
static int err;
static void run_program(void)
{
- int pid = fork(), status;
+ pid_t pid = fork();
+ int status;
if (pid < 0)
die("unable to fork");
diff --git a/merge-tree.c b/merge-tree.c
index 7cf00be..c2e9a86 100644
--- a/merge-tree.c
+++ b/merge-tree.c
@@ -152,7 +152,7 @@ static int same_entry(struct name_entry *a, struct name_entry *b)
{
return a->sha1 &&
b->sha1 &&
- !memcmp(a->sha1, b->sha1, 20) &&
+ !hashcmp(a->sha1, b->sha1) &&
a->mode == b->mode;
}
diff --git a/mktree.c b/mktree.c
index 9a6f0d2..56205d1 100644
--- a/mktree.c
+++ b/mktree.c
@@ -30,7 +30,7 @@ static void append_to_tree(unsigned mode, unsigned char *sha1, char *path)
ent = entries[used++] = xmalloc(sizeof(**entries) + len + 1);
ent->mode = mode;
ent->len = len;
- memcpy(ent->sha1, sha1, 20);
+ hashcpy(ent->sha1, sha1);
memcpy(ent->name, path, len+1);
}
@@ -49,7 +49,6 @@ static void write_tree(unsigned char *sha1)
int i;
qsort(entries, used, sizeof(*entries), ent_compare);
- size = 100;
for (size = i = 0; i < used; i++)
size += 32 + entries[i]->len;
buffer = xmalloc(size);
@@ -65,7 +64,7 @@ static void write_tree(unsigned char *sha1)
offset += sprintf(buffer + offset, "%o ", ent->mode);
offset += sprintf(buffer + offset, "%s", ent->name);
buffer[offset++] = 0;
- memcpy(buffer + offset, ent->sha1, 20);
+ hashcpy((unsigned char*)buffer + offset, ent->sha1);
offset += 20;
}
write_sha1_file(buffer, offset, tree_type, sha1);
diff --git a/object.c b/object.c
index b5d8ed4..60bf16b 100644
--- a/object.c
+++ b/object.c
@@ -58,7 +58,7 @@ struct object *lookup_object(const unsigned char *sha1)
i = hashtable_index(sha1);
while ((obj = obj_hash[i]) != NULL) {
- if (!memcmp(sha1, obj->sha1, 20))
+ if (!hashcmp(sha1, obj->sha1))
break;
i++;
if (i == obj_hash_size)
@@ -91,7 +91,7 @@ void created_object(const unsigned char *sha1, struct object *obj)
obj->used = 0;
obj->type = OBJ_NONE;
obj->flags = 0;
- memcpy(obj->sha1, sha1, 20);
+ hashcpy(obj->sha1, sha1);
if (obj_hash_size - 1 <= nr_objs * 2)
grow_object_hash();
diff --git a/pack-check.c b/pack-check.c
index 3a62e1b..04c6c00 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -29,10 +29,10 @@ static int verify_packfile(struct packed_git *p)
pack_base = p->pack_base;
SHA1_Update(&ctx, pack_base, pack_size - 20);
SHA1_Final(sha1, &ctx);
- if (memcmp(sha1, (char *) pack_base + pack_size - 20, 20))
+ if (hashcmp(sha1, (unsigned char *)pack_base + pack_size - 20))
return error("Packfile %s SHA1 mismatch with itself",
p->pack_name);
- if (memcmp(sha1, (char *) index_base + index_size - 40, 20))
+ if (hashcmp(sha1, (unsigned char *)index_base + index_size - 40))
return error("Packfile %s SHA1 mismatch with idx",
p->pack_name);
@@ -135,7 +135,7 @@ int verify_pack(struct packed_git *p, int verbose)
SHA1_Init(&ctx);
SHA1_Update(&ctx, index_base, index_size - 20);
SHA1_Final(sha1, &ctx);
- if (memcmp(sha1, (char *) index_base + index_size - 20, 20))
+ if (hashcmp(sha1, (unsigned char *)index_base + index_size - 20))
ret = error("Packfile index for %s SHA1 mismatch",
p->pack_name);
diff --git a/pack-redundant.c b/pack-redundant.c
index 41fb960..edb5524 100644
--- a/pack-redundant.c
+++ b/pack-redundant.c
@@ -13,7 +13,7 @@
static const char pack_redundant_usage[] =
"git-pack-redundant [ --verbose ] [ --alt-odb ] < --all | <.pack filename> ...>";
-static int load_all_packs = 0, verbose = 0, alt_odb = 0;
+static int load_all_packs, verbose, alt_odb;
struct llist_item {
struct llist_item *next;
@@ -37,7 +37,7 @@ struct pll {
struct pack_list *pl;
};
-static struct llist_item *free_nodes = NULL;
+static struct llist_item *free_nodes;
static inline void llist_item_put(struct llist_item *item)
{
@@ -139,7 +139,7 @@ static inline struct llist_item *llist_insert_sorted_unique(struct llist *list,
l = (hint == NULL) ? list->front : hint;
while (l) {
- int cmp = memcmp(l->sha1, sha1, 20);
+ int cmp = hashcmp(l->sha1, sha1);
if (cmp > 0) { /* we insert before this entry */
return llist_insert(list, prev, sha1);
}
@@ -162,7 +162,7 @@ redo_from_start:
l = (hint == NULL) ? list->front : hint;
prev = NULL;
while (l) {
- int cmp = memcmp(l->sha1, sha1, 20);
+ int cmp = hashcmp(l->sha1, sha1);
if (cmp > 0) /* not in list, since sorted */
return prev;
if(!cmp) { /* found */
@@ -256,7 +256,7 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
while (p1_off <= p1->pack->index_size - 3 * 20 &&
p2_off <= p2->pack->index_size - 3 * 20)
{
- int cmp = memcmp(p1_base + p1_off, p2_base + p2_off, 20);
+ int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off);
/* cmp ~ p1 - p2 */
if (cmp == 0) {
p1_hint = llist_sorted_remove(p1->unique_objects,
@@ -351,16 +351,16 @@ static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
{
size_t ret = 0;
int p1_off, p2_off;
- char *p1_base, *p2_base;
+ unsigned char *p1_base, *p2_base;
p1_off = p2_off = 256 * 4 + 4;
- p1_base = (char *)p1->index_base;
- p2_base = (char *)p2->index_base;
+ p1_base = (unsigned char *)p1->index_base;
+ p2_base = (unsigned char *)p2->index_base;
while (p1_off <= p1->index_size - 3 * 20 &&
p2_off <= p2->index_size - 3 * 20)
{
- int cmp = memcmp(p1_base + p1_off, p2_base + p2_off, 20);
+ int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off);
/* cmp ~ p1 - p2 */
if (cmp == 0) {
ret++;
diff --git a/pager.c b/pager.c
index 280f57f..dcb398d 100644
--- a/pager.c
+++ b/pager.c
@@ -15,11 +15,13 @@ void setup_pager(void)
{
pid_t pid;
int fd[2];
- const char *pager = getenv("PAGER");
+ const char *pager = getenv("GIT_PAGER");
if (!isatty(1))
return;
if (!pager)
+ pager = getenv("PAGER");
+ if (!pager)
pager = "less";
else if (!*pager || !strcmp(pager, "cat"))
return;
diff --git a/patch-id.c b/patch-id.c
index 3b4c80f..086d2d9 100644
--- a/patch-id.c
+++ b/patch-id.c
@@ -47,7 +47,7 @@ static void generate_id_list(void)
if (!get_sha1_hex(p, n)) {
flush_current_id(patchlen, sha1, &ctx);
- memcpy(sha1, n, 20);
+ hashcpy(sha1, n);
patchlen = 0;
continue;
}
diff --git a/read-cache.c b/read-cache.c
index c575eda..20c9d49 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -18,16 +18,16 @@
#define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) )
#define CACHE_EXT_TREE 0x54524545 /* "TREE" */
-struct cache_entry **active_cache = NULL;
+struct cache_entry **active_cache;
static time_t index_file_timestamp;
-unsigned int active_nr = 0, active_alloc = 0, active_cache_changed = 0;
+unsigned int active_nr, active_alloc, active_cache_changed;
-struct cache_tree *active_cache_tree = NULL;
+struct cache_tree *active_cache_tree;
-int cache_errno = 0;
+int cache_errno;
-static void *cache_mmap = NULL;
-static size_t cache_mmap_size = 0;
+static void *cache_mmap;
+static size_t cache_mmap_size;
/*
* This only updates the "non-critical" parts of the directory
@@ -60,7 +60,7 @@ static int ce_compare_data(struct cache_entry *ce, struct stat *st)
if (fd >= 0) {
unsigned char sha1[20];
if (!index_fd(sha1, fd, st, 0, NULL))
- match = memcmp(sha1, ce->sha1, 20);
+ match = hashcmp(sha1, ce->sha1);
/* index_fd() closed the file descriptor already */
}
return match;
@@ -169,9 +169,11 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
return changed;
}
-int ce_match_stat(struct cache_entry *ce, struct stat *st, int ignore_valid)
+int ce_match_stat(struct cache_entry *ce, struct stat *st, int options)
{
unsigned int changed;
+ int ignore_valid = options & 01;
+ int assume_racy_is_modified = options & 02;
/*
* If it's marked as always valid in the index, it's
@@ -200,8 +202,12 @@ int ce_match_stat(struct cache_entry *ce, struct stat *st, int ignore_valid)
*/
if (!changed &&
index_file_timestamp &&
- index_file_timestamp <= ntohl(ce->ce_mtime.sec))
- changed |= ce_modified_check_fs(ce, st);
+ index_file_timestamp <= ntohl(ce->ce_mtime.sec)) {
+ if (assume_racy_is_modified)
+ changed |= DATA_CHANGED;
+ else
+ changed |= ce_modified_check_fs(ce, st);
+ }
return changed;
}
@@ -738,7 +744,7 @@ static int verify_hdr(struct cache_header *hdr, unsigned long size)
SHA1_Init(&c);
SHA1_Update(&c, hdr, size - 20);
SHA1_Final(sha1, &c);
- if (memcmp(sha1, (char *) hdr + size - 20, 20))
+ if (hashcmp(sha1, (unsigned char *)hdr + size - 20))
return error("bad index file sha1 signature");
return 0;
}
@@ -857,6 +863,18 @@ int discard_cache()
static unsigned char write_buffer[WRITE_BUFFER_SIZE];
static unsigned long write_buffer_len;
+static int ce_write_flush(SHA_CTX *context, int fd)
+{
+ unsigned int buffered = write_buffer_len;
+ if (buffered) {
+ SHA1_Update(context, write_buffer, buffered);
+ if (write(fd, write_buffer, buffered) != buffered)
+ return -1;
+ write_buffer_len = 0;
+ }
+ return 0;
+}
+
static int ce_write(SHA_CTX *context, int fd, void *data, unsigned int len)
{
while (len) {
@@ -867,8 +885,8 @@ static int ce_write(SHA_CTX *context, int fd, void *data, unsigned int len)
memcpy(write_buffer + buffered, data, partial);
buffered += partial;
if (buffered == WRITE_BUFFER_SIZE) {
- SHA1_Update(context, write_buffer, WRITE_BUFFER_SIZE);
- if (write(fd, write_buffer, WRITE_BUFFER_SIZE) != WRITE_BUFFER_SIZE)
+ write_buffer_len = buffered;
+ if (ce_write_flush(context, fd))
return -1;
buffered = 0;
}
@@ -884,10 +902,8 @@ static int write_index_ext_header(SHA_CTX *context, int fd,
{
ext = htonl(ext);
sz = htonl(sz);
- if ((ce_write(context, fd, &ext, 4) < 0) ||
- (ce_write(context, fd, &sz, 4) < 0))
- return -1;
- return 0;
+ return ((ce_write(context, fd, &ext, 4) < 0) ||
+ (ce_write(context, fd, &sz, 4) < 0)) ? -1 : 0;
}
static int ce_flush(SHA_CTX *context, int fd)
@@ -909,9 +925,7 @@ static int ce_flush(SHA_CTX *context, int fd)
/* Append the SHA1 signature at the end */
SHA1_Final(write_buffer + left, context);
left += 20;
- if (write(fd, write_buffer, left) != left)
- return -1;
- return 0;
+ return (write(fd, write_buffer, left) != left) ? -1 : 0;
}
static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
@@ -940,7 +954,7 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
* $ echo filfre >nitfol
* $ git-update-index --add nitfol
*
- * but it does not. Whe the second update-index runs,
+ * but it does not. When the second update-index runs,
* it notices that the entry "frotz" has the same timestamp
* as index, and if we were to smudge it by resetting its
* size to zero here, then the object name recorded
diff --git a/receive-pack.c b/receive-pack.c
index 93929b5..2015316 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -8,10 +8,10 @@ static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
static const char *unpacker[] = { "unpack-objects", NULL };
-static int report_status = 0;
+static int report_status;
static char capabilities[] = "report-status";
-static int capabilities_sent = 0;
+static int capabilities_sent;
static int show_ref(const char *path, const unsigned char *sha1)
{
@@ -40,7 +40,7 @@ struct command {
char ref_name[FLEX_ARRAY]; /* more */
};
-static struct command *commands = NULL;
+static struct command *commands;
static int is_all_zeroes(const char *hex)
{
@@ -247,8 +247,8 @@ static void read_head_info(void)
report_status = 1;
}
cmd = xmalloc(sizeof(struct command) + len - 80);
- memcpy(cmd->old_sha1, old_sha1, 20);
- memcpy(cmd->new_sha1, new_sha1, 20);
+ hashcpy(cmd->old_sha1, old_sha1);
+ hashcpy(cmd->new_sha1, new_sha1);
memcpy(cmd->ref_name, line + 82, len - 81);
cmd->error_string = "n/a (unpacker error)";
cmd->next = NULL;
diff --git a/refs.c b/refs.c
index 86ef916..e70ef0a 100644
--- a/refs.c
+++ b/refs.c
@@ -29,7 +29,7 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
if (lstat(path, &st) < 0) {
if (reading || errno != ENOENT)
return NULL;
- memset(sha1, 0, 20);
+ hashclr(sha1);
return path;
}
@@ -281,7 +281,7 @@ static struct ref_lock *verify_lock(struct ref_lock *lock,
unlock_ref(lock);
return NULL;
}
- if (memcmp(lock->old_sha1, old_sha1, 20)) {
+ if (hashcmp(lock->old_sha1, old_sha1)) {
error("Ref %s is at %s but expected %s", lock->ref_file,
sha1_to_hex(lock->old_sha1), sha1_to_hex(old_sha1));
unlock_ref(lock);
@@ -411,7 +411,7 @@ int write_ref_sha1(struct ref_lock *lock,
if (!lock)
return -1;
- if (!lock->force_write && !memcmp(lock->old_sha1, sha1, 20)) {
+ if (!lock->force_write && !hashcmp(lock->old_sha1, sha1)) {
unlock_ref(lock);
return 0;
}
@@ -475,7 +475,7 @@ int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1)
die("Log %s is corrupt.", logfile);
if (get_sha1_hex(rec + 41, sha1))
die("Log %s is corrupt.", logfile);
- if (memcmp(logged_sha1, sha1, 20)) {
+ if (hashcmp(logged_sha1, sha1)) {
tz = strtoul(tz_c, NULL, 10);
fprintf(stderr,
"warning: Log %s has gap after %s.\n",
@@ -489,7 +489,7 @@ int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1)
else {
if (get_sha1_hex(rec + 41, logged_sha1))
die("Log %s is corrupt.", logfile);
- if (memcmp(logged_sha1, sha1, 20)) {
+ if (hashcmp(logged_sha1, sha1)) {
tz = strtoul(tz_c, NULL, 10);
fprintf(stderr,
"warning: Log %s unexpectedly ended on %s.\n",
diff --git a/revision.c b/revision.c
index 5a91d06..1d89d72 100644
--- a/revision.c
+++ b/revision.c
@@ -496,7 +496,7 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
it = get_reference(revs, arg, sha1, 0);
if (it->type != OBJ_TAG)
break;
- memcpy(sha1, ((struct tag*)it)->tagged->sha1, 20);
+ hashcpy(sha1, ((struct tag*)it)->tagged->sha1);
}
if (it->type != OBJ_COMMIT)
return 0;
diff --git a/run-command.c b/run-command.c
index ca67ee9..6190868 100644
--- a/run-command.c
+++ b/run-command.c
@@ -25,15 +25,15 @@ int run_command_v_opt(int argc, const char **argv, int flags)
}
for (;;) {
int status, code;
- int retval = waitpid(pid, &status, 0);
+ pid_t waiting = waitpid(pid, &status, 0);
- if (retval < 0) {
+ if (waiting < 0) {
if (errno == EINTR)
continue;
- error("waitpid failed (%s)", strerror(retval));
+ error("waitpid failed (%s)", strerror(errno));
return -ERR_RUN_COMMAND_WAITPID;
}
- if (retval != pid)
+ if (waiting != pid)
return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
if (WIFSIGNALED(status))
return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
diff --git a/send-pack.c b/send-pack.c
index 10bc8bc..fd79a61 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -9,10 +9,10 @@ static const char send_pack_usage[] =
"git-send-pack [--all] [--exec=git-receive-pack] <remote> [<head>...]\n"
" --all and explicit <head> specification are mutually exclusive.";
static const char *exec = "git-receive-pack";
-static int verbose = 0;
-static int send_all = 0;
-static int force_update = 0;
-static int use_thin_pack = 0;
+static int verbose;
+static int send_all;
+static int force_update;
+static int use_thin_pack;
static int is_zero_sha1(const unsigned char *sha1)
{
@@ -111,7 +111,7 @@ static void rev_list(int fd, struct ref *refs)
exec_rev_list(refs);
}
-static int pack_objects(int fd, struct ref *refs)
+static void pack_objects(int fd, struct ref *refs)
{
pid_t rev_list_pid;
@@ -126,7 +126,6 @@ static int pack_objects(int fd, struct ref *refs)
* We don't wait for the rev-list pipeline in the parent:
* we end up waiting for the other end instead
*/
- return 0;
}
static void unmark_and_free(struct commit_list *list, unsigned int mark)
@@ -186,7 +185,7 @@ static int one_local_ref(const char *refname, const unsigned char *sha1)
struct ref *ref;
int len = strlen(refname) + 1;
ref = xcalloc(1, sizeof(*ref) + len);
- memcpy(ref->new_sha1, sha1, 20);
+ hashcpy(ref->new_sha1, sha1);
memcpy(ref->name, refname, len);
*local_tail = ref;
local_tail = &ref->next;
@@ -266,7 +265,7 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
char old_hex[60], *new_hex;
if (!ref->peer_ref)
continue;
- if (!memcmp(ref->old_sha1, ref->peer_ref->new_sha1, 20)) {
+ if (!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
if (verbose)
fprintf(stderr, "'%s': up-to-date\n", ref->name);
continue;
@@ -311,7 +310,7 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
continue;
}
}
- memcpy(ref->new_sha1, ref->peer_ref->new_sha1, 20);
+ hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
if (is_zero_sha1(ref->new_sha1)) {
error("cannot happen anymore");
ret = -3;
diff --git a/sha1_file.c b/sha1_file.c
index 3db956d..46272b5 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -22,7 +22,7 @@
#endif
#endif
-const unsigned char null_sha1[20] = { 0, };
+const unsigned char null_sha1[20];
static unsigned int sha1_file_open_flag = O_NOATIME;
@@ -463,6 +463,7 @@ int use_packed_git(struct packed_git *p)
int fd;
struct stat st;
void *map;
+ struct pack_header *hdr;
pack_mapped += p->pack_size;
while (PACK_MAX_SZ < pack_mapped && unuse_one_packed_git())
@@ -482,13 +483,24 @@ int use_packed_git(struct packed_git *p)
die("packfile %s cannot be mapped.", p->pack_name);
p->pack_base = map;
+ /* Check if we understand this pack file. If we don't we're
+ * likely too old to handle it.
+ */
+ hdr = map;
+ if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
+ die("packfile %s isn't actually a pack.", p->pack_name);
+ if (!pack_version_ok(hdr->hdr_version))
+ die("packfile %s is version %i and not supported"
+ " (try upgrading GIT to a newer version)",
+ p->pack_name, ntohl(hdr->hdr_version));
+
/* Check if the pack file matches with the index file.
* this is cheap.
*/
- if (memcmp((char*)(p->index_base) + p->index_size - 40,
- (char *) p->pack_base + p->pack_size - 20,
- 20)) {
-
+ if (hashcmp((unsigned char *)(p->index_base) +
+ p->index_size - 40,
+ (unsigned char *)p->pack_base +
+ p->pack_size - 20)) {
die("packfile %s does not match index.", p->pack_name);
}
}
@@ -528,7 +540,7 @@ struct packed_git *add_packed_git(char *path, int path_len, int local)
p->pack_use_cnt = 0;
p->pack_local = local;
if ((path_len > 44) && !get_sha1_hex(path + path_len - 44, sha1))
- memcpy(p->sha1, sha1, 20);
+ hashcpy(p->sha1, sha1);
return p;
}
@@ -559,7 +571,7 @@ struct packed_git *parse_pack_index_file(const unsigned char *sha1, char *idx_pa
p->pack_base = NULL;
p->pack_last_used = 0;
p->pack_use_cnt = 0;
- memcpy(p->sha1, sha1, 20);
+ hashcpy(p->sha1, sha1);
return p;
}
@@ -643,11 +655,10 @@ int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long siz
SHA1_Update(&c, header, 1+sprintf(header, "%s %lu", type, size));
SHA1_Update(&c, map, size);
SHA1_Final(real_sha1, &c);
- return memcmp(sha1, real_sha1, 20) ? -1 : 0;
+ return hashcmp(sha1, real_sha1) ? -1 : 0;
}
-static void *map_sha1_file_internal(const unsigned char *sha1,
- unsigned long *size)
+void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
{
struct stat st;
void *map;
@@ -684,10 +695,26 @@ static void *map_sha1_file_internal(const unsigned char *sha1,
return map;
}
+int legacy_loose_object(unsigned char *map)
+{
+ unsigned int word;
+
+ /*
+ * Is it a zlib-compressed buffer? If so, the first byte
+ * must be 0x78 (15-bit window size, deflated), and the
+ * first 16-bit word is evenly divisible by 31
+ */
+ word = (map[0] << 8) + map[1];
+ if (map[0] == 0x78 && !(word % 31))
+ return 1;
+ else
+ return 0;
+}
+
static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz)
{
unsigned char c;
- unsigned int word, bits;
+ unsigned int bits;
unsigned long size;
static const char *typename[8] = {
NULL, /* OBJ_EXT */
@@ -703,13 +730,7 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon
stream->next_out = buffer;
stream->avail_out = bufsiz;
- /*
- * Is it a zlib-compressed buffer? If so, the first byte
- * must be 0x78 (15-bit window size, deflated), and the
- * first 16-bit word is evenly divisible by 31
- */
- word = (map[0] << 8) + map[1];
- if (map[0] == 0x78 && !(word % 31)) {
+ if (legacy_loose_object(map)) {
inflateInit(stream);
return inflate(stream, 0);
}
@@ -896,23 +917,19 @@ static unsigned long unpack_object_header(struct packed_git *p, unsigned long of
enum object_type *type, unsigned long *sizep)
{
unsigned shift;
- unsigned char *pack, c;
+ unsigned char c;
unsigned long size;
if (offset >= p->pack_size)
die("object offset outside of pack file");
-
- pack = (unsigned char *) p->pack_base + offset;
- c = *pack++;
- offset++;
+ c = *((unsigned char *)p->pack_base + offset++);
*type = (c >> 4) & 7;
size = c & 15;
shift = 4;
while (c & 0x80) {
if (offset >= p->pack_size)
die("object offset outside of pack file");
- c = *pack++;
- offset++;
+ c = *((unsigned char *)p->pack_base + offset++);
size += (c & 0x7f) << shift;
shift += 7;
}
@@ -932,7 +949,7 @@ int check_reuse_pack_delta(struct packed_git *p, unsigned long offset,
ptr = unpack_object_header(p, ptr, kindp, sizep);
if (*kindp != OBJ_DELTA)
goto done;
- memcpy(base, (char *) p->pack_base + ptr, 20);
+ hashcpy(base, (unsigned char *) p->pack_base + ptr);
status = 0;
done:
unuse_packed_git(p);
@@ -960,7 +977,7 @@ void packed_object_info_detail(struct pack_entry *e,
if (p->pack_size <= offset + 20)
die("pack file %s records an incomplete delta base",
p->pack_name);
- memcpy(base_sha1, pack, 20);
+ hashcpy(base_sha1, pack);
do {
struct pack_entry base_ent;
unsigned long junk;
@@ -975,16 +992,10 @@ void packed_object_info_detail(struct pack_entry *e,
}
switch (kind) {
case OBJ_COMMIT:
- strcpy(type, commit_type);
- break;
case OBJ_TREE:
- strcpy(type, tree_type);
- break;
case OBJ_BLOB:
- strcpy(type, blob_type);
- break;
case OBJ_TAG:
- strcpy(type, tag_type);
+ strcpy(type, type_names[kind]);
break;
default:
die("corrupted pack file %s containing object of kind %d",
@@ -1015,16 +1026,10 @@ static int packed_object_info(struct pack_entry *entry,
unuse_packed_git(p);
return retval;
case OBJ_COMMIT:
- strcpy(type, commit_type);
- break;
case OBJ_TREE:
- strcpy(type, tree_type);
- break;
case OBJ_BLOB:
- strcpy(type, blob_type);
- break;
case OBJ_TAG:
- strcpy(type, tag_type);
+ strcpy(type, type_names[kind]);
break;
default:
die("corrupted pack file %s containing object of kind %d",
@@ -1036,26 +1041,49 @@ static int packed_object_info(struct pack_entry *entry,
return 0;
}
-/* forward declaration for a mutually recursive function */
-static void *unpack_entry(struct pack_entry *, char *, unsigned long *);
+static void *unpack_compressed_entry(struct packed_git *p,
+ unsigned long offset,
+ unsigned long size)
+{
+ int st;
+ z_stream stream;
+ unsigned char *buffer;
+
+ buffer = xmalloc(size + 1);
+ buffer[size] = 0;
+ memset(&stream, 0, sizeof(stream));
+ stream.next_in = (unsigned char*)p->pack_base + offset;
+ stream.avail_in = p->pack_size - offset;
+ stream.next_out = buffer;
+ stream.avail_out = size;
+
+ inflateInit(&stream);
+ st = inflate(&stream, Z_FINISH);
+ inflateEnd(&stream);
+ if ((st != Z_STREAM_END) || stream.total_out != size) {
+ free(buffer);
+ return NULL;
+ }
-static void *unpack_delta_entry(unsigned char *base_sha1,
+ return buffer;
+}
+
+static void *unpack_delta_entry(struct packed_git *p,
+ unsigned long offset,
unsigned long delta_size,
- unsigned long left,
char *type,
- unsigned long *sizep,
- struct packed_git *p)
+ unsigned long *sizep)
{
struct pack_entry base_ent;
- void *data, *delta_data, *result, *base;
- unsigned long data_size, result_size, base_size;
- z_stream stream;
- int st;
+ void *delta_data, *result, *base;
+ unsigned long result_size, base_size;
+ unsigned char* base_sha1;
- if (left < 20)
+ if ((offset + 20) >= p->pack_size)
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))
die("failed to find delta-pack base object %s",
sha1_to_hex(base_sha1));
@@ -1064,23 +1092,7 @@ static void *unpack_delta_entry(unsigned char *base_sha1,
die("failed to read delta-pack base object %s",
sha1_to_hex(base_sha1));
- data = base_sha1 + 20;
- data_size = left - 20;
- delta_data = xmalloc(delta_size);
-
- memset(&stream, 0, sizeof(stream));
-
- stream.next_in = data;
- stream.avail_in = data_size;
- stream.next_out = delta_data;
- stream.avail_out = delta_size;
-
- inflateInit(&stream);
- st = inflate(&stream, Z_FINISH);
- inflateEnd(&stream);
- if ((st != Z_STREAM_END) || stream.total_out != delta_size)
- die("delta data unpack failed");
-
+ delta_data = unpack_compressed_entry(p, offset + 20, delta_size);
result = patch_delta(base, base_size,
delta_data, delta_size,
&result_size);
@@ -1092,33 +1104,6 @@ static void *unpack_delta_entry(unsigned char *base_sha1,
return result;
}
-static void *unpack_non_delta_entry(unsigned char *data,
- unsigned long size,
- unsigned long left)
-{
- int st;
- z_stream stream;
- unsigned char *buffer;
-
- buffer = xmalloc(size + 1);
- buffer[size] = 0;
- memset(&stream, 0, sizeof(stream));
- stream.next_in = data;
- stream.avail_in = left;
- stream.next_out = buffer;
- stream.avail_out = size;
-
- inflateInit(&stream);
- st = inflate(&stream, Z_FINISH);
- inflateEnd(&stream);
- if ((st != Z_STREAM_END) || stream.total_out != size) {
- free(buffer);
- return NULL;
- }
-
- return buffer;
-}
-
static void *unpack_entry(struct pack_entry *entry,
char *type, unsigned long *sizep)
{
@@ -1139,36 +1124,23 @@ void *unpack_entry_gently(struct pack_entry *entry,
char *type, unsigned long *sizep)
{
struct packed_git *p = entry->p;
- unsigned long offset, size, left;
- unsigned char *pack;
+ unsigned long offset, size;
enum object_type kind;
- void *retval;
offset = unpack_object_header(p, entry->offset, &kind, &size);
- pack = (unsigned char *) p->pack_base + offset;
- left = p->pack_size - offset;
switch (kind) {
case OBJ_DELTA:
- retval = unpack_delta_entry(pack, size, left, type, sizep, p);
- return retval;
+ return unpack_delta_entry(p, offset, size, type, sizep);
case OBJ_COMMIT:
- strcpy(type, commit_type);
- break;
case OBJ_TREE:
- strcpy(type, tree_type);
- break;
case OBJ_BLOB:
- strcpy(type, blob_type);
- break;
case OBJ_TAG:
- strcpy(type, tag_type);
- break;
+ strcpy(type, type_names[kind]);
+ *sizep = size;
+ return unpack_compressed_entry(p, offset, size);
default:
return NULL;
}
- *sizep = size;
- retval = unpack_non_delta_entry(pack, size, left);
- return retval;
}
int num_packed_objects(const struct packed_git *p)
@@ -1183,7 +1155,7 @@ int nth_packed_object_sha1(const struct packed_git *p, int n,
void *index = p->index_base + 256;
if (n < 0 || num_packed_objects(p) <= n)
return -1;
- memcpy(sha1, (char *) index + (24 * n) + 4, 20);
+ hashcpy(sha1, (unsigned char *) index + (24 * n) + 4);
return 0;
}
@@ -1197,10 +1169,10 @@ int find_pack_entry_one(const unsigned char *sha1,
do {
int mi = (lo + hi) / 2;
- int cmp = memcmp((char *) index + (24 * mi) + 4, sha1, 20);
+ int cmp = hashcmp((unsigned char *)index + (24 * mi) + 4, sha1);
if (!cmp) {
e->offset = ntohl(*((unsigned int *) ((char *) index + (24 * mi))));
- memcpy(e->sha1, sha1, 20);
+ hashcpy(e->sha1, sha1);
e->p = p;
return 1;
}
@@ -1246,7 +1218,7 @@ int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep
z_stream stream;
char hdr[128];
- map = map_sha1_file_internal(sha1, &mapsize);
+ map = map_sha1_file(sha1, &mapsize);
if (!map) {
struct pack_entry e;
@@ -1291,7 +1263,7 @@ void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size
if (find_pack_entry(sha1, &e))
return read_packed_sha1(sha1, type, size);
- map = map_sha1_file_internal(sha1, &mapsize);
+ map = map_sha1_file(sha1, &mapsize);
if (map) {
buf = unpack_sha1_file(map, mapsize, type, size);
munmap(map, mapsize);
@@ -1313,7 +1285,7 @@ void *read_object_with_reference(const unsigned char *sha1,
unsigned long isize;
unsigned char actual_sha1[20];
- memcpy(actual_sha1, sha1, 20);
+ hashcpy(actual_sha1, sha1);
while (1) {
int ref_length = -1;
const char *ref_type = NULL;
@@ -1324,7 +1296,7 @@ void *read_object_with_reference(const unsigned char *sha1,
if (!strcmp(type, required_type)) {
*size = isize;
if (actual_sha1_return)
- memcpy(actual_sha1_return, actual_sha1, 20);
+ hashcpy(actual_sha1_return, actual_sha1);
return buffer;
}
/* Handle references */
@@ -1519,7 +1491,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
*/
filename = write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
if (returnsha1)
- memcpy(returnsha1, sha1, 20);
+ hashcpy(returnsha1, sha1);
if (has_sha1_file(sha1))
return 0;
fd = open(filename, O_RDONLY);
@@ -1629,7 +1601,7 @@ int write_sha1_to_fd(int fd, const unsigned char *sha1)
{
int retval;
unsigned long objsize;
- void *buf = map_sha1_file_internal(sha1, &objsize);
+ void *buf = map_sha1_file(sha1, &objsize);
if (buf) {
retval = write_buffer(fd, buf, objsize);
@@ -1706,7 +1678,7 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
unlink(tmpfile);
return error("File %s corrupted", sha1_to_hex(sha1));
}
- if (memcmp(sha1, real_sha1, 20)) {
+ if (hashcmp(sha1, real_sha1)) {
unlink(tmpfile);
return error("File %s has bad hash", sha1_to_hex(sha1));
}
@@ -1768,7 +1740,7 @@ int read_pipe(int fd, char** return_buf, unsigned long* return_size)
off += iret;
if (off == size) {
size *= 2;
- buf = realloc(buf, size);
+ buf = xrealloc(buf, size);
}
}
} while (iret > 0);
diff --git a/sha1_name.c b/sha1_name.c
index c5a05fa..3f6b77c 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -84,7 +84,7 @@ static int find_short_packed_object(int len, const unsigned char *match, unsigne
int cmp;
nth_packed_object_sha1(p, mid, now);
- cmp = memcmp(match, now, 20);
+ cmp = hashcmp(match, now);
if (!cmp) {
first = mid;
break;
@@ -103,10 +103,10 @@ static int find_short_packed_object(int len, const unsigned char *match, unsigne
!match_sha(len, match, next)) {
/* unique within this pack */
if (!found) {
- memcpy(found_sha1, now, 20);
+ hashcpy(found_sha1, now);
found++;
}
- else if (memcmp(found_sha1, now, 20)) {
+ else if (hashcmp(found_sha1, now)) {
found = 2;
break;
}
@@ -120,7 +120,7 @@ static int find_short_packed_object(int len, const unsigned char *match, unsigne
}
}
if (found == 1)
- memcpy(sha1, found_sha1, 20);
+ hashcpy(sha1, found_sha1);
return found;
}
@@ -140,13 +140,13 @@ static int find_unique_short_object(int len, char *canonical,
if (1 < has_unpacked || 1 < has_packed)
return SHORT_NAME_AMBIGUOUS;
if (has_unpacked != has_packed) {
- memcpy(sha1, (has_packed ? packed_sha1 : unpacked_sha1), 20);
+ hashcpy(sha1, (has_packed ? packed_sha1 : unpacked_sha1));
return 0;
}
/* Both have unique ones -- do they match? */
- if (memcmp(packed_sha1, unpacked_sha1, 20))
+ if (hashcmp(packed_sha1, unpacked_sha1))
return SHORT_NAME_AMBIGUOUS;
- memcpy(sha1, packed_sha1, 20);
+ hashcpy(sha1, packed_sha1);
return 0;
}
@@ -159,7 +159,7 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
if (len < MINIMUM_ABBREV)
return -1;
- memset(res, 0, 20);
+ hashclr(res);
memset(canonical, 'x', 40);
for (i = 0; i < len ;i++) {
unsigned char c = name[i];
@@ -191,7 +191,7 @@ const char *find_unique_abbrev(const unsigned char *sha1, int len)
int status, is_null;
static char hex[41];
- is_null = !memcmp(sha1, null_sha1, 20);
+ is_null = is_null_sha1(sha1);
memcpy(hex, sha1_to_hex(sha1), 40);
if (len == 40 || !len)
return hex;
@@ -320,13 +320,13 @@ static int get_parent(const char *name, int len,
if (parse_commit(commit))
return -1;
if (!idx) {
- memcpy(result, commit->object.sha1, 20);
+ hashcpy(result, commit->object.sha1);
return 0;
}
p = commit->parents;
while (p) {
if (!--idx) {
- memcpy(result, p->item->object.sha1, 20);
+ hashcpy(result, p->item->object.sha1);
return 0;
}
p = p->next;
@@ -347,9 +347,9 @@ static int get_nth_ancestor(const char *name, int len,
if (!commit || parse_commit(commit) || !commit->parents)
return -1;
- memcpy(sha1, commit->parents->item->object.sha1, 20);
+ hashcpy(sha1, commit->parents->item->object.sha1);
}
- memcpy(result, sha1, 20);
+ hashcpy(result, sha1);
return 0;
}
@@ -401,7 +401,7 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
o = deref_tag(o, name, sp - name - 2);
if (!o || (!o->parsed && !parse_object(o->sha1)))
return -1;
- memcpy(sha1, o->sha1, 20);
+ hashcpy(sha1, o->sha1);
}
else {
/* At this point, the syntax look correct, so
@@ -413,7 +413,7 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
if (!o || (!o->parsed && !parse_object(o->sha1)))
return -1;
if (o->type == expected_type) {
- memcpy(sha1, o->sha1, 20);
+ hashcpy(sha1, o->sha1);
return 0;
}
if (o->type == OBJ_TAG)
@@ -520,7 +520,7 @@ int get_sha1(const char *name, unsigned char *sha1)
memcmp(ce->name, cp, namelen))
break;
if (ce_stage(ce) == stage) {
- memcpy(sha1, ce->sha1, 20);
+ hashcpy(sha1, ce->sha1);
return 0;
}
pos++;
diff --git a/ssh-fetch.c b/ssh-fetch.c
index c7d8fa8..b006c5c 100644
--- a/ssh-fetch.c
+++ b/ssh-fetch.c
@@ -17,7 +17,7 @@
static int fd_in;
static int fd_out;
-static unsigned char remote_version = 0;
+static unsigned char remote_version;
static unsigned char local_version = 1;
static ssize_t force_write(int fd, void *buffer, size_t length)
@@ -36,9 +36,9 @@ static ssize_t force_write(int fd, void *buffer, size_t length)
return ret;
}
-static int prefetches = 0;
+static int prefetches;
-static struct object_list *in_transit = NULL;
+static struct object_list *in_transit;
static struct object_list **end_of_transit = &in_transit;
void prefetch(unsigned char *sha1)
@@ -59,7 +59,7 @@ void prefetch(unsigned char *sha1)
}
static char conn_buf[4096];
-static size_t conn_buf_posn = 0;
+static size_t conn_buf_posn;
int fetch(unsigned char *sha1)
{
@@ -67,7 +67,7 @@ int fetch(unsigned char *sha1)
signed char remote;
struct object_list *temp;
- if (memcmp(sha1, in_transit->item->sha1, 20)) {
+ if (hashcmp(sha1, in_transit->item->sha1)) {
/* we must have already fetched it to clean the queue */
return has_sha1_file(sha1) ? 0 : -1;
}
diff --git a/ssh-upload.c b/ssh-upload.c
index 2da6661..20b15ea 100644
--- a/ssh-upload.c
+++ b/ssh-upload.c
@@ -15,9 +15,9 @@
#include <string.h>
static unsigned char local_version = 1;
-static unsigned char remote_version = 0;
+static unsigned char remote_version;
-static int verbose = 0;
+static int verbose;
static int serve_object(int fd_in, int fd_out) {
ssize_t size;
diff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh
new file mode 100755
index 0000000..74f5c2a
--- /dev/null
+++ b/t/t4116-apply-reverse.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-apply in reverse
+
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ for i in a b c d e f g h i j k l m n; do echo $i; done >file1 &&
+ tr "[ijk]" '\''[\0\1\2]'\'' <file1 >file2 &&
+
+ git add file1 file2 &&
+ git commit -m initial &&
+ git tag initial &&
+
+ for i in a b c g h i J K L m o n p q; do echo $i; done >file1 &&
+ tr "[mon]" '\''[\0\1\2]'\'' <file1 >file2 &&
+
+ git commit -a -m second &&
+ git tag second &&
+
+ git diff --binary initial second >patch
+
+'
+
+test_expect_success 'apply in forward' '
+
+ T0=`git rev-parse "second^{tree}"` &&
+ git reset --hard initial &&
+ git apply --index --binary patch &&
+ T1=`git write-tree` &&
+ test "$T0" = "$T1"
+'
+
+test_expect_success 'apply in reverse' '
+
+ git reset --hard second &&
+ git apply --reverse --binary --index patch &&
+ git diff >diff &&
+ diff -u /dev/null diff
+
+'
+
+test_expect_success 'setup separate repository lacking postimage' '
+
+ git tar-tree initial initial | tar xf - &&
+ (
+ cd initial && git init-db && git add .
+ ) &&
+
+ git tar-tree second second | tar xf - &&
+ (
+ cd second && git init-db && git add .
+ )
+
+'
+
+test_expect_success 'apply in forward without postimage' '
+
+ T0=`git rev-parse "second^{tree}"` &&
+ (
+ cd initial &&
+ git apply --index --binary ../patch &&
+ T1=`git write-tree` &&
+ test "$T0" = "$T1"
+ )
+'
+
+test_expect_success 'apply in reverse without postimage' '
+
+ T0=`git rev-parse "initial^{tree}"` &&
+ (
+ cd second &&
+ git apply --index --binary --reverse ../patch &&
+ T1=`git write-tree` &&
+ test "$T0" = "$T1"
+ )
+'
+
+test_done
diff --git a/t/t4117-apply-reject.sh b/t/t4117-apply-reject.sh
new file mode 100755
index 0000000..b4de075
--- /dev/null
+++ b/t/t4117-apply-reject.sh
@@ -0,0 +1,157 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-apply with rejects
+
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
+ do
+ echo $i
+ done >file1 &&
+ cat file1 >saved.file1 &&
+ git update-index --add file1 &&
+ git commit -m initial &&
+
+ for i in 1 2 A B 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 D 21
+ do
+ echo $i
+ done >file1 &&
+ git diff >patch.1 &&
+ cat file1 >clean &&
+
+ for i in 1 E 2 3 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 F 21
+ do
+ echo $i
+ done >expected &&
+
+ mv file1 file2 &&
+ git update-index --add --remove file1 file2 &&
+ git diff -M HEAD >patch.2 &&
+
+ rm -f file1 file2 &&
+ mv saved.file1 file1 &&
+ git update-index --add --remove file1 file2 &&
+
+ for i in 1 E 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 F 21
+ do
+ echo $i
+ done >file1 &&
+
+ cat file1 >saved.file1
+'
+
+test_expect_success 'apply without --reject should fail' '
+
+ if git apply patch.1
+ then
+ echo "Eh? Why?"
+ exit 1
+ fi
+
+ diff -u file1 saved.file1
+'
+
+test_expect_success 'apply without --reject should fail' '
+
+ if git apply --verbose patch.1
+ then
+ echo "Eh? Why?"
+ exit 1
+ fi
+
+ diff -u file1 saved.file1
+'
+
+test_expect_success 'apply with --reject should fail but update the file' '
+
+ cat saved.file1 >file1 &&
+ rm -f file1.rej file2.rej &&
+
+ if git apply --reject patch.1
+ then
+ echo "succeeds with --reject?"
+ exit 1
+ fi
+
+ diff -u file1 expected &&
+
+ cat file1.rej &&
+
+ if test -f file2.rej
+ then
+ echo "file2 should not have been touched"
+ exit 1
+ fi
+'
+
+test_expect_success 'apply with --reject should fail but update the file' '
+
+ cat saved.file1 >file1 &&
+ rm -f file1.rej file2.rej file2 &&
+
+ if git apply --reject patch.2 >rejects
+ then
+ echo "succeeds with --reject?"
+ exit 1
+ fi
+
+ test -f file1 && {
+ echo "file1 still exists?"
+ exit 1
+ }
+ diff -u file2 expected &&
+
+ cat file2.rej &&
+
+ if test -f file1.rej
+ then
+ echo "file2 should not have been touched"
+ exit 1
+ fi
+
+'
+
+test_expect_success 'the same test with --verbose' '
+
+ cat saved.file1 >file1 &&
+ rm -f file1.rej file2.rej file2 &&
+
+ if git apply --reject --verbose patch.2 >rejects
+ then
+ echo "succeeds with --reject?"
+ exit 1
+ fi
+
+ test -f file1 && {
+ echo "file1 still exists?"
+ exit 1
+ }
+ diff -u file2 expected &&
+
+ cat file2.rej &&
+
+ if test -f file1.rej
+ then
+ echo "file2 should not have been touched"
+ exit 1
+ fi
+
+'
+
+test_expect_success 'apply cleanly with --verbose' '
+
+ git cat-file -p HEAD:file1 >file1 &&
+ rm -f file?.rej file2 &&
+
+ git apply --verbose patch.1 &&
+
+ diff -u file1 clean
+'
+
+test_done
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 900ca93..b7fcdb3 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -60,6 +60,10 @@ test_expect_success \
grep -E "^R100.+path0/README.+path2/README"'
test_expect_success \
+ 'succeed when source is a prefix of destination' \
+ 'git-mv path2/COPYING path2/COPYING-renamed'
+
+test_expect_success \
'moving whole subdirectory into subdirectory' \
'git-mv path2 path1'
@@ -78,4 +82,8 @@ test_expect_failure \
'do not move directory over existing directory' \
'mkdir path0 && mkdir path0/path2 && git-mv path2 path0'
+test_expect_success \
+ 'move into "."' \
+ 'git-mv path1/path2/ .'
+
test_done
diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh
index 00a7d76..6bfb899 100755
--- a/t/t7002-grep.sh
+++ b/t/t7002-grep.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2006 Junio C Hamano
#
-test_description='git grep -w
+test_description='git grep various.
'
. ./test-lib.sh
@@ -19,7 +19,9 @@ test_expect_success setup '
echo x x xx x >x &&
echo y yy >y &&
echo zzz > z &&
- git add file x y z &&
+ mkdir t &&
+ echo test >t/t &&
+ git add file x y z t/t &&
git commit -m initial
'
@@ -80,6 +82,31 @@ do
diff expected actual
fi
'
+
+ test_expect_success "grep $L (t-1)" '
+ echo "${HC}t/t:1:test" >expected &&
+ git grep -n -e test $H >actual &&
+ diff expected actual
+ '
+
+ test_expect_success "grep $L (t-2)" '
+ echo "${HC}t:1:test" >expected &&
+ (
+ cd t &&
+ git grep -n -e test $H
+ ) >actual &&
+ diff expected actual
+ '
+
+ test_expect_success "grep $L (t-3)" '
+ echo "${HC}t/t:1:test" >expected &&
+ (
+ cd t &&
+ git grep --full-name -n -e test $H
+ ) >actual &&
+ diff expected actual
+ '
+
done
test_done
diff --git a/templates/hooks--commit-msg b/templates/hooks--commit-msg
index 643822d..0b906ca 100644
--- a/templates/hooks--commit-msg
+++ b/templates/hooks--commit-msg
@@ -11,4 +11,8 @@
# This example catches duplicate Signed-off-by lines.
test "" = "$(grep '^Signed-off-by: ' "$1" |
- sort | uniq -c | sed -e '/^[ ]*1 /d')"
+ sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
+ echo >&2 Duplicate Signed-off-by lines.
+ exit 1
+}
+
diff --git a/tree-diff.c b/tree-diff.c
index 1cdf8aa..7e2f4f0 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -15,7 +15,8 @@ static char *malloc_base(const char *base, const char *path, int pathlen)
return newbase;
}
-static int show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base);
+static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,
+ const char *base);
static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
{
@@ -38,8 +39,7 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
show_entry(opt, "+", t2, base);
return 1;
}
- if (!opt->find_copies_harder &&
- !memcmp(sha1, sha2, 20) && mode1 == mode2)
+ if (!opt->find_copies_harder && !hashcmp(sha1, sha2) && mode1 == mode2)
return 0;
/*
@@ -131,7 +131,8 @@ static void show_tree(struct diff_options *opt, const char *prefix, struct tree_
}
/* A file entry went away or appeared */
-static int show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base)
+static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,
+ const char *base)
{
unsigned mode;
const char *path;
@@ -152,11 +153,9 @@ static int show_entry(struct diff_options *opt, const char *prefix, struct tree_
free(tree);
free(newbase);
- return 0;
+ } else {
+ opt->add_remove(opt, prefix[0], mode, sha1, base, path);
}
-
- opt->add_remove(opt, prefix[0], mode, sha1, base, path);
- return 0;
}
int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
diff --git a/tree-walk.c b/tree-walk.c
index 3f83e98..14cc5ae 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -179,7 +179,7 @@ static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char
if (cmp < 0)
break;
if (entrylen == namelen) {
- memcpy(result, sha1, 20);
+ hashcpy(result, sha1);
return 0;
}
if (name[entrylen] != '/')
@@ -187,7 +187,7 @@ static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char
if (!S_ISDIR(*mode))
break;
if (++entrylen == namelen) {
- memcpy(result, sha1, 20);
+ hashcpy(result, sha1);
return 0;
}
return get_tree_entry(sha1, name + entrylen, result, mode);
diff --git a/tree.c b/tree.c
index a6032e3..ea386e5 100644
--- a/tree.c
+++ b/tree.c
@@ -25,7 +25,7 @@ static int read_one_entry(const unsigned char *sha1, const char *base, int basel
ce->ce_flags = create_ce_flags(baselen + len, stage);
memcpy(ce->name, base, baselen);
memcpy(ce->name + baselen, pathname, len+1);
- memcpy(ce->sha1, sha1, 20);
+ hashcpy(ce->sha1, sha1);
return add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
}
@@ -144,7 +144,7 @@ struct tree *lookup_tree(const unsigned char *sha1)
return (struct tree *) obj;
}
-static int track_tree_refs(struct tree *item)
+static void track_tree_refs(struct tree *item)
{
int n_refs = 0, i;
struct object_refs *refs;
@@ -174,7 +174,6 @@ static int track_tree_refs(struct tree *item)
refs->ref[i++] = obj;
}
set_object_refs(&item->object, refs);
- return 0;
}
int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size)
diff --git a/unpack-trees.c b/unpack-trees.c
index a20639b..3ac0289 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -200,7 +200,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
any_files = 1;
- memcpy(ce->sha1, posns[i]->sha1, 20);
+ hashcpy(ce->sha1, posns[i]->sha1);
src[i + o->merge] = ce;
subposns[i] = df_conflict_list;
posns[i] = posns[i]->next;
@@ -278,7 +278,7 @@ static void unlink_entry(char *name)
}
}
-static volatile int progress_update = 0;
+static volatile sig_atomic_t progress_update;
static void progress_interval(int signum)
{
@@ -417,7 +417,7 @@ static int same(struct cache_entry *a, struct cache_entry *b)
if (!a && !b)
return 1;
return a->ce_mode == b->ce_mode &&
- !memcmp(a->sha1, b->sha1, 20);
+ !hashcmp(a->sha1, b->sha1);
}
diff --git a/upload-pack.c b/upload-pack.c
index 07ecdb4..51ce936 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -14,14 +14,12 @@ static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=n
#define THEY_HAVE (1U << 0)
#define OUR_REF (1U << 1)
#define WANTED (1U << 2)
-#define MAX_HAS 256
-#define MAX_NEEDS 256
-static int nr_has = 0, nr_needs = 0, multi_ack = 0, nr_our_refs = 0;
-static int use_thin_pack = 0;
-static unsigned char has_sha1[MAX_HAS][20];
-static unsigned char needs_sha1[MAX_NEEDS][20];
-static unsigned int timeout = 0;
-static int use_sideband = 0;
+static int multi_ack, nr_our_refs;
+static int use_thin_pack;
+static struct object_array have_obj;
+static struct object_array want_obj;
+static unsigned int timeout;
+static int use_sideband;
static void reset_timeout(void)
{
@@ -83,7 +81,7 @@ static void create_pack_file(void)
*/
int lp_pipe[2], pu_pipe[2], pe_pipe[2];
pid_t pid_rev_list, pid_pack_objects;
- int create_full_pack = (nr_our_refs == nr_needs && !nr_has);
+ int create_full_pack = (nr_our_refs == want_obj.nr && !have_obj.nr);
char data[8193], progress[128];
char abort_msg[] = "aborting due to possible repository "
"corruption on the remote side.";
@@ -107,7 +105,7 @@ static void create_pack_file(void)
use_thin_pack = 0; /* no point doing it */
}
else
- args = nr_has + nr_needs + 5;
+ args = have_obj.nr + want_obj.nr + 5;
p = xmalloc(args * sizeof(char *));
argv = (const char **) p;
buf = xmalloc(args * 45);
@@ -118,20 +116,22 @@ static void create_pack_file(void)
close(lp_pipe[1]);
*p++ = "rev-list";
*p++ = use_thin_pack ? "--objects-edge" : "--objects";
- if (create_full_pack || MAX_NEEDS <= nr_needs)
+ if (create_full_pack)
*p++ = "--all";
else {
- for (i = 0; i < nr_needs; i++) {
+ for (i = 0; i < want_obj.nr; i++) {
+ struct object *o = want_obj.objects[i].item;
*p++ = buf;
- memcpy(buf, sha1_to_hex(needs_sha1[i]), 41);
+ memcpy(buf, sha1_to_hex(o->sha1), 41);
buf += 41;
}
}
if (!create_full_pack)
- for (i = 0; i < nr_has; i++) {
+ for (i = 0; i < have_obj.nr; i++) {
+ struct object *o = have_obj.objects[i].item;
*p++ = buf;
*buf++ = '^';
- memcpy(buf, sha1_to_hex(has_sha1[i]), 41);
+ memcpy(buf, sha1_to_hex(o->sha1), 41);
buf += 41;
}
*p++ = NULL;
@@ -322,28 +322,29 @@ static void create_pack_file(void)
static int got_sha1(char *hex, unsigned char *sha1)
{
+ struct object *o;
+
if (get_sha1_hex(hex, sha1))
die("git-upload-pack: expected SHA1 object, got '%s'", hex);
if (!has_sha1_file(sha1))
return 0;
- if (nr_has < MAX_HAS) {
- struct object *o = lookup_object(sha1);
- if (!(o && o->parsed))
- o = parse_object(sha1);
- if (!o)
- die("oops (%s)", sha1_to_hex(sha1));
- if (o->type == OBJ_COMMIT) {
- struct commit_list *parents;
- if (o->flags & THEY_HAVE)
- return 0;
- o->flags |= THEY_HAVE;
- for (parents = ((struct commit*)o)->parents;
- parents;
- parents = parents->next)
- parents->item->object.flags |= THEY_HAVE;
- }
- memcpy(has_sha1[nr_has++], sha1, 20);
+
+ o = lookup_object(sha1);
+ if (!(o && o->parsed))
+ o = parse_object(sha1);
+ if (!o)
+ die("oops (%s)", sha1_to_hex(sha1));
+ if (o->type == OBJ_COMMIT) {
+ struct commit_list *parents;
+ if (o->flags & THEY_HAVE)
+ return 0;
+ o->flags |= THEY_HAVE;
+ for (parents = ((struct commit*)o)->parents;
+ parents;
+ parents = parents->next)
+ parents->item->object.flags |= THEY_HAVE;
}
+ add_object_array(o, NULL, &have_obj);
return 1;
}
@@ -361,26 +362,24 @@ static int get_common_commits(void)
reset_timeout();
if (!len) {
- if (nr_has == 0 || multi_ack)
+ if (have_obj.nr == 0 || multi_ack)
packet_write(1, "NAK\n");
continue;
}
len = strip(line, len);
if (!strncmp(line, "have ", 5)) {
if (got_sha1(line+5, sha1) &&
- (multi_ack || nr_has == 1)) {
- if (nr_has >= MAX_HAS)
- multi_ack = 0;
+ (multi_ack || have_obj.nr == 1)) {
packet_write(1, "ACK %s%s\n",
- sha1_to_hex(sha1),
- multi_ack ? " continue" : "");
+ sha1_to_hex(sha1),
+ multi_ack ? " continue" : "");
if (multi_ack)
- memcpy(last_sha1, sha1, 20);
+ hashcpy(last_sha1, sha1);
}
continue;
}
if (!strcmp(line, "done")) {
- if (nr_has > 0) {
+ if (have_obj.nr > 0) {
if (multi_ack)
packet_write(1, "ACK %s\n",
sha1_to_hex(last_sha1));
@@ -393,31 +392,21 @@ static int get_common_commits(void)
}
}
-static int receive_needs(void)
+static void receive_needs(void)
{
static char line[1000];
- int len, needs;
+ int len;
- needs = 0;
for (;;) {
struct object *o;
- unsigned char dummy[20], *sha1_buf;
+ unsigned char sha1_buf[20];
len = packet_read_line(0, line, sizeof(line));
reset_timeout();
if (!len)
- return needs;
-
- sha1_buf = dummy;
- if (needs == MAX_NEEDS) {
- fprintf(stderr,
- "warning: supporting only a max of %d requests. "
- "sending everything instead.\n",
- MAX_NEEDS);
- }
- else if (needs < MAX_NEEDS)
- sha1_buf = needs_sha1[needs];
+ return;
- if (strncmp("want ", line, 5) || get_sha1_hex(line+5, sha1_buf))
+ if (strncmp("want ", line, 5) ||
+ get_sha1_hex(line+5, sha1_buf))
die("git-upload-pack: protocol error, "
"expected to get sha, not '%s'", line);
if (strstr(line+45, "multi_ack"))
@@ -440,7 +429,7 @@ static int receive_needs(void)
die("git-upload-pack: not our ref %s", line+5);
if (!(o->flags & WANTED)) {
o->flags |= WANTED;
- needs++;
+ add_object_array(o, NULL, &want_obj);
}
}
}
@@ -470,18 +459,17 @@ static int send_ref(const char *refname, const unsigned char *sha1)
return 0;
}
-static int upload_pack(void)
+static void upload_pack(void)
{
reset_timeout();
head_ref(send_ref);
for_each_ref(send_ref);
packet_flush(1);
- nr_needs = receive_needs();
- if (!nr_needs)
- return 0;
- get_common_commits();
- create_pack_file();
- return 0;
+ receive_needs();
+ if (want_obj.nr) {
+ get_common_commits();
+ create_pack_file();
+ }
}
int main(int argc, char **argv)
diff --git a/write_or_die.c b/write_or_die.c
new file mode 100644
index 0000000..ab4cb8a
--- /dev/null
+++ b/write_or_die.c
@@ -0,0 +1,20 @@
+#include "cache.h"
+
+void write_or_die(int fd, const void *buf, size_t count)
+{
+ const char *p = buf;
+ ssize_t written;
+
+ while (count > 0) {
+ written = xwrite(fd, p, count);
+ if (written == 0)
+ die("disk full?");
+ else if (written < 0) {
+ if (errno == EPIPE)
+ exit(0);
+ die("write error (%s)", strerror(errno));
+ }
+ count -= written;
+ p += written;
+ }
+}
diff --git a/xdiff-interface.c b/xdiff-interface.c
index 6a82da7..08602f5 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -69,9 +69,9 @@ int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
for (i = 0; i < nbuf; i++) {
if (mb[i].ptr[mb[i].size-1] != '\n') {
/* Incomplete line */
- priv->remainder = realloc(priv->remainder,
- priv->remainder_size +
- mb[i].size);
+ priv->remainder = xrealloc(priv->remainder,
+ priv->remainder_size +
+ mb[i].size);
memcpy(priv->remainder + priv->remainder_size,
mb[i].ptr, mb[i].size);
priv->remainder_size += mb[i].size;
@@ -83,9 +83,9 @@ int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
consume_one(priv, mb[i].ptr, mb[i].size);
continue;
}
- priv->remainder = realloc(priv->remainder,
- priv->remainder_size +
- mb[i].size);
+ priv->remainder = xrealloc(priv->remainder,
+ priv->remainder_size +
+ mb[i].size);
memcpy(priv->remainder + priv->remainder_size,
mb[i].ptr, mb[i].size);
consume_one(priv, priv->remainder,