Merge branch 'jc/detached-head'
authorJunio C Hamano <junkio@cox.net>
Fri, 12 Jan 2007 00:47:34 +0000 (16:47 -0800)
committerJunio C Hamano <junkio@cox.net>
Fri, 12 Jan 2007 00:47:34 +0000 (16:47 -0800)
* jc/detached-head:
git-checkout: handle local changes sanely when detaching HEAD
git-checkout: safety check for detached HEAD checks existing refs
git-checkout: fix branch name output from the command
git-checkout: safety when coming back from the detached HEAD state.
git-checkout: rewording comments regarding detached HEAD.
git-checkout: do not warn detaching HEAD when it is already detached.
Detached HEAD (experimental)
git-branch: show detached HEAD
git-status: show detached HEAD

72 files changed:
.gitignore
Documentation/git-am.txt
Documentation/git-applymbox.txt
Documentation/git-init-db.txt
Documentation/git-init.txt [new file with mode: 0644]
Documentation/git-mailinfo.txt
Documentation/git-pack-refs.txt
Documentation/git-remote.txt [new file with mode: 0644]
Documentation/git-svn.txt
Makefile
builtin-apply.c
builtin-archive.c
builtin-describe.c [new file with mode: 0644]
builtin-grep.c
builtin-mailinfo.c
builtin-pack-refs.c
builtin-prune.c
builtin-reflog.c
builtin-rerere.c
builtin-rm.c
builtin-tar-tree.c
builtin-upload-archive.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.c
dir.c
entry.c
fsck-objects.c
git-am.sh
git-applymbox.sh
git-clone.sh
git-commit.sh
git-compat-util.h
git-cvsserver.perl
git-rerere.perl [deleted file]
git-send-email.perl
git.c
gitweb/gitweb.perl
http-fetch.c
http-push.c
imap-send.c
index-pack.c
local-fetch.c
merge-recursive.c
path.c
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
sha1_file.c
ssh-fetch.c
ssh-upload.c
t/t1300-repo-config.sh
t/t1410-reflog.sh [new file with mode: 0755]
t/t3210-pack-refs.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 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 bc3ba14939054657bd82b8e3a8eb876e26ff14b0..5412135d763af3a8c68e18441b774654f07eadfd 100644 (file)
@@ -11,96 +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.
-
-Note that `git-init` is the same as `git-init-db`.
-
-
-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..e1fd688
--- /dev/null
@@ -0,0 +1,113 @@
+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
+because people are so used to it and many existing documents
+refer to it that way, and this will not change for some time to
+come.
+
+
+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 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
 ------
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 8df43cb819770fe8540bc392bede53ba16070831..1b013139af0359458c7abae2a12c8da608041849 100644 (file)
@@ -249,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>
@@ -321,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::
@@ -350,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.
 
@@ -486,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 d2a838276099c65ec7a45b4abd40ce614456c1df..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.
 #
@@ -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)
 
@@ -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;
diff --git a/builtin-describe.c b/builtin-describe.c
new file mode 100644 (file)
index 0000000..ad3b469
--- /dev/null
@@ -0,0 +1,176 @@
+#include "cache.h"
+#include "commit.h"
+#include "tag.h"
+#include "refs.h"
+#include "builtin.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 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 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 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 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..ca22452e64f99a24f5f13923d2d6a756bcf82ade 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)
@@ -54,56 +180,57 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
        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))
+       if (cb->cmd->stalefix &&
+           (!keep_entry(&old, osha1) || !keep_entry(&new, nsha1)))
                goto prune;
 
