static void rev_list_sort_heads (rev_list *rl, cvs_file *cvs) { rev_ref *h, **hp; cvs_symbol *hs, *hns; for (hp = &rl->heads; (h = *hp);) { if (!h->next) break; hs = cvs_find_symbol (cvs, h->name); hns = cvs_find_symbol (cvs, h->next->name); if (cvs_symbol_compare (hs, hns) > 0) { *hp = h->next; h->next = h->next->next; (*hp)->next = h; hp = &rl->heads; } else { hp = &h->next; } } #if DEBUG fprintf (stderr, "Sorted heads for %s\n", cvs->name); for (h = rl->heads; h;) { fprintf (stderr, "\t"); rev_list_dump_ref_parents (stderr, h->parent); dump_number_file (stderr, h->name, &h->number); fprintf (stderr, "\n"); h = h->next; } #endif }
static void rev_commit_dump (FILE *f, char *title, rev_commit *c, rev_commit *m) { fprintf (f, "\n%s\n", title); while (c) { int i; fprintf (f, "%c0x%x %s\n", c == m ? '>' : ' ', (int) c, ctime_nonl (&c->date)); for (i = 0; i < c->nfiles; i++) { fprintf (f, "\t%s", ctime_nonl (&c->files[i]->date)); dump_number_file (f, c->files[i]->name, &c->files[i]->number); fprintf (f, "\n"); } fprintf (f, "\n"); c = c->parent; } }
/* * Construct a branch using CVS revision numbers */ static rev_commit * rev_branch_cvs (cvs_file *cvs, cvs_number *branch) { cvs_number n; rev_commit *head = NULL; rev_commit *c, *p, *gc; Node *node; n = *branch; n.n[n.c-1] = -1; for (node = cvs_find_version (cvs, &n); node; node = node->next) { cvs_version *v = node->v; cvs_patch *p = node->p; rev_commit *c; if (!v) continue; c = calloc (1, sizeof (rev_commit)); c->date = v->date; c->commitid = v->commitid; c->author = v->author; if (p) c->log = p->log; if (v->dead) c->nfiles = 0; else c->nfiles = 1; /* leave this around so the branch merging stuff can find numbers */ c->file = rev_file_rev (cvs->name, &v->number, v->date); if (!v->dead) { node->file = c->file; c->file->mode = cvs->mode; } c->parent = head; head = c; } /* * Make sure the dates along the branch are well ordered. As we * want to preserve current data, push previous versions back to * align with newer revisions. */ for (c = head, gc = NULL; (p = c->parent); gc = c, c = p) { if (time_compare (p->file->date, c->file->date) > 0) { fprintf (stderr, "Warning: %s:", cvs->name); dump_number_file (stderr, " ", &p->file->number); dump_number_file (stderr, " is newer than", &c->file->number); /* Try to catch an odd one out, such as a commit with the * clock set wrong. Dont push back all commits for that, * just fix up the current commit instead of the * parent. */ if (gc && time_compare (p->file->date, gc->file->date) <= 0) { dump_number_file (stderr, ", adjusting", &c->file->number); c->file->date = p->file->date; c->date = p->date; } else { dump_number_file (stderr, ", adjusting", &c->file->number); p->file->date = c->file->date; p->date = c->date; } fprintf (stderr, "\n"); } } return head; }
static void rev_list_graft_branches (rev_list *rl, cvs_file *cvs) { rev_ref *h; rev_commit *c; cvs_version *cv; cvs_branch *cb; /* * Glue branches together */ for (h = rl->heads; h; h = h->next) { /* * skip master branch; it "can't" join * any other branches and it may well end with a vendor * branch revision of the file, which will then create * a loop back to the recorded branch point */ if (h == rl->heads) continue; if (h->tail) continue; /* * Find last commit on branch */ for (c = h->commit; c && c->parent; c = c->parent) if (c->tail) { c = NULL; /* already been done, skip */ break; } if (c) { /* * Walk the version tree, looking for the branch location. * Note that in the presense of vendor branches, the * branch location may actually be out on that vendor branch */ for (cv = cvs->versions; cv; cv = cv->next) { for (cb = cv->branches; cb; cb = cb->next) { if (cvs_number_compare (&cb->number, &c->file->number) == 0) { c->parent = rev_find_cvs_commit (rl, &cv->number); c->tail = 1; break; } } if (c->parent) { #if 0 /* * check for a parallel vendor branch */ for (cb = cv->branches; cb; cb = cb->next) { if (cvs_is_vendor (&cb->number)) { cvs_number v_n; rev_commit *v_c, *n_v_c; fprintf (stderr, "Found merge into vendor branch\n"); v_n = cb->number; v_c = NULL; /* * Walk to head of vendor branch */ while ((n_v_c = rev_find_cvs_commit (rl, &v_n))) { /* * Stop if we reach a date after the * branch version date */ if (time_compare (n_v_c->date, c->date) > 0) break; v_c = n_v_c; v_n.n[v_n.c - 1]++; } if (v_c) { fprintf (stderr, "%s: rewrite branch", cvs->name); dump_number_file (stderr, " branch point", &v_c->file->number); dump_number_file (stderr, " branch version", &c->file->number); fprintf (stderr, "\n"); c->parent = v_c; } } } #endif break; } } } } }
/* * "Vendor branches" (1.1.x) are created by importing sources from * an external source. In X.org, this was from XFree86 and DRI. When * these trees are imported, cvs sets the 'default' branch in each ,v file * to point along this branch. This means that tags made between * the time the vendor branch is imported and when a new revision * is committed to the head branch are placed on the vendor branch * In addition, any files without such a commit appear to adopt * the vendor branch as 'head'. We fix this by merging these two * branches together as if they were the same */ static void rev_list_patch_vendor_branch (rev_list *rl, cvs_file *cvs) { rev_ref *trunk = NULL; rev_ref *vendor = NULL; rev_ref *h; rev_commit *t, **tp, *v, **vp; rev_commit *vlast; rev_ref **h_p; int delete_head; trunk = rl->heads; for (h_p = &rl->heads; (h = *h_p);) { delete_head = 0; if (h->commit && cvs_is_vendor (&h->commit->file->number)) { /* * Find version 1.2 on the trunk. * This will reset the default branch set * when the initial import was done. * Subsequent imports will *not* set the default * branch, and should be on their own branch */ vendor = h; t = trunk->commit; v = vendor->commit; for (vlast = vendor->commit; vlast; vlast = vlast->parent) if (!vlast->parent) break; tp = &trunk->commit; /* * Find the latest trunk revision older than * the entire vendor branch */ while ((t = *tp)) { if (!t->parent || time_compare (vlast->file->date, t->parent->file->date) >= 0) { break; } tp = &t->parent; } if (t) { /* * If the first commit is older than the last element * of the vendor branch, paste them together and * nuke the vendor branch */ if (time_compare (vlast->file->date, t->file->date) >= 0) { delete_head = 1; } else { /* * Splice out any portion of the vendor branch * newer than a the next trunk commit after * the oldest branch commit. */ for (vp = &vendor->commit; (v = *vp); vp = &v->parent) if (time_compare (v->date, t->date) <= 0) break; if (vp == &vendor->commit) { /* * Nothing newer, nuke vendor branch */ delete_head = 1; } else { /* * Some newer stuff, patch parent */ *vp = NULL; } } } else delete_head = 1; /* * Patch up the remaining vendor branch pieces */ if (!delete_head) { rev_commit *vr; if (!vendor->name) { char rev[CVS_MAX_REV_LEN]; char name[MAXPATHLEN]; cvs_number branch; branch = vlast->file->number; branch.c--; cvs_number_string (&branch, rev); snprintf (name, sizeof (name), "import-%s", rev); vendor->name = atom (name); vendor->parent = trunk; vendor->degree = vlast->file->number.c; } for (vr = vendor->commit; vr; vr = vr->parent) { if (!vr->parent) { vr->tail = 1; vr->parent = v; break; } } } /* * Merge two branches based on dates */ while (t && v) { if (time_compare (v->file->date, t->file->date) >= 0) { *tp = v; tp = &v->parent; v = v->parent; } else { *tp = t; tp = &t->parent; t = t->parent; } } if (t) *tp = t; else *tp = v; } if (delete_head) { *h_p = h->next; free (h); } else { h_p = &(h->next); } } #if DEBUG fprintf (stderr, "%s spliced:\n", cvs->name); for (t = trunk->commit; t; t = t->parent) { dump_number_file (stderr, "\t", &t->file->number); fprintf (stderr, "\n"); } #endif }
/* * Merge a set of per-file branches into a global branch */ static void rev_branch_merge (rev_ref **branches, int nbranch, rev_ref *branch, rev_list *rl) { int nlive; int n; rev_commit *prev = NULL; rev_commit *head = NULL, **tail = &head; rev_commit **commits; rev_commit *commit; rev_commit *latest; rev_commit **p; int lazy = 0; time_t start = 0; ALLOC((commits = calloc (nbranch, sizeof (rev_commit *))), "rev_branch_merge"); nlive = 0; // printf("rev_branch_merge: nbranch=%d\n", nbranch); for (n = 0; n < nbranch; n++) { rev_commit *c; /* * Initialize commits to head of each branch */ c = commits[n] = branches[n]->commit; /* * Compute number of branches with remaining entries */ if (!c) continue; if (branches[n]->tail) { c->tailed = 1; continue; } nlive++; while (c && !c->tail) { if (!start || time_compare(c->date, start) < 0) { // printf(" 1:setting start=%ld:%s (from %s)\n", start, ctime_nonl(&start), c->file->name); start = c->date; } c = c->parent; } if (c && (c->file || c->date != c->parent->date)) { if (!start || time_compare(c->date, start) < 0) { // printf(" 2:setting start=%ld:%s (from %s)\n", start, ctime_nonl(&start), c->file->name); start = c->date; } } } for (n = 0; n < nbranch; n++) { rev_commit *c = commits[n]; #if 0 printf("Doing commit %p: @ %ld\n", c, c->date); if (c->file) printf(" %s\n", c->file->name); #endif if (!c->tailed) continue; if (!start || time_compare(start, c->date) >= 0) continue; if (c->file) { /* This case can occur if files have been added to a branch since it's creation. */ printf( "Warning: %s too late date %s through branch %s (%ld:%ld=%ld)\n", c->file->name, ctime_nonl(&c->date), branch->name, start, c->date, start-c->date); continue; } commits[n] = NULL; } /* * Walk down branches until each one has merged with the * parent branch */ while (nlive > 0 && nbranch > 0) { for (n = 0, p = commits, latest = NULL; n < nbranch; n++) { rev_commit *c = commits[n]; if (!c) continue; *p++ = c; if (c->tailed) continue; if (!latest || time_compare(latest->date, c->date) < 0) latest = c; } nbranch = p - commits; /* * Construct current commit */ if (!lazy) { commit = rev_commit_build (commits, latest, nbranch); if (rev_mode == ExecuteGit) lazy = 1; } else { commit = create_tree(latest); } /* * Step each branch */ nlive = 0; for (n = 0; n < nbranch; n++) { rev_commit *c = commits[n]; rev_commit *to; /* already got to parent branch? */ if (c->tailed) continue; /* not affected? */ if (c != latest && !rev_commit_match(c, latest)) { if (c->parent || c->file) nlive++; continue; } to = c->parent; /* starts here? */ if (!to) goto Kill; if (c->tail) { /* * Adding file independently added on another * non-trunk branch. */ if (!to->parent && !to->file) goto Kill; /* * If the parent is at the beginning of trunk * and it is younger than some events on our * branch, we have old CVS adding file * independently * added on another branch. */ if (start && time_compare(start, to->date) < 0) goto Kill; /* * XXX: we still can't be sure that it's * not a file added on trunk after parent * branch had forked off it but before * our branch's creation. */ to->tailed = 1; } else if (to->file) { nlive++; } else { /* * See if it's recent CVS adding a file * independently added on another branch. */ if (!to->parent) goto Kill; if (to->tail && to->date == to->parent->date) goto Kill; nlive++; } if (to->file) set_commit(to); else delete_commit(c); commits[n] = to; continue; Kill: delete_commit(c); commits[n] = NULL; } *tail = commit; tail = &commit->parent; prev = commit; } /* * Connect to parent branch */ nbranch = rev_commit_date_sort (commits, nbranch); if (nbranch && branch->parent ) { rev_ref *lost; int present; // present = 0; for (present = 0; present < nbranch; present++) if (commits[present]->file) { /* * Skip files which appear in the repository after * the first commit along the branch */ if (prev && commits[present]->date > prev->date && commits[present]->date == rev_commit_first_date (commits[present])) { fprintf (stderr, "Warning: file %s appears after branch %s date\n", commits[present]->file->name, branch->name); continue; } break; } if (present == nbranch) *tail = NULL; else if ((*tail = rev_commit_locate_one (branch->parent, commits[present]))) { if (prev && time_compare ((*tail)->date, prev->date) > 0) { fprintf (stderr, "Warning: branch point %s -> %s later than branch\n", branch->name, branch->parent->name); fprintf (stderr, "\ttrunk(%3d): %s %s", n, ctime_nonl (&commits[present]->date), commits[present]->file ? " " : "D" ); if (commits[present]->file) dump_number_file (stderr, commits[present]->file->name, &commits[present]->file->number); fprintf (stderr, "\n"); fprintf (stderr, "\tbranch(%3d): %s ", n, prev->file?ctime_nonl (&prev->file->date):"no file"); if (prev->file) { dump_number_file (stderr, prev->file->name, &prev->file->number); } fprintf (stderr, "\n"); } } else if ((*tail = rev_commit_locate_date (branch->parent, commits[present]->date))) fprintf (stderr, "Warning: branch point %s -> %s matched by date\n", branch->name, branch->parent->name); else { fprintf (stderr, "Error: branch point %s -> %s not found.", branch->name, branch->parent->name); if ((lost = rev_branch_of_commit (rl, commits[present]))) fprintf (stderr, " Possible match on %s.", lost->name); fprintf (stderr, "\n"); } if (*tail) { if (prev) prev->tail = 1; } else *tail = rev_commit_build (commits, commits[0], nbranch); } for (n = 0; n < nbranch; n++) if (commits[n]) commits[n]->tailed = 0; free (commits); branch->commit = head; }