Merge branch 'maint' to sync with 1.5.2.1
authorJunio C Hamano <junkio@cox.net>
Sun, 3 Jun 2007 02:36:20 +0000 (19:36 -0700)
committerJunio C Hamano <junkio@cox.net>
Sun, 3 Jun 2007 03:01:47 +0000 (20:01 -0700)
99 files changed:
.gitignore
.mailmap
Documentation/RelNotes-1.5.3.txt [new file with mode: 0644]
Documentation/SubmittingPatches
Documentation/cmd-list.perl
Documentation/config.txt
Documentation/git-am.txt
Documentation/git-applymbox.txt [deleted file]
Documentation/git-applypatch.txt [deleted file]
Documentation/git-cvsexportcommit.txt
Documentation/git-describe.txt
Documentation/git-gc.txt
Documentation/git-ls-tree.txt
Documentation/git-mailinfo.txt
Documentation/git-mailsplit.txt
Documentation/git-merge.txt
Documentation/git-name-rev.txt
Documentation/git-pack-objects.txt
Documentation/git-repack.txt
Documentation/git-rev-list.txt
Documentation/git-submodule.txt [new file with mode: 0644]
Documentation/git-update-ref.txt
Documentation/hooks.txt
Documentation/merge-options.txt
GIT-VERSION-GEN
Makefile
RelNotes
archive-tar.c
archive-zip.c
builtin-apply.c
builtin-archive.c
builtin-branch.c
builtin-count-objects.c
builtin-describe.c
builtin-fetch--tool.c
builtin-fsck.c
builtin-gc.c
builtin-ls-tree.c
builtin-mailsplit.c
builtin-name-rev.c
builtin-pack-objects.c
builtin-pack-refs.c
builtin-push.c
builtin-reflog.c
builtin-revert.c
builtin-update-index.c
builtin-update-ref.c
builtin.h
cache-tree.c
cache.h
config.c
connect.c
contrib/workdir/git-new-workdir
csum-file.c
csum-file.h
diff-delta.c
diff.c
dir.c
dir.h
entry.c
environment.c
fast-import.c
fetch-pack.c
git-applymbox.sh [deleted file]
git-applypatch.sh [deleted file]
git-checkout.sh
git-cvsexportcommit.perl
git-cvsserver.perl
git-fetch.sh
git-merge.sh
git-pull.sh
git-rebase.sh
git-repack.sh
git-submodule.sh [new file with mode: 0755]
gitweb/gitweb.perl
http-push.c
list-objects.c
pack-check.c
pack-redundant.c
peek-remote.c
progress.c
progress.h
read-cache.c
receive-pack.c
refs.c
refs.h
remote.c [new file with mode: 0644]
remote.h [new file with mode: 0644]
revision.c
run-command.c
run-command.h
send-pack.c
sha1_file.c
sha1_name.c
t/t6101-rev-parse-parents.sh
t/t7400-submodule-basic.sh [new file with mode: 0755]
t/t9400-git-cvsserver-server.sh
t/t9500-gitweb-standalone-no-errors.sh [new file with mode: 0755]
tree.c
index 4dc0c395fa7d6e7cff7588ee66c928c954cc014d..15aed7063134c041011fe3582627ad19c3f381da 100644 (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
index 4e0615e9beb06928e9440e3dd60014cc265f9ee5..aa8ee6b3f1671bbc3a07af7be544c4f83d0d7b1b 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -7,6 +7,8 @@
 
 Aneesh Kumar K.V <aneesh.kumar@gmail.com>
 Chris Shoemaker <c.shoemaker@cox.net>
+Dana L. How <danahow@gmail.com>
+Dana L. How <how@deathvalley.cswitch.com>
 Daniel Barkalow <barkalow@iabervon.org>
 David Kågedal <davidk@lysator.liu.se>
 Fredrik Kuivinen <freku045@student.liu.se>
@@ -19,8 +21,8 @@ Jon Loeliger <jdl@freescale.com>
 Jon Seymour <jon@blackcubes.dyndns.org>
 Karl Hasselström <kha@treskal.com>
 Kent Engstrom <kent@lysator.liu.se>
-Lars Doelle <lars.doelle@on-line.de>
 Lars Doelle <lars.doelle@on-line ! de>
+Lars Doelle <lars.doelle@on-line.de>
 Lukas Sandström <lukass@etek.chalmers.se>
 Martin Langhoff <martin@catalyst.net.nz>
 Michele Ballabio <barra_cuda@katamail.com>
@@ -34,12 +36,11 @@ Sean Estabrooks <seanlkml@sympatico.ca>
 Shawn O. Pearce <spearce@spearce.org>
 Theodore Ts'o <tytso@mit.edu>
 Tony Luck <tony.luck@intel.com>
-Uwe Kleine-König <zeisberg@informatik.uni-freiburg.de>
 Uwe Kleine-König <Uwe_Zeisberger@digi.com>
-Uwe Kleine-König <uzeisberger@io.fsforth.de>
 Uwe Kleine-König <ukleinek@informatik.uni-freiburg.de>
+Uwe Kleine-König <uzeisberger@io.fsforth.de>
+Uwe Kleine-König <zeisberg@informatik.uni-freiburg.de>
 Ville Skyttä <scop@xemacs.org>
 YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
 anonymous <linux@horizon.com>
 anonymous <linux@horizon.net>
-Dana L. How <how@deathvalley.cswitch.com>
diff --git a/Documentation/RelNotes-1.5.3.txt b/Documentation/RelNotes-1.5.3.txt
new file mode 100644 (file)
index 0000000..d111661
--- /dev/null
@@ -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
index b94d9a816664333e0b1767c22d1639b8839cee24..b5f2ecd2371c390c7cfb4132ae4f0dc9df1eab43 100644 (file)
@@ -239,7 +239,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 +247,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.
index 443802a9a3e4982c33f831217602ea600f0f2775..a181f753e0fefdeb036a826b8c0d0a5b54d0ae25 100755 (executable)
@@ -72,8 +72,6 @@ sub format_one {
 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 @@ sub format_one {
 git-ssh-upload                          synchingrepositories
 git-status                              mainporcelain
 git-stripspace                          purehelpers
+git-submodule                           mainporcelain
 git-svn                                 foreignscminterface
 git-svnimport                           foreignscminterface
 git-symbolic-ref                        plumbingmanipulators
index 7d9afe20f91c3922b909ef5e217c1b7640e2c600..5868d587a905e0aaa55194c7716f9e258a030474 100644 (file)
@@ -204,23 +204,16 @@ core.warnAmbiguousRefs::
        and might match multiple refs in the .git/refs/ tree. True by default.
 
 core.compression::
+       An integer -1..9, indicating a default compression level.
+       -1 is the zlib default. 0 means no compression,
+       and 1..9 are various speed/size tradeoffs, 9 being slowest.
+
+core.loosecompression::
        An integer -1..9, indicating the compression level for objects that
-       are not in a pack file. -1 is the zlib and git default. 0 means no
+       are not in a pack file. -1 is the zlib default. 0 means no
        compression, and 1..9 are various speed/size tradeoffs, 9 being
-       slowest.
-
-core.legacyheaders::
-       A boolean which
-       changes the format of loose objects so that they are more
-       efficient to pack and to send out of the repository over git
-       native protocol, since v1.4.2.  However, loose objects
-       written in the new format cannot be read by git older than
-       that version; people fetching from your repository using
-       older versions of git over dumb transports (e.g. http)
-       will also be affected.
-+
-To let git use the new loose object format, you have to
-set core.legacyheaders to false.
+       slowest.  If not set,  defaults to core.compression.  If that is
+       not set,  defaults to 0 (best speed).
 
 core.packedGitWindowSize::
        Number of bytes of a pack file to map into memory in a
@@ -397,6 +390,11 @@ format.suffix::
        `.patch`. Use this variable to change that suffix (make sure to
        include the dot if you want it).
 
+gc.aggressiveWindow::
+       The window size parameter used in the delta compression
+       algorithm used by 'git gc --aggressive'.  This defaults
+       to 10.
+
 gc.packrefs::
        `git gc` does not run `git pack-refs` in a bare repository by
        default so that older dumb-transport clients can still fetch
@@ -563,6 +561,22 @@ pack.depth::
        The maximum delta depth used by gitlink:git-pack-objects[1] when no
        maximum depth is given on the command line. Defaults to 50.
 
+pack.compression::
+       An integer -1..9, indicating the compression level for objects
+       in a pack file. -1 is the zlib default. 0 means no
+       compression, and 1..9 are various speed/size tradeoffs, 9 being
+       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.
index ba79773f79fc8823f1c611e51b163866ada26306..f3387f5d09959c1e94cb2e140d5ccc617b45ebb1 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
 'git-am' [--signoff] [--dotest=<dir>] [--keep] [--utf8 | --no-utf8]
          [--3way] [--interactive] [--binary]
          [--whitespace=<option>] [-C<n>] [-p<n>]
-         <mbox>...
+         <mbox>|<Maildir>...
 'git-am' [--skip | --resolved]
 
 DESCRIPTION
@@ -23,9 +23,10 @@ current branch.
 
 OPTIONS
 -------
-<mbox>...::
+<mbox>|<Maildir>...::
        The list of mailbox files to read patches from. If you do not
-       supply this argument, reads from the standard input.
+       supply this argument, reads from the standard input. If you supply
+       directories, they'll be treated as Maildirs.
 
 -s, --signoff::
        Add `Signed-off-by:` line to the commit message, using
@@ -126,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.
@@ -144,7 +144,7 @@ names.
 
 SEE ALSO
 --------
-gitlink:git-applymbox[1], gitlink:git-applypatch[1], gitlink:git-apply[1].
+gitlink:git-apply[1].
 
 
 Author
diff --git a/Documentation/git-applymbox.txt b/Documentation/git-applymbox.txt
deleted file mode 100644 (file)
index ea919ba..0000000
+++ /dev/null
@@ -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
-
diff --git a/Documentation/git-applypatch.txt b/Documentation/git-applypatch.txt
deleted file mode 100644 (file)
index 451434a..0000000
+++ /dev/null
@@ -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
-
index fd7f54093fc3d0a85126a0826affb712096aa144..da5c242241c01d6d7e7a5d648ff58263cc3e01dd 100644 (file)
@@ -8,7 +8,7 @@ git-cvsexportcommit - Export a single commit to a CVS checkout
 
 SYNOPSIS
 --------
-'git-cvsexportcommit' [-h] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
+'git-cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
 
 
 DESCRIPTION
@@ -58,6 +58,9 @@ OPTIONS
        Prepend the commit message with the provided prefix. 
        Useful for patch series and the like.
 
+-u::
+       Update affected files from cvs repository before attempting export.
+
 -v::
        Verbose.
 
index 47a583d3a605d95fb860e6ea7142cebf374ec5aa..dc47b65ced36ac856ad23e2bebd3f3342b50471a 100644 (file)
@@ -8,7 +8,7 @@ git-describe - Show the most recent tag that is reachable from a commit
 
 SYNOPSIS
 --------
-'git-describe' [--all] [--tags] [--abbrev=<n>] <committish>...
+'git-describe' [--all] [--tags] [--contains] [--abbrev=<n>] <committish>...
 
 DESCRIPTION
 -----------
@@ -31,6 +31,11 @@ OPTIONS
        Instead of using only the annotated tags, use any tag
        found in `.git/refs/tags`.
 
+--contains::
+       Instead of finding the tag that predates the commit, find
+       the tag that comes after the commit, and thus contains it.
+       Automatically implies --tags.
+
 --abbrev=<n>::
        Instead of using the default 8 hexadecimal digits as the
        abbreviated object name, use <n> digits.
index bc1658434a61bdba41699859f103a3c8e0802d95..c7742ca9630b13d1eeef16d175f8ca840ddff4b0 100644 (file)
@@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository
 
 SYNOPSIS
 --------
-'git-gc' [--prune]
+'git-gc' [--prune] [--aggressive]
 
 DESCRIPTION
 -----------
@@ -35,6 +35,13 @@ OPTIONS
        repository at the same time (e.g. never use this option
        in a cron script).
 
+--aggressive::
+       Usually 'git-gc' runs very quickly while providing good disk
+       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 used occasionally; every
+       few hundred changesets or so.
 
 Configuration
 -------------
@@ -67,6 +74,13 @@ The optional configuration variable 'gc.packrefs' determines if
 is not run in bare repositories by default, to allow older dumb-transport
 clients fetch from the repository,  but this will change in the future.
 
+The optional configuration variable 'gc.aggressiveWindow' controls how
+much time is spent optimizing the delta compression of the objects in
+the repository when the --aggressive option is specified.  The larger
+the value, the more time is spent optimizing the delta compression.  See
+the documentation for the --window' option in gitlink:git-repack[1] for
+more details.  This defaults to 10.
+
 See Also
 --------
 gitlink:git-prune[1]
index 78993940811c96752e87f7b9e7c1381066056950..ad7f1b9202c414a47573864a320ca766a147753e 100644 (file)
@@ -9,7 +9,7 @@ git-ls-tree - List the contents of a tree object
 SYNOPSIS
 --------
 [verse]
-'git-ls-tree' [-d] [-r] [-t] [-z]
+'git-ls-tree' [-d] [-r] [-t] [-l] [-z]
            [--name-only] [--name-status] [--full-name] [--abbrev=[<n>]]
            <tree-ish> [paths...]
 
@@ -36,6 +36,10 @@ OPTIONS
        Show tree entries even when going to recurse them. Has no effect
        if '-r' was not passed. '-d' implies '-t'.
 
+-l::
+--long::
+       Show object size of blob (file) entries.
+
 -z::
        \0 line termination on output.
 
@@ -65,6 +69,14 @@ Output Format
 When the `-z` option is not used, TAB, LF, and backslash characters
 in pathnames are represented as `\t`, `\n`, and `\\`, respectively.
 
+When the `-l` option is used, format changes to
+
+        <mode> SP <type> SP <object> SP <object size> TAB <file>
+
+Object size identified by <object> is given in bytes, and right-justified
+with minimum width of 7 characters.  Object size is given only for blobs
+(file) entries; for other entries `-` character is used in place of size.
+
 
 Author
 ------
index 8eadcebfcf5c26c599cde0d939d1046de69f2335..16956951dd122698f7602b78c91be999348b61e4 100644 (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.
 
index c11d6a530f67857bb28a5b3cee48bc5b01f5cb92..abb0903696c1239299807951e7d1d75ee094a7ed 100644 (file)
@@ -7,12 +7,15 @@ git-mailsplit - Simple UNIX mbox splitter program
 
 SYNOPSIS
 --------
-'git-mailsplit' [-b] [-f<nn>] [-d<prec>] -o<directory> [--] [<mbox>...]
+'git-mailsplit' [-b] [-f<nn>] [-d<prec>] -o<directory> [--] [<mbox>|<Maildir>...]
 
 DESCRIPTION
 -----------
-Splits a mbox file into a list of files: "0001" "0002" ..  in the specified
-directory so you can process them further from there.
+Splits a mbox file or a Maildir into a list of files: "0001" "0002" ..  in the
+specified directory so you can process them further from there.
+
+IMPORTANT: Maildir splitting relies upon filenames being sorted to output
+patches in the correct order.
 
 OPTIONS
 -------
@@ -20,6 +23,10 @@ OPTIONS
        Mbox file to split.  If not given, the mbox is read from
        the standard input.
 
+<Maildir>::
+       Root of the Maildir to split. This directory should contain the cur, tmp
+       and new subdirectories.
+
 <directory>::
        Directory in which to place the individual messages.
 
index 9c08efa53abc2797a7b7ca0e589d83b9a4387987..912ef29efce71d944af59588c6493591efc96bfc 100644 (file)
@@ -9,7 +9,7 @@ git-merge - Join two or more development histories together
 SYNOPSIS
 --------
 [verse]
-'git-merge' [-n] [--no-commit] [--squash] [-s <strategy>]...
+'git-merge' [-n] [--summary] [--no-commit] [--squash] [-s <strategy>]...
        [-m <msg>] <remote> <remote>...
 
 DESCRIPTION
index d6c8bf800f4456ea63ff7d9b0c01b2d4f0d9d179..9a1645d29272d86a4b66cd96e7312e8380850891 100644 (file)
@@ -34,6 +34,13 @@ OPTIONS
        Read from stdin, append "(<rev_name>)" to all sha1's of nameable
        commits, and pass to stdout
 
+--name-only::
+       Instead of printing both the SHA-1 and the name, print only
+       the name.  If given with --tags the usual tag prefix of
+       "tags/" is also ommitted from the name, matching the output
+       of gitlink::git-describe[1] more closely.  This option
+       cannot be combined with --stdin.
+
 EXAMPLE
 -------
 
index bd3ee456e336add5d880d6a1192646220e7c34d7..cfe127ad9ea560ac3354a92d91ff5064ce37e1df 100644 (file)
@@ -85,6 +85,11 @@ base-name::
        times to get to the necessary object.
        The default value for --window is 10 and --depth is 50.
 
+--max-pack-size=<n>::
+       Maximum size of each output packfile, expressed in MiB.
+       If specified,  multiple packfiles may be created.
+       The default is unlimited.
+
 --incremental::
        This flag causes an object already in a pack ignored
        even if it appears in the standard input.
@@ -127,6 +132,25 @@ base-name::
        This flag tells the command not to reuse existing deltas
        but compute them from scratch.
 
+--no-reuse-object::
+       This flag tells the command not to reuse existing object data at all,
+       including non deltified object, forcing recompression of everything.
+       This implies --no-reuse-delta. Useful only in the obscure case where
+       wholesale enforcement of a different compression level on the
+       packed data is desired.
+
+--compression=[N]::
+       Specifies compression level for newly-compressed data in the
+       generated pack.  If not specified,  pack compression level is
+       determined first by pack.compression,  then by core.compression,
+       and defaults to -1,  the zlib default,  if neither is set.
+       Data copied from loose objects will be recompressed
+       if core.legacyheaders was true when they were created or if
+       the loose compression level (see core.loosecompression and
+       core.compression) is now a different value than the pack
+       compression level.  Add --no-reuse-object if you want to force
+       a uniform compression level on all data no matter the source.
+
 --delta-base-offset::
        A packed archive can express base object of a delta as
        either 20-byte object name or as an offset in the
index cc3b0b21c727349eb3a51d9837bc006b987537c3..2847c9b8d7038f0010e26f61091552cb600f728b 100644 (file)
@@ -65,6 +65,11 @@ OPTIONS
        to be applied that many times to get to the necessary object.
        The default value for --window is 10 and --depth is 50.
 
+--max-pack-size=<n>::
+       Maximum size of each output packfile, expressed in MiB.
+       If specified,  multiple packfiles may be created.
+       The default is unlimited.
+
 
 Configuration
 -------------
index c3c2043d183e0e6c7292f5af7c28af57958c4dcd..0dba73f27607560d3e868306af78bad83dc3bfff 100644 (file)
@@ -25,6 +25,7 @@ SYNOPSIS
             [ \--cherry-pick ]
             [ \--encoding[=<encoding>] ]
             [ \--(author|committer|grep)=<pattern> ]
+            [ \--regexp-ignore-case ] [ \--extended-regexp ]
             [ \--date={local|relative|default} ]
             [ [\--objects | \--objects-edge] [ \--unpacked ] ]
             [ \--pretty | \--header ]
@@ -214,6 +215,15 @@ limiting may be applied.
        Limit the commits output to ones with log message that
        matches the specified pattern (regular expression).
 
+--regexp-ignore-case::
+
+       Match the regexp limiting patterns without regard to letters case.
+
+--extended-regexp::
+
+       Consider the limiting patterns to be extended regular expressions
+       instead of the default basic regular expressions.
+
 --remove-empty::
 
        Stop when a given path disappears from the tree.
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
new file mode 100644 (file)
index 0000000..cb0424f
--- /dev/null
@@ -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
index 9424feab320afcaa5a44bd2c2c6c86728bd8d004..f222616591f157beada3c5355ee0e56d68171761 100644 (file)
@@ -7,7 +7,7 @@ git-update-ref - Update the object name stored in a ref safely
 
 SYNOPSIS
 --------
-'git-update-ref' [-m <reason>] (-d <ref> <oldvalue> | <ref> <newvalue> [<oldvalue>])
+'git-update-ref' [-m <reason>] (-d <ref> <oldvalue> | [--no-deref] <ref> <newvalue> [<oldvalue>])
 
 DESCRIPTION
 -----------
@@ -36,6 +36,9 @@ them and update them as a regular file (i.e. it will allow the
 filesystem to follow them, but will overwrite such a symlink to
 somewhere else with a regular filename).
 
+If --no-deref is given, <ref> itself is overwritten, rather than
+the result of following the symbolic pointers.
+
 In general, using
 
        git-update-ref HEAD "$head"
index aabb9750fddbc28091c46780ccf7b835b62b04c7..6836477ca816a6caa860960c3456cfbbf5cd32f1 100644 (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
 ----------
index 182cef54be5963ff0f32a24329ca195607413e29..56f1d8d69d05bb4aa26d0dad6e576332588090e8 100644 (file)
@@ -1,3 +1,7 @@
+--summary::
+       Show a diffstat at the end of the merge. The diffstat is also
+       controlled by the configuration option merge.diffstat.
+
 -n, \--no-summary::
        Do not show diffstat at the end of the merge.
 
index bd30398dd3bb83cf9b91f465c3ae67c365d8e9a6..06c360b267f0d9e459e44ccaa81dd16b4384c627 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.5.2.1.GIT
+DEF_VER=v1.5.2.GIT
 
 LF='
 '
index fb11fa1987e162c13a9fba492da41e26fdad7195..a11ff6054926a92e49939eb53ab342c90704b295 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -206,10 +206,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 \
@@ -296,7 +296,8 @@ LIB_H = \
        diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.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 \
-       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 \
+       mailmap.h remote.h
 
 DIFF_OBJS = \
        diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@ -318,7 +319,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 \
@@ -941,7 +942,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)
 
@@ -954,26 +955,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
index 403fb970379c478ed49c50488a39705afe11d188..0de5e663a2ba03a15eb6711079c6bff0cba023cb 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.5.2.1.txt
\ No newline at end of file
+Documentation/RelNotes-1.5.3.txt
\ No newline at end of file
index 33e76576f149d89922deda1de5548be7bc42d0eb..66fe3e375b545613faba4e051dc41c1acb5d8cee 100644 (file)
@@ -167,7 +167,7 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
        } else {
                if (verbose)
                        fprintf(stderr, "%.*s\n", path->len, path->buf);
-               if (S_ISDIR(mode) || S_ISDIRLNK(mode)) {
+               if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
                        *header.typeflag = TYPEFLAG_DIR;
                        mode = (mode | 0777) & ~tar_umask;
                } else if (S_ISLNK(mode)) {
@@ -280,7 +280,7 @@ static int write_tar_entry(const unsigned char *sha1,
        memcpy(path.buf + baselen, filename, filenamelen);
        path.len = baselen + filenamelen;
        path.buf[path.len] = '\0';
-       if (S_ISDIR(mode) || S_ISDIRLNK(mode)) {
+       if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
                strbuf_append_string(&path, "/");
                buffer = NULL;
                size = 0;
index 3cbf6bb8ac4045803140ca5019126015e817155e..444e1623db66fe4f5d8983e1e9e2cf4083b005b4 100644 (file)
@@ -182,7 +182,7 @@ static int write_zip_entry(const unsigned char *sha1,
                goto out;
        }
 
-       if (S_ISDIR(mode) || S_ISDIRLNK(mode)) {
+       if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
                method = 0;
                attr2 = 16;
                result = (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
index 0399743c4e2288a0812faeea92c07a5f6a13fccc..e71789803749b22a3ec041b2bce533957cea1c72 100644 (file)
@@ -1671,6 +1671,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
        char *new = xmalloc(size);
        const char *oldlines, *newlines;
        int oldsize = 0, newsize = 0;
+       int new_blank_lines_at_end = 0;
        unsigned long leading, trailing;
        int pos, lines;
 
@@ -1678,6 +1679,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
                char first;
                int len = linelen(patch, size);
                int plen;
+               int added_blank_line = 0;
 
                if (!len)
                        break;
@@ -1699,6 +1701,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
                        else if (first == '+')
                                first = '-';
                }
+
                switch (first) {
                case '\n':
                        /* Newer GNU diff, empty context line */
@@ -1716,9 +1719,14 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
                                break;
                /* Fall-through for ' ' */
                case '+':
-                       if (first != '+' || !no_add)
-                               newsize += apply_line(new + newsize, patch,
-                                                     plen);
+                       if (first != '+' || !no_add) {
+                               int added = apply_line(new + newsize, patch,
+                                                      plen);
+                               newsize += added;
+                               if (first == '+' &&
+                                   added == 1 && new[newsize-1] == '\n')
+                                       added_blank_line = 1;
+                       }
                        break;
                case '@': case '\\':
                        /* Ignore it, we already handled it */
@@ -1728,6 +1736,10 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
                                error("invalid start of line: '%c'", first);
                        return -1;
                }
+               if (added_blank_line)
+                       new_blank_lines_at_end++;
+               else
+                       new_blank_lines_at_end = 0;
                patch += len;
                size -= len;
        }
@@ -1770,9 +1782,16 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
                if (match_beginning && offset)
                        offset = -1;
                if (offset >= 0) {
-                       int diff = newsize - oldsize;
-                       unsigned long size = desc->size + diff;
-                       unsigned long alloc = desc->alloc;
+                       int diff;
+                       unsigned long size, alloc;
+
+                       if (new_whitespace == strip_whitespace &&
+                           (desc->size - oldsize - offset == 0)) /* end of file? */
+                               newsize -= new_blank_lines_at_end;
+
+                       diff = newsize - oldsize;
+                       size = desc->size + diff;
+                       alloc = desc->alloc;
 
                        /* Warn if it was necessary to reduce the number
                         * of context lines.
index 7f4e409c998ba4a864bd39032556d71693ff6793..187491bc172571b783a0be4f4dfa3d94d58bb0fe 100644 (file)
@@ -45,7 +45,7 @@ static int run_remote_archiver(const char *remote, int argc,
        }
 
        url = xstrdup(remote);
-       pid = git_connect(fd, url, exec);
+       pid = git_connect(fd, url, exec, 0);
        if (pid < 0)
                return pid;
 
index 8956d0f84213afbd0e53e39bc863c05960f524f0..a5b6bbef6e903991cf70fd5978f5f89c48c60fb5 100644 (file)
@@ -462,7 +462,7 @@ static void create_branch(const char *name, const char *start_name,
                die("Not a valid branch point: '%s'.", start_name);
        hashcpy(sha1, commit->object.sha1);
 
-       lock = lock_any_ref_for_update(ref, NULL);
+       lock = lock_any_ref_for_update(ref, NULL, 0);
        if (!lock)
                die("Failed to lock ref for update: %s.", strerror(errno));
 
index ff90ebd465002882781507ecfcfc33cab2f759fc..4274ec19500953bd7b6775e6d66271e9e116fa86 100644 (file)
@@ -111,6 +111,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++;
                }
index 165917e40db78e7105a16d6e7a1653862a29fdc5..669110cb0645629ca5b152d8328aa91d63be1550 100644 (file)
@@ -3,6 +3,7 @@
 #include "tag.h"
 #include "refs.h"
 #include "builtin.h"
+#include "exec_cmd.h"
 
 #define SEEN           (1u<<0)
 #define MAX_TAGS       (FLAG_BITS - 1)
@@ -242,12 +243,15 @@ static void describe(const char *arg, int last_one)
 int cmd_describe(int argc, const char **argv, const char *prefix)
 {
        int i;
+       int contains = 0;
 
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
 
                if (*arg != '-')
                        break;
+               else if (!strcmp(arg, "--contains"))
+                       contains = 1;
                else if (!strcmp(arg, "--debug"))
                        debug = 1;
                else if (!strcmp(arg, "--all"))
@@ -272,6 +276,16 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
 
        save_commit_buffer = 0;
 
+       if (contains) {
+               const char **args = xmalloc((4 + argc - i) * sizeof(char*));
+               args[0] = "name-rev";
+               args[1] = "--name-only";
+               args[2] = "--tags";
+               memcpy(args + 3, argv + i, (argc - i) * sizeof(char*));
+               args[3 + argc - i] = NULL;
+               return cmd_name_rev(3 + argc - i, args, prefix);
+       }
+
        if (argc <= i)
                describe("HEAD", 1);
        else
index 12adb3833cd60771f8e63b2ebaecff3769fa74e2..ed4d5de5d5e6d0bef5b29f3d21a3e58ff7778c8b 100644 (file)
@@ -42,7 +42,7 @@ static int update_ref(const char *action,
        if (!rla)
                rla = "(reflog update)";
        snprintf(msg, sizeof(msg), "%s: %s", rla, action);
-       lock = lock_any_ref_for_update(refname, oldval);
+       lock = lock_any_ref_for_update(refname, oldval, 0);
        if (!lock)
                return 1;
        if (write_ref_sha1(lock, sha1, msg) < 0)
index 44ce629a498f986f239e008a61d32e10968907e0..9959818ceddf52ec24d9b24b0cade558f4d43070 100644 (file)
@@ -256,7 +256,7 @@ static int fsck_tree(struct tree *item)
                case S_IFREG | 0644:
                case S_IFLNK:
                case S_IFDIR:
-               case S_IFDIRLNK:
+               case S_IFGITLINK:
                        break;
                /*
                 * This is nonstandard, but we had a few of these
@@ -668,7 +668,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));
                }
@@ -715,7 +718,7 @@ int cmd_fsck(int argc, char **argv, const char *prefix)
                        struct object *obj;
 
                        mode = ntohl(active_cache[i]->ce_mode);
-                       if (S_ISDIRLNK(mode))
+                       if (S_ISGITLINK(mode))
                                continue;
                        blob = lookup_blob(active_cache[i]->sha1);
                        if (!blob)
index 3b1f8c2f3e6d352b43c671a409898c43cf1119ec..45025fba30c1fb594a696c26a7eac11862cfd8f9 100644 (file)
 
 #define FAILED_RUN "failed to run %s"
 
-static const char builtin_gc_usage[] = "git-gc [--prune]";
+static const char builtin_gc_usage[] = "git-gc [--prune] [--aggressive]";
 
-static int pack_refs = -1;
+static int pack_refs = 1;
+static int aggressive_window = -1;
 
-static const char *argv_pack_refs[] = {"pack-refs", "--prune", NULL};
+#define MAX_ADD 10
+static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL};
 static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL};
-static const char *argv_repack[] = {"repack", "-a", "-d", "-l", NULL};
+static const char *argv_repack[MAX_ADD] = {"repack", "-a", "-d", "-l", NULL};
 static const char *argv_prune[] = {"prune", NULL};
 static const char *argv_rerere[] = {"rerere", "gc", NULL};
 
@@ -34,13 +36,31 @@ static int gc_config(const char *var, const char *value)
                        pack_refs = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "gc.aggressivewindow")) {
+               aggressive_window = git_config_int(var, value);
+               return 0;
+       }
        return git_default_config(var, value);
 }
 
+static void append_option(const char **cmd, const char *opt, int max_length)
+{
+       int i;
+
+       for (i = 0; cmd[i]; i++)
+               ;
+
+       if (i + 2 >= max_length)
+               die("Too many options specified");
+       cmd[i++] = opt;
+       cmd[i] = NULL;
+}
+
 int cmd_gc(int argc, const char **argv, const char *prefix)
 {
        int i;
        int prune = 0;
+       char buf[80];
 
        git_config(gc_config);
 
@@ -53,6 +73,14 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                        prune = 1;
                        continue;
                }
+               if (!strcmp(arg, "--aggressive")) {
+                       append_option(argv_repack, "-f", MAX_ADD);
+                       if (aggressive_window > 0) {
+                               sprintf(buf, "--window=%d", aggressive_window);
+                               append_option(argv_repack, buf, MAX_ADD);
+                       }
+                       continue;
+               }
                /* perhaps other parameters later... */
                break;
        }
index 1cb4dca277b511315d3b914239c57621fc60bcf3..cb4be4fabb84bafd5518e81d2fd0ed6ee191641c 100644 (file)
@@ -15,6 +15,7 @@ static int line_termination = '\n';
 #define LS_TREE_ONLY 2
 #define LS_SHOW_TREES 4
 #define LS_NAME_ONLY 8
+#define LS_SHOW_SIZE 16
 static int abbrev;
 static int ls_options;
 static const char **pathspec;
@@ -22,7 +23,7 @@ static int chomp_prefix;
 static const char *ls_tree_prefix;
 
 static const char ls_tree_usage[] =
-       "git-ls-tree [-d] [-r] [-t] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=<n>]] <tree-ish> [path...]";
+       "git-ls-tree [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=<n>]] <tree-ish> [path...]";
 
 static int show_recursive(const char *base, int baselen, const char *pathname)
 {
@@ -59,8 +60,9 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen,
 {
        int retval = 0;
        const char *type = blob_type;
+       unsigned long size;
 
-       if (S_ISDIRLNK(mode)) {
+       if (S_ISGITLINK(mode)) {
                /*
                 * Maybe we want to have some recursive version here?
                 *
@@ -92,10 +94,24 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen,
            (baselen < chomp_prefix || memcmp(ls_tree_prefix, base, chomp_prefix)))
                return 0;
 
-       if (!(ls_options & LS_NAME_ONLY))
-               printf("%06o %s %s\t", mode, type,
-                               abbrev ? find_unique_abbrev(sha1,abbrev)
-                                       : sha1_to_hex(sha1));
+       if (!(ls_options & LS_NAME_ONLY)) {
+               if (ls_options & LS_SHOW_SIZE) {
+                       if (!strcmp(type, blob_type)) {
+                               sha1_object_info(sha1, &size);
+                               printf("%06o %s %s %7lu\t", mode, type,
+                                      abbrev ? find_unique_abbrev(sha1, abbrev)
+                                             : sha1_to_hex(sha1),
+                                      size);
+                       } else
+                               printf("%06o %s %s %7c\t", mode, type,
+                                      abbrev ? find_unique_abbrev(sha1, abbrev)
+                                             : sha1_to_hex(sha1),
+                                      '-');
+               } else
+                       printf("%06o %s %s\t", mode, type,
+                              abbrev ? find_unique_abbrev(sha1, abbrev)
+                                     : sha1_to_hex(sha1));
+       }
        write_name_quoted(base + chomp_prefix, baselen - chomp_prefix,
                          pathname,
                          line_termination, stdout);
@@ -126,12 +142,19 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
                case 't':
                        ls_options |= LS_SHOW_TREES;
                        break;
+               case 'l':
+                       ls_options |= LS_SHOW_SIZE;
+                       break;
                case '-':
                        if (!strcmp(argv[1]+2, "name-only") ||
                            !strcmp(argv[1]+2, "name-status")) {
                                ls_options |= LS_NAME_ONLY;
                                break;
                        }
+                       if (!strcmp(argv[1]+2, "long")) {
+                               ls_options |= LS_SHOW_SIZE;
+                               break;
+                       }
                        if (!strcmp(argv[1]+2, "full-name")) {
                                chomp_prefix = 0;
                                break;
index 3bca855aae857cde15f2a249f8e0a5c30b3e7d82..c9384255551f316f2416ce49372b820a6d8e3632 100644 (file)
@@ -6,9 +6,10 @@
  */
 #include "cache.h"
 #include "builtin.h"
+#include "path-list.h"
 
 static const char git_mailsplit_usage[] =
-"git-mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> <mbox>...";
+"git-mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> <mbox>|<Maildir>...";
 
 static int is_from_line(const char *line, int len)
 {
@@ -96,44 +97,107 @@ static int split_one(FILE *mbox, const char *name, int allow_bare)
        exit(1);
 }
 
-int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip)
+static int populate_maildir_list(struct path_list *list, const char *path)
 {
-       char *name = xmalloc(strlen(dir) + 2 + 3 * sizeof(skip));
+       DIR *dir;
+       struct dirent *dent;
+
+       if ((dir = opendir(path)) == NULL) {
+               error("cannot opendir %s (%s)", path, strerror(errno));
+               return -1;
+       }
+
+       while ((dent = readdir(dir)) != NULL) {
+               if (dent->d_name[0] == '.')
+                       continue;
+               path_list_insert(dent->d_name, list);
+       }
+
+       closedir(dir);
+
+       return 0;
+}
+
+static int split_maildir(const char *maildir, const char *dir,
+       int nr_prec, int skip)
+{
+       char file[PATH_MAX];
+       char curdir[PATH_MAX];
+       char name[PATH_MAX];
        int ret = -1;
+       int i;
+       struct path_list list = {NULL, 0, 0, 1};
 
-       while (*mbox) {
-               const char *file = *mbox++;
-               FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
-               int file_done = 0;
+       snprintf(curdir, sizeof(curdir), "%s/cur", maildir);
+       if (populate_maildir_list(&list, curdir) < 0)
+               goto out;
 
-               if ( !f ) {
-                       error("cannot open mbox %s", file);
+       for (i = 0; i < list.nr; i++) {
+               FILE *f;
+               snprintf(file, sizeof(file), "%s/%s", curdir, list.items[i].path);
+               f = fopen(file, "r");
+               if (!f) {
+                       error("cannot open mail %s (%s)", file, strerror(errno));
                        goto out;
                }
 
                if (fgets(buf, sizeof(buf), f) == NULL) {
-                       if (f == stdin)
-                               break; /* empty stdin is OK */
-                       error("cannot read mbox %s", file);
+                       error("cannot read mail %s (%s)", file, strerror(errno));
                        goto out;
                }
 
-               while (!file_done) {
-                       sprintf(name, "%s/%0*d", dir, nr_prec, ++skip);
-                       file_done = split_one(f, name, allow_bare);
+               sprintf(name, "%s/%0*d", dir, nr_prec, ++skip);
+               split_one(f, name, 1);
+
+               fclose(f);
+       }
+
+       path_list_clear(&list, 1);
+
+       ret = skip;
+out:
+       return ret;
+}
+
+int split_mbox(const char *file, const char *dir, int allow_bare,
+               int nr_prec, int skip)
+{
+       char name[PATH_MAX];
+       int ret = -1;
+
+       FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
+       int file_done = 0;
+
+       if (!f) {
+               error("cannot open mbox %s", file);
+               goto out;
+       }
+
+       if (fgets(buf, sizeof(buf), f) == NULL) {
+               /* empty stdin is OK */
+               if (f != stdin) {
+                       error("cannot read mbox %s", file);
+                       goto out;
                }
+               file_done = 1;
+       }
 
-               if (f != stdin)
-                       fclose(f);
+       while (!file_done) {
+               sprintf(name, "%s/%0*d", dir, nr_prec, ++skip);
+               file_done = split_one(f, name, allow_bare);
        }
+
+       if (f != stdin)
+               fclose(f);
+
        ret = skip;
 out:
-       free(name);
        return ret;
 }
+
 int cmd_mailsplit(int argc, const char **argv, const char *prefix)
 {
-       int nr = 0, nr_prec = 4, ret;
+       int nr = 0, nr_prec = 4, num = 0;
        int allow_bare = 0;
        const char *dir = NULL;
        const char **argp;
@@ -186,9 +250,41 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
                        argp = stdin_only;
        }
 
-       ret = split_mbox(argp, dir, allow_bare, nr_prec, nr);
-       if (ret != -1)
-               printf("%d\n", ret);
+       while (*argp) {
+               const char *arg = *argp++;
+               struct stat argstat;
+               int ret = 0;
+
+               if (arg[0] == '-' && arg[1] == 0) {
+                       ret = split_mbox(arg, dir, allow_bare, nr_prec, nr);
+                       if (ret < 0) {
+                               error("cannot split patches from stdin");
+                               return 1;
+                       }
+                       num += (ret - nr);
+                       nr = ret;
+                       continue;
+               }
+
+               if (stat(arg, &argstat) == -1) {
+                       error("cannot stat %s (%s)", arg, strerror(errno));
+                       return 1;
+               }
+
+               if (S_ISDIR(argstat.st_mode))
+                       ret = split_maildir(arg, dir, nr_prec, nr);
+               else
+                       ret = split_mbox(arg, dir, allow_bare, nr_prec, nr);
+
+               if (ret < 0) {
+                       error("cannot split patches from %s", arg);
+                       return 1;
+               }
+               num += (ret - nr);
+               nr = ret;
+       }
+
+       printf("%d\n", num);
 
-       return ret == -1;
+       return 0;
 }
index 2d94eaaa6a90f2efe4401873930533b12b85522f..d3c42ed67e1e2eed24c215c06706ab4b46fb5675 100644 (file)
@@ -85,6 +85,7 @@ static void name_rev(struct commit *commit,
 
 struct name_ref_data {
        int tags_only;
+       int name_only;
        const char *ref_filter;
 };
 
@@ -112,6 +113,10 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void
 
                if (!prefixcmp(path, "refs/heads/"))
                        path = path + 11;
+               else if (data->tags_only
+                   && data->name_only
+                   && !prefixcmp(path, "refs/tags/"))
+                       path = path + 10;
                else if (!prefixcmp(path, "refs/"))
                        path = path + 5;
 
@@ -151,7 +156,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
 {
        struct object_array revs = { 0, 0, NULL };
        int as_is = 0, all = 0, transform_stdin = 0;
-       struct name_ref_data data = { 0, NULL };
+       struct name_ref_data data = { 0, 0, NULL };
 
        git_config(git_default_config);
 
@@ -167,6 +172,9 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                        if (!strcmp(*argv, "--")) {
                                as_is = 1;
                                continue;
+                       } else if (!strcmp(*argv, "--name-only")) {
+                               data.name_only = 1;
+                               continue;
                        } else if (!strcmp(*argv, "--tags")) {
                                data.tags_only = 1;
                                continue;
@@ -267,14 +275,17 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                        struct object * obj = get_indexed_object(i);
                        if (!obj)
                                continue;
-                       printf("%s %s\n", sha1_to_hex(obj->sha1), get_rev_name(obj));
+                       if (!data.name_only)
+                               printf("%s ", sha1_to_hex(obj->sha1));
+                       printf("%s\n", get_rev_name(obj));
                }
        } else {
                int i;
-               for (i = 0; i < revs.nr; i++)
-                       printf("%s %s\n",
-                               revs.objects[i].name,
-                               get_rev_name(revs.objects[i].item));
+               for (i = 0; i < revs.nr; i++) {
+                       if (!data.name_only)
+                               printf("%s ", revs.objects[i].name);
+                       printf("%s\n", get_rev_name(revs.objects[i].item));
+               }
        }
 
        return 0;
index 966f843e4357d45b8c9acf96617bdacd8dc1b133..ccb25f6a9c298bd977c5f55a84dd9a20a1e54e23 100644 (file)
@@ -1,5 +1,6 @@
 #include "builtin.h"
 #include "cache.h"
+#include "attr.h"
 #include "object.h"
 #include "blob.h"
 #include "commit.h"
 #include "progress.h"
 
 static const char pack_usage[] = "\
-git-pack-objects [{ -q | --progress | --all-progress }] \n\
+git-pack-objects [{ -q | --progress | --all-progress }] [--max-pack-size=N] \n\
        [--local] [--incremental] [--window=N] [--depth=N] \n\
-       [--no-reuse-delta] [--delta-base-offset] [--non-empty] \n\
-       [--revs [--unpacked | --all]*] [--reflog] [--stdout | base-name] \n\
-       [<ref-list | <object-list]";
+       [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
+       [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
+       [--stdout | base-name] [<ref-list | <object-list]";
 
 struct object_entry {
        unsigned char sha1[20];
@@ -35,14 +36,16 @@ 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 */
        unsigned char in_pack_header_size;
        unsigned char preferred_base; /* we do not pack this, but is available
-                                      * to be used as the base objectto delta
+                                      * to be used as the base object to delta
                                       * objects against.
                                       */
+       unsigned char no_try_delta;
 };
 
 /*
@@ -52,22 +55,31 @@ struct object_entry {
  * nice "minimum seek" order.
  */
 static struct object_entry *objects;
-static uint32_t nr_objects, nr_alloc, nr_result;
+static struct object_entry **written_list;
+static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
 
 static int non_empty;
-static int no_reuse_delta;
+static int no_reuse_delta, no_reuse_object;
 static int local;
 static int incremental;
 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;
 static int depth = 50;
 static int pack_to_stdout;
 static int num_preferred_base;
 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,
@@ -346,76 +358,45 @@ static void copy_pack_data(struct sha1file *f,
        }
 }
 
-static int check_loose_inflate(unsigned char *data, unsigned long len, unsigned long expect)
-{
-       z_stream stream;
-       unsigned char fakebuf[4096];
-       int st;
-
-       memset(&stream, 0, sizeof(stream));
-       stream.next_in = data;
-       stream.avail_in = len;
-       stream.next_out = fakebuf;
-       stream.avail_out = sizeof(fakebuf);
-       inflateInit(&stream);
-
-       while (1) {
-               st = inflate(&stream, Z_FINISH);
-               if (st == Z_STREAM_END || st == Z_OK) {
-                       st = (stream.total_out == expect &&
-                             stream.total_in == len) ? 0 : -1;
-                       break;
-               }
-               if (st != Z_BUF_ERROR) {
-                       st = -1;
-                       break;
-               }
-               stream.next_out = fakebuf;
-               stream.avail_out = sizeof(fakebuf);
-       }
-       inflateEnd(&stream);
-       return st;
-}
-
-static int revalidate_loose_object(struct object_entry *entry,
-                                  unsigned char *map,
-                                  unsigned long mapsize)
-{
-       /* we already know this is a loose object with new type header. */
-       enum object_type type;
-       unsigned long size, used;
-
-       if (pack_to_stdout)
-               return 0;
-
-       used = unpack_object_header_gently(map, mapsize, &type, &size);
-       if (!used)
-               return -1;
-       map += used;
-       mapsize -= used;
-       return check_loose_inflate(map, mapsize, size);
-}
-
 static unsigned long write_object(struct sha1file *f,
-                                 struct object_entry *entry)
+                                 struct object_entry *entry,
+                                 off_t write_offset)
 {
        unsigned long size;
        enum object_type type;
        void *buf;
        unsigned char header[10];
+       unsigned char dheader[10];
        unsigned hdrlen;
        off_t datalen;
        enum object_type obj_type;
        int to_reuse = 0;
+       /* write limit if limited packsize and not first object */
+       unsigned long limit = pack_size_limit && nr_written ?
+                               pack_size_limit - write_offset : 0;
+                               /* no if no delta */
+       int usable_delta =      !entry->delta ? 0 :
+                               /* yes if unlimited packfile */
+                               !pack_size_limit ? 1 :
+                               /* no if base written to previous pack */
+                               entry->delta->offset == (off_t)-1 ? 0 :
+                               /* otherwise double-check written to this
+                                * pack,  like we do below
+                                */
+                               entry->delta->offset ? 1 : 0;
 
        if (!pack_to_stdout)
                crc32_begin(f);
 
        obj_type = entry->type;
-       if (! entry->in_pack)
+       if (no_reuse_object)
+               to_reuse = 0;   /* explicit */
+       else if (!entry->in_pack)
                to_reuse = 0;   /* can't reuse what we don't have */
        else if (obj_type == OBJ_REF_DELTA || obj_type == OBJ_OFS_DELTA)
-               to_reuse = 1;   /* check_object() decided it for us */
+                               /* check_object() decided it for us ... */
+               to_reuse = usable_delta;
+                               /* ... but pack split may override that */
        else if (obj_type != entry->in_pack_type)
                to_reuse = 0;   /* pack has delta which is unusable */
        else if (entry->delta)
@@ -425,44 +406,49 @@ static unsigned long write_object(struct sha1file *f,
                                 * and we do not need to deltify it.
                                 */
 
-       if (!entry->in_pack && !entry->delta) {
-               unsigned char *map;
-               unsigned long mapsize;
-               map = map_sha1_file(entry->sha1, &mapsize);
-               if (map && !legacy_loose_object(map)) {
-                       /* We can copy straight into the pack file */
-                       if (revalidate_loose_object(entry, map, mapsize))
-                               die("corrupt loose object %s",
-                                   sha1_to_hex(entry->sha1));
-                       sha1write(f, map, mapsize);
-                       munmap(map, mapsize);
-                       written++;
-                       reused++;
-                       return mapsize;
-               }
-               if (map)
-                       munmap(map, mapsize);
-       }
-
        if (!to_reuse) {
-               buf = read_sha1_file(entry->sha1, &type, &size);
-               if (!buf)
-                       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 (entry->delta) {
+               z_stream stream;
+               unsigned long maxsize;
+               void *out;
+               if (!usable_delta) {
+                       buf = read_sha1_file(entry->sha1, &obj_type, &size);
+                       if (!buf)
+                               die("unable to read %s", sha1_to_hex(entry->sha1));
+               } else if (entry->delta_data) {
+                       size = entry->delta_size;
+                       buf = entry->delta_data;
+                       entry->delta_data = NULL;
+                       obj_type = (allow_ofs_delta && entry->delta->offset) ?
+                               OBJ_OFS_DELTA : OBJ_REF_DELTA;
+               } else {
+                       buf = read_sha1_file(entry->sha1, &type, &size);
+                       if (!buf)
+                               die("unable to read %s", sha1_to_hex(entry->sha1));
                        buf = delta_against(buf, size, entry);
                        size = entry->delta_size;
                        obj_type = (allow_ofs_delta && entry->delta->offset) ?
                                OBJ_OFS_DELTA : OBJ_REF_DELTA;
                }
+               /* compress the data to store and put compressed length in datalen */
+               memset(&stream, 0, sizeof(stream));
+               deflateInit(&stream, pack_compression_level);
+               maxsize = deflateBound(&stream, size);
+               out = xmalloc(maxsize);
+               /* Compress it */
+               stream.next_in = buf;
+               stream.avail_in = size;
+               stream.next_out = out;
+               stream.avail_out = maxsize;
+               while (deflate(&stream, Z_FINISH) == Z_OK)
+                       /* nothing */;
+               deflateEnd(&stream);
+               datalen = stream.total_out;
+               deflateEnd(&stream);
                /*
                 * The object header is a byte of 'type' followed by zero or
                 * more bytes of length.
                 */
                hdrlen = encode_header(obj_type, size, header);
-               sha1write(f, header, hdrlen);
 
                if (obj_type == OBJ_OFS_DELTA) {
                        /*
@@ -471,21 +457,41 @@ static unsigned long write_object(struct sha1file *f,
                         * base from this object's position in the pack.
                         */
                        off_t ofs = entry->offset - entry->delta->offset;
-                       unsigned pos = sizeof(header) - 1;
-                       header[pos] = ofs & 127;
+                       unsigned pos = sizeof(dheader) - 1;
+                       dheader[pos] = ofs & 127;
                        while (ofs >>= 7)
-                               header[--pos] = 128 | (--ofs & 127);
-                       sha1write(f, header + pos, sizeof(header) - pos);
-                       hdrlen += sizeof(header) - pos;
+                               dheader[--pos] = 128 | (--ofs & 127);
+                       if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
+                               free(out);
+                               free(buf);
+                               return 0;
+                       }
+                       sha1write(f, header, hdrlen);
+                       sha1write(f, dheader + pos, sizeof(dheader) - pos);
+                       hdrlen += sizeof(dheader) - pos;
                } else if (obj_type == OBJ_REF_DELTA) {
                        /*
                         * Deltas with a base reference contain
                         * an additional 20 bytes for the base sha1.
                         */
+                       if (limit && hdrlen + 20 + datalen + 20 >= limit) {
+                               free(out);
+                               free(buf);
+                               return 0;
+                       }
+                       sha1write(f, header, hdrlen);
                        sha1write(f, entry->delta->sha1, 20);
                        hdrlen += 20;
+               } else {
+                       if (limit && hdrlen + datalen + 20 >= limit) {
+                               free(out);
+                               free(buf);
+                               return 0;
+                       }
+                       sha1write(f, header, hdrlen);
                }
-               datalen = sha1write_compressed(f, buf, size);
+               sha1write(f, out, datalen);
+               free(out);
                free(buf);
        }
        else {
@@ -500,20 +506,6 @@ static unsigned long write_object(struct sha1file *f,
                        reused_delta++;
                }
                hdrlen = encode_header(obj_type, entry->size, header);
-               sha1write(f, header, hdrlen);
-               if (obj_type == OBJ_OFS_DELTA) {
-                       off_t ofs = entry->offset - entry->delta->offset;
-                       unsigned pos = sizeof(header) - 1;
-                       header[pos] = ofs & 127;
-                       while (ofs >>= 7)
-                               header[--pos] = 128 | (--ofs & 127);
-                       sha1write(f, header + pos, sizeof(header) - pos);
-                       hdrlen += sizeof(header) - pos;
-               } else if (obj_type == OBJ_REF_DELTA) {
-                       sha1write(f, entry->delta->sha1, 20);
-                       hdrlen += 20;
-               }
-
                offset = entry->in_pack_offset;
                revidx = find_packed_object(p, offset);
                datalen = revidx[1].offset - offset;
@@ -522,6 +514,29 @@ static unsigned long write_object(struct sha1file *f,
                        die("bad packed object CRC for %s", sha1_to_hex(entry->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;
+                       unsigned pos = sizeof(dheader) - 1;
+                       dheader[pos] = ofs & 127;
+                       while (ofs >>= 7)
+                               dheader[--pos] = 128 | (--ofs & 127);
+                       if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit)
+                               return 0;
+                       sha1write(f, header, hdrlen);
+                       sha1write(f, dheader + pos, sizeof(dheader) - pos);
+                       hdrlen += sizeof(dheader) - pos;
+               } else if (obj_type == OBJ_REF_DELTA) {
+                       if (limit && hdrlen + 20 + datalen + 20 >= limit)
+                               return 0;
+                       sha1write(f, header, hdrlen);
+                       sha1write(f, entry->delta->sha1, 20);
+                       hdrlen += 20;
+               } else {
+                       if (limit && hdrlen + datalen + 20 >= limit)
+                               return 0;
+                       sha1write(f, header, hdrlen);
+               }
+
                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));
@@ -529,7 +544,7 @@ static unsigned long write_object(struct sha1file *f,
                unuse_pack(&w_curs);
                reused++;
        }
-       if (entry->delta)
+       if (usable_delta)
                written_delta++;
        written++;
        if (!pack_to_stdout)
@@ -548,11 +563,19 @@ static off_t write_one(struct sha1file *f,
                return offset;
 
        /* if we are deltified, write out base object first. */
-       if (e->delta)
+       if (e->delta) {
                offset = write_one(f, e->delta, offset);
+               if (!offset)
+                       return 0;
+       }
 
        e->offset = offset;
-       size = write_object(f, e);
+       size = write_object(f, e, offset);
+       if (!size) {
+               e->offset = 0;
+               return 0;
+       }
+       written_list[nr_written++] = e;
 
        /* make sure off_t is sufficiently large not to wrap */
        if (offset > offset + size)
@@ -566,49 +589,113 @@ static int open_object_dir_tmp(const char *path)
     return mkstemp(tmpname);
 }
 
-static off_t write_pack_file(void)
+/* forward declarations for write_pack_file */
+static void write_index_file(off_t last_obj_offset, unsigned char *sha1);
+static int adjust_perm(const char *path, mode_t mode);
+
+static void write_pack_file(void)
 {
-       uint32_t i;
+       uint32_t i = 0, j;
        struct sha1file *f;
-       off_t offset, last_obj_offset = 0;
+       off_t offset, offset_one, last_obj_offset = 0;
        struct pack_header hdr;
-       int do_progress = progress;
-
-       if (pack_to_stdout) {
-               f = sha1fd(1, "<stdout>");
-               do_progress >>= 1;
-       } else {
-               int fd = open_object_dir_tmp("tmp_pack_XXXXXX");
-               if (fd < 0)
-                       die("unable to create %s: %s\n", tmpname, strerror(errno));
-               pack_tmp_name = xstrdup(tmpname);
-               f = sha1fd(fd, pack_tmp_name);
-       }
+       int do_progress = progress >> pack_to_stdout;
+       uint32_t nr_remaining = nr_result;
 
        if (do_progress)
                start_progress(&progress_state, "Writing %u objects...", "", nr_result);
+       written_list = xmalloc(nr_objects * sizeof(struct object_entry *));
 
-       hdr.hdr_signature = htonl(PACK_SIGNATURE);
-       hdr.hdr_version = htonl(PACK_VERSION);
-       hdr.hdr_entries = htonl(nr_result);
-       sha1write(f, &hdr, sizeof(hdr));
-       offset = sizeof(hdr);
-       if (!nr_result)
-               goto done;
-       for (i = 0; i < nr_objects; i++) {
-               last_obj_offset = offset;
-               offset = write_one(f, objects + i, offset);
-               if (do_progress)
-                       display_progress(&progress_state, written);
-       }
+       do {
+               if (pack_to_stdout) {
+                       f = sha1fd(1, "<stdout>");
+               } else {
+                       int fd = open_object_dir_tmp("tmp_pack_XXXXXX");
+                       if (fd < 0)
+                               die("unable to create %s: %s\n", tmpname, strerror(errno));
+                       pack_tmp_name = xstrdup(tmpname);
+                       f = sha1fd(fd, pack_tmp_name);
+               }
+
+               hdr.hdr_signature = htonl(PACK_SIGNATURE);
+               hdr.hdr_version = htonl(PACK_VERSION);
+               hdr.hdr_entries = htonl(nr_remaining);
+               sha1write(f, &hdr, sizeof(hdr));
+               offset = sizeof(hdr);
+               nr_written = 0;
+               for (; i < nr_objects; i++) {
+                       last_obj_offset = offset;
+                       offset_one = write_one(f, objects + i, offset);
+                       if (!offset_one)
+                               break;
+                       offset = offset_one;
+                       if (do_progress)
+                               display_progress(&progress_state, written);
+               }
+
+               /*
+                * Did we write the wrong # entries in the header?
+                * If so, rewrite it like in fast-import
+                */
+               if (pack_to_stdout || nr_written == nr_remaining) {
+                       sha1close(f, pack_file_sha1, 1);
+               } else {
+                       sha1close(f, pack_file_sha1, 0);
+                       fixup_pack_header_footer(f->fd, pack_file_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);
+                       snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
+                                base_name, sha1_to_hex(object_list_sha1));
+                       if (adjust_perm(pack_tmp_name, mode))
+                               die("unable to make temporary pack file readable: %s",
+                                   strerror(errno));
+                       if (rename(pack_tmp_name, tmpname))
+                               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));
+                       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));
+               }
+
+               /* mark written objects as written to previous pack */
+               for (j = 0; j < nr_written; j++) {
+                       written_list[j]->offset = (off_t)-1;
+               }
+               nr_remaining -= nr_written;
+       } while (nr_remaining && i < nr_objects);
+
+       free(written_list);
        if (do_progress)
                stop_progress(&progress_state);
- done:
        if (written != nr_result)
                die("wrote %u objects while expecting %u", written, nr_result);
-       sha1close(f, pack_file_sha1, 1);
-
-       return last_obj_offset;
+       /*
+        * We have scanned through [0 ... i).  Since we have written
+        * the correct number of objects,  the remaining [i ... nr_objects)
+        * items must be either already written (due to out-of-order delta base)
+        * or a preferred base.  Count those which are neither and complain if any.
+        */
+       for (j = 0; i < nr_objects; i++) {
+               struct object_entry *e = objects + i;
+               j += !e->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)
@@ -635,18 +722,11 @@ static void write_index_file(off_t last_obj_offset, unsigned char *sha1)
        idx_tmp_name = xstrdup(tmpname);
        f = sha1fd(fd, idx_tmp_name);
 
-       if (nr_result) {
-               uint32_t j = 0;
-               sorted_by_sha =
-                       xcalloc(nr_result, sizeof(struct object_entry *));
-               for (i = 0; i < nr_objects; i++)
-                       if (!objects[i].preferred_base)
-                               sorted_by_sha[j++] = objects + i;
-               if (j != nr_result)
-                       die("listed %u objects while expecting %u", j, nr_result);
-               qsort(sorted_by_sha, nr_result, sizeof(*sorted_by_sha), sha1_sort);
+       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_result;
+               last = sorted_by_sha + nr_written;
        } else
                sorted_by_sha = list = last = NULL;
 
@@ -684,7 +764,7 @@ static void write_index_file(off_t last_obj_offset, unsigned char *sha1)
 
        /* Write the actual SHA1 entries. */
        list = sorted_by_sha;
-       for (i = 0; i < nr_result; i++) {
+       for (i = 0; i < nr_written; i++) {
                struct object_entry *entry = *list++;
                if (index_version < 2) {
                        uint32_t offset = htonl(entry->offset);
@@ -699,7 +779,7 @@ static void write_index_file(off_t last_obj_offset, unsigned char *sha1)
 
                /* write the crc32 table */
                list = sorted_by_sha;
-               for (i = 0; i < nr_objects; i++) {
+               for (i = 0; i < nr_written; i++) {
                        struct object_entry *entry = *list++;
                        uint32_t crc32_val = htonl(entry->crc32);
                        sha1write(f, &crc32_val, 4);
@@ -707,7 +787,7 @@ static void write_index_file(off_t last_obj_offset, unsigned char *sha1)
 
                /* write the 32-bit offset table */
                list = sorted_by_sha;
-               for (i = 0; i < nr_objects; i++) {
+               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++);
@@ -732,7 +812,6 @@ static void write_index_file(off_t last_obj_offset, unsigned char *sha1)
 
        sha1write(f, pack_file_sha1, 20);
        sha1close(f, NULL, 1);
-       free(sorted_by_sha);
        SHA1_Final(sha1, &ctx);
 }
 
@@ -788,6 +867,9 @@ static unsigned name_hash(const char *name)
        unsigned char c;
        unsigned hash = 0;
 
+       if (!name)
+               return 0;
+
        /*
         * This effectively just creates a sortable number from the
         * last sixteen non-whitespace characters. Last characters
@@ -801,13 +883,36 @@ static unsigned name_hash(const char *name)
        return hash;
 }
 
+static void setup_delta_attr_check(struct git_attr_check *check)
+{
+       static struct git_attr *attr_delta;
+
+       if (!attr_delta)
+               attr_delta = git_attr("delta", 5);
+
+       check[0].attr = attr_delta;
+}
+
+static int no_try_delta(const char *path)
+{
+       struct git_attr_check check[1];
+
+       setup_delta_attr_check(check);
+       if (git_checkattr(path, ARRAY_SIZE(check), check))
+               return 0;
+       if (ATTR_FALSE(check->value))
+               return 1;
+       return 0;
+}
+
 static int add_object_entry(const unsigned char *sha1, enum object_type type,
-                           unsigned hash, int exclude)
+                           const char *name, int exclude)
 {
        struct object_entry *entry;
        struct packed_git *p, *found_pack = NULL;
        off_t found_offset = 0;
        int ix;
+       unsigned hash = name_hash(name);
 
        ix = nr_objects ? locate_object_entry_hash(sha1) : -1;
        if (ix >= 0) {
@@ -864,6 +969,9 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
        if (progress)
                display_progress(&progress_state, nr_objects);
 
+       if (name && no_try_delta(name))
+               entry->no_try_delta = 1;
+
        return 1;
 }
 
@@ -996,10 +1104,9 @@ static void add_pbase_object(struct tree_desc *tree,
                if (cmp < 0)
                        return;
                if (name[cmplen] != '/') {
-                       unsigned hash = name_hash(fullname);
                        add_object_entry(entry.sha1,
                                         S_ISDIR(entry.mode) ? OBJ_TREE : OBJ_BLOB,
-                                        hash, 1);
+                                        fullname, 1);
                        return;
                }
                if (S_ISDIR(entry.mode)) {
@@ -1059,10 +1166,11 @@ static int check_pbase_path(unsigned hash)
        return 0;
 }
 
-static void add_preferred_base_object(const char *name, unsigned hash)
+static void add_preferred_base_object(const char *name)
 {
        struct pbase_tree *it;
        int cmplen;
+       unsigned hash = name_hash(name);
 
        if (!num_preferred_base || check_pbase_path(hash))
                return;
@@ -1070,7 +1178,7 @@ static void add_preferred_base_object(const char *name, unsigned hash)
        cmplen = name_cmp_len(name);
        for (it = pbase_tree; it; it = it->next) {
                if (cmplen == 0) {
-                       add_object_entry(it->pcache.sha1, OBJ_TREE, 0, 1);
+                       add_object_entry(it->pcache.sha1, OBJ_TREE, NULL, 1);
                }
                else {
                        struct tree_desc tree;
@@ -1125,8 +1233,8 @@ static void check_object(struct object_entry *entry)
                buf = use_pack(p, &w_curs, entry->in_pack_offset, &avail);
 
                /*
-                * We want in_pack_type even if we do not reuse delta.
-                * There is no point not reusing non-delta representations.
+                * We want in_pack_type even if we do not reuse delta
+                * since non-delta representations could still be reused.
                 */
                used = unpack_object_header_gently(buf, avail,
                                                   &entry->in_pack_type,
@@ -1282,6 +1390,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.
@@ -1351,18 +1476,32 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
        }
        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;
 }
 
@@ -1412,6 +1551,10 @@ static void find_deltas(struct object_entry **list, int window, int depth)
 
                if (entry->size < 50)
                        continue;
+
+               if (entry->no_try_delta)
+                       continue;
+
                free_delta_index(n->index);
                n->index = NULL;
                free(n->data);
@@ -1494,6 +1637,24 @@ static int git_pack_config(const char *k, const char *v)
                depth = git_config_int(k, v);
                return 0;
        }
+       if (!strcmp(k, "pack.compression")) {
+               int level = git_config_int(k, v);
+               if (level == -1)
+                       level = Z_DEFAULT_COMPRESSION;
+               else if (level < 0 || level > Z_BEST_COMPRESSION)
+                       die("bad pack compression level %d", level);
+               pack_compression_level = level;
+               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);
 }
 
@@ -1501,7 +1662,6 @@ static void read_object_list_from_stdin(void)
 {
        char line[40 + 1 + PATH_MAX + 2];
        unsigned char sha1[20];
-       unsigned hash;
 
        for (;;) {
                if (!fgets(line, sizeof(line), stdin)) {
@@ -1524,22 +1684,20 @@ static void read_object_list_from_stdin(void)
                if (get_sha1_hex(line, sha1))
                        die("expected sha1, got garbage:\n %s", line);
 
-               hash = name_hash(line+41);
-               add_preferred_base_object(line+41, hash);
-               add_object_entry(sha1, 0, hash, 0);
+               add_preferred_base_object(line+41);
+               add_object_entry(sha1, 0, line+41, 0);
        }
 }
 
 static void show_commit(struct commit *commit)
 {
-       add_object_entry(commit->object.sha1, OBJ_COMMIT, 0, 0);
+       add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);
 }
 
 static void show_object(struct object_array_entry *p)
 {
-       unsigned hash = name_hash(p->name);
-       add_preferred_base_object(p->name, hash);
-       add_object_entry(p->item->sha1, p->item->type, hash, 0);
+       add_preferred_base_object(p->name);
+       add_object_entry(p->item->sha1, p->item->type, p->name, 0);
 }
 
 static void show_edge(struct commit *commit)
@@ -1592,8 +1750,6 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        int use_internal_rev_list = 0;
        int thin = 0;
        uint32_t i;
-       off_t last_obj_offset;
-       const char *base_name = NULL;
        const char **rp_av;
        int rp_ac_alloc = 64;
        int rp_ac;
@@ -1605,6 +1761,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        rp_ac = 2;
 
        git_config(git_pack_config);
+       if (!pack_compression_seen && core_compression_seen)
+               pack_compression_level = core_compression_level;
 
        progress = isatty(2);
        for (i = 1; i < argc; i++) {
@@ -1625,6 +1783,25 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                        incremental = 1;
                        continue;
                }
+               if (!prefixcmp(arg, "--compression=")) {
+                       char *end;
+                       int level = strtoul(arg+14, &end, 0);
+                       if (!arg[14] || *end)
+                               usage(pack_usage);
+                       if (level == -1)
+                               level = Z_DEFAULT_COMPRESSION;
+                       else if (level < 0 || level > Z_BEST_COMPRESSION)
+                               die("bad pack compression level %d", level);
+                       pack_compression_level = level;
+                       continue;
+               }
+               if (!prefixcmp(arg, "--max-pack-size=")) {
+                       char *end;
+                       pack_size_limit = strtoul(arg+16, &end, 0) * 1024 * 1024;
+                       if (!arg[16] || *end)
+                               usage(pack_usage);
+                       continue;
+               }
                if (!prefixcmp(arg, "--window=")) {
                        char *end;
                        window = strtoul(arg+9, &end, 0);
@@ -1655,6 +1832,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                        no_reuse_delta = 1;
                        continue;
                }
+               if (!strcmp("--no-reuse-object", arg)) {
+                       no_reuse_object = no_reuse_delta = 1;
+                       continue;
+               }
                if (!strcmp("--delta-base-offset", arg)) {
                        allow_ofs_delta = 1;
                        continue;
@@ -1719,6 +1900,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        if (pack_to_stdout != !base_name)
                usage(pack_usage);
 
+       if (pack_to_stdout && pack_size_limit)
+               die("--max-pack-size cannot be used to build a pack for transfer.");
+
        if (!pack_to_stdout && thin)
                die("--thin cannot be used to build an indexable pack.");
 
@@ -1744,33 +1928,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                fprintf(stderr, "Result has %u objects.\n", nr_result);
        if (nr_result)
                prepare_pack(window, depth);
-       last_obj_offset = write_pack_file();
-       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);
-               snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
-                        base_name, sha1_to_hex(object_list_sha1));
-               if (adjust_perm(pack_tmp_name, mode))
-                       die("unable to make temporary pack file readable: %s",
-                           strerror(errno));
-               if (rename(pack_tmp_name, tmpname))
-                       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));
-               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));
-       }
+       write_pack_file();
        if (progress)
                fprintf(stderr, "Total %u (delta %u), reused %u (delta %u)\n",
                        written, written_delta, reused, reused_delta);
index d080e30d67a913529d46e0da169faa4b0be75580..1952950c9a4bf13dc728bb8d6a79493dad36df06 100644 (file)
@@ -12,9 +12,11 @@ struct ref_to_prune {
        char name[FLEX_ARRAY];
 };
 
+#define PACK_REFS_PRUNE        0x0001
+#define PACK_REFS_ALL  0x0002
+
 struct pack_refs_cb_data {
-       int prune;
-       int all;
+       unsigned int flags;
        struct ref_to_prune *ref_to_prune;
        FILE *refs_file;
 };
@@ -39,7 +41,7 @@ static int handle_one_ref(const char *path, const unsigned char *sha1,
        is_tag_ref = !prefixcmp(path, "refs/tags/");
 
        /* ALWAYS pack refs that were already packed or are tags */
-       if (!cb->all && !is_tag_ref && !(flags & REF_ISPACKED))
+       if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref && !(flags & REF_ISPACKED))
                return 0;
 
        fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path);
@@ -53,7 +55,7 @@ static int handle_one_ref(const char *path, const unsigned char *sha1,
                }
        }
 
-       if (cb->prune && !do_not_prune(flags)) {
+       if ((cb->flags & PACK_REFS_PRUNE) && !do_not_prune(flags)) {
                int namelen = strlen(path) + 1;
                struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
                hashcpy(n->sha1, sha1);
@@ -85,26 +87,51 @@ static void prune_refs(struct ref_to_prune *r)
 
 static struct lock_file packed;
 
-int cmd_pack_refs(int argc, const char **argv, const char *prefix)
+static int pack_refs(unsigned int flags)
 {
-       int fd, i;
+       int fd;
        struct pack_refs_cb_data cbdata;
 
        memset(&cbdata, 0, sizeof(cbdata));
+       cbdata.flags = flags;
+
+       fd = hold_lock_file_for_update(&packed, git_path("packed-refs"), 1);
+       cbdata.refs_file = fdopen(fd, "w");
+       if (!cbdata.refs_file)
+               die("unable to create ref-pack file structure (%s)",
+                   strerror(errno));
+
+       /* perhaps other traits later as well */
+       fprintf(cbdata.refs_file, "# pack-refs with: peeled \n");
+
+       for_each_ref(handle_one_ref, &cbdata);
+       if (fflush(cbdata.refs_file) || fsync(fd) || fclose(cbdata.refs_file))
+               die("failed to write ref-pack file (%s)", strerror(errno));
+       if (commit_lock_file(&packed) < 0)
+               die("unable to overwrite old ref-pack file (%s)", strerror(errno));
+       if (cbdata.flags & PACK_REFS_PRUNE)
+               prune_refs(cbdata.ref_to_prune);
+       return 0;
+}
 
-       cbdata.prune = 1;
+int cmd_pack_refs(int argc, const char **argv, const char *prefix)
+{
+       int i;
+       unsigned int flags;
+
+       flags = PACK_REFS_PRUNE;
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
                if (!strcmp(arg, "--prune")) {
-                       cbdata.prune = 1; /* now the default */
+                       flags |= PACK_REFS_PRUNE; /* now the default */
                        continue;
                }
                if (!strcmp(arg, "--no-prune")) {
-                       cbdata.prune = 0;
+                       flags &= ~PACK_REFS_PRUNE;
                        continue;
                }
                if (!strcmp(arg, "--all")) {
-                       cbdata.all = 1;
+                       flags |= PACK_REFS_ALL;
                        continue;
                }
                /* perhaps other parameters later... */
@@ -113,22 +140,5 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)
        if (i != argc)
                usage(builtin_pack_refs_usage);
 
-       fd = hold_lock_file_for_update(&packed, git_path("packed-refs"), 1);
-       cbdata.refs_file = fdopen(fd, "w");
-       if (!cbdata.refs_file)
-               die("unable to create ref-pack file structure (%s)",
-                   strerror(errno));
-
-       /* perhaps other traits later as well */
-       fprintf(cbdata.refs_file, "# pack-refs with: peeled \n");
-
-       for_each_ref(handle_one_ref, &cbdata);
-       fflush(cbdata.refs_file);
-       fsync(fd);
-       fclose(cbdata.refs_file);
-       if (commit_lock_file(&packed) < 0)
-               die("unable to overwrite old ref-pack file (%s)", strerror(errno));
-       if (cbdata.prune)
-               prune_refs(cbdata.ref_to_prune);
-       return 0;
+       return pack_refs(flags);
 }
index cb78401c946eac9019c2e4e953c3b87ef9e214f3..2612f07f74855ad6dec8ccd605279ab3a502e5e2 100644 (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);
-               }
-       }
-       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;
+       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);
        }
-
-       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);
 }
