// FIXME - we don't cope optimally with the situation where a branch is // created, files deleted, and then the branch tagged (without rtag). We'll // never know that the tag was placed on the branch; instead we'll place the tag // on the trunk. static void branch_graph (database_t * db, tag_t *** tree_order, tag_t *** tree_order_end) { // First, go through each tag, and put it on all the branches. for (tag_t * i = db->tags; i != db->tags_end; ++i) { i->changeset.unready_count = 0; for (version_t ** j = i->tag_files; j != i->tag_files_end; ++j) { if ((*j)->branch) record_branch_tag ((*j)->branch, i); if (*j != (*j)->file->versions && (*j)[-1].implicit_merge && (*j)[-1].used && (*j)[-1].branch) record_branch_tag ((*j)[-1].branch, i); } } // Go through each branch and put record it on the tags. for (tag_t * i = db->tags; i != db->tags_end; ++i) for (branch_tag_t * j = i->tags; j != i->tags_end; ++j) { ARRAY_EXTEND (j->tag->parents); j->tag->parents_end[-1].branch = i; j->tag->parents_end[-1].weight = j->weight; ++j->tag->changeset.unready_count; } // Do a cycle breaking pass of the branches. heap_t heap; heap_init (&heap, offsetof (tag_t, changeset.ready_index), tag_compare); // Release all the tags that are ready right now; also sort the parent // lists. for (tag_t * i = db->tags; i != db->tags_end; ++i) { ARRAY_SORT (i->parents, compare_pb); if (i->changeset.unready_count == 0) { i->is_released = true; heap_insert (&heap, i); } } while (!heap_empty (&heap)) tag_released (&heap, heap_pop (&heap), tree_order, tree_order_end); for (tag_t * i = db->tags; i != db->tags_end; ++i) while (!i->is_released) { break_cycle (&heap, i); while (!heap_empty (&heap)) tag_released (&heap, heap_pop (&heap), tree_order, tree_order_end); } heap_destroy (&heap); }
static void grab_by_option (FILE * out, const database_t * db, cvs_connection_t * s, const char * r_arg, const char * D_arg, version_t ** fetch, version_t ** fetch_end) { // Build an array of the paths that we're getting. FIXME - if changeset // versions were sorted we wouldn't need this. const char ** paths = NULL; const char ** paths_end = NULL; for (version_t ** i = fetch; i != fetch_end; ++i) { version_t * v = version_live (*i); assert (v && v->used && v->mark == SIZE_MAX); ARRAY_APPEND (paths, v->file->path); } assert (paths != paths_end); ARRAY_SORT (paths, (int(*)(const void *, const void *)) strcmp); const char * d = NULL; ssize_t d_len = SSIZE_MAX; for (const char ** i = paths; i != paths_end; ++i) { const char * slash = strrchr (*i, '/'); if (slash == NULL) continue; if (slash - *i == d_len && memcmp (*i, d, d_len) == 0) continue; // Tell the server about this directory. d = *i; d_len = slash - d; cvs_printf (s, "Directory %s/%.*s\n" "%s%.*s\n", s->module, (int) d_len, d, s->prefix, (int) d_len, d); } // Go to the main directory. cvs_printf (s, "Directory %s\n%.*s\n", s->module, (int) (strlen (s->prefix) - 1), s->prefix); // Update args: if (r_arg) cvs_printf (s, "Argument -r%s\n", r_arg); if (D_arg) cvs_printf (s, "Argument -D%s\n", D_arg); cvs_printf (s, "Argument -kk\n" "Argument --\n"); for (const char ** i = paths; i != paths_end; ++i) cvs_printf (s, "Argument %s\n", *i); xfree (paths); cvs_printff (s, "update\n"); read_versions (out, db, s); }
void create_changesets (database_t * db) { size_t total_versions = 0; for (file_t * i = db->files; i != db->files_end; ++i) total_versions += i->versions_end - i->versions; if (total_versions == 0) return; version_t ** version_list = ARRAY_ALLOC (version_t *, total_versions); version_t ** vp = version_list; for (file_t * i = db->files; i != db->files_end; ++i) for (version_t * j = i->versions; j != i->versions_end; ++j) *vp++ = j; assert (vp == version_list + total_versions); qsort (version_list, total_versions, sizeof (version_t *), version_compare_qsort); changeset_t * current = database_new_changeset (db); ARRAY_APPEND (current->versions, version_list[0]); version_list[0]->commit = current; current->time = version_list[0]->time; current->type = ct_commit; for (size_t i = 1; i < total_versions; ++i) { version_t * next = version_list[i]; if (!strings_match (*current->versions, next) || next->time - current->time > fuzz_span || next->time - current->versions_end[-1]->time > fuzz_gap) { ARRAY_TRIM (current->versions); current = database_new_changeset (db); current->time = next->time; current->type = ct_commit; } ARRAY_APPEND (current->versions, version_list[i]); version_list[i]->commit = current; } ARRAY_TRIM (current->versions); free (version_list); // Do a pass through the changesets; this breaks any cycles. heap_t ready_versions; heap_init (&ready_versions, offsetof (version_t, ready_index), version_compare_heap); prepare_for_emission (db, &ready_versions); ssize_t emitted_changesets = 0; changeset_t * changeset; while ((changeset = next_changeset_split (db, &ready_versions))) { changeset_emitted (db, &ready_versions, changeset); ++emitted_changesets; } // Sort the changeset version lists by file. for (changeset_t ** i = db->changesets; i != db->changesets_end; ++i) ARRAY_SORT ((*i)->versions, compare_version_by_file); assert (heap_empty (&ready_versions)); assert (heap_empty (&db->ready_changesets)); assert (emitted_changesets == db->changesets_end - db->changesets); heap_destroy (&ready_versions); }