Esempio n. 1
0
/*
 * Compare two timestamps
 *
 * Return 0 on success, < 0 otherwise
 *
 * On success, res_d will be set as:
 *	-1, if date in ts1 is before that of ts2;
 *	0, if date in ts1 is the same as that of ts2;
 *	1, if date in ts1 is after that of ts2;
 *
 * res_t will be set in a similar manner
 */
int timestamp_compare(const char *ts1, const char *ts2, int *res_d, int *res_t)
{
	char *date1 = NULL, *time1 = NULL;
	char *date2 = NULL, *time2 = NULL;
	int ret = -1;

	assert(ts1 && ts2);

	if (timestamp_split(ts1, &date1, &time1) < 0 ||
		timestamp_split(ts2, &date2, &time2) < 0) {
		log_error("Failed to get dates and times from %s and %s", ts1, ts2);
		goto failed;
	}

	if (time_compare(date1, date2, res_d, '-') < 0 ||
		time_compare(time1, time2, res_t, ':') < 0) {
		log_error("Failed to compare dates or times in %s and %s", ts1, ts2);
	} else {
		ret = 0;	/* Success */
	}

	/* Fall through */

failed:
	if (date1) {
		free(date1);
	}

	if (date2) {
		free(date2);
	}

	if (time1) {
		free(time1);
	}

	if (time2) {
		free(time2);
	}

	return ret;
}
Esempio n. 2
0
static rev_commit *
rev_commit_locate_date (rev_ref *branch, time_t date)
{
    rev_commit	*commit;

    for (commit = branch->commit; commit; commit = commit->parent)
    {
	if (time_compare (commit->date, date) <= 0)
	    return commit;
    }
    return NULL;
}
Esempio n. 3
0
int
rev_commit_later (rev_commit *a, rev_commit *b)
{
    long	t;

    assert (a != b);
    t = time_compare (a->date, b->date);
    if (t > 0)
	return 1;
    if (t < 0)
	return 0;
    if ((uintptr_t) a > (uintptr_t) b)
	return 1;
    return 0;
}
Esempio n. 4
0
static int
rev_commit_date_compare (const void *av, const void *bv)
{
    const rev_commit	*a = *(const rev_commit **) av;
    const rev_commit	*b = *(const rev_commit **) bv;
    int			t;

    /*
     * NULL entries sort last
     */
    if (!a && !b)
	return 0;
    else if (!a)
	return 1;
    else if (!b)
	return -1;
#if 0
    /*
     * Entries with no files sort next
     */
    if (a->nfiles != b->nfiles)
	return b->nfiles - a->nfiles;
#endif
    /*
     * tailed entries sort next
     */
    if (a->tailed != b->tailed)
	return a->tailed - b->tailed;
    /*
     * Newest entries sort first
     */
    t = -time_compare (a->date, b->date);
    if (t)
	return t;
    /*
     * Ensure total order by ordering based on file address
     */
    if ((uintptr_t) a->file > (uintptr_t) b->file)
	return -1;
    if ((uintptr_t) a->file < (uintptr_t) b->file)
	return 1;
    return 0;
}
Esempio n. 5
0
int
rev_file_later (rev_file *af, rev_file *bf)
{
    long	t;

    /*
     * When merging file lists, we should never see the same
     * object in both trees
     */
    assert (af != bf);

    t = time_compare (af->date, bf->date);

    if (t > 0)
	return 1;
    if (t < 0)
	return 0;
    if ((uintptr_t) af > (uintptr_t) bf)
	return 1;
    return 0;
}
Esempio n. 6
0
int
main (int argc, const char *argv[])
{
  int c;
  int i;
  int i_bad = 0;
  char dst[100];
  char *dst_p;
  int achars[] = {'!', '"', '%', '&', '(', ')', '*', '+', ',', '-', '.',
                  '/', '?', '<', '=', '>'};

  /*********************************************
   * Test ACHAR and DCHAR
   *********************************************/

  for (c='A'; c<='Z'; c++ ) {
    if (!iso9660_is_dchar(c)) {
      printf("Failed iso9660_is_dchar test on %c\n", c);
      i_bad++;
    }
    if (!iso9660_is_achar(c)) {
      printf("Failed iso9660_is_achar test on %c\n", c);
      i_bad++;
    }
  }

  if (i_bad) return i_bad;
  
  for (c='0'; c<='9'; c++ ) {
    if (!iso9660_is_dchar(c)) {
      printf("Failed iso9660_is_dchar test on %c\n", c);
      i_bad++;
    }
    if (!iso9660_is_achar(c)) {
      printf("Failed iso9660_is_achar test on %c\n", c);
      i_bad++;
    }
  }

  if (i_bad) return i_bad;

  for (i=0; i<=13; i++ ) {
    c=achars[i];
    if (iso9660_is_dchar(c)) {
      printf("Should not pass iso9660_is_dchar test on %c\n", c);
      i_bad++;
    }
    if (!iso9660_is_achar(c)) {
      printf("Failed iso9660_is_achar test on symbol %c\n", c);
      i_bad++;
    }
  }

  if (i_bad) return i_bad;

  /*********************************************
   * Test iso9660_strncpy_pad
   *********************************************/

  dst_p = iso9660_strncpy_pad(dst, "1_3", 5, ISO9660_DCHARS);
  if ( 0 != strncmp(dst, "1_3  ", 5) ) {
    printf("Failed iso9660_strncpy_pad DCHARS\n");
    return 31;
  }
  dst_p = iso9660_strncpy_pad(dst, "ABC!123", 2, ISO9660_ACHARS);
  if ( 0 != strncmp(dst, "AB", 2) ) {
    printf("Failed iso9660_strncpy_pad ACHARS truncation\n");
    return 32;
  }

  /*********************************************
   * Test iso9660_dirname_valid_p 
   *********************************************/

  if ( iso9660_dirname_valid_p("/NOGOOD") ) {
    printf("/NOGOOD should fail iso9660_dirname_valid_p\n");
    return 33;
  }
  if ( iso9660_dirname_valid_p("LONGDIRECTORY/NOGOOD") ) {
    printf("LONGDIRECTORY/NOGOOD should fail iso9660_dirname_valid_p\n");
    return 34;
  }
  if ( !iso9660_dirname_valid_p("OKAY/DIR") ) {
    printf("OKAY/DIR should pass iso9660_dirname_valid_p\n");
    return 35;
  }
  if ( iso9660_dirname_valid_p("OKAY/FILE.EXT") ) {
    printf("OKAY/FILENAME.EXT should fail iso9660_dirname_valid_p\n");
    return 36;
  }

  /*********************************************
   * Test iso9660_pathname_valid_p 
   *********************************************/

  if ( !iso9660_pathname_valid_p("OKAY/FILE.EXT") ) {
    printf("OKAY/FILE.EXT should pass iso9660_dirname_valid_p\n");
    return 37;
  }
  if ( iso9660_pathname_valid_p("OKAY/FILENAMETOOLONG.EXT") ) {
    printf("OKAY/FILENAMETOOLONG.EXT should fail iso9660_dirname_valid_p\n");
    return 38;
  }
  if ( iso9660_pathname_valid_p("OKAY/FILE.LONGEXT") ) {
    printf("OKAY/FILE.LONGEXT should fail iso9660_dirname_valid_p\n");
    return 39;
  }

  dst_p = iso9660_pathname_isofy ("this/file.ext", 1);
  if ( 0 != strncmp(dst_p, "this/file.ext;1", 16) ) {
    printf("Failed iso9660_pathname_isofy\n");
    free(dst_p);
    return 40;
  }
  free(dst_p);

  /*********************************************
   * Test get/set date 
   *********************************************/

  {
    struct tm *p_tm, tm;
    iso9660_dtime_t dtime;
    time_t now = time(NULL);

    memset(&dtime, 0, sizeof(dtime));
    p_tm = localtime(&now);
    iso9660_set_dtime(p_tm, &dtime);
    iso9660_get_dtime(&dtime, true, &tm);

    p_tm = gmtime(&now);
    iso9660_set_dtime_with_timezone(p_tm, 0, &dtime);
    if (!iso9660_get_dtime(&dtime, false, &tm)) {
      printf("Error returned by iso9660_get_dtime_with_timezone\n");
      return 41;
    }

    if ( !time_compare(p_tm, &tm) ) {
      printf("GMT time retrieved with iso9660_get_dtime_with_timezone() not same as that\n");
      printf("set with iso9660_set_dtime().\n");
      return 42;
    }

#ifdef HAVE_TM_GMTOFF    
    if ( !time_compare(p_tm, &tm) ) {
      return 43;
    }
    p_tm = gmtime(&now);
    iso9660_set_dtime(p_tm, &dtime);
    if (!iso9660_get_dtime(&dtime, false, &tm)) {
      printf("Error returned by iso9660_get_dtime\n");
      return 44;
    }

    if ( !time_compare(p_tm, &tm) ) {
      printf("GMT time retrieved with iso9660_get_dtime() not same as that\n");
      printf("set with iso9660_set_dtime().\n");
      return 45;
    }
    
    {
      iso9660_ltime_t ltime;
      p_tm = localtime(&now);
      iso9660_set_ltime(p_tm, &ltime);
      
      if (!iso9660_get_ltime(&ltime, &tm)) {
	printf("Problem running iso9660_get_ltime\n");
	return 46;
      }
      
      if ( ! time_compare(p_tm, &tm) ) {
	printf("local time retrieved with iso9660_get_ltime() not\n");
	printf("same as that set with iso9660_set_ltime().\n");
	return 47;
      }

      p_tm = gmtime(&now);
      iso9660_set_ltime(p_tm, &ltime);
      iso9660_get_ltime(&ltime, &tm);
      if ( ! time_compare(p_tm, &tm) ) {
	printf("GMT time retrieved with iso9660_get_ltime() not\n");
	printf("same as that set with iso9660_set_ltime().\n");
	return 48;
      }
    }
#endif
  }

  return 0;
}
Esempio n. 7
0
/*
 * 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;
}
Esempio n. 8
0
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;
		}
	    }
	}
    }
}
Esempio n. 9
0
/*
 * "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
}
Esempio n. 10
0
/*
 * 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;
}