index 4c39f1da98e5e690f28f5145a1ab5dba790da68d..ce093cad78ce8008cd8a60d3ab6be5663a712a9d 100644 (file)
@@ -249,7 +249,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
        /* we take the lock for the ref itself to prevent it from
         * getting updated.
         */
-       lock = lock_any_ref_for_update(ref, sha1);
+       lock = lock_any_ref_for_update(ref, sha1, 0);
        if (!lock)
                return error("cannot lock ref '%s'", ref);
        log_file = xstrdup(git_path("logs/%s", ref));
index ea2f15b977a8b62189755ae89e3ee704b45afae8..80c348c401d1024721012398022eba9ff33f1d34 100644 (file)
@@ -45,8 +45,10 @@ static void parse_options(int argc, const char **argv)
        if (argc < 2)
                usage(usage_str);
 
-       for (i = 1; i < argc - 1; i++) {
+       for (i = 1; i < argc; i++) {
                arg = argv[i];
+               if (arg[0] != '-')
+                       break;
                if (!strcmp(arg, "-n") || !strcmp(arg, "--no-commit"))
                        no_commit = 1;
                else if (!strcmp(arg, "-e") || !strcmp(arg, "--edit"))
@@ -59,7 +61,8 @@ static void parse_options(int argc, const char **argv)
                else if (strcmp(arg, "-r"))
                        usage(usage_str);
        }
-
+       if (i != argc - 1)
+               usage(usage_str);
        arg = argv[argc - 1];
        if (get_sha1(arg, sha1))
                die ("Cannot find '%s'", arg);
index 8f9899178b1755ee0acd4a34ee185a4f54fb4219..509369e9e7b1719e53ba2dd2d976066fb9513353 100644 (file)
@@ -134,7 +134,7 @@ static int process_directory(const char *path, int len, struct stat *st)
        /* Exact match: file or existing gitlink */
        if (pos >= 0) {
                struct cache_entry *ce = active_cache[pos];
-               if (S_ISDIRLNK(ntohl(ce->ce_mode))) {
+               if (S_ISGITLINK(ntohl(ce->ce_mode))) {
 
                        /* Do nothing to the index if there is no HEAD! */
                        if (resolve_gitlink_ref(path, "HEAD", sha1) < 0)
@@ -178,7 +178,7 @@ static int process_file(const char *path, int len, struct stat *st)
        int pos = cache_name_pos(path, len);
        struct cache_entry *ce = pos < 0 ? NULL : active_cache[pos];
 
-       if (ce && S_ISDIRLNK(ntohl(ce->ce_mode)))
+       if (ce && S_ISGITLINK(ntohl(ce->ce_mode)))
                return error("%s is already a gitlink, not replacing", path);
 
        return add_one_path(ce, path, len, st);
index 5ee960bf41c4518b6c5530acb123a7ed668538fc..feac2ed12d22dcd23db5823cd32a0f9f1c1c974a 100644 (file)
@@ -3,16 +3,17 @@
 #include "builtin.h"
 
 static const char git_update_ref_usage[] =
-"git-update-ref [-m <reason>] (-d <refname> <value> | <refname> <value> [<oldval>])";
+"git-update-ref [-m <reason>] (-d <refname> <value> | [--no-deref] <refname> <value> [<oldval>])";
 
 int cmd_update_ref(int argc, const char **argv, const char *prefix)
 {
        const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
        struct ref_lock *lock;
        unsigned char sha1[20], oldsha1[20];
-       int i, delete;
+       int i, delete, ref_flags;
 
        delete = 0;
+       ref_flags = 0;
        git_config(git_default_config);
 
        for (i = 1; i < argc; i++) {
@@ -30,6 +31,10 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
                        delete = 1;
                        continue;
                }
+               if (!strcmp("--no-deref", argv[i])) {
+                       ref_flags |= REF_NODEREF;
+                       continue;
+               }
                if (!refname) {
                        refname = argv[i];
                        continue;
@@ -59,7 +64,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
        if (oldval && *oldval && get_sha1(oldval, oldsha1))
                die("%s: not a valid old SHA1", oldval);
 
-       lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL);
+       lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, ref_flags);
        if (!lock)
                die("%s: cannot lock the ref", refname);
        if (write_ref_sha1(lock, sha1, msg) < 0)
index d3f3a7496e1c1adbe3f8bae36603fafee374c8c5..39290d1b8e6d66f3123632e7efacec3a750f1107 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -8,7 +8,7 @@ extern const char git_usage_string[];
 
 extern void help_unknown_cmd(const char *cmd);
 extern int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const char *msg, const char *patch);
-extern int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip);
+extern int split_mbox(const char *file, const char *dir, int allow_bare, int nr_prec, int skip);
 extern void stripspace(FILE *in, FILE *out);
 extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
 extern void prune_packed_objects(int);
index 6369cc7c536ba7b82a6afcb191628beefe889b72..350a79b768e7c032e1b4815f7201c9ce5f88bc8d 100644 (file)
@@ -326,7 +326,7 @@ static int update_one(struct cache_tree *it,
                        mode = ntohl(ce->ce_mode);
                        entlen = pathlen - baselen;
                }
-               if (mode != S_IFDIRLNK && !missing_ok && !has_sha1_file(sha1))
+               if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1))
                        return error("invalid object %s", sha1_to_hex(sha1));
 
                if (!ce->ce_mode)
