Merge commit '7be003b0261d13c99ed8d6764d20676a2b5c8347'

This commit is contained in:
Johannes Sixt
2007-07-21 16:29:42 +02:00
43 changed files with 1768 additions and 1462 deletions

4
.gitignore vendored
View File

@@ -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

View File

@@ -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>

View 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

View File

@@ -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.

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View 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

View File

@@ -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
----------

View File

@@ -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

View File

@@ -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" : "");

View File

@@ -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++;
}

View File

@@ -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));
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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
View File

@@ -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,

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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/ /&nbsp;/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/ /&nbsp;/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";

View File

@@ -9,6 +9,7 @@
#include "diff.h"
#include "revision.h"
#include "exec_cmd.h"
#include "remote.h"
#include <expat.h>

View File

@@ -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,

View File

@@ -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);

View File

@@ -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;

View File

@@ -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
View File

@@ -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
View File

@@ -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
View 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
View 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

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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
View 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

View File

@@ -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