Merge branch 'master' of git://git.kernel.org/pub/scm/git/git
authorJ. Bruce Fields <bfields@citi.umich.edu>
Mon, 15 Jan 2007 00:27:28 +0000 (19:27 -0500)
committerJ. Bruce Fields <bfields@citi.umich.edu>
Mon, 15 Jan 2007 00:27:28 +0000 (19:27 -0500)
134 files changed:
.gitignore
Documentation/config.txt
Documentation/core-tutorial.txt
Documentation/cvs-migration.txt
Documentation/everyday.txt
Documentation/git-am.txt
Documentation/git-applymbox.txt
Documentation/git-commit.txt
Documentation/git-cvsimport.txt
Documentation/git-init-db.txt
Documentation/git-init.txt [new file with mode: 0644]
Documentation/git-mailinfo.txt
Documentation/git-p4import.txt
Documentation/git-pack-refs.txt
Documentation/git-prune-packed.txt
Documentation/git-remote.txt [new file with mode: 0644]
Documentation/git-svn.txt
Documentation/git.txt
Documentation/glossary.txt
Documentation/hooks.txt
Documentation/howto/setup-git-server-over-http.txt
Documentation/repository-layout.txt
Documentation/tutorial-2.txt
Documentation/tutorial.txt
GIT-VERSION-GEN
INSTALL
Makefile
builtin-apply.c
builtin-archive.c
builtin-branch.c
builtin-describe.c [new file with mode: 0644]
builtin-grep.c
builtin-init-db.c
builtin-mailinfo.c
builtin-pack-objects.c
builtin-pack-refs.c
builtin-prune-packed.c
builtin-prune.c
builtin-reflog.c
builtin-rerere.c
builtin-rm.c
builtin-tar-tree.c
builtin-upload-archive.c
builtin-verify-pack.c
builtin.h
cache.h
commit.c
compat/pread.c [new file with mode: 0644]
config.c
contrib/emacs/git.el
daemon.c
describe.c [deleted file]
diff-lib.c
diff.c
diff.h
diffcore.h
dir.c
entry.c
environment.c
fsck-objects.c
generate-cmdlist.sh
git-am.sh
git-applymbox.sh
git-archimport.perl
git-checkout.sh
git-clean.sh
git-clone.sh
git-commit.sh
git-compat-util.h
git-cvsimport.perl
git-cvsserver.perl
git-fetch.sh
git-merge.sh
git-p4import.py
git-pull.sh
git-rebase.sh
git-remote.perl [new file with mode: 0755]
git-repack.sh
git-rerere.perl [deleted file]
git-reset.sh
git-revert.sh
git-send-email.perl
git-sh-setup.sh
git-svn.perl
git-svnimport.perl
git.c
gitweb/gitweb.perl
http-fetch.c
http-push.c
imap-send.c
index-pack.c
local-fetch.c
merge-recursive.c
pack-check.c
path.c
perl/Git.pm
reachable.c [new file with mode: 0644]
reachable.h [new file with mode: 0644]
read-cache.c
refs.c
refs.h
revision.c
send-pack.c
setup.c
sha1_file.c
ssh-fetch.c
ssh-upload.c
t/README
t/lib-git-svn.sh
t/t0000-basic.sh
t/t1300-repo-config.sh
t/t1410-reflog.sh [new file with mode: 0755]
t/t3200-branch.sh
t/t3210-pack-refs.sh
t/t3901-8859-1.txt [new file with mode: 0755]
t/t3901-i18n-patch.sh [new file with mode: 0755]
t/t3901-utf8.txt [new file with mode: 0755]
t/t4116-apply-reverse.sh
t/t5300-pack-object.sh
t/t5301-sliding-window.sh [new file with mode: 0755]
t/t5500-fetch-pack.sh
t/t5510-fetch.sh
t/t5520-pull.sh
t/t7001-mv.sh
t/t9104-git-svn-follow-parent.sh
t/test-lib.sh
test-delta.c
trace.c
tree-walk.c
unpack-file.c
upload-pack.c
write_or_die.c
wt-status.c
wt-status.h
index 2904f123496ddf8f69473675c183999d07838170..6da1cdbd0de72dc1bff64414e901484474d22a36 100644 (file)
@@ -50,6 +50,7 @@ git-http-fetch
 git-http-push
 git-imap-send
 git-index-pack
+git-init
 git-init-db
 git-instaweb
 git-local-fetch
@@ -92,6 +93,7 @@ git-rebase
 git-receive-pack
 git-reflog
 git-relink
+git-remote
 git-repack
 git-repo-config
 git-request-pull
index 4318bf9334d22541b741b4be3fe0315f0e1480e9..f7dba8977f4eb9a59171247250e136a33b1f7533 100644 (file)
@@ -100,7 +100,7 @@ core.sharedRepository::
        group-writable). When 'all' (or 'world' or 'everybody'), the
        repository will be readable by all users, additionally to being
        group-shareable. When 'umask' (or 'false'), git will use permissions
-       reported by umask(2). See gitlink:git-init-db[1]. False by default.
+       reported by umask(2). See gitlink:git-init[1]. False by default.
 
 core.warnAmbiguousRefs::
        If true, git will warn you if the ref name you passed it is ambiguous
@@ -118,6 +118,34 @@ core.legacyheaders::
        database directly (where the "http://" and "rsync://" protocols
        count as direct access).
 
+core.packedGitWindowSize::
+       Number of bytes of a pack file to map into memory in a
+       single mapping operation.  Larger window sizes may allow
+       your system to process a smaller number of large pack files
+       more quickly.  Smaller window sizes will negatively affect
+       performance due to increased calls to the operating system's
+       memory manager, but may improve performance when accessing
+       a large number of large pack files.
++
+Default is 1 MiB if NO_MMAP was set at compile time, otherwise 32
+MiB on 32 bit platforms and 1 GiB on 64 bit platforms.  This should
+be reasonable for all users/operating systems.  You probably do
+not need to adjust this value.
++
+Common unit suffixes of 'k', 'm', or 'g' are supported.
+
+core.packedGitLimit::
+       Maximum number of bytes to map simultaneously into memory
+       from pack files.  If Git needs to access more than this many
+       bytes at once to complete an operation it will unmap existing
+       regions to reclaim virtual address space within the process.
++
+Default is 256 MiB on 32 bit platforms and 8 GiB on 64 bit platforms.
+This should be reasonable for all users/operating systems, except on
+the largest projects.  You probably do not need to adjust this value.
++
+Common unit suffixes of 'k', 'm', or 'g' are supported.
+
 alias.*::
        Command aliases for the gitlink:git[1] command wrapper - e.g.
        after defining "alias.last = cat-file commit HEAD", the invocation
index 5ea611748c6da80ee7df0387c5c5021a7095f45e..0cd33fb5b7485f54861d30c896c183535413ad0d 100644 (file)
@@ -46,12 +46,12 @@ to import into git.
 For our first example, we're going to start a totally new repository from
 scratch, with no pre-existing files, and we'll call it `git-tutorial`.
 To start up, create a subdirectory for it, change into that
-subdirectory, and initialize the git infrastructure with `git-init-db`:
+subdirectory, and initialize the git infrastructure with `git-init`:
 
 ------------------------------------------------
 $ mkdir git-tutorial
 $ cd git-tutorial
-$ git-init-db
+$ git-init
 ------------------------------------------------
 
 to which git will reply
@@ -1371,11 +1371,11 @@ $ mkdir my-git.git
 ------------
 
 Then, make that directory into a git repository by running
-`git init-db`, but this time, since its name is not the usual
+`git init`, but this time, since its name is not the usual
 `.git`, we do things slightly differently:
 
 ------------
-$ GIT_DIR=my-git.git git-init-db
+$ GIT_DIR=my-git.git git-init
 ------------
 
 Make sure this directory is available for others you want your
@@ -1511,7 +1511,7 @@ A recommended workflow for a "project lead" goes like this:
 +
 If other people are pulling from your repository over dumb
 transport protocols (HTTP), you need to keep this repository
-'dumb transport friendly'.  After `git init-db`,
+'dumb transport friendly'.  After `git init`,
 `$GIT_DIR/hooks/post-update` copied from the standard templates
 would contain a call to `git-update-server-info` but the
 `post-update` hook itself is disabled by default -- enable it
index 8e09beaa799dbff462b258e5593fd5c79c91312a..775bf4266a769e3ad2ca6eb14333b2c569b98698 100644 (file)
@@ -80,7 +80,7 @@ it:
 ------------------------------------------------
 $ mkdir /pub/my-repo.git
 $ cd /pub/my-repo.git
-$ git --bare init-db --shared
+$ git --bare init --shared
 $ git --bare fetch /home/alice/myproject master:master
 ------------------------------------------------
 
index 2105a3d2e7337105263c8a9fef466609c63fc163..4e83994c588fc5f4853abc3839378281c58c9253 100644 (file)
@@ -25,7 +25,7 @@ Basic Repository[[Basic Repository]]
 
 Everybody uses these commands to maintain git repositories.
 
-  * gitlink:git-init-db[1] or gitlink:git-clone[1] to create a
+  * gitlink:git-init[1] or gitlink:git-clone[1] to create a
     new repository.
 
   * gitlink:git-fsck-objects[1] to check the repository for errors.
@@ -107,7 +107,7 @@ Use a tarball as a starting point for a new repository.::
 ------------
 $ tar zxf frotz.tar.gz
 $ cd frotz
-$ git-init-db
+$ git-init
 $ git add . <1>
 $ git commit -m 'import of frotz source tree.'
 $ git tag v2.43 <2>
index 910457d3b388f2c3b1dbe25f5609e3584fb4f113..53e81cb103c4fbd65c1c728fccd710a301eed5e4 100644 (file)
@@ -9,7 +9,7 @@ git-am - Apply a series of patches in a mailbox
 SYNOPSIS
 --------
 [verse]
-'git-am' [--signoff] [--dotest=<dir>] [--utf8] [--binary] [--3way]
+'git-am' [--signoff] [--dotest=<dir>] [--utf8 | --no-utf8] [--binary] [--3way]
          [--interactive] [--whitespace=<option>] <mbox>...
 'git-am' [--skip | --resolved]
 
@@ -29,8 +29,21 @@ OPTIONS
        Instead of `.dotest` directory, use <dir> as a working
        area to store extracted patches.
 
---utf8, --keep::
-       Pass `-u` and `-k` flags to `git-mailinfo` (see
+--keep::
+       Pass `-k` flag to `git-mailinfo` (see gitlink:git-mailinfo[1]).
+
+--utf8::
+       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 was optional in prior versions of git, but now it is the
+default.   You could use `--no-utf8` to override this.
+
+--no-utf8::
+       Do not pass `-u` flag to `git-mailinfo` (see
        gitlink:git-mailinfo[1]).
 
 --binary::
index f74c6a49b3c5d52ed662a83832c7d06915fac65b..95dc65a583552d4f7e2d1fa34c448832c66a3353 100644 (file)
@@ -42,13 +42,13 @@ OPTIONS
        and the current tree.
 
 -u::
-       By default, the commit log message, author name and
-       author email are taken from the e-mail without any
-       charset conversion, after minimally decoding MIME
-       transfer encoding.  This flag causes the resulting
-       commit to be encoded in utf-8 by transliterating them.
-       Note that the patch is always used as is without charset
-       conversion, even with this flag.
+       The commit log message, author name and author email are
+       taken from the e-mail, and after minimally decoding MIME
+       transfer encoding, re-coded in UTF-8 by transliterating
+       them.  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.
 
 -c .dotest/<num>::
        When the patch contained in an e-mail does not cleanly
index a7adf24fa5a601f372099988c1fedfb929ceaab1..b4528d72ba376bb0444002698196afa89f781fc1 100644 (file)
@@ -32,7 +32,8 @@ methods:
 
 4. by using the -a switch with the 'commit' command to automatically "add"
    changes from all known files i.e. files that have already been committed
-   before, and perform the actual commit.
+   before, and to automatically "rm" files that have been
+   removed from the working tree, and perform the actual commit.
 
 The gitlink:git-status[1] command can be used to obtain a
 summary of what is included by any of the above for the next
@@ -72,12 +73,8 @@ OPTIONS
        Add Signed-off-by line at the end of the commit message.
 
 --no-verify::
-       By default, the command looks for suspicious lines the
-       commit introduces, and aborts committing if there is one.
-       The definition of 'suspicious lines' is currently the
-       lines that has trailing whitespaces, and the lines whose
-       indentation has a SP character immediately followed by a
-       TAB character.  This option turns off the check.
+       This option bypasses the pre-commit hook.
+       See also link:hooks.html[hooks].
 
 -e|--edit::
        The message taken from file with `-F`, command line with
index d21d66bfebe368c034afdabf79e8f143813e619d..5c402de2675024c9286e2ee72506cb1d0a56fc31 100644 (file)
@@ -90,7 +90,8 @@ If you need to pass multiple options, separate them with a comma.
        Print a short usage message and exit.
 
 -z <fuzz>::
-        Pass the timestamp fuzz factor to cvsps.
+       Pass the timestamp fuzz factor to cvsps, in seconds. If unset,
+       cvsps defaults to 300s.
 
 -s <subst>::
        Substitute the character "/" in branch names with <subst>
@@ -99,6 +100,18 @@ If you need to pass multiple options, separate them with a comma.
        CVS by default uses the unix username when writing its
        commit logs. Using this option and an author-conv-file
        in this format
+
+-a::
+       Import all commits, including recent ones. cvsimport by default
+       skips commits that have a timestamp less than 10 minutes ago.
+
+-S <regex>::
+       Skip paths matching the regex.
+
+-L <limit>::
+       Limit the number of commits imported. Workaround for cases where
+       cvsimport leaks memory.
+
 +
 ---------
        exon=Andreas Ericsson <ae@op5.se>
index ca7d09dc0a8563a1d8005bc83a89d19c785c1a3d..5412135d763af3a8c68e18441b774654f07eadfd 100644 (file)
@@ -11,95 +11,9 @@ SYNOPSIS
 'git-init-db' [--template=<template_directory>] [--shared[=<permissions>]]
 
 
-OPTIONS
--------
-
---
-
---template=<template_directory>::
-
-Provide the directory from which templates will be used.  The default template
-directory is `/usr/share/git-core/templates`.
-
-When specified, `<template_directory>` is used as the source of the template
-files rather than the default.  The template files include some directory
-structure, some suggested "exclude patterns", and copies of non-executing
-"hook" files.  The suggested patterns and hook files are all modifiable and
-extensible.
-
---shared[={false|true|umask|group|all|world|everybody}]::
-
-Specify that the git repository is to be shared amongst several users.  This
-allows users belonging to the same group to push into that
-repository.  When specified, the config variable "core.sharedRepository" is
-set so that files and directories under `$GIT_DIR` are created with the
-requested permissions.  When not specified, git will use permissions reported
-by umask(2).
-
-The option can have the following values, defaulting to 'group' if no value
-is given:
-
- - 'umask' (or 'false'): Use permissions reported by umask(2). The default,
-   when `--shared` is not specified.
-
- - 'group' (or 'true'): Make the repository group-writable, (and g+sx, since
-   the git group may be not the primary group of all users).
-
- - 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository
-   readable by all users.
-
-By default, the configuration flag receive.denyNonFastforward is enabled
-in shared repositories, so that you cannot force a non fast-forwarding push
-into it.
-
---
-
-
 DESCRIPTION
 -----------
-This command creates an empty git repository - basically a `.git` directory
-with subdirectories for `objects`, `refs/heads`, `refs/tags`, and
-template files.
-An initial `HEAD` file that references the HEAD of the master branch
-is also created.
-
-If the `$GIT_DIR` environment variable is set then it specifies a path
-to use instead of `./.git` for the base of the repository.
-
-If the object storage directory is specified via the `$GIT_OBJECT_DIRECTORY`
-environment variable then the sha1 directories are created underneath -
-otherwise the default `$GIT_DIR/objects` directory is used.
-
-Running `git-init-db` in an existing repository is safe. It will not overwrite
-things that are already there. The primary reason for rerunning `git-init-db`
-is to pick up newly added templates.
-
-
-
-EXAMPLES
---------
-
-Start a new git repository for an existing code base::
-+
-----------------
-$ cd /path/to/my/codebase
-$ git-init-db   <1>
-$ git-add .     <2>
-----------------
-+
-<1> prepare /path/to/my/codebase/.git directory
-<2> add all existing file to the index
-
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
 
-GIT
----
-Part of the gitlink:git[7] suite
+This is a synonym for gitlink:git-init[1].  Please refer to the
+documentation of that command.
 
diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt
new file mode 100644 (file)
index 0000000..596b567
--- /dev/null
@@ -0,0 +1,111 @@
+git-init(1)
+===========
+
+NAME
+----
+git-init - Creates an empty git repository
+
+
+SYNOPSIS
+--------
+'git-init' [--template=<template_directory>] [--shared[=<permissions>]]
+
+
+OPTIONS
+-------
+
+--
+
+--template=<template_directory>::
+
+Provide the directory from which templates will be used.  The default template
+directory is `/usr/share/git-core/templates`.
+
+When specified, `<template_directory>` is used as the source of the template
+files rather than the default.  The template files include some directory
+structure, some suggested "exclude patterns", and copies of non-executing
+"hook" files.  The suggested patterns and hook files are all modifiable and
+extensible.
+
+--shared[={false|true|umask|group|all|world|everybody}]::
+
+Specify that the git repository is to be shared amongst several users.  This
+allows users belonging to the same group to push into that
+repository.  When specified, the config variable "core.sharedRepository" is
+set so that files and directories under `$GIT_DIR` are created with the
+requested permissions.  When not specified, git will use permissions reported
+by umask(2).
+
+The option can have the following values, defaulting to 'group' if no value
+is given:
+
+ - 'umask' (or 'false'): Use permissions reported by umask(2). The default,
+   when `--shared` is not specified.
+
+ - 'group' (or 'true'): Make the repository group-writable, (and g+sx, since
+   the git group may be not the primary group of all users).
+
+ - 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository
+   readable by all users.
+
+By default, the configuration flag receive.denyNonFastforward is enabled
+in shared repositories, so that you cannot force a non fast-forwarding push
+into it.
+
+--
+
+
+DESCRIPTION
+-----------
+This command creates an empty git repository - basically a `.git` directory
+with subdirectories for `objects`, `refs/heads`, `refs/tags`, and
+template files.
+An initial `HEAD` file that references the HEAD of the master branch
+is also created.
+
+If the `$GIT_DIR` environment variable is set then it specifies a path
+to use instead of `./.git` for the base of the repository.
+
+If the object storage directory is specified via the `$GIT_OBJECT_DIRECTORY`
+environment variable then the sha1 directories are created underneath -
+otherwise the default `$GIT_DIR/objects` directory is used.
+
+Running `git-init` in an existing repository is safe. It will not overwrite
+things that are already there. The primary reason for rerunning `git-init`
+is to pick up newly added templates.
+
+Note that `git-init` is the same as `git-init-db`.  The command
+was primarily meant to initialize the object database, but over
+time it has become responsible for setting up the other aspects
+of the repository, such as installing the default hooks and
+setting the configuration variables.  The old name is retained
+for backward compatibility reasons.
+
+
+EXAMPLES
+--------
+
+Start a new git repository for an existing code base::
++
+----------------
+$ cd /path/to/my/codebase
+$ git-init      <1>
+$ git-add .     <2>
+----------------
++
+<1> prepare /path/to/my/codebase/.git directory
+<2> add all existing file to the index
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
+
index ea0a06557f933ce48374ddbb751a64945ff65955..5088bbea84c0e6b681641b78b816755fa12f6b6d 100644 (file)
@@ -33,15 +33,13 @@ OPTIONS
        format-patch --mbox' output.
 
 -u::
-       By default, the commit log message, author name and
-       author email are taken from the e-mail without any
-       charset conversion, after minimally decoding MIME
-       transfer encoding.  This flag causes the resulting
-       commit to be encoded in the encoding specified by
-       i18n.commitencoding configuration (defaults to utf-8) by
-       transliterating them. 
-       Note that the patch is always used as is without charset
-       conversion, even with this flag.
+       The commit log message, author name and author email are
+       taken from the e-mail, and after minimally decoding MIME
+       transfer encoding, re-coded in UTF-8 by transliterating
+       them.  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.
 
 --encoding=<encoding>::
        Similar to -u but if the local convention is different
index ee9e8fa9090f88d1eb45ddbdb73866e0dc1fd53d..6edb9f12b87e2bc24fb69c8615e1798bb813e14e 100644 (file)
@@ -93,7 +93,7 @@ perforce branch into a branch named "jammy", like so:
 ------------
 $ mkdir -p /home/sean/import/jam
 $ cd /home/sean/import/jam
-$ git init-db
+$ git init
 $ git p4import //public/jam jammy
 ------------
 
index 5da51057713a5553db26d871cc561b0df7ca94ff..464269fbb994dcee96bb08197c4f3d22fc0d4ad8 100644 (file)
@@ -7,7 +7,7 @@ git-pack-refs - Pack heads and tags for efficient repository access
 
 SYNOPSIS
 --------
-'git-pack-refs' [--all] [--prune]
+'git-pack-refs' [--all] [--no-prune]
 
 DESCRIPTION
 -----------
@@ -40,10 +40,11 @@ developed and packing their tips does not help performance.
 This option causes branch tips to be packed as well.  Useful for
 a repository with many branches of historical interests.
 
-\--prune::
+\--no-prune::
+
+The command usually removes loose refs under `$GIT_DIR/refs`
+hierarchy after packing them.  This option tells it not to.
 
-After packing the refs, remove loose refs under `$GIT_DIR/refs`
-hierarchy.  This should probably become default.
 
 Author
 ------
index 234882685d923d632c4e09a38cb9ba18439dda4a..a79193fb007bcaa8dc47b2763e4167a6f31a3dff 100644 (file)
@@ -9,7 +9,7 @@ residing in a pack file.
 
 SYNOPSIS
 --------
-'git-prune-packed' [-n]
+'git-prune-packed' [-n] [-q]
 
 
 DESCRIPTION
@@ -32,6 +32,9 @@ OPTIONS
         Don't actually remove any objects, only show those that would have been
         removed.
 
+-q::
+       Squelch the progress indicator.
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
new file mode 100644 (file)
index 0000000..5b93a8c
--- /dev/null
@@ -0,0 +1,76 @@
+git-remote(1)
+============
+
+NAME
+----
+git-remote - manage set of tracked repositories
+
+
+SYNOPSIS
+--------
+[verse]
+'git-remote'
+'git-remote' add <name> <url>
+'git-remote' show <name>
+
+DESCRIPTION
+-----------
+
+Manage the set of repositories ("remotes") whose branches you track.
+
+With no arguments, shows a list of existing remotes.
+
+In the second form, adds a remote named <name> for the repository at
+<url>.  The command `git fetch <name>` can then be used to create and
+update remote-tracking branches <name>/<branch>.
+
+In the third form, gives some information about the remote <name>.
+
+The remote configuration is achieved using the `remote.origin.url` and
+`remote.origin.fetch` configuration variables.  (See
+gitlink:git-repo-config[1]).
+
+Examples
+--------
+
+Add a new remote, fetch, and check out a branch from it:
+
+------------
+$ git remote
+origin
+$ git branch -r
+origin/master
+$ git remote add linux-nfs git://linux-nfs.org/pub/nfs-2.6.git
+$ git remote
+linux-nfs
+origin
+$ git fetch
+* refs/remotes/linux-nfs/master: storing branch 'master' ...
+  commit: bf81b46
+$ git branch -r
+origin/master
+linux-nfs/master
+$ git checkout -b nfs linux-nfs/master
+...
+------------
+
+See Also
+--------
+gitlink:git-fetch[1]
+gitlink:git-branch[1]
+gitlink:git-repo-config[1]
+
+Author
+------
+Written by Junio Hamano
+
+
+Documentation
+--------------
+Documentation by J. Bruce Fields and the git-list <git@vger.kernel.org>.
+
+
+GIT
+---
+Part of the gitlink:git[7] suite
+
index ce63defffde85f4eb97d256417f78b5bc51365c6..9ed721118b7dd446657fe28968e1294ab9fbf421 100644 (file)
@@ -139,6 +139,24 @@ manually joining branches on commit.
        where the repository URL ends and where the repository path
        begins.
 
+-T<trunk_subdir>::
+--trunk=<trunk_subdir>::
+-t<tags_subdir>::
+--tags=<tags_subdir>::
+-b<branches_subdir>::
+--branches=<branches_subdir>::
+       These are the command-line options for multi-init.  Each of
+       these flags can point to a relative repository path
+       (--tags=project/tags') or a full url
+       (--tags=https://foo.org/project/tags)
+
+--prefix=<prefix>
+       This allows one to specify a prefix which is prepended to the
+       names of remotes.  The prefix does not automatically include a
+       trailing slash, so be sure you include one in the argument if
+       that is what you want.  This is useful if you wish to track
+       multiple projects that share a common repository.
+
 'multi-fetch'::
        This runs fetch on all known SVN branches we're tracking.  This
        will NOT discover new branches (unlike git-svnimport), so
@@ -153,7 +171,7 @@ OPTIONS
 --shared::
 --template=<template_directory>::
        Only used with the 'init' command.
-       These are passed directly to gitlink:git-init-db[1].
+       These are passed directly to gitlink:git-init[1].
 
 -r <ARG>::
 --revision <ARG>::
@@ -231,8 +249,7 @@ repo-config key: svn.authorsfile
 
 -q::
 --quiet::
-       Make git-svn less verbose.  This only affects git-svn if you
-       have the SVN::* libraries installed and are using them.
+       Make git-svn less verbose.
 
 --repack[=<n>]::
 --repack-flags=<flags>
@@ -303,8 +320,6 @@ for more information on using GIT_SVN_ID.
        started tracking a branch and never tracked the trunk it was
        descended from.
 
-       This relies on the SVN::* libraries to work.
-
 repo-config key: svn.followparent
 
 --no-metadata::
@@ -332,25 +347,6 @@ Run this if you used an old version of git-svn that used
 "git-svn-HEAD" instead of "remotes/git-svn" as the branch
 for tracking the remote.
 
---no-ignore-externals::
-Only used with the 'fetch' and 'rebuild' command.
-
-This command has no effect when you are using the SVN::*
-libraries with git, svn:externals are always avoided.
-
-By default, git-svn passes --ignore-externals to svn to avoid
-fetching svn:external trees into git.  Pass this flag to enable
-externals tracking directly via git.
-
-Versions of svn that do not support --ignore-externals are
-automatically detected and this flag will be automatically
-enabled for them.
-
-Otherwise, do not enable this flag unless you know what you're
-doing.
-
-repo-config key: svn.noignoreexternals
-
 --ignore-nodate::
 Only used with the 'fetch' command.
 
@@ -371,7 +367,7 @@ Basic Examples
 Tracking and contributing to a the trunk of a Subversion-managed project:
 
 ------------------------------------------------------------------------
-# Initialize a repo (like git init-db):
+# Initialize a repo (like git init):
        git-svn init http://svn.foo.org/project/trunk
 # Fetch remote revisions:
        git-svn fetch
@@ -392,7 +388,7 @@ See also:
 '<<tracking-multiple-repos,Tracking Multiple Repositories or Branches>>'
 
 ------------------------------------------------------------------------
-# Initialize a repo (like git init-db):
+# Initialize a repo (like git init):
        git-svn multi-init http://svn.foo.org/project \
                -T trunk -b branches -t tags
 # Fetch remote revisions:
@@ -468,49 +464,18 @@ This allows you to tie unfetched SVN revision 375 to your current HEAD:
        git-svn fetch 375=$(git-rev-parse HEAD)
 ------------------------------------------------
 
-Advanced Example: Tracking a Reorganized Repository
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Note: this example is now obsolete if you have SVN::* libraries
-installed.  Simply use --follow-parent when fetching.
-
 If you're tracking a directory that has moved, or otherwise been
 branched or tagged off of another directory in the repository and you
-care about the full history of the project, then you can read this
-section.
-
-This is how Yann Dirson tracked the trunk of the ufoai directory when
-the /trunk directory of his repository was moved to /ufoai/trunk and
-he needed to continue tracking /ufoai/trunk where /trunk left off.
+care about the full history of the project, then you can use
+the --follow-parent option.
 
-------------------------------------------------------------------------
-       # This log message shows when the repository was reorganized:
-       r166 | ydirson | 2006-03-02 01:36:55 +0100 (Thu, 02 Mar 2006) | 1 line
-       Changed paths:
-          D /trunk
-          A /ufoai/trunk (from /trunk:165)
-
-       # First we start tracking the old revisions:
-       GIT_SVN_ID=git-oldsvn git-svn init \
-                       https://svn.sourceforge.net/svnroot/ufoai/trunk
-       GIT_SVN_ID=git-oldsvn git-svn fetch -r1:165
-
-       # And now, we continue tracking the new revisions:
-       GIT_SVN_ID=git-newsvn git-svn init \
-             https://svn.sourceforge.net/svnroot/ufoai/ufoai/trunk
-       GIT_SVN_ID=git-newsvn git-svn fetch \
-             166=`git-rev-parse refs/remotes/git-oldsvn`
-------------------------------------------------------------------------
+------------------------------------------------
+       git-svn fetch --follow-parent
+------------------------------------------------
 
 BUGS
 ----
 
-If you are not using the SVN::* Perl libraries and somebody commits a
-conflicting changeset to SVN at a bad moment (right before you commit)
-causing a conflict and your commit to fail, your svn working tree
-($GIT_DIR/git-svn/tree) may be dirtied.  The easiest thing to do is
-probably just to rm -rf $GIT_DIR/git-svn/tree and run 'rebuild'.   You
-can avoid this problem entirely by using 'dcommit'.
-
 We ignore all SVN properties except svn:executable.  Too difficult to
 map them since we rely heavily on git write-tree being _exactly_ the
 same on both the SVN and git working trees and I prefer not to clutter
index 36b2126d847bf86f9efe5bdb1ce88acef9046633..f89d745efa73a9e22537aadb5c36857f04f030ec 100644 (file)
@@ -353,8 +353,8 @@ gitlink:git-hash-object[1]::
 gitlink:git-index-pack[1]::
        Build pack idx file for an existing packed archive.
 
-gitlink:git-init-db[1]::
-       Creates an empty git object database, or reinitialize an
+gitlink:git-init[1]::
+       Creates an empty git repository, or reinitialize an
        existing one.
 
 gitlink:git-merge-file[1]::
index 7c1a6592c112a76c23c2833537a13f40337c261c..bc917bbac3b21eff1baa70b24c689ff11f5a4323 100644 (file)
@@ -235,8 +235,11 @@ push::
        local head, the push fails.
 
 reachable::
-       An object is reachable from a ref/commit/tree/tag, if there is a
-       chain leading from the latter to the former.
+       All of the ancestors of a given commit are said to be reachable from
+       that commit.  More generally, one object is reachable from another if
+       we can reach the one from the other by a chain that follows tags to
+       whatever they tag, commits to their parents or trees, and trees to the
+       trees or blobs that they contain.
 
 rebase::
        To clean a branch by starting from the head of the main line of
@@ -256,7 +259,7 @@ refspec::
        means "grab the master branch head from the $URL and store
        it as my origin branch head".
        And `git push $URL refs/heads/master:refs/heads/to-upstream`
-       means "publish my master branch head as to-upstream master head
+       means "publish my master branch head as to-upstream branch
        at $URL".   See also gitlink:git-push[1]
 
 repository::
index 161123f142ff231bc8153242d7f6507c54b09e2b..e3b76f96eb303dc7eda20472c8c546cf51a54e67 100644 (file)
@@ -3,7 +3,7 @@ Hooks used by git
 
 Hooks are little scripts you can place in `$GIT_DIR/hooks`
 directory to trigger action at certain points.  When
-`git-init-db` is run, a handful example hooks are copied in the
+`git-init` is run, a handful example hooks are copied in the
 `hooks` directory of the new repository, but by default they are
 all disabled.  To enable a hook, make it executable with `chmod +x`.
 
index ba191569af9501117322e1881160226a74867007..a202f3a460b4446f085977f7423d1f43e189e5ef 100644 (file)
@@ -70,7 +70,7 @@ DocumentRoot /where/ever/httpd.conf" to find your root:
 Initialize a bare repository
 
     $ cd my-new-repo.git
-    $ git --bare init-db
+    $ git --bare init
 
 
 Change the ownership to your web-server's credentials. Use "grep ^User
index e20fb7e74c22390e810a392f2625a3ba6b325477..0fdd36614d8db6f52d1bdc6f8bf2e5a667edeff0 100644 (file)
@@ -102,7 +102,7 @@ branches::
 hooks::
        Hooks are customization scripts used by various git
        commands.  A handful of sample hooks are installed when
-       `git init-db` is run, but all of them are disabled by
+       `git init` is run, but all of them are disabled by
        default.  To enable, they need to be made executable.
        Read link:hooks.html[hooks] for more details about
        each hook.
index 60e54777dc6cef259a045512f42a50527f430fb9..f48894c9a25001374210fba3f6782ccec996a664 100644 (file)
@@ -17,7 +17,7 @@ Let's start a new project and create a small amount of history:
 ------------------------------------------------
 $ mkdir test-project
 $ cd test-project
-$ git init-db
+$ git init
 Initialized empty Git repository in .git/
 $ echo 'hello world' > file.txt
 $ git add .
index 01d4a47a97fa864c6948b8ae98ece1f00b1003da..d2bf0b905aa3f2b67f781310b9f60c05fa948c67 100644 (file)
@@ -32,7 +32,7 @@ can place it under git revision control as follows.
 ------------------------------------------------
 $ tar xzf project.tar.gz
 $ cd project
-$ git init-db
+$ git init
 ------------------------------------------------
 
 Git will reply
index 21ff949ea2be33aa3a51becff87f4d97d871459c..8502e4c5b2cd0235583e975d1e3670b97a4341c3 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.4.5-rc0.GIT
+DEF_VER=v1.5.0-rc1.GIT
 
 LF='
 '
diff --git a/INSTALL b/INSTALL
index e7aea60e92eba80d63e9596d8d78b9447c292bcf..361c65bacc50b4038cb9a32bab07c275a1ecdc76 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -95,7 +95,7 @@ Issues of note:
    repository itself.  For example, you could:
 
        $ mkdir manual && cd manual
-       $ git init-db
+       $ git init
        $ git fetch-pack git://git.kernel.org/pub/scm/git/git.git man html |
          while read a b
          do
index 180e1e0b94e121594a5ae2f994d12af6d1a2ea11..8432ab8ba1c69f1269808782b17e62ce92460b4e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 # The default target of this Makefile is...
-all:
+all::
 
 # Define NO_OPENSSL environment variable if you do not have OpenSSL.
 # This also implies MOZILLA_SHA1.
@@ -69,6 +69,9 @@ all:
 #
 # Define NO_MMAP if you want to avoid mmap.
 #
+# Define NO_PREAD if you have a problem with pread() system call (e.g.
+# cygwin.dll before v1.5.22).
+#
 # Define NO_FAST_WORKING_DIRECTORY if accessing objects in pack files is
 # generally faster on your platform than accessing the working directory.
 #
@@ -179,7 +182,7 @@ SCRIPT_SH = \
 SCRIPT_PERL = \
        git-add--interactive.perl \
        git-archimport.perl git-cvsimport.perl git-relink.perl \
-       git-cvsserver.perl \
+       git-cvsserver.perl git-remote.perl \
        git-svnimport.perl git-cvsexportcommit.perl \
        git-send-email.perl git-svn.perl
 
@@ -201,7 +204,7 @@ PROGRAMS = \
        git-update-server-info$X \
        git-upload-pack$X git-verify-pack$X \
        git-pack-redundant$X git-var$X \
-       git-describe$X git-merge-tree$X git-imap-send$X \
+       git-merge-tree$X git-imap-send$X \
        git-merge-recursive$X \
        $(EXTRA_PROGRAMS)
 
@@ -210,7 +213,7 @@ EXTRA_PROGRAMS =
 
 BUILT_INS = \
        git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \
-       git-get-tar-commit-id$X \
+       git-get-tar-commit-id$X git-init$X \
        $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
 
 # what 'all' will build and 'install' will install, in gitexecdir
@@ -251,6 +254,7 @@ LIB_OBJS = \
        interpolate.o \
        lockfile.o \
        object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \
+       reachable.o \
        quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \
        server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
        tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
@@ -271,6 +275,7 @@ BUILTIN_OBJS = \
        builtin-check-ref-format.o \
        builtin-commit-tree.o \
        builtin-count-objects.o \
+       builtin-describe.o \
        builtin-diff.o \
        builtin-diff-files.o \
        builtin-diff-index.o \
@@ -522,6 +527,10 @@ ifdef NO_MMAP
        COMPAT_CFLAGS += -DNO_MMAP
        COMPAT_OBJS += compat/mmap.o
 endif
+ifdef NO_PREAD
+       COMPAT_CFLAGS += -DNO_PREAD
+       COMPAT_OBJS += compat/pread.o
+endif
 ifdef NO_FAST_WORKING_DIRECTORY
        BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
 endif
@@ -596,9 +605,12 @@ export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
 
 ### Build rules
 
-all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi
+all:: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi
+ifneq (,$X)
+       $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), rm -f '$p';)
+endif
 
-all:
+all::
        $(MAKE) -C perl PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
        $(MAKE) -C templates
 
@@ -840,6 +852,9 @@ install: all
                        '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X'; \
        fi
        $(foreach p,$(BUILT_INS), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;)
+ifneq (,$X)
+       $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p';)
+endif
 
 install-doc:
        $(MAKE) -C Documentation install
index 1c3583706835339d2f3a0b0a5d3b989aae2a8142..54fd2cb0c71dc97a47d0286d7b201545a7308b01 100644 (file)
@@ -811,7 +811,8 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
                        struct fragment dummy;
                        if (parse_fragment_header(line, len, &dummy) < 0)
                                continue;
-                       error("patch fragment without header at line %d: %.*s", linenr, (int)len-1, line);
+                       die("patch fragment without header at line %d: %.*s",
+                           linenr, (int)len-1, line);
                }
 
                if (size < len + 6)
