Merge branches 'master' and 'jc/combine' into next
authorJunio C Hamano <junkio@cox.net>
Wed, 5 Apr 2006 09:58:14 +0000 (02:58 -0700)
committerJunio C Hamano <junkio@cox.net>
Wed, 5 Apr 2006 09:58:14 +0000 (02:58 -0700)
* master:
  Add git-clean command
  diff_flush(): leakfix.
  parse_date(): fix parsing 03/10/2006

* jc/combine:
  combine-diff: refactor built-in xdiff interface.

Documentation/diff-options.txt
Makefile
combine-diff.c
diff.c
diff.h
diffcore-pickaxe.c
git-clone.sh
http-fetch.c
xdiff-interface.c [new file with mode: 0644]
xdiff-interface.h [new file with mode: 0644]

index 2a0275e..ec6811c 100644 (file)
        changeset, not just the files that contain the change
        in <string>.
 
+--pickaxe-regex::
+       Make the <string> not a plain string but an extended POSIX
+       regex to match.
+
 -O<orderfile>::
        Output the patch in the order specified in the
        <orderfile>, which has one shell glob pattern per line.
index 3367b8c..3471177 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -208,7 +208,7 @@ LIB_OBJS = \
        quote.o read-cache.o refs.o run-command.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 \
+       fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
        $(DIFF_OBJS)
 
 GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
