#include "cache.h"
#include "commit.h"
#include "tree.h"
+#include "tree-walk.h"
#include "tag.h"
#include "blob.h"
#include "refs.h"
-
-const char *write_ref = NULL;
-
-const unsigned char *current_ref = NULL;
+#include "strbuf.h"
int get_tree = 0;
int get_history = 0;
int get_all = 0;
int get_verbosely = 0;
+int get_recover = 0;
static unsigned char current_commit_sha1[20];
void pull_say(const char *fmt, const char *hex)
what, missing_hex, sha1_to_hex(current_commit_sha1));
}
-static int make_sure_we_have_it(const char *what, unsigned char *sha1)
-{
- int status = 0;
-
- if (!has_sha1_file(sha1)) {
- status = fetch(sha1);
- if (status && what)
- report_missing(what, sha1);
- }
- return status;
-}
-
-static int process(unsigned char *sha1, const char *type);
+static int process(struct object *obj);
static int process_tree(struct tree *tree)
{
- struct tree_entry_list *entries;
+ struct tree_desc desc;
+ struct name_entry entry;
if (parse_tree(tree))
return -1;
- for (entries = tree->entries; entries; entries = entries->next) {
- if (process(entries->item.any->sha1,
- entries->directory ? tree_type : blob_type))
+ desc.buf = tree->buffer;
+ desc.size = tree->size;
+ while (tree_entry(&desc, &entry)) {
+ struct object *obj = NULL;
+
+ if (S_ISDIR(entry.mode)) {
+ struct tree *tree = lookup_tree(entry.sha1);
+ if (tree)
+ obj = &tree->object;
+ }
+ else {
+ struct blob *blob = lookup_blob(entry.sha1);
+ if (blob)
+ obj = &blob->object;
+ }
+ if (!obj || process(obj))
return -1;
}
+ free(tree->buffer);
+ tree->buffer = NULL;
+ tree->size = 0;
return 0;
}
-struct commit_list *complete = NULL;
+#define COMPLETE (1U << 0)
+#define SEEN (1U << 1)
+#define TO_SCAN (1U << 2)
+
+static struct commit_list *complete = NULL;
static int process_commit(struct commit *commit)
{
return -1;
while (complete && complete->item->date >= commit->date) {
- pop_most_recent_commit(&complete, 1);
+ pop_most_recent_commit(&complete, COMPLETE);
}
-
- if (commit->object.flags & 1)
+ if (commit->object.flags & COMPLETE)
return 0;
- memcpy(current_commit_sha1, commit->object.sha1, 20);
+ hashcpy(current_commit_sha1, commit->object.sha1);
+
+ pull_say("walk %s\n", sha1_to_hex(commit->object.sha1));
if (get_tree) {
- if (process(commit->tree->object.sha1, tree_type))
+ if (process(&commit->tree->object))
return -1;
if (!get_all)
get_tree = 0;
if (get_history) {
struct commit_list *parents = commit->parents;
for (; parents; parents = parents->next) {
- if (process(parents->item->object.sha1,
- commit_type))
+ if (process(&parents->item->object))
return -1;
}
}
{
if (parse_tag(tag))
return -1;
- return process(tag->tagged->sha1, NULL);
+ return process(tag->tagged);
}
static struct object_list *process_queue = NULL;
static int process_object(struct object *obj)
{
- if (obj->type == commit_type) {
+ if (obj->type == OBJ_COMMIT) {
if (process_commit((struct commit *)obj))
return -1;
return 0;
}
- if (obj->type == tree_type) {
+ if (obj->type == OBJ_TREE) {
if (process_tree((struct tree *)obj))
return -1;
return 0;
}
- if (obj->type == blob_type) {
+ if (obj->type == OBJ_BLOB) {
return 0;
}
- if (obj->type == tag_type) {
+ if (obj->type == OBJ_TAG) {
if (process_tag((struct tag *)obj))
return -1;
return 0;
}
return error("Unable to determine requirements "
"of type %s for %s",
- obj->type, sha1_to_hex(obj->sha1));
+ typename(obj->type), sha1_to_hex(obj->sha1));
}
-static int process(unsigned char *sha1, const char *type)
+static int process(struct object *obj)
{
- struct object *obj = lookup_object_type(sha1, type);
+ if (obj->flags & SEEN)
+ return 0;
+ obj->flags |= SEEN;
- if (has_sha1_file(sha1)) {
- parse_object(sha1);
+ if (has_sha1_file(obj->sha1)) {
/* We already have it, so we should scan it now. */
- return process_object(obj);
+ obj->flags |= TO_SCAN;
}
- if (object_list_contains(process_queue, obj))
- return 0;
+ else {
+ if (obj->flags & COMPLETE)
+ return 0;
+ prefetch(obj->sha1);
+ }
+
object_list_insert(obj, process_queue_end);
process_queue_end = &(*process_queue_end)->next;
-
- //fprintf(stderr, "prefetch %s\n", sha1_to_hex(sha1));
- prefetch(sha1);
-
return 0;
}
static int loop(void)
{
+ struct object_list *elem;
+
while (process_queue) {
struct object *obj = process_queue->item;
- /*
- fprintf(stderr, "%d objects to pull\n",
- object_list_length(process_queue));
- */
- process_queue = process_queue->next;
+ elem = process_queue;
+ process_queue = elem->next;
+ free(elem);
if (!process_queue)
process_queue_end = &process_queue;
- //fprintf(stderr, "fetch %s\n", sha1_to_hex(obj->sha1));
-
- if (make_sure_we_have_it(obj->type ? obj->type : "object",
- obj->sha1))
- return -1;
+ /* If we are not scanning this object, we placed it in
+ * the queue because we needed to fetch it first.
+ */
+ if (! (obj->flags & TO_SCAN)) {
+ if (fetch(obj->sha1)) {
+ report_missing(typename(obj->type), obj->sha1);
+ return -1;
+ }
+ }
if (!obj->type)
parse_object(obj->sha1);
if (process_object(obj))
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 object *obj = parse_object(sha1);
- while (obj->type == tag_type) {
- obj = ((struct tag *) obj)->tagged;
- parse_object(obj->sha1);
- }
- if (obj->type == commit_type) {
- obj->flags |= 1;
- insert_by_date((struct commit *) obj, &complete);
+ struct commit *commit = lookup_commit_reference_gently(sha1, 1);
+ if (commit) {
+ commit->object.flags |= COMPLETE;
+ insert_by_date(commit, &complete);
}
return 0;
}
-int pull(char *target)
+int pull_targets_stdin(char ***target, const char ***write_ref)
{
- unsigned char sha1[20];
- int fd = -1;
+ int targets = 0, targets_alloc = 0;
+ struct strbuf buf;
+ *target = NULL; *write_ref = NULL;
+ strbuf_init(&buf);
+ while (1) {
+ char *rf_one = NULL;
+ char *tg_one;
+
+ read_line(&buf, stdin, '\n');
+ if (buf.eof)
+ break;
+ tg_one = buf.buf;
+ rf_one = strchr(tg_one, '\t');
+ if (rf_one)
+ *rf_one++ = 0;
+
+ if (targets >= targets_alloc) {
+ targets_alloc = targets_alloc ? targets_alloc * 2 : 64;
+ *target = xrealloc(*target, targets_alloc * sizeof(**target));
+ *write_ref = xrealloc(*write_ref, targets_alloc * sizeof(**write_ref));
+ }
+ (*target)[targets] = xstrdup(tg_one);
+ (*write_ref)[targets] = rf_one ? xstrdup(rf_one) : NULL;
+ targets++;
+ }
+ return targets;
+}
+
+void pull_targets_free(int targets, char **target, const char **write_ref)
+{
+ while (targets--) {
+ free(target[targets]);
+ if (write_ref && write_ref[targets])
+ free((char *) write_ref[targets]);
+ }
+}
+
+int pull(int targets, char **target, const char **write_ref,
+ const char *write_ref_log_details)
+{
+ struct ref_lock **lock = xcalloc(targets, sizeof(struct ref_lock *));
+ unsigned char *sha1 = xmalloc(targets * 20);
+ char *msg;
+ int ret;
+ int i;
save_commit_buffer = 0;
- if (write_ref && current_ref) {
- fd = lock_ref_sha1(write_ref, current_ref);
- if (fd < 0)
- return -1;
+ track_object_refs = 0;
+
+ for (i = 0; i < targets; i++) {
+ if (!write_ref || !write_ref[i])
+ continue;
+
+ lock[i] = lock_ref_sha1(write_ref[i], NULL);
+ if (!lock[i]) {
+ error("Can't lock ref %s", write_ref[i]);
+ goto unlock_and_fail;
+ }
}
- for_each_ref(mark_complete);
+ if (!get_recover)
+ for_each_ref(mark_complete, NULL);
- if (interpret_target(target, sha1))
- return error("Could not interpret %s as something to pull",
- target);
- if (process(sha1, NULL))
- return -1;
- if (loop())
- return -1;
-
- if (write_ref) {
- if (current_ref) {
- write_ref_sha1(write_ref, fd, sha1);
- } else {
- write_ref_sha1_unlocked(write_ref, sha1);
+ for (i = 0; i < targets; i++) {
+ if (interpret_target(target[i], &sha1[20 * i])) {
+ error("Could not interpret %s as something to pull", target[i]);
+ goto unlock_and_fail;
}
+ if (process(lookup_unknown_object(&sha1[20 * i])))
+ goto unlock_and_fail;
+ }
+
+ if (loop())
+ goto unlock_and_fail;
+
+ if (write_ref_log_details) {
+ msg = xmalloc(strlen(write_ref_log_details) + 12);
+ sprintf(msg, "fetch from %s", write_ref_log_details);
+ } else {
+ msg = NULL;
+ }
+ for (i = 0; i < targets; i++) {
+ if (!write_ref || !write_ref[i])
+ continue;
+ ret = write_ref_sha1(lock[i], &sha1[20 * i], msg ? msg : "fetch (unknown)");
+ lock[i] = NULL;
+ if (ret)
+ goto unlock_and_fail;
}
+ free(msg);
+
return 0;
+
+
+unlock_and_fail:
+ for (i = 0; i < targets; i++)
+ if (lock[i])
+ unlock_ref(lock[i]);
+ return -1;
}