Merge branch 'lt/refs' into jc/for-each-ref-with-lt-refs
authorJunio C Hamano <junkio@cox.net>
Thu, 21 Sep 2006 07:29:37 +0000 (00:29 -0700)
committerJunio C Hamano <junkio@cox.net>
Thu, 21 Sep 2006 07:29:37 +0000 (00:29 -0700)
* lt/refs: (58 commits)
git-pack-refs --prune
pack-refs: do not pack symbolic refs.
Tell between packed, unpacked and symbolic refs.
Add callback data to for_each_ref() family.
symbolit-ref: fix resolve_ref conversion.
Fix broken sha1 locking
fsck-objects: adjust to resolve_ref() clean-up.
gitignore: git-pack-refs is a generated file.
wt-status: use simplified resolve_ref to find current branch
Fix t1400-update-ref test minimally
Enable the packed refs file format
Make ref resolution saner
Add support for negative refs
Start handling references internally as a sorted in-memory list
gitweb fix validating pg (page) parameter
git-repack(1): document --window and --depth
git-apply(1): document --unidiff-zero
gitweb: fix warnings in PATH_INFO code and add export_ok/strict_export
upload-archive: monitor child communication even more carefully.
gitweb: export options
...

70 files changed:
.gitignore
Documentation/config.txt
Documentation/git-apply.txt
Documentation/git-archive.txt [new file with mode: 0644]
Documentation/git-pack-objects.txt
Documentation/git-repack.txt
Documentation/git-upload-archive.txt [new file with mode: 0644]
Makefile
archive.h [new file with mode: 0644]
builtin-apply.c
builtin-archive.c [new file with mode: 0644]
builtin-count-objects.c
builtin-fmt-merge-msg.c
builtin-for-each-ref.c
builtin-init-db.c
builtin-name-rev.c
builtin-pack-objects.c
builtin-pack-refs.c [new file with mode: 0644]
builtin-prune-packed.c
builtin-prune.c
builtin-push.c
builtin-rev-list.c
builtin-rev-parse.c
builtin-runstatus.c [new file with mode: 0644]
builtin-show-branch.c
builtin-symbolic-ref.c
builtin-tar-tree.c
builtin-upload-archive.c [new file with mode: 0644]
builtin-zip-tree.c
builtin.h
cache.h
color.c [new file with mode: 0644]
color.h [new file with mode: 0644]
daemon.c
describe.c
diff.c
diff.h
dir.c
dir.h
fetch-clone.c
fetch-pack.c
fetch.c
fsck-objects.c
generate-cmdlist.sh
git-am.sh
git-checkout.sh
git-commit.sh
git.c
gitweb/gitweb.perl
http-fetch.c
http-push.c
list-objects.c [new file with mode: 0644]
list-objects.h [new file with mode: 0644]
receive-pack.c
refs.c
refs.h
revision.c
revision.h
send-pack.c
server-info.c
sha1_file.c
sha1_name.c
sideband.c [new file with mode: 0644]
sideband.h [new file with mode: 0644]
t/t1400-update-ref.sh
t/t3403-rebase-skip.sh
t/t4104-apply-boundary.sh [new file with mode: 0755]
upload-pack.c
wt-status.c [new file with mode: 0644]
wt-status.h [new file with mode: 0644]
index 0b08f37622352869f54febcdf5113e2df57fcf35..688a502d9c24a61cedd66c395b2c9f95a7711289 100644 (file)
@@ -8,6 +8,7 @@ git-apply
 git-applymbox
 git-applypatch
 git-archimport
+git-archive
 git-bisect
 git-branch
 git-cat-file
@@ -73,6 +74,7 @@ git-name-rev
 git-mv
 git-pack-redundant
 git-pack-objects
+git-pack-refs
 git-parse-remote
 git-patch-id
 git-peek-remote
@@ -95,6 +97,7 @@ git-rev-list
 git-rev-parse
 git-revert
 git-rm
+git-runstatus
 git-send-email
 git-send-pack
 git-sh-setup
@@ -119,6 +122,7 @@ git-unpack-objects
 git-update-index
 git-update-ref
 git-update-server-info
+git-upload-archive
 git-upload-pack
 git-upload-tar
 git-var
index ce722a2db07e2a41cfdebc235538cf4ca2a170ad..844cae4cf024b17206021d1c55819922cf99277d 100644 (file)
@@ -225,6 +225,20 @@ showbranch.default::
        The default set of branches for gitlink:git-show-branch[1].
        See gitlink:git-show-branch[1].
 
+status.color::
+       A boolean to enable/disable color in the output of
+       gitlink:git-status[1]. May be set to `true` (or `always`),
+       `false` (or `never`) or `auto`, in which case colors are used
+       only when the output is to a terminal. Defaults to false.
+
+status.color.<slot>::
+       Use customized color for status colorization. `<slot>` is
+       one of `header` (the header text of the status message),
+       `updated` (files which are updated but not committed),
+       `changed` (files which are changed but not updated in the index),
+       or `untracked` (files which are not tracked by git). The values of
+       these variables may be specified as in diff.color.<slot>.
+
 tar.umask::
        By default, gitlink:git-tar-tree[1] sets file and directories modes
        to 0666 or 0777. While this is both useful and acceptable for projects
index 0a6f7b32192df8a8a2266b0d302271a62c612b1f..d9137c74898f839a8ee5e79daf1080b2ec882dfd 100644 (file)
@@ -95,6 +95,16 @@ OPTIONS
        context exist they all must match.  By default no context is
        ever ignored.
 
+--unidiff-zero::
+       By default, gitlink:git-apply[1] expects that the patch being
+       applied is a unified diff with at least one line of context.
+       This provides good safety measures, but breaks down when
+       applying a diff generated with --unified=0. To bypass these
+       checks use '--unidiff-zero'.
++
+Note, for the reasons stated above usage of context-free patches are
+discouraged.
+
 --apply::
        If you use any of the options marked "Turns off
        'apply'" above, gitlink:git-apply[1] reads and outputs the
diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt
new file mode 100644 (file)
index 0000000..913528d
--- /dev/null
@@ -0,0 +1,100 @@
+git-archive(1)
+==============
+
+NAME
+----
+git-archive - Creates a archive of the files in the named tree
+
+
+SYNOPSIS
+--------
+'git-archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>]
+             [--remote=<repo>] <tree-ish> [path...]
+
+DESCRIPTION
+-----------
+Creates an archive of the specified format containing the tree
+structure for the named tree.  If <prefix> is specified it is
+prepended to the filenames in the archive.
+
+'git-archive' behaves differently when given a tree ID versus when
+given a commit ID or tag ID.  In the first case the current time is
+used as modification time of each file in the archive.  In the latter
+case the commit time as recorded in the referenced commit object is
+used instead.  Additionally the commit ID is stored in a global
+extended pax header if the tar format is used; it can be extracted
+using 'git-get-tar-commit-id'. In ZIP files it is stored as a file
+comment.
+
+OPTIONS
+-------
+
+--format=<fmt>::
+       Format of the resulting archive: 'tar', 'zip'...
+
+--list::
+       Show all available formats.
+
+--prefix=<prefix>/::
+       Prepend <prefix>/ to each filename in the archive.
+
+<extra>::
+       This can be any options that the archiver backend understand.
+
+--remote=<repo>::
+       Instead of making a tar archive from local repository,
+       retrieve a tar archive from a remote repository.
+
+<tree-ish>::
+       The tree or commit to produce an archive for.
+
+path::
+       If one or more paths are specified, include only these in the
+       archive, otherwise include all files and subdirectories.
+
+CONFIGURATION
+-------------
+By default, file and directories modes are set to 0666 or 0777 in tar
+archives.  It is possible to change this by setting the "umask" variable
+in the repository configuration as follows :
+
+[tar]
+        umask = 002    ;# group friendly
+
+The special umask value "user" indicates that the user's current umask
+will be used instead. The default value remains 0, which means world
+readable/writable files and directories.
+
+EXAMPLES
+--------
+git archive --format=tar --prefix=junk/ HEAD | (cd /var/tmp/ && tar xf -)::
+
+       Create a tar archive that contains the contents of the
+       latest commit on the current branch, and extracts it in
+       `/var/tmp/junk` directory.
+
+git archive --format=tar --prefix=git-1.4.0/ v1.4.0 | gzip >git-1.4.0.tar.gz::
+
+       Create a compressed tarball for v1.4.0 release.
+
+git archive --format=tar --prefix=git-1.4.0/ v1.4.0{caret}\{tree\} | gzip >git-1.4.0.tar.gz::
+
+       Create a compressed tarball for v1.4.0 release, but without a
+       global extended pax header.
+
+git archive --format=zip --prefix=git-docs/ HEAD:Documentation/ > git-1.4.0-docs.zip::
+
+       Put everything in the current head's Documentation/ directory
+       into 'git-1.4.0-docs.zip', with the prefix 'git-docs/'.
+
+Author
+------
+Written by Franck Bui-Huu and Rene Scharfe.
+
+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 4991f88c92b3d9f374f1a7e4a8aa8178a134f49f..d4661ddc2f84c1392b32ad3fdf5f4ebe5b465e00 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git-pack-objects' [-q] [--no-reuse-delta] [--non-empty]
        [--local] [--incremental] [--window=N] [--depth=N]
-       {--stdout | base-name} < object-list
+       [--revs [--unpacked | --all]*] [--stdout | base-name] < object-list
 
 
 DESCRIPTION
@@ -56,6 +56,24 @@ base-name::
        Write the pack contents (what would have been written to
        .pack file) out to the standard output.
 
+--revs::
+       Read the revision arguments from the standard input, instead of
+       individual object names.  The revision arguments are processed
+       the same way as gitlink:git-rev-list[1] with `--objects` flag
+       uses its `commit` arguments to build the list of objects it
+       outputs.  The objects on the resulting list are packed.
+
+--unpacked::
+       This implies `--revs`.  When processing the list of
+       revision arguments read from the standard input, limit
+       the objects packed to those that are not already packed.
+
+--all::
+       This implies `--revs`.  In addition to the list of
+       revision arguments read from the standard input, pretend
+       as if all refs under `$GIT_DIR/refs` are specifed to be
+       included.
+
 --window and --depth::
        These two options affects how the objects contained in
        the pack are stored using delta compression.  The
@@ -103,6 +121,7 @@ Documentation by Junio C Hamano
 
 See Also
 --------
+gitlink:git-rev-list[1]
 gitlink:git-repack[1]
 gitlink:git-prune-packed[1]
 
index 951622774af70e92590740c27fe021223f29442a..49f7e0a4a446b8e393b76f39136f28c74c085dff 100644 (file)
@@ -9,7 +9,7 @@ objects into pack files.
 
 SYNOPSIS
 --------
-'git-repack' [-a] [-d] [-f] [-l] [-n] [-q]
+'git-repack' [-a] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]
 
 DESCRIPTION
 -----------
@@ -56,6 +56,16 @@ OPTIONS
         Do not update the server information with
         `git update-server-info`.
 
+--window=[N], --depth=[N]::
+       These two options affects how the objects contained in the pack are
+       stored using delta compression. The objects are first internally
+       sorted by type, size and optionally names and compared against the
+       other objects within `--window` to see if using delta compression saves
+       space. `--depth` limits the maximum delta depth; making it too deep
+       affects the performance on the unpacker side, because delta data needs
+       to be applied that many times to get to the necessary object.
+
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
diff --git a/Documentation/git-upload-archive.txt b/Documentation/git-upload-archive.txt
new file mode 100644 (file)
index 0000000..388bb53
--- /dev/null
@@ -0,0 +1,37 @@
+git-upload-archive(1)
+====================
+
+NAME
+----
+git-upload-archive - Send archive
+
+
+SYNOPSIS
+--------
+'git-upload-archive' <directory>
+
+DESCRIPTION
+-----------
+Invoked by 'git-archive --remote' and sends a generated archive to the
+other end over the git protocol.
+
+This command is usually not invoked directly by the end user.  The UI
+for the protocol is on the 'git-archive' side, and the program pair
+is meant to be used to get an archive from a remote repository.
+
+OPTIONS
+-------
+<directory>::
+       The repository to get a tar archive from.
+
+Author
+------
+Written by Franck Bui-Huu.
+
+Documentation
+--------------
+Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
index f0e2e51c2ff5bfd3716fbb0318d948e4b487546e..54ac74e984ba3f7db0c7ac2f9d8fedfbe476382b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -126,6 +126,8 @@ GITWEB_CONFIG = gitweb_config.perl
 GITWEB_HOME_LINK_STR = projects
 GITWEB_SITENAME =
 GITWEB_PROJECTROOT = /pub/git
+GITWEB_EXPORT_OK =
+GITWEB_STRICT_EXPORT =
 GITWEB_BASE_URL =
 GITWEB_LIST =
 GITWEB_HOMETEXT = indextext.html
@@ -232,8 +234,8 @@ LIB_FILE=libgit.a
 XDIFF_LIB=xdiff/lib.a
 
 LIB_H = \
-       blob.h cache.h commit.h csum-file.h delta.h \
-       diff.h object.h pack.h pkt-line.h quote.h refs.h \
+       archive.h blob.h cache.h commit.h csum-file.h delta.h \
+       diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \
        run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
        tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h
 
@@ -245,17 +247,19 @@ DIFF_OBJS = \
 LIB_OBJS = \
        blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
        date.o diff-delta.o entry.o exec_cmd.o ident.o lockfile.o \
-       object.o pack-check.o patch-delta.o path.o pkt-line.o \
+       object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.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 \
        fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
-       write_or_die.o trace.o \
-       alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS)
+       write_or_die.o trace.o list-objects.o \
+       alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
+       color.o wt-status.o
 
 BUILTIN_OBJS = \
        builtin-add.o \
        builtin-apply.o \
+       builtin-archive.o \
        builtin-cat-file.o \
        builtin-checkout-index.o \
        builtin-check-ref-format.o \
@@ -286,6 +290,7 @@ BUILTIN_OBJS = \
        builtin-rev-list.o \
        builtin-rev-parse.o \
        builtin-rm.o \
+       builtin-runstatus.o \
        builtin-show-branch.o \
        builtin-stripspace.o \
        builtin-symbolic-ref.o \
@@ -293,10 +298,12 @@ BUILTIN_OBJS = \
        builtin-unpack-objects.o \
        builtin-update-index.o \
        builtin-update-ref.o \
+       builtin-upload-archive.o \
        builtin-upload-tar.o \
        builtin-verify-pack.o \
        builtin-write-tree.o \
-       builtin-zip-tree.o
+       builtin-zip-tree.o \
+       builtin-pack-refs.o
 
 GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
 LIBS = $(GITLIBS) -lz
@@ -632,6 +639,8 @@ gitweb/gitweb.cgi: gitweb/gitweb.perl
            -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
            -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
            -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
+           -e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \
+           -e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \
            -e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \
            -e 's|++GITWEB_LIST++|$(GITWEB_LIST)|g' \
            -e 's|++GITWEB_HOMETEXT++|$(GITWEB_HOMETEXT)|g' \
diff --git a/archive.h b/archive.h
new file mode 100644 (file)
index 0000000..16dcdb8
--- /dev/null
+++ b/archive.h
@@ -0,0 +1,47 @@
+#ifndef ARCHIVE_H
+#define ARCHIVE_H
+
+#define MAX_EXTRA_ARGS 32
+#define MAX_ARGS       (MAX_EXTRA_ARGS + 32)
+
+struct archiver_args {
+       const char *base;
+       struct tree *tree;
+       const unsigned char *commit_sha1;
+       time_t time;
+       const char **pathspec;
+       unsigned int verbose : 1;
+       void *extra;
+};
+
+typedef int (*write_archive_fn_t)(struct archiver_args *);
+
+typedef void *(*parse_extra_args_fn_t)(int argc, const char **argv);
+
+struct archiver {
+       const char *name;
+       struct archiver_args args;
+       write_archive_fn_t write_archive;
+       parse_extra_args_fn_t parse_extra;
+};
+
+extern struct archiver archivers[];
+
+extern int parse_archive_args(int argc,
+                             const char **argv,
+                             struct archiver *ar);
+
+extern void parse_treeish_arg(const char **treeish,
+                             struct archiver_args *ar_args,
+                             const char *prefix);
+
+extern void parse_pathspec_arg(const char **pathspec,
+                              struct archiver_args *args);
+/*
+ * Archive-format specific backends.
+ */
+extern int write_tar_archive(struct archiver_args *);
+extern int write_zip_archive(struct archiver_args *);
+extern void *parse_extra_zip_args(int argc, const char **argv);
+
+#endif /* ARCHIVE_H */
index 6e0864ce27619d1153575876e2a617a392906aed..25e90d8d29a8f55b20a98425edccfe49cad4c8e2 100644 (file)
@@ -27,6 +27,7 @@ static const char *prefix;
 static int prefix_length = -1;
 static int newfd = -1;
 
+static int unidiff_zero;
 static int p_value = 1;
 static int check_index;
 static int write_index;
@@ -854,11 +855,10 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
 }
 
 /*
- * Parse a unified diff. Note that this really needs
- * to parse each fragment separately, since the only
- * way to know the difference between a "---" that is
- * part of a patch, and a "---" that starts the next
- * patch is to look at the line counts..
+ * Parse a unified diff. Note that this really needs to parse each
+ * fragment separately, since the only way to know the difference
+ * between a "---" that is part of a patch, and a "---" that starts
+ * the next patch is to look at the line counts..
  */
 static int parse_fragment(char *line, unsigned long size, struct patch *patch, struct fragment *fragment)
 {
@@ -875,31 +875,14 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
        leading = 0;
        trailing = 0;
 
-       if (patch->is_new < 0) {
-               patch->is_new =  !oldlines;
-               if (!oldlines)
-                       patch->old_name = NULL;
-       }
-       if (patch->is_delete < 0) {
-               patch->is_delete = !newlines;
-               if (!newlines)
-                       patch->new_name = NULL;
-       }
-
-       if (patch->is_new && oldlines)
-               return error("new file depends on old contents");
-       if (patch->is_delete != !newlines) {
-               if (newlines)
-                       return error("deleted file still has contents");
-               fprintf(stderr, "** warning: file %s becomes empty but is not deleted\n", patch->new_name);
-       }
-
        /* Parse the thing.. */
        line += len;
        size -= len;
        linenr++;
        added = deleted = 0;
-       for (offset = len; size > 0; offset += len, size -= len, line += len, linenr++) {
+       for (offset = len;
+            0 < size;
+            offset += len, size -= len, line += len, linenr++) {
                if (!oldlines && !newlines)
                        break;
                len = linelen(line, size);
@@ -972,12 +955,18 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
 
        patch->lines_added += added;
        patch->lines_deleted += deleted;
+
+       if (0 < patch->is_new && oldlines)
+               return error("new file depends on old contents");
+       if (0 < patch->is_delete && newlines)
+               return error("deleted file still has contents");
        return offset;
 }
 
 static int parse_single_patch(char *line, unsigned long size, struct patch *patch)
 {
        unsigned long offset = 0;
+       unsigned long oldlines = 0, newlines = 0, context = 0;
        struct fragment **fragp = &patch->fragments;
 
        while (size > 4 && !memcmp(line, "@@ -", 4)) {
@@ -988,9 +977,11 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc
                len = parse_fragment(line, size, patch, fragment);
                if (len <= 0)
                        die("corrupt patch at line %d", linenr);
-
                fragment->patch = line;
                fragment->size = len;
+               oldlines += fragment->oldlines;
+               newlines += fragment->newlines;
+               context += fragment->leading + fragment->trailing;
 
                *fragp = fragment;
                fragp = &fragment->next;
@@ -999,6 +990,46 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc
                line += len;
                size -= len;
        }
+
+       /*
+        * If something was removed (i.e. we have old-lines) it cannot
+        * be creation, and if something was added it cannot be
+        * deletion.  However, the reverse is not true; --unified=0
+        * patches that only add are not necessarily creation even
+        * though they do not have any old lines, and ones that only
+        * delete are not necessarily deletion.
+        *
+        * Unfortunately, a real creation/deletion patch do _not_ have
+        * any context line by definition, so we cannot safely tell it
+        * apart with --unified=0 insanity.  At least if the patch has
+        * more than one hunk it is not creation or deletion.
+        */
+       if (patch->is_new < 0 &&
+           (oldlines || (patch->fragments && patch->fragments->next)))
+               patch->is_new = 0;
+       if (patch->is_delete < 0 &&
+           (newlines || (patch->fragments && patch->fragments->next)))
+               patch->is_delete = 0;
+       if (!unidiff_zero || context) {
+               /* If the user says the patch is not generated with
+                * --unified=0, or if we have seen context lines,
+                * then not having oldlines means the patch is creation,
+                * and not having newlines means the patch is deletion.
+                */
+               if (patch->is_new < 0 && !oldlines)
+                       patch->is_new = 1;
+               if (patch->is_delete < 0 && !newlines)
+                       patch->is_delete = 1;
+       }
+
+       if (0 < patch->is_new && oldlines)
+               die("new file %s depends on old contents", patch->new_name);
+       if (0 < patch->is_delete && newlines)
+               die("deleted file %s still has contents", patch->old_name);
+       if (!patch->is_delete && !newlines && context)
+               fprintf(stderr, "** warning: file %s becomes empty but "
+                       "is not deleted\n", patch->new_name);
+
        return offset;
 }
 
@@ -1556,9 +1587,19 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
        /*
         * If we don't have any leading/trailing data in the patch,
         * we want it to match at the beginning/end of the file.
+        *
+        * But that would break if the patch is generated with
+        * --unified=0; sane people wouldn't do that to cause us
+        * trouble, but we try to please not so sane ones as well.
         */
-       match_beginning = !leading && (frag->oldpos == 1);
-       match_end = !trailing;
+       if (unidiff_zero) {
+               match_beginning = (!leading && !frag->oldpos);
+               match_end = 0;
+       }
+       else {
+               match_beginning = !leading && (frag->oldpos == 1);
+               match_end = !trailing;
+       }
 
        lines = 0;
        pos = frag->newpos;
@@ -1804,7 +1845,7 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
        patch->result = desc.buffer;
        patch->resultsize = desc.size;
 
-       if (patch->is_delete && patch->resultsize)
+       if (0 < patch->is_delete && patch->resultsize)
                return error("removal patch leaves file contents");
 
        return 0;
@@ -1876,7 +1917,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
                                old_name, st_mode, patch->old_mode);
        }
 