-       if ((timestamp < cb->expire_unreachable) &&
+       if ((timestamp < cb->cmd->expire_unreachable) &&
            (!cb->ref_commit ||
             (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 +261,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 +290,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 +309,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 +323,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 +338,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 +361,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 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 5218548ccfe1d68d702698dc54e22c59f70b8116..cbe398d3bdea235c2b1c22664b598699f77d37c9 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -432,10 +432,12 @@ extern char *git_commit_encoding;
 extern char *git_log_output_encoding;
 
 extern int copy_fd(int ifd, int ofd);
+extern int read_in_full(int fd, void *buf, size_t count);
 extern void read_or_die(int fd, void *buf, size_t count);
-extern int write_in_full(int fd, const void *buf, size_t count, const char *);
+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..496d37aa020871aed111002c2be0380366a70baa 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;
@@ -1010,7 +1012,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 +1020,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 +1086,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 +1102,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 5cbd130be2664258b488d8b4ca914550e10906a0..733fb1a34cf809c79c86bc6f7136e9d98ef6cdf9 100644 (file)
--- a/config.c
+++ b/config.c
@@ -464,7 +464,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;
@@ -478,37 +486,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,
@@ -648,9 +693,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;
@@ -729,25 +775,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);
@@ -770,6 +824,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)
@@ -800,6 +859,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] == '[') {
@@ -830,15 +890,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;
-}
diff --git a/diff.c b/diff.c
index e1b016f47f8eb37a89bdb6cb98435bad7d9dc111..ad476f7c689262ce54c761615eac1d82355b33f9 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1389,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;
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 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 7c0bb6084b332db7f7b6b9058db4d92b828730f0..59d663ba9e0c3828d11b442c6fd9536a87d937cb 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -2,7 +2,7 @@
 #
 # 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
@@ -105,7 +105,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 +128,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 +228,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 3d388de62a9ee212c8f54f3a5dc9a8b823bc8934..cf761b2c694d9e6cbcfc8cd1e075095d34ba857b 100755 (executable)
@@ -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..c2beb76fe436279422ff5212aed573fb821bad7b 100755 (executable)
@@ -628,7 +628,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 e023bf141363d87abfef358134e484ea85f32a15..8781e8e22d575c155967c8766f5d74835c0b1f78 100644 (file)
@@ -107,6 +107,11 @@ extern int git_munmap(void *start, size_t length);
 #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);
@@ -197,6 +202,8 @@ static inline void *xmmap(void *start, size_t length,
 {
        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)
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";
 
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 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";
        }
 