@@ -2238,8 +2239,19 @@ static void remove_file(struct patch *patch)
                        die("unable to remove %s from index", patch->old_name);
                cache_tree_invalidate_path(active_cache_tree, patch->old_name);
        }
-       if (!cached)
-               unlink(patch->old_name);
+       if (!cached) {
+               if (!unlink(patch->old_name)) {
+                       char *name = xstrdup(patch->old_name);
+                       char *end = strrchr(name, '/');
+                       while (end) {
+                               *end = 0;
+                               if (rmdir(name))
+                                       break;
+                               end = strrchr(name, '/');
+                       }
+                       free(name);
+               }
+       }
 }
 
 static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size)
index 391cf43911a16ec49862c18b7dd4f1f094d49de8..32737d31621e8d1a900d944f97fcaae43d56a12e 100644 (file)
@@ -137,7 +137,6 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
                if (err || !S_ISDIR(mode))
                        die("current working directory is untracked");
 
-               free(tree);
                tree = parse_tree_indirect(tree_sha1);
        }
        ar_args->tree = tree;
index d3df5a57f127f44d7ff48a419d51ca108c7c9618..c760e188ea4169d878986ebd0d6c22702802aa59 100644 (file)
@@ -275,7 +275,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
        }
 }
 
-static void print_ref_list(int kinds, int verbose, int abbrev)
+static void print_ref_list(int kinds, int detached, int verbose, int abbrev)
 {
        int i;
        struct ref_list ref_list;
@@ -286,8 +286,20 @@ static void print_ref_list(int kinds, int verbose, int abbrev)
 
        qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
 
+       detached = (detached && (kinds & REF_LOCAL_BRANCH));
+       if (detached) {
+               struct ref_item item;
+               item.name = "(no branch)";
+               item.kind = REF_LOCAL_BRANCH;
+               hashcpy(item.sha1, head_sha1);
+               if (strlen(item.name) > ref_list.maxwidth)
+                             ref_list.maxwidth = strlen(item.name);
+               print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1);
+       }
+
        for (i = 0; i < ref_list.index; i++) {
-               int current = (ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
+               int current = !detached &&
+                       (ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
                        !strcmp(ref_list.list[i].name, head);
                print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose,
                               abbrev, current);
@@ -296,7 +308,8 @@ static void print_ref_list(int kinds, int verbose, int abbrev)
        free_ref_list(&ref_list);
 }
 
-static void create_branch(const char *name, const char *start,
+static void create_branch(const char *name, const char *start_name,
+                         unsigned char *start_sha1,
                          int force, int reflog)
 {
        struct ref_lock *lock;
@@ -315,9 +328,14 @@ static void create_branch(const char *name, const char *start,
                        die("Cannot force update the current branch.");
        }
 
-       if (get_sha1(start, sha1) ||
-           (commit = lookup_commit_reference(sha1)) == NULL)
-               die("Not a valid branch point: '%s'.", start);
+       if (start_sha1)
+               /* detached HEAD */
+               hashcpy(sha1, start_sha1);
+       else if (get_sha1(start_name, sha1))
+               die("Not a valid object name: '%s'.", start_name);
+
+       if ((commit = lookup_commit_reference(sha1)) == NULL)
+               die("Not a valid branch point: '%s'.", start_name);
        hashcpy(sha1, commit->object.sha1);
 
        lock = lock_any_ref_for_update(ref, NULL);
@@ -326,7 +344,8 @@ static void create_branch(const char *name, const char *start,
 
        if (reflog) {
                log_all_ref_updates = 1;
-               snprintf(msg, sizeof msg, "branch: Created from %s", start);
+               snprintf(msg, sizeof msg, "branch: Created from %s",
+                        start_name);
        }
 
        if (write_ref_sha1(lock, sha1, msg) < 0)
@@ -338,6 +357,9 @@ static void rename_branch(const char *oldname, const char *newname, int force)
        char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100];
        unsigned char sha1[20];
 
+       if (!oldname)
+               die("cannot rename the curren branch while not on any.");
+
        if (snprintf(oldref, sizeof(oldref), "refs/heads/%s", oldname) > sizeof(oldref))
                die("Old branchname too long");
 
@@ -367,7 +389,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 {
        int delete = 0, force_delete = 0, force_create = 0;
        int rename = 0, force_rename = 0;
-       int verbose = 0, abbrev = DEFAULT_ABBREV;
+       int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
        int reflog = 0;
        int kinds = REF_LOCAL_BRANCH;
        int i;
@@ -444,22 +466,27 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        head = xstrdup(resolve_ref("HEAD", head_sha1, 0, NULL));
        if (!head)
                die("Failed to resolve HEAD as a valid ref.");
-       if (strncmp(head, "refs/heads/", 11))
-               die("HEAD not found below refs/heads!");
-       head += 11;
+       if (!strcmp(head, "HEAD")) {
+               detached = 1;
+       }
+       else {
+               if (strncmp(head, "refs/heads/", 11))
+                       die("HEAD not found below refs/heads!");
+               head += 11;
+       }
 
        if (delete)
                return delete_branches(argc - i, argv + i, force_delete, kinds);
        else if (i == argc)
-               print_ref_list(kinds, verbose, abbrev);
+               print_ref_list(kinds, detached, verbose, abbrev);
        else if (rename && (i == argc - 1))
                rename_branch(head, argv[i], force_rename);
        else if (rename && (i == argc - 2))
                rename_branch(argv[i], argv[i + 1], force_rename);
        else if (i == argc - 1)
-               create_branch(argv[i], head, force_create, reflog);
+               create_branch(argv[i], head, head_sha1, force_create, reflog);
        else if (i == argc - 2)
-               create_branch(argv[i], argv[i + 1], force_create, reflog);
+               create_branch(argv[i], argv[i+1], NULL, force_create, reflog);
        else
                usage(builtin_branch_usage);
 
diff --git a/builtin-describe.c b/builtin-describe.c
new file mode 100644 (file)
index 0000000..a8c98ce
--- /dev/null
@@ -0,0 +1,233 @@
+#include "cache.h"
+#include "commit.h"
+#include "tag.h"
+#include "refs.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "builtin.h"
+
+static const char describe_usage[] =
+"git-describe [--all] [--tags] [--abbrev=<n>] <committish>*";
+
+static int all;        /* Default to annotated tags only */
+static int tags;       /* But allow any tags if --tags is specified */
+
+static int abbrev = DEFAULT_ABBREV;
+
+static int names, allocs;
+static struct commit_name {
+       struct commit *commit;
+       int prio; /* annotated tag = 2, tag = 1, head = 0 */
+       char path[FLEX_ARRAY]; /* more */
+} **name_array = NULL;
+
+static struct commit_name *match(struct commit *cmit)
+{
+       int i = names;
+       struct commit_name **p = name_array;
+
+       while (i-- > 0) {
+               struct commit_name *n = *p++;
+               if (n->commit == cmit)
+                       return n;
+       }
+       return NULL;
+}
+
+static void add_to_known_names(const char *path,
+                              struct commit *commit,
+                              int prio)
+{
+       int idx;
+       int len = strlen(path)+1;
+       struct commit_name *name = xmalloc(sizeof(struct commit_name) + len);
+
+       name->commit = commit;
+       name->prio = prio;
+       memcpy(name->path, path, len);
+       idx = names;
+       if (idx >= allocs) {
+               allocs = (idx + 50) * 3 / 2;
+               name_array = xrealloc(name_array, allocs*sizeof(*name_array));
+       }
+       name_array[idx] = name;
+       names = ++idx;
+}
+
+static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+{
+       struct commit *commit = lookup_commit_reference_gently(sha1, 1);
+       struct object *object;
+       int prio;
+
+       if (!commit)
+               return 0;
+       object = parse_object(sha1);
+       /* If --all, then any refs are used.
+        * If --tags, then any tags are used.
+        * Otherwise only annotated tags are used.
+        */
+       if (!strncmp(path, "refs/tags/", 10)) {
+               if (object->type == OBJ_TAG)
+                       prio = 2;
+               else
+                       prio = 1;
+       }
+       else
+               prio = 0;
+
+       if (!all) {
+               if (!prio)
+                       return 0;
+               if (!tags && prio < 2)
+                       return 0;
+       }
+       add_to_known_names(all ? path + 5 : path + 10, commit, prio);
+       return 0;
+}
+
+static int compare_names(const void *_a, const void *_b)
+{
+       struct commit_name *a = *(struct commit_name **)_a;
+       struct commit_name *b = *(struct commit_name **)_b;
+       unsigned long a_date = a->commit->date;
+       unsigned long b_date = b->commit->date;
+
+       if (a->prio != b->prio)
+               return b->prio - a->prio;
+       return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
+}
+
+struct possible_tag {
+       struct possible_tag *next;
+       struct commit_name *name;
+       unsigned long depth;
+};
+
+static void describe(const char *arg, int last_one)
+{
+       unsigned char sha1[20];
+       struct commit *cmit;
+       struct commit_list *list;
+       static int initialized = 0;
+       struct commit_name *n;
+       struct possible_tag *all_matches, *min_match, *cur_match;
+
+       if (get_sha1(arg, sha1))
+               die("Not a valid object name %s", arg);
+       cmit = lookup_commit_reference(sha1);
+       if (!cmit)
+               die("%s is not a valid '%s' object", arg, commit_type);
+
+       if (!initialized) {
+               initialized = 1;
+               for_each_ref(get_name, NULL);
+               qsort(name_array, names, sizeof(*name_array), compare_names);
+       }
+
+       n = match(cmit);
+       if (n) {
+               printf("%s\n", n->path);
+               return;
+       }
+
+       list = NULL;
+       all_matches = NULL;
+       cur_match = NULL;
+       commit_list_insert(cmit, &list);
+       while (list) {
+               struct commit *c = pop_commit(&list);
+               n = match(c);
+               if (n) {
+                       struct possible_tag *p = xmalloc(sizeof(*p));
+                       p->name = n;
+                       p->next = NULL;
+                       if (cur_match)
+                               cur_match->next = p;
+                       else
+                               all_matches = p;
+                       cur_match = p;
+               } else {
+                       struct commit_list *parents = c->parents;
+                       while (parents) {
+                               struct commit *p = parents->item;
+                               parse_commit(p);
+                               if (!(p->object.flags & SEEN)) {
+                                       p->object.flags |= SEEN;
+                                       insert_by_date(p, &list);
+                               }
+                               parents = parents->next;
+                       }
+               }
+       }
+
+       if (!all_matches)
+               die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
+
+       min_match = NULL;
+       for (cur_match = all_matches; cur_match; cur_match = cur_match->next) {
+               struct rev_info revs;
+               struct commit *tagged = cur_match->name->commit;
+
+               clear_commit_marks(cmit, -1);
+               init_revisions(&revs, NULL);
+               tagged->object.flags |= UNINTERESTING;
+               add_pending_object(&revs, &tagged->object, NULL);
+               add_pending_object(&revs, &cmit->object, NULL);
+
+               prepare_revision_walk(&revs);
+               cur_match->depth = 0;
+               while ((!min_match || cur_match->depth < min_match->depth)
+                       && get_revision(&revs))
+                       cur_match->depth++;
+               if (!min_match || cur_match->depth < min_match->depth)
+                       min_match = cur_match;
+               free_commit_list(revs.commits);
+       }
+       printf("%s-g%s\n", min_match->name->path,
+                  find_unique_abbrev(cmit->object.sha1, abbrev));
+
+       if (!last_one) {
+               for (cur_match = all_matches; cur_match; cur_match = min_match) {
+                       min_match = cur_match->next;
+                       free(cur_match);
+               }
+               clear_commit_marks(cmit, SEEN);
+       }
+}
+
+int cmd_describe(int argc, const char **argv, const char *prefix)
+{
+       int i;
+
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+
+               if (*arg != '-')
+                       break;
+               else if (!strcmp(arg, "--all"))
+                       all = 1;
+               else if (!strcmp(arg, "--tags"))
+                       tags = 1;
+               else if (!strncmp(arg, "--abbrev=", 9)) {
+                       abbrev = strtoul(arg + 9, NULL, 10);
+                       if (abbrev < MINIMUM_ABBREV || 40 < abbrev)
+                               abbrev = DEFAULT_ABBREV;
+               }
+               else
+                       usage(describe_usage);
+       }
+
+       save_commit_buffer = 0;
+
+       if (argc <= i)
+               describe("HEAD", 1);
+       else
+               while (i < argc) {
+                       describe(argv[i], (i == argc - 1));
+                       i++;
+               }
+
+       return 0;
+}
index 3b1b1cbbfa7bf348c17c025afa217ffff97087e3..2bfbdb71407dcd0ddfe970f6a614158d954cd4c0 100644 (file)
@@ -136,7 +136,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)
        if (i < 0)
                goto err_ret;
        data = xmalloc(st.st_size + 1);
-       if (st.st_size != xread(i, data, st.st_size)) {
+       if (st.st_size != read_in_full(i, data, st.st_size)) {
                error("'%s': short read %s", filename, strerror(errno));
                close(i);
                free(data);
index 97fd82ff0664aa9a79cf1ace20dac1a5c0064743..8e7540b6922696fb8100879eb9c4acf587f004db 100644 (file)
@@ -56,7 +56,7 @@ static void copy_templates_1(char *path, int baselen,
 
        /* Note: if ".git/hooks" file exists in the repository being
         * re-initialized, /etc/core-git/templates/hooks/update would
-        * cause git-init-db to fail here.  I think this is sane but
+        * cause git-init to fail here.  I think this is sane but
         * it means that the set of templates we ship by default, along
         * with the way the namespace under .git/ is organized, should
         * be really carefully chosen.
@@ -252,14 +252,18 @@ static int create_default_files(const char *git_dir, const char *template_path)
        }
        git_config_set("core.filemode", filemode ? "true" : "false");
 
-       /* Enable logAllRefUpdates if a working tree is attached */
-       if (!is_bare_git_dir(git_dir))
+       if (is_bare_repository()) {
+               git_config_set("core.bare", "true");
+       }
+       else {
+               git_config_set("core.bare", "false");
                git_config_set("core.logallrefupdates", "true");
+       }
        return reinit;
 }
 
 static const char init_db_usage[] =
-"git-init-db [--template=<template-directory>] [--shared]";
+"git-init [--template=<template-directory>] [--shared]";
 
 /*
  * If you want to, you can share the DB area with any number of branches.
index a67f3eb90b6f715714c6fa7bb931044630c74111..583da38b6750185eb38f04d91555aa75ee4a77b0 100644 (file)
@@ -515,12 +515,9 @@ static void convert_to_utf8(char *line, char *charset)
        char *input_charset = *charset ? charset : latin_one;
        char *out = reencode_string(line, metainfo_charset, input_charset);
 
-       if (!out) {
-               fprintf(stderr, "cannot convert from %s to %s\n",
-                       input_charset, metainfo_charset);
-               *charset = 0;
-               return;
-       }
+       if (!out)
+               die("cannot convert from %s to %s\n",
+                   input_charset, metainfo_charset);
        strcpy(line, out);
        free(out);
 }
@@ -797,17 +794,23 @@ static const char mailinfo_usage[] =
 
 int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 {
+       const char *def_charset;
+
        /* NEEDSWORK: might want to do the optional .git/ directory
         * discovery
         */
        git_config(git_default_config);
 
+       def_charset = (git_commit_encoding ? git_commit_encoding : "utf-8");
+       metainfo_charset = def_charset;
+
        while (1 < argc && argv[1][0] == '-') {
                if (!strcmp(argv[1], "-k"))
                        keep_subject = 1;
                else if (!strcmp(argv[1], "-u"))
-                       metainfo_charset = (git_commit_encoding
-                                           ? git_commit_encoding : "utf-8");
+                       metainfo_charset = def_charset;
+               else if (!strcmp(argv[1], "-n"))
+                       metainfo_charset = NULL;
                else if (!strncmp(argv[1], "--encoding=", 11))
                        metainfo_charset = argv[1] + 11;
                else
index 9e15beb3ba1e66987bd1fec51ed864414ffbf07c..42dd8c87a255b81fcc6306ba1ef4bb01dd8dce90 100644 (file)
@@ -276,7 +276,52 @@ static int encode_header(enum object_type type, unsigned long size, unsigned cha
  * we are going to reuse the existing object data as is.  make
  * sure it is not corrupt.
  */
-static int check_inflate(unsigned char *data, unsigned long len, unsigned long expect)
+static int check_pack_inflate(struct packed_git *p,
+               struct pack_window **w_curs,
+               unsigned long offset,
+               unsigned long len,
+               unsigned long expect)
+{
+       z_stream stream;
+       unsigned char fakebuf[4096], *in;
+       int st;
+
+       memset(&stream, 0, sizeof(stream));
+       inflateInit(&stream);
+       do {
+               in = use_pack(p, w_curs, offset, &stream.avail_in);
+               stream.next_in = in;
+               stream.next_out = fakebuf;
+               stream.avail_out = sizeof(fakebuf);
+               st = inflate(&stream, Z_FINISH);
+               offset += stream.next_in - in;
+       } while (st == Z_OK || st == Z_BUF_ERROR);
+       inflateEnd(&stream);
+       return (st == Z_STREAM_END &&
+               stream.total_out == expect &&
+               stream.total_in == len) ? 0 : -1;
+}
+
+static void copy_pack_data(struct sha1file *f,
+               struct packed_git *p,
+               struct pack_window **w_curs,
+               unsigned long offset,
+               unsigned long len)
+{
+       unsigned char *in;
+       unsigned int avail;
+
+       while (len) {
+               in = use_pack(p, w_curs, offset, &avail);
+               if (avail > len)
+                       avail = len;
+               sha1write(f, in, avail);
+               offset += avail;
+               len -= avail;
+       }
+}
+
+static int check_loose_inflate(unsigned char *data, unsigned long len, unsigned long expect)
 {
        z_stream stream;
        unsigned char fakebuf[4096];
@@ -323,7 +368,7 @@ static int revalidate_loose_object(struct object_entry *entry,
                return -1;
        map += used;
        mapsize -= used;
-       return check_inflate(map, mapsize, size);
+       return check_loose_inflate(map, mapsize, size);
 }
 
 static unsigned long write_object(struct sha1file *f,
@@ -416,6 +461,8 @@ static unsigned long write_object(struct sha1file *f,
        }
        else {
                struct packed_git *p = entry->in_pack;
+               struct pack_window *w_curs = NULL;
+               unsigned long offset;
 
                if (entry->delta) {
                        obj_type = (allow_ofs_delta && entry->delta->offset) ?
@@ -437,16 +484,14 @@ static unsigned long write_object(struct sha1file *f,
                        hdrlen += 20;
                }
 
-               use_packed_git(p);
-               buf = (char *) p->pack_base
-                       + entry->in_pack_offset
-                       + entry->in_pack_header_size;
+               offset = entry->in_pack_offset + entry->in_pack_header_size;
                datalen = find_packed_object_size(p, entry->in_pack_offset)
                                - entry->in_pack_header_size;
-               if (!pack_to_stdout && check_inflate(buf, datalen, entry->size))
+               if (!pack_to_stdout && check_pack_inflate(p, &w_curs,
+                               offset, datalen, entry->size))
                        die("corrupt delta in pack %s", sha1_to_hex(entry->sha1));
-               sha1write(f, buf, datalen);
-               unuse_packed_git(p);
+               copy_pack_data(f, p, &w_curs, offset, datalen);
+               unuse_pack(&w_curs);
                reused++;
        }
        if (entry->delta)
@@ -937,22 +982,19 @@ static void check_object(struct object_entry *entry)
 
        if (entry->in_pack && !entry->preferred_base) {
                struct packed_git *p = entry->in_pack;
+               struct pack_window *w_curs = NULL;
                unsigned long left = p->pack_size - entry->in_pack_offset;
                unsigned long size, used;
                unsigned char *buf;
                struct object_entry *base_entry = NULL;
 
-               use_packed_git(p);
-               buf = p->pack_base;
-               buf += entry->in_pack_offset;
+               buf = use_pack(p, &w_curs, entry->in_pack_offset, NULL);
 
                /* We want in_pack_type even if we do not reuse delta.
                 * There is no point not reusing non-delta representations.
                 */
                used = unpack_object_header_gently(buf, left,
                                                   &entry->in_pack_type, &size);
-               if (!used || left - used <= 20)
-                       die("corrupt pack for %s", sha1_to_hex(entry->sha1));
 
                /* Check if it is delta, and the base is also an object
                 * we are going to pack.  If so we will reuse the existing
@@ -961,21 +1003,26 @@ static void check_object(struct object_entry *entry)
                if (!no_reuse_delta) {
                        unsigned char c, *base_name;
                        unsigned long ofs;
+                       unsigned long used_0;
                        /* there is at least 20 bytes left in the pack */
                        switch (entry->in_pack_type) {
                        case OBJ_REF_DELTA:
-                               base_name = buf + used;
+                               base_name = use_pack(p, &w_curs,
+                                       entry->in_pack_offset + used, NULL);
                                used += 20;
                                break;
                        case OBJ_OFS_DELTA:
-                               c = buf[used++];
+                               buf = use_pack(p, &w_curs,
+                                       entry->in_pack_offset + used, NULL);
+                               used_0 = 0;
+                               c = buf[used_0++];
                                ofs = c & 127;
                                while (c & 128) {
                                        ofs += 1;
                                        if (!ofs || ofs & ~(~0UL >> 7))
                                                die("delta base offset overflow in pack for %s",
                                                    sha1_to_hex(entry->sha1));
-                                       c = buf[used++];
+                                       c = buf[used_0++];
                                        ofs = (ofs << 7) + (c & 127);
                                }
                                if (ofs >= entry->in_pack_offset)
@@ -983,6 +1030,7 @@ static void check_object(struct object_entry *entry)
                                            sha1_to_hex(entry->sha1));
                                ofs = entry->in_pack_offset - ofs;
                                base_name = find_packed_object_name(p, ofs);
+                               used += used_0;
                                break;
                        default:
                                base_name = NULL;
@@ -990,7 +1038,7 @@ static void check_object(struct object_entry *entry)
                        if (base_name)
                                base_entry = locate_object_entry(base_name);
                }
-               unuse_packed_git(p);
+               unuse_pack(&w_curs);
                entry->in_pack_header_size = used;
 
                if (base_entry) {
index 8dc5b9efffc3ad2cd56130155a94aa489d0d0b1b..6de7128b9d8e98dcb9850d9dc932ca522c97473a 100644 (file)
@@ -4,7 +4,7 @@
 #include "tag.h"
 
 static const char builtin_pack_refs_usage[] =
-"git-pack-refs [--all] [--prune]";
+"git-pack-refs [--all] [--prune | --no-prune]";
 
 struct ref_to_prune {
        struct ref_to_prune *next;
@@ -90,10 +90,15 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)
 
        memset(&cbdata, 0, sizeof(cbdata));
 
+       cbdata.prune = 1;
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
                if (!strcmp(arg, "--prune")) {
-                       cbdata.prune = 1;
+                       cbdata.prune = 1; /* now the default */
+                       continue;
+               }
+               if (!strcmp(arg, "--no-prune")) {
+                       cbdata.prune = 0;
                        continue;
                }
                if (!strcmp(arg, "--all")) {
index 24e3b0a8c21b43e0e82e5e505353213ca79f01b0..a57b76d7b7496f3cf81914672845bdebc75c91f8 100644 (file)
@@ -4,7 +4,10 @@
 static const char prune_packed_usage[] =
 "git-prune-packed [-n]";
 
-static void prune_dir(int i, DIR *dir, char *pathname, int len, int dryrun)
+#define DRY_RUN 01
+#define VERBOSE 02
+
+static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)
 {
        struct dirent *de;
        char hex[40];
@@ -20,7 +23,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int dryrun)
                if (!has_sha1_pack(sha1, NULL))
                        continue;
                memcpy(pathname + len, de->d_name, 38);
-               if (dryrun)
+               if (opts & DRY_RUN)
                        printf("rm -f %s\n", pathname);
                else if (unlink(pathname) < 0)
                        error("unable to unlink %s", pathname);
@@ -29,7 +32,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int dryrun)
        rmdir(pathname);
 }
 
-void prune_packed_objects(int dryrun)
+void prune_packed_objects(int opts)
 {
        int i;
        static char pathname[PATH_MAX];
@@ -46,24 +49,31 @@ void prune_packed_objects(int dryrun)
 
                sprintf(pathname + len, "%02x/", i);
                d = opendir(pathname);
+               if (opts == VERBOSE && (d || i == 255))
+                       fprintf(stderr, "Removing unused objects %d%%...\015",
+                               ((i+1) * 100) / 256);
                if (!d)
                        continue;
-               prune_dir(i, d, pathname, len + 3, dryrun);
+               prune_dir(i, d, pathname, len + 3, opts);
                closedir(d);
        }
+       if (opts == VERBOSE)
+               fprintf(stderr, "\nDone.\n");
 }
 
 int cmd_prune_packed(int argc, const char **argv, const char *prefix)
 {
        int i;
-       int dryrun = 0;
+       int opts = VERBOSE;
 
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
 
                if (*arg == '-') {
                        if (!strcmp(arg, "-n"))
-                               dryrun = 1;
+                               opts |= DRY_RUN;
+                       else if (!strcmp(arg, "-q"))
+                               opts &= ~VERBOSE;
                        else
                                usage(prune_packed_usage);
                        continue;
@@ -72,6 +82,6 @@ int cmd_prune_packed(int argc, const char **argv, const char *prefix)
                usage(prune_packed_usage);
        }
        sync();
-       prune_packed_objects(dryrun);
+       prune_packed_objects(opts);
        return 0;
 }
index b469c43bc55440d982005b81252ebfd711faa074..6f0ba0d04d789f17e3f0136a3591a9465af7e71d 100644 (file)
@@ -1,18 +1,12 @@
 #include "cache.h"
-#include "refs.h"
-#include "tag.h"
 #include "commit.h"
-#include "tree.h"
-#include "blob.h"
-#include "tree-walk.h"
 #include "diff.h"
 #include "revision.h"
 #include "builtin.h"
-#include "cache-tree.h"
+#include "reachable.h"
 
 static const char prune_usage[] = "git-prune [-n]";
 static int show_only;
-static struct rev_info revs;
 
 static int prune_object(char *path, const char *filename, const unsigned char *sha1)
 {
@@ -85,164 +79,10 @@ static void prune_object_dir(const char *path)
        }
 }
 
-static void process_blob(struct blob *blob,
-                        struct object_array *p,
-                        struct name_path *path,
-                        const char *name)
-{
-       struct object *obj = &blob->object;
-
-       if (obj->flags & SEEN)
-               return;
-       obj->flags |= SEEN;
-       /* Nothing to do, really .. The blob lookup was the important part */
-}
-
-static void process_tree(struct tree *tree,
-                        struct object_array *p,
-                        struct name_path *path,
-                        const char *name)
-{
-       struct object *obj = &tree->object;
-       struct tree_desc desc;
-       struct name_entry entry;
-       struct name_path me;
-
-       if (obj->flags & SEEN)
-               return;
-       obj->flags |= SEEN;
-       if (parse_tree(tree) < 0)
-               die("bad tree object %s", sha1_to_hex(obj->sha1));
-       name = xstrdup(name);
-       add_object(obj, p, path, name);
-       me.up = path;
-       me.elem = name;
-       me.elem_len = strlen(name);
-
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
-
-       while (tree_entry(&desc, &entry)) {
-               if (S_ISDIR(entry.mode))
-                       process_tree(lookup_tree(entry.sha1), p, &me, entry.path);
-               else
-                       process_blob(lookup_blob(entry.sha1), p, &me, entry.path);
-       }
-       free(tree->buffer);
-       tree->buffer = NULL;
-}
-
-static void process_tag(struct tag *tag, struct object_array *p, const char *name)
-{
-       struct object *obj = &tag->object;
-       struct name_path me;
-
-       if (obj->flags & SEEN)
-               return;
-       obj->flags |= SEEN;
-
-       me.up = NULL;
-       me.elem = "tag:/";
-       me.elem_len = 5;
-
-       if (parse_tag(tag) < 0)
-               die("bad tag object %s", sha1_to_hex(obj->sha1));
-       add_object(tag->tagged, p, NULL, name);
-}
-
-static void walk_commit_list(struct rev_info *revs)
-{
-       int i;
-       struct commit *commit;
-       struct object_array objects = { 0, 0, NULL };
-
-       /* Walk all commits, process their trees */
-       while ((commit = get_revision(revs)) != NULL)
-               process_tree(commit->tree, &objects, NULL, "");
-
-       /* Then walk all the pending objects, recursively processing them too */
-       for (i = 0; i < revs->pending.nr; i++) {
-               struct object_array_entry *pending = revs->pending.objects + i;
-               struct object *obj = pending->item;
-               const char *name = pending->name;
-               if (obj->type == OBJ_TAG) {
-                       process_tag((struct tag *) obj, &objects, name);
-                       continue;
-               }
-               if (obj->type == OBJ_TREE) {
-                       process_tree((struct tree *)obj, &objects, NULL, name);
-                       continue;
-               }
-               if (obj->type == OBJ_BLOB) {
-                       process_blob((struct blob *)obj, &objects, NULL, name);
-                       continue;
-               }
-               die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
-       }
-}
-
-static int add_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1, char *datail, void *cb_data)
-{
-       struct object *object;
-
-       object = parse_object(osha1);
-       if (object)
-               add_pending_object(&revs, object, "");
-       object = parse_object(nsha1);
-       if (object)
-               add_pending_object(&revs, object, "");
-       return 0;
-}
-
-static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
-{
-       struct object *object = parse_object(sha1);
-       if (!object)
-               die("bad object ref: %s:%s", path, sha1_to_hex(sha1));
-       add_pending_object(&revs, object, "");
-
-       for_each_reflog_ent(path, add_one_reflog_ent, NULL);
-
-       return 0;
-}
-
-static void add_one_tree(const unsigned char *sha1)
-{
-       struct tree *tree = lookup_tree(sha1);
-       add_pending_object(&revs, &tree->object, "");
-}
-
-static void add_cache_tree(struct cache_tree *it)
-{
-       int i;
-
-       if (it->entry_count >= 0)
-               add_one_tree(it->sha1);
-       for (i = 0; i < it->subtree_nr; i++)
-               add_cache_tree(it->down[i]->cache_tree);
-}
-
-static void add_cache_refs(void)
-{
-       int i;
-
-       read_cache();
-       for (i = 0; i < active_nr; i++) {
-               lookup_blob(active_cache[i]->sha1);
-               /*
-                * We could add the blobs to the pending list, but quite
-                * frankly, we don't care. Once we've looked them up, and
-                * added them as objects, we've really done everything
-                * there is to do for a blob
-                */
-       }
-       if (active_cache_tree)
-               add_cache_tree(active_cache_tree);
-}
-
 int cmd_prune(int argc, const char **argv, const char *prefix)
 {
        int i;
+       struct rev_info revs;
 
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
@@ -254,29 +94,8 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
        }
 
        save_commit_buffer = 0;
-
-       /*
-        * Set up revision parsing, and mark us as being interested
-        * in all object types, not just commits.
-        */
        init_revisions(&revs, prefix);
-       revs.tag_objects = 1;
-       revs.blob_objects = 1;
-       revs.tree_objects = 1;
-
-       /* Add all external refs */
-       for_each_ref(add_one_ref, NULL);
-
-       /* Add all refs from the index file */
-       add_cache_refs();
-
-       /*
-        * Set up the revision walk - this will move all commits
-        * from the pending list to the commit walking list.
-        */
-       prepare_revision_walk(&revs);
-
-       walk_commit_list(&revs);
+       mark_reachable_objects(&revs, 1);
 
        prune_object_dir(get_object_directory());
 
index d3f2f50d2bc7b4ef0f0e4801c70c5a15ff869f1f..7206b7a0139099c78b36f53c84705312f8daf8e5 100644 (file)
 #include "refs.h"
 #include "dir.h"
 #include "tree-walk.h"
+#include "diff.h"
+#include "revision.h"
+#include "reachable.h"
+
+/*
+ * reflog expire
+ */
+
+static const char reflog_expire_usage[] =
+"git-reflog expire [--verbose] [--dry-run] [--fix-stale] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
 
 static unsigned long default_reflog_expire;
 static unsigned long default_reflog_expire_unreachable;
 
+struct cmd_reflog_expire_cb {
+       struct rev_info revs;
+       int dry_run;
+       int stalefix;
+       int verbose;
+       unsigned long expire_total;
+       unsigned long expire_unreachable;
+};
+
 struct expire_reflog_cb {
        FILE *newlog;
        const char *ref;
        struct commit *ref_commit;
-       unsigned long expire_total;
-       unsigned long expire_unreachable;
+       struct cmd_reflog_expire_cb *cmd;
 };
 
+#define INCOMPLETE     (1u<<10)
+#define STUDYING       (1u<<11)
+
 static int tree_is_complete(const unsigned char *sha1)
 {
        struct tree_desc desc;
-       void *buf;
-       char type[20];
+       struct name_entry entry;
+       int complete;
+       struct tree *tree;
 
-       buf = read_sha1_file(sha1, type, &desc.size);
-       if (!buf)
+       tree = lookup_tree(sha1);
+       if (!tree)
+               return 0;
+       if (tree->object.flags & SEEN)
+               return 1;
+       if (tree->object.flags & INCOMPLETE)
                return 0;
-       desc.buf = buf;
-       while (desc.size) {
-               const unsigned char *elem;
-               const char *name;
-               unsigned mode;
-
-               elem = tree_entry_extract(&desc, &name, &mode);
-               if (!has_sha1_file(elem) ||
-                   (S_ISDIR(mode) && !tree_is_complete(elem))) {
-                       free(buf);
+
+       desc.buf = tree->buffer;
+       desc.size = tree->size;
+       if (!desc.buf) {
+               char type[20];
+               void *data = read_sha1_file(sha1, type, &desc.size);
+               if (!data) {
+                       tree->object.flags |= INCOMPLETE;
                        return 0;
                }
-               update_tree_entry(&desc);
+               desc.buf = data;
+               tree->buffer = data;
        }
-       free(buf);
-       return 1;
+       complete = 1;
+       while (tree_entry(&desc, &entry)) {
+               if (!has_sha1_file(entry.sha1) ||
+                   (S_ISDIR(entry.mode) && !tree_is_complete(entry.sha1))) {
+                       tree->object.flags |= INCOMPLETE;
+                       complete = 0;
+               }
+       }
+       free(tree->buffer);
+       tree->buffer = NULL;
+
+       if (complete)
+               tree->object.flags |= SEEN;
+       return complete;
+}
+
+static int commit_is_complete(struct commit *commit)
+{
+       struct object_array study;
+       struct object_array found;
+       int is_incomplete = 0;
+       int i;
+
+       /* early return */
+       if (commit->object.flags & SEEN)
+               return 1;
+       if (commit->object.flags & INCOMPLETE)
+               return 0;
+       /*
+        * Find all commits that are reachable and are not marked as
+        * SEEN.  Then make sure the trees and blobs contained are
+        * complete.  After that, mark these commits also as SEEN.
+        * If some of the objects that are needed to complete this
+        * commit are missing, mark this commit as INCOMPLETE.
+        */
+       memset(&study, 0, sizeof(study));
+       memset(&found, 0, sizeof(found));
+       add_object_array(&commit->object, NULL, &study);
+       add_object_array(&commit->object, NULL, &found);
+       commit->object.flags |= STUDYING;
+       while (study.nr) {
+               struct commit *c;
+               struct commit_list *parent;
+
+               c = (struct commit *)study.objects[--study.nr].item;
+               if (!c->object.parsed && !parse_object(c->object.sha1))
+                       c->object.flags |= INCOMPLETE;
+
+               if (c->object.flags & INCOMPLETE) {
+                       is_incomplete = 1;
+                       break;
+               }
+               else if (c->object.flags & SEEN)
+                       continue;
+               for (parent = c->parents; parent; parent = parent->next) {
+                       struct commit *p = parent->item;
+                       if (p->object.flags & STUDYING)
+                               continue;
+                       p->object.flags |= STUDYING;
+                       add_object_array(&p->object, NULL, &study);
+                       add_object_array(&p->object, NULL, &found);
+               }
+       }
+       if (!is_incomplete) {
+               /*
+                * make sure all commits in "found" array have all the
+                * necessary objects.
+                */
+               for (i = 0; i < found.nr; i++) {
+                       struct commit *c =
+                               (struct commit *)found.objects[i].item;
+                       if (!tree_is_complete(c->tree->object.sha1)) {
+                               is_incomplete = 1;
+                               c->object.flags |= INCOMPLETE;
+                       }
+               }
+               if (!is_incomplete) {
+                       /* mark all found commits as complete, iow SEEN */
+                       for (i = 0; i < found.nr; i++)
+                               found.objects[i].item->flags |= SEEN;
+               }
+       }
+       /* clear flags from the objects we traversed */
+       for (i = 0; i < found.nr; i++)
+               found.objects[i].item->flags &= ~STUDYING;
+       if (is_incomplete)
+               commit->object.flags |= INCOMPLETE;
+       else {
+               /*
+                * If we come here, we have (1) traversed the ancestry chain
+                * from the "commit" until we reach SEEN commits (which are
+                * known to be complete), and (2) made sure that the commits
+                * encountered during the above traversal refer to trees that
+                * are complete.  Which means that we know *all* the commits
+                * we have seen during this process are complete.
+                */
+               for (i = 0; i < found.nr; i++)
+                       found.objects[i].item->flags |= SEEN;
+       }
+       /* free object arrays */
+       free(study.objects);
+       free(found.objects);
+       return !is_incomplete;
 }
 
 static int keep_entry(struct commit **it, unsigned char *sha1)
 {
        struct commit *commit;
 
-       *it = NULL;
        if (is_null_sha1(sha1))
                return 1;
        commit = lookup_commit_reference_gently(sha1, 1);
        if (!commit)
                return 0;
 
-       /* Make sure everything in this commit exists. */
-       parse_object(commit->object.sha1);
-       if (!tree_is_complete(commit->tree->object.sha1))
+       /*
+        * Make sure everything in this commit exists.
+        *
+        * We have walked all the objects reachable from the refs
+        * and cache earlier.  The commits reachable by this commit
+        * must meet SEEN commits -- and then we should mark them as
+        * SEEN as well.
+        */
+       if (!commit_is_complete(commit))
                return 0;
        *it = commit;
        return 1;
 }
 
 static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
-                            char *data, void *cb_data)
+               const char *email, unsigned long timestamp, int tz,
+               const char *message, void *cb_data)
 {
        struct expire_reflog_cb *cb = cb_data;
-       unsigned long timestamp;
-       char *cp, *ep;
        struct commit *old, *new;
 
-       cp = strchr(data, '>');
-       if (!cp || *++cp != ' ')
-               goto prune;
-       timestamp = strtoul(cp, &ep, 10);
-       if (*ep != ' ')
-               goto prune;
-       if (timestamp < cb->expire_total)
+       if (timestamp < cb->cmd->expire_total)
                goto prune;
 
-       if (!keep_entry(&old, osha1) || !keep_entry(&new, nsha1))
+       old = new = NULL;
+       if (cb->cmd->stalefix &&
+           (!keep_entry(&old, osha1) || !keep_entry(&new, nsha1)))
                goto prune;
 
-       if ((timestamp < cb->expire_unreachable) &&
-           (!cb->ref_commit ||
-            (old && !in_merge_bases(old, cb->ref_commit)) ||
-            (new && !in_merge_bases(new, cb->ref_commit))))
-               goto prune;
+       if (timestamp < cb->cmd->expire_unreachable) {
+               if (!cb->ref_commit)
+                       goto prune;
+               if (!old && !is_null_sha1(osha1))
+                       old = lookup_commit_reference_gently(osha1, 1);
+               if (!new && !is_null_sha1(nsha1))
+                       new = lookup_commit_reference_gently(nsha1, 1);
+               if ((old && !in_merge_bases(old, cb->ref_commit)) ||
+                   (new && !in_merge_bases(new, cb->ref_commit)))
+                       goto prune;
+       }
 
-       if (cb->newlog)
-               fprintf(cb->newlog, "%s %s %s",
-                       sha1_to_hex(osha1), sha1_to_hex(nsha1), data);
+       if (cb->newlog) {
+               char sign = (tz < 0) ? '-' : '+';
+               int zone = (tz < 0) ? (-tz) : tz;
+               fprintf(cb->newlog, "%s %s %s %lu %c%04d\t%s",
+                       sha1_to_hex(osha1), sha1_to_hex(nsha1),
+                       email, timestamp, sign, zone,
+                       message);
+       }
+       if (cb->cmd->verbose)
+               printf("keep %s", message);
        return 0;
  prune:
-       if (!cb->newlog)
-               fprintf(stderr, "would prune %s", data);
+       if (!cb->newlog || cb->cmd->verbose)
+               printf("%sprune %s", cb->newlog ? "" : "would ", message);
        return 0;
 }
 
-struct cmd_reflog_expire_cb {
-       int dry_run;
-       unsigned long expire_total;
-       unsigned long expire_unreachable;
-};
-
 static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
 {
        struct cmd_reflog_expire_cb *cmd = cb_data;
@@ -134,8 +267,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
                fprintf(stderr,
                        "warning: ref '%s' does not point at a commit\n", ref);
        cb.ref = ref;
-       cb.expire_total = cmd->expire_total;
-       cb.expire_unreachable = cmd->expire_unreachable;
+       cb.cmd = cmd;
        for_each_reflog_ent(ref, expire_reflog_ent, &cb);
  finish:
        if (cb.newlog) {
@@ -164,9 +296,6 @@ static int reflog_expire_config(const char *var, const char *value)
        return 0;
 }
 
-static const char reflog_expire_usage[] =
-"git-reflog expire [--dry-run] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
-
 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 {
        struct cmd_reflog_expire_cb cb;
@@ -186,6 +315,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
        cb.expire_total = default_reflog_expire;
        cb.expire_unreachable = default_reflog_expire_unreachable;
 
+       /*
+        * We can trust the commits and objects reachable from refs
+        * even in older repository.  We cannot trust what's reachable
+        * from reflog if the repository was pruned with older git.
+        */
+
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
                if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
@@ -194,8 +329,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
                        cb.expire_total = approxidate(arg + 9);
                else if (!strncmp(arg, "--expire-unreachable=", 21))
                        cb.expire_unreachable = approxidate(arg + 21);
+               else if (!strcmp(arg, "--stale-fix"))
+                       cb.stalefix = 1;
                else if (!strcmp(arg, "--all"))
                        do_all = 1;
+               else if (!strcmp(arg, "--verbose"))
+                       cb.verbose = 1;
                else if (!strcmp(arg, "--")) {
                        i++;
                        break;
@@ -205,6 +344,15 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
                else
                        break;
        }
+       if (cb.stalefix) {
+               init_revisions(&cb.revs, prefix);
+               if (cb.verbose)
+                       printf("Marking reachable objects...");
+               mark_reachable_objects(&cb.revs, 0);
+               if (cb.verbose)
+                       putchar('\n');
+       }
+
        if (do_all)
                status |= for_each_ref(expire_reflog, &cb);
        while (i < argc) {
@@ -219,6 +367,10 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
        return status;
 }
 
+/*
+ * main "reflog"
+ */
+
 static const char reflog_usage[] =
 "git-reflog (expire | ...)";
 
index 079c0bdf36abce6a34af2678f7efbb56dfcfdf41..318d959d89669f09e12b1b97ca62f100e1a58ecd 100644 (file)
@@ -51,9 +51,11 @@ static int write_rr(struct path_list *rr, int out_fd)
        int i;
        for (i = 0; i < rr->nr; i++) {
                const char *path = rr->items[i].path;
-               write(out_fd, rr->items[i].util, 40);
-               write(out_fd, "\t", 1);
-               write(out_fd, path, strlen(path) + 1);
+               int length = strlen(path) + 1;
+               if (write_in_full(out_fd, rr->items[i].util, 40) != 40 ||
+                   write_in_full(out_fd, "\t", 1) != 1 ||
+                   write_in_full(out_fd, path, length) != length)
+                       die("unable to write rerere record");
        }
        close(out_fd);
        return commit_lock_file(&write_lock);
@@ -244,7 +246,8 @@ static int outf(void *dummy, mmbuffer_t *ptr, int nbuf)
 {
        int i;
        for (i = 0; i < nbuf; i++)
-               write(1, ptr[i].ptr, ptr[i].size);
+               if (write_in_full(1, ptr[i].ptr, ptr[i].size) != ptr[i].size)
+                       return -1;
        return 0;
 }
 
index 5b078c41943c9ce0ff1983896e8ad6ae38705f60..d81f289c3c28fa9afbc99facf6ea81b68306a46d 100644 (file)
@@ -32,6 +32,10 @@ static int remove_file(const char *name)
        char *slash;
 
        ret = unlink(name);
+       if (ret && errno == ENOENT)
+               /* The user has removed it from the filesystem by hand */
+               ret = errno = 0;
+
        if (!ret && (slash = strrchr(name, '/'))) {
                char *n = xstrdup(name);
                do {
@@ -204,7 +208,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                return 0;
 
        /*
-        * Then, unless we used "--cache", remove the filenames from
+        * Then, unless we used "--cached", remove the filenames from
         * the workspace. If we fail to remove the first one, we
         * abort the "git rm" (but once we've successfully removed
         * any file at all, we'll go ahead and commit to it all:
index 11e62fc141f592977373630c7671d3e7e914c314..8055ddab9b07e017a832e2fedf31b35fa0788a13 100644 (file)
@@ -74,7 +74,7 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
        char *content = buffer + RECORDSIZE;
        ssize_t n;
 
-       n = xread(0, buffer, HEADERSIZE);
+       n = read_in_full(0, buffer, HEADERSIZE);
        if (n < HEADERSIZE)
                die("git-get-tar-commit-id: read error");
        if (header->typeflag[0] != 'g')
@@ -82,7 +82,7 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
        if (memcmp(content, "52 comment=", 11))
                return 1;
 
-       n = xwrite(1, content + 11, 41);
+       n = write_in_full(1, content + 11, 41);
        if (n < 41)
                die("git-get-tar-commit-id: write error");
 
index e4156f8f48aeed307b1754bada173034dd81a311..48ae09e9b5268ce1f11cfba433680a147ca39f7e 100644 (file)
@@ -91,7 +91,7 @@ static void process_input(int child_fd, int band)
        char buf[16384];
        ssize_t sz = read(child_fd, buf, sizeof(buf));
        if (sz < 0) {
-               if (errno != EINTR)
+               if (errno != EAGAIN && errno != EINTR)
                        error_clnt("read error: %s\n", strerror(errno));
                return;
        }
index 7d39d9bcd178a65686d6757f8a89065d25f55b23..4e31c273f48e3983aaf99dc6525982d34b6fed06 100644 (file)
@@ -55,6 +55,7 @@ int cmd_verify_pack(int argc, const char **argv, const char *prefix)
        int no_more_options = 0;
        int nothing_done = 1;
 
+       git_config(git_default_config);
        while (1 < argc) {
                if (!no_more_options && argv[1][0] == '-') {
                        if (!strcmp("-v", argv[1]))
index df72d09447d0edd17d07eb97a9b3b36fa4b57531..0b3c9f62efed895bcfebe31f1164ac991ee01d41 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -25,6 +25,7 @@ extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
 extern int cmd_cherry(int argc, const char **argv, const char *prefix);
 extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
+extern int cmd_describe(int argc, const char **argv, const char *prefix);
 extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
 extern int cmd_diff_index(int argc, const char **argv, const char *prefix);
 extern int cmd_diff(int argc, const char **argv, const char *prefix);
diff --git a/cache.h b/cache.h
index 31b0819e83d7f76ed52db7771cf274e74b2156dc..620b6a4ed47b9b94700a7f9bec270aca7efe29db 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -127,7 +127,8 @@ extern int cache_errno;
 #define CONFIG_LOCAL_ENVIRONMENT "GIT_CONFIG_LOCAL"
 #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
 
-extern int is_bare_git_dir(const char *dir);
+extern int is_bare_repository_cfg;
+extern int is_bare_repository(void);
 extern const char *get_git_dir(void);
 extern char *get_object_directory(void);
 extern char *get_refs_directory(void);
@@ -197,6 +198,8 @@ extern int warn_ambiguous_refs;
 extern int shared_repository;
 extern const char *apply_default_whitespace;
 extern int zlib_compression_level;
+extern size_t packed_git_window_size;
+extern size_t packed_git_limit;
 
 #define GIT_REPO_VERSION 0
 extern int repository_format_version;
@@ -297,7 +300,7 @@ extern char *sha1_to_hex(const unsigned char *sha1);        /* static buffer result! */
 extern int read_ref(const char *filename, unsigned char *sha1);
 extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
 extern int create_symref(const char *ref, const char *refs_heads_master);
-extern int validate_symref(const char *ref);
+extern int validate_headref(const char *ref);
 
 extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
 extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);
@@ -336,14 +339,22 @@ extern struct alternate_object_database {
 } *alt_odb_list;
 extern void prepare_alt_odb(void);
 
+struct pack_window {
+       struct pack_window *next;
+       unsigned char *base;
+       off_t offset;
+       size_t len;
+       unsigned int last_used;
+       unsigned int inuse_cnt;
+};
+
 extern struct packed_git {
        struct packed_git *next;
-       unsigned long index_size;
-       unsigned long pack_size;
+       struct pack_window *windows;
        unsigned int *index_base;
-       void *pack_base;
-       unsigned int pack_last_used;
-       unsigned int pack_use_cnt;
+       off_t index_size;
+       off_t pack_size;
+       int pack_fd;
        int pack_local;
        unsigned char sha1[20];
        /* something like ".git/objects/pack/xxxxx.pack" */
@@ -389,13 +400,14 @@ extern void install_packed_git(struct packed_git *pack);
 extern struct packed_git *find_sha1_pack(const unsigned char *sha1, 
                                         struct packed_git *packs);
 
-extern int use_packed_git(struct packed_git *);
-extern void unuse_packed_git(struct packed_git *);
+extern void pack_report();
+extern unsigned char* use_pack(struct packed_git *, struct pack_window **, unsigned long, unsigned int *);
+extern void unuse_pack(struct pack_window **);
 extern struct packed_git *add_packed_git(char *, int, int);
 extern int num_packed_objects(const struct packed_git *p);
 extern int nth_packed_object_sha1(const struct packed_git *, int, unsigned char*);
 extern unsigned long find_pack_entry_one(const unsigned char *, struct packed_git *);
-extern void *unpack_entry_gently(struct packed_git *, unsigned long, char *, unsigned long *);
+extern void *unpack_entry(struct packed_git *, unsigned long, char *, unsigned long *);
 extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
 extern void packed_object_info_detail(struct packed_git *, unsigned long, char *, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
 
@@ -421,9 +433,11 @@ extern char *git_commit_encoding;
 extern char *git_log_output_encoding;
 
 extern int copy_fd(int ifd, int ofd);
-extern int write_in_full(int fd, const void *buf, size_t count, const char *);
+extern int read_in_full(int fd, void *buf, size_t count);
+extern int write_in_full(int fd, const void *buf, size_t count);
 extern void write_or_die(int fd, const void *buf, size_t count);
 extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
+extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
 
 /* pager.c */
 extern void setup_pager(void);
index 2a58175aca16dc211cdf5a380e82bc9c0f4d1326..9b2b842e7dcc153a12b35394a2e3f88f146b6225 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -249,8 +249,10 @@ int write_shallow_commits(int fd, int use_pack_protocol)
                        if (use_pack_protocol)
                                packet_write(fd, "shallow %s", hex);
                        else {
-                               write(fd, hex,  40);
-                               write(fd, "\n", 1);
+                               if (write_in_full(fd, hex,  40) != 40)
+                                       break;
+                               if (write_in_full(fd, "\n", 1) != 1)
+                                       break;
                        }
                }
        return count;
@@ -462,20 +464,29 @@ static int get_one_line(const char *msg, unsigned long len)
        return ret;
 }
 
+/* High bit set, or ISO-2022-INT */
+static int non_ascii(int ch)
+{
+       ch = (ch & 0xff);
+       return ((ch & 0x80) || (ch == 0x1b));
+}
+
 static int is_rfc2047_special(char ch)
 {
-       return ((ch & 0x80) || (ch == '=') || (ch == '?') || (ch == '_'));
+       return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
 }
 
-static int add_rfc2047(char *buf, const char *line, int len)
+static int add_rfc2047(char *buf, const char *line, int len,
+                      const char *encoding)
 {
        char *bp = buf;
        int i, needquote;
-       static const char q_utf8[] = "=?utf-8?q?";
+       char q_encoding[128];
+       const char *q_encoding_fmt = "=?%s?q?";
 
        for (i = needquote = 0; !needquote && i < len; i++) {
-               unsigned ch = line[i];
-               if (ch & 0x80)
+               int ch = line[i];
+               if (non_ascii(ch))
                        needquote++;
                if ((i + 1 < len) &&
                    (ch == '=' && line[i+1] == '?'))
@@ -484,8 +495,11 @@ static int add_rfc2047(char *buf, const char *line, int len)
        if (!needquote)
                return sprintf(buf, "%.*s", len, line);
 
-       memcpy(bp, q_utf8, sizeof(q_utf8)-1);
-       bp += sizeof(q_utf8)-1;
+       i = snprintf(q_encoding, sizeof(q_encoding), q_encoding_fmt, encoding);
+       if (sizeof(q_encoding) < i)
+               die("Insanely long encoding name %s", encoding);
+       memcpy(bp, q_encoding, i);
+       bp += i;
        for (i = 0; i < len; i++) {
                unsigned ch = line[i] & 0xFF;
                if (is_rfc2047_special(ch)) {
@@ -503,7 +517,8 @@ static int add_rfc2047(char *buf, const char *line, int len)
 }
 
 static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
-                        const char *line, int relative_date)
+                        const char *line, int relative_date,
+                        const char *encoding)
 {
        char *date;
        int namelen;
@@ -531,7 +546,8 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
                filler = "";
                strcpy(buf, "From: ");
                ret = strlen(buf);
-               ret += add_rfc2047(buf + ret, line, display_name_length);
+               ret += add_rfc2047(buf + ret, line, display_name_length,
+                                  encoding);
                memcpy(buf + ret, name_tail, namelen - display_name_length);
                ret += namelen - display_name_length;
                buf[ret++] = '\n';
@@ -666,21 +682,18 @@ static char *replace_encoding_header(char *buf, char *encoding)
        return buf;
 }
 
-static char *logmsg_reencode(const struct commit *commit)
+static char *logmsg_reencode(const struct commit *commit,
+                            char *output_encoding)
 {
        char *encoding;
        char *out;
-       char *output_encoding = (git_log_output_encoding
-                                ? git_log_output_encoding
-                                : git_commit_encoding);
+       char *utf8 = "utf-8";
 
-       if (!output_encoding)
-               output_encoding = "utf-8";
-       else if (!*output_encoding)
+       if (!*output_encoding)
                return NULL;
        encoding = get_header(commit, "encoding");
        if (!encoding)
-               return NULL;
+               encoding = utf8;
        if (!strcmp(encoding, output_encoding))
                out = strdup(commit->buffer);
        else
@@ -689,7 +702,8 @@ static char *logmsg_reencode(const struct commit *commit)
        if (out)
                out = replace_encoding_header(out, output_encoding);
 
-       free(encoding);
+       if (encoding != utf8)
+               free(encoding);
        if (!out)
                return NULL;
        return out;
@@ -709,8 +723,15 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
        int parents_shown = 0;
        const char *msg = commit->buffer;
        int plain_non_ascii = 0;
-       char *reencoded = logmsg_reencode(commit);
+       char *reencoded;
+       char *encoding;
 
+       encoding = (git_log_output_encoding
+                   ? git_log_output_encoding
+                   : git_commit_encoding);
+       if (!encoding)
+               encoding = "utf-8";
+       reencoded = logmsg_reencode(commit, encoding);
        if (reencoded)
                msg = reencoded;
 
@@ -736,7 +757,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
                                    i + 1 < len && msg[i+1] == '\n')
                                        in_body = 1;
                        }
-                       else if (ch & 0x80) {
+                       else if (non_ascii(ch)) {
                                plain_non_ascii = 1;
                                break;
                        }
@@ -795,13 +816,15 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
                                offset += add_user_info("Author", fmt,
                                                        buf + offset,
                                                        line + 7,
-                                                       relative_date);
+                                                       relative_date,
+                                                       encoding);
                        if (!memcmp(line, "committer ", 10) &&
                            (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER))
                                offset += add_user_info("Commit", fmt,
                                                        buf + offset,
                                                        line + 10,
-                                                       relative_date);
+                                                       relative_date,
+                                                       encoding);
                        continue;
                }
 
@@ -824,7 +847,8 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
                        int slen = strlen(subject);
                        memcpy(buf + offset, subject, slen);
                        offset += slen;
-                       offset += add_rfc2047(buf + offset, line, linelen);
+                       offset += add_rfc2047(buf + offset, line, linelen,
+                                             encoding);
                }
                else {
                        memset(buf + offset, ' ', indent);
@@ -835,11 +859,17 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
                if (fmt == CMIT_FMT_ONELINE)
                        break;
                if (subject && plain_non_ascii) {
-                       static const char header[] =
-                               "Content-Type: text/plain; charset=UTF-8\n"
+                       int sz;
+                       char header[512];
+                       const char *header_fmt =
+                               "Content-Type: text/plain; charset=%s\n"
                                "Content-Transfer-Encoding: 8bit\n";
-                       memcpy(buf + offset, header, sizeof(header)-1);
-                       offset += sizeof(header)-1;
+                       sz = snprintf(header, sizeof(header), header_fmt,
+                                     encoding);
+                       if (sizeof(header) < sz)
+                               die("Encoding name %s too long", encoding);
+                       memcpy(buf + offset, header, sz);
+                       offset += sz;
                }
                if (after_subject) {
                        int slen = strlen(after_subject);
@@ -1010,7 +1040,7 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
        free(nodes);
 }
 
-/* merge-rebase stuff */
+/* merge-base stuff */
 
 /* bits #0..15 in revision.h */
 #define PARENT1                (1u<<16)
@@ -1018,6 +1048,8 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
 #define STALE          (1u<<18)
 #define RESULT         (1u<<19)
 
+static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
+
 static struct commit *interesting(struct commit_list *list)
 {
        while (list) {
@@ -1082,6 +1114,7 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two)
        }
 
        /* Clean up the result to remove stale ones */
+       free_commit_list(list);
        list = result; result = NULL;
        while (list) {
                struct commit_list *n = list->next;
@@ -1097,7 +1130,6 @@ struct commit_list *get_merge_bases(struct commit *one,
                                    struct commit *two,
                                     int cleanup)
 {
-       const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
        struct commit_list *list;
        struct commit **rslt;
        struct commit_list *result;
diff --git a/compat/pread.c b/compat/pread.c
new file mode 100644 (file)
index 0000000..978cac4
--- /dev/null
@@ -0,0 +1,18 @@
+#include "../git-compat-util.h"
+
+ssize_t git_pread(int fd, void *buf, size_t count, off_t offset)
+{
+        off_t current_offset;
+        ssize_t rc;
+
+        current_offset = lseek(fd, 0, SEEK_CUR);
+
+        if (lseek(fd, offset, SEEK_SET) < 0)
+                return -1;
+
+        rc = read_in_full(fd, buf, count);
+
+        if (current_offset != lseek(fd, current_offset, SEEK_SET))
+                return -1;
+        return rc;
+}
index 458ae512f3b644979ddc1f6bb581fee2907dbc2f..b6082f597c118d75a9342d1e0bf5788c7a8257ee 100644 (file)
--- a/config.c
+++ b/config.c
@@ -269,6 +269,11 @@ int git_default_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.bare")) {
+               is_bare_repository_cfg = git_config_bool(var, value);
+               return 0;
+       }
+
        if (!strcmp(var, "core.ignorestat")) {
                assume_unchanged = git_config_bool(var, value);
                return 0;
@@ -304,6 +309,21 @@ int git_default_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.packedgitwindowsize")) {
+               int pgsz = getpagesize();
+               packed_git_window_size = git_config_int(var, value);
+               packed_git_window_size /= pgsz;
+               if (packed_git_window_size < 2)
+                       packed_git_window_size = 2;
+               packed_git_window_size *= pgsz;
+               return 0;
+       }
+
+       if (!strcmp(var, "core.packedgitlimit")) {
+               packed_git_limit = git_config_int(var, value);
+               return 0;
+       }
+
        if (!strcmp(var, "user.name")) {
                strlcpy(git_default_name, value, sizeof(git_default_name));
                return 0;
@@ -449,7 +469,15 @@ static int store_aux(const char* key, const char* value)
        return 0;
 }
 
-static void store_write_section(int fd, const char* key)
+static int write_error()
+{
+       fprintf(stderr, "Failed to write new configuration file\n");
+
+       /* Same error code as "failed to rename". */
+       return 4;
+}
+
+static int store_write_section(int fd, const char* key)
 {
        const char *dot = strchr(key, '.');
        int len1 = store.baselen, len2 = -1;
@@ -463,37 +491,74 @@ static void store_write_section(int fd, const char* key)
                }
        }
 
-       write(fd, "[", 1);
-       write(fd, key, len1);
+       if (write_in_full(fd, "[", 1) != 1 ||
+           write_in_full(fd, key, len1) != len1)
+               return 0;
        if (len2 >= 0) {
-               write(fd, " \"", 2);
+               if (write_in_full(fd, " \"", 2) != 2)
+                       return 0;
                while (--len2 >= 0) {
                        unsigned char c = *++dot;
                        if (c == '"')
-                               write(fd, "\\", 1);
-                       write(fd, &c, 1);
+                               if (write_in_full(fd, "\\", 1) != 1)
+                                       return 0;
+                       if (write_in_full(fd, &c, 1) != 1)
+                               return 0;
                }
-               write(fd, "\"", 1);
+               if (write_in_full(fd, "\"", 1) != 1)
+                       return 0;
        }
-       write(fd, "]\n", 2);
+       if (write_in_full(fd, "]\n", 2) != 2)
+               return 0;
+
+       return 1;
 }
 
-static void store_write_pair(int fd, const char* key, const char* value)
+static int store_write_pair(int fd, const char* key, const char* value)
 {
        int i;
+       int length = strlen(key+store.baselen+1);
+       int quote = 0;
 
-       write(fd, "\t", 1);
-       write(fd, key+store.baselen+1,
-               strlen(key+store.baselen+1));
-       write(fd, " = ", 3);
+       /* Check to see if the value needs to be quoted. */
+       if (value[0] == ' ')
+               quote = 1;
+       for (i = 0; value[i]; i++)
+               if (value[i] == ';' || value[i] == '#')
+                       quote = 1;
+       if (value[i-1] == ' ')
+               quote = 1;
+
+       if (write_in_full(fd, "\t", 1) != 1 ||
+           write_in_full(fd, key+store.baselen+1, length) != length ||
+           write_in_full(fd, " = ", 3) != 3)
+               return 0;
+       if (quote && write_in_full(fd, "\"", 1) != 1)
+               return 0;
        for (i = 0; value[i]; i++)
                switch (value[i]) {
-               case '\n': write(fd, "\\n", 2); break;
-               case '\t': write(fd, "\\t", 2); break;
-               case '"': case '\\': write(fd, "\\", 1);
-               default: write(fd, value+i, 1);
-       }
-       write(fd, "\n", 1);
+               case '\n':
+                       if (write_in_full(fd, "\\n", 2) != 2)
+                               return 0;
+                       break;
+               case '\t':
+                       if (write_in_full(fd, "\\t", 2) != 2)
+                               return 0;
+                       break;
+               case '"':
+               case '\\':
+                       if (write_in_full(fd, "\\", 1) != 1)
+                               return 0;
+               default:
+                       if (write_in_full(fd, value+i, 1) != 1)
+                               return 0;
+                       break;
+               }
+       if (quote && write_in_full(fd, "\"", 1) != 1)
+               return 0;
+       if (write_in_full(fd, "\n", 1) != 1)
+               return 0;
+       return 1;
 }
 
 static int find_beginning_of_line(const char* contents, int size,
@@ -633,9 +698,10 @@ int git_config_set_multivar(const char* key, const char* value,
                }
 
                store.key = (char*)key;
-               store_write_section(fd, key);
-               store_write_pair(fd, key, value);
-       } else{
+               if (!store_write_section(fd, key) ||
+                   !store_write_pair(fd, key, value))
+                       goto write_err_out;
+       } else {
                struct stat st;
                char* contents;
                int i, copy_begin, copy_end, new_line = 0;
@@ -695,7 +761,7 @@ int git_config_set_multivar(const char* key, const char* value,
                }
 
                fstat(in_fd, &st);
-               contents = mmap(NULL, st.st_size, PROT_READ,
+               contents = xmmap(NULL, st.st_size, PROT_READ,
                        MAP_PRIVATE, in_fd, 0);
                close(in_fd);
 
@@ -714,25 +780,33 @@ int git_config_set_multivar(const char* key, const char* value,
 
                        /* write the first part of the config */
                        if (copy_end > copy_begin) {
-                               write(fd, contents + copy_begin,
-                               copy_end - copy_begin);
-                               if (new_line)
-                                       write(fd, "\n", 1);
+                               if (write_in_full(fd, contents + copy_begin,
+                                                 copy_end - copy_begin) <
+                                   copy_end - copy_begin)
+                                       goto write_err_out;
+                               if (new_line &&
+                                   write_in_full(fd, "\n", 1) != 1)
+                                       goto write_err_out;
                        }
                        copy_begin = store.offset[i];
                }
 
                /* write the pair (value == NULL means unset) */
                if (value != NULL) {
-                       if (store.state == START)
-                               store_write_section(fd, key);
-                       store_write_pair(fd, key, value);
+                       if (store.state == START) {
+                               if (!store_write_section(fd, key))
+                                       goto write_err_out;
+                       }
+                       if (!store_write_pair(fd, key, value))
+                               goto write_err_out;
                }
 
                /* write the rest of the config */
                if (copy_begin < st.st_size)
-                       write(fd, contents + copy_begin,
-                               st.st_size - copy_begin);
+                       if (write_in_full(fd, contents + copy_begin,
+                                         st.st_size - copy_begin) <
+                           st.st_size - copy_begin)
+                               goto write_err_out;
 
                munmap(contents, st.st_size);
                unlink(config_filename);
@@ -755,6 +829,11 @@ int git_config_set_multivar(const char* key, const char* value,
                free(lock_file);
        }
        return ret;
+
+write_err_out:
+       ret = write_error();
+       goto out_free;
+
 }
 
 int git_config_rename_section(const char *old_name, const char *new_name)
@@ -785,6 +864,7 @@ int git_config_rename_section(const char *old_name, const char *new_name)
 
        while (fgets(buf, sizeof(buf), config_file)) {
                int i;
+               int length;
                for (i = 0; buf[i] && isspace(buf[i]); i++)
                        ; /* do nothing */
                if (buf[i] == '[') {
@@ -815,15 +895,22 @@ int git_config_rename_section(const char *old_name, const char *new_name)
                                /* old_name matches */
                                ret++;
                                store.baselen = strlen(new_name);
-                               store_write_section(out_fd, new_name);
+                               if (!store_write_section(out_fd, new_name)) {
+                                       ret = write_error();
+                                       goto out;
+                               }
                                continue;
                        }
                }
-               write(out_fd, buf, strlen(buf));
+               length = strlen(buf);
+               if (write_in_full(out_fd, buf, length) != length) {
+                       ret = write_error();
+                       goto out;
+               }
        }
        fclose(config_file);
        if (close(out_fd) || commit_lock_file(lock) < 0)
-               ret = error("Cannot commit config file!");
+                       ret = error("Cannot commit config file!");
  out:
        free(config_filename);
        return ret;
index ede3ab2bd8bd77f9bf110f83bb0dfb9688634aef..d90ba816e0521ccc0ee20592cbea3fad74fcb1a3 100644 (file)
@@ -280,6 +280,15 @@ and returns the process output as a string."
     (git-run-command nil nil "update-index" "--info-only" "--add" "--" (file-relative-name ignore-name)))
   (git-add-status-file (if created 'added 'modified) (file-relative-name ignore-name))))
 
+; propertize definition for XEmacs, stolen from erc-compat
+(eval-when-compile
+  (unless (fboundp 'propertize)
+    (defun propertize (string &rest props)
+      (let ((string (copy-sequence string)))
+        (while props
+          (put-text-property 0 (length string) (nth 0 props) (nth 1 props) string)
+          (setq props (cddr props)))
+        string))))
 
 ;;;; Wrappers for basic git commands
 ;;;; ------------------------------------------------------------
@@ -448,11 +457,10 @@ and returns the process output as a string."
 
 (defun git-fileinfo-prettyprint (info)
   "Pretty-printer for the git-fileinfo structure."
-  (insert (format "   %s %s %s  %s%s"
-                  (if (git-fileinfo->marked info) (propertize "*" 'face 'git-mark-face) " ")
-                  (git-status-code-as-string (git-fileinfo->state info))
-                  (git-permissions-as-string (git-fileinfo->old-perm info) (git-fileinfo->new-perm info))
-                  (git-escape-file-name (git-fileinfo->name info))
+  (insert (concat "   " (if (git-fileinfo->marked info) (propertize "*" 'face 'git-mark-face) " ")
+                  " " (git-status-code-as-string (git-fileinfo->state info))
+                  " " (git-permissions-as-string (git-fileinfo->old-perm info) (git-fileinfo->new-perm info))
+                  "  " (git-escape-file-name (git-fileinfo->name info))
                   (git-rename-as-string info))))
 
 (defun git-parse-status (status)
index b129b83e4026490c1e6e77861cd6f03a5007d01e..f039534d6536ae1898ffa618a6b077ff8ae14c90 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -102,7 +102,7 @@ static void logreport(int priority, const char *err, va_list params)
        buf[buflen++] = '\n';
        buf[buflen] = '\0';
 
-       write(2, buf, buflen);
+       write_in_full(2, buf, buflen);
 }
 
 static void logerror(const char *err, ...)
diff --git a/describe.c b/describe.c
deleted file mode 100644 (file)
index f4029ee..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-#include "cache.h"
-#include "commit.h"
-#include "tag.h"
-#include "refs.h"
-
-#define SEEN (1u << 0)
-
-static const char describe_usage[] =
-"git-describe [--all] [--tags] [--abbrev=<n>] <committish>*";
-
-static int all;        /* Default to annotated tags only */
-static int tags;       /* But allow any tags if --tags is specified */
-
-static int abbrev = DEFAULT_ABBREV;
-
-static int names, allocs;
-static struct commit_name {
-       const struct commit *commit;
-       int prio; /* annotated tag = 2, tag = 1, head = 0 */
-       char path[FLEX_ARRAY]; /* more */
-} **name_array = NULL;
-
-static struct commit_name *match(struct commit *cmit)
-{
-       int i = names;
-       struct commit_name **p = name_array;
-
-       while (i-- > 0) {
-               struct commit_name *n = *p++;
-               if (n->commit == cmit)
-                       return n;
-       }
-       return NULL;
-}
-
-static void add_to_known_names(const char *path,
-                              const struct commit *commit,
-                              int prio)
-{
-       int idx;
-       int len = strlen(path)+1;
-       struct commit_name *name = xmalloc(sizeof(struct commit_name) + len);
-
-       name->commit = commit;
-       name->prio = prio;
-       memcpy(name->path, path, len);
-       idx = names;
-       if (idx >= allocs) {
-               allocs = (idx + 50) * 3 / 2;
-               name_array = xrealloc(name_array, allocs*sizeof(*name_array));
-       }
-       name_array[idx] = name;
-       names = ++idx;
-}
-
-static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
-{
-       struct commit *commit = lookup_commit_reference_gently(sha1, 1);
-       struct object *object;
-       int prio;
-
-       if (!commit)
-               return 0;
-       object = parse_object(sha1);
-       /* If --all, then any refs are used.
-        * If --tags, then any tags are used.
-        * Otherwise only annotated tags are used.
-        */
-       if (!strncmp(path, "refs/tags/", 10)) {
-               if (object->type == OBJ_TAG)
-                       prio = 2;
-               else
-                       prio = 1;
-       }
-       else
-               prio = 0;
-
-       if (!all) {
-               if (!prio)
-                       return 0;
-               if (!tags && prio < 2)
-                       return 0;
-       }
-       add_to_known_names(all ? path + 5 : path + 10, commit, prio);
-       return 0;
-}
-
-static int compare_names(const void *_a, const void *_b)
-{
-       struct commit_name *a = *(struct commit_name **)_a;
-       struct commit_name *b = *(struct commit_name **)_b;
-       unsigned long a_date = a->commit->date;
-       unsigned long b_date = b->commit->date;
-
-       if (a->prio != b->prio)
-               return b->prio - a->prio;
-       return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
-}
-
-static void describe(const char *arg, int last_one)
-{
-       unsigned char sha1[20];
-       struct commit *cmit;
-       struct commit_list *list;
-       static int initialized = 0;
-       struct commit_name *n;
-
-       if (get_sha1(arg, sha1))
-               die("Not a valid object name %s", arg);
-       cmit = lookup_commit_reference(sha1);
-       if (!cmit)
-               die("%s is not a valid '%s' object", arg, commit_type);
-
-       if (!initialized) {
-               initialized = 1;
-               for_each_ref(get_name, NULL);
-               qsort(name_array, names, sizeof(*name_array), compare_names);
-       }
-
-       n = match(cmit);
-       if (n) {
-               printf("%s\n", n->path);
-               return;
-       }
-
-       list = NULL;
-       commit_list_insert(cmit, &list);
-       while (list) {
-               struct commit *c = pop_most_recent_commit(&list, SEEN);
-               n = match(c);
-               if (n) {
-                       printf("%s-g%s\n", n->path,
-                              find_unique_abbrev(cmit->object.sha1, abbrev));
-                       if (!last_one)
-                               clear_commit_marks(cmit, SEEN);
-                       return;
-               }
-       }
-       die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
-}
-
-int main(int argc, char **argv)
-{
-       int i;
-
-       for (i = 1; i < argc; i++) {
-               const char *arg = argv[i];
-
-               if (*arg != '-')
-                       break;
-               else if (!strcmp(arg, "--all"))
-                       all = 1;
-               else if (!strcmp(arg, "--tags"))
-                       tags = 1;
-               else if (!strncmp(arg, "--abbrev=", 9)) {
-                       abbrev = strtoul(arg + 9, NULL, 10);
-                       if (abbrev < MINIMUM_ABBREV || 40 < abbrev)
-                               abbrev = DEFAULT_ABBREV;
-               }
-               else
-                       usage(describe_usage);
-       }
-
-       setup_git_directory();
-
-       if (argc <= i)
-               describe("HEAD", 1);
-       else
-               while (i < argc) {
-                       describe(argv[i], (i == argc - 1));
-                       i++;
-               }
-
-       return 0;
-}
index fc69fb92a50c3dff67da76fedf1bf1df561c6065..2c9be60ed9b47c2e563f69e6c8b60195fd51a917 100644 (file)
@@ -97,7 +97,7 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
                         * Show the diff for the 'ce' if we found the one
                         * from the desired stage.
                         */
-                       diff_unmerge(&revs->diffopt, ce->name);
+                       diff_unmerge(&revs->diffopt, ce->name, 0, null_sha1);
                        if (ce_stage(ce) != diff_unmerged_stage)
                                continue;
                }
@@ -297,9 +297,12 @@ static int diff_cache(struct rev_info *revs,
                            !show_modified(revs, ce, ac[1], 0,
                                           cached, match_missing))
                                break;
-                       /* fallthru */
+                       diff_unmerge(&revs->diffopt, ce->name,
+                                    ntohl(ce->ce_mode), ce->sha1);
+                       break;
                case 3:
-                       diff_unmerge(&revs->diffopt, ce->name);
+                       diff_unmerge(&revs->diffopt, ce->name,
+                                    0, null_sha1);
                        break;
 
                default:
diff --git a/diff.c b/diff.c
index f14288bb8a100c43c6709f658b9a9ca44832fd7f..ad476f7c689262ce54c761615eac1d82355b33f9 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1341,10 +1341,8 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
                fd = open(s->path, O_RDONLY);
                if (fd < 0)
                        goto err_empty;
-               s->data = mmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
+               s->data = xmmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
                close(fd);
-               if (s->data == MAP_FAILED)
-                       goto err_empty;
                s->should_munmap = 1;
        }
        else {
@@ -1391,7 +1389,7 @@ static void prep_temp_blob(struct diff_tempfile *temp,
        fd = git_mkstemp(temp->tmp_path, TEMPFILE_PATH_LEN, ".diff_XXXXXX");
        if (fd < 0)
                die("unable to create temp-file");
-       if (write(fd, blob, size) != size)
+       if (write_in_full(fd, blob, size) != size)
                die("unable to write temp-file");
        close(fd);
        temp->name = temp->tmp_path;
@@ -2875,10 +2873,12 @@ void diff_change(struct diff_options *options,
 }
 
 void diff_unmerge(struct diff_options *options,
-                 const char *path)
+                 const char *path,
+                 unsigned mode, const unsigned char *sha1)
 {
        struct diff_filespec *one, *two;
        one = alloc_filespec(path);
        two = alloc_filespec(path);
-       diff_queue(&diff_queued_diff, one, two);
+       fill_filespec(one, sha1, mode);
+       diff_queue(&diff_queued_diff, one, two)->is_unmerged = 1;
 }
diff --git a/diff.h b/diff.h
index eff445596d98e46d40dd37843e690de27c5fabf1..7a347cf77d448817014ceeaed2d3cd99b5894ac6 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -144,7 +144,9 @@ extern void diff_change(struct diff_options *,
                        const char *base, const char *path);
 
 extern void diff_unmerge(struct diff_options *,
-                        const char *path);
+                        const char *path,
+                        unsigned mode,
+                        const unsigned char *sha1);
 
 extern int diff_scoreopt_parse(const char *opt);
 
index 2249bc2c05744ce5026744547fcb30195a19b3f1..1ea80671e30500f95fc1b648ccc6d5143ac0ac52 100644 (file)
@@ -54,9 +54,9 @@ struct diff_filepair {
        unsigned source_stays : 1; /* all of R/C are copies */
        unsigned broken_pair : 1;
        unsigned renamed_pair : 1;
+       unsigned is_unmerged : 1;
 };
-#define DIFF_PAIR_UNMERGED(p) \
-       (!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))
+#define DIFF_PAIR_UNMERGED(p) ((p)->is_unmerged)
 
 #define DIFF_PAIR_RENAME(p) ((p)->renamed_pair)
 
diff --git a/dir.c b/dir.c
index 0338d6c4e0ab4409d6023db96f5298d292692099..32b57f0125d1b9e95345eb23a9a32fbf8444b1b8 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -142,7 +142,7 @@ static int add_excludes_from_file_1(const char *fname,
                return 0;
        }
        buf = xmalloc(size+1);
-       if (read(fd, buf, size) != size)
+       if (read_in_full(fd, buf, size) != size)
                goto err;
        close(fd);
 
diff --git a/entry.c b/entry.c
index 88df7139477f94c236f93ca835c28ed4dd9543de..0ebf0f0c1996b3f5ec3e16e0b557294f255b7b29 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -89,7 +89,7 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat
                        return error("git-checkout-index: unable to create file %s (%s)",
                                path, strerror(errno));
                }
