Merge branch 'jc/bare'
authorJunio C Hamano <junkio@cox.net>
Fri, 12 Jan 2007 00:50:36 +0000 (16:50 -0800)
committerJunio C Hamano <junkio@cox.net>
Fri, 12 Jan 2007 00:50:36 +0000 (16:50 -0800)
* jc/bare:
  Disallow working directory commands in a bare repository.
  git-fetch: allow updating the current branch in a bare repository.
  Introduce is_bare_repository() and core.bare configuration variable
  Move initialization of log_all_ref_updates

77 files changed:
.gitignore
Documentation/git-am.txt
Documentation/git-applymbox.txt
Documentation/git-cvsimport.txt
Documentation/git-init-db.txt
Documentation/git-init.txt [new file with mode: 0644]
Documentation/git-mailinfo.txt
Documentation/git-pack-refs.txt
Documentation/git-remote.txt [new file with mode: 0644]
Documentation/git-svn.txt
Makefile
builtin-apply.c
builtin-archive.c
builtin-branch.c
builtin-describe.c [moved from describe.c with 97% similarity]
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
diff.c
dir.c
entry.c
fsck-objects.c
git-am.sh
git-applymbox.sh
git-checkout.sh
git-clone.sh
git-commit.sh
git-compat-util.h
git-cvsimport.perl
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
setup.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/t3200-branch.sh
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 2904f12..6da1cdb 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 910457d..53e81cb 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 f74c6a4..95dc65a 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 d21d66b..5c402de 100644 (file)
@@ -90,7 +90,8 @@ If you need to pass multiple options, separate them with a comma.
        Print a short usage message and exit.
 
 -z <fuzz>::
-        Pass the timestamp fuzz factor to cvsps.
+       Pass the timestamp fuzz factor to cvsps, in seconds. If unset,
+       cvsps defaults to 300s.
 
 -s <subst>::
        Substitute the character "/" in branch names with <subst>
@@ -99,6 +100,18 @@ If you need to pass multiple options, separate them with a comma.
        CVS by default uses the unix username when writing its
        commit logs. Using this option and an author-conv-file
        in this format
+
+-a::
+       Import all commits, including recent ones. cvsimport by default
+       skips commits that have a timestamp less than 10 minutes ago.
+
+-S <regex>::
+       Skip paths matching the regex.
+
+-L <limit>::
+       Limit the number of commits imported. Workaround for cases where
+       cvsimport leaks memory.
+
 +
 ---------
        exon=Andreas Ericsson <ae@op5.se>
index bc3ba14..5412135 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 ea0a065..5088bbe 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 5da5105..464269f 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 8df43cb..1b01313 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 d2a8382..8432ab8 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 1c35837..54fd2cb 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 391cf43..32737d3 100644 (file)
@@ -137,7 +137,6 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
                if (err || !S_ISDIR(mode))
                        die("current working directory is untracked");
 
-               free(tree);
                tree = parse_tree_indirect(tree_sha1);
        }
        ar_args->tree = tree;
index d3df5a5..c760e18 100644 (file)
@@ -275,7 +275,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
        }
 }
 