-       if (new_name && prev_patch && prev_patch->is_delete &&
+       if (new_name && prev_patch && 0 < prev_patch->is_delete &&
            !strcmp(prev_patch->old_name, new_name))
                /* A type-change diff is always split into a patch to
                 * delete old, immediately followed by a patch to
@@ -1889,7 +1930,8 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
        else
                ok_if_exists = 0;
 
-       if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) {
+       if (new_name &&
+           ((0 < patch->is_new) | (0 < patch->is_rename) | patch->is_copy)) {
                if (check_index &&
                    cache_name_pos(new_name, strlen(new_name)) >= 0 &&
                    !ok_if_exists)
@@ -1906,7 +1948,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
                                return error("%s: %s", new_name, strerror(errno));
                }
                if (!patch->new_mode) {
-                       if (patch->is_new)
+                       if (0 < patch->is_new)
                                patch->new_mode = S_IFREG | 0644;
                        else
                                patch->new_mode = patch->old_mode;
@@ -1957,7 +1999,7 @@ static void show_index_list(struct patch *list)
                const char *name;
 
                name = patch->old_name ? patch->old_name : patch->new_name;
-               if (patch->is_new)
+               if (0 < patch->is_new)
                        sha1_ptr = null_sha1;
                else if (get_sha1(patch->old_sha1_prefix, sha1))
                        die("sha1 information is lacking or useless (%s).",
@@ -2543,6 +2585,10 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                        apply_in_reverse = 1;
                        continue;
                }
+               if (!strcmp(arg, "--unidiff-zero")) {
+                       unidiff_zero = 1;
+                       continue;
+               }
                if (!strcmp(arg, "--reject")) {
                        apply = apply_with_reject = apply_verbosely = 1;
                        continue;
diff --git a/builtin-archive.c b/builtin-archive.c
new file mode 100644 (file)
index 0000000..6dabdee
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2006 Franck Bui-Huu
+ * Copyright (c) 2006 Rene Scharfe
+ */
+#include <time.h>
+#include "cache.h"
+#include "builtin.h"
+#include "archive.h"
+#include "commit.h"
+#include "tree-walk.h"
+#include "exec_cmd.h"
+#include "pkt-line.h"
+#include "sideband.h"
+
+static const char archive_usage[] = \
+"git-archive --format=<fmt> [--prefix=<prefix>/] [--verbose] [<extra>] <tree-ish> [path...]";
+
+struct archiver archivers[] = {
+       {
+               .name           = "tar",
+               .write_archive  = write_tar_archive,
+       },
+       {
+               .name           = "zip",
+               .write_archive  = write_zip_archive,
+               .parse_extra    = parse_extra_zip_args,
+       },
+};
+
+static int run_remote_archiver(const char *remote, int argc,
+                              const char **argv)
+{
+       char *url, buf[LARGE_PACKET_MAX];
+       int fd[2], i, len, rv;
+       pid_t pid;
+       const char *exec = "git-upload-archive";
+       int exec_at = 0;
+
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (!strncmp("--exec=", arg, 7)) {
+                       if (exec_at)
+                               die("multiple --exec specified");
+                       exec = arg + 7;
+                       exec_at = i;
+                       break;
+               }
+       }
+
+       url = xstrdup(remote);
+       pid = git_connect(fd, url, exec);
+       if (pid < 0)
+               return pid;
+
+       for (i = 1; i < argc; i++) {
+               if (i == exec_at)
+                       continue;
+               packet_write(fd[1], "argument %s\n", argv[i]);
+       }
+       packet_flush(fd[1]);
+
+       len = packet_read_line(fd[0], buf, sizeof(buf));
+       if (!len)
+               die("git-archive: expected ACK/NAK, got EOF");
+       if (buf[len-1] == '\n')
+               buf[--len] = 0;
+       if (strcmp(buf, "ACK")) {
+               if (len > 5 && !strncmp(buf, "NACK ", 5))
+                       die("git-archive: NACK %s", buf + 5);
+               die("git-archive: protocol error");
+       }
+
+       len = packet_read_line(fd[0], buf, sizeof(buf));
+       if (len)
+               die("git-archive: expected a flush");
+
+       /* Now, start reading from fd[0] and spit it out to stdout */
+       rv = recv_sideband("archive", fd[0], 1, 2, buf, sizeof(buf));
+       close(fd[0]);
+       rv |= finish_connect(pid);
+
+       return !!rv;
+}
+
+static int init_archiver(const char *name, struct archiver *ar)
+{
+       int rv = -1, i;
+
+       for (i = 0; i < ARRAY_SIZE(archivers); i++) {
+               if (!strcmp(name, archivers[i].name)) {
+                       memcpy(ar, &archivers[i], sizeof(struct archiver));
+                       rv = 0;
+                       break;
+               }
+       }
+       return rv;
+}
+
+void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args)
+{
+       ar_args->pathspec = get_pathspec(ar_args->base, pathspec);
+}
+
+void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
+                      const char *prefix)
+{
+       const char *name = argv[0];
+       const unsigned char *commit_sha1;
+       time_t archive_time;
+       struct tree *tree;
+       struct commit *commit;
+       unsigned char sha1[20];
+
+       if (get_sha1(name, sha1))
+               die("Not a valid object name");
+
+       commit = lookup_commit_reference_gently(sha1, 1);
+       if (commit) {
+               commit_sha1 = commit->object.sha1;
+               archive_time = commit->date;
+       } else {
+               commit_sha1 = NULL;
+               archive_time = time(NULL);
+       }
+
+       tree = parse_tree_indirect(sha1);
+       if (tree == NULL)
+               die("not a tree object");
+
+       if (prefix) {
+               unsigned char tree_sha1[20];
+               unsigned int mode;
+               int err;
+
+               err = get_tree_entry(tree->object.sha1, prefix,
+                                    tree_sha1, &mode);
+               if (err || !S_ISDIR(mode))
+                       die("current working directory is untracked");
+
+               free(tree);
+               tree = parse_tree_indirect(tree_sha1);
+       }
+       ar_args->tree = tree;
+       ar_args->commit_sha1 = commit_sha1;
+       ar_args->time = archive_time;
+}
+
+int parse_archive_args(int argc, const char **argv, struct archiver *ar)
+{
+       const char *extra_argv[MAX_EXTRA_ARGS];
+       int extra_argc = 0;
+       const char *format = NULL; /* might want to default to "tar" */
+       const char *base = "";
+       int verbose = 0;
+       int i;
+
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+
+               if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) {
+                       for (i = 0; i < ARRAY_SIZE(archivers); i++)
+                               printf("%s\n", archivers[i].name);
+                       exit(0);
+               }
+               if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) {
+                       verbose = 1;
+                       continue;
+               }
+               if (!strncmp(arg, "--format=", 9)) {
+                       format = arg + 9;
+                       continue;
+               }
+               if (!strncmp(arg, "--prefix=", 9)) {
+                       base = arg + 9;
+                       continue;
+               }
+               if (!strcmp(arg, "--")) {
+                       i++;
+                       break;
+               }
+               if (arg[0] == '-') {
+                       if (extra_argc > MAX_EXTRA_ARGS - 1)
+                               die("Too many extra options");
+                       extra_argv[extra_argc++] = arg;
+                       continue;
+               }
+               break;
+       }
+
+       /* We need at least one parameter -- tree-ish */
+       if (argc - 1 < i)
+               usage(archive_usage);
+       if (!format)
+               die("You must specify an archive format");
+       if (init_archiver(format, ar) < 0)
+               die("Unknown archive format '%s'", format);
+
+       if (extra_argc) {
+               if (!ar->parse_extra)
+                       die("'%s' format does not handle %s",
+                           ar->name, extra_argv[0]);
+               ar->args.extra = ar->parse_extra(extra_argc, extra_argv);
+       }
+       ar->args.verbose = verbose;
+       ar->args.base = base;
+
+       return i;
+}
+
+static const char *extract_remote_arg(int *ac, const char **av)
+{
+       int ix, iy, cnt = *ac;
+       int no_more_options = 0;
+       const char *remote = NULL;
+
+       for (ix = iy = 1; ix < cnt; ix++) {
+               const char *arg = av[ix];
+               if (!strcmp(arg, "--"))
+                       no_more_options = 1;
+               if (!no_more_options) {
+                       if (!strncmp(arg, "--remote=", 9)) {
+                               if (remote)
+                                       die("Multiple --remote specified");
+                               remote = arg + 9;
+                               continue;
+                       }
+                       if (arg[0] != '-')
+                               no_more_options = 1;
+               }
+               if (ix != iy)
+                       av[iy] = arg;
+               iy++;
+       }
+       if (remote) {
+               av[--cnt] = NULL;
+               *ac = cnt;
+       }
+       return remote;
+}
+
+int cmd_archive(int argc, const char **argv, const char *prefix)
+{
+       struct archiver ar;
+       int tree_idx;
+       const char *remote = NULL;
+
+       remote = extract_remote_arg(&argc, argv);
+       if (remote)
+               return run_remote_archiver(remote, argc, argv);
+
+       setlinebuf(stderr);
+
+       memset(&ar, 0, sizeof(ar));
+       tree_idx = parse_archive_args(argc, argv, &ar);
+       if (prefix == NULL)
+               prefix = setup_git_directory();
+
+       argv += tree_idx;
+       parse_treeish_arg(argv, &ar.args, prefix);
+       parse_pathspec_arg(argv + 1, &ar.args);
+
+       return ar.write_archive(&ar.args);
+}
index 1d3729aa9914d6c68dbb6ad11cb46504b4a688e0..73c59824238f58c2dc94544ef67ea51741d834ac 100644 (file)
@@ -62,7 +62,7 @@ static void count_objects(DIR *d, char *path, int len, int verbose,
                hex[40] = 0;
                if (get_sha1_hex(hex, sha1))
                        die("internal error");
-               if (has_sha1_pack(sha1))
+               if (has_sha1_pack(sha1, NULL))
                        (*packed_loose)++;
        }
 }
index c407c033e7d4f43818d62691688911de71a5cf11..3d3097d299d4c964e1acfff7a365501a8bd53587 100644 (file)
@@ -249,7 +249,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
        FILE *in = stdin;
        const char *sep = "";
        unsigned char head_sha1[20];
-       const char *head, *current_branch;
+       const char *current_branch;
 
        git_config(fmt_merge_msg_config);
 
@@ -277,10 +277,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
                usage(fmt_merge_msg_usage);
 
        /* get current branch */
-       head = xstrdup(git_path("HEAD"));
-       current_branch = resolve_ref(head, head_sha1, 1);
-       current_branch += strlen(head) - 4;
-       free((char *)head);
+       current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
        if (!strncmp(current_branch, "refs/heads/", 11))
                current_branch += 11;
 
index 698618b798184ad24eed43917b5d609f9abf5886..93d3d7eef06315c598952761a7f2755a2f95be2c 100644 (file)
@@ -585,24 +585,27 @@ static void get_value(struct refinfo *ref, int atom, struct atom_value **v)
        *v = &ref->value[atom];
 }
 
-static struct refinfo **grab_array;
-static const char **grab_pattern;
-static int *grab_cnt;
+struct grab_ref_cbdata {
+       struct refinfo **grab_array;
+       const char **grab_pattern;
+       int grab_cnt;
+};
 
 /*
  * A call-back given to for_each_ref().  It is unfortunate that we
  * need to use global variables to pass extra information to this
  * function.
  */
-static int grab_single_ref(const char *refname, const unsigned char *sha1)
+static int grab_single_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
+       struct grab_ref_cbdata *cb = cb_data;
        struct refinfo *ref;
        int cnt;
 
-       if (*grab_pattern) {
+       if (*cb->grab_pattern) {
                const char **pattern;
                int namelen = strlen(refname);
-               for (pattern = grab_pattern; *pattern; pattern++) {
+               for (pattern = cb->grab_pattern; *pattern; pattern++) {
                        const char *p = *pattern;
                        int plen = strlen(p);
 
@@ -626,25 +629,14 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1)
        ref->refname = xstrdup(refname);
        hashcpy(ref->objectname, sha1);
 
-       cnt = *grab_cnt;
-       grab_array = xrealloc(grab_array, sizeof(*grab_array) * (cnt + 1));
-       grab_array[cnt++] = ref;
-       *grab_cnt = cnt;
+       cnt = cb->grab_cnt;
+       cb->grab_array = xrealloc(cb->grab_array,
+                                 sizeof(*cb->grab_array) * (cnt + 1));
+       cb->grab_array[cnt++] = ref;
+       cb->grab_cnt = cnt;
        return 0;
 }
 
-static struct refinfo **grab_refs(const char **pattern, int *cnt)
-{
-       /* Sheesh, we really should make for-each-ref to take
-        * callback data.
-        */
-       *cnt = 0;
-       grab_pattern = pattern;
-       grab_cnt = cnt;
-       for_each_ref(grab_single_ref);
-       return grab_array;
-}
-
 static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b)
 {
        struct atom_value *va, *vb;
@@ -784,6 +776,7 @@ int cmd_for_each_ref(int ac, const char **av, char *prefix)
        int maxcount = 0;
        int quote_style = -1; /* unspecified yet */
        struct refinfo **refs;
+       struct grab_ref_cbdata cbdata;
 
        for (i = 1; i < ac; i++) {
                const char *arg = av[i];
@@ -855,7 +848,11 @@ int cmd_for_each_ref(int ac, const char **av, char *prefix)
 
        verify_format(format);
 
-       refs = grab_refs(av + i, &num_refs);
+       memset(&cbdata, 0, sizeof(cbdata));
+       cbdata.grab_pattern = av + i;
+       for_each_ref(grab_single_ref, &cbdata);
+       refs = cbdata.grab_array;
+       num_refs = cbdata.grab_cnt;
 
        for (i = 0; i < used_atom_cnt; i++) {
                if (used_atom[i][0] == '*') {
index 5085018e46d8ebefaf797d62dcc7c9f8f1d06d02..23b7714f894019c7d59fc9ede00d83c008d6bc89 100644 (file)
@@ -218,8 +218,8 @@ static void create_default_files(const char *git_dir, const char *template_path)
         * branch, if it does not exist yet.
         */
        strcpy(path + len, "HEAD");
-       if (read_ref(path, sha1) < 0) {
-               if (create_symref(path, "refs/heads/master") < 0)
+       if (read_ref("HEAD", sha1) < 0) {
+               if (create_symref("HEAD", "refs/heads/master") < 0)
                        exit(1);
        }
 
index 52886b69b068eca899bea574c5d9790395eeb006..618aa314d22dd16bb62b0a7deb3be14fda411ebe 100644 (file)
@@ -75,11 +75,10 @@ static void name_rev(struct commit *commit,
        }
 }
 
-static int tags_only;
-
-static int name_ref(const char *path, const unsigned char *sha1)
+static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data)
 {
        struct object *o = parse_object(sha1);
+       int tags_only = *(int*)cb_data;
        int deref = 0;
 
        if (tags_only && strncmp(path, "refs/tags/", 10))
@@ -131,6 +130,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
 {
        struct object_array revs = { 0, 0, NULL };
        int as_is = 0, all = 0, transform_stdin = 0;
+       int tags_only = 0;
 
        git_config(git_default_config);
 
@@ -186,7 +186,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                add_object_array((struct object *)commit, *argv, &revs);
        }
 
-       for_each_ref(name_ref);
+       for_each_ref(name_ref, &tags_only);
 
        if (transform_stdin) {
                char buffer[2048];
index 149fa283971712650f124e96aa5bf9e2f99b0ebb..8d7a1209d5effe83eb93ad4d0f5088806d625c70 100644 (file)
@@ -9,10 +9,13 @@
 #include "pack.h"
 #include "csum-file.h"
 #include "tree-walk.h"
+#include "diff.h"
+#include "revision.h"
+#include "list-objects.h"
 #include <sys/time.h>
 #include <signal.h>
 
-static const char pack_usage[] = "git-pack-objects [-q] [--no-reuse-delta] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list";
+static const char pack_usage[] = "git-pack-objects [-q] [--no-reuse-delta] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] [--revs [--unpacked | --all]*] [--stdout | base-name] <ref-list | <object-list]";
 
 struct object_entry {
        unsigned char sha1[20];
@@ -66,6 +69,7 @@ static int progress = 1;
 static volatile sig_atomic_t progress_update;
 static int window = 10;
 static int pack_to_stdout;
+static int num_preferred_base;
 
 /*
  * The object names in objects array are hashed with this hashtable,
@@ -838,7 +842,7 @@ static int check_pbase_path(unsigned hash)
        return 0;
 }
 
-static void add_preferred_base_object(char *name, unsigned hash)
+static void add_preferred_base_object(const char *name, unsigned hash)
 {
        struct pbase_tree *it;
        int cmplen = name_cmp_len(name);
@@ -867,6 +871,9 @@ static void add_preferred_base(unsigned char *sha1)
        unsigned long size;
        unsigned char tree_sha1[20];
 
+       if (window <= num_preferred_base++)
+               return;
+
        data = read_object_with_reference(sha1, tree_type, &size, tree_sha1);
        if (!data)
                return;
@@ -1326,14 +1333,105 @@ static int git_pack_config(const char *k, const char *v)
        return git_default_config(k, v);
 }
 
+static void read_object_list_from_stdin(void)
+{
+       char line[40 + 1 + PATH_MAX + 2];
+       unsigned char sha1[20];
+       unsigned hash;
+
+       for (;;) {
+               if (!fgets(line, sizeof(line), stdin)) {
+                       if (feof(stdin))
+                               break;
+                       if (!ferror(stdin))
+                               die("fgets returned NULL, not EOF, not error!");
+                       if (errno != EINTR)
+                               die("fgets: %s", strerror(errno));
+                       clearerr(stdin);
+                       continue;
+               }
+               if (line[0] == '-') {
+                       if (get_sha1_hex(line+1, sha1))
+                               die("expected edge sha1, got garbage:\n %s",
+                                   line);
+                       add_preferred_base(sha1);
+                       continue;
+               }
+               if (get_sha1_hex(line, sha1))
+                       die("expected sha1, got garbage:\n %s", line);
+
+               hash = name_hash(line+41);
+               add_preferred_base_object(line+41, hash);
+               add_object_entry(sha1, hash, 0);
+       }
+}
+
+static void show_commit(struct commit *commit)
+{
+       unsigned hash = name_hash("");
+       add_preferred_base_object("", hash);
+       add_object_entry(commit->object.sha1, hash, 0);
+}
+
+static void show_object(struct object_array_entry *p)
+{
+       unsigned hash = name_hash(p->name);
+       add_preferred_base_object(p->name, hash);
+       add_object_entry(p->item->sha1, hash, 0);
+}
+
+static void show_edge(struct commit *commit)
+{
+       add_preferred_base(commit->object.sha1);
+}
+
+static void get_object_list(int ac, const char **av)
+{
+       struct rev_info revs;
+       char line[1000];
+       int flags = 0;
+
+       init_revisions(&revs, NULL);
+       save_commit_buffer = 0;
+       track_object_refs = 0;
+       setup_revisions(ac, av, &revs, NULL);
+
+       while (fgets(line, sizeof(line), stdin) != NULL) {
+               int len = strlen(line);
+               if (line[len - 1] == '\n')
+                       line[--len] = 0;
+               if (!len)
+                       break;
+               if (*line == '-') {
+                       if (!strcmp(line, "--not")) {
+                               flags ^= UNINTERESTING;
+                               continue;
+                       }
+                       die("not a rev '%s'", line);
+               }
+               if (handle_revision_arg(line, &revs, flags, 1))
+                       die("bad revision '%s'", line);
+       }
+
+       prepare_revision_walk(&revs);
+       mark_edges_uninteresting(revs.commits, &revs, show_edge);
+       traverse_commit_list(&revs, show_commit, show_object);
+}
+
 int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 {
        SHA_CTX ctx;
-       char line[40 + 1 + PATH_MAX + 2];
        int depth = 10;
        struct object_entry **list;
-       int num_preferred_base = 0;
+       int use_internal_rev_list = 0;
+       int thin = 0;
        int i;
+       const char *rp_av[64];
+       int rp_ac;
+
+       rp_av[0] = "pack-objects";
+       rp_av[1] = "--objects"; /* --thin will make it --objects-edge */
+       rp_ac = 2;
 
        git_config(git_pack_config);
 
@@ -1341,63 +1439,99 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
 
-               if (*arg == '-') {
-                       if (!strcmp("--non-empty", arg)) {
-                               non_empty = 1;
-                               continue;
-                       }
-                       if (!strcmp("--local", arg)) {
-                               local = 1;
-                               continue;
-                       }
-                       if (!strcmp("--progress", arg)) {
-                               progress = 1;
-                               continue;
-                       }
-                       if (!strcmp("--incremental", arg)) {
-                               incremental = 1;
-                               continue;
-                       }
-                       if (!strncmp("--window=", arg, 9)) {
-                               char *end;
-                               window = strtoul(arg+9, &end, 0);
-                               if (!arg[9] || *end)
-                                       usage(pack_usage);
-                               continue;
-                       }
-                       if (!strncmp("--depth=", arg, 8)) {
-                               char *end;
-                               depth = strtoul(arg+8, &end, 0);
-                               if (!arg[8] || *end)
-                                       usage(pack_usage);
-                               continue;
-                       }
-                       if (!strcmp("--progress", arg)) {
-                               progress = 1;
-                               continue;
-                       }
-                       if (!strcmp("-q", arg)) {
-                               progress = 0;
-                               continue;
-                       }
-                       if (!strcmp("--no-reuse-delta", arg)) {
-                               no_reuse_delta = 1;
-                               continue;
-                       }
-                       if (!strcmp("--stdout", arg)) {
-                               pack_to_stdout = 1;
-                               continue;
-                       }
-                       usage(pack_usage);
+               if (*arg != '-')
+                       break;
+
+               if (!strcmp("--non-empty", arg)) {
+                       non_empty = 1;
+                       continue;
+               }
+               if (!strcmp("--local", arg)) {
+                       local = 1;
+                       continue;
+               }
+               if (!strcmp("--progress", arg)) {
+                       progress = 1;
+                       continue;
+               }
+               if (!strcmp("--incremental", arg)) {
+                       incremental = 1;
+                       continue;
+               }
+               if (!strncmp("--window=", arg, 9)) {
+                       char *end;
+                       window = strtoul(arg+9, &end, 0);
+                       if (!arg[9] || *end)
+                               usage(pack_usage);
+                       continue;
+               }
+               if (!strncmp("--depth=", arg, 8)) {
+                       char *end;
+                       depth = strtoul(arg+8, &end, 0);
+                       if (!arg[8] || *end)
+                               usage(pack_usage);
+                       continue;
+               }
+               if (!strcmp("--progress", arg)) {
+                       progress = 1;
+                       continue;
+               }
+               if (!strcmp("-q", arg)) {
+                       progress = 0;
+                       continue;
+               }
+               if (!strcmp("--no-reuse-delta", arg)) {
+                       no_reuse_delta = 1;
+                       continue;
+               }
+               if (!strcmp("--stdout", arg)) {
+                       pack_to_stdout = 1;
+                       continue;
+               }
+               if (!strcmp("--revs", arg)) {
+                       use_internal_rev_list = 1;
+                       continue;
+               }
+               if (!strcmp("--unpacked", arg) ||
+                   !strncmp("--unpacked=", arg, 11) ||
+                   !strcmp("--all", arg)) {
+                       use_internal_rev_list = 1;
+                       if (ARRAY_SIZE(rp_av) - 1 <= rp_ac)
+                               die("too many internal rev-list options");
+                       rp_av[rp_ac++] = arg;
+                       continue;
                }
-               if (base_name)
-                       usage(pack_usage);
-               base_name = arg;
+               if (!strcmp("--thin", arg)) {
+                       use_internal_rev_list = 1;
+                       thin = 1;
+                       rp_av[1] = "--objects-edge";
+                       continue;
+               }
+               usage(pack_usage);
        }
 
+       /* Traditionally "pack-objects [options] base extra" failed;
+        * we would however want to take refs parameter that would
+        * have been given to upstream rev-list ourselves, which means
+        * we somehow want to say what the base name is.  So the
+        * syntax would be:
+        *
+        * pack-objects [options] base <refs...>
+        *
+        * in other words, we would treat the first non-option as the
+        * base_name and send everything else to the internal revision
+        * walker.
+        */
+
+       if (!pack_to_stdout)
+               base_name = argv[i++];
+
        if (pack_to_stdout != !base_name)
                usage(pack_usage);
 
+       if (!pack_to_stdout && thin)
+               die("--thin cannot be used to build an indexable pack.");
+
        prepare_packed_git();
 
        if (progress) {
@@ -1405,35 +1539,13 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                setup_progress_signal();
        }
 
-       for (;;) {
-               unsigned char sha1[20];
-               unsigned hash;
-
-               if (!fgets(line, sizeof(line), stdin)) {
-                       if (feof(stdin))
-                               break;
-                       if (!ferror(stdin))
-                               die("fgets returned NULL, not EOF, not error!");
-                       if (errno != EINTR)
-                               die("fgets: %s", strerror(errno));
-                       clearerr(stdin);
-                       continue;
-               }
-
-               if (line[0] == '-') {
-                       if (get_sha1_hex(line+1, sha1))
-                               die("expected edge sha1, got garbage:\n %s",
-                                   line+1);
-                       if (num_preferred_base++ < window)
-                               add_preferred_base(sha1);
-                       continue;
-               }
-               if (get_sha1_hex(line, sha1))
-                       die("expected sha1, got garbage:\n %s", line);
-               hash = name_hash(line+41);
-               add_preferred_base_object(line+41, hash);
-               add_object_entry(sha1, hash, 0);
+       if (!use_internal_rev_list)
+               read_object_list_from_stdin();
+       else {
+               rp_av[rp_ac] = NULL;
+               get_object_list(rp_ac, rp_av);
        }
+
        if (progress)
                fprintf(stderr, "Done counting %d objects.\n", nr_objects);
        sorted_by_sha = create_final_object_list();
diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c
new file mode 100644 (file)
index 0000000..246dd63
--- /dev/null
@@ -0,0 +1,112 @@
+#include "cache.h"
+#include "refs.h"
+
+static const char *result_path, *lock_path;
+static const char builtin_pack_refs_usage[] =
+"git-pack-refs [--prune]";
+
+struct ref_to_prune {
+       struct ref_to_prune *next;
+       unsigned char sha1[20];
+       char name[FLEX_ARRAY];
+};
+
+struct pack_refs_cb_data {
+       int prune;
+       struct ref_to_prune *ref_to_prune;
+       FILE *refs_file;
+};
+
+static void remove_lock_file(void)
+{
+       if (lock_path)
+               unlink(lock_path);
+}
+
+static int do_not_prune(int flags)
+{
+       /* If it is already packed or if it is a symref,
+        * do not prune it.
+        */
+       return (flags & (REF_ISSYMREF|REF_ISPACKED));
+}
+
+static int handle_one_ref(const char *path, const unsigned char *sha1,
+                         int flags, void *cb_data)
+{
+       struct pack_refs_cb_data *cb = cb_data;
+
+       /* Do not pack the symbolic refs */
+       if (!(flags & REF_ISSYMREF))
+               fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path);
+       if (cb->prune && !do_not_prune(flags)) {
+               int namelen = strlen(path) + 1;
+               struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
+               hashcpy(n->sha1, sha1);
+               strcpy(n->name, path);
+               n->next = cb->ref_to_prune;
+               cb->ref_to_prune = n;
+       }
+       return 0;
+}
+
+/* make sure nobody touched the ref, and unlink */
+static void prune_ref(struct ref_to_prune *r)
+{
+       struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1, 1);
+
+       if (lock) {
+               unlink(git_path(r->name));
+               unlock_ref(lock);
+       }
+}
+
+static void prune_refs(struct ref_to_prune *r)
+{
+       while (r) {
+               prune_ref(r);
+               r = r->next;
+       }
+}
+
+int cmd_pack_refs(int argc, const char **argv, const char *prefix)
+{
+       int fd, i;
+       struct pack_refs_cb_data cbdata;
+
+       memset(&cbdata, 0, sizeof(cbdata));
+
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (!strcmp(arg, "--prune")) {
+                       cbdata.prune = 1;
+                       continue;
+               }
+               /* perhaps other parameters later... */
+               break;
+       }
+       if (i != argc)
+               usage(builtin_pack_refs_usage);
+
+       result_path = xstrdup(git_path("packed-refs"));
+       lock_path = xstrdup(mkpath("%s.lock", result_path));
+
+       fd = open(lock_path, O_CREAT | O_EXCL | O_WRONLY, 0666);
+       if (fd < 0)
+               die("unable to create new ref-pack file (%s)", strerror(errno));
+       atexit(remove_lock_file);
+
+       cbdata.refs_file = fdopen(fd, "w");
+       if (!cbdata.refs_file)
+               die("unable to create ref-pack file structure (%s)",
+                   strerror(errno));
+       for_each_ref(handle_one_ref, &cbdata);
+       fsync(fd);
+       fclose(cbdata.refs_file);
+       if (rename(lock_path, result_path) < 0)
+               die("unable to overwrite old ref-pack file (%s)", strerror(errno));
+       lock_path = NULL;
+       if (cbdata.prune)
+               prune_refs(cbdata.ref_to_prune);
+       return 0;
+}
index d3dd94d9ef7a50e156488ea5e9236d6d55f6e190..960db49859a286bf5f3515ee7c2bfe25fe44d09e 100644 (file)
@@ -19,7 +19,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len)
                memcpy(hex+2, de->d_name, 38);
                if (get_sha1_hex(hex, sha1))
                        continue;
-               if (!has_sha1_pack(sha1))
+               if (!has_sha1_pack(sha1, NULL))
                        continue;
                memcpy(pathname + len, de->d_name, 38);
                if (dryrun)
index 6228c7907b183fb686c9f4cc54347c3dc16f3ec4..e79b515c76d9eac9f1ce8e5d88ba0f7058ca53fb 100644 (file)
@@ -174,7 +174,7 @@ static void walk_commit_list(struct rev_info *revs)
        }
 }
 
-static int add_one_ref(const char *path, const unsigned char *sha1)
+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)
@@ -240,7 +240,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
        revs.tree_objects = 1;
 
        /* Add all external refs */
-       for_each_ref(add_one_ref);
+       for_each_ref(add_one_ref, NULL);
 
        /* Add all refs from the index file */
        add_cache_refs();
index c43f2566d519ddb238ca70065ba901881dce372b..581c44ba8e404b80e6a1c2010b501d9bb0642f0b 100644 (file)
@@ -27,7 +27,7 @@ static void add_refspec(const char *ref)
        refspec_nr = nr;
 }
 