-               wrote = write(fd, new, size);
+               wrote = write_in_full(fd, new, size);
                close(fd);
                free(new);
                if (wrote != size)
@@ -104,7 +104,7 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat
                                return error("git-checkout-index: unable to create "
                                                 "file %s (%s)", path, strerror(errno));
                        }
-                       wrote = write(fd, new, size);
+                       wrote = write_in_full(fd, new, size);
                        close(fd);
                        free(new);
                        if (wrote != size)
index a1502c4e87c0067c8cc276006317005a0da21a49..54c22f8248cc04647829c8a7558f2bb24515f30f 100644 (file)
@@ -15,7 +15,8 @@ int use_legacy_headers = 1;
 int trust_executable_bit = 1;
 int assume_unchanged;
 int prefer_symlink_refs;
-int log_all_ref_updates;
+int is_bare_repository_cfg = -1; /* unspecified */
+int log_all_ref_updates = -1; /* unspecified */
 int warn_ambiguous_refs = 1;
 int repository_format_version;
 char *git_commit_encoding;
@@ -23,6 +24,8 @@ char *git_log_output_encoding;
 int shared_repository = PERM_UMASK;
 const char *apply_default_whitespace;
 int zlib_compression_level = Z_DEFAULT_COMPRESSION;
+size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
+size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
 int pager_in_use;
 int pager_use_color = 1;
 