@@ -513,6 +513,11 @@ exec_cmd.o: exec_cmd.c
 http.o: http.c
        $(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $<
 
+ifdef NO_EXPAT
+http-fetch.o: http-fetch.c
+       $(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_EXPAT $<
+endif
+
 git-%$X: %.o $(GITLIBS)
        $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
@@ -535,7 +540,7 @@ git-imap-send$X: imap-send.o $(LIB_FILE)
 
 git-http-fetch$X: fetch.o http.o http-fetch.o $(LIB_FILE)
        $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
-               $(LIBS) $(CURL_LIBCURL)
+               $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
 git-http-push$X: revision.o http.o http-push.o $(LIB_FILE)
        $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
index 7693884..e17afd1 100644 (file)
@@ -4,6 +4,7 @@
 #include "diff.h"
 #include "diffcore.h"
 #include "quote.h"
+#include "xdiff-interface.h"
 
 static int uninteresting(struct diff_filepair *p)
 {
@@ -110,28 +111,6 @@ static char *grab_blob(const unsigned char *sha1, unsigned long *size)
        return blob;
 }
 
-#define TMPPATHLEN 50
-#define MAXLINELEN 10240
-
-static void write_to_temp_file(char *tmpfile, void *blob, unsigned long size)
-{
-       int fd = git_mkstemp(tmpfile, TMPPATHLEN, ".diff_XXXXXX");
-       if (fd < 0)
-               die("unable to create temp-file");
-       if (write(fd, blob, size) != size)
-               die("unable to write temp-file");
-       close(fd);
-}
-
-static void write_temp_blob(char *tmpfile, const unsigned char *sha1)
-{
-       unsigned long size;
-       void *blob;
-       blob = grab_blob(sha1, &size);
-       write_to_temp_file(tmpfile, blob, size);
-       free(blob);
-}
-
 static int parse_num(char **cp_p, unsigned int *num_p)
 {
        char *cp = *cp_p;
@@ -178,10 +157,9 @@ static int parse_hunk_header(char *line, int len,
        return -!!memcmp(cp, " @@", 3);
 }
 
-static void append_lost(struct sline *sline, int n, const char *line)
+static void append_lost(struct sline *sline, int n, const char *line, int len)
 {
        struct lline *lline;
-       int len = strlen(line);
        unsigned long this_mask = (1UL<<n);
        if (line[len-1] == '\n')
                len--;
@@ -216,70 +194,92 @@ static void append_lost(struct sline *sline, int n, const char *line)
        sline->lost_tail = &lline->next;
 }
 
-static void combine_diff(const unsigned char *parent, const char *ourtmp,
+struct combine_diff_state {
+       struct xdiff_emit_state xm;
+
+       unsigned int lno, ob, on, nb, nn;
+       unsigned long nmask;
+       int num_parent;
+       int n;
+       struct sline *sline;
+       struct sline *lost_bucket;
+};
+
+static void consume_line(void *state_, char *line, unsigned long len)
+{
+       struct combine_diff_state *state = state_;
+       if (5 < len && !memcmp("@@ -", line, 4)) {
+               if (parse_hunk_header(line, len,
+                                     &state->ob, &state->on,
+                                     &state->nb, &state->nn))
+                       return;
+               state->lno = state->nb;
+               if (!state->nb)
+                       /* @@ -1,2 +0,0 @@ to remove the
+                        * first two lines...
+                        */
+                       state->nb = 1;
+               if (state->nn == 0)
+                       /* @@ -X,Y +N,0 @@ removed Y lines
+                        * that would have come *after* line N
+                        * in the result.  Our lost buckets hang
+                        * to the line after the removed lines,
+                        */
+                       state->lost_bucket = &state->sline[state->nb];
+               else
+                       state->lost_bucket = &state->sline[state->nb-1];
+               if (!state->sline[state->nb-1].p_lno)
+                       state->sline[state->nb-1].p_lno =
+                               xcalloc(state->num_parent,
+                                       sizeof(unsigned long));
+               state->sline[state->nb-1].p_lno[state->n] = state->ob;
+               return;
+       }
+       if (!state->lost_bucket)
+               return; /* not in any hunk yet */
+       switch (line[0]) {
+       case '-':
+               append_lost(state->lost_bucket, state->n, line+1, len-1);
+               break;
+       case '+':
+               state->sline[state->lno-1].flag |= state->nmask;
+               state->lno++;
+               break;
+       }
+}
+
+static void combine_diff(const unsigned char *parent, mmfile_t *result_file,
                         struct sline *sline, int cnt, int n, int num_parent)
 {
-       FILE *in;
-       char parent_tmp[TMPPATHLEN];
-       char cmd[TMPPATHLEN * 2 + 1024];
-       char line[MAXLINELEN];
-       unsigned int lno, ob, on, nb, nn, p_lno;
+       unsigned int p_lno, lno;
        unsigned long nmask = (1UL << n);
-       struct sline *lost_bucket = NULL;
+       xpparam_t xpp;
+       xdemitconf_t xecfg;
+       mmfile_t parent_file;
+       xdemitcb_t ecb;
+       struct combine_diff_state state;
+       unsigned long sz;
 
        if (!cnt)
                return; /* result deleted */
 
-       write_temp_blob(parent_tmp, parent);
-       sprintf(cmd, "diff --unified=0 -La/x -Lb/x '%s' '%s'",
-               parent_tmp, ourtmp);
-       in = popen(cmd, "r");
-       if (!in)
-               die("cannot spawn %s", cmd);
-
-       lno = 1;
-       while (fgets(line, sizeof(line), in) != NULL) {
-               int len = strlen(line);
-               if (5 < len && !memcmp("@@ -", line, 4)) {
-                       if (parse_hunk_header(line, len,
-                                             &ob, &on, &nb, &nn))
-                               break;
-                       lno = nb;
-                       if (!nb)
-                               /* @@ -1,2 +0,0 @@ to remove the
-                                * first two lines...
-                                */
-                               nb = 1;
-                       if (nn == 0)
-                               /* @@ -X,Y +N,0 @@ removed Y lines
-                                * that would have come *after* line N
-                                * in the result.  Our lost buckets hang
-                                * to the line after the removed lines,
-                                */
-                               lost_bucket = &sline[nb];
-                       else
-                               lost_bucket = &sline[nb-1];
-                       if (!sline[nb-1].p_lno)
-                               sline[nb-1].p_lno =
-                                       xcalloc(num_parent,
-                                               sizeof(unsigned long));
-                       sline[nb-1].p_lno[n] = ob;
-                       continue;
-               }
-               if (!lost_bucket)
-                       continue; /* not in any hunk yet */
-               switch (line[0]) {
-               case '-':
-                       append_lost(lost_bucket, n, line+1);
-                       break;
-               case '+':
-                       sline[lno-1].flag |= nmask;
-                       lno++;
-                       break;
-               }
-       }
-       fclose(in);
-       unlink(parent_tmp);
+       parent_file.ptr = grab_blob(parent, &sz);
+       parent_file.size = sz;
+       xpp.flags = XDF_NEED_MINIMAL;
+       xecfg.ctxlen = 0;
+       xecfg.flags = 0;
+       ecb.outf = xdiff_outf;
+       ecb.priv = &state;
+       memset(&state, 0, sizeof(state));
+       state.xm.consume = consume_line;
+       state.nmask = nmask;
+       state.sline = sline;
+       state.lno = 1;
+       state.num_parent = num_parent;
+       state.n = n;
+
+       xdl_diff(&parent_file, result_file, &xpp, &xecfg, &ecb);
+       free(parent_file.ptr);
 
        /* Assign line numbers for this parent.
         *
@@ -625,61 +625,56 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
                           int dense, const char *header,
                           struct diff_options *opt)
 {
-       unsigned long size, cnt, lno;
+       unsigned long result_size, cnt, lno;
        char *result, *cp, *ep;
        struct sline *sline; /* survived lines */
        int mode_differs = 0;
        int i, show_hunks, shown_header = 0;
-       char ourtmp_buf[TMPPATHLEN];
-       char *ourtmp = ourtmp_buf;
        int working_tree_file = !memcmp(elem->sha1, null_sha1, 20);
        int abbrev = opt->full_index ? 40 : DEFAULT_ABBREV;
+       mmfile_t result_file;
 
        /* Read the result of merge first */
-       if (!working_tree_file) {
-               result = grab_blob(elem->sha1, &size);
-               write_to_temp_file(ourtmp, result, size);
-       }
+       if (!working_tree_file)
+               result = grab_blob(elem->sha1, &result_size);
        else {
                /* Used by diff-tree to read from the working tree */
                struct stat st;
                int fd;
-               ourtmp = elem->path;
-               if (0 <= (fd = open(ourtmp, O_RDONLY)) &&
+               if (0 <= (fd = open(elem->path, O_RDONLY)) &&
                    !fstat(fd, &st)) {
                        int len = st.st_size;
                        int cnt = 0;
 
                        elem->mode = canon_mode(st.st_mode);
-                       size = len;
+                       result_size = len;
                        result = xmalloc(len + 1);
                        while (cnt < len) {
                                int done = xread(fd, result+cnt, len-cnt);
                                if (done == 0)
                                        break;
                                if (done < 0)
-                                       die("read error '%s'", ourtmp);
+                                       die("read error '%s'", elem->path);
                                cnt += done;
                        }
                        result[len] = 0;
                }
                else {
                        /* deleted file */
-                       size = 0;
+                       result_size = 0;
                        elem->mode = 0;
                        result = xmalloc(1);
                        result[0] = 0;
-                       ourtmp = "/dev/null";
                }
                if (0 <= fd)
                        close(fd);
        }
 
-       for (cnt = 0, cp = result; cp - result < size; cp++) {
+       for (cnt = 0, cp = result; cp - result < result_size; cp++) {
                if (*cp == '\n')
                        cnt++;
        }
-       if (size && result[size-1] != '\n')
+       if (result_size && result[result_size-1] != '\n')
                cnt++; /* incomplete line */
 
        sline = xcalloc(cnt+1, sizeof(*sline));
@@ -689,7 +684,7 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
                sline[lno].lost_tail = &sline[lno].lost_head;
                sline[lno].flag = 0;
        }
-       for (lno = 0, cp = result; cp - result < size; cp++) {
+       for (lno = 0, cp = result; cp - result < result_size; cp++) {
                if (*cp == '\n') {
                        sline[lno].len = cp - sline[lno].bol;
                        lno++;
@@ -697,8 +692,11 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
                                sline[lno].bol = cp + 1;
                }
        }
-       if (size && result[size-1] != '\n')
-               sline[cnt-1].len = size - (sline[cnt-1].bol - result);
+       if (result_size && result[result_size-1] != '\n')
+               sline[cnt-1].len = result_size - (sline[cnt-1].bol - result);
+
+       result_file.ptr = result;
+       result_file.size = result_size;
 
        sline[0].p_lno = xcalloc((cnt+1) * num_parent, sizeof(unsigned long));
        for (lno = 0; lno < cnt; lno++)
@@ -714,7 +712,7 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
                        }
                }
                if (i <= j)
-                       combine_diff(elem->parent[i].sha1, ourtmp, sline,
+                       combine_diff(elem->parent[i].sha1, &result_file, sline,
                                     cnt, i, num_parent);
                if (elem->parent[i].mode != elem->mode)
                        mode_differs = 1;
@@ -767,8 +765,6 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
                }
                dump_sline(sline, cnt, num_parent);
        }
-       if (ourtmp == ourtmp_buf)
-               unlink(ourtmp);
        free(result);
 
        for (i = 0; i < cnt; i++) {
diff --git a/diff.c b/diff.c
index fe4664c..ce98a90 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -883,6 +883,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->filter = arg + 14;
        else if (!strcmp(arg, "--pickaxe-all"))
                options->pickaxe_opts = DIFF_PICKAXE_ALL;
+       else if (!strcmp(arg, "--pickaxe-regex"))
+               options->pickaxe_opts = DIFF_PICKAXE_REGEX;
        else if (!strncmp(arg, "-B", 2)) {
                if ((options->break_opt =
                     diff_scoreopt_parse(arg)) == -1)
diff --git a/diff.h b/diff.h
index a268d16..0cebec1 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -102,6 +102,7 @@ extern int diff_setup_done(struct diff_options *);
 #define DIFF_DETECT_COPY       2
 
 #define DIFF_PICKAXE_ALL       1
+#define DIFF_PICKAXE_REGEX     2
 
 extern void diffcore_std(struct diff_options *);
 
index 50e46ab..cfcce31 100644 (file)
@@ -5,8 +5,11 @@
 #include "diff.h"
 #include "diffcore.h"
 
+#include <regex.h>
+
 static unsigned int contains(struct diff_filespec *one,
-                            const char *needle, unsigned long len)
+                            const char *needle, unsigned long len,
+                            regex_t *regexp)
 {
        unsigned int cnt;
        unsigned long offset, sz;
@@ -18,15 +21,28 @@ static unsigned int contains(struct diff_filespec *one,
        data = one->data;
        cnt = 0;
 
-       /* Yes, I've heard of strstr(), but the thing is *data may
-        * not be NUL terminated.  Sue me.
-        */
-       for (offset = 0; offset + len <= sz; offset++) {
-               /* we count non-overlapping occurrences of needle */
-               if (!memcmp(needle, data + offset, len)) {
-                       offset += len - 1;
+       if (regexp) {
+               regmatch_t regmatch;
+               int flags = 0;
+
+               while (*data && !regexec(regexp, data, 1, &regmatch, flags)) {
+                       flags |= REG_NOTBOL;
+                       data += regmatch.rm_so;
+                       if (*data) data++;
                        cnt++;
                }
+
+       } else { /* Classic exact string match */
+               /* Yes, I've heard of strstr(), but the thing is *data may
+                * not be NUL terminated.  Sue me.
+                */
+               for (offset = 0; offset + len <= sz; offset++) {
+                       /* we count non-overlapping occurrences of needle */
+                       if (!memcmp(needle, data + offset, len)) {
+                               offset += len - 1;
+                               cnt++;
+                       }
+               }
        }
        return cnt;
 }
@@ -36,10 +52,24 @@ void diffcore_pickaxe(const char *needle, int opts)
        struct diff_queue_struct *q = &diff_queued_diff;
        unsigned long len = strlen(needle);
        int i, has_changes;
+       regex_t regex, *regexp = NULL;
        struct diff_queue_struct outq;
        outq.queue = NULL;
        outq.nr = outq.alloc = 0;
 
+       if (opts & DIFF_PICKAXE_REGEX) {
+               int err;
+               err = regcomp(&regex, needle, REG_EXTENDED | REG_NEWLINE);
+               if (err) {
+                       /* The POSIX.2 people are surely sick */
+                       char errbuf[1024];
+                       regerror(err, &regex, errbuf, 1024);
+                       regfree(&regex);
+                       die("invalid pickaxe regex: %s", errbuf);
+               }
+               regexp = &regex;
+       }
+
        if (opts & DIFF_PICKAXE_ALL) {
                /* Showing the whole changeset if needle exists */
                for (i = has_changes = 0; !has_changes && i < q->nr; i++) {
@@ -48,16 +78,16 @@ void diffcore_pickaxe(const char *needle, int opts)
                                if (!DIFF_FILE_VALID(p->two))
                                        continue; /* ignore unmerged */
                                /* created */
-                               if (contains(p->two, needle, len))
+                               if (contains(p->two, needle, len, regexp))
                                        has_changes++;
                        }
                        else if (!DIFF_FILE_VALID(p->two)) {
-                               if (contains(p->one, needle, len))
+                               if (contains(p->one, needle, len, regexp))
                                        has_changes++;
                        }
                        else if (!diff_unmodified_pair(p) &&
-                                contains(p->one, needle, len) !=
-                                contains(p->two, needle, len))
+                                contains(p->one, needle, len, regexp) !=
+                                contains(p->two, needle, len, regexp))
                                has_changes++;
                }
                if (has_changes)
@@ -80,16 +110,16 @@ void diffcore_pickaxe(const char *needle, int opts)
                                if (!DIFF_FILE_VALID(p->two))
                                        ; /* ignore unmerged */
                                /* created */
-                               else if (contains(p->two, needle, len))
+                               else if (contains(p->two, needle, len, regexp))
                                        has_changes = 1;
                        }
                        else if (!DIFF_FILE_VALID(p->two)) {
-                               if (contains(p->one, needle, len))
+                               if (contains(p->one, needle, len, regexp))
                                        has_changes = 1;
                        }
                        else if (!diff_unmodified_pair(p) &&
-                                contains(p->one, needle, len) !=
-                                contains(p->two, needle, len))
+                                contains(p->one, needle, len, regexp) !=
+                                contains(p->two, needle, len, regexp))
                                has_changes = 1;
 
                        if (has_changes)
@@ -98,6 +128,10 @@ void diffcore_pickaxe(const char *needle, int opts)
                                diff_free_filepair(p);
                }
 
+       if (opts & DIFF_PICKAXE_REGEX) {
+               regfree(&regex);
+       }
+
        free(q->queue);
        *q = outq;
        return;
index 823c74b..c013e48 100755 (executable)
@@ -52,7 +52,8 @@ Perhaps git-update-server-info needs to be run there?"
                git-http-fetch -v -a -w "$tname" "$name" "$1/" || exit 1
        done <"$clone_tmp/refs"
        rm -fr "$clone_tmp"
-       http_fetch "$1/HEAD" "$GIT_DIR/REMOTE_HEAD"
+       http_fetch "$1/HEAD" "$GIT_DIR/REMOTE_HEAD" ||
+       rm -f "$GIT_DIR/REMOTE_HEAD"
 }
 
 # Read git-fetch-pack -k output and store the remote branches.
@@ -324,7 +325,7 @@ test -d "$GIT_DIR/refs/reference-tmp" && rm -fr "$GIT_DIR/refs/reference-tmp"
 
 if test -f "$GIT_DIR/CLONE_HEAD"
 then
-       # Figure out where the remote HEAD points at.
+       # Read git-fetch-pack -k output and store the remote branches.
        perl -e "$copy_refs" "$GIT_DIR" "$use_separate_remote" "$origin"
 fi
 
@@ -332,22 +333,25 @@ cd "$D" || exit
 
 if test -z "$bare" && test -f "$GIT_DIR/REMOTE_HEAD"
 then
-       head_sha1=`cat "$GIT_DIR/REMOTE_HEAD"`
        # Figure out which remote branch HEAD points at.
        case "$use_separate_remote" in
        '')     remote_top=refs/heads ;;
        *)      remote_top="refs/remotes/$origin" ;;
        esac
 
-       # What to use to track the remote primary branch
-       if test -n "$use_separate_remote"
-       then
-               origin_tracking="remotes/$origin/master"
-       else
-               origin_tracking="heads/$origin"
-       fi
+       head_sha1=`cat "$GIT_DIR/REMOTE_HEAD"`
+       case "$head_sha1" in
+       'ref: refs/'*)
+               # Uh-oh, the remote told us (http transport done against
+               # new style repository with a symref HEAD).
+               # Ideally we should skip the guesswork but for now
+               # opt for minimum change.
+               head_sha1=`expr "$head_sha1" : 'ref: refs/heads/\(.*\)'`
+               head_sha1=`cat "$GIT_DIR/$remote_top/$head_sha1"`
+               ;;
+       esac
 