-static int expand_one_ref(const char *ref, const unsigned char *sha1)
+static int expand_one_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
 {
        /* Ignore the "refs/" at the beginning of the refname */
        ref += 5;
@@ -51,7 +51,7 @@ static void expand_refspecs(void)
        }
        if (!tags)
                return;
-       for_each_ref(expand_one_ref);
+       for_each_ref(expand_one_ref, NULL);
 }
 
 static void set_refspecs(const char **refs, int nr)
index 8fe8afbd0469c689cad111df6302508d352b6c29..1f3333da38c77a07840a8dfad052c301dbae9392 100644 (file)
@@ -7,6 +7,7 @@
 #include "tree-walk.h"
 #include "diff.h"
 #include "revision.h"
+#include "list-objects.h"
 #include "builtin.h"
 
 /* bits #0-15 in revision.h */
@@ -98,104 +99,24 @@ static void show_commit(struct commit *commit)
        commit->buffer = NULL;
 }
 
-static void process_blob(struct blob *blob,
-                        struct object_array *p,
-                        struct name_path *path,
-                        const char *name)
+static void show_object(struct object_array_entry *p)
 {
-       struct object *obj = &blob->object;
-
-       if (!revs.blob_objects)
-               return;
-       if (obj->flags & (UNINTERESTING | SEEN))
-               return;
-       obj->flags |= SEEN;
-       name = xstrdup(name);
-       add_object(obj, p, path, name);
-}
-
-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 (!revs.tree_objects)
-               return;
-       if (obj->flags & (UNINTERESTING | SEEN))
-               return;
-       if (parse_tree(tree) < 0)
-               die("bad tree object %s", sha1_to_hex(obj->sha1));
-       obj->flags |= SEEN;
-       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);
+       /* An object with name "foo\n0000000..." can be used to
+        * confuse downstream git-pack-objects very badly.
+        */
+       const char *ep = strchr(p->name, '\n');
+       if (ep) {
+               printf("%s %.*s\n", sha1_to_hex(p->item->sha1),
+                      (int) (ep - p->name),
+                      p->name);
        }
-       free(tree->buffer);
-       tree->buffer = NULL;
+       else
+               printf("%s %s\n", sha1_to_hex(p->item->sha1), p->name);
 }
 
-static void show_commit_list(struct rev_info *revs)
+static void show_edge(struct commit *commit)
 {
-       int i;
-       struct commit *commit;
-       struct object_array objects = { 0, 0, NULL };
-
-       while ((commit = get_revision(revs)) != NULL) {
-               process_tree(commit->tree, &objects, NULL, "");
-               show_commit(commit);
-       }
-       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->flags & (UNINTERESTING | SEEN))
-                       continue;
-               if (obj->type == OBJ_TAG) {
-                       obj->flags |= SEEN;
-                       add_object_array(obj, name, &objects);
-                       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);
-       }
-       for (i = 0; i < objects.nr; i++) {
-               struct object_array_entry *p = objects.objects + i;
-
-               /* An object with name "foo\n0000000..." can be used to
-                * confuse downstream git-pack-objects very badly.
-                */
-               const char *ep = strchr(p->name, '\n');
-               if (ep) {
-                       printf("%s %.*s\n", sha1_to_hex(p->item->sha1),
-                              (int) (ep - p->name),
-                              p->name);
-               }
-               else
-                       printf("%s %s\n", sha1_to_hex(p->item->sha1), p->name);
-       }
+       printf("-%s\n", sha1_to_hex(commit->object.sha1));
 }
 
 /*
@@ -276,35 +197,6 @@ static struct commit_list *find_bisection(struct commit_list *list)
        return best;
 }
 
-static void mark_edge_parents_uninteresting(struct commit *commit)
-{
-       struct commit_list *parents;
-
-       for (parents = commit->parents; parents; parents = parents->next) {
-               struct commit *parent = parents->item;
-               if (!(parent->object.flags & UNINTERESTING))
-                       continue;
-               mark_tree_uninteresting(parent->tree);
-               if (revs.edge_hint && !(parent->object.flags & SHOWN)) {
-                       parent->object.flags |= SHOWN;
-                       printf("-%s\n", sha1_to_hex(parent->object.sha1));
-               }
-       }
-}
-
-static void mark_edges_uninteresting(struct commit_list *list)
-{
-       for ( ; list; list = list->next) {
-               struct commit *commit = list->item;
-
-               if (commit->object.flags & UNINTERESTING) {
-                       mark_tree_uninteresting(commit->tree);
-                       continue;
-               }
-               mark_edge_parents_uninteresting(commit);
-       }
-}
-
 static void read_revisions_from_stdin(struct rev_info *revs)
 {
        char line[1000];
@@ -384,12 +276,12 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 
        prepare_revision_walk(&revs);
        if (revs.tree_objects)
-               mark_edges_uninteresting(revs.commits);
+               mark_edges_uninteresting(revs.commits, &revs, show_edge);
 
        if (bisect_list)
                revs.commits = find_bisection(revs.commits);
 
-       show_commit_list(&revs);
+       traverse_commit_list(&revs, show_commit, show_object);
 
        return 0;
 }
index fd3ccc8546455f60f56ceb59dbe98fb367a86d2a..3b716fba133ae2f0375ef73d6a50a4d93112b5e4 100644 (file)
@@ -137,7 +137,7 @@ static void show_default(void)
        }
 }
 
-static int show_reference(const char *refname, const unsigned char *sha1)
+static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
        show_rev(NORMAL, sha1, refname);
        return 0;
@@ -299,19 +299,19 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (!strcmp(arg, "--all")) {
-                               for_each_ref(show_reference);
+                               for_each_ref(show_reference, NULL);
                                continue;
                        }
                        if (!strcmp(arg, "--branches")) {
-                               for_each_branch_ref(show_reference);
+                               for_each_branch_ref(show_reference, NULL);
                                continue;
                        }
                        if (!strcmp(arg, "--tags")) {
-                               for_each_tag_ref(show_reference);
+                               for_each_tag_ref(show_reference, NULL);
                                continue;
                        }
                        if (!strcmp(arg, "--remotes")) {
-                               for_each_remote_ref(show_reference);
+                               for_each_remote_ref(show_reference, NULL);
                                continue;
                        }
                        if (!strcmp(arg, "--show-prefix")) {
diff --git a/builtin-runstatus.c b/builtin-runstatus.c
new file mode 100644 (file)
index 0000000..303c556
--- /dev/null
@@ -0,0 +1,36 @@
+#include "wt-status.h"
+#include "cache.h"
+
+extern int wt_status_use_color;
+
+static const char runstatus_usage[] =
+"git-runstatus [--color|--nocolor] [--amend] [--verbose]";
+
+int cmd_runstatus(int argc, const char **argv, const char *prefix)
+{
+       struct wt_status s;
+       int i;
+
+       git_config(git_status_config);
+       wt_status_prepare(&s);
+
+       for (i = 1; i < argc; i++) {
+               if (!strcmp(argv[i], "--color"))
+                       wt_status_use_color = 1;
+               else if (!strcmp(argv[i], "--nocolor"))
+                       wt_status_use_color = 0;
+               else if (!strcmp(argv[i], "--amend")) {
+                       s.amend = 1;
+                       s.reference = "HEAD^1";
+               }
+               else if (!strcmp(argv[i], "--verbose"))
+                       s.verbose = 1;
+               else if (!strcmp(argv[i], "--untracked"))
+                       s.untracked = 1;
+               else
+                       usage(runstatus_usage);
+       }
+
+       wt_status_print(&s);
+       return s.commitable ? 0 : 1;
+}
index 578c9fafd022ce8a8676d348120c2fc7b2b1c5dd..5d6ce568360629f3844513b81f97e4412002b88b 100644 (file)
@@ -346,7 +346,7 @@ static void sort_ref_range(int bottom, int top)
              compare_ref_name);
 }
 
-static int append_ref(const char *refname, const unsigned char *sha1)
+static int append_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
        struct commit *commit = lookup_commit_reference_gently(sha1, 1);
        int i;
@@ -369,7 +369,7 @@ static int append_ref(const char *refname, const unsigned char *sha1)
        return 0;
 }
 
-static int append_head_ref(const char *refname, const unsigned char *sha1)
+static int append_head_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
        unsigned char tmp[20];
        int ofs = 11;
@@ -380,14 +380,14 @@ static int append_head_ref(const char *refname, const unsigned char *sha1)
         */
        if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))
                ofs = 5;
-       return append_ref(refname + ofs, sha1);
+       return append_ref(refname + ofs, sha1, flag, cb_data);
 }
 
-static int append_tag_ref(const char *refname, const unsigned char *sha1)
+static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
        if (strncmp(refname, "refs/tags/", 10))
                return 0;
-       return append_ref(refname + 5, sha1);
+       return append_ref(refname + 5, sha1, flag, cb_data);
 }
 
 static const char *match_ref_pattern = NULL;
@@ -401,7 +401,7 @@ static int count_slash(const char *s)
        return cnt;
 }
 
-static int append_matching_ref(const char *refname, const unsigned char *sha1)
+static int append_matching_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
        /* we want to allow pattern hold/<asterisk> to show all
         * branches under refs/heads/hold/, and v0.99.9? to show
@@ -417,41 +417,33 @@ static int append_matching_ref(const char *refname, const unsigned char *sha1)
        if (fnmatch(match_ref_pattern, tail, 0))
                return 0;
        if (!strncmp("refs/heads/", refname, 11))
-               return append_head_ref(refname, sha1);
+               return append_head_ref(refname, sha1, flag, cb_data);
        if (!strncmp("refs/tags/", refname, 10))
-               return append_tag_ref(refname, sha1);
-       return append_ref(refname, sha1);
+               return append_tag_ref(refname, sha1, flag, cb_data);
+       return append_ref(refname, sha1, flag, cb_data);
 }
 
 static void snarf_refs(int head, int tag)
 {
        if (head) {
                int orig_cnt = ref_name_cnt;
-               for_each_ref(append_head_ref);
+               for_each_ref(append_head_ref, NULL);
                sort_ref_range(orig_cnt, ref_name_cnt);
        }
        if (tag) {
                int orig_cnt = ref_name_cnt;
-               for_each_ref(append_tag_ref);
+               for_each_ref(append_tag_ref, NULL);
                sort_ref_range(orig_cnt, ref_name_cnt);
        }
 }
 
-static int rev_is_head(char *head_path, int headlen, char *name,
+static int rev_is_head(char *head, int headlen, char *name,
                       unsigned char *head_sha1, unsigned char *sha1)
 {
-       int namelen;
-       if ((!head_path[0]) ||
+       if ((!head[0]) ||
            (head_sha1 && sha1 && hashcmp(head_sha1, sha1)))
                return 0;
-       namelen = strlen(name);
-       if ((headlen < namelen) ||
-           memcmp(head_path + headlen - namelen, name, namelen))
-               return 0;
-       if (headlen == namelen ||
-           head_path[headlen - namelen - 1] == '/')
-               return 1;
-       return 0;
+       return !strcmp(head, name);
 }
 
 static int show_merge_base(struct commit_list *seen, int num_rev)
@@ -495,7 +487,7 @@ static void append_one_rev(const char *av)
 {
        unsigned char revkey[20];
        if (!get_sha1(av, revkey)) {
-               append_ref(av, revkey);
+               append_ref(av, revkey, 0, NULL);
                return;
        }
        if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) {
@@ -503,7 +495,7 @@ static void append_one_rev(const char *av)
                int saved_matches = ref_name_cnt;
                match_ref_pattern = av;
                match_ref_slash = count_slash(av);
-               for_each_ref(append_matching_ref);
+               for_each_ref(append_matching_ref, NULL);
                if (saved_matches == ref_name_cnt &&
                    ref_name_cnt < MAX_REVS)
                        error("no matching refs with %s", av);
@@ -559,9 +551,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
        int all_heads = 0, all_tags = 0;
        int all_mask, all_revs;
        int lifo = 1;
-       char head_path[128];
-       const char *head_path_p;
-       int head_path_len;
+       char head[128];
+       const char *head_p;
+       int head_len;
        unsigned char head_sha1[20];
        int merge_base = 0;
        int independent = 0;
@@ -638,31 +630,31 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                ac--; av++;
        }
 
-       head_path_p = resolve_ref(git_path("HEAD"), head_sha1, 1);
-       if (head_path_p) {
-               head_path_len = strlen(head_path_p);
-               memcpy(head_path, head_path_p, head_path_len + 1);
+       head_p = resolve_ref("HEAD", head_sha1, 1, NULL);
+       if (head_p) {
+               head_len = strlen(head_p);
+               memcpy(head, head_p, head_len + 1);
        }
        else {
-               head_path_len = 0;
-               head_path[0] = 0;
+               head_len = 0;
+               head[0] = 0;
        }
 
-       if (with_current_branch && head_path_p) {
+       if (with_current_branch && head_p) {
                int has_head = 0;
                for (i = 0; !has_head && i < ref_name_cnt; i++) {
                        /* We are only interested in adding the branch
                         * HEAD points at.
                         */
-                       if (rev_is_head(head_path,
-                                       head_path_len,
+                       if (rev_is_head(head,
+                                       head_len,
                                        ref_name[i],
                                        head_sha1, NULL))
                                has_head++;
                }
                if (!has_head) {
-                       int pfxlen = strlen(git_path("refs/heads/"));
-                       append_one_rev(head_path + pfxlen);
+                       int pfxlen = strlen("refs/heads/");
+                       append_one_rev(head + pfxlen);
                }
        }
 