diff --git a/cache.h b/cache.h
index 5dff2f1d73eb1a41f664d959d18e6f2c0e102e64..8a9d1f3883150fd40ac1b6c0a44fa896c3133b04 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -40,8 +40,8 @@
  * happens that everybody shares the same bit representation
  * in the UNIX world (and apparently wider too..)
  */
-#define S_IFDIRLNK     0160000
-#define S_ISDIRLNK(m)  (((m) & S_IFMT) == S_IFDIRLNK)
+#define S_IFGITLINK    0160000
+#define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK)
 
 /*
  * Intensive research over the course of many years has shown that
@@ -123,8 +123,8 @@ static inline unsigned int create_ce_mode(unsigned int mode)
 {
        if (S_ISLNK(mode))
                return htonl(S_IFLNK);
-       if (S_ISDIR(mode) || S_ISDIRLNK(mode))
-               return htonl(S_IFDIRLNK);
+       if (S_ISDIR(mode) || S_ISGITLINK(mode))
+               return htonl(S_IFGITLINK);
        return htonl(S_IFREG | ce_permissions(mode));
 }
 static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode)
@@ -142,7 +142,7 @@ static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned in
 }
 #define canon_mode(mode) \
        (S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
-       S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFDIRLNK)
+       S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
 
 #define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)
 
@@ -273,7 +273,6 @@ extern void rollback_lock_file(struct lock_file *);
 extern int delete_ref(const char *, const unsigned char *sha1);
 
 /* Environment bits from configuration mechanism */