-       # The name under $remote_top the remote HEAD seems to point at
+       # The name under $remote_top the remote HEAD seems to point at.
        head_points_at=$(
                (
                        echo "master"
@@ -368,23 +372,28 @@ then
                )
        )
 
-       # Write out remotes/$origin file.
+       # Write out remotes/$origin file, and update our "$head_points_at".
        case "$head_points_at" in
        ?*)
                mkdir -p "$GIT_DIR/remotes" &&
-               echo >"$GIT_DIR/remotes/$origin" \
-               "URL: $repo
-Pull: refs/heads/$head_points_at:refs/$origin_tracking" &&
+               git-symbolic-ref HEAD "refs/heads/$head_points_at" &&
                case "$use_separate_remote" in
-               t) git-update-ref HEAD "$head_sha1" ;;
-               *) git-update-ref "refs/heads/$origin" $(git-rev-parse HEAD) ;;
+               t)      origin_track="$remote_top/$head_points_at"
+                       git-update-ref HEAD "$head_sha1" ;;
+               *)      origin_track="$remote_top/$origin"
+                       git-update-ref "refs/heads/$origin" "$head_sha1" ;;
                esac &&
+               echo >"$GIT_DIR/remotes/$origin" \
+               "URL: $repo
+Pull: refs/heads/$head_points_at:$origin_track" &&
                (cd "$GIT_DIR/$remote_top" && find . -type f -print) |
                while read dotslref
                do
                        name=`expr "$dotslref" : './\(.*\)'` &&