@@ -49,12 +52,15 @@ static void setup_git_env(void)
        git_graft_file = getenv(GRAFT_ENVIRONMENT);
        if (!git_graft_file)
                git_graft_file = xstrdup(git_path("info/grafts"));
-       log_all_ref_updates = !is_bare_git_dir(git_dir);
 }
 
-int is_bare_git_dir (const char *dir)
+int is_bare_repository(void)
 {
-       const char *s;
+       const char *dir, *s;
+       if (0 <= is_bare_repository_cfg)
+               return is_bare_repository_cfg;
+
+       dir = get_git_dir();
        if (!strcmp(dir, DEFAULT_GIT_DIR_ENVIRONMENT))
                return 0;
        s = strrchr(dir, '/');
index 1cc3b399bcf2b8ef875ded8dfcff5aef0f345811..81f00db90b2f31d5362654a3e6a86f9cb93cf474 100644 (file)
@@ -290,7 +290,7 @@ static int fsck_sha1(unsigned char *sha1)
 {
        struct object *obj = parse_object(sha1);
        if (!obj)
-               return error("%s: object not found", sha1_to_hex(sha1));
+               return error("%s: object corrupt or missing", sha1_to_hex(sha1));
        if (obj->flags & SEEN)
                return 0;
        obj->flags |= SEEN;
@@ -399,7 +399,9 @@ static void fsck_dir(int i, char *path)
 
 static int default_refs;
 
-static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1, char *datail, void *cb_data)
+static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+               const char *email, unsigned long timestamp, int tz,
+               const char *message, void *cb_data)
 {
        struct object *obj;
 
index 1de14ea82fe678e3afb66ea11d6dd6e306158387..975777f05ed69ef78f1fd91ac512f5b92a384141 100755 (executable)
@@ -22,7 +22,7 @@ commit
 diff
 fetch
 grep
-init-db
+init
 log
 merge
 mv
index 7c0bb6084b332db7f7b6b9058db4d92b828730f0..1252f26bbd46484f25e58e92680e553f7fd7c1da 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -2,11 +2,12 @@
 #
 # Copyright (c) 2005, 2006 Junio C Hamano
 
-USAGE='[--signoff] [--dotest=<dir>] [--utf8] [--binary] [--3way]
+USAGE='[--signoff] [--dotest=<dir>] [--utf8 | --no-utf8] [--binary] [--3way]
   [--interactive] [--whitespace=<option>] <mbox>...
   or, when resuming [--skip | --resolved]'
 . git-sh-setup
 set_reflog_action am
+require_work_tree
 
 git var GIT_COMMITTER_IDENT >/dev/null || exit
 
@@ -105,7 +106,7 @@ It does not apply to blobs recorded in its index."
 }
 
 prec=4
-dotest=.dotest sign= utf8= keep= skip= interactive= resolved= binary= ws= resolvemsg=
+dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= ws= resolvemsg=
 
 while case "$#" in 0) break;; esac
 do
@@ -128,7 +129,9 @@ do
        -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
        sign=t; shift ;;
        -u|--u|--ut|--utf|--utf8)
-       utf8=t; shift ;;
+       utf8=t; shift ;; # this is now default
+       --no-u|--no-ut|--no-utf|--no-utf8)
+       utf8=; shift ;;
        -k|--k|--ke|--kee|--keep)
        keep=t; shift ;;
 
@@ -226,6 +229,8 @@ fi
 if test "$(cat "$dotest/utf8")" = t
 then
        utf8=-u
+else
+       utf8=-n
 fi
 if test "$(cat "$dotest/keep")" = t
 then
index 5569fdcc3463b214411d5a168a00783b0d390044..1f68599ae51a671f8c7a3a01c6aa9c6b205dd217 100755 (executable)
@@ -23,11 +23,12 @@ USAGE='[-u] [-k] [-q] [-m] (-c .dotest/<num> | mbox) [signoff]'
 
 git var GIT_COMMITTER_IDENT >/dev/null || exit
 
-keep_subject= query_apply= continue= utf8= resume=t
+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 ;;
index ada60ec240a8b3eb4ef6efea6b6223fc8523705f..2e15781246c4a5997eb595885801fe7c6e741495 100755 (executable)
@@ -226,7 +226,7 @@ sub do_abrowse {
 unless (-d $git_dir) { # initial import
     if ($psets[0]{type} eq 'i' || $psets[0]{type} eq 't') {
         print "Starting import from $psets[0]{id}\n";
-       `git-init-db`;
+       `git-init`;
        die $! if $?;
        $import = 1;
     } else {
index 92ec069a3acacc2d12c2c709311969f3d0aa4153..66e40b90ebfa8e65e9c42731203f10dde0424b8c 100755 (executable)
@@ -3,9 +3,11 @@
 USAGE='[-f] [-b <new_branch>] [-m] [<branch>] [<paths>...]'
 SUBDIRECTORY_OK=Sometimes
 . git-sh-setup
+require_work_tree
 
 old_name=HEAD
 old=$(git-rev-parse --verify $old_name 2>/dev/null)
+oldbranch=$(git-symbolic-ref $old_name 2>/dev/null)
 new=
 new_name=
 force=
@@ -13,6 +15,8 @@ branch=
 newbranch=
 newbranch_log=
 merge=
+LF='
+'
 while [ "$#" != "0" ]; do
     arg="$1"
     shift
@@ -50,7 +54,7 @@ while [ "$#" != "0" ]; do
                                exit 1
                        fi
                        new="$rev"
-                       new_name="$arg^0"
+                       new_name="$arg"
                        if git-show-ref --verify --quiet -- "refs/heads/$arg"
                        then
                                branch="$arg"
@@ -131,31 +135,53 @@ fi
 
 # We are switching branches and checking out trees, so
 # we *NEED* to be at the toplevel.
-cdup=$(git-rev-parse --show-cdup)
-if test ! -z "$cdup"
-then
-       cd "$cdup"
-fi
+cd_to_toplevel
 
 [ -z "$new" ] && new=$old && new_name="$old_name"
 
-# If we don't have an old branch that we're switching to,
+# If we don't have an existing branch that we're switching to,
 # and we don't have a new branch name for the target we
-# are switching to, then we'd better just be checking out
-# what we already had
+# are switching to, then we are detaching our HEAD from any
+# branch.  However, if "git checkout HEAD" detaches the HEAD
+# from the current branch, even though that may be logically
+# correct, it feels somewhat funny.  More importantly, we do not
+# want "git checkout" nor "git checkout -f" to detach HEAD.
 
-[ -z "$branch$newbranch" ] &&
-       [ "$new" != "$old" ] &&
-       die "git checkout: provided reference cannot be checked out directly
+detached=
+detach_warn=
 
-  You need -b to associate a new branch with the wanted checkout. Example:
-  git checkout -b <new_branch_name> $arg
-"
+if test -z "$branch$newbranch" && test "$new" != "$old"
+then
+       detached="$new"
+       if test -n "$oldbranch"
+       then
+               detach_warn="warning: you are not on ANY branch anymore.
+If you meant to create a new branch from the commit, you need -b to
+associate a new branch with the wanted checkout.  Example:
+  git checkout -b <new_branch_name> $arg"
+       fi
+elif test -z "$oldbranch" && test -n "$branch"
+then
+       # Coming back...
+       if test -z "$force"
+       then
+               git show-ref -d -s | grep "$old" >/dev/null || {
+                       echo >&2 \
+"You are not on any branch and switching to branch '$new_name'
+may lose your changes.  At this point, you can do one of two things:
+ (1) Decide it is Ok and say 'git checkout -f $new_name';
+ (2) Start a new branch from the current commit, by saying
+     'git checkout -b <branch-name>'.
+Leaving your HEAD detached; not switching to branch '$new_name'."
+                       exit 1;
+               }
+       fi
+fi
 
 if [ "X$old" = X ]
 then
-       echo "warning: You do not appear to currently be on a branch." >&2
-       echo "warning: Forcing checkout of $new_name." >&2
+       echo >&2 "warning: You appear to be on a branch yet to be born."
+       echo >&2 "warning: Forcing checkout of $new_name."
        force=1
 fi
 
@@ -226,8 +252,25 @@ if [ "$?" -eq 0 ]; then
                git-update-ref -m "checkout: Created from $new_name" "refs/heads/$newbranch" $new || exit
                branch="$newbranch"
        fi
-       [ "$branch" ] &&
-       GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch"
+       if test -n "$branch"
+       then
+               GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch"
+       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...
+               #
+               echo "$detached" >"$GIT_DIR/HEAD.new" &&
+               mv "$GIT_DIR/HEAD.new" "$GIT_DIR/HEAD" ||
+                       die "Cannot detach HEAD"
+               if test -n "$detach_warn"
+               then
+                       echo >&2 "$detach_warn"
+               fi
+       fi
        rm -f "$GIT_DIR/MERGE_HEAD"
 else
        exit 1
index 071b974f496b8deff3a2d1b869c35d4f556dc17f..db177a7886b6407b4c4ad7b778a1ae99471355ac 100755 (executable)
@@ -14,6 +14,7 @@ When optional <paths>... arguments are given, the paths
 affected are further limited to those that match them.'
 SUBDIRECTORY_OK=Yes
 . git-sh-setup
+require_work_tree
 
 ignored=
 ignoredonly=
index 3d388de62a9ee212c8f54f3a5dc9a8b823bc8934..0f7bbbfb39cb4eb08379b8441b1ceab3d55d18e5 100755 (executable)
@@ -214,7 +214,7 @@ yes)
        GIT_DIR="$D" ;;
 *)
        GIT_DIR="$D/.git" ;;
-esac && export GIT_DIR && git-init-db ${template+"$template"} || usage
+esac && export GIT_DIR && git-init ${template+"$template"} || usage
 
 if test -n "$reference"
 then
@@ -355,7 +355,7 @@ then
        # The name under $remote_top the remote HEAD seems to point at.
        head_points_at=$(
                (
-                       echo "master"
+                       test -f "$GIT_DIR/$remote_top/master" && echo "master"
                        cd "$GIT_DIR/$remote_top" &&
                        find . -type f -print | sed -e 's/^\.\///'
                ) | (
index 04aad5e5da6fad46bc71c859286615c2f6ce11ab..e23918cd6c515e193d3bc5d476c360001fdc1222 100755 (executable)
@@ -6,6 +6,7 @@
 USAGE='[-a] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit>] [-u] [--amend] [-e] [--author <author>] [[-i | -o] <path>...]'
 SUBDIRECTORY_OK=Yes
 . git-sh-setup
+require_work_tree
 
 git-rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t
 
@@ -315,22 +316,16 @@ esac
 ################################################################
 # Prepare index to have a tree to be committed
 
-TOP=`git-rev-parse --show-cdup`
-if test -z "$TOP"
-then
-       TOP=./
-fi
-
 case "$all,$also" in
 t,)
        save_index &&
        (
-               cd "$TOP"
-               GIT_INDEX_FILE="$NEXT_INDEX"
-               export GIT_INDEX_FILE
+               cd_to_toplevel &&
+               GIT_INDEX_FILE="$NEXT_INDEX" &&
+               export GIT_INDEX_FILE &&
                git-diff-files --name-only -z |
                git-update-index --remove -z --stdin
-       )
+       ) || exit
        ;;
 ,t)
        save_index &&
@@ -338,11 +333,11 @@ t,)
 
        git-diff-files --name-only -z -- "$@"  |
        (
-               cd "$TOP"
-               GIT_INDEX_FILE="$NEXT_INDEX"
-               export GIT_INDEX_FILE
+               cd_to_toplevel &&
+               GIT_INDEX_FILE="$NEXT_INDEX" &&
+               export GIT_INDEX_FILE &&
                git-update-index --remove -z --stdin
-       )
+       ) || exit
        ;;
 ,)
        case "$#" in
@@ -434,7 +429,9 @@ then
        fi
 elif test "$use_commit" != ""
 then
-       git-cat-file commit "$use_commit" | sed -e '1,/^$/d'
+       encoding=$(git repo-config i18n.commitencoding || echo UTF-8)
+       git show -s --pretty=raw --encoding="$encoding" "$use_commit" |
+       sed -e '1,/^$/d' -e 's/^    //'
 elif test -f "$GIT_DIR/MERGE_MSG"
 then
        cat "$GIT_DIR/MERGE_MSG"
@@ -496,7 +493,8 @@ then
                q
        }
        '