-static void print_ref_list(int kinds, int verbose, int abbrev)
+static void print_ref_list(int kinds, int detached, int verbose, int abbrev)
 {
        int i;
        struct ref_list ref_list;
@@ -286,8 +286,20 @@ static void print_ref_list(int kinds, int verbose, int abbrev)
 
        qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
 
+       detached = (detached && (kinds & REF_LOCAL_BRANCH));
+       if (detached) {
+               struct ref_item item;
+               item.name = "(no branch)";
+               item.kind = REF_LOCAL_BRANCH;
+               hashcpy(item.sha1, head_sha1);
+               if (strlen(item.name) > ref_list.maxwidth)
+                             ref_list.maxwidth = strlen(item.name);
+               print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1);
+       }
+
        for (i = 0; i < ref_list.index; i++) {
-               int current = (ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
+               int current = !detached &&
+                       (ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
                        !strcmp(ref_list.list[i].name, head);
                print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose,
                               abbrev, current);
@@ -296,7 +308,8 @@ static void print_ref_list(int kinds, int verbose, int abbrev)
        free_ref_list(&ref_list);
 }
 
-static void create_branch(const char *name, const char *start,
+static void create_branch(const char *name, const char *start_name,
+                         unsigned char *start_sha1,
                          int force, int reflog)
 {
        struct ref_lock *lock;
@@ -315,9 +328,14 @@ static void create_branch(const char *name, const char *start,
                        die("Cannot force update the current branch.");
        }
 
-       if (get_sha1(start, sha1) ||
-           (commit = lookup_commit_reference(sha1)) == NULL)
-               die("Not a valid branch point: '%s'.", start);
+       if (start_sha1)
+               /* detached HEAD */
+               hashcpy(sha1, start_sha1);
+       else if (get_sha1(start_name, sha1))
+               die("Not a valid object name: '%s'.", start_name);
+
+       if ((commit = lookup_commit_reference(sha1)) == NULL)
+               die("Not a valid branch point: '%s'.", start_name);
        hashcpy(sha1, commit->object.sha1);
 
        lock = lock_any_ref_for_update(ref, NULL);
@@ -326,7 +344,8 @@ static void create_branch(const char *name, const char *start,
 
        if (reflog) {
                log_all_ref_updates = 1;
-               snprintf(msg, sizeof msg, "branch: Created from %s", start);
+               snprintf(msg, sizeof msg, "branch: Created from %s",
+                        start_name);
        }
 
        if (write_ref_sha1(lock, sha1, msg) < 0)
@@ -338,6 +357,9 @@ static void rename_branch(const char *oldname, const char *newname, int force)
        char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100];
        unsigned char sha1[20];
 
+       if (!oldname)
+               die("cannot rename the curren branch while not on any.");
+
        if (snprintf(oldref, sizeof(oldref), "refs/heads/%s", oldname) > sizeof(oldref))
                die("Old branchname too long");
 
@@ -367,7 +389,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 {
        int delete = 0, force_delete = 0, force_create = 0;
        int rename = 0, force_rename = 0;
-       int verbose = 0, abbrev = DEFAULT_ABBREV;
+       int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
        int reflog = 0;
        int kinds = REF_LOCAL_BRANCH;
        int i;
@@ -444,22 +466,27 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        head = xstrdup(resolve_ref("HEAD", head_sha1, 0, NULL));
        if (!head)
                die("Failed to resolve HEAD as a valid ref.");
-       if (strncmp(head, "refs/heads/", 11))
-               die("HEAD not found below refs/heads!");
-       head += 11;
+       if (!strcmp(head, "HEAD")) {
+               detached = 1;
+       }
+       else {
+               if (strncmp(head, "refs/heads/", 11))
+                       die("HEAD not found below refs/heads!");
+               head += 11;
+       }
 
        if (delete)
                return delete_branches(argc - i, argv + i, force_delete, kinds);
        else if (i == argc)
-               print_ref_list(kinds, verbose, abbrev);
+               print_ref_list(kinds, detached, verbose, abbrev);
        else if (rename && (i == argc - 1))
                rename_branch(head, argv[i], force_rename);
        else if (rename && (i == argc - 2))
                rename_branch(argv[i], argv[i + 1], force_rename);
        else if (i == argc - 1)
-               create_branch(argv[i], head, force_create, reflog);
+               create_branch(argv[i], head, head_sha1, force_create, reflog);
        else if (i == argc - 2)
-               create_branch(argv[i], argv[i + 1], force_create, reflog);
+               create_branch(argv[i], argv[i+1], NULL, force_create, reflog);
        else
                usage(builtin_branch_usage);
 
similarity index 97%
rename from describe.c
rename to builtin-describe.c
index f4029ee..ad3b469 100644 (file)
@@ -2,6 +2,7 @@
 #include "commit.h"
 #include "tag.h"
 #include "refs.h"
+#include "builtin.h"
 
 #define SEEN (1u << 0)
 
@@ -139,7 +140,7 @@ static void describe(const char *arg, int last_one)
        die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
 }
 
-int main(int argc, char **argv)
+int cmd_describe(int argc, const char **argv, const char *prefix)
 {
        int i;
 
@@ -161,7 +162,7 @@ int main(int argc, char **argv)
                        usage(describe_usage);
        }
 
-       setup_git_directory();
+       save_commit_buffer = 0;
 
        if (argc <= i)
                describe("HEAD", 1);
index 3b1b1cb..2bfbdb7 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 a67f3eb..583da38 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 8dc5b9e..6de7128 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 b469c43..6f0ba0d 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 d3f2f50..ca22452 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 079c0bd..318d959 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 5b078c4..d81f289 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 11e62fc..8055dda 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 e4156f8..48ae09e 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 df72d09..0b3c9f6 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 cff2569..c482c32 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -300,7 +300,7 @@ extern char *sha1_to_hex(const unsigned char *sha1);        /* static buffer result! */
 extern int read_ref(const char *filename, unsigned char *sha1);
 extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
 extern int create_symref(const char *ref, const char *refs_heads_master);
-extern int validate_symref(const char *ref);
+extern int validate_headref(const char *ref);
 
 extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
 extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);
@@ -433,10 +433,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 2a58175..496d37a 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 20e6ecc..b6082f5 100644 (file)
--- a/config.c
+++ b/config.c
@@ -469,7 +469,15 @@ static int store_aux(const char* key, const char* value)
        return 0;
 }
 
-static void store_write_section(int fd, const char* key)
+static int write_error()
+{
+       fprintf(stderr, "Failed to write new configuration file\n");
+
+       /* Same error code as "failed to rename". */
+       return 4;
+}
+
+static int store_write_section(int fd, const char* key)
 {
        const char *dot = strchr(key, '.');
        int len1 = store.baselen, len2 = -1;
@@ -483,37 +491,74 @@ static void store_write_section(int fd, const char* key)
                }
        }
 
-       write(fd, "[", 1);
-       write(fd, key, len1);
+       if (write_in_full(fd, "[", 1) != 1 ||
+           write_in_full(fd, key, len1) != len1)
+               return 0;
        if (len2 >= 0) {
-               write(fd, " \"", 2);
+               if (write_in_full(fd, " \"", 2) != 2)
+                       return 0;
                while (--len2 >= 0) {
                        unsigned char c = *++dot;
                        if (c == '"')
-                               write(fd, "\\", 1);
-                       write(fd, &c, 1);
+                               if (write_in_full(fd, "\\", 1) != 1)
+                                       return 0;
+                       if (write_in_full(fd, &c, 1) != 1)
+                               return 0;
                }
-               write(fd, "\"", 1);
+               if (write_in_full(fd, "\"", 1) != 1)
+                       return 0;
        }
-       write(fd, "]\n", 2);
+       if (write_in_full(fd, "]\n", 2) != 2)
+               return 0;
+
+       return 1;
 }
 
