git
/
walker.c
359 строк · 8.0 Кб
1#define USE_THE_REPOSITORY_VARIABLE2
3#include "git-compat-util.h"4#include "gettext.h"5#include "hex.h"6#include "walker.h"7#include "repository.h"8#include "object-store-ll.h"9#include "commit.h"10#include "strbuf.h"11#include "tree.h"12#include "tree-walk.h"13#include "tag.h"14#include "blob.h"15#include "refs.h"16#include "progress.h"17
18static struct object_id current_commit_oid;19
20void walker_say(struct walker *walker, const char *fmt, ...)21{
22if (walker->get_verbosely) {23va_list ap;24va_start(ap, fmt);25vfprintf(stderr, fmt, ap);26va_end(ap);27}28}
29
30static void report_missing(const struct object *obj)31{
32fprintf(stderr, "Cannot obtain needed %s %s\n",33obj->type ? type_name(obj->type): "object",34oid_to_hex(&obj->oid));35if (!is_null_oid(¤t_commit_oid))36fprintf(stderr, "while processing commit %s.\n",37oid_to_hex(¤t_commit_oid));38}
39
40static int process(struct walker *walker, struct object *obj);41
42static int process_tree(struct walker *walker, struct tree *tree)43{
44struct tree_desc desc;45struct name_entry entry;46
47if (parse_tree(tree))48return -1;49
50init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);51while (tree_entry(&desc, &entry)) {52struct object *obj = NULL;53
54/* submodule commits are not stored in the superproject */55if (S_ISGITLINK(entry.mode))56continue;57if (S_ISDIR(entry.mode)) {58struct tree *tree = lookup_tree(the_repository,59&entry.oid);60if (tree)61obj = &tree->object;62}63else {64struct blob *blob = lookup_blob(the_repository,65&entry.oid);66if (blob)67obj = &blob->object;68}69if (!obj || process(walker, obj))70return -1;71}72free_tree_buffer(tree);73return 0;74}
75
76/* Remember to update object flag allocation in object.h */
77#define COMPLETE (1U << 0)78#define SEEN (1U << 1)79#define TO_SCAN (1U << 2)80
81static struct commit_list *complete = NULL;82
83static int process_commit(struct walker *walker, struct commit *commit)84{
85struct commit_list *parents;86
87if (repo_parse_commit(the_repository, commit))88return -1;89
90while (complete && complete->item->date >= commit->date) {91pop_most_recent_commit(&complete, COMPLETE);92}93
94if (commit->object.flags & COMPLETE)95return 0;96
97oidcpy(¤t_commit_oid, &commit->object.oid);98
99walker_say(walker, "walk %s\n", oid_to_hex(&commit->object.oid));100
101if (process(walker, &repo_get_commit_tree(the_repository, commit)->object))102return -1;103
104for (parents = commit->parents; parents; parents = parents->next) {105if (process(walker, &parents->item->object))106return -1;107}108
109return 0;110}
111
112static int process_tag(struct walker *walker, struct tag *tag)113{
114if (parse_tag(tag))115return -1;116return process(walker, tag->tagged);117}
118
119static struct object_list *process_queue = NULL;120static struct object_list **process_queue_end = &process_queue;121
122static int process_object(struct walker *walker, struct object *obj)123{
124if (obj->type == OBJ_COMMIT) {125if (process_commit(walker, (struct commit *)obj))126return -1;127return 0;128}129if (obj->type == OBJ_TREE) {130if (process_tree(walker, (struct tree *)obj))131return -1;132return 0;133}134if (obj->type == OBJ_BLOB) {135return 0;136}137if (obj->type == OBJ_TAG) {138if (process_tag(walker, (struct tag *)obj))139return -1;140return 0;141}142return error("Unable to determine requirements "143"of type %s for %s",144type_name(obj->type), oid_to_hex(&obj->oid));145}
146
147static int process(struct walker *walker, struct object *obj)148{
149if (obj->flags & SEEN)150return 0;151obj->flags |= SEEN;152
153if (repo_has_object_file(the_repository, &obj->oid)) {154/* We already have it, so we should scan it now. */155obj->flags |= TO_SCAN;156}157else {158if (obj->flags & COMPLETE)159return 0;160walker->prefetch(walker, obj->oid.hash);161}162
163object_list_insert(obj, process_queue_end);164process_queue_end = &(*process_queue_end)->next;165return 0;166}
167
168static int loop(struct walker *walker)169{
170struct object_list *elem;171struct progress *progress = NULL;172uint64_t nr = 0;173
174if (walker->get_progress)175progress = start_delayed_progress(_("Fetching objects"), 0);176
177while (process_queue) {178struct object *obj = process_queue->item;179elem = process_queue;180process_queue = elem->next;181free(elem);182if (!process_queue)183process_queue_end = &process_queue;184
185/* If we are not scanning this object, we placed it in186* the queue because we needed to fetch it first.
187*/
188if (! (obj->flags & TO_SCAN)) {189if (walker->fetch(walker, obj->oid.hash)) {190stop_progress(&progress);191report_missing(obj);192return -1;193}194}195if (!obj->type)196parse_object(the_repository, &obj->oid);197if (process_object(walker, obj)) {198stop_progress(&progress);199return -1;200}201display_progress(progress, ++nr);202}203stop_progress(&progress);204return 0;205}
206
207static int interpret_target(struct walker *walker, char *target, struct object_id *oid)208{
209if (!get_oid_hex(target, oid))210return 0;211if (!check_refname_format(target, 0)) {212struct ref *ref = alloc_ref(target);213if (!walker->fetch_ref(walker, ref)) {214oidcpy(oid, &ref->old_oid);215free(ref);216return 0;217}218free(ref);219}220return -1;221}
222
223static int mark_complete(const char *path UNUSED,224const char *referent UNUSED,225const struct object_id *oid,226int flag UNUSED,227void *cb_data UNUSED)228{
229struct commit *commit = lookup_commit_reference_gently(the_repository,230oid, 1);231
232if (commit) {233commit->object.flags |= COMPLETE;234commit_list_insert(commit, &complete);235}236return 0;237}
238
239int walker_targets_stdin(char ***target, const char ***write_ref)240{
241int targets = 0, targets_alloc = 0;242struct strbuf buf = STRBUF_INIT;243*target = NULL; *write_ref = NULL;244while (1) {245char *rf_one = NULL;246char *tg_one;247
248if (strbuf_getline_lf(&buf, stdin) == EOF)249break;250tg_one = buf.buf;251rf_one = strchr(tg_one, '\t');252if (rf_one)253*rf_one++ = 0;254
255if (targets >= targets_alloc) {256targets_alloc = targets_alloc ? targets_alloc * 2 : 64;257REALLOC_ARRAY(*target, targets_alloc);258REALLOC_ARRAY(*write_ref, targets_alloc);259}260(*target)[targets] = xstrdup(tg_one);261(*write_ref)[targets] = xstrdup_or_null(rf_one);262targets++;263}264strbuf_release(&buf);265return targets;266}
267
268void walker_targets_free(int targets, char **target, const char **write_ref)269{
270while (targets--) {271free(target[targets]);272if (write_ref)273free((char *) write_ref[targets]);274}275}
276
277int walker_fetch(struct walker *walker, int targets, char **target,278const char **write_ref, const char *write_ref_log_details)279{
280struct strbuf refname = STRBUF_INIT;281struct strbuf err = STRBUF_INIT;282struct ref_transaction *transaction = NULL;283struct object_id *oids;284char *msg = NULL;285int i, ret = -1;286
287save_commit_buffer = 0;288
289ALLOC_ARRAY(oids, targets);290
291if (write_ref) {292transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),293&err);294if (!transaction) {295error("%s", err.buf);296goto done;297}298}299
300if (!walker->get_recover) {301refs_for_each_ref(get_main_ref_store(the_repository),302mark_complete, NULL);303commit_list_sort_by_date(&complete);304}305
306for (i = 0; i < targets; i++) {307if (interpret_target(walker, target[i], oids + i)) {308error("Could not interpret response from server '%s' as something to pull", target[i]);309goto done;310}311if (process(walker, lookup_unknown_object(the_repository, &oids[i])))312goto done;313}314
315if (loop(walker))316goto done;317if (!write_ref) {318ret = 0;319goto done;320}321if (write_ref_log_details) {322msg = xstrfmt("fetch from %s", write_ref_log_details);323} else {324msg = NULL;325}326for (i = 0; i < targets; i++) {327if (!write_ref[i])328continue;329strbuf_reset(&refname);330strbuf_addf(&refname, "refs/%s", write_ref[i]);331if (ref_transaction_update(transaction, refname.buf,332oids + i, NULL, NULL, NULL, 0,333msg ? msg : "fetch (unknown)",334&err)) {335error("%s", err.buf);336goto done;337}338}339if (ref_transaction_commit(transaction, &err)) {340error("%s", err.buf);341goto done;342}343
344ret = 0;345
346done:347ref_transaction_free(transaction);348free(msg);349free(oids);350strbuf_release(&err);351strbuf_release(&refname);352return ret;353}
354
355void walker_free(struct walker *walker)356{
357walker->cleanup(walker);358free(walker);359}
360