-extern int use_legacy_headers;
 extern int trust_executable_bit;
 extern int has_symlinks;
 extern int assume_unchanged;
@@ -283,6 +282,8 @@ extern int warn_ambiguous_refs;
 extern int shared_repository;
 extern const char *apply_default_whitespace;
 extern int zlib_compression_level;
+extern int core_compression_level;
+extern int core_compression_seen;
 extern size_t packed_git_window_size;
 extern size_t packed_git_limit;
 extern size_t delta_base_cache_limit;
@@ -354,7 +355,6 @@ extern int move_temp_to_file(const char *tmpfile, const char *filename);
 extern int has_sha1_pack(const unsigned char *sha1, const char **ignore);
 extern int has_sha1_file(const unsigned char *sha1);
 extern void *map_sha1_file(const unsigned char *sha1, unsigned long *);
-extern int legacy_loose_object(unsigned char *);
 
 extern int has_pack_file(const unsigned char *sha1);
 extern int has_pack_index(const unsigned char *sha1);
@@ -463,11 +463,10 @@ struct ref {
 #define REF_HEADS      (1u << 1)
 #define REF_TAGS       (1u << 2)
 
-extern pid_t git_connect(int fd[2], char *url, const char *prog);
+#define CONNECT_VERBOSE       (1u << 0)
+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);
@@ -484,10 +483,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);
index 7b655fdb78350bf9a8320075bef6cf4f1e87c50c..0614c2b29fa93b66065ec6bc20712d38193aec89 100644 (file)
--- a/config.c
+++ b/config.c
@@ -12,6 +12,8 @@
 static FILE *config_file;
 static const char *config_file_name;
 static int config_linenr;