@@ -713,8 +705,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
        if (1 < num_rev || extra < 0) {
                for (i = 0; i < num_rev; i++) {
                        int j;
-                       int is_head = rev_is_head(head_path,
-                                                 head_path_len,
+                       int is_head = rev_is_head(head,
+                                                 head_len,
                                                  ref_name[i],
                                                  head_sha1,
                                                  rev[i]->object.sha1);
index 1d3a5e229ae1a16211671e7591a7544af98721f8..d8be0527f4752131b0a1276ecedcc82714636c28 100644 (file)
@@ -1,5 +1,6 @@
 #include "builtin.h"
 #include "cache.h"
+#include "refs.h"
 
 static const char git_symbolic_ref_usage[] =
 "git-symbolic-ref name [ref]";
@@ -7,15 +8,14 @@ static const char git_symbolic_ref_usage[] =
 static void check_symref(const char *HEAD)
 {
        unsigned char sha1[20];
-       const char *git_HEAD = xstrdup(git_path("%s", HEAD));
-       const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 0);
-       if (git_refs_heads_master) {
-               /* we want to strip the .git/ part */
-               int pfxlen = strlen(git_HEAD) - strlen(HEAD);
-               puts(git_refs_heads_master + pfxlen);
-       }
-       else
+       int flag;
+       const char *refs_heads_master = resolve_ref(HEAD, sha1, 0, &flag);
+
+       if (!refs_heads_master)
                die("No such ref: %s", HEAD);
+       else if (!(flag & REF_ISSYMREF))
+               die("ref %s is not a symbolic ref", HEAD);
+       puts(refs_heads_master);
 }
 
 int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
@@ -26,7 +26,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
                check_symref(argv[1]);
                break;
        case 3:
-               create_symref(xstrdup(git_path("%s", argv[1])), argv[2]);
+               create_symref(argv[1], argv[2]);
                break;
        default:
                usage(git_symbolic_ref_usage);
index fa666f78c5b5e44617495abb2716eded8407626c..437eb726a9b207353545726f379ab8638c0eecb9 100644 (file)
@@ -3,12 +3,12 @@
  */
 #include <time.h>
 #include "cache.h"
-#include "tree-walk.h"
 #include "commit.h"
 #include "strbuf.h"
 #include "tar.h"
 #include "builtin.h"
 #include "pkt-line.h"
+#include "archive.h"
 
 #define RECORDSIZE     (512)
 #define BLOCKSIZE      (RECORDSIZE * 20)
@@ -21,6 +21,7 @@ static unsigned long offset;
 
 static time_t archive_time;
 static int tar_umask;
+static int verbose;
 
 /* writes out the whole block, but only if it is full */
 static void write_if_needed(void)
@@ -168,6 +169,8 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
                mode = 0100666;
                sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
        } else {
+               if (verbose)
+                       fprintf(stderr, "%.*s\n", path->len, path->buf);
                if (S_ISDIR(mode)) {
                        *header.typeflag = TYPEFLAG_DIR;
                        mode = (mode | 0777) & ~tar_umask;
@@ -244,37 +247,6 @@ static void write_global_extended_header(const unsigned char *sha1)
        free(ext_header.buf);
 }
 
-static void traverse_tree(struct tree_desc *tree, struct strbuf *path)
-{
-       int pathlen = path->len;
-       struct name_entry entry;
-
-       while (tree_entry(tree, &entry)) {
-               void *eltbuf;
-               char elttype[20];
-               unsigned long eltsize;
-
-               eltbuf = read_sha1_file(entry.sha1, elttype, &eltsize);
-               if (!eltbuf)
-                       die("cannot read %s", sha1_to_hex(entry.sha1));
-
-               path->len = pathlen;
-               strbuf_append_string(path, entry.path);
-               if (S_ISDIR(entry.mode))
-                       strbuf_append_string(path, "/");
-
-               write_entry(entry.sha1, path, entry.mode, eltbuf, eltsize);
-
-               if (S_ISDIR(entry.mode)) {
-                       struct tree_desc subtree;
-                       subtree.buf = eltbuf;
-                       subtree.size = eltsize;
-                       traverse_tree(&subtree, path);
-               }
-               free(eltbuf);
-       }
-}
-
 static int git_tar_config(const char *var, const char *value)
 {
        if (!strcmp(var, "tar.umask")) {
@@ -291,50 +263,95 @@ static int git_tar_config(const char *var, const char *value)
 
 static int generate_tar(int argc, const char **argv, const char *prefix)
 {
-       unsigned char sha1[20], tree_sha1[20];
-       struct commit *commit;
-       struct tree_desc tree;
-       struct strbuf current_path;
-       void *buffer;
-
-       current_path.buf = xmalloc(PATH_MAX);
-       current_path.alloc = PATH_MAX;
-       current_path.len = current_path.eof = 0;
+       struct archiver_args args;
+       int result;
+       char *base = NULL;
 
        git_config(git_tar_config);
 
-       switch (argc) {
-       case 3:
-               strbuf_append_string(&current_path, argv[2]);
-               strbuf_append_string(&current_path, "/");
-               /* FALLTHROUGH */
-       case 2:
-               if (get_sha1(argv[1], sha1))
-                       die("Not a valid object name %s", argv[1]);
-               break;
-       default:
+       memset(&args, 0, sizeof(args));
+       if (argc != 2 && argc != 3)
                usage(tar_tree_usage);
+       if (argc == 3) {
+               int baselen = strlen(argv[2]);
+               base = xmalloc(baselen + 2);
+               memcpy(base, argv[2], baselen);
+               base[baselen] = '/';
+               base[baselen + 1] = '\0';
        }
+       args.base = base;
+       parse_treeish_arg(argv + 1, &args, NULL);
 
-       commit = lookup_commit_reference_gently(sha1, 1);
-       if (commit) {
-               write_global_extended_header(commit->object.sha1);
-               archive_time = commit->date;
-       } else
-               archive_time = time(NULL);
-
-       tree.buf = buffer = read_object_with_reference(sha1, tree_type,
-                                                      &tree.size, tree_sha1);
-       if (!tree.buf)
-               die("not a reference to a tag, commit or tree object: %s",
-                   sha1_to_hex(sha1));
-
-       if (current_path.len > 0)
-               write_entry(tree_sha1, &current_path, 040777, NULL, 0);
-       traverse_tree(&tree, &current_path);
-       write_trailer();
+       result = write_tar_archive(&args);
+       free(base);
+
+       return result;
+}
+
+static int write_tar_entry(const unsigned char *sha1,
+                           const char *base, int baselen,
+                           const char *filename, unsigned mode, int stage)
+{
+       static struct strbuf path;
+       int filenamelen = strlen(filename);
+       void *buffer;
+       char type[20];
+       unsigned long size;
+
+       if (!path.alloc) {
+               path.buf = xmalloc(PATH_MAX);
+               path.alloc = PATH_MAX;
+               path.len = path.eof = 0;
+       }
+       if (path.alloc < baselen + filenamelen) {
+               free(path.buf);
+               path.buf = xmalloc(baselen + filenamelen);
+               path.alloc = baselen + filenamelen;
+       }
+       memcpy(path.buf, base, baselen);
+       memcpy(path.buf + baselen, filename, filenamelen);
+       path.len = baselen + filenamelen;
+       if (S_ISDIR(mode)) {
+               strbuf_append_string(&path, "/");
+               buffer = NULL;
+               size = 0;
+       } else {
+               buffer = read_sha1_file(sha1, type, &size);
+               if (!buffer)
+                       die("cannot read %s", sha1_to_hex(sha1));
+       }
+
+       write_entry(sha1, &path, mode, buffer, size);
        free(buffer);
-       free(current_path.buf);
+
+       return READ_TREE_RECURSIVE;
+}
+
+int write_tar_archive(struct archiver_args *args)
+{
+       int plen = args->base ? strlen(args->base) : 0;
+
+       git_config(git_tar_config);
+
+       archive_time = args->time;
+       verbose = args->verbose;
+
+       if (args->commit_sha1)
+               write_global_extended_header(args->commit_sha1);
+
+       if (args->base && plen > 0 && args->base[plen - 1] == '/') {
+               char *base = xstrdup(args->base);
+               int baselen = strlen(base);
+
+               while (baselen > 0 && base[baselen - 1] == '/')
+                       base[--baselen] = '\0';
+               write_tar_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
+               free(base);
+       }
+       read_tree_recursive(args->tree, args->base, plen, 0,
+                           args->pathspec, write_tar_entry);
+       write_trailer();
+
        return 0;
 }
 
diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c
new file mode 100644 (file)
index 0000000..0596865
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2006 Franck Bui-Huu
+ */
+#include <time.h>
+#include "cache.h"
+#include "builtin.h"
+#include "archive.h"
+#include "pkt-line.h"
+#include "sideband.h"
+#include <sys/wait.h>
+#include <sys/poll.h>
+
+static const char upload_archive_usage[] =
+       "git-upload-archive <repo>";
+
+static const char deadchild[] =
+"git-upload-archive: archiver died with error";
+
+static const char lostchild[] =
+"git-upload-archive: archiver process was lost";
+
+
+static int run_upload_archive(int argc, const char **argv, const char *prefix)
+{
+       struct archiver ar;
+       const char *sent_argv[MAX_ARGS];
+       const char *arg_cmd = "argument ";
+       char *p, buf[4096];
+       int treeish_idx;
+       int sent_argc;
+       int len;
+
+       if (argc != 2)
+               usage(upload_archive_usage);
+
+       if (strlen(argv[1]) > sizeof(buf))
+               die("insanely long repository name");
+
+       strcpy(buf, argv[1]); /* enter-repo smudges its argument */
+
+       if (!enter_repo(buf, 0))
+               die("not a git archive");
+
+       /* put received options in sent_argv[] */
+       sent_argc = 1;
+       sent_argv[0] = "git-upload-archive";
+       for (p = buf;;) {
+               /* This will die if not enough free space in buf */
+               len = packet_read_line(0, p, (buf + sizeof buf) - p);
+               if (len == 0)
+                       break;  /* got a flush */
+               if (sent_argc > MAX_ARGS - 2)
+                       die("Too many options (>29)");
+
+               if (p[len-1] == '\n') {
+                       p[--len] = 0;
+               }
+               if (len < strlen(arg_cmd) ||
+                   strncmp(arg_cmd, p, strlen(arg_cmd)))
+                       die("'argument' token or flush expected");
+
+               len -= strlen(arg_cmd);
+               memmove(p, p + strlen(arg_cmd), len);
+               sent_argv[sent_argc++] = p;
+               p += len;
+               *p++ = 0;
+       }
+       sent_argv[sent_argc] = NULL;
+
+       /* parse all options sent by the client */
+       treeish_idx = parse_archive_args(sent_argc, sent_argv, &ar);
+
+       parse_treeish_arg(sent_argv + treeish_idx, &ar.args, prefix);
+       parse_pathspec_arg(sent_argv + treeish_idx + 1, &ar.args);
+
+       return ar.write_archive(&ar.args);
+}
+
+static void error_clnt(const char *fmt, ...)
+{
+       char buf[1024];
+       va_list params;
+       int len;
+
+       va_start(params, fmt);
+       len = vsprintf(buf, fmt, params);
+       va_end(params);
+       send_sideband(1, 3, buf, len, LARGE_PACKET_MAX);
+       die("sent error to the client: %s", buf);
+}
+
+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)
+                       error_clnt("read error: %s\n", strerror(errno));
+               return;
+       }
+       send_sideband(1, band, buf, sz, LARGE_PACKET_MAX);
+}
+
+int cmd_upload_archive(int argc, const char **argv, const char *prefix)
+{
+       pid_t writer;
+       int fd1[2], fd2[2];
+       /*
+        * Set up sideband subprocess.
+        *
+        * We (parent) monitor and read from child, sending its fd#1 and fd#2
+        * multiplexed out to our fd#1.  If the child dies, we tell the other
+        * end over channel #3.
+        */
+       if (pipe(fd1) < 0 || pipe(fd2) < 0) {
+               int err = errno;
+               packet_write(1, "NACK pipe failed on the remote side\n");
+               die("upload-archive: %s", strerror(err));
+       }
+       writer = fork();
+       if (writer < 0) {
+               int err = errno;
+               packet_write(1, "NACK fork failed on the remote side\n");
+               die("upload-archive: %s", strerror(err));
+       }
+       if (!writer) {
+               /* child - connect fd#1 and fd#2 to the pipe */
+               dup2(fd1[1], 1);
+               dup2(fd2[1], 2);
+               close(fd1[1]); close(fd2[1]);
+               close(fd1[0]); close(fd2[0]); /* we do not read from pipe */
+
+               exit(run_upload_archive(argc, argv, prefix));
+       }
+
+       /* parent - read from child, multiplex and send out to fd#1 */
+       close(fd1[1]); close(fd2[1]); /* we do not write to pipe */
+       packet_write(1, "ACK\n");
+       packet_flush(1);
+
+       while (1) {
+               struct pollfd pfd[2];
+               int status;
+
+               pfd[0].fd = fd1[0];
+               pfd[0].events = POLLIN;
+               pfd[1].fd = fd2[0];
+               pfd[1].events = POLLIN;
+               if (poll(pfd, 2, -1) < 0) {
+                       if (errno != EINTR) {
+                               error("poll failed resuming: %s",
+                                     strerror(errno));
+                               sleep(1);
+                       }
+                       continue;
+               }
+               if (pfd[0].revents & POLLIN)
+                       /* Data stream ready */
+                       process_input(pfd[0].fd, 1);
+               if (pfd[1].revents & POLLIN)
+                       /* Status stream ready */
+                       process_input(pfd[1].fd, 2);
+               /* Always finish to read data when available */
+               if ((pfd[0].revents | pfd[1].revents) & POLLIN)
+                       continue;
+
+               if (waitpid(writer, &status, 0) < 0)
+                       error_clnt("%s", lostchild);
+               else if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
+                       error_clnt("%s", deadchild);
+               packet_flush(1);
+               break;
+       }
+       return 0;
+}
index 1c1f6830c1a8b9303eec3b1c11cfb1805d3049e4..52d4b7a17e39831964fe7c9c9c0cb374d0d61a62 100644 (file)
@@ -8,10 +8,12 @@
 #include "tree.h"
 #include "quote.h"
 #include "builtin.h"
+#include "archive.h"
 
 static const char zip_tree_usage[] =
 "git-zip-tree [-0|...|-9] <tree-ish> [ <base> ]";
 
+static int verbose;
 static int zip_date;
 static int zip_time;
 
@@ -163,6 +165,8 @@ static int write_zip_entry(const unsigned char *sha1,
        crc = crc32(0, Z_NULL, 0);
 
        path = construct_path(base, baselen, filename, S_ISDIR(mode), &pathlen);
+       if (verbose)
+               fprintf(stderr, "%s\n", path);
        if (pathlen > 0xffff) {
                error("path too long (%d chars, SHA1: %s): %s", pathlen,
                      sha1_to_hex(sha1), path);
@@ -351,3 +355,44 @@ int cmd_zip_tree(int argc, const char **argv, const char *prefix)
 
        return 0;
 }
+
+int write_zip_archive(struct archiver_args *args)
+{
+       int plen = strlen(args->base);
+
+       dos_time(&args->time, &zip_date, &zip_time);
+
+       zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);
+       zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
+       verbose = args->verbose;
+
+       if (args->base && plen > 0 && args->base[plen - 1] == '/') {
+               char *base = xstrdup(args->base);
+               int baselen = strlen(base);
+
+               while (baselen > 0 && base[baselen - 1] == '/')
+                       base[--baselen] = '\0';
+               write_zip_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
+               free(base);
+       }
+       read_tree_recursive(args->tree, args->base, plen, 0,
+                           args->pathspec, write_zip_entry);
+       write_zip_trailer(args->commit_sha1);
+
+       free(zip_dir);
+
+       return 0;
+}
+
+void *parse_extra_zip_args(int argc, const char **argv)
+{
+       for (; argc > 0; argc--, argv++) {
+               const char *arg = argv[0];
+
+               if (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\0')
+                       zlib_compression_level = arg[1] - '0';
+               else
+                       die("Unknown argument for zip format: %s", arg);
+       }
+       return NULL;
+}
index 37915bce4faeab22294b1f3d102c85271161117b..fc704bae1815e33d101bc0a499a9eacb72a11f5b 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -14,6 +14,7 @@ extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
 extern int cmd_apply(int argc, const char **argv, const char *prefix);
+extern int cmd_archive(int argc, const char **argv, const char *prefix);
 extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
 extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
 extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
@@ -47,6 +48,7 @@ extern int cmd_repo_config(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
 extern int cmd_rm(int argc, const char **argv, const char *prefix);
+extern int cmd_runstatus(int argc, const char **argv, const char *prefix);
 extern int cmd_show(int argc, const char **argv, const char *prefix);
 extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
 extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
@@ -56,10 +58,12 @@ extern int cmd_zip_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
 extern int cmd_update_index(int argc, const char **argv, const char *prefix);
 extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
+extern int cmd_upload_archive(int argc, const char **argv, const char *prefix);
 extern int cmd_upload_tar(int argc, const char **argv, const char *prefix);
 extern int cmd_version(int argc, const char **argv, const char *prefix);
 extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
 extern int cmd_write_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);
+extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);
 
 #endif
diff --git a/cache.h b/cache.h
index 8d099979d9a04231d73307c786d25bf412a3f22c..6def1551624fffd90044b8154ee78683143e343e 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -259,7 +259,7 @@ extern int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
 extern int write_sha1_to_fd(int fd, const unsigned char *sha1);
 extern int move_temp_to_file(const char *tmpfile, const char *filename);
 
-extern int has_sha1_pack(const unsigned char *sha1);
+extern int has_sha1_pack(const unsigned char *sha1, const char **ignore);
 extern int has_sha1_file(const unsigned char *sha1);
 extern void *map_sha1_file(const unsigned char *sha1, unsigned long *);
 extern int legacy_loose_object(unsigned char *);
@@ -286,9 +286,9 @@ extern int get_sha1(const char *str, unsigned char *sha1);
 extern int get_sha1_hex(const char *hex, unsigned char *sha1);
 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);
-extern int create_symref(const char *git_HEAD, const char *refs_heads_master);
-extern int validate_symref(const char *git_HEAD);
+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 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);
diff --git a/color.c b/color.c
new file mode 100644 (file)
index 0000000..d8c8399
--- /dev/null
+++ b/color.c
@@ -0,0 +1,176 @@
+#include "color.h"
+#include "cache.h"
+#include "git-compat-util.h"
+
+#include <stdarg.h>
+
+#define COLOR_RESET "\033[m"
+
+static int parse_color(const char *name, int len)
+{
+       static const char * const color_names[] = {
+               "normal", "black", "red", "green", "yellow",
+               "blue", "magenta", "cyan", "white"
+       };
+       char *end;
+       int i;
+       for (i = 0; i < ARRAY_SIZE(color_names); i++) {
+               const char *str = color_names[i];
+               if (!strncasecmp(name, str, len) && !str[len])
+                       return i - 1;
+       }
+       i = strtol(name, &end, 10);
+       if (*name && !*end && i >= -1 && i <= 255)
+               return i;
+       return -2;
+}
+
+static int parse_attr(const char *name, int len)
+{
+       static const int attr_values[] = { 1, 2, 4, 5, 7 };
+       static const char * const attr_names[] = {
+               "bold", "dim", "ul", "blink", "reverse"
+       };
+       int i;
+       for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
+               const char *str = attr_names[i];
+               if (!strncasecmp(name, str, len) && !str[len])
+                       return attr_values[i];
+       }
+       return -1;
+}
+
+void color_parse(const char *value, const char *var, char *dst)
+{
+       const char *ptr = value;
+       int attr = -1;
+       int fg = -2;
+       int bg = -2;
+
+       if (!strcasecmp(value, "reset")) {
+               strcpy(dst, "\033[m");
+               return;
+       }
+
+       /* [fg [bg]] [attr] */
+       while (*ptr) {
+               const char *word = ptr;
+               int val, len = 0;
+
+               while (word[len] && !isspace(word[len]))
+                       len++;
+
+               ptr = word + len;
+               while (*ptr && isspace(*ptr))
+                       ptr++;
+
+               val = parse_color(word, len);
+               if (val >= -1) {
+                       if (fg == -2) {
+                               fg = val;
+                               continue;
+                       }
+                       if (bg == -2) {
+                               bg = val;
+                               continue;
+                       }
+                       goto bad;
+               }
+               val = parse_attr(word, len);
+               if (val < 0 || attr != -1)
+                       goto bad;
+               attr = val;
+       }
+
+       if (attr >= 0 || fg >= 0 || bg >= 0) {
+               int sep = 0;
+
+               *dst++ = '\033';
+               *dst++ = '[';
+               if (attr >= 0) {
+                       *dst++ = '0' + attr;
+                       sep++;
+               }
+               if (fg >= 0) {
+                       if (sep++)
+                               *dst++ = ';';
+                       if (fg < 8) {
+                               *dst++ = '3';
+                               *dst++ = '0' + fg;
+                       } else {
+                               dst += sprintf(dst, "38;5;%d", fg);
+                       }
+               }
+               if (bg >= 0) {
+                       if (sep++)
+                               *dst++ = ';';
+                       if (bg < 8) {
+                               *dst++ = '4';
+                               *dst++ = '0' + bg;
+                       } else {
+                               dst += sprintf(dst, "48;5;%d", bg);
+                       }
+               }
+               *dst++ = 'm';
+       }
+       *dst = 0;
+       return;
+bad:
+       die("bad config value '%s' for variable '%s'", value, var);
+}
+
+int git_config_colorbool(const char *var, const char *value)
+{
+       if (!value)
+               return 1;
+       if (!strcasecmp(value, "auto")) {
+               if (isatty(1) || (pager_in_use && pager_use_color)) {
+                       char *term = getenv("TERM");
+                       if (term && strcmp(term, "dumb"))
+                               return 1;
+               }
+               return 0;
+       }
+       if (!strcasecmp(value, "never"))
+               return 0;
+       if (!strcasecmp(value, "always"))
+               return 1;
+       return git_config_bool(var, value);
+}
+
+static int color_vprintf(const char *color, const char *fmt,
+               va_list args, const char *trail)
+{
+       int r = 0;
+
+       if (*color)
+               r += printf("%s", color);
+       r += vprintf(fmt, args);
+       if (*color)
+               r += printf("%s", COLOR_RESET);
+       if (trail)
+               r += printf("%s", trail);
+       return r;
+}
+
+
+
+int color_printf(const char *color, const char *fmt, ...)
+{
+       va_list args;
+       int r;
+       va_start(args, fmt);
+       r = color_vprintf(color, fmt, args, NULL);
+       va_end(args);
+       return r;
+}
+
+int color_printf_ln(const char *color, const char *fmt, ...)
+{
+       va_list args;
+       int r;
+       va_start(args, fmt);
+       r = color_vprintf(color, fmt, args, "\n");
+       va_end(args);
+       return r;
+}
diff --git a/color.h b/color.h
new file mode 100644 (file)
index 0000000..88bb8ff
--- /dev/null
+++ b/color.h
@@ -0,0 +1,12 @@
+#ifndef COLOR_H
+#define COLOR_H
+
+/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
+#define COLOR_MAXLEN 24
+
+int git_config_colorbool(const char *var, const char *value);
+void color_parse(const char *var, const char *value, char *dst);
+int color_printf(const char *color, const char *fmt, ...);
+int color_printf_ln(const char *color, const char *fmt, ...);
+
+#endif /* COLOR_H */
index b14d8083bb14bd28a824c4417712842b1f70d829..a2954a0451316197621aed152c194d398aaee624 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -325,7 +325,14 @@ static int upload_pack(void)
        return -1;
 }
 
+static int upload_archive(void)
+{
+       execl_git_cmd("upload-archive", ".", NULL);
+       return -1;
+}
+
 static struct daemon_service daemon_service[] = {
+       { "upload-archive", "uploadarch", upload_archive, 0, 1 },
        { "upload-pack", "uploadpack", upload_pack, 1, 1 },
 };
 
index ab192f83ae27c5e0bf3f2a5f61684d0dc66deb32..f4029ee74e80305e9ec9e5793d2e12c07096f45a 100644 (file)
@@ -53,7 +53,7 @@ static void add_to_known_names(const char *path,
        names = ++idx;
 }
 
-static int get_name(const char *path, const unsigned char *sha1)
+static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
        struct commit *commit = lookup_commit_reference_gently(sha1, 1);
        struct object *object;
@@ -113,7 +113,7 @@ static void describe(const char *arg, int last_one)
 
        if (!initialized) {
                initialized = 1;
-               for_each_ref(get_name);
+               for_each_ref(get_name, NULL);
                qsort(name_array, names, sizeof(*name_array), compare_names);
        }
 
diff --git a/diff.c b/diff.c
index 6638865709888cc0ad2860ca1389a098b6f402f1..443e24861ba1857822dbe8e3c831f8037e4e832e 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -10,6 +10,7 @@
 #include "diffcore.h"
 #include "delta.h"
 #include "xdiff-interface.h"
+#include "color.h"
 
 static int use_size_cache;
 
@@ -17,8 +18,7 @@ static int diff_detect_rename_default;
 static int diff_rename_limit_default = -1;
 static int diff_use_color_default;
 