-static void store_write_pair(int fd, const char* key, const char* value)
+static int store_write_pair(int fd, const char* key, const char* value)
 {
        int i;
+       int length = strlen(key+store.baselen+1);
+       int quote = 0;
 
-       write(fd, "\t", 1);
-       write(fd, key+store.baselen+1,
-               strlen(key+store.baselen+1));
-       write(fd, " = ", 3);
+       /* Check to see if the value needs to be quoted. */
+       if (value[0] == ' ')
+               quote = 1;
+       for (i = 0; value[i]; i++)
+               if (value[i] == ';' || value[i] == '#')
+                       quote = 1;
+       if (value[i-1] == ' ')
+               quote = 1;
+
+       if (write_in_full(fd, "\t", 1) != 1 ||
+           write_in_full(fd, key+store.baselen+1, length) != length ||
+           write_in_full(fd, " = ", 3) != 3)
+               return 0;
+       if (quote && write_in_full(fd, "\"", 1) != 1)
+               return 0;
        for (i = 0; value[i]; i++)
                switch (value[i]) {
-               case '\n': write(fd, "\\n", 2); break;
-               case '\t': write(fd, "\\t", 2); break;
-               case '"': case '\\': write(fd, "\\", 1);
-               default: write(fd, value+i, 1);
-       }
-       write(fd, "\n", 1);
+               case '\n':
+                       if (write_in_full(fd, "\\n", 2) != 2)
+                               return 0;
+                       break;
+               case '\t':
+                       if (write_in_full(fd, "\\t", 2) != 2)
+                               return 0;
+                       break;
+               case '"':
+               case '\\':
+                       if (write_in_full(fd, "\\", 1) != 1)
+                               return 0;
+               default:
+                       if (write_in_full(fd, value+i, 1) != 1)
+                               return 0;
+                       break;
+               }
+       if (quote && write_in_full(fd, "\"", 1) != 1)
+               return 0;
+       if (write_in_full(fd, "\n", 1) != 1)
+               return 0;
+       return 1;
 }
 
 static int find_beginning_of_line(const char* contents, int size,
@@ -653,9 +698,10 @@ int git_config_set_multivar(const char* key, const char* value,
                }
 
                store.key = (char*)key;
-               store_write_section(fd, key);
-               store_write_pair(fd, key, value);
-       } else{
+               if (!store_write_section(fd, key) ||
+                   !store_write_pair(fd, key, value))
+                       goto write_err_out;
+       } else {
                struct stat st;
                char* contents;
                int i, copy_begin, copy_end, new_line = 0;
@@ -734,25 +780,33 @@ int git_config_set_multivar(const char* key, const char* value,
 
                        /* write the first part of the config */
                        if (copy_end > copy_begin) {
-                               write(fd, contents + copy_begin,
-                               copy_end - copy_begin);
-                               if (new_line)
-                                       write(fd, "\n", 1);
+                               if (write_in_full(fd, contents + copy_begin,
+                                                 copy_end - copy_begin) <
+                                   copy_end - copy_begin)
+                                       goto write_err_out;
+                               if (new_line &&
+                                   write_in_full(fd, "\n", 1) != 1)
+                                       goto write_err_out;
                        }
                        copy_begin = store.offset[i];
                }
 
                /* write the pair (value == NULL means unset) */
                if (value != NULL) {
-                       if (store.state == START)
-                               store_write_section(fd, key);
-                       store_write_pair(fd, key, value);
+                       if (store.state == START) {
+                               if (!store_write_section(fd, key))
+                                       goto write_err_out;
+                       }
+                       if (!store_write_pair(fd, key, value))
+                               goto write_err_out;
                }
 
                /* write the rest of the config */
                if (copy_begin < st.st_size)
-                       write(fd, contents + copy_begin,
-                               st.st_size - copy_begin);
+                       if (write_in_full(fd, contents + copy_begin,
+                                         st.st_size - copy_begin) <
+                           st.st_size - copy_begin)
+                               goto write_err_out;
 
                munmap(contents, st.st_size);
                unlink(config_filename);
@@ -775,6 +829,11 @@ out_free:
                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)
@@ -805,6 +864,7 @@ int git_config_rename_section(const char *old_name, const char *new_name)
 
        while (fgets(buf, sizeof(buf), config_file)) {
                int i;
+               int length;
                for (i = 0; buf[i] && isspace(buf[i]); i++)
                        ; /* do nothing */
                if (buf[i] == '[') {
@@ -835,15 +895,22 @@ int git_config_rename_section(const char *old_name, const char *new_name)
                                /* old_name matches */
                                ret++;
                                store.baselen = strlen(new_name);
-                               store_write_section(out_fd, new_name);
+                               if (!store_write_section(out_fd, new_name)) {
+                                       ret = write_error();
+                                       goto out;
+                               }
                                continue;
                        }
                }
-               write(out_fd, buf, strlen(buf));
+               length = strlen(buf);
+               if (write_in_full(out_fd, buf, length) != length) {
+                       ret = write_error();
+                       goto out;
+               }
        }
        fclose(config_file);
        if (close(out_fd) || commit_lock_file(lock) < 0)
-               ret = error("Cannot commit config file!");
+                       ret = error("Cannot commit config file!");
  out:
        free(config_filename);
        return ret;
index ede3ab2..d90ba81 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 b129b83..f039534 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/diff.c b/diff.c
index e1b016f..ad476f7 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 0338d6c..32b57f0 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 88df713..0ebf0f0 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 1cc3b39..81f00db 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 f50de61..1252f26 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
@@ -106,7 +106,7 @@ It does not apply to blobs recorded in its index."
 }
 
 prec=4