+static int zlib_compression_seen;
+
 static int get_next_char(void)
 {
        int c;
@@ -299,8 +301,14 @@ int git_default_config(const char *var, const char *value)
                return 0;
        }
 
-       if (!strcmp(var, "core.legacyheaders")) {
-               use_legacy_headers = git_config_bool(var, value);
+       if (!strcmp(var, "core.loosecompression")) {
+               int level = git_config_int(var, value);
+               if (level == -1)
+                       level = Z_DEFAULT_COMPRESSION;
+               else if (level < 0 || level > Z_BEST_COMPRESSION)
+                       die("bad zlib compression level %d", level);
+               zlib_compression_level = level;
+               zlib_compression_seen = 1;
                return 0;
        }
 
@@ -310,7 +318,10 @@ int git_default_config(const char *var, const char *value)
                        level = Z_DEFAULT_COMPRESSION;
                else if (level < 0 || level > Z_BEST_COMPRESSION)
                        die("bad zlib compression level %d", level);
-               zlib_compression_level = level;
+               core_compression_level = level;
+               core_compression_seen = 1;
+               if (!zlib_compression_seen)
+                       zlib_compression_level = level;
                return 0;
        }
 
index da89c9cfcf3469dfab789f2644f953ea46666e90..8cbda88dda02e7de74149af1b387e537f77bc497 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -4,6 +4,7 @@
 #include "quote.h"
 #include "refs.h"
 #include "run-command.h"
+#include "remote.h"
 
 static char *server_capabilities;
 
@@ -128,245 +129,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,
@@ -391,16 +153,34 @@ static enum protocol get_protocol(const char *name)
 
 #ifndef NO_IPV6
 
+static const char *ai_name(const struct addrinfo *ai)
+{
+       static char addr[INET_ADDRSTRLEN];
+       if ( AF_INET == ai->ai_family ) {
+               struct sockaddr_in *in;
+               in = (struct sockaddr_in *)ai->ai_addr;
+               inet_ntop(ai->ai_family, &in->sin_addr, addr, sizeof(addr));
+       } else if ( AF_INET6 == ai->ai_family ) {
+               struct sockaddr_in6 *in;
+               in = (struct sockaddr_in6 *)ai->ai_addr;
+               inet_ntop(ai->ai_family, &in->sin6_addr, addr, sizeof(addr));
+       } else {
+               strcpy(addr, "(unknown)");
+       }
+       return addr;
+}
+
 /*
  * Returns a connected socket() fd, or else die()s.
  */
-static int git_tcp_connect_sock(char *host)
+static int git_tcp_connect_sock(char *host, int flags)
 {
        int sockfd = -1, saved_errno = 0;
        char *colon, *end;
        const char *port = STR(DEFAULT_GIT_PORT);
        struct addrinfo hints, *ai0, *ai;
        int gai;
+       int cnt = 0;
 
        if (host[0] == '[') {
                end = strchr(host + 1, ']');
@@ -425,10 +205,16 @@ static int git_tcp_connect_sock(char *host)
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_protocol = IPPROTO_TCP;
 
+       if (flags & CONNECT_VERBOSE)
+               fprintf(stderr, "Looking up %s ... ", host);
+
        gai = getaddrinfo(host, port, &hints, &ai);
        if (gai)
                die("Unable to look up %s (port %s) (%s)", host, port, gai_strerror(gai));
 
+       if (flags & CONNECT_VERBOSE)
+               fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port);
+
        for (ai0 = ai; ai; ai = ai->ai_next) {
                sockfd = socket(ai->ai_family,
                                ai->ai_socktype, ai->ai_protocol);
@@ -438,10 +224,18 @@ static int git_tcp_connect_sock(char *host)
                }
                if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
                        saved_errno = errno;
+                       fprintf(stderr, "%s[%d: %s]: net=%s, errno=%s\n",
+                               host,
+                               cnt,
+                               ai_name(ai),
+                               hstrerror(h_errno),
+                               strerror(saved_errno));
                        close(sockfd);
                        sockfd = -1;
                        continue;
                }
+               if (flags & CONNECT_VERBOSE)
+                       fprintf(stderr, "%s ", ai_name(ai));
                break;
        }
 
@@ -450,6 +244,9 @@ static int git_tcp_connect_sock(char *host)
        if (sockfd < 0)
                die("unable to connect a socket (%s)", strerror(saved_errno));
 
+       if (flags & CONNECT_VERBOSE)
+               fprintf(stderr, "done.\n");
+
        return sockfd;
 }
 
@@ -458,7 +255,7 @@ static int git_tcp_connect_sock(char *host)
 /*
  * Returns a connected socket() fd, or else die()s.
  */
-static int git_tcp_connect_sock(char *host)
+static int git_tcp_connect_sock(char *host, int flags)
 {
        int sockfd = -1, saved_errno = 0;
        char *colon, *end;
@@ -467,6 +264,7 @@ static int git_tcp_connect_sock(char *host)
        struct sockaddr_in sa;
        char **ap;
        unsigned int nport;
+       int cnt;
 
        if (host[0] == '[') {
                end = strchr(host + 1, ']');
@@ -485,6 +283,9 @@ static int git_tcp_connect_sock(char *host)
                port = colon + 1;
        }
 
+       if (flags & CONNECT_VERBOSE)
+               fprintf(stderr, "Looking up %s ... ", host);
+
        he = gethostbyname(host);
        if (!he)
                die("Unable to look up %s (%s)", host, hstrerror(h_errno));
@@ -497,7 +298,10 @@ static int git_tcp_connect_sock(char *host)
                nport = se->s_port;
        }
 