diff --git a/git.c b/git.c
index bf55499dc3d555dfaa853b0c6e4aec5439c3532e..9ce545d67693c8713b4254d5fa0e186723e348c2 100644 (file)
--- a/git.c
+++ b/git.c
@@ -220,6 +220,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 },
index 25e5079a89dd22e7c8eb2b7e3ac276681eae83b4..88af2e638061f54cfd7f6ffc6ae777ef2756c603 100755 (executable)
@@ -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..87a27e0379c87efc8f04a4cf826f1bc7da242d34 100644 (file)
@@ -517,7 +517,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)
diff --git a/path.c b/path.c
index 94ddd7eafcb11313f00dac964de520c535d202ae..c5d25a4b903bd92930df4004d30d4dffa6054456 100644 (file)
--- a/path.c
+++ b/path.c
@@ -114,7 +114,7 @@ int validate_headref(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);
 
        /*
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 29cf9abe6455313d94d169cc5f16aac21cad3228..c54a61187711087b98138b9598db6353457e4df3 100644 (file)
@@ -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 52057455a20cb4641005c9248b5499cca83c77c1..d189d8ad99de2dd7a65a2b10dc2e353055c55509 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);
@@ -968,7 +968,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 +987,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);
@@ -1097,7 +1097,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 +1106,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..1e3b29a429e99fd04da54a3af661d07ddb870688 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);
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;
                }
index d9622d95e7e3d7b91982ae60dbc36a7996856682..18dd89b50a23a4a99b51d8e99d0c67cace2a1a2d 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;
@@ -407,9 +413,9 @@ struct packed_git *packed_git;
 void pack_report()
 {
        fprintf(stderr,
-               "pack_report: getpagesize()            = %10lu\n"
-               "pack_report: core.packedGitWindowSize = %10lu\n"
-               "pack_report: core.packedGitLimit      = %10lu\n",
+               "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);
@@ -417,7 +423,8 @@ void pack_report()
                "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              = %10lu / %10lu\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,
@@ -1611,20 +1618,15 @@ 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;
+       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 (!len)
+               return 0;
+       size = write_in_full(fd, buf, len);
+       if (!size)
+               return error("file write: disk full");
+       if (size < 0)
+               return error("file write error (%s)", strerror(errno));
        return 0;
 }
 
@@ -1869,7 +1871,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);
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 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 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'
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 6db1d3123d1761dee4850e0a56a9b86b4bf1722a..488de721da8a316c06bf53e9091633e5ac415390 100644 (file)
 #include "cache.h"
 
-void read_or_die(int fd, void *buf, size_t count)
+int read_in_full(int fd, void *buf, size_t count)
 {
        char *p = buf;
-       ssize_t loaded;
+       ssize_t total = 0;
+       ssize_t loaded = 0;
 
        while (count > 0) {
                loaded = xread(fd, p, count);
-               if (loaded == 0)
-                       die("unexpected end of file");
-               else if (loaded < 0)
-                       die("read error (%s)", strerror(errno));
+               if (loaded <= 0) {
+                       if (total)
+                               return total;
+                       else
+                               return loaded;
+               }
                count -= loaded;
                p += loaded;
+               total += loaded;
        }
+
+       return total;
 }
 
-void write_or_die(int fd, const void *buf, size_t count)
+void read_or_die(int fd, void *buf, size_t count)
+{
+       ssize_t loaded;
+
+       if (!count)
+               return;
+       loaded = read_in_full(fd, buf, count);
+       if (loaded == 0)
+               die("unexpected end of file");
+       else if (loaded < 0)
+               die("read error (%s)", strerror(errno));
+}
+
+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)
-                       die("disk full?");
-               else if (written < 0) {
-                       if (errno == EPIPE)
-                               exit(0);
-                       die("write error (%s)", strerror(errno));
+               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 total;
 }
 
-int write_or_whine(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;
 
-       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;
-               }
-               count -= written;
-               p += written;
+       if (!count)
+               return;
+       written = write_in_full(fd, buf, count);
+       if (written == 0)
+               die("disk full?");
+       else if (written < 0) {
+               if (errno == EPIPE)
+                       exit(0);
+               die("write error (%s)", strerror(errno));
+       }
+}
+
+int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg)
+{
+       ssize_t written;
+
+       if (!count)
+               return 1;
+       written = write_in_full(fd, buf, 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;
        }
 
        return 1;
 }
 
-int write_in_full(int fd, const void *buf, size_t count, const char *msg)
+int write_or_whine(int fd, const void *buf, size_t count, const char *msg)
 {
-       const char *p = buf;
        ssize_t written;
 
-       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;
+       if (!count)
+               return 1;
+       written = write_in_full(fd, buf, 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;
        }
 
        return 1;
index 2a002baabd2eeb166ffbe6634d7b1ecf0b04bbc6..daba9a6105882c671edd7da21f0a9ae34fbdaaa4 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 <file>...\" to incrementally add content to 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)
 {
@@ -47,10 +53,11 @@ void wt_status_prepare(struct wt_status *s)
        s->reference = "HEAD";
        s->amend = 0;
        s->verbose = 0;
-       s->commitable = 0;
        s->untracked = 0;
 
-       s->workdir_clean = 1;
+       s->commitable = 0;
+       s->workdir_dirty = 0;
+       s->workdir_untracked = 0;
 }
 
 static void wt_status_print_cached_header(const char *reference)
@@ -176,8 +183,14 @@ static void wt_status_print_changed_cb(struct diff_queue_struct *q,
        struct wt_status *s = data;
        int i;
        if (q->nr) {
-               s->workdir_clean = 0;
-               wt_status_print_header("Changed but not added", use_add_msg);
+               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]);
@@ -263,8 +276,9 @@ static void wt_status_print_untracked(struct wt_status *s)
                                continue;
                }
                if (!shown_header) {
-                       s->workdir_clean = 0;
-                       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");
@@ -320,12 +334,14 @@ void wt_status_print(struct wt_status *s)
        if (!s->commitable) {
                if (s->amend)
                        printf("# No changes\n");
-               else if (s->workdir_clean)
-                       printf(s->is_initial
-                              ? "nothing to commit\n"
-                              : "nothing to commit (working directory matches HEAD)\n");
-               else
+               else if (s->workdir_dirty)
                        printf("no changes added to commit (use \"git add\" and/or \"git commit [-a|-i|-o]\")\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");
        }
 }
 
index 892a86c76abe69ce69327a10a5d491cc47f8e82b..cfea4ae68805d74b825cae6935a4a0dd5de5134d 100644 (file)
@@ -12,11 +12,13 @@ struct wt_status {
        int is_initial;
        char *branch;
        const char *reference;
-       int commitable;
        int verbose;
        int amend;
        int untracked;
-       int workdir_clean;
+       /* 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);