-/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
-static char diff_colors[][24] = {
+static char diff_colors[][COLOR_MAXLEN] = {
        "\033[m",       /* reset */
        "",             /* normal */
        "\033[1m",      /* bold */
@@ -45,119 +45,6 @@ static int parse_diff_color_slot(const char *var, int ofs)
        die("bad config variable '%s'", var);
 }
 
-static int parse_color(const char *name, int len)
-{
-       static const char * const color_names[] = {
-               "normal", "black", "red", "green", "yellow",
-               "blue", "magenta", "cyan", "white"
-       };
-       char *end;
-       int i;
-       for (i = 0; i < ARRAY_SIZE(color_names); i++) {
-               const char *str = color_names[i];
-               if (!strncasecmp(name, str, len) && !str[len])
-                       return i - 1;
-       }
-       i = strtol(name, &end, 10);
-       if (*name && !*end && i >= -1 && i <= 255)
-               return i;
-       return -2;
-}
-
-static int parse_attr(const char *name, int len)
-{
-       static const int attr_values[] = { 1, 2, 4, 5, 7 };
-       static const char * const attr_names[] = {
-               "bold", "dim", "ul", "blink", "reverse"
-       };
-       int i;
-       for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
-               const char *str = attr_names[i];
-               if (!strncasecmp(name, str, len) && !str[len])
-                       return attr_values[i];
-       }
-       return -1;
-}
-
-static void parse_diff_color_value(const char *value, const char *var, char *dst)
-{
-       const char *ptr = value;
-       int attr = -1;
-       int fg = -2;
-       int bg = -2;
-
-       if (!strcasecmp(value, "reset")) {
-               strcpy(dst, "\033[m");
-               return;
-       }
-
-       /* [fg [bg]] [attr] */
-       while (*ptr) {
-               const char *word = ptr;
-               int val, len = 0;
-
-               while (word[len] && !isspace(word[len]))
-                       len++;
-
-               ptr = word + len;
-               while (*ptr && isspace(*ptr))
-                       ptr++;
-
-               val = parse_color(word, len);
-               if (val >= -1) {
-                       if (fg == -2) {
-                               fg = val;
-                               continue;
-                       }
-                       if (bg == -2) {
-                               bg = val;
-                               continue;
-                       }
-                       goto bad;
-               }
-               val = parse_attr(word, len);
-               if (val < 0 || attr != -1)
-                       goto bad;
-               attr = val;
-       }
-
-       if (attr >= 0 || fg >= 0 || bg >= 0) {
-               int sep = 0;
-
-               *dst++ = '\033';
-               *dst++ = '[';
-               if (attr >= 0) {
-                       *dst++ = '0' + attr;
-                       sep++;
-               }
-               if (fg >= 0) {
-                       if (sep++)
-                               *dst++ = ';';
-                       if (fg < 8) {
-                               *dst++ = '3';
-                               *dst++ = '0' + fg;
-                       } else {
-                               dst += sprintf(dst, "38;5;%d", fg);
-                       }
-               }
-               if (bg >= 0) {
-                       if (sep++)
-                               *dst++ = ';';
-                       if (bg < 8) {
-                               *dst++ = '4';
-                               *dst++ = '0' + bg;
-                       } else {
-                               dst += sprintf(dst, "48;5;%d", bg);
-                       }
-               }
-               *dst++ = 'm';
-       }
-       *dst = 0;
-       return;
-bad:
-       die("bad config value '%s' for variable '%s'", value, var);
-}
-
 /*
  * These are to give UI layer defaults.
  * The core-level commands such as git-diff-files should
@@ -171,22 +58,7 @@ int git_diff_ui_config(const char *var, const char *value)
                return 0;
        }
        if (!strcmp(var, "diff.color")) {
-               if (!value)
-                       diff_use_color_default = 1; /* bool */
-               else if (!strcasecmp(value, "auto")) {
-                       diff_use_color_default = 0;
-                       if (isatty(1) || (pager_in_use && pager_use_color)) {
-                               char *term = getenv("TERM");
-                               if (term && strcmp(term, "dumb"))
-                                       diff_use_color_default = 1;
-                       }
-               }
-               else if (!strcasecmp(value, "never"))
-                       diff_use_color_default = 0;
-               else if (!strcasecmp(value, "always"))
-                       diff_use_color_default = 1;
-               else
-                       diff_use_color_default = git_config_bool(var, value);
+               diff_use_color_default = git_config_colorbool(var, value);
                return 0;
        }
        if (!strcmp(var, "diff.renames")) {
@@ -201,7 +73,7 @@ int git_diff_ui_config(const char *var, const char *value)
        }
        if (!strncmp(var, "diff.color.", 11)) {
                int slot = parse_diff_color_slot(var, 11);
-               parse_diff_color_value(value, var, diff_colors[slot]);
+               color_parse(value, var, diff_colors[slot]);
                return 0;
        }
        return git_default_config(var, value);
@@ -2593,6 +2465,9 @@ void diff_flush(struct diff_options *options)
                }
        }
 
+       if (output_format & DIFF_FORMAT_CALLBACK)
+               options->format_callback(q, options, options->format_callback_data);
+
        for (i = 0; i < q->nr; i++)
                diff_free_filepair(q->queue[i]);
 free_queue:
diff --git a/diff.h b/diff.h
index b007240a5dda9a9e1e54f22813c0d250b812fa3e..b60a02e627415d4edaf5056e7f95b519b7fe8fc0 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -8,6 +8,7 @@
 
 struct rev_info;
 struct diff_options;
+struct diff_queue_struct;
 
 typedef void (*change_fn_t)(struct diff_options *options,
                 unsigned old_mode, unsigned new_mode,
@@ -20,6 +21,9 @@ typedef void (*add_remove_fn_t)(struct diff_options *options,
                    const unsigned char *sha1,
                    const char *base, const char *path);
 
+typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
+               struct diff_options *options, void *data);
+
 #define DIFF_FORMAT_RAW                0x0001
 #define DIFF_FORMAT_DIFFSTAT   0x0002
 #define DIFF_FORMAT_SUMMARY    0x0004
@@ -35,6 +39,8 @@ typedef void (*add_remove_fn_t)(struct diff_options *options,
  */
 #define DIFF_FORMAT_NO_OUTPUT  0x0080
 
+#define DIFF_FORMAT_CALLBACK   0x0100
+
 struct diff_options {
        const char *filter;
        const char *orderfile;
@@ -68,6 +74,8 @@ struct diff_options {
        int *pathlens;
        change_fn_t change;
        add_remove_fn_t add_remove;
+       diff_format_fn_t format_callback;
+       void *format_callback_data;
 };
 
 enum color_diff {
diff --git a/dir.c b/dir.c
index 5a40d8ff8c99c80bf8f0484425db6022c6f7d8e9..e2f472ba7f5d3e5146f110dd3eaae9e5494854e2 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -397,3 +397,10 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i
        qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
        return dir->nr;
 }
+
+int
+file_exists(const char *f)
+{
+  struct stat sb;
+  return stat(f, &sb) == 0;
+}
diff --git a/dir.h b/dir.h
index 56a1b7fce224b489aa4ac91766cfb47a3097f197..313f8ab64e7e3a6be1d22335eb54872c929d857e 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -47,5 +47,6 @@ extern int excluded(struct dir_struct *, const char *);
 extern void add_excludes_from_file(struct dir_struct *, const char *fname);
 extern void add_exclude(const char *string, const char *base,
                        int baselen, struct exclude_list *which);
+extern int file_exists(const char *);
 
 #endif
index c5cf4776fabb8f9f09028dd1f0cfaf1d55e1397c..b632ca0438b378944b37a7d64fd508ea3f7f470f 100644 (file)
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "exec_cmd.h"
 #include "pkt-line.h"
+#include "sideband.h"
 #include <sys/wait.h>
 #include <sys/time.h>
 
@@ -114,36 +115,13 @@ static pid_t setup_sideband(int sideband, const char *me, int fd[2], int xd[2])
                die("%s: unable to fork off sideband demultiplexer", me);
        if (!side_pid) {
                /* subprocess */
+               char buf[LARGE_PACKET_MAX];
+
                close(fd[0]);
                if (xd[0] != xd[1])
                        close(xd[1]);
-               while (1) {
-                       char buf[1024];
-                       int len = packet_read_line(xd[0], buf, sizeof(buf));
-                       if (len == 0)
-                               break;
-                       if (len < 1)
-                               die("%s: protocol error: no band designator",
-                                   me);
-                       len--;
-                       switch (buf[0] & 0xFF) {
-                       case 3:
-                               safe_write(2, "remote: ", 8);
-                               safe_write(2, buf+1, len);
-                               safe_write(2, "\n", 1);
-                               exit(1);
-                       case 2:
-                               safe_write(2, "remote: ", 8);
-                               safe_write(2, buf+1, len);
-                               continue;
-                       case 1:
-                               safe_write(fd[1], buf+1, len);
-                               continue;
-                       default:
-                               die("%s: protocol error: bad band #%d",
-                                   me, (buf[0] & 0xFF));
-                       }
-               }
+               if (recv_sideband(me, xd[0], fd[1], 2, buf, sizeof(buf)))
+                       exit(1);
                exit(0);
        }
        close(xd[0]);
index 1b4d8272dce834d10b16ecbcea7ae2fa974fc856..99ac08b2c2528358d51665ef3f301cd68947409c 100644 (file)
@@ -42,7 +42,7 @@ static void rev_list_push(struct commit *commit, int mark)
        }
 }
 
-static int rev_list_insert_ref(const char *path, const unsigned char *sha1)
+static int rev_list_insert_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
        struct object *o = deref_tag(parse_object(sha1), path, 0);
 
@@ -143,7 +143,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
        unsigned in_vain = 0;
        int got_continue = 0;
 
-       for_each_ref(rev_list_insert_ref);
+       for_each_ref(rev_list_insert_ref, NULL);
 
        fetching = 0;
        for ( ; refs ; refs = refs->next) {
@@ -166,10 +166,11 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                }
 
                if (!fetching)
-                       packet_write(fd[1], "want %s%s%s%s\n",
+                       packet_write(fd[1], "want %s%s%s%s%s\n",
                                     sha1_to_hex(remote),
                                     (multi_ack ? " multi_ack" : ""),
-                                    (use_sideband ? " side-band" : ""),
+                                    (use_sideband == 2 ? " side-band-64k" : ""),
+                                    (use_sideband == 1 ? " side-band" : ""),
                                     (use_thin_pack ? " thin-pack" : ""));
                else
                        packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
@@ -252,7 +253,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
 
 static struct commit_list *complete;
 
-static int mark_complete(const char *path, const unsigned char *sha1)
+static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
        struct object *o = parse_object(sha1);
 
@@ -364,7 +365,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
                }
        }
 
-       for_each_ref(mark_complete);
+       for_each_ref(mark_complete, NULL);
        if (cutoff)
                mark_recent_complete_commits(cutoff);
 
@@ -426,7 +427,12 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
                        fprintf(stderr, "Server supports multi_ack\n");
                multi_ack = 1;
        }
-       if (server_supports("side-band")) {
+       if (server_supports("side-band-64k")) {
+               if (verbose)
+                       fprintf(stderr, "Server supports side-band-64k\n");
+               use_sideband = 2;
+       }
+       else if (server_supports("side-band")) {
                if (verbose)
                        fprintf(stderr, "Server supports side-band\n");
                use_sideband = 1;
diff --git a/fetch.c b/fetch.c
index 34df8d37d7dc92f8b652c160c49f4ace6e44e19c..a2cbdfba8246a24b7da3ccf7cc0dbc1be2bd52bf 100644 (file)
--- a/fetch.c
+++ b/fetch.c
@@ -201,7 +201,7 @@ static int interpret_target(char *target, unsigned char *sha1)
        return -1;
 }
 
-static int mark_complete(const char *path, const unsigned char *sha1)
+static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
        struct commit *commit = lookup_commit_reference_gently(sha1, 1);
        if (commit) {
@@ -274,7 +274,7 @@ int pull(int targets, char **target, const char **write_ref,
        }
 
        if (!get_recover)
-               for_each_ref(mark_complete);
+               for_each_ref(mark_complete, NULL);
 
        for (i = 0; i < targets; i++) {
                if (interpret_target(target[i], &sha1[20 * i])) {
index 4d994f3fc83d71501bbfde5159a869b4f2a38e99..46b628cb94375e3f645f868efb04547ffc20e6e7 100644 (file)
@@ -402,7 +402,7 @@ static void fsck_dir(int i, char *path)
 
 static int default_refs;
 
-static int fsck_handle_ref(const char *refname, const unsigned char *sha1)
+static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
        struct object *obj;
 
@@ -424,7 +424,7 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1)
 
 static void get_default_heads(void)
 {
-       for_each_ref(fsck_handle_ref);
+       for_each_ref(fsck_handle_ref, NULL);
 
        /*
         * Not having any default heads isn't really fatal, but
@@ -458,15 +458,14 @@ static void fsck_object_dir(const char *path)
 static int fsck_head_link(void)
 {
        unsigned char sha1[20];
-       const char *git_HEAD = xstrdup(git_path("HEAD"));
-       const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 1);
-       int pfxlen = strlen(git_HEAD) - 4; /* strip .../.git/ part */
+       int flag;
+       const char *head_points_at = resolve_ref("HEAD", sha1, 1, &flag);
 
-       if (!git_refs_heads_master)
+       if (!head_points_at || !(flag & REF_ISSYMREF))
                return error("HEAD is not a symbolic ref");
-       if (strncmp(git_refs_heads_master + pfxlen, "refs/heads/", 11))
+       if (strncmp(head_points_at, "refs/heads/", 11))
                return error("HEAD points to something strange (%s)",
-                            git_refs_heads_master + pfxlen);
+                            head_points_at);
        if (is_null_sha1(sha1))
                return error("HEAD: not a valid git pointer");
        return 0;
index ec1eda20de8d510a9acfd981150a5f1a438a0fe4..5450918be339bf101a6562d518c0f4cc7b37c0f2 100755 (executable)
@@ -12,6 +12,7 @@ struct cmdname_help common_cmds[] = {"
 sort <<\EOF |
 add
 apply
+archive
 bisect
 branch
 checkout
index d0af786aec3f797943290cdc63fa77f393900160..afe322b20fb0b40dfeb1cb17dc3cad09096e943a 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -166,10 +166,25 @@ fi
 
 if test -d "$dotest"
 then
-       if test ",$#," != ",0," || ! tty -s
-       then
-               die "previous dotest directory $dotest still exists but mbox given."
-       fi
+       case "$#,$skip$resolved" in
+       0,*t*)
+               # Explicit resume command and we do not have file, so
+               # we are happy.
+               : ;;
+       0,)
+               # No file input but without resume parameters; catch
+               # user error to feed us a patch from standard input
+               # when there is already .dotest.  This is somewhat
+               # unreliable -- stdin could be /dev/null for example
+               # and the caller did not intend to feed us a patch but
+               # wanted to continue unattended.
+               tty -s
+               ;;
+       *)
+               false
+               ;;
+       esac ||
+       die "previous dotest directory $dotest still exists but mbox given."
        resume=yes
 else
        # Make sure we are not given --skip nor --resolved
index 580a9e8a233f0a94c84792eba7c3dc653fccf3a0..c60e029b6da34f43b45300400fbef0667ce9652f 100755 (executable)
@@ -22,7 +22,7 @@ while [ "$#" != "0" ]; do
                shift
                [ -z "$newbranch" ] &&
                        die "git checkout: -b needs a branch name"
-               [ -e "$GIT_DIR/refs/heads/$newbranch" ] &&
+               git-rev-parse --symbolic "heads/$newbranch" >&/dev/null &&
                        die "git checkout: branch $newbranch already exists"
                git-check-ref-format "heads/$newbranch" ||
                        die "git checkout: we do not like '$newbranch' as a branch name."
@@ -51,7 +51,7 @@ while [ "$#" != "0" ]; do
                        fi
                        new="$rev"
                        new_name="$arg^0"
-                       if [ -f "$GIT_DIR/refs/heads/$arg" ]; then
+                       if git-rev-parse "heads/$arg^0" >&/dev/null; then
                                branch="$arg"
                        fi
                elif rev=$(git-rev-parse --verify "$arg^{tree}" 2>/dev/null)
index 4cf3fab05cd3c3367173c86a65665ddfc1d89805..5a4c659b6fb5f0b23c6a10b0e3bd69893d9c380a 100755 (executable)
@@ -60,26 +60,6 @@ report () {
 }
 
 run_status () {
-    (
-       # We always show status for the whole tree.
-       cd "$TOP"
-
-       IS_INITIAL="$initial_commit"
-       REFERENCE=HEAD
-       case "$amend" in
-       t)
-               # If we are amending the initial commit, there
-               # is no HEAD^1.
-               if git-rev-parse --verify "HEAD^1" >/dev/null 2>&1
-               then
-                       REFERENCE="HEAD^1"
-                       IS_INITIAL=
-               else
-                       IS_INITIAL=t
-               fi
-               ;;
-       esac
-
        # If TMP_INDEX is defined, that means we are doing
        # "--only" partial commit, and that index file is used
        # to build the tree for the commit.  Otherwise, if
@@ -96,85 +76,14 @@ run_status () {
            export GIT_INDEX_FILE
        fi
 
-       case "$branch" in
-       refs/heads/master) ;;
-       *)  echo "# On branch $branch" ;;
-       esac
-
-       if test -z "$IS_INITIAL"
-       then
-           git-diff-index -M --cached --name-status \
-               --diff-filter=MDTCRA $REFERENCE |
-           sed -e '
-                   s/\\/\\\\/g
-                   s/ /\\ /g
-           ' |
-           report "Updated but not checked in" "will commit"
-           committable="$?"
-       else
-           echo '#
-# Initial commit
-#'
-           git-ls-files |
-           sed -e '
-                   s/\\/\\\\/g
-                   s/ /\\ /g
-                   s/^/A /
-           ' |
-           report "Updated but not checked in" "will commit"
-
-           committable="$?"
-       fi
-
-       git-diff-files  --name-status |
-       sed -e '
-               s/\\/\\\\/g
-               s/ /\\ /g
-       ' |
-       report "Changed but not updated" \
-           "use git-update-index to mark for commit"
-
-        option=""
-        if test -z "$untracked_files"; then
-            option="--directory --no-empty-directory"
-        fi
-       hdr_shown=
-       if test -f "$GIT_DIR/info/exclude"
-       then
-           git-ls-files --others $option \
-               --exclude-from="$GIT_DIR/info/exclude" \
-               --exclude-per-directory=.gitignore
-       else
-           git-ls-files --others $option \
-               --exclude-per-directory=.gitignore
-       fi |
-       while read line; do
-           if [ -z "$hdr_shown" ]; then
-               echo '#'
-               echo '# Untracked files:'
-               echo '#   (use "git add" to add to commit)'
-               echo '#'
-               hdr_shown=1
-           fi
-           echo "#     $line"
-       done
-
-       if test -n "$verbose" -a -z "$IS_INITIAL"
-       then
-           git-diff-index --cached -M -p --diff-filter=MDTCRA $REFERENCE
-       fi
-       case "$committable" in
-       0)
-               case "$amend" in
-               t)
-                       echo "# No changes" ;;
-               *)
-                       echo "nothing to commit" ;;
-               esac
-               exit 1 ;;
-       esac
-       exit 0
-    )
+  case "$status_only" in
+    t) color= ;;
+    *) color=--nocolor ;;
+  esac
+  git-runstatus ${color} \
+                ${verbose:+--verbose} \
+                ${amend:+--amend} \
+               ${untracked_files:+--untracked}
 }
 
 trap '
diff --git a/git.c b/git.c
index 10db27bca04b4c6e7278d740bc1af254c7247471..561be303dcf4a22ed03179244fd3424f76934cad 100644 (file)
--- a/git.c
+++ b/git.c
@@ -220,6 +220,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
        } commands[] = {
                { "add", cmd_add, RUN_SETUP },
                { "apply", cmd_apply },
+               { "archive", cmd_archive },
                { "cat-file", cmd_cat_file, RUN_SETUP },
                { "checkout-index", cmd_checkout_index, RUN_SETUP },
                { "check-ref-format", cmd_check_ref_format },
@@ -253,6 +254,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "rev-list", cmd_rev_list, RUN_SETUP },
                { "rev-parse", cmd_rev_parse, RUN_SETUP },
                { "rm", cmd_rm, RUN_SETUP },
+               { "runstatus", cmd_runstatus, RUN_SETUP },
                { "show-branch", cmd_show_branch, RUN_SETUP },
                { "show", cmd_show, RUN_SETUP | USE_PAGER },
                { "stripspace", cmd_stripspace },
@@ -262,11 +264,13 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "unpack-objects", cmd_unpack_objects, RUN_SETUP },
                { "update-index", cmd_update_index, RUN_SETUP },
                { "update-ref", cmd_update_ref, RUN_SETUP },
+               { "upload-archive", cmd_upload_archive },
                { "upload-tar", cmd_upload_tar },
                { "version", cmd_version },
                { "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
                { "write-tree", cmd_write_tree, RUN_SETUP },
                { "verify-pack", cmd_verify_pack },
+               { "pack-refs", cmd_pack_refs, RUN_SETUP },
        };
        int i;
 
index a81c8d4cf2d605232ae49aed36c3b8bd0f7ccfee..c77270c7cd7ef478f708dbbc6165ac420891ca5a 100755 (executable)
 # source of projects list
 our $projects_list = "++GITWEB_LIST++";
 
+# show repository only if this file exists
+# (only effective if this variable evaluates to true)
+our $export_ok = "++GITWEB_EXPORT_OK++";
+
+# only allow viewing of repositories also shown on the overview page
+our $strict_export = "++GITWEB_STRICT_EXPORT++";
+
 # list of git base URLs used for URL to where fetch project from,
 # i.e. full URL is "$git_base_url/$project"
 our @git_base_url_list = ("++GITWEB_BASE_URL++");
@@ -182,9 +189,6 @@ sub feature_pickaxe {
 # version of the core git binary
 our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown";
 
-# path to the current git repository
-our $git_dir;
-
 $projects_list ||= $projectroot;
 
 # ======================================================================
@@ -196,23 +200,16 @@ sub feature_pickaxe {
        }
 }
 