-       for (ap = he->h_addr_list; *ap; ap++) {
+       if (flags & CONNECT_VERBOSE)
+               fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port);
+
+       for (cnt = 0, ap = he->h_addr_list; *ap; ap++, cnt++) {
                sockfd = socket(he->h_addrtype, SOCK_STREAM, 0);
                if (sockfd < 0) {
                        saved_errno = errno;
@@ -511,25 +315,37 @@ static int git_tcp_connect_sock(char *host)
 
                if (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {
                        saved_errno = errno;
+                       fprintf(stderr, "%s[%d: %s]: net=%s, errno=%s\n",
+                               host,
+                               cnt,
+                               inet_ntoa(*(struct in_addr *)&sa.sin_addr),
+                               hstrerror(h_errno),
+                               strerror(saved_errno));
                        close(sockfd);
                        sockfd = -1;
                        continue;
                }
+               if (flags & CONNECT_VERBOSE)
+                       fprintf(stderr, "%s ",
+                               inet_ntoa(*(struct in_addr *)&sa.sin_addr));
                break;
        }
 
        if (sockfd < 0)
                die("unable to connect a socket (%s)", strerror(saved_errno));
 
+       if (flags & CONNECT_VERBOSE)
+               fprintf(stderr, "done.\n");
+
        return sockfd;
 }
 
 #endif /* NO_IPV6 */
 
 
-static void git_tcp_connect(int fd[2], char *host)
+static void git_tcp_connect(int fd[2], char *host, int flags)
 {
-       int sockfd = git_tcp_connect_sock(host);
+       int sockfd = git_tcp_connect_sock(host, flags);
 
        fd[0] = sockfd;
        fd[1] = dup(sockfd);
@@ -646,7 +462,7 @@ static void git_proxy_connect(int fd[2], char *host)
  *
  * Does not return a negative value on error; it just dies.
  */
-pid_t git_connect(int fd[2], char *url, const char *prog)
+pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
 {
        char *host, *path = url;
        char *end;
@@ -719,7 +535,7 @@ pid_t git_connect(int fd[2], char *url, const char *prog)
                if (git_use_proxy(host))
                        git_proxy_connect(fd, host);
                else
-                       git_tcp_connect(fd, host);
+                       git_tcp_connect(fd, host, flags);
                /*
                 * Separate original protocol components prog and path
                 * from extended components with a NUL byte.
index 9877b98508497c9377876bd08e8f130660b9ba05..f2a3615bbc44ed4edbee4014683621a91c1a7ab9 100755 (executable)
@@ -20,17 +20,19 @@ new_workdir=$2
 branch=$3
 
 # want to make sure that what is pointed to has a .git directory ...
-test -d "$orig_git/.git" || die "\"$orig_git\" is not a git repository!"
+git_dir=$(cd "$orig_git" 2>/dev/null &&
+  git rev-parse --git-dir 2>/dev/null) ||
+  die "\"$orig_git\" is not a git repository!"
 
 # don't link to a workdir
-if test -L "$orig_git/.git/config"
+if test -L "$git_dir/config"
 then
        die "\"$orig_git\" is a working directory only, please specify" \
                "a complete repository."
 fi
 
 # make sure the the links use full paths
-orig_git=$(cd "$orig_git"; pwd)
+git_dir=$(cd "$git_dir"; pwd)
 
 # create the workdir
 mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!"
@@ -45,13 +47,13 @@ do
                mkdir -p "$(dirname "$new_workdir/.git/$x")"
                ;;
        esac
-       ln -s "$orig_git/.git/$x" "$new_workdir/.git/$x"
+       ln -s "$git_dir/$x" "$new_workdir/.git/$x"
 done
 
 # now setup the workdir
 cd "$new_workdir"
 # copy the HEAD from the original repository as a default branch
-cp "$orig_git/.git/HEAD" .git/HEAD
+cp "$git_dir/HEAD" .git/HEAD
 # checkout the branch (either the same as HEAD from the original repository, or
 # the one that was asked for)
 git checkout -f $branch
index 7c806ada48d0fd58c091f9415fc8bb2f61bdd2e6..510934262437cf48c2b732bc2a392dd8167bee45 100644 (file)
@@ -29,18 +29,20 @@ static void sha1flush(struct sha1file *f, unsigned int count)
        }
 }
 
-int sha1close(struct sha1file *f, unsigned char *result, int update)
+int sha1close(struct sha1file *f, unsigned char *result, int final)
 {
        unsigned offset = f->offset;
        if (offset) {
                SHA1_Update(&f->ctx, f->buffer, offset);
                sha1flush(f, offset);
+               f->offset = 0;
        }
+       if (!final)
+               return 0;       /* only want to flush (no checksum write, no close) */
        SHA1_Final(f->buffer, &f->ctx);
        if (result)
                hashcpy(result, f->buffer);
-       if (update)
-               sha1flush(f, 20);
+       sha1flush(f, 20);
        if (close(f->fd))
                die("%s: sha1 file error on close (%s)", f->name, strerror(errno));
        free(f);
@@ -119,14 +121,14 @@ struct sha1file *sha1fd(int fd, const char *name)
        return f;
 }
 
-int sha1write_compressed(struct sha1file *f, void *in, unsigned int size)
+int sha1write_compressed(struct sha1file *f, void *in, unsigned int size, int level)
 {
        z_stream stream;
        unsigned long maxsize;
        void *out;
 
        memset(&stream, 0, sizeof(stream));
-       deflateInit(&stream, zlib_compression_level);
+       deflateInit(&stream, level);
        maxsize = deflateBound(&stream, size);
        out = xmalloc(maxsize);
 
index 7e1339189dcdc6e271fad5df7ad427954642ae9e..4e8b83e0936f255e0fff57127da56288a988a5fe 100644 (file)
@@ -16,7 +16,7 @@ extern struct sha1file *sha1fd(int fd, const char *name);
 extern struct sha1file *sha1create(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 extern int sha1close(struct sha1file *, unsigned char *, int);
 extern int sha1write(struct sha1file *, void *, unsigned int);
-extern int sha1write_compressed(struct sha1file *, void *, unsigned int);
+extern int sha1write_compressed(struct sha1file *, void *, unsigned int, int);
 extern void crc32_begin(struct sha1file *);
 extern uint32_t crc32_end(struct sha1file *);
 
index 9f998d0a73e0127d3a68a7caecb3727569149871..faf96e47130e3a2af26f55fc1173874f078617fc 100644 (file)
@@ -1,21 +1,14 @@
 /*
  * diff-delta.c: generate a delta between two buffers
  *
- *  Many parts of this file have been lifted from LibXDiff version 0.10.
- *  http://www.xmailserver.org/xdiff-lib.html
+ * This code was greatly inspired by parts of LibXDiff from Davide Libenzi
+ * http://www.xmailserver.org/xdiff-lib.html
  *
- *  LibXDiff was written by Davide Libenzi <davidel@xmailserver.org>
- *  Copyright (C) 2003 Davide Libenzi
+ * Rewritten for GIT by Nicolas Pitre <nico@cam.org>, (C) 2005-2007
  *
- *  Many mods for GIT usage by Nicolas Pitre <nico@cam.org>, (C) 2005.
- *
- *  This file is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Lesser General Public
- *  License as published by the Free Software Foundation; either
- *  version 2.1 of the License, or (at your option) any later version.
- *
- *  Use of this within git automatically means that the LGPL
- *  licensing gets turned into GPLv2 within this project.
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
  */
 
 #include "git-compat-util.h"
@@ -246,7 +239,7 @@ create_delta(const struct delta_index *index,
             const void *trg_buf, unsigned long trg_size,
             unsigned long *delta_size, unsigned long max_size)
 {
-       unsigned int i, outpos, outsize, val;
+       unsigned int i, outpos, outsize, moff, msize, val;
        int inscnt;
        const unsigned char *ref_data, *ref_top, *data, *top;
        unsigned char *out;
@@ -291,30 +284,33 @@ create_delta(const struct delta_index *index,
        }
        inscnt = i;
 
+       moff = 0;
+       msize = 0;
        while (data < top) {
-               unsigned int moff = 0, msize = 0;
-               struct index_entry *entry;
-               val ^= U[data[-RABIN_WINDOW]];
-               val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
-               i = val & index->hash_mask;
-               for (entry = index->hash[i]; entry; entry = entry->next) {
-                       const unsigned char *ref = entry->ptr;
-                       const unsigned char *src = data;
-                       unsigned int ref_size = ref_top - ref;
-                       if (entry->val != val)
-                               continue;
-                       if (ref_size > top - src)
-                               ref_size = top - src;
-                       if (ref_size > 0x10000)
-                               ref_size = 0x10000;
-                       if (ref_size <= msize)
-                               break;
-                       while (ref_size-- && *src++ == *ref)
-                               ref++;
-                       if (msize < ref - entry->ptr) {
-                               /* this is our best match so far */
-                               msize = ref - entry->ptr;
-                               moff = entry->ptr - ref_data;
+               if (msize < 4096) {
+                       struct index_entry *entry;
+                       val ^= U[data[-RABIN_WINDOW]];
+                       val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
+                       i = val & index->hash_mask;
+                       for (entry = index->hash[i]; entry; entry = entry->next) {
+                               const unsigned char *ref = entry->ptr;
+                               const unsigned char *src = data;
+                               unsigned int ref_size = ref_top - ref;
+                               if (entry->val != val)
+                                       continue;
+                               if (ref_size > top - src)
+                                       ref_size = top - src;
+                               if (ref_size <= msize)
+                                       break;
+                               while (ref_size-- && *src++ == *ref)
+                                       ref++;
+                               if (msize < ref - entry->ptr) {
+                                       /* this is our best match so far */
+                                       msize = ref - entry->ptr;
+                                       moff = entry->ptr - ref_data;
+                                       if (msize >= 4096) /* good enough */
+                                               break;
+                               }
                        }
                }
 
@@ -327,27 +323,13 @@ create_delta(const struct delta_index *index,
                                out[outpos - inscnt - 1] = inscnt;
                                inscnt = 0;
                        }
+                       msize = 0;
                } else {
+                       unsigned int left;
                        unsigned char *op;
 
-                       if (msize >= RABIN_WINDOW) {
-                               const unsigned char *sk;
-                               sk = data + msize - RABIN_WINDOW;
-                               val = 0;
-                               for (i = 0; i < RABIN_WINDOW; i++)
-                                       val = ((val << 8) | *sk++) ^ T[val >> RABIN_SHIFT];
-                       } else {
-                               const unsigned char *sk = data + 1;
-                               for (i = 1; i < msize; i++) {
-                                       val ^= U[sk[-RABIN_WINDOW]];
-                                       val = ((val << 8) | *sk++) ^ T[val >> RABIN_SHIFT];
-                               }
-                       }
-
                        if (inscnt) {
                                while (moff && ref_data[moff-1] == data[-1]) {
-                                       if (msize == 0x10000)
-                                               break;
                                        /* we can match one byte back */
                                        msize++;
                                        moff--;
@@ -363,23 +345,40 @@ create_delta(const struct delta_index *index,
                                inscnt = 0;
                        }
 
-                       data += msize;
+                       /* A copy op is currently limited to 64KB (pack v2) */
+                       left = (msize < 0x10000) ? 0 : (msize - 0x10000);
+                       msize -= left;
+
                        op = out + outpos++;
                        i = 0x80;
 
-                       if (moff & 0xff) { out[outpos++] = moff; i |= 0x01; }
-                       moff >>= 8;
-                       if (moff & 0xff) { out[outpos++] = moff; i |= 0x02; }
-                       moff >>= 8;
-                       if (moff & 0xff) { out[outpos++] = moff; i |= 0x04; }
-                       moff >>= 8;
-                       if (moff & 0xff) { out[outpos++] = moff; i |= 0x08; }
+                       if (moff & 0x000000ff)
+                               out[outpos++] = moff >> 0,  i |= 0x01;
+                       if (moff & 0x0000ff00)
+                               out[outpos++] = moff >> 8,  i |= 0x02;
+                       if (moff & 0x00ff0000)
+                               out[outpos++] = moff >> 16, i |= 0x04;
+                       if (moff & 0xff000000)
+                               out[outpos++] = moff >> 24, i |= 0x08;
 
-                       if (msize & 0xff) { out[outpos++] = msize; i |= 0x10; }
-                       msize >>= 8;
-                       if (msize & 0xff) { out[outpos++] = msize; i |= 0x20; }
+                       if (msize & 0x00ff)
+                               out[outpos++] = msize >> 0, i |= 0x10;
+                       if (msize & 0xff00)
+                               out[outpos++] = msize >> 8, i |= 0x20;
 
                        *op = i;
+
+                       data += msize;
+                       moff += msize;
+                       msize = left;
+
+                       if (msize < 4096) {
+                               int j;
+                               val = 0;
+                               for (j = -RABIN_WINDOW; j < 0; j++)
+                                       val = ((val << 8) | data[j])
+                                             ^ T[val >> RABIN_SHIFT];
+                       }
                }
 
                if (outpos >= outsize - MAX_OP_SIZE) {
@@ -389,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;
diff --git a/diff.c b/diff.c
index af282ddd8818aacdc516ff6f9dff4d84097cbab0..508bc51ed59998be463fd429ee074654baaad76a 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1465,7 +1465,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
        if (size_only && 0 < s->size)
                return 0;
 
-       if (S_ISDIRLNK(s->mode))
+       if (S_ISGITLINK(s->mode))
                return diff_populate_gitlink(s, size_only);
 
        if (!s->sha1_valid ||
diff --git a/dir.c b/dir.c
index 11fab7f4bf9b93be33795dfd5c1a3c55c8a8079d..f543f50f42d5bca1a6a6f981e8bb1b3cafd352ea 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -321,7 +321,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
                        break;
                if (endchar == '/')
                        return index_directory;
-               if (!endchar && S_ISDIRLNK(ntohl(ce->ce_mode)))
+               if (!endchar && S_ISGITLINK(ntohl(ce->ce_mode)))
                        return index_gitdir;
        }
        return index_nonexistent;
@@ -356,7 +356,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
  *      also true and the directory is empty, in which case
  *      we just ignore it entirely.
  *  (b) if it looks like a git directory, and we don't have
- *      'no_dirlinks' set we treat it as a gitlink, and show it
+ *      'no_gitlinks' set we treat it as a gitlink, and show it
  *      as a directory.
  *  (c) otherwise, we recurse into it.
  */
@@ -383,7 +383,7 @@ static enum directory_treatment treat_directory(struct dir_struct *dir,
        case index_nonexistent:
                if (dir->show_other_directories)
                        break;
-               if (!dir->no_dirlinks) {
+               if (!dir->no_gitlinks) {
                        unsigned char sha1[20];
                        if (resolve_gitlink_ref(dirname, "HEAD", sha1) == 0)
                                return show_directory;
diff --git a/dir.h b/dir.h
index 817c674da1e017cffea9dddae672f9125aca8475..172147fd3d4711c9ea95cd76048b7188bf9a1e2c 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -34,7 +34,7 @@ struct dir_struct {
        unsigned int show_ignored:1,
                     show_other_directories:1,
                     hide_empty_directories:1,
-                    no_dirlinks:1;
+                    no_gitlinks:1;
        struct dir_entry **entries;
 
        /* Exclude info */
diff --git a/entry.c b/entry.c
index 82bf7259a7d1d34a92a9bf214bccd9771748c582..ae6476496a81b23938569ab0e4c148ba45aba923 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -145,7 +145,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
                                                 "symlink %s (%s)", path, strerror(errno));
                }
                break;
-       case S_IFDIRLNK:
+       case S_IFGITLINK:
                if (to_tempfile)
                        return error("git-checkout-index: cannot create temporary subproject %s", path);
                if (mkdir(path, 0777) < 0)
@@ -194,7 +194,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t
                unlink(path);
                if (S_ISDIR(st.st_mode)) {
                        /* If it is a gitlink, leave it alone! */
-                       if (S_ISDIRLNK(ntohl(ce->ce_mode)))
+                       if (S_ISGITLINK(ntohl(ce->ce_mode)))
                                return 0;
                        if (!state->force)
                                return error("%s is a directory", path);
index 22316597df60648c850001e068938eaf39d57f6d..9d3e5eb72fea51eb30a201151aeffe1dfbb549d3 100644 (file)
@@ -11,7 +11,6 @@
 
 char git_default_email[MAX_GITNAME];
 char git_default_name[MAX_GITNAME];
-int use_legacy_headers = 1;
 int trust_executable_bit = 1;
 int has_symlinks = 1;
 int assume_unchanged;
@@ -24,7 +23,9 @@ const char *git_commit_encoding;
 const char *git_log_output_encoding;
 int shared_repository = PERM_UMASK;
 const char *apply_default_whitespace;
-int zlib_compression_level = Z_DEFAULT_COMPRESSION;
+int zlib_compression_level = Z_BEST_SPEED;
+int core_compression_level;
+int core_compression_seen;
 size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
 size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
 size_t delta_base_cache_limit = 16 * 1024 * 1024;
index 17554f68493a8409c64b47334a94e622d47d8133..f9bfcc72c87bf79fcb7a5faef01bc0d12aa15420 100644 (file)
@@ -1272,7 +1272,7 @@ static int update_branch(struct branch *b)
 
        if (read_ref(b->name, old_sha1))
                hashclr(old_sha1);
-       lock = lock_any_ref_for_update(b->name, old_sha1);
+       lock = lock_any_ref_for_update(b->name, old_sha1, 0);
        if (!lock)
                return error("Unable to lock %s", b->name);
        if (!force_update && !is_null_sha1(old_sha1)) {
index 06f4aeced439312c43f2736a1684f9e22218899b..aa59043c036e492a314c75080a61ab3d6a2b403d 100644 (file)
@@ -733,7 +733,7 @@ int main(int argc, char **argv)
        }
        if (!dest)
                usage(fetch_pack_usage);
-       pid = git_connect(fd, dest, uploadpack);
+       pid = git_connect(fd, dest, uploadpack, verbose ? CONNECT_VERBOSE : 0);
        if (pid < 0)
                return 1;
        if (heads && nr_heads)
diff --git a/git-applymbox.sh b/git-applymbox.sh
deleted file mode 100755 (executable)
index c18e80f..0000000
+++ /dev/null
@@ -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
diff --git a/git-applypatch.sh b/git-applypatch.sh
deleted file mode 100755 (executable)
index 8df2aee..0000000
+++ /dev/null
@@ -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
index ed7c2c5f6aab338c844329e3ae3d9c7ce003680f..6b6facfd5a224afca735d4693d304726f4032e6f 100755 (executable)
@@ -270,15 +270,7 @@ if [ "$?" -eq 0 ]; then
                fi
        elif test -n "$detached"
        then
-               # NEEDSWORK: we would want a command to detach the HEAD
-               # atomically, instead of this handcrafted command sequence.
-               # Perhaps:
-               #       git update-ref --detach HEAD $new
-               # or something like that...
-               #
-               git-rev-parse HEAD >"$GIT_DIR/HEAD.new" &&
-               mv "$GIT_DIR/HEAD.new" "$GIT_DIR/HEAD" &&
-               git-update-ref -m "checkout: moving to $arg" HEAD "$detached" ||
+               git-update-ref --no-deref -m "checkout: moving to $arg" HEAD "$detached" ||
                        die "Cannot detach HEAD"
                if test -n "$detach_warn"
                then
index d6ae99b8c00c278f6e4b0190dfb892b857f27103..42060ef6e165b6b39e4842138013cf264688ebd5 100755 (executable)
@@ -15,9 +15,9 @@
     die "GIT_DIR is not defined or is unreadable";
 }
 
-our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d);
+our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u);
 
-getopts('hPpvcfam:d:');
+getopts('uhPpvcfam:d:');
 
 $opt_h && usage();
 
 
 my %cvsstat;
 if (@canstatusfiles) {
+    if ($opt_u) {
+      my @updated = safe_pipe_capture(@cvs, 'update', @canstatusfiles);
+      print @updated;
+    }
     my @cvsoutput;
     @cvsoutput= safe_pipe_capture(@cvs, 'status', @canstatusfiles);
     my $matchcount = 0;
index 1de517791f8a293a9c770f6260ff48f9719845ad..2b4825a8eebad57fa9857593d0a8aa5928b313fe 100755 (executable)
 if (@ARGV && $ARGV[0] eq 'pserver') {
     $state->{method} = 'pserver';
     my $line = <STDIN>; chomp $line;
-    unless( $line eq 'BEGIN AUTH REQUEST') {
+    unless( $line =~ /^BEGIN (AUTH|VERIFICATION) REQUEST$/) {
        die "E Do not understand $line - expecting BEGIN AUTH REQUEST\n";
     }
+    my $request = $1;
     $line = <STDIN>; chomp $line;
     req_Root('root', $line) # reuse Root
        or die "E Invalid root $line \n";
     }
     $line = <STDIN>; chomp $line;    # validate the password?
     $line = <STDIN>; chomp $line;
-    unless ($line eq 'END AUTH REQUEST') {
-       die "E Do not understand $line -- expecting END AUTH REQUEST\n";
+    unless ($line eq "END $request REQUEST") {
+       die "E Do not understand $line -- expecting END $request REQUEST\n";
     }
     print "I LOVE YOU\n";
+    exit if $request eq 'VERIFICATION'; # cvs login
     # and now back to our regular programme...
 }
 
index 0e05cf1195737d2c7afc4b9447d7b4105908bf77..6d3a3468b3f22d8eeb1a2ba568a6c8ed48606dbd 100755 (executable)
@@ -61,7 +61,7 @@ do
                quiet=--quiet
                ;;
        -v|--verbose)
-               verbose=Yes
+               verbose="$verbose"Yes
                ;;
        -k|--k|--ke|--kee|--keep)
                keep='-k -k'
@@ -201,8 +201,14 @@ fetch_all_at_once () {
                        echo "$ls_remote_result" | \
                                git-fetch--tool pick-rref "$rref" "-"
                else
+                       flags=
+                       case $verbose in
+                       YesYes*)
+                           flags="-v"
+                           ;;
+                       esac
                        git-fetch-pack --thin $exec $keep $shallow_depth \
-                               $quiet $no_progress "$remote" $rref ||
+                               $quiet $no_progress $flags "$remote" $rref ||
                        echo failed "$remote"
                fi
        fi
index b2f8a2acd398b7c61b68a59c6d3631ca84be0727..981d69d35f4e6349d99192996af3b793071a6172 100755 (executable)
@@ -3,7 +3,7 @@
 # Copyright (c) 2005 Junio C Hamano
 #
 
-USAGE='[-n] [--no-commit] [--squash] [-s <strategy>] [-m=<merge-message>] <commit>+'
+USAGE='[-n] [--summary] [--no-commit] [--squash] [-s <strategy>] [-m=<merge-message>] <commit>+'
 
 SUBDIRECTORY_OK=Yes
 . git-sh-setup
@@ -88,11 +88,11 @@ finish () {
        '')
                ;;
        ?*)
-               case "$no_summary" in
-               '')
-                       git-diff-tree --stat --summary -M "$head" "$1"
-                       ;;
-               esac
+               if test "$show_diffstat" = t
+               then
+                       # We want color (if set), but no pager
+                       GIT_PAGER='' git-diff --stat --summary -M "$head" "$1"
+               fi
                ;;
        esac
 }
@@ -125,7 +125,9 @@ do
        case "$1" in
        -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\
                --no-summa|--no-summar|--no-summary)
-               no_summary=t ;;
+               show_diffstat=false ;;
+       --summary)
+               show_diffstat=t ;;
        --sq|--squ|--squa|--squas|--squash)
                squash=t no_commit=t ;;
        --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)
@@ -167,6 +169,11 @@ do
        shift
 done
 
+if test -z "$show_diffstat"; then
+    test "$(git-config --bool merge.diffstat)" = false && show_diffstat=false
+    test -z "$show_diffstat" && show_diffstat=t
+fi
+
 # This could be traditional "merge <msg> HEAD <commit>..."  and the
 # way we can tell it is to see if the second token is HEAD, but some
 # people might have misused the interface and used a committish that
index a3665d7751cc836dc669390df7b972c29575037b..ba0ca079e204a464ee05b6c000ed891ed3c08b33 100755 (executable)
@@ -22,6 +22,9 @@ do
        -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\
                --no-summa|--no-summar|--no-summary)
                no_summary=-n ;;
+       --summary)
+               no_summary=$1
+               ;;
        --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)
                no_commit=--no-commit ;;
        --sq|--squ|--squa|--squas|--squash)
index 2dc2c4fe9b4d4a7ff10398938efbed28fa6f43f7..2aa3a011dbe31e5167534a8bf443ff27dd96b38f 100755 (executable)
@@ -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"
@@ -307,7 +307,8 @@ fi
 if test -n "$verbose"
 then
        echo "Changes from $mb to $onto:"
-       git-diff-tree --stat --summary "$mb" "$onto"
+       # We want color (if set), but no pager
+       GIT_PAGER='' git-diff --stat --summary "$mb" "$onto"
 fi
 
 # Rewind the head to "$onto"; this saves our current head in ORIG_HEAD.
index ddfa8b44a17f1ea7ff039bca1d6132ba07e5cf12..8c32724be75b77f145106f96257afccb56cfc1ef 100755 (executable)
@@ -3,12 +3,12 @@
 # Copyright (c) 2005 Linus Torvalds
 #
 
-USAGE='[-a] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]'
+USAGE='[-a] [-d] [-f] [-l] [-n] [-q] [--max-pack-size=N] [--window=N] [--depth=N]'
 SUBDIRECTORY_OK='Yes'
 . git-sh-setup
 
 no_update_info= all_into_one= remove_redundant=
-local= quiet= no_reuse_delta= extra=
+local= quiet= no_reuse= extra=
 while case "$#" in 0) break ;; esac
 do
        case "$1" in
@@ -16,8 +16,9 @@ do
        -a)     all_into_one=t ;;
        -d)     remove_redundant=t ;;
        -q)     quiet=-q ;;
-       -f)     no_reuse_delta=--no-reuse-delta ;;
+       -f)     no_reuse=--no-reuse-object ;;
        -l)     local=--local ;;
+       --max-pack-size=*) extra="$extra $1" ;;
        --window=*) extra="$extra $1" ;;
        --depth=*) extra="$extra $1" ;;
        *)      usage ;;
@@ -35,7 +36,7 @@ true)
 esac
 
 PACKDIR="$GIT_OBJECT_DIRECTORY/pack"
-PACKTMP="$GIT_DIR/.tmp-$$-pack"
+PACKTMP="$GIT_OBJECT_DIRECTORY/.tmp-$$-pack"
 rm -f "$PACKTMP"-*
 trap 'rm -f "$PACKTMP"-*' 0 1 2 3 15
 
@@ -61,12 +62,14 @@ case ",$all_into_one," in
        ;;
 esac
 
-args="$args $local $quiet $no_reuse_delta$extra"
-name=$(git-pack-objects --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
+args="$args $local $quiet $no_reuse$extra"
+names=$(git-pack-objects --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
        exit 1
-if [ -z "$name" ]; then
+if [ -z "$names" ]; then
        echo Nothing new to pack.
-else
+fi
+for name in $names ; do
+       fullbases="$fullbases pack-$name"
        chmod a-w "$PACKTMP-$name.pack"
        chmod a-w "$PACKTMP-$name.idx"
        if test "$quiet" != '-q'; then
@@ -92,7 +95,7 @@ else
                exit 1
        }
        rm -f "$PACKDIR/old-pack-$name.pack" "$PACKDIR/old-pack-$name.idx"
-fi
+done
 
 if test "$remove_redundant" = t
 then
@@ -103,8 +106,8 @@ then
                ( cd "$PACKDIR" &&
                  for e in $existing
                  do
-                       case "$e" in
-                       pack-$name) ;;
+                       case " $fullbases " in
+                       *" $e "*) ;;
                        *)      rm -f "$e.pack" "$e.idx" "$e.keep" ;;
                        esac
                  done
diff --git a/git-submodule.sh b/git-submodule.sh
new file mode 100755 (executable)
index 0000000..6ed5a6c
--- /dev/null
@@ -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
index 5c7011a37b2e4dab8c38de54a3e531d2fa16ddbe..c3921cb0baf162b8294fe47b2fff749114f3061f 100755 (executable)
@@ -132,7 +132,7 @@ BEGIN
        # $feature{'snapshot'}{'default'} = [undef];
        # To have project specific config enable override in $GITWEB_CONFIG
        # $feature{'snapshot'}{'override'} = 1;
-       # and in project config gitweb.snapshot = none|gzip|bzip2;
+       # and in project config gitweb.snapshot = none|gzip|bzip2|zip;
        'snapshot' => {
                'sub' => \&feature_snapshot,
                'override' => 0,
@@ -244,6 +244,8 @@ sub feature_snapshot {
                return ('x-gzip', 'gz', 'gzip');
        } elsif ($val eq 'bzip2') {
                return ('x-bzip2', 'bz2', 'bzip2');
+       } elsif ($val eq 'zip') {
+               return ('x-zip', 'zip', '');
        } elsif ($val eq 'none') {
                return ();
        }
@@ -3976,19 +3978,26 @@ sub git_snapshot {
                $hash = git_get_head_hash($project);
        }
 
-       my $filename = decode_utf8(basename($project)) . "-$hash.tar.$suffix";
+       my $git = git_cmd_str();
+       my $name = $project;
+       $name =~ s/\047/\047\\\047\047/g;
+       my $filename = decode_utf8(basename($project));
+       my $cmd;
+       if ($suffix eq 'zip') {
+               $filename .= "-$hash.$suffix";
+               $cmd = "$git archive --format=zip --prefix=\'$name\'/ $hash";
+       } else {
+               $filename .= "-$hash.tar.$suffix";
+               $cmd = "$git archive --format=tar --prefix=\'$name\'/ $hash | $command";
+       }
 
        print $cgi->header(
                -type => "application/$ctype",
                -content_disposition => 'inline; filename="' . "$filename" . '"',
                -status => '200 OK');
 
-       my $git = git_cmd_str();
-       my $name = $project;
-       $name =~ s/\047/\047\\\047\047/g;
-       open my $fd, "-|",
-               "$git archive --format=tar --prefix=\'$name\'/ $hash | $command"
-               or die_error(undef, "Execute git-tar-tree failed");
+       open my $fd, "-|", $cmd
+               or die_error(undef, "Execute git-archive failed");
        binmode STDOUT, ':raw';
        print <$fd>;
        binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
index e3f767582bfc71fc3d1b90be8fa76ab3ad7e1664..79d2c3860825ba1e68fff555b4433731d51c1b52 100644 (file)
@@ -9,6 +9,7 @@
 #include "diff.h"
 #include "revision.h"
 #include "exec_cmd.h"
+#include "remote.h"
 
 #include <expat.h>
 
index 310f8d39082a12d2c3daddd1fca454686e7425c3..e5c88c278fe23eefbf4cb4dd0c66251b208e48bd 100644 (file)
@@ -87,7 +87,7 @@ static void process_tree(struct rev_info *revs,
                        process_tree(revs,
                                     lookup_tree(entry.sha1),
                                     p, &me, entry.path);
-               else if (S_ISDIRLNK(entry.mode))
+               else if (S_ISGITLINK(entry.mode))
                        process_gitlink(revs, entry.sha1,
                                        p, &me, entry.path);
                else
index d04536bbff7cba22ca67521d45e690dfa5aa8675..3623c716e3e3b7d4a28800e34afd0e5f0e13a43d 100644 (file)
@@ -73,12 +73,11 @@ static int verify_packfile(struct packed_git *p,
 }
 
 
-#define MAX_CHAIN 40
+#define MAX_CHAIN 50
 
 static void show_pack_info(struct packed_git *p)
 {
-       uint32_t nr_objects, i, chain_histogram[MAX_CHAIN];
-
+       uint32_t nr_objects, i, chain_histogram[MAX_CHAIN+1];
        nr_objects = p->num_objects;
        memset(chain_histogram, 0, sizeof(chain_histogram));
 
@@ -109,32 +108,37 @@ static void show_pack_info(struct packed_git *p)
                        printf("%-6s %lu %"PRIuMAX" %u %s\n",
                               type, size, (uintmax_t)offset,
                               delta_chain_length, sha1_to_hex(base_sha1));
-                       if (delta_chain_length < MAX_CHAIN)
+                       if (delta_chain_length <= MAX_CHAIN)
                                chain_histogram[delta_chain_length]++;
                        else
                                chain_histogram[0]++;
                }
        }
 
-       for (i = 0; i < MAX_CHAIN; i++) {
+       for (i = 0; i <= MAX_CHAIN; i++) {
                if (!chain_histogram[i])
                        continue;
-               printf("chain length %s %d: %d object%s\n",
-                      i ? "=" : ">=",
-                      i ? i : MAX_CHAIN,
-                      chain_histogram[i],
-                      1 < chain_histogram[i] ? "s" : "");
+               printf("chain length = %d: %d object%s\n", i,
+                      chain_histogram[i], chain_histogram[i] > 1 ? "s" : "");
        }
+       if (chain_histogram[0])
+               printf("chain length > %d: %d object%s\n", MAX_CHAIN,
+                      chain_histogram[0], chain_histogram[0] > 1 ? "s" : "");
 }
 
 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);
