mirror of
https://github.com/git/git.git
synced 2026-03-13 10:23:30 +01:00
Merge commit '7be003b0261d13c99ed8d6764d20676a2b5c8347'
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -7,8 +7,6 @@ git-add--interactive
|
||||
git-am
|
||||
git-annotate
|
||||
git-apply
|
||||
git-applymbox
|
||||
git-applypatch
|
||||
git-archimport
|
||||
git-archive
|
||||
git-bisect
|
||||
@@ -126,6 +124,7 @@ git-ssh-push
|
||||
git-ssh-upload
|
||||
git-status
|
||||
git-stripspace
|
||||
git-submodule
|
||||
git-svn
|
||||
git-svnimport
|
||||
git-symbolic-ref
|
||||
@@ -152,6 +151,7 @@ test-delta
|
||||
test-dump-cache-tree
|
||||
test-genrandom
|
||||
test-match-trees
|
||||
test-sha1
|
||||
common-cmds.h
|
||||
*.tar.gz
|
||||
*.dsc
|
||||
|
||||
9
.mailmap
9
.mailmap
@@ -7,6 +7,8 @@
|
||||
|
||||
Aneesh Kumar K.V <aneesh.kumar@gmail.com>
|
||||
Chris Shoemaker <c.shoemaker@cox.net>
|
||||
Dana L. How <danahow@gmail.com>
|
||||
Dana L. How <how@deathvalley.cswitch.com>
|
||||
Daniel Barkalow <barkalow@iabervon.org>
|
||||
David Kågedal <davidk@lysator.liu.se>
|
||||
Fredrik Kuivinen <freku045@student.liu.se>
|
||||
@@ -19,8 +21,8 @@ Jon Loeliger <jdl@freescale.com>
|
||||
Jon Seymour <jon@blackcubes.dyndns.org>
|
||||
Karl Hasselström <kha@treskal.com>
|
||||
Kent Engstrom <kent@lysator.liu.se>
|
||||
Lars Doelle <lars.doelle@on-line.de>
|
||||
Lars Doelle <lars.doelle@on-line ! de>
|
||||
Lars Doelle <lars.doelle@on-line.de>
|
||||
Lukas Sandström <lukass@etek.chalmers.se>
|
||||
Martin Langhoff <martin@catalyst.net.nz>
|
||||
Michele Ballabio <barra_cuda@katamail.com>
|
||||
@@ -34,12 +36,11 @@ Sean Estabrooks <seanlkml@sympatico.ca>
|
||||
Shawn O. Pearce <spearce@spearce.org>
|
||||
Theodore Ts'o <tytso@mit.edu>
|
||||
Tony Luck <tony.luck@intel.com>
|
||||
Uwe Kleine-König <zeisberg@informatik.uni-freiburg.de>
|
||||
Uwe Kleine-König <Uwe_Zeisberger@digi.com>
|
||||
Uwe Kleine-König <uzeisberger@io.fsforth.de>
|
||||
Uwe Kleine-König <ukleinek@informatik.uni-freiburg.de>
|
||||
Uwe Kleine-König <uzeisberger@io.fsforth.de>
|
||||
Uwe Kleine-König <zeisberg@informatik.uni-freiburg.de>
|
||||
Ville Skyttä <scop@xemacs.org>
|
||||
YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
|
||||
anonymous <linux@horizon.com>
|
||||
anonymous <linux@horizon.net>
|
||||
Dana L. How <how@deathvalley.cswitch.com>
|
||||
|
||||
109
Documentation/RelNotes-1.5.3.txt
Normal file
109
Documentation/RelNotes-1.5.3.txt
Normal file
@@ -0,0 +1,109 @@
|
||||
GIT v1.5.3 Release Notes (draft)
|
||||
========================
|
||||
|
||||
Updates since v1.5.2
|
||||
--------------------
|
||||
|
||||
* An initial interation of Porcelain level superproject support
|
||||
started to take shape.
|
||||
|
||||
* Thee are a handful pack-objects changes to help you cope better with
|
||||
repositories with pathologically large blobs in them.
|
||||
|
||||
* New commands and options.
|
||||
|
||||
- "git-submodule" command helps you manage the projects from
|
||||
the superproject that contain them.
|
||||
|
||||
- In addition to core.compression configuration option,
|
||||
core.loosecompression and pack.compression options can
|
||||
independently tweak zlib compression levels used for loose
|
||||
and packed objects.
|
||||
|
||||
- "git-ls-tree -l" shows size of blobs pointed at by the
|
||||
tree entries, similar to "/bin/ls -l".
|
||||
|
||||
- "git-rev-list" learned --regexp-ignore-case and
|
||||
--extended-regexp options to tweak its matching logic used
|
||||
for --grep fitering.
|
||||
|
||||
- "git-describe --contains" is a handier way to call more
|
||||
obscure command "git-name-rev --tags".
|
||||
|
||||
- "git gc --aggressive" tells the command to spend more cycles
|
||||
to optimize the repository harder.
|
||||
|
||||
- "git repack" can be told to split resulting packs to avoid
|
||||
exceeding limit specified with "--max-pack-size".
|
||||
|
||||
* Updated behavior of existing commands.
|
||||
|
||||
- "git push" pretends that you immediately fetched back from
|
||||
the remote by updating corresponding remote tracking
|
||||
branches if you have any.
|
||||
|
||||
- The diffstat given after a merge (or a pull) honors the
|
||||
color.diff configuration.
|
||||
|
||||
- "git-apply --whitespace=strip" removes blank lines added at
|
||||
the end of the file.
|
||||
|
||||
- fetch over git native protocols with -v shows connection
|
||||
status, and the IP address of the other end, to help
|
||||
diagnosing problems.
|
||||
|
||||
- core.legacyheaders is no more, although we still can read
|
||||
objects created in a new loose object format.
|
||||
|
||||
- "git-mailsplit" (hence "git-am") can read from Maildir
|
||||
formatted mailboxes.
|
||||
|
||||
- "git cvsserver" does not barf upon seeing "cvs login"
|
||||
request.
|
||||
|
||||
- "pack-objects" honors "delta" attribute set in
|
||||
.gitattributes. It does not attempt to deltify blobs that
|
||||
come from paths with delta attribute set to false.
|
||||
|
||||
- new-workdir script (in contrib) can now be used with a bare
|
||||
repository.
|
||||
|
||||
|
||||
* Builds
|
||||
|
||||
-
|
||||
|
||||
* Performance Tweaks
|
||||
|
||||
- git-pack-objects avoids re-deltification cost by caching
|
||||
small enough delta results it creates while looking for the
|
||||
best delta candidates.
|
||||
|
||||
- diff-delta code that is used for packing has been improved
|
||||
to work better on big files.
|
||||
|
||||
- when there are more than one pack files in the repository,
|
||||
the runtime used to try finding an object always from the
|
||||
newest packfile; it now tries the same packfile as we found
|
||||
the object requested the last time, which exploits the
|
||||
locality of references.
|
||||
|
||||
Fixes since v1.5.2
|
||||
------------------
|
||||
|
||||
All of the fixes in v1.5.2 maintenance series are included in
|
||||
this release, unless otherwise noted.
|
||||
|
||||
* Bugfixes
|
||||
|
||||
- .... This has not
|
||||
been backported to 1.5.2.x series, as it is rather an
|
||||
intrusive change.
|
||||
|
||||
|
||||
--
|
||||
exec >/var/tmp/1
|
||||
O=v1.5.2-45-ged82edc
|
||||
O=v1.5.2-172-g1a8b769
|
||||
echo O=`git describe refs/heads/master`
|
||||
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
|
||||
@@ -14,6 +14,8 @@ Checklist (and a short version for the impatient):
|
||||
commit message (or just use the option "-s" when
|
||||
committing) to confirm that you agree to the Developer's
|
||||
Certificate of Origin
|
||||
- make sure that you have tests for the bug you are fixing
|
||||
- make sure that the test suite passes after your commit
|
||||
|
||||
Patch:
|
||||
|
||||
@@ -33,6 +35,8 @@ Checklist (and a short version for the impatient):
|
||||
- if you change, add, or remove a command line option or
|
||||
make some other user interface change, the associated
|
||||
documentation should be updated as well.
|
||||
- if your name is not writable in ASCII, make sure that
|
||||
you send off a message in the correct encoding.
|
||||
|
||||
Long version:
|
||||
|
||||
@@ -239,7 +243,7 @@ One test you could do yourself if your MUA is set up correctly is:
|
||||
$ git fetch http://kernel.org/pub/scm/git/git.git master:test-apply
|
||||
$ git checkout test-apply
|
||||
$ git reset --hard
|
||||
$ git applymbox a.patch
|
||||
$ git am a.patch
|
||||
|
||||
If it does not apply correctly, there can be various reasons.
|
||||
|
||||
@@ -247,7 +251,7 @@ If it does not apply correctly, there can be various reasons.
|
||||
does not have much to do with your MUA. Please rebase the
|
||||
patch appropriately.
|
||||
|
||||
* Your MUA corrupted your patch; applymbox would complain that
|
||||
* Your MUA corrupted your patch; "am" would complain that
|
||||
the patch does not apply. Look at .dotest/ subdirectory and
|
||||
see what 'patch' file contains and check for the common
|
||||
corruption patterns mentioned above.
|
||||
|
||||
@@ -72,8 +72,6 @@ __DATA__
|
||||
git-add mainporcelain
|
||||
git-am mainporcelain
|
||||
git-annotate ancillaryinterrogators
|
||||
git-applymbox ancillaryinterrogators
|
||||
git-applypatch purehelpers
|
||||
git-apply plumbingmanipulators
|
||||
git-archimport foreignscminterface
|
||||
git-archive mainporcelain
|
||||
@@ -180,6 +178,7 @@ git-ssh-fetch synchingrepositories
|
||||
git-ssh-upload synchingrepositories
|
||||
git-status mainporcelain
|
||||
git-stripspace purehelpers
|
||||
git-submodule mainporcelain
|
||||
git-svn foreignscminterface
|
||||
git-svnimport foreignscminterface
|
||||
git-symbolic-ref plumbingmanipulators
|
||||
|
||||
@@ -568,6 +568,15 @@ pack.compression::
|
||||
slowest. If not set, defaults to core.compression. If that is
|
||||
not set, defaults to -1.
|
||||
|
||||
pack.deltaCacheSize::
|
||||
The maxium memory in bytes used for caching deltas in
|
||||
gitlink:git-pack-objects[1].
|
||||
A value of 0 means no limit. Defaults to 0.
|
||||
|
||||
pack.deltaCacheLimit::
|
||||
The maxium size of a delta, that is cached in
|
||||
gitlink:git-pack-objects[1]. Defaults to 1000.
|
||||
|
||||
pull.octopus::
|
||||
The default merge strategy to use when pulling multiple branches
|
||||
at once.
|
||||
|
||||
@@ -13,7 +13,6 @@ SYNOPSIS
|
||||
[--3way] [--interactive] [--binary]
|
||||
[--whitespace=<option>] [-C<n>] [-p<n>]
|
||||
<mbox>|<Maildir>...
|
||||
|
||||
'git-am' [--skip | --resolved]
|
||||
|
||||
DESCRIPTION
|
||||
@@ -128,8 +127,7 @@ is terminated before the first occurrence of such a line.
|
||||
|
||||
When initially invoking it, you give it names of the mailboxes
|
||||
to crunch. Upon seeing the first patch that does not apply, it
|
||||
aborts in the middle, just like 'git-applymbox' does. You can
|
||||
recover from this in one of two ways:
|
||||
aborts in the middle,. You can recover from this in one of two ways:
|
||||
|
||||
. skip the current patch by re-running the command with '--skip'
|
||||
option.
|
||||
@@ -146,7 +144,7 @@ names.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
gitlink:git-applymbox[1], gitlink:git-applypatch[1], gitlink:git-apply[1].
|
||||
gitlink:git-apply[1].
|
||||
|
||||
|
||||
Author
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
git-applymbox(1)
|
||||
================
|
||||
|
||||
NAME
|
||||
----
|
||||
git-applymbox - Apply a series of patches in a mailbox
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-applymbox' [-u] [-k] [-q] [-m] ( -c .dotest/<num> | <mbox> ) [ <signoff> ]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Splits mail messages in a mailbox into commit log message,
|
||||
authorship information and patches, and applies them to the
|
||||
current branch.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-q::
|
||||
Apply patches interactively. The user will be given
|
||||
opportunity to edit the log message and the patch before
|
||||
attempting to apply it.
|
||||
|
||||
-k::
|
||||
Usually the program 'cleans up' the Subject: header line
|
||||
to extract the title line for the commit log message,
|
||||
among which (1) remove 'Re:' or 're:', (2) leading
|
||||
whitespaces, (3) '[' up to ']', typically '[PATCH]', and
|
||||
then prepends "[PATCH] ". This flag forbids this
|
||||
munging, and is most useful when used to read back 'git
|
||||
format-patch -k' output.
|
||||
|
||||
-m::
|
||||
Patches are applied with `git-apply` command, and unless
|
||||
it cleanly applies without fuzz, the processing fails.
|
||||
With this flag, if a tree that the patch applies cleanly
|
||||
is found in a repository, the patch is applied to the
|
||||
tree and then a 3-way merge between the resulting tree
|
||||
and the current tree.
|
||||
|
||||
-u::
|
||||
Pass `-u` flag to `git-mailinfo` (see gitlink:git-mailinfo[1]).
|
||||
The proposed commit log message taken from the e-mail
|
||||
are re-coded into UTF-8 encoding (configuration variable
|
||||
`i18n.commitencoding` can be used to specify project's
|
||||
preferred encoding if it is not UTF-8). This used to be
|
||||
optional but now it is the default.
|
||||
+
|
||||
Note that the patch is always used as-is without charset
|
||||
conversion, even with this flag.
|
||||
|
||||
-n::
|
||||
Pass `-n` flag to `git-mailinfo` (see
|
||||
gitlink:git-mailinfo[1]).
|
||||
|
||||
-c .dotest/<num>::
|
||||
When the patch contained in an e-mail does not cleanly
|
||||
apply, the command exits with an error message. The
|
||||
patch and extracted message are found in .dotest/, and
|
||||
you could re-run 'git applymbox' with '-c .dotest/<num>'
|
||||
flag to restart the process after inspecting and fixing
|
||||
them.
|
||||
|
||||
<mbox>::
|
||||
The name of the file that contains the e-mail messages
|
||||
with patches. This file should be in the UNIX mailbox
|
||||
format. See 'SubmittingPatches' document to learn about
|
||||
the formatting convention for e-mail submission.
|
||||
|
||||
<signoff>::
|
||||
The name of the file that contains your "Signed-off-by"
|
||||
line. See 'SubmittingPatches' document to learn what
|
||||
"Signed-off-by" line means. You can also just say
|
||||
'yes', 'true', 'me', or 'please' to use an automatically
|
||||
generated "Signed-off-by" line based on your committer
|
||||
identity.
|
||||
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
gitlink:git-am[1], gitlink:git-applypatch[1].
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Linus Torvalds <torvalds@osdl.org>
|
||||
|
||||
Documentation
|
||||
--------------
|
||||
Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the gitlink:git[7] suite
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
git-applypatch(1)
|
||||
=================
|
||||
|
||||
NAME
|
||||
----
|
||||
git-applypatch - Apply one patch extracted from an e-mail
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-applypatch' <msg> <patch> <info> [<signoff>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This is usually not what an end user wants to run directly. See
|
||||
gitlink:git-am[1] instead.
|
||||
|
||||
Takes three files <msg>, <patch>, and <info> prepared from an
|
||||
e-mail message by 'git-mailinfo', and creates a commit. It is
|
||||
usually not necessary to use this command directly.
|
||||
|
||||
This command can run `applypatch-msg`, `pre-applypatch`, and
|
||||
`post-applypatch` hooks. See link:hooks.html[hooks] for more
|
||||
information.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
<msg>::
|
||||
Commit log message (sans the first line, which comes
|
||||
from e-mail Subject stored in <info>).
|
||||
|
||||
<patch>::
|
||||
The patch to apply.
|
||||
|
||||
<info>::
|
||||
Author and subject information extracted from e-mail,
|
||||
used on "author" line and as the first line of the
|
||||
commit log message.
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Linus Torvalds <torvalds@osdl.org>
|
||||
|
||||
Documentation
|
||||
--------------
|
||||
Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the gitlink:git[7] suite
|
||||
|
||||
@@ -10,7 +10,7 @@ SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]
|
||||
[--full] [--strict] [<object>*]
|
||||
[--full] [--strict] [--verbose] [<object>*]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -61,6 +61,9 @@ index file and all SHA1 references in .git/refs/* as heads.
|
||||
objects that triggers this check, but it is recommended
|
||||
to check new projects with this flag.
|
||||
|
||||
--verbose::
|
||||
Be chatty.
|
||||
|
||||
It tests SHA1 and general object sanity, and it does full tracking of
|
||||
the resulting reachability and everything else. It prints out any
|
||||
corruption it finds (missing or bad objects), and if you use the
|
||||
|
||||
@@ -37,10 +37,10 @@ OPTIONS
|
||||
|
||||
--aggressive::
|
||||
Usually 'git-gc' runs very quickly while providing good disk
|
||||
space utilization and performance. This option will cause
|
||||
git-gc to more aggressive optimize the repository at the expense
|
||||
space utilization and performance. This option will cause
|
||||
git-gc to more aggressively optimize the repository at the expense
|
||||
of taking much more time. The effects of this optimization are
|
||||
persistent, so this option only needs to be sporadically; every
|
||||
persistent, so this option only needs to be used occasionally; every
|
||||
few hundred changesets or so.
|
||||
|
||||
Configuration
|
||||
|
||||
@@ -16,7 +16,7 @@ DESCRIPTION
|
||||
Reading a single e-mail message from the standard input, and
|
||||
writes the commit log message in <msg> file, and the patches in
|
||||
<patch> file. The author name, e-mail and e-mail subject are
|
||||
written out to the standard output to be used by git-applypatch
|
||||
written out to the standard output to be used by git-am
|
||||
to create a commit. It is usually not necessary to use this
|
||||
command directly. See gitlink:git-am[1] instead.
|
||||
|
||||
|
||||
65
Documentation/git-submodule.txt
Normal file
65
Documentation/git-submodule.txt
Normal file
@@ -0,0 +1,65 @@
|
||||
git-submodule(1)
|
||||
================
|
||||
|
||||
NAME
|
||||
----
|
||||
git-submodule - Initialize, update or inspect submodules
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-submodule' [--quiet] [--cached] [status|init|update] [--] [<path>...]
|
||||
|
||||
|
||||
COMMANDS
|
||||
--------
|
||||
status::
|
||||
Show the status of the submodules. This will print the SHA-1 of the
|
||||
currently checked out commit for each submodule, along with the
|
||||
submodule path and the output of gitlink:git-describe[1] for the
|
||||
SHA-1. Each SHA-1 will be prefixed with `-` if the submodule is not
|
||||
initialized and `+` if the currently checked out submodule commit
|
||||
does not match the SHA-1 found in the index of the containing
|
||||
repository. This command is the default command for git-submodule.
|
||||
|
||||
init::
|
||||
Initialize the submodules, i.e. clone the git repositories specified
|
||||
in the .gitmodules file and checkout the submodule commits specified
|
||||
in the index of the containing repository. This will make the
|
||||
submodules HEAD be detached.
|
||||
|
||||
update::
|
||||
Update the initialized submodules, i.e. checkout the submodule commits
|
||||
specified in the index of the containing repository. This will make
|
||||
the submodules HEAD be detached.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-q, --quiet::
|
||||
Only print error messages.
|
||||
|
||||
--cached::
|
||||
Display the SHA-1 stored in the index, not the SHA-1 of the currently
|
||||
checked out submodule commit. This option is only valid for the
|
||||
status command.
|
||||
|
||||
<path>::
|
||||
Path to submodule(s). When specified this will restrict the command
|
||||
to only operate on the submodules found at the specified paths.
|
||||
|
||||
FILES
|
||||
-----
|
||||
When cloning submodules, a .gitmodules file in the top-level directory
|
||||
of the containing repository is used to find the url of each submodule.
|
||||
This file should be formatted in the same way as $GIR_DIR/config. The key
|
||||
to each submodule url is "module.$path.url".
|
||||
|
||||
|
||||
AUTHOR
|
||||
------
|
||||
Written by Lars Hjemli <hjemli@gmail.com>
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the gitlink:git[7] suite
|
||||
@@ -12,11 +12,10 @@ This document describes the currently defined hooks.
|
||||
applypatch-msg
|
||||
--------------
|
||||
|
||||
This hook is invoked by `git-applypatch` script, which is
|
||||
typically invoked by `git-applymbox`. It takes a single
|
||||
This hook is invoked by `git-am` script. It takes a single
|
||||
parameter, the name of the file that holds the proposed commit
|
||||
log message. Exiting with non-zero status causes
|
||||
`git-applypatch` to abort before applying the patch.
|
||||
`git-am` to abort before applying the patch.
|
||||
|
||||
The hook is allowed to edit the message file in place, and can
|
||||
be used to normalize the message into some project standard
|
||||
@@ -29,8 +28,7 @@ The default 'applypatch-msg' hook, when enabled, runs the
|
||||
pre-applypatch
|
||||
--------------
|
||||
|
||||
This hook is invoked by `git-applypatch` script, which is
|
||||
typically invoked by `git-applymbox`. It takes no parameter,
|
||||
This hook is invoked by `git-am`. It takes no parameter,
|
||||
and is invoked after the patch is applied, but before a commit
|
||||
is made. Exiting with non-zero status causes the working tree
|
||||
after application of the patch not committed.
|
||||
@@ -44,12 +42,11 @@ The default 'pre-applypatch' hook, when enabled, runs the
|
||||
post-applypatch
|
||||
---------------
|
||||
|
||||
This hook is invoked by `git-applypatch` script, which is
|
||||
typically invoked by `git-applymbox`. It takes no parameter,
|
||||
This hook is invoked by `git-am`. It takes no parameter,
|
||||
and is invoked after the patch is applied and a commit is made.
|
||||
|
||||
This hook is meant primarily for notification, and cannot affect
|
||||
the outcome of `git-applypatch`.
|
||||
the outcome of `git-am`.
|
||||
|
||||
pre-commit
|
||||
----------
|
||||
|
||||
33
Makefile
33
Makefile
@@ -208,10 +208,10 @@ SCRIPT_SH = \
|
||||
git-repack.sh git-request-pull.sh git-reset.sh \
|
||||
git-sh-setup.sh \
|
||||
git-tag.sh git-verify-tag.sh \
|
||||
git-applymbox.sh git-applypatch.sh git-am.sh \
|
||||
git-am.sh \
|
||||
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
|
||||
git-merge-resolve.sh git-merge-ours.sh \
|
||||
git-lost-found.sh git-quiltimport.sh
|
||||
git-lost-found.sh git-quiltimport.sh git-submodule.sh
|
||||
|
||||
SCRIPT_PERL = \
|
||||
git-add--interactive.perl \
|
||||
@@ -297,7 +297,8 @@ LIB_H = \
|
||||
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
|
||||
tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
|
||||
spawn-pipe.h \
|
||||
utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h mailmap.h
|
||||
utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \
|
||||
remote.h
|
||||
|
||||
DIFF_OBJS = \
|
||||
diff.o diff-lib.o diffcore-break.o diffcore-order.o \
|
||||
@@ -320,7 +321,7 @@ LIB_OBJS = \
|
||||
write_or_die.o trace.o list-objects.o grep.o match-trees.o \
|
||||
alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
|
||||
color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
|
||||
convert.o attr.o decorate.o progress.o mailmap.o symlinks.o
|
||||
convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o
|
||||
|
||||
BUILTIN_OBJS = \
|
||||
builtin-add.o \
|
||||
@@ -981,7 +982,7 @@ endif
|
||||
|
||||
### Testing rules
|
||||
|
||||
TEST_PROGRAMS = test-chmtime$X test-genrandom$X
|
||||
TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X
|
||||
|
||||
all: $(TEST_PROGRAMS)
|
||||
|
||||
@@ -995,26 +996,12 @@ export NO_SVN_TESTS
|
||||
test: all
|
||||
$(MAKE) -C t/ all
|
||||
|
||||
test-date$X: test-date.c date.o ctype.o
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) test-date.c date.o ctype.o
|
||||
test-date$X: date.o ctype.o
|
||||
|
||||
test-delta$X: test-delta.o diff-delta.o patch-delta.o $(GITLIBS)
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
|
||||
test-delta$X: diff-delta.o patch-delta.o
|
||||
|
||||
test-dump-cache-tree$X: dump-cache-tree.o $(GITLIBS)
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
|
||||
|
||||
test-sha1$X: test-sha1.o $(GITLIBS)
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
|
||||
|
||||
test-match-trees$X: test-match-trees.o $(GITLIBS)
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
|
||||
|
||||
test-chmtime$X: test-chmtime.c
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $<
|
||||
|
||||
test-genrandom$X: test-genrandom.c
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $<
|
||||
test-%$X: test-%.o $(GITLIBS)
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
|
||||
|
||||
check-sha1:: test-sha1$X
|
||||
./test-sha1.sh
|
||||
|
||||
@@ -55,7 +55,7 @@ static enum whitespace_eol {
|
||||
} new_whitespace = warn_on_whitespace;
|
||||
static int whitespace_error;
|
||||
static int squelch_whitespace_errors = 5;
|
||||
static int applied_after_stripping;
|
||||
static int applied_after_fixing_ws;
|
||||
static const char *patch_input_file;
|
||||
|
||||
static void parse_whitespace_option(const char *option)
|
||||
@@ -1661,7 +1661,7 @@ static int apply_line(char *output, const char *patch, int plen)
|
||||
if (add_nl_to_tail)
|
||||
output[plen++] = '\n';
|
||||
if (fixed)
|
||||
applied_after_stripping++;
|
||||
applied_after_fixing_ws++;
|
||||
return output + plen - buf;
|
||||
}
|
||||
|
||||
@@ -2888,18 +2888,17 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
|
||||
squelched == 1 ? "" : "s");
|
||||
}
|
||||
if (new_whitespace == error_on_whitespace)
|
||||
die("%d line%s add%s trailing whitespaces.",
|
||||
die("%d line%s add%s whitespace errors.",
|
||||
whitespace_error,
|
||||
whitespace_error == 1 ? "" : "s",
|
||||
whitespace_error == 1 ? "s" : "");
|
||||
if (applied_after_stripping)
|
||||
if (applied_after_fixing_ws)
|
||||
fprintf(stderr, "warning: %d line%s applied after"
|
||||
" stripping trailing whitespaces.\n",
|
||||
applied_after_stripping,
|
||||
applied_after_stripping == 1 ? "" : "s");
|
||||
" fixing whitespace errors.\n",
|
||||
applied_after_fixing_ws,
|
||||
applied_after_fixing_ws == 1 ? "" : "s");
|
||||
else if (whitespace_error)
|
||||
fprintf(stderr, "warning: %d line%s add%s trailing"
|
||||
" whitespaces.\n",
|
||||
fprintf(stderr, "warning: %d line%s add%s whitespace errors.\n",
|
||||
whitespace_error,
|
||||
whitespace_error == 1 ? "" : "s",
|
||||
whitespace_error == 1 ? "s" : "");
|
||||
|
||||
@@ -115,6 +115,8 @@ int cmd_count_objects(int ac, const char **av, const char *prefix)
|
||||
for (p = packed_git; p; p = p->next) {
|
||||
if (!p->pack_local)
|
||||
continue;
|
||||
if (open_pack_index(p))
|
||||
continue;
|
||||
packed += p->num_objects;
|
||||
num_pack++;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ static int check_strict;
|
||||
static int keep_cache_objects;
|
||||
static unsigned char head_sha1[20];
|
||||
static int errors_found;
|
||||
static int verbose;
|
||||
#define ERROR_OBJECT 01
|
||||
#define ERROR_REACHABLE 02
|
||||
|
||||
@@ -149,6 +150,9 @@ static void check_unreachable_object(struct object *obj)
|
||||
|
||||
static void check_object(struct object *obj)
|
||||
{
|
||||
if (verbose)
|
||||
fprintf(stderr, "Checking %s\n", sha1_to_hex(obj->sha1));
|
||||
|
||||
if (obj->flags & REACHABLE)
|
||||
check_reachable_object(obj);
|
||||
else
|
||||
@@ -161,6 +165,9 @@ static void check_connectivity(void)
|
||||
|
||||
/* Look up all the requirements, warn about missing objects.. */
|
||||
max = get_max_object_index();
|
||||
if (verbose)
|
||||
fprintf(stderr, "Checking connectivity (%d objects)\n", max);
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
struct object *obj = get_indexed_object(i);
|
||||
|
||||
@@ -229,6 +236,10 @@ static int fsck_tree(struct tree *item)
|
||||
const char *o_name;
|
||||
const unsigned char *o_sha1;
|
||||
|
||||
if (verbose)
|
||||
fprintf(stderr, "Checking tree %s\n",
|
||||
sha1_to_hex(item->object.sha1));
|
||||
|
||||
init_tree_desc(&desc, item->buffer, item->size);
|
||||
|
||||
o_mode = 0;
|
||||
@@ -317,6 +328,10 @@ static int fsck_commit(struct commit *commit)
|
||||
char *buffer = commit->buffer;
|
||||
unsigned char tree_sha1[20], sha1[20];
|
||||
|
||||
if (verbose)
|
||||
fprintf(stderr, "Checking commit %s\n",
|
||||
sha1_to_hex(commit->object.sha1));
|
||||
|
||||
if (memcmp(buffer, "tree ", 5))
|
||||
return objerror(&commit->object, "invalid format - expected 'tree' line");
|
||||
if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n')
|
||||
@@ -345,6 +360,10 @@ static int fsck_tag(struct tag *tag)
|
||||
{
|
||||
struct object *tagged = tag->tagged;
|
||||
|
||||
if (verbose)
|
||||
fprintf(stderr, "Checking tag %s\n",
|
||||
sha1_to_hex(tag->object.sha1));
|
||||
|
||||
if (!tagged) {
|
||||
return objerror(&tag->object, "could not load tagged object");
|
||||
}
|
||||
@@ -446,6 +465,9 @@ static void fsck_dir(int i, char *path)
|
||||
if (!dir)
|
||||
return;
|
||||
|
||||
if (verbose)
|
||||
fprintf(stderr, "Checking directory %s\n", path);
|
||||
|
||||
while ((de = readdir(dir)) != NULL) {
|
||||
char name[100];
|
||||
unsigned char sha1[20];
|
||||
@@ -480,6 +502,10 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
|
||||
{
|
||||
struct object *obj;
|
||||
|
||||
if (verbose)
|
||||
fprintf(stderr, "Checking reflog %s->%s\n",
|
||||
sha1_to_hex(osha1), sha1_to_hex(nsha1));
|
||||
|
||||
if (!is_null_sha1(osha1)) {
|
||||
obj = lookup_object(osha1);
|
||||
if (obj) {
|
||||
@@ -549,6 +575,10 @@ static void get_default_heads(void)
|
||||
static void fsck_object_dir(const char *path)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (verbose)
|
||||
fprintf(stderr, "Checking object directory\n");
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
static char dir[4096];
|
||||
sprintf(dir, "%s/%02x", path, i);
|
||||
@@ -564,6 +594,9 @@ static int fsck_head_link(void)
|
||||
int null_is_error = 0;
|
||||
const char *head_points_at = resolve_ref("HEAD", sha1, 0, &flag);
|
||||
|
||||
if (verbose)
|
||||
fprintf(stderr, "Checking HEAD link\n");
|
||||
|
||||
if (!head_points_at)
|
||||
return error("Invalid HEAD");
|
||||
if (!strcmp(head_points_at, "HEAD"))
|
||||
@@ -586,6 +619,9 @@ static int fsck_cache_tree(struct cache_tree *it)
|
||||
int i;
|
||||
int err = 0;
|
||||
|
||||
if (verbose)
|
||||
fprintf(stderr, "Checking cache tree\n");
|
||||
|
||||
if (0 <= it->entry_count) {
|
||||
struct object *obj = parse_object(it->sha1);
|
||||
if (!obj) {
|
||||
@@ -605,7 +641,7 @@ static int fsck_cache_tree(struct cache_tree *it)
|
||||
|
||||
static const char fsck_usage[] =
|
||||
"git-fsck [--tags] [--root] [[--unreachable] [--cache] [--full] "
|
||||
"[--strict] <head-sha1>*]";
|
||||
"[--strict] [--verbose] <head-sha1>*]";
|
||||
|
||||
int cmd_fsck(int argc, char **argv, const char *prefix)
|
||||
{
|
||||
@@ -645,6 +681,10 @@ int cmd_fsck(int argc, char **argv, const char *prefix)
|
||||
check_strict = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--verbose")) {
|
||||
verbose = 1;
|
||||
continue;
|
||||
}
|
||||
if (*arg == '-')
|
||||
usage(fsck_usage);
|
||||
}
|
||||
@@ -668,7 +708,10 @@ int cmd_fsck(int argc, char **argv, const char *prefix)
|
||||
verify_pack(p, 0);
|
||||
|
||||
for (p = packed_git; p; p = p->next) {
|
||||
uint32_t i, num = p->num_objects;
|
||||
uint32_t i, num;
|
||||
if (open_pack_index(p))
|
||||
continue;
|
||||
num = p->num_objects;
|
||||
for (i = 0; i < num; i++)
|
||||
fsck_sha1(nth_packed_object_sha1(p, i));
|
||||
}
|
||||
|
||||
@@ -23,10 +23,9 @@ git-pack-objects [{ -q | --progress | --all-progress }] [--max-pack-size=N] \n\
|
||||
[--stdout | base-name] [<ref-list | <object-list]";
|
||||
|
||||
struct object_entry {
|
||||
unsigned char sha1[20];
|
||||
uint32_t crc32; /* crc of raw pack data for this object */
|
||||
off_t offset; /* offset into the final pack file */
|
||||
struct pack_idx_entry idx;
|
||||
unsigned long size; /* uncompressed size */
|
||||
|
||||
unsigned int hash; /* name hint hash */
|
||||
unsigned int depth; /* delta depth */
|
||||
struct packed_git *in_pack; /* already in pack */
|
||||
@@ -36,6 +35,7 @@ struct object_entry {
|
||||
struct object_entry *delta_sibling; /* other deltified objects who
|
||||
* uses the same base as me
|
||||
*/
|
||||
void *delta_data; /* cached delta (uncompressed) */
|
||||
unsigned long delta_size; /* delta data size (uncompressed) */
|
||||
enum object_type type;
|
||||
enum object_type in_pack_type; /* could be delta */
|
||||
@@ -65,7 +65,6 @@ static int allow_ofs_delta;
|
||||
static const char *pack_tmp_name, *idx_tmp_name;
|
||||
static char tmpname[PATH_MAX];
|
||||
static const char *base_name;
|
||||
static unsigned char pack_file_sha1[20];
|
||||
static int progress = 1;
|
||||
static int window = 10;
|
||||
static uint32_t pack_size_limit;
|
||||
@@ -76,6 +75,10 @@ static struct progress progress_state;
|
||||
static int pack_compression_level = Z_DEFAULT_COMPRESSION;
|
||||
static int pack_compression_seen;
|
||||
|
||||
static unsigned long delta_cache_size = 0;
|
||||
static unsigned long max_delta_cache_size = 0;
|
||||
static unsigned long cache_max_small_delta_size = 1000;
|
||||
|
||||
/*
|
||||
* The object names in objects array are hashed with this hashtable,
|
||||
* to help looking up the entry by object name.
|
||||
@@ -241,11 +244,11 @@ static void *delta_against(void *buf, unsigned long size, struct object_entry *e
|
||||
{
|
||||
unsigned long othersize, delta_size;
|
||||
enum object_type type;
|
||||
void *otherbuf = read_sha1_file(entry->delta->sha1, &type, &othersize);
|
||||
void *otherbuf = read_sha1_file(entry->delta->idx.sha1, &type, &othersize);
|
||||
void *delta_buf;
|
||||
|
||||
if (!otherbuf)
|
||||
die("unable to read %s", sha1_to_hex(entry->delta->sha1));
|
||||
die("unable to read %s", sha1_to_hex(entry->delta->idx.sha1));
|
||||
delta_buf = diff_delta(otherbuf, othersize,
|
||||
buf, size, &delta_size, 0);
|
||||
if (!delta_buf || delta_size != entry->delta_size)
|
||||
@@ -374,11 +377,11 @@ static unsigned long write_object(struct sha1file *f,
|
||||
/* yes if unlimited packfile */
|
||||
!pack_size_limit ? 1 :
|
||||
/* no if base written to previous pack */
|
||||
entry->delta->offset == (off_t)-1 ? 0 :
|
||||
entry->delta->idx.offset == (off_t)-1 ? 0 :
|
||||
/* otherwise double-check written to this
|
||||
* pack, like we do below
|
||||
*/
|
||||
entry->delta->offset ? 1 : 0;
|
||||
entry->delta->idx.offset ? 1 : 0;
|
||||
|
||||
if (!pack_to_stdout)
|
||||
crc32_begin(f);
|
||||
@@ -405,24 +408,24 @@ static unsigned long write_object(struct sha1file *f,
|
||||
z_stream stream;
|
||||
unsigned long maxsize;
|
||||
void *out;
|
||||
buf = read_sha1_file(entry->sha1, &type, &size);
|
||||
if (!buf)
|
||||
die("unable to read %s", sha1_to_hex(entry->sha1));
|
||||
if (size != entry->size)
|
||||
die("object %s size inconsistency (%lu vs %lu)",
|
||||
sha1_to_hex(entry->sha1), size, entry->size);
|
||||
if (usable_delta) {
|
||||
buf = delta_against(buf, size, entry);
|
||||
if (!usable_delta) {
|
||||
buf = read_sha1_file(entry->idx.sha1, &obj_type, &size);
|
||||
if (!buf)
|
||||
die("unable to read %s", sha1_to_hex(entry->idx.sha1));
|
||||
} else if (entry->delta_data) {
|
||||
size = entry->delta_size;
|
||||
obj_type = (allow_ofs_delta && entry->delta->offset) ?
|
||||
buf = entry->delta_data;
|
||||
entry->delta_data = NULL;
|
||||
obj_type = (allow_ofs_delta && entry->delta->idx.offset) ?
|
||||
OBJ_OFS_DELTA : OBJ_REF_DELTA;
|
||||
} else {
|
||||
/*
|
||||
* recover real object type in case
|
||||
* check_object() wanted to re-use a delta,
|
||||
* but we couldn't since base was in previous split pack
|
||||
*/
|
||||
obj_type = type;
|
||||
buf = read_sha1_file(entry->idx.sha1, &type, &size);
|
||||
if (!buf)
|
||||
die("unable to read %s", sha1_to_hex(entry->idx.sha1));
|
||||
buf = delta_against(buf, size, entry);
|
||||
size = entry->delta_size;
|
||||
obj_type = (allow_ofs_delta && entry->delta->idx.offset) ?
|
||||
OBJ_OFS_DELTA : OBJ_REF_DELTA;
|
||||
}
|
||||
/* compress the data to store and put compressed length in datalen */
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
@@ -451,7 +454,7 @@ static unsigned long write_object(struct sha1file *f,
|
||||
* encoding of the relative offset for the delta
|
||||
* base from this object's position in the pack.
|
||||
*/
|
||||
off_t ofs = entry->offset - entry->delta->offset;
|
||||
off_t ofs = entry->idx.offset - entry->delta->idx.offset;
|
||||
unsigned pos = sizeof(dheader) - 1;
|
||||
dheader[pos] = ofs & 127;
|
||||
while (ofs >>= 7)
|
||||
@@ -475,7 +478,7 @@ static unsigned long write_object(struct sha1file *f,
|
||||
return 0;
|
||||
}
|
||||
sha1write(f, header, hdrlen);
|
||||
sha1write(f, entry->delta->sha1, 20);
|
||||
sha1write(f, entry->delta->idx.sha1, 20);
|
||||
hdrlen += 20;
|
||||
} else {
|
||||
if (limit && hdrlen + datalen + 20 >= limit) {
|
||||
@@ -496,7 +499,7 @@ static unsigned long write_object(struct sha1file *f,
|
||||
off_t offset;
|
||||
|
||||
if (entry->delta) {
|
||||
obj_type = (allow_ofs_delta && entry->delta->offset) ?
|
||||
obj_type = (allow_ofs_delta && entry->delta->idx.offset) ?
|
||||
OBJ_OFS_DELTA : OBJ_REF_DELTA;
|
||||
reused_delta++;
|
||||
}
|
||||
@@ -506,11 +509,11 @@ static unsigned long write_object(struct sha1file *f,
|
||||
datalen = revidx[1].offset - offset;
|
||||
if (!pack_to_stdout && p->index_version > 1 &&
|
||||
check_pack_crc(p, &w_curs, offset, datalen, revidx->nr))
|
||||
die("bad packed object CRC for %s", sha1_to_hex(entry->sha1));
|
||||
die("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
|
||||
offset += entry->in_pack_header_size;
|
||||
datalen -= entry->in_pack_header_size;
|
||||
if (obj_type == OBJ_OFS_DELTA) {
|
||||
off_t ofs = entry->offset - entry->delta->offset;
|
||||
off_t ofs = entry->idx.offset - entry->delta->idx.offset;
|
||||
unsigned pos = sizeof(dheader) - 1;
|
||||
dheader[pos] = ofs & 127;
|
||||
while (ofs >>= 7)
|
||||
@@ -524,7 +527,7 @@ static unsigned long write_object(struct sha1file *f,
|
||||
if (limit && hdrlen + 20 + datalen + 20 >= limit)
|
||||
return 0;
|
||||
sha1write(f, header, hdrlen);
|
||||
sha1write(f, entry->delta->sha1, 20);
|
||||
sha1write(f, entry->delta->idx.sha1, 20);
|
||||
hdrlen += 20;
|
||||
} else {
|
||||
if (limit && hdrlen + datalen + 20 >= limit)
|
||||
@@ -534,7 +537,7 @@ static unsigned long write_object(struct sha1file *f,
|
||||
|
||||
if (!pack_to_stdout && p->index_version == 1 &&
|
||||
check_pack_inflate(p, &w_curs, offset, datalen, entry->size))
|
||||
die("corrupt packed object for %s", sha1_to_hex(entry->sha1));
|
||||
die("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
|
||||
copy_pack_data(f, p, &w_curs, offset, datalen);
|
||||
unuse_pack(&w_curs);
|
||||
reused++;
|
||||
@@ -543,7 +546,7 @@ static unsigned long write_object(struct sha1file *f,
|
||||
written_delta++;
|
||||
written++;
|
||||
if (!pack_to_stdout)
|
||||
entry->crc32 = crc32_end(f);
|
||||
entry->idx.crc32 = crc32_end(f);
|
||||
return hdrlen + datalen;
|
||||
}
|
||||
|
||||
@@ -554,7 +557,7 @@ static off_t write_one(struct sha1file *f,
|
||||
unsigned long size;
|
||||
|
||||
/* offset is non zero if object is written already. */
|
||||
if (e->offset || e->preferred_base)
|
||||
if (e->idx.offset || e->preferred_base)
|
||||
return offset;
|
||||
|
||||
/* if we are deltified, write out base object first. */
|
||||
@@ -564,10 +567,10 @@ static off_t write_one(struct sha1file *f,
|
||||
return 0;
|
||||
}
|
||||
|
||||
e->offset = offset;
|
||||
e->idx.offset = offset;
|
||||
size = write_object(f, e, offset);
|
||||
if (!size) {
|
||||
e->offset = 0;
|
||||
e->idx.offset = 0;
|
||||
return 0;
|
||||
}
|
||||
written_list[nr_written++] = e;
|
||||
@@ -584,8 +587,7 @@ static int open_object_dir_tmp(const char *path)
|
||||
return mkstemp(tmpname);
|
||||
}
|
||||
|
||||
/* forward declarations for write_pack_file */
|
||||
static void write_index_file(off_t last_obj_offset, unsigned char *sha1);
|
||||
/* forward declaration for write_pack_file */
|
||||
static int adjust_perm(const char *path, mode_t mode);
|
||||
|
||||
static void write_pack_file(void)
|
||||
@@ -602,6 +604,8 @@ static void write_pack_file(void)
|
||||
written_list = xmalloc(nr_objects * sizeof(struct object_entry *));
|
||||
|
||||
do {
|
||||
unsigned char sha1[20];
|
||||
|
||||
if (pack_to_stdout) {
|
||||
f = sha1fd(1, "<stdout>");
|
||||
} else {
|
||||
@@ -633,23 +637,23 @@ static void write_pack_file(void)
|
||||
* If so, rewrite it like in fast-import
|
||||
*/
|
||||
if (pack_to_stdout || nr_written == nr_remaining) {
|
||||
sha1close(f, pack_file_sha1, 1);
|
||||
sha1close(f, sha1, 1);
|
||||
} else {
|
||||
sha1close(f, pack_file_sha1, 0);
|
||||
fixup_pack_header_footer(f->fd, pack_file_sha1, pack_tmp_name, nr_written);
|
||||
sha1close(f, sha1, 0);
|
||||
fixup_pack_header_footer(f->fd, sha1, pack_tmp_name, nr_written);
|
||||
close(f->fd);
|
||||
}
|
||||
|
||||
if (!pack_to_stdout) {
|
||||
unsigned char object_list_sha1[20];
|
||||
mode_t mode = umask(0);
|
||||
|
||||
umask(mode);
|
||||
mode = 0444 & ~mode;
|
||||
|
||||
write_index_file(last_obj_offset, object_list_sha1);
|
||||
idx_tmp_name = write_idx_file(NULL,
|
||||
(struct pack_idx_entry **) written_list, nr_written, sha1);
|
||||
snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
|
||||
base_name, sha1_to_hex(object_list_sha1));
|
||||
base_name, sha1_to_hex(sha1));
|
||||
if (adjust_perm(pack_tmp_name, mode))
|
||||
die("unable to make temporary pack file readable: %s",
|
||||
strerror(errno));
|
||||
@@ -657,19 +661,19 @@ static void write_pack_file(void)
|
||||
die("unable to rename temporary pack file: %s",
|
||||
strerror(errno));
|
||||
snprintf(tmpname, sizeof(tmpname), "%s-%s.idx",
|
||||
base_name, sha1_to_hex(object_list_sha1));
|
||||
base_name, sha1_to_hex(sha1));
|
||||
if (adjust_perm(idx_tmp_name, mode))
|
||||
die("unable to make temporary index file readable: %s",
|
||||
strerror(errno));
|
||||
if (rename(idx_tmp_name, tmpname))
|
||||
die("unable to rename temporary index file: %s",
|
||||
strerror(errno));
|
||||
puts(sha1_to_hex(object_list_sha1));
|
||||
puts(sha1_to_hex(sha1));
|
||||
}
|
||||
|
||||
/* mark written objects as written to previous pack */
|
||||
for (j = 0; j < nr_written; j++) {
|
||||
written_list[j]->offset = (off_t)-1;
|
||||
written_list[j]->idx.offset = (off_t)-1;
|
||||
}
|
||||
nr_remaining -= nr_written;
|
||||
} while (nr_remaining && i < nr_objects);
|
||||
@@ -687,129 +691,12 @@ static void write_pack_file(void)
|
||||
*/
|
||||
for (j = 0; i < nr_objects; i++) {
|
||||
struct object_entry *e = objects + i;
|
||||
j += !e->offset && !e->preferred_base;
|
||||
j += !e->idx.offset && !e->preferred_base;
|
||||
}
|
||||
if (j)
|
||||
die("wrote %u objects as expected but %u unwritten", written, j);
|
||||
}
|
||||
|
||||
static int sha1_sort(const void *_a, const void *_b)
|
||||
{
|
||||
const struct object_entry *a = *(struct object_entry **)_a;
|
||||
const struct object_entry *b = *(struct object_entry **)_b;
|
||||
return hashcmp(a->sha1, b->sha1);
|
||||
}
|
||||
|
||||
static uint32_t index_default_version = 1;
|
||||
static uint32_t index_off32_limit = 0x7fffffff;
|
||||
|
||||
static void write_index_file(off_t last_obj_offset, unsigned char *sha1)
|
||||
{
|
||||
struct sha1file *f;
|
||||
struct object_entry **sorted_by_sha, **list, **last;
|
||||
uint32_t array[256];
|
||||
uint32_t i, index_version;
|
||||
SHA_CTX ctx;
|
||||
|
||||
int fd = open_object_dir_tmp("tmp_idx_XXXXXX");
|
||||
if (fd < 0)
|
||||
die("unable to create %s: %s\n", tmpname, strerror(errno));
|
||||
idx_tmp_name = xstrdup(tmpname);
|
||||
f = sha1fd(fd, idx_tmp_name);
|
||||
|
||||
if (nr_written) {
|
||||
sorted_by_sha = written_list;
|
||||
qsort(sorted_by_sha, nr_written, sizeof(*sorted_by_sha), sha1_sort);
|
||||
list = sorted_by_sha;
|
||||
last = sorted_by_sha + nr_written;
|
||||
} else
|
||||
sorted_by_sha = list = last = NULL;
|
||||
|
||||
/* if last object's offset is >= 2^31 we should use index V2 */
|
||||
index_version = (last_obj_offset >> 31) ? 2 : index_default_version;
|
||||
|
||||
/* index versions 2 and above need a header */
|
||||
if (index_version >= 2) {
|
||||
struct pack_idx_header hdr;
|
||||
hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
|
||||
hdr.idx_version = htonl(index_version);
|
||||
sha1write(f, &hdr, sizeof(hdr));
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the first-level table (the list is sorted,
|
||||
* but we use a 256-entry lookup to be able to avoid
|
||||
* having to do eight extra binary search iterations).
|
||||
*/
|
||||
for (i = 0; i < 256; i++) {
|
||||
struct object_entry **next = list;
|
||||
while (next < last) {
|
||||
struct object_entry *entry = *next;
|
||||
if (entry->sha1[0] != i)
|
||||
break;
|
||||
next++;
|
||||
}
|
||||
array[i] = htonl(next - sorted_by_sha);
|
||||
list = next;
|
||||
}
|
||||
sha1write(f, array, 256 * 4);
|
||||
|
||||
/* Compute the SHA1 hash of sorted object names. */
|
||||
SHA1_Init(&ctx);
|
||||
|
||||
/* Write the actual SHA1 entries. */
|
||||
list = sorted_by_sha;
|
||||
for (i = 0; i < nr_written; i++) {
|
||||
struct object_entry *entry = *list++;
|
||||
if (index_version < 2) {
|
||||
uint32_t offset = htonl(entry->offset);
|
||||
sha1write(f, &offset, 4);
|
||||
}
|
||||
sha1write(f, entry->sha1, 20);
|
||||
SHA1_Update(&ctx, entry->sha1, 20);
|
||||
}
|
||||
|
||||
if (index_version >= 2) {
|
||||
unsigned int nr_large_offset = 0;
|
||||
|
||||
/* write the crc32 table */
|
||||
list = sorted_by_sha;
|
||||
for (i = 0; i < nr_written; i++) {
|
||||
struct object_entry *entry = *list++;
|
||||
uint32_t crc32_val = htonl(entry->crc32);
|
||||
sha1write(f, &crc32_val, 4);
|
||||
}
|
||||
|
||||
/* write the 32-bit offset table */
|
||||
list = sorted_by_sha;
|
||||
for (i = 0; i < nr_written; i++) {
|
||||
struct object_entry *entry = *list++;
|
||||
uint32_t offset = (entry->offset <= index_off32_limit) ?
|
||||
entry->offset : (0x80000000 | nr_large_offset++);
|
||||
offset = htonl(offset);
|
||||
sha1write(f, &offset, 4);
|
||||
}
|
||||
|
||||
/* write the large offset table */
|
||||
list = sorted_by_sha;
|
||||
while (nr_large_offset) {
|
||||
struct object_entry *entry = *list++;
|
||||
uint64_t offset = entry->offset;
|
||||
if (offset > index_off32_limit) {
|
||||
uint32_t split[2];
|
||||
split[0] = htonl(offset >> 32);
|
||||
split[1] = htonl(offset & 0xffffffff);
|
||||
sha1write(f, split, 8);
|
||||
nr_large_offset--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sha1write(f, pack_file_sha1, 20);
|
||||
sha1close(f, NULL, 1);
|
||||
SHA1_Final(sha1, &ctx);
|
||||
}
|
||||
|
||||
static int locate_object_entry_hash(const unsigned char *sha1)
|
||||
{
|
||||
int i;
|
||||
@@ -817,7 +704,7 @@ static int locate_object_entry_hash(const unsigned char *sha1)
|
||||
memcpy(&ui, sha1, sizeof(unsigned int));
|
||||
i = ui % object_ix_hashsz;
|
||||
while (0 < object_ix[i]) {
|
||||
if (!hashcmp(sha1, objects[object_ix[i] - 1].sha1))
|
||||
if (!hashcmp(sha1, objects[object_ix[i] - 1].idx.sha1))
|
||||
return i;
|
||||
if (++i == object_ix_hashsz)
|
||||
i = 0;
|
||||
@@ -849,7 +736,7 @@ static void rehash_objects(void)
|
||||
object_ix = xrealloc(object_ix, sizeof(int) * object_ix_hashsz);
|
||||
memset(object_ix, 0, sizeof(int) * object_ix_hashsz);
|
||||
for (i = 0, oe = objects; i < nr_objects; i++, oe++) {
|
||||
int ix = locate_object_entry_hash(oe->sha1);
|
||||
int ix = locate_object_entry_hash(oe->idx.sha1);
|
||||
if (0 <= ix)
|
||||
continue;
|
||||
ix = -1 - ix;
|
||||
@@ -943,7 +830,7 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
|
||||
|
||||
entry = objects + nr_objects++;
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
hashcpy(entry->sha1, sha1);
|
||||
hashcpy(entry->idx.sha1, sha1);
|
||||
entry->hash = hash;
|
||||
if (type)
|
||||
entry->type = type;
|
||||
@@ -1263,13 +1150,13 @@ static void check_object(struct object_entry *entry)
|
||||
ofs += 1;
|
||||
if (!ofs || MSB(ofs, 7))
|
||||
die("delta base offset overflow in pack for %s",
|
||||
sha1_to_hex(entry->sha1));
|
||||
sha1_to_hex(entry->idx.sha1));
|
||||
c = buf[used_0++];
|
||||
ofs = (ofs << 7) + (c & 127);
|
||||
}
|
||||
if (ofs >= entry->in_pack_offset)
|
||||
die("delta base offset out of bound for %s",
|
||||
sha1_to_hex(entry->sha1));
|
||||
sha1_to_hex(entry->idx.sha1));
|
||||
ofs = entry->in_pack_offset - ofs;
|
||||
if (!no_reuse_delta && !entry->preferred_base)
|
||||
base_ref = find_packed_object_name(p, ofs);
|
||||
@@ -1316,10 +1203,10 @@ static void check_object(struct object_entry *entry)
|
||||
unuse_pack(&w_curs);
|
||||
}
|
||||
|
||||
entry->type = sha1_object_info(entry->sha1, &entry->size);
|
||||
entry->type = sha1_object_info(entry->idx.sha1, &entry->size);
|
||||
if (entry->type < 0)
|
||||
die("unable to get type of object %s",
|
||||
sha1_to_hex(entry->sha1));
|
||||
sha1_to_hex(entry->idx.sha1));
|
||||
}
|
||||
|
||||
static int pack_offset_sort(const void *_a, const void *_b)
|
||||
@@ -1329,7 +1216,7 @@ static int pack_offset_sort(const void *_a, const void *_b)
|
||||
|
||||
/* avoid filesystem trashing with loose objects */
|
||||
if (!a->in_pack && !b->in_pack)
|
||||
return hashcmp(a->sha1, b->sha1);
|
||||
return hashcmp(a->idx.sha1, b->idx.sha1);
|
||||
|
||||
if (a->in_pack < b->in_pack)
|
||||
return -1;
|
||||
@@ -1385,6 +1272,23 @@ struct unpacked {
|
||||
struct delta_index *index;
|
||||
};
|
||||
|
||||
static int delta_cacheable(struct unpacked *trg, struct unpacked *src,
|
||||
unsigned long src_size, unsigned long trg_size,
|
||||
unsigned long delta_size)
|
||||
{
|
||||
if (max_delta_cache_size && delta_cache_size + delta_size > max_delta_cache_size)
|
||||
return 0;
|
||||
|
||||
if (delta_size < cache_max_small_delta_size)
|
||||
return 1;
|
||||
|
||||
/* cache delta, if objects are large enough compared to delta size */
|
||||
if ((src_size >> 20) + (trg_size >> 21) > (delta_size >> 10))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We search for deltas _backwards_ in a list sorted by type and
|
||||
* by size, so that we see progressively smaller and smaller files.
|
||||
@@ -1441,31 +1345,45 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
|
||||
|
||||
/* Load data if not already done */
|
||||
if (!trg->data) {
|
||||
trg->data = read_sha1_file(trg_entry->sha1, &type, &sz);
|
||||
trg->data = read_sha1_file(trg_entry->idx.sha1, &type, &sz);
|
||||
if (sz != trg_size)
|
||||
die("object %s inconsistent object length (%lu vs %lu)",
|
||||
sha1_to_hex(trg_entry->sha1), sz, trg_size);
|
||||
sha1_to_hex(trg_entry->idx.sha1), sz, trg_size);
|
||||
}
|
||||
if (!src->data) {
|
||||
src->data = read_sha1_file(src_entry->sha1, &type, &sz);
|
||||
src->data = read_sha1_file(src_entry->idx.sha1, &type, &sz);
|
||||
if (sz != src_size)
|
||||
die("object %s inconsistent object length (%lu vs %lu)",
|
||||
sha1_to_hex(src_entry->sha1), sz, src_size);
|
||||
sha1_to_hex(src_entry->idx.sha1), sz, src_size);
|
||||
}
|
||||
if (!src->index) {
|
||||
src->index = create_delta_index(src->data, src_size);
|
||||
if (!src->index)
|
||||
die("out of memory");
|
||||
if (!src->index) {
|
||||
static int warned = 0;
|
||||
if (!warned++)
|
||||
warning("suboptimal pack - out of memory");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
delta_buf = create_delta(src->index, trg->data, trg_size, &delta_size, max_size);
|
||||
if (!delta_buf)
|
||||
return 0;
|
||||
|
||||
if (trg_entry->delta_data) {
|
||||
delta_cache_size -= trg_entry->delta_size;
|
||||
free(trg_entry->delta_data);
|
||||
}
|
||||
trg_entry->delta_data = 0;
|
||||
trg_entry->delta = src_entry;
|
||||
trg_entry->delta_size = delta_size;
|
||||
trg_entry->depth = src_entry->depth + 1;
|
||||
free(delta_buf);
|
||||
|
||||
if (delta_cacheable(src, trg, src_size, trg_size, delta_size)) {
|
||||
trg_entry->delta_data = xrealloc(delta_buf, delta_size);
|
||||
delta_cache_size += trg_entry->delta_size;
|
||||
} else
|
||||
free(delta_buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1611,6 +1529,14 @@ static int git_pack_config(const char *k, const char *v)
|
||||
pack_compression_seen = 1;
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(k, "pack.deltacachesize")) {
|
||||
max_delta_cache_size = git_config_int(k, v);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(k, "pack.deltacachelimit")) {
|
||||
cache_max_small_delta_size = git_config_int(k, v);
|
||||
return 0;
|
||||
}
|
||||
return git_default_config(k, v);
|
||||
}
|
||||
|
||||
@@ -1825,12 +1751,12 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
if (!prefixcmp(arg, "--index-version=")) {
|
||||
char *c;
|
||||
index_default_version = strtoul(arg + 16, &c, 10);
|
||||
if (index_default_version > 2)
|
||||
pack_idx_default_version = strtoul(arg + 16, &c, 10);
|
||||
if (pack_idx_default_version > 2)
|
||||
die("bad %s", arg);
|
||||
if (*c == ',')
|
||||
index_off32_limit = strtoul(c+1, &c, 0);
|
||||
if (*c || index_off32_limit & 0x80000000)
|
||||
pack_idx_off32_limit = strtoul(c+1, &c, 0);
|
||||
if (*c || pack_idx_off32_limit & 0x80000000)
|
||||
die("bad %s", arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
322
builtin-push.c
322
builtin-push.c
@@ -5,17 +5,13 @@
|
||||
#include "refs.h"
|
||||
#include "run-command.h"
|
||||
#include "builtin.h"
|
||||
|
||||
#define MAX_URI (16)
|
||||
#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 int all, tags, force, thin = 1, verbose;
|
||||
static int all, force, thin = 1, verbose;
|
||||
static const char *receivepack;
|
||||
|
||||
#define BUF_SIZE (2084)
|
||||
static char buffer[BUF_SIZE];
|
||||
|
||||
static const char **refspec;
|
||||
static int refspec_nr;
|
||||
|
||||
@@ -27,285 +23,47 @@ static void add_refspec(const char *ref)
|
||||
refspec_nr = nr;
|
||||
}
|
||||
|
||||
static int expand_one_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
|
||||
{
|
||||
/* Ignore the "refs/" at the beginning of the refname */
|
||||
ref += 5;
|
||||
|
||||
if (!prefixcmp(ref, "tags/"))
|
||||
add_refspec(xstrdup(ref));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void expand_refspecs(void)
|
||||
{
|
||||
if (all) {
|
||||
if (refspec_nr)
|
||||
die("cannot mix '--all' and a refspec");
|
||||
|
||||
/*
|
||||
* No need to expand "--all" - we'll just use
|
||||
* the "--all" flag to send-pack
|
||||
*/
|
||||
return;
|
||||
}
|
||||
if (!tags)
|
||||
return;
|
||||
for_each_ref(expand_one_ref, NULL);
|
||||
}
|
||||
|
||||
struct wildcard_cb {
|
||||
const char *from_prefix;
|
||||
int from_prefix_len;
|
||||
const char *to_prefix;
|
||||
int to_prefix_len;
|
||||
int force;
|
||||
};
|
||||
|
||||
static int expand_wildcard_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
|
||||
{
|
||||
struct wildcard_cb *cb = cb_data;
|
||||
int len = strlen(ref);
|
||||
char *expanded, *newref;
|
||||
|
||||
if (len < cb->from_prefix_len ||
|
||||
memcmp(cb->from_prefix, ref, cb->from_prefix_len))
|
||||
return 0;
|
||||
expanded = xmalloc(len * 2 + cb->force +
|
||||
(cb->to_prefix_len - cb->from_prefix_len) + 2);
|
||||
newref = expanded + cb->force;
|
||||
if (cb->force)
|
||||
expanded[0] = '+';
|
||||
memcpy(newref, ref, len);
|
||||
newref[len] = ':';
|
||||
memcpy(newref + len + 1, cb->to_prefix, cb->to_prefix_len);
|
||||
strcpy(newref + len + 1 + cb->to_prefix_len,
|
||||
ref + cb->from_prefix_len);
|
||||
add_refspec(expanded);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wildcard_ref(const char *ref)
|
||||
{
|
||||
int len;
|
||||
const char *colon;
|
||||
struct wildcard_cb cb;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
if (ref[0] == '+') {
|
||||
cb.force = 1;
|
||||
ref++;
|
||||
}
|
||||
len = strlen(ref);
|
||||
colon = strchr(ref, ':');
|
||||
if (! (colon && ref < colon &&
|
||||
colon[-2] == '/' && colon[-1] == '*' &&
|
||||
/* "<mine>/<asterisk>:<yours>/<asterisk>" is at least 7 bytes */
|
||||
7 <= len &&
|
||||
ref[len-2] == '/' && ref[len-1] == '*') )
|
||||
return 0 ;
|
||||
cb.from_prefix = ref;
|
||||
cb.from_prefix_len = colon - ref - 1;
|
||||
cb.to_prefix = colon + 1;
|
||||
cb.to_prefix_len = len - (colon - ref) - 2;
|
||||
for_each_ref(expand_wildcard_ref, &cb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void set_refspecs(const char **refs, int nr)
|
||||
{
|
||||
if (nr) {
|
||||
int i;
|
||||
for (i = 0; i < nr; i++) {
|
||||
const char *ref = refs[i];
|
||||
if (!strcmp("tag", ref)) {
|
||||
char *tag;
|
||||
int len;
|
||||
if (nr <= ++i)
|
||||
die("tag shorthand without <tag>");
|
||||
len = strlen(refs[i]) + 11;
|
||||
tag = xmalloc(len);
|
||||
strcpy(tag, "refs/tags/");
|
||||
strcat(tag, refs[i]);
|
||||
ref = tag;
|
||||
}
|
||||
else if (wildcard_ref(ref))
|
||||
continue;
|
||||
add_refspec(ref);
|
||||
int i;
|
||||
for (i = 0; i < nr; i++) {
|
||||
const char *ref = refs[i];
|
||||
if (!strcmp("tag", ref)) {
|
||||
char *tag;
|
||||
int len;
|
||||
if (nr <= ++i)
|
||||
die("tag shorthand without <tag>");
|
||||
len = strlen(refs[i]) + 11;
|
||||
tag = xmalloc(len);
|
||||
strcpy(tag, "refs/tags/");
|
||||
strcat(tag, refs[i]);
|
||||
ref = tag;
|
||||
}
|
||||
add_refspec(ref);
|
||||
}
|
||||
expand_refspecs();
|
||||
}
|
||||
|
||||
static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
|
||||
{
|
||||
int n = 0;
|
||||
FILE *f = fopen(git_path("remotes/%s", repo), "r");
|
||||
int has_explicit_refspec = refspec_nr || all || tags;
|
||||
|
||||
if (!f)
|
||||
return -1;
|
||||
while (fgets(buffer, BUF_SIZE, f)) {
|
||||
int is_refspec;
|
||||
char *s, *p;
|
||||
|
||||
if (!prefixcmp(buffer, "URL:")) {
|
||||
is_refspec = 0;
|
||||
s = buffer + 4;
|
||||
} else if (!prefixcmp(buffer, "Push:")) {
|
||||
is_refspec = 1;
|
||||
s = buffer + 5;
|
||||
} else
|
||||
continue;
|
||||
|
||||
/* Remove whitespace at the head.. */
|
||||
while (isspace(*s))
|
||||
s++;
|
||||
if (!*s)
|
||||
continue;
|
||||
|
||||
/* ..and at the end */
|
||||
p = s + strlen(s);
|
||||
while (isspace(p[-1]))
|
||||
*--p = 0;
|
||||
|
||||
if (!is_refspec) {
|
||||
if (n < MAX_URI)
|
||||
uri[n++] = xstrdup(s);
|
||||
else
|
||||
error("more than %d URL's specified, ignoring the rest", MAX_URI);
|
||||
}
|
||||
else if (is_refspec && !has_explicit_refspec) {
|
||||
if (!wildcard_ref(s))
|
||||
add_refspec(xstrdup(s));
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
if (!n)
|
||||
die("remote '%s' has no URL", repo);
|
||||
return n;
|
||||
}
|
||||
|
||||
static const char **config_uri;
|
||||
static const char *config_repo;
|
||||
static int config_repo_len;
|
||||
static int config_current_uri;
|
||||
static int config_get_refspecs;
|
||||
static int config_get_receivepack;
|
||||
|
||||
static int get_remote_config(const char* key, const char* value)
|
||||
{
|
||||
if (!prefixcmp(key, "remote.") &&
|
||||
!strncmp(key + 7, config_repo, config_repo_len)) {
|
||||
if (!strcmp(key + 7 + config_repo_len, ".url")) {
|
||||
if (config_current_uri < MAX_URI)
|
||||
config_uri[config_current_uri++] = xstrdup(value);
|
||||
else
|
||||
error("more than %d URL's specified, ignoring the rest", MAX_URI);
|
||||
}
|
||||
else if (config_get_refspecs &&
|
||||
!strcmp(key + 7 + config_repo_len, ".push")) {
|
||||
if (!wildcard_ref(value))
|
||||
add_refspec(xstrdup(value));
|
||||
}
|
||||
else if (config_get_receivepack &&
|
||||
!strcmp(key + 7 + config_repo_len, ".receivepack")) {
|
||||
if (!receivepack) {
|
||||
char *rp = xmalloc(strlen(value) + 16);
|
||||
sprintf(rp, "--receive-pack=%s", value);
|
||||
receivepack = rp;
|
||||
} else
|
||||
error("more than one receivepack given, using the first");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI])
|
||||
{
|
||||
config_repo_len = strlen(repo);
|
||||
config_repo = repo;
|
||||
config_current_uri = 0;
|
||||
config_uri = uri;
|
||||
config_get_refspecs = !(refspec_nr || all || tags);
|
||||
config_get_receivepack = (receivepack == NULL);
|
||||
|
||||
git_config(get_remote_config);
|
||||
return config_current_uri;
|
||||
}
|
||||
|
||||
static int get_branches_uri(const char *repo, const char *uri[MAX_URI])
|
||||
{
|
||||
const char *slash = strchr(repo, '/');
|
||||
int n = slash ? slash - repo : 1000;
|
||||
FILE *f = fopen(git_path("branches/%.*s", n, repo), "r");
|
||||
char *s, *p;
|
||||
int len;
|
||||
|
||||
if (!f)
|
||||
return 0;
|
||||
s = fgets(buffer, BUF_SIZE, f);
|
||||
fclose(f);
|
||||
if (!s)
|
||||
return 0;
|
||||
while (isspace(*s))
|
||||
s++;
|
||||
if (!*s)
|
||||
return 0;
|
||||
p = s + strlen(s);
|
||||
while (isspace(p[-1]))
|
||||
*--p = 0;
|
||||
len = p - s;
|
||||
if (slash)
|
||||
len += strlen(slash);
|
||||
p = xmalloc(len + 1);
|
||||
strcpy(p, s);
|
||||
if (slash)
|
||||
strcat(p, slash);
|
||||
uri[0] = p;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read remotes and branches file, fill the push target URI
|
||||
* list. If there is no command line refspecs, read Push: lines
|
||||
* to set up the *refspec list as well.
|
||||
* return the number of push target URIs
|
||||
*/
|
||||
static int read_config(const char *repo, const char *uri[MAX_URI])
|
||||
{
|
||||
int n;
|
||||
|
||||
if (*repo != '/') {
|
||||
n = get_remotes_uri(repo, uri);
|
||||
if (n > 0)
|
||||
return n;
|
||||
|
||||
n = get_config_remotes_uri(repo, uri);
|
||||
if (n > 0)
|
||||
return n;
|
||||
|
||||
n = get_branches_uri(repo, uri);
|
||||
if (n > 0)
|
||||
return n;
|
||||
}
|
||||
|
||||
uri[0] = repo;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int do_push(const char *repo)
|
||||
{
|
||||
const char *uri[MAX_URI];
|
||||
int i, n, errs;
|
||||
int i, errs;
|
||||
int common_argc;
|
||||
const char **argv;
|
||||
int argc;
|
||||
struct remote *remote = remote_get(repo);
|
||||
|
||||
n = read_config(repo, uri);
|
||||
if (n <= 0)
|
||||
if (!remote)
|
||||
die("bad repository '%s'", repo);
|
||||
|
||||
if (remote->receivepack) {
|
||||
char *rp = xmalloc(strlen(remote->receivepack) + 16);
|
||||
sprintf(rp, "--receive-pack=%s", remote->receivepack);
|
||||
receivepack = rp;
|
||||
}
|
||||
if (!refspec && !all && remote->push_refspec_nr) {
|
||||
refspec = remote->push_refspec;
|
||||
refspec_nr = remote->push_refspec_nr;
|
||||
}
|
||||
|
||||
argv = xmalloc((refspec_nr + 10) * sizeof(char *));
|
||||
argv[0] = "dummy-send-pack";
|
||||
argc = 1;
|
||||
@@ -318,18 +76,23 @@ static int do_push(const char *repo)
|
||||
common_argc = argc;
|
||||
|
||||
errs = 0;
|
||||
for (i = 0; i < n; i++) {
|
||||
for (i = 0; i < remote->uri_nr; i++) {
|
||||
int err;
|
||||
int dest_argc = common_argc;
|
||||
int dest_refspec_nr = refspec_nr;
|
||||
const char **dest_refspec = refspec;
|
||||
const char *dest = uri[i];
|
||||
const char *dest = remote->uri[i];
|
||||
const char *sender = "send-pack";
|
||||
if (!prefixcmp(dest, "http://") ||
|
||||
!prefixcmp(dest, "https://"))
|
||||
sender = "http-push";
|
||||
else if (thin)
|
||||
argv[dest_argc++] = "--thin";
|
||||
else {
|
||||
char *rem = xmalloc(strlen(remote->name) + 10);
|
||||
sprintf(rem, "--remote=%s", remote->name);
|
||||
argv[dest_argc++] = rem;
|
||||
if (thin)
|
||||
argv[dest_argc++] = "--thin";
|
||||
}
|
||||
argv[0] = sender;
|
||||
argv[dest_argc++] = dest;
|
||||
while (dest_refspec_nr--)
|
||||
@@ -341,7 +104,7 @@ static int do_push(const char *repo)
|
||||
if (!err)
|
||||
continue;
|
||||
|
||||
error("failed to push to '%s'", uri[i]);
|
||||
error("failed to push to '%s'", remote->uri[i]);
|
||||
switch (err) {
|
||||
case -ERR_RUN_COMMAND_FORK:
|
||||
error("unable to fork for %s", sender);
|
||||
@@ -362,7 +125,7 @@ static int do_push(const char *repo)
|
||||
int cmd_push(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i;
|
||||
const char *repo = "origin"; /* default repository */
|
||||
const char *repo = NULL; /* default repository */
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
@@ -385,7 +148,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--tags")) {
|
||||
tags = 1;
|
||||
add_refspec("refs/tags/*");
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
|
||||
@@ -411,5 +174,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)
|
||||
usage(push_usage);
|
||||
}
|
||||
set_refspecs(argv + i, argc - i);
|
||||
if (all && refspec)
|
||||
usage(push_usage);
|
||||
|
||||
return do_push(repo);
|
||||
}
|
||||
|
||||
5
cache.h
5
cache.h
@@ -468,8 +468,6 @@ struct ref {
|
||||
extern pid_t git_connect(int fd[2], char *url, const char *prog, int flags);
|
||||
extern int finish_connect(pid_t pid);
|
||||
extern int path_match(const char *path, int nr, char **match);
|
||||
extern int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
|
||||
int nr_refspec, char **refspec, int all);
|
||||
extern int get_ack(int fd, unsigned char *result_sha1);
|
||||
extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags);
|
||||
extern int server_supports(const char *feature);
|
||||
@@ -486,10 +484,11 @@ extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
|
||||
struct packed_git *packs);
|
||||
|
||||
extern void pack_report(void);
|
||||
extern int open_pack_index(struct packed_git *);
|
||||
extern unsigned char* use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
|
||||
extern void unuse_pack(struct pack_window **);
|
||||
extern struct packed_git *add_packed_git(const char *, int, int);
|
||||
extern const unsigned char *nth_packed_object_sha1(const struct packed_git *, uint32_t);
|
||||
extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t);
|
||||
extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
|
||||
extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
|
||||
extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
|
||||
|
||||
240
connect.c
240
connect.c
@@ -4,6 +4,7 @@
|
||||
#include "quote.h"
|
||||
#include "refs.h"
|
||||
#include "run-command.h"
|
||||
#include "remote.h"
|
||||
#include "spawn-pipe.h"
|
||||
|
||||
static char *server_capabilities;
|
||||
@@ -129,245 +130,6 @@ int path_match(const char *path, int nr, char **match)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct refspec {
|
||||
char *src;
|
||||
char *dst;
|
||||
char force;
|
||||
};
|
||||
|
||||
/*
|
||||
* A:B means fast forward remote B with local A.
|
||||
* +A:B means overwrite remote B with local A.
|
||||
* +A is a shorthand for +A:A.
|
||||
* A is a shorthand for A:A.
|
||||
* :B means delete remote B.
|
||||
*/
|
||||
static struct refspec *parse_ref_spec(int nr_refspec, char **refspec)
|
||||
{
|
||||
int i;
|
||||
struct refspec *rs = xcalloc(sizeof(*rs), (nr_refspec + 1));
|
||||
for (i = 0; i < nr_refspec; i++) {
|
||||
char *sp, *dp, *ep;
|
||||
sp = refspec[i];
|
||||
if (*sp == '+') {
|
||||
rs[i].force = 1;
|
||||
sp++;
|
||||
}
|
||||
ep = strchr(sp, ':');
|
||||
if (ep) {
|
||||
dp = ep + 1;
|
||||
*ep = 0;
|
||||
}
|
||||
else
|
||||
dp = sp;
|
||||
rs[i].src = sp;
|
||||
rs[i].dst = dp;
|
||||
}
|
||||
rs[nr_refspec].src = rs[nr_refspec].dst = NULL;
|
||||
return rs;
|
||||
}
|
||||
|
||||
static int count_refspec_match(const char *pattern,
|
||||
struct ref *refs,
|
||||
struct ref **matched_ref)
|
||||
{
|
||||
int patlen = strlen(pattern);
|
||||
struct ref *matched_weak = NULL;
|
||||
struct ref *matched = NULL;
|
||||
int weak_match = 0;
|
||||
int match = 0;
|
||||
|
||||
for (weak_match = match = 0; refs; refs = refs->next) {
|
||||
char *name = refs->name;
|
||||
int namelen = strlen(name);
|
||||
int weak_match;
|
||||
|
||||
if (namelen < patlen ||
|
||||
memcmp(name + namelen - patlen, pattern, patlen))
|
||||
continue;
|
||||
if (namelen != patlen && name[namelen - patlen - 1] != '/')
|
||||
continue;
|
||||
|
||||
/* A match is "weak" if it is with refs outside
|
||||
* heads or tags, and did not specify the pattern
|
||||
* in full (e.g. "refs/remotes/origin/master") or at
|
||||
* least from the toplevel (e.g. "remotes/origin/master");
|
||||
* otherwise "git push $URL master" would result in
|
||||
* ambiguity between remotes/origin/master and heads/master
|
||||
* at the remote site.
|
||||
*/
|
||||
if (namelen != patlen &&
|
||||
patlen != namelen - 5 &&
|
||||
prefixcmp(name, "refs/heads/") &&
|
||||
prefixcmp(name, "refs/tags/")) {
|
||||
/* We want to catch the case where only weak
|
||||
* matches are found and there are multiple
|
||||
* matches, and where more than one strong
|
||||
* matches are found, as ambiguous. One
|
||||
* strong match with zero or more weak matches
|
||||
* are acceptable as a unique match.
|
||||
*/
|
||||
matched_weak = refs;
|
||||
weak_match++;
|
||||
}
|
||||
else {
|
||||
matched = refs;
|
||||
match++;
|
||||
}
|
||||
}
|
||||
if (!matched) {
|
||||
*matched_ref = matched_weak;
|
||||
return weak_match;
|
||||
}
|
||||
else {
|
||||
*matched_ref = matched;
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
||||
static void link_dst_tail(struct ref *ref, struct ref ***tail)
|
||||
{
|
||||
**tail = ref;
|
||||
*tail = &ref->next;
|
||||
**tail = NULL;
|
||||
}
|
||||
|
||||
static struct ref *try_explicit_object_name(const char *name)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
struct ref *ref;
|
||||
int len;
|
||||
|
||||
if (!*name) {
|
||||
ref = xcalloc(1, sizeof(*ref) + 20);
|
||||
strcpy(ref->name, "(delete)");
|
||||
hashclr(ref->new_sha1);
|
||||
return ref;
|
||||
}
|
||||
if (get_sha1(name, sha1))
|
||||
return NULL;
|
||||
len = strlen(name) + 1;
|
||||
ref = xcalloc(1, sizeof(*ref) + len);
|
||||
memcpy(ref->name, name, len);
|
||||
hashcpy(ref->new_sha1, sha1);
|
||||
return ref;
|
||||
}
|
||||
|
||||
static int match_explicit_refs(struct ref *src, struct ref *dst,
|
||||
struct ref ***dst_tail, struct refspec *rs)
|
||||
{
|
||||
int i, errs;
|
||||
for (i = errs = 0; rs[i].src; i++) {
|
||||
struct ref *matched_src, *matched_dst;
|
||||
|
||||
matched_src = matched_dst = NULL;
|
||||
switch (count_refspec_match(rs[i].src, src, &matched_src)) {
|
||||
case 1:
|
||||
break;
|
||||
case 0:
|
||||
/* The source could be in the get_sha1() format
|
||||
* not a reference name. :refs/other is a
|
||||
* way to delete 'other' ref at the remote end.
|
||||
*/
|
||||
matched_src = try_explicit_object_name(rs[i].src);
|
||||
if (matched_src)
|
||||
break;
|
||||
errs = 1;
|
||||
error("src refspec %s does not match any.",
|
||||
rs[i].src);
|
||||
break;
|
||||
default:
|
||||
errs = 1;
|
||||
error("src refspec %s matches more than one.",
|
||||
rs[i].src);
|
||||
break;
|
||||
}
|
||||
switch (count_refspec_match(rs[i].dst, dst, &matched_dst)) {
|
||||
case 1:
|
||||
break;
|
||||
case 0:
|
||||
if (!memcmp(rs[i].dst, "refs/", 5)) {
|
||||
int len = strlen(rs[i].dst) + 1;
|
||||
matched_dst = xcalloc(1, sizeof(*dst) + len);
|
||||
memcpy(matched_dst->name, rs[i].dst, len);
|
||||
link_dst_tail(matched_dst, dst_tail);
|
||||
}
|
||||
else if (!strcmp(rs[i].src, rs[i].dst) &&
|
||||
matched_src) {
|
||||
/* pushing "master:master" when
|
||||
* remote does not have master yet.
|
||||
*/
|
||||
int len = strlen(matched_src->name) + 1;
|
||||
matched_dst = xcalloc(1, sizeof(*dst) + len);
|
||||
memcpy(matched_dst->name, matched_src->name,
|
||||
len);
|
||||
link_dst_tail(matched_dst, dst_tail);
|
||||
}
|
||||
else {
|
||||
errs = 1;
|
||||
error("dst refspec %s does not match any "
|
||||
"existing ref on the remote and does "
|
||||
"not start with refs/.", rs[i].dst);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
errs = 1;
|
||||
error("dst refspec %s matches more than one.",
|
||||
rs[i].dst);
|
||||
break;
|
||||
}
|
||||
if (errs)
|
||||
continue;
|
||||
if (matched_dst->peer_ref) {
|
||||
errs = 1;
|
||||
error("dst ref %s receives from more than one src.",
|
||||
matched_dst->name);
|
||||
}
|
||||
else {
|
||||
matched_dst->peer_ref = matched_src;
|
||||
matched_dst->force = rs[i].force;
|
||||
}
|
||||
}
|
||||
return -errs;
|
||||
}
|
||||
|
||||
static struct ref *find_ref_by_name(struct ref *list, const char *name)
|
||||
{
|
||||
for ( ; list; list = list->next)
|
||||
if (!strcmp(list->name, name))
|
||||
return list;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
|
||||
int nr_refspec, char **refspec, int all)
|
||||
{
|
||||
struct refspec *rs = parse_ref_spec(nr_refspec, refspec);
|
||||
|
||||
if (nr_refspec)
|
||||
return match_explicit_refs(src, dst, dst_tail, rs);
|
||||
|
||||
/* pick the remainder */
|
||||
for ( ; src; src = src->next) {
|
||||
struct ref *dst_peer;
|
||||
if (src->peer_ref)
|
||||
continue;
|
||||
dst_peer = find_ref_by_name(dst, src->name);
|
||||
if ((dst_peer && dst_peer->peer_ref) || (!dst_peer && !all))
|
||||
continue;
|
||||
if (!dst_peer) {
|
||||
/* Create a new one and link it */
|
||||
int len = strlen(src->name) + 1;
|
||||
dst_peer = xcalloc(1, sizeof(*dst_peer) + len);
|
||||
memcpy(dst_peer->name, src->name, len);
|
||||
hashcpy(dst_peer->new_sha1, src->new_sha1);
|
||||
link_dst_tail(dst_peer, dst_tail);
|
||||
}
|
||||
dst_peer->peer_ref = src;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum protocol {
|
||||
PROTO_LOCAL = 1,
|
||||
PROTO_SSH,
|
||||
|
||||
@@ -388,7 +388,7 @@ create_delta(const struct delta_index *index,
|
||||
outsize = max_size + MAX_OP_SIZE + 1;
|
||||
if (max_size && outpos > max_size)
|
||||
break;
|
||||
out = xrealloc(out, outsize);
|
||||
out = realloc(out, outsize);
|
||||
if (!out) {
|
||||
free(tmp);
|
||||
return NULL;
|
||||
|
||||
121
git-applymbox.sh
121
git-applymbox.sh
@@ -1,121 +0,0 @@
|
||||
#!/bin/sh
|
||||
##
|
||||
## "dotest" is my stupid name for my patch-application script, which
|
||||
## I never got around to renaming after I tested it. We're now on the
|
||||
## second generation of scripts, still called "dotest".
|
||||
##
|
||||
## Update: Ryan Anderson finally shamed me into naming this "applymbox".
|
||||
##
|
||||
## You give it a mbox-format collection of emails, and it will try to
|
||||
## apply them to the kernel using "applypatch"
|
||||
##
|
||||
## The patch application may fail in the middle. In which case:
|
||||
## (1) look at .dotest/patch and fix it up to apply
|
||||
## (2) re-run applymbox with -c .dotest/msg-number for the current one.
|
||||
## Pay a special attention to the commit log message if you do this and
|
||||
## use a Signoff_file, because applypatch wants to append the sign-off
|
||||
## message to msg-clean every time it is run.
|
||||
##
|
||||
## git-am is supposed to be the newer and better tool for this job.
|
||||
|
||||
USAGE='[-u] [-k] [-q] [-m] (-c .dotest/<num> | mbox) [signoff]'
|
||||
. git-sh-setup
|
||||
|
||||
git var GIT_COMMITTER_IDENT >/dev/null || exit
|
||||
|
||||
keep_subject= query_apply= continue= utf8=-u resume=t
|
||||
while case "$#" in 0) break ;; esac
|
||||
do
|
||||
case "$1" in
|
||||
-u) utf8=-u ;;
|
||||
-n) utf8=-n ;;
|
||||
-k) keep_subject=-k ;;
|
||||
-q) query_apply=t ;;
|
||||
-c) continue="$2"; resume=f; shift ;;
|
||||
-m) fall_back_3way=t ;;
|
||||
-*) usage ;;
|
||||
*) break ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
case "$continue" in
|
||||
'')
|
||||
rm -rf .dotest
|
||||
mkdir .dotest
|
||||
num_msgs=$(git-mailsplit "$1" .dotest) || exit 1
|
||||
echo "$num_msgs patch(es) to process."
|
||||
shift
|
||||
esac
|
||||
|
||||
files=$(git-diff-index --cached --name-only HEAD) || exit
|
||||
if [ "$files" ]; then
|
||||
echo "Dirty index: cannot apply patches (dirty: $files)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$query_apply" in
|
||||
t) touch .dotest/.query_apply
|
||||
esac
|
||||
case "$fall_back_3way" in
|
||||
t) : >.dotest/.3way
|
||||
esac
|
||||
case "$keep_subject" in
|
||||
-k) : >.dotest/.keep_subject
|
||||
esac
|
||||
|
||||
signoff="$1"
|
||||
set x .dotest/0*
|
||||
shift
|
||||
while case "$#" in 0) break;; esac
|
||||
do
|
||||
i="$1"
|
||||
case "$resume,$continue" in
|
||||
f,$i) resume=t;;
|
||||
f,*) shift
|
||||
continue;;
|
||||
*)
|
||||
git-mailinfo $keep_subject $utf8 \
|
||||
.dotest/msg .dotest/patch <$i >.dotest/info || exit 1
|
||||
test -s .dotest/patch || {
|
||||
echo "Patch is empty. Was it split wrong?"
|
||||
exit 1
|
||||
}
|
||||
git-stripspace < .dotest/msg > .dotest/msg-clean
|
||||
;;
|
||||
esac
|
||||
while :; # for fixing up and retry
|
||||
do
|
||||
git-applypatch .dotest/msg-clean .dotest/patch .dotest/info "$signoff"
|
||||
case "$?" in
|
||||
0)
|
||||
# Remove the cleanly applied one to reduce clutter.
|
||||
rm -f .dotest/$i
|
||||
;;
|
||||
2)
|
||||
# 2 is a special exit code from applypatch to indicate that
|
||||
# the patch wasn't applied, but continue anyway
|
||||
;;
|
||||
*)
|
||||
ret=$?
|
||||
if test -f .dotest/.query_apply
|
||||
then
|
||||
echo >&2 "* Patch failed."
|
||||
echo >&2 "* You could fix it up in your editor and"
|
||||
echo >&2 " retry. If you want to do so, say yes here"
|
||||
echo >&2 " AFTER fixing .dotest/patch up."
|
||||
echo >&2 -n "Retry [y/N]? "
|
||||
read yesno
|
||||
case "$yesno" in
|
||||
[Yy]*)
|
||||
continue ;;
|
||||
esac
|
||||
fi
|
||||
exit $ret
|
||||
esac
|
||||
break
|
||||
done
|
||||
shift
|
||||
done
|
||||
# return to pristine
|
||||
rm -fr .dotest
|
||||
@@ -1,212 +0,0 @@
|
||||
#!/bin/sh
|
||||
##
|
||||
## applypatch takes four file arguments, and uses those to
|
||||
## apply the unpacked patch (surprise surprise) that they
|
||||
## represent to the current tree.
|
||||
##
|
||||
## The arguments are:
|
||||
## $1 - file with commit message
|
||||
## $2 - file with the actual patch
|
||||
## $3 - "info" file with Author, email and subject
|
||||
## $4 - optional file containing signoff to add
|
||||
##
|
||||
|
||||
USAGE='<msg> <patch> <info> [<signoff>]'
|
||||
. git-sh-setup
|
||||
|
||||
case "$#" in 3|4) ;; *) usage ;; esac
|
||||
|
||||
final=.dotest/final-commit
|
||||
##
|
||||
## If this file exists, we ask before applying
|
||||
##
|
||||
query_apply=.dotest/.query_apply
|
||||
|
||||
## We do not munge the first line of the commit message too much
|
||||
## if this file exists.
|
||||
keep_subject=.dotest/.keep_subject
|
||||
|
||||
## We do not attempt the 3-way merge fallback unless this file exists.
|
||||
fall_back_3way=.dotest/.3way
|
||||
|
||||
MSGFILE=$1
|
||||
PATCHFILE=$2
|
||||
INFO=$3
|
||||
SIGNOFF=$4
|
||||
EDIT=${VISUAL:-${EDITOR:-vi}}
|
||||
|
||||
export GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$INFO")"
|
||||
export GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$INFO")"
|
||||
export GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$INFO")"
|
||||
export SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$INFO")"
|
||||
|
||||
if test '' != "$SIGNOFF"
|
||||
then
|
||||
if test -f "$SIGNOFF"
|
||||
then
|
||||
SIGNOFF=`cat "$SIGNOFF"` || exit
|
||||
elif case "$SIGNOFF" in yes | true | me | please) : ;; *) false ;; esac
|
||||
then
|
||||
SIGNOFF=`git-var GIT_COMMITTER_IDENT | sed -e '
|
||||
s/>.*/>/
|
||||
s/^/Signed-off-by: /'
|
||||
`
|
||||
else
|
||||
SIGNOFF=
|
||||
fi
|
||||
if test '' != "$SIGNOFF"
|
||||
then
|
||||
LAST_SIGNED_OFF_BY=`
|
||||
sed -ne '/^Signed-off-by: /p' "$MSGFILE" |
|
||||
tail -n 1
|
||||
`
|
||||
test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
|
||||
test '' = "$LAST_SIGNED_OFF_BY" && echo
|
||||
echo "$SIGNOFF"
|
||||
} >>"$MSGFILE"
|
||||
fi
|
||||
fi
|
||||
|
||||
patch_header=
|
||||
test -f "$keep_subject" || patch_header='[PATCH] '
|
||||
|
||||
{
|
||||
echo "$patch_header$SUBJECT"
|
||||
if test -s "$MSGFILE"
|
||||
then
|
||||
echo
|
||||
cat "$MSGFILE"
|
||||
fi
|
||||
} >"$final"
|
||||
|
||||
interactive=yes
|
||||
test -f "$query_apply" || interactive=no
|
||||
|
||||
while [ "$interactive" = yes ]; do
|
||||
echo "Commit Body is:"
|
||||
echo "--------------------------"
|
||||
cat "$final"
|
||||
echo "--------------------------"
|
||||
printf "Apply? [y]es/[n]o/[e]dit/[a]ccept all "
|
||||
read reply
|
||||
case "$reply" in
|
||||
y|Y) interactive=no;;
|
||||
n|N) exit 2;; # special value to tell dotest to keep going
|
||||
e|E) "$EDIT" "$final";;
|
||||
a|A) rm -f "$query_apply"
|
||||
interactive=no ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if test -x "$GIT_DIR"/hooks/applypatch-msg
|
||||
then
|
||||
"$GIT_DIR"/hooks/applypatch-msg "$final" || exit
|
||||
fi
|
||||
|
||||
echo
|
||||
echo Applying "'$SUBJECT'"
|
||||
echo
|
||||
|
||||
git-apply --index "$PATCHFILE" || {
|
||||
|
||||
# git-apply exits with status 1 when the patch does not apply,
|
||||
# but it die()s with other failures, most notably upon corrupt
|
||||
# patch. In the latter case, there is no point to try applying
|
||||
# it to another tree and do 3-way merge.
|
||||
test $? = 1 || exit 1
|
||||
|
||||
test -f "$fall_back_3way" || exit 1
|
||||
|
||||
# Here if we know which revision the patch applies to,
|
||||
# we create a temporary working tree and index, apply the
|
||||
# patch, and attempt 3-way merge with the resulting tree.
|
||||
|
||||
O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
|
||||
rm -fr .patch-merge-*
|
||||
|
||||
if git-apply -z --index-info "$PATCHFILE" \
|
||||
>.patch-merge-index-info 2>/dev/null &&
|
||||
GIT_INDEX_FILE=.patch-merge-tmp-index \
|
||||
git-update-index -z --index-info <.patch-merge-index-info &&
|
||||
GIT_INDEX_FILE=.patch-merge-tmp-index \
|
||||
git-write-tree >.patch-merge-tmp-base &&
|
||||
(
|
||||
mkdir .patch-merge-tmp-dir &&
|
||||
cd .patch-merge-tmp-dir &&
|
||||
GIT_INDEX_FILE="../.patch-merge-tmp-index" \
|
||||
GIT_OBJECT_DIRECTORY="$O_OBJECT" \
|
||||
git-apply $binary --index
|
||||
) <"$PATCHFILE"
|
||||
then
|
||||
echo Using index info to reconstruct a base tree...
|
||||
mv .patch-merge-tmp-base .patch-merge-base
|
||||
mv .patch-merge-tmp-index .patch-merge-index
|
||||
else
|
||||
(
|
||||
N=10
|
||||
|
||||
# Otherwise, try nearby trees that can be used to apply the
|
||||
# patch.
|
||||
git-rev-list --max-count=$N HEAD
|
||||
|
||||
# or hoping the patch is against known tags...
|
||||
git-ls-remote --tags .
|
||||
) |
|
||||
while read base junk
|
||||
do
|
||||
# Try it if we have it as a tree.
|
||||
git-cat-file tree "$base" >/dev/null 2>&1 || continue
|
||||
|
||||
rm -fr .patch-merge-tmp-* &&
|
||||
mkdir .patch-merge-tmp-dir || break
|
||||
(
|
||||
cd .patch-merge-tmp-dir &&
|
||||
GIT_INDEX_FILE=../.patch-merge-tmp-index &&
|
||||
GIT_OBJECT_DIRECTORY="$O_OBJECT" &&
|
||||
export GIT_INDEX_FILE GIT_OBJECT_DIRECTORY &&
|
||||
git-read-tree "$base" &&
|
||||
git-apply --index &&
|
||||
mv ../.patch-merge-tmp-index ../.patch-merge-index &&
|
||||
echo "$base" >../.patch-merge-base
|
||||
) <"$PATCHFILE" 2>/dev/null && break
|
||||
done
|
||||
fi
|
||||
|
||||
test -f .patch-merge-index &&
|
||||
his_tree=$(GIT_INDEX_FILE=.patch-merge-index git-write-tree) &&
|
||||
orig_tree=$(cat .patch-merge-base) &&
|
||||
rm -fr .patch-merge-* || exit 1
|
||||
|
||||
echo Falling back to patching base and 3-way merge using $orig_tree...
|
||||
|
||||
# This is not so wrong. Depending on which base we picked,
|
||||
# orig_tree may be wildly different from ours, but his_tree
|
||||
# has the same set of wildly different changes in parts the
|
||||
# patch did not touch, so resolve ends up canceling them,
|
||||
# saying that we reverted all those changes.
|
||||
|
||||
if git-merge-resolve $orig_tree -- HEAD $his_tree
|
||||
then
|
||||
echo Done.
|
||||
else
|
||||
echo Failed to merge in the changes.
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
if test -x "$GIT_DIR"/hooks/pre-applypatch
|
||||
then
|
||||
"$GIT_DIR"/hooks/pre-applypatch || exit
|
||||
fi
|
||||
|
||||
tree=$(git-write-tree) || exit 1
|
||||
echo Wrote tree $tree
|
||||
parent=$(git-rev-parse --verify HEAD) &&
|
||||
commit=$(git-commit-tree $tree -p $parent <"$final") || exit 1
|
||||
echo Committed: $commit
|
||||
git-update-ref -m "applypatch: $SUBJECT" HEAD $commit $parent || exit
|
||||
|
||||
if test -x "$GIT_DIR"/hooks/post-applypatch
|
||||
then
|
||||
"$GIT_DIR"/hooks/post-applypatch
|
||||
fi
|
||||
@@ -55,7 +55,7 @@ continue_merge () {
|
||||
if test -n "$unmerged"
|
||||
then
|
||||
echo "You still have unmerged paths in your index"
|
||||
echo "did you forget update-index?"
|
||||
echo "did you forget to use git add?"
|
||||
die "$RESOLVEMSG"
|
||||
fi
|
||||
|
||||
@@ -126,7 +126,7 @@ do
|
||||
--continue)
|
||||
git-diff-files --quiet || {
|
||||
echo "You must edit all merge conflicts and then"
|
||||
echo "mark them as resolved using git update-index"
|
||||
echo "mark them as resolved using git add"
|
||||
exit 1
|
||||
}
|
||||
if test -d "$dotest"
|
||||
|
||||
194
git-submodule.sh
Executable file
194
git-submodule.sh
Executable file
@@ -0,0 +1,194 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# git-submodules.sh: init, update or list git submodules
|
||||
#
|
||||
# Copyright (c) 2007 Lars Hjemli
|
||||
|
||||
USAGE='[--quiet] [--cached] [status|init|update] [--] [<path>...]'
|
||||
. git-sh-setup
|
||||
require_work_tree
|
||||
|
||||
init=
|
||||
update=
|
||||
status=
|
||||
quiet=
|
||||
cached=
|
||||
|
||||
#
|
||||
# print stuff on stdout unless -q was specified
|
||||
#
|
||||
say()
|
||||
{
|
||||
if test -z "$quiet"
|
||||
then
|
||||
echo "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Run clone + checkout on missing submodules
|
||||
#
|
||||
# $@ = requested paths (default to all)
|
||||
#
|
||||
modules_init()
|
||||
{
|
||||
git ls-files --stage -- "$@" | grep -e '^160000 ' |
|
||||
while read mode sha1 stage path
|
||||
do
|
||||
# Skip submodule paths that already contain a .git directory.
|
||||
# This will also trigger if $path is a symlink to a git
|
||||
# repository
|
||||
test -d "$path"/.git && continue
|
||||
|
||||
# If there already is a directory at the submodule path,
|
||||
# expect it to be empty (since that is the default checkout
|
||||
# action) and try to remove it.
|
||||
# Note: if $path is a symlink to a directory the test will
|
||||
# succeed but the rmdir will fail. We might want to fix this.
|
||||
if test -d "$path"
|
||||
then
|
||||
rmdir "$path" 2>/dev/null ||
|
||||
die "Directory '$path' exist, but is neither empty nor a git repository"
|
||||
fi
|
||||
|
||||
test -e "$path" &&
|
||||
die "A file already exist at path '$path'"
|
||||
|
||||
url=$(GIT_CONFIG=.gitmodules git-config module."$path".url)
|
||||
test -z "$url" &&
|
||||
die "No url found for submodule '$path' in .gitmodules"
|
||||
|
||||
# MAYBE FIXME: this would be the place to check GIT_CONFIG
|
||||
# for a preferred url for this submodule, possibly like this:
|
||||
#
|
||||
# modname=$(GIT_CONFIG=.gitmodules git-config module."$path".name)
|
||||
# alturl=$(git-config module."$modname".url)
|
||||
#
|
||||
# This would let the versioned .gitmodules file use the submodule
|
||||
# path as key, while the unversioned GIT_CONFIG would use the
|
||||
# logical modulename (if present) as key. But this would need
|
||||
# another fallback mechanism if the module wasn't named.
|
||||
|
||||
git-clone -n "$url" "$path" ||
|
||||
die "Clone of submodule '$path' failed"
|
||||
|
||||
(unset GIT_DIR && cd "$path" && git-checkout -q "$sha1") ||
|
||||
die "Checkout of submodule '$path' failed"
|
||||
|
||||
say "Submodule '$path' initialized"
|
||||
done
|
||||
}
|
||||
|
||||
#
|
||||
# Checkout correct revision of each initialized submodule
|
||||
#
|
||||
# $@ = requested paths (default to all)
|
||||
#
|
||||
modules_update()
|
||||
{
|
||||
git ls-files --stage -- "$@" | grep -e '^160000 ' |
|
||||
while read mode sha1 stage path
|
||||
do
|
||||
if ! test -d "$path"/.git
|
||||
then
|
||||
# Only mention uninitialized submodules when its
|
||||
# path have been specified
|
||||
test "$#" != "0" &&
|
||||
say "Submodule '$path' not initialized"
|
||||
continue;
|
||||
fi
|
||||
subsha1=$(unset GIT_DIR && cd "$path" &&
|
||||
git-rev-parse --verify HEAD) ||
|
||||
die "Unable to find current revision of submodule '$path'"
|
||||
|
||||
if test "$subsha1" != "$sha1"
|
||||
then
|
||||
(unset GIT_DIR && cd "$path" && git-fetch &&
|
||||
git-checkout -q "$sha1") ||
|
||||
die "Unable to checkout '$sha1' in submodule '$path'"
|
||||
|
||||
say "Submodule '$path': checked out '$sha1'"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
#
|
||||
# List all registered submodules, prefixed with:
|
||||
# - submodule not initialized
|
||||
# + different revision checked out
|
||||
#
|
||||
# If --cached was specified the revision in the index will be printed
|
||||
# instead of the currently checked out revision.
|
||||
#
|
||||
# $@ = requested paths (default to all)
|
||||
#
|
||||
modules_list()
|
||||
{
|
||||
git ls-files --stage -- "$@" | grep -e '^160000 ' |
|
||||
while read mode sha1 stage path
|
||||
do
|
||||
if ! test -d "$path"/.git
|
||||
then
|
||||
say "-$sha1 $path"
|
||||
continue;
|
||||
fi
|
||||
revname=$(unset GIT_DIR && cd "$path" && git-describe $sha1)
|
||||
if git diff-files --quiet -- "$path"
|
||||
then
|
||||
say " $sha1 $path ($revname)"
|
||||
else
|
||||
if test -z "$cached"
|
||||
then
|
||||
sha1=$(unset GIT_DIR && cd "$path" && git-rev-parse --verify HEAD)
|
||||
revname=$(unset GIT_DIR && cd "$path" && git-describe $sha1)
|
||||
fi
|
||||
say "+$sha1 $path ($revname)"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
while case "$#" in 0) break ;; esac
|
||||
do
|
||||
case "$1" in
|
||||
init)
|
||||
init=1
|
||||
;;
|
||||
update)
|
||||
update=1
|
||||
;;
|
||||
status)
|
||||
status=1
|
||||
;;
|
||||
-q|--quiet)
|
||||
quiet=1
|
||||
;;
|
||||
--cached)
|
||||
cached=1
|
||||
;;
|
||||
--)
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
case "$init,$update,$status,$cached" in
|
||||
1,,,)
|
||||
modules_init "$@"
|
||||
;;
|
||||
,1,,)
|
||||
modules_update "$@"
|
||||
;;
|
||||
,,*,*)
|
||||
modules_list "$@"
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
@@ -94,6 +94,13 @@ our $default_text_plain_charset = undef;
|
||||
# (relative to the current git repository)
|
||||
our $mimetypes_file = undef;
|
||||
|
||||
# assume this charset if line contains non-UTF-8 characters;
|
||||
# it should be valid encoding (see Encoding::Supported(3pm) for list),
|
||||
# for which encoding all byte sequences are valid, for example
|
||||
# 'iso-8859-1' aka 'latin1' (it is decoded without checking, so it
|
||||
# could be even 'utf-8' for the old behavior)
|
||||
our $fallback_encoding = 'latin1';
|
||||
|
||||
# You define site-wide feature defaults here; override them with
|
||||
# $GITWEB_CONFIG as necessary.
|
||||
our %feature = (
|
||||
@@ -602,6 +609,20 @@ sub validate_refname {
|
||||
return $input;
|
||||
}
|
||||
|
||||
# decode sequences of octets in utf8 into Perl's internal form,
|
||||
# which is utf-8 with utf8 flag set if needed. gitweb writes out
|
||||
# in utf-8 thanks to "binmode STDOUT, ':utf8'" at beginning
|
||||
sub to_utf8 {
|
||||
my $str = shift;
|
||||
my $res;
|
||||
eval { $res = decode_utf8($str, Encode::FB_CROAK); };
|
||||
if (defined $res) {
|
||||
return $res;
|
||||
} else {
|
||||
return decode($fallback_encoding, $str, Encode::FB_DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
# quote unsafe chars, but keep the slash, even when it's not
|
||||
# correct, but quoted slashes look too horrible in bookmarks
|
||||
sub esc_param {
|
||||
@@ -626,7 +647,7 @@ sub esc_html ($;%) {
|
||||
my $str = shift;
|
||||
my %opts = @_;
|
||||
|
||||
$str = decode_utf8($str);
|
||||
$str = to_utf8($str);
|
||||
$str = $cgi->escapeHTML($str);
|
||||
if ($opts{'-nbsp'}) {
|
||||
$str =~ s/ / /g;
|
||||
@@ -640,7 +661,7 @@ sub esc_path {
|
||||
my $str = shift;
|
||||
my %opts = @_;
|
||||
|
||||
$str = decode_utf8($str);
|
||||
$str = to_utf8($str);
|
||||
$str = $cgi->escapeHTML($str);
|
||||
if ($opts{'-nbsp'}) {
|
||||
$str =~ s/ / /g;
|
||||
@@ -925,7 +946,7 @@ sub format_subject_html {
|
||||
|
||||
if (length($short) < length($long)) {
|
||||
return $cgi->a({-href => $href, -class => "list subject",
|
||||
-title => decode_utf8($long)},
|
||||
-title => to_utf8($long)},
|
||||
esc_html($short) . $extra);
|
||||
} else {
|
||||
return $cgi->a({-href => $href, -class => "list subject"},
|
||||
@@ -1239,7 +1260,7 @@ sub git_get_projects_list {
|
||||
if (check_export_ok("$projectroot/$path")) {
|
||||
my $pr = {
|
||||
path => $path,
|
||||
owner => decode_utf8($owner),
|
||||
owner => to_utf8($owner),
|
||||
};
|
||||
push @list, $pr;
|
||||
(my $forks_path = $path) =~ s/\.git$//;
|
||||
@@ -1269,7 +1290,7 @@ sub git_get_project_owner {
|
||||
$pr = unescape($pr);
|
||||
$ow = unescape($ow);
|
||||
if ($pr eq $project) {
|
||||
$owner = decode_utf8($ow);
|
||||
$owner = to_utf8($ow);
|
||||
last;
|
||||
}
|
||||
}
|
||||
@@ -1759,7 +1780,7 @@ sub get_file_owner {
|
||||
}
|
||||
my $owner = $gcos;
|
||||
$owner =~ s/[,;].*$//;
|
||||
return decode_utf8($owner);
|
||||
return to_utf8($owner);
|
||||
}
|
||||
|
||||
## ......................................................................
|
||||
@@ -1842,7 +1863,7 @@ sub git_header_html {
|
||||
|
||||
my $title = "$site_name";
|
||||
if (defined $project) {
|
||||
$title .= " - " . decode_utf8($project);
|
||||
$title .= " - " . to_utf8($project);
|
||||
if (defined $action) {
|
||||
$title .= "/$action";
|
||||
if (defined $file_name) {
|
||||
@@ -2116,7 +2137,7 @@ sub git_print_page_path {
|
||||
|
||||
print "<div class=\"page_path\">";
|
||||
print $cgi->a({-href => href(action=>"tree", hash_base=>$hb),
|
||||
-title => 'tree root'}, decode_utf8("[$project]"));
|
||||
-title => 'tree root'}, to_utf8("[$project]"));
|
||||
print " / ";
|
||||
if (defined $name) {
|
||||
my @dirname = split '/', $name;
|
||||
@@ -2936,7 +2957,7 @@ sub git_project_list_body {
|
||||
($pr->{'age'}, $pr->{'age_string'}) = @aa;
|
||||
if (!defined $pr->{'descr'}) {
|
||||
my $descr = git_get_project_description($pr->{'path'}) || "";
|
||||
$pr->{'descr_long'} = decode_utf8($descr);
|
||||
$pr->{'descr_long'} = to_utf8($descr);
|
||||
$pr->{'descr'} = chop_str($descr, 25, 5);
|
||||
}
|
||||
if (!defined $pr->{'owner'}) {
|
||||
@@ -3981,7 +4002,7 @@ sub git_snapshot {
|
||||
my $git = git_cmd_str();
|
||||
my $name = $project;
|
||||
$name =~ s/\047/\047\\\047\047/g;
|
||||
my $filename = decode_utf8(basename($project));
|
||||
my $filename = to_utf8(basename($project));
|
||||
my $cmd;
|
||||
if ($suffix eq 'zip') {
|
||||
$filename .= "-$hash.$suffix";
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "diff.h"
|
||||
#include "revision.h"
|
||||
#include "exec_cmd.h"
|
||||
#include "remote.h"
|
||||
|
||||
#include <expat.h>
|
||||
|
||||
|
||||
208
index-pack.c
208
index-pack.c
@@ -13,13 +13,11 @@ static const char index_pack_usage[] =
|
||||
|
||||
struct object_entry
|
||||
{
|
||||
off_t offset;
|
||||
struct pack_idx_entry idx;
|
||||
unsigned long size;
|
||||
unsigned int hdr_size;
|
||||
uint32_t crc32;
|
||||
enum object_type type;
|
||||
enum object_type real_type;
|
||||
unsigned char sha1[20];
|
||||
};
|
||||
|
||||
union delta_base {
|
||||
@@ -197,7 +195,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
|
||||
unsigned shift;
|
||||
void *data;
|
||||
|
||||
obj->offset = consumed_bytes;
|
||||
obj->idx.offset = consumed_bytes;
|
||||
input_crc32 = crc32(0, Z_NULL, 0);
|
||||
|
||||
p = fill(1);
|
||||
@@ -229,15 +227,15 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
|
||||
while (c & 128) {
|
||||
base_offset += 1;
|
||||
if (!base_offset || MSB(base_offset, 7))
|
||||
bad_object(obj->offset, "offset value overflow for delta base object");
|
||||
bad_object(obj->idx.offset, "offset value overflow for delta base object");
|
||||
p = fill(1);
|
||||
c = *p;
|
||||
use(1);
|
||||
base_offset = (base_offset << 7) + (c & 127);
|
||||
}
|
||||
delta_base->offset = obj->offset - base_offset;
|
||||
if (delta_base->offset >= obj->offset)
|
||||
bad_object(obj->offset, "delta base offset is out of bound");
|
||||
delta_base->offset = obj->idx.offset - base_offset;
|
||||
if (delta_base->offset >= obj->idx.offset)
|
||||
bad_object(obj->idx.offset, "delta base offset is out of bound");
|
||||
break;
|
||||
case OBJ_COMMIT:
|
||||
case OBJ_TREE:
|
||||
@@ -245,19 +243,19 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
|
||||
case OBJ_TAG:
|
||||
break;
|
||||
default:
|
||||
bad_object(obj->offset, "unknown object type %d", obj->type);
|
||||
bad_object(obj->idx.offset, "unknown object type %d", obj->type);
|
||||
}
|
||||
obj->hdr_size = consumed_bytes - obj->offset;
|
||||
obj->hdr_size = consumed_bytes - obj->idx.offset;
|
||||
|
||||
data = unpack_entry_data(obj->offset, obj->size);
|
||||
obj->crc32 = input_crc32;
|
||||
data = unpack_entry_data(obj->idx.offset, obj->size);
|
||||
obj->idx.crc32 = input_crc32;
|
||||
return data;
|
||||
}
|
||||
|
||||
static void *get_data_from_pack(struct object_entry *obj)
|
||||
{
|
||||
unsigned long from = obj[0].offset + obj[0].hdr_size;
|
||||
unsigned long len = obj[1].offset - from;
|
||||
unsigned long from = obj[0].idx.offset + obj[0].hdr_size;
|
||||
unsigned long len = obj[1].idx.offset - from;
|
||||
unsigned long rdy = 0;
|
||||
unsigned char *src, *data;
|
||||
z_stream stream;
|
||||
@@ -360,11 +358,11 @@ static void resolve_delta(struct object_entry *delta_obj, void *base_data,
|
||||
&result_size);
|
||||
free(delta_data);
|
||||
if (!result)
|
||||
bad_object(delta_obj->offset, "failed to apply delta");
|
||||
sha1_object(result, result_size, type, delta_obj->sha1);
|
||||
bad_object(delta_obj->idx.offset, "failed to apply delta");
|
||||
sha1_object(result, result_size, type, delta_obj->idx.sha1);
|
||||
nr_resolved_deltas++;
|
||||
|
||||
hashcpy(delta_base.sha1, delta_obj->sha1);
|
||||
hashcpy(delta_base.sha1, delta_obj->idx.sha1);
|
||||
if (!find_delta_children(&delta_base, &first, &last)) {
|
||||
for (j = first; j <= last; j++) {
|
||||
struct object_entry *child = objects + deltas[j].obj_no;
|
||||
@@ -374,7 +372,7 @@ static void resolve_delta(struct object_entry *delta_obj, void *base_data,
|
||||
}
|
||||
|
||||
memset(&delta_base, 0, sizeof(delta_base));
|
||||
delta_base.offset = delta_obj->offset;
|
||||
delta_base.offset = delta_obj->idx.offset;
|
||||
if (!find_delta_children(&delta_base, &first, &last)) {
|
||||
for (j = first; j <= last; j++) {
|
||||
struct object_entry *child = objects + deltas[j].obj_no;
|
||||
@@ -418,12 +416,12 @@ static void parse_pack_objects(unsigned char *sha1)
|
||||
delta->obj_no = i;
|
||||
delta++;
|
||||
} else
|
||||
sha1_object(data, obj->size, obj->type, obj->sha1);
|
||||
sha1_object(data, obj->size, obj->type, obj->idx.sha1);
|
||||
free(data);
|
||||
if (verbose)
|
||||
display_progress(&progress, i+1);
|
||||
}
|
||||
objects[i].offset = consumed_bytes;
|
||||
objects[i].idx.offset = consumed_bytes;
|
||||
if (verbose)
|
||||
stop_progress(&progress);
|
||||
|
||||
@@ -465,10 +463,10 @@ static void parse_pack_objects(unsigned char *sha1)
|
||||
|
||||
if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA)
|
||||
continue;
|
||||
hashcpy(base.sha1, obj->sha1);
|
||||
hashcpy(base.sha1, obj->idx.sha1);
|
||||
ref = !find_delta_children(&base, &ref_first, &ref_last);
|
||||
memset(&base, 0, sizeof(base));
|
||||
base.offset = obj->offset;
|
||||
base.offset = obj->idx.offset;
|
||||
ofs = !find_delta_children(&base, &ofs_first, &ofs_last);
|
||||
if (!ref && !ofs)
|
||||
continue;
|
||||
@@ -535,11 +533,11 @@ static void append_obj_to_pack(const unsigned char *sha1, void *buf,
|
||||
}
|
||||
header[n++] = c;
|
||||
write_or_die(output_fd, header, n);
|
||||
obj[0].crc32 = crc32(0, Z_NULL, 0);
|
||||
obj[0].crc32 = crc32(obj[0].crc32, header, n);
|
||||
obj[1].offset = obj[0].offset + n;
|
||||
obj[1].offset += write_compressed(output_fd, buf, size, &obj[0].crc32);
|
||||
hashcpy(obj->sha1, sha1);
|
||||
obj[0].idx.crc32 = crc32(0, Z_NULL, 0);
|
||||
obj[0].idx.crc32 = crc32(obj[0].idx.crc32, header, n);
|
||||
obj[1].idx.offset = obj[0].idx.offset + n;
|
||||
obj[1].idx.offset += write_compressed(output_fd, buf, size, &obj[0].idx.crc32);
|
||||
hashcpy(obj->idx.sha1, sha1);
|
||||
}
|
||||
|
||||
static int delta_pos_compare(const void *_a, const void *_b)
|
||||
@@ -602,145 +600,6 @@ static void fix_unresolved_deltas(int nr_unresolved)
|
||||
free(sorted_by_pos);
|
||||
}
|
||||
|
||||
static uint32_t index_default_version = 1;
|
||||
static uint32_t index_off32_limit = 0x7fffffff;
|
||||
|
||||
static int sha1_compare(const void *_a, const void *_b)
|
||||
{
|
||||
struct object_entry *a = *(struct object_entry **)_a;
|
||||
struct object_entry *b = *(struct object_entry **)_b;
|
||||
return hashcmp(a->sha1, b->sha1);
|
||||
}
|
||||
|
||||
/*
|
||||
* On entry *sha1 contains the pack content SHA1 hash, on exit it is
|
||||
* the SHA1 hash of sorted object names.
|
||||
*/
|
||||
static const char *write_index_file(const char *index_name, unsigned char *sha1)
|
||||
{
|
||||
struct sha1file *f;
|
||||
struct object_entry **sorted_by_sha, **list, **last;
|
||||
uint32_t array[256];
|
||||
int i, fd;
|
||||
SHA_CTX ctx;
|
||||
uint32_t index_version;
|
||||
|
||||
if (nr_objects) {
|
||||
sorted_by_sha =
|
||||
xcalloc(nr_objects, sizeof(struct object_entry *));
|
||||
list = sorted_by_sha;
|
||||
last = sorted_by_sha + nr_objects;
|
||||
for (i = 0; i < nr_objects; ++i)
|
||||
sorted_by_sha[i] = &objects[i];
|
||||
qsort(sorted_by_sha, nr_objects, sizeof(sorted_by_sha[0]),
|
||||
sha1_compare);
|
||||
}
|
||||
else
|
||||
sorted_by_sha = list = last = NULL;
|
||||
|
||||
if (!index_name) {
|
||||
static char tmpfile[PATH_MAX];
|
||||
snprintf(tmpfile, sizeof(tmpfile),
|
||||
"%s/tmp_idx_XXXXXX", get_object_directory());
|
||||
fd = mkstemp(tmpfile);
|
||||
index_name = xstrdup(tmpfile);
|
||||
} else {
|
||||
unlink(index_name);
|
||||
fd = open(index_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
|
||||
}
|
||||
if (fd < 0)
|
||||
die("unable to create %s: %s", index_name, strerror(errno));
|
||||
f = sha1fd(fd, index_name);
|
||||
|
||||
/* if last object's offset is >= 2^31 we should use index V2 */
|
||||
index_version = (objects[nr_objects-1].offset >> 31) ? 2 : index_default_version;
|
||||
|
||||
/* index versions 2 and above need a header */
|
||||
if (index_version >= 2) {
|
||||
struct pack_idx_header hdr;
|
||||
hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
|
||||
hdr.idx_version = htonl(index_version);
|
||||
sha1write(f, &hdr, sizeof(hdr));
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the first-level table (the list is sorted,
|
||||
* but we use a 256-entry lookup to be able to avoid
|
||||
* having to do eight extra binary search iterations).
|
||||
*/
|
||||
for (i = 0; i < 256; i++) {
|
||||
struct object_entry **next = list;
|
||||
while (next < last) {
|
||||
struct object_entry *obj = *next;
|
||||
if (obj->sha1[0] != i)
|
||||
break;
|
||||
next++;
|
||||
}
|
||||
array[i] = htonl(next - sorted_by_sha);
|
||||
list = next;
|
||||
}
|
||||
sha1write(f, array, 256 * 4);
|
||||
|
||||
/* compute the SHA1 hash of sorted object names. */
|
||||
SHA1_Init(&ctx);
|
||||
|
||||
/*
|
||||
* Write the actual SHA1 entries..
|
||||
*/
|
||||
list = sorted_by_sha;
|
||||
for (i = 0; i < nr_objects; i++) {
|
||||
struct object_entry *obj = *list++;
|
||||
if (index_version < 2) {
|
||||
uint32_t offset = htonl(obj->offset);
|
||||
sha1write(f, &offset, 4);
|
||||
}
|
||||
sha1write(f, obj->sha1, 20);
|
||||
SHA1_Update(&ctx, obj->sha1, 20);
|
||||
}
|
||||
|
||||
if (index_version >= 2) {
|
||||
unsigned int nr_large_offset = 0;
|
||||
|
||||
/* write the crc32 table */
|
||||
list = sorted_by_sha;
|
||||
for (i = 0; i < nr_objects; i++) {
|
||||
struct object_entry *obj = *list++;
|
||||
uint32_t crc32_val = htonl(obj->crc32);
|
||||
sha1write(f, &crc32_val, 4);
|
||||
}
|
||||
|
||||
/* write the 32-bit offset table */
|
||||
list = sorted_by_sha;
|
||||
for (i = 0; i < nr_objects; i++) {
|
||||
struct object_entry *obj = *list++;
|
||||
uint32_t offset = (obj->offset <= index_off32_limit) ?
|
||||
obj->offset : (0x80000000 | nr_large_offset++);
|
||||
offset = htonl(offset);
|
||||
sha1write(f, &offset, 4);
|
||||
}
|
||||
|
||||
/* write the large offset table */
|
||||
list = sorted_by_sha;
|
||||
while (nr_large_offset) {
|
||||
struct object_entry *obj = *list++;
|
||||
uint64_t offset = obj->offset;
|
||||
if (offset > index_off32_limit) {
|
||||
uint32_t split[2];
|
||||
split[0] = htonl(offset >> 32);
|
||||
split[1] = htonl(offset & 0xffffffff);
|
||||
sha1write(f, split, 8);
|
||||
nr_large_offset--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sha1write(f, sha1, 20);
|
||||
sha1close(f, NULL, 1);
|
||||
free(sorted_by_sha);
|
||||
SHA1_Final(sha1, &ctx);
|
||||
return index_name;
|
||||
}
|
||||
|
||||
static void final(const char *final_pack_name, const char *curr_pack_name,
|
||||
const char *final_index_name, const char *curr_index_name,
|
||||
const char *keep_name, const char *keep_msg,
|
||||
@@ -830,6 +689,7 @@ int main(int argc, char **argv)
|
||||
const char *curr_index, *index_name = NULL;
|
||||
const char *keep_name = NULL, *keep_msg = NULL;
|
||||
char *index_name_buf = NULL, *keep_name_buf = NULL;
|
||||
struct pack_idx_entry **idx_objects;
|
||||
unsigned char sha1[20];
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
@@ -865,12 +725,12 @@ int main(int argc, char **argv)
|
||||
index_name = argv[++i];
|
||||
} else if (!prefixcmp(arg, "--index-version=")) {
|
||||
char *c;
|
||||
index_default_version = strtoul(arg + 16, &c, 10);
|
||||
if (index_default_version > 2)
|
||||
pack_idx_default_version = strtoul(arg + 16, &c, 10);
|
||||
if (pack_idx_default_version > 2)
|
||||
die("bad %s", arg);
|
||||
if (*c == ',')
|
||||
index_off32_limit = strtoul(c+1, &c, 0);
|
||||
if (*c || index_off32_limit & 0x80000000)
|
||||
pack_idx_off32_limit = strtoul(c+1, &c, 0);
|
||||
if (*c || pack_idx_off32_limit & 0x80000000)
|
||||
die("bad %s", arg);
|
||||
} else
|
||||
usage(index_pack_usage);
|
||||
@@ -940,7 +800,13 @@ int main(int argc, char **argv)
|
||||
nr_deltas - nr_resolved_deltas);
|
||||
}
|
||||
free(deltas);
|
||||
curr_index = write_index_file(index_name, sha1);
|
||||
|
||||
idx_objects = xmalloc((nr_objects) * sizeof(struct pack_idx_entry *));
|
||||
for (i = 0; i < nr_objects; i++)
|
||||
idx_objects[i] = &objects[i].idx;
|
||||
curr_index = write_idx_file(index_name, idx_objects, nr_objects, sha1);
|
||||
free(idx_objects);
|
||||
|
||||
final(pack_name, curr_pack,
|
||||
index_name, curr_index,
|
||||
keep_name, keep_msg,
|
||||
|
||||
@@ -128,12 +128,17 @@ static void show_pack_info(struct packed_git *p)
|
||||
|
||||
int verify_pack(struct packed_git *p, int verbose)
|
||||
{
|
||||
off_t index_size = p->index_size;
|
||||
const unsigned char *index_base = p->index_data;
|
||||
off_t index_size;
|
||||
const unsigned char *index_base;
|
||||
SHA_CTX ctx;
|
||||
unsigned char sha1[20];
|
||||
int ret;
|
||||
|
||||
if (open_pack_index(p))
|
||||
return error("packfile %s index not opened", p->pack_name);
|
||||
index_size = p->index_size;
|
||||
index_base = p->index_data;
|
||||
|
||||
ret = 0;
|
||||
/* Verify SHA1 sum of the index file */
|
||||
SHA1_Init(&ctx);
|
||||
|
||||
@@ -550,6 +550,9 @@ static struct pack_list * add_pack(struct packed_git *p)
|
||||
l.pack = p;
|
||||
llist_init(&l.all_objects);
|
||||
|
||||
if (open_pack_index(p))
|
||||
return NULL;
|
||||
|
||||
base = p->index_data;
|
||||
base += 256 * 4 + ((p->index_version < 2) ? 4 : 8);
|
||||
step = (p->index_version < 2) ? 24 : 20;
|
||||
|
||||
142
pack-write.c
142
pack-write.c
@@ -1,5 +1,147 @@
|
||||
#include "cache.h"
|
||||
#include "pack.h"
|
||||
#include "csum-file.h"
|
||||
|
||||
uint32_t pack_idx_default_version = 1;
|
||||
uint32_t pack_idx_off32_limit = 0x7fffffff;
|
||||
|
||||
static int sha1_compare(const void *_a, const void *_b)
|
||||
{
|
||||
struct pack_idx_entry *a = *(struct pack_idx_entry **)_a;
|
||||
struct pack_idx_entry *b = *(struct pack_idx_entry **)_b;
|
||||
return hashcmp(a->sha1, b->sha1);
|
||||
}
|
||||
|
||||
/*
|
||||
* On entry *sha1 contains the pack content SHA1 hash, on exit it is
|
||||
* the SHA1 hash of sorted object names. The objects array passed in
|
||||
* will be sorted by SHA1 on exit.
|
||||
*/
|
||||
const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1)
|
||||
{
|
||||
struct sha1file *f;
|
||||
struct pack_idx_entry **sorted_by_sha, **list, **last;
|
||||
off_t last_obj_offset = 0;
|
||||
uint32_t array[256];
|
||||
int i, fd;
|
||||
SHA_CTX ctx;
|
||||
uint32_t index_version;
|
||||
|
||||
if (nr_objects) {
|
||||
sorted_by_sha = objects;
|
||||
list = sorted_by_sha;
|
||||
last = sorted_by_sha + nr_objects;
|
||||
for (i = 0; i < nr_objects; ++i) {
|
||||
if (objects[i]->offset > last_obj_offset)
|
||||
last_obj_offset = objects[i]->offset;
|
||||
}
|
||||
qsort(sorted_by_sha, nr_objects, sizeof(sorted_by_sha[0]),
|
||||
sha1_compare);
|
||||
}
|
||||
else
|
||||
sorted_by_sha = list = last = NULL;
|
||||
|
||||
if (!index_name) {
|
||||
static char tmpfile[PATH_MAX];
|
||||
snprintf(tmpfile, sizeof(tmpfile),
|
||||
"%s/tmp_idx_XXXXXX", get_object_directory());
|
||||
fd = mkstemp(tmpfile);
|
||||
index_name = xstrdup(tmpfile);
|
||||
} else {
|
||||
unlink(index_name);
|
||||
fd = open(index_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
|
||||
}
|
||||
if (fd < 0)
|
||||
die("unable to create %s: %s", index_name, strerror(errno));
|
||||
f = sha1fd(fd, index_name);
|
||||
|
||||
/* if last object's offset is >= 2^31 we should use index V2 */
|
||||
index_version = (last_obj_offset >> 31) ? 2 : pack_idx_default_version;
|
||||
|
||||
/* index versions 2 and above need a header */
|
||||
if (index_version >= 2) {
|
||||
struct pack_idx_header hdr;
|
||||
hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
|
||||
hdr.idx_version = htonl(index_version);
|
||||
sha1write(f, &hdr, sizeof(hdr));
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the first-level table (the list is sorted,
|
||||
* but we use a 256-entry lookup to be able to avoid
|
||||
* having to do eight extra binary search iterations).
|
||||
*/
|
||||
for (i = 0; i < 256; i++) {
|
||||
struct pack_idx_entry **next = list;
|
||||
while (next < last) {
|
||||
struct pack_idx_entry *obj = *next;
|
||||
if (obj->sha1[0] != i)
|
||||
break;
|
||||
next++;
|
||||
}
|
||||
array[i] = htonl(next - sorted_by_sha);
|
||||
list = next;
|
||||
}
|
||||
sha1write(f, array, 256 * 4);
|
||||
|
||||
/* compute the SHA1 hash of sorted object names. */
|
||||
SHA1_Init(&ctx);
|
||||
|
||||
/*
|
||||
* Write the actual SHA1 entries..
|
||||
*/
|
||||
list = sorted_by_sha;
|
||||
for (i = 0; i < nr_objects; i++) {
|
||||
struct pack_idx_entry *obj = *list++;
|
||||
if (index_version < 2) {
|
||||
uint32_t offset = htonl(obj->offset);
|
||||
sha1write(f, &offset, 4);
|
||||
}
|
||||
sha1write(f, obj->sha1, 20);
|
||||
SHA1_Update(&ctx, obj->sha1, 20);
|
||||
}
|
||||
|
||||
if (index_version >= 2) {
|
||||
unsigned int nr_large_offset = 0;
|
||||
|
||||
/* write the crc32 table */
|
||||
list = sorted_by_sha;
|
||||
for (i = 0; i < nr_objects; i++) {
|
||||
struct pack_idx_entry *obj = *list++;
|
||||
uint32_t crc32_val = htonl(obj->crc32);
|
||||
sha1write(f, &crc32_val, 4);
|
||||
}
|
||||
|
||||
/* write the 32-bit offset table */
|
||||
list = sorted_by_sha;
|
||||
for (i = 0; i < nr_objects; i++) {
|
||||
struct pack_idx_entry *obj = *list++;
|
||||
uint32_t offset = (obj->offset <= pack_idx_off32_limit) ?
|
||||
obj->offset : (0x80000000 | nr_large_offset++);
|
||||
offset = htonl(offset);
|
||||
sha1write(f, &offset, 4);
|
||||
}
|
||||
|
||||
/* write the large offset table */
|
||||
list = sorted_by_sha;
|
||||
while (nr_large_offset) {
|
||||
struct pack_idx_entry *obj = *list++;
|
||||
uint64_t offset = obj->offset;
|
||||
if (offset > pack_idx_off32_limit) {
|
||||
uint32_t split[2];
|
||||
split[0] = htonl(offset >> 32);
|
||||
split[1] = htonl(offset & 0xffffffff);
|
||||
sha1write(f, split, 8);
|
||||
nr_large_offset--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sha1write(f, sha1, 20);
|
||||
sha1close(f, NULL, 1);
|
||||
SHA1_Final(sha1, &ctx);
|
||||
return index_name;
|
||||
}
|
||||
|
||||
void fixup_pack_header_footer(int pack_fd,
|
||||
unsigned char *pack_file_sha1,
|
||||
|
||||
14
pack.h
14
pack.h
@@ -34,6 +34,10 @@ struct pack_header {
|
||||
*/
|
||||
#define PACK_IDX_SIGNATURE 0xff744f63 /* "\377tOc" */
|
||||
|
||||
/* These may be overridden by command-line parameters */
|
||||
extern uint32_t pack_idx_default_version;
|
||||
extern uint32_t pack_idx_off32_limit;
|
||||
|
||||
/*
|
||||
* Packed object index header
|
||||
*/
|
||||
@@ -42,6 +46,16 @@ struct pack_idx_header {
|
||||
uint32_t idx_version;
|
||||
};
|
||||
|
||||
/*
|
||||
* Common part of object structure used for write_idx_file
|
||||
*/
|
||||
struct pack_idx_entry {
|
||||
unsigned char sha1[20];
|
||||
uint32_t crc32;
|
||||
off_t offset;
|
||||
};
|
||||
|
||||
extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1);
|
||||
|
||||
extern int verify_pack(struct packed_git *, int);
|
||||
extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t);
|
||||
|
||||
27
refs.c
27
refs.c
@@ -603,15 +603,20 @@ int get_ref_sha1(const char *ref, unsigned char *sha1)
|
||||
|
||||
static inline int bad_ref_char(int ch)
|
||||
{
|
||||
return (((unsigned) ch) <= ' ' ||
|
||||
ch == '~' || ch == '^' || ch == ':' ||
|
||||
/* 2.13 Pattern Matching Notation */
|
||||
ch == '?' || ch == '*' || ch == '[');
|
||||
if (((unsigned) ch) <= ' ' ||
|
||||
ch == '~' || ch == '^' || ch == ':')
|
||||
return 1;
|
||||
/* 2.13 Pattern Matching Notation */
|
||||
if (ch == '?' || ch == '[') /* Unsupported */
|
||||
return 1;
|
||||
if (ch == '*') /* Supported at the end */
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_ref_format(const char *ref)
|
||||
{
|
||||
int ch, level;
|
||||
int ch, level, bad_type;
|
||||
const char *cp = ref;
|
||||
|
||||
level = 0;
|
||||
@@ -622,13 +627,19 @@ int check_ref_format(const char *ref)
|
||||
return -1; /* should not end with slashes */
|
||||
|
||||
/* we are at the beginning of the path component */
|
||||
if (ch == '.' || bad_ref_char(ch))
|
||||
if (ch == '.')
|
||||
return -1;
|
||||
bad_type = bad_ref_char(ch);
|
||||
if (bad_type) {
|
||||
return (bad_type == 2 && !*cp) ? -3 : -1;
|
||||
}
|
||||
|
||||
/* scan the rest of the path component */
|
||||
while ((ch = *cp++) != 0) {
|
||||
if (bad_ref_char(ch))
|
||||
return -1;
|
||||
bad_type = bad_ref_char(ch);
|
||||
if (bad_type) {
|
||||
return (bad_type == 2 && !*cp) ? -3 : -1;
|
||||
}
|
||||
if (ch == '/')
|
||||
break;
|
||||
if (ch == '.' && *cp == '.')
|
||||
|
||||
553
remote.c
Normal file
553
remote.c
Normal file
@@ -0,0 +1,553 @@
|
||||
#include "cache.h"
|
||||
#include "remote.h"
|
||||
#include "refs.h"
|
||||
|
||||
static struct remote **remotes;
|
||||
static int allocated_remotes;
|
||||
|
||||
#define BUF_SIZE (2048)
|
||||
static char buffer[BUF_SIZE];
|
||||
|
||||
static void add_push_refspec(struct remote *remote, const char *ref)
|
||||
{
|
||||
int nr = remote->push_refspec_nr + 1;
|
||||
remote->push_refspec =
|
||||
xrealloc(remote->push_refspec, nr * sizeof(char *));
|
||||
remote->push_refspec[nr-1] = ref;
|
||||
remote->push_refspec_nr = nr;
|
||||
}
|
||||
|
||||
static void add_fetch_refspec(struct remote *remote, const char *ref)
|
||||
{
|
||||
int nr = remote->fetch_refspec_nr + 1;
|
||||
remote->fetch_refspec =
|
||||
xrealloc(remote->fetch_refspec, nr * sizeof(char *));
|
||||
remote->fetch_refspec[nr-1] = ref;
|
||||
remote->fetch_refspec_nr = nr;
|
||||
}
|
||||
|
||||
static void add_uri(struct remote *remote, const char *uri)
|
||||
{
|
||||
int nr = remote->uri_nr + 1;
|
||||
remote->uri =
|
||||
xrealloc(remote->uri, nr * sizeof(char *));
|
||||
remote->uri[nr-1] = uri;
|
||||
remote->uri_nr = nr;
|
||||
}
|
||||
|
||||
static struct remote *make_remote(const char *name, int len)
|
||||
{
|
||||
int i, empty = -1;
|
||||
|
||||
for (i = 0; i < allocated_remotes; i++) {
|
||||
if (!remotes[i]) {
|
||||
if (empty < 0)
|
||||
empty = i;
|
||||
} else {
|
||||
if (len ? (!strncmp(name, remotes[i]->name, len) &&
|
||||
!remotes[i]->name[len]) :
|
||||
!strcmp(name, remotes[i]->name))
|
||||
return remotes[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty < 0) {
|
||||
empty = allocated_remotes;
|
||||
allocated_remotes += allocated_remotes ? allocated_remotes : 1;
|
||||
remotes = xrealloc(remotes,
|
||||
sizeof(*remotes) * allocated_remotes);
|
||||
memset(remotes + empty, 0,
|
||||
(allocated_remotes - empty) * sizeof(*remotes));
|
||||
}
|
||||
remotes[empty] = xcalloc(1, sizeof(struct remote));
|
||||
if (len)
|
||||
remotes[empty]->name = xstrndup(name, len);
|
||||
else
|
||||
remotes[empty]->name = xstrdup(name);
|
||||
return remotes[empty];
|
||||
}
|
||||
|
||||
static void read_remotes_file(struct remote *remote)
|
||||
{
|
||||
FILE *f = fopen(git_path("remotes/%s", remote->name), "r");
|
||||
|
||||
if (!f)
|
||||
return;
|
||||
while (fgets(buffer, BUF_SIZE, f)) {
|
||||
int value_list;
|
||||
char *s, *p;
|
||||
|
||||
if (!prefixcmp(buffer, "URL:")) {
|
||||
value_list = 0;
|
||||
s = buffer + 4;
|
||||
} else if (!prefixcmp(buffer, "Push:")) {
|
||||
value_list = 1;
|
||||
s = buffer + 5;
|
||||
} else if (!prefixcmp(buffer, "Pull:")) {
|
||||
value_list = 2;
|
||||
s = buffer + 5;
|
||||
} else
|
||||
continue;
|
||||
|
||||
while (isspace(*s))
|
||||
s++;
|
||||
if (!*s)
|
||||
continue;
|
||||
|
||||
p = s + strlen(s);
|
||||
while (isspace(p[-1]))
|
||||
*--p = 0;
|
||||
|
||||
switch (value_list) {
|
||||
case 0:
|
||||
add_uri(remote, xstrdup(s));
|
||||
break;
|
||||
case 1:
|
||||
add_push_refspec(remote, xstrdup(s));
|
||||
break;
|
||||
case 2:
|
||||
add_fetch_refspec(remote, xstrdup(s));
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static void read_branches_file(struct remote *remote)
|
||||
{
|
||||
const char *slash = strchr(remote->name, '/');
|
||||
int n = slash ? slash - remote->name : 1000;
|
||||
FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r");
|
||||
char *s, *p;
|
||||
int len;
|
||||
|
||||
if (!f)
|
||||
return;
|
||||
s = fgets(buffer, BUF_SIZE, f);
|
||||
fclose(f);
|
||||
if (!s)
|
||||
return;
|
||||
while (isspace(*s))
|
||||
s++;
|
||||
if (!*s)
|
||||
return;
|
||||
p = s + strlen(s);
|
||||
while (isspace(p[-1]))
|
||||
*--p = 0;
|
||||
len = p - s;
|
||||
if (slash)
|
||||
len += strlen(slash);
|
||||
p = xmalloc(len + 1);
|
||||
strcpy(p, s);
|
||||
if (slash)
|
||||
strcat(p, slash);
|
||||
add_uri(remote, p);
|
||||
}
|
||||
|
||||
static char *default_remote_name = NULL;
|
||||
static const char *current_branch = NULL;
|
||||
static int current_branch_len = 0;
|
||||
|
||||
static int handle_config(const char *key, const char *value)
|
||||
{
|
||||
const char *name;
|
||||
const char *subkey;
|
||||
struct remote *remote;
|
||||
if (!prefixcmp(key, "branch.") && current_branch &&
|
||||
!strncmp(key + 7, current_branch, current_branch_len) &&
|
||||
!strcmp(key + 7 + current_branch_len, ".remote")) {
|
||||
free(default_remote_name);
|
||||
default_remote_name = xstrdup(value);
|
||||
}
|
||||
if (prefixcmp(key, "remote."))
|
||||
return 0;
|
||||
name = key + 7;
|
||||
subkey = strrchr(name, '.');
|
||||
if (!subkey)
|
||||
return error("Config with no key for remote %s", name);
|
||||
if (*subkey == '/') {
|
||||
warning("Config remote shorthand cannot begin with '/': %s", name);
|
||||
return 0;
|
||||
}
|
||||
remote = make_remote(name, subkey - name);
|
||||
if (!value) {
|
||||
/* if we ever have a boolean variable, e.g. "remote.*.disabled"
|
||||
* [remote "frotz"]
|
||||
* disabled
|
||||
* is a valid way to set it to true; we get NULL in value so
|
||||
* we need to handle it here.
|
||||
*
|
||||
* if (!strcmp(subkey, ".disabled")) {
|
||||
* val = git_config_bool(key, value);
|
||||
* return 0;
|
||||
* } else
|
||||
*
|
||||
*/
|
||||
return 0; /* ignore unknown booleans */
|
||||
}
|
||||
if (!strcmp(subkey, ".url")) {
|
||||
add_uri(remote, xstrdup(value));
|
||||
} else if (!strcmp(subkey, ".push")) {
|
||||
add_push_refspec(remote, xstrdup(value));
|
||||
} else if (!strcmp(subkey, ".fetch")) {
|
||||
add_fetch_refspec(remote, xstrdup(value));
|
||||
} else if (!strcmp(subkey, ".receivepack")) {
|
||||
if (!remote->receivepack)
|
||||
remote->receivepack = xstrdup(value);
|
||||
else
|
||||
error("more than one receivepack given, using the first");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void read_config(void)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
const char *head_ref;
|
||||
int flag;
|
||||
if (default_remote_name) // did this already
|
||||
return;
|
||||
default_remote_name = xstrdup("origin");
|
||||
current_branch = NULL;
|
||||
head_ref = resolve_ref("HEAD", sha1, 0, &flag);
|
||||
if (head_ref && (flag & REF_ISSYMREF) &&
|
||||
!prefixcmp(head_ref, "refs/heads/")) {
|
||||
current_branch = head_ref + strlen("refs/heads/");
|
||||
current_branch_len = strlen(current_branch);
|
||||
}
|
||||
git_config(handle_config);
|
||||
}
|
||||
|
||||
static struct refspec *parse_ref_spec(int nr_refspec, const char **refspec)
|
||||
{
|
||||
int i;
|
||||
struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec);
|
||||
for (i = 0; i < nr_refspec; i++) {
|
||||
const char *sp, *ep, *gp;
|
||||
sp = refspec[i];
|
||||
if (*sp == '+') {
|
||||
rs[i].force = 1;
|
||||
sp++;
|
||||
}
|
||||
gp = strchr(sp, '*');
|
||||
ep = strchr(sp, ':');
|
||||
if (gp && ep && gp > ep)
|
||||
gp = NULL;
|
||||
if (ep) {
|
||||
if (ep[1]) {
|
||||
const char *glob = strchr(ep + 1, '*');
|
||||
if (!glob)
|
||||
gp = NULL;
|
||||
if (gp)
|
||||
rs[i].dst = xstrndup(ep + 1,
|
||||
glob - ep - 1);
|
||||
else
|
||||
rs[i].dst = xstrdup(ep + 1);
|
||||
}
|
||||
} else {
|
||||
ep = sp + strlen(sp);
|
||||
}
|
||||
if (gp) {
|
||||
rs[i].pattern = 1;
|
||||
ep = gp;
|
||||
}
|
||||
rs[i].src = xstrndup(sp, ep - sp);
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
|
||||
struct remote *remote_get(const char *name)
|
||||
{
|
||||
struct remote *ret;
|
||||
|
||||
read_config();
|
||||
if (!name)
|
||||
name = default_remote_name;
|
||||
ret = make_remote(name, 0);
|
||||
if (name[0] != '/') {
|
||||
if (!ret->uri)
|
||||
read_remotes_file(ret);
|
||||
if (!ret->uri)
|
||||
read_branches_file(ret);
|
||||
}
|
||||
if (!ret->uri)
|
||||
add_uri(ret, name);
|
||||
if (!ret->uri)
|
||||
return NULL;
|
||||
ret->fetch = parse_ref_spec(ret->fetch_refspec_nr, ret->fetch_refspec);
|
||||
ret->push = parse_ref_spec(ret->push_refspec_nr, ret->push_refspec);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int remote_has_uri(struct remote *remote, const char *uri)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < remote->uri_nr; i++) {
|
||||
if (!strcmp(remote->uri[i], uri))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int remote_find_tracking(struct remote *remote, struct refspec *refspec)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < remote->fetch_refspec_nr; i++) {
|
||||
struct refspec *fetch = &remote->fetch[i];
|
||||
if (!fetch->dst)
|
||||
continue;
|
||||
if (fetch->pattern) {
|
||||
if (!prefixcmp(refspec->src, fetch->src)) {
|
||||
refspec->dst =
|
||||
xmalloc(strlen(fetch->dst) +
|
||||
strlen(refspec->src) -
|
||||
strlen(fetch->src) + 1);
|
||||
strcpy(refspec->dst, fetch->dst);
|
||||
strcpy(refspec->dst + strlen(fetch->dst),
|
||||
refspec->src + strlen(fetch->src));
|
||||
refspec->force = fetch->force;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (!strcmp(refspec->src, fetch->src)) {
|
||||
refspec->dst = xstrdup(fetch->dst);
|
||||
refspec->force = fetch->force;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
refspec->dst = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int count_refspec_match(const char *pattern,
|
||||
struct ref *refs,
|
||||
struct ref **matched_ref)
|
||||
{
|
||||
int patlen = strlen(pattern);
|
||||
struct ref *matched_weak = NULL;
|
||||
struct ref *matched = NULL;
|
||||
int weak_match = 0;
|
||||
int match = 0;
|
||||
|
||||
for (weak_match = match = 0; refs; refs = refs->next) {
|
||||
char *name = refs->name;
|
||||
int namelen = strlen(name);
|
||||
int weak_match;
|
||||
|
||||
if (namelen < patlen ||
|
||||
memcmp(name + namelen - patlen, pattern, patlen))
|
||||
continue;
|
||||
if (namelen != patlen && name[namelen - patlen - 1] != '/')
|
||||
continue;
|
||||
|
||||
/* A match is "weak" if it is with refs outside
|
||||
* heads or tags, and did not specify the pattern
|
||||
* in full (e.g. "refs/remotes/origin/master") or at
|
||||
* least from the toplevel (e.g. "remotes/origin/master");
|
||||
* otherwise "git push $URL master" would result in
|
||||
* ambiguity between remotes/origin/master and heads/master
|
||||
* at the remote site.
|
||||
*/
|
||||
if (namelen != patlen &&
|
||||
patlen != namelen - 5 &&
|
||||
prefixcmp(name, "refs/heads/") &&
|
||||
prefixcmp(name, "refs/tags/")) {
|
||||
/* We want to catch the case where only weak
|
||||
* matches are found and there are multiple
|
||||
* matches, and where more than one strong
|
||||
* matches are found, as ambiguous. One
|
||||
* strong match with zero or more weak matches
|
||||
* are acceptable as a unique match.
|
||||
*/
|
||||
matched_weak = refs;
|
||||
weak_match++;
|
||||
}
|
||||
else {
|
||||
matched = refs;
|
||||
match++;
|
||||
}
|
||||
}
|
||||
if (!matched) {
|
||||
*matched_ref = matched_weak;
|
||||
return weak_match;
|
||||
}
|
||||
else {
|
||||
*matched_ref = matched;
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
||||
static void link_dst_tail(struct ref *ref, struct ref ***tail)
|
||||
{
|
||||
**tail = ref;
|
||||
*tail = &ref->next;
|
||||
**tail = NULL;
|
||||
}
|
||||
|
||||
static struct ref *try_explicit_object_name(const char *name)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
struct ref *ref;
|
||||
int len;
|
||||
|
||||
if (!*name) {
|
||||
ref = xcalloc(1, sizeof(*ref) + 20);
|
||||
strcpy(ref->name, "(delete)");
|
||||
hashclr(ref->new_sha1);
|
||||
return ref;
|
||||
}
|
||||
if (get_sha1(name, sha1))
|
||||
return NULL;
|
||||
len = strlen(name) + 1;
|
||||
ref = xcalloc(1, sizeof(*ref) + len);
|
||||
memcpy(ref->name, name, len);
|
||||
hashcpy(ref->new_sha1, sha1);
|
||||
return ref;
|
||||
}
|
||||
|
||||
static int match_explicit_refs(struct ref *src, struct ref *dst,
|
||||
struct ref ***dst_tail, struct refspec *rs,
|
||||
int rs_nr)
|
||||
{
|
||||
int i, errs;
|
||||
for (i = errs = 0; i < rs_nr; i++) {
|
||||
struct ref *matched_src, *matched_dst;
|
||||
|
||||
const char *dst_value = rs[i].dst;
|
||||
|
||||
if (rs[i].pattern)
|
||||
continue;
|
||||
|
||||
if (dst_value == NULL)
|
||||
dst_value = rs[i].src;
|
||||
|
||||
matched_src = matched_dst = NULL;
|
||||
switch (count_refspec_match(rs[i].src, src, &matched_src)) {
|
||||
case 1:
|
||||
break;
|
||||
case 0:
|
||||
/* The source could be in the get_sha1() format
|
||||
* not a reference name. :refs/other is a
|
||||
* way to delete 'other' ref at the remote end.
|
||||
*/
|
||||
matched_src = try_explicit_object_name(rs[i].src);
|
||||
if (matched_src)
|
||||
break;
|
||||
errs = 1;
|
||||
error("src refspec %s does not match any.",
|
||||
rs[i].src);
|
||||
break;
|
||||
default:
|
||||
errs = 1;
|
||||
error("src refspec %s matches more than one.",
|
||||
rs[i].src);
|
||||
break;
|
||||
}
|
||||
switch (count_refspec_match(dst_value, dst, &matched_dst)) {
|
||||
case 1:
|
||||
break;
|
||||
case 0:
|
||||
if (!memcmp(dst_value, "refs/", 5)) {
|
||||
int len = strlen(dst_value) + 1;
|
||||
matched_dst = xcalloc(1, sizeof(*dst) + len);
|
||||
memcpy(matched_dst->name, dst_value, len);
|
||||
link_dst_tail(matched_dst, dst_tail);
|
||||
}
|
||||
else if (!strcmp(rs[i].src, dst_value) &&
|
||||
matched_src) {
|
||||
/* pushing "master:master" when
|
||||
* remote does not have master yet.
|
||||
*/
|
||||
int len = strlen(matched_src->name) + 1;
|
||||
matched_dst = xcalloc(1, sizeof(*dst) + len);
|
||||
memcpy(matched_dst->name, matched_src->name,
|
||||
len);
|
||||
link_dst_tail(matched_dst, dst_tail);
|
||||
}
|
||||
else {
|
||||
errs = 1;
|
||||
error("dst refspec %s does not match any "
|
||||
"existing ref on the remote and does "
|
||||
"not start with refs/.", dst_value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
errs = 1;
|
||||
error("dst refspec %s matches more than one.",
|
||||
dst_value);
|
||||
break;
|
||||
}
|
||||
if (errs)
|
||||
continue;
|
||||
if (matched_dst->peer_ref) {
|
||||
errs = 1;
|
||||
error("dst ref %s receives from more than one src.",
|
||||
matched_dst->name);
|
||||
}
|
||||
else {
|
||||
matched_dst->peer_ref = matched_src;
|
||||
matched_dst->force = rs[i].force;
|
||||
}
|
||||
}
|
||||
return -errs;
|
||||
}
|
||||
|
||||
static struct ref *find_ref_by_name(struct ref *list, const char *name)
|
||||
{
|
||||
for ( ; list; list = list->next)
|
||||
if (!strcmp(list->name, name))
|
||||
return list;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int check_pattern_match(struct refspec *rs, int rs_nr, struct ref *src)
|
||||
{
|
||||
int i;
|
||||
if (!rs_nr)
|
||||
return 1;
|
||||
for (i = 0; i < rs_nr; i++) {
|
||||
if (rs[i].pattern && !prefixcmp(src->name, rs[i].src))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
|
||||
int nr_refspec, char **refspec, int all)
|
||||
{
|
||||
struct refspec *rs =
|
||||
parse_ref_spec(nr_refspec, (const char **) refspec);
|
||||
|
||||
if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
|
||||
return -1;
|
||||
|
||||
/* pick the remainder */
|
||||
for ( ; src; src = src->next) {
|
||||
struct ref *dst_peer;
|
||||
if (src->peer_ref)
|
||||
continue;
|
||||
if (!check_pattern_match(rs, nr_refspec, src))
|
||||
continue;
|
||||
|
||||
dst_peer = find_ref_by_name(dst, src->name);
|
||||
if (dst_peer && dst_peer->peer_ref)
|
||||
/* We're already sending something to this ref. */
|
||||
continue;
|
||||
if (!dst_peer && !nr_refspec && !all)
|
||||
/* Remote doesn't have it, and we have no
|
||||
* explicit pattern, and we don't have
|
||||
* --all. */
|
||||
continue;
|
||||
if (!dst_peer) {
|
||||
/* Create a new one and link it */
|
||||
int len = strlen(src->name) + 1;
|
||||
dst_peer = xcalloc(1, sizeof(*dst_peer) + len);
|
||||
memcpy(dst_peer->name, src->name, len);
|
||||
hashcpy(dst_peer->new_sha1, src->new_sha1);
|
||||
link_dst_tail(dst_peer, dst_tail);
|
||||
}
|
||||
dst_peer->peer_ref = src;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
41
remote.h
Normal file
41
remote.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef REMOTE_H
|
||||
#define REMOTE_H
|
||||
|
||||
struct remote {
|
||||
const char *name;
|
||||
|
||||
const char **uri;
|
||||
int uri_nr;
|
||||
|
||||
const char **push_refspec;
|
||||
struct refspec *push;
|
||||
int push_refspec_nr;
|
||||
|
||||
const char **fetch_refspec;
|
||||
struct refspec *fetch;
|
||||
int fetch_refspec_nr;
|
||||
|
||||
const char *receivepack;
|
||||
};
|
||||
|
||||
struct remote *remote_get(const char *name);
|
||||
|
||||
int remote_has_uri(struct remote *remote, const char *uri);
|
||||
|
||||
struct refspec {
|
||||
unsigned force : 1;
|
||||
unsigned pattern : 1;
|
||||
|
||||
const char *src;
|
||||
char *dst;
|
||||
};
|
||||
|
||||
int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
|
||||
int nr_refspec, char **refspec, int all);
|
||||
|
||||
/*
|
||||
* For the given remote, reads the refspec's src and sets the other fields.
|
||||
*/
|
||||
int remote_find_tracking(struct remote *remote, struct refspec *refspec);
|
||||
|
||||
#endif
|
||||
57
send-pack.c
57
send-pack.c
@@ -4,6 +4,7 @@
|
||||
#include "refs.h"
|
||||
#include "pkt-line.h"
|
||||
#include "run-command.h"
|
||||
#include "remote.h"
|
||||
|
||||
static const char send_pack_usage[] =
|
||||
"git-send-pack [--all] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
|
||||
@@ -176,7 +177,7 @@ static int receive_status(int in)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int send_pack(int in, int out, int nr_refspec, char **refspec)
|
||||
static int send_pack(int in, int out, struct remote *remote, int nr_refspec, char **refspec)
|
||||
{
|
||||
struct ref *ref;
|
||||
int new_refs;
|
||||
@@ -213,18 +214,19 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
|
||||
new_refs = 0;
|
||||
for (ref = remote_refs; ref; ref = ref->next) {
|
||||
char old_hex[60], *new_hex;
|
||||
int delete_ref;
|
||||
int will_delete_ref;
|
||||
|
||||
if (!ref->peer_ref)
|
||||
continue;
|
||||
|
||||
delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
|
||||
if (delete_ref && !allow_deleting_refs) {
|
||||
|
||||
will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
|
||||
if (will_delete_ref && !allow_deleting_refs) {
|
||||
error("remote does not support deleting refs");
|
||||
ret = -2;
|
||||
continue;
|
||||
}
|
||||
if (!delete_ref &&
|
||||
if (!will_delete_ref &&
|
||||
!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
|
||||
if (verbose)
|
||||
fprintf(stderr, "'%s': up-to-date\n", ref->name);
|
||||
@@ -251,7 +253,7 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
|
||||
*/
|
||||
|
||||
if (!force_update &&
|
||||
!delete_ref &&
|
||||
!will_delete_ref &&
|
||||
!is_null_sha1(ref->old_sha1) &&
|
||||
!ref->force) {
|
||||
if (!has_sha1_file(ref->old_sha1) ||
|
||||
@@ -275,7 +277,7 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
|
||||
}
|
||||
}
|
||||
hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
|
||||
if (!delete_ref)
|
||||
if (!will_delete_ref)
|
||||
new_refs++;
|
||||
strcpy(old_hex, sha1_to_hex(ref->old_sha1));
|
||||
new_hex = sha1_to_hex(ref->new_sha1);
|
||||
@@ -290,7 +292,7 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
|
||||
else
|
||||
packet_write(out, "%s %s %s",
|
||||
old_hex, new_hex, ref->name);
|
||||
if (delete_ref)
|
||||
if (will_delete_ref)
|
||||
fprintf(stderr, "deleting '%s'\n", ref->name);
|
||||
else {
|
||||
fprintf(stderr, "updating '%s'", ref->name);
|
||||
@@ -300,6 +302,28 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
|
||||
fprintf(stderr, "\n from %s\n to %s\n",
|
||||
old_hex, new_hex);
|
||||
}
|
||||
if (remote) {
|
||||
struct refspec rs;
|
||||
rs.src = ref->name;
|
||||
remote_find_tracking(remote, &rs);
|
||||
if (rs.dst) {
|
||||
struct ref_lock *lock;
|
||||
fprintf(stderr, " Also local %s\n", rs.dst);
|
||||
if (will_delete_ref) {
|
||||
if (delete_ref(rs.dst, NULL)) {
|
||||
error("Failed to delete");
|
||||
}
|
||||
} else {
|
||||
lock = lock_any_ref_for_update(rs.dst, NULL, 0);
|
||||
if (!lock)
|
||||
error("Failed to lock");
|
||||
else
|
||||
write_ref_sha1(lock, ref->new_sha1,
|
||||
"update by push");
|
||||
}
|
||||
free(rs.dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
packet_flush(out);
|
||||
@@ -330,6 +354,7 @@ static void verify_remote_names(int nr_heads, char **heads)
|
||||
case -2: /* ok but a single level -- that is fine for
|
||||
* a match pattern.
|
||||
*/
|
||||
case -3: /* ok but ends with a pattern-match character */
|
||||
continue;
|
||||
}
|
||||
die("remote part of refspec is not a valid name in %s",
|
||||
@@ -344,6 +369,8 @@ int main(int argc, char **argv)
|
||||
char **heads = NULL;
|
||||
int fd[2], ret;
|
||||
pid_t pid;
|
||||
char *remote_name = NULL;
|
||||
struct remote *remote = NULL;
|
||||
|
||||
setup_git_directory();
|
||||
git_config(git_default_config);
|
||||
@@ -361,6 +388,10 @@ int main(int argc, char **argv)
|
||||
receivepack = arg + 7;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--remote=")) {
|
||||
remote_name = arg + 9;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--all")) {
|
||||
send_all = 1;
|
||||
continue;
|
||||
@@ -393,10 +424,18 @@ int main(int argc, char **argv)
|
||||
usage(send_pack_usage);
|
||||
verify_remote_names(nr_heads, heads);
|
||||
|
||||
if (remote_name) {
|
||||
remote = remote_get(remote_name);
|
||||
if (!remote_has_uri(remote, dest)) {
|
||||
die("Destination %s is not a uri for %s",
|
||||
dest, remote_name);
|
||||
}
|
||||
}
|
||||
|
||||
pid = git_connect(fd, dest, receivepack, verbose ? CONNECT_VERBOSE : 0);
|
||||
if (pid < 0)
|
||||
return 1;
|
||||
ret = send_pack(fd[0], fd[1], nr_heads, heads);
|
||||
ret = send_pack(fd[0], fd[1], remote, nr_heads, heads);
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
ret |= finish_connect(pid);
|
||||
|
||||
65
sha1_file.c
65
sha1_file.c
@@ -388,11 +388,12 @@ void prepare_alt_odb(void)
|
||||
{
|
||||
const char *alt;
|
||||
|
||||
if (alt_odb_tail)
|
||||
return;
|
||||
|
||||
alt = getenv(ALTERNATE_DB_ENVIRONMENT);
|
||||
if (!alt) alt = "";
|
||||
|
||||
if (alt_odb_tail)
|
||||
return;
|
||||
alt_odb_tail = &alt_odb_list;
|
||||
link_alt_odb_entries(alt, alt + strlen(alt), ':', NULL, 0);
|
||||
|
||||
@@ -545,6 +546,21 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int open_pack_index(struct packed_git *p)
|
||||
{
|
||||
char *idx_name;
|
||||
int ret;
|
||||
|
||||
if (p->index_data)
|
||||
return 0;
|
||||
|
||||
idx_name = xstrdup(p->pack_name);
|
||||
strcpy(idx_name + strlen(idx_name) - strlen(".pack"), ".idx");
|
||||
ret = check_packed_git_idx(idx_name, p);
|
||||
free(idx_name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void scan_windows(struct packed_git *p,
|
||||
struct packed_git **lru_p,
|
||||
struct pack_window **lru_w,
|
||||
@@ -620,6 +636,9 @@ static int open_packed_git_1(struct packed_git *p)
|
||||
unsigned char *idx_sha1;
|
||||
long fd_flag;
|
||||
|
||||
if (!p->index_data && open_pack_index(p))
|
||||
return error("packfile %s index unavailable", p->pack_name);
|
||||
|
||||
p->pack_fd = open(p->pack_name, O_RDONLY);
|
||||
if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
|
||||
return -1;
|
||||
@@ -774,8 +793,7 @@ struct packed_git *add_packed_git(const char *path, int path_len, int local)
|
||||
return NULL;
|
||||
memcpy(p->pack_name, path, path_len);
|
||||
strcpy(p->pack_name + path_len, ".pack");
|
||||
if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode) ||
|
||||
check_packed_git_idx(path, p)) {
|
||||
if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode)) {
|
||||
free(p);
|
||||
return NULL;
|
||||
}
|
||||
@@ -783,6 +801,10 @@ struct packed_git *add_packed_git(const char *path, int path_len, int local)
|
||||
/* ok, it looks sane as far as we can check without
|
||||
* actually mapping the pack file.
|
||||
*/
|
||||
p->index_version = 0;
|
||||
p->index_data = NULL;
|
||||
p->index_size = 0;
|
||||
p->num_objects = 0;
|
||||
p->pack_size = st.st_size;
|
||||
p->next = NULL;
|
||||
p->windows = NULL;
|
||||
@@ -1595,10 +1617,15 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
|
||||
return data;
|
||||
}
|
||||
|
||||
const unsigned char *nth_packed_object_sha1(const struct packed_git *p,
|
||||
const unsigned char *nth_packed_object_sha1(struct packed_git *p,
|
||||
uint32_t n)
|
||||
{
|
||||
const unsigned char *index = p->index_data;
|
||||
if (!index) {
|
||||
if (open_pack_index(p))
|
||||
return NULL;
|
||||
index = p->index_data;
|
||||
}
|
||||
if (n >= p->num_objects)
|
||||
return NULL;
|
||||
index += 4 * 256;
|
||||
@@ -1635,6 +1662,12 @@ off_t find_pack_entry_one(const unsigned char *sha1,
|
||||
const unsigned char *index = p->index_data;
|
||||
unsigned hi, lo;
|
||||
|
||||
if (!index) {
|
||||
if (open_pack_index(p))
|
||||
return 0;
|
||||
level1_ofs = p->index_data;
|
||||
index = p->index_data;
|
||||
}
|
||||
if (p->index_version > 1) {
|
||||
level1_ofs += 2;
|
||||
index += 8;
|
||||
@@ -1677,20 +1710,25 @@ static int matches_pack_name(struct packed_git *p, const char *ig)
|
||||
|
||||
static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed)
|
||||
{
|
||||
static struct packed_git *last_found = (void *)1;
|
||||
struct packed_git *p;
|
||||
off_t offset;
|
||||
|
||||
prepare_packed_git();
|
||||
if (!packed_git)
|
||||
return 0;
|
||||
p = (last_found == (void *)1) ? packed_git : last_found;
|
||||
|
||||
for (p = packed_git; p; p = p->next) {
|
||||
do {
|
||||
if (ignore_packed) {
|
||||
const char **ig;
|
||||
for (ig = ignore_packed; *ig; ig++)
|
||||
if (!matches_pack_name(p, *ig))
|
||||
break;
|
||||
if (*ig)
|
||||
continue;
|
||||
goto next;
|
||||
}
|
||||
|
||||
offset = find_pack_entry_one(sha1, p);
|
||||
if (offset) {
|
||||
/*
|
||||
@@ -1703,14 +1741,23 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, cons
|
||||
*/
|
||||
if (p->pack_fd == -1 && open_packed_git(p)) {
|
||||
error("packfile %s cannot be accessed", p->pack_name);
|
||||
continue;
|
||||
goto next;
|
||||
}
|
||||
e->offset = offset;
|
||||
e->p = p;
|
||||
hashcpy(e->sha1, sha1);
|
||||
last_found = p;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
next:
|
||||
if (p == last_found)
|
||||
p = packed_git;
|
||||
else
|
||||
p = p->next;
|
||||
if (p == last_found)
|
||||
p = p->next;
|
||||
} while (p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -76,8 +76,11 @@ static int find_short_packed_object(int len, const unsigned char *match, unsigne
|
||||
|
||||
prepare_packed_git();
|
||||
for (p = packed_git; p && found < 2; p = p->next) {
|
||||
uint32_t num = p->num_objects;
|
||||
uint32_t first = 0, last = num;
|
||||
uint32_t num, last;
|
||||
uint32_t first = 0;
|
||||
open_pack_index(p);
|
||||
num = p->num_objects;
|
||||
last = num;
|
||||
while (first < last) {
|
||||
uint32_t mid = (first + last) / 2;
|
||||
const unsigned char *now;
|
||||
@@ -133,6 +136,7 @@ static int find_unique_short_object(int len, char *canonical,
|
||||
int has_unpacked, has_packed;
|
||||
unsigned char unpacked_sha1[20], packed_sha1[20];
|
||||
|
||||
prepare_alt_odb();
|
||||
has_unpacked = find_short_object_filename(len, canonical, unpacked_sha1);
|
||||
has_packed = find_short_packed_object(len, res, packed_sha1);
|
||||
if (!has_unpacked && !has_packed)
|
||||
@@ -654,7 +658,6 @@ int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode)
|
||||
const char *cp;
|
||||
|
||||
*mode = S_IFINVALID;
|
||||
prepare_alt_odb();
|
||||
ret = get_sha1_1(name, namelen, sha1);
|
||||
if (!ret)
|
||||
return ret;
|
||||
|
||||
@@ -29,5 +29,15 @@ test_expect_success 'final^1^3 not valid' "if git-rev-parse --verify final^1^3;
|
||||
test_expect_failure '--verify start2^1' 'git-rev-parse --verify start2^1'
|
||||
test_expect_success '--verify start2^0' 'git-rev-parse --verify start2^0'
|
||||
|
||||
test_expect_success 'repack for next test' 'git repack -a -d'
|
||||
test_expect_success 'short SHA-1 works' '
|
||||
start=`git rev-parse --verify start` &&
|
||||
echo $start &&
|
||||
abbrv=`echo $start | sed s/.\$//` &&
|
||||
echo $abbrv &&
|
||||
abbrv=`git rev-parse --verify $abbrv` &&
|
||||
echo $abbrv &&
|
||||
test $start = $abbrv'
|
||||
|
||||
test_done
|
||||
|
||||
|
||||
143
t/t7400-submodule-basic.sh
Executable file
143
t/t7400-submodule-basic.sh
Executable file
@@ -0,0 +1,143 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2007 Lars Hjemli
|
||||
#
|
||||
|
||||
test_description='Basic porcelain support for submodules
|
||||
|
||||
This test tries to verify basic sanity of the init, update and status
|
||||
subcommands of git-submodule.
|
||||
'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
#
|
||||
# Test setup:
|
||||
# -create a repository in directory lib
|
||||
# -add a couple of files
|
||||
# -add directory lib to 'superproject', this creates a DIRLINK entry
|
||||
# -add a couple of regular files to enable testing of submodule filtering
|
||||
# -mv lib subrepo
|
||||
# -add an entry to .gitmodules for path 'lib'
|
||||
#
|
||||
test_expect_success 'Prepare submodule testing' '
|
||||
mkdir lib &&
|
||||
cd lib &&
|
||||
git-init &&
|
||||
echo a >a &&
|
||||
git-add a &&
|
||||
git-commit -m "submodule commit 1" &&
|
||||
git-tag -a -m "rev-1" rev-1 &&
|
||||
rev1=$(git-rev-parse HEAD) &&
|
||||
if test -z "$rev1"
|
||||
then
|
||||
echo "[OOPS] submodule git-rev-parse returned nothing"
|
||||
false
|
||||
fi &&
|
||||
cd .. &&
|
||||
echo a >a &&
|
||||
echo z >z &&
|
||||
git-add a lib z &&
|
||||
git-commit -m "super commit 1" &&
|
||||
mv lib .subrepo &&
|
||||
GIT_CONFIG=.gitmodules git-config module.lib.url ./.subrepo
|
||||
'
|
||||
|
||||
test_expect_success 'status should only print one line' '
|
||||
lines=$(git-submodule status | wc -l) &&
|
||||
test $lines = 1
|
||||
'
|
||||
|
||||
test_expect_success 'status should initially be "missing"' '
|
||||
git-submodule status | grep "^-$rev1"
|
||||
'
|
||||
|
||||
test_expect_success 'init should fail when path is used by a file' '
|
||||
echo "hello" >lib &&
|
||||
if git-submodule init
|
||||
then
|
||||
echo "[OOPS] init should have failed"
|
||||
false
|
||||
elif test -f lib && test "$(cat lib)" != "hello"
|
||||
then
|
||||
echo "[OOPS] init failed but lib file was molested"
|
||||
false
|
||||
else
|
||||
rm lib
|
||||
fi
|
||||
'
|
||||
|
||||
test_expect_success 'init should fail when path is used by a nonempty directory' '
|
||||
mkdir lib &&
|
||||
echo "hello" >lib/a &&
|
||||
if git-submodule init
|
||||
then
|
||||
echo "[OOPS] init should have failed"
|
||||
false
|
||||
elif test "$(cat lib/a)" != "hello"
|
||||
then
|
||||
echo "[OOPS] init failed but lib/a was molested"
|
||||
false
|
||||
else
|
||||
rm lib/a
|
||||
fi
|
||||
'
|
||||
|
||||
test_expect_success 'init should work when path is an empty dir' '
|
||||
rm -rf lib &&
|
||||
mkdir lib &&
|
||||
git-submodule init &&
|
||||
head=$(cd lib && git-rev-parse HEAD) &&
|
||||
if test -z "$head"
|
||||
then
|
||||
echo "[OOPS] Failed to obtain submodule head"
|
||||
false
|
||||
elif test "$head" != "$rev1"
|
||||
then
|
||||
echo "[OOPS] Submodule head is $head but should have been $rev1"
|
||||
false
|
||||
fi
|
||||
'
|
||||
|
||||
test_expect_success 'status should be "up-to-date" after init' '
|
||||
git-submodule status | grep "^ $rev1"
|
||||
'
|
||||
|
||||
test_expect_success 'status should be "modified" after submodule commit' '
|
||||
cd lib &&
|
||||
echo b >b &&
|
||||
git-add b &&
|
||||
git-commit -m "submodule commit 2" &&
|
||||
rev2=$(git-rev-parse HEAD) &&
|
||||
cd .. &&
|
||||
if test -z "$rev2"
|
||||
then
|
||||
echo "[OOPS] submodule git-rev-parse returned nothing"
|
||||
false
|
||||
fi &&
|
||||
git-submodule status | grep "^+$rev2"
|
||||
'
|
||||
|
||||
test_expect_success 'the --cached sha1 should be rev1' '
|
||||
git-submodule --cached status | grep "^+$rev1"
|
||||
'
|
||||
|
||||
test_expect_success 'update should checkout rev1' '
|
||||
git-submodule update &&
|
||||
head=$(cd lib && git-rev-parse HEAD) &&
|
||||
if test -z "$head"
|
||||
then
|
||||
echo "[OOPS] submodule git-rev-parse returned nothing"
|
||||
false
|
||||
elif test "$head" != "$rev1"
|
||||
then
|
||||
echo "[OOPS] init did not checkout correct head"
|
||||
false
|
||||
fi
|
||||
'
|
||||
|
||||
test_expect_success 'status should be "up-to-date" after update' '
|
||||
git-submodule status | grep "^ $rev1"
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -487,4 +487,32 @@ test_expect_success \
|
||||
'gitweb_run "p=.git;a=atom"'
|
||||
test_debug 'cat gitweb.log'
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# encoding/decoding
|
||||
|
||||
test_expect_success \
|
||||
'encode(commit): utf8' \
|
||||
'. ../t3901-utf8.txt &&
|
||||
echo "UTF-8" >> file &&
|
||||
git add file &&
|
||||
git commit -F ../t3900/1-UTF-8.txt &&
|
||||
gitweb_run "p=.git;a=commit"'
|
||||
test_debug 'cat gitweb.log'
|
||||
|
||||
test_expect_success \
|
||||
'encode(commit): iso-8859-1' \
|
||||
'. ../t3901-8859-1.txt &&
|
||||
echo "ISO-8859-1" >> file &&
|
||||
git add file &&
|
||||
git config i18n.commitencoding ISO-8859-1 &&
|
||||
git commit -F ../t3900/ISO-8859-1.txt &&
|
||||
git config --unset i18n.commitencoding &&
|
||||
gitweb_run "p=.git;a=commit"'
|
||||
test_debug 'cat gitweb.log'
|
||||
|
||||
test_expect_success \
|
||||
'encode(log): utf-8 and iso-8859-1' \
|
||||
'gitweb_run "p=.git;a=log"'
|
||||
test_debug 'cat gitweb.log'
|
||||
|
||||
test_done
|
||||
|
||||
Reference in New Issue
Block a user