-                       test "$head_points_at" = "$name" ||
-                       test "$origin" = "$name" ||
+                       test "$use_separate_remote" = '' && {
+                               test "$head_points_at" = "$name" ||
+                               test "$origin" = "$name"
+                       } ||
                        echo "Pull: refs/heads/${name}:$remote_top/${name}"
                done >>"$GIT_DIR/remotes/$origin" &&
                case "$use_separate_remote" in
index dc67218..71a7daf 100644 (file)
@@ -4,6 +4,35 @@
 #include "fetch.h"
 #include "http.h"
 
+#ifndef NO_EXPAT
+#include <expat.h>
+
+/* Definitions for DAV requests */
+#define DAV_PROPFIND "PROPFIND"
+#define DAV_PROPFIND_RESP ".multistatus.response"
+#define DAV_PROPFIND_NAME ".multistatus.response.href"
+#define DAV_PROPFIND_COLLECTION ".multistatus.response.propstat.prop.resourcetype.collection"
+#define PROPFIND_ALL_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/>\n</D:propfind>"
+
+/* Definitions for processing XML DAV responses */
+#ifndef XML_STATUS_OK
+enum XML_Status {
+  XML_STATUS_OK = 1,
+  XML_STATUS_ERROR = 0
+};
+#define XML_STATUS_OK    1
+#define XML_STATUS_ERROR 0
+#endif
+
+/* Flags that control remote_ls processing */
+#define PROCESS_FILES (1u << 0)
+#define PROCESS_DIRS  (1u << 1)
+#define RECURSIVE     (1u << 2)
+
+/* Flags that remote_ls passes to callback functions */
+#define IS_DIR (1u << 0)
+#endif
+
 #define PREV_BUF_SIZE 4096
 #define RANGE_HEADER_SIZE 30
 