index 87077e150c1b53a26464089a2cabafafb32e6636..6bc3bdf3f4c33f369c63acf9284ce9af18286184 100644 (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;
index 96bfac498bdc58c90c9bc136a1df7bac9bfa0d92..ceb787170e17df130e26658e5a189bb63684ab3e 100644 (file)
@@ -64,7 +64,7 @@ int main(int argc, char **argv)
        if (!dest || i != argc - 1)
                usage(peek_remote_usage);
 
-       pid = git_connect(fd, dest, uploadpack);
+       pid = git_connect(fd, dest, uploadpack, 0);
        if (pid < 0)
                return 1;
        ret = peek_remote(fd, flags);
index 05f7890314e0bd596eb2e4110cdb98eeea61c9da..4344f4eed5e46c4e013018af8ef9ab062f740d8f 100644 (file)
@@ -62,11 +62,13 @@ int display_progress(struct progress *progress, unsigned n)
                        fprintf(stderr, "%s%4u%% (%u/%u) done\r",
                                progress->prefix, percent, n, progress->total);
                        progress_update = 0;
+                       progress->need_lf = 1;
                        return 1;
                }
        } else if (progress_update) {
                fprintf(stderr, "%s%u\r", progress->prefix, n);
                progress_update = 0;
+               progress->need_lf = 1;
                return 1;
        }
        return 0;
@@ -80,6 +82,7 @@ void start_progress(struct progress *progress, const char *title,
        progress->total = total;
        progress->last_percent = -1;
        progress->delay = 0;
+       progress->need_lf = 0;
        if (snprintf(buf, sizeof(buf), title, total))
                fprintf(stderr, "%s\n", buf);
        set_progress_signal();
@@ -95,12 +98,13 @@ void start_progress_delay(struct progress *progress, const char *title,
        progress->delayed_percent_treshold = percent_treshold;
        progress->delayed_title = title;
        progress->delay = delay;
+       progress->need_lf = 0;
        set_progress_signal();
 }
 
 void stop_progress(struct progress *progress)
 {
        clear_progress_signal();
-       if (progress->total)
+       if (progress->need_lf)
                fputc('\n', stderr);
 }
index 5ae1a89e5a4669553e44d5bd0cc339c308c60c0f..a7c17ca7c4bdad953508d03c20e73022b03bd25a 100644 (file)
@@ -8,6 +8,7 @@ struct progress {
        unsigned delay;
        unsigned delayed_percent_treshold;
        const char *delayed_title;
+       int need_lf;
 };
 
 int display_progress(struct progress *progress, unsigned n);
index d9f46da5cc547d8cfaa34c901be50a13995b120e..ad4e18753777a951e396116c989896f393d56e47 100644 (file)
@@ -92,7 +92,7 @@ static int ce_compare_gitlink(struct cache_entry *ce)
 
        /*
         * We don't actually require that the .git directory
-        * under DIRLNK directory be a valid git directory. It
+        * under GITLINK directory be a valid git directory. It
         * might even be missing (in case nobody populated that
         * sub-project).
         *
@@ -115,7 +115,7 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
                        return DATA_CHANGED;
                break;
        case S_IFDIR:
-               if (S_ISDIRLNK(ntohl(ce->ce_mode)))
+               if (S_ISGITLINK(ntohl(ce->ce_mode)))
                        return 0;
        default:
                return TYPE_CHANGED;
@@ -142,7 +142,7 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
                    (has_symlinks || !S_ISREG(st->st_mode)))
                        changed |= TYPE_CHANGED;
                break;
-       case S_IFDIRLNK:
+       case S_IFGITLINK:
                if (!S_ISDIR(st->st_mode))
                        changed |= TYPE_CHANGED;
                else if (ce_compare_gitlink(ce))
index 26aa26bcb5089adcf400d12b673519e328b49a23..d3c422be58c5ee1af7e75cf4186bc9bbfb19ec30 100644 (file)
@@ -209,7 +209,7 @@ static const char *update(struct command *cmd)
                return NULL; /* good */
        }
        else {
-               lock = lock_any_ref_for_update(name, old_sha1);
+               lock = lock_any_ref_for_update(name, old_sha1, 0);
                if (!lock) {
                        error("failed to lock %s", name);
                        return "failed to lock";
diff --git a/refs.c b/refs.c
index 89876bff871d007a6675f5790ce8cb34fe21fb39..ef4484d2937727b131eade1c6b62180cd9722976 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -603,15 +603,20 @@ int get_ref_sha1(const char *ref, unsigned char *sha1)
 
 static inline int bad_ref_char(int ch)
 {
-       return (((unsigned) ch) <= ' ' ||
-               ch == '~' || ch == '^' || ch == ':' ||
-               /* 2.13 Pattern Matching Notation */
-               ch == '?' || ch == '*' || ch == '[');
+       if (((unsigned) ch) <= ' ' ||
+           ch == '~' || ch == '^' || ch == ':')
+               return 1;
+       /* 2.13 Pattern Matching Notation */
+       if (ch == '?' || ch == '[') /* Unsupported */
+               return 1;
+       if (ch == '*') /* Supported at the end */
+               return 2;
+       return 0;
 }
 
 int check_ref_format(const char *ref)
 {
-       int ch, level;
+       int ch, level, bad_type;
        const char *cp = ref;
 
        level = 0;
@@ -622,13 +627,19 @@ int check_ref_format(const char *ref)
                        return -1; /* should not end with slashes */
 
                /* we are at the beginning of the path component */
-               if (ch == '.' || bad_ref_char(ch))
+               if (ch == '.')
                        return -1;
+               bad_type = bad_ref_char(ch);
+               if (bad_type) {
+                       return (bad_type == 2 && !*cp) ? -3 : -1;
+               }
 
                /* scan the rest of the path component */
                while ((ch = *cp++) != 0) {
-                       if (bad_ref_char(ch))
-                               return -1;
+                       bad_type = bad_ref_char(ch);
+                       if (bad_type) {
+                               return (bad_type == 2 && !*cp) ? -3 : -1;
+                       }
                        if (ch == '/')
                                break;
                        if (ch == '.' && *cp == '.')
@@ -736,19 +747,20 @@ static int is_refname_available(const char *ref, const char *oldref,
        return 1;
 }
 
-static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int *flag)
+static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int flags, int *type_p)
 {
        char *ref_file;
        const char *orig_ref = ref;
        struct ref_lock *lock;
        struct stat st;
        int last_errno = 0;
+       int type;
        int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
 
        lock = xcalloc(1, sizeof(struct ref_lock));
        lock->lock_fd = -1;
 
-       ref = resolve_ref(ref, lock->old_sha1, mustexist, flag);
+       ref = resolve_ref(ref, lock->old_sha1, mustexist, &type);
        if (!ref && errno == EISDIR) {
                /* we are trying to lock foo but we used to
                 * have foo/bar which now does not exist;
@@ -761,8 +773,10 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
                        error("there are still refs under '%s'", orig_ref);
                        goto error_return;
                }
-               ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, flag);
+               ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, &type);
        }
+       if (type_p)
+           *type_p = type;
        if (!ref) {
                last_errno = errno;
                error("unable to resolve reference %s: %s",
@@ -780,10 +794,15 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
 
        lock->lk = xcalloc(1, sizeof(struct lock_file));
 
+       if (flags & REF_NODEREF)
+               ref = orig_ref;
        lock->ref_name = xstrdup(ref);
        lock->orig_ref_name = xstrdup(orig_ref);
        ref_file = git_path("%s", ref);
-       lock->force_write = lstat(ref_file, &st) && errno == ENOENT;
+       if (lstat(ref_file, &st) && errno == ENOENT)
+               lock->force_write = 1;
+       if ((flags & REF_NODEREF) && (type & REF_ISSYMREF))
+               lock->force_write = 1;
 
        if (safe_create_leading_directories(ref_file)) {
                last_errno = errno;
@@ -806,14 +825,14 @@ struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
        if (check_ref_format(ref))
                return NULL;
        strcpy(refpath, mkpath("refs/%s", ref));
-       return lock_ref_sha1_basic(refpath, old_sha1, NULL);
+       return lock_ref_sha1_basic(refpath, old_sha1, 0, NULL);
 }
 
-struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1)
+struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags)
 {
        if (check_ref_format(ref) == -1)
                return NULL;
-       return lock_ref_sha1_basic(ref, old_sha1, NULL);
+       return lock_ref_sha1_basic(ref, old_sha1, flags, NULL);
 }
 
 static struct lock_file packlock;
@@ -858,7 +877,7 @@ int delete_ref(const char *refname, const unsigned char *sha1)
        struct ref_lock *lock;
        int err, i, ret = 0, flag = 0;
 
-       lock = lock_ref_sha1_basic(refname, sha1, &flag);
+       lock = lock_ref_sha1_basic(refname, sha1, 0, &flag);
        if (!lock)
                return 1;
        if (!(flag & REF_ISPACKED)) {
@@ -909,7 +928,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
        if (!is_refname_available(newref, oldref, get_loose_refs(), 0))
                return 1;
 
-       lock = lock_ref_sha1_basic(renamed_ref, NULL, NULL);
+       lock = lock_ref_sha1_basic(renamed_ref, NULL, 0, NULL);
        if (!lock)
                return error("unable to lock %s", renamed_ref);
        lock->force_write = 1;
@@ -963,7 +982,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
        }
        logmoved = log;
 
-       lock = lock_ref_sha1_basic(newref, NULL, NULL);
+       lock = lock_ref_sha1_basic(newref, NULL, 0, NULL);
        if (!lock) {
                error("unable to lock %s for update", newref);
                goto rollback;
@@ -979,7 +998,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
        return 0;
 
  rollback:
-       lock = lock_ref_sha1_basic(oldref, NULL, NULL);
+       lock = lock_ref_sha1_basic(oldref, NULL, 0, NULL);
        if (!lock) {
                error("unable to lock %s for rollback", oldref);
                goto rollbacklog;
diff --git a/refs.h b/refs.h
index f61f6d934e80b21432f93cd3b9f138770a9d2b86..f234eb76ba5d6aba03f484fe58d11ba81a90ff5a 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -33,7 +33,8 @@ extern int get_ref_sha1(const char *ref, unsigned char *sha1);
 extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1);
 
 /** Locks any ref (for 'HEAD' type refs). */
-extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1);
+#define REF_NODEREF    0x01
+extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags);
 
 /** Release any lock taken but not written. **/
 extern void unlock_ref(struct ref_lock *lock);
diff --git a/remote.c b/remote.c
new file mode 100644 (file)
index 0000000..d904616
--- /dev/null
+++ b/remote.c
@@ -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;
+}
diff --git a/remote.h b/remote.h
new file mode 100644 (file)
index 0000000..01dbcef
--- /dev/null
+++ b/remote.h
@@ -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
index 0125d41136871ad2cd07daaaabf40bd2f902a44f..0a29b53673e0e44547dfe91aaede5f312530a0ec 100644 (file)
@@ -881,6 +881,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
        const char **unrecognized = argv + 1;
        int left = 1;
        int all_match = 0;
+       int regflags = 0;
 
        /* First, search for "--" */
        seen_dashdash = 0;
@@ -1152,6 +1153,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                add_message_grep(revs, arg+7);
                                continue;
                        }
+                       if (!prefixcmp(arg, "--extended-regexp")) {
+                               regflags |= REG_EXTENDED;
+                               continue;
+                       }
+                       if (!prefixcmp(arg, "--regexp-ignore-case")) {
+                               regflags |= REG_ICASE;
+                               continue;
+                       }
                        if (!strcmp(arg, "--all-match")) {
                                all_match = 1;
                                continue;
@@ -1200,6 +1209,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                }
        }
 
+       if (revs->grep_filter)
+               revs->grep_filter->regflags |= regflags;
+
        if (show_merge)
                prepare_show_merge(revs);
        if (def && !revs->pending.nr) {
index eff523e191b35385895f6b077fc76c7c21819012..7e779d33ee9ea5f7d2e6aedc8c3a0a0476e87135 100644 (file)
@@ -73,6 +73,17 @@ int start_command(struct child_process *cmd)
                        close(cmd->out);
                }
 
+               if (cmd->dir && chdir(cmd->dir))
+                       die("exec %s: cd to %s failed (%s)", cmd->argv[0],
+                           cmd->dir, strerror(errno));
+               if (cmd->env) {
+                       for (; *cmd->env; cmd->env++) {
+                               if (strchr(*cmd->env, '='))
+                                       putenv((char*)*cmd->env);
+                               else
+                                       unsetenv(*cmd->env);
+                       }
+               }
                if (cmd->git_cmd) {
                        execv_git_cmd(cmd->argv);
                } else {
@@ -133,13 +144,37 @@ int run_command(struct child_process *cmd)
        return finish_command(cmd);
 }
 
+static void prepare_run_command_v_opt(struct child_process *cmd,
+                                     const char **argv,
+                                     int opt)
+{
+       memset(cmd, 0, sizeof(*cmd));
+       cmd->argv = argv;
+       cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
+       cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
+       cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
+}
+
 int run_command_v_opt(const char **argv, int opt)
 {
        struct child_process cmd;
-       memset(&cmd, 0, sizeof(cmd));
-       cmd.argv = argv;
-       cmd.no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
-       cmd.git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
-       cmd.stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
+       prepare_run_command_v_opt(&cmd, argv, opt);
+       return run_command(&cmd);
+}
+
+int run_command_v_opt_cd(const char **argv, int opt, const char *dir)
+{
+       struct child_process cmd;
+       prepare_run_command_v_opt(&cmd, argv, opt);
+       cmd.dir = dir;
+       return run_command(&cmd);
+}
+
+int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env)
+{
+       struct child_process cmd;
+       prepare_run_command_v_opt(&cmd, argv, opt);
+       cmd.dir = dir;
+       cmd.env = env;
        return run_command(&cmd);
 }
index 3680ef9d452490c67788b0ab027839a8383ed855..7958eb1e0b7a927019460e06d7a01622eddf81df 100644 (file)
@@ -16,6 +16,8 @@ struct child_process {
        pid_t pid;
        int in;
        int out;
+       const char *dir;
+       const char *const *env;
        unsigned close_in:1;
        unsigned close_out:1;
        unsigned no_stdin:1;
@@ -32,5 +34,12 @@ int run_command(struct child_process *);
 #define RUN_GIT_CMD         2  /*If this is to be git sub-command */
 #define RUN_COMMAND_STDOUT_TO_STDERR 4
 int run_command_v_opt(const char **argv, int opt);
+int run_command_v_opt_cd(const char **argv, int opt, const char *dir);
+
+/*
+ * env (the environment) is to be formatted like environ: "VAR=VALUE".
+ * To unset an environment variable use just "VAR".
+ */
+int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env);
 
 #endif
index d5b51628dfbcc44b22b5069df19eeac67dda1e57..fecbda981b7c6c2504abdd7afb94e35e73e18cce 100644 (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);
 
-       pid = git_connect(fd, dest, receivepack);
+       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);
index c2f807f4c282b99fe063e999ecf61a3dc12fb86d..30bcd46ceb761b0f0b17295d532a6ddb5f33387a 100644 (file)
@@ -376,11 +376,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);
 
@@ -530,6 +531,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,
@@ -605,6 +621,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;
@@ -757,8 +776,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;
        }
@@ -766,6 +784,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;
@@ -972,7 +994,7 @@ void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
        return map;
 }
 
-int legacy_loose_object(unsigned char *map)
+static int legacy_loose_object(unsigned char *map)
 {
        unsigned int word;
 
@@ -1034,6 +1056,14 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon
                return inflate(stream, 0);
        }
 
+
+       /*
+        * There used to be a second loose object header format which
+        * was meant to mimic the in-pack format, allowing for direct
+        * copy of the object data.  This format turned up not to be
+        * really worth it and we don't write it any longer.  But we
+        * can still read it.
+        */
        used = unpack_object_header_gently(map, mapsize, &type, &size);
        if (!used || !valid_loose_object_type[type])
                return -1;
@@ -1564,10 +1594,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;
@@ -1604,6 +1639,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;
@@ -1646,20 +1687,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) {
                        /*
@@ -1672,14 +1718,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;
 }
 
@@ -1962,40 +2017,6 @@ static int write_buffer(int fd, const void *buf, size_t len)
        return 0;
 }
 
-static int write_binary_header(unsigned char *hdr, enum object_type type, unsigned long len)
-{
-       int hdr_len;
-       unsigned char c;
-
-       c = (type << 4) | (len & 15);
-       len >>= 4;
-       hdr_len = 1;
-       while (len) {
-               *hdr++ = c | 0x80;
-               hdr_len++;
-               c = (len & 0x7f);
-               len >>= 7;
-       }
-       *hdr = c;
-       return hdr_len;
-}
-
-static void setup_object_header(z_stream *stream, const char *type, unsigned long len)
-{
-       int obj_type, hdrlen;
-
-       if (use_legacy_headers) {
-               while (deflate(stream, 0) == Z_OK)
-                       /* nothing */;
-               return;
-       }
-       obj_type = type_from_string(type);
-       hdrlen = write_binary_header(stream->next_out, obj_type, len);
-       stream->total_out = hdrlen;
-       stream->next_out += hdrlen;
-       stream->avail_out -= hdrlen;
-}
-
 int hash_sha1_file(const void *buf, unsigned long len, const char *type,
                    unsigned char *sha1)
 {
@@ -2062,7 +2083,8 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
        /* First header.. */
        stream.next_in = (unsigned char *)hdr;
        stream.avail_in = hdrlen;
-       setup_object_header(&stream, type, len);
+       while (deflate(&stream, 0) == Z_OK)
+               /* nothing */;
 
        /* Then the data itself.. */
        stream.next_in = buf;
index 55f25a2d3b49b1d565a9840b7dcfb2e22687cdd3..7df01af78805686572e32d5ade1460fc925a9a71 100644 (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;
index 7d354a1fae774ca0e77e554dad827f1a9b781818..b0252b9413e5274d87f5437882be1fd625bc8bef 100755 (executable)
@@ -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
 
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
new file mode 100755 (executable)
index 0000000..6274729
--- /dev/null
@@ -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
index d406a8824a382433cbc8c6f499ef3bc1f00301b2..e9ef315173f9596212d9104587486315603b2b51 100755 (executable)
@@ -47,6 +47,138 @@ test_expect_success 'basic checkout' \
   'GIT_CONFIG="$git_config" cvs -Q co -d cvswork master &&
    test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5))" = "empty/1.1/"'
 