-our $project = ($cgi->param('p') || $ENV{'PATH_INFO'});
-if (defined $project) {
-       $project =~ s|^/||;
-       $project =~ s|/$||;
-       $project = undef unless $project;
-}
+our $project = $cgi->param('p');
 if (defined $project) {
-       if (!validate_input($project)) {
-               die_error(undef, "Invalid project parameter");
-       }
-       if (!(-d "$projectroot/$project")) {
-               die_error(undef, "No such directory");
-       }
-       if (!(-e "$projectroot/$project/HEAD")) {
+       if (!validate_input($project) ||
+           !(-d "$projectroot/$project") ||
+           !(-e "$projectroot/$project/HEAD") ||
+           ($export_ok && !(-e "$projectroot/$project/$export_ok")) ||
+           ($strict_export && !project_in_list($project))) {
+               undef $project;
                die_error(undef, "No such project");
        }
-       $git_dir = "$projectroot/$project";
 }
 
 our $file_name = $cgi->param('f');
@@ -259,7 +256,7 @@ sub feature_pickaxe {
 
 our $page = $cgi->param('pg');
 if (defined $page) {
-       if ($page =~ m/[^0-9]$/) {
+       if ($page =~ m/[^0-9]/) {
                die_error(undef, "Invalid page parameter");
        }
 }
@@ -272,6 +269,43 @@ sub feature_pickaxe {
        $searchtext = quotemeta $searchtext;
 }
 
+# now read PATH_INFO and use it as alternative to parameters
+sub evaluate_path_info {
+       return if defined $project;
+       my $path_info = $ENV{"PATH_INFO"};
+       return if !$path_info;
+       $path_info =~ s,(^/|/$),,gs;
+       $path_info = validate_input($path_info);
+       return if !$path_info;
+       $project = $path_info;
+       while ($project && !-e "$projectroot/$project/HEAD") {
+               $project =~ s,/*[^/]*$,,;
+       }
+       if (!$project ||
+           ($export_ok && !-e "$projectroot/$project/$export_ok") ||
+           ($strict_export && !project_in_list($project))) {
+               undef $project;
+               return;
+       }
+       # do not change any parameters if an action is given using the query string
+       return if $action;
+       if ($path_info =~ m,^$project/([^/]+)/(.+)$,) {
+               # we got "project.git/branch/filename"
+               $action    ||= "blob_plain";
+               $hash_base ||= validate_input($1);
+               $file_name ||= validate_input($2);
+       } elsif ($path_info =~ m,^$project/([^/]+)$,) {
+               # we got "project.git/branch"
+               $action ||= "shortlog";
+               $hash   ||= validate_input($1);
+       }
+}
+evaluate_path_info();
+
+# path to the current git repository
+our $git_dir;
+$git_dir = "$projectroot/$project" if $project;
+
 # dispatch
 my %actions = (
        "blame" => \&git_blame2,
@@ -405,6 +439,12 @@ sub untabify {
        return $line;
 }
 
+sub project_in_list {
+       my $project = shift;
+       my @list = git_get_projects_list();
+       return @list && scalar(grep { $_->{'path'} eq $project } @list);
+}
+
 ## ----------------------------------------------------------------------
 ## HTML aware string manipulation
 
@@ -717,7 +757,8 @@ sub git_get_projects_list {
 
                                my $subdir = substr($File::Find::name, $pfxlen + 1);
                                # we check related file in $projectroot
-                               if (-e "$projectroot/$subdir/HEAD") {
+                               if (-e "$projectroot/$subdir/HEAD" && (!$export_ok ||
+                                   -e "$projectroot/$subdir/$export_ok")) {
                                        push @list, { path => $subdir };
                                        $File::Find::prune = 1;
                                }
@@ -738,7 +779,8 @@ sub git_get_projects_list {
                        if (!defined $path) {
                                next;
                        }
-                       if (-e "$projectroot/$path/HEAD") {
+                       if (-e "$projectroot/$path/HEAD" && (!$export_ok ||
+                           -e "$projectroot/$path/$export_ok")) {
                                my $pr = {
                                        path => $path,
                                        owner => decode("utf8", $owner, Encode::FB_DEFAULT),
@@ -2527,11 +2569,7 @@ sub git_heads {
 }
 
 sub git_blob_plain {
-       # blobs defined by non-textual hash id's can be cached
        my $expires;
-       if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
-               $expires = "+1d";
-       }
 
        if (!defined $hash) {
                if (defined $file_name) {
@@ -2541,7 +2579,11 @@ sub git_blob_plain {
                } else {
                        die_error(undef, "No file name defined");
                }
+       } elsif ($hash =~ m/^[0-9a-fA-F]{40}$/) {
+               # blobs defined by non-textual hash id's can be cached
+               $expires = "+1d";
        }
+
        my $type = shift;
        open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
                or die_error(undef, "Couldn't cat $file_name, $hash");
@@ -2569,11 +2611,7 @@ sub git_blob_plain {
 }
 
 sub git_blob {
-       # blobs defined by non-textual hash id's can be cached
        my $expires;
-       if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
-               $expires = "+1d";
-       }
 
        if (!defined $hash) {
                if (defined $file_name) {
@@ -2583,7 +2621,11 @@ sub git_blob {
                } else {
                        die_error(undef, "No file name defined");
                }
+       } elsif ($hash =~ m/^[0-9a-fA-F]{40}$/) {
+               # blobs defined by non-textual hash id's can be cached
+               $expires = "+1d";
        }
+
        my ($have_blame) = gitweb_check_feature('blame');
        open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
                or die_error(undef, "Couldn't cat $file_name, $hash");
index a113bb8c4b0c8d59b5b6df3c7689ba9f50818753..bc74f30f76fe0200f6c7ecc215ee1cd9211670f4 100644 (file)
@@ -144,6 +144,19 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
        return size;
 }
 
+static int missing__target(int code, int result)
+{
+       return  /* file:// URL -- do we ever use one??? */
+               (result == CURLE_FILE_COULDNT_READ_FILE) ||
+               /* http:// and https:// URL */
+               (code == 404 && result == CURLE_HTTP_RETURNED_ERROR) ||
+               /* ftp:// URL */
+               (code == 550 && result == CURLE_FTP_COULDNT_RETR_FILE)
+               ;
+}
+
+#define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
+
 static void fetch_alternates(const char *base);
 
 static void process_object_response(void *callback_data);
@@ -323,8 +336,7 @@ static void process_object_response(void *callback_data)
        obj_req->state = COMPLETE;
 
        /* Use alternates if necessary */
-       if (obj_req->http_code == 404 ||
-           obj_req->curl_result == CURLE_FILE_COULDNT_READ_FILE) {
+       if (missing_target(obj_req)) {
                fetch_alternates(alt->base);
                if (obj_req->repo->next != NULL) {
                        obj_req->repo =
@@ -537,8 +549,7 @@ static void process_alternates_response(void *callback_data)
                        return;
                }
        } else if (slot->curl_result != CURLE_OK) {
-               if (slot->http_code != 404 &&
-                   slot->curl_result != CURLE_FILE_COULDNT_READ_FILE) {
+               if (!missing_target(slot)) {
                        got_alternates = -1;
                        return;
                }
@@ -941,8 +952,7 @@ static int fetch_indices(struct alt_base *repo)
        if (start_active_slot(slot)) {
                run_active_slot(slot);
                if (results.curl_result != CURLE_OK) {
-                       if (results.http_code == 404 ||
-                           results.curl_result == CURLE_FILE_COULDNT_READ_FILE) {
+                       if (missing_target(&results)) {
                                repo->got_indices = 1;
                                free(buffer.buffer);
                                return 0;
@@ -1123,8 +1133,7 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1)
                ret = error("Request for %s aborted", hex);
        } else if (obj_req->curl_result != CURLE_OK &&
                   obj_req->http_code != 416) {
-               if (obj_req->http_code == 404 ||
-                   obj_req->curl_result == CURLE_FILE_COULDNT_READ_FILE)
+               if (missing_target(obj_req))
                        ret = -1; /* Be silent, it is probably in a pack. */
                else
                        ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",
index 670ff007beb8a5ba3043045a0b31399cd108c310..ecefdfd4f8c9c17282f5cec10640343359278028 100644 (file)
@@ -1864,7 +1864,7 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock)
 static struct ref *local_refs, **local_tail;
 static struct ref *remote_refs, **remote_tail;
 
-static int one_local_ref(const char *refname, const unsigned char *sha1)
+static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
        struct ref *ref;
        int len = strlen(refname) + 1;
@@ -1913,7 +1913,7 @@ static void one_remote_ref(char *refname)
 static void get_local_heads(void)
 {
        local_tail = &local_refs;
-       for_each_ref(one_local_ref);
+       for_each_ref(one_local_ref, NULL);
 }
 
 static void get_dav_remote_heads(void)
diff --git a/list-objects.c b/list-objects.c
new file mode 100644 (file)
index 0000000..f1fa21c
--- /dev/null
@@ -0,0 +1,140 @@
+#include "cache.h"
+#include "tag.h"
+#include "commit.h"
+#include "tree.h"
+#include "blob.h"
+#include "diff.h"
+#include "tree-walk.h"
+#include "revision.h"
+#include "list-objects.h"
+
+static void process_blob(struct rev_info *revs,
+                        struct blob *blob,
+                        struct object_array *p,
+                        struct name_path *path,
+                        const char *name)
+{
+       struct object *obj = &blob->object;
+
+       if (!revs->blob_objects)
+               return;
+       if (obj->flags & (UNINTERESTING | SEEN))
+               return;
+       obj->flags |= SEEN;
+       name = xstrdup(name);
+       add_object(obj, p, path, name);
+}
+
+static void process_tree(struct rev_info *revs,
+                        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 (!revs->tree_objects)
+               return;
+       if (obj->flags & (UNINTERESTING | SEEN))
+               return;
+       if (parse_tree(tree) < 0)
+               die("bad tree object %s", sha1_to_hex(obj->sha1));
+       obj->flags |= SEEN;
+       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(revs,
+                                    lookup_tree(entry.sha1),
+                                    p, &me, entry.path);
+               else
+                       process_blob(revs,
+                                    lookup_blob(entry.sha1),
+                                    p, &me, entry.path);
+       }
+       free(tree->buffer);
+       tree->buffer = NULL;
+}
+
+static void mark_edge_parents_uninteresting(struct commit *commit,
+                                           struct rev_info *revs,
+                                           show_edge_fn show_edge)
+{
+       struct commit_list *parents;
+
+       for (parents = commit->parents; parents; parents = parents->next) {
+               struct commit *parent = parents->item;
+               if (!(parent->object.flags & UNINTERESTING))
+                       continue;
+               mark_tree_uninteresting(parent->tree);
+               if (revs->edge_hint && !(parent->object.flags & SHOWN)) {
+                       parent->object.flags |= SHOWN;
+                       show_edge(parent);
+               }
+       }
+}
+
+void mark_edges_uninteresting(struct commit_list *list,
+                             struct rev_info *revs,
+                             show_edge_fn show_edge)
+{
+       for ( ; list; list = list->next) {
+               struct commit *commit = list->item;
+
+               if (commit->object.flags & UNINTERESTING) {
+                       mark_tree_uninteresting(commit->tree);
+                       continue;
+               }
+               mark_edge_parents_uninteresting(commit, revs, show_edge);
+       }
+}
+
+void traverse_commit_list(struct rev_info *revs,
+                         void (*show_commit)(struct commit *),
+                         void (*show_object)(struct object_array_entry *))
+{
+       int i;
+       struct commit *commit;
+       struct object_array objects = { 0, 0, NULL };
+
+       while ((commit = get_revision(revs)) != NULL) {
+               process_tree(revs, commit->tree, &objects, NULL, "");
+               show_commit(commit);
+       }
+       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->flags & (UNINTERESTING | SEEN))
+                       continue;
+               if (obj->type == OBJ_TAG) {
+                       obj->flags |= SEEN;
+                       add_object_array(obj, name, &objects);
+                       continue;
+               }
+               if (obj->type == OBJ_TREE) {
+                       process_tree(revs, (struct tree *)obj, &objects,
+                                    NULL, name);
+                       continue;
+               }
+               if (obj->type == OBJ_BLOB) {
+                       process_blob(revs, (struct blob *)obj, &objects,
+                                    NULL, name);
+                       continue;
+               }
+               die("unknown pending object %s (%s)",
+                   sha1_to_hex(obj->sha1), name);
+       }
+       for (i = 0; i < objects.nr; i++)
+               show_object(&objects.objects[i]);
+}
diff --git a/list-objects.h b/list-objects.h
new file mode 100644 (file)
index 0000000..0f41391
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef LIST_OBJECTS_H
+#define LIST_OBJECTS_H
+
+typedef void (*show_commit_fn)(struct commit *);
+typedef void (*show_object_fn)(struct object_array_entry *);
+typedef void (*show_edge_fn)(struct commit *);
+
+void traverse_commit_list(struct rev_info *revs, show_commit_fn, show_object_fn);
+
+void mark_edges_uninteresting(struct commit_list *, struct rev_info *, show_edge_fn);
+
+#endif
index 78f75da5ca99bd5457fd4e0f6e3af2737afc335a..abbcb6af0b61ba5d31db878ccd78e5d7c9c64971 100644 (file)
@@ -12,7 +12,7 @@ static int report_status;
 static char capabilities[] = "report-status";
 static int capabilities_sent;
 
-static int show_ref(const char *path, const unsigned char *sha1)
+static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
        if (capabilities_sent)
                packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
@@ -25,9 +25,9 @@ static int show_ref(const char *path, const unsigned char *sha1)
 
 static void write_head_info(void)
 {
-       for_each_ref(show_ref);
+       for_each_ref(show_ref, NULL);
        if (!capabilities_sent)
-               show_ref("capabilities^{}", null_sha1);
+               show_ref("capabilities^{}", null_sha1, 0, NULL);
 
 }
 
diff --git a/refs.c b/refs.c
index 5e653141ceb8183cfb7922c3ad89fd1a0110647a..40f16af18562dbe3e32be2dfe7c700c295ed744e 100644 (file)
--- a/refs.c
+++ b/refs.c
 
 #include <errno.h>
 
+struct ref_list {
+       struct ref_list *next;
+       unsigned char flag; /* ISSYMREF? ISPACKED? */
+       unsigned char sha1[20];
+       char name[FLEX_ARRAY];
+};
+
+static const char *parse_ref_line(char *line, unsigned char *sha1)
+{
+       /*
+        * 42: the answer to everything.
+        *
+        * In this case, it happens to be the answer to
+        *  40 (length of sha1 hex representation)
+        *  +1 (space in between hex and name)
+        *  +1 (newline at the end of the line)
+        */
+       int len = strlen(line) - 42;
+
+       if (len <= 0)
+               return NULL;
+       if (get_sha1_hex(line, sha1) < 0)
+               return NULL;
+       if (!isspace(line[40]))
+               return NULL;
+       line += 41;
+       if (isspace(*line))
+               return NULL;
+       if (line[len] != '\n')
+               return NULL;
+       line[len] = 0;
+       return line;
+}
+
+static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
+                               int flag, struct ref_list *list)
+{
+       int len;
+       struct ref_list **p = &list, *entry;
+
+       /* Find the place to insert the ref into.. */
+       while ((entry = *p) != NULL) {
+               int cmp = strcmp(entry->name, name);
+               if (cmp > 0)
+                       break;
+
+               /* Same as existing entry? */
+               if (!cmp)
+                       return list;
+               p = &entry->next;
+       }
+
+       /* Allocate it and add it in.. */
+       len = strlen(name) + 1;
+       entry = xmalloc(sizeof(struct ref_list) + len);
+       hashcpy(entry->sha1, sha1);
+       memcpy(entry->name, name, len);
+       entry->flag = flag;
+       entry->next = *p;
+       *p = entry;
+       return list;
+}
+
+static struct ref_list *get_packed_refs(void)
+{
+       static int did_refs = 0;
+       static struct ref_list *refs = NULL;
+
+       if (!did_refs) {
+               FILE *f = fopen(git_path("packed-refs"), "r");
+               if (f) {
+                       struct ref_list *list = NULL;
+                       char refline[PATH_MAX];
+                       while (fgets(refline, sizeof(refline), f)) {
+                               unsigned char sha1[20];
+                               const char *name = parse_ref_line(refline, sha1);
+                               if (!name)
+                                       continue;
+                               list = add_ref(name, sha1, REF_ISPACKED, list);
+                       }
+                       fclose(f);
+                       refs = list;
+               }
+               did_refs = 1;
+       }
+       return refs;
+}
+
+static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
+{
+       DIR *dir = opendir(git_path("%s", base));
+
+       if (dir) {
+               struct dirent *de;
+               int baselen = strlen(base);
+               char *ref = xmalloc(baselen + 257);
+
+               memcpy(ref, base, baselen);
+               if (baselen && base[baselen-1] != '/')
+                       ref[baselen++] = '/';
+
+               while ((de = readdir(dir)) != NULL) {
+                       unsigned char sha1[20];
+                       struct stat st;
+                       int flag;
+                       int namelen;
+
+                       if (de->d_name[0] == '.')
+                               continue;
+                       namelen = strlen(de->d_name);
+                       if (namelen > 255)
+                               continue;
+                       if (has_extension(de->d_name, ".lock"))
+                               continue;
+                       memcpy(ref + baselen, de->d_name, namelen+1);
+                       if (stat(git_path("%s", ref), &st) < 0)
+                               continue;
+                       if (S_ISDIR(st.st_mode)) {
+                               list = get_ref_dir(ref, list);
+                               continue;
+                       }
+                       if (!resolve_ref(ref, sha1, 1, &flag)) {
+                               error("%s points nowhere!", ref);
+                               continue;
+                       }
+                       list = add_ref(ref, sha1, flag, list);
+               }
+               free(ref);
+               closedir(dir);
+       }
+       return list;
+}
+
+static struct ref_list *get_loose_refs(void)
+{
+       static int did_refs = 0;
+       static struct ref_list *refs = NULL;
+
+       if (!did_refs) {
+               refs = get_ref_dir("refs", NULL);
+               did_refs = 1;
+       }
+       return refs;
+}
+
 /* We allow "recursive" symbolic refs. Only within reason, though */
 #define MAXDEPTH 5
 
-const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
+const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
 {
        int depth = MAXDEPTH, len;
        char buffer[256];
+       static char ref_buffer[256];
+
+       if (flag)
+               *flag = 0;
 
        for (;;) {
+               const char *path = git_path("%s", ref);
                struct stat st;
                char *buf;
                int fd;
@@ -27,17 +177,31 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
                 * reading.
                 */
                if (lstat(path, &st) < 0) {
+                       struct ref_list *list = get_packed_refs();
+                       while (list) {
+                               if (!strcmp(ref, list->name)) {
+                                       hashcpy(sha1, list->sha1);
+                                       if (flag)
+                                               *flag |= REF_ISPACKED;
+                                       return ref;
+                               }
+                               list = list->next;
+                       }
                        if (reading || errno != ENOENT)
                                return NULL;
                        hashclr(sha1);
-                       return path;
+                       return ref;
                }
 
                /* Follow "normalized" - ie "refs/.." symlinks by hand */
                if (S_ISLNK(st.st_mode)) {
                        len = readlink(path, buffer, sizeof(buffer)-1);
                        if (len >= 5 && !memcmp("refs/", buffer, 5)) {
-                               path = git_path("%.*s", len, buffer);
+                               buffer[len] = 0;
+                               strcpy(ref_buffer, buffer);
+                               ref = ref_buffer;
+                               if (flag)
+                                       *flag |= REF_ISSYMREF;
                                continue;
                        }
                }
@@ -62,19 +226,24 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
                while (len && isspace(*buf))
                        buf++, len--;
                while (len && isspace(buf[len-1]))
-                       buf[--len] = 0;
-               path = git_path("%.*s", len, buf);
+                       len--;
+               buf[len] = 0;
+               memcpy(ref_buffer, buf, len + 1);
+               ref = ref_buffer;
+               if (flag)
+                       *flag |= REF_ISSYMREF;
        }
        if (len < 40 || get_sha1_hex(buffer, sha1))
                return NULL;
-       return path;
+       return ref;
 }
 
-int create_symref(const char *git_HEAD, const char *refs_heads_master)
+int create_symref(const char *ref_target, const char *refs_heads_master)
 {
        const char *lockpath;
        char ref[1000];
        int fd, len, written;
+       const char *git_HEAD = git_path("%s", ref_target);
 
 #ifndef NO_SYMLINK_HEAD
        if (prefer_symlink_refs) {
@@ -112,104 +281,101 @@ int create_symref(const char *git_HEAD, const char *refs_heads_master)
        return 0;
 }
 
-int read_ref(const char *filename, unsigned char *sha1)
+int read_ref(const char *ref, unsigned char *sha1)
 {
-       if (resolve_ref(filename, sha1, 1))
+       if (resolve_ref(ref, sha1, 1, NULL))
                return 0;
        return -1;
 }
 
-static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1), int trim)
+static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
+                          void *cb_data)
 {
-       int retval = 0;
-       DIR *dir = opendir(git_path("%s", base));
-
-       if (dir) {
-               struct dirent *de;
-               int baselen = strlen(base);
-               char *path = xmalloc(baselen + 257);
-
-               if (!strncmp(base, "./", 2)) {
-                       base += 2;
-                       baselen -= 2;
+       int retval;
+       struct ref_list *packed = get_packed_refs();
+       struct ref_list *loose = get_loose_refs();
+
+       while (packed && loose) {
+               struct ref_list *entry;
+               int cmp = strcmp(packed->name, loose->name);
+               if (!cmp) {
+                       packed = packed->next;
+                       continue;
                }
-               memcpy(path, base, baselen);
-               if (baselen && base[baselen-1] != '/')
-                       path[baselen++] = '/';
-
-               while ((de = readdir(dir)) != NULL) {
-                       unsigned char sha1[20];
-                       struct stat st;
-                       int namelen;
+               if (cmp > 0) {
+                       entry = loose;
+                       loose = loose->next;
+               } else {
+                       entry = packed;
+                       packed = packed->next;
+               }
+               if (strncmp(base, entry->name, trim))
+                       continue;
+               if (is_null_sha1(entry->sha1))
+                       continue;
+               if (!has_sha1_file(entry->sha1)) {
+                       error("%s does not point to a valid object!", entry->name);
+                       continue;
+               }
+               retval = fn(entry->name + trim, entry->sha1,
+                           entry->flag, cb_data);
+               if (retval)
+                       return retval;
+       }
 
-                       if (de->d_name[0] == '.')
-                               continue;
-                       namelen = strlen(de->d_name);
-                       if (namelen > 255)
-                               continue;
-                       if (has_extension(de->d_name, ".lock"))
-                               continue;
-                       memcpy(path + baselen, de->d_name, namelen+1);
-                       if (stat(git_path("%s", path), &st) < 0)
-                               continue;
-                       if (S_ISDIR(st.st_mode)) {
-                               retval = do_for_each_ref(path, fn, trim);
-                               if (retval)
-                                       break;
-                               continue;
-                       }
-                       if (read_ref(git_path("%s", path), sha1) < 0) {
-                               error("%s points nowhere!", path);
-                               continue;
-                       }
-                       if (!has_sha1_file(sha1)) {
-                               error("%s does not point to a valid "
-                                     "commit object!", path);
-                               continue;
-                       }
-                       retval = fn(path + trim, sha1);
+       packed = packed ? packed : loose;
+       while (packed) {
+               if (!strncmp(base, packed->name, trim)) {
+                       retval = fn(packed->name + trim, packed->sha1,
+                                   packed->flag, cb_data);
                        if (retval)
-                               break;
+                               return retval;
                }
-               free(path);
-               closedir(dir);
+               packed = packed->next;
        }
-       return retval;
+       return 0;
 }
 
-int head_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int head_ref(each_ref_fn fn, void *cb_data)
 {
        unsigned char sha1[20];
-       if (!read_ref(git_path("HEAD"), sha1))
-               return fn("HEAD", sha1);
+       int flag;
+
+       if (resolve_ref("HEAD", sha1, 1, &flag))
+               return fn("HEAD", sha1, flag, cb_data);
        return 0;
 }
 
-int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int for_each_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs", fn, 0);
+       return do_for_each_ref("refs/", fn, 0, cb_data);
 }
 
-int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int for_each_tag_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs/tags", fn, 10);
+       return do_for_each_ref("refs/tags/", fn, 10, cb_data);
 }
 
-int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int for_each_branch_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs/heads", fn, 11);
+       return do_for_each_ref("refs/heads/", fn, 11, cb_data);
 }
 
-int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int for_each_remote_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs/remotes", fn, 13);
+       return do_for_each_ref("refs/remotes/", fn, 13, cb_data);
 }
 
+/* NEEDSWORK: This is only used by ssh-upload and it should go; the
+ * caller should do resolve_ref or read_ref like everybody else.  Or
+ * maybe everybody else should use get_ref_sha1() instead of doing
+ * read_ref().
+ */
 int get_ref_sha1(const char *ref, unsigned char *sha1)
 {
        if (check_ref_format(ref))
                return -1;
-       return read_ref(git_path("refs/%s", ref), sha1);
+       return read_ref(mkpath("refs/%s", ref), sha1);
 }
 
 /*
@@ -267,22 +433,13 @@ int check_ref_format(const char *ref)
 static struct ref_lock *verify_lock(struct ref_lock *lock,
        const unsigned char *old_sha1, int mustexist)
 {
-       char buf[40];
-       int nr, fd = open(lock->ref_file, O_RDONLY);
-       if (fd < 0 && (mustexist || errno != ENOENT)) {
-               error("Can't verify ref %s", lock->ref_file);
-               unlock_ref(lock);
-               return NULL;
-       }
-       nr = read(fd, buf, 40);
-       close(fd);
-       if (nr != 40 || get_sha1_hex(buf, lock->old_sha1) < 0) {
-               error("Can't verify ref %s", lock->ref_file);
+       if (!resolve_ref(lock->ref_name, lock->old_sha1, mustexist, NULL)) {
+               error("Can't verify ref %s", lock->ref_name);
                unlock_ref(lock);
                return NULL;
        }
        if (hashcmp(lock->old_sha1, old_sha1)) {
-               error("Ref %s is at %s but expected %s", lock->ref_file,
+               error("Ref %s is at %s but expected %s", lock->ref_name,
                        sha1_to_hex(lock->old_sha1), sha1_to_hex(old_sha1));
                unlock_ref(lock);
                return NULL;
@@ -290,36 +447,37 @@ static struct ref_lock *verify_lock(struct ref_lock *lock,
        return lock;
 }
 
-static struct ref_lock *lock_ref_sha1_basic(const char *path,
+static struct ref_lock *lock_ref_sha1_basic(const char *ref,
        int plen,
        const unsigned char *old_sha1, int mustexist)
 {
-       const char *orig_path = path;
+       char *ref_file;
+       const char *orig_ref = ref;
        struct ref_lock *lock;
        struct stat st;
 
        lock = xcalloc(1, sizeof(struct ref_lock));
        lock->lock_fd = -1;
 
-       plen = strlen(path) - plen;
-       path = resolve_ref(path, lock->old_sha1, mustexist);
-       if (!path) {
+       ref = resolve_ref(ref, lock->old_sha1, mustexist, NULL);
+       if (!ref) {
                int last_errno = errno;
                error("unable to resolve reference %s: %s",
-                       orig_path, strerror(errno));
+                       orig_ref, strerror(errno));
                unlock_ref(lock);
                errno = last_errno;
                return NULL;
        }
        lock->lk = xcalloc(1, sizeof(struct lock_file));
 
-       lock->ref_file = xstrdup(path);
-       lock->log_file = xstrdup(git_path("logs/%s", lock->ref_file + plen));
-       lock->force_write = lstat(lock->ref_file, &st) && errno == ENOENT;
+       lock->ref_name = xstrdup(ref);
+       lock->log_file = xstrdup(git_path("logs/%s", ref));
+       ref_file = git_path(ref);
+       lock->force_write = lstat(ref_file, &st) && errno == ENOENT;
 
-       if (safe_create_leading_directories(lock->ref_file))
-               die("unable to create directory for %s", lock->ref_file);
-       lock->lock_fd = hold_lock_file_for_update(lock->lk, lock->ref_file, 1);
+       if (safe_create_leading_directories(ref_file))
+               die("unable to create directory for %s", ref_file);
+       lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1);
 
        return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
 }
@@ -327,17 +485,18 @@ static struct ref_lock *lock_ref_sha1_basic(const char *path,
 struct ref_lock *lock_ref_sha1(const char *ref,
        const unsigned char *old_sha1, int mustexist)
 {
+       char refpath[PATH_MAX];
        if (check_ref_format(ref))
                return NULL;
-       return lock_ref_sha1_basic(git_path("refs/%s", ref),
-               5 + strlen(ref), old_sha1, mustexist);
+       strcpy(refpath, mkpath("refs/%s", ref));
+       return lock_ref_sha1_basic(refpath, strlen(refpath),
+               old_sha1, mustexist);
 }
 
 struct ref_lock *lock_any_ref_for_update(const char *ref,
        const unsigned char *old_sha1, int mustexist)
 {
-       return lock_ref_sha1_basic(git_path("%s", ref),
-               strlen(ref), old_sha1, mustexist);
+       return lock_ref_sha1_basic(ref, strlen(ref), old_sha1, mustexist);
 }
 
 void unlock_ref(struct ref_lock *lock)
@@ -348,7 +507,7 @@ void unlock_ref(struct ref_lock *lock)
                if (lock->lk)
                        rollback_lock_file(lock->lk);
        }
-       free(lock->ref_file);
+       free(lock->ref_name);
        free(lock->log_file);
        free(lock);
 }
@@ -425,7 +584,7 @@ int write_ref_sha1(struct ref_lock *lock,
                return -1;
        }
        if (commit_lock_file(lock->lk)) {
-               error("Couldn't set %s", lock->ref_file);
+               error("Couldn't set %s", lock->ref_name);
                unlock_ref(lock);
                return -1;
        }
diff --git a/refs.h b/refs.h
index 553155c04a4a3586e7a0f07f26979b50b9216aa3..305d408690cec81bf094702848f52364708c6613 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -2,7 +2,7 @@
 #define REFS_H
 
 struct ref_lock {
-       char *ref_file;
+       char *ref_name;
        char *log_file;
        struct lock_file *lk;
        unsigned char old_sha1[20];
@@ -14,11 +14,14 @@ struct ref_lock {
  * Calls the specified function for each ref file until it returns nonzero,
  * and returns the value
  */
-extern int head_ref(int (*fn)(const char *path, const unsigned char *sha1));
-extern int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1));
-extern int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1));
-extern int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1));
-extern int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1));
+#define REF_ISSYMREF 01
+#define REF_ISPACKED 02
+typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
+extern int head_ref(each_ref_fn, void *);
+extern int for_each_ref(each_ref_fn, void *);
+extern int for_each_tag_ref(each_ref_fn, void *);
+extern int for_each_branch_ref(each_ref_fn, void *);
+extern int for_each_remote_ref(each_ref_fn, void *);
 
 /** Reads the refs file specified into sha1 **/
 extern int get_ref_sha1(const char *ref, unsigned char *sha1);