-dotest=.dotest sign= utf8= keep= skip= interactive= resolved= binary= ws= resolvemsg=
+dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= ws= resolvemsg=
 
 while case "$#" in 0) break;; esac
 do
@@ -129,7 +129,9 @@ do
        -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
        sign=t; shift ;;
        -u|--u|--ut|--utf|--utf8)
-       utf8=t; shift ;;
+       utf8=t; shift ;; # this is now default
+       --no-u|--no-ut|--no-utf|--no-utf8)
+       utf8=; shift ;;
        -k|--k|--ke|--kee|--keep)
        keep=t; shift ;;
 
@@ -227,6 +229,8 @@ fi
 if test "$(cat "$dotest/utf8")" = t
 then
        utf8=-u
+else
+       utf8=-n
 fi
 if test "$(cat "$dotest/keep")" = t
 then
index 5569fdc..1f68599 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 a5649a0..a2b8e4f 100755 (executable)
@@ -7,6 +7,7 @@ require_work_tree
 
 old_name=HEAD
 old=$(git-rev-parse --verify $old_name 2>/dev/null)
+oldbranch=$(git-symbolic-ref $old_name 2>/dev/null)
 new=
 new_name=
 force=
@@ -14,6 +15,8 @@ branch=
 newbranch=
 newbranch_log=
 merge=
+LF='
+'
 while [ "$#" != "0" ]; do
     arg="$1"
     shift
@@ -51,7 +54,7 @@ while [ "$#" != "0" ]; do
                                exit 1
                        fi
                        new="$rev"
-                       new_name="$arg^0"
+                       new_name="$arg"
                        if git-show-ref --verify --quiet -- "refs/heads/$arg"
                        then
                                branch="$arg"
@@ -140,23 +143,49 @@ fi
 
 [ -z "$new" ] && new=$old && new_name="$old_name"
 
-# If we don't have an old branch that we're switching to,
+# If we don't have an existing branch that we're switching to,
 # and we don't have a new branch name for the target we
-# are switching to, then we'd better just be checking out
-# what we already had
+# are switching to, then we are detaching our HEAD from any
+# branch.  However, if "git checkout HEAD" detaches the HEAD
+# from the current branch, even though that may be logically
+# correct, it feels somewhat funny.  More importantly, we do not
+# want "git checkout" nor "git checkout -f" to detach HEAD.
 
-[ -z "$branch$newbranch" ] &&
-       [ "$new" != "$old" ] &&
-       die "git checkout: provided reference cannot be checked out directly
+detached=
+detach_warn=
 
-  You need -b to associate a new branch with the wanted checkout. Example:
-  git checkout -b <new_branch_name> $arg
-"
+if test -z "$branch$newbranch" && test "$new" != "$old"
+then
+       detached="$new"
+       if test -n "$oldbranch"
+       then
+               detach_warn="warning: you are not on ANY branch anymore.
+If you meant to create a new branch from the commit, you need -b to
+associate a new branch with the wanted checkout.  Example:
+  git checkout -b <new_branch_name> $arg"
+       fi
+elif test -z "$oldbranch" && test -n "$branch"
+then
+       # Coming back...
+       if test -z "$force"
+       then
+               git show-ref -d -s | grep "$old" >/dev/null || {
+                       echo >&2 \
+"You are not on any branch and switching to branch '$new_name'
+may lose your changes.  At this point, you can do one of two things:
+ (1) Decide it is Ok and say 'git checkout -f $new_name';
+ (2) Start a new branch from the current commit, by saying
+     'git checkout -b <branch-name>'.
+Leaving your HEAD detached; not switching to branch '$new_name'."
+                       exit 1;
+               }
+       fi
+fi
 
 if [ "X$old" = X ]
 then
-       echo "warning: You do not appear to currently be on a branch." >&2
-       echo "warning: Forcing checkout of $new_name." >&2
+       echo >&2 "warning: You appear to be on a branch yet to be born."
+       echo >&2 "warning: Forcing checkout of $new_name."
        force=1
 fi
 
@@ -227,8 +256,25 @@ if [ "$?" -eq 0 ]; then
                git-update-ref -m "checkout: Created from $new_name" "refs/heads/$newbranch" $new || exit
                branch="$newbranch"
        fi
-       [ "$branch" ] &&
-       GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch"
+       if test -n "$branch"
+       then
+               GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch"
+       elif test -n "$detached"
+       then
+               # NEEDSWORK: we would want a command to detach the HEAD
+               # atomically, instead of this handcrafted command sequence.
+               # Perhaps:
+               #       git update-ref --detach HEAD $new
+               # or something like that...
+               #
+               echo "$detached" >"$GIT_DIR/HEAD.new" &&
+               mv "$GIT_DIR/HEAD.new" "$GIT_DIR/HEAD" ||
+                       die "Cannot detach HEAD"
+               if test -n "$detach_warn"
+               then
+                       echo >&2 "$detach_warn"
+               fi
+       fi
        rm -f "$GIT_DIR/MERGE_HEAD"
 else
        exit 1
index 3d388de..cf761b2 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 557b903..eddd863 100755 (executable)
@@ -629,7 +629,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 e023bf1..8781e8e 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 2686775..1018f4f 100755 (executable)
@@ -29,7 +29,7 @@ use IPC::Open2;
 $SIG{'PIPE'}="IGNORE";
 $ENV{'TZ'}="UTC";
 