+#------------------------
+# PSERVER AUTHENTICATION
+#------------------------
+
+cat >request-anonymous  <<EOF
+BEGIN AUTH REQUEST
+$SERVERDIR
+anonymous
+
+END AUTH REQUEST
+EOF
+
+cat >request-git  <<EOF
+BEGIN AUTH REQUEST
+$SERVERDIR
+git
+
+END AUTH REQUEST
+EOF
+
+cat >login-anonymous <<EOF
+BEGIN VERIFICATION REQUEST
+$SERVERDIR
+anonymous
+
+END VERIFICATION REQUEST
+EOF
+
+cat >login-git <<EOF
+BEGIN VERIFICATION REQUEST
+$SERVERDIR
+git
+
+END VERIFICATION REQUEST
+EOF
+
+test_expect_success 'pserver authentication' \
+  'cat request-anonymous | git-cvsserver pserver >log 2>&1 &&
+   tail -n1 log | grep -q "^I LOVE YOU$"'
+
+test_expect_success 'pserver authentication failure (non-anonymous user)' \
+  'if cat request-git | git-cvsserver pserver >log 2>&1
+   then
+       false
+   else
+       true
+   fi &&
+   tail -n1 log | grep -q "^I HATE YOU$"'
+
+test_expect_success 'pserver authentication (login)' \
+  'cat login-anonymous | git-cvsserver pserver >log 2>&1 &&
+   tail -n1 log | grep -q "^I LOVE YOU$"'
+
+test_expect_success 'pserver authentication failure (login/non-anonymous user)' \
+  'if cat login-git | git-cvsserver pserver >log 2>&1
+   then
+       false
+   else
+       true
+   fi &&
+   tail -n1 log | grep -q "^I HATE YOU$"'
+
+
+#--------------
+# CONFIG TESTS
+#--------------
+
+test_expect_success 'gitcvs.enabled = false' \
+  'GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false &&
+   if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1
+   then
+     echo unexpected cvs success
+     false
+   else
+     true
+   fi &&
+   cat cvs.log | grep -q "GITCVS emulation disabled" &&
+   test ! -d cvswork2'
+
+rm -fr cvswork2
+test_expect_success 'gitcvs.ext.enabled = true' \
+  'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
+   GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false &&
+   GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
+   diff -q cvswork cvswork2'
+
+rm -fr cvswork2
+test_expect_success 'gitcvs.ext.enabled = false' \
+  'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled false &&
+   GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
+   if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1
+   then
+     echo unexpected cvs success
+     false
+   else
+     true
+   fi &&
+   cat cvs.log | grep -q "GITCVS emulation disabled" &&
+   test ! -d cvswork2'
+
+rm -fr cvswork2
+test_expect_success 'gitcvs.dbname' \
+  'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
+   GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs.%a.%m.sqlite &&
+   GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
+   diff -q cvswork cvswork2 &&
+   test -f "$SERVERDIR/gitcvs.ext.master.sqlite" &&
+   cmp "$SERVERDIR/gitcvs.master.sqlite" "$SERVERDIR/gitcvs.ext.master.sqlite"'
+
+rm -fr cvswork2
+test_expect_success 'gitcvs.ext.dbname' \
+  'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
+   GIT_DIR="$SERVERDIR" git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
+   GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
+   GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
+   diff -q cvswork cvswork2 &&
+   test -f "$SERVERDIR/gitcvs1.ext.master.sqlite" &&
+   test ! -f "$SERVERDIR/gitcvs2.ext.master.sqlite" &&
+   cmp "$SERVERDIR/gitcvs.master.sqlite" "$SERVERDIR/gitcvs1.ext.master.sqlite"'
+
+
+#------------
+# CVS UPDATE
+#------------
+
+rm -fr "$SERVERDIR"
+cd "$WORKDIR" &&
+git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
+GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
+GIT_DIR="$SERVERDIR" git config --bool gitcvs.logfile "$SERVERDIR/gitcvs.log" ||
+exit 1
+
 test_expect_success 'cvs update (create new file)' \
   'echo testfile1 >testfile1 &&
    git add testfile1 &&
@@ -123,4 +255,75 @@ test_expect_success 'cvs update (re-add deleted file)' \
    test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.4/" &&
    diff -q testfile1 ../testfile1'
 
+cd "$WORKDIR"
+test_expect_success 'cvs update (merge)' \
+  'echo Line 0 >expected &&
+   for i in 1 2 3 4 5 6 7
+   do
+     echo Line $i >>merge
+     echo Line $i >>expected
+   done &&
+   echo Line 8 >>expected &&
+   git add merge &&
+   git commit -q -m "Merge test (pre-merge)" &&
+   git push gitcvs.git >/dev/null &&
+   cd cvswork &&
+   GIT_CONFIG="$git_config" cvs -Q update &&
+   test "$(echo $(grep merge CVS/Entries|cut -d/ -f2,3,5))" = "merge/1.1/" &&
+   diff -q merge ../merge &&
+   ( echo Line 0; cat merge ) >merge.tmp &&
+   mv merge.tmp merge &&
+   cd "$WORKDIR" &&
+   echo Line 8 >>merge &&
+   git add merge &&
+   git commit -q -m "Merge test (merge)" &&
+   git push gitcvs.git >/dev/null &&
+   cd cvswork &&
+   sleep 1 && touch merge &&
+   GIT_CONFIG="$git_config" cvs -Q update &&
+   diff -q merge ../expected'
+
+cd "$WORKDIR"
+
+cat >expected.C <<EOF
+<<<<<<< merge.mine
+Line 0
+=======
+LINE 0
+>>>>>>> merge.3
+EOF
+
+for i in 1 2 3 4 5 6 7 8
+do
+  echo Line $i >>expected.C
+done
+
+test_expect_success 'cvs update (conflict merge)' \
+  '( echo LINE 0; cat merge ) >merge.tmp &&
+   mv merge.tmp merge &&
+   git add merge &&
+   git commit -q -m "Merge test (conflict)" &&
+   git push gitcvs.git >/dev/null &&
+   cd cvswork &&
+   GIT_CONFIG="$git_config" cvs -Q update &&
+   diff -q merge ../expected.C'
+
+cd "$WORKDIR"
+test_expect_success 'cvs update (-C)' \
+  'cd cvswork &&
+   GIT_CONFIG="$git_config" cvs -Q update -C &&
+   diff -q merge ../merge'
+
+cd "$WORKDIR"
+test_expect_success 'cvs update (merge no-op)' \
+   'echo Line 9 >>merge &&
+    cp merge cvswork/merge &&
+    git add merge &&
+    git commit -q -m "Merge test (no-op)" &&
+    git push gitcvs.git >/dev/null &&
+    cd cvswork &&
+    sleep 1 && touch merge &&
+    GIT_CONFIG="$git_config" cvs -Q update &&
+    diff -q merge ../merge'
+
 test_done
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
new file mode 100755 (executable)
index 0000000..b92ab63
--- /dev/null
@@ -0,0 +1,490 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Jakub Narebski
+#
+
+test_description='gitweb as standalone script (basic tests).
+
+This test runs gitweb (git web interface) as CGI script from
+commandline, and checks that it would not write any errors
+or warnings to log.'
+
+gitweb_init () {
+       cat >gitweb_config.perl <<EOF
+#!/usr/bin/perl
+
+# gitweb configuration for tests
+
+our \$version = "current";
+our \$GIT = "git";
+our \$projectroot = "$(pwd)";
+our \$home_link_str = "projects";
+our \$site_name = "[localhost]";
+our \$site_header = "";
+our \$site_footer = "";
+our \$home_text = "indextext.html";
+our @stylesheets = ("file:///$(pwd)/../../gitweb/gitweb.css");
+our \$logo = "file:///$(pwd)/../../gitweb/git-logo.png";
+our \$favicon = "file:///$(pwd)/../../gitweb/git-favicon.png";
+our \$projects_list = "";
+our \$export_ok = "";
+our \$strict_export = "";
+
+CGI::Carp::set_programname("gitweb/gitweb.cgi");
+EOF
+
+       cat >.git/description <<EOF
+$0 test repository
+EOF
+}
+
+gitweb_run () {
+       export GATEWAY_INTERFACE="CGI/1.1"
+       export HTTP_ACCEPT="*/*"
+       export REQUEST_METHOD="GET"
+       export QUERY_STRING=""$1""
+       export PATH_INFO=""$2""
+
+       export GITWEB_CONFIG=$(pwd)/gitweb_config.perl
+
+       # some of git commands write to STDERR on error, but this is not
+       # written to web server logs, so we are not interested in that:
+       # we are interested only in properly formatted errors/warnings
+       rm -f gitweb.log &&
+       perl -- $(pwd)/../../gitweb/gitweb.perl \
+               >/dev/null 2>gitweb.log &&
+       if grep -q -s "^[[]" gitweb.log >/dev/null; then false; else true; fi
+
+       # gitweb.log is left for debugging
+}
+
+. ./test-lib.sh
+
+gitweb_init
+
+# ----------------------------------------------------------------------
+# no commits (empty, just initialized repository)
+
+test_expect_success \
+       'no commits: projects_list (implicit)' \
+       'gitweb_run'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'no commits: projects_index' \
+       'gitweb_run "a=project_index"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'no commits: .git summary (implicit)' \
+       'gitweb_run "p=.git"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'no commits: .git commit (implicit HEAD)' \
+       'gitweb_run "p=.git;a=commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'no commits: .git commitdiff (implicit HEAD)' \
+       'gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'no commits: .git tree (implicit HEAD)' \
+       'gitweb_run "p=.git;a=tree"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'no commits: .git heads' \
+       'gitweb_run "p=.git;a=heads"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'no commits: .git tags' \
+       'gitweb_run "p=.git;a=tags"'
+test_debug 'cat gitweb.log'
+
+
+# ----------------------------------------------------------------------
+# initial commit
+
+test_expect_success \
+       'Make initial commit' \
+       'echo "Not an empty file." > file &&
+        git add file &&
+        git commit -a -m "Initial commit." &&
+        git branch b'
+
+test_expect_success \
+       'projects_list (implicit)' \
+       'gitweb_run'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'projects_index' \
+       'gitweb_run "a=project_index"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git summary (implicit)' \
+       'gitweb_run "p=.git"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git commit (implicit HEAD)' \
+       'gitweb_run "p=.git;a=commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git commitdiff (implicit HEAD, root commit)' \
+       'gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git commitdiff_plain (implicit HEAD, root commit)' \
+       'gitweb_run "p=.git;a=commitdiff_plain"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git commit (HEAD)' \
+       'gitweb_run "p=.git;a=commit;h=HEAD"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git tree (implicit HEAD)' \
+       'gitweb_run "p=.git;a=tree"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git blob (file)' \
+       'gitweb_run "p=.git;a=blob;f=file"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git blob_plain (file)' \
+       'gitweb_run "p=.git;a=blob_plain;f=file"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# nonexistent objects
+
+test_expect_success \
+       '.git commit (non-existent)' \
+       'gitweb_run "p=.git;a=commit;h=non-existent"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git commitdiff (non-existent)' \
+       'gitweb_run "p=.git;a=commitdiff;h=non-existent"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git commitdiff (non-existent vs HEAD)' \
+       'gitweb_run "p=.git;a=commitdiff;hp=non-existent;h=HEAD"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git tree (0000000000000000000000000000000000000000)' \
+       'gitweb_run "p=.git;a=tree;h=0000000000000000000000000000000000000000"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git tag (0000000000000000000000000000000000000000)' \
+       'gitweb_run "p=.git;a=tag;h=0000000000000000000000000000000000000000"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git blob (non-existent)' \
+       'gitweb_run "p=.git;a=blob;f=non-existent"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       '.git blob_plain (non-existent)' \
+       'gitweb_run "p=.git;a=blob_plain;f=non-existent"'
+test_debug 'cat gitweb.log'
+
+
+# ----------------------------------------------------------------------
+# commitdiff testing (implicit, one implicit tree-ish)
+
+test_expect_success \
+       'commitdiff(0): root' \
+       'gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): file added' \
+       'echo "New file" > new_file &&
+        git add new_file &&
+        git commit -a -m "File added." &&
+        gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): mode change' \
+       'chmod a+x new_file &&
+        git commit -a -m "Mode changed." &&
+        gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): file renamed' \
+       'git mv new_file renamed_file &&
+        git commit -a -m "File renamed." &&
+        gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): file to symlink' \
+       'rm renamed_file &&
+        ln -s file renamed_file &&
+        git commit -a -m "File to symlink." &&
+        gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): file deleted' \
+       'git rm renamed_file &&
+        rm -f renamed_file &&
+        git commit -a -m "File removed." &&
+        gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): file copied / new file' \
+       'cp file file2 &&
+        git add file2 &&
+        git commit -a -m "File copied." &&
+        gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): mode change and modified' \
+       'echo "New line" >> file2 &&
+        chmod a+x file2 &&
+        git commit -a -m "Mode change and modification." &&
+        gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): renamed and modified' \
+       'cat >file2<<EOF &&
+Dominus regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+EOF
+        git commit -a -m "File added." &&
+        git mv file2 file3 &&
+        echo "Propter nomen suum." >> file3 &&
+        git commit -a -m "File rename and modification." &&
+        gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): renamed, mode change and modified' \
+       'git mv file3 file2 &&
+        echo "Propter nomen suum." >> file2 &&
+        chmod a+x file2 &&
+        git commit -a -m "File rename, mode change and modification." &&
+        gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# commitdiff testing (taken from t4114-apply-typechange.sh)
+
+test_expect_success 'setup typechange commits' '
+       echo "hello world" > foo &&
+       echo "hi planet" > bar &&
+       git update-index --add foo bar &&
+       git commit -m initial &&
+       git branch initial &&
+       rm -f foo &&
+       ln -s bar foo &&
+       git update-index foo &&
+       git commit -m "foo symlinked to bar" &&
+       git branch foo-symlinked-to-bar &&
+       rm -f foo &&
+       echo "how far is the sun?" > foo &&
+       git update-index foo &&
+       git commit -m "foo back to file" &&
+       git branch foo-back-to-file &&
+       rm -f foo &&
+       git update-index --remove foo &&
+       mkdir foo &&
+       echo "if only I knew" > foo/baz &&
+       git update-index --add foo/baz &&
+       git commit -m "foo becomes a directory" &&
+       git branch "foo-becomes-a-directory" &&
+       echo "hello world" > foo/baz &&
+       git update-index foo/baz &&
+       git commit -m "foo/baz is the original foo" &&
+       git branch foo-baz-renamed-from-foo
+       '
+
+test_expect_success \
+       'commitdiff(2): file renamed from foo to foo/baz' \
+       'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-baz-renamed-from-foo"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(2): file renamed from foo/baz to foo' \
+       'gitweb_run "p=.git;a=commitdiff;hp=foo-baz-renamed-from-foo;h=initial"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(2): directory becomes file' \
+       'gitweb_run "p=.git;a=commitdiff;hp=foo-becomes-a-directory;h=initial"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(2): file becomes directory' \
+       'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-becomes-a-directory"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(2): file becomes symlink' \
+       'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-symlinked-to-bar"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(2): symlink becomes file' \
+       'gitweb_run "p=.git;a=commitdiff;hp=foo-symlinked-to-bar;h=foo-back-to-file"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(2): symlink becomes directory' \
+       'gitweb_run "p=.git;a=commitdiff;hp=foo-symlinked-to-bar;h=foo-becomes-a-directory"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(2): directory becomes symlink' \
+       'gitweb_run "p=.git;a=commitdiff;hp=foo-becomes-a-directory;h=foo-symlinked-to-bar"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# commit, commitdiff: merge, large
+test_expect_success \
+       'Create a merge' \
+       'git checkout b &&
+        echo "Branch" >> b &&
+        git add b &&
+        git commit -a -m "On branch" &&
+        git checkout master &&
+        git pull . b'
+
+test_expect_success \
+       'commit(0): merge commit' \
+       'gitweb_run "p=.git;a=commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(0): merge commit' \
+       'gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'Prepare large commit' \
+       'git checkout b &&
+        echo "To be changed" > 01-change &&
+        echo "To be renamed" > 02-pure-rename-from &&
+        echo "To be deleted" > 03-delete &&
+        echo "To be renamed and changed" > 04-rename-from &&
+        echo "To have mode changed" > 05-mode-change &&
+        echo "File to symlink" > 06-file-or-symlink &&
+        echo "To be changed and have mode changed" > 07-change-mode-change     &&
+        git add 0* &&
+        git commit -a -m "Prepare large commit" &&
+        echo "Changed" > 01-change &&
+        git mv 02-pure-rename-from 02-pure-rename-to &&
+        git rm 03-delete && rm -f 03-delete &&
+        echo "A new file" > 03-new &&
+        git add 03-new &&
+        git mv 04-rename-from 04-rename-to &&
+        echo "Changed" >> 04-rename-to &&
+        chmod a+x 05-mode-change &&
+        rm -f 06-file-or-symlink && ln -s 01-change 06-file-or-symlink &&
+        echo "Changed and have mode changed" > 07-change-mode-change   &&
+        chmod a+x 07-change-mode-change &&
+        git commit -a -m "Large commit" &&
+        git checkout master'
+
+test_expect_success \
+       'commit(1): large commit' \
+       'gitweb_run "p=.git;a=commit;h=b"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'commitdiff(1): large commit' \
+       'gitweb_run "p=.git;a=commitdiff;h=b"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# tags testing
+
+test_expect_success \
+       'tags: list of different types of tags' \
+       'git checkout master &&
+        git tag -a -m "Tag commit object" tag-commit HEAD &&
+        git tag -a -m "" tag-commit-nomessage HEAD &&
+        git tag -a -m "Tag tag object" tag-tag tag-commit &&
+        git tag -a -m "Tag tree object" tag-tree HEAD^{tree} &&
+        git tag -a -m "Tag blob object" tag-blob HEAD:file &&
+        git tag lightweight/tag-commit HEAD &&
+        git tag lightweight/tag-tag tag-commit &&
+        git tag lightweight/tag-tree HEAD^{tree} &&
+        git tag lightweight/tag-blob HEAD:file &&
+        gitweb_run "p=.git;a=tags"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'tag: Tag to commit object' \
+       'gitweb_run "p=.git;a=tag;h=tag-commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'tag: on lightweight tag (invalid)' \
+       'gitweb_run "p=.git;a=tag;h=lightweight/tag-commit"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# logs
+
+test_expect_success \
+       'logs: log (implicit HEAD)' \
+       'gitweb_run "p=.git;a=log"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'logs: shortlog (implicit HEAD)' \
+       'gitweb_run "p=.git;a=shortlog"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'logs: history (implicit HEAD, file)' \
+       'gitweb_run "p=.git;a=history;f=file"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# feed generation
+
+test_expect_success \
+       'feeds: OPML' \
+       'gitweb_run "a=opml"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'feed: RSS' \
+       'gitweb_run "p=.git;a=rss"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'feed: Atom' \
+       'gitweb_run "p=.git;a=atom"'
+test_debug 'cat gitweb.log'
+
+test_done
diff --git a/tree.c b/tree.c
index e4a39aa3c36964c9034d2393a8b9cb483d1543da..a3728270b4027cd56e09e1a56ec17e177d0a6998 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -157,7 +157,7 @@ static void track_tree_refs(struct tree *item)
        /* Count how many entries there are.. */
        init_tree_desc(&desc, item->buffer, item->size);
        while (tree_entry(&desc, &entry)) {
-               if (S_ISDIRLNK(entry.mode))
+               if (S_ISGITLINK(entry.mode))
                        continue;
                n_refs++;
        }
@@ -169,7 +169,7 @@ static void track_tree_refs(struct tree *item)
        while (tree_entry(&desc, &entry)) {
                struct object *obj;
 
-               if (S_ISDIRLNK(entry.mode))
+               if (S_ISGITLINK(entry.mode))
                        continue;
                if (S_ISDIR(entry.mode))
                        obj = &lookup_tree(entry.sha1)->object;