index db01682750a4dfa820ea3f0e4a99e306aa6b075a..cb13b90776793dba8d18af4aecefb9e0dde5628b 100644 (file)
@@ -416,7 +416,8 @@ static void limit_list(struct rev_info *revs)
 
                if (revs->max_age != -1 && (commit->date < revs->max_age))
                        obj->flags |= UNINTERESTING;
-               if (revs->unpacked && has_sha1_pack(obj->sha1))
+               if (revs->unpacked &&
+                   has_sha1_pack(obj->sha1, revs->ignore_packed))
                        obj->flags |= UNINTERESTING;
                add_parents_to_list(revs, commit, &list);
                if (obj->flags & UNINTERESTING) {
@@ -465,7 +466,7 @@ static void limit_list(struct rev_info *revs)
 static int all_flags;
 static struct rev_info *all_revs;
 
-static int handle_one_ref(const char *path, const unsigned char *sha1)
+static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
        struct object *object = get_reference(all_revs, path, sha1, all_flags);
        add_pending_object(all_revs, object, "");
@@ -476,7 +477,7 @@ static void handle_all(struct rev_info *revs, unsigned flags)
 {
        all_revs = revs;
        all_flags = flags;
-       for_each_ref(handle_one_ref);
+       for_each_ref(handle_one_ref, NULL);
 }
 
 static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
@@ -671,6 +672,16 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
        return 0;
 }
 
+static void add_ignore_packed(struct rev_info *revs, const char *name)
+{
+       int num = ++revs->num_ignore_packed;
+
+       revs->ignore_packed = xrealloc(revs->ignore_packed,
+                                      sizeof(const char **) * (num + 1));
+       revs->ignore_packed[num-1] = name;
+       revs->ignore_packed[num] = NULL;
+}
+
 /*
  * Parse revision information, filling in the "rev_info" structure,
  * and removing the used arguments from the argument list.
@@ -811,6 +822,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                        }
                        if (!strcmp(arg, "--unpacked")) {
                                revs->unpacked = 1;
+                               free(revs->ignore_packed);
+                               revs->ignore_packed = NULL;
+                               revs->num_ignore_packed = 0;
+                               continue;
+                       }
+                       if (!strncmp(arg, "--unpacked=", 11)) {
+                               revs->unpacked = 1;
+                               add_ignore_packed(revs, arg+11);
                                continue;
                        }
                        if (!strcmp(arg, "-r")) {
@@ -1057,7 +1076,8 @@ struct commit *get_revision(struct rev_info *revs)
                 */
                if (!revs->limited) {
                        if ((revs->unpacked &&
-                            has_sha1_pack(commit->object.sha1)) ||
+                            has_sha1_pack(commit->object.sha1,
+                                          revs->ignore_packed)) ||
                            (revs->max_age != -1 &&
                             (commit->date < revs->max_age)))
                                continue;
index c1f71afe6fa71075b64df03c1e9616f50325d29e..a5c35d05cbd6bceb12248dff0b1e5c3f3433aeae 100644 (file)
@@ -38,7 +38,7 @@ struct rev_info {
                        blob_objects:1,
                        edge_hint:1,
                        limited:1,
-                       unpacked:1,
+                       unpacked:1, /* see also ignore_packed below */
                        boundary:1,
                        parents:1;
 
@@ -57,6 +57,10 @@ struct rev_info {
        unsigned int    shown_one:1,
                        abbrev_commit:1,
                        relative_date:1;
+
+       const char **ignore_packed; /* pretend objects in these are unpacked */
+       int num_ignore_packed;
+
        unsigned int    abbrev;
        enum cmit_fmt   commit_format;
        struct log_info *loginfo;
index 5bb123a37696384c5413dac128529d1c1f679940..fbd792ccf4a55ddb5bb09273ceb9e103d242db0e 100644 (file)
@@ -215,7 +215,7 @@ static int ref_newer(const unsigned char *new_sha1,
 static struct ref *local_refs, **local_tail;
 static struct ref *remote_refs, **remote_tail;
 
-static int one_local_ref(const char *refname, const unsigned char *sha1)
+static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
        struct ref *ref;
        int len = strlen(refname) + 1;
@@ -230,7 +230,7 @@ static int one_local_ref(const char *refname, const unsigned char *sha1)
 static void get_local_heads(void)
 {
        local_tail = &local_refs;
-       for_each_ref(one_local_ref);
+       for_each_ref(one_local_ref, NULL);
 }
 
 static int receive_status(int in)
index 2fb8f571033918b870cde35979b377c98b9845b5..6cd38be3291e95e75a7d2348e825d49c8f7ad2af 100644 (file)
@@ -7,7 +7,7 @@
 /* refs */
 static FILE *info_ref_fp;
 
-static int add_info_ref(const char *path, const unsigned char *sha1)
+static int add_info_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
        struct object *o = parse_object(sha1);
 
@@ -34,7 +34,7 @@ static int update_info_refs(int force)
        info_ref_fp = fopen(path1, "w");
        if (!info_ref_fp)
                return error("unable to update %s", path0);
-       for_each_ref(add_info_ref);
+       for_each_ref(add_info_ref, NULL);
        fclose(info_ref_fp);
        rename(path1, path0);
        free(path0);
index b64b92de4e7a6c59edfb3e60ff1440a98cacdeea..b89edb9515aa38c2c404367aa28ec5531fcc7de1 100644 (file)
@@ -1217,12 +1217,20 @@ int find_pack_entry_one(const unsigned char *sha1,
        return 0;
 }
 
-static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
+static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed)
 {
        struct packed_git *p;
        prepare_packed_git();
 
        for (p = packed_git; p; p = p->next) {
+               if (ignore_packed) {
+                       const char **ig;
+                       for (ig = ignore_packed; *ig; ig++)
+                               if (!strcmp(p->pack_name, *ig))
+                                       break;
+                       if (*ig)
+                               continue;
+               }
                if (find_pack_entry_one(sha1, e, p))
                        return 1;
        }
@@ -1255,10 +1263,10 @@ int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep
        if (!map) {
                struct pack_entry e;
 
-               if (find_pack_entry(sha1, &e))
+               if (find_pack_entry(sha1, &e, NULL))
                        return packed_object_info(&e, type, sizep);
                reprepare_packed_git();
-               if (find_pack_entry(sha1, &e))
+               if (find_pack_entry(sha1, &e, NULL))
                        return packed_object_info(&e, type, sizep);
                return error("unable to find %s", sha1_to_hex(sha1));
        }
@@ -1281,7 +1289,7 @@ static void *read_packed_sha1(const unsigned char *sha1, char *type, unsigned lo
 {
        struct pack_entry e;
 
-       if (!find_pack_entry(sha1, &e)) {
+       if (!find_pack_entry(sha1, &e, NULL)) {
                error("cannot read sha1_file for %s", sha1_to_hex(sha1));
                return NULL;
        }
@@ -1294,7 +1302,7 @@ void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size
        void *map, *buf;
        struct pack_entry e;
 
-       if (find_pack_entry(sha1, &e))
+       if (find_pack_entry(sha1, &e, NULL))
                return read_packed_sha1(sha1, type, size);
        map = map_sha1_file(sha1, &mapsize);
        if (map) {
@@ -1303,7 +1311,7 @@ void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size
                return buf;
        }
        reprepare_packed_git();
-       if (find_pack_entry(sha1, &e))
+       if (find_pack_entry(sha1, &e, NULL))
                return read_packed_sha1(sha1, type, size);
        return NULL;
 }
@@ -1735,10 +1743,10 @@ int has_pack_file(const unsigned char *sha1)
        return 1;
 }
 
-int has_sha1_pack(const unsigned char *sha1)
+int has_sha1_pack(const unsigned char *sha1, const char **ignore_packed)
 {
        struct pack_entry e;
-       return find_pack_entry(sha1, &e);
+       return find_pack_entry(sha1, &e, ignore_packed);
 }
 
 int has_sha1_file(const unsigned char *sha1)
@@ -1746,7 +1754,7 @@ int has_sha1_file(const unsigned char *sha1)
        struct stat st;
        struct pack_entry e;
 
-       if (find_pack_entry(sha1, &e))
+       if (find_pack_entry(sha1, &e, NULL))
                return 1;
        return find_sha1_file(sha1, &st) ? 1 : 0;
 }
index 1fbc4438059b68f31aa25fb8e3b82b37a4d02819..84d24c6abf39f395281c7d80476575cbd482e6f0 100644 (file)
@@ -247,8 +247,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
                NULL
        };
        static const char *warning = "warning: refname '%.*s' is ambiguous.\n";
-       const char **p, *pathname;
-       char *real_path = NULL;
+       const char **p, *ref;
+       char *real_ref = NULL;
        int refs_found = 0, am;
        unsigned long at_time = (unsigned long)-1;
        unsigned char *this_result;
@@ -276,10 +276,10 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 
        for (p = fmt; *p; p++) {
                this_result = refs_found ? sha1_from_ref : sha1;
-               pathname = resolve_ref(git_path(*p, len, str), this_result, 1);
-               if (pathname) {
+               ref = resolve_ref(mkpath(*p, len, str), this_result, 1, NULL);
+               if (ref) {
                        if (!refs_found++)
-                               real_path = xstrdup(pathname);
+                               real_ref = xstrdup(ref);
                        if (!warn_ambiguous_refs)
                                break;
                }
@@ -293,12 +293,12 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 
        if (at_time != (unsigned long)-1) {
                read_ref_at(
-                       real_path + strlen(git_path(".")) - 1,
+                       real_ref,
                        at_time,
                        sha1);
        }
 
-       free(real_path);
+       free(real_ref);
        return 0;
 }
 
diff --git a/sideband.c b/sideband.c
new file mode 100644 (file)
index 0000000..1b14ff8
--- /dev/null
@@ -0,0 +1,74 @@
+#include "pkt-line.h"
+#include "sideband.h"
+
+/*
+ * Receive multiplexed output stream over git native protocol.
+ * in_stream is the input stream from the remote, which carries data
+ * in pkt_line format with band designator.  Demultiplex it into out
+ * and err and return error appropriately.  Band #1 carries the
+ * primary payload.  Things coming over band #2 is not necessarily
+ * error; they are usually informative message on the standard error
+ * stream, aka "verbose").  A message over band #3 is a signal that
+ * the remote died unexpectedly.  A flush() concludes the stream.
+ */
+int recv_sideband(const char *me, int in_stream, int out, int err, char *buf, int bufsz)
+{
+       while (1) {
+               int len = packet_read_line(in_stream, buf, bufsz);
+               if (len == 0)
+                       break;
+               if (len < 1) {
+                       len = sprintf(buf, "%s: protocol error: no band designator\n", me);
+                       safe_write(err, buf, len);
+                       return SIDEBAND_PROTOCOL_ERROR;
+               }
+               len--;
+               switch (buf[0] & 0xFF) {
+               case 3:
+                       safe_write(err, "remote: ", 8);
+                       safe_write(err, buf+1, len);
+                       safe_write(err, "\n", 1);
+                       return SIDEBAND_REMOTE_ERROR;
+               case 2:
+                       safe_write(err, "remote: ", 8);
+                       safe_write(err, buf+1, len);
+                       continue;
+               case 1:
+                       safe_write(out, buf+1, len);
+                       continue;
+               default:
+                       len = sprintf(buf + 1,
+                                     "%s: protocol error: bad band #%d\n",
+                                     me, buf[0] & 0xFF);
+                       safe_write(err, buf+1, len);
+                       return SIDEBAND_PROTOCOL_ERROR;
+               }
+       }
+       return 0;
+}
+
+/*
+ * fd is connected to the remote side; send the sideband data
+ * over multiplexed packet stream.
+ */
+ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max)
+{
+       ssize_t ssz = sz;
+       const char *p = data;
+
+       while (sz) {
+               unsigned n;
+               char hdr[5];
+
+               n = sz;
+               if (packet_max - 5 < n)
+                       n = packet_max - 5;
+               sprintf(hdr, "%04x", n + 5);
+               hdr[4] = band;
+               safe_write(fd, hdr, 5);
+               safe_write(fd, p, n);
+               p += n;
+               sz -= n;
+       }
+       return ssz;
+}
diff --git a/sideband.h b/sideband.h
new file mode 100644 (file)
index 0000000..4872106
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef SIDEBAND_H
+#define SIDEBAND_H
+
+#define SIDEBAND_PROTOCOL_ERROR -2
+#define SIDEBAND_REMOTE_ERROR -1
+
+#define DEFAULT_PACKET_MAX 1000
+#define LARGE_PACKET_MAX 65520
+
+int recv_sideband(const char *me, int in_stream, int out, int err, char *, int);
+ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);
+
+#endif
index ddc80bbeae7703b8c4a2ddcf12d8b1ec9c6bbe46..6a917f2ff48661a5d70fdcbcc700cbf659004818 100755 (executable)
@@ -19,51 +19,48 @@ n=$n_dir/fixes
 
 test_expect_success \
        "create $m" \
-       'git-update-ref $m $A &&
-        test $A = $(cat .git/$m)'
+       "git-update-ref $m $A &&
+        test $A"' = $(cat .git/'"$m"')'
 test_expect_success \
        "create $m" \
-       'git-update-ref $m $B $A &&
-        test $B = $(cat .git/$m)'
+       "git-update-ref $m $B $A &&
+        test $B"' = $(cat .git/'"$m"')'
 rm -f .git/$m
 
 test_expect_success \
        "fail to create $n" \
-       'touch .git/$n_dir
-        git-update-ref $n $A >out 2>err
-        test $? = 1 &&
-        test "" = "$(cat out)" &&
-        grep "error: unable to resolve reference" err &&
-        grep $n err'
+       "touch .git/$n_dir
+        git-update-ref $n $A >out 2>err"'
+        test $? != 0'
 rm -f .git/$n_dir out err
 
 test_expect_success \
        "create $m (by HEAD)" \
-       'git-update-ref HEAD $A &&
-        test $A = $(cat .git/$m)'
+       "git-update-ref HEAD $A &&
+        test $A"' = $(cat .git/'"$m"')'
 test_expect_success \
        "create $m (by HEAD)" \
-       'git-update-ref HEAD $B $A &&
-        test $B = $(cat .git/$m)'
+       "git-update-ref HEAD $B $A &&
+        test $B"' = $(cat .git/'"$m"')'
 rm -f .git/$m
 
 test_expect_failure \
        '(not) create HEAD with old sha1' \
-       'git-update-ref HEAD $A $B'
+       "git-update-ref HEAD $A $B"
 test_expect_failure \
        "(not) prior created .git/$m" \
-       'test -f .git/$m'
+       "test -f .git/$m"
 rm -f .git/$m
 
 test_expect_success \
        "create HEAD" \
-       'git-update-ref HEAD $A'
+       "git-update-ref HEAD $A"
 test_expect_failure \
        '(not) change HEAD with wrong SHA1' \
-       'git-update-ref HEAD $B $Z'
+       "git-update-ref HEAD $B $Z"
 test_expect_failure \
        "(not) changed .git/$m" \
-       'test $B = $(cat .git/$m)'
+       "test $B"' = $(cat .git/'"$m"')'
 rm -f .git/$m
 
 mkdir -p .git/logs/refs/heads
@@ -71,18 +68,18 @@ touch .git/logs/refs/heads/master
 test_expect_success \
        "create $m (logged by touch)" \
        'GIT_COMMITTER_DATE="2005-05-26 23:30" \
-        git-update-ref HEAD $A -m "Initial Creation" &&
-        test $A = $(cat .git/$m)'
+        git-update-ref HEAD '"$A"' -m "Initial Creation" &&
+        test '"$A"' = $(cat .git/'"$m"')'
 test_expect_success \
        "update $m (logged by touch)" \
        'GIT_COMMITTER_DATE="2005-05-26 23:31" \
-        git-update-ref HEAD $B $A -m "Switch" &&
-        test $B = $(cat .git/$m)'
+        git-update-ref HEAD'" $B $A "'-m "Switch" &&
+        test '"$B"' = $(cat .git/'"$m"')'
 test_expect_success \
        "set $m (logged by touch)" \
        'GIT_COMMITTER_DATE="2005-05-26 23:41" \
-        git-update-ref HEAD $A &&
-        test $A = $(cat .git/$m)'
+        git-update-ref HEAD'" $A &&
+        test $A"' = $(cat .git/'"$m"')'
 
 cat >expect <<EOF
 $Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000      Initial Creation
@@ -91,7 +88,7 @@ $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000
 EOF
 test_expect_success \
        "verifying $m's log" \
-       'diff expect .git/logs/$m'
+       "diff expect .git/logs/$m"
 rm -rf .git/$m .git/logs expect
 
 test_expect_success \
@@ -102,18 +99,18 @@ test_expect_success \
 test_expect_success \
        "create $m (logged by config)" \
        'GIT_COMMITTER_DATE="2005-05-26 23:32" \
-        git-update-ref HEAD $A -m "Initial Creation" &&
-        test $A = $(cat .git/$m)'
+        git-update-ref HEAD'" $A "'-m "Initial Creation" &&
+        test '"$A"' = $(cat .git/'"$m"')'
 test_expect_success \
        "update $m (logged by config)" \
        'GIT_COMMITTER_DATE="2005-05-26 23:33" \
-        git-update-ref HEAD $B $A -m "Switch" &&
-        test $B = $(cat .git/$m)'
+        git-update-ref HEAD'" $B $A "'-m "Switch" &&
+        test '"$B"' = $(cat .git/'"$m"')'
 test_expect_success \
        "set $m (logged by config)" \
        'GIT_COMMITTER_DATE="2005-05-26 23:43" \
-        git-update-ref HEAD $A &&
-        test $A = $(cat .git/$m)'
+        git-update-ref HEAD '"$A &&
+        test $A"' = $(cat .git/'"$m"')'
 
 cat >expect <<EOF
 $Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 +0000      Initial Creation
@@ -140,50 +137,50 @@ test_expect_success \
        'Query "master@{May 25 2005}" (before history)' \
        'rm -f o e
         git-rev-parse --verify "master@{May 25 2005}" >o 2>e &&
-        test $C = $(cat o) &&
-        test "warning: Log .git/logs/$m only goes back to $ed." = "$(cat e)"'
+        test '"$C"' = $(cat o) &&
+        test "warning: Log .git/logs/'"$m only goes back to $ed"'." = "$(cat e)"'
 test_expect_success \
        "Query master@{2005-05-25} (before history)" \
        'rm -f o e
         git-rev-parse --verify master@{2005-05-25} >o 2>e &&
-        test $C = $(cat o) &&
-        echo test "warning: Log .git/logs/$m only goes back to $ed." = "$(cat e)"'
+        test '"$C"' = $(cat o) &&
+        echo test "warning: Log .git/logs/'"$m only goes back to $ed"'." = "$(cat e)"'
 test_expect_success \
        'Query "master@{May 26 2005 23:31:59}" (1 second before history)' \
        'rm -f o e
         git-rev-parse --verify "master@{May 26 2005 23:31:59}" >o 2>e &&
-        test $C = $(cat o) &&
-        test "warning: Log .git/logs/$m only goes back to $ed." = "$(cat e)"'
+        test '"$C"' = $(cat o) &&
+        test "warning: Log .git/logs/'"$m only goes back to $ed"'." = "$(cat e)"'
 test_expect_success \
        'Query "master@{May 26 2005 23:32:00}" (exactly history start)' \
        'rm -f o e
         git-rev-parse --verify "master@{May 26 2005 23:32:00}" >o 2>e &&
-        test $A = $(cat o) &&
+        test '"$A"' = $(cat o) &&
         test "" = "$(cat e)"'
 test_expect_success \
        'Query "master@{2005-05-26 23:33:01}" (middle of history with gap)' \
        'rm -f o e
         git-rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e &&
-        test $B = $(cat o) &&
-        test "warning: Log .git/logs/$m has gap after $gd." = "$(cat e)"'
+        test '"$B"' = $(cat o) &&
+        test "warning: Log .git/logs/'"$m has gap after $gd"'." = "$(cat e)"'
 test_expect_success \
        'Query "master@{2005-05-26 23:38:00}" (middle of history)' \
        'rm -f o e
         git-rev-parse --verify "master@{2005-05-26 23:38:00}" >o 2>e &&
-        test $Z = $(cat o) &&
+        test '"$Z"' = $(cat o) &&
         test "" = "$(cat e)"'
 test_expect_success \
        'Query "master@{2005-05-26 23:43:00}" (exact end of history)' \
        'rm -f o e
         git-rev-parse --verify "master@{2005-05-26 23:43:00}" >o 2>e &&
-        test $E = $(cat o) &&
+        test '"$E"' = $(cat o) &&
         test "" = "$(cat e)"'
 test_expect_success \
        'Query "master@{2005-05-28}" (past end of history)' \
        'rm -f o e
         git-rev-parse --verify "master@{2005-05-28}" >o 2>e &&
-        test $D = $(cat o) &&
-        test "warning: Log .git/logs/$m unexpectedly ended on $ld." = "$(cat e)"'
+        test '"$D"' = $(cat o) &&
+        test "warning: Log .git/logs/'"$m unexpectedly ended on $ld"'." = "$(cat e)"'
 
 
 rm -f .git/$m .git/logs/$m expect
@@ -221,7 +218,7 @@ $h_FIXED $h_MERGED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151100 +0000      c
 EOF
 test_expect_success \
        'git-commit logged updates' \
-       'diff expect .git/logs/$m'
+       "diff expect .git/logs/$m"
 unset h_TEST h_OTHER h_FIXED h_MERGED
 
 test_expect_success \
index 8ab63c52765d6608dba3b4fcf0b80833af240a67..bb2531536160bb0a6510cc316d8d74a1da2312e7 100755 (executable)
@@ -37,7 +37,9 @@ test_expect_success setup '
        git branch skip-merge skip-reference
        '
 
-test_expect_failure 'rebase with git am -3 (default)' 'git rebase master'
+test_expect_failure 'rebase with git am -3 (default)' '
+       git rebase master
+'
 
 test_expect_success 'rebase --skip with am -3' '
        git reset --hard HEAD &&
diff --git a/t/t4104-apply-boundary.sh b/t/t4104-apply-boundary.sh
new file mode 100755 (executable)
index 0000000..2ff800c
--- /dev/null
@@ -0,0 +1,115 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-apply boundary tests
+
+'
+. ./test-lib.sh
+
+L="c d e f g h i j k l m n o p q r s t u v w x"
+
+test_expect_success setup '
+       for i in b '"$L"' y
+       do
+               echo $i
+       done >victim &&
+       cat victim >original &&
+       git update-index --add victim &&
+
+       : add to the head
+       for i in a b '"$L"' y
+       do
+               echo $i
+       done >victim &&
+       cat victim >add-a-expect &&
+       git diff victim >add-a-patch.with &&
+       git diff --unified=0 >add-a-patch.without &&
+
+       : modify at the head
+       for i in a '"$L"' y
+       do
+               echo $i
+       done >victim &&
+       cat victim >mod-a-expect &&
+       git diff victim >mod-a-patch.with &&
+       git diff --unified=0 >mod-a-patch.without &&
+
+       : remove from the head
+       for i in '"$L"' y
+       do
+               echo $i
+       done >victim &&
+       cat victim >del-a-expect &&
+       git diff victim >del-a-patch.with
+       git diff --unified=0 >del-a-patch.without &&
+
+       : add to the tail
+       for i in b '"$L"' y z
+       do
+               echo $i
+       done >victim &&
+       cat victim >add-z-expect &&
+       git diff victim >add-z-patch.with &&
+       git diff --unified=0 >add-z-patch.without &&
+
+       : modify at the tail
+       for i in a '"$L"' y
+       do
+               echo $i
+       done >victim &&
+       cat victim >mod-z-expect &&
+       git diff victim >mod-z-patch.with &&
+       git diff --unified=0 >mod-z-patch.without &&
+
+       : remove from the tail
+       for i in b '"$L"'
+       do
+               echo $i
+       done >victim &&
+       cat victim >del-z-expect &&
+       git diff victim >del-z-patch.with
+       git diff --unified=0 >del-z-patch.without &&
+
+       : done
+'
+
+for with in with without
+do
+       case "$with" in
+       with) u= ;;
+       without) u='--unidiff-zero ' ;;
+       esac
+       for kind in add-a add-z mod-a mod-z del-a del-z
+       do
+               test_expect_success "apply $kind-patch $with context" '
+                       cat original >victim &&
+                       git update-index victim &&
+                       git apply --index '"$u$kind-patch.$with"' || {
+                               cat '"$kind-patch.$with"'
+                               (exit 1)
+                       } &&
+                       diff -u '"$kind"'-expect victim
+               '
+       done
+done
+
+for kind in add-a add-z mod-a mod-z del-a del-z
+do
+       rm -f $kind-ng.without
+       sed     -e "s/^diff --git /diff /" \
+               -e '/^index /d' \
+               <$kind-patch.without >$kind-ng.without
+       test_expect_success "apply non-git $kind-patch without context" '
+               cat original >victim &&
+               git update-index victim &&
+               git apply --unidiff-zero --index '"$kind-ng.without"' || {
+                       cat '"$kind-ng.without"'
+                       (exit 1)
+               } &&
+               diff -u '"$kind"'-expect victim
+       '
+done
+
+test_done
index 51ce936b060d34a2f759dba4f7aeeecf47a59a4b..9412a9b2604af161bd8f88c27750e95a24a78097 100644 (file)
@@ -4,6 +4,7 @@
 #include "cache.h"
 #include "refs.h"
 #include "pkt-line.h"
