summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Documentation/diff-options.txt4
-rw-r--r--Documentation/git-clean.txt50
-rw-r--r--Makefile9
-rw-r--r--date.c4
-rw-r--r--diff.c46
-rw-r--r--diff.h1
-rw-r--r--diffcore-pickaxe.c66
-rwxr-xr-xgit-clean.sh80
-rwxr-xr-xgit-clone.sh47
-rwxr-xr-xgit-cvsimport.perl6
-rw-r--r--http-fetch.c278
12 files changed, 529 insertions, 63 deletions
diff --git a/.gitignore b/.gitignore
index 75891c393b..b5959d6311 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@ git-checkout
git-checkout-index
git-cherry
git-cherry-pick
+git-clean
git-clone
git-clone-pack
git-commit
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 2a0275eeda..ec6811c718 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -69,6 +69,10 @@
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.
diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt
new file mode 100644
index 0000000000..36890c543d
--- /dev/null
+++ b/Documentation/git-clean.txt
@@ -0,0 +1,50 @@
+git-clean(1)
+============
+
+NAME
+----
+git-clean - Remove untracked files from the working tree
+
+SYNOPSIS
+--------
+[verse]
+'git-clean' [-d] [-n] [-q] [-x | -X]
+
+DESCRIPTION
+-----------
+Removes files unknown to git. This allows to clean the working tree
+from files that are not under version control. If the '-x' option is
+specified, ignored files are also removed, allowing to remove all
+build products.
+
+OPTIONS
+-------
+-d::
+ Remove untracked directories in addition to untracked files.
+
+-n::
+ Don't actually remove anything, just show what would be done.
+
+-q::
+ Be quiet, only report errors, but not the files that are
+ successfully removed.
+
+-x::
+ Don't use the ignore rules. This allows removing all untracked
+ files, including build products. This can be used (possibly in
+ conjunction with gitlink:git-reset[1]) to create a pristine
+ working directory to test a clean build.
+
+-X::
+ Remove only files ignored by git. This may be useful to rebuild
+ everything from scratch, but keep manually created files.
+
+
+Author
+------
+Written by Pavel Roskin <proski@gnu.org>
+
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Makefile b/Makefile
index 7030370c6f..3471177efb 100644
--- a/Makefile
+++ b/Makefile
@@ -114,7 +114,7 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
SCRIPT_SH = \
git-add.sh git-bisect.sh git-branch.sh git-checkout.sh \
- git-cherry.sh git-clone.sh git-commit.sh \
+ git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \
git-count-objects.sh git-diff.sh git-fetch.sh \
git-format-patch.sh git-log.sh git-ls-remote.sh \
git-merge-one-file.sh git-parse-remote.sh \
@@ -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,$^) \
diff --git a/date.c b/date.c
index 1c1917b4e8..376d25d241 100644
--- a/date.c
+++ b/date.c
@@ -255,10 +255,10 @@ static int match_multi_number(unsigned long num, char c, const char *date, char
break;
}
/* mm/dd/yy ? */
- if (is_date(num3, num2, num, tm))
+ if (is_date(num3, num, num2, tm))
break;
/* dd/mm/yy ? */
- if (is_date(num3, num, num2, tm))
+ if (is_date(num3, num2, num, tm))
break;
return 0;
}
diff --git a/diff.c b/diff.c
index e496905bad..ce98a90805 100644
--- 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)
@@ -1280,28 +1282,34 @@ void diff_flush(struct diff_options *options)
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
- if ((diff_output_format == DIFF_FORMAT_NO_OUTPUT) ||
- (p->status == DIFF_STATUS_UNKNOWN))
- continue;
- if (p->status == 0)
- die("internal error in diff-resolve-rename-copy");
- switch (diff_output_format) {
- case DIFF_FORMAT_PATCH:
- diff_flush_patch(p, options);
- break;
- case DIFF_FORMAT_RAW:
- case DIFF_FORMAT_NAME_STATUS:
- diff_flush_raw(p, line_termination,
- inter_name_termination,
- options);
+
+ switch (p->status) {
+ case DIFF_STATUS_UNKNOWN:
break;
- case DIFF_FORMAT_NAME:
- diff_flush_name(p,
- inter_name_termination,
- line_termination);
+ case 0:
+ die("internal error in diff-resolve-rename-copy");
break;
+ default:
+ switch (diff_output_format) {
+ case DIFF_FORMAT_PATCH:
+ diff_flush_patch(p, options);
+ break;
+ case DIFF_FORMAT_RAW:
+ case DIFF_FORMAT_NAME_STATUS:
+ diff_flush_raw(p, line_termination,
+ inter_name_termination,
+ options);
+ break;
+ case DIFF_FORMAT_NAME:
+ diff_flush_name(p,
+ inter_name_termination,
+ line_termination);
+ break;
+ case DIFF_FORMAT_NO_OUTPUT:
+ break;
+ }
}
- diff_free_filepair(q->queue[i]);
+ diff_free_filepair(p);
}
free(q->queue);
q->queue = NULL;
diff --git a/diff.h b/diff.h
index a268d16ff7..0cebec113f 100644
--- 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 *);
diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
index 50e46ab863..cfcce315ba 100644
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -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;
diff --git a/git-clean.sh b/git-clean.sh
new file mode 100755
index 0000000000..b200868e60
--- /dev/null
+++ b/git-clean.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+#
+# Copyright (c) 2005-2006 Pavel Roskin
+#
+
+USAGE="[-d] [-n] [-q] [-x | -X]"
+LONG_USAGE='Clean untracked files from the working directory
+ -d remove directories as well
+ -n don'\''t remove anything, just show what would be done
+ -q be quiet, only report errors
+ -x remove ignored files as well
+ -X remove only ignored files as well'
+SUBDIRECTORY_OK=Yes
+. git-sh-setup
+
+ignored=
+ignoredonly=
+cleandir=
+quiet=
+rmf="rm -f"
+rmrf="rm -rf"
+rm_refuse="echo Not removing"
+echo1="echo"
+
+while case "$#" in 0) break ;; esac
+do
+ case "$1" in
+ -d)
+ cleandir=1
+ ;;
+ -n)
+ quiet=1
+ rmf="echo Would remove"
+ rmrf="echo Would remove"
+ rm_refuse="echo Would not remove"
+ echo1=":"
+ ;;
+ -q)
+ quiet=1
+ ;;
+ -x)
+ ignored=1
+ ;;
+ -X)
+ ignoredonly=1
+ ;;
+ *)
+ usage
+ esac
+ shift
+done
+
+case "$ignored,$ignoredonly" in
+ 1,1) usage;;
+esac
+
+if [ -z "$ignored" ]; then
+ excl="--exclude-per-directory=.gitignore"
+ if [ -f "$GIT_DIR/info/exclude" ]; then
+ excl_info="--exclude-from=$GIT_DIR/info/exclude"
+ fi
+ if [ "$ignoredonly" ]; then
+ excl="$excl --ignored"
+ fi
+fi
+
+git-ls-files --others --directory $excl ${excl_info:+"$excl_info"} |
+while read -r file; do
+ if [ -d "$file" -a ! -L "$file" ]; then
+ if [ -z "$cleandir" ]; then
+ $rm_refuse "$file"
+ continue
+ fi
+ $echo1 "Removing $file"
+ $rmrf "$file"
+ else
+ $echo1 "Removing $file"
+ $rmf "$file"
+ fi
+done
diff --git a/git-clone.sh b/git-clone.sh
index 823c74b913..c013e481d0 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -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
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index 3728294e74..c0ae00bda7 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -677,11 +677,7 @@ my $commit = sub {
waitpid($pid,0);
die "Error running git-commit-tree: $?\n" if $?;
- open(C,">$git_dir/refs/heads/$branch")
- or die "Cannot open branch $branch for update: $!\n";
- print C "$cid\n"
- or die "Cannot write branch $branch for update: $!\n";
- close(C)
+ system("git-update-ref refs/heads/$branch $cid") == 0
or die "Cannot write branch $branch for update: $!\n";
if($tag) {
diff --git a/http-fetch.c b/http-fetch.c
index dc67218ae7..71a7dafd69 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -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;