зеркало из https://github.com/microsoft/git.git
Merge branch 'master' into jc/web
* master: (114 commits) gitweb: Fix setting $/ in parse_commit() daemon: do not die on older clients. xdiff/xemit.c (xdl_find_func): Elide trailing white space in a context header. git-clone: honor --quiet Documentation for the [remote] config prune-packed: Fix uninitialized variable. ignore-errors requires cl git-send-email: do not pass custom Date: header Use column indexes in git-cvsserver where necessary. gitweb: Add '..' (up directory) to tree view if applicable gitweb: Improve git_print_page_path pager: default to LESS=FRSX not LESS=FRS Make prune also run prune-packed git-vc: better installation instructions gitweb: Do not esc_html $basedir argument to git_print_tree_entry gitweb: Whitespace cleanup - tabs are for indent, spaces are for align (2) Fix usagestring for git-branch git-merge: show usage if run without arguments add the capability for index-pack to read from a stream git-clone: define die() and use it. ...
This commit is contained in:
Коммит
ccbb3d17ac
|
@ -230,6 +230,22 @@ pull.octopus::
|
|||
pull.twohead::
|
||||
The default merge strategy to use when pulling a single branch.
|
||||
|
||||
remote.<name>.url::
|
||||
The URL of a remote repository. See gitlink:git-fetch[1] or
|
||||
gitlink:git-push[1].
|
||||
|
||||
remote.<name>.fetch::
|
||||
The default set of "refspec" for gitlink:git-fetch[1]. See
|
||||
gitlink:git-fetch[1].
|
||||
|
||||
remote.<name>.push::
|
||||
The default set of "refspec" for gitlink:git-push[1]. See
|
||||
gitlink:git-push[1].
|
||||
|
||||
repack.usedeltabaseoffset::
|
||||
Allow gitlink:git-repack[1] to create packs that uses
|
||||
delta-base offset. Defaults to false.
|
||||
|
||||
show.difftree::
|
||||
The default gitlink:git-diff-tree[1] arguments to be used
|
||||
for gitlink:git-show[1].
|
||||
|
|
|
@ -16,6 +16,11 @@
|
|||
The width of the filename part can be controlled by
|
||||
giving another width to it separated by a comma.
|
||||
|
||||
--numstat::
|
||||
Similar to \--stat, but shows number of added and
|
||||
deleted lines in decimal notation and pathname without
|
||||
abbreviation, to make it more machine friendly.
|
||||
|
||||
--summary::
|
||||
Output a condensed summary of extended header information
|
||||
such as creations, renames and mode changes.
|
||||
|
|
|
@ -7,7 +7,7 @@ git-cherry-pick - Apply the change introduced by an existing commit
|
|||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-cherry-pick' [--edit] [-n] [-r] <commit>
|
||||
'git-cherry-pick' [--edit] [-n] [-x] <commit>
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
@ -24,13 +24,22 @@ OPTIONS
|
|||
With this option, `git-cherry-pick` will let you edit the commit
|
||||
message prior committing.
|
||||
|
||||
-r|--replay::
|
||||
Usually the command appends which commit was
|
||||
-x::
|
||||
Cause the command to append which commit was
|
||||
cherry-picked after the original commit message when
|
||||
making a commit. This option, '--replay', causes it to
|
||||
use the original commit message intact. This is useful
|
||||
when you are reordering the patches in your private tree
|
||||
before publishing.
|
||||
making a commit. Do not use this option if you are
|
||||
cherry-picking from your private branch because the
|
||||
information is useless to the recipient. If on the
|
||||
other hand you are cherry-picking between two publicly
|
||||
visible branches (e.g. backporting a fix to a
|
||||
maintenance branch for an older release from a
|
||||
development branch), adding this information can be
|
||||
useful.
|
||||
|
||||
-r|--replay::
|
||||
It used to be that the command defaulted to do `-x`
|
||||
described above, and `-r` was to disable it. Now the
|
||||
default is not to do `-x` so this option is a no-op.
|
||||
|
||||
-n|--no-commit::
|
||||
Usually the command automatically creates a commit with
|
||||
|
|
|
@ -14,7 +14,7 @@ SYNOPSIS
|
|||
[-v | --invert-match] [-h|-H] [--full-name]
|
||||
[-E | --extended-regexp] [-G | --basic-regexp] [-F | --fixed-strings]
|
||||
[-n] [-l | --files-with-matches] [-L | --files-without-match]
|
||||
[-c | --count]
|
||||
[-c | --count] [--all-match]
|
||||
[-A <post-context>] [-B <pre-context>] [-C <context>]
|
||||
[-f <file>] [-e] <pattern> [--and|--or|--not|(|)|-e <pattern>...]
|
||||
[<tree>...]
|
||||
|
@ -96,6 +96,11 @@ OPTIONS
|
|||
higher precedence than `--or`. `-e` has to be used for all
|
||||
patterns.
|
||||
|
||||
--all-match::
|
||||
When giving multiple pattern expressions combined with `--or`,
|
||||
this flag is specified to limit the match to files that
|
||||
have lines to match all of them.
|
||||
|
||||
`<tree>...`::
|
||||
Search blobs in the trees for specified patterns.
|
||||
|
||||
|
@ -111,6 +116,10 @@ git grep -e \'#define\' --and \( -e MAX_PATH -e PATH_MAX \)::
|
|||
Looks for a line that has `#define` and either `MAX_PATH` or
|
||||
`PATH_MAX`.
|
||||
|
||||
git grep --all-match -e NODE -e Unexpected::
|
||||
Looks for a line that has `NODE` or `Unexpected` in
|
||||
files that have lines that match both.
|
||||
|
||||
Author
|
||||
------
|
||||
Originally written by Linus Torvalds <torvalds@osdl.org>, later
|
||||
|
|
|
@ -34,7 +34,7 @@ OPTIONS
|
|||
Report the list of objects being walked locally and the
|
||||
list of objects successfully sent to the remote repository.
|
||||
|
||||
<ref>...:
|
||||
<ref>...::
|
||||
The remote refs to update.
|
||||
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ git-pack-objects - Create a packed archive of objects
|
|||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-pack-objects' [-q] [--no-reuse-delta] [--non-empty]
|
||||
'git-pack-objects' [-q] [--no-reuse-delta] [--delta-base-offset] [--non-empty]
|
||||
[--local] [--incremental] [--window=N] [--depth=N]
|
||||
[--revs [--unpacked | --all]*] [--stdout | base-name] < object-list
|
||||
|
||||
|
@ -71,11 +71,11 @@ base-name::
|
|||
--all::
|
||||
This implies `--revs`. In addition to the list of
|
||||
revision arguments read from the standard input, pretend
|
||||
as if all refs under `$GIT_DIR/refs` are specifed to be
|
||||
as if all refs under `$GIT_DIR/refs` are specified to be
|
||||
included.
|
||||
|
||||
--window and --depth::
|
||||
These two options affects how the objects contained in
|
||||
--window=[N], --depth=[N]::
|
||||
These two options affect how the objects contained in
|
||||
the pack are stored using delta compression. The
|
||||
objects are first internally sorted by type, size and
|
||||
optionally names and compared against the other objects
|
||||
|
@ -84,6 +84,7 @@ base-name::
|
|||
it too deep affects the performance on the unpacker
|
||||
side, because delta data needs to be applied that many
|
||||
times to get to the necessary object.
|
||||
The default value for both --window and --depth is 10.
|
||||
|
||||
--incremental::
|
||||
This flag causes an object already in a pack ignored
|
||||
|
@ -110,6 +111,17 @@ base-name::
|
|||
This flag tells the command not to reuse existing deltas
|
||||
but compute them from scratch.
|
||||
|
||||
--delta-base-offset::
|
||||
A packed archive can express base object of a delta as
|
||||
either 20-byte object name or as an offset in the
|
||||
stream, but older version of git does not understand the
|
||||
latter. By default, git-pack-objects only uses the
|
||||
former format for better compatibility. This option
|
||||
allows the command to use the latter format for
|
||||
compactness. Depending on the average delta chain
|
||||
length, this option typically shrinks the resulting
|
||||
packfile by 3-5 per-cent.
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
|
|
|
@ -7,7 +7,7 @@ git-rebase - Rebase local commits to a new head
|
|||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-rebase' [--merge] [--onto <newbase>] <upstream> [<branch>]
|
||||
'git-rebase' [-v] [--merge] [--onto <newbase>] <upstream> [<branch>]
|
||||
|
||||
'git-rebase' --continue | --skip | --abort
|
||||
|
||||
|
@ -121,6 +121,9 @@ OPTIONS
|
|||
is used instead (`git-merge-recursive` when merging a single
|
||||
head, `git-merge-octopus` otherwise). This implies --merge.
|
||||
|
||||
-v, \--verbose::
|
||||
Display a diffstat of what changed upstream since the last rebase.
|
||||
|
||||
include::merge-strategies.txt[]
|
||||
|
||||
NOTES
|
||||
|
|
|
@ -57,13 +57,28 @@ OPTIONS
|
|||
`git update-server-info`.
|
||||
|
||||
--window=[N], --depth=[N]::
|
||||
These two options affects how the objects contained in the pack are
|
||||
These two options affect how the objects contained in the pack are
|
||||
stored using delta compression. The objects are first internally
|
||||
sorted by type, size and optionally names and compared against the
|
||||
other objects within `--window` to see if using delta compression saves
|
||||
space. `--depth` limits the maximum delta depth; making it too deep
|
||||
affects the performance on the unpacker side, because delta data needs
|
||||
to be applied that many times to get to the necessary object.
|
||||
The default value for both --window and --depth is 10.
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
When configuration variable `repack.UseDeltaBaseOffset` is set
|
||||
for the repository, the command passes `--delta-base-offset`
|
||||
option to `git-pack-objects`; this typically results in slightly
|
||||
smaller packs, but the generated packs are incompatible with
|
||||
versions of git older than (and including) v1.4.3; do not set
|
||||
the variable in a repository that older version of git needs to
|
||||
be able to read (this includes repositories from which packs can
|
||||
be copied out over http or rsync, and people who obtained packs
|
||||
that way can try to use older git with it).
|
||||
|
||||
|
||||
Author
|
||||
|
|
|
@ -111,7 +111,9 @@ SPECIFYING REVISIONS
|
|||
|
||||
A revision parameter typically, but not necessarily, names a
|
||||
commit object. They use what is called an 'extended SHA1'
|
||||
syntax.
|
||||
syntax. Here are various ways to spell object names. The
|
||||
ones listed near the end of this list are to name trees and
|
||||
blobs contained in a commit.
|
||||
|
||||
* The full SHA1 object name (40-byte hexadecimal string), or
|
||||
a substring of such that is unique within the repository.
|
||||
|
@ -119,6 +121,9 @@ syntax.
|
|||
name the same commit object if there are no other object in
|
||||
your repository whose object name starts with dae86e.
|
||||
|
||||
* An output from `git-describe`; i.e. a closest tag, followed by a
|
||||
dash, a 'g', and an abbreviated object name.
|
||||
|
||||
* A symbolic ref name. E.g. 'master' typically means the commit
|
||||
object referenced by $GIT_DIR/refs/heads/master. If you
|
||||
happen to have both heads/master and tags/master, you can
|
||||
|
@ -156,6 +161,15 @@ syntax.
|
|||
and dereference the tag recursively until a non-tag object is
|
||||
found.
|
||||
|
||||
* A suffix ':' followed by a path; this names the blob or tree
|
||||
at the given path in the tree-ish object named by the part
|
||||
before the colon.
|
||||
|
||||
* A colon, optionally followed by a stage number (0 to 3) and a
|
||||
colon, followed by a path; this names a blob object in the
|
||||
index at the given path. Missing stage number (and the colon
|
||||
that follows it) names an stage 0 entry.
|
||||
|
||||
Here is an illustration, by Jon Loeliger. Both node B and C are
|
||||
a commit parents of commit node A. Parent commits are ordered
|
||||
left-to-right.
|
||||
|
|
|
@ -43,7 +43,7 @@ OPTIONS
|
|||
<directory>::
|
||||
The repository to update.
|
||||
|
||||
<ref>...:
|
||||
<ref>...::
|
||||
The remote refs to update.
|
||||
|
||||
|
||||
|
|
|
@ -7,16 +7,29 @@ git-shortlog - Summarize 'git log' output
|
|||
|
||||
SYNOPSIS
|
||||
--------
|
||||
git-log --pretty=short | 'git-shortlog'
|
||||
git-log --pretty=short | 'git-shortlog' [-h] [-n] [-s]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Summarizes 'git log' output in a format suitable for inclusion
|
||||
in release announcements. Each commit will be grouped by author
|
||||
in release announcements. Each commit will be grouped by author and
|
||||
the first line of the commit message will be shown.
|
||||
|
||||
Additionally, "[PATCH]" will be stripped from the commit description.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
-h::
|
||||
Print a short usage message and exit.
|
||||
|
||||
-n::
|
||||
Sort output according to the number of commits per author instead
|
||||
of author alphabetic order.
|
||||
|
||||
-s::
|
||||
Supress commit description and provide a commit count summary only.
|
||||
|
||||
FILES
|
||||
-----
|
||||
'.mailmap'::
|
||||
|
|
|
@ -37,7 +37,9 @@ COMMANDS
|
|||
'init'::
|
||||
Creates an empty git repository with additional metadata
|
||||
directories for git-svn. The Subversion URL must be specified
|
||||
as a command-line argument.
|
||||
as a command-line argument. Optionally, the target directory
|
||||
to operate on can be specified as a second argument. Normally
|
||||
this command initializes the current directory.
|
||||
|
||||
'fetch'::
|
||||
|
||||
|
@ -63,7 +65,30 @@ manually joining branches on commit.
|
|||
This is advantageous over 'commit' (below) because it produces
|
||||
cleaner, more linear history.
|
||||
|
||||
'log'::
|
||||
This should make it easy to look up svn log messages when svn
|
||||
users refer to -r/--revision numbers.
|
||||
|
||||
The following features from `svn log' are supported:
|
||||
|
||||
--revision=<n>[:<n>] - is supported, non-numeric args are not:
|
||||
HEAD, NEXT, BASE, PREV, etc ...
|
||||
-v/--verbose - it's not completely compatible with
|
||||
the --verbose output in svn log, but
|
||||
reasonably close.
|
||||
--limit=<n> - is NOT the same as --max-count,
|
||||
doesn't count merged/excluded commits
|
||||
--incremental - supported
|
||||
|
||||
New features:
|
||||
|
||||
--show-commit - shows the git commit sha1, as well
|
||||
--oneline - our version of --pretty=oneline
|
||||
|
||||
Any other arguments are passed directly to `git log'
|
||||
|
||||
'commit'::
|
||||
You should consider using 'dcommit' instead of this command.
|
||||
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
|
||||
|
@ -86,12 +111,49 @@ manually joining branches on commit.
|
|||
directories. The output is suitable for appending to
|
||||
the $GIT_DIR/info/exclude file.
|
||||
|
||||
'commit-diff'::
|
||||
Commits the diff of two tree-ish arguments from the
|
||||
command-line. This command is intended for interopability with
|
||||
git-svnimport and does not rely on being inside an git-svn
|
||||
init-ed repository. This command takes three arguments, (a) the
|
||||
original tree to diff against, (b) the new tree result, (c) the
|
||||
URL of the target Subversion repository. The final argument
|
||||
(URL) may be omitted if you are working from a git-svn-aware
|
||||
repository (that has been init-ed with git-svn).
|
||||
|
||||
'graft-branches'::
|
||||
This command attempts to detect merges/branches from already
|
||||
imported history. Techniques used currently include regexes,
|
||||
file copies, and tree-matches). This command generates (or
|
||||
modifies) the $GIT_DIR/info/grafts file. This command is
|
||||
considered experimental, and inherently flawed because
|
||||
merge-tracking in SVN is inherently flawed and inconsistent
|
||||
across different repositories.
|
||||
|
||||
'multi-init'::
|
||||
This command supports git-svnimport-like command-line syntax for
|
||||
importing repositories that are layed out as recommended by the
|
||||
SVN folks. This is a bit more tolerant than the git-svnimport
|
||||
command-line syntax and doesn't require the user to figure out
|
||||
where the repository URL ends and where the repository path
|
||||
begins.
|
||||
|
||||
'multi-fetch'::
|
||||
This runs fetch on all known SVN branches we're tracking. This
|
||||
will NOT discover new branches (unlike git-svnimport), so
|
||||
multi-init will need to be re-run (it's idempotent).
|
||||
|
||||
--
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
--
|
||||
|
||||
--shared::
|
||||
--template=<template_directory>::
|
||||
Only used with the 'init' command.
|
||||
These are passed directly to gitlink:git-init-db[1].
|
||||
|
||||
-r <ARG>::
|
||||
--revision <ARG>::
|
||||
|
||||
|
@ -115,7 +177,7 @@ git-rev-list --pretty=oneline output can be used.
|
|||
|
||||
--rmdir::
|
||||
|
||||
Only used with the 'commit' command.
|
||||
Only used with the 'dcommit', 'commit' and 'commit-diff' commands.
|
||||
|
||||
Remove directories from the SVN tree if there are no files left
|
||||
behind. SVN can version empty directories, and they are not
|
||||
|
@ -128,7 +190,7 @@ repo-config key: svn.rmdir
|
|||
-e::
|
||||
--edit::
|
||||
|
||||
Only used with the 'commit' command.
|
||||
Only used with the 'dcommit', 'commit' and 'commit-diff' commands.
|
||||
|
||||
Edit the commit message before committing to SVN. This is off by
|
||||
default for objects that are commits, and forced on when committing
|
||||
|
@ -139,7 +201,7 @@ repo-config key: svn.edit
|
|||
-l<num>::
|
||||
--find-copies-harder::
|
||||
|
||||
Both of these are only used with the 'commit' command.
|
||||
Only used with the 'dcommit', 'commit' and 'commit-diff' commands.
|
||||
|
||||
They are both passed directly to git-diff-tree see
|
||||
gitlink:git-diff-tree[1] for more information.
|
||||
|
@ -164,7 +226,26 @@ 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
|
||||
repo-config key: svn.authorsfile
|
||||
|
||||
-q::
|
||||
--quiet::
|
||||
Make git-svn less verbose. This only affects git-svn if you
|
||||
have the SVN::* libraries installed and are using them.
|
||||
|
||||
--repack[=<n>]::
|
||||
--repack-flags=<flags>
|
||||
These should help keep disk usage sane for large fetches
|
||||
with many revisions.
|
||||
|
||||
--repack takes an optional argument for the number of revisions
|
||||
to fetch before repacking. This defaults to repacking every
|
||||
1000 commits fetched if no argument is specified.
|
||||
|
||||
--repack-flags are passed directly to gitlink:git-repack[1].
|
||||
|
||||
repo-config key: svn.repack
|
||||
repo-config key: svn.repackflags
|
||||
|
||||
-m::
|
||||
--merge::
|
||||
|
@ -215,6 +296,28 @@ section on
|
|||
'<<tracking-multiple-repos,Tracking Multiple Repositories or Branches>>'
|
||||
for more information on using GIT_SVN_ID.
|
||||
|
||||
--follow-parent::
|
||||
This is especially helpful when we're tracking a directory
|
||||
that has been moved around within the repository, or if we
|
||||
started tracking a branch and never tracked the trunk it was
|
||||
descended from.
|
||||
|
||||
This relies on the SVN::* libraries to work.
|
||||
|
||||
repo-config key: svn.followparent
|
||||
|
||||
--no-metadata::
|
||||
This gets rid of the git-svn-id: lines at the end of every commit.
|
||||
|
||||
With this, you lose the ability to use the rebuild command. If
|
||||
you ever lose your .git/svn/git-svn/.rev_db file, you won't be
|
||||
able to fetch again, either. This is fine for one-shot imports.
|
||||
|
||||
The 'git-svn log' command will not work on repositories using this,
|
||||
either.
|
||||
|
||||
repo-config key: svn.nometadata
|
||||
|
||||
--
|
||||
|
||||
COMPATIBILITY OPTIONS
|
||||
|
@ -231,6 +334,9 @@ for tracking the remote.
|
|||
--no-ignore-externals::
|
||||
Only used with the 'fetch' and 'rebuild' command.
|
||||
|
||||
This command has no effect when you are using the SVN::*
|
||||
libraries with git, svn:externals are always avoided.
|
||||
|
||||
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.
|
||||
|
@ -264,7 +370,7 @@ Basic Examples
|
|||
Tracking and contributing to an Subversion managed-project:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
# Initialize a tree (like git init-db):
|
||||
# Initialize a repo (like git init-db):
|
||||
git-svn init http://svn.foo.org/project/trunk
|
||||
# Fetch remote revisions:
|
||||
git-svn fetch
|
||||
|
@ -312,8 +418,8 @@ branches or directories in a Subversion repository, git-svn has a simple
|
|||
hack to allow it to track an arbitrary number of related _or_ unrelated
|
||||
SVN repositories via one git repository. Simply set the GIT_SVN_ID
|
||||
environment variable to a name other other than "git-svn" (the default)
|
||||
and git-svn will ignore the contents of the $GIT_DIR/git-svn directory
|
||||
and instead do all of its work in $GIT_DIR/$GIT_SVN_ID for that
|
||||
and git-svn will ignore the contents of the $GIT_DIR/svn/git-svn directory
|
||||
and instead do all of its work in $GIT_DIR/svn/$GIT_SVN_ID for that
|
||||
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.
|
||||
|
@ -341,6 +447,9 @@ This allows you to tie unfetched SVN revision 375 to your current HEAD:
|
|||
|
||||
Advanced Example: Tracking a Reorganized Repository
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Note: this example is now obsolete if you have SVN::* libraries
|
||||
installed. Simply use --follow-parent when fetching.
|
||||
|
||||
If you're tracking a directory that has moved, or otherwise been
|
||||
branched or tagged off of another directory in the repository and you
|
||||
care about the full history of the project, then you can read this
|
||||
|
@ -371,20 +480,18 @@ he needed to continue tracking /ufoai/trunk where /trunk left off.
|
|||
|
||||
BUGS
|
||||
----
|
||||
If somebody commits a conflicting changeset to SVN at a bad moment
|
||||
(right before you commit) causing a conflict and your commit to fail,
|
||||
your svn working tree ($GIT_DIR/git-svn/tree) may be dirtied. The
|
||||
easiest thing to do is probably just to rm -rf $GIT_DIR/git-svn/tree and
|
||||
run 'rebuild'.
|
||||
|
||||
If you are not using the SVN::* Perl libraries and somebody commits a
|
||||
conflicting changeset to SVN at a bad moment (right before you commit)
|
||||
causing a conflict and your commit to fail, your svn working tree
|
||||
($GIT_DIR/git-svn/tree) may be dirtied. The easiest thing to do is
|
||||
probably just to rm -rf $GIT_DIR/git-svn/tree and run 'rebuild'.
|
||||
|
||||
We ignore all SVN properties except svn:executable. Too difficult to
|
||||
map them since we rely heavily on git write-tree being _exactly_ the
|
||||
same on both the SVN and git working trees and I prefer not to clutter
|
||||
working trees with metadata files.
|
||||
|
||||
svn:keywords can't be ignored in Subversion (at least I don't know of
|
||||
a way to ignore them).
|
||||
|
||||
Renamed and copied directories are not detected by git and hence not
|
||||
tracked when committing to SVN. I do not plan on adding support for
|
||||
this as it's quite difficult and time-consuming to get working for all
|
||||
|
|
|
@ -243,6 +243,9 @@ gitlink:git-update-server-info[1]::
|
|||
Updates auxiliary information on a dumb server to help
|
||||
clients discover references and packs on it.
|
||||
|
||||
gitlink:git-upload-archive[1]::
|
||||
Invoked by 'git-archive' to send a generated archive.
|
||||
|
||||
gitlink:git-upload-pack[1]::
|
||||
Invoked by 'git-fetch-pack' to push
|
||||
what are asked for.
|
||||
|
|
|
@ -179,7 +179,7 @@ object name::
|
|||
character hexadecimal encoding of the hash of the object (possibly
|
||||
followed by a white space).
|
||||
|
||||
object type:
|
||||
object type::
|
||||
One of the identifiers "commit","tree","tag" and "blob" describing
|
||||
the type of an object.
|
||||
|
||||
|
@ -324,7 +324,7 @@ tag::
|
|||
A tag is most typically used to mark a particular point in the
|
||||
commit ancestry chain.
|
||||
|
||||
unmerged index:
|
||||
unmerged index::
|
||||
An index which contains unmerged index entries.
|
||||
|
||||
working tree::
|
||||
|
|
|
@ -51,6 +51,14 @@ lines are used for `git-push` and `git-fetch`/`git-pull`,
|
|||
respectively. Multiple `Push:` and `Pull:` lines may
|
||||
be specified for additional branch mappings.
|
||||
|
||||
Or, equivalently, in the `$GIT_DIR/config` (note the use
|
||||
of `fetch` instead of `Pull:`):
|
||||
|
||||
[remote "<remote>"]
|
||||
url = <url>
|
||||
push = <refspec>
|
||||
fetch = <refspec>
|
||||
|
||||
The name of a file in `$GIT_DIR/branches` directory can be
|
||||
specified as an older notation short-hand; the named
|
||||
file should contain a single line, a URL in one of the
|
||||
|
|
7
Makefile
7
Makefile
|
@ -764,6 +764,8 @@ $(LIB_FILE): $(LIB_OBJS)
|
|||
rm -f $@ && $(AR) rcs $@ $(LIB_OBJS)
|
||||
|
||||
XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o
|
||||
$(XDIFF_OBJS): xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \
|
||||
xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h
|
||||
|
||||
$(XDIFF_LIB): $(XDIFF_OBJS)
|
||||
rm -f $@ && $(AR) rcs $@ $(XDIFF_OBJS)
|
||||
|
@ -860,8 +862,9 @@ git.spec: git.spec.in
|
|||
mv $@+ $@
|
||||
|
||||
GIT_TARNAME=git-$(GIT_VERSION)
|
||||
dist: git.spec git-tar-tree
|
||||
./git-tar-tree HEAD^{tree} $(GIT_TARNAME) > $(GIT_TARNAME).tar
|
||||
dist: git.spec git-archive
|
||||
./git-archive --format=tar \
|
||||
--prefix=$(GIT_TARNAME)/ HEAD^{tree} > $(GIT_TARNAME).tar
|
||||
@mkdir -p $(GIT_TARNAME)
|
||||
@cp git.spec $(GIT_TARNAME)
|
||||
@echo $(GIT_VERSION) > $(GIT_TARNAME)/version
|
||||
|
|
|
@ -145,6 +145,7 @@ static int write_zip_entry(const unsigned char *sha1,
|
|||
{
|
||||
struct zip_local_header header;
|
||||
struct zip_dir_header dirent;
|
||||
unsigned long attr2;
|
||||
unsigned long compressed_size;
|
||||
unsigned long uncompressed_size;
|
||||
unsigned long crc;
|
||||
|
@ -172,12 +173,16 @@ static int write_zip_entry(const unsigned char *sha1,
|
|||
|
||||
if (S_ISDIR(mode)) {
|
||||
method = 0;
|
||||
attr2 = 16;
|
||||
result = READ_TREE_RECURSIVE;
|
||||
out = NULL;
|
||||
uncompressed_size = 0;
|
||||
compressed_size = 0;
|
||||
} else if (S_ISREG(mode)) {
|
||||
method = zlib_compression_level == 0 ? 0 : 8;
|
||||
} else if (S_ISREG(mode) || S_ISLNK(mode)) {
|
||||
method = 0;
|
||||
attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) : 0;
|
||||
if (S_ISREG(mode) && zlib_compression_level != 0)
|
||||
method = 8;
|
||||
result = 0;
|
||||
buffer = read_sha1_file(sha1, type, &size);
|
||||
if (!buffer)
|
||||
|
@ -213,8 +218,8 @@ static int write_zip_entry(const unsigned char *sha1,
|
|||
}
|
||||
|
||||
copy_le32(dirent.magic, 0x02014b50);
|
||||
copy_le16(dirent.creator_version, 0);
|
||||
copy_le16(dirent.version, 20);
|
||||
copy_le16(dirent.creator_version, S_ISLNK(mode) ? 0x0317 : 0);
|
||||
copy_le16(dirent.version, 10);
|
||||
copy_le16(dirent.flags, 0);
|
||||
copy_le16(dirent.compression_method, method);
|
||||
copy_le16(dirent.mtime, zip_time);
|
||||
|
@ -227,7 +232,7 @@ static int write_zip_entry(const unsigned char *sha1,
|
|||
copy_le16(dirent.comment_length, 0);
|
||||
copy_le16(dirent.disk, 0);
|
||||
copy_le16(dirent.attr1, 0);
|
||||
copy_le32(dirent.attr2, 0);
|
||||
copy_le32(dirent.attr2, attr2);
|
||||
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);
|
||||
|
@ -236,7 +241,7 @@ static int write_zip_entry(const unsigned char *sha1,
|
|||
zip_dir_entries++;
|
||||
|
||||
copy_le32(header.magic, 0x04034b50);
|
||||
copy_le16(header.version, 20);
|
||||
copy_le16(header.version, 10);
|
||||
copy_le16(header.flags, 0);
|
||||
copy_le16(header.compression_method, method);
|
||||
copy_le16(header.mtime, zip_time);
|
||||
|
|
|
@ -360,7 +360,7 @@ static int gitdiff_hdrend(const char *line, struct patch *patch)
|
|||
static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew)
|
||||
{
|
||||
if (!orig_name && !isnull)
|
||||
return find_name(line, NULL, 1, 0);
|
||||
return find_name(line, NULL, 1, TERM_TAB);
|
||||
|
||||
if (orig_name) {
|
||||
int len;
|
||||
|
@ -370,7 +370,7 @@ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name,
|
|||
len = strlen(name);
|
||||
if (isnull)
|
||||
die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr);
|
||||
another = find_name(line, NULL, 1, 0);
|
||||
another = find_name(line, NULL, 1, TERM_TAB);
|
||||
if (!another || memcmp(another, name, len))
|
||||
die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
|
||||
free(another);
|
||||
|
@ -934,6 +934,7 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
|
|||
switch (*line) {
|
||||
default:
|
||||
return -1;
|
||||
case '\n': /* newer GNU diff, an empty context line */
|
||||
case ' ':
|
||||
oldlines--;
|
||||
newlines--;
|
||||
|
@ -1623,6 +1624,14 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
|
|||
first = '-';
|
||||
}
|
||||
switch (first) {
|
||||
case '\n':
|
||||
/* Newer GNU diff, empty context line */
|
||||
if (plen < 0)
|
||||
/* ... followed by '\No newline'; nothing */
|
||||
break;
|
||||
old[oldsize++] = '\n';
|
||||
new[newsize++] = '\n';
|
||||
break;
|
||||
case ' ':
|
||||
case '-':
|
||||
memcpy(old + oldsize, patch + 1, plen);
|
||||
|
@ -1783,8 +1792,6 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
|
|||
{
|
||||
const char *name = patch->old_name ? patch->old_name : patch->new_name;
|
||||
unsigned char sha1[20];
|
||||
unsigned char hdr[50];
|
||||
int hdrlen;
|
||||
|
||||
/* For safety, we require patch index line to contain
|
||||
* full 40-byte textual SHA1 for old and new, at least for now.
|
||||
|
@ -1800,8 +1807,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
|
|||
/* See if the old one matches what the patch
|
||||
* applies to.
|
||||
*/
|
||||
write_sha1_file_prepare(desc->buffer, desc->size,
|
||||
blob_type, sha1, hdr, &hdrlen);
|
||||
hash_sha1_file(desc->buffer, desc->size, blob_type, sha1);
|
||||
if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
|
||||
return error("the patch applies to '%s' (%s), "
|
||||
"which does not match the "
|
||||
|
@ -1846,8 +1852,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
|
|||
name);
|
||||
|
||||
/* verify that the result matches */
|
||||
write_sha1_file_prepare(desc->buffer, desc->size, blob_type,
|
||||
sha1, hdr, &hdrlen);
|
||||
hash_sha1_file(desc->buffer, desc->size, blob_type, sha1);
|
||||
if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
|
||||
return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)", name, patch->new_sha1_prefix, sha1_to_hex(sha1));
|
||||
}
|
||||
|
@ -2112,7 +2117,7 @@ static void numstat_patch_list(struct patch *patch)
|
|||
quote_c_style(name, NULL, stdout, 0);
|
||||
else
|
||||
fputs(name, stdout);
|
||||
putchar('\n');
|
||||
putchar(line_termination);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ static int run_remote_archiver(const char *remote, int argc,
|
|||
die("git-archive: expected a flush");
|
||||
|
||||
/* Now, start reading from fd[0] and spit it out to stdout */
|
||||
rv = recv_sideband("archive", fd[0], 1, 2, buf, sizeof(buf));
|
||||
rv = recv_sideband("archive", fd[0], 1, 2);
|
||||
close(fd[0]);
|
||||
rv |= finish_connect(pid);
|
||||
|
||||
|
|
|
@ -596,6 +596,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||
GREP_CLOSE_PAREN);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--all-match", arg)) {
|
||||
opt.all_match = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("-e", arg)) {
|
||||
if (1 < argc) {
|
||||
append_grep_pattern(&opt, argv[1],
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include <sys/time.h>
|
||||
#include <signal.h>
|
||||
|
||||
static const char pack_usage[] = "git-pack-objects [-q] [--no-reuse-delta] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] [--revs [--unpacked | --all]*] [--stdout | base-name] <ref-list | <object-list]";
|
||||
static const char pack_usage[] = "git-pack-objects [-q] [--no-reuse-delta] [--delta-base-offset] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] [--revs [--unpacked | --all]*] [--stdout | base-name] <ref-list | <object-list]";
|
||||
|
||||
struct object_entry {
|
||||
unsigned char sha1[20];
|
||||
|
@ -29,6 +29,7 @@ struct object_entry {
|
|||
enum object_type type;
|
||||
enum object_type in_pack_type; /* could be delta */
|
||||
unsigned long delta_size; /* delta data size (uncompressed) */
|
||||
#define in_pack_header_size delta_size /* only when reusing pack data */
|
||||
struct object_entry *delta; /* delta base object */
|
||||
struct packed_git *in_pack; /* already in pack */
|
||||
unsigned int in_pack_offset;
|
||||
|
@ -60,6 +61,8 @@ static int non_empty;
|
|||
static int no_reuse_delta;
|
||||
static int local;
|
||||
static int incremental;
|
||||
static int allow_ofs_delta;
|
||||
|
||||
static struct object_entry **sorted_by_sha, **sorted_by_type;
|
||||
static struct object_entry *objects;
|
||||
static int nr_objects, nr_alloc, nr_result;
|
||||
|
@ -84,17 +87,25 @@ static int object_ix_hashsz;
|
|||
* Pack index for existing packs give us easy access to the offsets into
|
||||
* corresponding pack file where each object's data starts, but the entries
|
||||
* do not store the size of the compressed representation (uncompressed
|
||||
* size is easily available by examining the pack entry header). We build
|
||||
* a hashtable of existing packs (pack_revindex), and keep reverse index
|
||||
* here -- pack index file is sorted by object name mapping to offset; this
|
||||
* pack_revindex[].revindex array is an ordered list of offsets, so if you
|
||||
* know the offset of an object, next offset is where its packed
|
||||
* representation ends.
|
||||
* size is easily available by examining the pack entry header). It is
|
||||
* also rather expensive to find the sha1 for an object given its offset.
|
||||
*
|
||||
* We build a hashtable of existing packs (pack_revindex), and keep reverse
|
||||
* index here -- pack index file is sorted by object name mapping to offset;
|
||||
* this pack_revindex[].revindex array is a list of offset/index_nr pairs
|
||||
* ordered by offset, so if you know the offset of an object, next offset
|
||||
* is where its packed representation ends and the index_nr can be used to
|
||||
* get the object sha1 from the main index.
|
||||
*/
|
||||
struct revindex_entry {
|
||||
unsigned int offset;
|
||||
unsigned int nr;
|
||||
};
|
||||
struct pack_revindex {
|
||||
struct packed_git *p;
|
||||
unsigned long *revindex;
|
||||
} *pack_revindex = NULL;
|
||||
struct revindex_entry *revindex;
|
||||
};
|
||||
static struct pack_revindex *pack_revindex;
|
||||
static int pack_revindex_hashsz;
|
||||
|
||||
/*
|
||||
|
@ -141,14 +152,9 @@ static void prepare_pack_ix(void)
|
|||
|
||||
static int cmp_offset(const void *a_, const void *b_)
|
||||
{
|
||||
unsigned long a = *(unsigned long *) a_;
|
||||
unsigned long b = *(unsigned long *) b_;
|
||||
if (a < b)
|
||||
return -1;
|
||||
else if (a == b)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
const struct revindex_entry *a = a_;
|
||||
const struct revindex_entry *b = b_;
|
||||
return (a->offset < b->offset) ? -1 : (a->offset > b->offset) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -161,25 +167,27 @@ static void prepare_pack_revindex(struct pack_revindex *rix)
|
|||
int i;
|
||||
void *index = p->index_base + 256;
|
||||
|
||||
rix->revindex = xmalloc(sizeof(unsigned long) * (num_ent + 1));
|
||||
rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1));
|
||||
for (i = 0; i < num_ent; i++) {
|
||||
unsigned int hl = *((unsigned int *)((char *) index + 24*i));
|
||||
rix->revindex[i] = ntohl(hl);
|
||||
rix->revindex[i].offset = ntohl(hl);
|
||||
rix->revindex[i].nr = i;
|
||||
}
|
||||
/* This knows the pack format -- the 20-byte trailer
|
||||
* follows immediately after the last object data.
|
||||
*/
|
||||
rix->revindex[num_ent] = p->pack_size - 20;
|
||||
qsort(rix->revindex, num_ent, sizeof(unsigned long), cmp_offset);
|
||||
rix->revindex[num_ent].offset = p->pack_size - 20;
|
||||
rix->revindex[num_ent].nr = -1;
|
||||
qsort(rix->revindex, num_ent, sizeof(*rix->revindex), cmp_offset);
|
||||
}
|
||||
|
||||
static unsigned long find_packed_object_size(struct packed_git *p,
|
||||
unsigned long ofs)
|
||||
static struct revindex_entry * find_packed_object(struct packed_git *p,
|
||||
unsigned int ofs)
|
||||
{
|
||||
int num;
|
||||
int lo, hi;
|
||||
struct pack_revindex *rix;
|
||||
unsigned long *revindex;
|
||||
struct revindex_entry *revindex;
|
||||
num = pack_revindex_ix(p);
|
||||
if (num < 0)
|
||||
die("internal error: pack revindex uninitialized");
|
||||
|
@ -191,10 +199,10 @@ static unsigned long find_packed_object_size(struct packed_git *p,
|
|||
hi = num_packed_objects(p) + 1;
|
||||
do {
|
||||
int mi = (lo + hi) / 2;
|
||||
if (revindex[mi] == ofs) {
|
||||
return revindex[mi+1] - ofs;
|
||||
if (revindex[mi].offset == ofs) {
|
||||
return revindex + mi;
|
||||
}
|
||||
else if (ofs < revindex[mi])
|
||||
else if (ofs < revindex[mi].offset)
|
||||
hi = mi;
|
||||
else
|
||||
lo = mi + 1;
|
||||
|
@ -202,6 +210,20 @@ static unsigned long find_packed_object_size(struct packed_git *p,
|
|||
die("internal error: pack revindex corrupt");
|
||||
}
|
||||
|
||||
static unsigned long find_packed_object_size(struct packed_git *p,
|
||||
unsigned long ofs)
|
||||
{
|
||||
struct revindex_entry *entry = find_packed_object(p, ofs);
|
||||
return entry[1].offset - ofs;
|
||||
}
|
||||
|
||||
static unsigned char *find_packed_object_name(struct packed_git *p,
|
||||
unsigned long ofs)
|
||||
{
|
||||
struct revindex_entry *entry = find_packed_object(p, ofs);
|
||||
return (unsigned char *)(p->index_base + 256) + 24 * entry->nr + 4;
|
||||
}
|
||||
|
||||
static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
|
||||
{
|
||||
unsigned long othersize, delta_size;
|
||||
|
@ -232,7 +254,7 @@ static int encode_header(enum object_type type, unsigned long size, unsigned cha
|
|||
int n = 1;
|
||||
unsigned char c;
|
||||
|
||||
if (type < OBJ_COMMIT || type > OBJ_DELTA)
|
||||
if (type < OBJ_COMMIT || type > OBJ_REF_DELTA)
|
||||
die("bad type %d", type);
|
||||
|
||||
c = (type << 4) | (size & 15);
|
||||
|
@ -247,6 +269,10 @@ static int encode_header(enum object_type type, unsigned long size, unsigned cha
|
|||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* we are going to reuse the existing object data as is. make
|
||||
* sure it is not corrupt.
|
||||
*/
|
||||
static int check_inflate(unsigned char *data, unsigned long len, unsigned long expect)
|
||||
{
|
||||
z_stream stream;
|
||||
|
@ -278,32 +304,6 @@ static int check_inflate(unsigned char *data, unsigned long len, unsigned long e
|
|||
return st;
|
||||
}
|
||||
|
||||
/*
|
||||
* we are going to reuse the existing pack entry data. make
|
||||
* sure it is not corrupt.
|
||||
*/
|
||||
static int revalidate_pack_entry(struct object_entry *entry, unsigned char *data, unsigned long len)
|
||||
{
|
||||
enum object_type type;
|
||||
unsigned long size, used;
|
||||
|
||||
if (pack_to_stdout)
|
||||
return 0;
|
||||
|
||||
/* the caller has already called use_packed_git() for us,
|
||||
* so it is safe to access the pack data from mmapped location.
|
||||
* make sure the entry inflates correctly.
|
||||
*/
|
||||
used = unpack_object_header_gently(data, len, &type, &size);
|
||||
if (!used)
|
||||
return -1;
|
||||
if (type == OBJ_DELTA)
|
||||
used += 20; /* skip base object name */
|
||||
data += used;
|
||||
len -= used;
|
||||
return check_inflate(data, len, entry->size);
|
||||
}
|
||||
|
||||
static int revalidate_loose_object(struct object_entry *entry,
|
||||
unsigned char *map,
|
||||
unsigned long mapsize)
|
||||
|
@ -334,13 +334,10 @@ static unsigned long write_object(struct sha1file *f,
|
|||
enum object_type obj_type;
|
||||
int to_reuse = 0;
|
||||
|
||||
if (entry->preferred_base)
|
||||
return 0;
|
||||
|
||||
obj_type = entry->type;
|
||||
if (! entry->in_pack)
|
||||
to_reuse = 0; /* can't reuse what we don't have */
|
||||
else if (obj_type == OBJ_DELTA)
|
||||
else if (obj_type == OBJ_REF_DELTA || obj_type == OBJ_OFS_DELTA)
|
||||
to_reuse = 1; /* check_object() decided it for us */
|
||||
else if (obj_type != entry->in_pack_type)
|
||||
to_reuse = 0; /* pack has delta which is unusable */
|
||||
|
@ -380,18 +377,35 @@ static unsigned long write_object(struct sha1file *f,
|
|||
if (entry->delta) {
|
||||
buf = delta_against(buf, size, entry);
|
||||
size = entry->delta_size;
|
||||
obj_type = OBJ_DELTA;
|
||||
obj_type = (allow_ofs_delta && entry->delta->offset) ?
|
||||
OBJ_OFS_DELTA : OBJ_REF_DELTA;
|
||||
}
|
||||
/*
|
||||
* The object header is a byte of 'type' followed by zero or
|
||||
* more bytes of length. For deltas, the 20 bytes of delta
|
||||
* sha1 follows that.
|
||||
* more bytes of length.
|
||||
*/
|
||||
hdrlen = encode_header(obj_type, size, header);
|
||||
sha1write(f, header, hdrlen);
|
||||
|
||||
if (entry->delta) {
|
||||
sha1write(f, entry->delta, 20);
|
||||
if (obj_type == OBJ_OFS_DELTA) {
|
||||
/*
|
||||
* Deltas with relative base contain an additional
|
||||
* encoding of the relative offset for the delta
|
||||
* base from this object's position in the pack.
|
||||
*/
|
||||
unsigned long ofs = entry->offset - entry->delta->offset;
|
||||
unsigned pos = sizeof(header) - 1;
|
||||
header[pos] = ofs & 127;
|
||||
while (ofs >>= 7)
|
||||
header[--pos] = 128 | (--ofs & 127);
|
||||
sha1write(f, header + pos, sizeof(header) - pos);
|
||||
hdrlen += sizeof(header) - pos;
|
||||
} else if (obj_type == OBJ_REF_DELTA) {
|
||||
/*
|
||||
* Deltas with a base reference contain
|
||||
* an additional 20 bytes for the base sha1.
|
||||
*/
|
||||
sha1write(f, entry->delta->sha1, 20);
|
||||
hdrlen += 20;
|
||||
}
|
||||
datalen = sha1write_compressed(f, buf, size);
|
||||
|
@ -399,21 +413,40 @@ static unsigned long write_object(struct sha1file *f,
|
|||
}
|
||||
else {
|
||||
struct packed_git *p = entry->in_pack;
|
||||
|
||||
if (entry->delta) {
|
||||
obj_type = (allow_ofs_delta && entry->delta->offset) ?
|
||||
OBJ_OFS_DELTA : OBJ_REF_DELTA;
|
||||
reused_delta++;
|
||||
}
|
||||
hdrlen = encode_header(obj_type, entry->size, header);
|
||||
sha1write(f, header, hdrlen);
|
||||
if (obj_type == OBJ_OFS_DELTA) {
|
||||
unsigned long ofs = entry->offset - entry->delta->offset;
|
||||
unsigned pos = sizeof(header) - 1;
|
||||
header[pos] = ofs & 127;
|
||||
while (ofs >>= 7)
|
||||
header[--pos] = 128 | (--ofs & 127);
|
||||
sha1write(f, header + pos, sizeof(header) - pos);
|
||||
hdrlen += sizeof(header) - pos;
|
||||
} else if (obj_type == OBJ_REF_DELTA) {
|
||||
sha1write(f, entry->delta->sha1, 20);
|
||||
hdrlen += 20;
|
||||
}
|
||||
|
||||
use_packed_git(p);
|
||||
|
||||
datalen = find_packed_object_size(p, entry->in_pack_offset);
|
||||
buf = (char *) p->pack_base + entry->in_pack_offset;
|
||||
|
||||
if (revalidate_pack_entry(entry, buf, datalen))
|
||||
buf = (char *) p->pack_base
|
||||
+ entry->in_pack_offset
|
||||
+ entry->in_pack_header_size;
|
||||
datalen = find_packed_object_size(p, entry->in_pack_offset)
|
||||
- entry->in_pack_header_size;
|
||||
if (!pack_to_stdout && check_inflate(buf, datalen, entry->size))
|
||||
die("corrupt delta in pack %s", sha1_to_hex(entry->sha1));
|
||||
sha1write(f, buf, datalen);
|
||||
unuse_packed_git(p);
|
||||
hdrlen = 0; /* not really */
|
||||
if (obj_type == OBJ_DELTA)
|
||||
reused_delta++;
|
||||
reused++;
|
||||
}
|
||||
if (obj_type == OBJ_DELTA)
|
||||
if (entry->delta)
|
||||
written_delta++;
|
||||
written++;
|
||||
return hdrlen + datalen;
|
||||
|
@ -423,17 +456,16 @@ static unsigned long write_one(struct sha1file *f,
|
|||
struct object_entry *e,
|
||||
unsigned long offset)
|
||||
{
|
||||
if (e->offset)
|
||||
if (e->offset || e->preferred_base)
|
||||
/* offset starts from header size and cannot be zero
|
||||
* if it is written already.
|
||||
*/
|
||||
return offset;
|
||||
e->offset = offset;
|
||||
offset += write_object(f, e);
|
||||
/* if we are deltified, write out its base object. */
|
||||
/* if we are deltified, write out its base object first. */
|
||||
if (e->delta)
|
||||
offset = write_one(f, e->delta, offset);
|
||||
return offset;
|
||||
e->offset = offset;
|
||||
return offset + write_object(f, e);
|
||||
}
|
||||
|
||||
static void write_pack_file(void)
|
||||
|
@ -899,26 +931,64 @@ static void check_object(struct object_entry *entry)
|
|||
char type[20];
|
||||
|
||||
if (entry->in_pack && !entry->preferred_base) {
|
||||
unsigned char base[20];
|
||||
unsigned long size;
|
||||
struct object_entry *base_entry;
|
||||
struct packed_git *p = entry->in_pack;
|
||||
unsigned long left = p->pack_size - entry->in_pack_offset;
|
||||
unsigned long size, used;
|
||||
unsigned char *buf;
|
||||
struct object_entry *base_entry = NULL;
|
||||
|
||||
use_packed_git(p);
|
||||
buf = p->pack_base;
|
||||
buf += entry->in_pack_offset;
|
||||
|
||||
/* We want in_pack_type even if we do not reuse delta.
|
||||
* There is no point not reusing non-delta representations.
|
||||
*/
|
||||
check_reuse_pack_delta(entry->in_pack,
|
||||
entry->in_pack_offset,
|
||||
base, &size,
|
||||
&entry->in_pack_type);
|
||||
used = unpack_object_header_gently(buf, left,
|
||||
&entry->in_pack_type, &size);
|
||||
if (!used || left - used <= 20)
|
||||
die("corrupt pack for %s", sha1_to_hex(entry->sha1));
|
||||
|
||||
/* Check if it is delta, and the base is also an object
|
||||
* we are going to pack. If so we will reuse the existing
|
||||
* delta.
|
||||
*/
|
||||
if (!no_reuse_delta &&
|
||||
entry->in_pack_type == OBJ_DELTA &&
|
||||
(base_entry = locate_object_entry(base)) &&
|
||||
(!base_entry->preferred_base)) {
|
||||
if (!no_reuse_delta) {
|
||||
unsigned char c, *base_name;
|
||||
unsigned long ofs;
|
||||
/* there is at least 20 bytes left in the pack */
|
||||
switch (entry->in_pack_type) {
|
||||
case OBJ_REF_DELTA:
|
||||
base_name = buf + used;
|
||||
used += 20;
|
||||
break;
|
||||
case OBJ_OFS_DELTA:
|
||||
c = buf[used++];
|
||||
ofs = c & 127;
|
||||
while (c & 128) {
|
||||
ofs += 1;
|
||||
if (!ofs || ofs & ~(~0UL >> 7))
|
||||
die("delta base offset overflow in pack for %s",
|
||||
sha1_to_hex(entry->sha1));
|
||||
c = buf[used++];
|
||||
ofs = (ofs << 7) + (c & 127);
|
||||
}
|
||||
if (ofs >= entry->in_pack_offset)
|
||||
die("delta base offset out of bound for %s",
|
||||
sha1_to_hex(entry->sha1));
|
||||
ofs = entry->in_pack_offset - ofs;
|
||||
base_name = find_packed_object_name(p, ofs);
|
||||
break;
|
||||
default:
|
||||
base_name = NULL;
|
||||
}
|
||||
if (base_name)
|
||||
base_entry = locate_object_entry(base_name);
|
||||
}
|
||||
unuse_packed_git(p);
|
||||
entry->in_pack_header_size = used;
|
||||
|
||||
if (base_entry) {
|
||||
|
||||
/* Depth value does not matter - find_deltas()
|
||||
* will never consider reused delta as the
|
||||
|
@ -927,9 +997,9 @@ static void check_object(struct object_entry *entry)
|
|||
*/
|
||||
|
||||
/* uncompressed size of the delta data */
|
||||
entry->size = entry->delta_size = size;
|
||||
entry->size = size;
|
||||
entry->delta = base_entry;
|
||||
entry->type = OBJ_DELTA;
|
||||
entry->type = entry->in_pack_type;
|
||||
|
||||
entry->delta_sibling = base_entry->delta_child;
|
||||
base_entry->delta_child = entry;
|
||||
|
@ -1484,6 +1554,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
|
|||
no_reuse_delta = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--delta-base-offset", arg)) {
|
||||
allow_ofs_delta = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--stdout", arg)) {
|
||||
pack_to_stdout = 1;
|
||||
continue;
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
static const char prune_packed_usage[] =
|
||||
"git-prune-packed [-n]";
|
||||
|
||||
static int dryrun;
|
||||
|
||||
static void prune_dir(int i, DIR *dir, char *pathname, int len)
|
||||
static void prune_dir(int i, DIR *dir, char *pathname, int len, int dryrun)
|
||||
{
|
||||
struct dirent *de;
|
||||
char hex[40];
|
||||
|
@ -31,7 +29,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len)
|
|||
rmdir(pathname);
|
||||
}
|
||||
|
||||
static void prune_packed_objects(void)
|
||||
void prune_packed_objects(int dryrun)
|
||||
{
|
||||
int i;
|
||||
static char pathname[PATH_MAX];
|
||||
|
@ -50,7 +48,7 @@ static void prune_packed_objects(void)
|
|||
d = opendir(pathname);
|
||||
if (!d)
|
||||
continue;
|
||||
prune_dir(i, d, pathname, len + 3);
|
||||
prune_dir(i, d, pathname, len + 3, dryrun);
|
||||
closedir(d);
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +56,7 @@ static void prune_packed_objects(void)
|
|||
int cmd_prune_packed(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i;
|
||||
int dryrun = 0;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
|
@ -73,6 +72,6 @@ int cmd_prune_packed(int argc, const char **argv, const char *prefix)
|
|||
usage(prune_packed_usage);
|
||||
}
|
||||
sync();
|
||||
prune_packed_objects();
|
||||
prune_packed_objects(dryrun);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -255,5 +255,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
|
|||
|
||||
prune_object_dir(get_object_directory());
|
||||
|
||||
sync();
|
||||
prune_packed_objects(show_only);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] < pack-fil
|
|||
|
||||
/* We always read in 4kB chunks. */
|
||||
static unsigned char buffer[4096];
|
||||
static unsigned long offset, len;
|
||||
static unsigned long offset, len, consumed_bytes;
|
||||
static SHA_CTX ctx;
|
||||
|
||||
/*
|
||||
|
@ -51,6 +51,7 @@ static void use(int bytes)
|
|||
die("used more bytes than were available");
|
||||
len -= bytes;
|
||||
offset += bytes;
|
||||
consumed_bytes += bytes;
|
||||
}
|
||||
|
||||
static void *get_data(unsigned long size)
|
||||
|
@ -89,35 +90,49 @@ static void *get_data(unsigned long size)
|
|||
|
||||
struct delta_info {
|
||||
unsigned char base_sha1[20];
|
||||
unsigned long base_offset;
|
||||
unsigned long size;
|
||||
void *delta;
|
||||
unsigned nr;
|
||||
struct delta_info *next;
|
||||
};
|
||||
|
||||
static struct delta_info *delta_list;
|
||||
|
||||
static void add_delta_to_list(unsigned char *base_sha1, void *delta, unsigned long size)
|
||||
static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
|
||||
unsigned long base_offset,
|
||||
void *delta, unsigned long size)
|
||||
{
|
||||
struct delta_info *info = xmalloc(sizeof(*info));
|
||||
|
||||
hashcpy(info->base_sha1, base_sha1);
|
||||
info->base_offset = base_offset;
|
||||
info->size = size;
|
||||
info->delta = delta;
|
||||
info->nr = nr;
|
||||
info->next = delta_list;
|
||||
delta_list = info;
|
||||
}
|
||||
|
||||
static void added_object(unsigned char *sha1, const char *type, void *data, unsigned long size);
|
||||
|
||||
static void write_object(void *buf, unsigned long size, const char *type)
|
||||
{
|
||||
struct obj_info {
|
||||
unsigned long offset;
|
||||
unsigned char sha1[20];
|
||||
if (write_sha1_file(buf, size, type, sha1) < 0)
|
||||
};
|
||||
|
||||
static struct obj_info *obj_list;
|
||||
|
||||
static void added_object(unsigned nr, const char *type, void *data,
|
||||
unsigned long size);
|
||||
|
||||
static void write_object(unsigned nr, void *buf, unsigned long size,
|
||||
const char *type)
|
||||
{
|
||||
if (write_sha1_file(buf, size, type, obj_list[nr].sha1) < 0)
|
||||
die("failed to write object");
|
||||
added_object(sha1, type, buf, size);
|
||||
added_object(nr, type, buf, size);
|
||||
}
|
||||
|
||||
static void resolve_delta(const char *type,
|
||||
static void resolve_delta(unsigned nr, const char *type,
|
||||
void *base, unsigned long base_size,
|
||||
void *delta, unsigned long delta_size)
|
||||
{
|
||||
|
@ -130,20 +145,23 @@ static void resolve_delta(const char *type,
|
|||
if (!result)
|
||||
die("failed to apply delta");
|
||||
free(delta);
|
||||
write_object(result, result_size, type);
|
||||
write_object(nr, result, result_size, type);
|
||||
free(result);
|
||||
}
|
||||
|
||||
static void added_object(unsigned char *sha1, const char *type, void *data, unsigned long size)
|
||||
static void added_object(unsigned nr, const char *type, void *data,
|
||||
unsigned long size)
|
||||
{
|
||||
struct delta_info **p = &delta_list;
|
||||
struct delta_info *info;
|
||||
|
||||
while ((info = *p) != NULL) {
|
||||
if (!hashcmp(info->base_sha1, sha1)) {
|
||||
if (!hashcmp(info->base_sha1, obj_list[nr].sha1) ||
|
||||
info->base_offset == obj_list[nr].offset) {
|
||||
*p = info->next;
|
||||
p = &delta_list;
|
||||
resolve_delta(type, data, size, info->delta, info->size);
|
||||
resolve_delta(info->nr, type, data, size,
|
||||
info->delta, info->size);
|
||||
free(info);
|
||||
continue;
|
||||
}
|
||||
|
@ -151,7 +169,8 @@ static void added_object(unsigned char *sha1, const char *type, void *data, unsi
|
|||
}
|
||||
}
|
||||
|
||||
static void unpack_non_delta_entry(enum object_type kind, unsigned long size)
|
||||
static void unpack_non_delta_entry(enum object_type kind, unsigned long size,
|
||||
unsigned nr)
|
||||
{
|
||||
void *buf = get_data(size);
|
||||
const char *type;
|
||||
|
@ -164,30 +183,80 @@ static void unpack_non_delta_entry(enum object_type kind, unsigned long size)
|
|||
default: die("bad type %d", kind);
|
||||
}
|
||||
if (!dry_run && buf)
|
||||
write_object(buf, size, type);
|
||||
write_object(nr, buf, size, type);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void unpack_delta_entry(unsigned long delta_size)
|
||||
static void unpack_delta_entry(enum object_type kind, unsigned long delta_size,
|
||||
unsigned nr)
|
||||
{
|
||||
void *delta_data, *base;
|
||||
unsigned long base_size;
|
||||
char type[20];
|
||||
unsigned char base_sha1[20];
|
||||
|
||||
hashcpy(base_sha1, fill(20));
|
||||
use(20);
|
||||
if (kind == OBJ_REF_DELTA) {
|
||||
hashcpy(base_sha1, fill(20));
|
||||
use(20);
|
||||
delta_data = get_data(delta_size);
|
||||
if (dry_run || !delta_data) {
|
||||
free(delta_data);
|
||||
return;
|
||||
}
|
||||
if (!has_sha1_file(base_sha1)) {
|
||||
hashcpy(obj_list[nr].sha1, null_sha1);
|
||||
add_delta_to_list(nr, base_sha1, 0, delta_data, delta_size);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
unsigned base_found = 0;
|
||||
unsigned char *pack, c;
|
||||
unsigned long base_offset;
|
||||
unsigned lo, mid, hi;
|
||||
|
||||
delta_data = get_data(delta_size);
|
||||
if (dry_run || !delta_data) {
|
||||
free(delta_data);
|
||||
return;
|
||||
pack = fill(1);
|
||||
c = *pack;
|
||||
use(1);
|
||||
base_offset = c & 127;
|
||||
while (c & 128) {
|
||||
base_offset += 1;
|
||||
if (!base_offset || base_offset & ~(~0UL >> 7))
|
||||
die("offset value overflow for delta base object");
|
||||
pack = fill(1);
|
||||
c = *pack;
|
||||
use(1);
|
||||
base_offset = (base_offset << 7) + (c & 127);
|
||||
}
|
||||
base_offset = obj_list[nr].offset - base_offset;
|
||||
|
||||
delta_data = get_data(delta_size);
|
||||
if (dry_run || !delta_data) {
|
||||
free(delta_data);
|
||||
return;
|
||||
}
|
||||
lo = 0;
|
||||
hi = nr;
|
||||
while (lo < hi) {
|
||||
mid = (lo + hi)/2;
|
||||
if (base_offset < obj_list[mid].offset) {
|
||||
hi = mid;
|
||||
} else if (base_offset > obj_list[mid].offset) {
|
||||
lo = mid + 1;
|
||||
} else {
|
||||
hashcpy(base_sha1, obj_list[mid].sha1);
|
||||
base_found = !is_null_sha1(base_sha1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!base_found) {
|
||||
/* The delta base object is itself a delta that
|
||||
has not been resolved yet. */
|
||||
hashcpy(obj_list[nr].sha1, null_sha1);
|
||||
add_delta_to_list(nr, null_sha1, base_offset, delta_data, delta_size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_sha1_file(base_sha1)) {
|
||||
add_delta_to_list(base_sha1, delta_data, delta_size);
|
||||
return;
|
||||
}
|
||||
base = read_sha1_file(base_sha1, type, &base_size);
|
||||
if (!base) {
|
||||
error("failed to read delta-pack base object %s",
|
||||
|
@ -197,7 +266,7 @@ static void unpack_delta_entry(unsigned long delta_size)
|
|||
has_errors = 1;
|
||||
return;
|
||||
}
|
||||
resolve_delta(type, base, base_size, delta_data, delta_size);
|
||||
resolve_delta(nr, type, base, base_size, delta_data, delta_size);
|
||||
free(base);
|
||||
}
|
||||
|
||||
|
@ -208,6 +277,8 @@ static void unpack_one(unsigned nr, unsigned total)
|
|||
unsigned long size;
|
||||
enum object_type type;
|
||||
|
||||
obj_list[nr].offset = consumed_bytes;
|
||||
|
||||
pack = fill(1);
|
||||
c = *pack;
|
||||
use(1);
|
||||
|
@ -216,7 +287,7 @@ static void unpack_one(unsigned nr, unsigned total)
|
|||
shift = 4;
|
||||
while (c & 0x80) {
|
||||
pack = fill(1);
|
||||
c = *pack++;
|
||||
c = *pack;
|
||||
use(1);
|
||||
size += (c & 0x7f) << shift;
|
||||
shift += 7;
|
||||
|
@ -225,13 +296,14 @@ static void unpack_one(unsigned nr, unsigned total)
|
|||
static unsigned long last_sec;
|
||||
static unsigned last_percent;
|
||||
struct timeval now;
|
||||
unsigned percentage = (nr * 100) / total;
|
||||
unsigned percentage = ((nr+1) * 100) / total;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
if (percentage != last_percent || now.tv_sec != last_sec) {
|
||||
last_sec = now.tv_sec;
|
||||
last_percent = percentage;
|
||||
fprintf(stderr, "%4u%% (%u/%u) done\r", percentage, nr, total);
|
||||
fprintf(stderr, "%4u%% (%u/%u) done\r",
|
||||
percentage, (nr+1), total);
|
||||
}
|
||||
}
|
||||
switch (type) {
|
||||
|
@ -239,10 +311,11 @@ static void unpack_one(unsigned nr, unsigned total)
|
|||
case OBJ_TREE:
|
||||
case OBJ_BLOB:
|
||||
case OBJ_TAG:
|
||||
unpack_non_delta_entry(type, size);
|
||||
unpack_non_delta_entry(type, size, nr);
|
||||
return;
|
||||
case OBJ_DELTA:
|
||||
unpack_delta_entry(size);
|
||||
case OBJ_REF_DELTA:
|
||||
case OBJ_OFS_DELTA:
|
||||
unpack_delta_entry(type, size, nr);
|
||||
return;
|
||||
default:
|
||||
error("bad object type %d", type);
|
||||
|
@ -265,9 +338,10 @@ static void unpack_all(void)
|
|||
die("unknown pack file version %d", ntohl(hdr->hdr_version));
|
||||
fprintf(stderr, "Unpacking %d objects\n", nr_objects);
|
||||
|
||||
obj_list = xmalloc(nr_objects * sizeof(*obj_list));
|
||||
use(sizeof(struct pack_header));
|
||||
for (i = 0; i < nr_objects; i++)
|
||||
unpack_one(i+1, nr_objects);
|
||||
unpack_one(i, nr_objects);
|
||||
if (delta_list)
|
||||
die("unresolved deltas left after unpacking");
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ extern int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const cha
|
|||
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 void prune_packed_objects(int);
|
||||
|
||||
extern int cmd_add(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_apply(int argc, const char **argv, const char *prefix);
|
||||
|
|
|
@ -344,12 +344,8 @@ static int update_one(struct cache_tree *it,
|
|||
#endif
|
||||
}
|
||||
|
||||
if (dryrun) {
|
||||
unsigned char hdr[200];
|
||||
int hdrlen;
|
||||
write_sha1_file_prepare(buffer, offset, tree_type, it->sha1,
|
||||
hdr, &hdrlen);
|
||||
}
|
||||
if (dryrun)
|
||||
hash_sha1_file(buffer, offset, tree_type, it->sha1);
|
||||
else
|
||||
write_sha1_file(buffer, offset, tree_type, it->sha1);
|
||||
free(buffer);
|
||||
|
|
12
cache.h
12
cache.h
|
@ -245,13 +245,8 @@ char *enter_repo(char *path, int strict);
|
|||
extern int sha1_object_info(const unsigned char *, char *, unsigned long *);
|
||||
extern void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned long *size);
|
||||
extern void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size);
|
||||
extern int hash_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *sha1);
|
||||
extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
|
||||
extern char *write_sha1_file_prepare(void *buf,
|
||||
unsigned long len,
|
||||
const char *type,
|
||||
unsigned char *sha1,
|
||||
unsigned char *hdr,
|
||||
int *hdrlen);
|
||||
|
||||
extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
|
||||
|
||||
|
@ -274,8 +269,9 @@ enum object_type {
|
|||
OBJ_TREE = 2,
|
||||
OBJ_BLOB = 3,
|
||||
OBJ_TAG = 4,
|
||||
/* 5/6 for future expansion */
|
||||
OBJ_DELTA = 7,
|
||||
/* 5 for future expansion */
|
||||
OBJ_OFS_DELTA = 6,
|
||||
OBJ_REF_DELTA = 7,
|
||||
OBJ_BAD,
|
||||
};
|
||||
|
||||
|
|
|
@ -856,8 +856,10 @@ void diff_tree_combined(const unsigned char *sha1,
|
|||
/* show stat against the first parent even
|
||||
* when doing combined diff.
|
||||
*/
|
||||
if (i == 0 && opt->output_format & DIFF_FORMAT_DIFFSTAT)
|
||||
diffopts.output_format = DIFF_FORMAT_DIFFSTAT;
|
||||
int stat_opt = (opt->output_format &
|
||||
(DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT));
|
||||
if (i == 0 && stat_opt)
|
||||
diffopts.output_format = stat_opt;
|
||||
else
|
||||
diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
|
||||
diff_tree_sha1(parent[i], sha1, "", &diffopts);
|
||||
|
@ -887,7 +889,8 @@ void diff_tree_combined(const unsigned char *sha1,
|
|||
}
|
||||
needsep = 1;
|
||||
}
|
||||
else if (opt->output_format & DIFF_FORMAT_DIFFSTAT)
|
||||
else if (opt->output_format &
|
||||
(DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT))
|
||||
needsep = 1;
|
||||
if (opt->output_format & DIFF_FORMAT_PATCH) {
|
||||
if (needsep)
|
||||
|
|
11
commit.c
11
commit.c
|
@ -548,10 +548,13 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com
|
|||
|
||||
while (parent) {
|
||||
struct commit *p = parent->item;
|
||||
const char *hex = abbrev
|
||||
? find_unique_abbrev(p->object.sha1, abbrev)
|
||||
: sha1_to_hex(p->object.sha1);
|
||||
const char *dots = (abbrev && strlen(hex) != 40) ? "..." : "";
|
||||
const char *hex = NULL;
|
||||
const char *dots;
|
||||
if (abbrev)
|
||||
hex = find_unique_abbrev(p->object.sha1, abbrev);
|
||||
if (!hex)
|
||||
hex = sha1_to_hex(p->object.sha1);
|
||||
dots = (abbrev && strlen(hex) != 40) ? "..." : "";
|
||||
parent = parent->next;
|
||||
|
||||
offset += sprintf(buf + offset, " %s%s", hex, dots);
|
||||
|
|
|
@ -422,8 +422,8 @@ and returns the process output as a string."
|
|||
(propertize
|
||||
(concat " ("
|
||||
(if (eq state 'copy) "copied from "
|
||||
(if (eq (git-fileinfo->state info) 'added) "renamed to "
|
||||
"renamed from "))
|
||||
(if (eq (git-fileinfo->state info) 'added) "renamed from "
|
||||
"renamed to "))
|
||||
(git-escape-file-name (git-fileinfo->orig-name info))
|
||||
")") 'face 'git-status-face)
|
||||
"")))
|
||||
|
|
|
@ -23,13 +23,18 @@
|
|||
;; system.
|
||||
;;
|
||||
;; To install: put this file on the load-path and add GIT to the list
|
||||
;; of supported backends in `vc-handled-backends'.
|
||||
;; of supported backends in `vc-handled-backends'; the following line,
|
||||
;; placed in your ~/.emacs, will accomplish this:
|
||||
;;
|
||||
;; (add-to-list 'vc-handled-backends 'GIT)
|
||||
;;
|
||||
;; TODO
|
||||
;; - changelog generation
|
||||
;; - working with revisions other than HEAD
|
||||
;;
|
||||
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(defvar git-commits-coding-system 'utf-8
|
||||
"Default coding system for git commits.")
|
||||
|
||||
|
@ -119,10 +124,10 @@
|
|||
(defun vc-git-annotate-command (file buf &optional rev)
|
||||
; FIXME: rev is ignored
|
||||
(let ((name (file-relative-name file)))
|
||||
(call-process "git" nil buf nil "annotate" name)))
|
||||
(call-process "git" nil buf nil "blame" name)))
|
||||
|
||||
(defun vc-git-annotate-time ()
|
||||
(and (re-search-forward "[0-9a-f]+\t(.*\t\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\) \\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\) \\([-+0-9]+\\)\t[0-9]+)" nil t)
|
||||
(and (re-search-forward "[0-9a-f]+ (.* \\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\) \\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\) \\([-+0-9]+\\) +[0-9]+)" nil t)
|
||||
(vc-annotate-convert-time
|
||||
(apply #'encode-time (mapcar (lambda (match) (string-to-number (match-string match))) '(6 5 4 3 2 1 7))))))
|
||||
|
||||
|
|
6
daemon.c
6
daemon.c
|
@ -450,6 +450,8 @@ void fill_in_extra_table_entries(struct interp *itable)
|
|||
* Replace literal host with lowercase-ized hostname.
|
||||
*/
|
||||
hp = interp_table[INTERP_SLOT_HOST].value;
|
||||
if (!hp)
|
||||
return;
|
||||
for ( ; *hp; hp++)
|
||||
*hp = tolower(*hp);
|
||||
|
||||
|
@ -544,8 +546,10 @@ static int execute(struct sockaddr *addr)
|
|||
loginfo("Extended attributes (%d bytes) exist <%.*s>",
|
||||
(int) pktlen - len,
|
||||
(int) pktlen - len, line + len + 1);
|
||||
if (len && line[len-1] == '\n')
|
||||
if (len && line[len-1] == '\n') {
|
||||
line[--len] = 0;
|
||||
pktlen--;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the path interpolation table for this connection.
|
||||
|
|
|
@ -308,8 +308,8 @@ create_delta(const struct delta_index *index,
|
|||
continue;
|
||||
if (ref_size > top - src)
|
||||
ref_size = top - src;
|
||||
if (ref_size > 0xffffff)
|
||||
ref_size = 0xffffff;
|
||||
if (ref_size > 0x10000)
|
||||
ref_size = 0x10000;
|
||||
if (ref_size <= msize)
|
||||
break;
|
||||
while (ref_size-- && *src++ == *ref)
|
||||
|
@ -318,8 +318,6 @@ create_delta(const struct delta_index *index,
|
|||
/* this is our best match so far */
|
||||
msize = ref - entry->ptr;
|
||||
moff = entry->ptr - ref_data;
|
||||
if (msize >= 0x10000)
|
||||
break; /* this is good enough */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -383,8 +381,6 @@ create_delta(const struct delta_index *index,
|
|||
if (msize & 0xff) { out[outpos++] = msize; i |= 0x10; }
|
||||
msize >>= 8;
|
||||
if (msize & 0xff) { out[outpos++] = msize; i |= 0x20; }
|
||||
msize >>= 8;
|
||||
if (msize & 0xff) { out[outpos++] = msize; i |= 0x40; }
|
||||
|
||||
*op = i;
|
||||
}
|
||||
|
|
30
diff.c
30
diff.c
|
@ -795,6 +795,23 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
|
|||
set, total_files, adds, dels, reset);
|
||||
}
|
||||
|
||||
static void show_numstat(struct diffstat_t* data, struct diff_options *options)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < data->nr; i++) {
|
||||
struct diffstat_file *file = data->files[i];
|
||||
|
||||
printf("%d\t%d\t", file->added, file->deleted);
|
||||
if (options->line_termination &&
|
||||
quote_c_style(file->name, NULL, NULL, 0))
|
||||
quote_c_style(file->name, NULL, stdout, 0);
|
||||
else
|
||||
fputs(file->name, stdout);
|
||||
putchar(options->line_termination);
|
||||
}
|
||||
}
|
||||
|
||||
struct checkdiff_t {
|
||||
struct xdiff_emit_state xm;
|
||||
const char *filename;
|
||||
|
@ -1731,6 +1748,7 @@ int diff_setup_done(struct diff_options *options)
|
|||
DIFF_FORMAT_CHECKDIFF |
|
||||
DIFF_FORMAT_NO_OUTPUT))
|
||||
options->output_format &= ~(DIFF_FORMAT_RAW |
|
||||
DIFF_FORMAT_NUMSTAT |
|
||||
DIFF_FORMAT_DIFFSTAT |
|
||||
DIFF_FORMAT_SUMMARY |
|
||||
DIFF_FORMAT_PATCH);
|
||||
|
@ -1740,7 +1758,9 @@ int diff_setup_done(struct diff_options *options)
|
|||
* recursive bits for other formats here.
|
||||
*/
|
||||
if (options->output_format & (DIFF_FORMAT_PATCH |
|
||||
DIFF_FORMAT_NUMSTAT |
|
||||
DIFF_FORMAT_DIFFSTAT |
|
||||
DIFF_FORMAT_SUMMARY |
|
||||
DIFF_FORMAT_CHECKDIFF))
|
||||
options->recursive = 1;
|
||||
/*
|
||||
|
@ -1828,6 +1848,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
|
|||
else if (!strcmp(arg, "--patch-with-raw")) {
|
||||
options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW;
|
||||
}
|
||||
else if (!strcmp(arg, "--numstat")) {
|
||||
options->output_format |= DIFF_FORMAT_NUMSTAT;
|
||||
}
|
||||
else if (!strncmp(arg, "--stat", 6)) {
|
||||
char *end;
|
||||
int width = options->stat_width;
|
||||
|
@ -2602,7 +2625,7 @@ void diff_flush(struct diff_options *options)
|
|||
separator++;
|
||||
}
|
||||
|
||||
if (output_format & DIFF_FORMAT_DIFFSTAT) {
|
||||
if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_NUMSTAT)) {
|
||||
struct diffstat_t diffstat;
|
||||
|
||||
memset(&diffstat, 0, sizeof(struct diffstat_t));
|
||||
|
@ -2612,7 +2635,10 @@ void diff_flush(struct diff_options *options)
|
|||
if (check_pair_status(p))
|
||||
diff_flush_stat(p, options, &diffstat);
|
||||
}
|
||||
show_stats(&diffstat, options);
|
||||
if (output_format & DIFF_FORMAT_NUMSTAT)
|
||||
show_numstat(&diffstat, options);
|
||||
if (output_format & DIFF_FORMAT_DIFFSTAT)
|
||||
show_stats(&diffstat, options);
|
||||
separator++;
|
||||
}
|
||||
|
||||
|
|
16
diff.h
16
diff.h
|
@ -26,20 +26,21 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
|
|||
|
||||
#define DIFF_FORMAT_RAW 0x0001
|
||||
#define DIFF_FORMAT_DIFFSTAT 0x0002
|
||||
#define DIFF_FORMAT_SUMMARY 0x0004
|
||||
#define DIFF_FORMAT_PATCH 0x0008
|
||||
#define DIFF_FORMAT_NUMSTAT 0x0004
|
||||
#define DIFF_FORMAT_SUMMARY 0x0008
|
||||
#define DIFF_FORMAT_PATCH 0x0010
|
||||
|
||||
/* These override all above */
|
||||
#define DIFF_FORMAT_NAME 0x0010
|
||||
#define DIFF_FORMAT_NAME_STATUS 0x0020
|
||||
#define DIFF_FORMAT_CHECKDIFF 0x0040
|
||||
#define DIFF_FORMAT_NAME 0x0100
|
||||
#define DIFF_FORMAT_NAME_STATUS 0x0200
|
||||
#define DIFF_FORMAT_CHECKDIFF 0x0400
|
||||
|
||||
/* Same as output_format = 0 but we know that -s flag was given
|
||||
* and we should not give default value to output_format.
|
||||
*/
|
||||
#define DIFF_FORMAT_NO_OUTPUT 0x0080
|
||||
#define DIFF_FORMAT_NO_OUTPUT 0x0800
|
||||
|
||||
#define DIFF_FORMAT_CALLBACK 0x0100
|
||||
#define DIFF_FORMAT_CALLBACK 0x1000
|
||||
|
||||
struct diff_options {
|
||||
const char *filter;
|
||||
|
@ -170,6 +171,7 @@ extern void diffcore_std_no_resolve(struct diff_options *);
|
|||
" --patch-with-raw\n" \
|
||||
" output both a patch and the diff-raw format.\n" \
|
||||
" --stat show diffstat instead of patch.\n" \
|
||||
" --numstat show numeric diffstat instead of patch.\n" \
|
||||
" --patch-with-stat\n" \
|
||||
" output a patch and prepend its diffstat.\n" \
|
||||
" --name-only show only names of changed files.\n" \
|
||||
|
|
|
@ -115,12 +115,10 @@ static pid_t setup_sideband(int sideband, const char *me, int fd[2], int xd[2])
|
|||
die("%s: unable to fork off sideband demultiplexer", me);
|
||||
if (!side_pid) {
|
||||
/* subprocess */
|
||||
char buf[LARGE_PACKET_MAX];
|
||||
|
||||
close(fd[0]);
|
||||
if (xd[0] != xd[1])
|
||||
close(xd[1]);
|
||||
if (recv_sideband(me, xd[0], fd[1], 2, buf, sizeof(buf)))
|
||||
if (recv_sideband(me, xd[0], fd[1], 2))
|
||||
exit(1);
|
||||
exit(0);
|
||||
}
|
||||
|
|
|
@ -166,12 +166,13 @@ static int find_common(int fd[2], unsigned char *result_sha1,
|
|||
}
|
||||
|
||||
if (!fetching)
|
||||
packet_write(fd[1], "want %s%s%s%s%s\n",
|
||||
packet_write(fd[1], "want %s%s%s%s%s%s\n",
|
||||
sha1_to_hex(remote),
|
||||
(multi_ack ? " multi_ack" : ""),
|
||||
(use_sideband == 2 ? " side-band-64k" : ""),
|
||||
(use_sideband == 1 ? " side-band" : ""),
|
||||
(use_thin_pack ? " thin-pack" : ""));
|
||||
(use_thin_pack ? " thin-pack" : ""),
|
||||
" ofs-delta");
|
||||
else
|
||||
packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
|
||||
fetching++;
|
||||
|
|
|
@ -179,11 +179,12 @@ bisect_reset() {
|
|||
*)
|
||||
usage ;;
|
||||
esac
|
||||
git checkout "$branch" &&
|
||||
rm -fr "$GIT_DIR/refs/bisect"
|
||||
rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
|
||||
rm -f "$GIT_DIR/BISECT_LOG"
|
||||
rm -f "$GIT_DIR/BISECT_NAMES"
|
||||
if git checkout "$branch"; then
|
||||
rm -fr "$GIT_DIR/refs/bisect"
|
||||
rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
|
||||
rm -f "$GIT_DIR/BISECT_LOG"
|
||||
rm -f "$GIT_DIR/BISECT_NAMES"
|
||||
fi
|
||||
}
|
||||
|
||||
bisect_replay () {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh
|
||||
|
||||
USAGE='[-l] [(-d | -D) <branchname>] | [[-f] <branchname> [<start-point>]] | -r'
|
||||
USAGE='[-l] [-f] <branchname> [<start-point>] | (-d | -D) <branchname> | [-r]'
|
||||
LONG_USAGE='If no arguments, show available branches and mark current branch with a star.
|
||||
If one argument, create a new branch <branchname> based off of current HEAD.
|
||||
If two arguments, create a new branch <branchname> based off of <start-point>.'
|
||||
|
|
66
git-clone.sh
66
git-clone.sh
|
@ -8,11 +8,15 @@
|
|||
# See git-sh-setup why.
|
||||
unset CDPATH
|
||||
|
||||
usage() {
|
||||
echo >&2 "Usage: $0 [--template=<template_directory>] [--use-separate-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
|
||||
die() {
|
||||
echo >&2 "$@"
|
||||
exit 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
die "Usage: $0 [--template=<template_directory>] [--use-separate-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
|
||||
}
|
||||
|
||||
get_repo_base() {
|
||||
(cd "$1" && (cd .git ; pwd)) 2> /dev/null
|
||||
}
|
||||
|
@ -35,11 +39,9 @@ clone_dumb_http () {
|
|||
"`git-repo-config --bool http.noEPSV`" = true ]; then
|
||||
curl_extra_args="${curl_extra_args} --disable-epsv"
|
||||
fi
|
||||
http_fetch "$1/info/refs" "$clone_tmp/refs" || {
|
||||
echo >&2 "Cannot get remote repository information.
|
||||
http_fetch "$1/info/refs" "$clone_tmp/refs" ||
|
||||
die "Cannot get remote repository information.
|
||||
Perhaps git-update-server-info needs to be run there?"
|
||||
exit 1;
|
||||
}
|
||||
while read sha1 refname
|
||||
do
|
||||
name=`expr "z$refname" : 'zrefs/\(.*\)'` &&
|
||||
|
@ -143,17 +145,12 @@ while
|
|||
'')
|
||||
usage ;;
|
||||
*/*)
|
||||
echo >&2 "'$2' is not suitable for an origin name"
|
||||
exit 1
|
||||
die "'$2' is not suitable for an origin name"
|
||||
esac
|
||||
git-check-ref-format "heads/$2" || {
|
||||
echo >&2 "'$2' is not suitable for a branch name"
|
||||
exit 1
|
||||
}
|
||||
test -z "$origin_override" || {
|
||||
echo >&2 "Do not give more than one --origin options."
|
||||
exit 1
|
||||
}
|
||||
git-check-ref-format "heads/$2" ||
|
||||
die "'$2' is not suitable for a branch name"
|
||||
test -z "$origin_override" ||
|
||||
die "Do not give more than one --origin options."
|
||||
origin_override=yes
|
||||
origin="$2"; shift
|
||||
;;
|
||||
|
@ -169,24 +166,19 @@ do
|
|||
done
|
||||
|
||||
repo="$1"
|
||||
if test -z "$repo"
|
||||
then
|
||||
echo >&2 'you must specify a repository to clone.'
|
||||
exit 1
|
||||
fi
|
||||
test -n "$repo" ||
|
||||
die 'you must specify a repository to clone.'
|
||||
|
||||
# --bare implies --no-checkout
|
||||
if test yes = "$bare"
|
||||
then
|
||||
if test yes = "$origin_override"
|
||||
then
|
||||
echo >&2 '--bare and --origin $origin options are incompatible.'
|
||||
exit 1
|
||||
die '--bare and --origin $origin options are incompatible.'
|
||||
fi
|
||||
if test t = "$use_separate_remote"
|
||||
then
|
||||
echo >&2 '--bare and --use-separate-remote options are incompatible.'
|
||||
exit 1
|
||||
die '--bare and --use-separate-remote options are incompatible.'
|
||||
fi
|
||||
no_checkout=yes
|
||||
fi
|
||||
|
@ -206,7 +198,7 @@ fi
|
|||
dir="$2"
|
||||
# Try using "humanish" part of source repo if user didn't specify one
|
||||
[ -z "$dir" ] && dir=$(echo "$repo" | sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
|
||||
[ -e "$dir" ] && echo "$dir already exists." && usage
|
||||
[ -e "$dir" ] && die "destination directory '$dir' already exists."
|
||||
mkdir -p "$dir" &&
|
||||
D=$(cd "$dir" && pwd) &&
|
||||
trap 'err=$?; cd ..; rm -rf "$D"; exit $err' 0
|
||||
|
@ -233,7 +225,7 @@ then
|
|||
cd reference-tmp &&
|
||||
tar xf -)
|
||||
else
|
||||
echo >&2 "$reference: not a local directory." && usage
|
||||
die "reference repository '$reference' is not a local directory."
|
||||
fi
|
||||
fi
|
||||
|
||||
|
@ -242,10 +234,8 @@ rm -f "$GIT_DIR/CLONE_HEAD"
|
|||
# We do local magic only when the user tells us to.
|
||||
case "$local,$use_local" in
|
||||
yes,yes)
|
||||
( cd "$repo/objects" ) || {
|
||||
echo >&2 "-l flag seen but $repo is not local."
|
||||
exit 1
|
||||
}
|
||||
( cd "$repo/objects" ) ||
|
||||
die "-l flag seen but repository '$repo' is not local."
|
||||
|
||||
case "$local_shared" in
|
||||
no)
|
||||
|
@ -307,18 +297,15 @@ yes,yes)
|
|||
then
|
||||
clone_dumb_http "$repo" "$D"
|
||||
else
|
||||
echo >&2 "http transport not supported, rebuild Git with curl support"
|
||||
exit 1
|
||||
die "http transport not supported, rebuild Git with curl support"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
cd "$D" && case "$upload_pack" in
|
||||
case "$upload_pack" in
|
||||
'') git-fetch-pack --all -k $quiet "$repo" ;;
|
||||
*) git-fetch-pack --all -k $quiet "$upload_pack" "$repo" ;;
|
||||
esac >"$GIT_DIR/CLONE_HEAD" || {
|
||||
echo >&2 "fetch-pack from '$repo' failed."
|
||||
exit 1
|
||||
}
|
||||
esac >"$GIT_DIR/CLONE_HEAD" ||
|
||||
die "fetch-pack from '$repo' failed."
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
@ -414,7 +401,8 @@ Pull: refs/heads/$head_points_at:$origin_track" &&
|
|||
|
||||
case "$no_checkout" in
|
||||
'')
|
||||
git-read-tree -m -u -v HEAD HEAD
|
||||
test "z$quiet" = z && v=-v || v=
|
||||
git-read-tree -m -u $v HEAD HEAD
|
||||
esac
|
||||
fi
|
||||
rm -f "$GIT_DIR/CLONE_HEAD" "$GIT_DIR/REMOTE_HEAD"
|
||||
|
|
485
git-commit.sh
485
git-commit.sh
|
@ -32,33 +32,6 @@ save_index () {
|
|||
cp -p "$THIS_INDEX" "$NEXT_INDEX"
|
||||
}
|
||||
|
||||
report () {
|
||||
header="#
|
||||
# $1:
|
||||
# ($2)
|
||||
#
|
||||
"
|
||||
trailer=""
|
||||
while read status name newname
|
||||
do
|
||||
printf '%s' "$header"
|
||||
header=""
|
||||
trailer="#
|
||||
"
|
||||
case "$status" in
|
||||
M ) echo "# modified: $name";;
|
||||
D*) echo "# deleted: $name";;
|
||||
T ) echo "# typechange: $name";;
|
||||
C*) echo "# copied: $name -> $newname";;
|
||||
R*) echo "# renamed: $name -> $newname";;
|
||||
A*) echo "# new file: $name";;
|
||||
U ) echo "# unmerged: $name";;
|
||||
esac
|
||||
done
|
||||
printf '%s' "$trailer"
|
||||
[ "$header" ]
|
||||
}
|
||||
|
||||
run_status () {
|
||||
# If TMP_INDEX is defined, that means we are doing
|
||||
# "--only" partial commit, and that index file is used
|
||||
|
@ -68,21 +41,21 @@ run_status () {
|
|||
# so the regular index file is what we use to compare.
|
||||
if test '' != "$TMP_INDEX"
|
||||
then
|
||||
GIT_INDEX_FILE="$TMP_INDEX"
|
||||
export GIT_INDEX_FILE
|
||||
GIT_INDEX_FILE="$TMP_INDEX"
|
||||
export GIT_INDEX_FILE
|
||||
elif test -f "$NEXT_INDEX"
|
||||
then
|
||||
GIT_INDEX_FILE="$NEXT_INDEX"
|
||||
export GIT_INDEX_FILE
|
||||
GIT_INDEX_FILE="$NEXT_INDEX"
|
||||
export GIT_INDEX_FILE
|
||||
fi
|
||||
|
||||
case "$status_only" in
|
||||
t) color= ;;
|
||||
*) color=--nocolor ;;
|
||||
esac
|
||||
git-runstatus ${color} \
|
||||
${verbose:+--verbose} \
|
||||
${amend:+--amend} \
|
||||
case "$status_only" in
|
||||
t) color= ;;
|
||||
*) color=--nocolor ;;
|
||||
esac
|
||||
git-runstatus ${color} \
|
||||
${verbose:+--verbose} \
|
||||
${amend:+--amend} \
|
||||
${untracked_files:+--untracked}
|
||||
}
|
||||
|
||||
|
@ -114,179 +87,181 @@ only_include_assumed=
|
|||
untracked_files=
|
||||
while case "$#" in 0) break;; esac
|
||||
do
|
||||
case "$1" in
|
||||
-F|--F|-f|--f|--fi|--fil|--file)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
no_edit=t
|
||||
log_given=t$log_given
|
||||
logfile="$1"
|
||||
shift
|
||||
;;
|
||||
-F*|-f*)
|
||||
no_edit=t
|
||||
log_given=t$log_given
|
||||
logfile=`expr "z$1" : 'z-[Ff]\(.*\)'`
|
||||
shift
|
||||
;;
|
||||
--F=*|--f=*|--fi=*|--fil=*|--file=*)
|
||||
no_edit=t
|
||||
log_given=t$log_given
|
||||
logfile=`expr "z$1" : 'z-[^=]*=\(.*\)'`
|
||||
shift
|
||||
;;
|
||||
-a|--a|--al|--all)
|
||||
all=t
|
||||
shift
|
||||
;;
|
||||
--au=*|--aut=*|--auth=*|--autho=*|--author=*)
|
||||
force_author=`expr "z$1" : 'z-[^=]*=\(.*\)'`
|
||||
shift
|
||||
;;
|
||||
--au|--aut|--auth|--autho|--author)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
force_author="$1"
|
||||
shift
|
||||
;;
|
||||
-e|--e|--ed|--edi|--edit)
|
||||
edit_flag=t
|
||||
shift
|
||||
;;
|
||||
-i|--i|--in|--inc|--incl|--inclu|--includ|--include)
|
||||
also=t
|
||||
shift
|
||||
;;
|
||||
-o|--o|--on|--onl|--only)
|
||||
only=t
|
||||
shift
|
||||
;;
|
||||
-m|--m|--me|--mes|--mess|--messa|--messag|--message)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
log_given=m$log_given
|
||||
if test "$log_message" = ''
|
||||
then
|
||||
log_message="$1"
|
||||
else
|
||||
log_message="$log_message
|
||||
case "$1" in
|
||||
-F|--F|-f|--f|--fi|--fil|--file)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
no_edit=t
|
||||
log_given=t$log_given
|
||||
logfile="$1"
|
||||
shift
|
||||
;;
|
||||
-F*|-f*)
|
||||
no_edit=t
|
||||
log_given=t$log_given
|
||||
logfile=`expr "z$1" : 'z-[Ff]\(.*\)'`
|
||||
shift
|
||||
;;
|
||||
--F=*|--f=*|--fi=*|--fil=*|--file=*)
|
||||
no_edit=t
|
||||
log_given=t$log_given
|
||||
logfile=`expr "z$1" : 'z-[^=]*=\(.*\)'`
|
||||
shift
|
||||
;;
|
||||
-a|--a|--al|--all)
|
||||
all=t
|
||||
shift
|
||||
;;
|
||||
--au=*|--aut=*|--auth=*|--autho=*|--author=*)
|
||||
force_author=`expr "z$1" : 'z-[^=]*=\(.*\)'`
|
||||
shift
|
||||
;;
|
||||
--au|--aut|--auth|--autho|--author)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
force_author="$1"
|
||||
shift
|
||||
;;
|
||||
-e|--e|--ed|--edi|--edit)
|
||||
edit_flag=t
|
||||
shift
|
||||
;;
|
||||
-i|--i|--in|--inc|--incl|--inclu|--includ|--include)
|
||||
also=t
|
||||
shift
|
||||
;;
|
||||
-o|--o|--on|--onl|--only)
|
||||
only=t
|
||||
shift
|
||||
;;
|
||||
-m|--m|--me|--mes|--mess|--messa|--messag|--message)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
log_given=m$log_given
|
||||
if test "$log_message" = ''
|
||||
then
|
||||
log_message="$1"
|
||||
else
|
||||
log_message="$log_message
|
||||
|
||||
$1"
|
||||
fi
|
||||
no_edit=t
|
||||
shift
|
||||
;;
|
||||
-m*)
|
||||
log_given=m$log_given
|
||||
if test "$log_message" = ''
|
||||
then
|
||||
log_message=`expr "z$1" : 'z-m\(.*\)'`
|
||||
else
|
||||
log_message="$log_message
|
||||
fi
|
||||
no_edit=t
|
||||
shift
|
||||
;;
|
||||
-m*)
|
||||
log_given=m$log_given
|
||||
if test "$log_message" = ''
|
||||
then
|
||||
log_message=`expr "z$1" : 'z-m\(.*\)'`
|
||||
else
|
||||
log_message="$log_message
|
||||
|
||||
`expr "z$1" : 'z-m\(.*\)'`"
|
||||
fi
|
||||
no_edit=t
|
||||
shift
|
||||
;;
|
||||
--m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
|
||||
log_given=m$log_given
|
||||
if test "$log_message" = ''
|
||||
then
|
||||
log_message=`expr "z$1" : 'z-[^=]*=\(.*\)'`
|
||||
else
|
||||
log_message="$log_message
|
||||
fi
|
||||
no_edit=t
|
||||
shift
|
||||
;;
|
||||
--m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
|
||||
log_given=m$log_given
|
||||
if test "$log_message" = ''
|
||||
then
|
||||
log_message=`expr "z$1" : 'z-[^=]*=\(.*\)'`
|
||||
else
|
||||
log_message="$log_message
|
||||
|
||||
`expr "z$1" : 'zq-[^=]*=\(.*\)'`"
|
||||
fi
|
||||
no_edit=t
|
||||
shift
|
||||
;;
|
||||
-n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|--no-verify)
|
||||
verify=
|
||||
shift
|
||||
;;
|
||||
--a|--am|--ame|--amen|--amend)
|
||||
amend=t
|
||||
log_given=t$log_given
|
||||
use_commit=HEAD
|
||||
shift
|
||||
;;
|
||||
-c)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
log_given=t$log_given
|
||||
use_commit="$1"
|
||||
no_edit=
|
||||
shift
|
||||
;;
|
||||
--ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\
|
||||
--reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\
|
||||
--reedit-messag=*|--reedit-message=*)
|
||||
log_given=t$log_given
|
||||
use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'`
|
||||
no_edit=
|
||||
shift
|
||||
;;
|
||||
--ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\
|
||||
--reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|--reedit-message)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
log_given=t$log_given
|
||||
use_commit="$1"
|
||||
no_edit=
|
||||
shift
|
||||
;;
|
||||
-C)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
log_given=t$log_given
|
||||
use_commit="$1"
|
||||
no_edit=t
|
||||
shift
|
||||
;;
|
||||
--reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\
|
||||
--reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\
|
||||
--reuse-message=*)
|
||||
log_given=t$log_given
|
||||
use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'`
|
||||
no_edit=t
|
||||
shift
|
||||
;;
|
||||
--reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\
|
||||
--reuse-mess|--reuse-messa|--reuse-messag|--reuse-message)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
log_given=t$log_given
|
||||
use_commit="$1"
|
||||
no_edit=t
|
||||
shift
|
||||
;;
|
||||
-s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
|
||||
signoff=t
|
||||
shift
|
||||
;;
|
||||
-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
|
||||
verbose=t
|
||||
shift
|
||||
;;
|
||||
-u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|--untracked|\
|
||||
--untracked-|--untracked-f|--untracked-fi|--untracked-fil|--untracked-file|\
|
||||
--untracked-files)
|
||||
untracked_files=t
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
no_edit=t
|
||||
shift
|
||||
;;
|
||||
-n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|\
|
||||
--no-verify)
|
||||
verify=
|
||||
shift
|
||||
;;
|
||||
--a|--am|--ame|--amen|--amend)
|
||||
amend=t
|
||||
log_given=t$log_given
|
||||
use_commit=HEAD
|
||||
shift
|
||||
;;
|
||||
-c)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
log_given=t$log_given
|
||||
use_commit="$1"
|
||||
no_edit=
|
||||
shift
|
||||
;;
|
||||
--ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\
|
||||
--reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\
|
||||
--reedit-messag=*|--reedit-message=*)
|
||||
log_given=t$log_given
|
||||
use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'`
|
||||
no_edit=
|
||||
shift
|
||||
;;
|
||||
--ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\
|
||||
--reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|\
|
||||
--reedit-message)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
log_given=t$log_given
|
||||
use_commit="$1"
|
||||
no_edit=
|
||||
shift
|
||||
;;
|
||||
-C)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
log_given=t$log_given
|
||||
use_commit="$1"
|
||||
no_edit=t
|
||||
shift
|
||||
;;
|
||||
--reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\
|
||||
--reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\
|
||||
--reuse-message=*)
|
||||
log_given=t$log_given
|
||||
use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'`
|
||||
no_edit=t
|
||||
shift
|
||||
;;
|
||||
--reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\
|
||||
--reuse-mess|--reuse-messa|--reuse-messag|--reuse-message)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
log_given=t$log_given
|
||||
use_commit="$1"
|
||||
no_edit=t
|
||||
shift
|
||||
;;
|
||||
-s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
|
||||
signoff=t
|
||||
shift
|
||||
;;
|
||||
-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
|
||||
verbose=t
|
||||
shift
|
||||
;;
|
||||
-u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|\
|
||||
--untracked|--untracked-|--untracked-f|--untracked-fi|--untracked-fil|\
|
||||
--untracked-file|--untracked-files)
|
||||
untracked_files=t
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
case "$edit_flag" in t) no_edit= ;; esac
|
||||
|
||||
|
@ -295,33 +270,33 @@ case "$edit_flag" in t) no_edit= ;; esac
|
|||
|
||||
case "$amend,$initial_commit" in
|
||||
t,t)
|
||||
die "You do not have anything to amend." ;;
|
||||
die "You do not have anything to amend." ;;
|
||||
t,)
|
||||
if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
|
||||
die "You are in the middle of a merge -- cannot amend."
|
||||
fi ;;
|
||||
if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
|
||||
die "You are in the middle of a merge -- cannot amend."
|
||||
fi ;;
|
||||
esac
|
||||
|
||||
case "$log_given" in
|
||||
tt*)
|
||||
die "Only one of -c/-C/-F can be used." ;;
|
||||
die "Only one of -c/-C/-F can be used." ;;
|
||||
*tm*|*mt*)
|
||||
die "Option -m cannot be combined with -c/-C/-F." ;;
|
||||
die "Option -m cannot be combined with -c/-C/-F." ;;
|
||||
esac
|
||||
|
||||
case "$#,$also,$only,$amend" in
|
||||
*,t,t,*)
|
||||
die "Only one of --include/--only can be used." ;;
|
||||
die "Only one of --include/--only can be used." ;;
|
||||
0,t,,* | 0,,t,)
|
||||
die "No paths with --include/--only does not make sense." ;;
|
||||
die "No paths with --include/--only does not make sense." ;;
|
||||
0,,t,t)
|
||||
only_include_assumed="# Clever... amending the last one with dirty index." ;;
|
||||
only_include_assumed="# Clever... amending the last one with dirty index." ;;
|
||||
0,,,*)
|
||||
;;
|
||||
;;
|
||||
*,,,*)
|
||||
only_include_assumed="# Explicit paths specified without -i nor -o; assuming --only paths..."
|
||||
also=
|
||||
;;
|
||||
only_include_assumed="# Explicit paths specified without -i nor -o; assuming --only paths..."
|
||||
also=
|
||||
;;
|
||||
esac
|
||||
unset only
|
||||
case "$all,$also,$#" in
|
||||
|
@ -368,47 +343,47 @@ t,)
|
|||
,)
|
||||
case "$#" in
|
||||
0)
|
||||
;; # commit as-is
|
||||
;; # commit as-is
|
||||
*)
|
||||
if test -f "$GIT_DIR/MERGE_HEAD"
|
||||
then
|
||||
refuse_partial "Cannot do a partial commit during a merge."
|
||||
fi
|
||||
TMP_INDEX="$GIT_DIR/tmp-index$$"
|
||||
if test -z "$initial_commit"
|
||||
then
|
||||
# make sure index is clean at the specified paths, or
|
||||
# they are additions.
|
||||
dirty_in_index=`git-diff-index --cached --name-status \
|
||||
--diff-filter=DMTU HEAD -- "$@"`
|
||||
test -z "$dirty_in_index" ||
|
||||
refuse_partial "Different in index and the last commit:
|
||||
if test -f "$GIT_DIR/MERGE_HEAD"
|
||||
then
|
||||
refuse_partial "Cannot do a partial commit during a merge."
|
||||
fi
|
||||
TMP_INDEX="$GIT_DIR/tmp-index$$"
|
||||
if test -z "$initial_commit"
|
||||
then
|
||||
# make sure index is clean at the specified paths, or
|
||||
# they are additions.
|
||||
dirty_in_index=`git-diff-index --cached --name-status \
|
||||
--diff-filter=DMTU HEAD -- "$@"`
|
||||
test -z "$dirty_in_index" ||
|
||||
refuse_partial "Different in index and the last commit:
|
||||
$dirty_in_index"
|
||||
fi
|
||||
commit_only=`git-ls-files --error-unmatch -- "$@"` || exit
|
||||
fi
|
||||
commit_only=`git-ls-files --error-unmatch -- "$@"` || exit
|
||||
|
||||
# Build the temporary index and update the real index
|
||||
# the same way.
|
||||
if test -z "$initial_commit"
|
||||
then
|
||||
cp "$THIS_INDEX" "$TMP_INDEX"
|
||||
GIT_INDEX_FILE="$TMP_INDEX" git-read-tree -m HEAD
|
||||
else
|
||||
rm -f "$TMP_INDEX"
|
||||
fi || exit
|
||||
# Build the temporary index and update the real index
|
||||
# the same way.
|
||||
if test -z "$initial_commit"
|
||||
then
|
||||
cp "$THIS_INDEX" "$TMP_INDEX"
|
||||
GIT_INDEX_FILE="$TMP_INDEX" git-read-tree -m HEAD
|
||||
else
|
||||
rm -f "$TMP_INDEX"
|
||||
fi || exit
|
||||
|
||||
echo "$commit_only" |
|
||||
GIT_INDEX_FILE="$TMP_INDEX" \
|
||||
git-update-index --add --remove --stdin &&
|
||||
echo "$commit_only" |
|
||||
GIT_INDEX_FILE="$TMP_INDEX" \
|
||||
git-update-index --add --remove --stdin &&
|
||||
|
||||
save_index &&
|
||||
echo "$commit_only" |
|
||||
(
|
||||
GIT_INDEX_FILE="$NEXT_INDEX"
|
||||
export GIT_INDEX_FILE
|
||||
git-update-index --remove --stdin
|
||||
) || exit
|
||||
;;
|
||||
save_index &&
|
||||
echo "$commit_only" |
|
||||
(
|
||||
GIT_INDEX_FILE="$NEXT_INDEX"
|
||||
export GIT_INDEX_FILE
|
||||
git-update-index --remove --stdin
|
||||
) || exit
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
@ -426,7 +401,7 @@ else
|
|||
fi
|
||||
|
||||
GIT_INDEX_FILE="$USE_INDEX" \
|
||||
git-update-index -q $unmerged_ok_if_status --refresh || exit
|
||||
git-update-index -q $unmerged_ok_if_status --refresh || exit
|
||||
|
||||
################################################################
|
||||
# If the request is status, just show it and exit.
|
||||
|
|
|
@ -275,7 +275,7 @@ sub req_Directory
|
|||
$state->{directory} = "" if ( $state->{directory} eq "." );
|
||||
$state->{directory} .= "/" if ( $state->{directory} =~ /\S/ );
|
||||
|
||||
if ( not defined($state->{prependdir}) and $state->{localdir} eq "." and $state->{path} =~ /\S/ )
|
||||
if ( (not defined($state->{prependdir}) or $state->{prependdir} eq '') and $state->{localdir} eq "." and $state->{path} =~ /\S/ )
|
||||
{
|
||||
$log->info("Setting prepend to '$state->{path}'");
|
||||
$state->{prependdir} = $state->{path};
|
||||
|
@ -805,7 +805,14 @@ sub req_update
|
|||
$meta = $updater->getmeta($filename);
|
||||
}
|
||||
|
||||
next unless ( $meta->{revision} );
|
||||
if ( ! defined $meta )
|
||||
{
|
||||
$meta = {
|
||||
name => $filename,
|
||||
revision => 0,
|
||||
filehash => 'added'
|
||||
};
|
||||
}
|
||||
|
||||
my $oldmeta = $meta;
|
||||
|
||||
|
@ -835,7 +842,7 @@ sub req_update
|
|||
and not exists ( $state->{opt}{C} ) )
|
||||
{
|
||||
$log->info("Tell the client the file is modified");
|
||||
print "MT text U\n";
|
||||
print "MT text M \n";
|
||||
print "MT fname $filename\n";
|
||||
print "MT newline\n";
|
||||
next;
|
||||
|
@ -855,15 +862,36 @@ sub req_update
|
|||
}
|
||||
}
|
||||
elsif ( not defined ( $state->{entries}{$filename}{modified_hash} )
|
||||
or $state->{entries}{$filename}{modified_hash} eq $oldmeta->{filehash} )
|
||||
or $state->{entries}{$filename}{modified_hash} eq $oldmeta->{filehash}
|
||||
or $meta->{filehash} eq 'added' )
|
||||
{
|
||||
$log->info("Updating '$filename'");
|
||||
# normal update, just send the new revision (either U=Update, or A=Add, or R=Remove)
|
||||
print "MT +updated\n";
|
||||
print "MT text U\n";
|
||||
print "MT fname $filename\n";
|
||||
print "MT newline\n";
|
||||
print "MT -updated\n";
|
||||
# normal update, just send the new revision (either U=Update,
|
||||
# or A=Add, or R=Remove)
|
||||
if ( defined($wrev) && $wrev < 0 )
|
||||
{
|
||||
$log->info("Tell the client the file is scheduled for removal");
|
||||
print "MT text R \n";
|
||||
print "MT fname $filename\n";
|
||||
print "MT newline\n";
|
||||
next;
|
||||
}
|
||||
elsif ( !defined($wrev) || $wrev == 0 )
|
||||
{
|
||||
$log->info("Tell the client the file will be added");
|
||||
print "MT text A \n";
|
||||
print "MT fname $filename\n";
|
||||
print "MT newline\n";
|
||||
next;
|
||||
|
||||
}
|
||||
else {
|
||||
$log->info("Updating '$filename' $wrev");
|
||||
print "MT +updated\n";
|
||||
print "MT text U \n";
|
||||
print "MT fname $filename\n";
|
||||
print "MT newline\n";
|
||||
print "MT -updated\n";
|
||||
}
|
||||
|
||||
my ( $filepart, $dirpart ) = filenamesplit($filename,1);
|
||||
|
||||
|
@ -1709,6 +1737,17 @@ sub argsfromdir
|
|||
|
||||
return if ( scalar ( @{$state->{args}} ) > 1 );
|
||||
|
||||
my @gethead = @{$updater->gethead};
|
||||
|
||||
# push added files
|
||||
foreach my $file (keys %{$state->{entries}}) {
|
||||
if ( exists $state->{entries}{$file}{revision} &&
|
||||
$state->{entries}{$file}{revision} == 0 )
|
||||
{
|
||||
push @gethead, { name => $file, filehash => 'added' };
|
||||
}
|
||||
}
|
||||
|
||||
if ( scalar(@{$state->{args}}) == 1 )
|
||||
{
|
||||
my $arg = $state->{args}[0];
|
||||
|
@ -1716,7 +1755,7 @@ sub argsfromdir
|
|||
|
||||
$log->info("Only one arg specified, checking for directory expansion on '$arg'");
|
||||
|
||||
foreach my $file ( @{$updater->gethead} )
|
||||
foreach my $file ( @gethead )
|
||||
{
|
||||
next if ( $file->{filehash} eq "deleted" and not defined ( $state->{entries}{$file->{name}} ) );
|
||||
next unless ( $file->{name} =~ /^$arg\// or $file->{name} eq $arg );
|
||||
|
@ -1729,7 +1768,7 @@ sub argsfromdir
|
|||
|
||||
$state->{args} = [];
|
||||
|
||||
foreach my $file ( @{$updater->gethead} )
|
||||
foreach my $file ( @gethead )
|
||||
{
|
||||
next if ( $file->{filehash} eq "deleted" and not defined ( $state->{entries}{$file->{name}} ) );
|
||||
next unless ( $file->{name} =~ s/^$state->{prependdir}// );
|
||||
|
@ -2079,9 +2118,17 @@ sub new
|
|||
mode TEXT NOT NULL
|
||||
)
|
||||
");
|
||||
$self->{dbh}->do("
|
||||
CREATE INDEX revision_ix1
|
||||
ON revision (name,revision)
|
||||
");
|
||||
$self->{dbh}->do("
|
||||
CREATE INDEX revision_ix2
|
||||
ON revision (name,commithash)
|
||||
");
|
||||
}
|
||||
|
||||
# Construct the revision table if required
|
||||
# Construct the head table if required
|
||||
unless ( $self->{tables}{head} )
|
||||
{
|
||||
$self->{dbh}->do("
|
||||
|
@ -2095,6 +2142,10 @@ sub new
|
|||
mode TEXT NOT NULL
|
||||
)
|
||||
");
|
||||
$self->{dbh}->do("
|
||||
CREATE INDEX head_ix1
|
||||
ON head (name)
|
||||
");
|
||||
}
|
||||
|
||||
# Construct the properties table if required
|
||||
|
|
47
git-fetch.sh
47
git-fetch.sh
|
@ -129,22 +129,25 @@ append_fetch_head () {
|
|||
then
|
||||
headc_=$(git-rev-parse --verify "$head_^0") || exit
|
||||
echo "$headc_ $not_for_merge_ $note_" >>"$GIT_DIR/FETCH_HEAD"
|
||||
[ "$verbose" ] && echo >&2 "* committish: $head_"
|
||||
[ "$verbose" ] && echo >&2 " $note_"
|
||||
else
|
||||
echo "$head_ not-for-merge $note_" >>"$GIT_DIR/FETCH_HEAD"
|
||||
[ "$verbose" ] && echo >&2 "* non-commit: $head_"
|
||||
[ "$verbose" ] && echo >&2 " $note_"
|
||||
fi
|
||||
if test "$local_name_" != ""
|
||||
then
|
||||
# We are storing the head locally. Make sure that it is
|
||||
# a fast forward (aka "reverse push").
|
||||
fast_forward_local "$local_name_" "$head_" "$note_"
|
||||
fi
|
||||
|
||||
update_local_ref "$local_name_" "$head_" "$note_"
|
||||
}
|
||||
|
||||
fast_forward_local () {
|
||||
update_local_ref () {
|
||||
# If we are storing the head locally make sure that it is
|
||||
# a fast forward (aka "reverse push").
|
||||
|
||||
label_=$(git-cat-file -t $2)
|
||||
newshort_=$(git-rev-parse --short $2)
|
||||
if test -z "$1" ; then
|
||||
[ "$verbose" ] && echo >&2 "* fetched $3"
|
||||
[ "$verbose" ] && echo >&2 " $label_: $newshort_"
|
||||
return 0
|
||||
fi
|
||||
oldshort_=$(git-rev-parse --short "$1" 2>/dev/null)
|
||||
mkdir -p "$(dirname "$GIT_DIR/$1")"
|
||||
case "$1" in
|
||||
refs/tags/*)
|
||||
|
@ -154,13 +157,16 @@ fast_forward_local () {
|
|||
then
|
||||
if now_=$(cat "$GIT_DIR/$1") && test "$now_" = "$2"
|
||||
then
|
||||
[ "$verbose" ] && echo >&2 "* $1: same as $3" ||:
|
||||
[ "$verbose" ] && echo >&2 "* $1: same as $3"
|
||||
[ "$verbose" ] && echo >&2 " $label_: $newshort_" ||:
|
||||
else
|
||||
echo >&2 "* $1: updating with $3"
|
||||
echo >&2 " $label_: $newshort_"
|
||||
git-update-ref -m "$rloga: updating tag" "$1" "$2"
|
||||
fi
|
||||
else
|
||||
echo >&2 "* $1: storing $3"
|
||||
echo >&2 " $label_: $newshort_"
|
||||
git-update-ref -m "$rloga: storing tag" "$1" "$2"
|
||||
fi
|
||||
;;
|
||||
|
@ -178,31 +184,34 @@ fast_forward_local () {
|
|||
if test -n "$verbose"
|
||||
then
|
||||
echo >&2 "* $1: same as $3"
|
||||
echo >&2 " $label_: $newshort_"
|
||||
fi
|
||||
;;
|
||||
*,$local)
|
||||
echo >&2 "* $1: fast forward to $3"
|
||||
echo >&2 " from $local to $2"
|
||||
echo >&2 " old..new: $oldshort_..$newshort_"
|
||||
git-update-ref -m "$rloga: fast-forward" "$1" "$2" "$local"
|
||||
;;
|
||||
*)
|
||||
false
|
||||
;;
|
||||
esac || {
|
||||
echo >&2 "* $1: does not fast forward to $3;"
|
||||
case ",$force,$single_force," in
|
||||
*,t,*)
|
||||
echo >&2 " forcing update."
|
||||
echo >&2 "* $1: forcing update to non-fast forward $3"
|
||||
echo >&2 " old...new: $oldshort_...$newshort_"
|
||||
git-update-ref -m "$rloga: forced-update" "$1" "$2" "$local"
|
||||
;;
|
||||
*)
|
||||
echo >&2 " not updating."
|
||||
echo >&2 "* $1: not updating to non-fast forward $3"
|
||||
echo >&2 " old...new: $oldshort_...$newshort_"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
else
|
||||
echo >&2 "* $1: storing $3"
|
||||
echo >&2 " $label_: $newshort_"
|
||||
git-update-ref -m "$rloga: storing head" "$1" "$2"
|
||||
fi
|
||||
;;
|
||||
|
@ -436,10 +445,10 @@ esac
|
|||
|
||||
# If the original head was empty (i.e. no "master" yet), or
|
||||
# if we were told not to worry, we do not have to check.
|
||||
case ",$update_head_ok,$orig_head," in
|
||||
*,, | t,* )
|
||||
case "$orig_head" in
|
||||
'')
|
||||
;;
|
||||
*)
|
||||
?*)
|
||||
curr_head=$(git-rev-parse --verify HEAD 2>/dev/null)
|
||||
if test "$curr_head" != "$orig_head"
|
||||
then
|
||||
|
|
|
@ -93,6 +93,8 @@ finish () {
|
|||
esac
|
||||
}
|
||||
|
||||
case "$#" in 0) usage ;; esac
|
||||
|
||||
rloga=
|
||||
while case "$#" in 0) break ;; esac
|
||||
do
|
||||
|
@ -197,7 +199,7 @@ f,*)
|
|||
;;
|
||||
?,1,"$head",*)
|
||||
# Again the most common case of merging one remote.
|
||||
echo "Updating from $head to $1"
|
||||
echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $1)"
|
||||
git-update-index --refresh 2>/dev/null
|
||||
new_head=$(git-rev-parse --verify "$1^0") &&
|
||||
git-read-tree -u -v -m $head "$new_head" &&
|
||||
|
|
|
@ -58,7 +58,7 @@ then
|
|||
|
||||
echo >&2 "Warning: fetch updated the current branch head."
|
||||
echo >&2 "Warning: fast forwarding your working tree from"
|
||||
echo >&2 "Warning: $orig_head commit."
|
||||
echo >&2 "Warning: commit $orig_head."
|
||||
git-update-index --refresh 2>/dev/null
|
||||
git-read-tree -u -m "$orig_head" "$curr_head" ||
|
||||
die 'Cannot fast-forward your working tree.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# Copyright (c) 2005 Junio C Hamano.
|
||||
#
|
||||
|
||||
USAGE='[--onto <newbase>] <upstream> [<branch>]'
|
||||
USAGE='[-v] [--onto <newbase>] <upstream> [<branch>]'
|
||||
LONG_USAGE='git-rebase replaces <branch> with a new branch of the
|
||||
same name. When the --onto option is provided the new branch starts
|
||||
out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
|
||||
|
@ -39,6 +39,7 @@ strategy=recursive
|
|||
do_merge=
|
||||
dotest=$GIT_DIR/.dotest-merge
|
||||
prec=4
|
||||
verbose=
|
||||
|
||||
continue_merge () {
|
||||
test -n "$prev_head" || die "prev_head must be defined"
|
||||
|
@ -190,6 +191,9 @@ do
|
|||
esac
|
||||
do_merge=t
|
||||
;;
|
||||
-v|--verbose)
|
||||
verbose=t
|
||||
;;
|
||||
-*)
|
||||
usage
|
||||
;;
|
||||
|
@ -273,6 +277,12 @@ then
|
|||
exit 0
|
||||
fi
|
||||
|
||||
if test -n "$verbose"
|
||||
then
|
||||
echo "Changes from $mb to $onto:"
|
||||
git-diff-tree --stat --summary "$mb" "$onto"
|
||||
fi
|
||||
|
||||
# Rewind the head to "$onto"; this saves our current head in ORIG_HEAD.
|
||||
git-reset --hard "$onto"
|
||||
|
||||
|
@ -286,7 +296,7 @@ fi
|
|||
|
||||
if test -z "$do_merge"
|
||||
then
|
||||
git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
|
||||
git-format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD |
|
||||
git am --binary -3 -k --resolvemsg="$RESOLVEMSG" \
|
||||
--reflog-action=rebase
|
||||
exit $?
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# Copyright (c) 2005 Linus Torvalds
|
||||
#
|
||||
|
||||
USAGE='[-a] [-d] [-f] [-l] [-n] [-q]'
|
||||
USAGE='[-a] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]'
|
||||
SUBDIRECTORY_OK='Yes'
|
||||
. git-sh-setup
|
||||
|
||||
|
@ -25,6 +25,15 @@ do
|
|||
shift
|
||||
done
|
||||
|
||||
# Later we will default repack.UseDeltaBaseOffset to true
|
||||
default_dbo=false
|
||||
|
||||
case "`git repo-config --bool repack.usedeltabaseoffset ||
|
||||
echo $default_dbo`" in
|
||||
true)
|
||||
extra="$extra --delta-base-offset" ;;
|
||||
esac
|
||||
|
||||
PACKDIR="$GIT_OBJECT_DIRECTORY/pack"
|
||||
PACKTMP="$GIT_DIR/.tmp-$$-pack"
|
||||
rm -f "$PACKTMP"-*
|
||||
|
|
|
@ -46,7 +46,7 @@ case "$common" in
|
|||
exit 0
|
||||
;;
|
||||
"$head")
|
||||
echo "Updating from $head to $merge"
|
||||
echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $merge)"
|
||||
git-read-tree -u -m $head $merge || exit 1
|
||||
git-update-ref -m "resolve $merge_name: Fast forward" \
|
||||
HEAD "$merge" "$head"
|
||||
|
|
|
@ -7,18 +7,20 @@
|
|||
case "$0" in
|
||||
*-revert* )
|
||||
test -t 0 && edit=-e
|
||||
replay=
|
||||
me=revert
|
||||
USAGE='[--edit | --no-edit] [-n] <commit-ish>' ;;
|
||||
*-cherry-pick* )
|
||||
replay=t
|
||||
edit=
|
||||
me=cherry-pick
|
||||
USAGE='[--edit] [-n] [-r] <commit-ish>' ;;
|
||||
USAGE='[--edit] [-n] [-r] [-x] <commit-ish>' ;;
|
||||
* )
|
||||
die "What are you talking about?" ;;
|
||||
esac
|
||||
. git-sh-setup
|
||||
|
||||
no_commit= replay=
|
||||
no_commit=
|
||||
while case "$#" in 0) break ;; esac
|
||||
do
|
||||
case "$1" in
|
||||
|
@ -32,8 +34,10 @@ do
|
|||
--n|--no|--no-|--no-e|--no-ed|--no-edi|--no-edit)
|
||||
edit=
|
||||
;;
|
||||
-r|--r|--re|--rep|--repl|--repla|--replay)
|
||||
replay=t
|
||||
-r)
|
||||
: no-op ;;
|
||||
-x|--i-really-want-to-expose-my-private-commit-object-name)
|
||||
replay=
|
||||
;;
|
||||
-*)
|
||||
usage
|
||||
|
@ -121,7 +125,7 @@ cherry-pick)
|
|||
git-cat-file commit $commit | sed -e '1,/^$/d'
|
||||
case "$replay" in
|
||||
'')
|
||||
echo "(cherry picked from $commit commit)"
|
||||
echo "(cherry picked from commit $commit)"
|
||||
test "$rev" = "$commit" ||
|
||||
echo "(original 'git cherry-pick' arguments: $@)"
|
||||
;;
|
||||
|
|
|
@ -83,11 +83,12 @@ sub cleanup_compose_files();
|
|||
my $compose_filename = ".msg.$$";
|
||||
|
||||
# Variables we fill in automatically, or via prompting:
|
||||
my (@to,@cc,@initial_cc,@bcclist,
|
||||
my (@to,@cc,@initial_cc,@bcclist,@xh,
|
||||
$initial_reply_to,$initial_subject,@files,$from,$compose,$time);
|
||||
|
||||
# Behavior modification variables
|
||||
my ($chain_reply_to, $quiet, $suppress_from, $no_signed_off_cc) = (1, 0, 0, 0);
|
||||
my ($chain_reply_to, $quiet, $suppress_from, $no_signed_off_cc,
|
||||
$dry_run) = (1, 0, 0, 0, 0);
|
||||
my $smtp_server;
|
||||
|
||||
# Example reply to:
|
||||
|
@ -116,6 +117,7 @@ my $rc = GetOptions("from=s" => \$from,
|
|||
"quiet" => \$quiet,
|
||||
"suppress-from" => \$suppress_from,
|
||||
"no-signed-off-cc|no-signed-off-by-cc" => \$no_signed_off_cc,
|
||||
"dry-run" => \$dry_run,
|
||||
);
|
||||
|
||||
# Verify the user input
|
||||
|
@ -409,6 +411,11 @@ sub send_message
|
|||
$gitversion = Git::version();
|
||||
}
|
||||
|
||||
my ($author_name) = ($from =~ /^(.*?)\s+</);
|
||||
if ($author_name && $author_name =~ /\./ && $author_name !~ /^".*"$/) {
|
||||
my ($name, $addr) = ($from =~ /^(.*?)(\s+<.*)/);
|
||||
$from = "\"$name\"$addr";
|
||||
}
|
||||
my $header = "From: $from
|
||||
To: $to
|
||||
Cc: $cc
|
||||
|
@ -422,8 +429,13 @@ X-Mailer: git-send-email $gitversion
|
|||
$header .= "In-Reply-To: $reply_to\n";
|
||||
$header .= "References: $references\n";
|
||||
}
|
||||
if (@xh) {
|
||||
$header .= join("\n", @xh) . "\n";
|
||||
}
|
||||
|
||||
if ($smtp_server =~ m#^/#) {
|
||||
if ($dry_run) {
|
||||
# We don't want to send the email.
|
||||
} elsif ($smtp_server =~ m#^/#) {
|
||||
my $pid = open my $sm, '|-';
|
||||
defined $pid or die $!;
|
||||
if (!$pid) {
|
||||
|
@ -472,15 +484,22 @@ foreach my $t (@files) {
|
|||
|
||||
my $author_not_sender = undef;
|
||||
@cc = @initial_cc;
|
||||
my $found_mbox = 0;
|
||||
@xh = ();
|
||||
my $input_format = undef;
|
||||
my $header_done = 0;
|
||||
$message = "";
|
||||
while(<F>) {
|
||||
if (!$header_done) {
|
||||
$found_mbox = 1, next if (/^From /);
|
||||
if (/^From /) {
|
||||
$input_format = 'mbox';
|
||||
next;
|
||||
}
|
||||
chomp;
|
||||
if (!defined $input_format && /^[-A-Za-z]+:\s/) {
|
||||
$input_format = 'mbox';
|
||||
}
|
||||
|
||||
if ($found_mbox) {
|
||||
if (defined $input_format && $input_format eq 'mbox') {
|
||||
if (/^Subject:\s+(.*)$/) {
|
||||
$subject = $1;
|
||||
|
||||
|
@ -495,6 +514,9 @@ foreach my $t (@files) {
|
|||
$2, $_) unless $quiet;
|
||||
push @cc, $2;
|
||||
}
|
||||
elsif (!/^Date:\s/ && /^[-A-Za-z]+:\s+\S/) {
|
||||
push @xh, $_;
|
||||
}
|
||||
|
||||
} else {
|
||||
# In the traditional
|
||||
|
@ -502,6 +524,7 @@ foreach my $t (@files) {
|
|||
# line 1 = cc
|
||||
# line 2 = subject
|
||||
# So let's support that, too.
|
||||
$input_format = 'lots';
|
||||
if (@cc == 0) {
|
||||
printf("(non-mbox) Adding cc: %s from line '%s'\n",
|
||||
$_, $_) unless $quiet;
|
||||
|
@ -538,7 +561,7 @@ foreach my $t (@files) {
|
|||
send_message();
|
||||
|
||||
# set up for the next message
|
||||
if ($chain_reply_to || length($reply_to) == 0) {
|
||||
if ($chain_reply_to || !defined $reply_to || length($reply_to) == 0) {
|
||||
$reply_to = $message_id;
|
||||
if (length $references > 0) {
|
||||
$references .= " $message_id";
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
#!/usr/bin/perl -w
|
||||
|
||||
use strict;
|
||||
use Getopt::Std;
|
||||
use File::Basename qw(basename dirname);
|
||||
|
||||
our ($opt_h, $opt_n, $opt_s);
|
||||
getopts('hns');
|
||||
|
||||
$opt_h && usage();
|
||||
|
||||
sub usage {
|
||||
print STDERR "Usage: ${\basename $0} [-h] [-n] [-s] < <log_data>\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
my (%mailmap);
|
||||
my (%email);
|
||||
|
@ -38,16 +50,38 @@ sub by_name($$) {
|
|||
|
||||
uc($a) cmp uc($b);
|
||||
}
|
||||
sub by_nbentries($$) {
|
||||
my ($a, $b) = @_;
|
||||
my $a_entries = $map{$a};
|
||||
my $b_entries = $map{$b};
|
||||
|
||||
@$b_entries - @$a_entries || by_name $a, $b;
|
||||
}
|
||||
|
||||
my $sort_method = $opt_n ? \&by_nbentries : \&by_name;
|
||||
|
||||
sub summary_output {
|
||||
my ($obj, $num, $key);
|
||||
|
||||
foreach $key (sort $sort_method keys %map) {
|
||||
$obj = $map{$key};
|
||||
$num = @$obj;
|
||||
printf "%s: %u\n", $key, $num;
|
||||
$n_output += $num;
|
||||
}
|
||||
}
|
||||
|
||||
sub shortlog_output {
|
||||
my ($obj, $key, $desc);
|
||||
my ($obj, $num, $key, $desc);
|
||||
|
||||
foreach $key (sort $sort_method keys %map) {
|
||||
$obj = $map{$key};
|
||||
$num = @$obj;
|
||||
|
||||
foreach $key (sort by_name keys %map) {
|
||||
# output author
|
||||
printf "%s:\n", $key;
|
||||
printf "%s (%u):\n", $key, $num;
|
||||
|
||||
# output author's 1-line summaries
|
||||
$obj = $map{$key};
|
||||
foreach $desc (reverse @$obj) {
|
||||
print " $desc\n";
|
||||
$n_output++;
|
||||
|
@ -152,7 +186,7 @@ sub finalize {
|
|||
|
||||
&setup_mailmap;
|
||||
&changelog_input;
|
||||
&shortlog_output;
|
||||
$opt_s ? &summary_output : &shortlog_output;
|
||||
&finalize;
|
||||
exit(0);
|
||||
|
||||
|
|
78
git-svn.perl
78
git-svn.perl
|
@ -40,8 +40,22 @@ memoize('cmt_metadata');
|
|||
memoize('get_commit_time');
|
||||
|
||||
my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib);
|
||||
|
||||
sub nag_lib {
|
||||
print STDERR <<EOF;
|
||||
! Please consider installing the SVN Perl libraries (version 1.1.0 or
|
||||
! newer). You will generally get better performance and fewer bugs,
|
||||
! especially if you:
|
||||
! 1) have a case-insensitive filesystem
|
||||
! 2) replace symlinks with files (and vice-versa) in commits
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
$_use_lib = 1 unless $ENV{GIT_SVN_NO_LIB};
|
||||
libsvn_load();
|
||||
nag_lib() unless $_use_lib;
|
||||
|
||||
my $_optimize_commits = 1 unless $ENV{GIT_SVN_NO_OPTIMIZE_COMMITS};
|
||||
my $sha1 = qr/[a-f\d]{40}/;
|
||||
my $sha1_short = qr/[a-f\d]{4,40}/;
|
||||
|
@ -52,7 +66,7 @@ my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
|
|||
$_template, $_shared, $_no_default_regex, $_no_graft_copy,
|
||||
$_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,
|
||||
$_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m,
|
||||
$_merge, $_strategy, $_dry_run, $_ignore_nodate);
|
||||
$_merge, $_strategy, $_dry_run, $_ignore_nodate, $_non_recursive);
|
||||
my (@_branch_from, %tree_map, %users, %rusers, %equiv);
|
||||
my ($_svn_co_url_revs, $_svn_pg_peg_revs);
|
||||
my @repo_path_split_cache;
|
||||
|
@ -114,6 +128,7 @@ my %cmd = (
|
|||
'incremental' => \$_incremental,
|
||||
'oneline' => \$_oneline,
|
||||
'show-commit' => \$_show_commit,
|
||||
'non-recursive' => \$_non_recursive,
|
||||
'authors-file|A=s' => \$_authors,
|
||||
} ],
|
||||
'commit-diff' => [ \&commit_diff, 'Commit a diff between two trees',
|
||||
|
@ -168,11 +183,11 @@ Usage: $0 <command> [options] [arguments]\n
|
|||
|
||||
foreach (sort keys %cmd) {
|
||||
next if $cmd && $cmd ne $_;
|
||||
print $fd ' ',pack('A13',$_),$cmd{$_}->[1],"\n";
|
||||
print $fd ' ',pack('A17',$_),$cmd{$_}->[1],"\n";
|
||||
foreach (keys %{$cmd{$_}->[2]}) {
|
||||
# prints out arguments as they should be passed:
|
||||
my $x = s#[:=]s$## ? '<arg>' : s#[:=]i$## ? '<num>' : '';
|
||||
print $fd ' ' x 17, join(', ', map { length $_ > 1 ?
|
||||
print $fd ' ' x 21, join(', ', map { length $_ > 1 ?
|
||||
"--$_" : "-$_" }
|
||||
split /\|/,$_)," $x\n";
|
||||
}
|
||||
|
@ -521,7 +536,7 @@ sub commit_lib {
|
|||
$SVN = libsvn_connect($repo);
|
||||
my $ed = SVN::Git::Editor->new(
|
||||
{ r => $r_last,
|
||||
ra => $SVN,
|
||||
ra => $SVN_LOG,
|
||||
c => $c,
|
||||
svn_path => $SVN_PATH
|
||||
},
|
||||
|
@ -682,12 +697,17 @@ sub multi_init {
|
|||
}
|
||||
$_trunk = $url . $_trunk;
|
||||
}
|
||||
my $ch_id;
|
||||
if ($GIT_SVN eq 'git-svn') {
|
||||
print "GIT_SVN_ID set to 'trunk' for $_trunk\n";
|
||||
$ch_id = 1;
|
||||
$GIT_SVN = $ENV{GIT_SVN_ID} = 'trunk';
|
||||
}
|
||||
init_vars();
|
||||
init($_trunk);
|
||||
unless (-d $GIT_SVN_DIR) {
|
||||
print "GIT_SVN_ID set to 'trunk' for $_trunk\n" if $ch_id;
|
||||
init($_trunk);
|
||||
sys('git-repo-config', 'svn.trunk', $_trunk);
|
||||
}
|
||||
complete_url_ls_init($url, $_branches, '--branches/-b', '');
|
||||
complete_url_ls_init($url, $_tags, '--tags/-t', 'tags/');
|
||||
}
|
||||
|
@ -747,13 +767,18 @@ sub show_log {
|
|||
# ignore
|
||||
} elsif (/^:\d{6} \d{6} $sha1_short/o) {
|
||||
push @{$c->{raw}}, $_;
|
||||
} elsif (/^[ACRMDT]\t/) {
|
||||
# we could add $SVN_PATH here, but that requires
|
||||
# remote access at the moment (repo_path_split)...
|
||||
s#^([ACRMDT])\t# $1 #;
|
||||
push @{$c->{changed}}, $_;
|
||||
} elsif (/^diff /) {
|
||||
$d = 1;
|
||||
push @{$c->{diff}}, $_;
|
||||
} elsif ($d) {
|
||||
push @{$c->{diff}}, $_;
|
||||
} elsif (/^ (git-svn-id:.+)$/) {
|
||||
(undef, $c->{r}, undef) = extract_metadata($1);
|
||||
($c->{url}, $c->{r}, undef) = extract_metadata($1);
|
||||
} elsif (s/^ //) {
|
||||
push @{$c->{l}}, $_;
|
||||
}
|
||||
|
@ -807,7 +832,7 @@ sub commit_diff {
|
|||
$SVN ||= libsvn_connect($repo);
|
||||
my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : ();
|
||||
my $ed = SVN::Git::Editor->new({ r => $SVN->get_latest_revnum,
|
||||
ra => $SVN, c => $tb,
|
||||
ra => $SVN_LOG, c => $tb,
|
||||
svn_path => $SVN_PATH
|
||||
},
|
||||
$SVN->get_commit_editor($_message,
|
||||
|
@ -845,7 +870,8 @@ sub git_svn_log_cmd {
|
|||
my ($r_min, $r_max) = @_;
|
||||
my @cmd = (qw/git-log --abbrev-commit --pretty=raw
|
||||
--default/, "refs/remotes/$GIT_SVN");
|
||||
push @cmd, '--summary' if $_verbose;
|
||||
push @cmd, '-r' unless $_non_recursive;
|
||||
push @cmd, qw/--raw --name-status/ if $_verbose;
|
||||
return @cmd unless defined $r_max;
|
||||
if ($r_max == $r_min) {
|
||||
push @cmd, '--max-count=1';
|
||||
|
@ -856,7 +882,7 @@ sub git_svn_log_cmd {
|
|||
my ($c_min, $c_max);
|
||||
$c_max = revdb_get($REVDB, $r_max);
|
||||
$c_min = revdb_get($REVDB, $r_min);
|
||||
if ($c_min && $c_max) {
|
||||
if (defined $c_min && defined $c_max) {
|
||||
if ($r_max > $r_max) {
|
||||
push @cmd, "$c_min..$c_max";
|
||||
} else {
|
||||
|
@ -937,16 +963,21 @@ sub complete_url_ls_init {
|
|||
print STDERR "W: Unrecognized URL: $u\n";
|
||||
die "This should never happen\n";
|
||||
}
|
||||
# don't try to init already existing refs
|
||||
my $id = $pfx.$1;
|
||||
print "init $u => $id\n";
|
||||
$GIT_SVN = $ENV{GIT_SVN_ID} = $id;
|
||||
init_vars();
|
||||
init($u);
|
||||
unless (-d $GIT_SVN_DIR) {
|
||||
print "init $u => $id\n";
|
||||
init($u);
|
||||
}
|
||||
}
|
||||
exit 0;
|
||||
}
|
||||
waitpid $pid, 0;
|
||||
croak $? if $?;
|
||||
my ($n) = ($switch =~ /^--(\w+)/);
|
||||
sys('git-repo-config', "svn.$n", $var);
|
||||
}
|
||||
|
||||
sub common_prefix {
|
||||
|
@ -2551,6 +2582,12 @@ sub show_commit {
|
|||
}
|
||||
}
|
||||
|
||||
sub show_commit_changed_paths {
|
||||
my ($c) = @_;
|
||||
return unless $c->{changed};
|
||||
print "Changed paths:\n", @{$c->{changed}};
|
||||
}
|
||||
|
||||
sub show_commit_normal {
|
||||
my ($c) = @_;
|
||||
print '-' x72, "\nr$c->{r} | ";
|
||||
|
@ -2560,7 +2597,8 @@ sub show_commit_normal {
|
|||
my $nr_line = 0;
|
||||
|
||||
if (my $l = $c->{l}) {
|
||||
while ($l->[$#$l] eq "\n" && $l->[($#$l - 1)] eq "\n") {
|
||||
while ($l->[$#$l] eq "\n" && $#$l > 0
|
||||
&& $l->[($#$l - 1)] eq "\n") {
|
||||
pop @$l;
|
||||
}
|
||||
$nr_line = scalar @$l;
|
||||
|
@ -2572,11 +2610,15 @@ sub show_commit_normal {
|
|||
} else {
|
||||
$nr_line .= ' lines';
|
||||
}
|
||||
print $nr_line, "\n\n";
|
||||
print $nr_line, "\n";
|
||||
show_commit_changed_paths($c);
|
||||
print "\n";
|
||||
print $_ foreach @$l;
|
||||
}
|
||||
} else {
|
||||
print "1 line\n\n";
|
||||
print "1 line\n";
|
||||
show_commit_changed_paths($c);
|
||||
print "\n";
|
||||
|
||||
}
|
||||
foreach my $x (qw/raw diff/) {
|
||||
|
@ -3312,9 +3354,11 @@ sub chg_file {
|
|||
seek $fh, 0, 0 or croak $!;
|
||||
|
||||
my $exp = $md5->hexdigest;
|
||||
my $atd = $self->apply_textdelta($fbat, undef, $self->{pool});
|
||||
my $got = SVN::TxDelta::send_stream($fh, @$atd, $self->{pool});
|
||||
my $pool = SVN::Pool->new;
|
||||
my $atd = $self->apply_textdelta($fbat, undef, $pool);
|
||||
my $got = SVN::TxDelta::send_stream($fh, @$atd, $pool);
|
||||
die "Checksum mismatch\nexpected: $exp\ngot: $got\n" if ($got ne $exp);
|
||||
$pool->clear;
|
||||
|
||||
close $fh or croak $!;
|
||||
}
|
||||
|
|
|
@ -193,6 +193,13 @@ sub ignore {
|
|||
}
|
||||
}
|
||||
|
||||
sub dir_list {
|
||||
my($self,$path,$rev) = @_;
|
||||
my ($dirents,undef,$properties)
|
||||
= $self->{'svn'}->get_dir($path,$rev,undef);
|
||||
return $dirents;
|
||||
}
|
||||
|
||||
package main;
|
||||
use URI;
|
||||
|
||||
|
@ -342,35 +349,16 @@ if ($opt_A) {
|
|||
|
||||
open BRANCHES,">>", "$git_dir/svn2git";
|
||||
|
||||
sub node_kind($$$) {
|
||||
my ($branch, $path, $revision) = @_;
|
||||
sub node_kind($$) {
|
||||
my ($svnpath, $revision) = @_;
|
||||
my $pool=SVN::Pool->new;
|
||||
my $kind = $svn->{'svn'}->check_path(revert_split_path($branch,$path),$revision,$pool);
|
||||
my $kind = $svn->{'svn'}->check_path($svnpath,$revision,$pool);
|
||||
$pool->clear;
|
||||
return $kind;
|
||||
}
|
||||
|
||||
sub revert_split_path($$) {
|
||||
my($branch,$path) = @_;
|
||||
|
||||
my $svnpath;
|
||||
$path = "" if $path eq "/"; # this should not happen, but ...
|
||||
if($branch eq "/") {
|
||||
$svnpath = "$trunk_name/$path";
|
||||
} elsif($branch =~ m#^/#) {
|
||||
$svnpath = "$tag_name$branch/$path";
|
||||
} else {
|
||||
$svnpath = "$branch_name/$branch/$path";
|
||||
}
|
||||
|
||||
$svnpath =~ s#/+$##;
|
||||
return $svnpath;
|
||||
}
|
||||
|
||||
sub get_file($$$) {
|
||||
my($rev,$branch,$path) = @_;
|
||||
|
||||
my $svnpath = revert_split_path($branch,$path);
|
||||
my($svnpath,$rev,$path) = @_;
|
||||
|
||||
# now get it
|
||||
my ($name,$mode);
|
||||
|
@ -413,10 +401,9 @@ sub get_file($$$) {
|
|||
}
|
||||
|
||||
sub get_ignore($$$$$) {
|
||||
my($new,$old,$rev,$branch,$path) = @_;
|
||||
my($new,$old,$rev,$path,$svnpath) = @_;
|
||||
|
||||
return unless $opt_I;
|
||||
my $svnpath = revert_split_path($branch,$path);
|
||||
my $name = $svn->ignore("$svnpath",$rev);
|
||||
if ($path eq '/') {
|
||||
$path = $opt_I;
|
||||
|
@ -435,7 +422,7 @@ sub get_ignore($$$$$) {
|
|||
close $F;
|
||||
unlink $name;
|
||||
push(@$new,['0644',$sha,$path]);
|
||||
} else {
|
||||
} elsif (defined $old) {
|
||||
push(@$old,$path);
|
||||
}
|
||||
}
|
||||
|
@ -480,6 +467,27 @@ sub branch_rev($$) {
|
|||
return $therev;
|
||||
}
|
||||
|
||||
sub expand_svndir($$$);
|
||||
|
||||
sub expand_svndir($$$)
|
||||
{
|
||||
my ($svnpath, $rev, $path) = @_;
|
||||
my @list;
|
||||
get_ignore(\@list, undef, $rev, $path, $svnpath);
|
||||
my $dirents = $svn->dir_list($svnpath, $rev);
|
||||
foreach my $p(keys %$dirents) {
|
||||
my $kind = node_kind($svnpath.'/'.$p, $rev);
|
||||
if ($kind eq $SVN::Node::file) {
|
||||
my $f = get_file($svnpath.'/'.$p, $rev, $path.'/'.$p);
|
||||
push(@list, $f) if $f;
|
||||
} elsif ($kind eq $SVN::Node::dir) {
|
||||
push(@list,
|
||||
expand_svndir($svnpath.'/'.$p, $rev, $path.'/'.$p));
|
||||
}
|
||||
}
|
||||
return @list;
|
||||
}
|
||||
|
||||
sub copy_path($$$$$$$$) {
|
||||
# Somebody copied a whole subdirectory.
|
||||
# We need to find the index entries from the old version which the
|
||||
|
@ -488,8 +496,11 @@ sub copy_path($$$$$$$$) {
|
|||
my($newrev,$newbranch,$path,$oldpath,$rev,$node_kind,$new,$parents) = @_;
|
||||
|
||||
my($srcbranch,$srcpath) = split_path($rev,$oldpath);
|
||||
unless(defined $srcbranch) {
|
||||
print "Path not found when copying from $oldpath @ $rev\n";
|
||||
unless(defined $srcbranch && defined $srcpath) {
|
||||
print "Path not found when copying from $oldpath @ $rev.\n".
|
||||
"Will try to copy from original SVN location...\n"
|
||||
if $opt_v;
|
||||
push (@$new, expand_svndir($oldpath, $rev, $path));
|
||||
return;
|
||||
}
|
||||
my $therev = branch_rev($srcbranch, $rev);
|
||||
|
@ -503,7 +514,7 @@ sub copy_path($$$$$$$$) {
|
|||
}
|
||||
print "$newrev:$newbranch:$path: copying from $srcbranch:$srcpath @ $rev\n" if $opt_v;
|
||||
if ($node_kind eq $SVN::Node::dir) {
|
||||
$srcpath =~ s#/*$#/#;
|
||||
$srcpath =~ s#/*$#/#;
|
||||
}
|
||||
|
||||
my $pid = open my $f,'-|';
|
||||
|
@ -582,10 +593,12 @@ sub commit {
|
|||
if(defined $oldpath) {
|
||||
my $p;
|
||||
($parent,$p) = split_path($revision,$oldpath);
|
||||
if($parent eq "/") {
|
||||
$parent = $opt_o;
|
||||
} else {
|
||||
$parent =~ s#^/##; # if it's a tag
|
||||
if(defined $parent) {
|
||||
if($parent eq "/") {
|
||||
$parent = $opt_o;
|
||||
} else {
|
||||
$parent =~ s#^/##; # if it's a tag
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$parent = undef;
|
||||
|
@ -651,9 +664,10 @@ sub commit {
|
|||
push(@old,$path); # remove any old stuff
|
||||
}
|
||||
if(($action->[0] eq "A") || ($action->[0] eq "R")) {
|
||||
my $node_kind = node_kind($branch,$path,$revision);
|
||||
my $node_kind = node_kind($action->[3], $revision);
|
||||
if ($node_kind eq $SVN::Node::file) {
|
||||
my $f = get_file($revision,$branch,$path);
|
||||
my $f = get_file($action->[3],
|
||||
$revision, $path);
|
||||
if ($f) {
|
||||
push(@new,$f) if $f;
|
||||
} else {
|
||||
|
@ -668,19 +682,20 @@ sub commit {
|
|||
\@new, \@parents);
|
||||
} else {
|
||||
get_ignore(\@new, \@old, $revision,
|
||||
$branch, $path);
|
||||
$path, $action->[3]);
|
||||
}
|
||||
}
|
||||
} elsif ($action->[0] eq "D") {
|
||||
push(@old,$path);
|
||||
} elsif ($action->[0] eq "M") {
|
||||
my $node_kind = node_kind($branch,$path,$revision);
|
||||
my $node_kind = node_kind($action->[3], $revision);
|
||||
if ($node_kind eq $SVN::Node::file) {
|
||||
my $f = get_file($revision,$branch,$path);
|
||||
my $f = get_file($action->[3],
|
||||
$revision, $path);
|
||||
push(@new,$f) if $f;
|
||||
} elsif ($node_kind eq $SVN::Node::dir) {
|
||||
get_ignore(\@new, \@old, $revision,
|
||||
$branch,$path);
|
||||
$path, $action->[3]);
|
||||
}
|
||||
} else {
|
||||
die "$revision: unknown action '".$action->[0]."' for $path\n";
|
||||
|
@ -838,7 +853,7 @@ sub commit {
|
|||
print $out ("object $cid\n".
|
||||
"type commit\n".
|
||||
"tag $dest\n".
|
||||
"tagger $committer_name <$committer_email>\n") and
|
||||
"tagger $committer_name <$committer_email> 0 +0000\n") and
|
||||
close($out)
|
||||
or die "Cannot create tag object $dest: $!\n";
|
||||
|
||||
|
|
6
git.c
6
git.c
|
@ -16,7 +16,7 @@
|
|||
#include "builtin.h"
|
||||
|
||||
const char git_usage_string[] =
|
||||
"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ ARGS ]";
|
||||
"git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate] [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]";
|
||||
|
||||
static void prepend_to_path(const char *dir, int len)
|
||||
{
|
||||
|
@ -226,7 +226,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
|
|||
{ "check-ref-format", cmd_check_ref_format },
|
||||
{ "commit-tree", cmd_commit_tree, RUN_SETUP },
|
||||
{ "count-objects", cmd_count_objects, RUN_SETUP },
|
||||
{ "diff", cmd_diff, RUN_SETUP },
|
||||
{ "diff", cmd_diff, RUN_SETUP | USE_PAGER },
|
||||
{ "diff-files", cmd_diff_files, RUN_SETUP },
|
||||
{ "diff-index", cmd_diff_index, RUN_SETUP },
|
||||
{ "diff-stages", cmd_diff_stages, RUN_SETUP },
|
||||
|
@ -258,7 +258,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
|
|||
{ "show", cmd_show, RUN_SETUP | USE_PAGER },
|
||||
{ "stripspace", cmd_stripspace },
|
||||
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
|
||||
{ "tar-tree", cmd_tar_tree, RUN_SETUP },
|
||||
{ "tar-tree", cmd_tar_tree },
|
||||
{ "unpack-objects", cmd_unpack_objects, RUN_SETUP },
|
||||
{ "update-index", cmd_update_index, RUN_SETUP },
|
||||
{ "update-ref", cmd_update_ref, RUN_SETUP },
|
||||
|
|
|
@ -97,7 +97,7 @@ find $RPM_BUILD_ROOT -type f -name '*.bs' -empty -exec rm -f {} ';'
|
|||
find $RPM_BUILD_ROOT -type f -name perllocal.pod -exec rm -f {} ';'
|
||||
|
||||
(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "arch|svn|cvs|email|gitk" | sed -e s@^$RPM_BUILD_ROOT@@) > bin-man-doc-files
|
||||
(find $RPM_BUILD_ROOT%{perl_vendorarch} -type f | sed -e s@^$RPM_BUILD_ROOT@@) >> perl-files
|
||||
(find $RPM_BUILD_ROOT%{perl_vendorlib} -type f | sed -e s@^$RPM_BUILD_ROOT@@) >> perl-files
|
||||
%if %{!?_without_docs:1}0
|
||||
(find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "arch|svn|git-cvs|email|gitk" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files
|
||||
%else
|
||||
|
|
|
@ -26,12 +26,26 @@ You can specify the following configuration variables when building GIT:
|
|||
* 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
|
||||
This file will be loaded using 'require' and can be used to override any
|
||||
of the options above as well as some other options - see the top of
|
||||
'gitweb.cgi' for their full list and description. 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.
|
||||
|
||||
|
||||
Runtime gitweb configuration
|
||||
----------------------------
|
||||
|
||||
You can adjust gitweb behaviour using the file specified in `GITWEB_CONFIG`
|
||||
(defaults to 'gitweb_config.perl' in the same directory as the CGI).
|
||||
See the top of 'gitweb.cgi' for the list of variables and some description.
|
||||
The most notable thing that is not configurable at compile time are the
|
||||
optional features, stored in the '%features' variable. You can find further
|
||||
description on how to reconfigure the default features setting in your
|
||||
`GITWEB_CONFIG` or per-project in `project.git/config` inside 'gitweb.cgi'.
|
||||
|
||||
|
||||
Webserver configuration
|
||||
-----------------------
|
||||
|
||||
|
@ -43,6 +57,7 @@ repositories, you can configure apache like this:
|
|||
DocumentRoot /pub/git
|
||||
RewriteEngine on
|
||||
RewriteRule ^/(.*\.git/(?!/?(info|objects|refs)).*)?$ /cgi-bin/gitweb.cgi%{REQUEST_URI} [L,PT]
|
||||
SetEnv GITWEB_CONFIG /etc/gitweb.conf
|
||||
</VirtualHost>
|
||||
|
||||
The above configuration expects your public repositories to live under
|
||||
|
@ -51,6 +66,12 @@ both as cloneable GIT URL and as browseable gitweb interface.
|
|||
If you then start your git-daemon with --base-path=/pub/git --export-all
|
||||
then you can even use the git:// URL with exactly the same path.
|
||||
|
||||
Setting the environment variable GITWEB_CONFIG will tell gitweb to use
|
||||
the named file (i.e. in this example /etc/gitweb.conf) as a
|
||||
configuration for gitweb. Perl variables defined in here will
|
||||
override the defaults given at the head of the gitweb.perl (or
|
||||
gitweb.cgi). Look at the comments in that file for information on
|
||||
which variables and what they mean.
|
||||
|
||||
|
||||
Originally written by:
|
||||
|
|
|
@ -16,6 +16,11 @@ a:hover, a:visited, a:active {
|
|||
color: #880000;
|
||||
}
|
||||
|
||||
img.logo {
|
||||
float: right;
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
div.page_header {
|
||||
height: 25px;
|
||||
padding: 8px;
|
||||
|
@ -173,6 +178,12 @@ table.blame {
|
|||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table.blame td {
|
||||
padding: 0px 5px;
|
||||
font-size: 12px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
th {
|
||||
padding: 2px 5px;
|
||||
font-size: 12px;
|
||||
|
|
|
@ -54,11 +54,19 @@ our $stylesheet;
|
|||
# default is not to define style sheet, but it can be overwritten later
|
||||
undef $stylesheet;
|
||||
|
||||
# URI of GIT logo
|
||||
# URI of default stylesheet
|
||||
our $stylesheet = "++GITWEB_CSS++";
|
||||
# URI of GIT logo (72x27 size)
|
||||
our $logo = "++GITWEB_LOGO++";
|
||||
# URI of GIT favicon, assumed to be image/png type
|
||||
our $favicon = "++GITWEB_FAVICON++";
|
||||
|
||||
# URI and label (title) of GIT logo link
|
||||
#our $logo_url = "http://www.kernel.org/pub/software/scm/git/docs/";
|
||||
#our $logo_label = "git documentation";
|
||||
our $logo_url = "http://git.or.cz/";
|
||||
our $logo_label = "git homepage";
|
||||
|
||||
# source of projects list
|
||||
our $projects_list = "++GITWEB_LIST++";
|
||||
|
||||
|
@ -95,21 +103,66 @@ our %feature = (
|
|||
#
|
||||
# use gitweb_check_feature(<feature>) to check if <feature> is enabled
|
||||
|
||||
# Enable the 'blame' blob view, showing the last commit that modified
|
||||
# each line in the file. This can be very CPU-intensive.
|
||||
|
||||
# To enable system wide have in $GITWEB_CONFIG
|
||||
# $feature{'blame'}{'default'} = [1];
|
||||
# To have project specific config enable override in $GITWEB_CONFIG
|
||||
# $feature{'blame'}{'override'} = 1;
|
||||
# and in project config gitweb.blame = 0|1;
|
||||
'blame' => {
|
||||
'sub' => \&feature_blame,
|
||||
'override' => 0,
|
||||
'default' => [0]},
|
||||
|
||||
# Enable the 'snapshot' link, providing a compressed tarball of any
|
||||
# tree. This can potentially generate high traffic if you have large
|
||||
# project.
|
||||
|
||||
# To disable system wide have in $GITWEB_CONFIG
|
||||
# $feature{'snapshot'}{'default'} = [undef];
|
||||
# To have project specific config enable override in $GITWEB_CONFIG
|
||||
# $feature{'blame'}{'override'} = 1;
|
||||
# and in project config gitweb.snapshot = none|gzip|bzip2;
|
||||
'snapshot' => {
|
||||
'sub' => \&feature_snapshot,
|
||||
'override' => 0,
|
||||
# => [content-encoding, suffix, program]
|
||||
'default' => ['x-gzip', 'gz', 'gzip']},
|
||||
|
||||
# Enable the pickaxe search, which will list the commits that modified
|
||||
# a given string in a file. This can be practical and quite faster
|
||||
# alternative to 'blame', but still potentially CPU-intensive.
|
||||
|
||||
# To enable system wide have in $GITWEB_CONFIG
|
||||
# $feature{'pickaxe'}{'default'} = [1];
|
||||
# To have project specific config enable override in $GITWEB_CONFIG
|
||||
# $feature{'pickaxe'}{'override'} = 1;
|
||||
# and in project config gitweb.pickaxe = 0|1;
|
||||
'pickaxe' => {
|
||||
'sub' => \&feature_pickaxe,
|
||||
'override' => 0,
|
||||
'default' => [1]},
|
||||
|
||||
# Make gitweb use an alternative format of the URLs which can be
|
||||
# more readable and natural-looking: project name is embedded
|
||||
# directly in the path and the query string contains other
|
||||
# auxiliary information. All gitweb installations recognize
|
||||
# URL in either format; this configures in which formats gitweb
|
||||
# generates links.
|
||||
|
||||
# To enable system wide have in $GITWEB_CONFIG
|
||||
# $feature{'pathinfo'}{'default'} = [1];
|
||||
# Project specific override is not supported.
|
||||
|
||||
# Note that you will need to change the default location of CSS,
|
||||
# favicon, logo and possibly other files to an absolute URL. Also,
|
||||
# if gitweb.cgi serves as your indexfile, you will need to force
|
||||
# $my_uri to contain the script name in your $GITWEB_CONFIG.
|
||||
'pathinfo' => {
|
||||
'override' => 0,
|
||||
'default' => [0]},
|
||||
);
|
||||
|
||||
sub gitweb_check_feature {
|
||||
|
@ -120,15 +173,13 @@ sub gitweb_check_feature {
|
|||
$feature{$name}{'override'},
|
||||
@{$feature{$name}{'default'}});
|
||||
if (!$override) { return @defaults; }
|
||||
if (!defined $sub) {
|
||||
warn "feature $name is not overrideable";
|
||||
return @defaults;
|
||||
}
|
||||
return $sub->(@defaults);
|
||||
}
|
||||
|
||||
# To enable system wide have in $GITWEB_CONFIG
|
||||
# $feature{'blame'}{'default'} = [1];
|
||||
# To have project specific config enable override in $GITWEB_CONFIG
|
||||
# $feature{'blame'}{'override'} = 1;
|
||||
# and in project config gitweb.blame = 0|1;
|
||||
|
||||
sub feature_blame {
|
||||
my ($val) = git_get_project_config('blame', '--bool');
|
||||
|
||||
|
@ -141,12 +192,6 @@ sub feature_blame {
|
|||
return $_[0];
|
||||
}
|
||||
|
||||
# To disable system wide have in $GITWEB_CONFIG
|
||||
# $feature{'snapshot'}{'default'} = [undef];
|
||||
# To have project specific config enable override in $GITWEB_CONFIG
|
||||
# $feature{'blame'}{'override'} = 1;
|
||||
# and in project config gitweb.snapshot = none|gzip|bzip2
|
||||
|
||||
sub feature_snapshot {
|
||||
my ($ctype, $suffix, $command) = @_;
|
||||
|
||||
|
@ -170,12 +215,6 @@ sub gitweb_have_snapshot {
|
|||
return $have_snapshot;
|
||||
}
|
||||
|
||||
# To enable system wide have in $GITWEB_CONFIG
|
||||
# $feature{'pickaxe'}{'default'} = [1];
|
||||
# To have project specific config enable override in $GITWEB_CONFIG
|
||||
# $feature{'pickaxe'}{'override'} = 1;
|
||||
# and in project config gitweb.pickaxe = 0|1;
|
||||
|
||||
sub feature_pickaxe {
|
||||
my ($val) = git_get_project_config('pickaxe', '--bool');
|
||||
|
||||
|
@ -399,6 +438,10 @@ exit;
|
|||
|
||||
sub href(%) {
|
||||
my %params = @_;
|
||||
my $href = $my_uri;
|
||||
|
||||
# XXX: Warning: If you touch this, check the search form for updating,
|
||||
# too.
|
||||
|
||||
my @mapping = (
|
||||
project => "p",
|
||||
|
@ -417,6 +460,19 @@ sub href(%) {
|
|||
|
||||
$params{'project'} = $project unless exists $params{'project'};
|
||||
|
||||
my ($use_pathinfo) = gitweb_check_feature('pathinfo');
|
||||
if ($use_pathinfo) {
|
||||
# use PATH_INFO for project name
|
||||
$href .= "/$params{'project'}" if defined $params{'project'};
|
||||
delete $params{'project'};
|
||||
|
||||
# Summary just uses the project path URL
|
||||
if (defined $params{'action'} && $params{'action'} eq 'summary') {
|
||||
delete $params{'action'};
|
||||
}
|
||||
}
|
||||
|
||||
# now encode the parameters explicitly
|
||||
my @result = ();
|
||||
for (my $i = 0; $i < @mapping; $i += 2) {
|
||||
my ($name, $symbol) = ($mapping[$i], $mapping[$i+1]);
|
||||
|
@ -424,7 +480,9 @@ sub href(%) {
|
|||
push @result, $symbol . "=" . esc_param($params{$name});
|
||||
}
|
||||
}
|
||||
return "$my_uri?" . join(';', @result);
|
||||
$href .= "?" . join(';', @result) if scalar @result;
|
||||
|
||||
return $href;
|
||||
}
|
||||
|
||||
|
||||
|
@ -464,6 +522,12 @@ sub validate_refname {
|
|||
return $input;
|
||||
}
|
||||
|
||||
# very thin wrapper for decode("utf8", $str, Encode::FB_DEFAULT);
|
||||
sub to_utf8 {
|
||||
my $str = shift;
|
||||
return decode("utf8", $str, Encode::FB_DEFAULT);
|
||||
}
|
||||
|
||||
# quote unsafe chars, but keep the slash, even when it's not
|
||||
# correct, but quoted slashes look too horrible in bookmarks
|
||||
sub esc_param {
|
||||
|
@ -486,7 +550,7 @@ sub esc_url {
|
|||
# replace invalid utf8 character with SUBSTITUTION sequence
|
||||
sub esc_html {
|
||||
my $str = shift;
|
||||
$str = decode("utf8", $str, Encode::FB_DEFAULT);
|
||||
$str = to_utf8($str);
|
||||
$str = escapeHTML($str);
|
||||
$str =~ s/\014/^L/g; # escape FORM FEED (FF) character (e.g. in COPYING file)
|
||||
$str =~ s/\033/^[/g; # "escape" ESCAPE (\e) character (e.g. commit 20a3847d8a5032ce41f90dcc68abfb36e6fee9b1)
|
||||
|
@ -689,7 +753,7 @@ sub format_subject_html {
|
|||
|
||||
if (length($short) < length($long)) {
|
||||
return $cgi->a({-href => $href, -class => "list subject",
|
||||
-title => decode("utf8", $long, Encode::FB_DEFAULT)},
|
||||
-title => to_utf8($long)},
|
||||
esc_html($short) . $extra);
|
||||
} else {
|
||||
return $cgi->a({-href => $href, -class => "list subject"},
|
||||
|
@ -864,7 +928,7 @@ sub git_get_projects_list {
|
|||
if (check_export_ok("$projectroot/$path")) {
|
||||
my $pr = {
|
||||
path => $path,
|
||||
owner => decode("utf8", $owner, Encode::FB_DEFAULT),
|
||||
owner => to_utf8($owner),
|
||||
};
|
||||
push @list, $pr
|
||||
}
|
||||
|
@ -893,7 +957,7 @@ sub git_get_project_owner {
|
|||
$pr = unescape($pr);
|
||||
$ow = unescape($ow);
|
||||
if ($pr eq $project) {
|
||||
$owner = decode("utf8", $ow, Encode::FB_DEFAULT);
|
||||
$owner = to_utf8($ow);
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
@ -1037,12 +1101,11 @@ sub parse_commit {
|
|||
if (defined $commit_text) {
|
||||
@commit_lines = @$commit_text;
|
||||
} else {
|
||||
$/ = "\0";
|
||||
local $/ = "\0";
|
||||
open my $fd, "-|", git_cmd(), "rev-list", "--header", "--parents", "--max-count=1", $commit_id
|
||||
or return;
|
||||
@commit_lines = split '\n', <$fd>;
|
||||
close $fd or return;
|
||||
$/ = "\n";
|
||||
pop @commit_lines;
|
||||
}
|
||||
my $header = shift @commit_lines;
|
||||
|
@ -1102,6 +1165,9 @@ sub parse_commit {
|
|||
last;
|
||||
}
|
||||
}
|
||||
if ($co{'title'} eq "") {
|
||||
$co{'title'} = $co{'title_short'} = '(no commit message)';
|
||||
}
|
||||
# remove added spaces
|
||||
foreach my $line (@commit_lines) {
|
||||
$line =~ s/^ //;
|
||||
|
@ -1273,7 +1339,7 @@ sub get_file_owner {
|
|||
}
|
||||
my $owner = $gcos;
|
||||
$owner =~ s/[,;].*$//;
|
||||
return decode("utf8", $owner, Encode::FB_DEFAULT);
|
||||
return to_utf8($owner);
|
||||
}
|
||||
|
||||
## ......................................................................
|
||||
|
@ -1429,9 +1495,9 @@ EOF
|
|||
}
|
||||
|
||||
print "<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";
|
||||
$cgi->a({-href => esc_url($logo_url),
|
||||
-title => $logo_label},
|
||||
qq(<img src="$logo" width="72" height="27" alt="git" class="logo"/>));
|
||||
print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / ";
|
||||
if (defined $project) {
|
||||
print $cgi->a({-href => href(action=>"summary")}, esc_html($project));
|
||||
|
@ -1452,6 +1518,7 @@ EOF
|
|||
}
|
||||
$cgi->param("a", "search");
|
||||
$cgi->param("h", $search_hash);
|
||||
$cgi->param("p", $project);
|
||||
print $cgi->startform(-method => "get", -action => $my_uri) .
|
||||
"<div class=\"search\">\n" .
|
||||
$cgi->hidden(-name => "p") . "\n" .
|
||||
|
@ -1612,17 +1679,16 @@ sub git_print_page_path {
|
|||
my $type = shift;
|
||||
my $hb = shift;
|
||||
|
||||
if (!defined $name) {
|
||||
print "<div class=\"page_path\">/</div>\n";
|
||||
} else {
|
||||
|
||||
print "<div class=\"page_path\">";
|
||||
print $cgi->a({-href => href(action=>"tree", hash_base=>$hb),
|
||||
-title => 'tree root'}, "[$project]");
|
||||
print " / ";
|
||||
if (defined $name) {
|
||||
my @dirname = split '/', $name;
|
||||
my $basename = pop @dirname;
|
||||
my $fullname = '';
|
||||
|
||||
print "<div class=\"page_path\">";
|
||||
print $cgi->a({-href => href(action=>"tree", hash_base=>$hb),
|
||||
-title => 'tree root'}, "[$project]");
|
||||
print " / ";
|
||||
foreach my $dir (@dirname) {
|
||||
$fullname .= ($fullname ? '/' : '') . $dir;
|
||||
print $cgi->a({-href => href(action=>"tree", file_name=>$fullname,
|
||||
|
@ -1638,11 +1704,12 @@ sub git_print_page_path {
|
|||
print $cgi->a({-href => href(action=>"tree", file_name=>$file_name,
|
||||
hash_base=>$hb),
|
||||
-title => $name}, esc_html($basename));
|
||||
print " / ";
|
||||
} else {
|
||||
print esc_html($basename);
|
||||
}
|
||||
print "<br/></div>\n";
|
||||
}
|
||||
print "<br/></div>\n";
|
||||
}
|
||||
|
||||
# sub git_print_log (\@;%) {
|
||||
|
@ -1719,13 +1786,13 @@ sub git_print_tree_entry {
|
|||
if ($t->{'type'} eq "blob") {
|
||||
print "<td class=\"list\">" .
|
||||
$cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
|
||||
file_name=>"$basedir$t->{'name'}", %base_key),
|
||||
-class => "list"}, esc_html($t->{'name'})) . "</td>\n";
|
||||
file_name=>"$basedir$t->{'name'}", %base_key),
|
||||
-class => "list"}, esc_html($t->{'name'})) . "</td>\n";
|
||||
print "<td class=\"link\">";
|
||||
if ($have_blame) {
|
||||
print $cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'},
|
||||
file_name=>"$basedir$t->{'name'}", %base_key)},
|
||||
"blame");
|
||||
file_name=>"$basedir$t->{'name'}", %base_key)},
|
||||
"blame");
|
||||
}
|
||||
if (defined $hash_base) {
|
||||
if ($have_blame) {
|
||||
|
@ -1737,8 +1804,8 @@ sub git_print_tree_entry {
|
|||
}
|
||||
print " | " .
|
||||
$cgi->a({-href => href(action=>"blob_plain", hash_base=>$hash_base,
|
||||
file_name=>"$basedir$t->{'name'}")},
|
||||
"raw");
|
||||
file_name=>"$basedir$t->{'name'}")},
|
||||
"raw");
|
||||
print "</td>\n";
|
||||
|
||||
} elsif ($t->{'type'} eq "tree") {
|
||||
|
@ -1806,7 +1873,7 @@ sub git_difftree_body {
|
|||
print "<td>";
|
||||
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
|
||||
hash_base=>$hash, file_name=>$diff{'file'}),
|
||||
-class => "list"}, esc_html($diff{'file'}));
|
||||
-class => "list"}, esc_html($diff{'file'}));
|
||||
print "</td>\n";
|
||||
print "<td>$mode_chng</td>\n";
|
||||
print "<td class=\"link\">";
|
||||
|
@ -1833,11 +1900,11 @@ sub git_difftree_body {
|
|||
print " | ";
|
||||
}
|
||||
print $cgi->a({-href => href(action=>"blame", hash_base=>$parent,
|
||||
file_name=>$diff{'file'})},
|
||||
"blame") . " | ";
|
||||
file_name=>$diff{'file'})},
|
||||
"blame") . " | ";
|
||||
print $cgi->a({-href => href(action=>"history", hash_base=>$parent,
|
||||
file_name=>$diff{'file'})},
|
||||
"history");
|
||||
file_name=>$diff{'file'})},
|
||||
"history");
|
||||
print "</td>\n";
|
||||
|
||||
} elsif ($diff{'status'} eq "M" || $diff{'status'} eq "T") { # modified, or type changed
|
||||
|
@ -1858,8 +1925,8 @@ sub git_difftree_body {
|
|||
}
|
||||
print "<td>";
|
||||
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
|
||||
hash_base=>$hash, file_name=>$diff{'file'}),
|
||||
-class => "list"}, esc_html($diff{'file'}));
|
||||
hash_base=>$hash, file_name=>$diff{'file'}),
|
||||
-class => "list"}, esc_html($diff{'file'}));
|
||||
print "</td>\n";
|
||||
print "<td>$mode_chnge</td>\n";
|
||||
print "<td class=\"link\">";
|
||||
|
@ -1870,19 +1937,19 @@ sub git_difftree_body {
|
|||
print $cgi->a({-href => "#patch$patchno"}, "patch");
|
||||
} else {
|
||||
print $cgi->a({-href => href(action=>"blobdiff",
|
||||
hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
|
||||
hash_base=>$hash, hash_parent_base=>$parent,
|
||||
file_name=>$diff{'file'})},
|
||||
"diff");
|
||||
hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
|
||||
hash_base=>$hash, hash_parent_base=>$parent,
|
||||
file_name=>$diff{'file'})},
|
||||
"diff");
|
||||
}
|
||||
print " | ";
|
||||
}
|
||||
print $cgi->a({-href => href(action=>"blame", hash_base=>$hash,
|
||||
file_name=>$diff{'file'})},
|
||||
"blame") . " | ";
|
||||
file_name=>$diff{'file'})},
|
||||
"blame") . " | ";
|
||||
print $cgi->a({-href => href(action=>"history", hash_base=>$hash,
|
||||
file_name=>$diff{'file'})},
|
||||
"history");
|
||||
file_name=>$diff{'file'})},
|
||||
"history");
|
||||
print "</td>\n";
|
||||
|
||||
} elsif ($diff{'status'} eq "R" || $diff{'status'} eq "C") { # renamed or copied
|
||||
|
@ -1910,19 +1977,19 @@ sub git_difftree_body {
|
|||
print $cgi->a({-href => "#patch$patchno"}, "patch");
|
||||
} else {
|
||||
print $cgi->a({-href => href(action=>"blobdiff",
|
||||
hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
|
||||
hash_base=>$hash, hash_parent_base=>$parent,
|
||||
file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})},
|
||||
"diff");
|
||||
hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
|
||||
hash_base=>$hash, hash_parent_base=>$parent,
|
||||
file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})},
|
||||
"diff");
|
||||
}
|
||||
print " | ";
|
||||
}
|
||||
print $cgi->a({-href => href(action=>"blame", hash_base=>$parent,
|
||||
file_name=>$diff{'from_file'})},
|
||||
"blame") . " | ";
|
||||
file_name=>$diff{'from_file'})},
|
||||
"blame") . " | ";
|
||||
print $cgi->a({-href => href(action=>"history", hash_base=>$parent,
|
||||
file_name=>$diff{'from_file'})},
|
||||
"history");
|
||||
file_name=>$diff{'from_file'})},
|
||||
"history");
|
||||
print "</td>\n";
|
||||
|
||||
} # we should not encounter Unmerged (U) or Unknown (X) status
|
||||
|
@ -1974,14 +2041,14 @@ sub git_patchset_body {
|
|||
print "<div class=\"diff_info\">" . file_type($diffinfo->{'to_mode'}) . ":" .
|
||||
$cgi->a({-href => href(action=>"blob", hash_base=>$hash,
|
||||
hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'file'})},
|
||||
$diffinfo->{'to_id'}) . "(new)" .
|
||||
$diffinfo->{'to_id'}) . " (new)" .
|
||||
"</div>\n"; # class="diff_info"
|
||||
|
||||
} elsif ($diffinfo->{'status'} eq "D") { # deleted
|
||||
print "<div class=\"diff_info\">" . file_type($diffinfo->{'from_mode'}) . ":" .
|
||||
$cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent,
|
||||
hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'file'})},
|
||||
$diffinfo->{'from_id'}) . "(deleted)" .
|
||||
$diffinfo->{'from_id'}) . " (deleted)" .
|
||||
"</div>\n"; # class="diff_info"
|
||||
|
||||
} elsif ($diffinfo->{'status'} eq "R" || # renamed
|
||||
|
@ -2085,8 +2152,10 @@ sub git_shortlog_body {
|
|||
print "</td>\n" .
|
||||
"<td class=\"link\">" .
|
||||
$cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " .
|
||||
$cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree") . " | " .
|
||||
$cgi->a({-href => href(action=>"snapshot", hash=>$commit)}, "snapshot");
|
||||
$cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree");
|
||||
if (gitweb_have_snapshot()) {
|
||||
print " | " . $cgi->a({-href => href(action=>"snapshot", hash=>$commit)}, "snapshot");
|
||||
}
|
||||
print "</td>\n" .
|
||||
"</tr>\n";
|
||||
}
|
||||
|
@ -2832,7 +2901,7 @@ sub git_tree {
|
|||
my $refs = git_get_references();
|
||||
my $ref = format_ref_marker($refs, $hash_base);
|
||||
git_header_html();
|
||||
my $base = "";
|
||||
my $basedir = '';
|
||||
my ($have_blame) = gitweb_check_feature('blame');
|
||||
if (defined $hash_base && (my %co = parse_commit($hash_base))) {
|
||||
my @views_nav = ();
|
||||
|
@ -2849,7 +2918,7 @@ sub git_tree {
|
|||
# FIXME: Should be available when we have no hash base as well.
|
||||
push @views_nav,
|
||||
$cgi->a({-href => href(action=>"snapshot", hash=>$hash)},
|
||||
"snapshot");
|
||||
"snapshot");
|
||||
}
|
||||
git_print_page_nav('tree','', $hash_base, undef, undef, join(' | ', @views_nav));
|
||||
git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base);
|
||||
|
@ -2860,12 +2929,39 @@ sub git_tree {
|
|||
print "<div class=\"title\">$hash</div>\n";
|
||||
}
|
||||
if (defined $file_name) {
|
||||
$base = esc_html("$file_name/");
|
||||
$basedir = $file_name;
|
||||
if ($basedir ne '' && substr($basedir, -1) ne '/') {
|
||||
$basedir .= '/';
|
||||
}
|
||||
}
|
||||
git_print_page_path($file_name, 'tree', $hash_base);
|
||||
print "<div class=\"page_body\">\n";
|
||||
print "<table cellspacing=\"0\">\n";
|
||||
my $alternate = 1;
|
||||
# '..' (top directory) link if possible
|
||||
if (defined $hash_base &&
|
||||
defined $file_name && $file_name =~ m![^/]+$!) {
|
||||
if ($alternate) {
|
||||
print "<tr class=\"dark\">\n";
|
||||
} else {
|
||||
print "<tr class=\"light\">\n";
|
||||
}
|
||||
$alternate ^= 1;
|
||||
|
||||
my $up = $file_name;
|
||||
$up =~ s!/?[^/]+$!!;
|
||||
undef $up unless $up;
|
||||
# based on git_print_tree_entry
|
||||
print '<td class="mode">' . mode_str('040000') . "</td>\n";
|
||||
print '<td class="list">';
|
||||
print $cgi->a({-href => href(action=>"tree", hash_base=>$hash_base,
|
||||
file_name=>$up)},
|
||||
"..");
|
||||
print "</td>\n";
|
||||
print "<td class=\"link\"></td>\n";
|
||||
|
||||
print "</tr>\n";
|
||||
}
|
||||
foreach my $line (@entries) {
|
||||
my %t = parse_ls_tree_line($line, -z => 1);
|
||||
|
||||
|
@ -2876,7 +2972,7 @@ sub git_tree {
|
|||
}
|
||||
$alternate ^= 1;
|
||||
|
||||
git_print_tree_entry(\%t, $base, $hash_base, $have_blame);
|
||||
git_print_tree_entry(\%t, $basedir, $hash_base, $have_blame);
|
||||
|
||||
print "</tr>\n";
|
||||
}
|
||||
|
@ -2904,9 +3000,12 @@ sub git_snapshot {
|
|||
-content_disposition => 'inline; filename="' . "$filename" . '"',
|
||||
-status => '200 OK');
|
||||
|
||||
my $git_command = git_cmd_str();
|
||||
open my $fd, "-|", "$git_command tar-tree $hash \'$project\' | $command" or
|
||||
die_error(undef, "Execute git-tar-tree failed.");
|
||||
my $git = git_cmd_str();
|
||||
my $name = $project;
|
||||
$name =~ s/\047/\047\\\047\047/g;
|
||||
open my $fd, "-|",
|
||||
"$git archive --format=tar --prefix=\'$name\'/ $hash | $command"
|
||||
or die_error(undef, "Execute git-tar-tree failed.");
|
||||
binmode STDOUT, ':raw';
|
||||
print <$fd>;
|
||||
binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
|
||||
|
@ -3003,13 +3102,8 @@ sub git_commit {
|
|||
$cgi->a({-href => href(action=>"blame", hash_parent=>$parent, file_name=>$file_name)},
|
||||
"blame");
|
||||
}
|
||||
if (defined $co{'parent'}) {
|
||||
push @views_nav,
|
||||
$cgi->a({-href => href(action=>"shortlog", hash=>$hash)}, "shortlog"),
|
||||
$cgi->a({-href => href(action=>"log", hash=>$hash)}, "log");
|
||||
}
|
||||
git_header_html(undef, $expires);
|
||||
git_print_page_nav('commit', defined $co{'parent'} ? '' : 'commitdiff',
|
||||
git_print_page_nav('commit', '',
|
||||
$hash, $co{'tree'}, $hash,
|
||||
join (' | ', @views_nav));
|
||||
|
||||
|
@ -3652,7 +3746,7 @@ XML
|
|||
"<![CDATA[\n";
|
||||
my $comment = $co{'comment'};
|
||||
foreach my $line (@$comment) {
|
||||
$line = decode("utf8", $line, Encode::FB_DEFAULT);
|
||||
$line = to_utf8($line);
|
||||
print "$line<br/>\n";
|
||||
}
|
||||
print "<br/>\n";
|
||||
|
@ -3661,7 +3755,7 @@ XML
|
|||
next;
|
||||
}
|
||||
my $file = esc_html(unquote($7));
|
||||
$file = decode("utf8", $file, Encode::FB_DEFAULT);
|
||||
$file = to_utf8($file);
|
||||
print "$file<br/>\n";
|
||||
}
|
||||
print "]]>\n" .
|
||||
|
|
114
grep.c
114
grep.c
|
@ -34,7 +34,7 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
|
|||
}
|
||||
}
|
||||
|
||||
static struct grep_expr *compile_pattern_expr(struct grep_pat **);
|
||||
static struct grep_expr *compile_pattern_or(struct grep_pat **);
|
||||
static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
|
||||
{
|
||||
struct grep_pat *p;
|
||||
|
@ -52,7 +52,7 @@ static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
|
|||
return x;
|
||||
case GREP_OPEN_PAREN:
|
||||
*list = p->next;
|
||||
x = compile_pattern_expr(list);
|
||||
x = compile_pattern_or(list);
|
||||
if (!x)
|
||||
return NULL;
|
||||
if (!*list || (*list)->token != GREP_CLOSE_PAREN)
|
||||
|
@ -138,6 +138,9 @@ void compile_grep_patterns(struct grep_opt *opt)
|
|||
{
|
||||
struct grep_pat *p;
|
||||
|
||||
if (opt->all_match)
|
||||
opt->extended = 1;
|
||||
|
||||
for (p = opt->pattern_list; p; p = p->next) {
|
||||
switch (p->token) {
|
||||
case GREP_PATTERN: /* atom */
|
||||
|
@ -309,40 +312,63 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol
|
|||
return hit;
|
||||
}
|
||||
|
||||
static int match_expr_eval(struct grep_opt *opt,
|
||||
static int match_expr_eval(struct grep_opt *o,
|
||||
struct grep_expr *x,
|
||||
char *bol, char *eol,
|
||||
enum grep_context ctx)
|
||||
enum grep_context ctx,
|
||||
int collect_hits)
|
||||
{
|
||||
int h = 0;
|
||||
|
||||
switch (x->node) {
|
||||
case GREP_NODE_ATOM:
|
||||
return match_one_pattern(opt, x->u.atom, bol, eol, ctx);
|
||||
h = match_one_pattern(o, x->u.atom, bol, eol, ctx);
|
||||
break;
|
||||
case GREP_NODE_NOT:
|
||||
return !match_expr_eval(opt, x->u.unary, bol, eol, ctx);
|
||||
h = !match_expr_eval(o, x->u.unary, bol, eol, ctx, 0);
|
||||
break;
|
||||
case GREP_NODE_AND:
|
||||
return (match_expr_eval(opt, x->u.binary.left, bol, eol, ctx) &&
|
||||
match_expr_eval(opt, x->u.binary.right, bol, eol, ctx));
|
||||
if (!collect_hits)
|
||||
return (match_expr_eval(o, x->u.binary.left,
|
||||
bol, eol, ctx, 0) &&
|
||||
match_expr_eval(o, x->u.binary.right,
|
||||
bol, eol, ctx, 0));
|
||||
h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);
|
||||
h &= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0);
|
||||
break;
|
||||
case GREP_NODE_OR:
|
||||
return (match_expr_eval(opt, x->u.binary.left, bol, eol, ctx) ||
|
||||
match_expr_eval(opt, x->u.binary.right, bol, eol, ctx));
|
||||
if (!collect_hits)
|
||||
return (match_expr_eval(o, x->u.binary.left,
|
||||
bol, eol, ctx, 0) ||
|
||||
match_expr_eval(o, x->u.binary.right,
|
||||
bol, eol, ctx, 0));
|
||||
h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);
|
||||
x->u.binary.left->hit |= h;
|
||||
h |= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 1);
|
||||
break;
|
||||
default:
|
||||
die("Unexpected node type (internal error) %d\n", x->node);
|
||||
}
|
||||
die("Unexpected node type (internal error) %d\n", x->node);
|
||||
if (collect_hits)
|
||||
x->hit |= h;
|
||||
return h;
|
||||
}
|
||||
|
||||
static int match_expr(struct grep_opt *opt, char *bol, char *eol,
|
||||
enum grep_context ctx)
|
||||
enum grep_context ctx, int collect_hits)
|
||||
{
|
||||
struct grep_expr *x = opt->pattern_expression;
|
||||
return match_expr_eval(opt, x, bol, eol, ctx);
|
||||
return match_expr_eval(opt, x, bol, eol, ctx, collect_hits);
|
||||
}
|
||||
|
||||
static int match_line(struct grep_opt *opt, char *bol, char *eol,
|
||||
enum grep_context ctx)
|
||||
enum grep_context ctx, int collect_hits)
|
||||
{
|
||||
struct grep_pat *p;
|
||||
if (opt->extended)
|
||||
return match_expr(opt, bol, eol, ctx);
|
||||
return match_expr(opt, bol, eol, ctx, collect_hits);
|
||||
|
||||
/* we do not call with collect_hits without being extended */
|
||||
for (p = opt->pattern_list; p; p = p->next) {
|
||||
if (match_one_pattern(opt, p, bol, eol, ctx))
|
||||
return 1;
|
||||
|
@ -350,7 +376,8 @@ static int match_line(struct grep_opt *opt, char *bol, char *eol,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size)
|
||||
static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
||||
char *buf, unsigned long size, int collect_hits)
|
||||
{
|
||||
char *bol = buf;
|
||||
unsigned long left = size;
|
||||
|
@ -386,7 +413,7 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long
|
|||
|
||||
while (left) {
|
||||
char *eol, ch;
|
||||
int hit = 0;
|
||||
int hit;
|
||||
|
||||
eol = end_of_line(bol, &left);
|
||||
ch = *eol;
|
||||
|
@ -395,9 +422,12 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long
|
|||
if ((ctx == GREP_CONTEXT_HEAD) && (eol == bol))
|
||||
ctx = GREP_CONTEXT_BODY;
|
||||
|
||||
hit = match_line(opt, bol, eol, ctx);
|
||||
hit = match_line(opt, bol, eol, ctx, collect_hits);
|
||||
*eol = ch;
|
||||
|
||||
if (collect_hits)
|
||||
goto next_line;
|
||||
|
||||
/* "grep -v -e foo -e bla" should list lines
|
||||
* that do not have either, so inversion should
|
||||
* be done outside.
|
||||
|
@ -477,6 +507,8 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long
|
|||
}
|
||||
|
||||
free(prev);
|
||||
if (collect_hits)
|
||||
return 0;
|
||||
|
||||
if (opt->status_only)
|
||||
return 0;
|
||||
|
@ -496,3 +528,49 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long
|
|||
return !!last_hit;
|
||||
}
|
||||
|
||||
static void clr_hit_marker(struct grep_expr *x)
|
||||
{
|
||||
/* All-hit markers are meaningful only at the very top level
|
||||
* OR node.
|
||||
*/
|
||||
while (1) {
|
||||
x->hit = 0;
|
||||
if (x->node != GREP_NODE_OR)
|
||||
return;
|
||||
x->u.binary.left->hit = 0;
|
||||
x = x->u.binary.right;
|
||||
}
|
||||
}
|
||||
|
||||
static int chk_hit_marker(struct grep_expr *x)
|
||||
{
|
||||
/* Top level nodes have hit markers. See if they all are hits */
|
||||
while (1) {
|
||||
if (x->node != GREP_NODE_OR)
|
||||
return x->hit;
|
||||
if (!x->u.binary.left->hit)
|
||||
return 0;
|
||||
x = x->u.binary.right;
|
||||
}
|
||||
}
|
||||
|
||||
int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size)
|
||||
{
|
||||
/*
|
||||
* we do not have to do the two-pass grep when we do not check
|
||||
* buffer-wide "all-match".
|
||||
*/
|
||||
if (!opt->all_match)
|
||||
return grep_buffer_1(opt, name, buf, size, 0);
|
||||
|
||||
/* Otherwise the toplevel "or" terms hit a bit differently.
|
||||
* We first clear hit markers from them.
|
||||
*/
|
||||
clr_hit_marker(opt->pattern_expression);
|
||||
grep_buffer_1(opt, name, buf, size, 1);
|
||||
|
||||
if (!chk_hit_marker(opt->pattern_expression))
|
||||
return 0;
|
||||
|
||||
return grep_buffer_1(opt, name, buf, size, 0);
|
||||
}
|
||||
|
|
2
grep.h
2
grep.h
|
@ -35,6 +35,7 @@ enum grep_expr_node {
|
|||
|
||||
struct grep_expr {
|
||||
enum grep_expr_node node;
|
||||
unsigned hit;
|
||||
union {
|
||||
struct grep_pat *atom;
|
||||
struct grep_expr *unary;
|
||||
|
@ -59,6 +60,7 @@ struct grep_opt {
|
|||
unsigned count:1;
|
||||
unsigned word_regexp:1;
|
||||
unsigned fixed:1;
|
||||
unsigned all_match:1;
|
||||
#define GREP_BINARY_DEFAULT 0
|
||||
#define GREP_BINARY_NOMATCH 1
|
||||
#define GREP_BINARY_TEXT 2
|
||||
|
|
257
http-fetch.c
257
http-fetch.c
|
@ -4,35 +4,6 @@
|
|||
#include "fetch.h"
|
||||
#include "http.h"
|
||||
|
||||
#ifndef NO_EXPAT
|
||||
#include <expat.h>
|
||||
|
||||
/* Definitions for DAV requests */
|
||||
#define DAV_PROPFIND "PROPFIND"
|
||||
#define DAV_PROPFIND_RESP ".multistatus.response"
|
||||
#define DAV_PROPFIND_NAME ".multistatus.response.href"
|
||||
#define DAV_PROPFIND_COLLECTION ".multistatus.response.propstat.prop.resourcetype.collection"
|
||||
#define PROPFIND_ALL_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/>\n</D:propfind>"
|
||||
|
||||
/* Definitions for processing XML DAV responses */
|
||||
#ifndef XML_STATUS_OK
|
||||
enum XML_Status {
|
||||
XML_STATUS_OK = 1,
|
||||
XML_STATUS_ERROR = 0
|
||||
};
|
||||
#define XML_STATUS_OK 1
|
||||
#define XML_STATUS_ERROR 0
|
||||
#endif
|
||||
|
||||
/* Flags that control remote_ls processing */
|
||||
#define PROCESS_FILES (1u << 0)
|
||||
#define PROCESS_DIRS (1u << 1)
|
||||
#define RECURSIVE (1u << 2)
|
||||
|
||||
/* Flags that remote_ls passes to callback functions */
|
||||
#define IS_DIR (1u << 0)
|
||||
#endif
|
||||
|
||||
#define PREV_BUF_SIZE 4096
|
||||
#define RANGE_HEADER_SIZE 30
|
||||
|
||||
|
@ -90,30 +61,6 @@ struct alternates_request {
|
|||
int http_specific;
|
||||
};
|
||||
|
||||
#ifndef NO_EXPAT
|
||||
struct xml_ctx
|
||||
{
|
||||
char *name;
|
||||
int len;
|
||||
char *cdata;
|
||||
void (*userFunc)(struct xml_ctx *ctx, int tag_closed);
|
||||
void *userData;
|
||||
};
|
||||
|
||||
struct remote_ls_ctx
|
||||
{
|
||||
struct alt_base *repo;
|
||||
char *path;
|
||||
void (*userFunc)(struct remote_ls_ctx *ls);
|
||||
void *userData;
|
||||
int flags;
|
||||
char *dentry_name;
|
||||
int dentry_flags;
|
||||
int rc;
|
||||
struct remote_ls_ctx *parent;
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct object_request *object_queue_head;
|
||||
|
||||
static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
|
||||
|
@ -714,204 +661,6 @@ static void fetch_alternates(const char *base)
|
|||
free(url);
|
||||
}
|
||||
|
||||
#ifndef NO_EXPAT
|
||||
static void
|
||||
xml_start_tag(void *userData, const char *name, const char **atts)
|
||||
{
|
||||
struct xml_ctx *ctx = (struct xml_ctx *)userData;
|
||||
const char *c = strchr(name, ':');
|
||||
int new_len;
|
||||
|
||||
if (c == NULL)
|
||||
c = name;
|
||||
else
|
||||
c++;
|
||||
|
||||
new_len = strlen(ctx->name) + strlen(c) + 2;
|
||||
|
||||
if (new_len > ctx->len) {
|
||||
ctx->name = xrealloc(ctx->name, new_len);
|
||||
ctx->len = new_len;
|
||||
}
|
||||
strcat(ctx->name, ".");
|
||||
strcat(ctx->name, c);
|
||||
|
||||
free(ctx->cdata);
|
||||
ctx->cdata = NULL;
|
||||
|
||||
ctx->userFunc(ctx, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
xml_end_tag(void *userData, const char *name)
|
||||
{
|
||||
struct xml_ctx *ctx = (struct xml_ctx *)userData;
|
||||
const char *c = strchr(name, ':');
|
||||
char *ep;
|
||||
|
||||
ctx->userFunc(ctx, 1);
|
||||
|
||||
if (c == NULL)
|
||||
c = name;
|
||||
else
|
||||
c++;
|
||||
|
||||
ep = ctx->name + strlen(ctx->name) - strlen(c) - 1;
|
||||
*ep = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
xml_cdata(void *userData, const XML_Char *s, int len)
|
||||
{
|
||||
struct xml_ctx *ctx = (struct xml_ctx *)userData;
|
||||
free(ctx->cdata);
|
||||
ctx->cdata = xmalloc(len + 1);
|
||||
strlcpy(ctx->cdata, s, len + 1);
|
||||
}
|
||||
|
||||
static int remote_ls(struct alt_base *repo, const char *path, int flags,
|
||||
void (*userFunc)(struct remote_ls_ctx *ls),
|
||||
void *userData);
|
||||
|
||||
static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
|
||||
{
|
||||
struct remote_ls_ctx *ls = (struct remote_ls_ctx *)ctx->userData;
|
||||
|
||||
if (tag_closed) {
|
||||
if (!strcmp(ctx->name, DAV_PROPFIND_RESP) && ls->dentry_name) {
|
||||
if (ls->dentry_flags & IS_DIR) {
|
||||
if (ls->flags & PROCESS_DIRS) {
|
||||
ls->userFunc(ls);
|
||||
}
|
||||
if (strcmp(ls->dentry_name, ls->path) &&
|
||||
ls->flags & RECURSIVE) {
|
||||
ls->rc = remote_ls(ls->repo,
|
||||
ls->dentry_name,
|
||||
ls->flags,
|
||||
ls->userFunc,
|
||||
ls->userData);
|
||||
}
|
||||
} else if (ls->flags & PROCESS_FILES) {
|
||||
ls->userFunc(ls);
|
||||
}
|
||||
} else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) {
|
||||
ls->dentry_name = xmalloc(strlen(ctx->cdata) -
|
||||
ls->repo->path_len + 1);
|
||||
strcpy(ls->dentry_name, ctx->cdata + ls->repo->path_len);
|
||||
} else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {
|
||||
ls->dentry_flags |= IS_DIR;
|
||||
}
|
||||
} else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) {
|
||||
free(ls->dentry_name);
|
||||
ls->dentry_name = NULL;
|
||||
ls->dentry_flags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int remote_ls(struct alt_base *repo, const char *path, int flags,
|
||||
void (*userFunc)(struct remote_ls_ctx *ls),
|
||||
void *userData)
|
||||
{
|
||||
char *url = xmalloc(strlen(repo->base) + strlen(path) + 1);
|
||||
struct active_request_slot *slot;
|
||||
struct slot_results results;
|
||||
struct buffer in_buffer;
|
||||
struct buffer out_buffer;
|
||||
char *in_data;
|
||||
char *out_data;
|
||||
XML_Parser parser = XML_ParserCreate(NULL);
|
||||
enum XML_Status result;
|
||||
struct curl_slist *dav_headers = NULL;
|
||||
struct xml_ctx ctx;
|
||||
struct remote_ls_ctx ls;
|
||||
|
||||
ls.flags = flags;
|
||||
ls.repo = repo;
|
||||
ls.path = xstrdup(path);
|
||||
ls.dentry_name = NULL;
|
||||
ls.dentry_flags = 0;
|
||||
ls.userData = userData;
|
||||
ls.userFunc = userFunc;
|
||||
ls.rc = 0;
|
||||
|
||||
sprintf(url, "%s%s", repo->base, path);
|
||||
|
||||
out_buffer.size = strlen(PROPFIND_ALL_REQUEST);
|
||||
out_data = xmalloc(out_buffer.size + 1);
|
||||
snprintf(out_data, out_buffer.size + 1, PROPFIND_ALL_REQUEST);
|
||||
out_buffer.posn = 0;
|
||||
out_buffer.buffer = out_data;
|
||||
|
||||
in_buffer.size = 4096;
|
||||
in_data = xmalloc(in_buffer.size);
|
||||
in_buffer.posn = 0;
|
||||
in_buffer.buffer = in_data;
|
||||
|
||||
dav_headers = curl_slist_append(dav_headers, "Depth: 1");
|
||||
dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
|
||||
|
||||
slot = get_active_slot();
|
||||
slot->results = &results;
|
||||
curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
|
||||
|
||||
if (start_active_slot(slot)) {
|
||||
run_active_slot(slot);
|
||||
if (results.curl_result == CURLE_OK) {
|
||||
ctx.name = xcalloc(10, 1);
|
||||
ctx.len = 0;
|
||||
ctx.cdata = NULL;
|
||||
ctx.userFunc = handle_remote_ls_ctx;
|
||||
ctx.userData = &ls;
|
||||
XML_SetUserData(parser, &ctx);
|
||||
XML_SetElementHandler(parser, xml_start_tag,
|
||||
xml_end_tag);
|
||||
XML_SetCharacterDataHandler(parser, xml_cdata);
|
||||
result = XML_Parse(parser, in_buffer.buffer,
|
||||
in_buffer.posn, 1);
|
||||
free(ctx.name);
|
||||
|
||||
if (result != XML_STATUS_OK) {
|
||||
ls.rc = error("XML error: %s",
|
||||
XML_ErrorString(
|
||||
XML_GetErrorCode(parser)));
|
||||
}
|
||||
} else {
|
||||
ls.rc = -1;
|
||||
}
|
||||
} else {
|
||||
ls.rc = error("Unable to start PROPFIND request");
|
||||
}
|
||||
|
||||
free(ls.path);
|
||||
free(url);
|
||||
free(out_data);
|
||||
free(in_buffer.buffer);
|
||||
curl_slist_free_all(dav_headers);
|
||||
|
||||
return ls.rc;
|
||||
}
|
||||
|
||||
static void process_ls_pack(struct remote_ls_ctx *ls)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
|
||||
if (strlen(ls->dentry_name) == 63 &&
|
||||
!strncmp(ls->dentry_name, "objects/pack/pack-", 18) &&
|
||||
has_extension(ls->dentry_name, ".pack")) {
|
||||
get_sha1_hex(ls->dentry_name + 18, sha1);
|
||||
setup_index(ls->repo, sha1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int fetch_indices(struct alt_base *repo)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
|
@ -934,12 +683,6 @@ static int fetch_indices(struct alt_base *repo)
|
|||
if (get_verbosely)
|
||||
fprintf(stderr, "Getting pack list for %s\n", repo->base);
|
||||
|
||||
#ifndef NO_EXPAT
|
||||
if (remote_ls(repo, "objects/pack/", PROCESS_FILES,
|
||||
process_ls_pack, NULL) == 0)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
url = xmalloc(strlen(repo->base) + 21);
|
||||
sprintf(url, "%s/objects/info/packs", repo->base);
|
||||
|
||||
|
|
|
@ -1226,6 +1226,14 @@ split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs )
|
|||
if (msg->len < 5 || strncmp( data, "From ", 5 ))
|
||||
return 0;
|
||||
|
||||
p = strchr( data, '\n' );
|
||||
if (p) {
|
||||
p = &p[1];
|
||||
msg->len -= p-data;
|
||||
*ofs += p-data;
|
||||
data = p;
|
||||
}
|
||||
|
||||
p = strstr( data, "\nFrom " );
|
||||
if (p)
|
||||
msg->len = &p[1] - data;
|
||||
|
|
331
index-pack.c
331
index-pack.c
|
@ -13,63 +13,93 @@ static const char index_pack_usage[] =
|
|||
struct object_entry
|
||||
{
|
||||
unsigned long offset;
|
||||
unsigned long size;
|
||||
unsigned int hdr_size;
|
||||
enum object_type type;
|
||||
enum object_type real_type;
|
||||
unsigned char sha1[20];
|
||||
};
|
||||
|
||||
union delta_base {
|
||||
unsigned char sha1[20];
|
||||
unsigned long offset;
|
||||
};
|
||||
|
||||
/*
|
||||
* Even if sizeof(union delta_base) == 24 on 64-bit archs, we really want
|
||||
* to memcmp() only the first 20 bytes.
|
||||
*/
|
||||
#define UNION_BASE_SZ 20
|
||||
|
||||
struct delta_entry
|
||||
{
|
||||
struct object_entry *obj;
|
||||
unsigned char base_sha1[20];
|
||||
union delta_base base;
|
||||
};
|
||||
|
||||
static const char *pack_name;
|
||||
static unsigned char *pack_base;
|
||||
static unsigned long pack_size;
|
||||
static struct object_entry *objects;
|
||||
static struct delta_entry *deltas;
|
||||
static int nr_objects;
|
||||
static int nr_deltas;
|
||||
|
||||
/* We always read in 4kB chunks. */
|
||||
static unsigned char input_buffer[4096];
|
||||
static unsigned long input_offset, input_len, consumed_bytes;
|
||||
static SHA_CTX input_ctx;
|
||||
static int input_fd;
|
||||
|
||||
/*
|
||||
* Make sure at least "min" bytes are available in the buffer, and
|
||||
* return the pointer to the buffer.
|
||||
*/
|
||||
static void * fill(int min)
|
||||
{
|
||||
if (min <= input_len)
|
||||
return input_buffer + input_offset;
|
||||
if (min > sizeof(input_buffer))
|
||||
die("cannot fill %d bytes", min);
|
||||
if (input_offset) {
|
||||
SHA1_Update(&input_ctx, input_buffer, input_offset);
|
||||
memcpy(input_buffer, input_buffer + input_offset, input_len);
|
||||
input_offset = 0;
|
||||
}
|
||||
do {
|
||||
int ret = xread(input_fd, input_buffer + input_len,
|
||||
sizeof(input_buffer) - input_len);
|
||||
if (ret <= 0) {
|
||||
if (!ret)
|
||||
die("early EOF");
|
||||
die("read error on input: %s", strerror(errno));
|
||||
}
|
||||
input_len += ret;
|
||||
} while (input_len < min);
|
||||
return input_buffer;
|
||||
}
|
||||
|
||||
static void use(int bytes)
|
||||
{
|
||||
if (bytes > input_len)
|
||||
die("used more bytes than were available");
|
||||
input_len -= bytes;
|
||||
input_offset += bytes;
|
||||
consumed_bytes += bytes;
|
||||
}
|
||||
|
||||
static void open_pack_file(void)
|
||||
{
|
||||
int fd;
|
||||
struct stat st;
|
||||
|
||||
fd = open(pack_name, O_RDONLY);
|
||||
if (fd < 0)
|
||||
input_fd = open(pack_name, O_RDONLY);
|
||||
if (input_fd < 0)
|
||||
die("cannot open packfile '%s': %s", pack_name,
|
||||
strerror(errno));
|
||||
if (fstat(fd, &st)) {
|
||||
int err = errno;
|
||||
close(fd);
|
||||
die("cannot fstat packfile '%s': %s", pack_name,
|
||||
strerror(err));
|
||||
}
|
||||
pack_size = st.st_size;
|
||||
pack_base = mmap(NULL, pack_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (pack_base == MAP_FAILED) {
|
||||
int err = errno;
|
||||
close(fd);
|
||||
die("cannot mmap packfile '%s': %s", pack_name,
|
||||
strerror(err));
|
||||
}
|
||||
close(fd);
|
||||
SHA1_Init(&input_ctx);
|
||||
}
|
||||
|
||||
static void parse_pack_header(void)
|
||||
{
|
||||
const struct pack_header *hdr;
|
||||
unsigned char sha1[20];
|
||||
SHA_CTX ctx;
|
||||
|
||||
/* Ensure there are enough bytes for the header and final SHA1 */
|
||||
if (pack_size < sizeof(struct pack_header) + 20)
|
||||
die("packfile '%s' is too small", pack_name);
|
||||
struct pack_header *hdr = fill(sizeof(struct pack_header));
|
||||
|
||||
/* Header consistency check */
|
||||
hdr = (void *)pack_base;
|
||||
if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
|
||||
die("packfile '%s' signature mismatch", pack_name);
|
||||
if (!pack_version_ok(hdr->hdr_version))
|
||||
|
@ -77,13 +107,8 @@ static void parse_pack_header(void)
|
|||
pack_name, ntohl(hdr->hdr_version));
|
||||
|
||||
nr_objects = ntohl(hdr->hdr_entries);
|
||||
|
||||
/* Check packfile integrity */
|
||||
SHA1_Init(&ctx);
|
||||
SHA1_Update(&ctx, pack_base, pack_size - 20);
|
||||
SHA1_Final(sha1, &ctx);
|
||||
if (hashcmp(sha1, pack_base + pack_size - 20))
|
||||
die("packfile '%s' SHA1 mismatch", pack_name);
|
||||
use(sizeof(struct pack_header));
|
||||
/*fprintf(stderr, "Indexing %d objects\n", nr_objects);*/
|
||||
}
|
||||
|
||||
static void bad_object(unsigned long offset, const char *format,
|
||||
|
@ -101,86 +126,121 @@ static void bad_object(unsigned long offset, const char *format, ...)
|
|||
pack_name, offset, buf);
|
||||
}
|
||||
|
||||
static void *unpack_entry_data(unsigned long offset,
|
||||
unsigned long *current_pos, unsigned long size)
|
||||
static void *unpack_entry_data(unsigned long offset, unsigned long size)
|
||||
{
|
||||
unsigned long pack_limit = pack_size - 20;
|
||||
unsigned long pos = *current_pos;
|
||||
z_stream stream;
|
||||
void *buf = xmalloc(size);
|
||||
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
stream.next_out = buf;
|
||||
stream.avail_out = size;
|
||||
stream.next_in = pack_base + pos;
|
||||
stream.avail_in = pack_limit - pos;
|
||||
stream.next_in = fill(1);
|
||||
stream.avail_in = input_len;
|
||||
inflateInit(&stream);
|
||||
|
||||
for (;;) {
|
||||
int ret = inflate(&stream, 0);
|
||||
if (ret == Z_STREAM_END)
|
||||
use(input_len - stream.avail_in);
|
||||
if (stream.total_out == size && ret == Z_STREAM_END)
|
||||
break;
|
||||
if (ret != Z_OK)
|
||||
bad_object(offset, "inflate returned %d", ret);
|
||||
stream.next_in = fill(1);
|
||||
stream.avail_in = input_len;
|
||||
}
|
||||
inflateEnd(&stream);
|
||||
if (stream.total_out != size)
|
||||
bad_object(offset, "size mismatch (expected %lu, got %lu)",
|
||||
size, stream.total_out);
|
||||
*current_pos = pack_limit - stream.avail_in;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void *unpack_raw_entry(unsigned long offset,
|
||||
enum object_type *obj_type,
|
||||
unsigned long *obj_size,
|
||||
unsigned char *delta_base,
|
||||
unsigned long *next_obj_offset)
|
||||
static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_base)
|
||||
{
|
||||
unsigned long pack_limit = pack_size - 20;
|
||||
unsigned long pos = offset;
|
||||
unsigned char c;
|
||||
unsigned long size;
|
||||
unsigned char *p, c;
|
||||
unsigned long size, base_offset;
|
||||
unsigned shift;
|
||||
enum object_type type;
|
||||
void *data;
|
||||
|
||||
c = pack_base[pos++];
|
||||
type = (c >> 4) & 7;
|
||||
obj->offset = consumed_bytes;
|
||||
|
||||
p = fill(1);
|
||||
c = *p;
|
||||
use(1);
|
||||
obj->type = (c >> 4) & 7;
|
||||
size = (c & 15);
|
||||
shift = 4;
|
||||
while (c & 0x80) {
|
||||
if (pos >= pack_limit)
|
||||
bad_object(offset, "object extends past end of pack");
|
||||
c = pack_base[pos++];
|
||||
p = fill(1);
|
||||
c = *p;
|
||||
use(1);
|
||||
size += (c & 0x7fUL) << shift;
|
||||
shift += 7;
|
||||
}
|
||||
obj->size = size;
|
||||
|
||||
switch (type) {
|
||||
case OBJ_DELTA:
|
||||
if (pos + 20 >= pack_limit)
|
||||
bad_object(offset, "object extends past end of pack");
|
||||
hashcpy(delta_base, pack_base + pos);
|
||||
pos += 20;
|
||||
/* fallthru */
|
||||
switch (obj->type) {
|
||||
case OBJ_REF_DELTA:
|
||||
hashcpy(delta_base->sha1, fill(20));
|
||||
use(20);
|
||||
break;
|
||||
case OBJ_OFS_DELTA:
|
||||
memset(delta_base, 0, sizeof(*delta_base));
|
||||
p = fill(1);
|
||||
c = *p;
|
||||
use(1);
|
||||
base_offset = c & 127;
|
||||
while (c & 128) {
|
||||
base_offset += 1;
|
||||
if (!base_offset || base_offset & ~(~0UL >> 7))
|
||||
bad_object(obj->offset, "offset value overflow for delta base object");
|
||||
p = fill(1);
|
||||
c = *p;
|
||||
use(1);
|
||||
base_offset = (base_offset << 7) + (c & 127);
|
||||
}
|
||||
delta_base->offset = obj->offset - base_offset;
|
||||
if (delta_base->offset >= obj->offset)
|
||||
bad_object(obj->offset, "delta base offset is out of bound");
|
||||
break;
|
||||
case OBJ_COMMIT:
|
||||
case OBJ_TREE:
|
||||
case OBJ_BLOB:
|
||||
case OBJ_TAG:
|
||||
data = unpack_entry_data(offset, &pos, size);
|
||||
break;
|
||||
default:
|
||||
bad_object(offset, "bad object type %d", type);
|
||||
bad_object(obj->offset, "bad object type %d", obj->type);
|
||||
}
|
||||
obj->hdr_size = consumed_bytes - obj->offset;
|
||||
|
||||
*obj_type = type;
|
||||
*obj_size = size;
|
||||
*next_obj_offset = pos;
|
||||
return unpack_entry_data(obj->offset, obj->size);
|
||||
}
|
||||
|
||||
static void * get_data_from_pack(struct object_entry *obj)
|
||||
{
|
||||
unsigned long from = obj[0].offset + obj[0].hdr_size;
|
||||
unsigned long len = obj[1].offset - from;
|
||||
unsigned pg_offset = from % getpagesize();
|
||||
unsigned char *map, *data;
|
||||
z_stream stream;
|
||||
int st;
|
||||
|
||||
map = mmap(NULL, len + pg_offset, PROT_READ, MAP_PRIVATE,
|
||||
input_fd, from - pg_offset);
|
||||
if (map == MAP_FAILED)
|
||||
die("cannot mmap packfile '%s': %s", pack_name, strerror(errno));
|
||||
data = xmalloc(obj->size);
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
stream.next_out = data;
|
||||
stream.avail_out = obj->size;
|
||||
stream.next_in = map + pg_offset;
|
||||
stream.avail_in = len;
|
||||
inflateInit(&stream);
|
||||
while ((st = inflate(&stream, Z_FINISH)) == Z_OK);
|
||||
inflateEnd(&stream);
|
||||
if (st != Z_STREAM_END || stream.total_out != obj->size)
|
||||
die("serious inflate inconsistency");
|
||||
munmap(map, len + pg_offset);
|
||||
return data;
|
||||
}
|
||||
|
||||
static int find_delta(const unsigned char *base_sha1)
|
||||
static int find_delta(const union delta_base *base)
|
||||
{
|
||||
int first = 0, last = nr_deltas;
|
||||
|
||||
|
@ -189,7 +249,7 @@ static int find_delta(const unsigned char *base_sha1)
|
|||
struct delta_entry *delta = &deltas[next];
|
||||
int cmp;
|
||||
|
||||
cmp = hashcmp(base_sha1, delta->base_sha1);
|
||||
cmp = memcmp(base, &delta->base, UNION_BASE_SZ);
|
||||
if (!cmp)
|
||||
return next;
|
||||
if (cmp < 0) {
|
||||
|
@ -201,18 +261,18 @@ static int find_delta(const unsigned char *base_sha1)
|
|||
return -first-1;
|
||||
}
|
||||
|
||||
static int find_deltas_based_on_sha1(const unsigned char *base_sha1,
|
||||
int *first_index, int *last_index)
|
||||
static int find_delta_childs(const union delta_base *base,
|
||||
int *first_index, int *last_index)
|
||||
{
|
||||
int first = find_delta(base_sha1);
|
||||
int first = find_delta(base);
|
||||
int last = first;
|
||||
int end = nr_deltas - 1;
|
||||
|
||||
if (first < 0)
|
||||
return -1;
|
||||
while (first > 0 && !hashcmp(deltas[first - 1].base_sha1, base_sha1))
|
||||
while (first > 0 && !memcmp(&deltas[first - 1].base, base, UNION_BASE_SZ))
|
||||
--first;
|
||||
while (last < end && !hashcmp(deltas[last + 1].base_sha1, base_sha1))
|
||||
while (last < end && !memcmp(&deltas[last + 1].base, base, UNION_BASE_SZ))
|
||||
++last;
|
||||
*first_index = first;
|
||||
*last_index = last;
|
||||
|
@ -252,25 +312,34 @@ static void resolve_delta(struct delta_entry *delta, void *base_data,
|
|||
unsigned long delta_size;
|
||||
void *result;
|
||||
unsigned long result_size;
|
||||
enum object_type delta_type;
|
||||
unsigned char base_sha1[20];
|
||||
unsigned long next_obj_offset;
|
||||
union delta_base delta_base;
|
||||
int j, first, last;
|
||||
|
||||
obj->real_type = type;
|
||||
delta_data = unpack_raw_entry(obj->offset, &delta_type,
|
||||
&delta_size, base_sha1,
|
||||
&next_obj_offset);
|
||||
delta_data = get_data_from_pack(obj);
|
||||
delta_size = obj->size;
|
||||
result = patch_delta(base_data, base_size, delta_data, delta_size,
|
||||
&result_size);
|
||||
free(delta_data);
|
||||
if (!result)
|
||||
bad_object(obj->offset, "failed to apply delta");
|
||||
sha1_object(result, result_size, type, obj->sha1);
|
||||
if (!find_deltas_based_on_sha1(obj->sha1, &first, &last)) {
|
||||
|
||||
hashcpy(delta_base.sha1, obj->sha1);
|
||||
if (!find_delta_childs(&delta_base, &first, &last)) {
|
||||
for (j = first; j <= last; j++)
|
||||
resolve_delta(&deltas[j], result, result_size, type);
|
||||
if (deltas[j].obj->type == OBJ_REF_DELTA)
|
||||
resolve_delta(&deltas[j], result, result_size, type);
|
||||
}
|
||||
|
||||
memset(&delta_base, 0, sizeof(delta_base));
|
||||
delta_base.offset = obj->offset;
|
||||
if (!find_delta_childs(&delta_base, &first, &last)) {
|
||||
for (j = first; j <= last; j++)
|
||||
if (deltas[j].obj->type == OBJ_OFS_DELTA)
|
||||
resolve_delta(&deltas[j], result, result_size, type);
|
||||
}
|
||||
|
||||
free(result);
|
||||
}
|
||||
|
||||
|
@ -278,16 +347,16 @@ 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 hashcmp(delta_a->base_sha1, delta_b->base_sha1);
|
||||
return memcmp(&delta_a->base, &delta_b->base, UNION_BASE_SZ);
|
||||
}
|
||||
|
||||
static void parse_pack_objects(void)
|
||||
/* Parse all objects and return the pack content SHA1 hash */
|
||||
static void parse_pack_objects(unsigned char *sha1)
|
||||
{
|
||||
int i;
|
||||
unsigned long offset = sizeof(struct pack_header);
|
||||
unsigned char base_sha1[20];
|
||||
struct delta_entry *delta = deltas;
|
||||
void *data;
|
||||
unsigned long data_size;
|
||||
struct stat st;
|
||||
|
||||
/*
|
||||
* First pass:
|
||||
|
@ -297,22 +366,32 @@ static void parse_pack_objects(void)
|
|||
*/
|
||||
for (i = 0; i < nr_objects; i++) {
|
||||
struct object_entry *obj = &objects[i];
|
||||
obj->offset = offset;
|
||||
data = unpack_raw_entry(offset, &obj->type, &data_size,
|
||||
base_sha1, &offset);
|
||||
data = unpack_raw_entry(obj, &delta->base);
|
||||
obj->real_type = obj->type;
|
||||
if (obj->type == OBJ_DELTA) {
|
||||
struct delta_entry *delta = &deltas[nr_deltas++];
|
||||
if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA) {
|
||||
nr_deltas++;
|
||||
delta->obj = obj;
|
||||
hashcpy(delta->base_sha1, base_sha1);
|
||||
delta++;
|
||||
} else
|
||||
sha1_object(data, data_size, obj->type, obj->sha1);
|
||||
sha1_object(data, obj->size, obj->type, obj->sha1);
|
||||
free(data);
|
||||
}
|
||||
if (offset != pack_size - 20)
|
||||
objects[i].offset = consumed_bytes;
|
||||
|
||||
/* Check pack integrity */
|
||||
SHA1_Update(&input_ctx, input_buffer, input_offset);
|
||||
SHA1_Final(sha1, &input_ctx);
|
||||
if (hashcmp(fill(20), sha1))
|
||||
die("packfile '%s' SHA1 mismatch", pack_name);
|
||||
use(20);
|
||||
|
||||
/* If input_fd is a file, we should have reached its end now. */
|
||||
if (fstat(input_fd, &st))
|
||||
die("cannot fstat packfile '%s': %s", pack_name, strerror(errno));
|
||||
if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
|
||||
die("packfile '%s' has junk at the end", pack_name);
|
||||
|
||||
/* Sort deltas by base SHA1 for fast searching */
|
||||
/* Sort deltas by base SHA1/offset for fast searching */
|
||||
qsort(deltas, nr_deltas, sizeof(struct delta_entry),
|
||||
compare_delta_entry);
|
||||
|
||||
|
@ -326,22 +405,36 @@ static void parse_pack_objects(void)
|
|||
*/
|
||||
for (i = 0; i < nr_objects; i++) {
|
||||
struct object_entry *obj = &objects[i];
|
||||
int j, first, last;
|
||||
union delta_base base;
|
||||
int j, ref, ref_first, ref_last, ofs, ofs_first, ofs_last;
|
||||
|
||||
if (obj->type == OBJ_DELTA)
|
||||
if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA)
|
||||
continue;
|
||||
if (find_deltas_based_on_sha1(obj->sha1, &first, &last))
|
||||
hashcpy(base.sha1, obj->sha1);
|
||||
ref = !find_delta_childs(&base, &ref_first, &ref_last);
|
||||
memset(&base, 0, sizeof(base));
|
||||
base.offset = obj->offset;
|
||||
ofs = !find_delta_childs(&base, &ofs_first, &ofs_last);
|
||||
if (!ref && !ofs)
|
||||
continue;
|
||||
data = unpack_raw_entry(obj->offset, &obj->type, &data_size,
|
||||
base_sha1, &offset);
|
||||
for (j = first; j <= last; j++)
|
||||
resolve_delta(&deltas[j], data, data_size, obj->type);
|
||||
data = get_data_from_pack(obj);
|
||||
if (ref)
|
||||
for (j = ref_first; j <= ref_last; j++)
|
||||
if (deltas[j].obj->type == OBJ_REF_DELTA)
|
||||
resolve_delta(&deltas[j], data,
|
||||
obj->size, obj->type);
|
||||
if (ofs)
|
||||
for (j = ofs_first; j <= ofs_last; j++)
|
||||
if (deltas[j].obj->type == OBJ_OFS_DELTA)
|
||||
resolve_delta(&deltas[j], data,
|
||||
obj->size, obj->type);
|
||||
free(data);
|
||||
}
|
||||
|
||||
/* Check for unresolved deltas */
|
||||
for (i = 0; i < nr_deltas; i++) {
|
||||
if (deltas[i].obj->real_type == OBJ_DELTA)
|
||||
if (deltas[i].obj->real_type == OBJ_REF_DELTA ||
|
||||
deltas[i].obj->real_type == OBJ_OFS_DELTA)
|
||||
die("packfile '%s' has unresolved deltas", pack_name);
|
||||
}
|
||||
}
|
||||
|
@ -353,6 +446,10 @@ static int sha1_compare(const void *_a, const void *_b)
|
|||
return hashcmp(a->sha1, b->sha1);
|
||||
}
|
||||
|
||||
/*
|
||||
* On entry *sha1 contains the pack content SHA1 hash, on exit it is
|
||||
* the SHA1 hash of sorted object names.
|
||||
*/
|
||||
static void write_index_file(const char *index_name, unsigned char *sha1)
|
||||
{
|
||||
struct sha1file *f;
|
||||
|
@ -412,7 +509,7 @@ static void write_index_file(const char *index_name, unsigned char *sha1)
|
|||
sha1write(f, obj->sha1, 20);
|
||||
SHA1_Update(&ctx, obj->sha1, 20);
|
||||
}
|
||||
sha1write(f, pack_base + pack_size - 20, 20);
|
||||
sha1write(f, sha1, 20);
|
||||
sha1close(f, NULL, 1);
|
||||
free(sorted_by_sha);
|
||||
SHA1_Final(sha1, &ctx);
|
||||
|
@ -458,9 +555,9 @@ int main(int argc, char **argv)
|
|||
|
||||
open_pack_file();
|
||||
parse_pack_header();
|
||||
objects = xcalloc(nr_objects, sizeof(struct object_entry));
|
||||
objects = xcalloc(nr_objects + 1, sizeof(struct object_entry));
|
||||
deltas = xcalloc(nr_objects, sizeof(struct delta_entry));
|
||||
parse_pack_objects();
|
||||
parse_pack_objects(sha1);
|
||||
free(deltas);
|
||||
write_index_file(index_name, sha1);
|
||||
free(objects);
|
||||
|
|
|
@ -1235,13 +1235,10 @@ int merge(struct commit *h1,
|
|||
if (merged_common_ancestors == NULL) {
|
||||
/* if there is no common ancestor, make an empty tree */
|
||||
struct tree *tree = xcalloc(1, sizeof(struct tree));
|
||||
unsigned char hdr[40];
|
||||
int hdrlen;
|
||||
|
||||
tree->object.parsed = 1;
|
||||
tree->object.type = OBJ_TREE;
|
||||
write_sha1_file_prepare(NULL, 0, tree_type, tree->object.sha1,
|
||||
hdr, &hdrlen);
|
||||
hash_sha1_file(NULL, 0, tree_type, tree->object.sha1);
|
||||
merged_common_ancestors = make_virtual_commit(tree, "ancestor");
|
||||
}
|
||||
|
||||
|
|
5
pack.h
5
pack.h
|
@ -7,7 +7,7 @@
|
|||
* Packed object header
|
||||
*/
|
||||
#define PACK_SIGNATURE 0x5041434b /* "PACK" */
|
||||
#define PACK_VERSION 3
|
||||
#define PACK_VERSION 2
|
||||
#define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3))
|
||||
struct pack_header {
|
||||
unsigned int hdr_signature;
|
||||
|
@ -16,7 +16,4 @@ struct pack_header {
|
|||
};
|
||||
|
||||
extern int verify_pack(struct packed_git *, int);
|
||||
extern int check_reuse_pack_delta(struct packed_git *, unsigned long,
|
||||
unsigned char *, unsigned long *,
|
||||
enum object_type *);
|
||||
#endif
|
||||
|
|
2
pager.c
2
pager.c
|
@ -50,7 +50,7 @@ void setup_pager(void)
|
|||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
|
||||
setenv("LESS", "-RS", 0);
|
||||
setenv("LESS", "FRSX", 0);
|
||||
run_pager(pager);
|
||||
die("unable to execute pager '%s'", pager);
|
||||
exit(255);
|
||||
|
|
|
@ -732,6 +732,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
|||
int i, flags, seen_dashdash, show_merge;
|
||||
const char **unrecognized = argv + 1;
|
||||
int left = 1;
|
||||
int all_match = 0;
|
||||
|
||||
/* First, search for "--" */
|
||||
seen_dashdash = 0;
|
||||
|
@ -967,6 +968,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
|||
add_message_grep(revs, arg+7);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--all-match")) {
|
||||
all_match = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
|
||||
if (opts > 0) {
|
||||
|
@ -1028,8 +1033,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
|||
if (diff_setup_done(&revs->diffopt) < 0)
|
||||
die("diff_setup_done failed");
|
||||
|
||||
if (revs->grep_filter)
|
||||
if (revs->grep_filter) {
|
||||
revs->grep_filter->all_match = all_match;
|
||||
compile_grep_patterns(revs->grep_filter);
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
|
171
sha1_file.c
171
sha1_file.c
|
@ -671,14 +671,8 @@ static void reprepare_packed_git(void)
|
|||
|
||||
int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type)
|
||||
{
|
||||
char header[100];
|
||||
unsigned char real_sha1[20];
|
||||
SHA_CTX c;
|
||||
|
||||
SHA1_Init(&c);
|
||||
SHA1_Update(&c, header, 1+sprintf(header, "%s %lu", type, size));
|
||||
SHA1_Update(&c, map, size);
|
||||
SHA1_Final(real_sha1, &c);
|
||||
hash_sha1_file(map, size, type, real_sha1);
|
||||
return hashcmp(sha1, real_sha1) ? -1 : 0;
|
||||
}
|
||||
|
||||
|
@ -883,26 +877,61 @@ void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned l
|
|||
return unpack_sha1_rest(&stream, hdr, *size);
|
||||
}
|
||||
|
||||
static unsigned long get_delta_base(struct packed_git *p,
|
||||
unsigned long offset,
|
||||
enum object_type kind,
|
||||
unsigned long delta_obj_offset,
|
||||
unsigned long *base_obj_offset)
|
||||
{
|
||||
unsigned char *base_info = (unsigned char *) p->pack_base + offset;
|
||||
unsigned long base_offset;
|
||||
|
||||
/* there must be at least 20 bytes left regardless of delta type */
|
||||
if (p->pack_size <= offset + 20)
|
||||
die("truncated pack file");
|
||||
|
||||
if (kind == OBJ_OFS_DELTA) {
|
||||
unsigned used = 0;
|
||||
unsigned char c = base_info[used++];
|
||||
base_offset = c & 127;
|
||||
while (c & 128) {
|
||||
base_offset += 1;
|
||||
if (!base_offset || base_offset & ~(~0UL >> 7))
|
||||
die("offset value overflow for delta base object");
|
||||
c = base_info[used++];
|
||||
base_offset = (base_offset << 7) + (c & 127);
|
||||
}
|
||||
base_offset = delta_obj_offset - base_offset;
|
||||
if (base_offset >= delta_obj_offset)
|
||||
die("delta base offset out of bound");
|
||||
offset += used;
|
||||
} else if (kind == OBJ_REF_DELTA) {
|
||||
/* The base entry _must_ be in the same pack */
|
||||
base_offset = find_pack_entry_one(base_info, p);
|
||||
if (!base_offset)
|
||||
die("failed to find delta-pack base object %s",
|
||||
sha1_to_hex(base_info));
|
||||
offset += 20;
|
||||
} else
|
||||
die("I am totally screwed");
|
||||
*base_obj_offset = base_offset;
|
||||
return offset;
|
||||
}
|
||||
|
||||
/* forward declaration for a mutually recursive function */
|
||||
static int packed_object_info(struct packed_git *p, unsigned long offset,
|
||||
char *type, unsigned long *sizep);
|
||||
|
||||
static int packed_delta_info(struct packed_git *p,
|
||||
unsigned long offset,
|
||||
enum object_type kind,
|
||||
unsigned long obj_offset,
|
||||
char *type,
|
||||
unsigned long *sizep)
|
||||
{
|
||||
unsigned long base_offset;
|
||||
unsigned char *base_sha1 = (unsigned char *) p->pack_base + offset;
|
||||
|
||||
if (p->pack_size < offset + 20)
|
||||
die("truncated pack file");
|
||||
/* The base entry _must_ be in the same pack */
|
||||
base_offset = find_pack_entry_one(base_sha1, p);
|
||||
if (!base_offset)
|
||||
die("failed to find delta-pack base object %s",
|
||||
sha1_to_hex(base_sha1));
|
||||
offset += 20;
|
||||
offset = get_delta_base(p, offset, kind, obj_offset, &base_offset);
|
||||
|
||||
/* We choose to only get the type of the base object and
|
||||
* ignore potentially corrupt pack file that expects the delta
|
||||
|
@ -914,7 +943,7 @@ static int packed_delta_info(struct packed_git *p,
|
|||
|
||||
if (sizep) {
|
||||
const unsigned char *data;
|
||||
unsigned char delta_head[64];
|
||||
unsigned char delta_head[20];
|
||||
unsigned long result_size;
|
||||
z_stream stream;
|
||||
int st;
|
||||
|
@ -965,25 +994,6 @@ static unsigned long unpack_object_header(struct packed_git *p, unsigned long of
|
|||
return offset + used;
|
||||
}
|
||||
|
||||
int check_reuse_pack_delta(struct packed_git *p, unsigned long offset,
|
||||
unsigned char *base, unsigned long *sizep,
|
||||
enum object_type *kindp)
|
||||
{
|
||||
unsigned long ptr;
|
||||
int status = -1;
|
||||
|
||||
use_packed_git(p);
|
||||
ptr = offset;
|
||||
ptr = unpack_object_header(p, ptr, kindp, sizep);
|
||||
if (*kindp != OBJ_DELTA)
|
||||
goto done;
|
||||
hashcpy(base, (unsigned char *) p->pack_base + ptr);
|
||||
status = 0;
|
||||
done:
|
||||
unuse_packed_git(p);
|
||||
return status;
|
||||
}
|
||||
|
||||
void packed_object_info_detail(struct packed_git *p,
|
||||
unsigned long offset,
|
||||
char *type,
|
||||
|
@ -992,11 +1002,12 @@ void packed_object_info_detail(struct packed_git *p,
|
|||
unsigned int *delta_chain_length,
|
||||
unsigned char *base_sha1)
|
||||
{
|
||||
unsigned long val;
|
||||
unsigned long obj_offset, val;
|
||||
unsigned char *next_sha1;
|
||||
enum object_type kind;
|
||||
|
||||
*delta_chain_length = 0;
|
||||
obj_offset = offset;
|
||||
offset = unpack_object_header(p, offset, &kind, size);
|
||||
|
||||
for (;;) {
|
||||
|
@ -1011,7 +1022,13 @@ void packed_object_info_detail(struct packed_git *p,
|
|||
strcpy(type, type_names[kind]);
|
||||
*store_size = 0; /* notyet */
|
||||
return;
|
||||
case OBJ_DELTA:
|
||||
case OBJ_OFS_DELTA:
|
||||
get_delta_base(p, offset, kind, obj_offset, &offset);
|
||||
if (*delta_chain_length == 0) {
|
||||
/* TODO: find base_sha1 as pointed by offset */
|
||||
}
|
||||
break;
|
||||
case OBJ_REF_DELTA:
|
||||
if (p->pack_size <= offset + 20)
|
||||
die("pack file %s records an incomplete delta base",
|
||||
p->pack_name);
|
||||
|
@ -1021,6 +1038,7 @@ void packed_object_info_detail(struct packed_git *p,
|
|||
offset = find_pack_entry_one(next_sha1, p);
|
||||
break;
|
||||
}
|
||||
obj_offset = offset;
|
||||
offset = unpack_object_header(p, offset, &kind, &val);
|
||||
(*delta_chain_length)++;
|
||||
}
|
||||
|
@ -1029,15 +1047,15 @@ void packed_object_info_detail(struct packed_git *p,
|
|||
static int packed_object_info(struct packed_git *p, unsigned long offset,
|
||||
char *type, unsigned long *sizep)
|
||||
{
|
||||
unsigned long size;
|
||||
unsigned long size, obj_offset = offset;
|
||||
enum object_type kind;
|
||||
|
||||
offset = unpack_object_header(p, offset, &kind, &size);
|
||||
|
||||
if (kind == OBJ_DELTA)
|
||||
return packed_delta_info(p, offset, type, sizep);
|
||||
|
||||
switch (kind) {
|
||||
case OBJ_OFS_DELTA:
|
||||
case OBJ_REF_DELTA:
|
||||
return packed_delta_info(p, offset, kind, obj_offset, type, sizep);
|
||||
case OBJ_COMMIT:
|
||||
case OBJ_TREE:
|
||||
case OBJ_BLOB:
|
||||
|
@ -1083,23 +1101,15 @@ static void *unpack_compressed_entry(struct packed_git *p,
|
|||
static void *unpack_delta_entry(struct packed_git *p,
|
||||
unsigned long offset,
|
||||
unsigned long delta_size,
|
||||
enum object_type kind,
|
||||
unsigned long obj_offset,
|
||||
char *type,
|
||||
unsigned long *sizep)
|
||||
{
|
||||
void *delta_data, *result, *base;
|
||||
unsigned long result_size, base_size, base_offset;
|
||||
unsigned char *base_sha1;
|
||||
|
||||
if (p->pack_size < offset + 20)
|
||||
die("truncated pack file");
|
||||
/* The base entry _must_ be in the same pack */
|
||||
base_sha1 = (unsigned char*)p->pack_base + offset;
|
||||
base_offset = find_pack_entry_one(base_sha1, p);
|
||||
if (!base_offset)
|
||||
die("failed to find delta-pack base object %s",
|
||||
sha1_to_hex(base_sha1));
|
||||
offset += 20;
|
||||
|
||||
offset = get_delta_base(p, offset, kind, obj_offset, &base_offset);
|
||||
base = unpack_entry_gently(p, base_offset, type, &base_size);
|
||||
if (!base)
|
||||
die("failed to read delta base object at %lu from %s",
|
||||
|
@ -1136,13 +1146,14 @@ static void *unpack_entry(struct pack_entry *entry,
|
|||
void *unpack_entry_gently(struct packed_git *p, unsigned long offset,
|
||||
char *type, unsigned long *sizep)
|
||||
{
|
||||
unsigned long size;
|
||||
unsigned long size, obj_offset = offset;
|
||||
enum object_type kind;
|
||||
|
||||
offset = unpack_object_header(p, offset, &kind, &size);
|
||||
switch (kind) {
|
||||
case OBJ_DELTA:
|
||||
return unpack_delta_entry(p, offset, size, type, sizep);
|
||||
case OBJ_OFS_DELTA:
|
||||
case OBJ_REF_DELTA:
|
||||
return unpack_delta_entry(p, offset, size, kind, obj_offset, type, sizep);
|
||||
case OBJ_COMMIT:
|
||||
case OBJ_TREE:
|
||||
case OBJ_BLOB:
|
||||
|
@ -1347,12 +1358,9 @@ void *read_object_with_reference(const unsigned char *sha1,
|
|||
}
|
||||
}
|
||||
|
||||
char *write_sha1_file_prepare(void *buf,
|
||||
unsigned long len,
|
||||
const char *type,
|
||||
unsigned char *sha1,
|
||||
unsigned char *hdr,
|
||||
int *hdrlen)
|
||||
static void write_sha1_file_prepare(void *buf, unsigned long len,
|
||||
const char *type, unsigned char *sha1,
|
||||
unsigned char *hdr, int *hdrlen)
|
||||
{
|
||||
SHA_CTX c;
|
||||
|
||||
|
@ -1364,8 +1372,6 @@ char *write_sha1_file_prepare(void *buf,
|
|||
SHA1_Update(&c, hdr, *hdrlen);
|
||||
SHA1_Update(&c, buf, len);
|
||||
SHA1_Final(sha1, &c);
|
||||
|
||||
return sha1_file_name(sha1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1501,6 +1507,15 @@ static void setup_object_header(z_stream *stream, const char *type, unsigned lon
|
|||
stream->avail_out -= hdr;
|
||||
}
|
||||
|
||||
int hash_sha1_file(void *buf, unsigned long len, const char *type,
|
||||
unsigned char *sha1)
|
||||
{
|
||||
unsigned char hdr[50];
|
||||
int hdrlen;
|
||||
write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
|
||||
{
|
||||
int size;
|
||||
|
@ -1515,7 +1530,8 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
|
|||
/* Normally if we have it in the pack then we do not bother writing
|
||||
* it out into .git/objects/??/?{38} file.
|
||||
*/
|
||||
filename = write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
|
||||
write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
|
||||
filename = sha1_file_name(sha1);
|
||||
if (returnsha1)
|
||||
hashcpy(returnsha1, sha1);
|
||||
if (has_sha1_file(sha1))
|
||||
|
@ -1784,8 +1800,6 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
|
|||
unsigned long size = 4096;
|
||||
char *buf = xmalloc(size);
|
||||
int ret;
|
||||
unsigned char hdr[50];
|
||||
int hdrlen;
|
||||
|
||||
if (read_pipe(fd, &buf, &size)) {
|
||||
free(buf);
|
||||
|
@ -1796,10 +1810,8 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
|
|||
type = blob_type;
|
||||
if (write_object)
|
||||
ret = write_sha1_file(buf, size, type, sha1);
|
||||
else {
|
||||
write_sha1_file_prepare(buf, size, type, sha1, hdr, &hdrlen);
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
ret = hash_sha1_file(buf, size, type, sha1);
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1809,8 +1821,6 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, con
|
|||
unsigned long size = st->st_size;
|
||||
void *buf;
|
||||
int ret;
|
||||
unsigned char hdr[50];
|
||||
int hdrlen;
|
||||
|
||||
buf = "";
|
||||
if (size)
|
||||
|
@ -1823,10 +1833,8 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, con
|
|||
type = blob_type;
|
||||
if (write_object)
|
||||
ret = write_sha1_file(buf, size, type, sha1);
|
||||
else {
|
||||
write_sha1_file_prepare(buf, size, type, sha1, hdr, &hdrlen);
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
ret = hash_sha1_file(buf, size, type, sha1);
|
||||
if (size)
|
||||
munmap(buf, size);
|
||||
return ret;
|
||||
|
@ -1855,12 +1863,9 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write
|
|||
return error("readlink(\"%s\"): %s", path,
|
||||
errstr);
|
||||
}
|
||||
if (!write_object) {
|
||||
unsigned char hdr[50];
|
||||
int hdrlen;
|
||||
write_sha1_file_prepare(target, st->st_size, blob_type,
|
||||
sha1, hdr, &hdrlen);
|
||||
} else if (write_sha1_file(target, st->st_size, blob_type, sha1))
|
||||
if (!write_object)
|
||||
hash_sha1_file(target, st->st_size, blob_type, sha1);
|
||||
else if (write_sha1_file(target, st->st_size, blob_type, sha1))
|
||||
return error("%s: failed to insert into database",
|
||||
path);
|
||||
free(target);
|
||||
|
|
|
@ -157,7 +157,7 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
|
|||
char canonical[40];
|
||||
unsigned char res[20];
|
||||
|
||||
if (len < MINIMUM_ABBREV)
|
||||
if (len < MINIMUM_ABBREV || len > 40)
|
||||
return -1;
|
||||
hashclr(res);
|
||||
memset(canonical, 'x', 40);
|
||||
|
|
|
@ -8,7 +8,7 @@ int main(int argc, char **argv)
|
|||
static unsigned int top_index[256];
|
||||
|
||||
if (fread(top_index, sizeof(top_index), 1, stdin) != 1)
|
||||
die("unable to read idex");
|
||||
die("unable to read index");
|
||||
nr = 0;
|
||||
for (i = 0; i < 256; i++) {
|
||||
unsigned n = ntohl(top_index[i]);
|
||||
|
|
28
sideband.c
28
sideband.c
|
@ -11,10 +11,13 @@
|
|||
* stream, aka "verbose"). A message over band #3 is a signal that
|
||||
* the remote died unexpectedly. A flush() concludes the stream.
|
||||
*/
|
||||
int recv_sideband(const char *me, int in_stream, int out, int err, char *buf, int bufsz)
|
||||
int recv_sideband(const char *me, int in_stream, int out, int err)
|
||||
{
|
||||
char buf[7 + LARGE_PACKET_MAX + 1];
|
||||
strcpy(buf, "remote:");
|
||||
while (1) {
|
||||
int len = packet_read_line(in_stream, buf, bufsz);
|
||||
int band, len;
|
||||
len = packet_read_line(in_stream, buf+7, LARGE_PACKET_MAX);
|
||||
if (len == 0)
|
||||
break;
|
||||
if (len < 1) {
|
||||
|
@ -22,25 +25,26 @@ int recv_sideband(const char *me, int in_stream, int out, int err, char *buf, in
|
|||
safe_write(err, buf, len);
|
||||
return SIDEBAND_PROTOCOL_ERROR;
|
||||
}
|
||||
band = buf[7] & 0xff;
|
||||
len--;
|
||||
switch (buf[0] & 0xFF) {
|
||||
switch (band) {
|
||||
case 3:
|
||||
safe_write(err, "remote: ", 8);
|
||||
safe_write(err, buf+1, len);
|
||||
safe_write(err, "\n", 1);
|
||||
buf[7] = ' ';
|
||||
buf[8+len] = '\n';
|
||||
safe_write(err, buf, 8+len+1);
|
||||
return SIDEBAND_REMOTE_ERROR;
|
||||
case 2:
|
||||
safe_write(err, "remote: ", 8);
|
||||
safe_write(err, buf+1, len);
|
||||
buf[7] = ' ';
|
||||
safe_write(err, buf, 8+len);
|
||||
continue;
|
||||
case 1:
|
||||
safe_write(out, buf+1, len);
|
||||
safe_write(out, buf+8, len);
|
||||
continue;
|
||||
default:
|
||||
len = sprintf(buf + 1,
|
||||
len = sprintf(buf,
|
||||
"%s: protocol error: bad band #%d\n",
|
||||
me, buf[0] & 0xFF);
|
||||
safe_write(err, buf+1, len);
|
||||
me, band);
|
||||
safe_write(err, buf, len);
|
||||
return SIDEBAND_PROTOCOL_ERROR;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#define DEFAULT_PACKET_MAX 1000
|
||||
#define LARGE_PACKET_MAX 65520
|
||||
|
||||
int recv_sideband(const char *me, int in_stream, int out, int err, char *, int);
|
||||
int recv_sideband(const char *me, int in_stream, int out, int err);
|
||||
ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -5,7 +5,7 @@ Date: Mon Jun 26 00:00:00 2006 +0000
|
|||
|
||||
Initial
|
||||
|
||||
create mode 040000 dir
|
||||
create mode 100644 dir/sub
|
||||
create mode 100644 file0
|
||||
create mode 100644 file2
|
||||
$
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2006 Johannes E. Schindelin
|
||||
#
|
||||
|
||||
test_description='Test special whitespace in diff engine.
|
||||
|
||||
'
|
||||
. ./test-lib.sh
|
||||
. ../diff-lib.sh
|
||||
|
||||
# Ray Lehtiniemi's example
|
||||
|
||||
cat << EOF > x
|
||||
do {
|
||||
nothing;
|
||||
} while (0);
|
||||
EOF
|
||||
|
||||
git-update-index --add x
|
||||
|
||||
cat << EOF > x
|
||||
do
|
||||
{
|
||||
nothing;
|
||||
}
|
||||
while (0);
|
||||
EOF
|
||||
|
||||
cat << EOF > expect
|
||||
diff --git a/x b/x
|
||||
index adf3937..6edc172 100644
|
||||
--- a/x
|
||||
+++ b/x
|
||||
@@ -1,3 +1,5 @@
|
||||
-do {
|
||||
+do
|
||||
+{
|
||||
nothing;
|
||||
-} while (0);
|
||||
+}
|
||||
+while (0);
|
||||
EOF
|
||||
|
||||
git-diff > out
|
||||
test_expect_success "Ray's example without options" 'diff -u expect out'
|
||||
|
||||
git-diff -w > out
|
||||
test_expect_success "Ray's example with -w" 'diff -u expect out'
|
||||
|
||||
git-diff -b > out
|
||||
test_expect_success "Ray's example with -b" 'diff -u expect out'
|
||||
|
||||
tr 'Q' '\015' << EOF > x
|
||||
whitespace at beginning
|
||||
whitespace change
|
||||
whitespace in the middle
|
||||
whitespace at end
|
||||
unchanged line
|
||||
CR at endQ
|
||||
EOF
|
||||
|
||||
git-update-index x
|
||||
|
||||
cat << EOF > x
|
||||
whitespace at beginning
|
||||
whitespace change
|
||||
white space in the middle
|
||||
whitespace at end
|
||||
unchanged line
|
||||
CR at end
|
||||
EOF
|
||||
|
||||
tr 'Q' '\015' << EOF > expect
|
||||
diff --git a/x b/x
|
||||
index d99af23..8b32fb5 100644
|
||||
--- a/x
|
||||
+++ b/x
|
||||
@@ -1,6 +1,6 @@
|
||||
-whitespace at beginning
|
||||
-whitespace change
|
||||
-whitespace in the middle
|
||||
-whitespace at end
|
||||
+ whitespace at beginning
|
||||
+whitespace change
|
||||
+white space in the middle
|
||||
+whitespace at end
|
||||
unchanged line
|
||||
-CR at endQ
|
||||
+CR at end
|
||||
EOF
|
||||
git-diff > out
|
||||
test_expect_success 'another test, without options' 'diff -u expect out'
|
||||
|
||||
cat << EOF > expect
|
||||
diff --git a/x b/x
|
||||
index d99af23..8b32fb5 100644
|
||||
EOF
|
||||
git-diff -w > out
|
||||
test_expect_success 'another test, with -w' 'diff -u expect out'
|
||||
|
||||
tr 'Q' '\015' << EOF > expect
|
||||
diff --git a/x b/x
|
||||
index d99af23..8b32fb5 100644
|
||||
--- a/x
|
||||
+++ b/x
|
||||
@@ -1,6 +1,6 @@
|
||||
-whitespace at beginning
|
||||
+ whitespace at beginning
|
||||
whitespace change
|
||||
-whitespace in the middle
|
||||
-whitespace at end
|
||||
+white space in the middle
|
||||
+whitespace at end
|
||||
unchanged line
|
||||
-CR at endQ
|
||||
+CR at end
|
||||
EOF
|
||||
git-diff -b > out
|
||||
test_expect_success 'another test, with -b' 'diff -u expect out'
|
||||
|
||||
test_done
|
|
@ -0,0 +1,55 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2006 Junio C Hamano
|
||||
#
|
||||
|
||||
test_description='git-apply with new style GNU diff with empty context
|
||||
|
||||
'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success setup '
|
||||
{
|
||||
echo; echo;
|
||||
echo A; echo B; echo C;
|
||||
echo;
|
||||
} >file1 &&
|
||||
cat file1 >file1.orig &&
|
||||
{
|
||||
cat file1 &&
|
||||
echo Q | tr -d "\\012"
|
||||
} >file2 &&
|
||||
cat file2 >file2.orig
|
||||
git add file1 file2 &&
|
||||
sed -e "/^B/d" <file1.orig >file1 &&
|
||||
sed -e "/^B/d" <file2.orig >file2 &&
|
||||
cat file1 >file1.mods &&
|
||||
cat file2 >file2.mods &&
|
||||
git diff |
|
||||
sed -e "s/^ \$//" >diff.output
|
||||
'
|
||||
|
||||
test_expect_success 'apply --numstat' '
|
||||
|
||||
git apply --numstat diff.output >actual &&
|
||||
{
|
||||
echo "0 1 file1" &&
|
||||
echo "0 1 file2"
|
||||
} >expect &&
|
||||
diff -u expect actual
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'apply --apply' '
|
||||
|
||||
cat file1.orig >file1 &&
|
||||
cat file2.orig >file2 &&
|
||||
git update-index file1 file2 &&
|
||||
git apply --index diff.output &&
|
||||
diff -u file1.mods file1 &&
|
||||
diff -u file2.mods file2
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
@ -26,6 +26,7 @@ commit id embedding:
|
|||
|
||||
. ./test-lib.sh
|
||||
TAR=${TAR:-tar}
|
||||
UNZIP=${UNZIP:-unzip}
|
||||
|
||||
test_expect_success \
|
||||
'populate workdir' \
|
||||
|
@ -95,4 +96,38 @@ test_expect_success \
|
|||
'validate file contents with prefix' \
|
||||
'diff -r a c/prefix/a'
|
||||
|
||||
test_expect_success \
|
||||
'git-archive --format=zip' \
|
||||
'git-archive --format=zip HEAD >d.zip'
|
||||
|
||||
test_expect_success \
|
||||
'extract ZIP archive' \
|
||||
'(mkdir d && cd d && $UNZIP ../d.zip)'
|
||||
|
||||
test_expect_success \
|
||||
'validate filenames' \
|
||||
'(cd d/a && find .) | sort >d.lst &&
|
||||
diff a.lst d.lst'
|
||||
|
||||
test_expect_success \
|
||||
'validate file contents' \
|
||||
'diff -r a d/a'
|
||||
|
||||
test_expect_success \
|
||||
'git-archive --format=zip with prefix' \
|
||||
'git-archive --format=zip --prefix=prefix/ HEAD >e.zip'
|
||||
|
||||
test_expect_success \
|
||||
'extract ZIP archive with prefix' \
|
||||
'(mkdir e && cd e && $UNZIP ../e.zip)'
|
||||
|
||||
test_expect_success \
|
||||
'validate filenames with prefix' \
|
||||
'(cd e/prefix/a && find .) | sort >e.lst &&
|
||||
diff a.lst e.lst'
|
||||
|
||||
test_expect_success \
|
||||
'validate file contents with prefix' \
|
||||
'diff -r a e/prefix/a'
|
||||
|
||||
test_done
|
||||
|
|
|
@ -25,6 +25,12 @@ test_create_repo foo
|
|||
# clone doesn't like it if there is no HEAD. Is that a bug?
|
||||
(cd foo && touch file && git add file && git commit -m 'add file' >/dev/null 2>&1)
|
||||
|
||||
# source repository given to git-clone should be relative to the
|
||||
# current path not to the target dir
|
||||
test_expect_failure \
|
||||
'clone of non-existent (relative to $PWD) source should fail' \
|
||||
'git-clone ../foo baz'
|
||||
|
||||
test_expect_success \
|
||||
'clone should work now that source exists' \
|
||||
'git-clone foo bar'
|
||||
|
|
|
@ -135,6 +135,7 @@ test_expect_failure () {
|
|||
else
|
||||
test_failure_ "$@"
|
||||
fi
|
||||
echo >&3 ""
|
||||
}
|
||||
|
||||
test_expect_success () {
|
||||
|
@ -148,6 +149,7 @@ test_expect_success () {
|
|||
else
|
||||
test_failure_ "$@"
|
||||
fi
|
||||
echo >&3 ""
|
||||
}
|
||||
|
||||
test_expect_code () {
|
||||
|
@ -161,6 +163,7 @@ test_expect_code () {
|
|||
else
|
||||
test_failure_ "$@"
|
||||
fi
|
||||
echo >&3 ""
|
||||
}
|
||||
|
||||
# Most tests can use the created repository, but some amy need to create more.
|
||||
|
|
3
trace.c
3
trace.c
|
@ -55,7 +55,8 @@ static int get_trace_fd(int *need_close)
|
|||
{
|
||||
char *trace = getenv("GIT_TRACE");
|
||||
|
||||
if (!trace || !strcmp(trace, "0") || !strcasecmp(trace, "false"))
|
||||
if (!trace || !strcmp(trace, "") ||
|
||||
!strcmp(trace, "0") || !strcasecmp(trace, "false"))
|
||||
return 0;
|
||||
if (!strcmp(trace, "1") || !strcasecmp(trace, "true"))
|
||||
return STDERR_FILENO;
|
||||
|
|
|
@ -16,7 +16,7 @@ static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=n
|
|||
#define OUR_REF (1U << 1)
|
||||
#define WANTED (1U << 2)
|
||||
static int multi_ack, nr_our_refs;
|
||||
static int use_thin_pack;
|
||||
static int use_thin_pack, use_ofs_delta;
|
||||
static struct object_array have_obj;
|
||||
static struct object_array want_obj;
|
||||
static unsigned int timeout;
|
||||
|
@ -137,7 +137,9 @@ static void create_pack_file(void)
|
|||
close(pu_pipe[1]);
|
||||
close(pe_pipe[0]);
|
||||
close(pe_pipe[1]);
|
||||
execl_git_cmd("pack-objects", "--stdout", "--progress", NULL);
|
||||
execl_git_cmd("pack-objects", "--stdout", "--progress",
|
||||
use_ofs_delta ? "--delta-base-offset" : NULL,
|
||||
NULL);
|
||||
kill(pid_rev_list, SIGKILL);
|
||||
die("git-upload-pack: unable to exec git-pack-objects");
|
||||
}
|
||||
|
@ -393,6 +395,8 @@ static void receive_needs(void)
|
|||
multi_ack = 1;
|
||||
if (strstr(line+45, "thin-pack"))
|
||||
use_thin_pack = 1;
|
||||
if (strstr(line+45, "ofs-delta"))
|
||||
use_ofs_delta = 1;
|
||||
if (strstr(line+45, "side-band-64k"))
|
||||
use_sideband = LARGE_PACKET_MAX;
|
||||
else if (strstr(line+45, "side-band"))
|
||||
|
@ -418,7 +422,7 @@ static void receive_needs(void)
|
|||
|
||||
static int send_ref(const char *refname, const unsigned char *sha1)
|
||||
{
|
||||
static const char *capabilities = "multi_ack thin-pack side-band side-band-64k";
|
||||
static const char *capabilities = "multi_ack thin-pack side-band side-band-64k ofs-delta";
|
||||
struct object *o = parse_object(sha1);
|
||||
|
||||
if (!o)
|
||||
|
|
|
@ -90,7 +90,7 @@ static void xdl_find_func(xdfile_t *xf, long i, char *buf, long sz, long *ll) {
|
|||
*rec == '#')) { /* #define? */
|
||||
if (len > sz)
|
||||
len = sz;
|
||||
if (len && rec[len - 1] == '\n')
|
||||
while (0 < len && isspace((unsigned char)rec[len - 1]))
|
||||
len--;
|
||||
memcpy(buf, rec, len);
|
||||
*ll = len;
|
||||
|
|
|
@ -24,14 +24,15 @@
|
|||
#define XMACROS_H
|
||||
|
||||
|
||||
#define GR_PRIME 0x9e370001UL
|
||||
|
||||
|
||||
#define XDL_MIN(a, b) ((a) < (b) ? (a): (b))
|
||||
#define XDL_MAX(a, b) ((a) > (b) ? (a): (b))
|
||||
#define XDL_ABS(v) ((v) >= 0 ? (v): -(v))
|
||||
#define XDL_ISDIGIT(c) ((c) >= '0' && (c) <= '9')
|
||||
#define XDL_HASHLONG(v, b) (((unsigned long)(v) * GR_PRIME) >> ((CHAR_BIT * sizeof(unsigned long)) - (b)))
|
||||
#define XDL_ADDBITS(v,b) ((v) + ((v) >> (b)))
|
||||
#define XDL_MASKBITS(b) ((1UL << (b)) - 1)
|
||||
#define XDL_HASHLONG(v,b) (XDL_ADDBITS((unsigned long)(v), b) & XDL_MASKBITS(b))
|
||||
#define XDL_PTRFREE(p) do { if (p) { xdl_free(p); (p) = NULL; } } while (0)
|
||||
#define XDL_LE32_PUT(p, v) \
|
||||
do { \
|
||||
|
|
|
@ -191,36 +191,30 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
|
|||
int i1, i2;
|
||||
|
||||
if (flags & XDF_IGNORE_WHITESPACE) {
|
||||
for (i1 = i2 = 0; i1 < s1 && i2 < s2; i1++, i2++) {
|
||||
for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) {
|
||||
if (isspace(l1[i1]))
|
||||
while (isspace(l1[i1]) && i1 < s1)
|
||||
i1++;
|
||||
else if (isspace(l2[i2]))
|
||||
if (isspace(l2[i2]))
|
||||
while (isspace(l2[i2]) && i2 < s2)
|
||||
i2++;
|
||||
else if (l1[i1] != l2[i2])
|
||||
return l2[i2] - l1[i1];
|
||||
if (i1 < s1 && i2 < s2 && l1[i1++] != l2[i2++])
|
||||
return 0;
|
||||
}
|
||||
if (i1 >= s1)
|
||||
return 1;
|
||||
else if (i2 >= s2)
|
||||
return -1;
|
||||
return (i1 >= s1 && i2 >= s2);
|
||||
} else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
|
||||
for (i1 = i2 = 0; i1 < s1 && i2 < s2; i1++, i2++) {
|
||||
for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) {
|
||||
if (isspace(l1[i1])) {
|
||||
if (!isspace(l2[i2]))
|
||||
return -1;
|
||||
return 0;
|
||||
while (isspace(l1[i1]) && i1 < s1)
|
||||
i1++;
|
||||
while (isspace(l2[i2]) && i2 < s2)
|
||||
i2++;
|
||||
} else if (l1[i1] != l2[i2])
|
||||
return l2[i2] - l1[i1];
|
||||
} else if (l1[i1++] != l2[i2++])
|
||||
return 0;
|
||||
}
|
||||
if (i1 >= s1)
|
||||
return 1;
|
||||
else if (i2 >= s2)
|
||||
return -1;
|
||||
return (i1 >= s1 && i2 >= s2);
|
||||
} else
|
||||
return s1 == s2 && !memcmp(l1, l2, s1);
|
||||
|
||||
|
@ -233,7 +227,8 @@ unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
|
|||
|
||||
for (; ptr < top && *ptr != '\n'; ptr++) {
|
||||
if (isspace(*ptr) && (flags & XDF_WHITESPACE_FLAGS)) {
|
||||
while (ptr < top && isspace(*ptr) && ptr[1] != '\n')
|
||||
while (ptr + 1 < top && isspace(ptr[1])
|
||||
&& ptr[1] != '\n')
|
||||
ptr++;
|
||||
if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
|
||||
ha += (ha << 5);
|
||||
|
|
Загрузка…
Ссылка в новой задаче