-       set_author_env=`git-cat-file commit "$use_commit" |
+       encoding=$(git repo-config i18n.commitencoding || echo UTF-8)
+       set_author_env=`git show -s --pretty=raw --encoding="$encoding" "$use_commit" |
        LANG=C LC_ALL=C sed -ne "$pick_author_script"`
        eval "$set_author_env"
        export GIT_AUTHOR_NAME
@@ -628,7 +626,7 @@ then
        if test -z "$quiet"
        then
                echo "Created${initial_commit:+ initial} commit $commit"
-               git-diff-tree --shortstat --summary --root --no-commit-id HEAD
+               git-diff-tree --shortstat --summary --root --no-commit-id HEAD --
        fi
 fi
 
index 5d9eb2615b2e21979f547199121828aafbf02135..8781e8e22d575c155967c8766f5d74835c0b1f78 100644 (file)
@@ -92,12 +92,26 @@ extern void set_warn_routine(void (*routine)(const char *warn, va_list params));
 extern void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
 extern int git_munmap(void *start, size_t length);
 
+#define DEFAULT_PACKED_GIT_WINDOW_SIZE (1 * 1024 * 1024)
+
 #else /* NO_MMAP */
 
 #include <sys/mman.h>
+#define DEFAULT_PACKED_GIT_WINDOW_SIZE \
+       (sizeof(void*) >= 8 \
+               ?  1 * 1024 * 1024 * 1024 \
+               : 32 * 1024 * 1024)
 
 #endif /* NO_MMAP */
 
+#define DEFAULT_PACKED_GIT_LIMIT \
+       ((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256))
+
+#ifdef NO_PREAD
+#define pread git_pread
+extern ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
+#endif
+
 #ifdef NO_SETENV
 #define setenv gitsetenv
 extern int gitsetenv(const char *, const char *, int);
@@ -118,11 +132,17 @@ extern char *gitstrcasestr(const char *haystack, const char *needle);
 extern size_t gitstrlcpy(char *, const char *, size_t);
 #endif
 
+extern void release_pack_memory(size_t);
+
 static inline char* xstrdup(const char *str)
 {
        char *ret = strdup(str);
-       if (!ret)
-               die("Out of memory, strdup failed");
+       if (!ret) {
+               release_pack_memory(strlen(str) + 1);
+               ret = strdup(str);
+               if (!ret)
+                       die("Out of memory, strdup failed");
+       }
        return ret;
 }
 
@@ -131,8 +151,14 @@ static inline void *xmalloc(size_t size)
        void *ret = malloc(size);
        if (!ret && !size)
                ret = malloc(1);
-       if (!ret)
-               die("Out of memory, malloc failed");
+       if (!ret) {
+               release_pack_memory(size);
+               ret = malloc(size);
+               if (!ret && !size)
+                       ret = malloc(1);
+               if (!ret)
+                       die("Out of memory, malloc failed");
+       }
 #ifdef XMALLOC_POISON
        memset(ret, 0xA5, size);
 #endif
@@ -144,8 +170,14 @@ static inline void *xrealloc(void *ptr, size_t size)
        void *ret = realloc(ptr, size);
        if (!ret && !size)
                ret = realloc(ptr, 1);
-       if (!ret)
-               die("Out of memory, realloc failed");
+       if (!ret) {
+               release_pack_memory(size);
+               ret = realloc(ptr, size);
+               if (!ret && !size)
+                       ret = realloc(ptr, 1);
+               if (!ret)
+                       die("Out of memory, realloc failed");
+       }
        return ret;
 }
 
@@ -154,8 +186,29 @@ static inline void *xcalloc(size_t nmemb, size_t size)
        void *ret = calloc(nmemb, size);
        if (!ret && (!nmemb || !size))
                ret = calloc(1, 1);
-       if (!ret)
-               die("Out of memory, calloc failed");
+       if (!ret) {
+               release_pack_memory(nmemb * size);
+               ret = calloc(nmemb, size);
+               if (!ret && (!nmemb || !size))
+                       ret = calloc(1, 1);
+               if (!ret)
+                       die("Out of memory, calloc failed");
+       }
+       return ret;
+}
+
+static inline void *xmmap(void *start, size_t length,
+       int prot, int flags, int fd, off_t offset)
+{
+       void *ret = mmap(start, length, prot, flags, fd, offset);
+       if (ret == MAP_FAILED) {
+               if (!length)
+                       return NULL;
+               release_pack_memory(length);
+               ret = mmap(start, length, prot, flags, fd, offset);
+               if (ret == MAP_FAILED)
+                       die("Out of memory? mmap failed: %s", strerror(errno));
+       }
        return ret;
 }
 
index c5bf2d19cda6104f4fa9ade7c1407bb6ab8b5d1e..35ef0c0ee54a8f284c1afd7754f2e69e03d925c1 100755 (executable)
@@ -29,7 +29,7 @@
 $SIG{'PIPE'}="IGNORE";
 $ENV{'TZ'}="UTC";
 
-our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L);
+our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L, $opt_a);
 my (%conv_author_name, %conv_author_email);
 
 sub usage() {
@@ -37,7 +37,7 @@ ()
 Usage: ${\basename $0}     # fetch/update GIT from CVS
        [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
        [-p opts-for-cvsps] [-C GIT_repository] [-z fuzz] [-i] [-k] [-u]
-       [-s subst] [-m] [-M regex] [-S regex] [CVS_module]
+       [-s subst] [-a] [-m] [-M regex] [-S regex] [CVS_module]
 END
        exit(1);
 }
@@ -105,6 +105,8 @@ ($)
 }
 $opt_o ||= "origin";
 $opt_s ||= "-";
+$opt_a ||= 0;
+
 my $git_tree = $opt_C;
 $git_tree ||= ".";
 
@@ -129,6 +131,11 @@ ($)
        push (@mergerx, qr/$opt_M/);
 }
 
+# Remember UTC of our starting time
+# we'll want to avoid importing commits
+# that are too recent
+our $starttime = time();
+
 select(STDERR); $|=1; select(STDOUT);
 
 
@@ -513,7 +520,7 @@ ($$)
 my %index; # holds filenames of one index per branch
 
 unless (-d $git_dir) {
-       system("git-init-db");
+       system("git-init");
        die "Cannot init the GIT db at $git_tree: $?\n" if $?;
        system("git-read-tree");
        die "Cannot init an empty tree: $?\n" if $?;
@@ -568,9 +575,11 @@ ($$)
 # run cvsps into a file unless we are getting
 # it passed as a file via $opt_P
 #
+my $cvspsfile;
 unless ($opt_P) {
        print "Running cvsps...\n" if $opt_v;
        my $pid = open(CVSPS,"-|");
+       my $cvspsfh;
        die "Cannot fork: $!\n" unless defined $pid;
        unless ($pid) {
                my @opt;
@@ -583,18 +592,18 @@ ($$)
                exec("cvsps","--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
                die "Could not start cvsps: $!\n";
        }
-       my ($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
-                                            DIR => File::Spec->tmpdir());
+       ($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
+                                         DIR => File::Spec->tmpdir());
        while (<CVSPS>) {
            print $cvspsfh $_;
        }
        close CVSPS;
        close $cvspsfh;
-       $opt_P = $cvspsfile;
+} else {
+       $cvspsfile = $opt_P;
 }
 
-
-open(CVS, "<$opt_P") or die $!;
+open(CVS, "<$cvspsfile") or die $!;
 
 ## cvsps output:
 #---------------------
@@ -651,7 +660,7 @@ ()
 sub commit {
        if ($branch eq $opt_o && !$index{branch} && !get_headref($branch, $git_dir)) {
            # looks like an initial commit
-           # use the index primed by git-init-db
+           # use the index primed by git-init
            $ENV{GIT_INDEX_FILE} = '.git/index';
            $index{$branch} = '.git/index';
        } else {
@@ -824,6 +833,15 @@ sub commit {
                        $state = 11;
                        next;
                }
+               if (!$opt_a && $starttime - 300 - (defined $opt_z ? $opt_z : 300) <= $date) {
+                       # skip if the commit is too recent
+                       # that the cvsps default fuzz is 300s, we give ourselves another
+                       # 300s just in case -- this also prevents skipping commits
+                       # due to server clock drift
+                       print "skip patchset $patchset: $date too recent\n" if $opt_v;
+                       $state = 11;
+                       next;
+               }
                if (exists $ignorebranch{$branch}) {
                        print STDERR "Skipping $branch\n";
                        $state = 11;
@@ -920,6 +938,10 @@ sub commit {
 }
 commit() if $branch and $state != 11;
 
+unless ($opt_P) {
+       unlink($cvspsfile);
+}
+
 # The heuristic of repacking every 1024 commits can leave a
 # lot of unpacked data.  If there is more than 1MB worth of
 # not-packed objects, repack once more.
index df395126b86bbed4d8f785e7eccbdb091c3f888b..a33a876ff652fea5a0a8933b919982557a7093de 100755 (executable)
@@ -1181,12 +1181,15 @@ sub req_ci
         $filename = filecleanup($filename);
 
         my $meta = $updater->getmeta($filename);
+       unless (defined $meta->{revision}) {
+         $meta->{revision} = 1;
+       }
 
         my ( $filepart, $dirpart ) = filenamesplit($filename, 1);
 
         $log->debug("Checked-in $dirpart : $filename");
 
-        if ( $meta->{filehash} eq "deleted" )
+        if ( defined $meta->{filehash} && $meta->{filehash} eq "deleted" )
         {
             print "Remove-entry $dirpart\n";
             print "$filename\n";
@@ -2184,7 +2187,10 @@ sub update
     # first lets get the commit list
     $ENV{GIT_DIR} = $self->{git_path};
 
-    my $commitinfo = `git-cat-file commit $self->{module} 2>&1`;
+    my $commitsha1 = `git rev-parse $self->{module}`;
+    chomp $commitsha1;
+
+    my $commitinfo = `git cat-file commit $self->{module} 2>&1`;
     unless ( $commitinfo =~ /tree\s+[a-zA-Z0-9]{40}/ )
     {
         die("Invalid module '$self->{module}'");
@@ -2194,6 +2200,10 @@ sub update
     my $git_log;
     my $lastcommit = $self->_get_prop("last_commit");
 
+    if (defined $lastcommit && $lastcommit eq $commitsha1) { # up-to-date
+         return 1;
+    }
+
     # Start exclusive lock here...
     $self->{dbh}->begin_work() or die "Cannot lock database for BEGIN";
 
index 466fe59e35e03a6f9955f78e4238d9e0a60b8dc0..87b940b85b5b128f29dc6084bb545640e5bf6b92 100755 (executable)
@@ -5,12 +5,8 @@ USAGE='<fetch-options> <repository> <refspec>...'
 SUBDIRECTORY_OK=Yes
 . git-sh-setup
 set_reflog_action "fetch $*"
+cd_to_toplevel ;# probably unnecessary...
 
-TOP=$(git-rev-parse --show-cdup)
-if test ! -z "$TOP"
-then
-       cd "$TOP"
-fi
 . git-parse-remote
 _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
 _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
@@ -231,11 +227,12 @@ update_local_ref () {
     esac
 }
 
-case "$update_head_ok" in
-'')
+# updating the current HEAD with git-fetch in a bare
+# repository is always fine.
+if test -z "$update_head_ok" && test $(is_bare_repository) = false
+then
        orig_head=$(git-rev-parse --verify HEAD 2>/dev/null)
-       ;;
-esac
+fi
 
 # If --tags (and later --heads or --all) is specified, then we are
 # not talking about defaults stored in Pull: line of remotes or
index 477002910ede7f6bc6bcb9a79f6ddebd248f6bd2..7b590268edb44cc5455fe9bdbcbfc0698a6fb800 100755 (executable)
@@ -5,11 +5,14 @@
 
 USAGE='[-n] [--no-commit] [--squash] [-s <strategy>] [-m=<merge-message>] <commit>+'
 
+SUBDIRECTORY_OK=Yes
 . git-sh-setup
 set_reflog_action "merge $*"
+require_work_tree
+cd_to_toplevel
 
 test -z "$(git ls-files -u)" ||
-       die "You are in a middle of conflicted merge."
+       die "You are in the middle of a conflicted merge."
 
 LF='
 '
@@ -298,24 +301,30 @@ f,*)
        ;;
 ?,1,*,)
        # We are not doing octopus, not fast forward, and have only
-       # one common.  See if it is really trivial.
-       git var GIT_COMMITTER_IDENT >/dev/null || exit
-
-       echo "Trying really trivial in-index merge..."
+       # one common.
        git-update-index --refresh 2>/dev/null
-       if git-read-tree --trivial -m -u -v $common $head "$1" &&
-          result_tree=$(git-write-tree)
-       then
-           echo "Wonderful."
-           result_commit=$(
-               echo "$merge_msg" |
-               git-commit-tree $result_tree -p HEAD -p "$1"
-           ) || exit
-           finish "$result_commit" "In-index merge"
-           dropsave
-           exit 0
-       fi
-       echo "Nope."
+       case " $use_strategies " in
+       *' recursive '*|*' recur '*)
+               : run merge later
+               ;;
+       *)
+               # See if it is really trivial.
+               git var GIT_COMMITTER_IDENT >/dev/null || exit
+               echo "Trying really trivial in-index merge..."
+               if git-read-tree --trivial -m -u -v $common $head "$1" &&
+                  result_tree=$(git-write-tree)
+               then
+                       echo "Wonderful."
+                       result_commit=$(
+                               echo "$merge_msg" |
+                               git-commit-tree $result_tree -p HEAD -p "$1"
+                       ) || exit
+                       finish "$result_commit" "In-index merge"
+                       dropsave
+                       exit 0
+               fi
+               echo "Nope."
+       esac
        ;;
 *)
        # An octopus.  If we can reach all the remote we are up to date.
index 908941dd77007e71f5aa036bb190a14d628050c8..5c56cace0ef132c117e9e0f5624eaf375a5b84d2 100644 (file)
@@ -163,7 +163,7 @@ def __init__(self):
             self.gitdir = self.get_single("rev-parse --git-dir")
             report(2, "gdir:", self.gitdir)
         except:
-            die("Not a git repository... did you forget to \"git init-db\" ?")
+            die("Not a git repository... did you forget to \"git init\" ?")
         try:
             self.cdup = self.get_single("rev-parse --show-cdup")
             if self.cdup != "":
index c184fb81a4dab622d79120bcc986c3bf2b07df7f..959261757c1fd8df50f7894ba449b76d621f3c45 100755 (executable)
@@ -6,11 +6,14 @@
 
 USAGE='[-n | --no-summary] [--no-commit] [-s strategy]... [<fetch-options>] <repo> <head>...'
 LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEAD.'
+SUBDIRECTORY_OK=Yes
 . git-sh-setup
 set_reflog_action "pull $*"
+require_work_tree
+cd_to_toplevel
 
 test -z "$(git ls-files -u)" ||
-       die "You are in a middle of conflicted merge."
+       die "You are in the middle of a conflicted merge."
 
 strategy_args= no_summary= no_commit= squash=
 while case "$#,$1" in 0) break ;; *,-*) ;; *) break ;; esac
index 828c59ce61a8317f23ba9113e4de6e09a58e70ce..c8bd0f99d1306cd311eaf45f73581efc8bb0e512 100755 (executable)
@@ -27,8 +27,12 @@ Example:       git-rebase master~1 topic
        /                   -->           /
   D---E---F---G master          D---E---F---G master
 '
+
+SUBDIRECTORY_OK=Yes
 . git-sh-setup
 set_reflog_action rebase
+require_work_tree
+cd_to_toplevel
 
 RESOLVEMSG="
 When you have resolved this problem run \"git rebase --continue\".
diff --git a/git-remote.perl b/git-remote.perl
new file mode 100755 (executable)
index 0000000..fc055b6
--- /dev/null
@@ -0,0 +1,282 @@
+#!/usr/bin/perl -w
+
+use Git;
+my $git = Git->repository();
+
+sub add_remote_config {
+       my ($hash, $name, $what, $value) = @_;
+       if ($what eq 'url') {
+               if (exists $hash->{$name}{'URL'}) {
+                       print STDERR "Warning: more than one remote.$name.url\n";
+               }
+               $hash->{$name}{'URL'} = $value;
+       }
+       elsif ($what eq 'fetch') {
+               $hash->{$name}{'FETCH'} ||= [];
+               push @{$hash->{$name}{'FETCH'}}, $value;
+       }
+       if (!exists $hash->{$name}{'SOURCE'}) {
+               $hash->{$name}{'SOURCE'} = 'config';
+       }
+}
+
+sub add_remote_remotes {
+       my ($hash, $file, $name) = @_;
+
+       if (exists $hash->{$name}) {
+               $hash->{$name}{'WARNING'} = 'ignored due to config';
+               return;
+       }
+
+       my $fh;
+       if (!open($fh, '<', $file)) {
+               print STDERR "Warning: cannot open $file\n";
+               return;
+       }
+       my $it = { 'SOURCE' => 'remotes' };
+       $hash->{$name} = $it;
+       while (<$fh>) {
+               chomp;
+               if (/^URL:\s*(.*)$/) {
+                       # Having more than one is Ok -- it is used for push.
+                       if (! exists $it->{'URL'}) {
+                               $it->{'URL'} = $1;
+                       }
+               }
+               elsif (/^Push:\s*(.*)$/) {
+                       ; # later
+               }
+               elsif (/^Pull:\s*(.*)$/) {
+                       $it->{'FETCH'} ||= [];
+                       push @{$it->{'FETCH'}}, $1;
+               }
+               elsif (/^\#/) {
+                       ; # ignore
+               }
+               else {
+                       print STDERR "Warning: funny line in $file: $_\n";
+               }
+       }
+       close($fh);
+}
+
+sub list_remote {
+       my ($git) = @_;
+       my %seen = ();
+       my @remotes = eval {
+               $git->command(qw(repo-config --get-regexp), '^remote\.');
+       };
+       for (@remotes) {
+               if (/^remote\.([^.]*)\.(\S*)\s+(.*)$/) {
+                       add_remote_config(\%seen, $1, $2, $3);
+               }
+       }
+
+       my $dir = $git->repo_path() . "/remotes";
+       if (opendir(my $dh, $dir)) {
+               local $_;
+               while ($_ = readdir($dh)) {
+                       chomp;
+                       next if (! -f "$dir/$_" || ! -r _);
+                       add_remote_remotes(\%seen, "$dir/$_", $_);
+               }
+       }
+
+       return \%seen;
+}
+
+sub add_branch_config {
+       my ($hash, $name, $what, $value) = @_;
+       if ($what eq 'remote') {
+               if (exists $hash->{$name}{'REMOTE'}) {
+                       print STDERR "Warning: more than one branch.$name.remote\n";
+               }
+               $hash->{$name}{'REMOTE'} = $value;
+       }
+       elsif ($what eq 'merge') {
+               $hash->{$name}{'MERGE'} ||= [];
+               push @{$hash->{$name}{'MERGE'}}, $value;
+       }
+}
+
+sub list_branch {
+       my ($git) = @_;
+       my %seen = ();
+       my @branches = eval {
+               $git->command(qw(repo-config --get-regexp), '^branch\.');
+       };
+       for (@branches) {
+               if (/^branch\.([^.]*)\.(\S*)\s+(.*)$/) {
+                       add_branch_config(\%seen, $1, $2, $3);
+               }
+       }
+
+       return \%seen;
+}
+
+my $remote = list_remote($git);
+my $branch = list_branch($git);
+
+sub update_ls_remote {
+       my ($harder, $info) = @_;
+
+       return if (($harder == 0) ||
+                  (($harder == 1) && exists $info->{'LS_REMOTE'}));
+
+       my @ref = map {
+               s|^[0-9a-f]{40}\s+refs/heads/||;
+               $_;
+       } $git->command(qw(ls-remote --heads), $info->{'URL'});
+       $info->{'LS_REMOTE'} = \@ref;
+}
+
+sub show_wildcard_mapping {
+       my ($forced, $ours, $ls) = @_;
+       my %refs;
+       for (@$ls) {
+               $refs{$_} = 01; # bit #0 to say "they have"
+       }
+       for ($git->command('for-each-ref', "refs/remotes/$ours")) {
+               chomp;
+               next unless (s|^[0-9a-f]{40}\s[a-z]+\srefs/remotes/$ours/||);
+               next if ($_ eq 'HEAD');
+               $refs{$_} ||= 0;
+               $refs{$_} |= 02; # bit #1 to say "we have"
+       }
+       my (@new, @stale, @tracked);
+       for (sort keys %refs) {
+               my $have = $refs{$_};
+               if ($have == 1) {
+                       push @new, $_;
+               }
+               elsif ($have == 2) {
+                       push @stale, $_;
+               }
+               elsif ($have == 3) {
+                       push @tracked, $_;
+               }
+       }
+       if (@new) {
+               print "  New remote branches (next fetch will store in remotes/$ours)\n";
+               print "    @new\n";
+       }
+       if (@stale) {
+               print "  Stale tracking branches in remotes/$ours (you'd better remove them)\n";
+               print "    @stale\n";
+       }
+       if (@tracked) {
+               print "  Tracked remote branches\n";
+               print "    @tracked\n";
+       }
+}
+
+sub show_mapping {
+       my ($name, $info) = @_;
+       my $fetch = $info->{'FETCH'};
+       my $ls = $info->{'LS_REMOTE'};
+       my (@stale, @tracked);
+
+       for (@$fetch) {
+               next unless (/(\+)?([^:]+):(.*)/);
+               my ($forced, $theirs, $ours) = ($1, $2, $3);
+               if ($theirs eq 'refs/heads/*' &&
+                   $ours =~ /^refs\/remotes\/(.*)\/\*$/) {
+                       # wildcard mapping
+                       show_wildcard_mapping($forced, $1, $ls);
+               }
+               elsif ($theirs =~ /\*/ || $ours =~ /\*/) {
+                       print STDERR "Warning: unrecognized mapping in remotes.$name.fetch: $_\n";
+               }
+               elsif ($theirs =~ s|^refs/heads/||) {
+                       if (!grep { $_ eq $theirs } @$ls) {
+                               push @stale, $theirs;
+                       }
+                       elsif ($ours ne '') {
+                               push @tracked, $theirs;
+                       }
+               }
+       }
+       if (@stale) {
+               print "  Stale tracking branches in remotes/$name (you'd better remove them)\n";
+               print "    @stale\n";
+       }
+       if (@tracked) {
+               print "  Tracked remote branches\n";
+               print "    @tracked\n";
+       }
+}
+
+sub show_remote {
+       my ($name, $ls_remote) = @_;
+       if (!exists $remote->{$name}) {
+               print STDERR "No such remote $name\n";
+               return;
+       }
+       my $info = $remote->{$name};
+       update_ls_remote($ls_remote, $info);
+
+       print "* remote $name\n";
+       print "  URL: $info->{'URL'}\n";
+       for my $branchname (sort keys %$branch) {
+               next if ($branch->{$branchname}{'REMOTE'} ne $name);
+               my @merged = map {
+                       s|^refs/heads/||;
+                       $_;
+               } split(' ',"@{$branch->{$branchname}{'MERGE'}}");
+               next unless (@merged);
+               print "  Remote branch(es) merged with 'git pull' while on branch $branchname\n";
+               print "    @merged\n";
+       }
+       if ($info->{'LS_REMOTE'}) {
+               show_mapping($name, $info);
+       }
+}
+
+sub add_remote {
+       my ($name, $url) = @_;
+       if (exists $remote->{$name}) {
+               print STDERR "remote $name already exists.\n";
+               exit(1);
+       }
+       $git->command('repo-config', "remote.$name.url", $url);
+       $git->command('repo-config', "remote.$name.fetch",
+                     "+refs/heads/*:refs/remotes/$name/*");
+}
+
+if (!@ARGV) {
+       for (sort keys %$remote) {
+               print "$_\n";
+       }
+}
+elsif ($ARGV[0] eq 'show') {
+       my $ls_remote = 1;
+       my $i;
+       for ($i = 1; $i < @ARGV; $i++) {
+               if ($ARGV[$i] eq '-n') {
+                       $ls_remote = 0;
+               }
+               else {
+                       last;
+               }
+       }
+       if ($i >= @ARGV) {
+               print STDERR "Usage: git remote show <remote>\n";
+               exit(1);
+       }
+       for (; $i < @ARGV; $i++) {
+               show_remote($ARGV[$i], $ls_remote);
+       }
+}
+elsif ($ARGV[0] eq 'add') {
+       if (@ARGV != 3) {
+               print STDERR "Usage: git remote add <name> <url>\n";
+               exit(1);
+       }
+       add_remote($ARGV[1], $ARGV[2]);
+}
+else {
+       print STDERR "Usage: git remote\n";
+       print STDERR "       git remote add <name> <url>\n";
+       print STDERR "       git remote show <name>\n";
+       exit(1);
+}
index 375434b1dcdd3183c85b1278c4415e9136508192..da8e67f7a558229aa09216e7eb74713ca6c970cd 100755 (executable)
@@ -110,7 +110,7 @@ then
                  done
                )
        fi
-       git-prune-packed
+       git-prune-packed $quiet
 fi
 
 case "$no_update_info" in
diff --git a/git-rerere.perl b/git-rerere.perl
deleted file mode 100755 (executable)
index 4f69209..0000000
+++ /dev/null
@@ -1,284 +0,0 @@
-#!/usr/bin/perl
-#
-# REuse REcorded REsolve.  This tool records a conflicted automerge
-# result and its hand resolution, and helps to resolve future
-# automerge that results in the same conflict.
-#
-# To enable this feature, create a directory 'rr-cache' under your
-# .git/ directory.
-
-use Digest;
-use File::Path;
-use File::Copy;
-
-my $git_dir = $::ENV{GIT_DIR} || ".git";
-my $rr_dir = "$git_dir/rr-cache";
-my $merge_rr = "$git_dir/rr-cache/MERGE_RR";
-
-my %merge_rr = ();
-
-sub read_rr {
-       if (!-f $merge_rr) {
-               %merge_rr = ();
-               return;
-       }
-       my $in;
-       local $/ = "\0";
-       open $in, "<$merge_rr" or die "$!: $merge_rr";
-       while (<$in>) {
-               chomp;
-               my ($name, $path) = /^([0-9a-f]{40})\t(.*)$/s;
-               $merge_rr{$path} = $name;
-       }
-       close $in;
-}
-
-sub write_rr {
-       my $out;
-       open $out, ">$merge_rr" or die "$!: $merge_rr";
-       for my $path (sort keys %merge_rr) {
-               my $name = $merge_rr{$path};
-               print $out "$name\t$path\0";
-       }
-       close $out;
-}
-
-sub compute_conflict_name {
-       my ($path) = @_;
-       my @side = ();
-       my $in;
-       open $in, "<$path"  or die "$!: $path";
-
-       my $sha1 = Digest->new("SHA-1");
-       my $hunk = 0;
-       while (<$in>) {
-               if (/^<<<<<<< .*/) {
-                       $hunk++;
-                       @side = ([], undef);
-               }
-               elsif (/^=======$/) {
-                       $side[1] = [];
-               }
-               elsif (/^>>>>>>> .*/) {
-                       my ($one, $two);
-                       $one = join('', @{$side[0]});
-                       $two = join('', @{$side[1]});
-                       if ($two le $one) {
-                               ($one, $two) = ($two, $one);
-                       }
-                       $sha1->add($one);
-                       $sha1->add("\0");
-                       $sha1->add($two);
-                       $sha1->add("\0");
-                       @side = ();
-               }
-               elsif (@side == 0) {
-                       next;
-               }
-               elsif (defined $side[1]) {
-                       push @{$side[1]}, $_;
-               }
-               else {
-                       push @{$side[0]}, $_;
-               }
-       }
-       close $in;
-       return ($sha1->hexdigest, $hunk);
-}
-
-sub record_preimage {
-       my ($path, $name) = @_;
-       my @side = ();
-       my ($in, $out);
-       open $in, "<$path"  or die "$!: $path";
-       open $out, ">$name" or die "$!: $name";
-
-       while (<$in>) {
-               if (/^<<<<<<< .*/) {
-                       @side = ([], undef);
-               }
-               elsif (/^=======$/) {
-                       $side[1] = [];
-               }
-               elsif (/^>>>>>>> .*/) {
-                       my ($one, $two);
-                       $one = join('', @{$side[0]});
-                       $two = join('', @{$side[1]});
-                       if ($two le $one) {
-                               ($one, $two) = ($two, $one);
-                       }
-                       print $out "<<<<<<<\n";
-                       print $out $one;
-                       print $out "=======\n";
-                       print $out $two;
-                       print $out ">>>>>>>\n";
-                       @side = ();
-               }
-               elsif (@side == 0) {
-                       print $out $_;
-               }
-               elsif (defined $side[1]) {
-                       push @{$side[1]}, $_;
-               }
-               else {
-                       push @{$side[0]}, $_;
-               }
-       }
-       close $out;
-       close $in;
-}
-
-sub find_conflict {
-       my $in;
-       local $/ = "\0";
-       my $pid = open($in, '-|');
-       die "$!" unless defined $pid;
-       if (!$pid) {
-               exec(qw(git ls-files -z -u)) or die "$!: ls-files";
-       }
-       my %path = ();
-       my @path = ();
-       while (<$in>) {
-               chomp;
-               my ($mode, $sha1, $stage, $path) =
-                   /^([0-7]+) ([0-9a-f]{40}) ([123])\t(.*)$/s;
-               $path{$path} |= (1 << $stage);
-       }
-       close $in;
-       while (my ($path, $status) = each %path) {
-               if ($status == 14) { push @path, $path; }
-       }
-       return @path;
-}
-
-sub merge {
-       my ($name, $path) = @_;
-       record_preimage($path, "$rr_dir/$name/thisimage");
-       unless (system('git', 'merge-file', map { "$rr_dir/$name/${_}image" }
-                      qw(this pre post))) {
-               my $in;
-               open $in, "<$rr_dir/$name/thisimage" or
-                   die "$!: $name/thisimage";
-               my $out;
-               open $out, ">$path" or die "$!: $path";
-               while (<$in>) { print $out $_; }
-               close $in;
-               close $out;
-               return 1;
-       }
-       return 0;
-}
-
-sub garbage_collect_rerere {
-       # We should allow specifying these from the command line and
-       # that is why the caller gives @ARGV to us, but I am lazy.
-
-       my $cutoff_noresolve = 15; # two weeks
-       my $cutoff_resolve = 60; # two months
-       my @to_remove;
-       while (<$rr_dir/*/preimage>) {
-               my ($dir) = /^(.*)\/preimage$/;
-               my $cutoff = ((-f "$dir/postimage")
-                             ? $cutoff_resolve
-                             : $cutoff_noresolve);
-               my $age = -M "$_";
-               if ($cutoff <= $age) {
-                       push @to_remove, $dir;
-               }
-       }
-       if (@to_remove) {
-               rmtree(\@to_remove);
-       }
-}
-
--d "$rr_dir" || exit(0);
-
-read_rr();
-
-if (@ARGV) {
-       my $arg = shift @ARGV;
-       if ($arg eq 'clear') {
-               for my $path (keys %merge_rr) {
-                       my $name = $merge_rr{$path};
-                       if (-d "$rr_dir/$name" &&
-                           ! -f "$rr_dir/$name/postimage") {
-                               rmtree(["$rr_dir/$name"]);
-                       }
-               }
-               unlink $merge_rr;
-       }
-       elsif ($arg eq 'status') {
-               for my $path (keys %merge_rr) {
-                       print $path, "\n";
-               }
-       }
-       elsif ($arg eq 'diff') {
-               for my $path (keys %merge_rr) {
-                       my $name = $merge_rr{$path};
-                       system('diff', ((@ARGV == 0) ? ('-u') : @ARGV),
-                               '-L', "a/$path", '-L', "b/$path",
-                               "$rr_dir/$name/preimage", $path);
-               }
-       }
-       elsif ($arg eq 'gc') {
-               garbage_collect_rerere(@ARGV);
-       }
-       else {
-               die "$0 unknown command: $arg\n";
-       }
-       exit 0;
-}
-
-my %conflict = map { $_ => 1 } find_conflict();
-
-# MERGE_RR records paths with conflicts immediately after merge
-# failed.  Some of the conflicted paths might have been hand resolved
-# in the working tree since then, but the initial run would catch all
-# and register their preimages.
-
-for my $path (keys %conflict) {
-       # This path has conflict.  If it is not recorded yet,
-       # record the pre-image.
-       if (!exists $merge_rr{$path}) {
-               my ($name, $hunk) = compute_conflict_name($path);
-               next unless ($hunk);
-               $merge_rr{$path} = $name;
-               if (! -d "$rr_dir/$name") {
-                       mkpath("$rr_dir/$name", 0, 0777);
-                       print STDERR "Recorded preimage for '$path'\n";
-                       record_preimage($path, "$rr_dir/$name/preimage");
-               }
-       }
-}
-
-# Now some of the paths that had conflicts earlier might have been
-# hand resolved.  Others may be similar to a conflict already that
-# was resolved before.
-
-for my $path (keys %merge_rr) {
-       my $name = $merge_rr{$path};
-
-       # We could resolve this automatically if we have images.
-       if (-f "$rr_dir/$name/preimage" &&
-           -f "$rr_dir/$name/postimage") {
-               if (merge($name, $path)) {
-                       print STDERR "Resolved '$path' using previous resolution.\n";
-                       # Then we do not have to worry about this path
-                       # anymore.
-                       delete $merge_rr{$path};
-                       next;
-               }
-       }
-
-       # Let's see if we have resolved it.
-       (undef, my $hunk) = compute_conflict_name($path);
-       next if ($hunk);
-
-       print STDERR "Recorded resolution for '$path'.\n";
-       copy($path, "$rr_dir/$name/postimage");
-       # And we do not have to worry about this path anymore.
-       delete $merge_rr{$path};
-}
-
-# Write out the rest.
-write_rr();
index a9693701a34dac623fcddb1943ffe326bdb05405..91c7e6e664eeac0a130044d3d026ccbbfa00f3b3 100755 (executable)
@@ -6,6 +6,7 @@ USAGE='[--mixed | --soft | --hard]  [<commit-ish>] [ [--] <paths>...]'
 SUBDIRECTORY_OK=Yes
 . git-sh-setup
 set_reflog_action "reset $*"
+require_work_tree
 
 update= reset_type=--mixed
 unset rev
@@ -44,17 +45,15 @@ if test $# != 0
 then
        test "$reset_type" == "--mixed" ||
                die "Cannot do partial $reset_type reset."
-       git ls-tree -r --full-name $rev -- "$@" |
-       git update-index --add --index-info || exit
+
+       git-diff-index --cached $rev -- "$@" |
+       sed -e 's/^:\([0-7][0-7]*\) [0-7][0-7]* \([0-9a-f][0-9a-f]*\) [0-9a-f][0-9a-f]* [A-Z]   \(.*\)$/\1 \2   \3/' |
+       git update-index --add --remove --index-info || exit
        git update-index --refresh
        exit
 fi
 
-TOP=$(git-rev-parse --show-cdup)
-if test ! -z "$TOP"
-then
-       cd "$TOP"
-fi
+cd_to_toplevel
 
 if test "$reset_type" = "--hard"
 then
index 50cc47b0634201e7acaaa0a0642be779aaf7c2da..71cbcbc2b886b2770c61c4b3a496db913ef56d9d 100755 (executable)
@@ -16,9 +16,14 @@ case "$0" in
        me=cherry-pick
        USAGE='[--edit] [-n] [-r] [-x] <commit-ish>'  ;;
 * )
-       die "What are you talking about?" ;;
+       echo >&2 "What are you talking about?"
+       exit 1 ;;
 esac
+
+SUBDIRECTORY_OK=Yes ;# we will cd up
 . git-sh-setup
+require_work_tree
+cd_to_toplevel
 
 no_commit=
 while case "$#" in 0) break ;; esac
@@ -76,6 +81,8 @@ prev=$(git-rev-parse --verify "$commit^1" 2>/dev/null) ||
 git-rev-parse --verify "$commit^2" >/dev/null 2>&1 &&
        die "Cannot run $me a multi-parent commit."
 
+encoding=$(git repo-config i18n.commitencoding || echo UTF-8)
+
 # "commit" is an existing commit.  We would want to apply
 # the difference it introduces since its first parent "prev"
 # on top of the current HEAD if we are cherry-pick.  Or the
@@ -83,10 +90,11 @@ git-rev-parse --verify "$commit^2" >/dev/null 2>&1 &&
 
 case "$me" in
 revert)
-       git-rev-list --pretty=oneline --max-count=1 $commit |
+       git show -s --pretty=oneline --encoding="$encoding" $commit |
        sed -e '
                s/^[^ ]* /Revert "/
-               s/$/"/'
+               s/$/"/
+       '
        echo
        echo "This reverts commit $commit."
        test "$rev" = "$commit" ||
@@ -115,14 +123,17 @@ cherry-pick)
 
                q
        }'
-       set_author_env=`git-cat-file commit "$commit" |
+
+       logmsg=`git show -s --pretty=raw --encoding="$encoding" "$commit"`
+       set_author_env=`echo "$logmsg" |
        LANG=C LC_ALL=C sed -ne "$pick_author_script"`
        eval "$set_author_env"
        export GIT_AUTHOR_NAME
        export GIT_AUTHOR_EMAIL
        export GIT_AUTHOR_DATE
 
-       git-cat-file commit $commit | sed -e '1,/^$/d'
+       echo "$logmsg" |
+       sed -e '1,/^$/d' -e 's/^    //'
        case "$replay" in
        '')
                echo "(cherry picked from commit $commit)"
index ba39d393843733b46f309deb105c90cabcee2cb1..8dc2ee0cf7461ace62c5ef7afac6dedc7814ddfb 100755 (executable)
@@ -402,6 +402,15 @@ sub make_message_id
 $cc = "";
 $time = time - scalar $#files;
 
+sub unquote_rfc2047 {
+       local ($_) = @_;
+       if (s/=\?utf-8\?q\?(.*)\?=/$1/g) {
+               s/_/ /g;
+               s/=([0-9A-F]{2})/chr(hex($1))/eg;
+       }
+       return "$_ - unquoted";
+}
+
 sub send_message
 {
        my @recipients = unique_email_list(@to);
@@ -555,6 +564,7 @@ sub send_message
        }
        close F;
        if (defined $author_not_sender) {
+               $author_not_sender = unquote_rfc2047($author_not_sender);
                $message = "From: $author_not_sender\n\n$message";
        }
 
index 87b939c0e4857ec9c9245c01f609a059788f656a..6b1c1423ebb7fde53aae566fa09211d331c422bc 100755 (executable)
@@ -28,6 +28,30 @@ set_reflog_action() {
        fi
 }
 
+is_bare_repository () {
+       git-repo-config --bool --get core.bare ||
+       case "$GIT_DIR" in
+       .git | */.git) echo false ;;
+       *) echo true ;;
+       esac
+}
+
+cd_to_toplevel () {
+       cdup=$(git-rev-parse --show-cdup)
+       if test ! -z "$cdup"
+       then
+               cd "$cdup" || {
+                       echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree"
+                       exit 1
+               }
+       fi
+}
+
+require_work_tree () {
+       test $(is_bare_repository) = false ||
+       die "fatal: $0 cannot be used without a working tree."
+}
+
 if [ -z "$LONG_USAGE" ]
 then
        LONG_USAGE="Usage: $0 $USAGE"
@@ -47,7 +71,11 @@ esac
 if [ -z "$SUBDIRECTORY_OK" ]
 then
        : ${GIT_DIR=.git}
-       GIT_DIR=$(GIT_DIR="$GIT_DIR" git-rev-parse --git-dir) || exit
+       GIT_DIR=$(GIT_DIR="$GIT_DIR" git-rev-parse --git-dir) || {
+               exit=$?
+               echo >&2 "You need to run this command from the toplevel of the working tree."
+               exit $exit
+       }
 else
        GIT_DIR=$(git-rev-parse --git-dir) || exit
 fi
index 1da31fdc7cf9c90e6ec3dfd815201e36b9650890..9986a0c9bb29fe348050775ba06fd93c89bb11b5 100755 (executable)
@@ -70,7 +70,7 @@
        $_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m,
        $_merge, $_strategy, $_dry_run, $_ignore_nodate, $_non_recursive,
        $_username, $_config_dir, $_no_auth_cache,
-       $_pager, $_color);
+       $_pager, $_color, $_prefix);
 my (@_branch_from, %tree_map, %users, %rusers, %equiv);
 my ($_svn_can_do_switch);
 my @repo_path_split_cache;
                         'username=s' => \$_username,
                         'config-dir=s' => \$_config_dir,
                         'no-auth-cache' => \$_no_auth_cache,