+#include "sideband.h"
 #include "tag.h"
 #include "object.h"
 #include "commit.h"
@@ -19,6 +20,9 @@ static int use_thin_pack;
 static struct object_array have_obj;
 static struct object_array want_obj;
 static unsigned int timeout;
+/* 0 for no sideband,
+ * otherwise maximum packet size (up to 65520 bytes).
+ */
 static int use_sideband;
 
 static void reset_timeout(void)
@@ -33,45 +37,18 @@ static int strip(char *line, int len)
        return len;
 }
 
-#define PACKET_MAX 1000
 static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
 {
-       ssize_t ssz;
-       const char *p;
-
-       if (!data) {
-               if (!use_sideband)
-                       return 0;
-               packet_flush(1);
-       }
-
-       if (!use_sideband) {
-               if (fd == 3)
-                       /* emergency quit */
-                       fd = 2;
-               if (fd == 2) {
-                       xwrite(fd, data, sz);
-                       return sz;
-               }
-               return safe_write(fd, data, sz);
-       }
-       p = data;
-       ssz = sz;
-       while (sz) {
-               unsigned n;
-               char hdr[5];
-
-               n = sz;
-               if (PACKET_MAX - 5 < n)
-                       n = PACKET_MAX - 5;
-               sprintf(hdr, "%04x", n + 5);
-               hdr[4] = fd;
-               safe_write(1, hdr, 5);
-               safe_write(1, p, n);
-               p += n;
-               sz -= n;
+       if (use_sideband)
+               return send_sideband(1, fd, data, sz, use_sideband);
+       if (fd == 3)
+               /* emergency quit */
+               fd = 2;
+       if (fd == 2) {
+               xwrite(fd, data, sz);
+               return sz;
        }
-       return ssz;
+       return safe_write(fd, data, sz);
 }
 
 static void create_pack_file(void)
@@ -308,7 +285,8 @@ static void create_pack_file(void)
                                goto fail;
                        fprintf(stderr, "flushed.\n");
                }
-               send_client_data(1, NULL, 0);
+               if (use_sideband)
+                       packet_flush(1);
                return;
        }
  fail:
@@ -351,7 +329,8 @@ static int got_sha1(char *hex, unsigned char *sha1)
 static int get_common_commits(void)
 {
        static char line[1000];
-       unsigned char sha1[20], last_sha1[20];
+       unsigned char sha1[20];
+       char hex[41], last_hex[41];
        int len;
 
        track_object_refs = 0;
@@ -368,21 +347,22 @@ static int get_common_commits(void)
                }
                len = strip(line, len);
                if (!strncmp(line, "have ", 5)) {
-                       if (got_sha1(line+5, sha1) &&
-                           (multi_ack || have_obj.nr == 1)) {
-                               packet_write(1, "ACK %s%s\n",
-                                            sha1_to_hex(sha1),
-                                            multi_ack ?  " continue" : "");
-                               if (multi_ack)
-                                       hashcpy(last_sha1, sha1);
+                       if (got_sha1(line+5, sha1)) {
+                               memcpy(hex, sha1_to_hex(sha1), 41);
+                               if (multi_ack) {
+                                       const char *msg = "ACK %s continue\n";
+                                       packet_write(1, msg, hex);
+                                       memcpy(last_hex, hex, 41);
+                               }
+                               else if (have_obj.nr == 1)
+                                       packet_write(1, "ACK %s\n", hex);
                        }
                        continue;
                }
                if (!strcmp(line, "done")) {
                        if (have_obj.nr > 0) {
                                if (multi_ack)
-                                       packet_write(1, "ACK %s\n",
-                                                       sha1_to_hex(last_sha1));
+                                       packet_write(1, "ACK %s\n", last_hex);
                                return 0;
                        }
                        packet_write(1, "NAK\n");
@@ -413,8 +393,10 @@ static void receive_needs(void)
                        multi_ack = 1;
                if (strstr(line+45, "thin-pack"))
                        use_thin_pack = 1;
-               if (strstr(line+45, "side-band"))
-                       use_sideband = 1;
+               if (strstr(line+45, "side-band-64k"))
+                       use_sideband = LARGE_PACKET_MAX;
+               else if (strstr(line+45, "side-band"))
+                       use_sideband = DEFAULT_PACKET_MAX;
 
                /* We have sent all our refs already, and the other end
                 * should have chosen out of them; otherwise they are
@@ -434,9 +416,9 @@ static void receive_needs(void)
        }
 }
 
-static int send_ref(const char *refname, const unsigned char *sha1)
+static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
-       static const char *capabilities = "multi_ack thin-pack side-band";
+       static const char *capabilities = "multi_ack thin-pack side-band side-band-64k";
        struct object *o = parse_object(sha1);
 
        if (!o)
@@ -462,8 +444,8 @@ static int send_ref(const char *refname, const unsigned char *sha1)
 static void upload_pack(void)
 {
        reset_timeout();
-       head_ref(send_ref);
-       for_each_ref(send_ref);
+       head_ref(send_ref, NULL);
+       for_each_ref(send_ref, NULL);
        packet_flush(1);
        receive_needs();
        if (want_obj.nr) {
diff --git a/wt-status.c b/wt-status.c
new file mode 100644 (file)
index 0000000..d8e284c
--- /dev/null
@@ -0,0 +1,274 @@
+#include "wt-status.h"
+#include "color.h"
+#include "cache.h"
+#include "object.h"
+#include "dir.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "diffcore.h"
+
+int wt_status_use_color = 0;
+static char wt_status_colors[][COLOR_MAXLEN] = {
+       "",         /* WT_STATUS_HEADER: normal */
+       "\033[32m", /* WT_STATUS_UPDATED: green */
+       "\033[31m", /* WT_STATUS_CHANGED: red */
+       "\033[31m", /* WT_STATUS_UNTRACKED: red */
+};
+
+static int parse_status_slot(const char *var, int offset)
+{
+       if (!strcasecmp(var+offset, "header"))
+               return WT_STATUS_HEADER;
+       if (!strcasecmp(var+offset, "updated"))
+               return WT_STATUS_UPDATED;
+       if (!strcasecmp(var+offset, "changed"))
+               return WT_STATUS_CHANGED;
+       if (!strcasecmp(var+offset, "untracked"))
+               return WT_STATUS_UNTRACKED;
+       die("bad config variable '%s'", var);
+}
+
+static const char* color(int slot)
+{
+       return wt_status_use_color ? wt_status_colors[slot] : "";
+}
+
+void wt_status_prepare(struct wt_status *s)
+{
+       unsigned char sha1[20];
+       const char *head;
+
+       s->is_initial = get_sha1("HEAD", sha1) ? 1 : 0;
+
+       head = resolve_ref("HEAD", sha1, 0, NULL);
+       s->branch = head ? xstrdup(head) : NULL;
+
+       s->reference = "HEAD";
+       s->amend = 0;
+       s->verbose = 0;
+       s->commitable = 0;
+       s->untracked = 0;
+}
+
+static void wt_status_print_header(const char *main, const char *sub)
+{
+       const char *c = color(WT_STATUS_HEADER);
+       color_printf_ln(c, "# %s:", main);
+       color_printf_ln(c, "#   (%s)", sub);
+       color_printf_ln(c, "#");
+}
+
+static void wt_status_print_trailer(void)
+{
+       color_printf_ln(color(WT_STATUS_HEADER), "#");
+}
+
+static void wt_status_print_filepair(int t, struct diff_filepair *p)
+{
+       const char *c = color(t);
+       color_printf(color(WT_STATUS_HEADER), "#\t");
+       switch (p->status) {
+       case DIFF_STATUS_ADDED:
+               color_printf(c, "new file: %s", p->one->path); break;
+       case DIFF_STATUS_COPIED:
+               color_printf(c, "copied: %s -> %s",
+                               p->one->path, p->two->path);
+               break;
+       case DIFF_STATUS_DELETED:
+               color_printf(c, "deleted: %s", p->one->path); break;
+       case DIFF_STATUS_MODIFIED:
+               color_printf(c, "modified: %s", p->one->path); break;
+       case DIFF_STATUS_RENAMED:
+               color_printf(c, "renamed: %s -> %s",
+                               p->one->path, p->two->path);
+               break;
+       case DIFF_STATUS_TYPE_CHANGED:
+               color_printf(c, "typechange: %s", p->one->path); break;
+       case DIFF_STATUS_UNKNOWN:
+               color_printf(c, "unknown: %s", p->one->path); break;
+       case DIFF_STATUS_UNMERGED:
+               color_printf(c, "unmerged: %s", p->one->path); break;
+       default:
+               die("bug: unhandled diff status %c", p->status);
+       }
+       printf("\n");
+}
+
+static void wt_status_print_updated_cb(struct diff_queue_struct *q,
+               struct diff_options *options,
+               void *data)
+{
+       struct wt_status *s = data;
+       int shown_header = 0;
+       int i;
+       if (q->nr) {
+       }
+       for (i = 0; i < q->nr; i++) {
+               if (q->queue[i]->status == 'U')
+                       continue;
+               if (!shown_header) {
+                       wt_status_print_header("Updated but not checked in",
+                                       "will commit");
+                       s->commitable = 1;
+                       shown_header = 1;
+               }
+               wt_status_print_filepair(WT_STATUS_UPDATED, q->queue[i]);
+       }
+       if (shown_header)
+               wt_status_print_trailer();
+}
+
+static void wt_status_print_changed_cb(struct diff_queue_struct *q,
+                        struct diff_options *options,
+                        void *data)
+{
+       int i;
+       if (q->nr)
+               wt_status_print_header("Changed but not updated",
+                               "use git-update-index to mark for commit");
+       for (i = 0; i < q->nr; i++)
+               wt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]);
+       if (q->nr)
+               wt_status_print_trailer();
+}
+
+void wt_status_print_initial(struct wt_status *s)
+{
+       int i;
+       read_cache();
+       if (active_nr) {
+               s->commitable = 1;
+               wt_status_print_header("Updated but not checked in",
+                               "will commit");
+       }
+       for (i = 0; i < active_nr; i++) {
+               color_printf(color(WT_STATUS_HEADER), "#\t");
+               color_printf_ln(color(WT_STATUS_UPDATED), "new file: %s",
+                               active_cache[i]->name);
+       }
+       if (active_nr)
+               wt_status_print_trailer();
+}
+
+static void wt_status_print_updated(struct wt_status *s)
+{
+       struct rev_info rev;
+       const char *argv[] = { NULL, NULL, NULL };
+       argv[1] = s->reference;
+       init_revisions(&rev, NULL);
+       setup_revisions(2, argv, &rev, NULL);
+       rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
+       rev.diffopt.format_callback = wt_status_print_updated_cb;
+       rev.diffopt.format_callback_data = s;
+       rev.diffopt.detect_rename = 1;
+       run_diff_index(&rev, 1);
+}
+
+static void wt_status_print_changed(struct wt_status *s)
+{
+       struct rev_info rev;
+       const char *argv[] = { NULL, NULL };
+       init_revisions(&rev, "");
+       setup_revisions(1, argv, &rev, NULL);
+       rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
+       rev.diffopt.format_callback = wt_status_print_changed_cb;
+       rev.diffopt.format_callback_data = s;
+       run_diff_files(&rev, 0);
+}
+
+static void wt_status_print_untracked(const struct wt_status *s)
+{
+       struct dir_struct dir;
+       const char *x;
+       int i;
+       int shown_header = 0;
+
+       memset(&dir, 0, sizeof(dir));
+
+       dir.exclude_per_dir = ".gitignore";
+       if (!s->untracked) {
+               dir.show_other_directories = 1;
+               dir.hide_empty_directories = 1;
+       }
+       x = git_path("info/exclude");
+       if (file_exists(x))
+               add_excludes_from_file(&dir, x);
+
+       read_directory(&dir, ".", "", 0);
+       for(i = 0; i < dir.nr; i++) {
+               /* check for matching entry, which is unmerged; lifted from
+                * builtin-ls-files:show_other_files */
+               struct dir_entry *ent = dir.entries[i];
+               int pos = cache_name_pos(ent->name, ent->len);
+               struct cache_entry *ce;
+               if (0 <= pos)
+                       die("bug in wt_status_print_untracked");
+               pos = -pos - 1;
+               if (pos < active_nr) {
+                       ce = active_cache[pos];
+                       if (ce_namelen(ce) == ent->len &&
+                           !memcmp(ce->name, ent->name, ent->len))
+                               continue;
+               }
+               if (!shown_header) {
+                       wt_status_print_header("Untracked files",
+                               "use \"git add\" to add to commit");
+                       shown_header = 1;
+               }
+               color_printf(color(WT_STATUS_HEADER), "#\t");
+               color_printf_ln(color(WT_STATUS_UNTRACKED), "%.*s",
+                               ent->len, ent->name);
+       }
+}
+
+static void wt_status_print_verbose(struct wt_status *s)
+{
+       struct rev_info rev;
+       const char *argv[] = { NULL, NULL, NULL };
+       argv[1] = s->reference;
+       init_revisions(&rev, NULL);
+       setup_revisions(2, argv, &rev, NULL);
+       rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
+       rev.diffopt.detect_rename = 1;
+       run_diff_index(&rev, 1);
+}
+
+void wt_status_print(struct wt_status *s)
+{
+       if (s->branch && strcmp(s->branch, "refs/heads/master"))
+               color_printf_ln(color(WT_STATUS_HEADER),
+                       "# On branch %s", s->branch);
+
+       if (s->is_initial) {
+               color_printf_ln(color(WT_STATUS_HEADER), "#");
+               color_printf_ln(color(WT_STATUS_HEADER), "# Initial commit");
+               color_printf_ln(color(WT_STATUS_HEADER), "#");
+               wt_status_print_initial(s);
+       }
+       else {
+               wt_status_print_updated(s);
+               discard_cache();
+       }
+
+       wt_status_print_changed(s);
+       wt_status_print_untracked(s);
+
+       if (s->verbose && !s->is_initial)
+               wt_status_print_verbose(s);
+       if (!s->commitable)
+               printf("%s\n", s->amend ? "# No changes" : "nothing to commit");
+}
+
+int git_status_config(const char *k, const char *v)
+{
+       if (!strcmp(k, "status.color")) {
+               wt_status_use_color = git_config_colorbool(k, v);
+               return 0;
+       }
+       if (!strncmp(k, "status.color.", 13)) {
+               int slot = parse_status_slot(k, 13);
+               color_parse(v, k, wt_status_colors[slot]);
+       }
+       return git_default_config(k, v);
+}
diff --git a/wt-status.h b/wt-status.h
new file mode 100644 (file)
index 0000000..0a5a5b7
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef STATUS_H
+#define STATUS_H
+
+enum color_wt_status {
+       WT_STATUS_HEADER,
+       WT_STATUS_UPDATED,
+       WT_STATUS_CHANGED,
+       WT_STATUS_UNTRACKED,
+};
+
+struct wt_status {
+       int is_initial;
+       char *branch;
+       const char *reference;
+       int commitable;
+       int verbose;
+       int amend;
+       int untracked;
+};
+
+int git_status_config(const char *var, const char *value);
+void wt_status_prepare(struct wt_status *s);
+void wt_status_print(struct wt_status *s);
+
+#endif /* STATUS_H */