mirror of
https://github.com/git/git.git
synced 2026-03-13 10:23:30 +01:00
Merge commit 'spearce/master'
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -25,7 +25,6 @@ git-clone
|
||||
git-commit
|
||||
git-commit-tree
|
||||
git-config
|
||||
git-convert-objects
|
||||
git-count-objects
|
||||
git-cvsexportcommit
|
||||
git-cvsimport
|
||||
@@ -172,3 +171,6 @@ config.status
|
||||
config.mak.autogen
|
||||
config.mak.append
|
||||
configure
|
||||
tags
|
||||
TAGS
|
||||
cscope*
|
||||
|
||||
58
Documentation/RelNotes-1.5.3.5.txt
Normal file
58
Documentation/RelNotes-1.5.3.5.txt
Normal file
@@ -0,0 +1,58 @@
|
||||
GIT v1.5.3.5 Release Notes
|
||||
==========================
|
||||
|
||||
Fixes since v1.5.3.4
|
||||
--------------------
|
||||
|
||||
* "git-config" silently ignored options after --list; now it will
|
||||
error out with a usage message.
|
||||
|
||||
* "git-config --file" failed if the argument used a relative path
|
||||
as it changed directories before opening the file.
|
||||
|
||||
* "git-config --file" now displays a proper error message if it
|
||||
cannot read the file specified on the command line.
|
||||
|
||||
* "git-config", "git-diff", "git-apply" failed if run from a
|
||||
subdirectory with relative GIT_DIR and GIT_WORK_TREE set.
|
||||
|
||||
* "git-blame" crashed if run during a merge conflict.
|
||||
|
||||
* "git-add -i" did not handle single line hunks correctly.
|
||||
|
||||
* "git-rebase -i" and "git-stash apply" failed if external diff
|
||||
drivers were used for one or more files in a commit. They now
|
||||
avoid calling the external diff drivers.
|
||||
|
||||
* "git-log --follow" did not work unless diff generation (e.g. -p)
|
||||
was also requested.
|
||||
|
||||
* "git-log" printed extra newlines between commits when a diff
|
||||
was generated internally (e.g. -S or --follow) but not displayed.
|
||||
|
||||
* "git-push" error message is more helpful when pushing to a
|
||||
repository with no matching refs and none specified.
|
||||
|
||||
* "git-filter-branch" now updates the working directory when it
|
||||
has finished filtering the current branch.
|
||||
|
||||
* "git-instaweb" no longer fails on Mac OS X.
|
||||
|
||||
* "git-cvsexportcommit" didn't always create new parent directories
|
||||
before trying to create new child directories. Fixed.
|
||||
|
||||
* "git-fetch" printed a scary (but bogus) error message while
|
||||
fetching a tag that pointed to a tree or blob. The error did
|
||||
not impact correctness, only user perception. The bogus error
|
||||
is no longer printed.
|
||||
|
||||
* Git segfaulted when reading an invalid .gitattributes file. Fixed.
|
||||
|
||||
* post-receive-email example hook fixed was fixed for
|
||||
non-fast-forward updates.
|
||||
|
||||
* Documentation updates for supported (but previously undocumented)
|
||||
options of "git-archive" and "git-reflog".
|
||||
|
||||
* "make clean" no longer deletes the configure script that ships
|
||||
with the git tarball, making multiple architecture builds easier.
|
||||
35
Documentation/RelNotes-1.5.4.txt
Normal file
35
Documentation/RelNotes-1.5.4.txt
Normal file
@@ -0,0 +1,35 @@
|
||||
GIT v1.5.4 Release Notes
|
||||
========================
|
||||
|
||||
Updates since v1.5.3
|
||||
--------------------
|
||||
|
||||
* git-reset is now built-in.
|
||||
|
||||
* git-send-email can optionally talk over ssmtp and use SMTP-AUTH.
|
||||
|
||||
* git-rebase learned --whitespace option.
|
||||
|
||||
* git-remote knows --mirror mode.
|
||||
|
||||
* git-merge can call the "post-merge" hook.
|
||||
|
||||
* git-pack-objects can optionally run deltification with multiple threads.
|
||||
|
||||
* git-archive can optionally substitute keywords in files marked with
|
||||
export-subst attribute.
|
||||
|
||||
* Various Perforce importer updates.
|
||||
|
||||
Fixes since v1.5.3
|
||||
------------------
|
||||
|
||||
All of the fixes in v1.5.3 maintenance series are included in
|
||||
this release, unless otherwise noted.
|
||||
|
||||
--
|
||||
exec >/var/tmp/1
|
||||
O=v1.5.3.2-99-ge4b2890
|
||||
echo O=`git describe refs/heads/master`
|
||||
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
|
||||
|
||||
@@ -94,7 +94,6 @@ git-clone mainporcelain
|
||||
git-commit mainporcelain
|
||||
git-commit-tree plumbingmanipulators
|
||||
git-config ancillarymanipulators
|
||||
git-convert-objects ancillarymanipulators
|
||||
git-count-objects ancillaryinterrogators
|
||||
git-cvsexportcommit foreignscminterface
|
||||
git-cvsimport foreignscminterface
|
||||
|
||||
@@ -188,7 +188,7 @@ core.worktree::
|
||||
Set the path to the working tree. The value will not be
|
||||
used in combination with repositories found automatically in
|
||||
a .git directory (i.e. $GIT_DIR is not set).
|
||||
This can be overriden by the GIT_WORK_TREE environment
|
||||
This can be overridden by the GIT_WORK_TREE environment
|
||||
variable and the '--work-tree' command line option.
|
||||
|
||||
core.logAllRefUpdates::
|
||||
@@ -337,6 +337,12 @@ branch.<name>.merge::
|
||||
branch.<name>.merge to the desired branch, and use the special setting
|
||||
`.` (a period) for branch.<name>.remote.
|
||||
|
||||
branch.<name>.mergeoptions::
|
||||
Sets default options for merging into branch <name>. The syntax and
|
||||
supported options are equal to that of gitlink:git-merge[1], but
|
||||
option values containing whitespace characters are currently not
|
||||
supported.
|
||||
|
||||
clean.requireForce::
|
||||
A boolean to make git-clean do nothing unless given -f or -n. Defaults
|
||||
to false.
|
||||
@@ -439,6 +445,19 @@ gc.aggressiveWindow::
|
||||
algorithm used by 'git gc --aggressive'. This defaults
|
||||
to 10.
|
||||
|
||||
gc.auto::
|
||||
When there are approximately more than this many loose
|
||||
objects in the repository, `git gc --auto` will pack them.
|
||||
Some Porcelain commands use this command to perform a
|
||||
light-weight garbage collection from time to time. Setting
|
||||
this to 0 disables it.
|
||||
|
||||
gc.autopacklimit::
|
||||
When there are more than this many packs that are not
|
||||
marked with `*.keep` file in the repository, `git gc
|
||||
--auto` consolidates them into one larger pack. Setting
|
||||
this to 0 disables this.
|
||||
|
||||
gc.packrefs::
|
||||
`git gc` does not run `git pack-refs` in a bare repository by
|
||||
default so that older dumb-transport clients can still fetch
|
||||
@@ -588,7 +607,7 @@ merge.verbosity::
|
||||
message if conflicts were detected. Level 1 outputs only
|
||||
conflicts, 2 outputs conflicts and file changes. Level 5 and
|
||||
above outputs debugging information. The default is level 2.
|
||||
Can be overriden by 'GIT_MERGE_VERBOSITY' environment variable.
|
||||
Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable.
|
||||
|
||||
merge.<driver>.name::
|
||||
Defines a human readable name for a custom low-level
|
||||
@@ -630,9 +649,17 @@ pack.deltaCacheSize::
|
||||
A value of 0 means no limit. Defaults to 0.
|
||||
|
||||
pack.deltaCacheLimit::
|
||||
The maxium size of a delta, that is cached in
|
||||
The maximum size of a delta, that is cached in
|
||||
gitlink:git-pack-objects[1]. Defaults to 1000.
|
||||
|
||||
pack.threads::
|
||||
Specifies the number of threads to spawn when searching for best
|
||||
delta matches. This requires that gitlink:git-pack-objects[1]
|
||||
be compiled with pthreads otherwise this option is ignored with a
|
||||
warning. This is meant to reduce packing time on multiprocessor
|
||||
machines. The required amount of memory for the delta search window
|
||||
is however multiplied by the number of threads.
|
||||
|
||||
pull.octopus::
|
||||
The default merge strategy to use when pulling multiple branches
|
||||
at once.
|
||||
|
||||
@@ -10,7 +10,8 @@ SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>]
|
||||
[--remote=<repo>] <tree-ish> [path...]
|
||||
[--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish>
|
||||
[path...]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -52,6 +53,10 @@ OPTIONS
|
||||
Instead of making a tar archive from local repository,
|
||||
retrieve a tar archive from a remote repository.
|
||||
|
||||
--exec=<git-upload-archive>::
|
||||
Used with --remote to specify the path to the
|
||||
git-upload-archive executable on the remote side.
|
||||
|
||||
<tree-ish>::
|
||||
The tree or commit to produce an archive for.
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ $ git diff topic...master <3>
|
||||
+
|
||||
<1> Changes between the tips of the topic and the master branches.
|
||||
<2> Same as above.
|
||||
<3> Changes that occured on the master branch since when the topic
|
||||
<3> Changes that occurred on the master branch since when the topic
|
||||
branch was started off it.
|
||||
|
||||
Limiting the diff output::
|
||||
|
||||
@@ -180,8 +180,7 @@ A significantly faster version:
|
||||
git filter-branch --index-filter 'git update-index --remove filename' HEAD
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
Now, you will get the rewritten history saved in the branch 'newbranch'
|
||||
(your current branch is left untouched).
|
||||
Now, you will get the rewritten history saved in HEAD.
|
||||
|
||||
To set a commit (which typically is at the tip of another
|
||||
history) to be the parent of the current initial commit, in
|
||||
|
||||
@@ -100,6 +100,11 @@ In any case, a field name that refers to a field inapplicable to
|
||||
the object referred by the ref does not cause an error. It
|
||||
returns an empty string instead.
|
||||
|
||||
As a special case for the date-type fields, you may specify a format for
|
||||
the date by adding one of `:default`, `:relative`, `:short`, `:local`,
|
||||
`:iso8601` or `:rfc2822` to the end of the fieldname; e.g.
|
||||
`%(taggerdate:relative)`.
|
||||
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
|
||||
@@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-gc' [--prune] [--aggressive]
|
||||
'git-gc' [--prune] [--aggressive] [--auto]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -19,7 +19,8 @@ created from prior invocations of gitlink:git-add[1].
|
||||
|
||||
Users are encouraged to run this task on a regular basis within
|
||||
each repository to maintain good disk space utilization and good
|
||||
operating performance.
|
||||
operating performance. Some git commands may automatically run
|
||||
`git-gc`; see the `--auto` flag below for details.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
@@ -43,6 +44,25 @@ OPTIONS
|
||||
persistent, so this option only needs to be used occasionally; every
|
||||
few hundred changesets or so.
|
||||
|
||||
--auto::
|
||||
With this option, `git gc` checks whether any housekeeping is
|
||||
required; if not, it exits without performing any work.
|
||||
Some git commands run `git gc --auto` after performing
|
||||
operations that could create many loose objects.
|
||||
+
|
||||
Housekeeping is required if there are too many loose objects or
|
||||
too many packs in the repository. If the number of loose objects
|
||||
exceeds the value of the `gc.auto` configuration variable, then
|
||||
all loose objects are combined into a single pack using
|
||||
`git-repack -d -l`. Setting the value of `gc.auto` to 0
|
||||
disables automatic packing of loose objects.
|
||||
+
|
||||
If the number of packs exceeds the value of `gc.autopacklimit`,
|
||||
then existing packs (except those marked with a `.keep` file)
|
||||
are consolidated into a single pack by using the `-A` option of
|
||||
`git-repack`. Setting `gc.autopacklimit` to 0 disables
|
||||
automatic consolidation of packs.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ OPTIONS
|
||||
a default name determined from the pack content. If
|
||||
<pack-file> is not specified consider using --keep to
|
||||
prevent a race condition between this process and
|
||||
gitlink::git-repack[1] .
|
||||
gitlink::git-repack[1].
|
||||
|
||||
--fix-thin::
|
||||
It is possible for gitlink:git-pack-objects[1] to build
|
||||
|
||||
@@ -27,7 +27,7 @@ OPTIONS
|
||||
The HTTP daemon command-line that will be executed.
|
||||
Command-line options may be specified here, and the
|
||||
configuration file will be added at the end of the command-line.
|
||||
Currently, lighttpd and apache2 are the only supported servers.
|
||||
Currently lighttpd, apache2 and webrick are supported.
|
||||
(Default: lighttpd)
|
||||
|
||||
-m|--module-path::
|
||||
|
||||
@@ -40,7 +40,7 @@ If "git-merge-index" is called with multiple <file>s (or -a) then it
|
||||
processes them in turn only stopping if merge returns a non-zero exit
|
||||
code.
|
||||
|
||||
Typically this is run with the a script calling git's imitation of
|
||||
Typically this is run with a script calling git's imitation of
|
||||
the merge command from the RCS package.
|
||||
|
||||
A sample script called "git-merge-one-file" is included in the
|
||||
|
||||
@@ -58,6 +58,10 @@ merge.verbosity::
|
||||
above outputs debugging information. The default is level 2.
|
||||
Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable.
|
||||
|
||||
branch.<name>.mergeoptions::
|
||||
Sets default options for merging into branch <name>. The syntax and
|
||||
supported options are equal to that of git-merge, but option values
|
||||
containing whitespace characters are currently not supported.
|
||||
|
||||
HOW MERGE WORKS
|
||||
---------------
|
||||
|
||||
@@ -169,6 +169,14 @@ base-name::
|
||||
length, this option typically shrinks the resulting
|
||||
packfile by 3-5 per-cent.
|
||||
|
||||
--threads=<n>::
|
||||
Specifies the number of threads to spawn when searching for best
|
||||
delta matches. This requires that pack-objects be compiled with
|
||||
pthreads otherwise this option is ignored with a warning.
|
||||
This is meant to reduce packing time on multiprocessor machines.
|
||||
The required amount of memory for the delta search window is
|
||||
however multiplied by the number of threads.
|
||||
|
||||
--index-version=<version>[,<offset>]::
|
||||
This is intended to be used by the test suite only. It allows
|
||||
to force the version for the generated pack index, and to force
|
||||
|
||||
@@ -9,7 +9,7 @@ git-push - Update remote refs along with associated objects
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-push' [--all] [--tags] [--receive-pack=<git-receive-pack>]
|
||||
'git-push' [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>]
|
||||
[--repo=all] [-f | --force] [-v] [<repository> <refspec>...]
|
||||
|
||||
DESCRIPTION
|
||||
@@ -63,6 +63,9 @@ the remote repository.
|
||||
Instead of naming each ref to push, specifies that all
|
||||
refs under `$GIT_DIR/refs/heads/` be pushed.
|
||||
|
||||
\--dry-run::
|
||||
Do everything except actually send the updates.
|
||||
|
||||
\--tags::
|
||||
All refs under `$GIT_DIR/refs/tags` are pushed, in
|
||||
addition to refspecs explicitly listed on the command
|
||||
|
||||
@@ -8,8 +8,9 @@ git-rebase - Forward-port local commits to the updated upstream head
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge] [-C<n>]
|
||||
[-p | --preserve-merges] [--onto <newbase>] <upstream> [<branch>]
|
||||
'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge]
|
||||
[-C<n>] [ --whitespace=<option>] [-p | --preserve-merges]
|
||||
[--onto <newbase>] <upstream> [<branch>]
|
||||
'git-rebase' --continue | --skip | --abort
|
||||
|
||||
DESCRIPTION
|
||||
@@ -27,7 +28,10 @@ The current branch is reset to <upstream>, or <newbase> if the
|
||||
`git reset --hard <upstream>` (or <newbase>).
|
||||
|
||||
The commits that were previously saved into the temporary area are
|
||||
then reapplied to the current branch, one by one, in order.
|
||||
then reapplied to the current branch, one by one, in order. Note that
|
||||
any commits in HEAD which introduce the same textual changes as a commit
|
||||
in HEAD..<upstream> are omitted (i.e., a patch already accepted upstream
|
||||
with a different commit message or timestamp will be skipped).
|
||||
|
||||
It is possible that a merge failure will prevent this process from being
|
||||
completely automatic. You will have to resolve any such merge failure
|
||||
@@ -61,6 +65,26 @@ would be:
|
||||
The latter form is just a short-hand of `git checkout topic`
|
||||
followed by `git rebase master`.
|
||||
|
||||
If the upstream branch already contains a change you have made (e.g.,
|
||||
because you mailed a patch which was applied upstream), then that commit
|
||||
will be skipped. For example, running `git-rebase master` on the
|
||||
following history (in which A' and A introduce the same set of changes,
|
||||
but have different committer information):
|
||||
|
||||
------------
|
||||
A---B---C topic
|
||||
/
|
||||
D---E---A'---F master
|
||||
------------
|
||||
|
||||
will result in:
|
||||
|
||||
------------
|
||||
B'---C' topic
|
||||
/
|
||||
D---E---A'---F master
|
||||
------------
|
||||
|
||||
Here is how you would transplant a topic branch based on one
|
||||
branch to another, to pretend that you forked the topic branch
|
||||
from the latter branch, using `rebase --onto`.
|
||||
@@ -209,6 +233,10 @@ OPTIONS
|
||||
context exist they all must match. By default no context is
|
||||
ever ignored.
|
||||
|
||||
--whitespace=<nowarn|warn|error|error-all|strip>::
|
||||
This flag is passed to the `git-apply` program
|
||||
(see gitlink:git-apply[1]) that applies the patch.
|
||||
|
||||
-i, \--interactive::
|
||||
Make a list of the commits which are about to be rebased. Let the
|
||||
user edit that list before rebasing. This mode can also be used to
|
||||
|
||||
@@ -16,7 +16,7 @@ The command takes various subcommands, and different options
|
||||
depending on the subcommand:
|
||||
|
||||
[verse]
|
||||
git reflog expire [--dry-run] [--stale-fix]
|
||||
git reflog expire [--dry-run] [--stale-fix] [--verbose]
|
||||
[--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...
|
||||
|
||||
git reflog [show] [log-options]
|
||||
@@ -68,6 +68,9 @@ them.
|
||||
--all::
|
||||
Instead of listing <refs> explicitly, prune all refs.
|
||||
|
||||
--verbose::
|
||||
Print extra information on screen.
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Junio C Hamano <junkio@cox.net>
|
||||
|
||||
@@ -10,7 +10,8 @@ SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-remote'
|
||||
'git-remote' add [-t <branch>] [-m <branch>] [-f] <name> <url>
|
||||
'git-remote' add [-t <branch>] [-m <branch>] [-f] [--mirror] <name> <url>
|
||||
'git-remote' rm <name>
|
||||
'git-remote' show <name>
|
||||
'git-remote' prune <name>
|
||||
'git-remote' update [group]
|
||||
@@ -45,6 +46,15 @@ multiple branches without grabbing all branches.
|
||||
With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set
|
||||
up to point at remote's `<master>` branch instead of whatever
|
||||
branch the `HEAD` at the remote repository actually points at.
|
||||
+
|
||||
In mirror mode, enabled with `--mirror`, the refs will not be stored
|
||||
in the 'refs/remotes/' namespace, but in 'refs/heads/'. This option
|
||||
only makes sense in bare repositories.
|
||||
|
||||
'rm'::
|
||||
|
||||
Remove the remote named <name>. All remote tracking branches and
|
||||
configuration settings for the remote are removed.
|
||||
|
||||
'show'::
|
||||
|
||||
|
||||
@@ -75,6 +75,12 @@ The --cc option must be repeated for each user you want on the cc list.
|
||||
Make git-send-email less verbose. One line per email should be
|
||||
all that is output.
|
||||
|
||||
--identity::
|
||||
A configuration identity. When given, causes values in the
|
||||
'sendemail.<identity>' subsection to take precedence over
|
||||
values in the 'sendemail' section. The default identity is
|
||||
the value of 'sendemail.identity'.
|
||||
|
||||
--smtp-server::
|
||||
If set, specifies the outgoing SMTP server to use (e.g.
|
||||
`smtp.example.com` or a raw IP address). Alternatively it can
|
||||
@@ -85,6 +91,22 @@ The --cc option must be repeated for each user you want on the cc list.
|
||||
`/usr/lib/sendmail` if such program is available, or
|
||||
`localhost` otherwise.
|
||||
|
||||
--smtp-server-port::
|
||||
Specifies a port different from the default port (SMTP
|
||||
servers typically listen to smtp port 25 and ssmtp port
|
||||
465).
|
||||
|
||||
--smtp-user, --smtp-pass::
|
||||
Username and password for SMTP-AUTH. Defaults are the values of
|
||||
the configuration values 'sendemail.smtpuser' and
|
||||
'sendemail.smtppass', but see also 'sendemail.identity'.
|
||||
If not set, authentication is not attempted.
|
||||
|
||||
--smtp-ssl::
|
||||
If set, connects to the SMTP server using SSL.
|
||||
Default is the value of the 'sendemail.smtpssl' configuration value;
|
||||
if that is unspecified, does not use SSL.
|
||||
|
||||
--subject::
|
||||
Specify the initial subject of the email thread.
|
||||
Only necessary if --compose is also set. If --compose
|
||||
@@ -122,6 +144,13 @@ The --to option must be repeated for each user you want on the to list.
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
sendemail.identity::
|
||||
The default configuration identity. When specified,
|
||||
'sendemail.<identity>.<item>' will have higher precedence than
|
||||
'sendemail.<item>'. This is useful to declare multiple SMTP
|
||||
identities and to hoist sensitive authentication information
|
||||
out of the repository and into the global configuation file.
|
||||
|
||||
sendemail.aliasesfile::
|
||||
To avoid typing long email addresses, point this to one or more
|
||||
email aliases files. You must also supply 'sendemail.aliasfiletype'.
|
||||
@@ -141,7 +170,16 @@ sendemail.chainreplyto::
|
||||
parameter.
|
||||
|
||||
sendemail.smtpserver::
|
||||
Default smtp server to use.
|
||||
Default SMTP server to use.
|
||||
|
||||
sendemail.smtpuser::
|
||||
Default SMTP-AUTH username.
|
||||
|
||||
sendemail.smtppass::
|
||||
Default SMTP-AUTH password.
|
||||
|
||||
sendemail.smtpssl::
|
||||
Boolean value specifying the default to the '--smtp-ssl' parameter.
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
@@ -8,7 +8,7 @@ git-send-pack - Push objects over git protocol to another repository
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-send-pack' [--all] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]
|
||||
'git-send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -34,6 +34,9 @@ OPTIONS
|
||||
Instead of explicitly specifying which refs to update,
|
||||
update all heads that locally exist.
|
||||
|
||||
\--dry-run::
|
||||
Do everything except actually send the updates.
|
||||
|
||||
\--force::
|
||||
Usually, the command refuses to update a remote ref that
|
||||
is not an ancestor of the local ref used to overwrite it.
|
||||
|
||||
@@ -57,7 +57,7 @@ stash@{1}: On master: 9cc0589... Add git-stash
|
||||
|
||||
show [<stash>]::
|
||||
|
||||
Show the changes recorded in the stash as a diff between the the
|
||||
Show the changes recorded in the stash as a diff between the
|
||||
stashed state and its original parent. When no `<stash>` is given,
|
||||
shows the latest one. By default, the command shows the diffstat, but
|
||||
it will accept any format known to `git-diff` (e.g., `git-stash show
|
||||
|
||||
@@ -21,6 +21,9 @@ add::
|
||||
repository is cloned at the specified path, added to the
|
||||
changeset and registered in .gitmodules. If no path is
|
||||
specified, the path is deduced from the repository specification.
|
||||
If the repository url begins with ./ or ../, it is stored as
|
||||
given but resolved as a relative path from the main project's
|
||||
url when cloning.
|
||||
|
||||
status::
|
||||
Show the status of the submodules. This will print the SHA-1 of the
|
||||
|
||||
@@ -404,7 +404,7 @@ section because they affect the 'git-svn-id:' metadata line.
|
||||
BASIC EXAMPLES
|
||||
--------------
|
||||
|
||||
Tracking and contributing to a the trunk of a Subversion-managed project:
|
||||
Tracking and contributing to the trunk of a Subversion-managed project:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
# Clone a repo (like git clone):
|
||||
|
||||
@@ -112,7 +112,7 @@ You really want to call the new version "X" too, 'even though'
|
||||
others have already seen the old one. So just use "git tag -f"
|
||||
again, as if you hadn't already published the old one.
|
||||
|
||||
However, Git does *not* (and it should not)change tags behind
|
||||
However, Git does *not* (and it should not) change tags behind
|
||||
users back. So if somebody already got the old tag, doing a "git
|
||||
pull" on your tree shouldn't just make them overwrite the old
|
||||
one.
|
||||
@@ -214,6 +214,27 @@ having tracking branches. Again, the heuristic to automatically
|
||||
follow such tags is a good thing.
|
||||
|
||||
|
||||
On Backdating Tags
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you have imported some changes from another VCS and would like
|
||||
to add tags for major releases of your work, it is useful to be able
|
||||
to specify the date to embed inside of the tag object. The data in
|
||||
the tag object affects, for example, the ordering of tags in the
|
||||
gitweb interface.
|
||||
|
||||
To set the date used in future tag objects, set the environment
|
||||
variable GIT_AUTHOR_DATE to one or more of the date and time. The
|
||||
date and time can be specified in a number of ways; the most common
|
||||
is "YYYY-MM-DD HH:MM".
|
||||
|
||||
An example follows.
|
||||
|
||||
------------
|
||||
$ GIT_AUTHOR_DATE="2006-10-02 10:31" git tag -s v1.0.1
|
||||
------------
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Linus Torvalds <torvalds@osdl.org>,
|
||||
|
||||
@@ -46,6 +46,9 @@ Documentation for older releases are available here:
|
||||
* link:v1.5.3/git.html[documentation for release 1.5.3]
|
||||
|
||||
* release notes for
|
||||
link:RelNotes-1.5.3.4.txt[1.5.3.4],
|
||||
link:RelNotes-1.5.3.3.txt[1.5.3.3],
|
||||
link:RelNotes-1.5.3.2.txt[1.5.3.2],
|
||||
link:RelNotes-1.5.3.1.txt[1.5.3.1].
|
||||
|
||||
* release notes for
|
||||
@@ -323,7 +326,7 @@ For a more complete list of ways to spell object names, see
|
||||
File/Directory Structure
|
||||
------------------------
|
||||
|
||||
Please see link:repository-layout.html[repository layout] document.
|
||||
Please see the link:repository-layout.html[repository layout] document.
|
||||
|
||||
Read link:hooks.html[hooks] for more details about each hook.
|
||||
|
||||
@@ -333,7 +336,7 @@ Higher level SCMs may provide and manage additional information in the
|
||||
|
||||
Terminology
|
||||
-----------
|
||||
Please see link:glossary.html[glossary] document.
|
||||
Please see the link:glossary.html[glossary] document.
|
||||
|
||||
|
||||
Environment Variables
|
||||
|
||||
@@ -409,6 +409,23 @@ frotz unspecified
|
||||
----------------------------------------------------------------
|
||||
|
||||
|
||||
Creating an archive
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
`export-subst`
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
If the attribute `export-subst` is set for a file then git will expand
|
||||
several placeholders when adding this file to an archive. The
|
||||
expansion depends on the availability of a commit ID, i.e. if
|
||||
gitlink:git-archive[1] has been given a tree instead of a commit or a
|
||||
tag then no replacement will be done. The placeholders are the same
|
||||
as those for the option `--pretty=format:` of gitlink:git-log[1],
|
||||
except that they need to be wrapped like this: `$Format:PLACEHOLDERS$`
|
||||
in the file. E.g. the string `$Format:%H$` will be replaced by the
|
||||
commit hash.
|
||||
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the gitlink:git[7] suite
|
||||
|
||||
@@ -52,8 +52,8 @@ GIT Glossary
|
||||
[[def_cherry-picking]]cherry-picking::
|
||||
In <<def_SCM,SCM>> jargon, "cherry pick" means to choose a subset of
|
||||
changes out of a series of changes (typically commits) and record them
|
||||
as a new series of changes on top of different codebase. In GIT, this is
|
||||
performed by "git cherry-pick" command to extract the change introduced
|
||||
as a new series of changes on top of a different codebase. In GIT, this is
|
||||
performed by the "git cherry-pick" command to extract the change introduced
|
||||
by an existing <<def_commit,commit>> and to record it based on the tip
|
||||
of the current <<def_branch,branch>> as a new commit.
|
||||
|
||||
@@ -281,7 +281,7 @@ This commit is referred to as a "merge commit", or sometimes just a
|
||||
[[def_pickaxe]]pickaxe::
|
||||
The term <<def_pickaxe,pickaxe>> refers to an option to the diffcore
|
||||
routines that help select changes that add or delete a given text
|
||||
string. With the --pickaxe-all option, it can be used to view the full
|
||||
string. With the `--pickaxe-all` option, it can be used to view the full
|
||||
<<def_changeset,changeset>> that introduced or removed, say, a
|
||||
particular line of text. See gitlink:git-diff[1].
|
||||
|
||||
@@ -301,8 +301,8 @@ This commit is referred to as a "merge commit", or sometimes just a
|
||||
[[def_push]]push::
|
||||
Pushing a <<def_branch,branch>> means to get the branch's
|
||||
<<def_head_ref,head ref>> from a remote <<def_repository,repository>>,
|
||||
find out if it is an ancestor to the branch's local
|
||||
head ref is a direct, and in that case, putting all
|
||||
find out if it is a direct ancestor to the branch's local
|
||||
head ref, and in that case, putting all
|
||||
objects, which are <<def_reachable,reachable>> from the local
|
||||
head ref, and which are missing from the remote
|
||||
repository, into the remote
|
||||
@@ -347,7 +347,7 @@ This commit is referred to as a "merge commit", or sometimes just a
|
||||
it as my origin branch head". And `git push
|
||||
$URL refs/heads/master:refs/heads/to-upstream` means "publish my
|
||||
master branch head as to-upstream branch at $URL". See also
|
||||
gitlink:git-push[1]
|
||||
gitlink:git-push[1].
|
||||
|
||||
[[def_repository]]repository::
|
||||
A collection of <<def_ref,refs>> together with an
|
||||
|
||||
@@ -87,6 +87,33 @@ parameter, and is invoked after a commit is made.
|
||||
This hook is meant primarily for notification, and cannot affect
|
||||
the outcome of `git-commit`.
|
||||
|
||||
post-checkout
|
||||
-----------
|
||||
|
||||
This hook is invoked when a `git-checkout` is run after having updated the
|
||||
worktree. The hook is given three parameters: the ref of the previous HEAD,
|
||||
the ref of the new HEAD (which may or may not have changed), and a flag
|
||||
indicating whether the checkout was a branch checkout (changing branches,
|
||||
flag=1) or a file checkout (retrieving a file from the index, flag=0).
|
||||
This hook cannot affect the outcome of `git-checkout`.
|
||||
|
||||
This hook can be used to perform repository validity checks, auto-display
|
||||
differences from the previous HEAD if different, or set working dir metadata
|
||||
properties.
|
||||
|
||||
post-merge
|
||||
-----------
|
||||
|
||||
This hook is invoked by `git-merge`, which happens when a `git pull`
|
||||
is done on a local repository. The hook takes a single parameter, a status
|
||||
flag specifying whether or not the merge being done was a squash merge.
|
||||
This hook cannot affect the outcome of `git-merge`.
|
||||
|
||||
This hook can be used in conjunction with a corresponding pre-commit hook to
|
||||
save and restore any form of metadata associated with the working tree
|
||||
(eg: permissions/ownership, ACLS, etc). See contrib/hooks/setgitperms.perl
|
||||
for an example of how to do this.
|
||||
|
||||
[[pre-receive]]
|
||||
pre-receive
|
||||
-----------
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
not autocommit, to give the user a chance to inspect and
|
||||
further tweak the merge result before committing.
|
||||
|
||||
--commit::
|
||||
Perform the merge and commit the result. This option can
|
||||
be used to override --no-commit.
|
||||
|
||||
--squash::
|
||||
Produce the working tree and index state as if a real
|
||||
merge happened, but do not actually make a commit or
|
||||
@@ -19,6 +23,19 @@
|
||||
top of the current branch whose effect is the same as
|
||||
merging another branch (or more in case of an octopus).
|
||||
|
||||
--no-squash::
|
||||
Perform the merge and commit the result. This option can
|
||||
be used to override --squash.
|
||||
|
||||
--no-ff::
|
||||
Generate a merge commit even if the merge resolved as a
|
||||
fast-forward.
|
||||
|
||||
--ff::
|
||||
Do not generate a merge commit if the merge resolved as
|
||||
a fast-forward, only update the branch pointer. This is
|
||||
the default behavior of git-merge.
|
||||
|
||||
-s <strategy>, \--strategy=<strategy>::
|
||||
Use the given merge strategy; can be supplied more than
|
||||
once to specify them in the order they should be tried.
|
||||
|
||||
@@ -926,7 +926,7 @@ file such that it contained the given content either before or after the
|
||||
commit. You can find out with this:
|
||||
|
||||
-------------------------------------------------
|
||||
$ git log --raw --abbrev=40 --pretty=oneline -- filename |
|
||||
$ git log --raw --abbrev=40 --pretty=oneline |
|
||||
grep -B 1 `git hash-object filename`
|
||||
-------------------------------------------------
|
||||
|
||||
@@ -1495,7 +1495,7 @@ Ensuring good performance
|
||||
-------------------------
|
||||
|
||||
On large repositories, git depends on compression to keep the history
|
||||
information from taking up to much space on disk or in memory.
|
||||
information from taking up too much space on disk or in memory.
|
||||
|
||||
This compression is not performed automatically. Therefore you
|
||||
should occasionally run gitlink:git-gc[1]:
|
||||
@@ -1536,7 +1536,7 @@ dangling tree b24c2473f1fd3d91352a624795be026d64c8841f
|
||||
Dangling objects are not a problem. At worst they may take up a little
|
||||
extra disk space. They can sometimes provide a last-resort method for
|
||||
recovering lost work--see <<dangling-objects>> for details. However, if
|
||||
you wish, you can remove them with gitlink:git-prune[1] or the --prune
|
||||
you wish, you can remove them with gitlink:git-prune[1] or the `--prune`
|
||||
option to gitlink:git-gc[1]:
|
||||
|
||||
-------------------------------------------------
|
||||
@@ -1555,7 +1555,7 @@ Recovering lost changes
|
||||
Reflogs
|
||||
^^^^^^^
|
||||
|
||||
Say you modify a branch with gitlink:git-reset[1] --hard, and then
|
||||
Say you modify a branch with `gitlink:git-reset[1] --hard`, and then
|
||||
realize that the branch was the only reference you had to that point in
|
||||
history.
|
||||
|
||||
@@ -1684,7 +1684,7 @@ $ git pull
|
||||
More generally, a branch that is created from a remote branch will pull
|
||||
by default from that branch. See the descriptions of the
|
||||
branch.<name>.remote and branch.<name>.merge options in
|
||||
gitlink:git-config[1], and the discussion of the --track option in
|
||||
gitlink:git-config[1], and the discussion of the `--track` option in
|
||||
gitlink:git-checkout[1], to learn how to control these defaults.
|
||||
|
||||
In addition to saving you keystrokes, "git pull" also helps you by
|
||||
@@ -1782,7 +1782,7 @@ $ git clone /path/to/repository
|
||||
$ git pull /path/to/other/repository
|
||||
-------------------------------------------------
|
||||
|
||||
or an ssh url:
|
||||
or an ssh URL:
|
||||
|
||||
-------------------------------------------------
|
||||
$ git clone ssh://yourhost/~you/repository
|
||||
@@ -1843,7 +1843,7 @@ Exporting a git repository via the git protocol
|
||||
This is the preferred method.
|
||||
|
||||
If someone else administers the server, they should tell you what
|
||||
directory to put the repository in, and what git:// url it will appear
|
||||
directory to put the repository in, and what git:// URL it will appear
|
||||
at. You can then skip to the section
|
||||
"<<pushing-changes-to-a-public-repository,Pushing changes to a public
|
||||
repository>>", below.
|
||||
@@ -1880,8 +1880,8 @@ $ chmod a+x hooks/post-update
|
||||
gitlink:git-update-server-info[1], and the documentation
|
||||
link:hooks.html[Hooks used by git].)
|
||||
|
||||
Advertise the url of proj.git. Anybody else should then be able to
|
||||
clone or pull from that url, for example with a command line like:
|
||||
Advertise the URL of proj.git. Anybody else should then be able to
|
||||
clone or pull from that URL, for example with a command line like:
|
||||
|
||||
-------------------------------------------------
|
||||
$ git clone http://yourserver.com/~you/proj.git
|
||||
@@ -1920,7 +1920,7 @@ As with git-fetch, git-push will complain if this does not result in
|
||||
a <<fast-forwards,fast forward>>. Normally this is a sign of
|
||||
something wrong. However, if you are sure you know what you're
|
||||
doing, you may force git-push to perform the update anyway by
|
||||
proceeding the branch name by a plus sign:
|
||||
preceding the branch name by a plus sign:
|
||||
|
||||
-------------------------------------------------
|
||||
$ git push ssh://yourserver.com/~you/proj.git +master
|
||||
@@ -2040,7 +2040,7 @@ $ git branch --track test origin/master
|
||||
$ git branch --track release origin/master
|
||||
-------------------------------------------------
|
||||
|
||||
These can be easily kept up to date using gitlink:git-pull[1]
|
||||
These can be easily kept up to date using gitlink:git-pull[1].
|
||||
|
||||
-------------------------------------------------
|
||||
$ git checkout test && git pull
|
||||
@@ -2132,7 +2132,7 @@ changes are in a specific branch, use:
|
||||
$ git log linux..branchname | git-shortlog
|
||||
-------------------------------------------------
|
||||
|
||||
To see whether it has already been merged into the test or release branches
|
||||
To see whether it has already been merged into the test or release branches,
|
||||
use:
|
||||
|
||||
-------------------------------------------------
|
||||
@@ -2145,12 +2145,12 @@ or
|
||||
$ git log release..branchname
|
||||
-------------------------------------------------
|
||||
|
||||
(If this branch has not yet been merged you will see some log entries.
|
||||
(If this branch has not yet been merged, you will see some log entries.
|
||||
If it has been merged, then there will be no output.)
|
||||
|
||||
Once a patch completes the great cycle (moving from test to release,
|
||||
then pulled by Linus, and finally coming back into your local
|
||||
"origin/master" branch) the branch for this change is no longer needed.
|
||||
"origin/master" branch), the branch for this change is no longer needed.
|
||||
You detect this when the output from:
|
||||
|
||||
-------------------------------------------------
|
||||
@@ -2412,7 +2412,7 @@ $ git rebase --continue
|
||||
|
||||
and git will continue applying the rest of the patches.
|
||||
|
||||
At any point you may use the --abort option to abort this process and
|
||||
At any point you may use the `--abort` option to abort this process and
|
||||
return mywork to the state it had before you started the rebase:
|
||||
|
||||
-------------------------------------------------
|
||||
@@ -2479,9 +2479,9 @@ $ git checkout -b mywork-new origin
|
||||
$ gitk origin..mywork &
|
||||
-------------------------------------------------
|
||||
|
||||
And browse through the list of patches in the mywork branch using gitk,
|
||||
and browse through the list of patches in the mywork branch using gitk,
|
||||
applying them (possibly in a different order) to mywork-new using
|
||||
cherry-pick, and possibly modifying them as you go using commit --amend.
|
||||
cherry-pick, and possibly modifying them as you go using `commit --amend`.
|
||||
The gitlink:git-gui[1] command may also help as it allows you to
|
||||
individually select diff hunks for inclusion in the index (by
|
||||
right-clicking on the diff hunk and choosing "Stage Hunk for Commit").
|
||||
@@ -2739,7 +2739,7 @@ others:
|
||||
|
||||
- Git can quickly determine whether two objects are identical or not,
|
||||
just by comparing names.
|
||||
- Since object names are computed the same way in ever repository, the
|
||||
- Since object names are computed the same way in every repository, the
|
||||
same content stored in two repositories will always be stored under
|
||||
the same name.
|
||||
- Git can detect errors when it reads an object, by checking that the
|
||||
@@ -2756,7 +2756,7 @@ There are four different types of objects: "blob", "tree", "commit", and
|
||||
"blob" objects into a directory structure. In addition, a tree object
|
||||
can refer to other tree objects, thus creating a directory hierarchy.
|
||||
- A <<def_commit_object,"commit" object>> ties such directory hierarchies
|
||||
together into a <<def_DAG,directed acyclic graph>> of revisions - each
|
||||
together into a <<def_DAG,directed acyclic graph>> of revisions--each
|
||||
commit contains the object name of exactly one tree designating the
|
||||
directory hierarchy at the time of the commit. In addition, a commit
|
||||
refers to "parent" commit objects that describe the history of how we
|
||||
@@ -3029,7 +3029,7 @@ There are also other situations that cause dangling objects. For
|
||||
example, a "dangling blob" may arise because you did a "git add" of a
|
||||
file, but then, before you actually committed it and made it part of the
|
||||
bigger picture, you changed something else in that file and committed
|
||||
that *updated* thing - the old state that you added originally ends up
|
||||
that *updated* thing--the old state that you added originally ends up
|
||||
not being pointed to by any commit or tree, so it's now a dangling blob
|
||||
object.
|
||||
|
||||
@@ -3044,7 +3044,7 @@ up pointing to them, so they end up "dangling" in your repository.
|
||||
Generally, dangling objects aren't anything to worry about. They can
|
||||
even be very useful: if you screw something up, the dangling objects can
|
||||
be how you recover your old tree (say, you did a rebase, and realized
|
||||
that you really didn't want to - you can look at what dangling objects
|
||||
that you really didn't want to--you can look at what dangling objects
|
||||
you have, and decide to reset your head to some old dangling state).
|
||||
|
||||
For commits, you can just use:
|
||||
@@ -3088,10 +3088,10 @@ $ git prune
|
||||
------------------------------------------------
|
||||
|
||||
and they'll be gone. But you should only run "git prune" on a quiescent
|
||||
repository - it's kind of like doing a filesystem fsck recovery: you
|
||||
repository--it's kind of like doing a filesystem fsck recovery: you
|
||||
don't want to do that while the filesystem is mounted.
|
||||
|
||||
(The same is true of "git-fsck" itself, btw - but since
|
||||
(The same is true of "git-fsck" itself, btw, but since
|
||||
git-fsck never actually *changes* the repository, it just reports
|
||||
on what it found, git-fsck itself is never "dangerous" to run.
|
||||
Running it while somebody is actually changing the repository can cause
|
||||
@@ -3425,9 +3425,10 @@ The Workflow
|
||||
------------
|
||||
|
||||
High-level operations such as gitlink:git-commit[1],
|
||||
gitlink:git-checkout[1] and git-reset[1] work by moving data between the
|
||||
working tree, the index, and the object database. Git provides
|
||||
low-level operations which perform each of these steps individually.
|
||||
gitlink:git-checkout[1] and gitlink:git-reset[1] work by moving data
|
||||
between the working tree, the index, and the object database. Git
|
||||
provides low-level operations which perform each of these steps
|
||||
individually.
|
||||
|
||||
Generally, all "git" operations work on the index file. Some operations
|
||||
work *purely* on the index file (showing the current state of the
|
||||
@@ -3482,7 +3483,7 @@ You write your current index file to a "tree" object with the program
|
||||
$ git write-tree
|
||||
-------------------------------------------------
|
||||
|
||||
that doesn't come with any options - it will just write out the
|
||||
that doesn't come with any options--it will just write out the
|
||||
current index into the set of tree objects that describe that state,
|
||||
and it will return the name of the resulting top-level tree. You can
|
||||
use that tree to re-generate the index at any time by going in the
|
||||
@@ -3493,7 +3494,7 @@ object database -> index
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You read a "tree" file from the object database, and use that to
|
||||
populate (and overwrite - don't do this if your index contains any
|
||||
populate (and overwrite--don't do this if your index contains any
|
||||
unsaved state that you might want to restore later!) your current
|
||||
index. Normal operation is just
|
||||
|
||||
@@ -3541,7 +3542,7 @@ Tying it all together
|
||||
|
||||
To commit a tree you have instantiated with "git-write-tree", you'd
|
||||
create a "commit" object that refers to that tree and the history
|
||||
behind it - most notably the "parent" commits that preceded it in
|
||||
behind it--most notably the "parent" commits that preceded it in
|
||||
history.
|
||||
|
||||
Normally a "commit" has one parent: the previous state of the tree
|
||||
@@ -3684,7 +3685,7 @@ Once you know the three trees you are going to merge (the one "original"
|
||||
tree, aka the common tree, and the two "result" trees, aka the branches
|
||||
you want to merge), you do a "merge" read into the index. This will
|
||||
complain if it has to throw away your old index contents, so you should
|
||||
make sure that you've committed those - in fact you would normally
|
||||
make sure that you've committed those--in fact you would normally
|
||||
always do a merge against your last commit (which should thus match what
|
||||
you have in your current index anyway).
|
||||
|
||||
@@ -3704,7 +3705,7 @@ Merging multiple trees, continued
|
||||
---------------------------------
|
||||
|
||||
Sadly, many merges aren't trivial. If there are files that have
|
||||
been added.moved or removed, or if both branches have modified the
|
||||
been added, moved or removed, or if both branches have modified the
|
||||
same file, you will be left with an index tree that contains "merge
|
||||
entries" in it. Such an index tree can 'NOT' be written out to a tree
|
||||
object, and you will have to resolve any such merge clashes using
|
||||
@@ -3956,7 +3957,7 @@ Two things are interesting here:
|
||||
|
||||
- `get_sha1()` returns 0 on _success_. This might surprise some new
|
||||
Git hackers, but there is a long tradition in UNIX to return different
|
||||
negative numbers in case of different errors -- and 0 on success.
|
||||
negative numbers in case of different errors--and 0 on success.
|
||||
|
||||
- the variable `sha1` in the function signature of `get_sha1()` is `unsigned
|
||||
char \*`, but is actually expected to be a pointer to `unsigned
|
||||
@@ -4061,7 +4062,7 @@ $ git branch new # create branch "new" starting at current HEAD
|
||||
$ git branch -d new # delete branch "new"
|
||||
-----------------------------------------------
|
||||
|
||||
Instead of basing new branch on current HEAD (the default), use:
|
||||
Instead of basing a new branch on current HEAD (the default), use:
|
||||
|
||||
-----------------------------------------------
|
||||
$ git branch new test # branch named "test"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
GVF=GIT-VERSION-FILE
|
||||
DEF_VER=v1.5.3.4.GIT
|
||||
DEF_VER=v1.5.3.GIT
|
||||
|
||||
LF='
|
||||
'
|
||||
|
||||
39
Makefile
39
Makefile
@@ -28,6 +28,8 @@ all::
|
||||
#
|
||||
# Define NO_STRCASESTR if you don't have strcasestr.
|
||||
#
|
||||
# Define NO_MEMMEM if you don't have memmem.
|
||||
#
|
||||
# Define NO_STRLCPY if you don't have strlcpy.
|
||||
#
|
||||
# Define NO_STRTOUMAX if you don't have strtoumax in the C library.
|
||||
@@ -122,6 +124,9 @@ all::
|
||||
# If not set it defaults to the bare 'wish'. If it is set to the empty
|
||||
# string then NO_TCLTK will be forced (this is used by configure script).
|
||||
#
|
||||
# Define THREADED_DELTA_SEARCH if you have pthreads and wish to exploit
|
||||
# parallel delta searching when packing objects.
|
||||
#
|
||||
|
||||
GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
|
||||
@$(SHELL_PATH) ./GIT-VERSION-GEN
|
||||
@@ -160,6 +165,7 @@ GITWEB_CONFIG = gitweb_config.perl
|
||||
GITWEB_HOME_LINK_STR = projects
|
||||
GITWEB_SITENAME =
|
||||
GITWEB_PROJECTROOT = /pub/git
|
||||
GITWEB_PROJECT_MAXDEPTH = 2007
|
||||
GITWEB_EXPORT_OK =
|
||||
GITWEB_STRICT_EXPORT =
|
||||
GITWEB_BASE_URL =
|
||||
@@ -206,7 +212,7 @@ SCRIPT_SH = \
|
||||
git-ls-remote.sh \
|
||||
git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
|
||||
git-pull.sh git-rebase.sh git-rebase--interactive.sh \
|
||||
git-repack.sh git-request-pull.sh git-reset.sh \
|
||||
git-repack.sh git-request-pull.sh \
|
||||
git-sh-setup.sh \
|
||||
git-am.sh \
|
||||
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
|
||||
@@ -354,6 +360,7 @@ BUILTIN_OBJS = \
|
||||
builtin-reflog.o \
|
||||
builtin-config.o \
|
||||
builtin-rerere.o \
|
||||
builtin-reset.o \
|
||||
builtin-rev-list.o \
|
||||
builtin-rev-parse.o \
|
||||
builtin-revert.o \
|
||||
@@ -397,12 +404,14 @@ ifeq ($(uname_S),Darwin)
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
OLD_ICONV = UnfortunatelyYes
|
||||
NO_STRLCPY = YesPlease
|
||||
NO_MEMMEM = YesPlease
|
||||
endif
|
||||
ifeq ($(uname_S),SunOS)
|
||||
NEEDS_SOCKET = YesPlease
|
||||
NEEDS_NSL = YesPlease
|
||||
SHELL_PATH = /bin/bash
|
||||
NO_STRCASESTR = YesPlease
|
||||
NO_MEMMEM = YesPlease
|
||||
NO_HSTRERROR = YesPlease
|
||||
ifeq ($(uname_R),5.8)
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
@@ -425,6 +434,7 @@ ifeq ($(uname_O),Cygwin)
|
||||
NO_D_TYPE_IN_DIRENT = YesPlease
|
||||
NO_D_INO_IN_DIRENT = YesPlease
|
||||
NO_STRCASESTR = YesPlease
|
||||
NO_MEMMEM = YesPlease
|
||||
NO_SYMLINK_HEAD = YesPlease
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
|
||||
@@ -438,11 +448,13 @@ ifeq ($(uname_O),Cygwin)
|
||||
endif
|
||||
ifeq ($(uname_S),FreeBSD)
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
NO_MEMMEM = YesPlease
|
||||
BASIC_CFLAGS += -I/usr/local/include
|
||||
BASIC_LDFLAGS += -L/usr/local/lib
|
||||
endif
|
||||
ifeq ($(uname_S),OpenBSD)
|
||||
NO_STRCASESTR = YesPlease
|
||||
NO_MEMMEM = YesPlease
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
BASIC_CFLAGS += -I/usr/local/include
|
||||
BASIC_LDFLAGS += -L/usr/local/lib
|
||||
@@ -457,6 +469,7 @@ ifeq ($(uname_S),NetBSD)
|
||||
endif
|
||||
ifeq ($(uname_S),AIX)
|
||||
NO_STRCASESTR=YesPlease
|
||||
NO_MEMMEM = YesPlease
|
||||
NO_STRLCPY = YesPlease
|
||||
NEEDS_LIBICONV=YesPlease
|
||||
endif
|
||||
@@ -468,6 +481,7 @@ ifeq ($(uname_S),IRIX64)
|
||||
NO_IPV6=YesPlease
|
||||
NO_SETENV=YesPlease
|
||||
NO_STRCASESTR=YesPlease
|
||||
NO_MEMMEM = YesPlease
|
||||
NO_STRLCPY = YesPlease
|
||||
NO_SOCKADDR_STORAGE=YesPlease
|
||||
SHELL_PATH=/usr/gnu/bin/bash
|
||||
@@ -688,6 +702,15 @@ ifdef NO_HSTRERROR
|
||||
COMPAT_CFLAGS += -DNO_HSTRERROR
|
||||
COMPAT_OBJS += compat/hstrerror.o
|
||||
endif
|
||||
ifdef NO_MEMMEM
|
||||
COMPAT_CFLAGS += -DNO_MEMMEM
|
||||
COMPAT_OBJS += compat/memmem.o
|
||||
endif
|
||||
|
||||
ifdef THREADED_DELTA_SEARCH
|
||||
BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH
|
||||
EXTLIBS += -lpthread
|
||||
endif
|
||||
|
||||
ifeq ($(TCLTK_PATH),)
|
||||
NO_TCLTK=NoThanks
|
||||
@@ -836,6 +859,7 @@ gitweb/gitweb.cgi: gitweb/gitweb.perl
|
||||
-e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
|
||||
-e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
|
||||
-e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
|
||||
-e 's|"++GITWEB_PROJECT_MAXDEPTH++"|$(GITWEB_PROJECT_MAXDEPTH)|g' \
|
||||
-e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \
|
||||
-e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \
|
||||
-e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \
|
||||
@@ -955,6 +979,10 @@ tags:
|
||||
$(RM) tags
|
||||
$(FIND) . -name '*.[hcS]' -print | xargs ctags -a
|
||||
|
||||
cscope:
|
||||
$(RM) cscope*
|
||||
$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
|
||||
|
||||
### Detect prefix changes
|
||||
TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
|
||||
$(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
|
||||
@@ -1096,14 +1124,17 @@ dist-doc:
|
||||
|
||||
### Cleaning rules
|
||||
|
||||
distclean: clean
|
||||
$(RM) configure
|
||||
|
||||
clean:
|
||||
$(RM) *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \
|
||||
$(LIB_FILE) $(XDIFF_LIB)
|
||||
$(RM) $(ALL_PROGRAMS) $(BUILT_INS) git$X
|
||||
$(RM) $(TEST_PROGRAMS)
|
||||
$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
|
||||
$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope*
|
||||
$(RM) -r autom4te.cache
|
||||
$(RM) configure config.log config.mak.autogen config.mak.append config.status config.cache
|
||||
$(RM) config.log config.mak.autogen config.mak.append config.status config.cache
|
||||
$(RM) -r $(GIT_TARNAME) .doc-tmp-dir
|
||||
$(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
|
||||
$(RM) $(htmldocs).tar.gz $(manpages).tar.gz
|
||||
@@ -1119,7 +1150,7 @@ endif
|
||||
$(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS
|
||||
|
||||
.PHONY: all install clean strip
|
||||
.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags .FORCE-GIT-CFLAGS
|
||||
.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags cscope .FORCE-GIT-CFLAGS
|
||||
|
||||
### Check documentation
|
||||
#
|
||||
|
||||
2
RelNotes
2
RelNotes
@@ -1 +1 @@
|
||||
Documentation/RelNotes-1.5.3.4.txt
|
||||
Documentation/RelNotes-1.5.4.txt
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "commit.h"
|
||||
#include "strbuf.h"
|
||||
#include "tar.h"
|
||||
#include "builtin.h"
|
||||
#include "archive.h"
|
||||
@@ -17,6 +16,7 @@ static unsigned long offset;
|
||||
static time_t archive_time;
|
||||
static int tar_umask = 002;
|
||||
static int verbose;
|
||||
static const struct commit *commit;
|
||||
|
||||
/* writes out the whole block, but only if it is full */
|
||||
static void write_if_needed(void)
|
||||
@@ -78,19 +78,6 @@ static void write_trailer(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void strbuf_append_string(struct strbuf *sb, const char *s)
|
||||
{
|
||||
int slen = strlen(s);
|
||||
int total = sb->len + slen;
|
||||
if (total + 1 > sb->alloc) {
|
||||
sb->buf = xrealloc(sb->buf, total + 1);
|
||||
sb->alloc = total + 1;
|
||||
}
|
||||
memcpy(sb->buf + sb->len, s, slen);
|
||||
sb->len = total;
|
||||
sb->buf[total] = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* pax extended header records have the format "%u %s=%s\n". %u contains
|
||||
* the size of the whole string (including the %u), the first %s is the
|
||||
@@ -100,26 +87,17 @@ static void strbuf_append_string(struct strbuf *sb, const char *s)
|
||||
static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,
|
||||
const char *value, unsigned int valuelen)
|
||||
{
|
||||
char *p;
|
||||
int len, total, tmp;
|
||||
int len, tmp;
|
||||
|
||||
/* "%u %s=%s\n" */
|
||||
len = 1 + 1 + strlen(keyword) + 1 + valuelen + 1;
|
||||
for (tmp = len; tmp > 9; tmp /= 10)
|
||||
len++;
|
||||
|
||||
total = sb->len + len;
|
||||
if (total > sb->alloc) {
|
||||
sb->buf = xrealloc(sb->buf, total);
|
||||
sb->alloc = total;
|
||||
}
|
||||
|
||||
p = sb->buf;
|
||||
p += sprintf(p, "%u %s=", len, keyword);
|
||||
memcpy(p, value, valuelen);
|
||||
p += valuelen;
|
||||
*p = '\n';
|
||||
sb->len = total;
|
||||
strbuf_grow(sb, len);
|
||||
strbuf_addf(sb, "%u %s=", len, keyword);
|
||||
strbuf_add(sb, value, valuelen);
|
||||
strbuf_addch(sb, '\n');
|
||||
}
|
||||
|
||||
static unsigned int ustar_header_chksum(const struct ustar_header *header)
|
||||
@@ -153,8 +131,7 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
|
||||
struct strbuf ext_header;
|
||||
|
||||
memset(&header, 0, sizeof(header));
|
||||
ext_header.buf = NULL;
|
||||
ext_header.len = ext_header.alloc = 0;
|
||||
strbuf_init(&ext_header, 0);
|
||||
|
||||
if (!sha1) {
|
||||
*header.typeflag = TYPEFLAG_GLOBAL_HEADER;
|
||||
@@ -166,7 +143,7 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
|
||||
sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
|
||||
} else {
|
||||
if (verbose)
|
||||
fprintf(stderr, "%.*s\n", path->len, path->buf);
|
||||
fprintf(stderr, "%.*s\n", (int)path->len, path->buf);
|
||||
if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
|
||||
*header.typeflag = TYPEFLAG_DIR;
|
||||
mode = (mode | 0777) & ~tar_umask;
|
||||
@@ -225,8 +202,8 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
|
||||
|
||||
if (ext_header.len > 0) {
|
||||
write_entry(sha1, NULL, 0, ext_header.buf, ext_header.len);
|
||||
free(ext_header.buf);
|
||||
}
|
||||
strbuf_release(&ext_header);
|
||||
write_blocked(&header, sizeof(header));
|
||||
if (S_ISREG(mode) && buffer && size > 0)
|
||||
write_blocked(buffer, size);
|
||||
@@ -235,11 +212,11 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
|
||||
static void write_global_extended_header(const unsigned char *sha1)
|
||||
{
|
||||
struct strbuf ext_header;
|
||||
ext_header.buf = NULL;
|
||||
ext_header.len = ext_header.alloc = 0;
|
||||
|
||||
strbuf_init(&ext_header, 0);
|
||||
strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40);
|
||||
write_entry(NULL, NULL, 0, ext_header.buf, ext_header.len);
|
||||
free(ext_header.buf);
|
||||
strbuf_release(&ext_header);
|
||||
}
|
||||
|
||||
static int git_tar_config(const char *var, const char *value)
|
||||
@@ -260,32 +237,22 @@ static int write_tar_entry(const unsigned char *sha1,
|
||||
const char *base, int baselen,
|
||||
const char *filename, unsigned mode, int stage)
|
||||
{
|
||||
static struct strbuf path;
|
||||
int filenamelen = strlen(filename);
|
||||
static struct strbuf path = STRBUF_INIT;
|
||||
void *buffer;
|
||||
enum object_type type;
|
||||
unsigned long size;
|
||||
|
||||
if (!path.alloc) {
|
||||
path.buf = xmalloc(PATH_MAX);
|
||||
path.alloc = PATH_MAX;
|
||||
path.len = path.eof = 0;
|
||||
}
|
||||
if (path.alloc < baselen + filenamelen + 1) {
|
||||
free(path.buf);
|
||||
path.buf = xmalloc(baselen + filenamelen + 1);
|
||||
path.alloc = baselen + filenamelen + 1;
|
||||
}
|
||||
memcpy(path.buf, base, baselen);
|
||||
memcpy(path.buf + baselen, filename, filenamelen);
|
||||
path.len = baselen + filenamelen;
|
||||
path.buf[path.len] = '\0';
|
||||
strbuf_reset(&path);
|
||||
strbuf_grow(&path, PATH_MAX);
|
||||
strbuf_add(&path, base, baselen);
|
||||
strbuf_addstr(&path, filename);
|
||||
if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
|
||||
strbuf_append_string(&path, "/");
|
||||
strbuf_addch(&path, '/');
|
||||
buffer = NULL;
|
||||
size = 0;
|
||||
} else {
|
||||
buffer = convert_sha1_file(path.buf, sha1, mode, &type, &size);
|
||||
buffer = sha1_file_to_archive(path.buf, sha1, mode, &type,
|
||||
&size, commit);
|
||||
if (!buffer)
|
||||
die("cannot read %s", sha1_to_hex(sha1));
|
||||
}
|
||||
@@ -304,6 +271,7 @@ int write_tar_archive(struct archiver_args *args)
|
||||
|
||||
archive_time = args->time;
|
||||
verbose = args->verbose;
|
||||
commit = args->commit;
|
||||
|
||||
if (args->commit_sha1)
|
||||
write_global_extended_header(args->commit_sha1);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
static int verbose;
|
||||
static int zip_date;
|
||||
static int zip_time;
|
||||
static const struct commit *commit;
|
||||
|
||||
static unsigned char *zip_dir;
|
||||
static unsigned int zip_dir_size;
|
||||
@@ -191,11 +192,13 @@ static int write_zip_entry(const unsigned char *sha1,
|
||||
compressed_size = 0;
|
||||
} else if (S_ISREG(mode) || S_ISLNK(mode)) {
|
||||
method = 0;
|
||||
attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) : 0;
|
||||
attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) :
|
||||
(mode & 0111) ? ((mode) << 16) : 0;
|
||||
if (S_ISREG(mode) && zlib_compression_level != 0)
|
||||
method = 8;
|
||||
result = 0;
|
||||
buffer = convert_sha1_file(path, sha1, mode, &type, &size);
|
||||
buffer = sha1_file_to_archive(path, sha1, mode, &type, &size,
|
||||
commit);
|
||||
if (!buffer)
|
||||
die("cannot read %s", sha1_to_hex(sha1));
|
||||
crc = crc32(crc, buffer, size);
|
||||
@@ -229,7 +232,8 @@ static int write_zip_entry(const unsigned char *sha1,
|
||||
}
|
||||
|
||||
copy_le32(dirent.magic, 0x02014b50);
|
||||
copy_le16(dirent.creator_version, S_ISLNK(mode) ? 0x0317 : 0);
|
||||
copy_le16(dirent.creator_version,
|
||||
S_ISLNK(mode) || (S_ISREG(mode) && (mode & 0111)) ? 0x0317 : 0);
|
||||
copy_le16(dirent.version, 10);
|
||||
copy_le16(dirent.flags, 0);
|
||||
copy_le16(dirent.compression_method, method);
|
||||
@@ -316,6 +320,7 @@ int write_zip_archive(struct archiver_args *args)
|
||||
zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);
|
||||
zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
|
||||
verbose = args->verbose;
|
||||
commit = args->commit;
|
||||
|
||||
if (args->base && plen > 0 && args->base[plen - 1] == '/') {
|
||||
char *base = xstrdup(args->base);
|
||||
|
||||
@@ -8,6 +8,7 @@ struct archiver_args {
|
||||
const char *base;
|
||||
struct tree *tree;
|
||||
const unsigned char *commit_sha1;
|
||||
const struct commit *commit;
|
||||
time_t time;
|
||||
const char **pathspec;
|
||||
unsigned int verbose : 1;
|
||||
@@ -42,4 +43,6 @@ extern int write_tar_archive(struct archiver_args *);
|
||||
extern int write_zip_archive(struct archiver_args *);
|
||||
extern void *parse_extra_zip_args(int argc, const char **argv);
|
||||
|
||||
extern void *sha1_file_to_archive(const char *path, const unsigned char *sha1, unsigned int mode, enum object_type *type, unsigned long *size, const struct commit *commit);
|
||||
|
||||
#endif /* ARCHIVE_H */
|
||||
|
||||
12
attr.c
12
attr.c
@@ -160,12 +160,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,
|
||||
else if (!equals)
|
||||
e->setto = ATTR__TRUE;
|
||||
else {
|
||||
char *value;
|
||||
int vallen = ep - equals;
|
||||
value = xmalloc(vallen);
|
||||
memcpy(value, equals+1, vallen-1);
|
||||
value[vallen-1] = 0;
|
||||
e->setto = value;
|
||||
e->setto = xmemdupz(equals + 1, ep - equals - 1);
|
||||
}
|
||||
e->attr = git_attr(cp, len);
|
||||
}
|
||||
@@ -214,8 +209,11 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
|
||||
num_attr = 0;
|
||||
cp = name + namelen;
|
||||
cp = cp + strspn(cp, blank);
|
||||
while (*cp)
|
||||
while (*cp) {
|
||||
cp = parse_attr(src, lineno, cp, &num_attr, res);
|
||||
if (!cp)
|
||||
return NULL;
|
||||
}
|
||||
if (pass)
|
||||
break;
|
||||
res = xcalloc(1,
|
||||
|
||||
@@ -71,12 +71,8 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec,
|
||||
baselen = common_prefix(pathspec);
|
||||
path = ".";
|
||||
base = "";
|
||||
if (baselen) {
|
||||
char *common = xmalloc(baselen + 1);
|
||||
memcpy(common, *pathspec, baselen);
|
||||
common[baselen] = 0;
|
||||
path = base = common;
|
||||
}
|
||||
if (baselen)
|
||||
path = base = xmemdupz(*pathspec, baselen);
|
||||
|
||||
/* Read the directory and prune it */
|
||||
read_directory(dir, path, base, baselen, pathspec);
|
||||
@@ -103,7 +99,6 @@ static void update_callback(struct diff_queue_struct *q,
|
||||
break;
|
||||
case DIFF_STATUS_DELETED:
|
||||
remove_file_from_cache(path);
|
||||
cache_tree_invalidate_path(active_cache_tree, path);
|
||||
if (verbose)
|
||||
printf("remove '%s'\n", path);
|
||||
break;
|
||||
|
||||
485
builtin-apply.c
485
builtin-apply.c
@@ -163,15 +163,14 @@ static void say_patch_name(FILE *output, const char *pre, struct patch *patch, c
|
||||
fputs(pre, output);
|
||||
if (patch->old_name && patch->new_name &&
|
||||
strcmp(patch->old_name, patch->new_name)) {
|
||||
write_name_quoted(NULL, 0, patch->old_name, 1, output);
|
||||
quote_c_style(patch->old_name, NULL, output, 0);
|
||||
fputs(" => ", output);
|
||||
write_name_quoted(NULL, 0, patch->new_name, 1, output);
|
||||
}
|
||||
else {
|
||||
quote_c_style(patch->new_name, NULL, output, 0);
|
||||
} else {
|
||||
const char *n = patch->new_name;
|
||||
if (!n)
|
||||
n = patch->old_name;
|
||||
write_name_quoted(NULL, 0, n, 1, output);
|
||||
quote_c_style(n, NULL, output, 0);
|
||||
}
|
||||
fputs(post, output);
|
||||
}
|
||||
@@ -179,36 +178,18 @@ static void say_patch_name(FILE *output, const char *pre, struct patch *patch, c
|
||||
#define CHUNKSIZE (8192)
|
||||
#define SLOP (16)
|
||||
|
||||
static void *read_patch_file(int fd, unsigned long *sizep)
|
||||
static void read_patch_file(struct strbuf *sb, int fd)
|
||||
{
|
||||
unsigned long size = 0, alloc = CHUNKSIZE;
|
||||
void *buffer = xmalloc(alloc);
|
||||
|
||||
for (;;) {
|
||||
ssize_t nr = alloc - size;
|
||||
if (nr < 1024) {
|
||||
alloc += CHUNKSIZE;
|
||||
buffer = xrealloc(buffer, alloc);
|
||||
nr = alloc - size;
|
||||
}
|
||||
nr = xread(fd, (char *) buffer + size, nr);
|
||||
if (!nr)
|
||||
break;
|
||||
if (nr < 0)
|
||||
die("git-apply: read returned %s", strerror(errno));
|
||||
size += nr;
|
||||
}
|
||||
*sizep = size;
|
||||
if (strbuf_read(sb, fd, 0) < 0)
|
||||
die("git-apply: read returned %s", strerror(errno));
|
||||
|
||||
/*
|
||||
* Make sure that we have some slop in the buffer
|
||||
* so that we can do speculative "memcmp" etc, and
|
||||
* see to it that it is NUL-filled.
|
||||
*/
|
||||
if (alloc < size + SLOP)
|
||||
buffer = xrealloc(buffer, size + SLOP);
|
||||
memset((char *) buffer + size, 0, SLOP);
|
||||
return buffer;
|
||||
strbuf_grow(sb, SLOP);
|
||||
memset(sb->buf + sb->len, 0, SLOP);
|
||||
}
|
||||
|
||||
static unsigned long linelen(const char *buffer, unsigned long size)
|
||||
@@ -244,35 +225,33 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)
|
||||
{
|
||||
int len;
|
||||
const char *start = line;
|
||||
char *name;
|
||||
|
||||
if (*line == '"') {
|
||||
struct strbuf name;
|
||||
|
||||
/* Proposed "new-style" GNU patch/diff format; see
|
||||
* http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
|
||||
*/
|
||||
name = unquote_c_style(line, NULL);
|
||||
if (name) {
|
||||
char *cp = name;
|
||||
while (p_value) {
|
||||
strbuf_init(&name, 0);
|
||||
if (!unquote_c_style(&name, line, NULL)) {
|
||||
char *cp;
|
||||
|
||||
for (cp = name.buf; p_value; p_value--) {
|
||||
cp = strchr(cp, '/');
|
||||
if (!cp)
|
||||
break;
|
||||
cp++;
|
||||
p_value--;
|
||||
}
|
||||
if (cp) {
|
||||
/* name can later be freed, so we need
|
||||
* to memmove, not just return cp
|
||||
*/
|
||||
memmove(name, cp, strlen(cp) + 1);
|
||||
strbuf_remove(&name, 0, cp - name.buf);
|
||||
free(def);
|
||||
return name;
|
||||
}
|
||||
else {
|
||||
free(name);
|
||||
name = NULL;
|
||||
return strbuf_detach(&name, NULL);
|
||||
}
|
||||
}
|
||||
strbuf_release(&name);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
@@ -304,13 +283,10 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)
|
||||
int deflen = strlen(def);
|
||||
if (deflen < len && !strncmp(start, def, deflen))
|
||||
return def;
|
||||
free(def);
|
||||
}
|
||||
|
||||
name = xmalloc(len + 1);
|
||||
memcpy(name, start, len);
|
||||
name[len] = 0;
|
||||
free(def);
|
||||
return name;
|
||||
return xmemdupz(start, len);
|
||||
}
|
||||
|
||||
static int count_slashes(const char *cp)
|
||||
@@ -583,29 +559,30 @@ static const char *stop_at_slash(const char *line, int llen)
|
||||
*/
|
||||
static char *git_header_name(char *line, int llen)
|
||||
{
|
||||
int len;
|
||||
const char *name;
|
||||
const char *second = NULL;
|
||||
size_t len;
|
||||
|
||||
line += strlen("diff --git ");
|
||||
llen -= strlen("diff --git ");
|
||||
|
||||
if (*line == '"') {
|
||||
const char *cp;
|
||||
char *first = unquote_c_style(line, &second);
|
||||
if (!first)
|
||||
return NULL;
|
||||
struct strbuf first;
|
||||
struct strbuf sp;
|
||||
|
||||
strbuf_init(&first, 0);
|
||||
strbuf_init(&sp, 0);
|
||||
|
||||
if (unquote_c_style(&first, line, &second))
|
||||
goto free_and_fail1;
|
||||
|
||||
/* advance to the first slash */
|
||||
cp = stop_at_slash(first, strlen(first));
|
||||
if (!cp || cp == first) {
|
||||
/* we do not accept absolute paths */
|
||||
free_first_and_fail:
|
||||
free(first);
|
||||
return NULL;
|
||||
}
|
||||
len = strlen(cp+1);
|
||||
memmove(first, cp+1, len+1); /* including NUL */
|
||||
cp = stop_at_slash(first.buf, first.len);
|
||||
/* we do not accept absolute paths */
|
||||
if (!cp || cp == first.buf)
|
||||
goto free_and_fail1;
|
||||
strbuf_remove(&first, 0, cp + 1 - first.buf);
|
||||
|
||||
/* second points at one past closing dq of name.
|
||||
* find the second name.
|
||||
@@ -614,40 +591,40 @@ static char *git_header_name(char *line, int llen)
|
||||
second++;
|
||||
|
||||
if (line + llen <= second)
|
||||
goto free_first_and_fail;
|
||||
goto free_and_fail1;
|
||||
if (*second == '"') {
|
||||
char *sp = unquote_c_style(second, NULL);
|
||||
if (!sp)
|
||||
goto free_first_and_fail;
|
||||
cp = stop_at_slash(sp, strlen(sp));
|
||||
if (!cp || cp == sp) {
|
||||
free_both_and_fail:
|
||||
free(sp);
|
||||
goto free_first_and_fail;
|
||||
}
|
||||
if (unquote_c_style(&sp, second, NULL))
|
||||
goto free_and_fail1;
|
||||
cp = stop_at_slash(sp.buf, sp.len);
|
||||
if (!cp || cp == sp.buf)
|
||||
goto free_and_fail1;
|
||||
/* They must match, otherwise ignore */
|
||||
if (strcmp(cp+1, first))
|
||||
goto free_both_and_fail;
|
||||
free(sp);
|
||||
return first;
|
||||
if (strcmp(cp + 1, first.buf))
|
||||
goto free_and_fail1;
|
||||
strbuf_release(&sp);
|
||||
return strbuf_detach(&first, NULL);
|
||||
}
|
||||
|
||||
/* unquoted second */
|
||||
cp = stop_at_slash(second, line + llen - second);
|
||||
if (!cp || cp == second)
|
||||
goto free_first_and_fail;
|
||||
goto free_and_fail1;
|
||||
cp++;
|
||||
if (line + llen - cp != len + 1 ||
|
||||
memcmp(first, cp, len))
|
||||
goto free_first_and_fail;
|
||||
return first;
|
||||
if (line + llen - cp != first.len + 1 ||
|
||||
memcmp(first.buf, cp, first.len))
|
||||
goto free_and_fail1;
|
||||
return strbuf_detach(&first, NULL);
|
||||
|
||||
free_and_fail1:
|
||||
strbuf_release(&first);
|
||||
strbuf_release(&sp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* unquoted first name */
|
||||
name = stop_at_slash(line, llen);
|
||||
if (!name || name == line)
|
||||
return NULL;
|
||||
|
||||
name++;
|
||||
|
||||
/* since the first name is unquoted, a dq if exists must be
|
||||
@@ -655,28 +632,30 @@ static char *git_header_name(char *line, int llen)
|
||||
*/
|
||||
for (second = name; second < line + llen; second++) {
|
||||
if (*second == '"') {
|
||||
const char *cp = second;
|
||||
struct strbuf sp;
|
||||
const char *np;
|
||||
char *sp = unquote_c_style(second, NULL);
|
||||
|
||||
if (!sp)
|
||||
return NULL;
|
||||
np = stop_at_slash(sp, strlen(sp));
|
||||
if (!np || np == sp) {
|
||||
free_second_and_fail:
|
||||
free(sp);
|
||||
return NULL;
|
||||
}
|
||||
strbuf_init(&sp, 0);
|
||||
if (unquote_c_style(&sp, second, NULL))
|
||||
goto free_and_fail2;
|
||||
|
||||
np = stop_at_slash(sp.buf, sp.len);
|
||||
if (!np || np == sp.buf)
|
||||
goto free_and_fail2;
|
||||
np++;
|
||||
len = strlen(np);
|
||||
if (len < cp - name &&
|
||||
|
||||
len = sp.buf + sp.len - np;
|
||||
if (len < second - name &&
|
||||
!strncmp(np, name, len) &&
|
||||
isspace(name[len])) {
|
||||
/* Good */
|
||||
memmove(sp, np, len + 1);
|
||||
return sp;
|
||||
strbuf_remove(&sp, 0, np - sp.buf);
|
||||
return strbuf_detach(&sp, NULL);
|
||||
}
|
||||
goto free_second_and_fail;
|
||||
|
||||
free_and_fail2:
|
||||
strbuf_release(&sp);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,10 +679,7 @@ static char *git_header_name(char *line, int llen)
|
||||
break;
|
||||
}
|
||||
if (second[len] == '\n' && !memcmp(name, second, len)) {
|
||||
char *ret = xmalloc(len + 1);
|
||||
memcpy(ret, name, len);
|
||||
ret[len] = 0;
|
||||
return ret;
|
||||
return xmemdupz(name, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1397,96 +1373,66 @@ static const char minuses[]= "--------------------------------------------------
|
||||
|
||||
static void show_stats(struct patch *patch)
|
||||
{
|
||||
const char *prefix = "";
|
||||
char *name = patch->new_name;
|
||||
char *qname = NULL;
|
||||
int len, max, add, del, total;
|
||||
struct strbuf qname;
|
||||
char *cp = patch->new_name ? patch->new_name : patch->old_name;
|
||||
int max, add, del;
|
||||
|
||||
if (!name)
|
||||
name = patch->old_name;
|
||||
|
||||
if (0 < (len = quote_c_style(name, NULL, NULL, 0))) {
|
||||
qname = xmalloc(len + 1);
|
||||
quote_c_style(name, qname, NULL, 0);
|
||||
name = qname;
|
||||
}
|
||||
strbuf_init(&qname, 0);
|
||||
quote_c_style(cp, &qname, NULL, 0);
|
||||
|
||||
/*
|
||||
* "scale" the filename
|
||||
*/
|
||||
len = strlen(name);
|
||||
max = max_len;
|
||||
if (max > 50)
|
||||
max = 50;
|
||||
if (len > max) {
|
||||
char *slash;
|
||||
prefix = "...";
|
||||
max -= 3;
|
||||
name += len - max;
|
||||
slash = strchr(name, '/');
|
||||
if (slash)
|
||||
name = slash;
|
||||
|
||||
if (qname.len > max) {
|
||||
cp = strchr(qname.buf + qname.len + 3 - max, '/');
|
||||
if (!cp)
|
||||
cp = qname.buf + qname.len + 3 - max;
|
||||
strbuf_splice(&qname, 0, cp - qname.buf, "...", 3);
|
||||
}
|
||||
len = max;
|
||||
|
||||
if (patch->is_binary) {
|
||||
printf(" %-*s | Bin\n", max, qname.buf);
|
||||
strbuf_release(&qname);
|
||||
return;
|
||||
}
|
||||
|
||||
printf(" %-*s |", max, qname.buf);
|
||||
strbuf_release(&qname);
|
||||
|
||||
/*
|
||||
* scale the add/delete
|
||||
*/
|
||||
max = max_change;
|
||||
if (max + len > 70)
|
||||
max = 70 - len;
|
||||
|
||||
max = max + max_change > 70 ? 70 - max : max_change;
|
||||
add = patch->lines_added;
|
||||
del = patch->lines_deleted;
|
||||
total = add + del;
|
||||
|
||||
if (max_change > 0) {
|
||||
total = (total * max + max_change / 2) / max_change;
|
||||
int total = ((add + del) * max + max_change / 2) / max_change;
|
||||
add = (add * max + max_change / 2) / max_change;
|
||||
del = total - add;
|
||||
}
|
||||
if (patch->is_binary)
|
||||
printf(" %s%-*s | Bin\n", prefix, len, name);
|
||||
else
|
||||
printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
|
||||
len, name, patch->lines_added + patch->lines_deleted,
|
||||
add, pluses, del, minuses);
|
||||
free(qname);
|
||||
printf("%5d %.*s%.*s\n", patch->lines_added + patch->lines_deleted,
|
||||
add, pluses, del, minuses);
|
||||
}
|
||||
|
||||
static int read_old_data(struct stat *st, const char *path, char **buf_p, unsigned long *alloc_p, unsigned long *size_p)
|
||||
static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
|
||||
{
|
||||
int fd;
|
||||
unsigned long got;
|
||||
unsigned long nsize;
|
||||
char *nbuf;
|
||||
unsigned long size = *size_p;
|
||||
char *buf = *buf_p;
|
||||
|
||||
switch (st->st_mode & S_IFMT) {
|
||||
case S_IFLNK:
|
||||
return readlink(path, buf, size) != size;
|
||||
strbuf_grow(buf, st->st_size);
|
||||
if (readlink(path, buf->buf, st->st_size) != st->st_size)
|
||||
return -1;
|
||||
strbuf_setlen(buf, st->st_size);
|
||||
return 0;
|
||||
case S_IFREG:
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return error("unable to open %s", path);
|
||||
got = 0;
|
||||
for (;;) {
|
||||
ssize_t ret = xread(fd, buf + got, size - got);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
got += ret;
|
||||
}
|
||||
close(fd);
|
||||
nsize = got;
|
||||
nbuf = convert_to_git(path, buf, &nsize);
|
||||
if (nbuf) {
|
||||
free(buf);
|
||||
*buf_p = nbuf;
|
||||
*alloc_p = nsize;
|
||||
*size_p = nsize;
|
||||
}
|
||||
return got != size;
|
||||
if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
|
||||
return error("unable to open or read %s", path);
|
||||
convert_to_git(path, buf->buf, buf->len, buf);
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
@@ -1591,12 +1537,6 @@ static void remove_last_line(const char **rbuf, int *rsize)
|
||||
*rsize = offset + 1;
|
||||
}
|
||||
|
||||
struct buffer_desc {
|
||||
char *buffer;
|
||||
unsigned long size;
|
||||
unsigned long alloc;
|
||||
};
|
||||
|
||||
static int apply_line(char *output, const char *patch, int plen)
|
||||
{
|
||||
/* plen is number of bytes to be copied from patch,
|
||||
@@ -1673,10 +1613,9 @@ static int apply_line(char *output, const char *patch, int plen)
|
||||
return output + plen - buf;
|
||||
}
|
||||
|
||||
static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, int inaccurate_eof)
|
||||
static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int inaccurate_eof)
|
||||
{
|
||||
int match_beginning, match_end;
|
||||
char *buf = desc->buffer;
|
||||
const char *patch = frag->patch;
|
||||
int offset, size = frag->size;
|
||||
char *old = xmalloc(size);
|
||||
@@ -1787,24 +1726,17 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
|
||||
lines = 0;
|
||||
pos = frag->newpos;
|
||||
for (;;) {
|
||||
offset = find_offset(buf, desc->size,
|
||||
offset = find_offset(buf->buf, buf->len,
|
||||
oldlines, oldsize, pos, &lines);
|
||||
if (match_end && offset + oldsize != desc->size)
|
||||
if (match_end && offset + oldsize != buf->len)
|
||||
offset = -1;
|
||||
if (match_beginning && offset)
|
||||
offset = -1;
|
||||
if (offset >= 0) {
|
||||
int diff;
|
||||
unsigned long size, alloc;
|
||||
|
||||
if (new_whitespace == strip_whitespace &&
|
||||
(desc->size - oldsize - offset == 0)) /* end of file? */
|
||||
(buf->len - oldsize - offset == 0)) /* end of file? */
|
||||
newsize -= new_blank_lines_at_end;
|
||||
|
||||
diff = newsize - oldsize;
|
||||
size = desc->size + diff;
|
||||
alloc = desc->alloc;
|
||||
|
||||
/* Warn if it was necessary to reduce the number
|
||||
* of context lines.
|
||||
*/
|
||||
@@ -1814,19 +1746,8 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
|
||||
" to apply fragment at %d\n",
|
||||
leading, trailing, pos + lines);
|
||||
|
||||
if (size > alloc) {
|
||||
alloc = size + 8192;
|
||||
desc->alloc = alloc;
|
||||
buf = xrealloc(buf, alloc);
|
||||
desc->buffer = buf;
|
||||
}
|
||||
desc->size = size;
|
||||
memmove(buf + offset + newsize,
|
||||
buf + offset + oldsize,
|
||||
size - offset - newsize);
|
||||
memcpy(buf + offset, newlines, newsize);
|
||||
strbuf_splice(buf, offset, oldsize, newlines, newsize);
|
||||
offset = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1862,12 +1783,11 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
|
||||
static int apply_binary_fragment(struct strbuf *buf, struct patch *patch)
|
||||
{
|
||||
unsigned long dst_size;
|
||||
struct fragment *fragment = patch->fragments;
|
||||
void *data;
|
||||
void *result;
|
||||
unsigned long len;
|
||||
void *dst;
|
||||
|
||||
/* Binary patch is irreversible without the optional second hunk */
|
||||
if (apply_in_reverse) {
|
||||
@@ -1878,29 +1798,24 @@ static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
|
||||
? patch->new_name : patch->old_name);
|
||||
fragment = fragment->next;
|
||||
}
|
||||
data = (void*) fragment->patch;
|
||||
switch (fragment->binary_patch_method) {
|
||||
case BINARY_DELTA_DEFLATED:
|
||||
result = patch_delta(desc->buffer, desc->size,
|
||||
data,
|
||||
fragment->size,
|
||||
&dst_size);
|
||||
free(desc->buffer);
|
||||
desc->buffer = result;
|
||||
break;
|
||||
dst = patch_delta(buf->buf, buf->len, fragment->patch,
|
||||
fragment->size, &len);
|
||||
if (!dst)
|
||||
return -1;
|
||||
/* XXX patch_delta NUL-terminates */
|
||||
strbuf_attach(buf, dst, len, len + 1);
|
||||
return 0;
|
||||
case BINARY_LITERAL_DEFLATED:
|
||||
free(desc->buffer);
|
||||
desc->buffer = data;
|
||||
dst_size = fragment->size;
|
||||
break;
|
||||
strbuf_reset(buf);
|
||||
strbuf_add(buf, fragment->patch, fragment->size);
|
||||
return 0;
|
||||
}
|
||||
if (!desc->buffer)
|
||||
return -1;
|
||||
desc->size = desc->alloc = dst_size;
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int apply_binary(struct buffer_desc *desc, struct patch *patch)
|
||||
static int apply_binary(struct strbuf *buf, struct patch *patch)
|
||||
{
|
||||
const char *name = patch->old_name ? patch->old_name : patch->new_name;
|
||||
unsigned char sha1[20];
|
||||
@@ -1919,7 +1834,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
|
||||
/* See if the old one matches what the patch
|
||||
* applies to.
|
||||
*/
|
||||
hash_sha1_file(desc->buffer, desc->size, blob_type, sha1);
|
||||
hash_sha1_file(buf->buf, buf->len, 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 "
|
||||
@@ -1928,16 +1843,14 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
|
||||
}
|
||||
else {
|
||||
/* Otherwise, the old one must be empty. */
|
||||
if (desc->size)
|
||||
if (buf->len)
|
||||
return error("the patch applies to an empty "
|
||||
"'%s' but it is not empty", name);
|
||||
}
|
||||
|
||||
get_sha1_hex(patch->new_sha1_prefix, sha1);
|
||||
if (is_null_sha1(sha1)) {
|
||||
free(desc->buffer);
|
||||
desc->alloc = desc->size = 0;
|
||||
desc->buffer = NULL;
|
||||
strbuf_release(buf);
|
||||
return 0; /* deletion patch */
|
||||
}
|
||||
|
||||
@@ -1945,43 +1858,44 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
|
||||
/* We already have the postimage */
|
||||
enum object_type type;
|
||||
unsigned long size;
|
||||
char *result;
|
||||
|
||||
free(desc->buffer);
|
||||
desc->buffer = read_sha1_file(sha1, &type, &size);
|
||||
if (!desc->buffer)
|
||||
result = read_sha1_file(sha1, &type, &size);
|
||||
if (!result)
|
||||
return error("the necessary postimage %s for "
|
||||
"'%s' cannot be read",
|
||||
patch->new_sha1_prefix, name);
|
||||
desc->alloc = desc->size = size;
|
||||
}
|
||||
else {
|
||||
/* We have verified desc matches the preimage;
|
||||
/* XXX read_sha1_file NUL-terminates */
|
||||
strbuf_attach(buf, result, size, size + 1);
|
||||
} else {
|
||||
/* We have verified buf matches the preimage;
|
||||
* apply the patch data to it, which is stored
|
||||
* in the patch->fragments->{patch,size}.
|
||||
*/
|
||||
if (apply_binary_fragment(desc, patch))
|
||||
if (apply_binary_fragment(buf, patch))
|
||||
return error("binary patch does not apply to '%s'",
|
||||
name);
|
||||
|
||||
/* verify that the result matches */
|
||||
hash_sha1_file(desc->buffer, desc->size, blob_type, sha1);
|
||||
hash_sha1_file(buf->buf, buf->len, 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));
|
||||
return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)",
|
||||
name, patch->new_sha1_prefix, sha1_to_hex(sha1));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
|
||||
static int apply_fragments(struct strbuf *buf, struct patch *patch)
|
||||
{
|
||||
struct fragment *frag = patch->fragments;
|
||||
const char *name = patch->old_name ? patch->old_name : patch->new_name;
|
||||
|
||||
if (patch->is_binary)
|
||||
return apply_binary(desc, patch);
|
||||
return apply_binary(buf, patch);
|
||||
|
||||
while (frag) {
|
||||
if (apply_one_fragment(desc, frag, patch->inaccurate_eof)) {
|
||||
if (apply_one_fragment(buf, frag, patch->inaccurate_eof)) {
|
||||
error("patch failed: %s:%ld", name, frag->oldpos);
|
||||
if (!apply_with_reject)
|
||||
return -1;
|
||||
@@ -1992,76 +1906,56 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_file_or_gitlink(struct cache_entry *ce, char **buf_p,
|
||||
unsigned long *size_p)
|
||||
static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
|
||||
{
|
||||
if (!ce)
|
||||
return 0;
|
||||
|
||||
if (S_ISGITLINK(ntohl(ce->ce_mode))) {
|
||||
*buf_p = xmalloc(100);
|
||||
*size_p = snprintf(*buf_p, 100,
|
||||
"Subproject commit %s\n", sha1_to_hex(ce->sha1));
|
||||
strbuf_grow(buf, 100);
|
||||
strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(ce->sha1));
|
||||
} else {
|
||||
enum object_type type;
|
||||
*buf_p = read_sha1_file(ce->sha1, &type, size_p);
|
||||
if (!*buf_p)
|
||||
unsigned long sz;
|
||||
char *result;
|
||||
|
||||
result = read_sha1_file(ce->sha1, &type, &sz);
|
||||
if (!result)
|
||||
return -1;
|
||||
/* XXX read_sha1_file NUL-terminates */
|
||||
strbuf_attach(buf, result, sz, sz + 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
|
||||
{
|
||||
char *buf;
|
||||
unsigned long size, alloc;
|
||||
struct buffer_desc desc;
|
||||
struct strbuf buf;
|
||||
|
||||
size = 0;
|
||||
alloc = 0;
|
||||
buf = NULL;
|
||||
strbuf_init(&buf, 0);
|
||||
if (cached) {
|
||||
if (read_file_or_gitlink(ce, &buf, &size))
|
||||
if (read_file_or_gitlink(ce, &buf))
|
||||
return error("read of %s failed", patch->old_name);
|
||||
alloc = size;
|
||||
} else if (patch->old_name) {
|
||||
if (S_ISGITLINK(patch->old_mode)) {
|
||||
if (ce)
|
||||
read_file_or_gitlink(ce, &buf, &size);
|
||||
else {
|
||||
if (ce) {
|
||||
read_file_or_gitlink(ce, &buf);
|
||||
} else {
|
||||
/*
|
||||
* There is no way to apply subproject
|
||||
* patch without looking at the index.
|
||||
*/
|
||||
patch->fragments = NULL;
|
||||
size = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
size = xsize_t(st->st_size);
|
||||
alloc = size + 8192;
|
||||
buf = xmalloc(alloc);
|
||||
if (read_old_data(st, patch->old_name,
|
||||
&buf, &alloc, &size))
|
||||
return error("read of %s failed",
|
||||
patch->old_name);
|
||||
} else {
|
||||
if (read_old_data(st, patch->old_name, &buf))
|
||||
return error("read of %s failed", patch->old_name);
|
||||
}
|
||||
}
|
||||
|
||||
desc.size = size;
|
||||
desc.alloc = alloc;
|
||||
desc.buffer = buf;
|
||||
|
||||
if (apply_fragments(&desc, patch) < 0)
|
||||
if (apply_fragments(&buf, patch) < 0)
|
||||
return -1; /* note with --reject this succeeds. */
|
||||
|
||||
/* NUL terminate the result */
|
||||
if (desc.alloc <= desc.size)
|
||||
desc.buffer = xrealloc(desc.buffer, desc.size + 1);
|
||||
desc.buffer[desc.size] = 0;
|
||||
|
||||
patch->result = desc.buffer;
|
||||
patch->resultsize = desc.size;
|
||||
patch->result = strbuf_detach(&buf, &patch->resultsize);
|
||||
|
||||
if (0 < patch->is_delete && patch->resultsize)
|
||||
return error("removal patch leaves file contents");
|
||||
@@ -2315,13 +2209,8 @@ static void numstat_patch_list(struct patch *patch)
|
||||
if (patch->is_binary)
|
||||
printf("-\t-\t");
|
||||
else
|
||||
printf("%d\t%d\t",
|
||||
patch->lines_added, patch->lines_deleted);
|
||||
if (line_termination && quote_c_style(name, NULL, NULL, 0))
|
||||
quote_c_style(name, NULL, stdout, 0);
|
||||
else
|
||||
fputs(name, stdout);
|
||||
putchar(line_termination);
|
||||
printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
|
||||
write_name_quoted(name, stdout, line_termination);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2430,7 +2319,6 @@ static void remove_file(struct patch *patch, int rmdir_empty)
|
||||
if (update_index) {
|
||||
if (remove_file_from_cache(patch->old_name) < 0)
|
||||
die("unable to remove %s from index", patch->old_name);
|
||||
cache_tree_invalidate_path(active_cache_tree, patch->old_name);
|
||||
}
|
||||
if (!cached) {
|
||||
if (S_ISGITLINK(patch->old_mode)) {
|
||||
@@ -2487,7 +2375,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
|
||||
static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
|
||||
{
|
||||
int fd;
|
||||
char *nbuf;
|
||||
struct strbuf nbuf;
|
||||
|
||||
if (S_ISGITLINK(mode)) {
|
||||
struct stat st;
|
||||
@@ -2506,23 +2394,16 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
nbuf = convert_to_working_tree(path, buf, &size);
|
||||
if (nbuf)
|
||||
buf = nbuf;
|
||||
|
||||
while (size) {
|
||||
int written = xwrite(fd, buf, size);
|
||||
if (written < 0)
|
||||
die("writing file %s: %s", path, strerror(errno));
|
||||
if (!written)
|
||||
die("out of space writing file %s", path);
|
||||
buf += written;
|
||||
size -= written;
|
||||
strbuf_init(&nbuf, 0);
|
||||
if (convert_to_working_tree(path, buf, size, &nbuf)) {
|
||||
size = nbuf.len;
|
||||
buf = nbuf.buf;
|
||||
}
|
||||
write_or_die(fd, buf, size);
|
||||
strbuf_release(&nbuf);
|
||||
|
||||
if (close(fd) < 0)
|
||||
die("closing file %s: %s", path, strerror(errno));
|
||||
if (nbuf)
|
||||
free(nbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2585,7 +2466,6 @@ static void create_file(struct patch *patch)
|
||||
mode = S_IFREG | 0644;
|
||||
create_one_file(path, mode, buf, size);
|
||||
add_index_file(path, mode, buf, size);
|
||||
cache_tree_invalidate_path(active_cache_tree, path);
|
||||
}
|
||||
|
||||
/* phase zero is to remove, phase one is to create */
|
||||
@@ -2756,22 +2636,22 @@ static void prefix_patches(struct patch *p)
|
||||
|
||||
static int apply_patch(int fd, const char *filename, int inaccurate_eof)
|
||||
{
|
||||
unsigned long offset, size;
|
||||
char *buffer = read_patch_file(fd, &size);
|
||||
size_t offset;
|
||||
struct strbuf buf;
|
||||
struct patch *list = NULL, **listp = &list;
|
||||
int skipped_patch = 0;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
patch_input_file = filename;
|
||||
if (!buffer)
|
||||
return -1;
|
||||
read_patch_file(&buf, fd);
|
||||
offset = 0;
|
||||
while (size > 0) {
|
||||
while (offset < buf.len) {
|
||||
struct patch *patch;
|
||||
int nr;
|
||||
|
||||
patch = xcalloc(1, sizeof(*patch));
|
||||
patch->inaccurate_eof = inaccurate_eof;
|
||||
nr = parse_chunk(buffer + offset, size, patch);
|
||||
nr = parse_chunk(buf.buf + offset, buf.len - offset, patch);
|
||||
if (nr < 0)
|
||||
break;
|
||||
if (apply_in_reverse)
|
||||
@@ -2789,7 +2669,6 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
|
||||
skipped_patch++;
|
||||
}
|
||||
offset += nr;
|
||||
size -= nr;
|
||||
}
|
||||
|
||||
if (whitespace_error && (new_whitespace == error_on_whitespace))
|
||||
@@ -2824,7 +2703,7 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
|
||||
if (summary)
|
||||
summary_patch_list(list);
|
||||
|
||||
free(buffer);
|
||||
strbuf_release(&buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "exec_cmd.h"
|
||||
#include "pkt-line.h"
|
||||
#include "sideband.h"
|
||||
#include "attr.h"
|
||||
|
||||
static const char archive_usage[] = \
|
||||
"git-archive --format=<fmt> [--prefix=<prefix>/] [--verbose] [<extra>] <tree-ish> [path...]";
|
||||
@@ -80,6 +81,84 @@ static int run_remote_archiver(const char *remote, int argc,
|
||||
return !!rv;
|
||||
}
|
||||
|
||||
static void format_subst(const struct commit *commit,
|
||||
const char *src, size_t len,
|
||||
struct strbuf *buf)
|
||||
{
|
||||
char *to_free = NULL;
|
||||
struct strbuf fmt;
|
||||
|
||||
if (src == buf->buf)
|
||||
to_free = strbuf_detach(buf, NULL);
|
||||
strbuf_init(&fmt, 0);
|
||||
for (;;) {
|
||||
const char *b, *c;
|
||||
|
||||
b = memmem(src, len, "$Format:", 8);
|
||||
if (!b || src + len < b + 9)
|
||||
break;
|
||||
c = memchr(b + 8, '$', len - 8);
|
||||
if (!c)
|
||||
break;
|
||||
|
||||
strbuf_reset(&fmt);
|
||||
strbuf_add(&fmt, b + 8, c - b - 8);
|
||||
|
||||
strbuf_add(buf, src, b - src);
|
||||
format_commit_message(commit, fmt.buf, buf);
|
||||
len -= c + 1 - src;
|
||||
src = c + 1;
|
||||
}
|
||||
strbuf_add(buf, src, len);
|
||||
strbuf_release(&fmt);
|
||||
free(to_free);
|
||||
}
|
||||
|
||||
static int convert_to_archive(const char *path,
|
||||
const void *src, size_t len,
|
||||
struct strbuf *buf,
|
||||
const struct commit *commit)
|
||||
{
|
||||
static struct git_attr *attr_export_subst;
|
||||
struct git_attr_check check[1];
|
||||
|
||||
if (!commit)
|
||||
return 0;
|
||||
|
||||
if (!attr_export_subst)
|
||||
attr_export_subst = git_attr("export-subst", 12);
|
||||
|
||||
check[0].attr = attr_export_subst;
|
||||
if (git_checkattr(path, ARRAY_SIZE(check), check))
|
||||
return 0;
|
||||
if (!ATTR_TRUE(check[0].value))
|
||||
return 0;
|
||||
|
||||
format_subst(commit, src, len, buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *sha1_file_to_archive(const char *path, const unsigned char *sha1,
|
||||
unsigned int mode, enum object_type *type,
|
||||
unsigned long *sizep,
|
||||
const struct commit *commit)
|
||||
{
|
||||
void *buffer;
|
||||
|
||||
buffer = read_sha1_file(sha1, type, sizep);
|
||||
if (buffer && S_ISREG(mode)) {
|
||||
struct strbuf buf;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
strbuf_attach(&buf, buffer, *sizep, *sizep + 1);
|
||||
convert_to_working_tree(path, buf.buf, buf.len, &buf);
|
||||
convert_to_archive(path, buf.buf, buf.len, &buf, commit);
|
||||
buffer = strbuf_detach(&buf, sizep);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static int init_archiver(const char *name, struct archiver *ar)
|
||||
{
|
||||
int rv = -1, i;
|
||||
@@ -109,7 +188,7 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
|
||||
const unsigned char *commit_sha1;
|
||||
time_t archive_time;
|
||||
struct tree *tree;
|
||||
struct commit *commit;
|
||||
const struct commit *commit;
|
||||
unsigned char sha1[20];
|
||||
|
||||
if (get_sha1(name, sha1))
|
||||
@@ -142,6 +221,7 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
|
||||
}
|
||||
ar_args->tree = tree;
|
||||
ar_args->commit_sha1 = commit_sha1;
|
||||
ar_args->commit = commit;
|
||||
ar_args->time = archive_time;
|
||||
}
|
||||
|
||||
|
||||
@@ -1430,8 +1430,7 @@ static void get_commit_info(struct commit *commit,
|
||||
static void write_filename_info(const char *path)
|
||||
{
|
||||
printf("filename ");
|
||||
write_name_quoted(NULL, 0, path, 1, stdout);
|
||||
putchar('\n');
|
||||
write_name_quoted(path, stdout, '\n');
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2001,11 +2000,9 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
|
||||
struct commit *commit;
|
||||
struct origin *origin;
|
||||
unsigned char head_sha1[20];
|
||||
char *buf;
|
||||
struct strbuf buf;
|
||||
const char *ident;
|
||||
int fd;
|
||||
time_t now;
|
||||
unsigned long fin_size;
|
||||
int size, len;
|
||||
struct cache_entry *ce;
|
||||
unsigned mode;
|
||||
@@ -2023,9 +2020,11 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
|
||||
|
||||
origin = make_origin(commit, path);
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
if (!contents_from || strcmp("-", contents_from)) {
|
||||
struct stat st;
|
||||
const char *read_from;
|
||||
unsigned long fin_size;
|
||||
|
||||
if (contents_from) {
|
||||
if (stat(contents_from, &st) < 0)
|
||||
@@ -2038,19 +2037,16 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
|
||||
read_from = path;
|
||||
}
|
||||
fin_size = xsize_t(st.st_size);
|
||||
buf = xmalloc(fin_size+1);
|
||||
mode = canon_mode(st.st_mode);
|
||||
switch (st.st_mode & S_IFMT) {
|
||||
case S_IFREG:
|
||||
fd = open(read_from, O_RDONLY);
|
||||
if (fd < 0)
|
||||
die("cannot open %s", read_from);
|
||||
if (read_in_full(fd, buf, fin_size) != fin_size)
|
||||
die("cannot read %s", read_from);
|
||||
if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
|
||||
die("cannot open or read %s", read_from);
|
||||
break;
|
||||
case S_IFLNK:
|
||||
if (readlink(read_from, buf, fin_size+1) != fin_size)
|
||||
if (readlink(read_from, buf.buf, buf.alloc) != fin_size)
|
||||
die("cannot readlink %s", read_from);
|
||||
buf.len = fin_size;
|
||||
break;
|
||||
default:
|
||||
die("unsupported file type %s", read_from);
|
||||
@@ -2059,26 +2055,14 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
|
||||
else {
|
||||
/* Reading from stdin */
|
||||
contents_from = "standard input";
|
||||
buf = NULL;
|
||||
fin_size = 0;
|
||||
mode = 0;
|
||||
while (1) {
|
||||
ssize_t cnt = 8192;
|
||||
buf = xrealloc(buf, fin_size + cnt);
|
||||
cnt = xread(0, buf + fin_size, cnt);
|
||||
if (cnt < 0)
|
||||
die("read error %s from stdin",
|
||||
strerror(errno));
|
||||
if (!cnt)
|
||||
break;
|
||||
fin_size += cnt;
|
||||
}
|
||||
buf = xrealloc(buf, fin_size + 1);
|
||||
if (strbuf_read(&buf, 0, 0) < 0)
|
||||
die("read error %s from stdin", strerror(errno));
|
||||
}
|
||||
buf[fin_size] = 0;
|
||||
origin->file.ptr = buf;
|
||||
origin->file.size = fin_size;
|
||||
pretend_sha1_file(buf, fin_size, OBJ_BLOB, origin->blob_sha1);
|
||||
convert_to_git(path, buf.buf, buf.len, &buf);
|
||||
origin->file.ptr = buf.buf;
|
||||
origin->file.size = buf.len;
|
||||
pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1);
|
||||
commit->util = origin;
|
||||
|
||||
/*
|
||||
|
||||
@@ -268,23 +268,22 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
char *subject = NULL;
|
||||
unsigned long subject_len = 0;
|
||||
struct strbuf subject;
|
||||
const char *sub = " **** invalid ref ****";
|
||||
|
||||
strbuf_init(&subject, 0);
|
||||
|
||||
commit = lookup_commit(item->sha1);
|
||||
if (commit && !parse_commit(commit)) {
|
||||
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
|
||||
&subject, &subject_len, 0,
|
||||
NULL, NULL, 0);
|
||||
sub = subject;
|
||||
pretty_print_commit(CMIT_FMT_ONELINE, commit,
|
||||
&subject, 0, NULL, NULL, 0);
|
||||
sub = subject.buf;
|
||||
}
|
||||
printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color),
|
||||
maxwidth, item->name,
|
||||
branch_get_color(COLOR_BRANCH_RESET),
|
||||
find_unique_abbrev(item->sha1, abbrev), sub);
|
||||
if (subject)
|
||||
free(subject);
|
||||
strbuf_release(&subject);
|
||||
} else {
|
||||
printf("%c %s%s%s\n", c, branch_get_color(color), item->name,
|
||||
branch_get_color(COLOR_BRANCH_RESET));
|
||||
|
||||
@@ -56,7 +56,7 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
|
||||
else if (ATTR_UNSET(value))
|
||||
value = "unspecified";
|
||||
|
||||
write_name_quoted("", 0, argv[i], 1, stdout);
|
||||
quote_c_style(argv[i], NULL, stdout, 0);
|
||||
printf(": %s: %s\n", argv[j+1], value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
*/
|
||||
#include "builtin.h"
|
||||
#include "cache.h"
|
||||
#include "strbuf.h"
|
||||
#include "quote.h"
|
||||
#include "cache-tree.h"
|
||||
|
||||
@@ -67,9 +66,7 @@ static void write_tempfile_record(const char *name, int prefix_length)
|
||||
fputs(topath[checkout_stage], stdout);
|
||||
|
||||
putchar('\t');
|
||||
write_name_quoted("", 0, name + prefix_length,
|
||||
line_termination, stdout);
|
||||
putchar(line_termination);
|
||||
write_name_quoted(name + prefix_length, stdout, line_termination);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
topath[i][0] = 0;
|
||||
@@ -271,28 +268,28 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
|
||||
if (read_from_stdin) {
|
||||
struct strbuf buf;
|
||||
struct strbuf buf, nbuf;
|
||||
|
||||
if (all)
|
||||
die("git-checkout-index: don't mix '--all' and '--stdin'");
|
||||
strbuf_init(&buf);
|
||||
while (1) {
|
||||
char *path_name;
|
||||
const char *p;
|
||||
|
||||
read_line(&buf, stdin, line_termination);
|
||||
if (buf.eof)
|
||||
break;
|
||||
if (line_termination && buf.buf[0] == '"')
|
||||
path_name = unquote_c_style(buf.buf, NULL);
|
||||
else
|
||||
path_name = buf.buf;
|
||||
p = prefix_path(prefix, prefix_length, path_name);
|
||||
strbuf_init(&buf, 0);
|
||||
strbuf_init(&nbuf, 0);
|
||||
while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
|
||||
const char *p;
|
||||
if (line_termination && buf.buf[0] == '"') {
|
||||
strbuf_reset(&nbuf);
|
||||
if (unquote_c_style(&nbuf, buf.buf, NULL))
|
||||
die("line is badly quoted");
|
||||
strbuf_swap(&buf, &nbuf);
|
||||
}
|
||||
p = prefix_path(prefix, prefix_length, buf.buf);
|
||||
checkout_file(p, prefix_length);
|
||||
if (p < path_name || p > path_name + strlen(path_name))
|
||||
if (p < buf.buf || p > buf.buf + buf.len)
|
||||
free((char *)p);
|
||||
if (path_name != buf.buf)
|
||||
free(path_name);
|
||||
}
|
||||
strbuf_release(&nbuf);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
if (all)
|
||||
|
||||
@@ -14,36 +14,6 @@
|
||||
/*
|
||||
* FIXME! Share the code with "write-tree.c"
|
||||
*/
|
||||
static void init_buffer(char **bufp, unsigned int *sizep)
|
||||
{
|
||||
*bufp = xmalloc(BLOCKING);
|
||||
*sizep = 0;
|
||||
}
|
||||
|
||||
static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...)
|
||||
{
|
||||
char one_line[2048];
|
||||
va_list args;
|
||||
int len;
|
||||
unsigned long alloc, size, newsize;
|
||||
char *buf;
|
||||
|
||||
va_start(args, fmt);
|
||||
len = vsnprintf(one_line, sizeof(one_line), fmt, args);
|
||||
va_end(args);
|
||||
size = *sizep;
|
||||
newsize = size + len + 1;
|
||||
alloc = (size + 32767) & ~32767;
|
||||
buf = *bufp;
|
||||
if (newsize > alloc) {
|
||||
alloc = (newsize + 32767) & ~32767;
|
||||
buf = xrealloc(buf, alloc);
|
||||
*bufp = buf;
|
||||
}
|
||||
*sizep = newsize - 1;
|
||||
memcpy(buf + size, one_line, len);
|
||||
}
|
||||
|
||||
static void check_valid(unsigned char *sha1, enum object_type expect)
|
||||
{
|
||||
enum object_type type = sha1_object_info(sha1, NULL);
|
||||
@@ -87,9 +57,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
|
||||
int parents = 0;
|
||||
unsigned char tree_sha1[20];
|
||||
unsigned char commit_sha1[20];
|
||||
char comment[1000];
|
||||
char *buffer;
|
||||
unsigned int size;
|
||||
struct strbuf buffer;
|
||||
int encoding_is_utf8;
|
||||
|
||||
git_config(git_default_config);
|
||||
@@ -118,8 +86,8 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
|
||||
/* Not having i18n.commitencoding is the same as having utf-8 */
|
||||
encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
|
||||
|
||||
init_buffer(&buffer, &size);
|
||||
add_buffer(&buffer, &size, "tree %s\n", sha1_to_hex(tree_sha1));
|
||||
strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
|
||||
strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree_sha1));
|
||||
|
||||
/*
|
||||
* NOTE! This ordering means that the same exact tree merged with a
|
||||
@@ -127,26 +95,24 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
|
||||
* if everything else stays the same.
|
||||
*/
|
||||
for (i = 0; i < parents; i++)
|
||||
add_buffer(&buffer, &size, "parent %s\n", sha1_to_hex(parent_sha1[i]));
|
||||
strbuf_addf(&buffer, "parent %s\n", sha1_to_hex(parent_sha1[i]));
|
||||
|
||||
/* Person/date information */
|
||||
add_buffer(&buffer, &size, "author %s\n", git_author_info(1));
|
||||
add_buffer(&buffer, &size, "committer %s\n", git_committer_info(1));
|
||||
strbuf_addf(&buffer, "author %s\n", git_author_info(1));
|
||||
strbuf_addf(&buffer, "committer %s\n", git_committer_info(1));
|
||||
if (!encoding_is_utf8)
|
||||
add_buffer(&buffer, &size,
|
||||
"encoding %s\n", git_commit_encoding);
|
||||
add_buffer(&buffer, &size, "\n");
|
||||
strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
|
||||
strbuf_addch(&buffer, '\n');
|
||||
|
||||
/* And add the comment */
|
||||
while (fgets(comment, sizeof(comment), stdin) != NULL)
|
||||
add_buffer(&buffer, &size, "%s", comment);
|
||||
if (strbuf_read(&buffer, 0, 0) < 0)
|
||||
die("git-commit-tree: read returned %s", strerror(errno));
|
||||
|
||||
/* And check the encoding */
|
||||
buffer[size] = '\0';
|
||||
if (encoding_is_utf8 && !is_utf8(buffer))
|
||||
if (encoding_is_utf8 && !is_utf8(buffer.buf))
|
||||
fprintf(stderr, commit_utf8_warn);
|
||||
|
||||
if (!write_sha1_file(buffer, size, commit_type, commit_sha1)) {
|
||||
if (!write_sha1_file(buffer.buf, buffer.len, commit_type, commit_sha1)) {
|
||||
printf("%s\n", sha1_to_hex(commit_sha1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -165,15 +165,21 @@ int cmd_config(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int nongit = 0;
|
||||
char* value;
|
||||
setup_git_directory_gently(&nongit);
|
||||
const char *file = setup_git_directory_gently(&nongit);
|
||||
|
||||
while (1 < argc) {
|
||||
if (!strcmp(argv[1], "--int"))
|
||||
type = T_INT;
|
||||
else if (!strcmp(argv[1], "--bool"))
|
||||
type = T_BOOL;
|
||||
else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l"))
|
||||
return git_config(show_all_config);
|
||||
else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) {
|
||||
if (argc != 2)
|
||||
usage(git_config_set_usage);
|
||||
if (git_config(show_all_config) < 0 && file && errno)
|
||||
die("unable to read config file %s: %s", file,
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
else if (!strcmp(argv[1], "--global")) {
|
||||
char *home = getenv("HOME");
|
||||
if (home) {
|
||||
@@ -189,7 +195,12 @@ int cmd_config(int argc, const char **argv, const char *prefix)
|
||||
else if (!strcmp(argv[1], "--file") || !strcmp(argv[1], "-f")) {
|
||||
if (argc < 3)
|
||||
usage(git_config_set_usage);
|
||||
setenv(CONFIG_ENVIRONMENT, argv[2], 1);
|
||||
if (!is_absolute_path(argv[2]) && file)
|
||||
file = prefix_filename(file, strlen(file),
|
||||
argv[2]);
|
||||
else
|
||||
file = argv[2];
|
||||
setenv(CONFIG_ENVIRONMENT, file, 1);
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
@@ -3,26 +3,14 @@
|
||||
#include "refs.h"
|
||||
#include "commit.h"
|
||||
|
||||
#define CHUNK_SIZE 1024
|
||||
|
||||
static char *get_stdin(void)
|
||||
{
|
||||
size_t offset = 0;
|
||||
char *data = xmalloc(CHUNK_SIZE);
|
||||
|
||||
while (1) {
|
||||
ssize_t cnt = xread(0, data + offset, CHUNK_SIZE);
|
||||
if (cnt < 0)
|
||||
die("error reading standard input: %s",
|
||||
strerror(errno));
|
||||
if (cnt == 0) {
|
||||
data[offset] = 0;
|
||||
break;
|
||||
}
|
||||
offset += cnt;
|
||||
data = xrealloc(data, offset + CHUNK_SIZE);
|
||||
struct strbuf buf;
|
||||
strbuf_init(&buf, 0);
|
||||
if (strbuf_read(&buf, 0, 1024) < 0) {
|
||||
die("error reading standard input: %s", strerror(errno));
|
||||
}
|
||||
return data;
|
||||
return strbuf_detach(&buf, NULL);
|
||||
}
|
||||
|
||||
static void show_new(enum object_type type, unsigned char *sha1_new)
|
||||
@@ -31,24 +19,19 @@ static void show_new(enum object_type type, unsigned char *sha1_new)
|
||||
find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
|
||||
}
|
||||
|
||||
static int update_ref(const char *action,
|
||||
static int update_ref_env(const char *action,
|
||||
const char *refname,
|
||||
unsigned char *sha1,
|
||||
unsigned char *oldval)
|
||||
{
|
||||
char msg[1024];
|
||||
char *rla = getenv("GIT_REFLOG_ACTION");
|
||||
static struct ref_lock *lock;
|
||||
|
||||
if (!rla)
|
||||
rla = "(reflog update)";
|
||||
snprintf(msg, sizeof(msg), "%s: %s", rla, action);
|
||||
lock = lock_any_ref_for_update(refname, oldval, 0);
|
||||
if (!lock)
|
||||
return 1;
|
||||
if (write_ref_sha1(lock, sha1, msg) < 0)
|
||||
return 1;
|
||||
return 0;
|
||||
if (snprintf(msg, sizeof(msg), "%s: %s", rla, action) >= sizeof(msg))
|
||||
warning("reflog message too long: %.*s...", 50, msg);
|
||||
return update_ref(msg, refname, sha1, oldval, 0, QUIET_ON_ERR);
|
||||
}
|
||||
|
||||
static int update_local_ref(const char *name,
|
||||
@@ -88,7 +71,7 @@ static int update_local_ref(const char *name,
|
||||
fprintf(stderr, "* %s: storing %s\n",
|
||||
name, note);
|
||||
show_new(type, sha1_new);
|
||||
return update_ref(msg, name, sha1_new, NULL);
|
||||
return update_ref_env(msg, name, sha1_new, NULL);
|
||||
}
|
||||
|
||||
if (!hashcmp(sha1_old, sha1_new)) {
|
||||
@@ -102,7 +85,7 @@ static int update_local_ref(const char *name,
|
||||
if (!strncmp(name, "refs/tags/", 10)) {
|
||||
fprintf(stderr, "* %s: updating with %s\n", name, note);
|
||||
show_new(type, sha1_new);
|
||||
return update_ref("updating tag", name, sha1_new, NULL);
|
||||
return update_ref_env("updating tag", name, sha1_new, NULL);
|
||||
}
|
||||
|
||||
current = lookup_commit_reference(sha1_old);
|
||||
@@ -117,7 +100,7 @@ static int update_local_ref(const char *name,
|
||||
fprintf(stderr, "* %s: fast forward to %s\n",
|
||||
name, note);
|
||||
fprintf(stderr, " old..new: %s..%s\n", oldh, newh);
|
||||
return update_ref("fast forward", name, sha1_new, sha1_old);
|
||||
return update_ref_env("fast forward", name, sha1_new, sha1_old);
|
||||
}
|
||||
if (!force) {
|
||||
fprintf(stderr,
|
||||
@@ -131,7 +114,7 @@ static int update_local_ref(const char *name,
|
||||
"* %s: forcing update to non-fast forward %s\n",
|
||||
name, note);
|
||||
fprintf(stderr, " old...new: %s...%s\n", oldh, newh);
|
||||
return update_ref("forced-update", name, sha1_new, sha1_old);
|
||||
return update_ref_env("forced-update", name, sha1_new, sha1_old);
|
||||
}
|
||||
|
||||
static int append_fetch_head(FILE *fp,
|
||||
@@ -148,7 +131,7 @@ static int append_fetch_head(FILE *fp,
|
||||
|
||||
if (get_sha1(head, sha1))
|
||||
return error("Not a valid object name: %s", head);
|
||||
commit = lookup_commit_reference(sha1);
|
||||
commit = lookup_commit_reference_gently(sha1, 1);
|
||||
if (!commit)
|
||||
not_for_merge = 1;
|
||||
|
||||
@@ -239,19 +222,15 @@ static char *find_local_name(const char *remote_name, const char *refs,
|
||||
}
|
||||
if (!strncmp(remote_name, ref, len) && ref[len] == ':') {
|
||||
const char *local_part = ref + len + 1;
|
||||
char *ret;
|
||||
int retlen;
|
||||
|
||||
if (!next)
|
||||
retlen = strlen(local_part);
|
||||
else
|
||||
retlen = next - local_part;
|
||||
ret = xmalloc(retlen + 1);
|
||||
memcpy(ret, local_part, retlen);
|
||||
ret[retlen] = 0;
|
||||
*force_p = single_force;
|
||||
*not_for_merge_p = not_for_merge;
|
||||
return ret;
|
||||
return xmemdupz(local_part, retlen);
|
||||
}
|
||||
ref = next;
|
||||
}
|
||||
|
||||
@@ -142,12 +142,10 @@ static int handle_line(char *line)
|
||||
if (!strcmp(".", src) || !strcmp(src, origin)) {
|
||||
int len = strlen(origin);
|
||||
if (origin[0] == '\'' && origin[len - 1] == '\'') {
|
||||
char *new_origin = xmalloc(len - 1);
|
||||
memcpy(new_origin, origin + 1, len - 2);
|
||||
new_origin[len - 2] = 0;
|
||||
origin = new_origin;
|
||||
} else
|
||||
origin = xmemdupz(origin + 1, len - 2);
|
||||
} else {
|
||||
origin = xstrdup(origin);
|
||||
}
|
||||
} else {
|
||||
char *new_origin = xmalloc(strlen(origin) + strlen(src) + 5);
|
||||
sprintf(new_origin, "%s of %s", origin, src);
|
||||
@@ -213,14 +211,11 @@ static void shortlog(const char *name, unsigned char *sha1,
|
||||
|
||||
bol += 2;
|
||||
eol = strchr(bol, '\n');
|
||||
|
||||
if (eol) {
|
||||
int len = eol - bol;
|
||||
oneline = xmalloc(len + 1);
|
||||
memcpy(oneline, bol, len);
|
||||
oneline[len] = 0;
|
||||
} else
|
||||
oneline = xmemdupz(bol, eol - bol);
|
||||
} else {
|
||||
oneline = xstrdup(bol);
|
||||
}
|
||||
append_to_list(&subjects, oneline, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,6 @@ static int used_atom_cnt, sort_atom_limit, need_tagged;
|
||||
static int parse_atom(const char *atom, const char *ep)
|
||||
{
|
||||
const char *sp;
|
||||
char *n;
|
||||
int i, at;
|
||||
|
||||
sp = atom;
|
||||
@@ -106,7 +105,16 @@ static int parse_atom(const char *atom, const char *ep)
|
||||
/* Is the atom a valid one? */
|
||||
for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
|
||||
int len = strlen(valid_atom[i].name);
|
||||
if (len == ep - sp && !memcmp(valid_atom[i].name, sp, len))
|
||||
/*
|
||||
* If the atom name has a colon, strip it and everything after
|
||||
* it off - it specifies the format for this entry, and
|
||||
* shouldn't be used for checking against the valid_atom
|
||||
* table.
|
||||
*/
|
||||
const char *formatp = strchr(sp, ':');
|
||||
if (!formatp || ep < formatp)
|
||||
formatp = ep;
|
||||
if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -120,10 +128,7 @@ static int parse_atom(const char *atom, const char *ep)
|
||||
(sizeof *used_atom) * used_atom_cnt);
|
||||
used_atom_type = xrealloc(used_atom_type,
|
||||
(sizeof(*used_atom_type) * used_atom_cnt));
|
||||
n = xmalloc(ep - atom + 1);
|
||||
memcpy(n, atom, ep - atom);
|
||||
n[ep-atom] = 0;
|
||||
used_atom[at] = n;
|
||||
used_atom[at] = xmemdupz(atom, ep - atom);
|
||||
used_atom_type[at] = valid_atom[i].cmp_type;
|
||||
return at;
|
||||
}
|
||||
@@ -307,54 +312,50 @@ static const char *find_wholine(const char *who, int wholen, const char *buf, un
|
||||
static const char *copy_line(const char *buf)
|
||||
{
|
||||
const char *eol = strchr(buf, '\n');
|
||||
char *line;
|
||||
int len;
|
||||
if (!eol)
|
||||
return "";
|
||||
len = eol - buf;
|
||||
line = xmalloc(len + 1);
|
||||
memcpy(line, buf, len);
|
||||
line[len] = 0;
|
||||
return line;
|
||||
return xmemdupz(buf, eol - buf);
|
||||
}
|
||||
|
||||
static const char *copy_name(const char *buf)
|
||||
{
|
||||
const char *eol = strchr(buf, '\n');
|
||||
const char *eoname = strstr(buf, " <");
|
||||
char *line;
|
||||
int len;
|
||||
if (!(eoname && eol && eoname < eol))
|
||||
return "";
|
||||
len = eoname - buf;
|
||||
line = xmalloc(len + 1);
|
||||
memcpy(line, buf, len);
|
||||
line[len] = 0;
|
||||
return line;
|
||||
const char *cp;
|
||||
for (cp = buf; *cp && *cp != '\n'; cp++) {
|
||||
if (!strncmp(cp, " <", 2))
|
||||
return xmemdupz(buf, cp - buf);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static const char *copy_email(const char *buf)
|
||||
{
|
||||
const char *email = strchr(buf, '<');
|
||||
const char *eoemail = strchr(email, '>');
|
||||
char *line;
|
||||
int len;
|
||||
if (!email || !eoemail)
|
||||
return "";
|
||||
eoemail++;
|
||||
len = eoemail - email;
|
||||
line = xmalloc(len + 1);
|
||||
memcpy(line, email, len);
|
||||
line[len] = 0;
|
||||
return line;
|
||||
return xmemdupz(email, eoemail + 1 - email);
|
||||
}
|
||||
|
||||
static void grab_date(const char *buf, struct atom_value *v)
|
||||
static void grab_date(const char *buf, struct atom_value *v, const char *atomname)
|
||||
{
|
||||
const char *eoemail = strstr(buf, "> ");
|
||||
char *zone;
|
||||
unsigned long timestamp;
|
||||
long tz;
|
||||
enum date_mode date_mode = DATE_NORMAL;
|
||||
const char *formatp;
|
||||
|
||||
/*
|
||||
* We got here because atomname ends in "date" or "date<something>";
|
||||
* it's not possible that <something> is not ":<format>" because
|
||||
* parse_atom() wouldn't have allowed it, so we can assume that no
|
||||
* ":" means no format is specified, and use the default.
|
||||
*/
|
||||
formatp = strchr(atomname, ':');
|
||||
if (formatp != NULL) {
|
||||
formatp++;
|
||||
date_mode = parse_date_format(formatp);
|
||||
}
|
||||
|
||||
if (!eoemail)
|
||||
goto bad;
|
||||
@@ -364,7 +365,7 @@ static void grab_date(const char *buf, struct atom_value *v)
|
||||
tz = strtol(zone, NULL, 10);
|
||||
if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
|
||||
goto bad;
|
||||
v->s = xstrdup(show_date(timestamp, tz, 0));
|
||||
v->s = xstrdup(show_date(timestamp, tz, date_mode));
|
||||
v->ul = timestamp;
|
||||
return;
|
||||
bad:
|
||||
@@ -391,7 +392,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
|
||||
if (name[wholen] != 0 &&
|
||||
strcmp(name + wholen, "name") &&
|
||||
strcmp(name + wholen, "email") &&
|
||||
strcmp(name + wholen, "date"))
|
||||
prefixcmp(name + wholen, "date"))
|
||||
continue;
|
||||
if (!wholine)
|
||||
wholine = find_wholine(who, wholen, buf, sz);
|
||||
@@ -403,8 +404,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
|
||||
v->s = copy_name(wholine);
|
||||
else if (!strcmp(name + wholen, "email"))
|
||||
v->s = copy_email(wholine);
|
||||
else if (!strcmp(name + wholen, "date"))
|
||||
grab_date(wholine, v);
|
||||
else if (!prefixcmp(name + wholen, "date"))
|
||||
grab_date(wholine, v, name);
|
||||
}
|
||||
|
||||
/* For a tag or a commit object, if "creator" or "creatordate" is
|
||||
@@ -424,8 +425,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
|
||||
if (deref)
|
||||
name++;
|
||||
|
||||
if (!strcmp(name, "creatordate"))
|
||||
grab_date(wholine, v);
|
||||
if (!prefixcmp(name, "creatordate"))
|
||||
grab_date(wholine, v, name);
|
||||
else if (!strcmp(name, "creator"))
|
||||
v->s = copy_line(wholine);
|
||||
}
|
||||
|
||||
141
builtin-gc.c
141
builtin-gc.c
@@ -20,11 +20,13 @@ static const char builtin_gc_usage[] = "git-gc [--prune] [--aggressive]";
|
||||
|
||||
static int pack_refs = 1;
|
||||
static int aggressive_window = -1;
|
||||
static int gc_auto_threshold = 6700;
|
||||
static int gc_auto_pack_limit = 20;
|
||||
|
||||
#define MAX_ADD 10
|
||||
static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL};
|
||||
static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL};
|
||||
static const char *argv_repack[MAX_ADD] = {"repack", "-a", "-d", "-l", NULL};
|
||||
static const char *argv_repack[MAX_ADD] = {"repack", "-d", "-l", NULL};
|
||||
static const char *argv_prune[] = {"prune", NULL};
|
||||
static const char *argv_rerere[] = {"rerere", "gc", NULL};
|
||||
|
||||
@@ -41,6 +43,14 @@ static int gc_config(const char *var, const char *value)
|
||||
aggressive_window = git_config_int(var, value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "gc.auto")) {
|
||||
gc_auto_threshold = git_config_int(var, value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "gc.autopacklimit")) {
|
||||
gc_auto_pack_limit = git_config_int(var, value);
|
||||
return 0;
|
||||
}
|
||||
return git_default_config(var, value);
|
||||
}
|
||||
|
||||
@@ -57,10 +67,107 @@ static void append_option(const char **cmd, const char *opt, int max_length)
|
||||
cmd[i] = NULL;
|
||||
}
|
||||
|
||||
static int too_many_loose_objects(void)
|
||||
{
|
||||
/*
|
||||
* Quickly check if a "gc" is needed, by estimating how
|
||||
* many loose objects there are. Because SHA-1 is evenly
|
||||
* distributed, we can check only one and get a reasonable
|
||||
* estimate.
|
||||
*/
|
||||
char path[PATH_MAX];
|
||||
const char *objdir = get_object_directory();
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
int auto_threshold;
|
||||
int num_loose = 0;
|
||||
int needed = 0;
|
||||
|
||||
if (gc_auto_threshold <= 0)
|
||||
return 0;
|
||||
|
||||
if (sizeof(path) <= snprintf(path, sizeof(path), "%s/17", objdir)) {
|
||||
warning("insanely long object directory %.*s", 50, objdir);
|
||||
return 0;
|
||||
}
|
||||
dir = opendir(path);
|
||||
if (!dir)
|
||||
return 0;
|
||||
|
||||
auto_threshold = (gc_auto_threshold + 255) / 256;
|
||||
while ((ent = readdir(dir)) != NULL) {
|
||||
if (strspn(ent->d_name, "0123456789abcdef") != 38 ||
|
||||
ent->d_name[38] != '\0')
|
||||
continue;
|
||||
if (++num_loose > auto_threshold) {
|
||||
needed = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
return needed;
|
||||
}
|
||||
|
||||
static int too_many_packs(void)
|
||||
{
|
||||
struct packed_git *p;
|
||||
int cnt;
|
||||
|
||||
if (gc_auto_pack_limit <= 0)
|
||||
return 0;
|
||||
|
||||
prepare_packed_git();
|
||||
for (cnt = 0, p = packed_git; p; p = p->next) {
|
||||
char path[PATH_MAX];
|
||||
size_t len;
|
||||
int keep;
|
||||
|
||||
if (!p->pack_local)
|
||||
continue;
|
||||
len = strlen(p->pack_name);
|
||||
if (PATH_MAX <= len + 1)
|
||||
continue; /* oops, give up */
|
||||
memcpy(path, p->pack_name, len-5);
|
||||
memcpy(path + len - 5, ".keep", 6);
|
||||
keep = access(p->pack_name, F_OK) && (errno == ENOENT);
|
||||
if (keep)
|
||||
continue;
|
||||
/*
|
||||
* Perhaps check the size of the pack and count only
|
||||
* very small ones here?
|
||||
*/
|
||||
cnt++;
|
||||
}
|
||||
return gc_auto_pack_limit <= cnt;
|
||||
}
|
||||
|
||||
static int need_to_gc(void)
|
||||
{
|
||||
/*
|
||||
* Setting gc.auto and gc.autopacklimit to 0 or negative can
|
||||
* disable the automatic gc.
|
||||
*/
|
||||
if (gc_auto_threshold <= 0 && gc_auto_pack_limit <= 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If there are too many loose objects, but not too many
|
||||
* packs, we run "repack -d -l". If there are too many packs,
|
||||
* we run "repack -A -d -l". Otherwise we tell the caller
|
||||
* there is no need.
|
||||
*/
|
||||
if (too_many_packs())
|
||||
append_option(argv_repack, "-A", MAX_ADD);
|
||||
else if (!too_many_loose_objects())
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cmd_gc(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i;
|
||||
int prune = 0;
|
||||
int auto_gc = 0;
|
||||
char buf[80];
|
||||
|
||||
git_config(gc_config);
|
||||
@@ -82,12 +189,38 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/* perhaps other parameters later... */
|
||||
if (!strcmp(arg, "--auto")) {
|
||||
auto_gc = 1;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (i != argc)
|
||||
usage(builtin_gc_usage);
|
||||
|
||||
if (auto_gc) {
|
||||
/*
|
||||
* Auto-gc should be least intrusive as possible.
|
||||
*/
|
||||
prune = 0;
|
||||
if (!need_to_gc())
|
||||
return 0;
|
||||
fprintf(stderr, "Packing your repository for optimum "
|
||||
"performance. You may also\n"
|
||||
"run \"git gc\" manually. See "
|
||||
"\"git help gc\" for more information.\n");
|
||||
} else {
|
||||
/*
|
||||
* Use safer (for shared repos) "-A" option to
|
||||
* repack when not pruning. Auto-gc makes its
|
||||
* own decision.
|
||||
*/
|
||||
if (prune)
|
||||
append_option(argv_repack, "-a", MAX_ADD);
|
||||
else
|
||||
append_option(argv_repack, "-A", MAX_ADD);
|
||||
}
|
||||
|
||||
if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
|
||||
return error(FAILED_RUN, argv_pack_refs[0]);
|
||||
|
||||
@@ -103,5 +236,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
|
||||
if (run_command_v_opt(argv_rerere, RUN_GIT_CMD))
|
||||
return error(FAILED_RUN, argv_rerere[0]);
|
||||
|
||||
if (auto_gc && too_many_loose_objects())
|
||||
warning("There are too many unreachable loose objects; "
|
||||
"run 'git prune' to remove them.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -441,8 +441,6 @@ static const char *clean_message_id(const char *msg_id)
|
||||
{
|
||||
char ch;
|
||||
const char *a, *z, *m;
|
||||
char *n;
|
||||
size_t len;
|
||||
|
||||
m = msg_id;
|
||||
while ((ch = *m) && (isspace(ch) || (ch == '<')))
|
||||
@@ -458,11 +456,7 @@ static const char *clean_message_id(const char *msg_id)
|
||||
die("insane in-reply-to: %s", msg_id);
|
||||
if (++z == m)
|
||||
return a;
|
||||
len = z - a;
|
||||
n = xmalloc(len + 1);
|
||||
memcpy(n, a, len);
|
||||
n[len] = 0;
|
||||
return n;
|
||||
return xmemdupz(a, z - a);
|
||||
}
|
||||
|
||||
int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
@@ -541,9 +535,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
endpos = strchr(committer, '>');
|
||||
if (!endpos)
|
||||
die("bogos committer info %s\n", committer);
|
||||
add_signoff = xmalloc(endpos - committer + 2);
|
||||
memcpy(add_signoff, committer, endpos - committer + 1);
|
||||
add_signoff[endpos - committer + 1] = 0;
|
||||
add_signoff = xmemdupz(committer, endpos - committer + 1);
|
||||
}
|
||||
else if (!strcmp(argv[i], "--attach")) {
|
||||
rev.mime_boundary = git_version_string;
|
||||
@@ -792,13 +784,13 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
|
||||
sign = '-';
|
||||
|
||||
if (verbose) {
|
||||
char *buf = NULL;
|
||||
unsigned long buflen = 0;
|
||||
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
|
||||
&buf, &buflen, 0, NULL, NULL, 0);
|
||||
struct strbuf buf;
|
||||
strbuf_init(&buf, 0);
|
||||
pretty_print_commit(CMIT_FMT_ONELINE, commit,
|
||||
&buf, 0, NULL, NULL, 0);
|
||||
printf("%c %s %s\n", sign,
|
||||
sha1_to_hex(commit->object.sha1), buf);
|
||||
free(buf);
|
||||
sha1_to_hex(commit->object.sha1), buf.buf);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
else {
|
||||
printf("%c %s\n", sign,
|
||||
|
||||
@@ -84,8 +84,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
|
||||
return;
|
||||
|
||||
fputs(tag, stdout);
|
||||
write_name_quoted("", 0, ent->name + offset, line_terminator, stdout);
|
||||
putchar(line_terminator);
|
||||
write_name_quoted(ent->name + offset, stdout, line_terminator);
|
||||
}
|
||||
|
||||
static void show_other_files(struct dir_struct *dir)
|
||||
@@ -208,21 +207,15 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
|
||||
|
||||
if (!show_stage) {
|
||||
fputs(tag, stdout);
|
||||
write_name_quoted("", 0, ce->name + offset,
|
||||
line_terminator, stdout);
|
||||
putchar(line_terminator);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
printf("%s%06o %s %d\t",
|
||||
tag,
|
||||
ntohl(ce->ce_mode),
|
||||
abbrev ? find_unique_abbrev(ce->sha1,abbrev)
|
||||
: sha1_to_hex(ce->sha1),
|
||||
ce_stage(ce));
|
||||
write_name_quoted("", 0, ce->name + offset,
|
||||
line_terminator, stdout);
|
||||
putchar(line_terminator);
|
||||
}
|
||||
write_name_quoted(ce->name + offset, stdout, line_terminator);
|
||||
}
|
||||
|
||||
static void show_files(struct dir_struct *dir, const char *prefix)
|
||||
@@ -300,7 +293,6 @@ static void prune_cache(const char *prefix)
|
||||
static const char *verify_pathspec(const char *prefix)
|
||||
{
|
||||
const char **p, *n, *prev;
|
||||
char *real_prefix;
|
||||
unsigned long max;
|
||||
|
||||
prev = NULL;
|
||||
@@ -327,14 +319,8 @@ static const char *verify_pathspec(const char *prefix)
|
||||
if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
|
||||
die("git-ls-files: cannot generate relative filenames containing '..'");
|
||||
|
||||
real_prefix = NULL;
|
||||
prefix_len = max;
|
||||
if (max) {
|
||||
real_prefix = xmalloc(max + 1);
|
||||
memcpy(real_prefix, prev, max);
|
||||
real_prefix[max] = 0;
|
||||
}
|
||||
return real_prefix;
|
||||
return max ? xmemdupz(prev, max) : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -112,10 +112,8 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen,
|
||||
abbrev ? find_unique_abbrev(sha1, abbrev)
|
||||
: sha1_to_hex(sha1));
|
||||
}
|
||||
write_name_quoted(base + chomp_prefix, baselen - chomp_prefix,
|
||||
pathname,
|
||||
line_termination, stdout);
|
||||
putchar(line_termination);
|
||||
write_name_quotedpfx(base + chomp_prefix, baselen - chomp_prefix,
|
||||
pathname, stdout, line_termination);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
12
builtin-mv.c
12
builtin-mv.c
@@ -22,10 +22,7 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
|
||||
for (i = 0; i < count; i++) {
|
||||
int length = strlen(result[i]);
|
||||
if (length > 0 && result[i][length - 1] == '/') {
|
||||
char *without_slash = xmalloc(length);
|
||||
memcpy(without_slash, result[i], length - 1);
|
||||
without_slash[length - 1] = '\0';
|
||||
result[i] = without_slash;
|
||||
result[i] = xmemdupz(result[i], length - 1);
|
||||
}
|
||||
if (base_name) {
|
||||
const char *last_slash = strrchr(result[i], '/');
|
||||
@@ -276,11 +273,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
|
||||
add_file_to_cache(path, verbose);
|
||||
}
|
||||
|
||||
for (i = 0; i < deleted.nr; i++) {
|
||||
const char *path = deleted.items[i].path;
|
||||
remove_file_from_cache(path);
|
||||
cache_tree_invalidate_path(active_cache_tree, path);
|
||||
}
|
||||
for (i = 0; i < deleted.nr; i++)
|
||||
remove_file_from_cache(deleted.items[i].path);
|
||||
|
||||
if (active_cache_changed) {
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
|
||||
@@ -15,13 +15,17 @@
|
||||
#include "list-objects.h"
|
||||
#include "progress.h"
|
||||
|
||||
#ifdef THREADED_DELTA_SEARCH
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
static const char pack_usage[] = "\
|
||||
git-pack-objects [{ -q | --progress | --all-progress }] \n\
|
||||
[--max-pack-size=N] [--local] [--incremental] \n\
|
||||
[--window=N] [--window-memory=N] [--depth=N] \n\
|
||||
[--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
|
||||
[--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
|
||||
[--stdout | base-name] [<ref-list | <object-list]";
|
||||
[--threads=N] [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
|
||||
[--stdout | base-name] [--keep-unreachable] [<ref-list | <object-list]";
|
||||
|
||||
struct object_entry {
|
||||
struct pack_idx_entry idx;
|
||||
@@ -57,7 +61,7 @@ static struct object_entry **written_list;
|
||||
static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
|
||||
|
||||
static int non_empty;
|
||||
static int no_reuse_delta, no_reuse_object;
|
||||
static int no_reuse_delta, no_reuse_object, keep_unreachable;
|
||||
static int local;
|
||||
static int incremental;
|
||||
static int allow_ofs_delta;
|
||||
@@ -68,6 +72,7 @@ static int progress = 1;
|
||||
static int window = 10;
|
||||
static uint32_t pack_size_limit;
|
||||
static int depth = 50;
|
||||
static int delta_search_threads = 1;
|
||||
static int pack_to_stdout;
|
||||
static int num_preferred_base;
|
||||
static struct progress progress_state;
|
||||
@@ -78,7 +83,6 @@ static unsigned long delta_cache_size = 0;
|
||||
static unsigned long max_delta_cache_size = 0;
|
||||
static unsigned long cache_max_small_delta_size = 1000;
|
||||
|
||||
static unsigned long window_memory_usage = 0;
|
||||
static unsigned long window_memory_limit = 0;
|
||||
|
||||
/*
|
||||
@@ -1291,6 +1295,31 @@ static int delta_cacheable(unsigned long src_size, unsigned long trg_size,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef THREADED_DELTA_SEARCH
|
||||
|
||||
static pthread_mutex_t read_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
#define read_lock() pthread_mutex_lock(&read_mutex)
|
||||
#define read_unlock() pthread_mutex_unlock(&read_mutex)
|
||||
|
||||
static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
#define cache_lock() pthread_mutex_lock(&cache_mutex)
|
||||
#define cache_unlock() pthread_mutex_unlock(&cache_mutex)
|
||||
|
||||
static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
#define progress_lock() pthread_mutex_lock(&progress_mutex)
|
||||
#define progress_unlock() pthread_mutex_unlock(&progress_mutex)
|
||||
|
||||
#else
|
||||
|
||||
#define read_lock() (void)0
|
||||
#define read_unlock() (void)0
|
||||
#define cache_lock() (void)0
|
||||
#define cache_unlock() (void)0
|
||||
#define progress_lock() (void)0
|
||||
#define progress_unlock() (void)0
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We search for deltas _backwards_ in a list sorted by type and
|
||||
* by size, so that we see progressively smaller and smaller files.
|
||||
@@ -1300,7 +1329,7 @@ static int delta_cacheable(unsigned long src_size, unsigned long trg_size,
|
||||
* one.
|
||||
*/
|
||||
static int try_delta(struct unpacked *trg, struct unpacked *src,
|
||||
unsigned max_depth)
|
||||
unsigned max_depth, unsigned long *mem_usage)
|
||||
{
|
||||
struct object_entry *trg_entry = trg->entry;
|
||||
struct object_entry *src_entry = src->entry;
|
||||
@@ -1313,12 +1342,6 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
|
||||
if (trg_entry->type != src_entry->type)
|
||||
return -1;
|
||||
|
||||
/* We do not compute delta to *create* objects we are not
|
||||
* going to pack.
|
||||
*/
|
||||
if (trg_entry->preferred_base)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* We do not bother to try a delta that we discarded
|
||||
* on an earlier try, but only when reusing delta data.
|
||||
@@ -1355,24 +1378,28 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
|
||||
|
||||
/* Load data if not already done */
|
||||
if (!trg->data) {
|
||||
read_lock();
|
||||
trg->data = read_sha1_file(trg_entry->idx.sha1, &type, &sz);
|
||||
read_unlock();
|
||||
if (!trg->data)
|
||||
die("object %s cannot be read",
|
||||
sha1_to_hex(trg_entry->idx.sha1));
|
||||
if (sz != trg_size)
|
||||
die("object %s inconsistent object length (%lu vs %lu)",
|
||||
sha1_to_hex(trg_entry->idx.sha1), sz, trg_size);
|
||||
window_memory_usage += sz;
|
||||
*mem_usage += sz;
|
||||
}
|
||||
if (!src->data) {
|
||||
read_lock();
|
||||
src->data = read_sha1_file(src_entry->idx.sha1, &type, &sz);
|
||||
read_unlock();
|
||||
if (!src->data)
|
||||
die("object %s cannot be read",
|
||||
sha1_to_hex(src_entry->idx.sha1));
|
||||
if (sz != src_size)
|
||||
die("object %s inconsistent object length (%lu vs %lu)",
|
||||
sha1_to_hex(src_entry->idx.sha1), sz, src_size);
|
||||
window_memory_usage += sz;
|
||||
*mem_usage += sz;
|
||||
}
|
||||
if (!src->index) {
|
||||
src->index = create_delta_index(src->data, src_size);
|
||||
@@ -1382,7 +1409,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
|
||||
warning("suboptimal pack - out of memory");
|
||||
return 0;
|
||||
}
|
||||
window_memory_usage += sizeof_delta_index(src->index);
|
||||
*mem_usage += sizeof_delta_index(src->index);
|
||||
}
|
||||
|
||||
delta_buf = create_delta(src->index, trg->data, trg_size, &delta_size, max_size);
|
||||
@@ -1402,17 +1429,27 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
|
||||
trg_entry->delta_size = delta_size;
|
||||
trg->depth = src->depth + 1;
|
||||
|
||||
/*
|
||||
* Handle memory allocation outside of the cache
|
||||
* accounting lock. Compiler will optimize the strangeness
|
||||
* away when THREADED_DELTA_SEARCH is not defined.
|
||||
*/
|
||||
if (trg_entry->delta_data)
|
||||
free(trg_entry->delta_data);
|
||||
cache_lock();
|
||||
if (trg_entry->delta_data) {
|
||||
delta_cache_size -= trg_entry->delta_size;
|
||||
free(trg_entry->delta_data);
|
||||
trg_entry->delta_data = NULL;
|
||||
}
|
||||
|
||||
if (delta_cacheable(src_size, trg_size, delta_size)) {
|
||||
trg_entry->delta_data = xrealloc(delta_buf, delta_size);
|
||||
delta_cache_size += trg_entry->delta_size;
|
||||
} else
|
||||
cache_unlock();
|
||||
trg_entry->delta_data = xrealloc(delta_buf, delta_size);
|
||||
} else {
|
||||
cache_unlock();
|
||||
free(delta_buf);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1429,68 +1466,60 @@ static unsigned int check_delta_limit(struct object_entry *me, unsigned int n)
|
||||
return m;
|
||||
}
|
||||
|
||||
static void free_unpacked(struct unpacked *n)
|
||||
static unsigned long free_unpacked(struct unpacked *n)
|
||||
{
|
||||
window_memory_usage -= sizeof_delta_index(n->index);
|
||||
unsigned long freed_mem = sizeof_delta_index(n->index);
|
||||
free_delta_index(n->index);
|
||||
n->index = NULL;
|
||||
if (n->data) {
|
||||
freed_mem += n->entry->size;
|
||||
free(n->data);
|
||||
n->data = NULL;
|
||||
window_memory_usage -= n->entry->size;
|
||||
}
|
||||
n->entry = NULL;
|
||||
n->depth = 0;
|
||||
return freed_mem;
|
||||
}
|
||||
|
||||
static void find_deltas(struct object_entry **list, int window, int depth)
|
||||
static void find_deltas(struct object_entry **list, unsigned list_size,
|
||||
int window, int depth, unsigned *processed)
|
||||
{
|
||||
uint32_t i = nr_objects, idx = 0, count = 0, processed = 0;
|
||||
uint32_t i = list_size, idx = 0, count = 0;
|
||||
unsigned int array_size = window * sizeof(struct unpacked);
|
||||
struct unpacked *array;
|
||||
int max_depth;
|
||||
unsigned long mem_usage = 0;
|
||||
|
||||
if (!nr_objects)
|
||||
return;
|
||||
array = xmalloc(array_size);
|
||||
memset(array, 0, array_size);
|
||||
if (progress)
|
||||
start_progress(&progress_state, "Deltifying %u objects...", "", nr_result);
|
||||
|
||||
do {
|
||||
struct object_entry *entry = list[--i];
|
||||
struct unpacked *n = array + idx;
|
||||
int j;
|
||||
int j, max_depth, best_base = -1;
|
||||
|
||||
if (!entry->preferred_base)
|
||||
processed++;
|
||||
|
||||
if (progress)
|
||||
display_progress(&progress_state, processed);
|
||||
|
||||
if (entry->delta)
|
||||
/* This happens if we decided to reuse existing
|
||||
* delta from a pack. "!no_reuse_delta &&" is implied.
|
||||
*/
|
||||
continue;
|
||||
|
||||
if (entry->size < 50)
|
||||
continue;
|
||||
|
||||
if (entry->no_try_delta)
|
||||
continue;
|
||||
|
||||
free_unpacked(n);
|
||||
mem_usage -= free_unpacked(n);
|
||||
n->entry = entry;
|
||||
|
||||
while (window_memory_limit &&
|
||||
window_memory_usage > window_memory_limit &&
|
||||
mem_usage > window_memory_limit &&
|
||||
count > 1) {
|
||||
uint32_t tail = (idx + window - count) % window;
|
||||
free_unpacked(array + tail);
|
||||
mem_usage -= free_unpacked(array + tail);
|
||||
count--;
|
||||
}
|
||||
|
||||
/* We do not compute delta to *create* objects we are not
|
||||
* going to pack.
|
||||
*/
|
||||
if (entry->preferred_base)
|
||||
goto next;
|
||||
|
||||
progress_lock();
|
||||
(*processed)++;
|
||||
if (progress)
|
||||
display_progress(&progress_state, *processed);
|
||||
progress_unlock();
|
||||
|
||||
/*
|
||||
* If the current object is at pack edge, take the depth the
|
||||
* objects that depend on the current object into account
|
||||
@@ -1505,6 +1534,7 @@ static void find_deltas(struct object_entry **list, int window, int depth)
|
||||
|
||||
j = window;
|
||||
while (--j > 0) {
|
||||
int ret;
|
||||
uint32_t other_idx = idx + j;
|
||||
struct unpacked *m;
|
||||
if (other_idx >= window)
|
||||
@@ -1512,8 +1542,11 @@ static void find_deltas(struct object_entry **list, int window, int depth)
|
||||
m = array + other_idx;
|
||||
if (!m->entry)
|
||||
break;
|
||||
if (try_delta(n, m, max_depth) < 0)
|
||||
ret = try_delta(n, m, max_depth, &mem_usage);
|
||||
if (ret < 0)
|
||||
break;
|
||||
else if (ret > 0)
|
||||
best_base = other_idx;
|
||||
}
|
||||
|
||||
/* if we made n a delta, and if n is already at max
|
||||
@@ -1523,6 +1556,23 @@ static void find_deltas(struct object_entry **list, int window, int depth)
|
||||
if (entry->delta && depth <= n->depth)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Move the best delta base up in the window, after the
|
||||
* currently deltified object, to keep it longer. It will
|
||||
* be the first base object to be attempted next.
|
||||
*/
|
||||
if (entry->delta) {
|
||||
struct unpacked swap = array[best_base];
|
||||
int dist = (window + idx - best_base) % window;
|
||||
int dst = best_base;
|
||||
while (dist--) {
|
||||
int src = (dst + 1) % window;
|
||||
array[dst] = array[src];
|
||||
dst = src;
|
||||
}
|
||||
array[dst] = swap;
|
||||
}
|
||||
|
||||
next:
|
||||
idx++;
|
||||
if (count + 1 < window)
|
||||
@@ -1531,9 +1581,6 @@ static void find_deltas(struct object_entry **list, int window, int depth)
|
||||
idx = 0;
|
||||
} while (i > 0);
|
||||
|
||||
if (progress)
|
||||
stop_progress(&progress_state);
|
||||
|
||||
for (i = 0; i < window; ++i) {
|
||||
free_delta_index(array[i].index);
|
||||
free(array[i].data);
|
||||
@@ -1541,21 +1588,145 @@ static void find_deltas(struct object_entry **list, int window, int depth)
|
||||
free(array);
|
||||
}
|
||||
|
||||
#ifdef THREADED_DELTA_SEARCH
|
||||
|
||||
struct thread_params {
|
||||
pthread_t thread;
|
||||
struct object_entry **list;
|
||||
unsigned list_size;
|
||||
int window;
|
||||
int depth;
|
||||
unsigned *processed;
|
||||
};
|
||||
|
||||
static pthread_mutex_t data_request = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_mutex_t data_ready = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_mutex_t data_provider = PTHREAD_MUTEX_INITIALIZER;
|
||||
static struct thread_params *data_requester;
|
||||
|
||||
static void *threaded_find_deltas(void *arg)
|
||||
{
|
||||
struct thread_params *me = arg;
|
||||
|
||||
for (;;) {
|
||||
pthread_mutex_lock(&data_request);
|
||||
data_requester = me;
|
||||
pthread_mutex_unlock(&data_provider);
|
||||
pthread_mutex_lock(&data_ready);
|
||||
pthread_mutex_unlock(&data_request);
|
||||
|
||||
if (!me->list_size)
|
||||
return NULL;
|
||||
|
||||
find_deltas(me->list, me->list_size,
|
||||
me->window, me->depth, me->processed);
|
||||
}
|
||||
}
|
||||
|
||||
static void ll_find_deltas(struct object_entry **list, unsigned list_size,
|
||||
int window, int depth, unsigned *processed)
|
||||
{
|
||||
struct thread_params *target, p[delta_search_threads];
|
||||
int i, ret;
|
||||
unsigned chunk_size;
|
||||
|
||||
if (delta_search_threads <= 1) {
|
||||
find_deltas(list, list_size, window, depth, processed);
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&data_provider);
|
||||
pthread_mutex_lock(&data_ready);
|
||||
|
||||
for (i = 0; i < delta_search_threads; i++) {
|
||||
p[i].window = window;
|
||||
p[i].depth = depth;
|
||||
p[i].processed = processed;
|
||||
ret = pthread_create(&p[i].thread, NULL,
|
||||
threaded_find_deltas, &p[i]);
|
||||
if (ret)
|
||||
die("unable to create thread: %s", strerror(ret));
|
||||
}
|
||||
|
||||
/* this should be auto-tuned somehow */
|
||||
chunk_size = window * 1000;
|
||||
|
||||
do {
|
||||
unsigned sublist_size = chunk_size;
|
||||
if (sublist_size > list_size)
|
||||
sublist_size = list_size;
|
||||
|
||||
/* try to split chunks on "path" boundaries */
|
||||
while (sublist_size < list_size && list[sublist_size]->hash &&
|
||||
list[sublist_size]->hash == list[sublist_size-1]->hash)
|
||||
sublist_size++;
|
||||
|
||||
pthread_mutex_lock(&data_provider);
|
||||
target = data_requester;
|
||||
target->list = list;
|
||||
target->list_size = sublist_size;
|
||||
pthread_mutex_unlock(&data_ready);
|
||||
|
||||
list += sublist_size;
|
||||
list_size -= sublist_size;
|
||||
if (!sublist_size) {
|
||||
pthread_join(target->thread, NULL);
|
||||
i--;
|
||||
}
|
||||
} while (i);
|
||||
}
|
||||
|
||||
#else
|
||||
#define ll_find_deltas find_deltas
|
||||
#endif
|
||||
|
||||
static void prepare_pack(int window, int depth)
|
||||
{
|
||||
struct object_entry **delta_list;
|
||||
uint32_t i;
|
||||
uint32_t i, n, nr_deltas;
|
||||
|
||||
get_object_details();
|
||||
|
||||
if (!window || !depth)
|
||||
if (!nr_objects || !window || !depth)
|
||||
return;
|
||||
|
||||
delta_list = xmalloc(nr_objects * sizeof(*delta_list));
|
||||
for (i = 0; i < nr_objects; i++)
|
||||
delta_list[i] = objects + i;
|
||||
qsort(delta_list, nr_objects, sizeof(*delta_list), type_size_sort);
|
||||
find_deltas(delta_list, window+1, depth);
|
||||
nr_deltas = n = 0;
|
||||
|
||||
for (i = 0; i < nr_objects; i++) {
|
||||
struct object_entry *entry = objects + i;
|
||||
|
||||
if (entry->delta)
|
||||
/* This happens if we decided to reuse existing
|
||||
* delta from a pack. "!no_reuse_delta &&" is implied.
|
||||
*/
|
||||
continue;
|
||||
|
||||
if (entry->size < 50)
|
||||
continue;
|
||||
|
||||
if (entry->no_try_delta)
|
||||
continue;
|
||||
|
||||
if (!entry->preferred_base)
|
||||
nr_deltas++;
|
||||
|
||||
delta_list[n++] = entry;
|
||||
}
|
||||
|
||||
if (nr_deltas) {
|
||||
unsigned nr_done = 0;
|
||||
if (progress)
|
||||
start_progress(&progress_state,
|
||||
"Deltifying %u objects...", "",
|
||||
nr_deltas);
|
||||
qsort(delta_list, n, sizeof(*delta_list), type_size_sort);
|
||||
ll_find_deltas(delta_list, n, window+1, depth, &nr_done);
|
||||
if (progress)
|
||||
stop_progress(&progress_state);
|
||||
if (nr_done != nr_deltas)
|
||||
die("inconsistency with delta count");
|
||||
}
|
||||
free(delta_list);
|
||||
}
|
||||
|
||||
@@ -1591,6 +1762,17 @@ static int git_pack_config(const char *k, const char *v)
|
||||
cache_max_small_delta_size = git_config_int(k, v);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(k, "pack.threads")) {
|
||||
delta_search_threads = git_config_int(k, v);
|
||||
if (delta_search_threads < 1)
|
||||
die("invalid number of threads specified (%d)",
|
||||
delta_search_threads);
|
||||
#ifndef THREADED_DELTA_SEARCH
|
||||
if (delta_search_threads > 1)
|
||||
warning("no threads support, ignoring %s", k);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
return git_default_config(k, v);
|
||||
}
|
||||
|
||||
@@ -1625,15 +1807,19 @@ static void read_object_list_from_stdin(void)
|
||||
}
|
||||
}
|
||||
|
||||
#define OBJECT_ADDED (1u<<20)
|
||||
|
||||
static void show_commit(struct commit *commit)
|
||||
{
|
||||
add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);
|
||||
commit->object.flags |= OBJECT_ADDED;
|
||||
}
|
||||
|
||||
static void show_object(struct object_array_entry *p)
|
||||
{
|
||||
add_preferred_base_object(p->name);
|
||||
add_object_entry(p->item->sha1, p->item->type, p->name, 0);
|
||||
p->item->flags |= OBJECT_ADDED;
|
||||
}
|
||||
|
||||
static void show_edge(struct commit *commit)
|
||||
@@ -1641,6 +1827,86 @@ static void show_edge(struct commit *commit)
|
||||
add_preferred_base(commit->object.sha1);
|
||||
}
|
||||
|
||||
struct in_pack_object {
|
||||
off_t offset;
|
||||
struct object *object;
|
||||
};
|
||||
|
||||
struct in_pack {
|
||||
int alloc;
|
||||
int nr;
|
||||
struct in_pack_object *array;
|
||||
};
|
||||
|
||||
static void mark_in_pack_object(struct object *object, struct packed_git *p, struct in_pack *in_pack)
|
||||
{
|
||||
in_pack->array[in_pack->nr].offset = find_pack_entry_one(object->sha1, p);
|
||||
in_pack->array[in_pack->nr].object = object;
|
||||
in_pack->nr++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare the objects in the offset order, in order to emulate the
|
||||
* "git-rev-list --objects" output that produced the pack originally.
|
||||
*/
|
||||
static int ofscmp(const void *a_, const void *b_)
|
||||
{
|
||||
struct in_pack_object *a = (struct in_pack_object *)a_;
|
||||
struct in_pack_object *b = (struct in_pack_object *)b_;
|
||||
|
||||
if (a->offset < b->offset)
|
||||
return -1;
|
||||
else if (a->offset > b->offset)
|
||||
return 1;
|
||||
else
|
||||
return hashcmp(a->object->sha1, b->object->sha1);
|
||||
}
|
||||
|
||||
static void add_objects_in_unpacked_packs(struct rev_info *revs)
|
||||
{
|
||||
struct packed_git *p;
|
||||
struct in_pack in_pack;
|
||||
uint32_t i;
|
||||
|
||||
memset(&in_pack, 0, sizeof(in_pack));
|
||||
|
||||
for (p = packed_git; p; p = p->next) {
|
||||
const unsigned char *sha1;
|
||||
struct object *o;
|
||||
|
||||
for (i = 0; i < revs->num_ignore_packed; i++) {
|
||||
if (matches_pack_name(p, revs->ignore_packed[i]))
|
||||
break;
|
||||
}
|
||||
if (revs->num_ignore_packed <= i)
|
||||
continue;
|
||||
if (open_pack_index(p))
|
||||
die("cannot open pack index");
|
||||
|
||||
ALLOC_GROW(in_pack.array,
|
||||
in_pack.nr + p->num_objects,
|
||||
in_pack.alloc);
|
||||
|
||||
for (i = 0; i < p->num_objects; i++) {
|
||||
sha1 = nth_packed_object_sha1(p, i);
|
||||
o = lookup_unknown_object(sha1);
|
||||
if (!(o->flags & OBJECT_ADDED))
|
||||
mark_in_pack_object(o, p, &in_pack);
|
||||
o->flags |= OBJECT_ADDED;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_pack.nr) {
|
||||
qsort(in_pack.array, in_pack.nr, sizeof(in_pack.array[0]),
|
||||
ofscmp);
|
||||
for (i = 0; i < in_pack.nr; i++) {
|
||||
struct object *o = in_pack.array[i].object;
|
||||
add_object_entry(o->sha1, o->type, "", 0);
|
||||
}
|
||||
}
|
||||
free(in_pack.array);
|
||||
}
|
||||
|
||||
static void get_object_list(int ac, const char **av)
|
||||
{
|
||||
struct rev_info revs;
|
||||
@@ -1672,6 +1938,9 @@ static void get_object_list(int ac, const char **av)
|
||||
prepare_revision_walk(&revs);
|
||||
mark_edges_uninteresting(revs.commits, &revs, show_edge);
|
||||
traverse_commit_list(&revs, show_commit, show_object);
|
||||
|
||||
if (keep_unreachable)
|
||||
add_objects_in_unpacked_packs(&revs);
|
||||
}
|
||||
|
||||
static int adjust_perm(const char *path, mode_t mode)
|
||||
@@ -1750,6 +2019,18 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
|
||||
usage(pack_usage);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--threads=")) {
|
||||
char *end;
|
||||
delta_search_threads = strtoul(arg+10, &end, 0);
|
||||
if (!arg[10] || *end || delta_search_threads < 1)
|
||||
usage(pack_usage);
|
||||
#ifndef THREADED_DELTA_SEARCH
|
||||
if (delta_search_threads > 1)
|
||||
warning("no threads support, "
|
||||
"ignoring %s", arg);
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--depth=")) {
|
||||
char *end;
|
||||
depth = strtoul(arg+8, &end, 0);
|
||||
@@ -1789,6 +2070,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
|
||||
use_internal_rev_list = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--keep-unreachable", arg)) {
|
||||
keep_unreachable = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--unpacked", arg) ||
|
||||
!prefixcmp(arg, "--unpacked=") ||
|
||||
!strcmp("--reflog", arg) ||
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
#include "builtin.h"
|
||||
#include "remote.h"
|
||||
|
||||
static const char push_usage[] = "git-push [--all] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]";
|
||||
static const char push_usage[] = "git-push [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]";
|
||||
|
||||
static int all, force, thin, verbose;
|
||||
static int all, dry_run, force, thin, verbose;
|
||||
static const char *receivepack;
|
||||
|
||||
static const char **refspec;
|
||||
@@ -69,6 +69,8 @@ static int do_push(const char *repo)
|
||||
argc = 1;
|
||||
if (all)
|
||||
argv[argc++] = "--all";
|
||||
if (dry_run)
|
||||
argv[argc++] = "--dry-run";
|
||||
if (force)
|
||||
argv[argc++] = "--force";
|
||||
if (receivepack)
|
||||
@@ -147,6 +149,10 @@ int cmd_push(int argc, const char **argv, const char *prefix)
|
||||
all = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--dry-run")) {
|
||||
dry_run = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--tags")) {
|
||||
add_refspec("refs/tags/*");
|
||||
continue;
|
||||
|
||||
@@ -66,40 +66,15 @@ static int write_rr(struct path_list *rr, int out_fd)
|
||||
return commit_lock_file(&write_lock);
|
||||
}
|
||||
|
||||
struct buffer {
|
||||
char *ptr;
|
||||
int nr, alloc;
|
||||
};
|
||||
|
||||
static void append_line(struct buffer *buffer, const char *line)
|
||||
{
|
||||
int len = strlen(line);
|
||||
|
||||
if (buffer->nr + len > buffer->alloc) {
|
||||
buffer->alloc = alloc_nr(buffer->nr + len);
|
||||
buffer->ptr = xrealloc(buffer->ptr, buffer->alloc);
|
||||
}
|
||||
memcpy(buffer->ptr + buffer->nr, line, len);
|
||||
buffer->nr += len;
|
||||
}
|
||||
|
||||
static void clear_buffer(struct buffer *buffer)
|
||||
{
|
||||
free(buffer->ptr);
|
||||
buffer->ptr = NULL;
|
||||
buffer->nr = buffer->alloc = 0;
|
||||
}
|
||||
|
||||
static int handle_file(const char *path,
|
||||
unsigned char *sha1, const char *output)
|
||||
{
|
||||
SHA_CTX ctx;
|
||||
char buf[1024];
|
||||
int hunk = 0, hunk_no = 0;
|
||||
struct buffer minus = { NULL, 0, 0 }, plus = { NULL, 0, 0 };
|
||||
struct buffer *one = &minus, *two = +
|
||||
struct strbuf one, two;
|
||||
FILE *f = fopen(path, "r");
|
||||
FILE *out;
|
||||
FILE *out = NULL;
|
||||
|
||||
if (!f)
|
||||
return error("Could not open %s", path);
|
||||
@@ -110,51 +85,50 @@ static int handle_file(const char *path,
|
||||
fclose(f);
|
||||
return error("Could not write %s", output);
|
||||
}
|
||||
} else
|
||||
out = NULL;
|
||||
}
|
||||
|
||||
if (sha1)
|
||||
SHA1_Init(&ctx);
|
||||
|
||||
strbuf_init(&one, 0);
|
||||
strbuf_init(&two, 0);
|
||||
while (fgets(buf, sizeof(buf), f)) {
|
||||
if (!prefixcmp(buf, "<<<<<<< "))
|
||||
hunk = 1;
|
||||
else if (!prefixcmp(buf, "======="))
|
||||
hunk = 2;
|
||||
else if (!prefixcmp(buf, ">>>>>>> ")) {
|
||||
int one_is_longer = (one->nr > two->nr);
|
||||
int common_len = one_is_longer ? two->nr : one->nr;
|
||||
int cmp = memcmp(one->ptr, two->ptr, common_len);
|
||||
int cmp = strbuf_cmp(&one, &two);
|
||||
|
||||
hunk_no++;
|
||||
hunk = 0;
|
||||
if ((cmp > 0) || ((cmp == 0) && one_is_longer)) {
|
||||
struct buffer *swap = one;
|
||||
one = two;
|
||||
two = swap;
|
||||
if (cmp > 0) {
|
||||
strbuf_swap(&one, &two);
|
||||
}
|
||||
if (out) {
|
||||
fputs("<<<<<<<\n", out);
|
||||
fwrite(one->ptr, one->nr, 1, out);
|
||||
fwrite(one.buf, one.len, 1, out);
|
||||
fputs("=======\n", out);
|
||||
fwrite(two->ptr, two->nr, 1, out);
|
||||
fwrite(two.buf, two.len, 1, out);
|
||||
fputs(">>>>>>>\n", out);
|
||||
}
|
||||
if (sha1) {
|
||||
SHA1_Update(&ctx, one->ptr, one->nr);
|
||||
SHA1_Update(&ctx, "\0", 1);
|
||||
SHA1_Update(&ctx, two->ptr, two->nr);
|
||||
SHA1_Update(&ctx, "\0", 1);
|
||||
SHA1_Update(&ctx, one.buf ? one.buf : "",
|
||||
one.len + 1);
|
||||
SHA1_Update(&ctx, two.buf ? two.buf : "",
|
||||
two.len + 1);
|
||||
}
|
||||
clear_buffer(one);
|
||||
clear_buffer(two);
|
||||
strbuf_reset(&one);
|
||||
strbuf_reset(&two);
|
||||
} else if (hunk == 1)
|
||||
append_line(one, buf);
|
||||
strbuf_addstr(&one, buf);
|
||||
else if (hunk == 2)
|
||||
append_line(two, buf);
|
||||
strbuf_addstr(&two, buf);
|
||||
else if (out)
|
||||
fputs(buf, out);
|
||||
}
|
||||
strbuf_release(&one);
|
||||
strbuf_release(&two);
|
||||
|
||||
fclose(f);
|
||||
if (out)
|
||||
|
||||
279
builtin-reset.c
Normal file
279
builtin-reset.c
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* "git reset" builtin command
|
||||
*
|
||||
* Copyright (c) 2007 Carlos Rica
|
||||
*
|
||||
* Based on git-reset.sh, which is
|
||||
*
|
||||
* Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "tag.h"
|
||||
#include "object.h"
|
||||
#include "commit.h"
|
||||
#include "run-command.h"
|
||||
#include "refs.h"
|
||||
#include "diff.h"
|
||||
#include "diffcore.h"
|
||||
#include "tree.h"
|
||||
|
||||
static const char builtin_reset_usage[] =
|
||||
"git-reset [--mixed | --soft | --hard] [<commit-ish>] [ [--] <paths>...]";
|
||||
|
||||
static char *args_to_str(const char **argv)
|
||||
{
|
||||
char *buf = NULL;
|
||||
unsigned long len, space = 0, nr = 0;
|
||||
|
||||
for (; *argv; argv++) {
|
||||
len = strlen(*argv);
|
||||
ALLOC_GROW(buf, nr + 1 + len, space);
|
||||
if (nr)
|
||||
buf[nr++] = ' ';
|
||||
memcpy(buf + nr, *argv, len);
|
||||
nr += len;
|
||||
}
|
||||
ALLOC_GROW(buf, nr + 1, space);
|
||||
buf[nr] = '\0';
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static inline int is_merge(void)
|
||||
{
|
||||
return !access(git_path("MERGE_HEAD"), F_OK);
|
||||
}
|
||||
|
||||
static int unmerged_files(void)
|
||||
{
|
||||
char b;
|
||||
ssize_t len;
|
||||
struct child_process cmd;
|
||||
const char *argv_ls_files[] = {"ls-files", "--unmerged", NULL};
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.argv = argv_ls_files;
|
||||
cmd.git_cmd = 1;
|
||||
cmd.out = -1;
|
||||
|
||||
if (start_command(&cmd))
|
||||
die("Could not run sub-command: git ls-files");
|
||||
|
||||
len = xread(cmd.out, &b, 1);
|
||||
if (len < 0)
|
||||
die("Could not read output from git ls-files: %s",
|
||||
strerror(errno));
|
||||
finish_command(&cmd);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int reset_index_file(const unsigned char *sha1, int is_hard_reset)
|
||||
{
|
||||
int i = 0;
|
||||
const char *args[6];
|
||||
|
||||
args[i++] = "read-tree";
|
||||
args[i++] = "-v";
|
||||
args[i++] = "--reset";
|
||||
if (is_hard_reset)
|
||||
args[i++] = "-u";
|
||||
args[i++] = sha1_to_hex(sha1);
|
||||
args[i] = NULL;
|
||||
|
||||
return run_command_v_opt(args, RUN_GIT_CMD);
|
||||
}
|
||||
|
||||
static void print_new_head_line(struct commit *commit)
|
||||
{
|
||||
const char *hex, *dots = "...", *body;
|
||||
|
||||
hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
|
||||
if (!hex) {
|
||||
hex = sha1_to_hex(commit->object.sha1);
|
||||
dots = "";
|
||||
}
|
||||
printf("HEAD is now at %s%s", hex, dots);
|
||||
body = strstr(commit->buffer, "\n\n");
|
||||
if (body) {
|
||||
const char *eol;
|
||||
size_t len;
|
||||
body += 2;
|
||||
eol = strchr(body, '\n');
|
||||
len = eol ? eol - body : strlen(body);
|
||||
printf(" %.*s\n", (int) len, body);
|
||||
}
|
||||
else
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int update_index_refresh(void)
|
||||
{
|
||||
const char *argv_update_index[] = {"update-index", "--refresh", NULL};
|
||||
return run_command_v_opt(argv_update_index, RUN_GIT_CMD);
|
||||
}
|
||||
|
||||
static void update_index_from_diff(struct diff_queue_struct *q,
|
||||
struct diff_options *opt, void *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* do_diff_cache() mangled the index */
|
||||
discard_cache();
|
||||
read_cache();
|
||||
|
||||
for (i = 0; i < q->nr; i++) {
|
||||
struct diff_filespec *one = q->queue[i]->one;
|
||||
if (one->mode) {
|
||||
struct cache_entry *ce;
|
||||
ce = make_cache_entry(one->mode, one->sha1, one->path,
|
||||
0, 0);
|
||||
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD |
|
||||
ADD_CACHE_OK_TO_REPLACE);
|
||||
} else
|
||||
remove_file_from_cache(one->path);
|
||||
}
|
||||
}
|
||||
|
||||
static int read_from_tree(const char *prefix, const char **argv,
|
||||
unsigned char *tree_sha1)
|
||||
{
|
||||
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
|
||||
int index_fd;
|
||||
struct diff_options opt;
|
||||
|
||||
memset(&opt, 0, sizeof(opt));
|
||||
diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt);
|
||||
opt.output_format = DIFF_FORMAT_CALLBACK;
|
||||
opt.format_callback = update_index_from_diff;
|
||||
|
||||
index_fd = hold_locked_index(lock, 1);
|
||||
read_cache();
|
||||
if (do_diff_cache(tree_sha1, &opt))
|
||||
return 1;
|
||||
diffcore_std(&opt);
|
||||
diff_flush(&opt);
|
||||
return write_cache(index_fd, active_cache, active_nr) ||
|
||||
close(index_fd) ||
|
||||
commit_locked_index(lock);
|
||||
}
|
||||
|
||||
static void prepend_reflog_action(const char *action, char *buf, size_t size)
|
||||
{
|
||||
const char *sep = ": ";
|
||||
const char *rla = getenv("GIT_REFLOG_ACTION");
|
||||
if (!rla)
|
||||
rla = sep = "";
|
||||
if (snprintf(buf, size, "%s%s%s", rla, sep, action) >= size)
|
||||
warning("Reflog action message too long: %.*s...", 50, buf);
|
||||
}
|
||||
|
||||
enum reset_type { MIXED, SOFT, HARD, NONE };
|
||||
static char *reset_type_names[] = { "mixed", "soft", "hard", NULL };
|
||||
|
||||
int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i = 1, reset_type = NONE, update_ref_status = 0;
|
||||
const char *rev = "HEAD";
|
||||
unsigned char sha1[20], *orig = NULL, sha1_orig[20],
|
||||
*old_orig = NULL, sha1_old_orig[20];
|
||||
struct commit *commit;
|
||||
char *reflog_action, msg[1024];
|
||||
|
||||
git_config(git_default_config);
|
||||
|
||||
reflog_action = args_to_str(argv);
|
||||
setenv("GIT_REFLOG_ACTION", reflog_action, 0);
|
||||
|
||||
if (i < argc) {
|
||||
if (!strcmp(argv[i], "--mixed")) {
|
||||
reset_type = MIXED;
|
||||
i++;
|
||||
}
|
||||
else if (!strcmp(argv[i], "--soft")) {
|
||||
reset_type = SOFT;
|
||||
i++;
|
||||
}
|
||||
else if (!strcmp(argv[i], "--hard")) {
|
||||
reset_type = HARD;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < argc && argv[i][0] != '-')
|
||||
rev = argv[i++];
|
||||
|
||||
if (get_sha1(rev, sha1))
|
||||
die("Failed to resolve '%s' as a valid ref.", rev);
|
||||
|
||||
commit = lookup_commit_reference(sha1);
|
||||
if (!commit)
|
||||
die("Could not parse object '%s'.", rev);
|
||||
hashcpy(sha1, commit->object.sha1);
|
||||
|
||||
if (i < argc && !strcmp(argv[i], "--"))
|
||||
i++;
|
||||
else if (i < argc && argv[i][0] == '-')
|
||||
usage(builtin_reset_usage);
|
||||
|
||||
/* git reset tree [--] paths... can be used to
|
||||
* load chosen paths from the tree into the index without
|
||||
* affecting the working tree nor HEAD. */
|
||||
if (i < argc) {
|
||||
if (reset_type == MIXED)
|
||||
warning("--mixed option is deprecated with paths.");
|
||||
else if (reset_type != NONE)
|
||||
die("Cannot do %s reset with paths.",
|
||||
reset_type_names[reset_type]);
|
||||
if (read_from_tree(prefix, argv + i, sha1))
|
||||
return 1;
|
||||
return update_index_refresh() ? 1 : 0;
|
||||
}
|
||||
if (reset_type == NONE)
|
||||
reset_type = MIXED; /* by default */
|
||||
|
||||
/* Soft reset does not touch the index file nor the working tree
|
||||
* at all, but requires them in a good order. Other resets reset
|
||||
* the index file to the tree object we are switching to. */
|
||||
if (reset_type == SOFT) {
|
||||
if (is_merge() || unmerged_files())
|
||||
die("Cannot do a soft reset in the middle of a merge.");
|
||||
}
|
||||
else if (reset_index_file(sha1, (reset_type == HARD)))
|
||||
die("Could not reset index file to revision '%s'.", rev);
|
||||
|
||||
/* Any resets update HEAD to the head being switched to,
|
||||
* saving the previous head in ORIG_HEAD before. */
|
||||
if (!get_sha1("ORIG_HEAD", sha1_old_orig))
|
||||
old_orig = sha1_old_orig;
|
||||
if (!get_sha1("HEAD", sha1_orig)) {
|
||||
orig = sha1_orig;
|
||||
prepend_reflog_action("updating ORIG_HEAD", msg, sizeof(msg));
|
||||
update_ref(msg, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
|
||||
}
|
||||
else if (old_orig)
|
||||
delete_ref("ORIG_HEAD", old_orig);
|
||||
prepend_reflog_action("updating HEAD", msg, sizeof(msg));
|
||||
update_ref_status = update_ref(msg, "HEAD", sha1, orig, 0, MSG_ON_ERR);
|
||||
|
||||
switch (reset_type) {
|
||||
case HARD:
|
||||
if (!update_ref_status)
|
||||
print_new_head_line(commit);
|
||||
break;
|
||||
case SOFT: /* Nothing else to do. */
|
||||
break;
|
||||
case MIXED: /* Report what has not been updated. */
|
||||
update_index_refresh();
|
||||
break;
|
||||
}
|
||||
|
||||
unlink(git_path("MERGE_HEAD"));
|
||||
unlink(git_path("rr-cache/MERGE_RR"));
|
||||
unlink(git_path("MERGE_MSG"));
|
||||
unlink(git_path("SQUASH_MSG"));
|
||||
|
||||
free(reflog_action);
|
||||
|
||||
return update_ref_status;
|
||||
}
|
||||
@@ -80,13 +80,13 @@ static void show_commit(struct commit *commit)
|
||||
putchar('\n');
|
||||
|
||||
if (revs.verbose_header) {
|
||||
char *buf = NULL;
|
||||
unsigned long buflen = 0;
|
||||
pretty_print_commit(revs.commit_format, commit, ~0,
|
||||
&buf, &buflen,
|
||||
revs.abbrev, NULL, NULL, revs.date_mode);
|
||||
printf("%s%c", buf, hdr_termination);
|
||||
free(buf);
|
||||
struct strbuf buf;
|
||||
strbuf_init(&buf, 0);
|
||||
pretty_print_commit(revs.commit_format, commit,
|
||||
&buf, revs.abbrev, NULL, NULL, revs.date_mode);
|
||||
if (buf.len)
|
||||
printf("%s%c", buf.buf, hdr_termination);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
maybe_flush_or_die(stdout, "stdout");
|
||||
if (commit->parents) {
|
||||
@@ -189,7 +189,7 @@ static int count_interesting_parents(struct commit *commit)
|
||||
return count;
|
||||
}
|
||||
|
||||
static inline int halfway(struct commit_list *p, int distance, int nr)
|
||||
static inline int halfway(struct commit_list *p, int nr)
|
||||
{
|
||||
/*
|
||||
* Don't short-cut something we are not going to return!
|
||||
@@ -202,8 +202,7 @@ static inline int halfway(struct commit_list *p, int distance, int nr)
|
||||
* 2 and 3 are halfway of 5.
|
||||
* 3 is halfway of 6 but 2 and 4 are not.
|
||||
*/
|
||||
distance *= 2;
|
||||
switch (distance - nr) {
|
||||
switch (2 * weight(p) - nr) {
|
||||
case -1: case 0: case 1:
|
||||
return 1;
|
||||
default:
|
||||
@@ -255,6 +254,30 @@ static void show_list(const char *debug, int counted, int nr,
|
||||
}
|
||||
#endif /* DEBUG_BISECT */
|
||||
|
||||
static struct commit_list *best_bisection(struct commit_list *list, int nr)
|
||||
{
|
||||
struct commit_list *p, *best;
|
||||
int best_distance = -1;
|
||||
|
||||
best = list;
|
||||
for (p = list; p; p = p->next) {
|
||||
int distance;
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (revs.prune_fn && !(flags & TREECHANGE))
|
||||
continue;
|
||||
distance = weight(p);
|
||||
if (nr - distance < distance)
|
||||
distance = nr - distance;
|
||||
if (distance > best_distance) {
|
||||
best = p;
|
||||
best_distance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
/*
|
||||
* zero or positive weight is the number of interesting commits it can
|
||||
* reach, including itself. Especially, weight = 0 means it does not
|
||||
@@ -268,39 +291,12 @@ static void show_list(const char *debug, int counted, int nr,
|
||||
* unknown. After running count_distance() first, they will get zero
|
||||
* or positive distance.
|
||||
*/
|
||||
|
||||
static struct commit_list *find_bisection(struct commit_list *list,
|
||||
int *reaches, int *all)
|
||||
static struct commit_list *do_find_bisection(struct commit_list *list,
|
||||
int nr, int *weights)
|
||||
{
|
||||
int n, nr, on_list, counted, distance;
|
||||
struct commit_list *p, *best, *next, *last;
|
||||
int *weights;
|
||||
int n, counted;
|
||||
struct commit_list *p;
|
||||
|
||||
show_list("bisection 2 entry", 0, 0, list);
|
||||
|
||||
/*
|
||||
* Count the number of total and tree-changing items on the
|
||||
* list, while reversing the list.
|
||||
*/
|
||||
for (nr = on_list = 0, last = NULL, p = list;
|
||||
p;
|
||||
p = next) {
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
next = p->next;
|
||||
if (flags & UNINTERESTING)
|
||||
continue;
|
||||
p->next = last;
|
||||
last = p;
|
||||
if (!revs.prune_fn || (flags & TREECHANGE))
|
||||
nr++;
|
||||
on_list++;
|
||||
}
|
||||
list = last;
|
||||
show_list("bisection 2 sorted", 0, nr, list);
|
||||
|
||||
*all = nr;
|
||||
weights = xcalloc(on_list, sizeof(*weights));
|
||||
counted = 0;
|
||||
|
||||
for (n = 0, p = list; p; p = p->next) {
|
||||
@@ -349,20 +345,14 @@ static struct commit_list *find_bisection(struct commit_list *list,
|
||||
for (p = list; p; p = p->next) {
|
||||
if (p->item->object.flags & UNINTERESTING)
|
||||
continue;
|
||||
n = weight(p);
|
||||
if (n != -2)
|
||||
if (weight(p) != -2)
|
||||
continue;
|
||||
distance = count_distance(p);
|
||||
weight_set(p, count_distance(p));
|
||||
clear_distance(list);
|
||||
weight_set(p, distance);
|
||||
|
||||
/* Does it happen to be at exactly half-way? */
|
||||
if (halfway(p, distance, nr)) {
|
||||
p->next = NULL;
|
||||
*reaches = distance;
|
||||
free(weights);
|
||||
if (halfway(p, nr))
|
||||
return p;
|
||||
}
|
||||
counted++;
|
||||
}
|
||||
|
||||
@@ -399,38 +389,59 @@ static struct commit_list *find_bisection(struct commit_list *list,
|
||||
weight_set(p, weight(q));
|
||||
|
||||
/* Does it happen to be at exactly half-way? */
|
||||
distance = weight(p);
|
||||
if (halfway(p, distance, nr)) {
|
||||
p->next = NULL;
|
||||
*reaches = distance;
|
||||
free(weights);
|
||||
if (halfway(p, nr))
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
show_list("bisection 2 counted all", counted, nr, list);
|
||||
|
||||
/* Then find the best one */
|
||||
counted = -1;
|
||||
best = list;
|
||||
for (p = list; p; p = p->next) {
|
||||
return best_bisection(list, nr);
|
||||
}
|
||||
|
||||
static struct commit_list *find_bisection(struct commit_list *list,
|
||||
int *reaches, int *all)
|
||||
{
|
||||
int nr, on_list;
|
||||
struct commit_list *p, *best, *next, *last;
|
||||
int *weights;
|
||||
|
||||
show_list("bisection 2 entry", 0, 0, list);
|
||||
|
||||
/*
|
||||
* Count the number of total and tree-changing items on the
|
||||
* list, while reversing the list.
|
||||
*/
|
||||
for (nr = on_list = 0, last = NULL, p = list;
|
||||
p;
|
||||
p = next) {
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (revs.prune_fn && !(flags & TREECHANGE))
|
||||
next = p->next;
|
||||
if (flags & UNINTERESTING)
|
||||
continue;
|
||||
distance = weight(p);
|
||||
if (nr - distance < distance)
|
||||
distance = nr - distance;
|
||||
if (distance > counted) {
|
||||
best = p;
|
||||
counted = distance;
|
||||
*reaches = weight(p);
|
||||
}
|
||||
p->next = last;
|
||||
last = p;
|
||||
if (!revs.prune_fn || (flags & TREECHANGE))
|
||||
nr++;
|
||||
on_list++;
|
||||
}
|
||||
if (best)
|
||||
list = last;
|
||||
show_list("bisection 2 sorted", 0, nr, list);
|
||||
|
||||
*all = nr;
|
||||
weights = xcalloc(on_list, sizeof(*weights));
|
||||
|
||||
/* Do the real work of finding bisection commit. */
|
||||
best = do_find_bisection(list, nr, weights);
|
||||
|
||||
if (best) {
|
||||
best->next = NULL;
|
||||
*reaches = weight(best);
|
||||
}
|
||||
free(weights);
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
|
||||
@@ -168,9 +168,7 @@ static void set_author_ident_env(const char *message)
|
||||
char *line, *pend, *email, *timestamp;
|
||||
|
||||
p += 7;
|
||||
line = xmalloc(eol + 1 - p);
|
||||
memcpy(line, p, eol - p);
|
||||
line[eol - p] = '\0';
|
||||
line = xmemdupz(p, eol - p);
|
||||
email = strchr(line, '<');
|
||||
if (!email)
|
||||
die ("Could not extract author email from %s",
|
||||
|
||||
@@ -227,7 +227,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
|
||||
|
||||
if (remove_file_from_cache(path))
|
||||
die("git-rm: unable to remove %s", path);
|
||||
cache_tree_invalidate_path(active_cache_tree, path);
|
||||
}
|
||||
|
||||
if (show_only)
|
||||
|
||||
@@ -39,10 +39,7 @@ static void insert_author_oneline(struct path_list *list,
|
||||
while (authorlen > 0 && isspace(author[authorlen - 1]))
|
||||
authorlen--;
|
||||
|
||||
buffer = xmalloc(authorlen + 1);
|
||||
memcpy(buffer, author, authorlen);
|
||||
buffer[authorlen] = '\0';
|
||||
|
||||
buffer = xmemdupz(author, authorlen);
|
||||
item = path_list_insert(buffer, list);
|
||||
if (item->util == NULL)
|
||||
item->util = xcalloc(1, sizeof(struct path_list));
|
||||
@@ -66,13 +63,9 @@ static void insert_author_oneline(struct path_list *list,
|
||||
oneline++;
|
||||
onelinelen--;
|
||||
}
|
||||
|
||||
while (onelinelen > 0 && isspace(oneline[onelinelen - 1]))
|
||||
onelinelen--;
|
||||
|
||||
buffer = xmalloc(onelinelen + 1);
|
||||
memcpy(buffer, oneline, onelinelen);
|
||||
buffer[onelinelen] = '\0';
|
||||
buffer = xmemdupz(oneline, onelinelen);
|
||||
|
||||
if (dot3) {
|
||||
int dot3len = strlen(dot3);
|
||||
|
||||
@@ -259,16 +259,15 @@ static void join_revs(struct commit_list **list_p,
|
||||
|
||||
static void show_one_commit(struct commit *commit, int no_name)
|
||||
{
|
||||
char *pretty = NULL;
|
||||
struct strbuf pretty;
|
||||
const char *pretty_str = "(unavailable)";
|
||||
unsigned long pretty_len = 0;
|
||||
struct commit_name *name = commit->util;
|
||||
|
||||
strbuf_init(&pretty, 0);
|
||||
if (commit->object.parsed) {
|
||||
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
|
||||
&pretty, &pretty_len,
|
||||
0, NULL, NULL, 0);
|
||||
pretty_str = pretty;
|
||||
pretty_print_commit(CMIT_FMT_ONELINE, commit,
|
||||
&pretty, 0, NULL, NULL, 0);
|
||||
pretty_str = pretty.buf;
|
||||
}
|
||||
if (!prefixcmp(pretty_str, "[PATCH] "))
|
||||
pretty_str += 8;
|
||||
@@ -289,7 +288,7 @@ static void show_one_commit(struct commit *commit, int no_name)
|
||||
find_unique_abbrev(commit->object.sha1, 7));
|
||||
}
|
||||
puts(pretty_str);
|
||||
free(pretty);
|
||||
strbuf_release(&pretty);
|
||||
}
|
||||
|
||||
static char *ref_name[MAX_REVS + 1];
|
||||
|
||||
@@ -8,17 +8,13 @@
|
||||
*/
|
||||
static size_t cleanup(char *line, size_t len)
|
||||
{
|
||||
if (len) {
|
||||
if (line[len - 1] == '\n')
|
||||
len--;
|
||||
|
||||
while (len) {
|
||||
unsigned char c = line[len - 1];
|
||||
if (!isspace(c))
|
||||
break;
|
||||
len--;
|
||||
}
|
||||
while (len) {
|
||||
unsigned char c = line[len - 1];
|
||||
if (!isspace(c))
|
||||
break;
|
||||
len--;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -34,66 +30,60 @@ static size_t cleanup(char *line, size_t len)
|
||||
* If the input has only empty lines and spaces,
|
||||
* no output will be produced.
|
||||
*
|
||||
* If last line has a newline at the end, it will be removed.
|
||||
* If last line does not have a newline at the end, one is added.
|
||||
*
|
||||
* Enable skip_comments to skip every line starting with "#".
|
||||
*/
|
||||
size_t stripspace(char *buffer, size_t length, int skip_comments)
|
||||
void stripspace(struct strbuf *sb, int skip_comments)
|
||||
{
|
||||
int empties = -1;
|
||||
int empties = 0;
|
||||
size_t i, j, len, newlen;
|
||||
char *eol;
|
||||
|
||||
for (i = j = 0; i < length; i += len, j += newlen) {
|
||||
eol = memchr(buffer + i, '\n', length - i);
|
||||
len = eol ? eol - (buffer + i) + 1 : length - i;
|
||||
/* We may have to add a newline. */
|
||||
strbuf_grow(sb, 1);
|
||||
|
||||
if (skip_comments && len && buffer[i] == '#') {
|
||||
for (i = j = 0; i < sb->len; i += len, j += newlen) {
|
||||
eol = memchr(sb->buf + i, '\n', sb->len - i);
|
||||
len = eol ? eol - (sb->buf + i) + 1 : sb->len - i;
|
||||
|
||||
if (skip_comments && len && sb->buf[i] == '#') {
|
||||
newlen = 0;
|
||||
continue;
|
||||
}
|
||||
newlen = cleanup(buffer + i, len);
|
||||
newlen = cleanup(sb->buf + i, len);
|
||||
|
||||
/* Not just an empty line? */
|
||||
if (newlen) {
|
||||
if (empties != -1)
|
||||
buffer[j++] = '\n';
|
||||
if (empties > 0)
|
||||
buffer[j++] = '\n';
|
||||
if (empties > 0 && j > 0)
|
||||
sb->buf[j++] = '\n';
|
||||
empties = 0;
|
||||
memmove(buffer + j, buffer + i, newlen);
|
||||
continue;
|
||||
memmove(sb->buf + j, sb->buf + i, newlen);
|
||||
sb->buf[newlen + j++] = '\n';
|
||||
} else {
|
||||
empties++;
|
||||
}
|
||||
if (empties < 0)
|
||||
continue;
|
||||
empties++;
|
||||
}
|
||||
|
||||
return j;
|
||||
strbuf_setlen(sb, j);
|
||||
}
|
||||
|
||||
int cmd_stripspace(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
char *buffer;
|
||||
unsigned long size;
|
||||
struct strbuf buf;
|
||||
int strip_comments = 0;
|
||||
|
||||
if (argc > 1 && (!strcmp(argv[1], "-s") ||
|
||||
!strcmp(argv[1], "--strip-comments")))
|
||||
strip_comments = 1;
|
||||
|
||||
size = 1024;
|
||||
buffer = xmalloc(size);
|
||||
if (read_fd(0, &buffer, &size)) {
|
||||
free(buffer);
|
||||
strbuf_init(&buf, 0);
|
||||
if (strbuf_read(&buf, 0, 1024) < 0)
|
||||
die("could not read the input");
|
||||
}
|
||||
|
||||
size = stripspace(buffer, size, strip_comments);
|
||||
write_or_die(1, buffer, size);
|
||||
if (size)
|
||||
putc('\n', stdout);
|
||||
stripspace(&buf, strip_comments);
|
||||
|
||||
free(buffer);
|
||||
write_or_die(1, buf.buf, buf.len);
|
||||
strbuf_release(&buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -17,12 +17,11 @@ static const char builtin_tag_usage[] =
|
||||
|
||||
static char signingkey[1000];
|
||||
|
||||
static void launch_editor(const char *path, char **buffer, unsigned long *len)
|
||||
static void launch_editor(const char *path, struct strbuf *buffer)
|
||||
{
|
||||
const char *editor, *terminal;
|
||||
struct child_process child;
|
||||
const char *args[3];
|
||||
int fd;
|
||||
|
||||
editor = getenv("GIT_EDITOR");
|
||||
if (!editor && editor_program)
|
||||
@@ -52,15 +51,9 @@ static void launch_editor(const char *path, char **buffer, unsigned long *len)
|
||||
if (run_command(&child))
|
||||
die("There was a problem with the editor %s.", editor);
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
die("could not open '%s': %s", path, strerror(errno));
|
||||
if (read_fd(fd, buffer, len)) {
|
||||
free(*buffer);
|
||||
if (strbuf_read_file(buffer, path, 0) < 0)
|
||||
die("could not read message file '%s': %s",
|
||||
path, strerror(errno));
|
||||
}
|
||||
close(fd);
|
||||
path, strerror(errno));
|
||||
}
|
||||
|
||||
struct tag_filter {
|
||||
@@ -184,7 +177,7 @@ static int verify_tag(const char *name, const char *ref,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t do_sign(char *buffer, size_t size, size_t max)
|
||||
static int do_sign(struct strbuf *buffer)
|
||||
{
|
||||
struct child_process gpg;
|
||||
const char *args[4];
|
||||
@@ -216,22 +209,22 @@ static ssize_t do_sign(char *buffer, size_t size, size_t max)
|
||||
if (start_command(&gpg))
|
||||
return error("could not run gpg.");
|
||||
|
||||
if (write_in_full(gpg.in, buffer, size) != size) {
|
||||
if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
|
||||
close(gpg.in);
|
||||
finish_command(&gpg);
|
||||
return error("gpg did not accept the tag data");
|
||||
}
|
||||
close(gpg.in);
|
||||
gpg.close_in = 0;
|
||||
len = read_in_full(gpg.out, buffer + size, max - size);
|
||||
len = strbuf_read(buffer, gpg.out, 1024);
|
||||
|
||||
if (finish_command(&gpg) || !len || len < 0)
|
||||
return error("gpg failed to sign the tag");
|
||||
|
||||
if (len == max - size)
|
||||
if (len < 0)
|
||||
return error("could not read the entire signature from gpg.");
|
||||
|
||||
return size + len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char tag_template[] =
|
||||
@@ -254,15 +247,13 @@ static int git_tag_config(const char *var, const char *value)
|
||||
return git_default_config(var, value);
|
||||
}
|
||||
|
||||
#define MAX_SIGNATURE_LENGTH 1024
|
||||
/* message must be NULL or allocated, it will be reallocated and freed */
|
||||
static void create_tag(const unsigned char *object, const char *tag,
|
||||
char *message, int sign, unsigned char *result)
|
||||
struct strbuf *buf, int message, int sign,
|
||||
unsigned char *result)
|
||||
{
|
||||
enum object_type type;
|
||||
char header_buf[1024], *buffer = NULL;
|
||||
int header_len, max_size;
|
||||
unsigned long size = 0;
|
||||
char header_buf[1024];
|
||||
int header_len;
|
||||
|
||||
type = sha1_object_info(object, NULL);
|
||||
if (type <= OBJ_NONE)
|
||||
@@ -294,53 +285,37 @@ static void create_tag(const unsigned char *object, const char *tag,
|
||||
write_or_die(fd, tag_template, strlen(tag_template));
|
||||
close(fd);
|
||||
|
||||
launch_editor(path, &buffer, &size);
|
||||
launch_editor(path, buf);
|
||||
|
||||
unlink(path);
|
||||
free(path);
|
||||
}
|
||||
else {
|
||||
buffer = message;
|
||||
size = strlen(message);
|
||||
}
|
||||
|
||||
size = stripspace(buffer, size, 1);
|
||||
stripspace(buf, 1);
|
||||
|
||||
if (!message && !size)
|
||||
if (!message && !buf->len)
|
||||
die("no tag message?");
|
||||
|
||||
/* insert the header and add the '\n' if needed: */
|
||||
max_size = header_len + size + (sign ? MAX_SIGNATURE_LENGTH : 0) + 1;
|
||||
buffer = xrealloc(buffer, max_size);
|
||||
if (size)
|
||||
buffer[size++] = '\n';
|
||||
memmove(buffer + header_len, buffer, size);
|
||||
memcpy(buffer, header_buf, header_len);
|
||||
size += header_len;
|
||||
strbuf_insert(buf, 0, header_buf, header_len);
|
||||
|
||||
if (sign) {
|
||||
ssize_t r = do_sign(buffer, size, max_size);
|
||||
if (r < 0)
|
||||
die("unable to sign the tag");
|
||||
size = r;
|
||||
}
|
||||
|
||||
if (write_sha1_file(buffer, size, tag_type, result) < 0)
|
||||
if (sign && do_sign(buf) < 0)
|
||||
die("unable to sign the tag");
|
||||
if (write_sha1_file(buf->buf, buf->len, tag_type, result) < 0)
|
||||
die("unable to write tag file");
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct strbuf buf;
|
||||
unsigned char object[20], prev[20];
|
||||
int annotate = 0, sign = 0, force = 0, lines = 0;
|
||||
char *message = NULL;
|
||||
int annotate = 0, sign = 0, force = 0, lines = 0, message = 0;
|
||||
char ref[PATH_MAX];
|
||||
const char *object_ref, *tag;
|
||||
int i;
|
||||
struct ref_lock *lock;
|
||||
|
||||
git_config(git_tag_config);
|
||||
strbuf_init(&buf, 0);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
@@ -376,13 +351,11 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||
die("option -m needs an argument.");
|
||||
if (message)
|
||||
die("only one -F or -m option is allowed.");
|
||||
message = xstrdup(argv[i]);
|
||||
strbuf_addstr(&buf, argv[i]);
|
||||
message = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-F")) {
|
||||
unsigned long len;
|
||||
int fd;
|
||||
|
||||
annotate = 1;
|
||||
i++;
|
||||
if (i == argc)
|
||||
@@ -390,20 +363,15 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||
if (message)
|
||||
die("only one -F or -m option is allowed.");
|
||||
|
||||
if (!strcmp(argv[i], "-"))
|
||||
fd = 0;
|
||||
else {
|
||||
fd = open(argv[i], O_RDONLY);
|
||||
if (fd < 0)
|
||||
die("could not open '%s': %s",
|
||||
if (!strcmp(argv[i], "-")) {
|
||||
if (strbuf_read(&buf, 0, 1024) < 0)
|
||||
die("cannot read %s", argv[i]);
|
||||
} else {
|
||||
if (strbuf_read_file(&buf, argv[i], 1024) < 0)
|
||||
die("could not open or read '%s': %s",
|
||||
argv[i], strerror(errno));
|
||||
}
|
||||
len = 1024;
|
||||
message = xmalloc(len);
|
||||
if (read_fd(fd, &message, &len)) {
|
||||
free(message);
|
||||
die("cannot read %s", argv[i]);
|
||||
}
|
||||
message = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-u")) {
|
||||
@@ -451,7 +419,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||
die("tag '%s' already exists", tag);
|
||||
|
||||
if (annotate)
|
||||
create_tag(object, tag, message, sign, object);
|
||||
create_tag(object, tag, &buf, message, sign, object);
|
||||
|
||||
lock = lock_any_ref_for_update(ref, prev, 0);
|
||||
if (!lock)
|
||||
@@ -459,5 +427,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||
if (write_ref_sha1(lock, object, NULL) < 0)
|
||||
die("%s: cannot update the ref", ref);
|
||||
|
||||
strbuf_release(&buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* Copyright (C) Linus Torvalds, 2005
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "strbuf.h"
|
||||
#include "quote.h"
|
||||
#include "cache-tree.h"
|
||||
#include "tree-walk.h"
|
||||
@@ -195,11 +194,6 @@ static int process_path(const char *path)
|
||||
int len;
|
||||
struct stat st;
|
||||
|
||||
/* We probably want to do this in remove_file_from_cache() and
|
||||
* add_cache_entry() instead...
|
||||
*/
|
||||
cache_tree_invalidate_path(active_cache_tree, path);
|
||||
|
||||
/*
|
||||
* First things first: get the stat information, to decide
|
||||
* what to do about the pathname!
|
||||
@@ -239,7 +233,6 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
|
||||
return error("%s: cannot add to the index - missing --add option?",
|
||||
path);
|
||||
report("add '%s'", path);
|
||||
cache_tree_invalidate_path(active_cache_tree, path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -284,7 +277,6 @@ static void update_one(const char *path, const char *prefix, int prefix_length)
|
||||
die("Unable to mark file %s", path);
|
||||
goto free_return;
|
||||
}
|
||||
cache_tree_invalidate_path(active_cache_tree, path);
|
||||
|
||||
if (force_remove) {
|
||||
if (remove_file_from_cache(p))
|
||||
@@ -303,8 +295,11 @@ static void update_one(const char *path, const char *prefix, int prefix_length)
|
||||
static void read_index_info(int line_termination)
|
||||
{
|
||||
struct strbuf buf;
|
||||
strbuf_init(&buf);
|
||||
while (1) {
|
||||
struct strbuf uq;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
strbuf_init(&uq, 0);
|
||||
while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
|
||||
char *ptr, *tab;
|
||||
char *path_name;
|
||||
unsigned char sha1[20];
|
||||
@@ -328,10 +323,6 @@ static void read_index_info(int line_termination)
|
||||
* This format is to put higher order stages into the
|
||||
* index file and matches git-ls-files --stage output.
|
||||
*/
|
||||
read_line(&buf, stdin, line_termination);
|
||||
if (buf.eof)
|
||||
break;
|
||||
|
||||
errno = 0;
|
||||
ul = strtoul(buf.buf, &ptr, 8);
|
||||
if (ptr == buf.buf || *ptr != ' '
|
||||
@@ -356,18 +347,19 @@ static void read_index_info(int line_termination)
|
||||
if (get_sha1_hex(tab - 40, sha1) || tab[-41] != ' ')
|
||||
goto bad_line;
|
||||
|
||||
if (line_termination && ptr[0] == '"')
|
||||
path_name = unquote_c_style(ptr, NULL);
|
||||
else
|
||||
path_name = ptr;
|
||||
path_name = ptr;
|
||||
if (line_termination && path_name[0] == '"') {
|
||||
strbuf_reset(&uq);
|
||||
if (unquote_c_style(&uq, path_name, NULL)) {
|
||||
die("git-update-index: bad quoting of path name");
|
||||
}
|
||||
path_name = uq.buf;
|
||||
}
|
||||
|
||||
if (!verify_path(path_name)) {
|
||||
fprintf(stderr, "Ignoring path %s\n", path_name);
|
||||
if (path_name != ptr)
|
||||
free(path_name);
|
||||
continue;
|
||||
}
|
||||
cache_tree_invalidate_path(active_cache_tree, path_name);
|
||||
|
||||
if (!mode) {
|
||||
/* mode == 0 means there is no such path -- remove */
|
||||
@@ -385,13 +377,13 @@ static void read_index_info(int line_termination)
|
||||
die("git-update-index: unable to update %s",
|
||||
path_name);
|
||||
}
|
||||
if (path_name != ptr)
|
||||
free(path_name);
|
||||
continue;
|
||||
|
||||
bad_line:
|
||||
die("malformed index info %s", buf.buf);
|
||||
}
|
||||
strbuf_release(&buf);
|
||||
strbuf_release(&uq);
|
||||
}
|
||||
|
||||
static const char update_index_usage[] =
|
||||
@@ -474,7 +466,6 @@ static int unresolve_one(const char *path)
|
||||
goto free_return;
|
||||
}
|
||||
|
||||
cache_tree_invalidate_path(active_cache_tree, path);
|
||||
remove_file_from_cache(path);
|
||||
if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) {
|
||||
error("%s: cannot add our version to the index.", path);
|
||||
@@ -715,27 +706,27 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
|
||||
free((char*)p);
|
||||
}
|
||||
if (read_from_stdin) {
|
||||
struct strbuf buf;
|
||||
strbuf_init(&buf);
|
||||
while (1) {
|
||||
char *path_name;
|
||||
struct strbuf buf, nbuf;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
strbuf_init(&nbuf, 0);
|
||||
while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
|
||||
const char *p;
|
||||
read_line(&buf, stdin, line_termination);
|
||||
if (buf.eof)
|
||||
break;
|
||||
if (line_termination && buf.buf[0] == '"')
|
||||
path_name = unquote_c_style(buf.buf, NULL);
|
||||
else
|
||||
path_name = buf.buf;
|
||||
p = prefix_path(prefix, prefix_length, path_name);
|
||||
if (line_termination && buf.buf[0] == '"') {
|
||||
strbuf_reset(&nbuf);
|
||||
if (unquote_c_style(&nbuf, buf.buf, NULL))
|
||||
die("line is badly quoted");
|
||||
strbuf_swap(&buf, &nbuf);
|
||||
}
|
||||
p = prefix_path(prefix, prefix_length, buf.buf);
|
||||
update_one(p, NULL, 0);
|
||||
if (set_executable_bit)
|
||||
chmod_path(set_executable_bit, p);
|
||||
if (p < path_name || p > path_name + strlen(path_name))
|
||||
free((char*) p);
|
||||
if (path_name != buf.buf)
|
||||
free(path_name);
|
||||
if (p < buf.buf || p > buf.buf + buf.len)
|
||||
free((char *)p);
|
||||
}
|
||||
strbuf_release(&nbuf);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
finish:
|
||||
|
||||
@@ -8,7 +8,6 @@ static const char git_update_ref_usage[] =
|
||||
int cmd_update_ref(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
|
||||
struct ref_lock *lock;
|
||||
unsigned char sha1[20], oldsha1[20];
|
||||
int i, delete, ref_flags;
|
||||
|
||||
@@ -62,10 +61,6 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
|
||||
if (oldval && *oldval && get_sha1(oldval, oldsha1))
|
||||
die("%s: not a valid old SHA1", oldval);
|
||||
|
||||
lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, ref_flags);
|
||||
if (!lock)
|
||||
die("%s: cannot lock the ref", refname);
|
||||
if (write_ref_sha1(lock, sha1, msg) < 0)
|
||||
die("%s: cannot update the ref", refname);
|
||||
return 0;
|
||||
return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
|
||||
ref_flags, DIE_ON_ERR);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose)
|
||||
|
||||
/* find the length without signature */
|
||||
len = 0;
|
||||
while (len < size && prefixcmp(buf + len, PGP_SIGNATURE "\n")) {
|
||||
while (len < size && prefixcmp(buf + len, PGP_SIGNATURE)) {
|
||||
eol = memchr(buf + len, '\n', size - len);
|
||||
len += eol ? eol - (buf + len) + 1 : size - len;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ extern const char git_version_string[];
|
||||
extern const char git_usage_string[];
|
||||
|
||||
extern void help_unknown_cmd(const char *cmd);
|
||||
extern size_t stripspace(char *buffer, size_t length, int skip_comments);
|
||||
extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
|
||||
extern void prune_packed_objects(int);
|
||||
|
||||
@@ -60,6 +59,7 @@ extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_reflog(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_config(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_rerere(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_reset(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_revert(int argc, const char **argv, const char *prefix);
|
||||
|
||||
60
cache-tree.c
60
cache-tree.c
@@ -235,8 +235,7 @@ static int update_one(struct cache_tree *it,
|
||||
int missing_ok,
|
||||
int dryrun)
|
||||
{
|
||||
unsigned long size, offset;
|
||||
char *buffer;
|
||||
struct strbuf buffer;
|
||||
int i;
|
||||
|
||||
if (0 <= it->entry_count && has_sha1_file(it->sha1))
|
||||
@@ -293,9 +292,7 @@ static int update_one(struct cache_tree *it,
|
||||
/*
|
||||
* Then write out the tree object for this level.
|
||||
*/
|
||||
size = 8192;
|
||||
buffer = xmalloc(size);
|
||||
offset = 0;
|
||||
strbuf_init(&buffer, 8192);
|
||||
|
||||
for (i = 0; i < entries; i++) {
|
||||
struct cache_entry *ce = cache[i];
|
||||
@@ -332,15 +329,9 @@ static int update_one(struct cache_tree *it,
|
||||
if (!ce->ce_mode)
|
||||
continue; /* entry being removed */
|
||||
|
||||
if (size < offset + entlen + 100) {
|
||||
size = alloc_nr(offset + entlen + 100);
|
||||
buffer = xrealloc(buffer, size);
|
||||
}
|
||||
offset += sprintf(buffer + offset,
|
||||
"%o %.*s", mode, entlen, path + baselen);
|
||||
buffer[offset++] = 0;
|
||||
hashcpy((unsigned char*)buffer + offset, sha1);
|
||||
offset += 20;
|
||||
strbuf_grow(&buffer, entlen + 100);
|
||||
strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0');
|
||||
strbuf_add(&buffer, sha1, 20);
|
||||
|
||||
#if DEBUG
|
||||
fprintf(stderr, "cache-tree update-one %o %.*s\n",
|
||||
@@ -349,10 +340,10 @@ static int update_one(struct cache_tree *it,
|
||||
}
|
||||
|
||||
if (dryrun)
|
||||
hash_sha1_file(buffer, offset, tree_type, it->sha1);
|
||||
hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1);
|
||||
else
|
||||
write_sha1_file(buffer, offset, tree_type, it->sha1);
|
||||
free(buffer);
|
||||
write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1);
|
||||
strbuf_release(&buffer);
|
||||
it->entry_count = i;
|
||||
#if DEBUG
|
||||
fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n",
|
||||
@@ -378,12 +369,8 @@ int cache_tree_update(struct cache_tree *it,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *write_one(struct cache_tree *it,
|
||||
char *path,
|
||||
int pathlen,
|
||||
char *buffer,
|
||||
unsigned long *size,
|
||||
unsigned long *offset)
|
||||
static void write_one(struct strbuf *buffer, struct cache_tree *it,
|
||||
const char *path, int pathlen)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -393,13 +380,9 @@ static void *write_one(struct cache_tree *it,
|
||||
* tree-sha1 (missing if invalid)
|
||||
* subtree_nr "cache-tree" entries for subtrees.
|
||||
*/
|
||||
if (*size < *offset + pathlen + 100) {
|
||||
*size = alloc_nr(*offset + pathlen + 100);
|
||||
buffer = xrealloc(buffer, *size);
|
||||
}
|
||||
*offset += sprintf(buffer + *offset, "%.*s%c%d %d\n",
|
||||
pathlen, path, 0,
|
||||
it->entry_count, it->subtree_nr);
|
||||
strbuf_grow(buffer, pathlen + 100);
|
||||
strbuf_add(buffer, path, pathlen);
|
||||
strbuf_addf(buffer, "%c%d %d\n", 0, it->entry_count, it->subtree_nr);
|
||||
|
||||
#if DEBUG
|
||||
if (0 <= it->entry_count)
|
||||
@@ -412,8 +395,7 @@ static void *write_one(struct cache_tree *it,
|
||||
#endif
|
||||
|
||||
if (0 <= it->entry_count) {
|
||||
hashcpy((unsigned char*)buffer + *offset, it->sha1);
|
||||
*offset += 20;
|
||||
strbuf_add(buffer, it->sha1, 20);
|
||||
}
|
||||
for (i = 0; i < it->subtree_nr; i++) {
|
||||
struct cache_tree_sub *down = it->down[i];
|
||||
@@ -423,21 +405,13 @@ static void *write_one(struct cache_tree *it,
|
||||
prev->name, prev->namelen) <= 0)
|
||||
die("fatal - unsorted cache subtree");
|
||||
}
|
||||
buffer = write_one(down->cache_tree, down->name, down->namelen,
|
||||
buffer, size, offset);
|
||||
write_one(buffer, down->cache_tree, down->name, down->namelen);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void *cache_tree_write(struct cache_tree *root, unsigned long *size_p)
|
||||
void cache_tree_write(struct strbuf *sb, struct cache_tree *root)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
unsigned long size = 8192;
|
||||
char *buffer = xmalloc(size);
|
||||
|
||||
*size_p = 0;
|
||||
path[0] = 0;
|
||||
return write_one(root, path, 0, buffer, &size, size_p);
|
||||
write_one(sb, root, "", 0);
|
||||
}
|
||||
|
||||
static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)
|
||||
|
||||
@@ -22,7 +22,7 @@ void cache_tree_free(struct cache_tree **);
|
||||
void cache_tree_invalidate_path(struct cache_tree *, const char *);
|
||||
struct cache_tree_sub *cache_tree_sub(struct cache_tree *, const char *);
|
||||
|
||||
void *cache_tree_write(struct cache_tree *root, unsigned long *size_p);
|
||||
void cache_tree_write(struct strbuf *, struct cache_tree *root);
|
||||
struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
|
||||
|
||||
int cache_tree_fully_valid(struct cache_tree *);
|
||||
|
||||
12
cache.h
12
cache.h
@@ -2,6 +2,7 @@
|
||||
#define CACHE_H
|
||||
|
||||
#include "git-compat-util.h"
|
||||
#include "strbuf.h"
|
||||
|
||||
#include SHA1_HEADER
|
||||
#include <zlib.h>
|
||||
@@ -270,7 +271,6 @@ extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat
|
||||
extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int);
|
||||
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
|
||||
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
|
||||
extern int read_fd(int fd, char **return_buf, unsigned long *return_size);
|
||||
extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
|
||||
extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
|
||||
extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
|
||||
@@ -437,6 +437,7 @@ const char *show_date(unsigned long time, int timezone, enum date_mode mode);
|
||||
int parse_date(const char *date, char *buf, int bufsize);
|
||||
void datestamp(char *buf, int bufsize);
|
||||
unsigned long approxidate(const char *);
|
||||
enum date_mode parse_date_format(const char *format);
|
||||
|
||||
extern const char *git_author_info(int);
|
||||
extern const char *git_committer_info(int);
|
||||
@@ -535,6 +536,7 @@ extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsign
|
||||
extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
|
||||
extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
|
||||
extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
|
||||
extern int matches_pack_name(struct packed_git *p, const char *name);
|
||||
|
||||
/* Dumb servers support */
|
||||
extern int update_server_info(int);
|
||||
@@ -591,15 +593,13 @@ extern void *alloc_object_node(void);
|
||||
extern void alloc_report(void);
|
||||
|
||||
/* trace.c */
|
||||
extern int nfasprintf(char **str, const char *fmt, ...);
|
||||
extern int nfvasprintf(char **str, const char *fmt, va_list va);
|
||||
extern void trace_printf(const char *format, ...);
|
||||
extern void trace_argv_printf(const char **argv, int count, const char *format, ...);
|
||||
|
||||
/* convert.c */
|
||||
extern char *convert_to_git(const char *path, const char *src, unsigned long *sizep);
|
||||
extern char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep);
|
||||
extern void *convert_sha1_file(const char *path, const unsigned char *sha1, unsigned int mode, enum object_type *type, unsigned long *size);
|
||||
/* returns 1 if *dst was used */
|
||||
extern int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst);
|
||||
extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
|
||||
|
||||
/* diff.c */
|
||||
extern int diff_auto_refresh_index;
|
||||
|
||||
@@ -650,10 +650,7 @@ static void dump_quoted_path(const char *prefix, const char *path,
|
||||
const char *c_meta, const char *c_reset)
|
||||
{
|
||||
printf("%s%s", c_meta, prefix);
|
||||
if (quote_c_style(path, NULL, NULL, 0))
|
||||
quote_c_style(path, NULL, stdout, 0);
|
||||
else
|
||||
printf("%s", path);
|
||||
quote_c_style(path, NULL, stdout, 0);
|
||||
printf("%s\n", c_reset);
|
||||
}
|
||||
|
||||
@@ -900,16 +897,7 @@ static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct re
|
||||
putchar(inter_name_termination);
|
||||
}
|
||||
|
||||
if (line_termination) {
|
||||
if (quote_c_style(p->path, NULL, NULL, 0))
|
||||
quote_c_style(p->path, NULL, stdout, 0);
|
||||
else
|
||||
printf("%s", p->path);
|
||||
putchar(line_termination);
|
||||
}
|
||||
else {
|
||||
printf("%s%c", p->path, line_termination);
|
||||
}
|
||||
write_name_quoted(p->path, stdout, line_termination);
|
||||
}
|
||||
|
||||
void show_combined_diff(struct combine_diff_path *p,
|
||||
|
||||
453
commit.c
453
commit.c
@@ -441,28 +441,33 @@ struct commit *pop_most_recent_commit(struct commit_list **list,
|
||||
|
||||
void clear_commit_marks(struct commit *commit, unsigned int mark)
|
||||
{
|
||||
struct commit_list *parents;
|
||||
while (commit) {
|
||||
struct commit_list *parents;
|
||||
|
||||
commit->object.flags &= ~mark;
|
||||
parents = commit->parents;
|
||||
while (parents) {
|
||||
struct commit *parent = parents->item;
|
||||
if (!(mark & commit->object.flags))
|
||||
return;
|
||||
|
||||
/* Have we already cleared this? */
|
||||
if (mark & parent->object.flags)
|
||||
clear_commit_marks(parent, mark);
|
||||
parents = parents->next;
|
||||
commit->object.flags &= ~mark;
|
||||
|
||||
parents = commit->parents;
|
||||
if (!parents)
|
||||
return;
|
||||
|
||||
while ((parents = parents->next))
|
||||
clear_commit_marks(parents->item, mark);
|
||||
|
||||
commit = commit->parents->item;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic support for pretty-printing the header
|
||||
*/
|
||||
static int get_one_line(const char *msg, unsigned long len)
|
||||
static int get_one_line(const char *msg)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
while (len--) {
|
||||
for (;;) {
|
||||
char c = *msg++;
|
||||
if (!c)
|
||||
break;
|
||||
@@ -485,31 +490,25 @@ static int is_rfc2047_special(char ch)
|
||||
return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
|
||||
}
|
||||
|
||||
static int add_rfc2047(char *buf, const char *line, int len,
|
||||
static void add_rfc2047(struct strbuf *sb, const char *line, int len,
|
||||
const char *encoding)
|
||||
{
|
||||
char *bp = buf;
|
||||
int i, needquote;
|
||||
char q_encoding[128];
|
||||
const char *q_encoding_fmt = "=?%s?q?";
|
||||
int i, last;
|
||||
|
||||
for (i = needquote = 0; !needquote && i < len; i++) {
|
||||
for (i = 0; i < len; i++) {
|
||||
int ch = line[i];
|
||||
if (non_ascii(ch))
|
||||
needquote++;
|
||||
if ((i + 1 < len) &&
|
||||
(ch == '=' && line[i+1] == '?'))
|
||||
needquote++;
|
||||
goto needquote;
|
||||
if ((i + 1 < len) && (ch == '=' && line[i+1] == '?'))
|
||||
goto needquote;
|
||||
}
|
||||
if (!needquote)
|
||||
return sprintf(buf, "%.*s", len, line);
|
||||
strbuf_add(sb, line, len);
|
||||
return;
|
||||
|
||||
i = snprintf(q_encoding, sizeof(q_encoding), q_encoding_fmt, encoding);
|
||||
if (sizeof(q_encoding) < i)
|
||||
die("Insanely long encoding name %s", encoding);
|
||||
memcpy(bp, q_encoding, i);
|
||||
bp += i;
|
||||
for (i = 0; i < len; i++) {
|
||||
needquote:
|
||||
strbuf_grow(sb, len * 3 + strlen(encoding) + 100);
|
||||
strbuf_addf(sb, "=?%s?q?", encoding);
|
||||
for (i = last = 0; i < len; i++) {
|
||||
unsigned ch = line[i] & 0xFF;
|
||||
/*
|
||||
* We encode ' ' using '=20' even though rfc2047
|
||||
@@ -518,40 +517,30 @@ static int add_rfc2047(char *buf, const char *line, int len,
|
||||
* leave the underscore in place.
|
||||
*/
|
||||
if (is_rfc2047_special(ch) || ch == ' ') {
|
||||
sprintf(bp, "=%02X", ch);
|
||||
bp += 3;
|
||||
strbuf_add(sb, line + last, i - last);
|
||||
strbuf_addf(sb, "=%02X", ch);
|
||||
last = i + 1;
|
||||
}
|
||||
else
|
||||
*bp++ = ch;
|
||||
}
|
||||
memcpy(bp, "?=", 2);
|
||||
bp += 2;
|
||||
return bp - buf;
|
||||
strbuf_add(sb, line + last, len - last);
|
||||
strbuf_addstr(sb, "?=");
|
||||
}
|
||||
|
||||
static unsigned long bound_rfc2047(unsigned long len, const char *encoding)
|
||||
{
|
||||
/* upper bound of q encoded string of length 'len' */
|
||||
unsigned long elen = strlen(encoding);
|
||||
|
||||
return len * 3 + elen + 100;
|
||||
}
|
||||
|
||||
static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
|
||||
static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
|
||||
const char *line, enum date_mode dmode,
|
||||
const char *encoding)
|
||||
{
|
||||
char *date;
|
||||
int namelen;
|
||||
unsigned long time;
|
||||
int tz, ret;
|
||||
int tz;
|
||||
const char *filler = " ";
|
||||
|
||||
if (fmt == CMIT_FMT_ONELINE)
|
||||
return 0;
|
||||
return;
|
||||
date = strchr(line, '>');
|
||||
if (!date)
|
||||
return 0;
|
||||
return;
|
||||
namelen = ++date - line;
|
||||
time = strtoul(date, &date, 10);
|
||||
tz = strtol(date, NULL, 10);
|
||||
@@ -560,42 +549,34 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
|
||||
char *name_tail = strchr(line, '<');
|
||||
int display_name_length;
|
||||
if (!name_tail)
|
||||
return 0;
|
||||
return;
|
||||
while (line < name_tail && isspace(name_tail[-1]))
|
||||
name_tail--;
|
||||
display_name_length = name_tail - line;
|
||||
filler = "";
|
||||
strcpy(buf, "From: ");
|
||||
ret = strlen(buf);
|
||||
ret += add_rfc2047(buf + ret, line, display_name_length,
|
||||
encoding);
|
||||
memcpy(buf + ret, name_tail, namelen - display_name_length);
|
||||
ret += namelen - display_name_length;
|
||||
buf[ret++] = '\n';
|
||||
}
|
||||
else {
|
||||
ret = sprintf(buf, "%s: %.*s%.*s\n", what,
|
||||
strbuf_addstr(sb, "From: ");
|
||||
add_rfc2047(sb, line, display_name_length, encoding);
|
||||
strbuf_add(sb, name_tail, namelen - display_name_length);
|
||||
strbuf_addch(sb, '\n');
|
||||
} else {
|
||||
strbuf_addf(sb, "%s: %.*s%.*s\n", what,
|
||||
(fmt == CMIT_FMT_FULLER) ? 4 : 0,
|
||||
filler, namelen, line);
|
||||
}
|
||||
switch (fmt) {
|
||||
case CMIT_FMT_MEDIUM:
|
||||
ret += sprintf(buf + ret, "Date: %s\n",
|
||||
show_date(time, tz, dmode));
|
||||
strbuf_addf(sb, "Date: %s\n", show_date(time, tz, dmode));
|
||||
break;
|
||||
case CMIT_FMT_EMAIL:
|
||||
ret += sprintf(buf + ret, "Date: %s\n",
|
||||
show_date(time, tz, DATE_RFC2822));
|
||||
strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822));
|
||||
break;
|
||||
case CMIT_FMT_FULLER:
|
||||
ret += sprintf(buf + ret, "%sDate: %s\n", what,
|
||||
show_date(time, tz, dmode));
|
||||
strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode));
|
||||
break;
|
||||
default:
|
||||
/* notin' */
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int is_empty_line(const char *line, int *len_p)
|
||||
@@ -607,16 +588,16 @@ static int is_empty_line(const char *line, int *len_p)
|
||||
return !len;
|
||||
}
|
||||
|
||||
static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *commit, int abbrev)
|
||||
static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb,
|
||||
const struct commit *commit, int abbrev)
|
||||
{
|
||||
struct commit_list *parent = commit->parents;
|
||||
int offset;
|
||||
|
||||
if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
|
||||
!parent || !parent->next)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
offset = sprintf(buf, "Merge:");
|
||||
strbuf_addstr(sb, "Merge:");
|
||||
|
||||
while (parent) {
|
||||
struct commit *p = parent->item;
|
||||
@@ -629,10 +610,9 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com
|
||||
dots = (abbrev && strlen(hex) != 40) ? "..." : "";
|
||||
parent = parent->next;
|
||||
|
||||
offset += sprintf(buf + offset, " %s%s", hex, dots);
|
||||
strbuf_addf(sb, " %s%s", hex, dots);
|
||||
}
|
||||
buf[offset++] = '\n';
|
||||
return offset;
|
||||
strbuf_addch(sb, '\n');
|
||||
}
|
||||
|
||||
static char *get_header(const struct commit *commit, const char *key)
|
||||
@@ -653,11 +633,7 @@ static char *get_header(const struct commit *commit, const char *key)
|
||||
if (eol - line > key_len &&
|
||||
!strncmp(line, key, key_len) &&
|
||||
line[key_len] == ' ') {
|
||||
int len = eol - line - key_len;
|
||||
char *ret = xmalloc(len);
|
||||
memcpy(ret, line + key_len + 1, len - 1);
|
||||
ret[len - 1] = '\0';
|
||||
return ret;
|
||||
return xmemdupz(line + key_len + 1, eol - line - key_len - 1);
|
||||
}
|
||||
line = next;
|
||||
}
|
||||
@@ -665,47 +641,34 @@ static char *get_header(const struct commit *commit, const char *key)
|
||||
|
||||
static char *replace_encoding_header(char *buf, const char *encoding)
|
||||
{
|
||||
char *encoding_header = strstr(buf, "\nencoding ");
|
||||
char *header_end = strstr(buf, "\n\n");
|
||||
char *end_of_encoding_header;
|
||||
int encoding_header_pos;
|
||||
int encoding_header_len;
|
||||
int new_len;
|
||||
int need_len;
|
||||
int buflen = strlen(buf) + 1;
|
||||
struct strbuf tmp;
|
||||
size_t start, len;
|
||||
char *cp = buf;
|
||||
|
||||
if (!header_end)
|
||||
header_end = buf + buflen;
|
||||
if (!encoding_header || encoding_header >= header_end)
|
||||
return buf;
|
||||
encoding_header++;
|
||||
end_of_encoding_header = strchr(encoding_header, '\n');
|
||||
if (!end_of_encoding_header)
|
||||
/* guess if there is an encoding header before a \n\n */
|
||||
while (strncmp(cp, "encoding ", strlen("encoding "))) {
|
||||
cp = strchr(cp, '\n');
|
||||
if (!cp || *++cp == '\n')
|
||||
return buf;
|
||||
}
|
||||
start = cp - buf;
|
||||
cp = strchr(cp, '\n');
|
||||
if (!cp)
|
||||
return buf; /* should not happen but be defensive */
|
||||
end_of_encoding_header++;
|
||||
|
||||
encoding_header_len = end_of_encoding_header - encoding_header;
|
||||
encoding_header_pos = encoding_header - buf;
|
||||
len = cp + 1 - (buf + start);
|
||||
|
||||
strbuf_init(&tmp, 0);
|
||||
strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1);
|
||||
if (is_encoding_utf8(encoding)) {
|
||||
/* we have re-coded to UTF-8; drop the header */
|
||||
memmove(encoding_header, end_of_encoding_header,
|
||||
buflen - (encoding_header_pos + encoding_header_len));
|
||||
return buf;
|
||||
strbuf_remove(&tmp, start, len);
|
||||
} else {
|
||||
/* just replaces XXXX in 'encoding XXXX\n' */
|
||||
strbuf_splice(&tmp, start + strlen("encoding "),
|
||||
len - strlen("encoding \n"),
|
||||
encoding, strlen(encoding));
|
||||
}
|
||||
new_len = strlen(encoding);
|
||||
need_len = new_len + strlen("encoding \n");
|
||||
if (encoding_header_len < need_len) {
|
||||
buf = xrealloc(buf, buflen + (need_len - encoding_header_len));
|
||||
encoding_header = buf + encoding_header_pos;
|
||||
end_of_encoding_header = encoding_header + encoding_header_len;
|
||||
}
|
||||
memmove(end_of_encoding_header + (need_len - encoding_header_len),
|
||||
end_of_encoding_header,
|
||||
buflen - (encoding_header_pos + encoding_header_len));
|
||||
memcpy(encoding_header + 9, encoding, strlen(encoding));
|
||||
encoding_header[9 + new_len] = '\n';
|
||||
return buf;
|
||||
return strbuf_detach(&tmp, NULL);
|
||||
}
|
||||
|
||||
static char *logmsg_reencode(const struct commit *commit,
|
||||
@@ -747,7 +710,7 @@ static void fill_person(struct interp *table, const char *msg, int len)
|
||||
start = end + 1;
|
||||
while (end > 0 && isspace(msg[end - 1]))
|
||||
end--;
|
||||
table[0].value = xstrndup(msg, end);
|
||||
table[0].value = xmemdupz(msg, end);
|
||||
|
||||
if (start >= len)
|
||||
return;
|
||||
@@ -759,7 +722,7 @@ static void fill_person(struct interp *table, const char *msg, int len)
|
||||
if (end >= len)
|
||||
return;
|
||||
|
||||
table[1].value = xstrndup(msg + start, end - start);
|
||||
table[1].value = xmemdupz(msg + start, end - start);
|
||||
|
||||
/* parse date */
|
||||
for (start = end + 1; start < len && isspace(msg[start]); start++)
|
||||
@@ -770,7 +733,7 @@ static void fill_person(struct interp *table, const char *msg, int len)
|
||||
if (msg + start == ep)
|
||||
return;
|
||||
|
||||
table[5].value = xstrndup(msg + start, ep - (msg + start));
|
||||
table[5].value = xmemdupz(msg + start, ep - (msg + start));
|
||||
|
||||
/* parse tz */
|
||||
for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
|
||||
@@ -787,8 +750,8 @@ static void fill_person(struct interp *table, const char *msg, int len)
|
||||
interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601));
|
||||
}
|
||||
|
||||
static long format_commit_message(const struct commit *commit,
|
||||
const char *msg, char **buf_p, unsigned long *space_p)
|
||||
void format_commit_message(const struct commit *commit,
|
||||
const void *format, struct strbuf *sb)
|
||||
{
|
||||
struct interp table[] = {
|
||||
{ "%H" }, /* commit hash */
|
||||
@@ -841,8 +804,10 @@ static long format_commit_message(const struct commit *commit,
|
||||
};
|
||||
struct commit_list *p;
|
||||
char parents[1024];
|
||||
unsigned long len;
|
||||
int i;
|
||||
enum { HEADER, SUBJECT, BODY } state;
|
||||
const char *msg = commit->buffer;
|
||||
|
||||
if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table))
|
||||
die("invalid interp table!");
|
||||
@@ -895,7 +860,7 @@ static long format_commit_message(const struct commit *commit,
|
||||
; /* do nothing */
|
||||
|
||||
if (state == SUBJECT) {
|
||||
table[ISUBJECT].value = xstrndup(msg + i, eol - i);
|
||||
table[ISUBJECT].value = xmemdupz(msg + i, eol - i);
|
||||
i = eol;
|
||||
}
|
||||
if (i == eol) {
|
||||
@@ -911,30 +876,21 @@ static long format_commit_message(const struct commit *commit,
|
||||
msg + i + 10, eol - i - 10);
|
||||
else if (!prefixcmp(msg + i, "encoding "))
|
||||
table[IENCODING].value =
|
||||
xstrndup(msg + i + 9, eol - i - 9);
|
||||
xmemdupz(msg + i + 9, eol - i - 9);
|
||||
i = eol;
|
||||
}
|
||||
if (msg[i])
|
||||
table[IBODY].value = xstrdup(msg + i);
|
||||
for (i = 0; i < ARRAY_SIZE(table); i++)
|
||||
if (!table[i].value)
|
||||
interp_set_entry(table, i, "<unknown>");
|
||||
|
||||
do {
|
||||
char *buf = *buf_p;
|
||||
unsigned long space = *space_p;
|
||||
|
||||
space = interpolate(buf, space, user_format,
|
||||
table, ARRAY_SIZE(table));
|
||||
if (!space)
|
||||
break;
|
||||
buf = xrealloc(buf, space);
|
||||
*buf_p = buf;
|
||||
*space_p = space;
|
||||
} while (1);
|
||||
len = interpolate(sb->buf + sb->len, strbuf_avail(sb),
|
||||
format, table, ARRAY_SIZE(table));
|
||||
if (len > strbuf_avail(sb)) {
|
||||
strbuf_grow(sb, len);
|
||||
interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1,
|
||||
format, table, ARRAY_SIZE(table));
|
||||
}
|
||||
strbuf_setlen(sb, sb->len + len);
|
||||
interp_clear_table(table, ARRAY_SIZE(table));
|
||||
|
||||
return strlen(*buf_p);
|
||||
}
|
||||
|
||||
static void pp_header(enum cmit_fmt fmt,
|
||||
@@ -943,34 +899,24 @@ static void pp_header(enum cmit_fmt fmt,
|
||||
const char *encoding,
|
||||
const struct commit *commit,
|
||||
const char **msg_p,
|
||||
unsigned long *len_p,
|
||||
unsigned long *ofs_p,
|
||||
char **buf_p,
|
||||
unsigned long *space_p)
|
||||
struct strbuf *sb)
|
||||
{
|
||||
int parents_shown = 0;
|
||||
|
||||
for (;;) {
|
||||
const char *line = *msg_p;
|
||||
char *dst;
|
||||
int linelen = get_one_line(*msg_p, *len_p);
|
||||
unsigned long len;
|
||||
int linelen = get_one_line(*msg_p);
|
||||
|
||||
if (!linelen)
|
||||
return;
|
||||
*msg_p += linelen;
|
||||
*len_p -= linelen;
|
||||
|
||||
if (linelen == 1)
|
||||
/* End of header */
|
||||
return;
|
||||
|
||||
ALLOC_GROW(*buf_p, linelen + *ofs_p + 20, *space_p);
|
||||
dst = *buf_p + *ofs_p;
|
||||
|
||||
if (fmt == CMIT_FMT_RAW) {
|
||||
memcpy(dst, line, linelen);
|
||||
*ofs_p += linelen;
|
||||
strbuf_add(sb, line, linelen);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -988,10 +934,8 @@ static void pp_header(enum cmit_fmt fmt,
|
||||
parent = parent->next, num++)
|
||||
;
|
||||
/* with enough slop */
|
||||
num = *ofs_p + num * 50 + 20;
|
||||
ALLOC_GROW(*buf_p, num, *space_p);
|
||||
dst = *buf_p + *ofs_p;
|
||||
*ofs_p += add_merge_info(fmt, dst, commit, abbrev);
|
||||
strbuf_grow(sb, num * 50 + 20);
|
||||
add_merge_info(fmt, sb, commit, abbrev);
|
||||
parents_shown = 1;
|
||||
}
|
||||
|
||||
@@ -1001,129 +945,82 @@ static void pp_header(enum cmit_fmt fmt,
|
||||
* FULLER shows both authors and dates.
|
||||
*/
|
||||
if (!memcmp(line, "author ", 7)) {
|
||||
len = linelen;
|
||||
if (fmt == CMIT_FMT_EMAIL)
|
||||
len = bound_rfc2047(linelen, encoding);
|
||||
ALLOC_GROW(*buf_p, *ofs_p + len + 80, *space_p);
|
||||
dst = *buf_p + *ofs_p;
|
||||
*ofs_p += add_user_info("Author", fmt, dst,
|
||||
line + 7, dmode, encoding);
|
||||
strbuf_grow(sb, linelen + 80);
|
||||
add_user_info("Author", fmt, sb, line + 7, dmode, encoding);
|
||||
}
|
||||
|
||||
if (!memcmp(line, "committer ", 10) &&
|
||||
(fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) {
|
||||
len = linelen;
|
||||
if (fmt == CMIT_FMT_EMAIL)
|
||||
len = bound_rfc2047(linelen, encoding);
|
||||
ALLOC_GROW(*buf_p, *ofs_p + len + 80, *space_p);
|
||||
dst = *buf_p + *ofs_p;
|
||||
*ofs_p += add_user_info("Commit", fmt, dst,
|
||||
line + 10, dmode, encoding);
|
||||
strbuf_grow(sb, linelen + 80);
|
||||
add_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pp_title_line(enum cmit_fmt fmt,
|
||||
const char **msg_p,
|
||||
unsigned long *len_p,
|
||||
unsigned long *ofs_p,
|
||||
char **buf_p,
|
||||
unsigned long *space_p,
|
||||
int indent,
|
||||
struct strbuf *sb,
|
||||
const char *subject,
|
||||
const char *after_subject,
|
||||
const char *encoding,
|
||||
int plain_non_ascii)
|
||||
{
|
||||
char *title;
|
||||
unsigned long title_alloc, title_len;
|
||||
unsigned long len;
|
||||
struct strbuf title;
|
||||
|
||||
strbuf_init(&title, 80);
|
||||
|
||||
title_len = 0;
|
||||
title_alloc = 80;
|
||||
title = xmalloc(title_alloc);
|
||||
for (;;) {
|
||||
const char *line = *msg_p;
|
||||
int linelen = get_one_line(line, *len_p);
|
||||
*msg_p += linelen;
|
||||
*len_p -= linelen;
|
||||
int linelen = get_one_line(line);
|
||||
|
||||
*msg_p += linelen;
|
||||
if (!linelen || is_empty_line(line, &linelen))
|
||||
break;
|
||||
|
||||
if (title_alloc <= title_len + linelen + 2) {
|
||||
title_alloc = title_len + linelen + 80;
|
||||
title = xrealloc(title, title_alloc);
|
||||
}
|
||||
len = 0;
|
||||
if (title_len) {
|
||||
strbuf_grow(&title, linelen + 2);
|
||||
if (title.len) {
|
||||
if (fmt == CMIT_FMT_EMAIL) {
|
||||
len++;
|
||||
title[title_len++] = '\n';
|
||||
strbuf_addch(&title, '\n');
|
||||
}
|
||||
len++;
|
||||
title[title_len++] = ' ';
|
||||
strbuf_addch(&title, ' ');
|
||||
}
|
||||
memcpy(title + title_len, line, linelen);
|
||||
title_len += linelen;
|
||||
strbuf_add(&title, line, linelen);
|
||||
}
|
||||
|
||||
/* Enough slop for the MIME header and rfc2047 */
|
||||
len = bound_rfc2047(title_len, encoding)+ 1000;
|
||||
if (subject)
|
||||
len += strlen(subject);
|
||||
if (after_subject)
|
||||
len += strlen(after_subject);
|
||||
if (encoding)
|
||||
len += strlen(encoding);
|
||||
ALLOC_GROW(*buf_p, title_len + *ofs_p + len, *space_p);
|
||||
|
||||
strbuf_grow(sb, title.len + 1024);
|
||||
if (subject) {
|
||||
len = strlen(subject);
|
||||
memcpy(*buf_p + *ofs_p, subject, len);
|
||||
*ofs_p += len;
|
||||
*ofs_p += add_rfc2047(*buf_p + *ofs_p,
|
||||
title, title_len, encoding);
|
||||
strbuf_addstr(sb, subject);
|
||||
add_rfc2047(sb, title.buf, title.len, encoding);
|
||||
} else {
|
||||
memcpy(*buf_p + *ofs_p, title, title_len);
|
||||
*ofs_p += title_len;
|
||||
strbuf_addbuf(sb, &title);
|
||||
}
|
||||
(*buf_p)[(*ofs_p)++] = '\n';
|
||||
strbuf_addch(sb, '\n');
|
||||
|
||||
if (plain_non_ascii) {
|
||||
const char *header_fmt =
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=%s\n"
|
||||
"Content-Transfer-Encoding: 8bit\n";
|
||||
*ofs_p += snprintf(*buf_p + *ofs_p,
|
||||
*space_p - *ofs_p,
|
||||
header_fmt, encoding);
|
||||
strbuf_addf(sb, header_fmt, encoding);
|
||||
}
|
||||
if (after_subject) {
|
||||
len = strlen(after_subject);
|
||||
memcpy(*buf_p + *ofs_p, after_subject, len);
|
||||
*ofs_p += len;
|
||||
strbuf_addstr(sb, after_subject);
|
||||
}
|
||||
free(title);
|
||||
if (fmt == CMIT_FMT_EMAIL) {
|
||||
ALLOC_GROW(*buf_p, *ofs_p + 20, *space_p);
|
||||
(*buf_p)[(*ofs_p)++] = '\n';
|
||||
strbuf_addch(sb, '\n');
|
||||
}
|
||||
strbuf_release(&title);
|
||||
}
|
||||
|
||||
static void pp_remainder(enum cmit_fmt fmt,
|
||||
const char **msg_p,
|
||||
unsigned long *len_p,
|
||||
unsigned long *ofs_p,
|
||||
char **buf_p,
|
||||
unsigned long *space_p,
|
||||
struct strbuf *sb,
|
||||
int indent)
|
||||
{
|
||||
int first = 1;
|
||||
for (;;) {
|
||||
const char *line = *msg_p;
|
||||
int linelen = get_one_line(line, *len_p);
|
||||
int linelen = get_one_line(line);
|
||||
*msg_p += linelen;
|
||||
*len_p -= linelen;
|
||||
|
||||
if (!linelen)
|
||||
break;
|
||||
@@ -1136,36 +1033,32 @@ static void pp_remainder(enum cmit_fmt fmt,
|
||||
}
|
||||
first = 0;
|
||||
|
||||
ALLOC_GROW(*buf_p, *ofs_p + linelen + indent + 20, *space_p);
|
||||
strbuf_grow(sb, linelen + indent + 20);
|
||||
if (indent) {
|
||||
memset(*buf_p + *ofs_p, ' ', indent);
|
||||
*ofs_p += indent;
|
||||
memset(sb->buf + sb->len, ' ', indent);
|
||||
strbuf_setlen(sb, sb->len + indent);
|
||||
}
|
||||
memcpy(*buf_p + *ofs_p, line, linelen);
|
||||
*ofs_p += linelen;
|
||||
(*buf_p)[(*ofs_p)++] = '\n';
|
||||
strbuf_add(sb, line, linelen);
|
||||
strbuf_addch(sb, '\n');
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long pretty_print_commit(enum cmit_fmt fmt,
|
||||
const struct commit *commit,
|
||||
unsigned long len,
|
||||
char **buf_p, unsigned long *space_p,
|
||||
int abbrev, const char *subject,
|
||||
const char *after_subject,
|
||||
void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
|
||||
struct strbuf *sb, int abbrev,
|
||||
const char *subject, const char *after_subject,
|
||||
enum date_mode dmode)
|
||||
{
|
||||
unsigned long offset = 0;
|
||||
unsigned long beginning_of_body;
|
||||
int indent = 4;
|
||||
const char *msg = commit->buffer;
|
||||
int plain_non_ascii = 0;
|
||||
char *reencoded;
|
||||
const char *encoding;
|
||||
char *buf;
|
||||
|
||||
if (fmt == CMIT_FMT_USERFORMAT)
|
||||
return format_commit_message(commit, msg, buf_p, space_p);
|
||||
if (fmt == CMIT_FMT_USERFORMAT) {
|
||||
format_commit_message(commit, user_format, sb);
|
||||
return;
|
||||
}
|
||||
|
||||
encoding = (git_log_output_encoding
|
||||
? git_log_output_encoding
|
||||
@@ -1175,7 +1068,6 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
|
||||
reencoded = logmsg_reencode(commit, encoding);
|
||||
if (reencoded) {
|
||||
msg = reencoded;
|
||||
len = strlen(reencoded);
|
||||
}
|
||||
|
||||
if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
|
||||
@@ -1190,14 +1082,13 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
|
||||
if (fmt == CMIT_FMT_EMAIL && !after_subject) {
|
||||
int i, ch, in_body;
|
||||
|
||||
for (in_body = i = 0; (ch = msg[i]) && i < len; i++) {
|
||||
for (in_body = i = 0; (ch = msg[i]); i++) {
|
||||
if (!in_body) {
|
||||
/* author could be non 7-bit ASCII but
|
||||
* the log may be so; skip over the
|
||||
* header part first.
|
||||
*/
|
||||
if (ch == '\n' &&
|
||||
i + 1 < len && msg[i+1] == '\n')
|
||||
if (ch == '\n' && msg[i+1] == '\n')
|
||||
in_body = 1;
|
||||
}
|
||||
else if (non_ascii(ch)) {
|
||||
@@ -1207,59 +1098,44 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
|
||||
}
|
||||
}
|
||||
|
||||
pp_header(fmt, abbrev, dmode, encoding,
|
||||
commit, &msg, &len,
|
||||
&offset, buf_p, space_p);
|
||||
pp_header(fmt, abbrev, dmode, encoding, commit, &msg, sb);
|
||||
if (fmt != CMIT_FMT_ONELINE && !subject) {
|
||||
ALLOC_GROW(*buf_p, offset + 20, *space_p);
|
||||
(*buf_p)[offset++] = '\n';
|
||||
strbuf_addch(sb, '\n');
|
||||
}
|
||||
|
||||
/* Skip excess blank lines at the beginning of body, if any... */
|
||||
for (;;) {
|
||||
int linelen = get_one_line(msg, len);
|
||||
int linelen = get_one_line(msg);
|
||||
int ll = linelen;
|
||||
if (!linelen)
|
||||
break;
|
||||
if (!is_empty_line(msg, &ll))
|
||||
break;
|
||||
msg += linelen;
|
||||
len -= linelen;
|
||||
}
|
||||
|
||||
/* These formats treat the title line specially. */
|
||||
if (fmt == CMIT_FMT_ONELINE
|
||||
|| fmt == CMIT_FMT_EMAIL)
|
||||
pp_title_line(fmt, &msg, &len, &offset,
|
||||
buf_p, space_p, indent,
|
||||
subject, after_subject, encoding,
|
||||
plain_non_ascii);
|
||||
if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
|
||||
pp_title_line(fmt, &msg, sb, subject,
|
||||
after_subject, encoding, plain_non_ascii);
|
||||
|
||||
beginning_of_body = offset;
|
||||
beginning_of_body = sb->len;
|
||||
if (fmt != CMIT_FMT_ONELINE)
|
||||
pp_remainder(fmt, &msg, &len, &offset,
|
||||
buf_p, space_p, indent);
|
||||
|
||||
while (offset && isspace((*buf_p)[offset-1]))
|
||||
offset--;
|
||||
|
||||
ALLOC_GROW(*buf_p, offset + 20, *space_p);
|
||||
buf = *buf_p;
|
||||
pp_remainder(fmt, &msg, sb, indent);
|
||||
strbuf_rtrim(sb);
|
||||
|
||||
/* Make sure there is an EOLN for the non-oneline case */
|
||||
if (fmt != CMIT_FMT_ONELINE)
|
||||
buf[offset++] = '\n';
|
||||
strbuf_addch(sb, '\n');
|
||||
|
||||
/*
|
||||
* The caller may append additional body text in e-mail
|
||||
* format. Make sure we did not strip the blank line
|
||||
* between the header and the body.
|
||||
*/
|
||||
if (fmt == CMIT_FMT_EMAIL && offset <= beginning_of_body)
|
||||
buf[offset++] = '\n';
|
||||
buf[offset] = '\0';
|
||||
if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
|
||||
strbuf_addch(sb, '\n');
|
||||
free(reencoded);
|
||||
return offset;
|
||||
}
|
||||
|
||||
struct commit *pop_commit(struct commit_list **stack)
|
||||
@@ -1338,12 +1214,12 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
|
||||
next=next->next;
|
||||
}
|
||||
/*
|
||||
* find the tips
|
||||
*
|
||||
* tips are nodes not reachable from any other node in the list
|
||||
*
|
||||
* the tips serve as a starting set for the work queue.
|
||||
*/
|
||||
* find the tips
|
||||
*
|
||||
* tips are nodes not reachable from any other node in the list
|
||||
*
|
||||
* the tips serve as a starting set for the work queue.
|
||||
*/
|
||||
next=*list;
|
||||
insert = &work;
|
||||
while (next) {
|
||||
@@ -1370,9 +1246,9 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
|
||||
if (pn) {
|
||||
/*
|
||||
* parents are only enqueued for emission
|
||||
* when all their children have been emitted thereby
|
||||
* guaranteeing topological order.
|
||||
*/
|
||||
* when all their children have been emitted thereby
|
||||
* guaranteeing topological order.
|
||||
*/
|
||||
pn->indegree--;
|
||||
if (!pn->indegree) {
|
||||
if (!lifo)
|
||||
@@ -1384,9 +1260,9 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
|
||||
parents=parents->next;
|
||||
}
|
||||
/*
|
||||
* work_item is a commit all of whose children
|
||||
* have already been emitted. we can emit it now.
|
||||
*/
|
||||
* work_item is a commit all of whose children
|
||||
* have already been emitted. we can emit it now.
|
||||
*/
|
||||
*pptr = work_node->list_item;
|
||||
pptr = &(*pptr)->next;
|
||||
*pptr = NULL;
|
||||
@@ -1482,8 +1358,7 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two)
|
||||
}
|
||||
|
||||
struct commit_list *get_merge_bases(struct commit *one,
|
||||
struct commit *two,
|
||||
int cleanup)
|
||||
struct commit *two, int cleanup)
|
||||
{
|
||||
struct commit_list *list;
|
||||
struct commit **rslt;
|
||||
|
||||
8
commit.h
8
commit.h
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "object.h"
|
||||
#include "tree.h"
|
||||
#include "strbuf.h"
|
||||
#include "decorate.h"
|
||||
|
||||
struct commit_list {
|
||||
@@ -61,7 +62,12 @@ enum cmit_fmt {
|
||||
};
|
||||
|
||||
extern enum cmit_fmt get_commit_format(const char *arg);
|
||||
extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char **buf_p, unsigned long *space_p, int abbrev, const char *subject, const char *after_subject, enum date_mode dmode);
|
||||
extern void format_commit_message(const struct commit *commit,
|
||||
const void *format, struct strbuf *sb);
|
||||
extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,
|
||||
struct strbuf *,
|
||||
int abbrev, const char *subject,
|
||||
const char *after_subject, enum date_mode);
|
||||
|
||||
/** Removes the first commit from a list sorted by date, and adds all
|
||||
* of its parents.
|
||||
|
||||
29
compat/memmem.c
Normal file
29
compat/memmem.c
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "../git-compat-util.h"
|
||||
|
||||
void *gitmemmem(const void *haystack, size_t haystack_len,
|
||||
const void *needle, size_t needle_len)
|
||||
{
|
||||
const char *begin = haystack;
|
||||
const char *last_possible = begin + haystack_len - needle_len;
|
||||
|
||||
/*
|
||||
* The first occurrence of the empty string is deemed to occur at
|
||||
* the beginning of the string.
|
||||
*/
|
||||
if (needle_len == 0)
|
||||
return (void *)begin;
|
||||
|
||||
/*
|
||||
* Sanity check, otherwise the loop might search through the whole
|
||||
* memory.
|
||||
*/
|
||||
if (haystack_len < needle_len)
|
||||
return NULL;
|
||||
|
||||
for (; begin <= last_possible; begin++) {
|
||||
if (!memcmp(begin, needle, needle_len))
|
||||
return (void *)begin;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
25
connect.c
25
connect.c
@@ -410,9 +410,7 @@ static int git_proxy_command_options(const char *var, const char *value)
|
||||
if (matchlen == 4 &&
|
||||
!memcmp(value, "none", 4))
|
||||
matchlen = 0;
|
||||
git_proxy_command = xmalloc(matchlen + 1);
|
||||
memcpy(git_proxy_command, value, matchlen);
|
||||
git_proxy_command[matchlen] = 0;
|
||||
git_proxy_command = xmemdupz(value, matchlen);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -504,6 +502,7 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
|
||||
pid_t pid;
|
||||
enum protocol protocol = PROTO_LOCAL;
|
||||
int free_path = 0;
|
||||
struct strbuf cmd;
|
||||
char *port = NULL;
|
||||
|
||||
/* Without this we cannot rely on waitpid() to tell
|
||||
@@ -594,20 +593,15 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
|
||||
if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0)
|
||||
die("unable to create pipe pair for communication");
|
||||
|
||||
char command[MAX_CMD_LEN];
|
||||
char *posn = command;
|
||||
int size = MAX_CMD_LEN;
|
||||
int of = 0;
|
||||
|
||||
of |= add_to_string(&posn, &size, prog, 0);
|
||||
of |= add_to_string(&posn, &size, " ", 0);
|
||||
of |= add_to_string(&posn, &size, path, 1);
|
||||
|
||||
if (of)
|
||||
strbuf_init(&cmd, MAX_CMD_LEN);
|
||||
strbuf_addstr(&cmd, prog);
|
||||
strbuf_addch(&cmd, ' ');
|
||||
sq_quote_buf(&cmd, path);
|
||||
if (cmd.len >= MAX_CMD_LEN)
|
||||
die("command line too long");
|
||||
|
||||
if (protocol == PROTO_SSH) {
|
||||
const char *arg[] = { NULL, NULL, NULL, host, command, NULL };
|
||||
const char *arg[] = { NULL, NULL, NULL, host, cmd.buf, NULL };
|
||||
const char **argv;
|
||||
const char *ssh = getenv("GIT_SSH");
|
||||
if (!ssh) ssh = "ssh";
|
||||
@@ -621,7 +615,7 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
|
||||
pid = spawnvpe_pipe(ssh, argv, environ, pipefd[1], pipefd[0]);
|
||||
}
|
||||
else {
|
||||
const char *argv[] = { NULL, "-c", command, NULL };
|
||||
const char *argv[] = { NULL, "-c", cmd.buf, NULL };
|
||||
const char **env = copy_environ();
|
||||
env_unsetenv(env, ALTERNATE_DB_ENVIRONMENT);
|
||||
env_unsetenv(env, DB_ENVIRONMENT);
|
||||
@@ -635,6 +629,7 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
|
||||
die("unable to fork");
|
||||
fd[0] = pipefd[0][0];
|
||||
fd[1] = pipefd[1][1];
|
||||
strbuf_release(&cmd);
|
||||
if (free_path)
|
||||
free(path);
|
||||
return pid;
|
||||
|
||||
@@ -299,7 +299,6 @@ __git_commands ()
|
||||
check-attr) : plumbing;;
|
||||
check-ref-format) : plumbing;;
|
||||
commit-tree) : plumbing;;
|
||||
convert-objects) : plumbing;;
|
||||
cvsexportcommit) : export;;
|
||||
cvsimport) : import;;
|
||||
cvsserver) : daemon;;
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
;; TODO
|
||||
;; - portability to XEmacs
|
||||
;; - better handling of subprocess errors
|
||||
;; - hook into file save (after-save-hook)
|
||||
;; - diff against other branch
|
||||
;; - renaming files from the status buffer
|
||||
;; - creating tags
|
||||
@@ -97,6 +96,21 @@ if there is already one that displays the same directory."
|
||||
:group 'git
|
||||
:type 'string)
|
||||
|
||||
(defcustom git-show-uptodate nil
|
||||
"Whether to display up-to-date files."
|
||||
:group 'git
|
||||
:type 'boolean)
|
||||
|
||||
(defcustom git-show-ignored nil
|
||||
"Whether to display ignored files."
|
||||
:group 'git
|
||||
:type 'boolean)
|
||||
|
||||
(defcustom git-show-unknown t
|
||||
"Whether to display unknown files."
|
||||
:group 'git
|
||||
:type 'boolean)
|
||||
|
||||
|
||||
(defface git-status-face
|
||||
'((((class color) (background light)) (:foreground "purple"))
|
||||
@@ -205,22 +219,15 @@ and returns the process output as a string."
|
||||
(message "Running git %s...done" (car args))
|
||||
buffer))
|
||||
|
||||
(defun git-run-command (buffer env &rest args)
|
||||
(message "Running git %s..." (car args))
|
||||
(apply #'git-call-process-env buffer env args)
|
||||
(message "Running git %s...done" (car args)))
|
||||
|
||||
(defun git-run-command-region (buffer start end env &rest args)
|
||||
"Run a git command with specified buffer region as input."
|
||||
(message "Running git %s..." (car args))
|
||||
(unless (eq 0 (if env
|
||||
(git-run-process-region
|
||||
buffer start end "env"
|
||||
(append (git-get-env-strings env) (list "git") args))
|
||||
(git-run-process-region
|
||||
buffer start end "git" args)))
|
||||
(error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))
|
||||
(message "Running git %s...done" (car args)))
|
||||
(error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string))))
|
||||
|
||||
(defun git-run-hook (hook env &rest args)
|
||||
"Run a git hook and display its output if any."
|
||||
@@ -297,6 +304,13 @@ and returns the process output as a string."
|
||||
"\"")
|
||||
name))
|
||||
|
||||
(defun git-success-message (text files)
|
||||
"Print a success message after having handled FILES."
|
||||
(let ((n (length files)))
|
||||
(if (equal n 1)
|
||||
(message "%s %s" text (car files))
|
||||
(message "%s %d files" text n))))
|
||||
|
||||
(defun git-get-top-dir (dir)
|
||||
"Retrieve the top-level directory of a git tree."
|
||||
(let ((cdup (with-output-to-string
|
||||
@@ -323,7 +337,7 @@ and returns the process output as a string."
|
||||
(sort-lines nil (point-min) (point-max))
|
||||
(save-buffer))
|
||||
(when created
|
||||
(git-run-command nil nil "update-index" "--add" "--" (file-relative-name ignore-name)))
|
||||
(git-call-process-env nil nil "update-index" "--add" "--" (file-relative-name ignore-name)))
|
||||
(git-update-status-files (list (file-relative-name ignore-name)) 'unknown)))
|
||||
|
||||
; propertize definition for XEmacs, stolen from erc-compat
|
||||
@@ -470,14 +484,36 @@ and returns the process output as a string."
|
||||
"Remove everything from the status list."
|
||||
(ewoc-filter status (lambda (info) nil)))
|
||||
|
||||
(defun git-set-files-state (files state)
|
||||
"Set the state of a list of files."
|
||||
(dolist (info files)
|
||||
(unless (eq (git-fileinfo->state info) state)
|
||||
(setf (git-fileinfo->state info) state)
|
||||
(setf (git-fileinfo->rename-state info) nil)
|
||||
(setf (git-fileinfo->orig-name info) nil)
|
||||
(setf (git-fileinfo->needs-refresh info) t))))
|
||||
(defun git-set-fileinfo-state (info state)
|
||||
"Set the state of a file info."
|
||||
(unless (eq (git-fileinfo->state info) state)
|
||||
(setf (git-fileinfo->state info) state
|
||||
(git-fileinfo->old-perm info) 0
|
||||
(git-fileinfo->new-perm info) 0
|
||||
(git-fileinfo->rename-state info) nil
|
||||
(git-fileinfo->orig-name info) nil
|
||||
(git-fileinfo->needs-refresh info) t)))
|
||||
|
||||
(defun git-status-filenames-map (status func files &rest args)
|
||||
"Apply FUNC to the status files names in the FILES list."
|
||||
(when files
|
||||
(setq files (sort files #'string-lessp))
|
||||
(let ((file (pop files))
|
||||
(node (ewoc-nth status 0)))
|
||||
(while (and file node)
|
||||
(let ((info (ewoc-data node)))
|
||||
(if (string-lessp (git-fileinfo->name info) file)
|
||||
(setq node (ewoc-next status node))
|
||||
(if (string-equal (git-fileinfo->name info) file)
|
||||
(apply func info args))
|
||||
(setq file (pop files))))))))
|
||||
|
||||
(defun git-set-filenames-state (status files state)
|
||||
"Set the state of a list of named files."
|
||||
(when files
|
||||
(git-status-filenames-map status #'git-set-fileinfo-state files state)
|
||||
(unless state ;; delete files whose state has been set to nil
|
||||
(ewoc-filter status (lambda (info) (git-fileinfo->state info))))))
|
||||
|
||||
(defun git-state-code (code)
|
||||
"Convert from a string to a added/deleted/modified state."
|
||||
@@ -532,21 +568,38 @@ and returns the process output as a string."
|
||||
" " (git-escape-file-name (git-fileinfo->name info))
|
||||
(git-rename-as-string info))))
|
||||
|
||||
(defun git-insert-fileinfo (status info &optional refresh)
|
||||
"Insert INFO in the status buffer, optionally refreshing an existing one."
|
||||
(let ((node (and refresh
|
||||
(git-find-status-file status (git-fileinfo->name info)))))
|
||||
(setf (git-fileinfo->needs-refresh info) t)
|
||||
(when node ;preserve the marked flag
|
||||
(setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node))))
|
||||
(if node (setf (ewoc-data node) info) (ewoc-enter-last status info))))
|
||||
(defun git-insert-info-list (status infolist)
|
||||
"Insert a list of file infos in the status buffer, replacing existing ones if any."
|
||||
(setq infolist (sort infolist
|
||||
(lambda (info1 info2)
|
||||
(string-lessp (git-fileinfo->name info1)
|
||||
(git-fileinfo->name info2)))))
|
||||
(let ((info (pop infolist))
|
||||
(node (ewoc-nth status 0)))
|
||||
(while info
|
||||
(setf (git-fileinfo->needs-refresh info) t)
|
||||
(cond ((not node)
|
||||
(ewoc-enter-last status info)
|
||||
(setq info (pop infolist)))
|
||||
((string-lessp (git-fileinfo->name (ewoc-data node))
|
||||
(git-fileinfo->name info))
|
||||
(setq node (ewoc-next status node)))
|
||||
((string-equal (git-fileinfo->name (ewoc-data node))
|
||||
(git-fileinfo->name info))
|
||||
;; preserve the marked flag
|
||||
(setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node)))
|
||||
(setf (ewoc-data node) info)
|
||||
(setq info (pop infolist)))
|
||||
(t
|
||||
(ewoc-enter-before status node info)
|
||||
(setq info (pop infolist)))))))
|
||||
|
||||
(defun git-run-diff-index (status files)
|
||||
"Run git-diff-index on FILES and parse the results into STATUS.
|
||||
Return the list of files that haven't been handled."
|
||||
(let ((refresh files))
|
||||
(let (infolist)
|
||||
(with-temp-buffer
|
||||
(apply #'git-run-command t nil "diff-index" "-z" "-M" "HEAD" "--" files)
|
||||
(apply #'git-call-process-env t nil "diff-index" "-z" "-M" "HEAD" "--" files)
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward
|
||||
":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0"
|
||||
@@ -558,13 +611,14 @@ Return the list of files that haven't been handled."
|
||||
(new-name (match-string 8)))
|
||||
(if new-name ; copy or rename
|
||||
(if (eq ?C (string-to-char state))
|
||||
(git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) refresh)
|
||||
(git-insert-fileinfo status (git-create-fileinfo 'deleted name 0 0 'rename new-name) refresh)
|
||||
(git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'rename name)) refresh)
|
||||
(git-insert-fileinfo status (git-create-fileinfo (git-state-code state) name old-perm new-perm) refresh))
|
||||
(push (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) infolist)
|
||||
(push (git-create-fileinfo 'deleted name 0 0 'rename new-name) infolist)
|
||||
(push (git-create-fileinfo 'added new-name old-perm new-perm 'rename name) infolist))
|
||||
(push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist))
|
||||
(setq files (delete name files))
|
||||
(when new-name (setq files (delete new-name files)))))))
|
||||
files)
|
||||
(when new-name (setq files (delete new-name files))))))
|
||||
(git-insert-info-list status infolist)
|
||||
files))
|
||||
|
||||
(defun git-find-status-file (status file)
|
||||
"Find a given file in the status ewoc and return its node."
|
||||
@@ -576,27 +630,26 @@ Return the list of files that haven't been handled."
|
||||
(defun git-run-ls-files (status files default-state &rest options)
|
||||
"Run git-ls-files on FILES and parse the results into STATUS.
|
||||
Return the list of files that haven't been handled."
|
||||
(let ((refresh files))
|
||||
(let (infolist)
|
||||
(with-temp-buffer
|
||||
(apply #'git-run-command t nil "ls-files" "-z" "-t" (append options (list "--") files))
|
||||
(apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files))
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "\\([HMRCK?]\\) \\([^\0]*\\)\0" nil t 1)
|
||||
(let ((state (match-string 1))
|
||||
(name (match-string 2)))
|
||||
(git-insert-fileinfo status (git-create-fileinfo (or (git-state-code state) default-state) name) refresh)
|
||||
(setq files (delete name files))))))
|
||||
files)
|
||||
(while (re-search-forward "\\([^\0]*\\)\0" nil t 1)
|
||||
(let ((name (match-string 1)))
|
||||
(push (git-create-fileinfo default-state name) infolist)
|
||||
(setq files (delete name files)))))
|
||||
(git-insert-info-list status infolist)
|
||||
files))
|
||||
|
||||
(defun git-run-ls-unmerged (status files)
|
||||
"Run git-ls-files -u on FILES and parse the results into STATUS."
|
||||
(with-temp-buffer
|
||||
(apply #'git-run-command t nil "ls-files" "-z" "-u" "--" files)
|
||||
(apply #'git-call-process-env t nil "ls-files" "-z" "-u" "--" files)
|
||||
(goto-char (point-min))
|
||||
(let (unmerged-files)
|
||||
(while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t)
|
||||
(let ((node (git-find-status-file status (match-string 1))))
|
||||
(when node (push (ewoc-data node) unmerged-files))))
|
||||
(git-set-files-state unmerged-files 'unmerged))))
|
||||
(push (match-string 1) unmerged-files))
|
||||
(git-set-filenames-state status unmerged-files 'unmerged))))
|
||||
|
||||
(defun git-get-exclude-files ()
|
||||
"Get the list of exclude files to pass to git-ls-files."
|
||||
@@ -608,34 +661,30 @@ Return the list of files that haven't been handled."
|
||||
(push config files))
|
||||
files))
|
||||
|
||||
(defun git-run-ls-files-with-excludes (status files default-state &rest options)
|
||||
"Run git-ls-files on FILES with appropriate --exclude-from options."
|
||||
(let ((exclude-files (git-get-exclude-files)))
|
||||
(apply #'git-run-ls-files status files default-state
|
||||
(concat "--exclude-per-directory=" git-per-dir-ignore-file)
|
||||
(append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files)))))
|
||||
|
||||
(defun git-update-status-files (files &optional default-state)
|
||||
"Update the status of FILES from the index."
|
||||
(unless git-status (error "Not in git-status buffer."))
|
||||
(let* ((status git-status)
|
||||
(remaining-files
|
||||
(unless files
|
||||
(when git-show-uptodate (git-run-ls-files git-status nil 'uptodate "-c")))
|
||||
(let* ((remaining-files
|
||||
(if (git-empty-db-p) ; we need some special handling for an empty db
|
||||
(git-run-ls-files status files 'added "-c")
|
||||
(git-run-diff-index status files))))
|
||||
(git-run-ls-unmerged status files)
|
||||
(when (or (not files) remaining-files)
|
||||
(let ((exclude-files (git-get-exclude-files)))
|
||||
(setq remaining-files (apply #'git-run-ls-files status remaining-files 'unknown "-o"
|
||||
(concat "--exclude-per-directory=" git-per-dir-ignore-file)
|
||||
(mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files)))))
|
||||
; mark remaining files with the default state (or remove them if nil)
|
||||
(when remaining-files
|
||||
(if default-state
|
||||
(ewoc-map (lambda (info)
|
||||
(when (member (git-fileinfo->name info) remaining-files)
|
||||
(git-set-files-state (list info) default-state))
|
||||
nil)
|
||||
status)
|
||||
(ewoc-filter status
|
||||
(lambda (info files)
|
||||
(not (member (git-fileinfo->name info) files)))
|
||||
remaining-files)))
|
||||
(git-run-ls-files git-status files 'added "-c")
|
||||
(git-run-diff-index git-status files))))
|
||||
(git-run-ls-unmerged git-status files)
|
||||
(when (or remaining-files (and git-show-unknown (not files)))
|
||||
(setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'unknown "-o")))
|
||||
(when (or remaining-files (and git-show-ignored (not files)))
|
||||
(setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'ignored "-o" "-i")))
|
||||
(git-set-filenames-state git-status remaining-files default-state)
|
||||
(git-refresh-files)
|
||||
(git-refresh-ewoc-hf status)))
|
||||
(git-refresh-ewoc-hf git-status)))
|
||||
|
||||
(defun git-marked-files ()
|
||||
"Return a list of all marked files, or if none a list containing just the file at cursor position."
|
||||
@@ -698,11 +747,11 @@ Return the list of files that haven't been handled."
|
||||
('deleted (push info deleted))
|
||||
('modified (push info modified))))
|
||||
(when added
|
||||
(apply #'git-run-command nil env "update-index" "--add" "--" (git-get-filenames added)))
|
||||
(apply #'git-call-process-env nil env "update-index" "--add" "--" (git-get-filenames added)))
|
||||
(when deleted
|
||||
(apply #'git-run-command nil env "update-index" "--remove" "--" (git-get-filenames deleted)))
|
||||
(apply #'git-call-process-env nil env "update-index" "--remove" "--" (git-get-filenames deleted)))
|
||||
(when modified
|
||||
(apply #'git-run-command nil env "update-index" "--" (git-get-filenames modified)))))
|
||||
(apply #'git-call-process-env nil env "update-index" "--" (git-get-filenames modified)))))
|
||||
|
||||
(defun git-run-pre-commit-hook ()
|
||||
"Run the pre-commit hook if any."
|
||||
@@ -734,6 +783,7 @@ Return the list of files that haven't been handled."
|
||||
head-tree (git-rev-parse "HEAD^{tree}")))
|
||||
(if files
|
||||
(progn
|
||||
(message "Running git commit...")
|
||||
(git-read-tree head-tree index-file)
|
||||
(git-update-index nil files) ;update both the default index
|
||||
(git-update-index index-file files) ;and the temporary one
|
||||
@@ -744,8 +794,8 @@ Return the list of files that haven't been handled."
|
||||
(condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
|
||||
(condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
|
||||
(with-current-buffer buffer (erase-buffer))
|
||||
(git-set-files-state files 'uptodate)
|
||||
(git-run-command nil nil "rerere")
|
||||
(dolist (info files) (git-set-fileinfo-state info 'uptodate))
|
||||
(git-call-process-env nil nil "rerere")
|
||||
(git-refresh-files)
|
||||
(git-refresh-ewoc-hf git-status)
|
||||
(message "Committed %s." commit)
|
||||
@@ -853,11 +903,12 @@ Return the list of files that haven't been handled."
|
||||
(defun git-add-file ()
|
||||
"Add marked file(s) to the index cache."
|
||||
(interactive)
|
||||
(let ((files (git-get-filenames (git-marked-files-state 'unknown))))
|
||||
(let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored))))
|
||||
(unless files
|
||||
(push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
|
||||
(apply #'git-run-command nil nil "update-index" "--add" "--" files)
|
||||
(git-update-status-files files 'uptodate)))
|
||||
(apply #'git-call-process-env nil nil "update-index" "--add" "--" files)
|
||||
(git-update-status-files files 'uptodate)
|
||||
(git-success-message "Added" files)))
|
||||
|
||||
(defun git-ignore-file ()
|
||||
"Add marked file(s) to the ignore list."
|
||||
@@ -866,12 +917,13 @@ Return the list of files that haven't been handled."
|
||||
(unless files
|
||||
(push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files))
|
||||
(dolist (f files) (git-append-to-ignore f))
|
||||
(git-update-status-files files 'ignored)))
|
||||
(git-update-status-files files 'ignored)
|
||||
(git-success-message "Ignored" files)))
|
||||
|
||||
(defun git-remove-file ()
|
||||
"Remove the marked file(s)."
|
||||
(interactive)
|
||||
(let ((files (git-get-filenames (git-marked-files-state 'added 'modified 'unknown 'uptodate))))
|
||||
(let ((files (git-get-filenames (git-marked-files-state 'added 'modified 'unknown 'uptodate 'ignored))))
|
||||
(unless files
|
||||
(push (file-relative-name (read-file-name "File to remove: " nil nil t)) files))
|
||||
(if (yes-or-no-p
|
||||
@@ -879,8 +931,9 @@ Return the list of files that haven't been handled."
|
||||
(progn
|
||||
(dolist (name files)
|
||||
(when (file-exists-p name) (delete-file name)))
|
||||
(apply #'git-run-command nil nil "update-index" "--remove" "--" files)
|
||||
(git-update-status-files files nil))
|
||||
(apply #'git-call-process-env nil nil "update-index" "--remove" "--" files)
|
||||
(git-update-status-files files nil)
|
||||
(git-success-message "Removed" files))
|
||||
(message "Aborting"))))
|
||||
|
||||
(defun git-revert-file ()
|
||||
@@ -898,29 +951,65 @@ Return the list of files that haven't been handled."
|
||||
('unmerged (push (git-fileinfo->name info) modified))
|
||||
('modified (push (git-fileinfo->name info) modified))))
|
||||
(when added
|
||||
(apply #'git-run-command nil nil "update-index" "--force-remove" "--" added))
|
||||
(apply #'git-call-process-env nil nil "update-index" "--force-remove" "--" added))
|
||||
(when modified
|
||||
(apply #'git-run-command nil nil "checkout" "HEAD" modified))
|
||||
(git-update-status-files (append added modified) 'uptodate))))
|
||||
(apply #'git-call-process-env nil nil "checkout" "HEAD" modified))
|
||||
(git-update-status-files (append added modified) 'uptodate)
|
||||
(git-success-message "Reverted" files))))
|
||||
|
||||
(defun git-resolve-file ()
|
||||
"Resolve conflicts in marked file(s)."
|
||||
(interactive)
|
||||
(let ((files (git-get-filenames (git-marked-files-state 'unmerged))))
|
||||
(when files
|
||||
(apply #'git-run-command nil nil "update-index" "--" files)
|
||||
(git-update-status-files files 'uptodate))))
|
||||
(apply #'git-call-process-env nil nil "update-index" "--" files)
|
||||
(git-update-status-files files 'uptodate)
|
||||
(git-success-message "Resolved" files))))
|
||||
|
||||
(defun git-remove-handled ()
|
||||
"Remove handled files from the status list."
|
||||
(interactive)
|
||||
(ewoc-filter git-status
|
||||
(lambda (info)
|
||||
(not (or (eq (git-fileinfo->state info) 'ignored)
|
||||
(eq (git-fileinfo->state info) 'uptodate)))))
|
||||
(case (git-fileinfo->state info)
|
||||
('ignored git-show-ignored)
|
||||
('uptodate git-show-uptodate)
|
||||
('unknown git-show-unknown)
|
||||
(t t))))
|
||||
(unless (ewoc-nth git-status 0) ; refresh header if list is empty
|
||||
(git-refresh-ewoc-hf git-status)))
|
||||
|
||||
(defun git-toggle-show-uptodate ()
|
||||
"Toogle the option for showing up-to-date files."
|
||||
(interactive)
|
||||
(if (setq git-show-uptodate (not git-show-uptodate))
|
||||
(git-refresh-status)
|
||||
(git-remove-handled)))
|
||||
|
||||
(defun git-toggle-show-ignored ()
|
||||
"Toogle the option for showing ignored files."
|
||||
(interactive)
|
||||
(if (setq git-show-ignored (not git-show-ignored))
|
||||
(progn
|
||||
(message "Inserting ignored files...")
|
||||
(git-run-ls-files-with-excludes git-status nil 'ignored "-o" "-i")
|
||||
(git-refresh-files)
|
||||
(git-refresh-ewoc-hf git-status)
|
||||
(message "Inserting ignored files...done"))
|
||||
(git-remove-handled)))
|
||||
|
||||
(defun git-toggle-show-unknown ()
|
||||
"Toogle the option for showing unknown files."
|
||||
(interactive)
|
||||
(if (setq git-show-unknown (not git-show-unknown))
|
||||
(progn
|
||||
(message "Inserting unknown files...")
|
||||
(git-run-ls-files-with-excludes git-status nil 'unknown "-o")
|
||||
(git-refresh-files)
|
||||
(git-refresh-ewoc-hf git-status)
|
||||
(message "Inserting unknown files...done"))
|
||||
(git-remove-handled)))
|
||||
|
||||
(defun git-setup-diff-buffer (buffer)
|
||||
"Setup a buffer for displaying a diff."
|
||||
(let ((dir default-directory))
|
||||
@@ -1118,12 +1207,23 @@ Return the list of files that haven't been handled."
|
||||
(interactive)
|
||||
(let* ((status git-status)
|
||||
(pos (ewoc-locate status))
|
||||
(marked-files (git-get-filenames (ewoc-collect status (lambda (info) (git-fileinfo->marked info)))))
|
||||
(cur-name (and pos (git-fileinfo->name (ewoc-data pos)))))
|
||||
(unless status (error "Not in git-status buffer."))
|
||||
(git-run-command nil nil "update-index" "--refresh")
|
||||
(message "Refreshing git status...")
|
||||
(git-call-process-env nil nil "update-index" "--refresh")
|
||||
(git-clear-status status)
|
||||
(git-update-status-files nil)
|
||||
; restore file marks
|
||||
(when marked-files
|
||||
(git-status-filenames-map status
|
||||
(lambda (info)
|
||||
(setf (git-fileinfo->marked info) t)
|
||||
(setf (git-fileinfo->needs-refresh info) t))
|
||||
marked-files)
|
||||
(git-refresh-files))
|
||||
; move point to the current file name if any
|
||||
(message "Refreshing git status...done")
|
||||
(let ((node (and cur-name (git-find-status-file status cur-name))))
|
||||
(when node (ewoc-goto-node status node)))))
|
||||
|
||||
@@ -1146,7 +1246,8 @@ Return the list of files that haven't been handled."
|
||||
|
||||
(unless git-status-mode-map
|
||||
(let ((map (make-keymap))
|
||||
(diff-map (make-sparse-keymap)))
|
||||
(diff-map (make-sparse-keymap))
|
||||
(toggle-map (make-sparse-keymap)))
|
||||
(suppress-keymap map)
|
||||
(define-key map "?" 'git-help)
|
||||
(define-key map "h" 'git-help)
|
||||
@@ -1170,6 +1271,7 @@ Return the list of files that haven't been handled."
|
||||
(define-key map "q" 'git-status-quit)
|
||||
(define-key map "r" 'git-remove-file)
|
||||
(define-key map "R" 'git-resolve-file)
|
||||
(define-key map "t" toggle-map)
|
||||
(define-key map "T" 'git-toggle-all-marks)
|
||||
(define-key map "u" 'git-unmark-file)
|
||||
(define-key map "U" 'git-revert-file)
|
||||
@@ -1186,6 +1288,11 @@ Return the list of files that haven't been handled."
|
||||
(define-key diff-map "h" 'git-diff-file-merge-head)
|
||||
(define-key diff-map "m" 'git-diff-file-mine)
|
||||
(define-key diff-map "o" 'git-diff-file-other)
|
||||
; the toggle submap
|
||||
(define-key toggle-map "u" 'git-toggle-show-uptodate)
|
||||
(define-key toggle-map "i" 'git-toggle-show-ignored)
|
||||
(define-key toggle-map "k" 'git-toggle-show-unknown)
|
||||
(define-key toggle-map "m" 'git-toggle-all-marks)
|
||||
(setq git-status-mode-map map)))
|
||||
|
||||
;; git mode should only run in the *git status* buffer
|
||||
@@ -1207,6 +1314,9 @@ Commands:
|
||||
(let ((status (ewoc-create 'git-fileinfo-prettyprint "" "")))
|
||||
(set (make-local-variable 'git-status) status))
|
||||
(set (make-local-variable 'list-buffers-directory) default-directory)
|
||||
(make-local-variable 'git-show-uptodate)
|
||||
(make-local-variable 'git-show-ignored)
|
||||
(make-local-variable 'git-show-unknown)
|
||||
(run-hooks 'git-status-mode-hook)))
|
||||
|
||||
(defun git-find-status-buffer (dir)
|
||||
@@ -1235,9 +1345,24 @@ Commands:
|
||||
(cd dir)
|
||||
(git-status-mode)
|
||||
(git-refresh-status)
|
||||
(goto-char (point-min)))
|
||||
(goto-char (point-min))
|
||||
(add-hook 'after-save-hook 'git-update-saved-file))
|
||||
(message "%s is not a git working tree." dir)))
|
||||
|
||||
(defun git-update-saved-file ()
|
||||
"Update the corresponding git-status buffer when a file is saved.
|
||||
Meant to be used in `after-save-hook'."
|
||||
(let* ((file (expand-file-name buffer-file-name))
|
||||
(dir (condition-case nil (git-get-top-dir (file-name-directory file))))
|
||||
(buffer (and dir (git-find-status-buffer dir))))
|
||||
(when buffer
|
||||
(with-current-buffer buffer
|
||||
(let ((filename (file-relative-name file dir)))
|
||||
; skip files located inside the .git directory
|
||||
(unless (string-match "^\\.git/" filename)
|
||||
(git-call-process-env nil nil "add" "--refresh" "--" filename)
|
||||
(git-update-status-files (list filename) 'uptodate)))))))
|
||||
|
||||
(defun git-help ()
|
||||
"Display help for Git mode."
|
||||
(interactive)
|
||||
|
||||
64
contrib/fast-import/git-import.perl
Executable file
64
contrib/fast-import/git-import.perl
Executable file
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# Performs an initial import of a directory. This is the equivalent
|
||||
# of doing 'git init; git add .; git commit'. It's a little slower,
|
||||
# but is meant to be a simple fast-import example.
|
||||
|
||||
use strict;
|
||||
use File::Find;
|
||||
|
||||
my $USAGE = 'Usage: git-import branch import-message';
|
||||
my $branch = shift or die "$USAGE\n";
|
||||
my $message = shift or die "$USAGE\n";
|
||||
|
||||
chomp(my $username = `git config user.name`);
|
||||
chomp(my $email = `git config user.email`);
|
||||
die 'You need to set user name and email'
|
||||
unless $username && $email;
|
||||
|
||||
system('git init');
|
||||
open(my $fi, '|-', qw(git fast-import --date-format=now))
|
||||
or die "unable to spawn fast-import: $!";
|
||||
|
||||
print $fi <<EOF;
|
||||
commit refs/heads/$branch
|
||||
committer $username <$email> now
|
||||
data <<MSGEOF
|
||||
$message
|
||||
MSGEOF
|
||||
|
||||
EOF
|
||||
|
||||
find(
|
||||
sub {
|
||||
if($File::Find::name eq './.git') {
|
||||
$File::Find::prune = 1;
|
||||
return;
|
||||
}
|
||||
return unless -f $_;
|
||||
|
||||
my $fn = $File::Find::name;
|
||||
$fn =~ s#^.\/##;
|
||||
|
||||
open(my $in, '<', $_)
|
||||
or die "unable to open $fn: $!";
|
||||
my @st = stat($in)
|
||||
or die "unable to stat $fn: $!";
|
||||
my $len = $st[7];
|
||||
|
||||
print $fi "M 644 inline $fn\n";
|
||||
print $fi "data $len\n";
|
||||
while($len > 0) {
|
||||
my $r = read($in, my $buf, $len < 4096 ? $len : 4096);
|
||||
defined($r) or die "read error from $fn: $!";
|
||||
$r > 0 or die "premature EOF from $fn: $!";
|
||||
print $fi $buf;
|
||||
$len -= $r;
|
||||
}
|
||||
print $fi "\n";
|
||||
|
||||
}, '.'
|
||||
);
|
||||
|
||||
close($fi);
|
||||
exit $?;
|
||||
38
contrib/fast-import/git-import.sh
Executable file
38
contrib/fast-import/git-import.sh
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Performs an initial import of a directory. This is the equivalent
|
||||
# of doing 'git init; git add .; git commit'. It's a lot slower,
|
||||
# but is meant to be a simple fast-import example.
|
||||
|
||||
if [ -z "$1" -o -z "$2" ]; then
|
||||
echo "Usage: git-import branch import-message"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
USERNAME="$(git config user.name)"
|
||||
EMAIL="$(git config user.email)"
|
||||
|
||||
if [ -z "$USERNAME" -o -z "$EMAIL" ]; then
|
||||
echo "You need to set user name and email"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git init
|
||||
|
||||
(
|
||||
cat <<EOF
|
||||
commit refs/heads/$1
|
||||
committer $USERNAME <$EMAIL> now
|
||||
data <<MSGEOF
|
||||
$2
|
||||
MSGEOF
|
||||
|
||||
EOF
|
||||
find * -type f|while read i;do
|
||||
echo "M 100644 inline $i"
|
||||
echo data $(stat -c '%s' "$i")
|
||||
cat "$i"
|
||||
echo
|
||||
done
|
||||
echo
|
||||
) | git fast-import --date-format=now
|
||||
@@ -289,6 +289,19 @@ def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent
|
||||
def originP4BranchesExist():
|
||||
return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master")
|
||||
|
||||
def p4ChangesForPaths(depotPaths, changeRange):
|
||||
assert depotPaths
|
||||
output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, changeRange)
|
||||
for p in depotPaths]))
|
||||
|
||||
changes = []
|
||||
for line in output:
|
||||
changeNum = line.split(" ")[1]
|
||||
changes.append(int(changeNum))
|
||||
|
||||
changes.sort()
|
||||
return changes
|
||||
|
||||
class Command:
|
||||
def __init__(self):
|
||||
self.usage = "usage: %prog [options]"
|
||||
@@ -672,9 +685,8 @@ class P4Submit(Command):
|
||||
f.close();
|
||||
|
||||
os.chdir(self.clientPath)
|
||||
response = raw_input("Do you want to sync %s with p4 sync? [y]es/[n]o " % self.clientPath)
|
||||
if response == "y" or response == "yes":
|
||||
system("p4 sync ...")
|
||||
print "Syncronizing p4 checkout..."
|
||||
system("p4 sync ...")
|
||||
|
||||
if self.reset:
|
||||
self.firstTime = True
|
||||
@@ -713,10 +725,14 @@ class P4Submit(Command):
|
||||
else:
|
||||
print "All changes applied!"
|
||||
os.chdir(self.oldWorkingDirectory)
|
||||
response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ")
|
||||
|
||||
sync = P4Sync()
|
||||
sync.run([])
|
||||
|
||||
response = raw_input("Do you want to rebase current HEAD from Perforce now using git-p4 rebase? [y]es/[n]o ")
|
||||
if response == "y" or response == "yes":
|
||||
rebase = P4Rebase()
|
||||
rebase.run([])
|
||||
rebase.rebase()
|
||||
os.remove(self.configFile)
|
||||
|
||||
return True
|
||||
@@ -1110,6 +1126,186 @@ class P4Sync(Command):
|
||||
self.keepRepoPath = (d.has_key('options')
|
||||
and ('keepRepoPath' in d['options']))
|
||||
|
||||
def gitRefForBranch(self, branch):
|
||||
if branch == "main":
|
||||
return self.refPrefix + "master"
|
||||
|
||||
if len(branch) <= 0:
|
||||
return branch
|
||||
|
||||
return self.refPrefix + self.projectName + branch
|
||||
|
||||
def gitCommitByP4Change(self, ref, change):
|
||||
if self.verbose:
|
||||
print "looking in ref " + ref + " for change %s using bisect..." % change
|
||||
|
||||
earliestCommit = ""
|
||||
latestCommit = parseRevision(ref)
|
||||
|
||||
while True:
|
||||
if self.verbose:
|
||||
print "trying: earliest %s latest %s" % (earliestCommit, latestCommit)
|
||||
next = read_pipe("git rev-list --bisect %s %s" % (latestCommit, earliestCommit)).strip()
|
||||
if len(next) == 0:
|
||||
if self.verbose:
|
||||
print "argh"
|
||||
return ""
|
||||
log = extractLogMessageFromGitCommit(next)
|
||||
settings = extractSettingsGitLog(log)
|
||||
currentChange = int(settings['change'])
|
||||
if self.verbose:
|
||||
print "current change %s" % currentChange
|
||||
|
||||
if currentChange == change:
|
||||
if self.verbose:
|
||||
print "found %s" % next
|
||||
return next
|
||||
|
||||
if currentChange < change:
|
||||
earliestCommit = "^%s" % next
|
||||
else:
|
||||
latestCommit = "%s" % next
|
||||
|
||||
return ""
|
||||
|
||||
def importNewBranch(self, branch, maxChange):
|
||||
# make fast-import flush all changes to disk and update the refs using the checkpoint
|
||||
# command so that we can try to find the branch parent in the git history
|
||||
self.gitStream.write("checkpoint\n\n");
|
||||
self.gitStream.flush();
|
||||
branchPrefix = self.depotPaths[0] + branch + "/"
|
||||
range = "@1,%s" % maxChange
|
||||
#print "prefix" + branchPrefix
|
||||
changes = p4ChangesForPaths([branchPrefix], range)
|
||||
if len(changes) <= 0:
|
||||
return False
|
||||
firstChange = changes[0]
|
||||
#print "first change in branch: %s" % firstChange
|
||||
sourceBranch = self.knownBranches[branch]
|
||||
sourceDepotPath = self.depotPaths[0] + sourceBranch
|
||||
sourceRef = self.gitRefForBranch(sourceBranch)
|
||||
#print "source " + sourceBranch
|
||||
|
||||
branchParentChange = int(p4Cmd("changes -m 1 %s...@1,%s" % (sourceDepotPath, firstChange))["change"])
|
||||
#print "branch parent: %s" % branchParentChange
|
||||
gitParent = self.gitCommitByP4Change(sourceRef, branchParentChange)
|
||||
if len(gitParent) > 0:
|
||||
self.initialParents[self.gitRefForBranch(branch)] = gitParent
|
||||
#print "parent git commit: %s" % gitParent
|
||||
|
||||
self.importChanges(changes)
|
||||
return True
|
||||
|
||||
def importChanges(self, changes):
|
||||
cnt = 1
|
||||
for change in changes:
|
||||
description = p4Cmd("describe %s" % change)
|
||||
self.updateOptionDict(description)
|
||||
|
||||
if not self.silent:
|
||||
sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
|
||||
sys.stdout.flush()
|
||||
cnt = cnt + 1
|
||||
|
||||
try:
|
||||
if self.detectBranches:
|
||||
branches = self.splitFilesIntoBranches(description)
|
||||
for branch in branches.keys():
|
||||
## HACK --hwn
|
||||
branchPrefix = self.depotPaths[0] + branch + "/"
|
||||
|
||||
parent = ""
|
||||
|
||||
filesForCommit = branches[branch]
|
||||
|
||||
if self.verbose:
|
||||
print "branch is %s" % branch
|
||||
|
||||
self.updatedBranches.add(branch)
|
||||
|
||||
if branch not in self.createdBranches:
|
||||
self.createdBranches.add(branch)
|
||||
parent = self.knownBranches[branch]
|
||||
if parent == branch:
|
||||
parent = ""
|
||||
else:
|
||||
fullBranch = self.projectName + branch
|
||||
if fullBranch not in self.p4BranchesInGit:
|
||||
if not self.silent:
|
||||
print("\n Importing new branch %s" % fullBranch);
|
||||
if self.importNewBranch(branch, change - 1):
|
||||
parent = ""
|
||||
self.p4BranchesInGit.append(fullBranch)
|
||||
if not self.silent:
|
||||
print("\n Resuming with change %s" % change);
|
||||
|
||||
if self.verbose:
|
||||
print "parent determined through known branches: %s" % parent
|
||||
|
||||
branch = self.gitRefForBranch(branch)
|
||||
parent = self.gitRefForBranch(parent)
|
||||
|
||||
if self.verbose:
|
||||
print "looking for initial parent for %s; current parent is %s" % (branch, parent)
|
||||
|
||||
if len(parent) == 0 and branch in self.initialParents:
|
||||
parent = self.initialParents[branch]
|
||||
del self.initialParents[branch]
|
||||
|
||||
self.commit(description, filesForCommit, branch, [branchPrefix], parent)
|
||||
else:
|
||||
files = self.extractFilesFromCommit(description)
|
||||
self.commit(description, files, self.branch, self.depotPaths,
|
||||
self.initialParent)
|
||||
self.initialParent = ""
|
||||
except IOError:
|
||||
print self.gitError.read()
|
||||
sys.exit(1)
|
||||
|
||||
def importHeadRevision(self, revision):
|
||||
print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch)
|
||||
|
||||
details = { "user" : "git perforce import user", "time" : int(time.time()) }
|
||||
details["desc"] = ("Initial import of %s from the state at revision %s"
|
||||
% (' '.join(self.depotPaths), revision))
|
||||
details["change"] = revision
|
||||
newestRevision = 0
|
||||
|
||||
fileCnt = 0
|
||||
for info in p4CmdList("files "
|
||||
+ ' '.join(["%s...%s"
|
||||
% (p, revision)
|
||||
for p in self.depotPaths])):
|
||||
|
||||
if info['code'] == 'error':
|
||||
sys.stderr.write("p4 returned an error: %s\n"
|
||||
% info['data'])
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
change = int(info["change"])
|
||||
if change > newestRevision:
|
||||
newestRevision = change
|
||||
|
||||
if info["action"] == "delete":
|
||||
# don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
|
||||
#fileCnt = fileCnt + 1
|
||||
continue
|
||||
|
||||
for prop in ["depotFile", "rev", "action", "type" ]:
|
||||
details["%s%s" % (prop, fileCnt)] = info[prop]
|
||||
|
||||
fileCnt = fileCnt + 1
|
||||
|
||||
details["change"] = newestRevision
|
||||
self.updateOptionDict(details)
|
||||
try:
|
||||
self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths)
|
||||
except IOError:
|
||||
print "IO error with git fast-import. Is your git version recent enough?"
|
||||
print self.gitError.read()
|
||||
|
||||
|
||||
def run(self, args):
|
||||
self.depotPaths = []
|
||||
self.changeRange = ""
|
||||
@@ -1207,7 +1403,7 @@ class P4Sync(Command):
|
||||
|
||||
self.depotPaths = sorted(args)
|
||||
|
||||
self.revision = ""
|
||||
revision = ""
|
||||
self.users = {}
|
||||
|
||||
newPaths = []
|
||||
@@ -1218,15 +1414,15 @@ class P4Sync(Command):
|
||||
if self.changeRange == "@all":
|
||||
self.changeRange = ""
|
||||
elif ',' not in self.changeRange:
|
||||
self.revision = self.changeRange
|
||||
revision = self.changeRange
|
||||
self.changeRange = ""
|
||||
p = p[:atIdx]
|
||||
elif p.find("#") != -1:
|
||||
hashIdx = p.index("#")
|
||||
self.revision = p[hashIdx:]
|
||||
revision = p[hashIdx:]
|
||||
p = p[:hashIdx]
|
||||
elif self.previousDepotPaths == []:
|
||||
self.revision = "#head"
|
||||
revision = "#head"
|
||||
|
||||
p = re.sub ("\.\.\.$", "", p)
|
||||
if not p.endswith("/"):
|
||||
@@ -1267,49 +1463,8 @@ class P4Sync(Command):
|
||||
self.gitStream = importProcess.stdin
|
||||
self.gitError = importProcess.stderr
|
||||
|
||||
if self.revision:
|
||||
print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), self.revision, self.branch)
|
||||
|
||||
details = { "user" : "git perforce import user", "time" : int(time.time()) }
|
||||
details["desc"] = ("Initial import of %s from the state at revision %s"
|
||||
% (' '.join(self.depotPaths), self.revision))
|
||||
details["change"] = self.revision
|
||||
newestRevision = 0
|
||||
|
||||
fileCnt = 0
|
||||
for info in p4CmdList("files "
|
||||
+ ' '.join(["%s...%s"
|
||||
% (p, self.revision)
|
||||
for p in self.depotPaths])):
|
||||
|
||||
if info['code'] == 'error':
|
||||
sys.stderr.write("p4 returned an error: %s\n"
|
||||
% info['data'])
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
change = int(info["change"])
|
||||
if change > newestRevision:
|
||||
newestRevision = change
|
||||
|
||||
if info["action"] == "delete":
|
||||
# don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
|
||||
#fileCnt = fileCnt + 1
|
||||
continue
|
||||
|
||||
for prop in ["depotFile", "rev", "action", "type" ]:
|
||||
details["%s%s" % (prop, fileCnt)] = info[prop]
|
||||
|
||||
fileCnt = fileCnt + 1
|
||||
|
||||
details["change"] = newestRevision
|
||||
self.updateOptionDict(details)
|
||||
try:
|
||||
self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths)
|
||||
except IOError:
|
||||
print "IO error with git fast-import. Is your git version recent enough?"
|
||||
print self.gitError.read()
|
||||
|
||||
if revision:
|
||||
self.importHeadRevision(revision)
|
||||
else:
|
||||
changes = []
|
||||
|
||||
@@ -1327,15 +1482,7 @@ class P4Sync(Command):
|
||||
if self.verbose:
|
||||
print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
|
||||
self.changeRange)
|
||||
assert self.depotPaths
|
||||
output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, self.changeRange)
|
||||
for p in self.depotPaths]))
|
||||
|
||||
for line in output:
|
||||
changeNum = line.split(" ")[1]
|
||||
changes.append(int(changeNum))
|
||||
|
||||
changes.sort()
|
||||
changes = p4ChangesForPaths(self.depotPaths, self.changeRange)
|
||||
|
||||
if len(self.maxChanges) > 0:
|
||||
changes = changes[:min(int(self.maxChanges), len(changes))]
|
||||
@@ -1350,74 +1497,7 @@ class P4Sync(Command):
|
||||
|
||||
self.updatedBranches = set()
|
||||
|
||||
cnt = 1
|
||||
for change in changes:
|
||||
description = p4Cmd("describe %s" % change)
|
||||
self.updateOptionDict(description)
|
||||
|
||||
if not self.silent:
|
||||
sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
|
||||
sys.stdout.flush()
|
||||
cnt = cnt + 1
|
||||
|
||||
try:
|
||||
if self.detectBranches:
|
||||
branches = self.splitFilesIntoBranches(description)
|
||||
for branch in branches.keys():
|
||||
## HACK --hwn
|
||||
branchPrefix = self.depotPaths[0] + branch + "/"
|
||||
|
||||
parent = ""
|
||||
|
||||
filesForCommit = branches[branch]
|
||||
|
||||
if self.verbose:
|
||||
print "branch is %s" % branch
|
||||
|
||||
self.updatedBranches.add(branch)
|
||||
|
||||
if branch not in self.createdBranches:
|
||||
self.createdBranches.add(branch)
|
||||
parent = self.knownBranches[branch]
|
||||
if parent == branch:
|
||||
parent = ""
|
||||
elif self.verbose:
|
||||
print "parent determined through known branches: %s" % parent
|
||||
|
||||
# main branch? use master
|
||||
if branch == "main":
|
||||
branch = "master"
|
||||
else:
|
||||
|
||||
## FIXME
|
||||
branch = self.projectName + branch
|
||||
|
||||
if parent == "main":
|
||||
parent = "master"
|
||||
elif len(parent) > 0:
|
||||
## FIXME
|
||||
parent = self.projectName + parent
|
||||
|
||||
branch = self.refPrefix + branch
|
||||
if len(parent) > 0:
|
||||
parent = self.refPrefix + parent
|
||||
|
||||
if self.verbose:
|
||||
print "looking for initial parent for %s; current parent is %s" % (branch, parent)
|
||||
|
||||
if len(parent) == 0 and branch in self.initialParents:
|
||||
parent = self.initialParents[branch]
|
||||
del self.initialParents[branch]
|
||||
|
||||
self.commit(description, filesForCommit, branch, [branchPrefix], parent)
|
||||
else:
|
||||
files = self.extractFilesFromCommit(description)
|
||||
self.commit(description, files, self.branch, self.depotPaths,
|
||||
self.initialParent)
|
||||
self.initialParent = ""
|
||||
except IOError:
|
||||
print self.gitError.read()
|
||||
sys.exit(1)
|
||||
self.importChanges(changes)
|
||||
|
||||
if not self.silent:
|
||||
print ""
|
||||
@@ -1427,7 +1507,6 @@ class P4Sync(Command):
|
||||
sys.stdout.write("%s " % b)
|
||||
sys.stdout.write("\n")
|
||||
|
||||
|
||||
self.gitStream.close()
|
||||
if importProcess.wait() != 0:
|
||||
die("fast-import failed: %s" % self.gitError.read())
|
||||
@@ -1448,6 +1527,9 @@ class P4Rebase(Command):
|
||||
sync = P4Sync()
|
||||
sync.run([])
|
||||
|
||||
return self.rebase()
|
||||
|
||||
def rebase(self):
|
||||
[upstream, settings] = findUpstreamBranchPoint()
|
||||
if len(upstream) == 0:
|
||||
die("Cannot find upstream branchpoint for rebase")
|
||||
@@ -1569,6 +1651,7 @@ def printUsage(commands):
|
||||
commands = {
|
||||
"debug" : P4Debug,
|
||||
"submit" : P4Submit,
|
||||
"commit" : P4Submit,
|
||||
"sync" : P4Sync,
|
||||
"rebase" : P4Rebase,
|
||||
"clone" : P4Clone,
|
||||
|
||||
@@ -27,12 +27,20 @@ import math
|
||||
import string
|
||||
import fcntl
|
||||
|
||||
try:
|
||||
import gtksourceview2
|
||||
have_gtksourceview2 = True
|
||||
except ImportError:
|
||||
have_gtksourceview2 = False
|
||||
|
||||
try:
|
||||
import gtksourceview
|
||||
have_gtksourceview = True
|
||||
except ImportError:
|
||||
have_gtksourceview = False
|
||||
print "Running without gtksourceview module"
|
||||
|
||||
if not have_gtksourceview2 and not have_gtksourceview:
|
||||
print "Running without gtksourceview2 or gtksourceview module"
|
||||
|
||||
re_ident = re.compile('(author|committer) (?P<ident>.*) (?P<epoch>\d+) (?P<tz>[+-]\d{4})')
|
||||
|
||||
@@ -58,6 +66,26 @@ def show_date(epoch, tz):
|
||||
|
||||
return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(secs))
|
||||
|
||||
def get_source_buffer_and_view():
|
||||
if have_gtksourceview2:
|
||||
buffer = gtksourceview2.Buffer()
|
||||
slm = gtksourceview2.LanguageManager()
|
||||
gsl = slm.get_language("diff")
|
||||
buffer.set_highlight_syntax(True)
|
||||
buffer.set_language(gsl)
|
||||
view = gtksourceview2.View(buffer)
|
||||
elif have_gtksourceview:
|
||||
buffer = gtksourceview.SourceBuffer()
|
||||
slm = gtksourceview.SourceLanguagesManager()
|
||||
gsl = slm.get_language_from_mime_type("text/x-patch")
|
||||
buffer.set_highlight(True)
|
||||
buffer.set_language(gsl)
|
||||
view = gtksourceview.SourceView(buffer)
|
||||
else:
|
||||
buffer = gtk.TextBuffer()
|
||||
view = gtk.TextView(buffer)
|
||||
return (buffer, view)
|
||||
|
||||
|
||||
class CellRendererGraph(gtk.GenericCellRenderer):
|
||||
"""Cell renderer for directed graph.
|
||||
@@ -582,17 +610,7 @@ class DiffWindow(object):
|
||||
hpan.pack1(scrollwin, True, True)
|
||||
scrollwin.show()
|
||||
|
||||
if have_gtksourceview:
|
||||
self.buffer = gtksourceview.SourceBuffer()
|
||||
slm = gtksourceview.SourceLanguagesManager()
|
||||
gsl = slm.get_language_from_mime_type("text/x-patch")
|
||||
self.buffer.set_highlight(True)
|
||||
self.buffer.set_language(gsl)
|
||||
sourceview = gtksourceview.SourceView(self.buffer)
|
||||
else:
|
||||
self.buffer = gtk.TextBuffer()
|
||||
sourceview = gtk.TextView(self.buffer)
|
||||
|
||||
(self.buffer, sourceview) = get_source_buffer_and_view()
|
||||
|
||||
sourceview.set_editable(False)
|
||||
sourceview.modify_font(pango.FontDescription("Monospace"))
|
||||
@@ -956,16 +974,7 @@ class GitView(object):
|
||||
vbox.pack_start(scrollwin, expand=True, fill=True)
|
||||
scrollwin.show()
|
||||
|
||||
if have_gtksourceview:
|
||||
self.message_buffer = gtksourceview.SourceBuffer()
|
||||
slm = gtksourceview.SourceLanguagesManager()
|
||||
gsl = slm.get_language_from_mime_type("text/x-patch")
|
||||
self.message_buffer.set_highlight(True)
|
||||
self.message_buffer.set_language(gsl)
|
||||
sourceview = gtksourceview.SourceView(self.message_buffer)
|
||||
else:
|
||||
self.message_buffer = gtk.TextBuffer()
|
||||
sourceview = gtk.TextView(self.message_buffer)
|
||||
(self.message_buffer, sourceview) = get_source_buffer_and_view()
|
||||
|
||||
sourceview.set_editable(False)
|
||||
sourceview.modify_font(pango.FontDescription("Monospace"))
|
||||
|
||||
@@ -29,6 +29,8 @@ hgvers = {}
|
||||
hgchildren = {}
|
||||
# Current branch for each hg revision
|
||||
hgbranch = {}
|
||||
# Number of new changesets converted from hg
|
||||
hgnewcsets = 0
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
@@ -40,6 +42,8 @@ def usage():
|
||||
options:
|
||||
-s, --gitstate=FILE: name of the state to be saved/read
|
||||
for incrementals
|
||||
-n, --nrepack=INT: number of changesets that will trigger
|
||||
a repack (default=0, -1 to deactivate)
|
||||
|
||||
required:
|
||||
hgprj: name of the HG project to import (directory)
|
||||
@@ -68,14 +72,16 @@ def getgitenv(user, date):
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
state = ''
|
||||
opt_nrepack = 0
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], 's:t:', ['gitstate=', 'tempdir='])
|
||||
opts, args = getopt.getopt(sys.argv[1:], 's:t:n:', ['gitstate=', 'tempdir=', 'nrepack='])
|
||||
for o, a in opts:
|
||||
if o in ('-s', '--gitstate'):
|
||||
state = a
|
||||
state = os.path.abspath(state)
|
||||
|
||||
if o in ('-n', '--nrepack'):
|
||||
opt_nrepack = int(a)
|
||||
if len(args) != 1:
|
||||
raise('params')
|
||||
except:
|
||||
@@ -138,6 +144,7 @@ for cset in range(int(tip) + 1):
|
||||
# incremental, already seen
|
||||
if hgvers.has_key(str(cset)):
|
||||
continue
|
||||
hgnewcsets += 1
|
||||
|
||||
# get info
|
||||
prnts = os.popen('hg log -r %d | grep ^parent: | cut -f 2 -d :' % cset).readlines()
|
||||
@@ -222,7 +229,8 @@ for cset in range(int(tip) + 1):
|
||||
print 'record', cset, '->', vvv
|
||||
hgvers[str(cset)] = vvv
|
||||
|
||||
os.system('git-repack -a -d')
|
||||
if hgnewcsets >= opt_nrepack and opt_nrepack != -1:
|
||||
os.system('git-repack -a -d')
|
||||
|
||||
# write the state for incrementals
|
||||
if state:
|
||||
|
||||
@@ -138,7 +138,15 @@ generate_email()
|
||||
|
||||
# Check if we've got anyone to send to
|
||||
if [ -z "$recipients" ]; then
|
||||
echo >&2 "*** hooks.recipients is not set so no email will be sent"
|
||||
case "$refname_type" in
|
||||
"annotated tag")
|
||||
config_name="hooks.announcelist"
|
||||
;;
|
||||
*)
|
||||
config_name="hooks.mailinglist"
|
||||
;;
|
||||
esac
|
||||
echo >&2 "*** $config_name is not set so no email will be sent"
|
||||
echo >&2 "*** for $refname update $oldrev->$newrev"
|
||||
exit 0
|
||||
fi
|
||||
@@ -323,7 +331,7 @@ generate_update_branch_email()
|
||||
echo " via $rev ($revtype)"
|
||||
done
|
||||
|
||||
if [ -z "$fastforward" ]; then
|
||||
if [ "$fast_forward" ]; then
|
||||
echo " from $oldrev ($oldrev_type)"
|
||||
else
|
||||
# 1. Existing revisions were removed. In this case newrev is a
|
||||
|
||||
214
contrib/hooks/setgitperms.perl
Normal file
214
contrib/hooks/setgitperms.perl
Normal file
@@ -0,0 +1,214 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# Copyright (c) 2006 Josh England
|
||||
#
|
||||
# This script can be used to save/restore full permissions and ownership data
|
||||
# within a git working tree.
|
||||
#
|
||||
# To save permissions/ownership data, place this script in your .git/hooks
|
||||
# directory and enable a `pre-commit` hook with the following lines:
|
||||
# #!/bin/sh
|
||||
# SUBDIRECTORY_OK=1 . git-sh-setup
|
||||
# $GIT_DIR/hooks/setgitperms.perl -r
|
||||
#
|
||||
# To restore permissions/ownership data, place this script in your .git/hooks
|
||||
# directory and enable a `post-merge` and `post-checkout` hook with the
|
||||
# following lines:
|
||||
# #!/bin/sh
|
||||
# SUBDIRECTORY_OK=1 . git-sh-setup
|
||||
# $GIT_DIR/hooks/setgitperms.perl -w
|
||||
#
|
||||
use strict;
|
||||
use Getopt::Long;
|
||||
use File::Find;
|
||||
use File::Basename;
|
||||
|
||||
my $usage =
|
||||
"Usage: setgitperms.perl [OPTION]... <--read|--write>
|
||||
This program uses a file `.gitmeta` to store/restore permissions and uid/gid
|
||||
info for all files/dirs tracked by git in the repository.
|
||||
|
||||
---------------------------------Read Mode-------------------------------------
|
||||
-r, --read Reads perms/etc from working dir into a .gitmeta file
|
||||
-s, --stdout Output to stdout instead of .gitmeta
|
||||
-d, --diff Show unified diff of perms file (XOR with --stdout)
|
||||
|
||||
---------------------------------Write Mode------------------------------------
|
||||
-w, --write Modify perms/etc in working dir to match the .gitmeta file
|
||||
-v, --verbose Be verbose
|
||||
|
||||
\n";
|
||||
|
||||
my ($stdout, $showdiff, $verbose, $read_mode, $write_mode);
|
||||
|
||||
if ((@ARGV < 0) || !GetOptions(
|
||||
"stdout", \$stdout,
|
||||
"diff", \$showdiff,
|
||||
"read", \$read_mode,
|
||||
"write", \$write_mode,
|
||||
"verbose", \$verbose,
|
||||
)) { die $usage; }
|
||||
die $usage unless ($read_mode xor $write_mode);
|
||||
|
||||
my $topdir = `git-rev-parse --show-cdup` or die "\n"; chomp $topdir;
|
||||
my $gitdir = $topdir . '.git';
|
||||
my $gitmeta = $topdir . '.gitmeta';
|
||||
|
||||
if ($write_mode) {
|
||||
# Update the working dir permissions/ownership based on data from .gitmeta
|
||||
open (IN, "<$gitmeta") or die "Could not open $gitmeta for reading: $!\n";
|
||||
while (defined ($_ = <IN>)) {
|
||||
chomp;
|
||||
if (/^(.*) mode=(\S+)\s+uid=(\d+)\s+gid=(\d+)/) {
|
||||
# Compare recorded perms to actual perms in the working dir
|
||||
my ($path, $mode, $uid, $gid) = ($1, $2, $3, $4);
|
||||
my $fullpath = $topdir . $path;
|
||||
my (undef,undef,$wmode,undef,$wuid,$wgid) = lstat($fullpath);
|
||||
$wmode = sprintf "%04o", $wmode & 07777;
|
||||
if ($mode ne $wmode) {
|
||||
$verbose && print "Updating permissions on $path: old=$wmode, new=$mode\n";
|
||||
chmod oct($mode), $fullpath;
|
||||
}
|
||||
if ($uid != $wuid || $gid != $wgid) {
|
||||
if ($verbose) {
|
||||
# Print out user/group names instead of uid/gid
|
||||
my $pwname = getpwuid($uid);
|
||||
my $grpname = getgrgid($gid);
|
||||
my $wpwname = getpwuid($wuid);
|
||||
my $wgrpname = getgrgid($wgid);
|
||||
$pwname = $uid if !defined $pwname;
|
||||
$grpname = $gid if !defined $grpname;
|
||||
$wpwname = $wuid if !defined $wpwname;
|
||||
$wgrpname = $wgid if !defined $wgrpname;
|
||||
|
||||
print "Updating uid/gid on $path: old=$wpwname/$wgrpname, new=$pwname/$grpname\n";
|
||||
}
|
||||
chown $uid, $gid, $fullpath;
|
||||
}
|
||||
}
|
||||
else {
|
||||
warn "Invalid input format in $gitmeta:\n\t$_\n";
|
||||
}
|
||||
}
|
||||
close IN;
|
||||
}
|
||||
elsif ($read_mode) {
|
||||
# Handle merge conflicts in the .gitperms file
|
||||
if (-e "$gitdir/MERGE_MSG") {
|
||||
if (`grep ====== $gitmeta`) {
|
||||
# Conflict not resolved -- abort the commit
|
||||
print "PERMISSIONS/OWNERSHIP CONFLICT\n";
|
||||
print " Resolve the conflict in the $gitmeta file and then run\n";
|
||||
print " `.git/hooks/setgitperms.perl --write` to reconcile.\n";
|
||||
exit 1;
|
||||
}
|
||||
elsif (`grep $gitmeta $gitdir/MERGE_MSG`) {
|
||||
# A conflict in .gitmeta has been manually resolved. Verify that
|
||||
# the working dir perms matches the current .gitmeta perms for
|
||||
# each file/dir that conflicted.
|
||||
# This is here because a `setgitperms.perl --write` was not
|
||||
# performed due to a merge conflict, so permissions/ownership
|
||||
# may not be consistent with the manually merged .gitmeta file.
|
||||
my @conflict_diff = `git show \$(cat $gitdir/MERGE_HEAD)`;
|
||||
my @conflict_files;
|
||||
my $metadiff = 0;
|
||||
|
||||
# Build a list of files that conflicted from the .gitmeta diff
|
||||
foreach my $line (@conflict_diff) {
|
||||
if ($line =~ m|^diff --git a/$gitmeta b/$gitmeta|) {
|
||||
$metadiff = 1;
|
||||
}
|
||||
elsif ($line =~ /^diff --git/) {
|
||||
$metadiff = 0;
|
||||
}
|
||||
elsif ($metadiff && $line =~ /^\+(.*) mode=/) {
|
||||
push @conflict_files, $1;
|
||||
}
|
||||
}
|
||||
|
||||
# Verify that each conflict file now has permissions consistent
|
||||
# with the .gitmeta file
|
||||
foreach my $file (@conflict_files) {
|
||||
my $absfile = $topdir . $file;
|
||||
my $gm_entry = `grep "^$file mode=" $gitmeta`;
|
||||
if ($gm_entry =~ /mode=(\d+) uid=(\d+) gid=(\d+)/) {
|
||||
my ($gm_mode, $gm_uid, $gm_gid) = ($1, $2, $3);
|
||||
my (undef,undef,$mode,undef,$uid,$gid) = lstat("$absfile");
|
||||
$mode = sprintf("%04o", $mode & 07777);
|
||||
if (($gm_mode ne $mode) || ($gm_uid != $uid)
|
||||
|| ($gm_gid != $gid)) {
|
||||
print "PERMISSIONS/OWNERSHIP CONFLICT\n";
|
||||
print " Mismatch found for file: $file\n";
|
||||
print " Run `.git/hooks/setgitperms.perl --write` to reconcile.\n";
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
print "Warning! Permissions/ownership no longer being tracked for file: $file\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# No merge conflicts -- write out perms/ownership data to .gitmeta file
|
||||
unless ($stdout) {
|
||||
open (OUT, ">$gitmeta.tmp") or die "Could not open $gitmeta.tmp for writing: $!\n";
|
||||
}
|
||||
|
||||
my @files = `git-ls-files`;
|
||||
my %dirs;
|
||||
|
||||
foreach my $path (@files) {
|
||||
chomp $path;
|
||||
# We have to manually add stats for parent directories
|
||||
my $parent = dirname($path);
|
||||
while (!exists $dirs{$parent}) {
|
||||
$dirs{$parent} = 1;
|
||||
next if $parent eq '.';
|
||||
printstats($parent);
|
||||
$parent = dirname($parent);
|
||||
}
|
||||
# Now the git-tracked file
|
||||
printstats($path);
|
||||
}
|
||||
|
||||
# diff the temporary metadata file to see if anything has changed
|
||||
# If no metadata has changed, don't overwrite the real file
|
||||
# This is just so `git commit -a` doesn't try to commit a bogus update
|
||||
unless ($stdout) {
|
||||
if (! -e $gitmeta) {
|
||||
rename "$gitmeta.tmp", $gitmeta;
|
||||
}
|
||||
else {
|
||||
my $diff = `diff -U 0 $gitmeta $gitmeta.tmp`;
|
||||
if ($diff ne '') {
|
||||
rename "$gitmeta.tmp", $gitmeta;
|
||||
}
|
||||
else {
|
||||
unlink "$gitmeta.tmp";
|
||||
}
|
||||
if ($showdiff) {
|
||||
print $diff;
|
||||
}
|
||||
}
|
||||
close OUT;
|
||||
}
|
||||
# Make sure the .gitmeta file is tracked
|
||||
system("git add $gitmeta");
|
||||
}
|
||||
|
||||
|
||||
sub printstats {
|
||||
my $path = $_[0];
|
||||
$path =~ s/@/\@/g;
|
||||
my (undef,undef,$mode,undef,$uid,$gid) = lstat($path);
|
||||
$path =~ s/%/\%/g;
|
||||
if ($stdout) {
|
||||
print $path;
|
||||
printf " mode=%04o uid=$uid gid=$gid\n", $mode & 07777;
|
||||
}
|
||||
else {
|
||||
print OUT $path;
|
||||
printf OUT " mode=%04o uid=$uid gid=$gid\n", $mode & 07777;
|
||||
}
|
||||
}
|
||||
426
convert.c
426
convert.c
@@ -80,24 +80,19 @@ static int is_binary(unsigned long size, struct text_stat *stats)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep, int action)
|
||||
static int crlf_to_git(const char *path, const char *src, size_t len,
|
||||
struct strbuf *buf, int action)
|
||||
{
|
||||
char *buffer, *dst;
|
||||
unsigned long size, nsize;
|
||||
struct text_stat stats;
|
||||
char *dst;
|
||||
|
||||
if ((action == CRLF_BINARY) || !auto_crlf)
|
||||
return NULL;
|
||||
|
||||
size = *sizep;
|
||||
if (!size)
|
||||
return NULL;
|
||||
|
||||
gather_stats(src, size, &stats);
|
||||
if ((action == CRLF_BINARY) || !auto_crlf || !len)
|
||||
return 0;
|
||||
|
||||
gather_stats(src, len, &stats);
|
||||
/* No CR? Nothing to convert, regardless. */
|
||||
if (!stats.cr)
|
||||
return NULL;
|
||||
return 0;
|
||||
|
||||
if (action == CRLF_GUESS) {
|
||||
/*
|
||||
@@ -106,24 +101,19 @@ static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep
|
||||
* stuff?
|
||||
*/
|
||||
if (stats.cr != stats.crlf)
|
||||
return NULL;
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* And add some heuristics for binary vs text, of course...
|
||||
*/
|
||||
if (is_binary(size, &stats))
|
||||
return NULL;
|
||||
if (is_binary(len, &stats))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, allocate a new buffer, fill it in, and return it
|
||||
* to let the caller know that we switched buffers.
|
||||
*/
|
||||
nsize = size - stats.crlf;
|
||||
buffer = xmalloc(nsize);
|
||||
*sizep = nsize;
|
||||
|
||||
dst = buffer;
|
||||
/* only grow if not in place */
|
||||
if (strbuf_avail(buf) + buf->len < len)
|
||||
strbuf_grow(buf, len - buf->len);
|
||||
dst = buf->buf;
|
||||
if (action == CRLF_GUESS) {
|
||||
/*
|
||||
* If we guessed, we already know we rejected a file with
|
||||
@@ -134,71 +124,72 @@ static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep
|
||||
unsigned char c = *src++;
|
||||
if (c != '\r')
|
||||
*dst++ = c;
|
||||
} while (--size);
|
||||
} while (--len);
|
||||
} else {
|
||||
do {
|
||||
unsigned char c = *src++;
|
||||
if (! (c == '\r' && (1 < size && *src == '\n')))
|
||||
if (! (c == '\r' && (1 < len && *src == '\n')))
|
||||
*dst++ = c;
|
||||
} while (--size);
|
||||
} while (--len);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
strbuf_setlen(buf, dst - buf->buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char *crlf_to_worktree(const char *path, const char *src, unsigned long *sizep, int action)
|
||||
static int crlf_to_worktree(const char *path, const char *src, size_t len,
|
||||
struct strbuf *buf, int action)
|
||||
{
|
||||
char *buffer, *dst;
|
||||
unsigned long size, nsize;
|
||||
char *to_free = NULL;
|
||||
struct text_stat stats;
|
||||
unsigned char last;
|
||||
|
||||
if ((action == CRLF_BINARY) || (action == CRLF_INPUT) ||
|
||||
auto_crlf <= 0)
|
||||
return NULL;
|
||||
return 0;
|
||||
|
||||
size = *sizep;
|
||||
if (!size)
|
||||
return NULL;
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
gather_stats(src, size, &stats);
|
||||
gather_stats(src, len, &stats);
|
||||
|
||||
/* No LF? Nothing to convert, regardless. */
|
||||
if (!stats.lf)
|
||||
return NULL;
|
||||
return 0;
|
||||
|
||||
/* Was it already in CRLF format? */
|
||||
if (stats.lf == stats.crlf)
|
||||
return NULL;
|
||||
return 0;
|
||||
|
||||
if (action == CRLF_GUESS) {
|
||||
/* If we have any bare CR characters, we're not going to touch it */
|
||||
if (stats.cr != stats.crlf)
|
||||
return NULL;
|
||||
return 0;
|
||||
|
||||
if (is_binary(size, &stats))
|
||||
return NULL;
|
||||
if (is_binary(len, &stats))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, allocate a new buffer, fill it in, and return it
|
||||
* to let the caller know that we switched buffers.
|
||||
*/
|
||||
nsize = size + stats.lf - stats.crlf;
|
||||
buffer = xmalloc(nsize);
|
||||
*sizep = nsize;
|
||||
last = 0;
|
||||
/* are we "faking" in place editing ? */
|
||||
if (src == buf->buf)
|
||||
to_free = strbuf_detach(buf, NULL);
|
||||
|
||||
dst = buffer;
|
||||
do {
|
||||
unsigned char c = *src++;
|
||||
if (c == '\n' && last != '\r')
|
||||
*dst++ = '\r';
|
||||
*dst++ = c;
|
||||
last = c;
|
||||
} while (--size);
|
||||
strbuf_grow(buf, len + stats.lf - stats.crlf);
|
||||
for (;;) {
|
||||
const char *nl = memchr(src, '\n', len);
|
||||
if (!nl)
|
||||
break;
|
||||
if (nl > src && nl[-1] == '\r') {
|
||||
strbuf_add(buf, src, nl + 1 - src);
|
||||
} else {
|
||||
strbuf_add(buf, src, nl - src);
|
||||
strbuf_addstr(buf, "\r\n");
|
||||
}
|
||||
len -= nl + 1 - src;
|
||||
src = nl + 1;
|
||||
}
|
||||
strbuf_add(buf, src, len);
|
||||
|
||||
return buffer;
|
||||
free(to_free);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int filter_buffer(const char *path, const char *src,
|
||||
@@ -246,8 +237,8 @@ static int filter_buffer(const char *path, const char *src,
|
||||
return (write_err || status);
|
||||
}
|
||||
|
||||
static char *apply_filter(const char *path, const char *src,
|
||||
unsigned long *sizep, const char *cmd)
|
||||
static int apply_filter(const char *path, const char *src, size_t len,
|
||||
struct strbuf *dst, const char *cmd)
|
||||
{
|
||||
/*
|
||||
* Create a pipeline to have the command filter the buffer's
|
||||
@@ -255,21 +246,19 @@ static char *apply_filter(const char *path, const char *src,
|
||||
*
|
||||
* (child --> cmd) --> us
|
||||
*/
|
||||
const int SLOP = 4096;
|
||||
int pipe_feed[2];
|
||||
int status;
|
||||
char *dst;
|
||||
unsigned long dstsize, dstalloc;
|
||||
int status, ret = 1;
|
||||
struct child_process child_process;
|
||||
struct strbuf nbuf;
|
||||
|
||||
if (!cmd)
|
||||
return NULL;
|
||||
return 0;
|
||||
|
||||
memset(&child_process, 0, sizeof(child_process));
|
||||
|
||||
if (pipe(pipe_feed) < 0) {
|
||||
error("cannot create pipe to run external filter %s", cmd);
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
fflush(NULL);
|
||||
@@ -278,54 +267,36 @@ static char *apply_filter(const char *path, const char *src,
|
||||
error("cannot fork to run external filter %s", cmd);
|
||||
close(pipe_feed[0]);
|
||||
close(pipe_feed[1]);
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
if (!child_process.pid) {
|
||||
dup2(pipe_feed[1], 1);
|
||||
close(pipe_feed[0]);
|
||||
close(pipe_feed[1]);
|
||||
exit(filter_buffer(path, src, *sizep, cmd));
|
||||
exit(filter_buffer(path, src, len, cmd));
|
||||
}
|
||||
close(pipe_feed[1]);
|
||||
|
||||
dstalloc = *sizep;
|
||||
dst = xmalloc(dstalloc);
|
||||
dstsize = 0;
|
||||
|
||||
while (1) {
|
||||
ssize_t numread = xread(pipe_feed[0], dst + dstsize,
|
||||
dstalloc - dstsize);
|
||||
|
||||
if (numread <= 0) {
|
||||
if (!numread)
|
||||
break;
|
||||
error("read from external filter %s failed", cmd);
|
||||
free(dst);
|
||||
dst = NULL;
|
||||
break;
|
||||
}
|
||||
dstsize += numread;
|
||||
if (dstalloc <= dstsize + SLOP) {
|
||||
dstalloc = dstsize + SLOP;
|
||||
dst = xrealloc(dst, dstalloc);
|
||||
}
|
||||
strbuf_init(&nbuf, 0);
|
||||
if (strbuf_read(&nbuf, pipe_feed[0], len) < 0) {
|
||||
error("read from external filter %s failed", cmd);
|
||||
ret = 0;
|
||||
}
|
||||
if (close(pipe_feed[0])) {
|
||||
error("read from external filter %s failed", cmd);
|
||||
free(dst);
|
||||
dst = NULL;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
status = finish_command(&child_process);
|
||||
if (status) {
|
||||
error("external filter %s failed %d", cmd, -status);
|
||||
free(dst);
|
||||
dst = NULL;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (dst)
|
||||
*sizep = dstsize;
|
||||
return dst;
|
||||
if (ret) {
|
||||
strbuf_swap(dst, &nbuf);
|
||||
}
|
||||
strbuf_release(&nbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct convert_driver {
|
||||
@@ -353,13 +324,8 @@ static int read_convert_config(const char *var, const char *value)
|
||||
if (!strncmp(drv->name, name, namelen) && !drv->name[namelen])
|
||||
break;
|
||||
if (!drv) {
|
||||
char *namebuf;
|
||||
drv = xcalloc(1, sizeof(struct convert_driver));
|
||||
namebuf = xmalloc(namelen + 1);
|
||||
memcpy(namebuf, name, namelen);
|
||||
namebuf[namelen] = 0;
|
||||
drv->name = namebuf;
|
||||
drv->next = NULL;
|
||||
drv->name = xmemdupz(name, namelen);
|
||||
*user_convert_tail = drv;
|
||||
user_convert_tail = &(drv->next);
|
||||
}
|
||||
@@ -449,137 +415,106 @@ static int count_ident(const char *cp, unsigned long size)
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static char *ident_to_git(const char *path, const char *src, unsigned long *sizep, int ident)
|
||||
static int ident_to_git(const char *path, const char *src, size_t len,
|
||||
struct strbuf *buf, int ident)
|
||||
{
|
||||
int cnt;
|
||||
unsigned long size;
|
||||
char *dst, *buf;
|
||||
char *dst, *dollar;
|
||||
|
||||
if (!ident)
|
||||
return NULL;
|
||||
size = *sizep;
|
||||
cnt = count_ident(src, size);
|
||||
if (!cnt)
|
||||
return NULL;
|
||||
buf = xmalloc(size);
|
||||
if (!ident || !count_ident(src, len))
|
||||
return 0;
|
||||
|
||||
for (dst = buf; size; size--) {
|
||||
char ch = *src++;
|
||||
*dst++ = ch;
|
||||
if ((ch == '$') && (3 <= size) &&
|
||||
!memcmp("Id:", src, 3)) {
|
||||
unsigned long rem = size - 3;
|
||||
const char *cp = src + 3;
|
||||
do {
|
||||
ch = *cp++;
|
||||
if (ch == '$')
|
||||
break;
|
||||
rem--;
|
||||
} while (rem);
|
||||
if (!rem)
|
||||
continue;
|
||||
/* only grow if not in place */
|
||||
if (strbuf_avail(buf) + buf->len < len)
|
||||
strbuf_grow(buf, len - buf->len);
|
||||
dst = buf->buf;
|
||||
for (;;) {
|
||||
dollar = memchr(src, '$', len);
|
||||
if (!dollar)
|
||||
break;
|
||||
memcpy(dst, src, dollar + 1 - src);
|
||||
dst += dollar + 1 - src;
|
||||
len -= dollar + 1 - src;
|
||||
src = dollar + 1;
|
||||
|
||||
if (len > 3 && !memcmp(src, "Id:", 3)) {
|
||||
dollar = memchr(src + 3, '$', len - 3);
|
||||
if (!dollar)
|
||||
break;
|
||||
memcpy(dst, "Id$", 3);
|
||||
dst += 3;
|
||||
size -= (cp - src);
|
||||
src = cp;
|
||||
len -= dollar + 1 - src;
|
||||
src = dollar + 1;
|
||||
}
|
||||
}
|
||||
|
||||
*sizep = dst - buf;
|
||||
return buf;
|
||||
memcpy(dst, src, len);
|
||||
strbuf_setlen(buf, dst + len - buf->buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char *ident_to_worktree(const char *path, const char *src, unsigned long *sizep, int ident)
|
||||
static int ident_to_worktree(const char *path, const char *src, size_t len,
|
||||
struct strbuf *buf, int ident)
|
||||
{
|
||||
int cnt;
|
||||
unsigned long size;
|
||||
char *dst, *buf;
|
||||
unsigned char sha1[20];
|
||||
char *to_free = NULL, *dollar;
|
||||
int cnt;
|
||||
|
||||
if (!ident)
|
||||
return NULL;
|
||||
return 0;
|
||||
|
||||
size = *sizep;
|
||||
cnt = count_ident(src, size);
|
||||
cnt = count_ident(src, len);
|
||||
if (!cnt)
|
||||
return NULL;
|
||||
return 0;
|
||||
|
||||
hash_sha1_file(src, size, "blob", sha1);
|
||||
buf = xmalloc(size + cnt * 43);
|
||||
/* are we "faking" in place editing ? */
|
||||
if (src == buf->buf)
|
||||
to_free = strbuf_detach(buf, NULL);
|
||||
hash_sha1_file(src, len, "blob", sha1);
|
||||
|
||||
for (dst = buf; size; size--) {
|
||||
const char *cp;
|
||||
/* Fetch next source character, move the pointer on */
|
||||
char ch = *src++;
|
||||
/* Copy the current character to the destination */
|
||||
*dst++ = ch;
|
||||
/* If the current character is "$" or there are less than three
|
||||
* remaining bytes or the two bytes following this one are not
|
||||
* "Id", then simply read the next character */
|
||||
if ((ch != '$') || (size < 3) || memcmp("Id", src, 2))
|
||||
strbuf_grow(buf, len + cnt * 43);
|
||||
for (;;) {
|
||||
/* step 1: run to the next '$' */
|
||||
dollar = memchr(src, '$', len);
|
||||
if (!dollar)
|
||||
break;
|
||||
strbuf_add(buf, src, dollar + 1 - src);
|
||||
len -= dollar + 1 - src;
|
||||
src = dollar + 1;
|
||||
|
||||
/* step 2: does it looks like a bit like Id:xxx$ or Id$ ? */
|
||||
if (len < 3 || memcmp("Id", src, 2))
|
||||
continue;
|
||||
/*
|
||||
* Here when
|
||||
* - There are more than 2 bytes remaining
|
||||
* - The current three bytes are "$Id"
|
||||
* with
|
||||
* - ch == "$"
|
||||
* - src[0] == "I"
|
||||
*/
|
||||
|
||||
/*
|
||||
* It's possible that an expanded Id has crept its way into the
|
||||
* repository, we cope with that by stripping the expansion out
|
||||
*/
|
||||
if (src[2] == ':') {
|
||||
/* Expanded keywords have "$Id:" at the front */
|
||||
|
||||
/* discard up to but not including the closing $ */
|
||||
unsigned long rem = size - 3;
|
||||
/* Point at first byte after the ":" */
|
||||
cp = src + 3;
|
||||
/* step 3: skip over Id$ or Id:xxxxx$ */
|
||||
if (src[2] == '$') {
|
||||
src += 3;
|
||||
len -= 3;
|
||||
} else if (src[2] == ':') {
|
||||
/*
|
||||
* Throw away characters until either
|
||||
* - we reach a "$"
|
||||
* - we run out of bytes (rem == 0)
|
||||
* It's possible that an expanded Id has crept its way into the
|
||||
* repository, we cope with that by stripping the expansion out
|
||||
*/
|
||||
do {
|
||||
ch = *cp;
|
||||
if (ch == '$')
|
||||
break;
|
||||
cp++;
|
||||
rem--;
|
||||
} while (rem);
|
||||
/* If the above finished because it ran out of characters, then
|
||||
* this is an incomplete keyword, so don't run the expansion */
|
||||
if (!rem)
|
||||
continue;
|
||||
} else if (src[2] == '$')
|
||||
cp = src + 2;
|
||||
else
|
||||
/* Anything other than "$Id:XXX$" or $Id$ and we skip the
|
||||
* expansion */
|
||||
dollar = memchr(src + 3, '$', len - 3);
|
||||
if (!dollar) {
|
||||
/* incomplete keyword, no more '$', so just quit the loop */
|
||||
break;
|
||||
}
|
||||
|
||||
len -= dollar + 1 - src;
|
||||
src = dollar + 1;
|
||||
} else {
|
||||
/* it wasn't a "Id$" or "Id:xxxx$" */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* cp is now pointing at the last $ of the keyword */
|
||||
|
||||
memcpy(dst, "Id: ", 4);
|
||||
dst += 4;
|
||||
memcpy(dst, sha1_to_hex(sha1), 40);
|
||||
dst += 40;
|
||||
*dst++ = ' ';
|
||||
|
||||
/* Adjust for the characters we've discarded */
|
||||
size -= (cp - src);
|
||||
src = cp;
|
||||
|
||||
/* Copy the final "$" */
|
||||
*dst++ = *src++;
|
||||
size--;
|
||||
/* step 4: substitute */
|
||||
strbuf_addstr(buf, "Id: ");
|
||||
strbuf_add(buf, sha1_to_hex(sha1), 40);
|
||||
strbuf_addstr(buf, " $");
|
||||
}
|
||||
strbuf_add(buf, src, len);
|
||||
|
||||
*sizep = dst - buf;
|
||||
return buf;
|
||||
free(to_free);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int git_path_check_crlf(const char *path, struct git_attr_check *check)
|
||||
@@ -618,13 +553,12 @@ static int git_path_check_ident(const char *path, struct git_attr_check *check)
|
||||
return !!ATTR_TRUE(value);
|
||||
}
|
||||
|
||||
char *convert_to_git(const char *path, const char *src, unsigned long *sizep)
|
||||
int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst)
|
||||
{
|
||||
struct git_attr_check check[3];
|
||||
int crlf = CRLF_GUESS;
|
||||
int ident = 0;
|
||||
int ident = 0, ret = 0;
|
||||
char *filter = NULL;
|
||||
char *buf, *buf2;
|
||||
|
||||
setup_convert_check(check);
|
||||
if (!git_checkattr(path, ARRAY_SIZE(check), check)) {
|
||||
@@ -636,30 +570,25 @@ char *convert_to_git(const char *path, const char *src, unsigned long *sizep)
|
||||
filter = drv->clean;
|
||||
}
|
||||
|
||||
buf = apply_filter(path, src, sizep, filter);
|
||||
|
||||
buf2 = crlf_to_git(path, buf ? buf : src, sizep, crlf);
|
||||
if (buf2) {
|
||||
free(buf);
|
||||
buf = buf2;
|
||||
ret |= apply_filter(path, src, len, dst, filter);
|
||||
if (ret) {
|
||||
src = dst->buf;
|
||||
len = dst->len;
|
||||
}
|
||||
|
||||
buf2 = ident_to_git(path, buf ? buf : src, sizep, ident);
|
||||
if (buf2) {
|
||||
free(buf);
|
||||
buf = buf2;
|
||||
ret |= crlf_to_git(path, src, len, dst, crlf);
|
||||
if (ret) {
|
||||
src = dst->buf;
|
||||
len = dst->len;
|
||||
}
|
||||
|
||||
return buf;
|
||||
return ret | ident_to_git(path, src, len, dst, ident);
|
||||
}
|
||||
|
||||
char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep)
|
||||
int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
|
||||
{
|
||||
struct git_attr_check check[3];
|
||||
int crlf = CRLF_GUESS;
|
||||
int ident = 0;
|
||||
int ident = 0, ret = 0;
|
||||
char *filter = NULL;
|
||||
char *buf, *buf2;
|
||||
|
||||
setup_convert_check(check);
|
||||
if (!git_checkattr(path, ARRAY_SIZE(check), check)) {
|
||||
@@ -671,34 +600,15 @@ char *convert_to_working_tree(const char *path, const char *src, unsigned long *
|
||||
filter = drv->smudge;
|
||||
}
|
||||
|
||||
buf = ident_to_worktree(path, src, sizep, ident);
|
||||
|
||||
buf2 = crlf_to_worktree(path, buf ? buf : src, sizep, crlf);
|
||||
if (buf2) {
|
||||
free(buf);
|
||||
buf = buf2;
|
||||
ret |= ident_to_worktree(path, src, len, dst, ident);
|
||||
if (ret) {
|
||||
src = dst->buf;
|
||||
len = dst->len;
|
||||
}
|
||||
|
||||
buf2 = apply_filter(path, buf ? buf : src, sizep, filter);
|
||||
if (buf2) {
|
||||
free(buf);
|
||||
buf = buf2;
|
||||
ret |= crlf_to_worktree(path, src, len, dst, crlf);
|
||||
if (ret) {
|
||||
src = dst->buf;
|
||||
len = dst->len;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void *convert_sha1_file(const char *path, const unsigned char *sha1,
|
||||
unsigned int mode, enum object_type *type,
|
||||
unsigned long *size)
|
||||
{
|
||||
void *buffer = read_sha1_file(sha1, type, size);
|
||||
if (S_ISREG(mode) && buffer) {
|
||||
void *converted = convert_to_working_tree(path, buffer, size);
|
||||
if (converted) {
|
||||
free(buffer);
|
||||
buffer = converted;
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
return ret | apply_filter(path, src, len, dst, filter);
|
||||
}
|
||||
|
||||
4
daemon.c
4
daemon.c
@@ -9,6 +9,10 @@
|
||||
#define HOST_NAME_MAX 256
|
||||
#endif
|
||||
|
||||
#ifndef NI_MAXSERV
|
||||
#define NI_MAXSERV 32
|
||||
#endif
|
||||
|
||||
static int log_syslog;
|
||||
static int verbose;
|
||||
static int reuseaddr;
|
||||
|
||||
20
date.c
20
date.c
@@ -584,6 +584,26 @@ int parse_date(const char *date, char *result, int maxlen)
|
||||
return date_string(then, offset, result, maxlen);
|
||||
}
|
||||
|
||||
enum date_mode parse_date_format(const char *format)
|
||||
{
|
||||
if (!strcmp(format, "relative"))
|
||||
return DATE_RELATIVE;
|
||||
else if (!strcmp(format, "iso8601") ||
|
||||
!strcmp(format, "iso"))
|
||||
return DATE_ISO8601;
|
||||
else if (!strcmp(format, "rfc2822") ||
|
||||
!strcmp(format, "rfc"))
|
||||
return DATE_RFC2822;
|
||||
else if (!strcmp(format, "short"))
|
||||
return DATE_SHORT;
|
||||
else if (!strcmp(format, "local"))
|
||||
return DATE_LOCAL;
|
||||
else if (!strcmp(format, "default"))
|
||||
return DATE_NORMAL;
|
||||
else
|
||||
die("unknown date format %s", format);
|
||||
}
|
||||
|
||||
void datestamp(char *buf, int bufsize)
|
||||
{
|
||||
time_t now;
|
||||
|
||||
109
diff-delta.c
109
diff-delta.c
@@ -115,7 +115,11 @@ static const unsigned int U[256] = {
|
||||
struct index_entry {
|
||||
const unsigned char *ptr;
|
||||
unsigned int val;
|
||||
struct index_entry *next;
|
||||
};
|
||||
|
||||
struct unpacked_index_entry {
|
||||
struct index_entry entry;
|
||||
struct unpacked_index_entry *next;
|
||||
};
|
||||
|
||||
struct delta_index {
|
||||
@@ -131,7 +135,8 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
|
||||
unsigned int i, hsize, hmask, entries, prev_val, *hash_count;
|
||||
const unsigned char *data, *buffer = buf;
|
||||
struct delta_index *index;
|
||||
struct index_entry *entry, **hash;
|
||||
struct unpacked_index_entry *entry, **hash;
|
||||
struct index_entry *packed_entry, **packed_hash;
|
||||
void *mem;
|
||||
unsigned long memsize;
|
||||
|
||||
@@ -148,28 +153,21 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
|
||||
hmask = hsize - 1;
|
||||
|
||||
/* allocate lookup index */
|
||||
memsize = sizeof(*index) +
|
||||
sizeof(*hash) * hsize +
|
||||
memsize = sizeof(*hash) * hsize +
|
||||
sizeof(*entry) * entries;
|
||||
mem = malloc(memsize);
|
||||
if (!mem)
|
||||
return NULL;
|
||||
index = mem;
|
||||
mem = index + 1;
|
||||
hash = mem;
|
||||
mem = hash + hsize;
|
||||
entry = mem;
|
||||
|
||||
index->memsize = memsize;
|
||||
index->src_buf = buf;
|
||||
index->src_size = bufsize;
|
||||
index->hash_mask = hmask;
|
||||
memset(hash, 0, hsize * sizeof(*hash));
|
||||
|
||||
/* allocate an array to count hash entries */
|
||||
hash_count = calloc(hsize, sizeof(*hash_count));
|
||||
if (!hash_count) {
|
||||
free(index);
|
||||
free(hash);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -183,12 +181,13 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
|
||||
val = ((val << 8) | data[i]) ^ T[val >> RABIN_SHIFT];
|
||||
if (val == prev_val) {
|
||||
/* keep the lowest of consecutive identical blocks */
|
||||
entry[-1].ptr = data + RABIN_WINDOW;
|
||||
entry[-1].entry.ptr = data + RABIN_WINDOW;
|
||||
--entries;
|
||||
} else {
|
||||
prev_val = val;
|
||||
i = val & hmask;
|
||||
entry->ptr = data + RABIN_WINDOW;
|
||||
entry->val = val;
|
||||
entry->entry.ptr = data + RABIN_WINDOW;
|
||||
entry->entry.val = val;
|
||||
entry->next = hash[i];
|
||||
hash[i] = entry++;
|
||||
hash_count[i]++;
|
||||
@@ -208,20 +207,84 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
|
||||
* the reference buffer.
|
||||
*/
|
||||
for (i = 0; i < hsize; i++) {
|
||||
if (hash_count[i] < HASH_LIMIT)
|
||||
int acc;
|
||||
|
||||
if (hash_count[i] <= HASH_LIMIT)
|
||||
continue;
|
||||
|
||||
entries -= hash_count[i] - HASH_LIMIT;
|
||||
/* We leave exactly HASH_LIMIT entries in the bucket */
|
||||
|
||||
entry = hash[i];
|
||||
acc = 0;
|
||||
do {
|
||||
struct index_entry *keep = entry;
|
||||
int skip = hash_count[i] / HASH_LIMIT;
|
||||
do {
|
||||
entry = entry->next;
|
||||
} while(--skip && entry);
|
||||
keep->next = entry;
|
||||
} while(entry);
|
||||
acc += hash_count[i] - HASH_LIMIT;
|
||||
if (acc > 0) {
|
||||
struct unpacked_index_entry *keep = entry;
|
||||
do {
|
||||
entry = entry->next;
|
||||
acc -= HASH_LIMIT;
|
||||
} while (acc > 0);
|
||||
keep->next = entry->next;
|
||||
}
|
||||
entry = entry->next;
|
||||
} while (entry);
|
||||
|
||||
/* Assume that this loop is gone through exactly
|
||||
* HASH_LIMIT times and is entered and left with
|
||||
* acc==0. So the first statement in the loop
|
||||
* contributes (hash_count[i]-HASH_LIMIT)*HASH_LIMIT
|
||||
* to the accumulator, and the inner loop consequently
|
||||
* is run (hash_count[i]-HASH_LIMIT) times, removing
|
||||
* one element from the list each time. Since acc
|
||||
* balances out to 0 at the final run, the inner loop
|
||||
* body can't be left with entry==NULL. So we indeed
|
||||
* encounter entry==NULL in the outer loop only.
|
||||
*/
|
||||
}
|
||||
free(hash_count);
|
||||
|
||||
/* Now create the packed index in array form rather than
|
||||
* linked lists */
|
||||
|
||||
memsize = sizeof(*index)
|
||||
+ sizeof(*packed_hash) * (hsize+1)
|
||||
+ sizeof(*packed_entry) * entries;
|
||||
|
||||
mem = malloc(memsize);
|
||||
|
||||
if (!mem) {
|
||||
free(hash);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
index = mem;
|
||||
index->memsize = memsize;
|
||||
index->src_buf = buf;
|
||||
index->src_size = bufsize;
|
||||
index->hash_mask = hmask;
|
||||
|
||||
mem = index + 1;
|
||||
packed_hash = mem;
|
||||
mem = packed_hash + (hsize+1);
|
||||
packed_entry = mem;
|
||||
|
||||
/* Coalesce all entries belonging to one linked list into
|
||||
* consecutive array entries */
|
||||
|
||||
for (i = 0; i < hsize; i++) {
|
||||
packed_hash[i] = packed_entry;
|
||||
for (entry = hash[i]; entry; entry = entry->next)
|
||||
*packed_entry++ = entry->entry;
|
||||
}
|
||||
|
||||
/* Sentinel value to indicate the length of the last hash
|
||||
* bucket */
|
||||
|
||||
packed_hash[hsize] = packed_entry;
|
||||
assert(packed_entry - (struct index_entry *)mem == entries);
|
||||
free(hash);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
@@ -302,7 +365,7 @@ create_delta(const struct delta_index *index,
|
||||
val ^= U[data[-RABIN_WINDOW]];
|
||||
val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
|
||||
i = val & index->hash_mask;
|
||||
for (entry = index->hash[i]; entry; entry = entry->next) {
|
||||
for (entry = index->hash[i]; entry < index->hash[i+1]; entry++) {
|
||||
const unsigned char *ref = entry->ptr;
|
||||
const unsigned char *src = data;
|
||||
unsigned int ref_size = ref_top - ref;
|
||||
|
||||
350
diff.c
350
diff.c
@@ -84,13 +84,8 @@ static int parse_lldiff_command(const char *var, const char *ep, const char *val
|
||||
if (!strncmp(drv->name, name, namelen) && !drv->name[namelen])
|
||||
break;
|
||||
if (!drv) {
|
||||
char *namebuf;
|
||||
drv = xcalloc(1, sizeof(struct ll_diff_driver));
|
||||
namebuf = xmalloc(namelen + 1);
|
||||
memcpy(namebuf, name, namelen);
|
||||
namebuf[namelen] = 0;
|
||||
drv->name = namebuf;
|
||||
drv->next = NULL;
|
||||
drv->name = xmemdupz(name, namelen);
|
||||
if (!user_diff_tail)
|
||||
user_diff_tail = &user_diff;
|
||||
*user_diff_tail = drv;
|
||||
@@ -127,12 +122,8 @@ static int parse_funcname_pattern(const char *var, const char *ep, const char *v
|
||||
if (!strncmp(pp->name, name, namelen) && !pp->name[namelen])
|
||||
break;
|
||||
if (!pp) {
|
||||
char *namebuf;
|
||||
pp = xcalloc(1, sizeof(*pp));
|
||||
namebuf = xmalloc(namelen + 1);
|
||||
memcpy(namebuf, name, namelen);
|
||||
namebuf[namelen] = 0;
|
||||
pp->name = namebuf;
|
||||
pp->name = xmemdupz(name, namelen);
|
||||
pp->next = funcname_pattern_list;
|
||||
funcname_pattern_list = pp;
|
||||
}
|
||||
@@ -191,44 +182,23 @@ int git_diff_ui_config(const char *var, const char *value)
|
||||
return git_default_config(var, value);
|
||||
}
|
||||
|
||||
static char *quote_one(const char *str)
|
||||
{
|
||||
int needlen;
|
||||
char *xp;
|
||||
|
||||
if (!str)
|
||||
return NULL;
|
||||
needlen = quote_c_style(str, NULL, NULL, 0);
|
||||
if (!needlen)
|
||||
return xstrdup(str);
|
||||
xp = xmalloc(needlen + 1);
|
||||
quote_c_style(str, xp, NULL, 0);
|
||||
return xp;
|
||||
}
|
||||
|
||||
static char *quote_two(const char *one, const char *two)
|
||||
{
|
||||
int need_one = quote_c_style(one, NULL, NULL, 1);
|
||||
int need_two = quote_c_style(two, NULL, NULL, 1);
|
||||
char *xp;
|
||||
struct strbuf res;
|
||||
|
||||
strbuf_init(&res, 0);
|
||||
if (need_one + need_two) {
|
||||
if (!need_one) need_one = strlen(one);
|
||||
if (!need_two) need_one = strlen(two);
|
||||
|
||||
xp = xmalloc(need_one + need_two + 3);
|
||||
xp[0] = '"';
|
||||
quote_c_style(one, xp + 1, NULL, 1);
|
||||
quote_c_style(two, xp + need_one + 1, NULL, 1);
|
||||
strcpy(xp + need_one + need_two + 1, "\"");
|
||||
return xp;
|
||||
strbuf_addch(&res, '"');
|
||||
quote_c_style(one, &res, NULL, 1);
|
||||
quote_c_style(two, &res, NULL, 1);
|
||||
strbuf_addch(&res, '"');
|
||||
} else {
|
||||
strbuf_addstr(&res, one);
|
||||
strbuf_addstr(&res, two);
|
||||
}
|
||||
need_one = strlen(one);
|
||||
need_two = strlen(two);
|
||||
xp = xmalloc(need_one + need_two + 1);
|
||||
strcpy(xp, one);
|
||||
strcpy(xp + need_one, two);
|
||||
return xp;
|
||||
return strbuf_detach(&res, NULL);
|
||||
}
|
||||
|
||||
static const char *external_diff(void)
|
||||
@@ -680,27 +650,20 @@ static char *pprint_rename(const char *a, const char *b)
|
||||
{
|
||||
const char *old = a;
|
||||
const char *new = b;
|
||||
char *name = NULL;
|
||||
struct strbuf name;
|
||||
int pfx_length, sfx_length;
|
||||
int len_a = strlen(a);
|
||||
int len_b = strlen(b);
|
||||
int a_midlen, b_midlen;
|
||||
int qlen_a = quote_c_style(a, NULL, NULL, 0);
|
||||
int qlen_b = quote_c_style(b, NULL, NULL, 0);
|
||||
|
||||
strbuf_init(&name, 0);
|
||||
if (qlen_a || qlen_b) {
|
||||
if (qlen_a) len_a = qlen_a;
|
||||
if (qlen_b) len_b = qlen_b;
|
||||
name = xmalloc( len_a + len_b + 5 );
|
||||
if (qlen_a)
|
||||
quote_c_style(a, name, NULL, 0);
|
||||
else
|
||||
memcpy(name, a, len_a);
|
||||
memcpy(name + len_a, " => ", 4);
|
||||
if (qlen_b)
|
||||
quote_c_style(b, name + len_a + 4, NULL, 0);
|
||||
else
|
||||
memcpy(name + len_a + 4, b, len_b + 1);
|
||||
return name;
|
||||
quote_c_style(a, &name, NULL, 0);
|
||||
strbuf_addstr(&name, " => ");
|
||||
quote_c_style(b, &name, NULL, 0);
|
||||
return strbuf_detach(&name, NULL);
|
||||
}
|
||||
|
||||
/* Find common prefix */
|
||||
@@ -729,24 +692,26 @@ static char *pprint_rename(const char *a, const char *b)
|
||||
* pfx{sfx-a => sfx-b}
|
||||
* name-a => name-b
|
||||
*/
|
||||
if (pfx_length + sfx_length) {
|
||||
int a_midlen = len_a - pfx_length - sfx_length;
|
||||
int b_midlen = len_b - pfx_length - sfx_length;
|
||||
if (a_midlen < 0) a_midlen = 0;
|
||||
if (b_midlen < 0) b_midlen = 0;
|
||||
a_midlen = len_a - pfx_length - sfx_length;
|
||||
b_midlen = len_b - pfx_length - sfx_length;
|
||||
if (a_midlen < 0)
|
||||
a_midlen = 0;
|
||||
if (b_midlen < 0)
|
||||
b_midlen = 0;
|
||||
|
||||
name = xmalloc(pfx_length + a_midlen + b_midlen + sfx_length + 7);
|
||||
sprintf(name, "%.*s{%.*s => %.*s}%s",
|
||||
pfx_length, a,
|
||||
a_midlen, a + pfx_length,
|
||||
b_midlen, b + pfx_length,
|
||||
a + len_a - sfx_length);
|
||||
strbuf_grow(&name, pfx_length + a_midlen + b_midlen + sfx_length + 7);
|
||||
if (pfx_length + sfx_length) {
|
||||
strbuf_add(&name, a, pfx_length);
|
||||
strbuf_addch(&name, '{');
|
||||
}
|
||||
else {
|
||||
name = xmalloc(len_a + len_b + 5);
|
||||
sprintf(name, "%s => %s", a, b);
|
||||
strbuf_add(&name, a + pfx_length, a_midlen);
|
||||
strbuf_addstr(&name, " => ");
|
||||
strbuf_add(&name, b + pfx_length, b_midlen);
|
||||
if (pfx_length + sfx_length) {
|
||||
strbuf_addch(&name, '}');
|
||||
strbuf_add(&name, a + len_a - sfx_length, sfx_length);
|
||||
}
|
||||
return name;
|
||||
return strbuf_detach(&name, NULL);
|
||||
}
|
||||
|
||||
struct diffstat_t {
|
||||
@@ -859,12 +824,13 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
|
||||
int change = file->added + file->deleted;
|
||||
|
||||
if (!file->is_renamed) { /* renames are already quoted by pprint_rename */
|
||||
len = quote_c_style(file->name, NULL, NULL, 0);
|
||||
if (len) {
|
||||
char *qname = xmalloc(len + 1);
|
||||
quote_c_style(file->name, qname, NULL, 0);
|
||||
struct strbuf buf;
|
||||
strbuf_init(&buf, 0);
|
||||
if (quote_c_style(file->name, &buf, NULL, 0)) {
|
||||
free(file->name);
|
||||
file->name = qname;
|
||||
file->name = strbuf_detach(&buf, NULL);
|
||||
} else {
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1002,12 +968,12 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options)
|
||||
printf("-\t-\t");
|
||||
else
|
||||
printf("%d\t%d\t", file->added, file->deleted);
|
||||
if (options->line_termination && !file->is_renamed &&
|
||||
quote_c_style(file->name, NULL, NULL, 0))
|
||||
quote_c_style(file->name, NULL, stdout, 0);
|
||||
else
|
||||
if (!file->is_renamed) {
|
||||
write_name_quoted(file->name, stdout, options->line_termination);
|
||||
} else {
|
||||
fputs(file->name, stdout);
|
||||
putchar(options->line_termination);
|
||||
putchar(options->line_termination);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1546,26 +1512,15 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int
|
||||
|
||||
static int populate_from_stdin(struct diff_filespec *s)
|
||||
{
|
||||
#define INCREMENT 1024
|
||||
char *buf;
|
||||
unsigned long size;
|
||||
ssize_t got;
|
||||
struct strbuf buf;
|
||||
|
||||
size = 0;
|
||||
buf = NULL;
|
||||
while (1) {
|
||||
buf = xrealloc(buf, size + INCREMENT);
|
||||
got = xread(0, buf + size, INCREMENT);
|
||||
if (!got)
|
||||
break; /* EOF */
|
||||
if (got < 0)
|
||||
return error("error while reading from stdin %s",
|
||||
strbuf_init(&buf, 0);
|
||||
if (strbuf_read(&buf, 0, 0) < 0)
|
||||
return error("error while reading from stdin %s",
|
||||
strerror(errno));
|
||||
size += got;
|
||||
}
|
||||
|
||||
s->should_munmap = 0;
|
||||
s->data = buf;
|
||||
s->size = size;
|
||||
s->data = strbuf_detach(&buf, &s->size);
|
||||
s->should_free = 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -1610,10 +1565,9 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
|
||||
|
||||
if (!s->sha1_valid ||
|
||||
reuse_worktree_file(s->path, s->sha1, 0)) {
|
||||
struct strbuf buf;
|
||||
struct stat st;
|
||||
int fd;
|
||||
char *buf;
|
||||
unsigned long size;
|
||||
|
||||
if (!strcmp(s->path, "-"))
|
||||
return populate_from_stdin(s);
|
||||
@@ -1654,13 +1608,11 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
|
||||
/*
|
||||
* Convert from working tree format to canonical git format
|
||||
*/
|
||||
size = s->size;
|
||||
buf = convert_to_git(s->path, s->data, &size);
|
||||
if (buf) {
|
||||
strbuf_init(&buf, 0);
|
||||
if (convert_to_git(s->path, s->data, s->size, &buf)) {
|
||||
munmap(s->data, s->size);
|
||||
s->should_munmap = 0;
|
||||
s->data = buf;
|
||||
s->size = size;
|
||||
s->data = strbuf_detach(&buf, &s->size);
|
||||
s->should_free = 1;
|
||||
}
|
||||
}
|
||||
@@ -1962,50 +1914,46 @@ static int similarity_index(struct diff_filepair *p)
|
||||
static void run_diff(struct diff_filepair *p, struct diff_options *o)
|
||||
{
|
||||
const char *pgm = external_diff();
|
||||
char msg[PATH_MAX*2+300], *xfrm_msg;
|
||||
struct diff_filespec *one;
|
||||
struct diff_filespec *two;
|
||||
struct strbuf msg;
|
||||
char *xfrm_msg;
|
||||
struct diff_filespec *one = p->one;
|
||||
struct diff_filespec *two = p->two;
|
||||
const char *name;
|
||||
const char *other;
|
||||
char *name_munged, *other_munged;
|
||||
int complete_rewrite = 0;
|
||||
int len;
|
||||
|
||||
|
||||
if (DIFF_PAIR_UNMERGED(p)) {
|
||||
/* unmerged */
|
||||
run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, o, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
name = p->one->path;
|
||||
name = p->one->path;
|
||||
other = (strcmp(name, p->two->path) ? p->two->path : NULL);
|
||||
name_munged = quote_one(name);
|
||||
other_munged = quote_one(other);
|
||||
one = p->one; two = p->two;
|
||||
|
||||
diff_fill_sha1_info(one);
|
||||
diff_fill_sha1_info(two);
|
||||
|
||||
len = 0;
|
||||
strbuf_init(&msg, PATH_MAX * 2 + 300);
|
||||
switch (p->status) {
|
||||
case DIFF_STATUS_COPIED:
|
||||
len += snprintf(msg + len, sizeof(msg) - len,
|
||||
"similarity index %d%%\n"
|
||||
"copy from %s\n"
|
||||
"copy to %s\n",
|
||||
similarity_index(p), name_munged, other_munged);
|
||||
strbuf_addf(&msg, "similarity index %d%%", similarity_index(p));
|
||||
strbuf_addstr(&msg, "\ncopy from ");
|
||||
quote_c_style(name, &msg, NULL, 0);
|
||||
strbuf_addstr(&msg, "\ncopy to ");
|
||||
quote_c_style(other, &msg, NULL, 0);
|
||||
strbuf_addch(&msg, '\n');
|
||||
break;
|
||||
case DIFF_STATUS_RENAMED:
|
||||
len += snprintf(msg + len, sizeof(msg) - len,
|
||||
"similarity index %d%%\n"
|
||||
"rename from %s\n"
|
||||
"rename to %s\n",
|
||||
similarity_index(p), name_munged, other_munged);
|
||||
strbuf_addf(&msg, "similarity index %d%%", similarity_index(p));
|
||||
strbuf_addstr(&msg, "\nrename from ");
|
||||
quote_c_style(name, &msg, NULL, 0);
|
||||
strbuf_addstr(&msg, "\nrename to ");
|
||||
quote_c_style(other, &msg, NULL, 0);
|
||||
strbuf_addch(&msg, '\n');
|
||||
break;
|
||||
case DIFF_STATUS_MODIFIED:
|
||||
if (p->score) {
|
||||
len += snprintf(msg + len, sizeof(msg) - len,
|
||||
"dissimilarity index %d%%\n",
|
||||
strbuf_addf(&msg, "dissimilarity index %d%%\n",
|
||||
similarity_index(p));
|
||||
complete_rewrite = 1;
|
||||
break;
|
||||
@@ -2025,19 +1973,17 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
|
||||
(!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
|
||||
abbrev = 40;
|
||||
}
|
||||
len += snprintf(msg + len, sizeof(msg) - len,
|
||||
"index %.*s..%.*s",
|
||||
strbuf_addf(&msg, "index %.*s..%.*s",
|
||||
abbrev, sha1_to_hex(one->sha1),
|
||||
abbrev, sha1_to_hex(two->sha1));
|
||||
if (one->mode == two->mode)
|
||||
len += snprintf(msg + len, sizeof(msg) - len,
|
||||
" %06o", one->mode);
|
||||
len += snprintf(msg + len, sizeof(msg) - len, "\n");
|
||||
strbuf_addf(&msg, " %06o", one->mode);
|
||||
strbuf_addch(&msg, '\n');
|
||||
}
|
||||
|
||||
if (len)
|
||||
msg[--len] = 0;
|
||||
xfrm_msg = len ? msg : NULL;
|
||||
if (msg.len)
|
||||
strbuf_setlen(&msg, msg.len - 1);
|
||||
xfrm_msg = msg.len ? msg.buf : NULL;
|
||||
|
||||
if (!pgm &&
|
||||
DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
|
||||
@@ -2056,8 +2002,7 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
|
||||
run_diff_cmd(pgm, name, other, one, two, xfrm_msg, o,
|
||||
complete_rewrite);
|
||||
|
||||
free(name_munged);
|
||||
free(other_munged);
|
||||
strbuf_release(&msg);
|
||||
}
|
||||
|
||||
static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
|
||||
@@ -2513,72 +2458,30 @@ const char *diff_unique_abbrev(const unsigned char *sha1, int len)
|
||||
return sha1_to_hex(sha1);
|
||||
}
|
||||
|
||||
static void diff_flush_raw(struct diff_filepair *p,
|
||||
struct diff_options *options)
|
||||
static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt)
|
||||
{
|
||||
int two_paths;
|
||||
char status[10];
|
||||
int abbrev = options->abbrev;
|
||||
const char *path_one, *path_two;
|
||||
int inter_name_termination = '\t';
|
||||
int line_termination = options->line_termination;
|
||||
int line_termination = opt->line_termination;
|
||||
int inter_name_termination = line_termination ? '\t' : '\0';
|
||||
|
||||
if (!line_termination)
|
||||
inter_name_termination = 0;
|
||||
|
||||
path_one = p->one->path;
|
||||
path_two = p->two->path;
|
||||
if (line_termination) {
|
||||
path_one = quote_one(path_one);
|
||||
path_two = quote_one(path_two);
|
||||
if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) {
|
||||
printf(":%06o %06o %s ", p->one->mode, p->two->mode,
|
||||
diff_unique_abbrev(p->one->sha1, opt->abbrev));
|
||||
printf("%s ", diff_unique_abbrev(p->two->sha1, opt->abbrev));
|
||||
}
|
||||
if (p->score) {
|
||||
printf("%c%03d%c", p->status, similarity_index(p),
|
||||
inter_name_termination);
|
||||
} else {
|
||||
printf("%c%c", p->status, inter_name_termination);
|
||||
}
|
||||
|
||||
if (p->score)
|
||||
sprintf(status, "%c%03d", p->status, similarity_index(p));
|
||||
else {
|
||||
status[0] = p->status;
|
||||
status[1] = 0;
|
||||
if (p->status == DIFF_STATUS_COPIED || p->status == DIFF_STATUS_RENAMED) {
|
||||
write_name_quoted(p->one->path, stdout, inter_name_termination);
|
||||
write_name_quoted(p->two->path, stdout, line_termination);
|
||||
} else {
|
||||
const char *path = p->one->mode ? p->one->path : p->two->path;
|
||||
write_name_quoted(path, stdout, line_termination);
|
||||
}
|
||||
switch (p->status) {
|
||||
case DIFF_STATUS_COPIED:
|
||||
case DIFF_STATUS_RENAMED:
|
||||
two_paths = 1;
|
||||
break;
|
||||
case DIFF_STATUS_ADDED:
|
||||
case DIFF_STATUS_DELETED:
|
||||
two_paths = 0;
|
||||
break;
|
||||
default:
|
||||
two_paths = 0;
|
||||
break;
|
||||
}
|
||||
if (!(options->output_format & DIFF_FORMAT_NAME_STATUS)) {
|
||||
printf(":%06o %06o %s ",
|
||||
p->one->mode, p->two->mode,
|
||||
diff_unique_abbrev(p->one->sha1, abbrev));
|
||||
printf("%s ",
|
||||
diff_unique_abbrev(p->two->sha1, abbrev));
|
||||
}
|
||||
printf("%s%c%s", status, inter_name_termination,
|
||||
two_paths || p->one->mode ? path_one : path_two);
|
||||
if (two_paths)
|
||||
printf("%c%s", inter_name_termination, path_two);
|
||||
putchar(line_termination);
|
||||
if (path_one != p->one->path)
|
||||
free((void*)path_one);
|
||||
if (path_two != p->two->path)
|
||||
free((void*)path_two);
|
||||
}
|
||||
|
||||
static void diff_flush_name(struct diff_filepair *p, struct diff_options *opt)
|
||||
{
|
||||
char *path = p->two->path;
|
||||
|
||||
if (opt->line_termination)
|
||||
path = quote_one(p->two->path);
|
||||
printf("%s%c", path, opt->line_termination);
|
||||
if (p->two->path != path)
|
||||
free(path);
|
||||
}
|
||||
|
||||
int diff_unmodified_pair(struct diff_filepair *p)
|
||||
@@ -2588,14 +2491,11 @@ int diff_unmodified_pair(struct diff_filepair *p)
|
||||
* let transformers to produce diff_filepairs any way they want,
|
||||
* and filter and clean them up here before producing the output.
|
||||
*/
|
||||
struct diff_filespec *one, *two;
|
||||
struct diff_filespec *one = p->one, *two = p->two;
|
||||
|
||||
if (DIFF_PAIR_UNMERGED(p))
|
||||
return 0; /* unmerged is interesting */
|
||||
|
||||
one = p->one;
|
||||
two = p->two;
|
||||
|
||||
/* deletion, addition, mode or type change
|
||||
* and rename are all interesting.
|
||||
*/
|
||||
@@ -2784,32 +2684,27 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt)
|
||||
else if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS))
|
||||
diff_flush_raw(p, opt);
|
||||
else if (fmt & DIFF_FORMAT_NAME)
|
||||
diff_flush_name(p, opt);
|
||||
write_name_quoted(p->two->path, stdout, opt->line_termination);
|
||||
}
|
||||
|
||||
static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs)
|
||||
{
|
||||
char *name = quote_one(fs->path);
|
||||
if (fs->mode)
|
||||
printf(" %s mode %06o %s\n", newdelete, fs->mode, name);
|
||||
printf(" %s mode %06o ", newdelete, fs->mode);
|
||||
else
|
||||
printf(" %s %s\n", newdelete, name);
|
||||
free(name);
|
||||
printf(" %s ", newdelete);
|
||||
write_name_quoted(fs->path, stdout, '\n');
|
||||
}
|
||||
|
||||
|
||||
static void show_mode_change(struct diff_filepair *p, int show_name)
|
||||
{
|
||||
if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) {
|
||||
printf(" mode change %06o => %06o%c", p->one->mode, p->two->mode,
|
||||
show_name ? ' ' : '\n');
|
||||
if (show_name) {
|
||||
char *name = quote_one(p->two->path);
|
||||
printf(" mode change %06o => %06o %s\n",
|
||||
p->one->mode, p->two->mode, name);
|
||||
free(name);
|
||||
write_name_quoted(p->two->path, stdout, '\n');
|
||||
}
|
||||
else
|
||||
printf(" mode change %06o => %06o\n",
|
||||
p->one->mode, p->two->mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2839,12 +2734,11 @@ static void diff_summary(struct diff_filepair *p)
|
||||
break;
|
||||
default:
|
||||
if (p->score) {
|
||||
char *name = quote_one(p->two->path);
|
||||
printf(" rewrite %s (%d%%)\n", name,
|
||||
similarity_index(p));
|
||||
free(name);
|
||||
show_mode_change(p, 0);
|
||||
} else show_mode_change(p, 1);
|
||||
puts(" rewrite ");
|
||||
write_name_quoted(p->two->path, stdout, ' ');
|
||||
printf("(%d%%)\n", similarity_index(p));
|
||||
}
|
||||
show_mode_change(p, !p->score);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2858,14 +2752,14 @@ struct patch_id_t {
|
||||
static int remove_space(char *line, int len)
|
||||
{
|
||||
int i;
|
||||
char *dst = line;
|
||||
unsigned char c;
|
||||
char *dst = line;
|
||||
unsigned char c;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
if (!isspace((c = line[i])))
|
||||
*dst++ = c;
|
||||
for (i = 0; i < len; i++)
|
||||
if (!isspace((c = line[i])))
|
||||
*dst++ = c;
|
||||
|
||||
return dst - line;
|
||||
return dst - line;
|
||||
}
|
||||
|
||||
static void patch_id_consume(void *priv, char *line, unsigned long len)
|
||||
|
||||
@@ -46,22 +46,6 @@ struct spanhash_top {
|
||||
struct spanhash data[FLEX_ARRAY];
|
||||
};
|
||||
|
||||
static struct spanhash *spanhash_find(struct spanhash_top *top,
|
||||
unsigned int hashval)
|
||||
{
|
||||
int sz = 1 << top->alloc_log2;
|
||||
int bucket = hashval & (sz - 1);
|
||||
while (1) {
|
||||
struct spanhash *h = &(top->data[bucket++]);
|
||||
if (!h->cnt)
|
||||
return NULL;
|
||||
if (h->hashval == hashval)
|
||||
return h;
|
||||
if (sz <= bucket)
|
||||
bucket = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static struct spanhash_top *spanhash_rehash(struct spanhash_top *orig)
|
||||
{
|
||||
struct spanhash_top *new;
|
||||
@@ -122,6 +106,20 @@ static struct spanhash_top *add_spanhash(struct spanhash_top *top,
|
||||
}
|
||||
}
|
||||
|
||||
static int spanhash_cmp(const void *a_, const void *b_)
|
||||
{
|
||||
const struct spanhash *a = a_;
|
||||
const struct spanhash *b = b_;
|
||||
|
||||
/* A count of zero compares at the end.. */
|
||||
if (!a->cnt)
|
||||
return !b->cnt ? 0 : 1;
|
||||
if (!b->cnt)
|
||||
return -1;
|
||||
return a->hashval < b->hashval ? -1 :
|
||||
a->hashval > b->hashval ? 1 : 0;
|
||||
}
|
||||
|
||||
static struct spanhash_top *hash_chars(struct diff_filespec *one)
|
||||
{
|
||||
int i, n;
|
||||
@@ -158,6 +156,10 @@ static struct spanhash_top *hash_chars(struct diff_filespec *one)
|
||||
n = 0;
|
||||
accum1 = accum2 = 0;
|
||||
}
|
||||
qsort(hash->data,
|
||||
1ul << hash->alloc_log2,
|
||||
sizeof(hash->data[0]),
|
||||
spanhash_cmp);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -169,7 +171,7 @@ int diffcore_count_changes(struct diff_filespec *src,
|
||||
unsigned long *src_copied,
|
||||
unsigned long *literal_added)
|
||||
{
|
||||
int i, ssz;
|
||||
struct spanhash *s, *d;
|
||||
struct spanhash_top *src_count, *dst_count;
|
||||
unsigned long sc, la;
|
||||
|
||||
@@ -190,22 +192,26 @@ int diffcore_count_changes(struct diff_filespec *src,
|
||||
}
|
||||
sc = la = 0;
|
||||
|
||||
ssz = 1 << src_count->alloc_log2;
|
||||
for (i = 0; i < ssz; i++) {
|
||||
struct spanhash *s = &(src_count->data[i]);
|
||||
struct spanhash *d;
|
||||
s = src_count->data;
|
||||
d = dst_count->data;
|
||||
for (;;) {
|
||||
unsigned dst_cnt, src_cnt;
|
||||
if (!s->cnt)
|
||||
continue;
|
||||
break; /* we checked all in src */
|
||||
while (d->cnt) {
|
||||
if (d->hashval >= s->hashval)
|
||||
break;
|
||||
d++;
|
||||
}
|
||||
src_cnt = s->cnt;
|
||||
d = spanhash_find(dst_count, s->hashval);
|
||||
dst_cnt = d ? d->cnt : 0;
|
||||
dst_cnt = d->hashval == s->hashval ? d->cnt : 0;
|
||||
if (src_cnt < dst_cnt) {
|
||||
la += dst_cnt - src_cnt;
|
||||
sc += src_cnt;
|
||||
}
|
||||
else
|
||||
sc += dst_cnt;
|
||||
s++;
|
||||
}
|
||||
|
||||
if (!src_count_p)
|
||||
|
||||
@@ -48,11 +48,8 @@ static void prepare_order(const char *orderfile)
|
||||
if (*ep == '\n') {
|
||||
*ep = 0;
|
||||
order[cnt] = cp;
|
||||
}
|
||||
else {
|
||||
order[cnt] = xmalloc(ep-cp+1);
|
||||
memcpy(order[cnt], cp, ep-cp);
|
||||
order[cnt][ep-cp] = 0;
|
||||
} else {
|
||||
order[cnt] = xmemdupz(cp, ep - cp);
|
||||
}
|
||||
cnt++;
|
||||
}
|
||||
|
||||
9
entry.c
9
entry.c
@@ -104,7 +104,8 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
|
||||
long wrote;
|
||||
|
||||
switch (ntohl(ce->ce_mode) & S_IFMT) {
|
||||
char *buf, *new;
|
||||
char *new;
|
||||
struct strbuf buf;
|
||||
unsigned long size;
|
||||
|
||||
case S_IFREG:
|
||||
@@ -116,10 +117,10 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
|
||||
/*
|
||||
* Convert from git internal format to working tree format
|
||||
*/
|
||||
buf = convert_to_working_tree(ce->name, new, &size);
|
||||
if (buf) {
|
||||
strbuf_init(&buf, 0);
|
||||
if (convert_to_working_tree(ce->name, new, size, &buf)) {
|
||||
free(new);
|
||||
new = buf;
|
||||
new = strbuf_detach(&buf, &size);
|
||||
}
|
||||
|
||||
if (to_tempfile) {
|
||||
|
||||
312
fast-import.c
312
fast-import.c
@@ -149,7 +149,6 @@ Format of STDIN stream:
|
||||
#include "pack.h"
|
||||
#include "refs.h"
|
||||
#include "csum-file.h"
|
||||
#include "strbuf.h"
|
||||
#include "quote.h"
|
||||
|
||||
#define PACK_ID_BITS 16
|
||||
@@ -183,11 +182,10 @@ struct mark_set
|
||||
|
||||
struct last_object
|
||||
{
|
||||
void *data;
|
||||
unsigned long len;
|
||||
struct strbuf data;
|
||||
uint32_t offset;
|
||||
unsigned int depth;
|
||||
unsigned no_free:1;
|
||||
unsigned no_swap : 1;
|
||||
};
|
||||
|
||||
struct mem_pool
|
||||
@@ -251,12 +249,6 @@ struct tag
|
||||
unsigned char sha1[20];
|
||||
};
|
||||
|
||||
struct dbuf
|
||||
{
|
||||
void *buffer;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
struct hash_list
|
||||
{
|
||||
struct hash_list *next;
|
||||
@@ -317,15 +309,15 @@ static struct mark_set *marks;
|
||||
static const char* mark_file;
|
||||
|
||||
/* Our last blob */
|
||||
static struct last_object last_blob;
|
||||
static struct last_object last_blob = { STRBUF_INIT, 0, 0, 0 };
|
||||
|
||||
/* Tree management */
|
||||
static unsigned int tree_entry_alloc = 1000;
|
||||
static void *avail_tree_entry;
|
||||
static unsigned int avail_tree_table_sz = 100;
|
||||
static struct avail_tree_content **avail_tree_table;
|
||||
static struct dbuf old_tree;
|
||||
static struct dbuf new_tree;
|
||||
static struct strbuf old_tree = STRBUF_INIT;
|
||||
static struct strbuf new_tree = STRBUF_INIT;
|
||||
|
||||
/* Branch data */
|
||||
static unsigned long max_active_branches = 5;
|
||||
@@ -340,14 +332,14 @@ static struct tag *last_tag;
|
||||
|
||||
/* Input stream parsing */
|
||||
static whenspec_type whenspec = WHENSPEC_RAW;
|
||||
static struct strbuf command_buf;
|
||||
static struct strbuf command_buf = STRBUF_INIT;
|
||||
static int unread_command_buf;
|
||||
static struct recent_command cmd_hist = {&cmd_hist, &cmd_hist, NULL};
|
||||
static struct recent_command *cmd_tail = &cmd_hist;
|
||||
static struct recent_command *rc_free;
|
||||
static unsigned int cmd_save = 100;
|
||||
static uintmax_t next_mark;
|
||||
static struct dbuf new_data;
|
||||
static struct strbuf new_data = STRBUF_INIT;
|
||||
|
||||
static void write_branch_report(FILE *rpt, struct branch *b)
|
||||
{
|
||||
@@ -567,17 +559,6 @@ static char *pool_strdup(const char *s)
|
||||
return r;
|
||||
}
|
||||
|
||||
static void size_dbuf(struct dbuf *b, size_t maxlen)
|
||||
{
|
||||
if (b->buffer) {
|
||||
if (b->capacity >= maxlen)
|
||||
return;
|
||||
free(b->buffer);
|
||||
}
|
||||
b->capacity = ((maxlen / 1024) + 1) * 1024;
|
||||
b->buffer = xmalloc(b->capacity);
|
||||
}
|
||||
|
||||
static void insert_mark(uintmax_t idnum, struct object_entry *oe)
|
||||
{
|
||||
struct mark_set *s = marks;
|
||||
@@ -968,9 +949,7 @@ static void end_packfile(void)
|
||||
free(old_p);
|
||||
|
||||
/* We can't carry a delta across packfiles. */
|
||||
free(last_blob.data);
|
||||
last_blob.data = NULL;
|
||||
last_blob.len = 0;
|
||||
strbuf_release(&last_blob.data);
|
||||
last_blob.offset = 0;
|
||||
last_blob.depth = 0;
|
||||
}
|
||||
@@ -1006,8 +985,7 @@ static size_t encode_header(
|
||||
|
||||
static int store_object(
|
||||
enum object_type type,
|
||||
void *dat,
|
||||
size_t datlen,
|
||||
struct strbuf *dat,
|
||||
struct last_object *last,
|
||||
unsigned char *sha1out,
|
||||
uintmax_t mark)
|
||||
@@ -1021,10 +999,10 @@ static int store_object(
|
||||
z_stream s;
|
||||
|
||||
hdrlen = sprintf((char*)hdr,"%s %lu", typename(type),
|
||||
(unsigned long)datlen) + 1;
|
||||
(unsigned long)dat->len) + 1;
|
||||
SHA1_Init(&c);
|
||||
SHA1_Update(&c, hdr, hdrlen);
|
||||
SHA1_Update(&c, dat, datlen);
|
||||
SHA1_Update(&c, dat->buf, dat->len);
|
||||
SHA1_Final(sha1, &c);
|
||||
if (sha1out)
|
||||
hashcpy(sha1out, sha1);
|
||||
@@ -1043,11 +1021,11 @@ static int store_object(
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (last && last->data && last->depth < max_depth) {
|
||||
delta = diff_delta(last->data, last->len,
|
||||
dat, datlen,
|
||||
if (last && last->data.buf && last->depth < max_depth) {
|
||||
delta = diff_delta(last->data.buf, last->data.len,
|
||||
dat->buf, dat->len,
|
||||
&deltalen, 0);
|
||||
if (delta && deltalen >= datlen) {
|
||||
if (delta && deltalen >= dat->len) {
|
||||
free(delta);
|
||||
delta = NULL;
|
||||
}
|
||||
@@ -1060,8 +1038,8 @@ static int store_object(
|
||||
s.next_in = delta;
|
||||
s.avail_in = deltalen;
|
||||
} else {
|
||||
s.next_in = dat;
|
||||
s.avail_in = datlen;
|
||||
s.next_in = (void *)dat->buf;
|
||||
s.avail_in = dat->len;
|
||||
}
|
||||
s.avail_out = deflateBound(&s, s.avail_in);
|
||||
s.next_out = out = xmalloc(s.avail_out);
|
||||
@@ -1084,8 +1062,8 @@ static int store_object(
|
||||
|
||||
memset(&s, 0, sizeof(s));
|
||||
deflateInit(&s, zlib_compression_level);
|
||||
s.next_in = dat;
|
||||
s.avail_in = datlen;
|
||||
s.next_in = (void *)dat->buf;
|
||||
s.avail_in = dat->len;
|
||||
s.avail_out = deflateBound(&s, s.avail_in);
|
||||
s.next_out = out = xrealloc(out, s.avail_out);
|
||||
while (deflate(&s, Z_FINISH) == Z_OK)
|
||||
@@ -1119,7 +1097,7 @@ static int store_object(
|
||||
} else {
|
||||
if (last)
|
||||
last->depth = 0;
|
||||
hdrlen = encode_header(type, datlen, hdr);
|
||||
hdrlen = encode_header(type, dat->len, hdr);
|
||||
write_or_die(pack_data->pack_fd, hdr, hdrlen);
|
||||
pack_size += hdrlen;
|
||||
}
|
||||
@@ -1130,11 +1108,12 @@ static int store_object(
|
||||
free(out);
|
||||
free(delta);
|
||||
if (last) {
|
||||
if (!last->no_free)
|
||||
free(last->data);
|
||||
last->data = dat;
|
||||
if (last->no_swap) {
|
||||
last->data = *dat;
|
||||
} else {
|
||||
strbuf_swap(&last->data, dat);
|
||||
}
|
||||
last->offset = e->offset;
|
||||
last->len = datlen;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1230,14 +1209,10 @@ static int tecmp1 (const void *_a, const void *_b)
|
||||
b->name->str_dat, b->name->str_len, b->versions[1].mode);
|
||||
}
|
||||
|
||||
static void mktree(struct tree_content *t,
|
||||
int v,
|
||||
unsigned long *szp,
|
||||
struct dbuf *b)
|
||||
static void mktree(struct tree_content *t, int v, struct strbuf *b)
|
||||
{
|
||||
size_t maxlen = 0;
|
||||
unsigned int i;
|
||||
char *c;
|
||||
|
||||
if (!v)
|
||||
qsort(t->entries,t->entry_count,sizeof(t->entries[0]),tecmp0);
|
||||
@@ -1249,28 +1224,23 @@ static void mktree(struct tree_content *t,
|
||||
maxlen += t->entries[i]->name->str_len + 34;
|
||||
}
|
||||
|
||||
size_dbuf(b, maxlen);
|
||||
c = b->buffer;
|
||||
strbuf_reset(b);
|
||||
strbuf_grow(b, maxlen);
|
||||
for (i = 0; i < t->entry_count; i++) {
|
||||
struct tree_entry *e = t->entries[i];
|
||||
if (!e->versions[v].mode)
|
||||
continue;
|
||||
c += sprintf(c, "%o", (unsigned int)e->versions[v].mode);
|
||||
*c++ = ' ';
|
||||
strcpy(c, e->name->str_dat);
|
||||
c += e->name->str_len + 1;
|
||||
hashcpy((unsigned char*)c, e->versions[v].sha1);
|
||||
c += 20;
|
||||
strbuf_addf(b, "%o %s%c", (unsigned int)e->versions[v].mode,
|
||||
e->name->str_dat, '\0');
|
||||
strbuf_add(b, e->versions[v].sha1, 20);
|
||||
}
|
||||
*szp = c - (char*)b->buffer;
|
||||
}
|
||||
|
||||
static void store_tree(struct tree_entry *root)
|
||||
{
|
||||
struct tree_content *t = root->tree;
|
||||
unsigned int i, j, del;
|
||||
unsigned long new_len;
|
||||
struct last_object lo;
|
||||
struct last_object lo = { STRBUF_INIT, 0, 0, /* no_swap */ 1 };
|
||||
struct object_entry *le;
|
||||
|
||||
if (!is_null_sha1(root->versions[1].sha1))
|
||||
@@ -1282,23 +1252,15 @@ static void store_tree(struct tree_entry *root)
|
||||
}
|
||||
|
||||
le = find_object(root->versions[0].sha1);
|
||||
if (!S_ISDIR(root->versions[0].mode)
|
||||
|| !le
|
||||
|| le->pack_id != pack_id) {
|
||||
lo.data = NULL;
|
||||
lo.depth = 0;
|
||||
lo.no_free = 0;
|
||||
} else {
|
||||
mktree(t, 0, &lo.len, &old_tree);
|
||||
lo.data = old_tree.buffer;
|
||||
if (S_ISDIR(root->versions[0].mode) && le && le->pack_id == pack_id) {
|
||||
mktree(t, 0, &old_tree);
|
||||
lo.data = old_tree;
|
||||
lo.offset = le->offset;
|
||||
lo.depth = t->delta_depth;
|
||||
lo.no_free = 1;
|
||||
}
|
||||
|
||||
mktree(t, 1, &new_len, &new_tree);
|
||||
store_object(OBJ_TREE, new_tree.buffer, new_len,
|
||||
&lo, root->versions[1].sha1, 0);
|
||||
mktree(t, 1, &new_tree);
|
||||
store_object(OBJ_TREE, &new_tree, &lo, root->versions[1].sha1, 0);
|
||||
|
||||
t->delta_depth = lo.depth;
|
||||
for (i = 0, j = 0, del = 0; i < t->entry_count; i++) {
|
||||
@@ -1585,20 +1547,25 @@ static void dump_marks(void)
|
||||
mark_file, strerror(errno));
|
||||
}
|
||||
|
||||
static void read_next_command(void)
|
||||
static int read_next_command(void)
|
||||
{
|
||||
static int stdin_eof = 0;
|
||||
|
||||
if (stdin_eof) {
|
||||
unread_command_buf = 0;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
do {
|
||||
if (unread_command_buf) {
|
||||
unread_command_buf = 0;
|
||||
if (command_buf.eof)
|
||||
return;
|
||||
} else {
|
||||
struct recent_command *rc;
|
||||
|
||||
command_buf.buf = NULL;
|
||||
read_line(&command_buf, stdin, '\n');
|
||||
if (command_buf.eof)
|
||||
return;
|
||||
strbuf_detach(&command_buf, NULL);
|
||||
stdin_eof = strbuf_getline(&command_buf, stdin, '\n');
|
||||
if (stdin_eof)
|
||||
return EOF;
|
||||
|
||||
rc = rc_free;
|
||||
if (rc)
|
||||
@@ -1617,6 +1584,8 @@ static void read_next_command(void)
|
||||
cmd_tail = rc;
|
||||
}
|
||||
} while (command_buf.buf[0] == '#');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void skip_optional_lf(void)
|
||||
@@ -1636,42 +1605,35 @@ static void cmd_mark(void)
|
||||
next_mark = 0;
|
||||
}
|
||||
|
||||
static void *cmd_data (size_t *size)
|
||||
static void cmd_data(struct strbuf *sb)
|
||||
{
|
||||
size_t length;
|
||||
char *buffer;
|
||||
strbuf_reset(sb);
|
||||
|
||||
if (prefixcmp(command_buf.buf, "data "))
|
||||
die("Expected 'data n' command, found: %s", command_buf.buf);
|
||||
|
||||
if (!prefixcmp(command_buf.buf + 5, "<<")) {
|
||||
char *term = xstrdup(command_buf.buf + 5 + 2);
|
||||
size_t sz = 8192, term_len = command_buf.len - 5 - 2;
|
||||
length = 0;
|
||||
buffer = xmalloc(sz);
|
||||
command_buf.buf = NULL;
|
||||
size_t term_len = command_buf.len - 5 - 2;
|
||||
|
||||
for (;;) {
|
||||
read_line(&command_buf, stdin, '\n');
|
||||
if (command_buf.eof)
|
||||
if (strbuf_getline(&command_buf, stdin, '\n') == EOF)
|
||||
die("EOF in data (terminator '%s' not found)", term);
|
||||
if (term_len == command_buf.len
|
||||
&& !strcmp(term, command_buf.buf))
|
||||
break;
|
||||
ALLOC_GROW(buffer, length + command_buf.len, sz);
|
||||
memcpy(buffer + length,
|
||||
command_buf.buf,
|
||||
command_buf.len - 1);
|
||||
length += command_buf.len - 1;
|
||||
buffer[length++] = '\n';
|
||||
strbuf_addbuf(sb, &command_buf);
|
||||
strbuf_addch(sb, '\n');
|
||||
}
|
||||
free(term);
|
||||
}
|
||||
else {
|
||||
size_t n = 0;
|
||||
size_t n = 0, length;
|
||||
|
||||
length = strtoul(command_buf.buf + 5, NULL, 10);
|
||||
buffer = xmalloc(length);
|
||||
|
||||
while (n < length) {
|
||||
size_t s = fread(buffer + n, 1, length - n, stdin);
|
||||
size_t s = strbuf_fread(sb, length - n, stdin);
|
||||
if (!s && feof(stdin))
|
||||
die("EOF in data (%lu bytes remaining)",
|
||||
(unsigned long)(length - n));
|
||||
@@ -1680,8 +1642,6 @@ static void *cmd_data (size_t *size)
|
||||
}
|
||||
|
||||
skip_optional_lf();
|
||||
*size = length;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static int validate_raw_date(const char *src, char *result, int maxlen)
|
||||
@@ -1744,15 +1704,12 @@ static char *parse_ident(const char *buf)
|
||||
|
||||
static void cmd_new_blob(void)
|
||||
{
|
||||
size_t l;
|
||||
void *d;
|
||||
static struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
read_next_command();
|
||||
cmd_mark();
|
||||
d = cmd_data(&l);
|
||||
|
||||
if (store_object(OBJ_BLOB, d, l, &last_blob, NULL, next_mark))
|
||||
free(d);
|
||||
cmd_data(&buf);
|
||||
store_object(OBJ_BLOB, &buf, &last_blob, NULL, next_mark);
|
||||
}
|
||||
|
||||
static void unload_one_branch(void)
|
||||
@@ -1802,7 +1759,7 @@ static void load_branch(struct branch *b)
|
||||
static void file_change_m(struct branch *b)
|
||||
{
|
||||
const char *p = command_buf.buf + 2;
|
||||
char *p_uq;
|
||||
static struct strbuf uq = STRBUF_INIT;
|
||||
const char *endp;
|
||||
struct object_entry *oe = oe;
|
||||
unsigned char sha1[20];
|
||||
@@ -1840,22 +1797,23 @@ static void file_change_m(struct branch *b)
|
||||
if (*p++ != ' ')
|
||||
die("Missing space after SHA1: %s", command_buf.buf);
|
||||
|
||||
p_uq = unquote_c_style(p, &endp);
|
||||
if (p_uq) {
|
||||
strbuf_reset(&uq);
|
||||
if (!unquote_c_style(&uq, p, &endp)) {
|
||||
if (*endp)
|
||||
die("Garbage after path in: %s", command_buf.buf);
|
||||
p = p_uq;
|
||||
p = uq.buf;
|
||||
}
|
||||
|
||||
if (inline_data) {
|
||||
size_t l;
|
||||
void *d;
|
||||
if (!p_uq)
|
||||
p = p_uq = xstrdup(p);
|
||||
static struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
if (p != uq.buf) {
|
||||
strbuf_addstr(&uq, p);
|
||||
p = uq.buf;
|
||||
}
|
||||
read_next_command();
|
||||
d = cmd_data(&l);
|
||||
if (store_object(OBJ_BLOB, d, l, &last_blob, sha1, 0))
|
||||
free(d);
|
||||
cmd_data(&buf);
|
||||
store_object(OBJ_BLOB, &buf, &last_blob, sha1, 0);
|
||||
} else if (oe) {
|
||||
if (oe->type != OBJ_BLOB)
|
||||
die("Not a blob (actually a %s): %s",
|
||||
@@ -1870,58 +1828,54 @@ static void file_change_m(struct branch *b)
|
||||
}
|
||||
|
||||
tree_content_set(&b->branch_tree, p, sha1, S_IFREG | mode, NULL);
|
||||
free(p_uq);
|
||||
}
|
||||
|
||||
static void file_change_d(struct branch *b)
|
||||
{
|
||||
const char *p = command_buf.buf + 2;
|
||||
char *p_uq;
|
||||
static struct strbuf uq = STRBUF_INIT;
|
||||
const char *endp;
|
||||
|
||||
p_uq = unquote_c_style(p, &endp);
|
||||
if (p_uq) {
|
||||
strbuf_reset(&uq);
|
||||
if (!unquote_c_style(&uq, p, &endp)) {
|
||||
if (*endp)
|
||||
die("Garbage after path in: %s", command_buf.buf);
|
||||
p = p_uq;
|
||||
p = uq.buf;
|
||||
}
|
||||
tree_content_remove(&b->branch_tree, p, NULL);
|
||||
free(p_uq);
|
||||
}
|
||||
|
||||
static void file_change_cr(struct branch *b, int rename)
|
||||
{
|
||||
const char *s, *d;
|
||||
char *s_uq, *d_uq;
|
||||
static struct strbuf s_uq = STRBUF_INIT;
|
||||
static struct strbuf d_uq = STRBUF_INIT;
|
||||
const char *endp;
|
||||
struct tree_entry leaf;
|
||||
|
||||
s = command_buf.buf + 2;
|
||||
s_uq = unquote_c_style(s, &endp);
|
||||
if (s_uq) {
|
||||
strbuf_reset(&s_uq);
|
||||
if (!unquote_c_style(&s_uq, s, &endp)) {
|
||||
if (*endp != ' ')
|
||||
die("Missing space after source: %s", command_buf.buf);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
endp = strchr(s, ' ');
|
||||
if (!endp)
|
||||
die("Missing space after source: %s", command_buf.buf);
|
||||
s_uq = xmalloc(endp - s + 1);
|
||||
memcpy(s_uq, s, endp - s);
|
||||
s_uq[endp - s] = 0;
|
||||
strbuf_add(&s_uq, s, endp - s);
|
||||
}
|
||||
s = s_uq;
|
||||
s = s_uq.buf;
|
||||
|
||||
endp++;
|
||||
if (!*endp)
|
||||
die("Missing dest: %s", command_buf.buf);
|
||||
|
||||
d = endp;
|
||||
d_uq = unquote_c_style(d, &endp);
|
||||
if (d_uq) {
|
||||
strbuf_reset(&d_uq);
|
||||
if (!unquote_c_style(&d_uq, d, &endp)) {
|
||||
if (*endp)
|
||||
die("Garbage after dest in: %s", command_buf.buf);
|
||||
d = d_uq;
|
||||
d = d_uq.buf;
|
||||
}
|
||||
|
||||
memset(&leaf, 0, sizeof(leaf));
|
||||
@@ -1935,9 +1889,6 @@ static void file_change_cr(struct branch *b, int rename)
|
||||
leaf.versions[1].sha1,
|
||||
leaf.versions[1].mode,
|
||||
leaf.tree);
|
||||
|
||||
free(s_uq);
|
||||
free(d_uq);
|
||||
}
|
||||
|
||||
static void file_change_deleteall(struct branch *b)
|
||||
@@ -2062,9 +2013,8 @@ static struct hash_list *cmd_merge(unsigned int *count)
|
||||
|
||||
static void cmd_new_commit(void)
|
||||
{
|
||||
static struct strbuf msg = STRBUF_INIT;
|
||||
struct branch *b;
|
||||
void *msg;
|
||||
size_t msglen;
|
||||
char *sp;
|
||||
char *author = NULL;
|
||||
char *committer = NULL;
|
||||
@@ -2089,7 +2039,7 @@ static void cmd_new_commit(void)
|
||||
}
|
||||
if (!committer)
|
||||
die("Expected committer but didn't get one");
|
||||
msg = cmd_data(&msglen);
|
||||
cmd_data(&msg);
|
||||
read_next_command();
|
||||
cmd_from(b);
|
||||
merge_list = cmd_merge(&merge_count);
|
||||
@@ -2101,7 +2051,7 @@ static void cmd_new_commit(void)
|
||||
}
|
||||
|
||||
/* file_change* */
|
||||
while (!command_buf.eof && command_buf.len > 1) {
|
||||
while (command_buf.len > 0) {
|
||||
if (!prefixcmp(command_buf.buf, "M "))
|
||||
file_change_m(b);
|
||||
else if (!prefixcmp(command_buf.buf, "D "))
|
||||
@@ -2116,53 +2066,47 @@ static void cmd_new_commit(void)
|
||||
unread_command_buf = 1;
|
||||
break;
|
||||
}
|
||||
read_next_command();
|
||||
if (read_next_command() == EOF)
|
||||
break;
|
||||
}
|
||||
|
||||
/* build the tree and the commit */
|
||||
store_tree(&b->branch_tree);
|
||||
hashcpy(b->branch_tree.versions[0].sha1,
|
||||
b->branch_tree.versions[1].sha1);
|
||||
size_dbuf(&new_data, 114 + msglen
|
||||
+ merge_count * 49
|
||||
+ (author
|
||||
? strlen(author) + strlen(committer)
|
||||
: 2 * strlen(committer)));
|
||||
sp = new_data.buffer;
|
||||
sp += sprintf(sp, "tree %s\n",
|
||||
|
||||
strbuf_reset(&new_data);
|
||||
strbuf_addf(&new_data, "tree %s\n",
|
||||
sha1_to_hex(b->branch_tree.versions[1].sha1));
|
||||
if (!is_null_sha1(b->sha1))
|
||||
sp += sprintf(sp, "parent %s\n", sha1_to_hex(b->sha1));
|
||||
strbuf_addf(&new_data, "parent %s\n", sha1_to_hex(b->sha1));
|
||||
while (merge_list) {
|
||||
struct hash_list *next = merge_list->next;
|
||||
sp += sprintf(sp, "parent %s\n", sha1_to_hex(merge_list->sha1));
|
||||
strbuf_addf(&new_data, "parent %s\n", sha1_to_hex(merge_list->sha1));
|
||||
free(merge_list);
|
||||
merge_list = next;
|
||||
}
|
||||
sp += sprintf(sp, "author %s\n", author ? author : committer);
|
||||
sp += sprintf(sp, "committer %s\n", committer);
|
||||
*sp++ = '\n';
|
||||
memcpy(sp, msg, msglen);
|
||||
sp += msglen;
|
||||
strbuf_addf(&new_data,
|
||||
"author %s\n"
|
||||
"committer %s\n"
|
||||
"\n",
|
||||
author ? author : committer, committer);
|
||||
strbuf_addbuf(&new_data, &msg);
|
||||
free(author);
|
||||
free(committer);
|
||||
free(msg);
|
||||
|
||||
if (!store_object(OBJ_COMMIT,
|
||||
new_data.buffer, sp - (char*)new_data.buffer,
|
||||
NULL, b->sha1, next_mark))
|
||||
if (!store_object(OBJ_COMMIT, &new_data, NULL, b->sha1, next_mark))
|
||||
b->pack_id = pack_id;
|
||||
b->last_commit = object_count_by_type[OBJ_COMMIT];
|
||||
}
|
||||
|
||||
static void cmd_new_tag(void)
|
||||
{
|
||||
static struct strbuf msg = STRBUF_INIT;
|
||||
char *sp;
|
||||
const char *from;
|
||||
char *tagger;
|
||||
struct branch *s;
|
||||
void *msg;
|
||||
size_t msglen;
|
||||
struct tag *t;
|
||||
uintmax_t from_mark = 0;
|
||||
unsigned char sha1[20];
|
||||
@@ -2213,24 +2157,21 @@ static void cmd_new_tag(void)
|
||||
|
||||
/* tag payload/message */
|
||||
read_next_command();
|
||||
msg = cmd_data(&msglen);
|
||||
cmd_data(&msg);
|
||||
|
||||
/* build the tag object */
|
||||
size_dbuf(&new_data, 67+strlen(t->name)+strlen(tagger)+msglen);
|
||||
sp = new_data.buffer;
|
||||
sp += sprintf(sp, "object %s\n", sha1_to_hex(sha1));
|
||||
sp += sprintf(sp, "type %s\n", commit_type);
|
||||
sp += sprintf(sp, "tag %s\n", t->name);
|
||||
sp += sprintf(sp, "tagger %s\n", tagger);
|
||||
*sp++ = '\n';
|
||||
memcpy(sp, msg, msglen);
|
||||
sp += msglen;
|
||||
strbuf_reset(&new_data);
|
||||
strbuf_addf(&new_data,
|
||||
"object %s\n"
|
||||
"type %s\n"
|
||||
"tag %s\n"
|
||||
"tagger %s\n"
|
||||
"\n",
|
||||
sha1_to_hex(sha1), commit_type, t->name, tagger);
|
||||
strbuf_addbuf(&new_data, &msg);
|
||||
free(tagger);
|
||||
free(msg);
|
||||
|
||||
if (store_object(OBJ_TAG, new_data.buffer,
|
||||
sp - (char*)new_data.buffer,
|
||||
NULL, t->sha1, 0))
|
||||
if (store_object(OBJ_TAG, &new_data, NULL, t->sha1, 0))
|
||||
t->pack_id = MAX_PACK_ID;
|
||||
else
|
||||
t->pack_id = pack_id;
|
||||
@@ -2256,7 +2197,7 @@ static void cmd_reset_branch(void)
|
||||
else
|
||||
b = new_branch(sp);
|
||||
read_next_command();
|
||||
if (!cmd_from(b) && command_buf.len > 1)
|
||||
if (!cmd_from(b) && command_buf.len > 0)
|
||||
unread_command_buf = 1;
|
||||
}
|
||||
|
||||
@@ -2273,7 +2214,7 @@ static void cmd_checkpoint(void)
|
||||
|
||||
static void cmd_progress(void)
|
||||
{
|
||||
fwrite(command_buf.buf, 1, command_buf.len - 1, stdout);
|
||||
fwrite(command_buf.buf, 1, command_buf.len, stdout);
|
||||
fputc('\n', stdout);
|
||||
fflush(stdout);
|
||||
skip_optional_lf();
|
||||
@@ -2323,7 +2264,7 @@ int main(int argc, const char **argv)
|
||||
|
||||
git_config(git_default_config);
|
||||
alloc_objects(object_entry_alloc);
|
||||
strbuf_init(&command_buf);
|
||||
strbuf_init(&command_buf, 0);
|
||||
atom_table = xcalloc(atom_table_sz, sizeof(struct atom_str*));
|
||||
branch_table = xcalloc(branch_table_sz, sizeof(struct branch*));
|
||||
avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*));
|
||||
@@ -2381,11 +2322,8 @@ int main(int argc, const char **argv)
|
||||
prepare_packed_git();
|
||||
start_packfile();
|
||||
set_die_routine(die_nicely);
|
||||
for (;;) {
|
||||
read_next_command();
|
||||
if (command_buf.eof)
|
||||
break;
|
||||
else if (!strcmp("blob", command_buf.buf))
|
||||
while (read_next_command() != EOF) {
|
||||
if (!strcmp("blob", command_buf.buf))
|
||||
cmd_new_blob();
|
||||
else if (!prefixcmp(command_buf.buf, "commit "))
|
||||
cmd_new_commit();
|
||||
|
||||
7
fetch.c
7
fetch.c
@@ -6,7 +6,6 @@
|
||||
#include "tag.h"
|
||||
#include "blob.h"
|
||||
#include "refs.h"
|
||||
#include "strbuf.h"
|
||||
|
||||
int get_tree = 0;
|
||||
int get_history = 0;
|
||||
@@ -218,13 +217,12 @@ int pull_targets_stdin(char ***target, const char ***write_ref)
|
||||
int targets = 0, targets_alloc = 0;
|
||||
struct strbuf buf;
|
||||
*target = NULL; *write_ref = NULL;
|
||||
strbuf_init(&buf);
|
||||
strbuf_init(&buf, 0);
|
||||
while (1) {
|
||||
char *rf_one = NULL;
|
||||
char *tg_one;
|
||||
|
||||
read_line(&buf, stdin, '\n');
|
||||
if (buf.eof)
|
||||
if (strbuf_getline(&buf, stdin, '\n') == EOF)
|
||||
break;
|
||||
tg_one = buf.buf;
|
||||
rf_one = strchr(tg_one, '\t');
|
||||
@@ -240,6 +238,7 @@ int pull_targets_stdin(char ***target, const char ***write_ref)
|
||||
(*write_ref)[targets] = rf_one ? xstrdup(rf_one) : NULL;
|
||||
targets++;
|
||||
}
|
||||
strbuf_release(&buf);
|
||||
return targets;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user