-our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L);
+our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L, $opt_a);
 my (%conv_author_name, %conv_author_email);
 
 sub usage() {
@@ -37,7 +37,7 @@ sub usage() {
 Usage: ${\basename $0}     # fetch/update GIT from CVS
        [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
        [-p opts-for-cvsps] [-C GIT_repository] [-z fuzz] [-i] [-k] [-u]
-       [-s subst] [-m] [-M regex] [-S regex] [CVS_module]
+       [-s subst] [-a] [-m] [-M regex] [-S regex] [CVS_module]
 END
        exit(1);
 }
@@ -105,6 +105,8 @@ if ($opt_d) {
 }
 $opt_o ||= "origin";
 $opt_s ||= "-";
+$opt_a ||= 0;
+
 my $git_tree = $opt_C;
 $git_tree ||= ".";
 
@@ -573,9 +575,11 @@ if ($opt_A) {
 # run cvsps into a file unless we are getting
 # it passed as a file via $opt_P
 #
+my $cvspsfile;
 unless ($opt_P) {
        print "Running cvsps...\n" if $opt_v;
        my $pid = open(CVSPS,"-|");
+       my $cvspsfh;
        die "Cannot fork: $!\n" unless defined $pid;
        unless ($pid) {
                my @opt;
@@ -588,18 +592,18 @@ unless ($opt_P) {
                exec("cvsps","--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
                die "Could not start cvsps: $!\n";
        }
-       my ($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
-                                            DIR => File::Spec->tmpdir());
+       ($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
+                                         DIR => File::Spec->tmpdir());
        while (<CVSPS>) {
            print $cvspsfh $_;
        }
        close CVSPS;
        close $cvspsfh;
-       $opt_P = $cvspsfile;
+} else {
+       $cvspsfile = $opt_P;
 }
 
-
-open(CVS, "<$opt_P") or die $!;
+open(CVS, "<$cvspsfile") or die $!;
 
 ## cvsps output:
 #---------------------
@@ -829,7 +833,7 @@ while (<CVS>) {
                        $state = 11;
                        next;
                }
-               if ( $starttime - 300 - (defined $opt_z ? $opt_z : 300) <= $date) {
+               if (!$opt_a && $starttime - 300 - (defined $opt_z ? $opt_z : 300) <= $date) {
                        # skip if the commit is too recent
                        # that the cvsps default fuzz is 300s, we give ourselves another
                        # 300s just in case -- this also prevents skipping commits
@@ -934,6 +938,10 @@ while (<CVS>) {
 }
 commit() if $branch and $state != 11;
 
+unless ($opt_P) {
+       unlink($cvspsfile);
+}
+
 # The heuristic of repacking every 1024 commits can leave a
 # lot of unpacked data.  If there is more than 1MB worth of
 # not-packed objects, repack once more.
index df39512..a33a876 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 ba39d39..8dc2ee0 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 @@ foreach my $t (@files) {
        }
        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 692773b..72a1486 100644 (file)
--- a/git.c
+++ b/git.c
@@ -225,6 +225,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "cherry", cmd_cherry, RUN_SETUP },
                { "commit-tree", cmd_commit_tree, RUN_SETUP },
                { "count-objects", cmd_count_objects, RUN_SETUP },
+               { "describe", cmd_describe, RUN_SETUP },
                { "diff", cmd_diff, RUN_SETUP | USE_PAGER },
                { "diff-files", cmd_diff_files, RUN_SETUP },
                { "diff-index", cmd_diff_index, RUN_SETUP },
index 25e5079..88af2e6 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 396552d..67dfb0a 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 ecefdfd..0a15f53 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 ad91858..3eaf025 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 5f6d128..72e0962 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 7b6875c..cf99cb7 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 bac16f5..5237021 100644 (file)
@@ -110,35 +110,6 @@ static void output_commit_title(struct commit *commit)
        }
 }
 
-static const char *current_index_file = NULL;
-static const char *original_index_file;
-static const char *temporary_index_file;
-static int cache_dirty = 0;
-
-static int flush_cache(void)
-{
-       /* flush temporary index */
-       struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
-       int fd = hold_lock_file_for_update(lock, current_index_file, 1);
-       if (write_cache(fd, active_cache, active_nr) ||
-                       close(fd) || commit_lock_file(lock))
-               die ("unable to write %s", current_index_file);
-       discard_cache();
-       cache_dirty = 0;
-       return 0;
-}
-
-static void setup_index(int temp)
-{
-       current_index_file = temp ? temporary_index_file: original_index_file;
-       if (cache_dirty) {
-               discard_cache();
-               cache_dirty = 0;
-       }
-       unlink(temporary_index_file);
-       discard_cache();
-}
-
 static struct cache_entry *make_cache_entry(unsigned int mode,
                const unsigned char *sha1, const char *path, int stage, int refresh)
 {
@@ -167,9 +138,6 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
                const char *path, int stage, int refresh, int options)
 {
        struct cache_entry *ce;
-       if (!cache_dirty)
-               read_cache_from(current_index_file);
-       cache_dirty++;
        ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh);
        if (!ce)
                return error("cache_addinfo failed: %s", strerror(cache_errno));
@@ -187,26 +155,6 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
  */
 static int index_only = 0;
 
-static int git_read_tree(struct tree *tree)
-{
-       int rc;
-       struct object_list *trees = NULL;
-       struct unpack_trees_options opts;
-
-       if (cache_dirty)
-               die("read-tree with dirty cache");
-
-       memset(&opts, 0, sizeof(opts));
-       object_list_append(&tree->object, &trees);
-       rc = unpack_trees(trees, &opts);
-       cache_tree_free(&active_cache_tree);
-
-       if (rc == 0)
-               cache_dirty = 1;
-
-       return rc;
-}
-
 static int git_merge_trees(int index_only,
                           struct tree *common,
                           struct tree *head,
@@ -216,11 +164,6 @@ static int git_merge_trees(int index_only,
        struct object_list *trees = NULL;
        struct unpack_trees_options opts;
 
-       if (!cache_dirty) {
-               read_cache_from(current_index_file);
-               cache_dirty = 1;
-       }
-
        memset(&opts, 0, sizeof(opts));
        if (index_only)
                opts.index_only = 1;
@@ -236,39 +179,37 @@ static int git_merge_trees(int index_only,
 
        rc = unpack_trees(trees, &opts);
        cache_tree_free(&active_cache_tree);
-
-       cache_dirty = 1;
-
        return rc;
 }
 
+static int unmerged_index(void)
+{
+       int i;
+       for (i = 0; i < active_nr; i++) {
+               struct cache_entry *ce = active_cache[i];
+               if (ce_stage(ce))
+                       return 1;
+       }
+       return 0;
+}
+
 static struct tree *git_write_tree(void)
 {
        struct tree *result = NULL;
 
-       if (cache_dirty) {
-               unsigned i;
-               for (i = 0; i < active_nr; i++) {
-                       struct cache_entry *ce = active_cache[i];
-                       if (ce_stage(ce))
-                               return NULL;
-               }
-       } else
-               read_cache_from(current_index_file);
+       if (unmerged_index())
+               return NULL;
 
        if (!active_cache_tree)
                active_cache_tree = cache_tree();
 
        if (!cache_tree_fully_valid(active_cache_tree) &&
-                       cache_tree_update(active_cache_tree,
-                               active_cache, active_nr, 0, 0) < 0)
+           cache_tree_update(active_cache_tree,
+                             active_cache, active_nr, 0, 0) < 0)
                die("error building trees");
 
        result = lookup_tree(active_cache_tree->sha1);
 
-       flush_cache();
-       cache_dirty = 0;
-
        return result;
 }
 
@@ -331,10 +272,7 @@ static struct path_list *get_unmerged(void)
        int i;
 
        unmerged->strdup_paths = 1;
-       if (!cache_dirty) {
-               read_cache_from(current_index_file);
-               cache_dirty++;
-       }
+
        for (i = 0; i < active_nr; i++) {
                struct path_list_item *item;
                struct stage_data *e;
@@ -469,9 +407,6 @@ static int remove_file(int clean, const char *path, int no_wd)
        int update_working_directory = !index_only && !no_wd;
 
        if (update_cache) {
-               if (!cache_dirty)
-                       read_cache_from(current_index_file);
-               cache_dirty++;
                if (remove_file_from_cache(path))
                        return -1;
        }
@@ -517,7 +452,7 @@ static int mkdir_p(const char *path, unsigned long mode)
 static void flush_buffer(int fd, const char *buf, unsigned long size)
 {
        while (size > 0) {
-               long ret = xwrite(fd, buf, size);
+               long ret = write_in_full(fd, buf, size);
                if (ret < 0) {
                        /* Ignore epipe */
                        if (errno == EPIPE)
@@ -954,8 +889,6 @@ static int process_renames(struct path_list *a_renames,
        path_list_clear(&a_by_dst, 0);
        path_list_clear(&b_by_dst, 0);
 
-       if (cache_dirty)
-               flush_cache();
        return clean_merge;
 }
 
@@ -1083,9 +1016,6 @@ static int process_entry(const char *path, struct stage_data *entry,
        } else
                die("Fatal merge failure, shouldn't happen.");
 
-       if (cache_dirty)
-               flush_cache();
-
        return clean_merge;
 }
 
@@ -1110,9 +1040,7 @@ static int merge_trees(struct tree *head,
                    sha1_to_hex(head->object.sha1),
                    sha1_to_hex(merge->object.sha1));
 
-       *result = git_write_tree();
-
-       if (!*result) {
+       if (unmerged_index()) {
                struct path_list *entries, *re_head, *re_merge;
                int i;
                path_list_clear(&current_file_set, 1);
@@ -1138,10 +1066,6 @@ static int merge_trees(struct tree *head,
                path_list_clear(re_head, 0);
                path_list_clear(entries, 1);
 
-               if (clean || index_only)
-                       *result = git_write_tree();
-               else
-                       *result = NULL;
        } else {
                clean = 1;
                printf("merging of trees %s and %s resulted in %s\n",
@@ -1149,6 +1073,8 @@ static int merge_trees(struct tree *head,
                       sha1_to_hex(merge->object.sha1),
                       sha1_to_hex((*result)->object.sha1));
        }
+       if (index_only)
+               *result = git_write_tree();
 
        return clean;
 }
@@ -1173,10 +1099,10 @@ static int merge(struct commit *h1,
                 const char *branch1,
                 const char *branch2,
                 int call_depth /* =0 */,
-                struct commit *ancestor /* =None */,
+                struct commit_list *ca,
                 struct commit **result)
 {
-       struct commit_list *ca = NULL, *iter;
+       struct commit_list *iter;
        struct commit *merged_common_ancestors;
        struct tree *mrtree;
        int clean;
@@ -1185,10 +1111,10 @@ static int merge(struct commit *h1,
        output_commit_title(h1);
        output_commit_title(h2);
 
-       if (ancestor)
-               commit_list_insert(ancestor, &ca);
-       else
-               ca = reverse_commit_list(get_merge_bases(h1, h2, 1));
+       if (!ca) {
+               ca = get_merge_bases(h1, h2, 1);
+               ca = reverse_commit_list(ca);
+       }
 
        output("found %u common ancestor(s):", commit_list_count(ca));
        for (iter = ca; iter; iter = iter->next)
@@ -1214,6 +1140,7 @@ static int merge(struct commit *h1,
                 * merge_trees has always overwritten it: the commited
                 * "conflicts" were already resolved.
                 */
+               discard_cache();
                merge(merged_common_ancestors, iter->item,
                      "Temporary merge branch 1",
                      "Temporary merge branch 2",
@@ -1226,25 +1153,21 @@ static int merge(struct commit *h1,
                        die("merge returned no commit");
        }
 
+       discard_cache();
        if (call_depth == 0) {
-               setup_index(0 /* $GIT_DIR/index */);
+               read_cache();
                index_only = 0;
-       } else {
-               setup_index(1 /* temporary index */);
-               git_read_tree(h1->tree);
+       } else
                index_only = 1;
-       }
 
        clean = merge_trees(h1->tree, h2->tree, merged_common_ancestors->tree,
                            branch1, branch2, &mrtree);
 
-       if (!ancestor && (clean || index_only)) {
+       if (index_only) {
                *result = make_virtual_commit(mrtree, "merged tree");
                commit_list_insert(h1, &(*result)->parents);
                commit_list_insert(h2, &(*result)->parents->next);
-       } else
-               *result = NULL;
-
+       }
        return clean;
 }
 
@@ -1280,19 +1203,16 @@ static struct commit *get_ref(const char *ref)
 
 int main(int argc, char *argv[])
 {
-       static const char *bases[2];
+       static const char *bases[20];
        static unsigned bases_count = 0;
        int i, clean;
        const char *branch1, *branch2;
        struct commit *result, *h1, *h2;
+       struct commit_list *ca = NULL;
+       struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+       int index_fd;
 
        git_config(git_default_config); /* core.filemode */
-       original_index_file = getenv(INDEX_ENVIRONMENT);
-
-       if (!original_index_file)
-               original_index_file = xstrdup(git_path("index"));
-
-       temporary_index_file = xstrdup(git_path("mrg-rcrsv-tmp-idx"));
 
        if (argc < 4)
                die("Usage: %s <base>... -- <head> <remote> ...\n", argv[0]);
@@ -1316,18 +1236,18 @@ int main(int argc, char *argv[])
        branch2 = better_branch_name(branch2);
        printf("Merging %s with %s\n", branch1, branch2);
 
-       if (bases_count == 1) {
-               struct commit *ancestor = get_ref(bases[0]);
-               clean = merge(h1, h2, branch1, branch2, 0, ancestor, &result);
-       } else
-               clean = merge(h1, h2, branch1, branch2, 0, NULL, &result);
+       index_fd = hold_lock_file_for_update(lock, get_index_file(), 1);
 
-       if (cache_dirty)
-               flush_cache();
+       for (i = 0; i < bases_count; i++) {
+               struct commit *ancestor = get_ref(bases[i]);
+               ca = commit_list_insert(ancestor, &ca);
+       }
+       clean = merge(h1, h2, branch1, branch2, 0, ca, &result);
+
+       if (active_cache_changed &&
+           (write_cache(index_fd, active_cache, active_nr) ||
+            close(index_fd) || commit_lock_file(lock)))
+                       die ("unable to write %s", get_index_file());
 
        return clean ? 0: 1;
 }
-
-/*
-vim: sw=8 noet
-*/
diff --git a/path.c b/path.c
index 066f621..c5d25a4 100644 (file)
--- a/path.c
+++ b/path.c
@@ -90,10 +90,11 @@ int git_mkstemp(char *path, size_t len, const char *template)
 }
 
 
-int validate_symref(const char *path)
+int validate_headref(const char *path)
 {
        struct stat st;
        char *buf, buffer[256];
+       unsigned char sha1[20];
        int len, fd;
 
        if (lstat(path, &st) < 0)
@@ -113,20 +114,29 @@ int validate_symref(const char *path)
        fd = open(path, O_RDONLY);
        if (fd < 0)
                return -1;
-       len = read(fd, buffer, sizeof(buffer)-1);
+       len = read_in_full(fd, buffer, sizeof(buffer)-1);
        close(fd);
 
        /*
         * Is it a symbolic ref?
         */
-       if (len < 4 || memcmp("ref:", buffer, 4))
+       if (len < 4)
                return -1;
-       buf = buffer + 4;
-       len -= 4;
-       while (len && isspace(*buf))
-               buf++, len--;
-       if (len >= 5 && !memcmp("refs/", buf, 5))
+       if (!memcmp("ref:", buffer, 4)) {
+               buf = buffer + 4;
+               len -= 4;
+               while (len && isspace(*buf))
+                       buf++, len--;
+               if (len >= 5 && !memcmp("refs/", buf, 5))
+                       return 0;
+       }
+
+       /*
+        * Is this a detached HEAD?
+        */
+       if (!get_sha1_hex(buffer, sha1))
                return 0;
+
        return -1;
 }
 
@@ -241,7 +251,7 @@ char *enter_repo(char *path, int strict)
                return NULL;
 
        if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
-           validate_symref("HEAD") == 0) {
+           validate_headref("HEAD") == 0) {
                putenv("GIT_DIR=.");
                check_repository_format();
                return path;
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 29cf9ab..c54a611 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 499086b..689ac50 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);
@@ -971,7 +971,7 @@ static int log_ref_write(struct ref_lock *lock,
                        sha1_to_hex(sha1),
                        committer);
        }
-       written = len <= maxlen ? write(logfd, logrec, len) : -1;
+       written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1;
        free(logrec);
        close(logfd);
        if (written != len)
@@ -990,8 +990,8 @@ int write_ref_sha1(struct ref_lock *lock,
                unlock_ref(lock);
                return 0;
        }
-       if (write(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
-           write(lock->lock_fd, &term, 1) != 1
+       if (write_in_full(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
+           write_in_full(lock->lock_fd, &term, 1) != 1
                || close(lock->lock_fd) < 0) {
                error("Couldn't write %s", lock->lk->filename);
                unlock_ref(lock);
@@ -1100,7 +1100,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
        return 0;
 }
 
-void for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
+int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
 {
        const char *logfile;
        FILE *logfp;
@@ -1109,19 +1109,35 @@ void for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
        logfile = git_path("logs/%s", ref);
        logfp = fopen(logfile, "r");
        if (!logfp)
-               return;
+               return -1;
        while (fgets(buf, sizeof(buf), logfp)) {
                unsigned char osha1[20], nsha1[20];
-               int len;
+               char *email_end, *message;
+               unsigned long timestamp;
+               int len, ret, tz;
 
                /* old SP new SP name <email> SP time TAB msg LF */
                len = strlen(buf);
                if (len < 83 || buf[len-1] != '\n' ||
                    get_sha1_hex(buf, osha1) || buf[40] != ' ' ||
-                   get_sha1_hex(buf + 41, nsha1) || buf[81] != ' ')
+                   get_sha1_hex(buf + 41, nsha1) || buf[81] != ' ' ||
+                   !(email_end = strchr(buf + 82, '>')) ||
+                   email_end[1] != ' ' ||
+                   !(timestamp = strtoul(email_end + 2, &message, 10)) ||
+                   !message || message[0] != ' ' ||
+                   (message[1] != '+' && message[1] != '-') ||
+                   !isdigit(message[2]) || !isdigit(message[3]) ||
+                   !isdigit(message[4]) || !isdigit(message[5]) ||
+                   message[6] != '\t')
                        continue; /* corrupt? */
-               fn(osha1, nsha1, buf+82, cb_data);
+               email_end[1] = '\0';
+               tz = strtol(message + 1, NULL, 10);
+               message += 7;
+               ret = fn(osha1, nsha1, buf+82, timestamp, tz, message, cb_data);
+               if (ret)
+                       return ret;
        }
        fclose(logfp);
+       return 0;
 }
 
diff --git a/refs.h b/refs.h
index de43cc7..0e877e8 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 6e4ec46..1e3b29a 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 c195d08..6756264 100644 (file)
@@ -65,14 +65,14 @@ static int pack_objects(int fd, struct ref *refs)
                        memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);
                        buf[0] = '^';
                        buf[41] = '\n';
-                       if (!write_in_full(pipe_fd[1], buf, 42,
+                       if (!write_or_whine(pipe_fd[1], buf, 42,
                                                "send-pack: send refs"))
                                break;
                }
                if (!is_null_sha1(refs->new_sha1)) {
                        memcpy(buf, sha1_to_hex(refs->new_sha1), 40);
                        buf[40] = '\n';
-                       if (!write_in_full(pipe_fd[1], buf, 41,
+                       if (!write_or_whine(pipe_fd[1], buf, 41,
                                                "send-pack: send refs"))
                                break;
                }
diff --git a/setup.c b/setup.c
index 2ae57f7..cc97f9f 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -138,7 +138,8 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
  *    GIT_OBJECT_DIRECTORY environment variable
  *  - a refs/ directory
  *  - either a HEAD symlink or a HEAD file that is formatted as
- *    a proper "ref:".
+ *    a proper "ref:", or a regular file HEAD that has a properly
+ *    formatted sha1 object name.
  */
 static int is_git_directory(const char *suspect)
 {
@@ -161,7 +162,7 @@ static int is_git_directory(const char *suspect)
                return 0;
 
        strcpy(path + len, "/HEAD");
-       if (validate_symref(path))
+       if (validate_headref(path))
                return 0;
 
        return 1;
index d9622d9..18dd89b 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 b006c5c..4c172b6 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 0b52ae1..2f04572 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 a29caa0..60acdd3 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 a6ea0f6..bb80e42 100755 (executable)
@@ -48,7 +48,7 @@ test_expect_success \
         test ! -f .git/logs/refs/heads/d/e/f'
 
 cat >expect <<EOF
-0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000     checkout: Created from master^0
+0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000     checkout: Created from master
 EOF
 test_expect_success \
     'git checkout -b g/h/i -l should create a branch and a log' \
index b1e9f2e..16bdae4 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 795aa08..16595ef 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 495e5ed..27fef86 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 22f4550..70f8999 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 ccddf1d..d24acc2 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 c568ef0..3a466c6 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 6db1d31..488de72 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 c48127d..daba9a6 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");
@@ -288,9 +302,18 @@ void wt_status_print(struct wt_status *s)
        unsigned char sha1[20];
        s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
 
-       if (s->branch)
+       if (s->branch) {
+               const char *on_what = "On branch ";
+               const char *branch_name = s->branch;
+               if (!strncmp(branch_name, "refs/heads/", 11))
+                       branch_name += 11;
+               else if (!strcmp(branch_name, "HEAD")) {
+                       branch_name = "";
+                       on_what = "Not currently on any branch.";
+               }
                color_printf_ln(color(WT_STATUS_HEADER),
-                       "# On branch %s", s->branch);
+                       "# %s%s", on_what, branch_name);
+       }
 
        if (s->is_initial) {
                color_printf_ln(color(WT_STATUS_HEADER), "#");
@@ -311,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 892a86c..cfea4ae 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);