@@ -15,6 +44,7 @@ static struct curl_slist *no_pragma_header;
 struct alt_base
 {
        char *base;
+       int path_len;
        int got_indices;
        struct packed_git *packs;
        struct alt_base *next;
@@ -58,6 +88,30 @@ struct alternates_request {
        int http_specific;
 };
 
+#ifndef NO_EXPAT
+struct xml_ctx
+{
+       char *name;
+       int len;
+       char *cdata;
+       void (*userFunc)(struct xml_ctx *ctx, int tag_closed);
+       void *userData;
+};
+
+struct remote_ls_ctx
+{
+       struct alt_base *repo;
+       char *path;
+       void (*userFunc)(struct remote_ls_ctx *ls);
+       void *userData;
+       int flags;
+       char *dentry_name;
+       int dentry_flags;
+       int rc;
+       struct remote_ls_ctx *parent;
+};
+#endif
+
 static struct object_request *object_queue_head = NULL;
 
 static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
@@ -500,6 +554,7 @@ static void process_alternates_response(void *callback_data)
                        int serverlen = 0;
                        struct alt_base *newalt;
                        char *target = NULL;
+                       char *path;
                        if (data[i] == '/') {
                                serverlen = strchr(base + 8, '/') - base;
                                okay = 1;
@@ -540,6 +595,13 @@ static void process_alternates_response(void *callback_data)
                                newalt->base = target;
                                newalt->got_indices = 0;
                                newalt->packs = NULL;
+                               path = strstr(target, "//");
+                               if (path) {
+                                       path = index(path+2, '/');
+                                       if (path)
+                                               newalt->path_len = strlen(path);
+                               }
+
                                while (tail->next != NULL)
                                        tail = tail->next;
                                tail->next = newalt;
@@ -611,6 +673,209 @@ static void fetch_alternates(char *base)
        free(url);
 }
 
+#ifndef NO_EXPAT
+static void
+xml_start_tag(void *userData, const char *name, const char **atts)
+{
+       struct xml_ctx *ctx = (struct xml_ctx *)userData;
+       const char *c = index(name, ':');
+       int new_len;
+
+       if (c == NULL)
+               c = name;
+       else
+               c++;
+
+       new_len = strlen(ctx->name) + strlen(c) + 2;
+
+       if (new_len > ctx->len) {
+               ctx->name = xrealloc(ctx->name, new_len);
+               ctx->len = new_len;
+       }
+       strcat(ctx->name, ".");
+       strcat(ctx->name, c);
+
+       if (ctx->cdata) {
+               free(ctx->cdata);
+               ctx->cdata = NULL;
+       }
+
+       ctx->userFunc(ctx, 0);
+}
+
+static void
+xml_end_tag(void *userData, const char *name)
+{
+       struct xml_ctx *ctx = (struct xml_ctx *)userData;
+       const char *c = index(name, ':');
+       char *ep;
+
+       ctx->userFunc(ctx, 1);
+
+       if (c == NULL)
+               c = name;
+       else
+               c++;
+
+       ep = ctx->name + strlen(ctx->name) - strlen(c) - 1;
+       *ep = 0;
+}
+
+static void
+xml_cdata(void *userData, const XML_Char *s, int len)
+{
+       struct xml_ctx *ctx = (struct xml_ctx *)userData;
+       if (ctx->cdata)
+               free(ctx->cdata);
+       ctx->cdata = xcalloc(len+1, 1);
+       strncpy(ctx->cdata, s, len);
+}
+
+static int remote_ls(struct alt_base *repo, const char *path, int flags,
+                    void (*userFunc)(struct remote_ls_ctx *ls),
+                    void *userData);
+
+static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
+{
+       struct remote_ls_ctx *ls = (struct remote_ls_ctx *)ctx->userData;
+
+       if (tag_closed) {
+               if (!strcmp(ctx->name, DAV_PROPFIND_RESP) && ls->dentry_name) {
+                       if (ls->dentry_flags & IS_DIR) {
+                               if (ls->flags & PROCESS_DIRS) {
+                                       ls->userFunc(ls);
+                               }
+                               if (strcmp(ls->dentry_name, ls->path) &&
+                                   ls->flags & RECURSIVE) {
+                                       ls->rc = remote_ls(ls->repo,
+                                                          ls->dentry_name,
+                                                          ls->flags,
+                                                          ls->userFunc,
+                                                          ls->userData);
+                               }
+                       } else if (ls->flags & PROCESS_FILES) {
+                               ls->userFunc(ls);
+                       }
+               } else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) {
+                       ls->dentry_name = xmalloc(strlen(ctx->cdata) -
+                                                 ls->repo->path_len + 1);
+                       strcpy(ls->dentry_name, ctx->cdata + ls->repo->path_len);
+               } else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {
+                       ls->dentry_flags |= IS_DIR;
+               }
+       } else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) {
+               if (ls->dentry_name) {
+                       free(ls->dentry_name);
+               }
+               ls->dentry_name = NULL;
+               ls->dentry_flags = 0;
+       }
+}
+
+static int remote_ls(struct alt_base *repo, const char *path, int flags,
+                    void (*userFunc)(struct remote_ls_ctx *ls),
+                    void *userData)
+{
+       char *url = xmalloc(strlen(repo->base) + strlen(path) + 1);
+       struct active_request_slot *slot;
+       struct slot_results results;
+       struct buffer in_buffer;
+       struct buffer out_buffer;
+       char *in_data;
+       char *out_data;
+       XML_Parser parser = XML_ParserCreate(NULL);
+       enum XML_Status result;
+       struct curl_slist *dav_headers = NULL;
+       struct xml_ctx ctx;
+       struct remote_ls_ctx ls;
+
+       ls.flags = flags;
+       ls.repo = repo;
+       ls.path = strdup(path);
+       ls.dentry_name = NULL;
+       ls.dentry_flags = 0;
+       ls.userData = userData;
+       ls.userFunc = userFunc;
+       ls.rc = 0;
+
+       sprintf(url, "%s%s", repo->base, path);
+
+       out_buffer.size = strlen(PROPFIND_ALL_REQUEST);
+       out_data = xmalloc(out_buffer.size + 1);
+       snprintf(out_data, out_buffer.size + 1, PROPFIND_ALL_REQUEST);
+       out_buffer.posn = 0;
+       out_buffer.buffer = out_data;
+
+       in_buffer.size = 4096;
+       in_data = xmalloc(in_buffer.size);
+       in_buffer.posn = 0;
+       in_buffer.buffer = in_data;
+
+       dav_headers = curl_slist_append(dav_headers, "Depth: 1");
+       dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
+
+       slot = get_active_slot();
+       slot->results = &results;
+       curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
+       curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
+       curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+       curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
+       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
+       curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+       curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
+       curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);
+       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
+
+       if (start_active_slot(slot)) {
+               run_active_slot(slot);
+               if (results.curl_result == CURLE_OK) {
+                       ctx.name = xcalloc(10, 1);
+                       ctx.len = 0;
+                       ctx.cdata = NULL;
+                       ctx.userFunc = handle_remote_ls_ctx;
+                       ctx.userData = &ls;
+                       XML_SetUserData(parser, &ctx);
+                       XML_SetElementHandler(parser, xml_start_tag,
+                                             xml_end_tag);
+                       XML_SetCharacterDataHandler(parser, xml_cdata);
+                       result = XML_Parse(parser, in_buffer.buffer,
+                                          in_buffer.posn, 1);
+                       free(ctx.name);
+
+                       if (result != XML_STATUS_OK) {
+                               ls.rc = error("XML error: %s",
+                                             XML_ErrorString(
+                                                     XML_GetErrorCode(parser)));
+                       }
+               } else {
+                       ls.rc = -1;
+               }
+       } else {
+               ls.rc = error("Unable to start PROPFIND request");
+       }
+
+       free(ls.path);
+       free(url);
+       free(out_data);
+       free(in_buffer.buffer);
+       curl_slist_free_all(dav_headers);
+
+       return ls.rc;
+}
+
+static void process_ls_pack(struct remote_ls_ctx *ls)
+{
+       unsigned char sha1[20];
+
+       if (strlen(ls->dentry_name) == 63 &&
+           !strncmp(ls->dentry_name, "objects/pack/pack-", 18) &&
+           !strncmp(ls->dentry_name+58, ".pack", 5)) {
+               get_sha1_hex(ls->dentry_name + 18, sha1);
+               setup_index(ls->repo, sha1);
+       }
+}
+#endif
+
 static int fetch_indices(struct alt_base *repo)
 {
        unsigned char sha1[20];
@@ -633,6 +898,12 @@ static int fetch_indices(struct alt_base *repo)
        if (get_verbosely)
                fprintf(stderr, "Getting pack list for %s\n", repo->base);
 
+#ifndef NO_EXPAT
+       if (remote_ls(repo, "objects/pack/", PROCESS_FILES,
+                     process_ls_pack, NULL) == 0)
+               return 0;
+#endif
+
        url = xmalloc(strlen(repo->base) + 21);
        sprintf(url, "%s/objects/info/packs", repo->base);
 
@@ -947,6 +1218,7 @@ int main(int argc, char **argv)
 {
        char *commit_id;
        char *url;
+       char *path;
        int arg = 1;
        int rc = 0;
 
@@ -987,6 +1259,12 @@ int main(int argc, char **argv)
        alt->got_indices = 0;
        alt->packs = NULL;
        alt->next = NULL;
+       path = strstr(url, "//");
+       if (path) {
+               path = index(path+2, '/');
+               if (path)
+                       alt->path_len = strlen(path);
+       }
 
        if (pull(commit_id))
                rc = 1;
diff --git a/xdiff-interface.c b/xdiff-interface.c
new file mode 100644 (file)
index 0000000..0a66ded
--- /dev/null
@@ -0,0 +1,58 @@
+#include "cache.h"
+#include "xdiff-interface.h"
+
+static void consume_one(void *priv_, char *s, unsigned long size)
+{
+       struct xdiff_emit_state *priv = priv_;
+       char *ep;
+       while (size) {
+               unsigned long this_size;
+               ep = memchr(s, '\n', size);
+               this_size = (ep == NULL) ? size : (ep - s + 1);
+               priv->consume(priv, s, this_size);
+               size -= this_size;
+               s += this_size;
+       }
+}
+
+int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
+{
+       struct xdiff_emit_state *priv = priv_;
+       int i;
+
+       for (i = 0; i < nbuf; i++) {
+               if (mb[i].ptr[mb[i].size-1] != '\n') {
+                       /* Incomplete line */
+                       priv->remainder = realloc(priv->remainder,
+                                                 priv->remainder_size +
+                                                 mb[i].size);
+                       memcpy(priv->remainder + priv->remainder_size,
+                              mb[i].ptr, mb[i].size);
+                       priv->remainder_size += mb[i].size;
+                       continue;
+               }
+
+               /* we have a complete line */
+               if (!priv->remainder) {
+                       consume_one(priv, mb[i].ptr, mb[i].size);
+                       continue;
+               }
+               priv->remainder = realloc(priv->remainder,
+                                         priv->remainder_size +
+                                         mb[i].size);
+               memcpy(priv->remainder + priv->remainder_size,
+                      mb[i].ptr, mb[i].size);
+               consume_one(priv, priv->remainder,
+                           priv->remainder_size + mb[i].size);
+               free(priv->remainder);
+               priv->remainder = NULL;
+               priv->remainder_size = 0;
+       }
+       if (priv->remainder) {
+               consume_one(priv, priv->remainder, priv->remainder_size);
+               free(priv->remainder);
+               priv->remainder = NULL;
+               priv->remainder_size = 0;
+       }
+       return 0;
+}
diff --git a/xdiff-interface.h b/xdiff-interface.h
new file mode 100644 (file)
index 0000000..e2f0d66
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef XDIFF_INTERFACE_H
+#define XDIFF_INTERFACE_H
+
+#include "xdiff/xdiff.h"
+
+struct xdiff_emit_state;
+
+typedef void (*xdiff_emit_consume_fn)(void *, char *, unsigned long);
+
+struct xdiff_emit_state {
+       xdiff_emit_consume_fn consume;
+       char *remainder;
+       unsigned long remainder_size;
+};
+
+int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf);
+
+#endif