+                        'prefix=s' => \$_prefix,
                        } ],
        'multi-fetch' => [ \&multi_fetch,
                        'Fetch multiple trees (like git-svnimport)',
@@ -285,7 +286,7 @@ sub init {
 
        $SVN_URL = $url;
        unless (-d $GIT_DIR) {
-               my @init_db = ('init-db');
+               my @init_db = ('init');
                push @init_db, "--template=$_template" if defined $_template;
                push @init_db, "--shared" if defined $_shared;
                command_noisy(@init_db);
@@ -595,8 +596,9 @@ sub multi_init {
                        command_noisy('repo-config', 'svn.trunk', $trunk_url);
                }
        }
-       complete_url_ls_init($url, $_branches, '--branches/-b', '');
-       complete_url_ls_init($url, $_tags, '--tags/-t', 'tags/');
+       $_prefix = '' unless defined $_prefix;
+       complete_url_ls_init($url, $_branches, '--branches/-b', $_prefix);
+       complete_url_ls_init($url, $_tags, '--tags/-t', $_prefix . 'tags/');
 }
 
 sub multi_fetch {
@@ -1084,7 +1086,7 @@ sub graft_merge_msg {
        my ($grafts, $l_map, $u, $p, @re) = @_;
 
        my $x = $l_map->{$u}->{$p};
-       my $rl = rev_list_raw($x);
+       my $rl = rev_list_raw("refs/remotes/$x");
        while (my $c = next_rev_list_entry($rl)) {
                foreach my $re (@re) {
                        my (@br) = ($c->{m} =~ /$re/g);
index afbbe63c622c29e86e3a7ac971e6f88ed93dd6a2..3af8c7e1106d755b1589750ff5673ff6a6cd3b14 100755 (executable)
@@ -285,7 +285,7 @@ ($$)
 my $last_branch;
 my $current_rev = $opt_s || 1;
 unless(-d $git_dir) {
-       system("git-init-db");
+       system("git-init");
        die "Cannot init the GIT db at $git_tree: $?\n" if $?;
        system("git-read-tree");
        die "Cannot init an empty tree: $?\n" if $?;
@@ -943,10 +943,10 @@ sub commit_all {
 print "Processing from $current_rev to $opt_l ...\n" if $opt_v;
 
 my $from_rev;
-my $to_rev = $current_rev;
+my $to_rev = $current_rev - 1;
 
 while ($to_rev < $opt_l) {
-       $from_rev = $to_rev;
+       $from_rev = $to_rev + 1;
        $to_rev = $from_rev + $repack_after;
        $to_rev = $opt_l if $opt_l < $to_rev;
        print "Fetching from $from_rev to $to_rev ...\n" if $opt_v;
diff --git a/git.c b/git.c
index c82ca458e47186cd926dc83030cb7a6c8a697e8d..72a1486f30a93c6822611c834a391bfc351c398f 100644 (file)
--- a/git.c
+++ b/git.c
@@ -199,6 +199,11 @@ const char git_version_string[] = GIT_VERSION;
 
 #define RUN_SETUP      (1<<0)
 #define USE_PAGER      (1<<1)
+/*
+ * require working tree to be present -- anything uses this needs
+ * RUN_SETUP for reading from the configuration file.
+ */
+#define NOT_BARE       (1<<2)
 
 static void handle_internal_command(int argc, const char **argv, char **envp)
 {
@@ -208,7 +213,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                int (*fn)(int, const char **, const char *);
                int option;
        } commands[] = {
-               { "add", cmd_add, RUN_SETUP },
+               { "add", cmd_add, RUN_SETUP | NOT_BARE },
                { "annotate", cmd_annotate, },
                { "apply", cmd_apply },
                { "archive", cmd_archive },
@@ -220,6 +225,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "cherry", cmd_cherry, RUN_SETUP },
                { "commit-tree", cmd_commit_tree, RUN_SETUP },
                { "count-objects", cmd_count_objects, RUN_SETUP },
+               { "describe", cmd_describe, RUN_SETUP },
                { "diff", cmd_diff, RUN_SETUP | USE_PAGER },
                { "diff-files", cmd_diff_files, RUN_SETUP },
                { "diff-index", cmd_diff_index, RUN_SETUP },
@@ -231,6 +237,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "get-tar-commit-id", cmd_get_tar_commit_id },
                { "grep", cmd_grep, RUN_SETUP },
                { "help", cmd_help },
+               { "init", cmd_init_db },
                { "init-db", cmd_init_db },
                { "log", cmd_log, RUN_SETUP | USE_PAGER },
                { "ls-files", cmd_ls_files, RUN_SETUP },
@@ -238,7 +245,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "mailinfo", cmd_mailinfo },
                { "mailsplit", cmd_mailsplit },
                { "merge-file", cmd_merge_file },
-               { "mv", cmd_mv, RUN_SETUP },
+               { "mv", cmd_mv, RUN_SETUP | NOT_BARE },
                { "name-rev", cmd_name_rev, RUN_SETUP },
                { "pack-objects", cmd_pack_objects, RUN_SETUP },
                { "pickaxe", cmd_blame, RUN_SETUP | USE_PAGER },
@@ -251,8 +258,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "rerere", cmd_rerere, RUN_SETUP },
                { "rev-list", cmd_rev_list, RUN_SETUP },
                { "rev-parse", cmd_rev_parse, RUN_SETUP },
-               { "rm", cmd_rm, RUN_SETUP },
-               { "runstatus", cmd_runstatus, RUN_SETUP },
+               { "rm", cmd_rm, RUN_SETUP | NOT_BARE },
+               { "runstatus", cmd_runstatus, RUN_SETUP | NOT_BARE },
                { "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
                { "show-branch", cmd_show_branch, RUN_SETUP },
                { "show", cmd_show, RUN_SETUP | USE_PAGER },
@@ -289,6 +296,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                        prefix = setup_git_directory();
                if (p->option & USE_PAGER)
                        setup_pager();
+               if ((p->option & NOT_BARE) && is_bare_repository())
+                       die("%s cannot be used in a bare git directory", cmd);
                trace_argv_printf(argv, argc, "trace: built-in: git");
 
                exit(p->fn(argc, argv, prefix));
index f46a42296da0e66c0946c392c232058dcd09e34a..88af2e638061f54cfd7f6ffc6ae777ef2756c603 100755 (executable)
@@ -2239,7 +2239,7 @@ sub git_difftree_body {
                        }
                        print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
                                                     hash_base=>$hash, file_name=>$diff{'file'})},
-                                     "blob") . " | ";
+                                     "blob");
                        print "</td>\n";
 
                } elsif ($diff{'status'} eq "D") { # deleted
@@ -2412,7 +2412,6 @@ sub git_patchset_body {
 
                        push @diff_header, $patch_line;
                }
-               #last PATCH unless $patch_line;
                my $last_patch_line = $patch_line;
 
                # check if current patch belong to current raw line
@@ -2522,7 +2521,10 @@ sub git_patchset_body {
 
                # from-file/to-file diff header
                $patch_line = $last_patch_line;
-               last PATCH unless $patch_line;
+               if (! $patch_line) {
+                       print "</div>\n"; # class="patch"
+                       last PATCH;
+               }
                next PATCH if ($patch_line =~ m/^diff /);
                #assert($patch_line =~ m/^---/) if DEBUG;
                if ($from{'href'} && $patch_line =~ m!^--- "?a/!) {
@@ -2533,7 +2535,6 @@ sub git_patchset_body {
                print "<div class=\"diff from_file\">$patch_line</div>\n";
 
                $patch_line = <$fd>;
-               #last PATCH unless $patch_line;
                chomp $patch_line;
 
                #assert($patch_line =~ m/^+++/) if DEBUG;
index 396552da022a1dca9c43738be4d87c664f31a06c..67dfb0a0337b63bd4c1ef5b9d3228735bf66f1b3 100644 (file)
@@ -71,7 +71,7 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
        int posn = 0;
        struct object_request *obj_req = (struct object_request *)data;
        do {
-               ssize_t retval = write(obj_req->local,
+               ssize_t retval = xwrite(obj_req->local,
                                       (char *) ptr + posn, size - posn);
                if (retval < 0)
                        return posn;
@@ -175,7 +175,7 @@ static void start_object_request(struct object_request *obj_req)
        prevlocal = open(prevfile, O_RDONLY);
        if (prevlocal != -1) {
                do {
-                       prev_read = read(prevlocal, prev_buf, PREV_BUF_SIZE);
+                       prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
                        if (prev_read>0) {
                                if (fwrite_sha1_file(prev_buf,
                                                     1,
@@ -809,6 +809,7 @@ static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
                return error("Unable to start request");
        }
 
+       target->pack_size = ftell(packfile);
        fclose(packfile);
 
        ret = move_temp_to_file(tmpfile, filename);
index ecefdfd4f8c9c17282f5cec10640343359278028..0a15f5378288992826e7042a3b9dff92fef4c080 100644 (file)
@@ -195,7 +195,7 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
        int posn = 0;
        struct transfer_request *request = (struct transfer_request *)data;
        do {
-               ssize_t retval = write(request->local_fileno,
+               ssize_t retval = xwrite(request->local_fileno,
                                       (char *) ptr + posn, size - posn);
                if (retval < 0)
                        return posn;
@@ -288,7 +288,7 @@ static void start_fetch_loose(struct transfer_request *request)
        prevlocal = open(prevfile, O_RDONLY);
        if (prevlocal != -1) {
                do {
-                       prev_read = read(prevlocal, prev_buf, PREV_BUF_SIZE);
+                       prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
                        if (prev_read>0) {
                                if (fwrite_sha1_file(prev_buf,
                                                     1,
@@ -770,11 +770,14 @@ static void finish_request(struct transfer_request *request)
                                request->url, curl_errorstr);
                        remote->can_update_info_refs = 0;
                } else {
+                       off_t pack_size = ftell(request->local_stream);
+
                        fclose(request->local_stream);
                        request->local_stream = NULL;
                        if (!move_temp_to_file(request->tmpfile,
                                               request->filename)) {
                                target = (struct packed_git *)request->userData;
+                               target->pack_size = pack_size;
                                lst = &remote->packs;
                                while (*lst != target)
                                        lst = &((*lst)->next);
index ad91858bc43d046ea199f5a52c372b2a243c647f..3eaf025720c5432065b851ce98f861eee27eadee 100644 (file)
@@ -224,7 +224,7 @@ socket_perror( const char *func, Socket_t *sock, int ret )
 static int
 socket_read( Socket_t *sock, char *buf, int len )
 {
-       int n = read( sock->fd, buf, len );
+       int n = xread( sock->fd, buf, len );
        if (n <= 0) {
                socket_perror( "read", sock, n );
                close( sock->fd );
@@ -236,7 +236,7 @@ socket_read( Socket_t *sock, char *buf, int len )
 static int
 socket_write( Socket_t *sock, const char *buf, int len )
 {
-       int n = write( sock->fd, buf, len );
+       int n = write_in_full( sock->fd, buf, len );
        if (n != len) {
                socket_perror( "write", sock, n );
                close( sock->fd );
@@ -390,7 +390,7 @@ arc4_init( void )
                fprintf( stderr, "Fatal: no random number source available.\n" );
                exit( 3 );
        }
-       if (read( fd, dat, 128 ) != 128) {
+       if (read_in_full( fd, dat, 128 ) != 128) {
                fprintf( stderr, "Fatal: cannot read random number source.\n" );
                exit( 3 );
        }
index 5f6d128a836f8ed9447f81280ae679e19c9939ff..72e0962415d74c856917f6bb56e6fa2fea950c25 100644 (file)
@@ -638,7 +638,7 @@ static void readjust_pack_header_and_sha1(unsigned char *sha1)
        /* Rewrite pack header with updated object number */
        if (lseek(output_fd, 0, SEEK_SET) != 0)
                die("cannot seek back: %s", strerror(errno));
-       if (xread(output_fd, &hdr, sizeof(hdr)) != sizeof(hdr))
+       if (read_in_full(output_fd, &hdr, sizeof(hdr)) != sizeof(hdr))
                die("cannot read pack header back: %s", strerror(errno));
        hdr.hdr_entries = htonl(nr_objects);
        if (lseek(output_fd, 0, SEEK_SET) != 0)
@@ -814,7 +814,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                char buf[48];
                int len = snprintf(buf, sizeof(buf), "%s\t%s\n",
                                   report, sha1_to_hex(sha1));
-               xwrite(1, buf, len);
+               write_or_die(1, buf, len);
 
                /*
                 * Let's just mimic git-unpack-objects here and write
index 7b6875cce6c5a08db06e637abc48ce8f26216db4..cf99cb72dd48f65a8c3dd1606b77334f79afab8f 100644 (file)
@@ -184,7 +184,7 @@ int fetch_ref(char *ref, unsigned char *sha1)
                fprintf(stderr, "cannot open %s\n", filename);
                return -1;
        }
-       if (read(ifd, hex, 40) != 40 || get_sha1_hex(hex, sha1)) {
+       if (read_in_full(ifd, hex, 40) != 40 || get_sha1_hex(hex, sha1)) {
                close(ifd);
                fprintf(stderr, "cannot read from %s\n", filename);
                return -1;
index bac16f577c95937a76738dce5d659cff71bd03ab..b4acbb74080ad308625fcff09be0af51c2b4111d 100644 (file)
@@ -110,35 +110,6 @@ static void output_commit_title(struct commit *commit)
        }
 }
 
-static const char *current_index_file = NULL;
-static const char *original_index_file;
-static const char *temporary_index_file;
-static int cache_dirty = 0;
-
-static int flush_cache(void)
-{
-       /* flush temporary index */
-       struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
-       int fd = hold_lock_file_for_update(lock, current_index_file, 1);
-       if (write_cache(fd, active_cache, active_nr) ||
-                       close(fd) || commit_lock_file(lock))
-               die ("unable to write %s", current_index_file);
-       discard_cache();
-       cache_dirty = 0;
-       return 0;
-}
-
-static void setup_index(int temp)
-{
-       current_index_file = temp ? temporary_index_file: original_index_file;
-       if (cache_dirty) {
-               discard_cache();
-               cache_dirty = 0;
-       }
-       unlink(temporary_index_file);
-       discard_cache();
-}
-
 static struct cache_entry *make_cache_entry(unsigned int mode,
                const unsigned char *sha1, const char *path, int stage, int refresh)
 {
@@ -167,9 +138,6 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
                const char *path, int stage, int refresh, int options)
 {
        struct cache_entry *ce;
-       if (!cache_dirty)
-               read_cache_from(current_index_file);
-       cache_dirty++;
        ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh);
        if (!ce)
                return error("cache_addinfo failed: %s", strerror(cache_errno));
@@ -187,26 +155,6 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
  */
 static int index_only = 0;
 
-static int git_read_tree(struct tree *tree)
-{
-       int rc;
-       struct object_list *trees = NULL;
-       struct unpack_trees_options opts;
-
-       if (cache_dirty)
-               die("read-tree with dirty cache");
-
-       memset(&opts, 0, sizeof(opts));
-       object_list_append(&tree->object, &trees);
-       rc = unpack_trees(trees, &opts);
-       cache_tree_free(&active_cache_tree);
-
-       if (rc == 0)
-               cache_dirty = 1;
-
-       return rc;
-}
-
 static int git_merge_trees(int index_only,
                           struct tree *common,
                           struct tree *head,
@@ -216,11 +164,6 @@ static int git_merge_trees(int index_only,
        struct object_list *trees = NULL;
        struct unpack_trees_options opts;
 
-       if (!cache_dirty) {
-               read_cache_from(current_index_file);
-               cache_dirty = 1;
-       }
-
        memset(&opts, 0, sizeof(opts));
        if (index_only)
                opts.index_only = 1;
@@ -236,39 +179,37 @@ static int git_merge_trees(int index_only,
 
        rc = unpack_trees(trees, &opts);
        cache_tree_free(&active_cache_tree);
-
-       cache_dirty = 1;
-
        return rc;
 }
 
+static int unmerged_index(void)
+{
+       int i;
+       for (i = 0; i < active_nr; i++) {
+               struct cache_entry *ce = active_cache[i];
+               if (ce_stage(ce))
+                       return 1;
+       }
+       return 0;
+}
+
 static struct tree *git_write_tree(void)
 {
        struct tree *result = NULL;
 
-       if (cache_dirty) {
-               unsigned i;
-               for (i = 0; i < active_nr; i++) {
-                       struct cache_entry *ce = active_cache[i];
-                       if (ce_stage(ce))
-                               return NULL;
-               }
-       } else
-               read_cache_from(current_index_file);
+       if (unmerged_index())
+               return NULL;
 
        if (!active_cache_tree)
                active_cache_tree = cache_tree();
 
        if (!cache_tree_fully_valid(active_cache_tree) &&
-                       cache_tree_update(active_cache_tree,
-                               active_cache, active_nr, 0, 0) < 0)
+           cache_tree_update(active_cache_tree,
+                             active_cache, active_nr, 0, 0) < 0)
                die("error building trees");
 
        result = lookup_tree(active_cache_tree->sha1);
 
-       flush_cache();
-       cache_dirty = 0;
-
        return result;
 }
 
@@ -331,10 +272,7 @@ static struct path_list *get_unmerged(void)
        int i;
 
        unmerged->strdup_paths = 1;
-       if (!cache_dirty) {
-               read_cache_from(current_index_file);
-               cache_dirty++;
-       }
+
        for (i = 0; i < active_nr; i++) {
                struct path_list_item *item;
                struct stage_data *e;
@@ -469,9 +407,6 @@ static int remove_file(int clean, const char *path, int no_wd)
        int update_working_directory = !index_only && !no_wd;
 
        if (update_cache) {
-               if (!cache_dirty)
-                       read_cache_from(current_index_file);
-               cache_dirty++;
                if (remove_file_from_cache(path))
                        return -1;
        }
@@ -517,7 +452,7 @@ static int mkdir_p(const char *path, unsigned long mode)
 static void flush_buffer(int fd, const char *buf, unsigned long size)
 {
        while (size > 0) {
-               long ret = xwrite(fd, buf, size);
+               long ret = write_in_full(fd, buf, size);
                if (ret < 0) {
                        /* Ignore epipe */
                        if (errno == EPIPE)
@@ -954,8 +889,6 @@ static int process_renames(struct path_list *a_renames,
        path_list_clear(&a_by_dst, 0);
        path_list_clear(&b_by_dst, 0);
 
-       if (cache_dirty)
-               flush_cache();
        return clean_merge;
 }
 
@@ -1083,9 +1016,6 @@ static int process_entry(const char *path, struct stage_data *entry,
        } else
                die("Fatal merge failure, shouldn't happen.");
 
-       if (cache_dirty)
-               flush_cache();
-
        return clean_merge;
 }
 
@@ -1110,9 +1040,7 @@ static int merge_trees(struct tree *head,
                    sha1_to_hex(head->object.sha1),
                    sha1_to_hex(merge->object.sha1));
 
-       *result = git_write_tree();
-
-       if (!*result) {
+       if (unmerged_index()) {
                struct path_list *entries, *re_head, *re_merge;
                int i;
                path_list_clear(&current_file_set, 1);
@@ -1138,17 +1066,12 @@ static int merge_trees(struct tree *head,
                path_list_clear(re_head, 0);
                path_list_clear(entries, 1);
 
-               if (clean || index_only)
-                       *result = git_write_tree();
-               else
-                       *result = NULL;
-       } else {
-               clean = 1;
-               printf("merging of trees %s and %s resulted in %s\n",
-                      sha1_to_hex(head->object.sha1),
-                      sha1_to_hex(merge->object.sha1),
-                      sha1_to_hex((*result)->object.sha1));
        }
+       else
+               clean = 1;
+
+       if (index_only)
+               *result = git_write_tree();
 
        return clean;
 }
@@ -1173,10 +1096,10 @@ static int merge(struct commit *h1,
                 const char *branch1,
                 const char *branch2,
                 int call_depth /* =0 */,
-                struct commit *ancestor /* =None */,
+                struct commit_list *ca,
                 struct commit **result)
 {
-       struct commit_list *ca = NULL, *iter;
+       struct commit_list *iter;
        struct commit *merged_common_ancestors;
        struct tree *mrtree;
        int clean;
@@ -1185,10 +1108,10 @@ static int merge(struct commit *h1,
        output_commit_title(h1);
        output_commit_title(h2);
 
-       if (ancestor)
-               commit_list_insert(ancestor, &ca);
-       else
-               ca = reverse_commit_list(get_merge_bases(h1, h2, 1));
+       if (!ca) {
+               ca = get_merge_bases(h1, h2, 1);
+               ca = reverse_commit_list(ca);
+       }
 
        output("found %u common ancestor(s):", commit_list_count(ca));
        for (iter = ca; iter; iter = iter->next)
@@ -1214,6 +1137,7 @@ static int merge(struct commit *h1,
                 * merge_trees has always overwritten it: the commited
                 * "conflicts" were already resolved.
                 */
+               discard_cache();
                merge(merged_common_ancestors, iter->item,
                      "Temporary merge branch 1",
                      "Temporary merge branch 2",
@@ -1226,25 +1150,21 @@ static int merge(struct commit *h1,
                        die("merge returned no commit");
        }
 
+       discard_cache();
        if (call_depth == 0) {
-               setup_index(0 /* $GIT_DIR/index */);
+               read_cache();
                index_only = 0;
-       } else {
-               setup_index(1 /* temporary index */);
-               git_read_tree(h1->tree);
+       } else
                index_only = 1;
-       }
 
        clean = merge_trees(h1->tree, h2->tree, merged_common_ancestors->tree,
                            branch1, branch2, &mrtree);
 
-       if (!ancestor && (clean || index_only)) {
+       if (index_only) {
                *result = make_virtual_commit(mrtree, "merged tree");
                commit_list_insert(h1, &(*result)->parents);
                commit_list_insert(h2, &(*result)->parents->next);
-       } else
-               *result = NULL;
-
+       }
        return clean;
 }
 
@@ -1280,19 +1200,16 @@ static struct commit *get_ref(const char *ref)
 
 int main(int argc, char *argv[])
 {
-       static const char *bases[2];
+       static const char *bases[20];
        static unsigned bases_count = 0;
        int i, clean;
        const char *branch1, *branch2;
        struct commit *result, *h1, *h2;
+       struct commit_list *ca = NULL;
+       struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+       int index_fd;
 
        git_config(git_default_config); /* core.filemode */
-       original_index_file = getenv(INDEX_ENVIRONMENT);
-
-       if (!original_index_file)
-               original_index_file = xstrdup(git_path("index"));
-
-       temporary_index_file = xstrdup(git_path("mrg-rcrsv-tmp-idx"));
 
        if (argc < 4)
                die("Usage: %s <base>... -- <head> <remote> ...\n", argv[0]);
@@ -1316,18 +1233,18 @@ int main(int argc, char *argv[])
        branch2 = better_branch_name(branch2);
        printf("Merging %s with %s\n", branch1, branch2);
 
-       if (bases_count == 1) {
-               struct commit *ancestor = get_ref(bases[0]);
-               clean = merge(h1, h2, branch1, branch2, 0, ancestor, &result);
-       } else
-               clean = merge(h1, h2, branch1, branch2, 0, NULL, &result);
+       index_fd = hold_lock_file_for_update(lock, get_index_file(), 1);
 
-       if (cache_dirty)
-               flush_cache();
+       for (i = 0; i < bases_count; i++) {
+               struct commit *ancestor = get_ref(bases[i]);
+               ca = commit_list_insert(ancestor, &ca);
+       }
+       clean = merge(h1, h2, branch1, branch2, 0, ca, &result);
+
+       if (active_cache_changed &&
+           (write_cache(index_fd, active_cache, active_nr) ||
+            close(index_fd) || commit_lock_file(lock)))
+                       die ("unable to write %s", get_index_file());
 
        return clean ? 0: 1;
 }
-
-/*
-vim: sw=8 noet
-*/
index 8e123b71ed7898d2054ed9ef729c70ed40422bc3..08a9fd8dc09056c1b21cb7414f2467cffcdef077 100644 (file)
@@ -1,55 +1,45 @@
 #include "cache.h"
 #include "pack.h"
 
-#define BATCH (1u<<20)
-
-static int verify_packfile(struct packed_git *p)
+static int verify_packfile(struct packed_git *p,
+               struct pack_window **w_curs)
 {
        unsigned long index_size = p->index_size;
        void *index_base = p->index_base;
        SHA_CTX ctx;
        unsigned char sha1[20];
-       struct pack_header *hdr;
+       unsigned long offset = 0, pack_sig = p->pack_size - 20;
        int nr_objects, err, i;
-       unsigned char *packdata;
-       unsigned long datasize;
-
-       /* Header consistency check */
-       hdr = p->pack_base;
-       if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
-               return error("Packfile %s signature mismatch", p->pack_name);
-       if (!pack_version_ok(hdr->hdr_version))
-               return error("Packfile version %d unsupported",
-                            ntohl(hdr->hdr_version));
-       nr_objects = ntohl(hdr->hdr_entries);
-       if (num_packed_objects(p) != nr_objects)
-               return error("Packfile claims to have %d objects, "
-                            "while idx size expects %d", nr_objects,
-                            num_packed_objects(p));
-
-       /* Check integrity of pack data with its SHA-1 checksum */
+
+       /* Note that the pack header checks are actually performed by
+        * use_pack when it first opens the pack file.  If anything
+        * goes wrong during those checks then the call will die out
+        * immediately.
+        */
+
        SHA1_Init(&ctx);
-       packdata = p->pack_base;
-       datasize = p->pack_size - 20;
-       while (datasize) {
-               unsigned long batch = (datasize < BATCH) ? datasize : BATCH;
-               SHA1_Update(&ctx, packdata, batch);
-               datasize -= batch;
-               packdata += batch;
+       while (offset < pack_sig) {
+               unsigned int remaining;
+               unsigned char *in = use_pack(p, w_curs, offset, &remaining);
+               offset += remaining;
+               if (offset > pack_sig)
+                       remaining -= offset - pack_sig;
+               SHA1_Update(&ctx, in, remaining);
        }
        SHA1_Final(sha1, &ctx);
-
-       if (hashcmp(sha1, (unsigned char *)(p->pack_base) + p->pack_size - 20))
+       if (hashcmp(sha1, use_pack(p, w_curs, pack_sig, NULL)))
                return error("Packfile %s SHA1 mismatch with itself",
                             p->pack_name);
        if (hashcmp(sha1, (unsigned char *)index_base + index_size - 40))
                return error("Packfile %s SHA1 mismatch with idx",
                             p->pack_name);
+       unuse_pack(w_curs);
 
        /* Make sure everything reachable from idx is valid.  Since we
         * have verified that nr_objects matches between idx and pack,
         * we do not do scan-streaming check on the pack file.
         */
+       nr_objects = num_packed_objects(p);
        for (i = err = 0; i < nr_objects; i++) {
                unsigned char sha1[20];
                void *data;
@@ -61,7 +51,7 @@ static int verify_packfile(struct packed_git *p)
                offset = find_pack_entry_one(sha1, p);
                if (!offset)
                        die("internal error pack-check find-pack-entry-one");
-               data = unpack_entry_gently(p, offset, type, &size);
+               data = unpack_entry(p, offset, type, &size);
                if (!data) {
                        err = error("cannot unpack %s from %s",
                                    sha1_to_hex(sha1), p->pack_name);
@@ -84,12 +74,10 @@ static int verify_packfile(struct packed_git *p)
 
 static void show_pack_info(struct packed_git *p)
 {
-       struct pack_header *hdr;
        int nr_objects, i;
        unsigned int chain_histogram[MAX_CHAIN];
 
-       hdr = p->pack_base;
-       nr_objects = ntohl(hdr->hdr_entries);
+       nr_objects = num_packed_objects(p);
        memset(chain_histogram, 0, sizeof(chain_histogram));
 
        for (i = 0; i < nr_objects; i++) {
@@ -152,18 +140,16 @@ int verify_pack(struct packed_git *p, int verbose)
 
        if (!ret) {
                /* Verify pack file */
-               use_packed_git(p);
-               ret = verify_packfile(p);
-               unuse_packed_git(p);
+               struct pack_window *w_curs = NULL;
+               ret = verify_packfile(p, &w_curs);
+               unuse_pack(&w_curs);
        }
 
        if (verbose) {
                if (ret)
                        printf("%s: bad\n", p->pack_name);
                else {
-                       use_packed_git(p);
                        show_pack_info(p);
-                       unuse_packed_git(p);
                        printf("%s: ok\n", p->pack_name);
                }
        }
diff --git a/path.c b/path.c
index 066f62195508033a5f72504e4805ea436424296e..c5d25a4b903bd92930df4004d30d4dffa6054456 100644 (file)
--- a/path.c
+++ b/path.c
@@ -90,10 +90,11 @@ int git_mkstemp(char *path, size_t len, const char *template)
 }
 
 
-int validate_symref(const char *path)
+int validate_headref(const char *path)
 {
        struct stat st;
        char *buf, buffer[256];
+       unsigned char sha1[20];
        int len, fd;
 
        if (lstat(path, &st) < 0)
@@ -113,20 +114,29 @@ int validate_symref(const char *path)
        fd = open(path, O_RDONLY);
        if (fd < 0)
                return -1;
-       len = read(fd, buffer, sizeof(buffer)-1);
+       len = read_in_full(fd, buffer, sizeof(buffer)-1);
        close(fd);
 
        /*
         * Is it a symbolic ref?
         */
-       if (len < 4 || memcmp("ref:", buffer, 4))
+       if (len < 4)
                return -1;
-       buf = buffer + 4;
-       len -= 4;
-       while (len && isspace(*buf))
-               buf++, len--;
-       if (len >= 5 && !memcmp("refs/", buf, 5))
+       if (!memcmp("ref:", buffer, 4)) {
+               buf = buffer + 4;
+               len -= 4;
+               while (len && isspace(*buf))
+                       buf++, len--;
+               if (len >= 5 && !memcmp("refs/", buf, 5))
+                       return 0;
+       }
+
+       /*
+        * Is this a detached HEAD?
+        */
+       if (!get_sha1_hex(buffer, sha1))
                return 0;
+
        return -1;
 }
 
@@ -241,7 +251,7 @@ char *enter_repo(char *path, int strict)
                return NULL;
 
        if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
-           validate_symref("HEAD") == 0) {
+           validate_headref("HEAD") == 0) {
                putenv("GIT_DIR=.");
                check_repository_format();
                return path;
index 2b26b65bfb00c60535919d7b9359a5549f9e9709..3474ad320f6c90a67eb4a5973092f4210d173e9d 100644 (file)
@@ -63,7 +63,7 @@ =head1 DESCRIPTION
 the generic command interface.
 
 While some commands can be executed outside of any context (e.g. 'version'
-or 'init-db'), most operations require a repository context, which in practice
+or 'init'), most operations require a repository context, which in practice
 means getting an instance of the Git object using the repository() constructor.
 (In the future, we will also get a new_repository() constructor.) All commands
 called as methods of the object are then executed in the context of the
diff --git a/reachable.c b/reachable.c
new file mode 100644 (file)
index 0000000..a6a3348
--- /dev/null
@@ -0,0 +1,201 @@
+#include "cache.h"
+#include "refs.h"
+#include "tag.h"
+#include "commit.h"
+#include "blob.h"
+#include "diff.h"
+#include "revision.h"
+#include "reachable.h"
+#include "cache-tree.h"
+
+static void process_blob(struct blob *blob,
+                        struct object_array *p,
+                        struct name_path *path,
+                        const char *name)
+{
+       struct object *obj = &blob->object;
+
+       if (obj->flags & SEEN)
+               return;
+       obj->flags |= SEEN;
+       /* Nothing to do, really .. The blob lookup was the important part */
+}
+
+static void process_tree(struct tree *tree,
+                        struct object_array *p,
+                        struct name_path *path,
+                        const char *name)
+{
+       struct object *obj = &tree->object;
+       struct tree_desc desc;
+       struct name_entry entry;
+       struct name_path me;
+
+       if (obj->flags & SEEN)
+               return;
+       obj->flags |= SEEN;
+       if (parse_tree(tree) < 0)
+               die("bad tree object %s", sha1_to_hex(obj->sha1));
+       name = xstrdup(name);
+       add_object(obj, p, path, name);
+       me.up = path;
+       me.elem = name;
+       me.elem_len = strlen(name);
+
+       desc.buf = tree->buffer;
+       desc.size = tree->size;
+
+       while (tree_entry(&desc, &entry)) {
+               if (S_ISDIR(entry.mode))
+                       process_tree(lookup_tree(entry.sha1), p, &me, entry.path);
+               else
+                       process_blob(lookup_blob(entry.sha1), p, &me, entry.path);
+       }
+       free(tree->buffer);
+       tree->buffer = NULL;
+}
+
+static void process_tag(struct tag *tag, struct object_array *p, const char *name)
+{
+       struct object *obj = &tag->object;
+       struct name_path me;
+
+       if (obj->flags & SEEN)
+               return;
+       obj->flags |= SEEN;
+
+       me.up = NULL;
+       me.elem = "tag:/";
+       me.elem_len = 5;
+
+       if (parse_tag(tag) < 0)
+               die("bad tag object %s", sha1_to_hex(obj->sha1));
+       add_object(tag->tagged, p, NULL, name);
+}
+
+static void walk_commit_list(struct rev_info *revs)
+{
+       int i;
+       struct commit *commit;
+       struct object_array objects = { 0, 0, NULL };
+
+       /* Walk all commits, process their trees */
+       while ((commit = get_revision(revs)) != NULL)
+               process_tree(commit->tree, &objects, NULL, "");
+
+       /* Then walk all the pending objects, recursively processing them too */
+       for (i = 0; i < revs->pending.nr; i++) {
+               struct object_array_entry *pending = revs->pending.objects + i;
+               struct object *obj = pending->item;
+               const char *name = pending->name;
+               if (obj->type == OBJ_TAG) {
+                       process_tag((struct tag *) obj, &objects, name);
+                       continue;
+               }
+               if (obj->type == OBJ_TREE) {
+                       process_tree((struct tree *)obj, &objects, NULL, name);
+                       continue;
+               }
+               if (obj->type == OBJ_BLOB) {
+                       process_blob((struct blob *)obj, &objects, NULL, name);
+                       continue;
+               }
+               die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
+       }
+}
+
+static int add_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+               const char *email, unsigned long timestamp, int tz,
+               const char *message, void *cb_data)
+{
+       struct object *object;
+       struct rev_info *revs = (struct rev_info *)cb_data;
+
+       object = parse_object(osha1);
+       if (object)
+               add_pending_object(revs, object, "");
+       object = parse_object(nsha1);
+       if (object)
+               add_pending_object(revs, object, "");
+       return 0;
+}
+
+static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+{
+       struct object *object = parse_object(sha1);
+       struct rev_info *revs = (struct rev_info *)cb_data;
+
+       if (!object)
+               die("bad object ref: %s:%s", path, sha1_to_hex(sha1));
+       add_pending_object(revs, object, "");
+
+       return 0;
+}
+
+static int add_one_reflog(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+{
+       for_each_reflog_ent(path, add_one_reflog_ent, cb_data);
+       return 0;
+}
+
+static void add_one_tree(const unsigned char *sha1, struct rev_info *revs)
+{
+       struct tree *tree = lookup_tree(sha1);
+       add_pending_object(revs, &tree->object, "");
+}
+
+static void add_cache_tree(struct cache_tree *it, struct rev_info *revs)
+{
+       int i;
+
+       if (it->entry_count >= 0)
+               add_one_tree(it->sha1, revs);
+       for (i = 0; i < it->subtree_nr; i++)
+               add_cache_tree(it->down[i]->cache_tree, revs);
+}
+
+static void add_cache_refs(struct rev_info *revs)
+{
+       int i;
+
+       read_cache();
+       for (i = 0; i < active_nr; i++) {
+               lookup_blob(active_cache[i]->sha1);
+               /*
+                * We could add the blobs to the pending list, but quite
+                * frankly, we don't care. Once we've looked them up, and
+                * added them as objects, we've really done everything
+                * there is to do for a blob
+                */
+       }
+       if (active_cache_tree)
+               add_cache_tree(active_cache_tree, revs);
+}
+
+void mark_reachable_objects(struct rev_info *revs, int mark_reflog)
+{
+       /*
+        * Set up revision parsing, and mark us as being interested
+        * in all object types, not just commits.
+        */
+       revs->tag_objects = 1;
+       revs->blob_objects = 1;
+       revs->tree_objects = 1;
+
+       /* Add all refs from the index file */
+       add_cache_refs(revs);
+
+       /* Add all external refs */
+       for_each_ref(add_one_ref, revs);
+
+       /* Add all reflog info from refs */
+       if (mark_reflog)
+               for_each_ref(add_one_reflog, revs);
+
+       /*
+        * Set up the revision walk - this will move all commits
+        * from the pending list to the commit walking list.
+        */
+       prepare_revision_walk(revs);
+       walk_commit_list(revs);
+}
diff --git a/reachable.h b/reachable.h
new file mode 100644 (file)
index 0000000..4075181
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef REACHEABLE_H
+#define REACHEABLE_H
+
+extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog);
+
+#endif
index b8d83ccd9f7985d60f69b7cd44db698d4e932612..c54a61187711087b98138b9598db6353457e4df3 100644 (file)
@@ -793,16 +793,16 @@ int read_cache_from(const char *path)
                die("index file open failed (%s)", strerror(errno));
        }
 
-       cache_mmap = MAP_FAILED;
        if (!fstat(fd, &st)) {
                cache_mmap_size = st.st_size;
                errno = EINVAL;
                if (cache_mmap_size >= sizeof(struct cache_header) + 20)
-                       cache_mmap = mmap(NULL, cache_mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
-       }
+                       cache_mmap = xmmap(NULL, cache_mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+               else
+                       die("index file smaller than expected");
+       } else
+               die("cannot stat the open index (%s)", strerror(errno));
        close(fd);
-       if (cache_mmap == MAP_FAILED)
-               die("index file mmap failed (%s)", strerror(errno));
 
        hdr = cache_mmap;
        if (verify_hdr(hdr, cache_mmap_size) < 0)
@@ -870,7 +870,7 @@ static int ce_write_flush(SHA_CTX *context, int fd)
        unsigned int buffered = write_buffer_len;
        if (buffered) {
                SHA1_Update(context, write_buffer, buffered);
-               if (write(fd, write_buffer, buffered) != buffered)
+               if (write_in_full(fd, write_buffer, buffered) != buffered)
                        return -1;
                write_buffer_len = 0;
        }
@@ -919,7 +919,7 @@ static int ce_flush(SHA_CTX *context, int fd)
 
        /* Flush first if not enough space for SHA1 signature */
        if (left + 20 > WRITE_BUFFER_SIZE) {
-               if (write(fd, write_buffer, left) != left)
+               if (write_in_full(fd, write_buffer, left) != left)
                        return -1;
                left = 0;
        }
@@ -927,7 +927,7 @@ static int ce_flush(SHA_CTX *context, int fd)
        /* Append the SHA1 signature at the end */
        SHA1_Final(write_buffer + left, context);
        left += 20;
-       return (write(fd, write_buffer, left) != left) ? -1 : 0;
+       return (write_in_full(fd, write_buffer, left) != left) ? -1 : 0;
 }
 
 static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
@@ -1010,7 +1010,7 @@ int write_cache(int newfd, struct cache_entry **cache, int entries)
                if (data &&
                    !write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sz) &&
                    !ce_write(&c, newfd, data, sz))
-                       ;
+                       free(data);
                else {
                        free(data);
                        return -1;
diff --git a/refs.c b/refs.c
index f76b4fe20dea81f99b330103ce9d05cc6364c96c..689ac50bae64a1068f2d9b1d720817748fc2289d 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -284,7 +284,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
                fd = open(path, O_RDONLY);
                if (fd < 0)
                        return NULL;
-               len = read(fd, buffer, sizeof(buffer)-1);
+               len = read_in_full(fd, buffer, sizeof(buffer)-1);
                close(fd);
 
                /*
@@ -332,7 +332,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master)
        }
        lockpath = mkpath("%s.lock", git_HEAD);
        fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666); 
-       written = write(fd, ref, len);
+       written = write_in_full(fd, ref, len);
        close(fd);
        if (written != len) {
                unlink(lockpath);
@@ -923,6 +923,9 @@ static int log_ref_write(struct ref_lock *lock,
        char *logrec;
        const char *committer;
 
+       if (log_all_ref_updates < 0)
+               log_all_ref_updates = !is_bare_repository();
+
        if (log_all_ref_updates &&
            (!strncmp(lock->ref_name, "refs/heads/", 11) ||
             !strncmp(lock->ref_name, "refs/remotes/", 13))) {
@@ -968,7 +971,7 @@ static int log_ref_write(struct ref_lock *lock,
                        sha1_to_hex(sha1),
                        committer);
        }
-       written = len <= maxlen ? write(logfd, logrec, len) : -1;
+       written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1;
        free(logrec);
        close(logfd);
        if (written != len)
@@ -987,8 +990,8 @@ int write_ref_sha1(struct ref_lock *lock,
                unlock_ref(lock);
                return 0;
        }
-       if (write(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
-           write(lock->lock_fd, &term, 1) != 1
+       if (write_in_full(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
+           write_in_full(lock->lock_fd, &term, 1) != 1
                || close(lock->lock_fd) < 0) {
                error("Couldn't write %s", lock->lk->filename);
                unlock_ref(lock);
@@ -1025,7 +1028,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
        fstat(logfd, &st);
        if (!st.st_size)
                die("Log %s is empty.", logfile);
-       logdata = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, logfd, 0);
+       logdata = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, logfd, 0);
        close(logfd);
 
        lastrec = NULL;
@@ -1097,7 +1100,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
        return 0;
 }
 
-void for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
+int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
 {
        const char *logfile;
        FILE *logfp;
@@ -1106,19 +1109,35 @@ void for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
        logfile = git_path("logs/%s", ref);
        logfp = fopen(logfile, "r");
        if (!logfp)
-               return;
+               return -1;
        while (fgets(buf, sizeof(buf), logfp)) {
                unsigned char osha1[20], nsha1[20];
-               int len;
+               char *email_end, *message;
+               unsigned long timestamp;
+               int len, ret, tz;
 
                /* old SP new SP name <email> SP time TAB msg LF */
                len = strlen(buf);
                if (len < 83 || buf[len-1] != '\n' ||
                    get_sha1_hex(buf, osha1) || buf[40] != ' ' ||
-                   get_sha1_hex(buf + 41, nsha1) || buf[81] != ' ')
+                   get_sha1_hex(buf + 41, nsha1) || buf[81] != ' ' ||
+                   !(email_end = strchr(buf + 82, '>')) ||
+                   email_end[1] != ' ' ||
+                   !(timestamp = strtoul(email_end + 2, &message, 10)) ||
+                   !message || message[0] != ' ' ||
+                   (message[1] != '+' && message[1] != '-') ||
+                   !isdigit(message[2]) || !isdigit(message[3]) ||
+                   !isdigit(message[4]) || !isdigit(message[5]) ||
+                   message[6] != '\t')
                        continue; /* corrupt? */
-               fn(osha1, nsha1, buf+82, cb_data);
+               email_end[1] = '\0';
+               tz = strtol(message + 1, NULL, 10);
+               message += 7;
+               ret = fn(osha1, nsha1, buf+82, timestamp, tz, message, cb_data);
+               if (ret)
+                       return ret;
        }
        fclose(logfp);
+       return 0;
 }
 
diff --git a/refs.h b/refs.h
index de43cc768af559538b2db9f3383bbf2e08713ae0..0e877e82ee1346c3d9d965581c62b337d596336f 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -45,8 +45,8 @@ extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, cons
 extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1);
 
 /* iterate over reflog entries */
-typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, char *, void *);
-void for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data);
+typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *);
+int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data);
 
 /** Returns 0 if target has the right format for a ref. **/
 extern int check_ref_format(const char *target);
index 6e4ec463024a3e6cc4199ea1937f3b11e0a0dace..f2ddd95e29e5eaef2cb4beb28c34921396d94fdf 100644 (file)
@@ -505,7 +505,9 @@ static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
        }
 }
 
-static int handle_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1, char *detail, void *cb_data)
+static int handle_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+               const char *email, unsigned long timestamp, int tz,
+               const char *message, void *cb_data)
 {
        handle_one_reflog_commit(osha1, cb_data);
        handle_one_reflog_commit(nsha1, cb_data);
@@ -1119,21 +1121,23 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 void prepare_revision_walk(struct rev_info *revs)
 {
        int nr = revs->pending.nr;
-       struct object_array_entry *list = revs->pending.objects;
+       struct object_array_entry *e, *list;
 
+       e = list = revs->pending.objects;
        revs->pending.nr = 0;
        revs->pending.alloc = 0;
        revs->pending.objects = NULL;
        while (--nr >= 0) {
-               struct commit *commit = handle_commit(revs, list->item, list->name);
+               struct commit *commit = handle_commit(revs, e->item, e->name);
                if (commit) {
                        if (!(commit->object.flags & SEEN)) {
                                commit->object.flags |= SEEN;
                                insert_by_date(commit, &revs->commits);
                        }
                }
-               list++;
+               e++;
        }
+       free(list);
 
        if (revs->no_walk)
                return;
index c195d080db7c80420e1226f7591c9e9c3059536a..6756264b293e18467a59dc0222ee82e586cda76f 100644 (file)
@@ -65,14 +65,14 @@ static int pack_objects(int fd, struct ref *refs)
                        memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);
                        buf[0] = '^';
                        buf[41] = '\n';
-                       if (!write_in_full(pipe_fd[1], buf, 42,
+                       if (!write_or_whine(pipe_fd[1], buf, 42,
                                                "send-pack: send refs"))
                                break;
                }
                if (!is_null_sha1(refs->new_sha1)) {
                        memcpy(buf, sha1_to_hex(refs->new_sha1), 40);
                        buf[40] = '\n';
-                       if (!write_in_full(pipe_fd[1], buf, 41,
+                       if (!write_or_whine(pipe_fd[1], buf, 41,
                                                "send-pack: send refs"))
                                break;
                }
diff --git a/setup.c b/setup.c
index 2ae57f7c94e304ef3468a27cd245c314a08046ed..cc97f9f5c1b8ebfb21a4487d4c5b0ee972797779 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -138,7 +138,8 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
  *    GIT_OBJECT_DIRECTORY environment variable
  *  - a refs/ directory
  *  - either a HEAD symlink or a HEAD file that is formatted as
- *    a proper "ref:".
+ *    a proper "ref:", or a regular file HEAD that has a properly
+ *    formatted sha1 object name.
  */
 static int is_git_directory(const char *suspect)
 {
@@ -161,7 +162,7 @@ static int is_git_directory(const char *suspect)
                return 0;
 
        strcpy(path + len, "/HEAD");
-       if (validate_symref(path))
+       if (validate_headref(path))
                return 0;
 
        return 1;
index 1c4df5b73e9dff900c69c0994eb21105614511b6..1b1c0f7b4dc814764ead5ba1af77070ed381110c 100644 (file)
 #endif
 #endif
 
+#ifdef NO_C99_FORMAT
+#define SZ_FMT "lu"
+#else
+#define SZ_FMT "zu"
+#endif
+
 const unsigned char null_sha1[20];
 
 static unsigned int sha1_file_open_flag = O_NOATIME;
@@ -355,10 +361,8 @@ static void read_info_alternates(const char * relative_base, int depth)
                close(fd);
                return;
        }
-       map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       map = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
        close(fd);
-       if (map == MAP_FAILED)
-               return;
 
        link_alt_odb_entries(map, map + st.st_size, '\n', relative_base, depth);
 
@@ -397,11 +401,36 @@ static char *find_sha1_file(const unsigned char *sha1, struct stat *st)
        return NULL;
 }
 
-#define PACK_MAX_SZ (1<<26)
-static int pack_used_ctr;
-static unsigned long pack_mapped;
+static unsigned int pack_used_ctr;
+static unsigned int pack_mmap_calls;
+static unsigned int peak_pack_open_windows;
+static unsigned int pack_open_windows;
+static size_t peak_pack_mapped;
+static size_t pack_mapped;
+static size_t page_size;
 struct packed_git *packed_git;
 
+void pack_report()
+{
+       fprintf(stderr,
+               "pack_report: getpagesize()            = %10" SZ_FMT "\n"
+               "pack_report: core.packedGitWindowSize = %10" SZ_FMT "\n"
+               "pack_report: core.packedGitLimit      = %10" SZ_FMT "\n",
+               page_size,
+               packed_git_window_size,
+               packed_git_limit);
+       fprintf(stderr,
+               "pack_report: pack_used_ctr            = %10u\n"
+               "pack_report: pack_mmap_calls          = %10u\n"
+               "pack_report: pack_open_windows        = %10u / %10u\n"
+               "pack_report: pack_mapped              = "
+                       "%10" SZ_FMT " / %10" SZ_FMT "\n",
+               pack_used_ctr,
+               pack_mmap_calls,
+               pack_open_windows, peak_pack_open_windows,
+               pack_mapped, peak_pack_mapped);
+}
+
 static int check_packed_git_idx(const char *path, unsigned long *idx_size_,
                                void **idx_map_)
 {
@@ -418,10 +447,8 @@ static int check_packed_git_idx(const char *path, unsigned long *idx_size_,
                return -1;
        }
        idx_size = st.st_size;
-       idx_map = mmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
        close(fd);
-       if (idx_map == MAP_FAILED)
-               return -1;
 
        index = idx_map;
        *idx_map_ = idx_map;
@@ -451,86 +478,200 @@ static int check_packed_git_idx(const char *path, unsigned long *idx_size_,
        return 0;
 }
 
-static int unuse_one_packed_git(void)
+static void scan_windows(struct packed_git *p,
+       struct packed_git **lru_p,
+       struct pack_window **lru_w,
+       struct pack_window **lru_l)
 {
-       struct packed_git *p, *lru = NULL;
+       struct pack_window *w, *w_l;
 
-       for (p = packed_git; p; p = p->next) {
-               if (p->pack_use_cnt || !p->pack_base)
-                       continue;
-               if (!lru || p->pack_last_used < lru->pack_last_used)
-                       lru = p;
+       for (w_l = NULL, w = p->windows; w; w = w->next) {
+               if (!w->inuse_cnt) {
+                       if (!*lru_w || w->last_used < (*lru_w)->last_used) {
+                               *lru_p = p;
+                               *lru_w = w;
+                               *lru_l = w_l;
+                       }
+               }
+               w_l = w;
        }
-       if (!lru)
-               return 0;
-       munmap(lru->pack_base, lru->pack_size);
-       lru->pack_base = NULL;
-       return 1;
 }
 
-void unuse_packed_git(struct packed_git *p)
+static int unuse_one_window(struct packed_git *current)
+{
+       struct packed_git *p, *lru_p = NULL;
+       struct pack_window *lru_w = NULL, *lru_l = NULL;
+
+       if (current)
+               scan_windows(current, &lru_p, &lru_w, &lru_l);
+       for (p = packed_git; p; p = p->next)
+               scan_windows(p, &lru_p, &lru_w, &lru_l);
+       if (lru_p) {
+               munmap(lru_w->base, lru_w->len);
+               pack_mapped -= lru_w->len;
+               if (lru_l)
+                       lru_l->next = lru_w->next;
+               else {
+                       lru_p->windows = lru_w->next;
+                       if (!lru_p->windows && lru_p != current) {
+                               close(lru_p->pack_fd);
+                               lru_p->pack_fd = -1;
+                       }
+               }
+               free(lru_w);
+               pack_open_windows--;
+               return 1;
+       }
+       return 0;
+}
+
+void release_pack_memory(size_t need)
+{
+       size_t cur = pack_mapped;
+       while (need >= (cur - pack_mapped) && unuse_one_window(NULL))
+               ; /* nothing */
+}
+
+void unuse_pack(struct pack_window **w_cursor)
 {
-       p->pack_use_cnt--;
+       struct pack_window *w = *w_cursor;
+       if (w) {
+               w->inuse_cnt--;
+               *w_cursor = NULL;
+       }
 }
 
-int use_packed_git(struct packed_git *p)
+static void open_packed_git(struct packed_git *p)
 {
+       struct stat st;
+       struct pack_header hdr;
+       unsigned char sha1[20];
+       unsigned char *idx_sha1;
+       long fd_flag;
+
+       p->pack_fd = open(p->pack_name, O_RDONLY);
+       if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
+               die("packfile %s cannot be opened", p->pack_name);
+
+       /* If we created the struct before we had the pack we lack size. */
        if (!p->pack_size) {
-               struct stat st;
-               /* We created the struct before we had the pack */
-               stat(p->pack_name, &st);
                if (!S_ISREG(st.st_mode))
                        die("packfile %s not a regular file", p->pack_name);
                p->pack_size = st.st_size;
-       }
-       if (!p->pack_base) {
-               int fd;
-               struct stat st;
-               void *map;
-               struct pack_header *hdr;
-
-               pack_mapped += p->pack_size;
-               while (PACK_MAX_SZ < pack_mapped && unuse_one_packed_git())
-                       ; /* nothing */
-               fd = open(p->pack_name, O_RDONLY);
-               if (fd < 0)
-                       die("packfile %s cannot be opened", p->pack_name);
-               if (fstat(fd, &st)) {
-                       close(fd);
-                       die("packfile %s cannot be opened", p->pack_name);
-               }
-               if (st.st_size != p->pack_size)
-                       die("packfile %s size mismatch.", p->pack_name);
-               map = mmap(NULL, p->pack_size, PROT_READ, MAP_PRIVATE, fd, 0);
-               close(fd);
-               if (map == MAP_FAILED)
-                       die("packfile %s cannot be mapped.", p->pack_name);
-               p->pack_base = map;
+       } else if (p->pack_size != st.st_size)
+               die("packfile %s size changed", p->pack_name);
 
-               /* Check if we understand this pack file.  If we don't we're
-                * likely too old to handle it.
-                */
-               hdr = map;
-               if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
-                       die("packfile %s isn't actually a pack.", p->pack_name);
-               if (!pack_version_ok(hdr->hdr_version))
-                       die("packfile %s is version %i and not supported"
-                               " (try upgrading GIT to a newer version)",
-                               p->pack_name, ntohl(hdr->hdr_version));
-
-               /* Check if the pack file matches with the index file.
-                * this is cheap.
-                */
-               if (hashcmp((unsigned char *)(p->index_base) +
-                           p->index_size - 40,
-                           (unsigned char *)p->pack_base +
-                           p->pack_size - 20)) {
-                       die("packfile %s does not match index.", p->pack_name);
+       /* We leave these file descriptors open with sliding mmap;
+        * there is no point keeping them open across exec(), though.
+        */
+       fd_flag = fcntl(p->pack_fd, F_GETFD, 0);
+       if (fd_flag < 0)
+               die("cannot determine file descriptor flags");
+       fd_flag |= FD_CLOEXEC;
+       if (fcntl(p->pack_fd, F_SETFD, fd_flag) == -1)
+               die("cannot set FD_CLOEXEC");
+
+       /* Verify we recognize this pack file format. */
+       if (read_in_full(p->pack_fd, &hdr, sizeof(hdr)) != sizeof(hdr))
+               die("file %s is far too short to be a packfile", p->pack_name);
+       if (hdr.hdr_signature != htonl(PACK_SIGNATURE))
+               die("file %s is not a GIT packfile", p->pack_name);
+       if (!pack_version_ok(hdr.hdr_version))
+               die("packfile %s is version %u and not supported"
+                       " (try upgrading GIT to a newer version)",
+                       p->pack_name, ntohl(hdr.hdr_version));
+
+       /* Verify the pack matches its index. */
+       if (num_packed_objects(p) != ntohl(hdr.hdr_entries))
+               die("packfile %s claims to have %u objects"
+                       " while index size indicates %u objects",
+                       p->pack_name, ntohl(hdr.hdr_entries),
+                       num_packed_objects(p));
+       if (lseek(p->pack_fd, p->pack_size - sizeof(sha1), SEEK_SET) == -1)
+               die("end of packfile %s is unavailable", p->pack_name);
+       if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1))
+               die("packfile %s signature is unavailable", p->pack_name);
+       idx_sha1 = ((unsigned char *)p->index_base) + p->index_size - 40;
+       if (hashcmp(sha1, idx_sha1))
+               die("packfile %s does not match index", p->pack_name);
+}
+
+static int in_window(struct pack_window *win, unsigned long offset)
+{
+       /* We must promise at least 20 bytes (one hash) after the
+        * offset is available from this window, otherwise the offset
+        * is not actually in this window and a different window (which
+        * has that one hash excess) must be used.  This is to support
+        * the object header and delta base parsing routines below.
+        */
+       off_t win_off = win->offset;
+       return win_off <= offset
+               && (offset + 20) <= (win_off + win->len);
+}
+
+unsigned char* use_pack(struct packed_git *p,
+               struct pack_window **w_cursor,
+               unsigned long offset,
+               unsigned int *left)
+{
+       struct pack_window *win = *w_cursor;
+
+       if (p->pack_fd == -1)
+               open_packed_git(p);
+
+       /* Since packfiles end in a hash of their content and its
+        * pointless to ask for an offset into the middle of that
+        * hash, and the in_window function above wouldn't match
+        * don't allow an offset too close to the end of the file.
+        */
+       if (offset > (p->pack_size - 20))
+               die("offset beyond end of packfile (truncated pack?)");
+
+       if (!win || !in_window(win, offset)) {
+               if (win)
+                       win->inuse_cnt--;
+               for (win = p->windows; win; win = win->next) {
+                       if (in_window(win, offset))
+                               break;
+               }
+               if (!win) {
+                       if (!page_size)
+                               page_size = getpagesize();
+                       win = xcalloc(1, sizeof(*win));
+                       win->offset = (offset / page_size) * page_size;
+                       win->len = p->pack_size - win->offset;
+                       if (win->len > packed_git_window_size)
+                               win->len = packed_git_window_size;
+                       pack_mapped += win->len;
+                       while (packed_git_limit < pack_mapped
+                               && unuse_one_window(p))
+                               ; /* nothing */
+                       win->base = xmmap(NULL, win->len,
+                               PROT_READ, MAP_PRIVATE,
+                               p->pack_fd, win->offset);
+                       if (win->base == MAP_FAILED)
+                               die("packfile %s cannot be mapped: %s",
+                                       p->pack_name,
+                                       strerror(errno));
+                       pack_mmap_calls++;
+                       pack_open_windows++;
+                       if (pack_mapped > peak_pack_mapped)
+                               peak_pack_mapped = pack_mapped;
+                       if (pack_open_windows > peak_pack_open_windows)
+                               peak_pack_open_windows = pack_open_windows;
+                       win->next = p->windows;
+                       p->windows = win;
                }
        }
-       p->pack_last_used = pack_used_ctr++;
-       p->pack_use_cnt++;
-       return 0;
+       if (win != *w_cursor) {
+               win->last_used = pack_used_ctr++;
+               win->inuse_cnt++;
+               *w_cursor = win;
+       }
+       offset -= win->offset;
+       if (left)
+               *left = win->len - offset;
+       return win->base + offset;
 }
 
 struct packed_git *add_packed_git(char *path, int path_len, int local)
@@ -559,9 +700,8 @@ struct packed_git *add_packed_git(char *path, int path_len, int local)
        p->pack_size = st.st_size;
        p->index_base = idx_map;
        p->next = NULL;
-       p->pack_base = NULL;
-       p->pack_last_used = 0;
-       p->pack_use_cnt = 0;
+       p->windows = NULL;
+       p->pack_fd = -1;
        p->pack_local = local;
        if ((path_len > 44) && !get_sha1_hex(path + path_len - 44, sha1))
                hashcpy(p->sha1, sha1);
@@ -592,9 +732,8 @@ struct packed_git *parse_pack_index_file(const unsigned char *sha1, char *idx_pa
        p->pack_size = 0;
        p->index_base = idx_map;
        p->next = NULL;
-       p->pack_base = NULL;
-       p->pack_last_used = 0;
-       p->pack_use_cnt = 0;
+       p->windows = NULL;
+       p->pack_fd = -1;
        hashcpy(p->sha1, sha1);
        return p;
 }
@@ -705,10 +844,8 @@ void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
                 */
                sha1_file_open_flag = 0;
        }
-       map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       map = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
        close(fd);
-       if (map == MAP_FAILED)
-               return NULL;
        *size = st.st_size;
        return map;
 }
@@ -878,18 +1015,21 @@ void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned l
 }
 
 static unsigned long get_delta_base(struct packed_git *p,
+                                   struct pack_window **w_curs,
                                    unsigned long offset,
                                    enum object_type kind,
                                    unsigned long delta_obj_offset,
                                    unsigned long *base_obj_offset)
 {
-       unsigned char *base_info = (unsigned char *) p->pack_base + offset;
+       unsigned char *base_info = use_pack(p, w_curs, offset, NULL);
        unsigned long base_offset;
 
-       /* there must be at least 20 bytes left regardless of delta type */
-       if (p->pack_size <= offset + 20)
-               die("truncated pack file");
-
+       /* use_pack() assured us we have [base_info, base_info + 20)
+        * as a range that we can look at without walking off the
+        * end of the mapped window.  Its actually the hash size
+        * that is assured.  An OFS_DELTA longer than the hash size
+        * is stupid, as then a REF_DELTA would be smaller to store.
+        */
        if (kind == OBJ_OFS_DELTA) {
                unsigned used = 0;
                unsigned char c = base_info[used++];
@@ -923,6 +1063,7 @@ static int packed_object_info(struct packed_git *p, unsigned long offset,
                              char *type, unsigned long *sizep);
 
 static int packed_delta_info(struct packed_git *p,
+                            struct pack_window **w_curs,
                             unsigned long offset,
                             enum object_type kind,
                             unsigned long obj_offset,
@@ -931,7 +1072,8 @@ static int packed_delta_info(struct packed_git *p,
 {
        unsigned long base_offset;
 
-       offset = get_delta_base(p, offset, kind, obj_offset, &base_offset);
+       offset = get_delta_base(p, w_curs, offset, kind,
+               obj_offset, &base_offset);
 
        /* We choose to only get the type of the base object and
         * ignore potentially corrupt pack file that expects the delta
@@ -943,20 +1085,23 @@ static int packed_delta_info(struct packed_git *p,
 
        if (sizep) {
                const unsigned char *data;
-               unsigned char delta_head[20];
+               unsigned char delta_head[20], *in;
                unsigned long result_size;
                z_stream stream;
                int st;
 
                memset(&stream, 0, sizeof(stream));
-
-               stream.next_in = (unsigned char *) p->pack_base + offset;
-               stream.avail_in = p->pack_size - offset;
                stream.next_out = delta_head;
                stream.avail_out = sizeof(delta_head);
 
                inflateInit(&stream);
-               st = inflate(&stream, Z_FINISH);
+               do {
+                       in = use_pack(p, w_curs, offset, &stream.avail_in);
+                       stream.next_in = in;
+                       st = inflate(&stream, Z_FINISH);
+                       offset += stream.next_in - in;
+               } while ((st == Z_OK || st == Z_BUF_ERROR)
+                       && stream.total_out < sizeof(delta_head));
                inflateEnd(&stream);
                if ((st != Z_STREAM_END) &&
                    stream.total_out != sizeof(delta_head))
@@ -977,17 +1122,24 @@ static int packed_delta_info(struct packed_git *p,
        return 0;
 }
 
-static unsigned long unpack_object_header(struct packed_git *p, unsigned long offset,
-       enum object_type *type, unsigned long *sizep)
+static unsigned long unpack_object_header(struct packed_git *p,
+               struct pack_window **w_curs,
+               unsigned long offset,
+               enum object_type *type,
+               unsigned long *sizep)
 {
+       unsigned char *base;
+       unsigned int left;
        unsigned long used;
 
-       if (p->pack_size <= offset)
-               die("object offset outside of pack file");
-
-       used = unpack_object_header_gently((unsigned char *)p->pack_base +
-                                          offset,
-                                          p->pack_size - offset, type, sizep);
+       /* use_pack() assures us we have [base, base + 20) available
+        * as a range that we can look at at.  (Its actually the hash
+        * size that is assurred.)  With our object header encoding
+        * the maximum deflated object size is 2^137, which is just
+        * insane, so we know won't exceed what we have been given.
+        */
+       base = use_pack(p, w_curs, offset, &left);
+       used = unpack_object_header_gently(base, left, type, sizep);
        if (!used)
                die("object offset outside of pack file");
 
@@ -1002,13 +1154,14 @@ void packed_object_info_detail(struct packed_git *p,
                               unsigned int *delta_chain_length,
                               unsigned char *base_sha1)
 {
+       struct pack_window *w_curs = NULL;
        unsigned long obj_offset, val;
        unsigned char *next_sha1;
        enum object_type kind;
 
        *delta_chain_length = 0;
        obj_offset = offset;
-       offset = unpack_object_header(p, offset, &kind, size);
+       offset = unpack_object_header(p, &w_curs, offset, &kind, size);
 
        for (;;) {
                switch (kind) {
@@ -1021,25 +1174,24 @@ void packed_object_info_detail(struct packed_git *p,
                case OBJ_TAG:
                        strcpy(type, type_names[kind]);
                        *store_size = 0; /* notyet */
+                       unuse_pack(&w_curs);
                        return;
                case OBJ_OFS_DELTA:
-                       get_delta_base(p, offset, kind, obj_offset, &offset);
+                       get_delta_base(p, &w_curs, offset, kind,
+                               obj_offset, &offset);
                        if (*delta_chain_length == 0) {
                                /* TODO: find base_sha1 as pointed by offset */
                        }
                        break;
                case OBJ_REF_DELTA:
-                       if (p->pack_size <= offset + 20)
-                               die("pack file %s records an incomplete delta base",
-                                   p->pack_name);
-                       next_sha1 = (unsigned char *) p->pack_base + offset;
+                       next_sha1 = use_pack(p, &w_curs, offset, NULL);
                        if (*delta_chain_length == 0)
                                hashcpy(base_sha1, next_sha1);
                        offset = find_pack_entry_one(next_sha1, p);
                        break;
                }
                obj_offset = offset;
-               offset = unpack_object_header(p, offset, &kind, &val);
+               offset = unpack_object_header(p, &w_curs, offset, &kind, &val);
                (*delta_chain_length)++;
        }
 }
@@ -1047,20 +1199,26 @@ void packed_object_info_detail(struct packed_git *p,
 static int packed_object_info(struct packed_git *p, unsigned long offset,
                              char *type, unsigned long *sizep)
 {
+       struct pack_window *w_curs = NULL;
        unsigned long size, obj_offset = offset;
        enum object_type kind;
+       int r;
 
-       offset = unpack_object_header(p, offset, &kind, &size);
+       offset = unpack_object_header(p, &w_curs, offset, &kind, &size);
 
        switch (kind) {
        case OBJ_OFS_DELTA:
        case OBJ_REF_DELTA:
-               return packed_delta_info(p, offset, kind, obj_offset, type, sizep);
+               r = packed_delta_info(p, &w_curs, offset, kind,
+                       obj_offset, type, sizep);
+               unuse_pack(&w_curs);
+               return r;
        case OBJ_COMMIT:
        case OBJ_TREE:
        case OBJ_BLOB:
        case OBJ_TAG:
                strcpy(type, type_names[kind]);
+               unuse_pack(&w_curs);
                break;
        default:
                die("pack %s contains unknown object type %d",
@@ -1072,23 +1230,27 @@ static int packed_object_info(struct packed_git *p, unsigned long offset,
 }
 
 static void *unpack_compressed_entry(struct packed_git *p,
+                                   struct pack_window **w_curs,
                                    unsigned long offset,
                                    unsigned long size)
 {
        int st;
        z_stream stream;
-       unsigned char *buffer;
+       unsigned char *buffer, *in;
 
        buffer = xmalloc(size + 1);
        buffer[size] = 0;
        memset(&stream, 0, sizeof(stream));
-       stream.next_in = (unsigned char*)p->pack_base + offset;
-       stream.avail_in = p->pack_size - offset;
        stream.next_out = buffer;
        stream.avail_out = size;
 
        inflateInit(&stream);
-       st = inflate(&stream, Z_FINISH);
+       do {
+               in = use_pack(p, w_curs, offset, &stream.avail_in);
+               stream.next_in = in;
+               st = inflate(&stream, Z_FINISH);
+               offset += stream.next_in - in;
+       } while (st == Z_OK || st == Z_BUF_ERROR);
        inflateEnd(&stream);
        if ((st != Z_STREAM_END) || stream.total_out != size) {
                free(buffer);
@@ -1099,6 +1261,7 @@ static void *unpack_compressed_entry(struct packed_git *p,
 }
 
 static void *unpack_delta_entry(struct packed_git *p,
+                               struct pack_window **w_curs,
                                unsigned long offset,
                                unsigned long delta_size,
                                enum object_type kind,
@@ -1109,13 +1272,14 @@ static void *unpack_delta_entry(struct packed_git *p,
        void *delta_data, *result, *base;
        unsigned long result_size, base_size, base_offset;
 
-       offset = get_delta_base(p, offset, kind, obj_offset, &base_offset);
-       base = unpack_entry_gently(p, base_offset, type, &base_size);
+       offset = get_delta_base(p, w_curs, offset, kind,
+               obj_offset, &base_offset);
+       base = unpack_entry(p, base_offset, type, &base_size);
        if (!base)
                die("failed to read delta base object at %lu from %s",
                    base_offset, p->pack_name);
 
-       delta_data = unpack_compressed_entry(p, offset, delta_size);
+       delta_data = unpack_compressed_entry(p, w_curs, offset, delta_size);
        result = patch_delta(base, base_size,
                             delta_data, delta_size,
                             &result_size);
@@ -1127,43 +1291,34 @@ static void *unpack_delta_entry(struct packed_git *p,
        return result;
 }
 
-static void *unpack_entry(struct pack_entry *entry,
-                         char *type, unsigned long *sizep)
-{
-       struct packed_git *p = entry->p;
-       void *retval;
-
-       if (use_packed_git(p))
-               die("cannot map packed file");
-       retval = unpack_entry_gently(p, entry->offset, type, sizep);
-       unuse_packed_git(p);
-       if (!retval)
-               die("corrupted pack file %s", p->pack_name);
-       return retval;
-}
-
-/* The caller is responsible for use_packed_git()/unuse_packed_git() pair */
-void *unpack_entry_gently(struct packed_git *p, unsigned long offset,
+void *unpack_entry(struct packed_git *p, unsigned long offset,
                          char *type, unsigned long *sizep)
 {
+       struct pack_window *w_curs = NULL;
        unsigned long size, obj_offset = offset;
        enum object_type kind;
+       void *retval;
 
-       offset = unpack_object_header(p, offset, &kind, &size);
+       offset = unpack_object_header(p, &w_curs, offset, &kind, &size);
        switch (kind) {
        case OBJ_OFS_DELTA:
        case OBJ_REF_DELTA:
-               return unpack_delta_entry(p, offset, size, kind, obj_offset, type, sizep);
+               retval = unpack_delta_entry(p, &w_curs, offset, size,
+                       kind, obj_offset, type, sizep);
+               break;
        case OBJ_COMMIT:
        case OBJ_TREE:
        case OBJ_BLOB:
        case OBJ_TAG:
                strcpy(type, type_names[kind]);
                *sizep = size;
-               return unpack_compressed_entry(p, offset, size);
+               retval = unpack_compressed_entry(p, &w_curs, offset, size);
+               break;
        default:
-               return NULL;
+               die("unknown object type %i in %s", kind, p->pack_name);
        }
+       unuse_pack(&w_curs);
+       return retval;
 }
 
 int num_packed_objects(const struct packed_git *p)
@@ -1289,7 +1444,6 @@ static int sha1_loose_object_info(const unsigned char *sha1, char *type, unsigne
 
 int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep)
 {
-       int status;
        struct pack_entry e;
 
        if (!find_pack_entry(sha1, &e, NULL)) {
@@ -1297,11 +1451,7 @@ int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep
                if (!find_pack_entry(sha1, &e, NULL))
                        return sha1_loose_object_info(sha1, type, sizep);
        }
-       if (use_packed_git(e.p))
-               die("cannot map packed file");
-       status = packed_object_info(e.p, e.offset, type, sizep);
-       unuse_packed_git(e.p);
-       return status;
+       return packed_object_info(e.p, e.offset, type, sizep);
 }
 
 static void *read_packed_sha1(const unsigned char *sha1, char *type, unsigned long *size)
@@ -1312,7 +1462,7 @@ static void *read_packed_sha1(const unsigned char *sha1, char *type, unsigned lo
                error("cannot read sha1_file for %s", sha1_to_hex(sha1));
                return NULL;
        }
-       return unpack_entry(&e, type, size);
+       return unpack_entry(e.p, e.offset, type, size);
 }
 
 void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size)
@@ -1470,20 +1620,8 @@ int move_temp_to_file(const char *tmpfile, const char *filename)
 
 static int write_buffer(int fd, const void *buf, size_t len)
 {
-       while (len) {
-               ssize_t size;
-
-               size = write(fd, buf, len);
-               if (!size)
-                       return error("file write: disk full");
-               if (size < 0) {
-                       if (errno == EINTR || errno == EAGAIN)
-                               continue;
-                       return error("file write error (%s)", strerror(errno));
-               }
-               len -= size;
-               buf = (char *) buf + size;
-       }
+       if (write_in_full(fd, buf, len) < 0)
+               return error("file write error (%s)", strerror(errno));
        return 0;
 }
 
@@ -1728,7 +1866,7 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
                        if (ret != Z_OK)
                                break;
                }
-               size = read(fd, buffer + *bufposn, bufsize - *bufposn);
+               size = xread(fd, buffer + *bufposn, bufsize - *bufposn);
                if (size <= 0) {
                        close(local);
                        unlink(tmpfile);
@@ -1851,10 +1989,8 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, con
 
        buf = "";
        if (size)
-               buf = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+               buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
        close(fd);
-       if (buf == MAP_FAILED)
-               return -1;
 
        if (!type)
                type = blob_type;
index b006c5c9802d96bc504188e10a9698a1f943477c..4c172b68246eaa41f9f6630c481e6a7aa5ee295a 100644 (file)
@@ -20,22 +20,6 @@ static int fd_out;
 static unsigned char remote_version;
 static unsigned char local_version = 1;
 
-static ssize_t force_write(int fd, void *buffer, size_t length)
-{
-       ssize_t ret = 0;
-       while (ret < length) {
-               ssize_t size = write(fd, (char *) buffer + ret, length - ret);
-               if (size < 0) {
-                       return size;
-               }
-               if (size == 0) {
-                       return ret;
-               }
-               ret += size;
-       }
-       return ret;
-}
-
 static int prefetches;
 
 static struct object_list *in_transit;
@@ -53,8 +37,9 @@ void prefetch(unsigned char *sha1)
        node->item = lookup_unknown_object(sha1);
        *end_of_transit = node;
        end_of_transit = &node->next;
-       force_write(fd_out, &type, 1);
-       force_write(fd_out, sha1, 20);
+       /* XXX: what if these writes fail? */
+       write_in_full(fd_out, &type, 1);
+       write_in_full(fd_out, sha1, 20);
        prefetches++;
 }
 
@@ -82,7 +67,7 @@ int fetch(unsigned char *sha1)
                remote = conn_buf[0];
                memmove(conn_buf, conn_buf + 1, --conn_buf_posn);
        } else {
-               if (read(fd_in, &remote, 1) < 1)
+               if (xread(fd_in, &remote, 1) < 1)
                        return -1;
        }
        /* fprintf(stderr, "Got %d\n", remote); */
@@ -97,9 +82,11 @@ int fetch(unsigned char *sha1)
 static int get_version(void)
 {
        char type = 'v';
-       write(fd_out, &type, 1);
-       write(fd_out, &local_version, 1);
-       if (read(fd_in, &remote_version, 1) < 1) {
+       if (write_in_full(fd_out, &type, 1) != 1 ||
+           write_in_full(fd_out, &local_version, 1)) {
+               return error("Couldn't request version from remote end");
+       }
+       if (xread(fd_in, &remote_version, 1) < 1) {
                return error("Couldn't read version from remote end");
        }
        return 0;
@@ -109,12 +96,17 @@ int fetch_ref(char *ref, unsigned char *sha1)
 {
        signed char remote;
        char type = 'r';
-       write(fd_out, &type, 1);
-       write(fd_out, ref, strlen(ref) + 1);
-       read(fd_in, &remote, 1);
+       int length = strlen(ref) + 1;
+       if (write_in_full(fd_out, &type, 1) != 1 ||
+           write_in_full(fd_out, ref, length) != length)
+               return -1;
+
+       if (read_in_full(fd_in, &remote, 1) != 1)
+               return -1;
        if (remote < 0)
                return remote;
-       read(fd_in, sha1, 20);
+       if (read_in_full(fd_in, sha1, 20) != 20)
+               return -1;
        return 0;
 }
 
index 0b52ae15cbd216bff5002c89e0c8af84ea68ed1a..2f045727875707198dd1763d75c9e8c7406e9042 100644 (file)
@@ -21,17 +21,14 @@ static int serve_object(int fd_in, int fd_out) {
        ssize_t size;
        unsigned char sha1[20];
        signed char remote;
-       int posn = 0;
-       do {
-               size = read(fd_in, sha1 + posn, 20 - posn);
-               if (size < 0) {
-                       perror("git-ssh-upload: read ");
-                       return -1;
-               }
-               if (!size)
-                       return -1;
-               posn += size;
-       } while (posn < 20);
+
+       size = read_in_full(fd_in, sha1, 20);
+       if (size < 0) {
+               perror("git-ssh-upload: read ");
+               return -1;
+       }
+       if (!size)
+               return -1;
        
        if (verbose)
                fprintf(stderr, "Serving %s\n", sha1_to_hex(sha1));
@@ -44,7 +41,8 @@ static int serve_object(int fd_in, int fd_out) {
                remote = -1;
        }
        
-       write(fd_out, &remote, 1);
+       if (write_in_full(fd_out, &remote, 1) != 1)
+               return 0;
        
        if (remote < 0)
                return 0;
@@ -54,9 +52,9 @@ static int serve_object(int fd_in, int fd_out) {
 
 static int serve_version(int fd_in, int fd_out)
 {
-       if (read(fd_in, &remote_version, 1) < 1)
+       if (xread(fd_in, &remote_version, 1) < 1)
                return -1;
-       write(fd_out, &local_version, 1);
+       write_in_full(fd_out, &local_version, 1);
        return 0;
 }
 
@@ -67,7 +65,7 @@ static int serve_ref(int fd_in, int fd_out)
        int posn = 0;
        signed char remote = 0;
        do {
-               if (read(fd_in, ref + posn, 1) < 1)
+               if (posn >= PATH_MAX || xread(fd_in, ref + posn, 1) < 1)
                        return -1;
                posn++;
        } while (ref[posn - 1]);
@@ -77,10 +75,11 @@ static int serve_ref(int fd_in, int fd_out)
 
        if (get_ref_sha1(ref, sha1))
                remote = -1;
-       write(fd_out, &remote, 1);
+       if (write_in_full(fd_out, &remote, 1) != 1)
+               return 0;
        if (remote)
                return 0;
-       write(fd_out, sha1, 20);
+       write_in_full(fd_out, sha1, 20);
         return 0;
 }
 
@@ -89,7 +88,7 @@ static void service(int fd_in, int fd_out) {
        char type;
        int retval;
        do {
-               retval = read(fd_in, &type, 1);
+               retval = xread(fd_in, &type, 1);
                if (retval < 1) {
                        if (retval < 0)
                                perror("git-ssh-upload: read ");
index 7abab1dafe83527a5ff84808824057f549a08080..36f251761739c6cda0053bbe26cc6331ed7be40c 100644 (file)
--- a/t/README
+++ b/t/README
@@ -18,7 +18,7 @@ The easiest way to run tests is to say "make".  This runs all
 the tests.
 
     *** t0000-basic.sh ***
-    *   ok 1: .git/objects should be empty after git-init-db in an empty repo.
+    *   ok 1: .git/objects should be empty after git-init in an empty repo.
     *   ok 2: .git/objects should have 256 subdirectories.
     *   ok 3: git-update-index without --add should fail adding.
     ...
index af42ccc8d157e54b97de435194d22e5ed895ab4b..bb1d7b84bcdd3a413e900a0002baf268fa631f3f 100644 (file)
@@ -25,14 +25,15 @@ perl -w -e "
 use SVN::Core;
 use SVN::Repos;
 \$SVN::Core::VERSION gt '1.1.0' or exit(42);
-SVN::Repos::create('$svnrepo', undef, undef, undef,
-                           { 'fs-config' => 'fsfs'});
-"
+system(qw/svnadmin create --fs-type fsfs/, '$svnrepo') == 0 or exit(41);
+" >&3 2>&4
 x=$?
 if test $x -ne 0
 then
        if test $x -eq 42; then
                err='Perl SVN libraries must be >= 1.1.0'
+       elif test $x -eq 41; then
+               err='svnadmin failed to create fsfs repository'
        else
                err='Perl SVN libraries not found or unusable, skipping test'
        fi
index 0cd1c41866c6ca344ed99fae3d71014dd9321e2d..186de7024336ca98bd5dcea6f675e1fc221d0ce0 100755 (executable)
@@ -31,12 +31,12 @@ fi
 . ./test-lib.sh
 
 ################################################################
-# init-db has been done in an empty repository.
+# git-init has been done in an empty repository.
 # make sure it is empty.
 
 find .git/objects -type f -print >should-be-empty
 test_expect_success \
-    '.git/objects should be empty after git-init-db in an empty repo.' \
+    '.git/objects should be empty after git-init in an empty repo.' \
     'cmp -s /dev/null should-be-empty' 
 
 # also it should have 2 subdirectories; no fan-out anymore, pack, and info.
index a29caa06dc6545b7fc23b3446a713b75f49cd146..60acdd368bfcb6ccb4698f6fd7533dd25befd9b0 100755 (executable)
@@ -401,5 +401,22 @@ test_expect_success numbers '
        test z1048576 = "z$m"
 '
 
+rm .git/config
+
+git-repo-config quote.leading " test"
+git-repo-config quote.ending "test "
+git-repo-config quote.semicolon "test;test"
+git-repo-config quote.hash "test#test"
+
+cat > expect << EOF
+[quote]
+       leading = " test"
+       ending = "test "
+       semicolon = "test;test"
+       hash = "test#test"
+EOF
+
+test_expect_success 'quoting' 'cmp .git/config expect'
+
 test_done
 
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
new file mode 100755 (executable)
index 0000000..8e8d526
--- /dev/null
@@ -0,0 +1,178 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Junio C Hamano
+#
+
+test_description='Test prune and reflog expiration'
+. ./test-lib.sh
+
+check_have () {
+       gaah= &&
+       for N in "$@"
+       do
+               eval "o=\$$N" && git cat-file -t $o || {
+                       echo Gaah $N
+                       gaah=$N
+                       break
+               }
+       done &&
+       test -z "$gaah"
+}
+
+check_fsck () {
+       output=$(git fsck-objects --full)
+       case "$1" in
+       '')
+               test -z "$output" ;;
+       *)
+               echo "$output" | grep "$1" ;;
+       esac
+}
+
+corrupt () {
+       aa=${1%??????????????????????????????????????} zz=${1#??}
+       mv .git/objects/$aa/$zz .git/$aa$zz
+}
+
+recover () {
+       aa=${1%??????????????????????????????????????} zz=${1#??}
+       mkdir -p .git/objects/$aa
+       mv .git/$aa$zz .git/objects/$aa/$zz
+}
+
+check_dont_have () {
+       gaah= &&
+       for N in "$@"
+       do
+               eval "o=\$$N"
+               git cat-file -t $o && {
+                       echo Gaah $N
+                       gaah=$N
+                       break
+               }
+       done
+       test -z "$gaah"
+}
+
+test_expect_success setup '
+       mkdir -p A/B &&
+       echo rat >C &&
+       echo ox >A/D &&
+       echo tiger >A/B/E &&
+       git add . &&
+
+       test_tick && git commit -m rabbit &&
+       H=`git rev-parse --verify HEAD` &&
+       A=`git rev-parse --verify HEAD:A` &&
+       B=`git rev-parse --verify HEAD:A/B` &&
+       C=`git rev-parse --verify HEAD:C` &&
+       D=`git rev-parse --verify HEAD:A/D` &&
+       E=`git rev-parse --verify HEAD:A/B/E` &&
+       check_fsck &&
+
+       chmod +x C &&
+       ( test "`git repo-config --bool core.filemode`" != false ||
+         echo executable >>C ) &&
+       git add C &&
+       test_tick && git commit -m dragon &&
+       L=`git rev-parse --verify HEAD` &&
+       check_fsck &&
+
+       rm -f C A/B/E &&
+       echo snake >F &&
+       echo horse >A/G &&
+       git add F A/G &&
+       test_tick && git commit -a -m sheep &&
+       F=`git rev-parse --verify HEAD:F` &&
+       G=`git rev-parse --verify HEAD:A/G` &&
+       I=`git rev-parse --verify HEAD:A` &&
+       J=`git rev-parse --verify HEAD` &&
+       check_fsck &&
+
+       rm -f A/G &&
+       test_tick && git commit -a -m monkey &&
+       K=`git rev-parse --verify HEAD` &&
+       check_fsck &&
+
+       check_have A B C D E F G H I J K L &&
+
+       git prune &&
+
+       check_have A B C D E F G H I J K L &&
+
+       check_fsck &&
+
+       loglen=$(wc -l <.git/logs/refs/heads/master) &&
+       test $loglen = 4
+'
+
+test_expect_success rewind '
+       test_tick && git reset --hard HEAD~2 &&
+       test -f C &&
+       test -f A/B/E &&
+       ! test -f F &&
+       ! test -f A/G &&
+
+       check_have A B C D E F G H I J K L &&
+
+       git prune &&
+
+       check_have A B C D E F G H I J K L &&
+
+       loglen=$(wc -l <.git/logs/refs/heads/master) &&
+       test $loglen = 5
+'
+
+test_expect_success 'corrupt and check' '
+
+       corrupt $F &&
+       check_fsck "missing blob $F"
+
+'
+
+test_expect_success 'reflog expire --dry-run should not touch reflog' '
+
+       git reflog expire --dry-run \
+               --expire=$(($test_tick - 10000)) \
+               --expire-unreachable=$(($test_tick - 10000)) \
+               --stale-fix \
+               --all &&
+
+       loglen=$(wc -l <.git/logs/refs/heads/master) &&
+       test $loglen = 5 &&
+
+       check_fsck "missing blob $F"
+'
+
+test_expect_success 'reflog expire' '
+
+       git reflog expire --verbose \
+               --expire=$(($test_tick - 10000)) \
+               --expire-unreachable=$(($test_tick - 10000)) \
+               --stale-fix \
+               --all &&
+
+       loglen=$(wc -l <.git/logs/refs/heads/master) &&
+       test $loglen = 2 &&
+
+       check_fsck "dangling commit $K"
+'
+
+test_expect_success 'prune and fsck' '
+
+       git prune &&
+       check_fsck &&
+
+       check_have A B C D E H L &&
+       check_dont_have F G I J K
+
+'
+
+test_expect_success 'recover and check' '
+
+       recover $F &&
+       check_fsck "dangling blob $F"
+
+'
+
+test_done
index a6ea0f6a196c5285c229021c96bb83f33a3aa748..bb80e4286a7144e88aca9839c26b9fd99ff6cd4e 100755 (executable)
@@ -48,7 +48,7 @@ test_expect_success \
         test ! -f .git/logs/refs/heads/d/e/f'
 
 cat >expect <<EOF
-0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000     checkout: Created from master^0
+0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000     checkout: Created from master
 EOF
 test_expect_success \
     'git checkout -b g/h/i -l should create a branch and a log' \
index b1e9f2eed22b9938a6dbba89a98f6dcc9f2dded1..16bdae4f2377cd93a509ff0c74ac62cc00f0b619 100755 (executable)
@@ -34,7 +34,7 @@ test_expect_success \
     'see if a branch still exists when packed' \
     'git-branch b &&
      git-pack-refs --all &&
-     rm .git/refs/heads/b &&
+     rm -f .git/refs/heads/b &&
      echo "$SHA1 refs/heads/b" >expect &&
      git-show-ref b >result &&
      diff expect result'
diff --git a/t/t3901-8859-1.txt b/t/t3901-8859-1.txt
new file mode 100755 (executable)
index 0000000..38c21a6
--- /dev/null
@@ -0,0 +1,4 @@
+: to be sourced in t3901 -- this is latin-1
+GIT_AUTHOR_NAME="Áéí óú" &&
+GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME &&
+export GIT_AUTHOR_NAME GIT_COMMITTER_NAME
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
new file mode 100755 (executable)
index 0000000..eda0e2d
--- /dev/null
@@ -0,0 +1,255 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='i18n settings and format-patch | am pipe'
+
+. ./test-lib.sh
+
+check_encoding () {
+       # Make sure characters are not corrupted
+       cnt="$1" header="$2" i=1 j=0 bad=0
+       while test "$i" -le $cnt
+       do
+               git format-patch --encoding=UTF-8 --stdout HEAD~$i..HEAD~$j |
+               grep "^From: =?UTF-8?q?=C3=81=C3=A9=C3=AD_=C3=B3=C3=BA?=" &&
+               git-cat-file commit HEAD~$j |
+               case "$header" in
+               8859)
+                       grep "^encoding ISO-8859-1" ;;
+               *)
+                       ! grep "^encoding ISO-8859-1" ;;
+               esac || {
+                       bad=1
+                       break
+               }
+               j=$i
+               i=$(($i+1))
+       done
+       (exit $bad)
+}
+
+test_expect_success setup '
+       git-repo-config i18n.commitencoding UTF-8 &&
+
+       # use UTF-8 in author and committer name to match the
+       # i18n.commitencoding settings
+       . ../t3901-utf8.txt &&
+
+       test_tick &&
+       echo "$GIT_AUTHOR_NAME" >mine &&
+       git add mine &&
+       git commit -s -m "Initial commit" &&
+
+       test_tick &&
+       echo Hello world >mine &&
+       git add mine &&
+       git commit -s -m "Second on main" &&
+
+       # the first commit on the side branch is UTF-8
+       test_tick &&
+       git checkout -b side master^ &&
+       echo Another file >yours &&
+       git add yours &&
+       git commit -s -m "Second on side" &&
+
+       # the second one on the side branch is ISO-8859-1
+       git-repo-config i18n.commitencoding ISO-8859-1 &&
+       # use author and committer name in ISO-8859-1 to match it.
+       . ../t3901-8859-1.txt &&
+       test_tick &&
+       echo Yet another >theirs &&
+       git add theirs &&
+       git commit -s -m "Third on side" &&
+
+       # Back to default
+       git-repo-config i18n.commitencoding UTF-8
+'
+
+test_expect_success 'format-patch output (ISO-8859-1)' '
+       git-repo-config i18n.logoutputencoding ISO-8859-1 &&
+
+       git format-patch --stdout master..HEAD^ >out-l1 &&
+       git format-patch --stdout HEAD^ >out-l2 &&
+       grep "^Content-Type: text/plain; charset=ISO-8859-1" out-l1 &&
+       grep "^From: =?ISO-8859-1?q?=C1=E9=ED_=F3=FA?=" out-l1 &&
+       grep "^Content-Type: text/plain; charset=ISO-8859-1" out-l2 &&
+       grep "^From: =?ISO-8859-1?q?=C1=E9=ED_=F3=FA?=" out-l2
+'
+
+test_expect_success 'format-patch output (UTF-8)' '
+       git repo-config i18n.logoutputencoding UTF-8 &&
+
+       git format-patch --stdout master..HEAD^ >out-u1 &&
+       git format-patch --stdout HEAD^ >out-u2 &&
+       grep "^Content-Type: text/plain; charset=UTF-8" out-u1 &&
+       grep "^From: =?UTF-8?q?=C3=81=C3=A9=C3=AD_=C3=B3=C3=BA?=" out-u1 &&
+       grep "^Content-Type: text/plain; charset=UTF-8" out-u2 &&
+       grep "^From: =?UTF-8?q?=C3=81=C3=A9=C3=AD_=C3=B3=C3=BA?=" out-u2
+'
+
+test_expect_success 'rebase (U/U)' '
+       # We want the result of rebase in UTF-8
+       git-repo-config i18n.commitencoding UTF-8 &&
+
+       # The test is about logoutputencoding not affecting the
+       # final outcome -- it is used internally to generate the
+       # patch and the log.
+
+       git repo-config i18n.logoutputencoding UTF-8 &&
+
+       # The result will be committed by GIT_COMMITTER_NAME --
+       # we want UTF-8 encoded name.
+       . ../t3901-utf8.txt &&
+       git checkout -b test &&
+       git-rebase master &&
+
+       check_encoding 2
+'
+
+test_expect_success 'rebase (U/L)' '
+       git-repo-config i18n.commitencoding UTF-8 &&
+       git repo-config i18n.logoutputencoding ISO-8859-1 &&
+       . ../t3901-utf8.txt &&
+
+       git reset --hard side &&
+       git-rebase master &&
+
+       check_encoding 2
+'
+
+test_expect_success 'rebase (L/L)' '
+       # In this test we want ISO-8859-1 encoded commits as the result
+       git-repo-config i18n.commitencoding ISO-8859-1 &&
+       git repo-config i18n.logoutputencoding ISO-8859-1 &&
+       . ../t3901-8859-1.txt &&
+
+       git reset --hard side &&
+       git-rebase master &&
+
+       check_encoding 2 8859
+'
+
+test_expect_success 'rebase (L/U)' '
+       # This is pathological -- use UTF-8 as intermediate form
+       # to get ISO-8859-1 results.
+       git-repo-config i18n.commitencoding ISO-8859-1 &&
+       git repo-config i18n.logoutputencoding UTF-8 &&
+       . ../t3901-8859-1.txt &&
+
+       git reset --hard side &&
+       git-rebase master &&
+
+       check_encoding 2 8859
+'
+
+test_expect_success 'cherry-pick(U/U)' '
+       # Both the commitencoding and logoutputencoding is set to UTF-8.
+
+       git-repo-config i18n.commitencoding UTF-8 &&
+       git repo-config i18n.logoutputencoding UTF-8 &&
+       . ../t3901-utf8.txt &&
+
+       git reset --hard master &&
+       git cherry-pick side^ &&
+       git cherry-pick side &&
+       EDITOR=: VISUAL=: git revert HEAD &&
+
+       check_encoding 3
+'
+
+test_expect_success 'cherry-pick(L/L)' '
+       # Both the commitencoding and logoutputencoding is set to ISO-8859-1
+
+       git-repo-config i18n.commitencoding ISO-8859-1 &&
+       git repo-config i18n.logoutputencoding ISO-8859-1 &&
+       . ../t3901-8859-1.txt &&
+
+       git reset --hard master &&
+       git cherry-pick side^ &&
+       git cherry-pick side &&
+       EDITOR=: VISUAL=: git revert HEAD &&
+
+       check_encoding 3 8859
+'
+
+test_expect_success 'cherry-pick(U/L)' '
+       # Commitencoding is set to UTF-8 but logoutputencoding is ISO-8859-1
+
+       git-repo-config i18n.commitencoding UTF-8 &&
+       git repo-config i18n.logoutputencoding ISO-8859-1 &&
+       . ../t3901-utf8.txt &&
+
+       git reset --hard master &&
+       git cherry-pick side^ &&
+       git cherry-pick side &&
+       EDITOR=: VISUAL=: git revert HEAD &&
+
+       check_encoding 3
+'
+
+test_expect_success 'cherry-pick(L/U)' '
+       # Again, the commitencoding is set to ISO-8859-1 but
+       # logoutputencoding is set to UTF-8.
+
+       git-repo-config i18n.commitencoding ISO-8859-1 &&
+       git repo-config i18n.logoutputencoding UTF-8 &&
+       . ../t3901-8859-1.txt &&
+
+       git reset --hard master &&
+       git cherry-pick side^ &&
+       git cherry-pick side &&
+       EDITOR=: VISUAL=: git revert HEAD &&
+
+       check_encoding 3 8859
+'
+
+test_expect_success 'rebase --merge (U/U)' '
+       git-repo-config i18n.commitencoding UTF-8 &&
+       git repo-config i18n.logoutputencoding UTF-8 &&
+       . ../t3901-utf8.txt &&
+
+       git reset --hard side &&
+       git-rebase --merge master &&
+
+       check_encoding 2
+'
+
+test_expect_success 'rebase --merge (U/L)' '
+       git-repo-config i18n.commitencoding UTF-8 &&
+       git repo-config i18n.logoutputencoding ISO-8859-1 &&
+       . ../t3901-utf8.txt &&
+
+       git reset --hard side &&
+       git-rebase --merge master &&
+
+       check_encoding 2
+'
+
+test_expect_success 'rebase --merge (L/L)' '
+       # In this test we want ISO-8859-1 encoded commits as the result
+       git-repo-config i18n.commitencoding ISO-8859-1 &&
+       git repo-config i18n.logoutputencoding ISO-8859-1 &&
+       . ../t3901-8859-1.txt &&
+
+       git reset --hard side &&
+       git-rebase --merge master &&
+
+       check_encoding 2 8859
+'
+
+test_expect_success 'rebase --merge (L/U)' '
+       # This is pathological -- use UTF-8 as intermediate form
+       # to get ISO-8859-1 results.
+       git-repo-config i18n.commitencoding ISO-8859-1 &&
+       git repo-config i18n.logoutputencoding UTF-8 &&
+       . ../t3901-8859-1.txt &&
+
+       git reset --hard side &&
+       git-rebase --merge master &&
+
+       check_encoding 2 8859
+'
+
+test_done
diff --git a/t/t3901-utf8.txt b/t/t3901-utf8.txt
new file mode 100755 (executable)
index 0000000..5f5205c
--- /dev/null
@@ -0,0 +1,4 @@
+: to be sourced in t3901 -- this is utf8
+GIT_AUTHOR_NAME="Áéí óú" &&
+GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME &&
+export GIT_AUTHOR_NAME GIT_COMMITTER_NAME
index 74f5c2a5755c8d29045498031c0bc3721a00b9c7..aa2c869e0e7ffcfba2e4349cf5af0e2badcb5516 100755 (executable)
@@ -50,12 +50,12 @@ test_expect_success 'setup separate repository lacking postimage' '
 
        git tar-tree initial initial | tar xf - &&
        (
-               cd initial && git init-db && git add .
+               cd initial && git init && git add .
        ) &&
 
        git tar-tree second second | tar xf - &&
        (
-               cd second && git init-db && git add .
+               cd second && git init && git add .
        )
 
 '
index de45ac4e0fcea5b7acab245af9876b58ec1ccea3..f51154745586b246ecb30caee3f2150441911e1d 100755 (executable)
@@ -44,7 +44,7 @@ test_expect_success \
     'unpack without delta' \
     "GIT_OBJECT_DIRECTORY=.git2/objects &&
      export GIT_OBJECT_DIRECTORY &&
-     git-init-db &&
+     git-init &&
      git-unpack-objects -n <test-1-${packname_1}.pack &&
      git-unpack-objects <test-1-${packname_1}.pack"
 
@@ -75,7 +75,7 @@ test_expect_success \
     'unpack with delta' \
     'GIT_OBJECT_DIRECTORY=.git2/objects &&
      export GIT_OBJECT_DIRECTORY &&
-     git-init-db &&
+     git-init &&
      git-unpack-objects -n <test-2-${packname_2}.pack &&
      git-unpack-objects <test-2-${packname_2}.pack'
 
@@ -100,7 +100,7 @@ test_expect_success \
     'use packed objects' \
     'GIT_OBJECT_DIRECTORY=.git2/objects &&
      export GIT_OBJECT_DIRECTORY &&
-     git-init-db &&
+     git-init &&
      cp test-1-${packname_1}.pack test-1-${packname_1}.idx .git2/objects/pack && {
         git-diff-tree --root -p $commit &&
         while read object
diff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh
new file mode 100755 (executable)
index 0000000..5a7232a
--- /dev/null
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Shawn Pearce
+#
+
+test_description='mmap sliding window tests'
+. ./test-lib.sh
+
+test_expect_success \
+    'setup' \
+    'rm -f .git/index*
+     for i in a b c
+     do
+         echo $i >$i &&
+         dd if=/dev/urandom bs=32k count=1 >>$i &&
+         git-update-index --add $i || return 1
+     done &&
+     echo d >d && cat c >>d && git-update-index --add d &&
+     tree=`git-write-tree` &&
+     commit1=`git-commit-tree $tree </dev/null` &&
+     git-update-ref HEAD $commit1 &&
+     git-repack -a -d &&
+     test "`git-count-objects`" = "0 objects, 0 kilobytes" &&
+     pack1=`ls .git/objects/pack/*.pack` &&
+     test -f "$pack1"'
+
+test_expect_success \
+    'verify-pack -v, defaults' \
+    'git-verify-pack -v "$pack1"'
+
+test_expect_success \
+    'verify-pack -v, packedGitWindowSize == 1 page' \
+    'git-repo-config core.packedGitWindowSize 512 &&
+     git-verify-pack -v "$pack1"'
+
+test_expect_success \
+    'verify-pack -v, packedGit{WindowSize,Limit} == 1 page' \
+    'git-repo-config core.packedGitWindowSize 512 &&
+     git-repo-config core.packedGitLimit 512 &&
+     git-verify-pack -v "$pack1"'
+
+test_expect_success \
+    'repack -a -d, packedGit{WindowSize,Limit} == 1 page' \
+    'git-repo-config core.packedGitWindowSize 512 &&
+     git-repo-config core.packedGitLimit 512 &&
+     commit2=`git-commit-tree $tree -p $commit1 </dev/null` &&
+     git-update-ref HEAD $commit2 &&
+     git-repack -a -d &&
+     test "`git-count-objects`" = "0 objects, 0 kilobytes" &&
+     pack2=`ls .git/objects/pack/*.pack` &&
+     test -f "$pack2"
+     test "$pack1" \!= "$pack2"'
+
+test_expect_success \
+    'verify-pack -v, defaults' \
+    'git-repo-config --unset core.packedGitWindowSize &&
+     git-repo-config --unset core.packedGitLimit &&
+     git-verify-pack -v "$pack2"'
+
+test_done
index 77c3c575d89ad5707ad8f6b22f62b58aef458104..ef78df67ea39e619f1118f427ad4dcc07e923bca 100755 (executable)
@@ -97,7 +97,7 @@ pull_to_client () {
 (
        mkdir client &&
        cd client &&
-       git-init-db 2>> log2.txt
+       git-init 2>> log2.txt
 )
 
 add A1
index 90eeeba2a31949a84daeb34b1996eaa4818321e6..3ce9446210bc56443e7a39273699983c1d9a65a4 100755 (executable)
@@ -73,7 +73,7 @@ test_expect_success 'fetch following tags' '
 
        mkdir four &&
        cd four &&
-       git init-db &&
+       git init &&
 
        git fetch .. :track &&
        git show-ref --verify refs/tags/anno &&
index f841574573545fa333e00b6815ba0df7d6931273..7eb37838bb788dfb68361bca95860fd532276deb 100755 (executable)
@@ -17,7 +17,7 @@ test_expect_success setup '
 test_expect_success 'pulling into void' '
        mkdir cloned &&
        cd cloned &&
-       git init-db &&
+       git init &&
        git pull ..
 '
 
index 2f4ff82e149c497d79583f6b431b29d597e4a1ae..344033249cc1ea3f7066d4d6007ade6cc1a2c5de 100755 (executable)
@@ -88,7 +88,7 @@ test_expect_success \
 
 test_expect_success "Michael Cassar's test case" '
        rm -fr .git papers partA &&
-       git init-db &&
+       git init &&
        mkdir -p papers/unsorted papers/all-papers partA &&
        echo a > papers/unsorted/Thesis.pdf &&
        echo b > partA/outline.txt &&
@@ -109,7 +109,7 @@ rm -fr papers partA path?
 
 test_expect_success "Sergey Vlasov's test case" '
        rm -fr .git &&
-       git init-db &&
+       git init &&
        mkdir ab &&
        date >ab.c &&
        date >ab/d &&
index 400c21cd49b6307eeef6d4d25cb34e612b1d0a71..8d2e2fec395a328f4bf6a65af3c7eba5a98cc133 100755 (executable)
@@ -17,6 +17,7 @@ test_expect_success 'initialize repo' "
        cd wc &&
        echo world >> trunk/readme &&
        svn commit -m 'another commit' &&
+       svn up &&
        svn mv -m 'rename to thunk' trunk thunk &&
        svn up &&
        echo goodbye >> thunk/readme &&
index 72ea2b259875e209752022f63bd57f9ca83a0cc4..8e3ee6cd7b38e07d3fd39904066c8e64f1d3642f 100755 (executable)
@@ -220,8 +220,8 @@ test_create_repo () {
        repo="$1"
        mkdir "$repo"
        cd "$repo" || error "Cannot setup test environment"
-       "$GIT_EXEC_PATH/git" init-db --template=$GIT_EXEC_PATH/templates/blt/ >/dev/null 2>&1 ||
-       error "cannot run git init-db -- have you built things yet?"
+       "$GIT_EXEC_PATH/git" init --template=$GIT_EXEC_PATH/templates/blt/ >/dev/null 2>&1 ||
+       error "cannot run git init -- have you built things yet?"
        mv .git/hooks .git/hooks-disabled
        cd "$owd"
 }
index 795aa08377aaa9a229affaa054e243251436f755..16595ef0a9022b1a9c677a36205526eb3567a93d 100644 (file)
@@ -68,7 +68,7 @@ int main(int argc, char *argv[])
        }
 
        fd = open (argv[4], O_WRONLY|O_CREAT|O_TRUNC, 0666);
-       if (fd < 0 || write(fd, out_buf, out_size) != out_size) {
+       if (fd < 0 || write_in_full(fd, out_buf, out_size) != out_size) {
                perror(argv[4]);
                return 1;
        }
diff --git a/trace.c b/trace.c
index 495e5ed92a68837a8abbd1569b2ccb59a3d50429..27fef868c4e2276638873cbda7d353cf4b8f67ed 100644 (file)
--- a/trace.c
+++ b/trace.c
@@ -101,7 +101,7 @@ void trace_printf(const char *format, ...)
        nfvasprintf(&trace_str, format, rest);
        va_end(rest);
 
-       write_or_whine(fd, trace_str, strlen(trace_str), err_msg);
+       write_or_whine_pipe(fd, trace_str, strlen(trace_str), err_msg);
 
        free(trace_str);
 
@@ -139,7 +139,7 @@ void trace_argv_printf(const char **argv, int count, const char *format, ...)
        strncpy(trace_str + format_len, argv_str, argv_len);
        strcpy(trace_str + trace_len - 1, "\n");
 
-       write_or_whine(fd, trace_str, trace_len, err_msg);
+       write_or_whine_pipe(fd, trace_str, trace_len, err_msg);
 
        free(argv_str);
        free(format_str);
index 22f4550b6ca4d2cb185fc19f6be414a8a0874a60..70f899957e8ce511f0f5a470e8b6927d58d1b63e 100644 (file)
@@ -199,10 +199,17 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
        int retval;
        void *tree;
        struct tree_desc t;
+       unsigned char root[20];
 
-       tree = read_object_with_reference(tree_sha1, tree_type, &t.size, NULL);
+       tree = read_object_with_reference(tree_sha1, tree_type, &t.size, root);
        if (!tree)
                return -1;
+
+       if (name[0] == '\0') {
+               hashcpy(sha1, root);
+               return 0;
+       }
+
        t.buf = tree;
        retval = find_tree_entry(&t, name, sha1, mode);
        free(tree);
index ccddf1d4b0cf7fd3a699d8b33cf5bc4c5c4435b7..d24acc2a67c4b7ba112bd192680b137f30a06003 100644 (file)
@@ -17,7 +17,7 @@ static char *create_temp_file(unsigned char *sha1)
        fd = mkstemp(path);
        if (fd < 0)
                die("unable to create temp-file");
-       if (write(fd, buf, size) != size)
+       if (write_in_full(fd, buf, size) != size)
                die("unable to write temp-file");
        close(fd);
        return path;
index c568ef066c9e7e0021468cfe7cb904f40ac477f1..3a466c6a3e66fdde2bb20b580db841c0600190bb 100644 (file)
@@ -55,6 +55,7 @@ static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
                /* emergency quit */
                fd = 2;
        if (fd == 2) {
+               /* XXX: are we happy to lose stuff here? */
                xwrite(fd, data, sz);
                return sz;
        }
@@ -242,7 +243,7 @@ static void create_pack_file(void)
                                        *cp++ = buffered;
                                        outsz++;
                                }
-                               sz = read(pu_pipe[0], cp,
+                               sz = xread(pu_pipe[0], cp,
                                          sizeof(data) - outsz);
                                if (0 < sz)
                                                ;
@@ -267,7 +268,7 @@ static void create_pack_file(void)
                                /* Status ready; we ship that in the side-band
                                 * or dump to the standard error.
                                 */
-                               sz = read(pe_pipe[0], progress,
+                               sz = xread(pe_pipe[0], progress,
                                          sizeof(progress));
                                if (0 < sz)
                                        send_client_data(2, progress, sz);
index 650f13fc012b14a6aaa62534727d4ef9cdce58bd..046e79d485feaa26d305868012f73b04088366b6 100644 (file)
@@ -1,67 +1,71 @@
 #include "cache.h"
 
-void write_or_die(int fd, const void *buf, size_t count)
+int read_in_full(int fd, void *buf, size_t count)
 {
-       const char *p = buf;
-       ssize_t written;
+       char *p = buf;
+       ssize_t total = 0;
 
        while (count > 0) {
-               written = xwrite(fd, p, count);
-               if (written == 0)
-                       die("disk full?");
-               else if (written < 0) {
-                       if (errno == EPIPE)
-                               exit(0);
-                       die("write error (%s)", strerror(errno));
-               }
-               count -= written;
-               p += written;
+               ssize_t loaded = xread(fd, p, count);
+               if (loaded <= 0)
+                       return total ? total : loaded;
+               count -= loaded;
+               p += loaded;
+               total += loaded;
        }
+
+       return total;
 }
 
-int write_or_whine(int fd, const void *buf, size_t count, const char *msg)
+int write_in_full(int fd, const void *buf, size_t count)
 {
        const char *p = buf;
-       ssize_t written;
+       ssize_t total = 0;
 
        while (count > 0) {
-               written = xwrite(fd, p, count);
-               if (written == 0) {
-                       fprintf(stderr, "%s: disk full?\n", msg);
-                       return 0;
-               }
-               else if (written < 0) {
-                       if (errno == EPIPE)
-                               exit(0);
-                       fprintf(stderr, "%s: write error (%s)\n",
-                               msg, strerror(errno));
-                       return 0;
+               size_t written = xwrite(fd, p, count);
+               if (written < 0)
+                       return -1;
+               if (!written) {
+                       errno = ENOSPC;
+                       return -1;
                }
                count -= written;
                p += written;
+               total += written;
        }
 
-       return 1;
+       return total;
 }
 
-int write_in_full(int fd, const void *buf, size_t count, const char *msg)
+void write_or_die(int fd, const void *buf, size_t count)
 {
-       const char *p = buf;
-       ssize_t written;
+       if (write_in_full(fd, buf, count) < 0) {
+               if (errno == EPIPE)
+                       exit(0);
+               die("write error (%s)", strerror(errno));
+       }
+}
 
-       while (count > 0) {
-               written = xwrite(fd, p, count);
-               if (written == 0) {
-                       fprintf(stderr, "%s: disk full?\n", msg);
-                       return 0;
-               }
-               else if (written < 0) {
-                       fprintf(stderr, "%s: write error (%s)\n",
-                               msg, strerror(errno));
-                       return 0;
-               }
-               count -= written;
-               p += written;
+int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg)
+{
+       if (write_in_full(fd, buf, count) < 0) {
+               if (errno == EPIPE)
+                       exit(0);
+               fprintf(stderr, "%s: write error (%s)\n",
+                       msg, strerror(errno));
+               return 0;
+       }
+
+       return 1;
+}
+
+int write_or_whine(int fd, const void *buf, size_t count, const char *msg)
+{
+       if (write_in_full(fd, buf, count) < 0) {
+               fprintf(stderr, "%s: write error (%s)\n",
+                       msg, strerror(errno));
+               return 0;
        }
 
        return 1;
index db427384ff454aa07e38c588537cbaa6b835dee6..b7250e43100244abcdaa5bc923574aad7f815d0b 100644 (file)
@@ -15,7 +15,13 @@ static char wt_status_colors[][COLOR_MAXLEN] = {
        "\033[31m", /* WT_STATUS_CHANGED: red */
        "\033[31m", /* WT_STATUS_UNTRACKED: red */
 };
-static const char* use_add_msg = "use \"git add file1 file2\" to include for commit";
+
+static const char use_add_msg[] =
+"use \"git add <file>...\" to update what will be committed";
+static const char use_add_rm_msg[] =
+"use \"git add/rm <file>...\" to update what will be committed";
+static const char use_add_to_include_msg[] =
+"use \"git add <file>...\" to include in what will be committed";
 
 static int parse_status_slot(const char *var, int offset)
 {
@@ -41,16 +47,29 @@ void wt_status_prepare(struct wt_status *s)
        unsigned char sha1[20];
        const char *head;
 
-       s->is_initial = get_sha1("HEAD", sha1) ? 1 : 0;
-
        head = resolve_ref("HEAD", sha1, 0, NULL);
        s->branch = head ? xstrdup(head) : NULL;
 
        s->reference = "HEAD";
        s->amend = 0;
        s->verbose = 0;
-       s->commitable = 0;
        s->untracked = 0;
+
+       s->commitable = 0;
+       s->workdir_dirty = 0;
+       s->workdir_untracked = 0;
+}
+
+static void wt_status_print_cached_header(const char *reference)
+{
+       const char *c = color(WT_STATUS_HEADER);
+       color_printf_ln(c, "# Changes to be committed:");
+       if (reference) {
+               color_printf_ln(c, "#   (use \"git reset %s <file>...\" to unstage)", reference);
+       } else {
+               color_printf_ln(c, "#   (use \"git rm --cached <file>...\" to unstage)");
+       }
+       color_printf_ln(c, "#");
 }
 
 static void wt_status_print_header(const char *main, const char *sub)
@@ -147,8 +166,7 @@ static void wt_status_print_updated_cb(struct diff_queue_struct *q,
                if (q->queue[i]->status == 'U')
                        continue;
                if (!shown_header) {
-                       wt_status_print_header("Added but not yet committed",
-                                       "will commit");
+                       wt_status_print_cached_header(s->reference);
                        s->commitable = 1;
                        shown_header = 1;
                }
@@ -162,9 +180,18 @@ static void wt_status_print_changed_cb(struct diff_queue_struct *q,
                         struct diff_options *options,
                         void *data)
 {
+       struct wt_status *s = data;
        int i;
-       if (q->nr)
-               wt_status_print_header("Changed but not added", use_add_msg);
+       if (q->nr) {
+               const char *msg = use_add_msg;
+               s->workdir_dirty = 1;
+               for (i = 0; i < q->nr; i++)
+                       if (q->queue[i]->status == DIFF_STATUS_DELETED) {
+                               msg = use_add_rm_msg;
+                               break;
+                       }
+               wt_status_print_header("Changed but not updated", msg);
+       }
        for (i = 0; i < q->nr; i++)
                wt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]);
        if (q->nr)
@@ -179,8 +206,7 @@ void wt_status_print_initial(struct wt_status *s)
        read_cache();
        if (active_nr) {
                s->commitable = 1;
-               wt_status_print_header("Added but not yet committed",
-                               "will commit");
+               wt_status_print_cached_header(NULL);
        }
        for (i = 0; i < active_nr; i++) {
                color_printf(color(WT_STATUS_HEADER), "#\t");
@@ -215,7 +241,7 @@ static void wt_status_print_changed(struct wt_status *s)
        run_diff_files(&rev, 0);
 }
 
-static void wt_status_print_untracked(const struct wt_status *s)
+static void wt_status_print_untracked(struct wt_status *s)
 {
        struct dir_struct dir;
        const char *x;
@@ -250,7 +276,9 @@ static void wt_status_print_untracked(const struct wt_status *s)
                                continue;
                }
                if (!shown_header) {
-                       wt_status_print_header("Untracked files", use_add_msg);
+                       s->workdir_untracked = 1;
+                       wt_status_print_header("Untracked files",
+                                              use_add_to_include_msg);
                        shown_header = 1;
                }
                color_printf(color(WT_STATUS_HEADER), "#\t");
@@ -271,9 +299,21 @@ static void wt_status_print_verbose(struct wt_status *s)
 
 void wt_status_print(struct wt_status *s)
 {
-       if (s->branch)
+       unsigned char sha1[20];
+       s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
+
+       if (s->branch) {
+               const char *on_what = "On branch ";
+               const char *branch_name = s->branch;
+               if (!strncmp(branch_name, "refs/heads/", 11))
+                       branch_name += 11;
+               else if (!strcmp(branch_name, "HEAD")) {
+                       branch_name = "";
+                       on_what = "Not currently on any branch.";
+               }
                color_printf_ln(color(WT_STATUS_HEADER),
-                       "# On branch %s", s->branch);
+                       "# %s%s", on_what, branch_name);
+       }
 
        if (s->is_initial) {
                color_printf_ln(color(WT_STATUS_HEADER), "#");
@@ -291,10 +331,18 @@ void wt_status_print(struct wt_status *s)
 
        if (s->verbose && !s->is_initial)
                wt_status_print_verbose(s);
-       if (!s->commitable)
-               printf("%s (%s)\n",
-                       s->amend ? "# No changes" : "nothing to commit",
-                       use_add_msg);
+       if (!s->commitable) {
+               if (s->amend)
+                       printf("# No changes\n");
+               else if (s->workdir_dirty)
+                       printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
+               else if (s->workdir_untracked)
+                       printf("nothing added to commit but untracked files present (use \"git add\" to track)\n");
+               else if (s->is_initial)
+                       printf("nothing to commit (create/copy files and use \"git add\" to track)\n");
+               else
+                       printf("nothing to commit (working directory clean)\n");
+       }
 }
 
 int git_status_config(const char *k, const char *v)
index 0a5a5b7ba9fc1d50c7dcab8220a4fb77ffecddb4..cfea4ae68805d74b825cae6935a4a0dd5de5134d 100644 (file)
@@ -12,10 +12,13 @@ struct wt_status {
        int is_initial;
        char *branch;
        const char *reference;
-       int commitable;
        int verbose;
        int amend;
        int untracked;
+       /* These are computed during processing of the individual sections */
+       int commitable;
+       int workdir_dirty;
+       int workdir_untracked;
 };
 
 int git_status_config(const char *var, const char *value);