547bbe0a4ad38df6b0b7a511d6a2ce24b3625fe7 max Thu Sep 17 05:08:41 2026 -0700 hubtools import session: keep the track order of the session, refs #34405 #Preview2 week - bugs introduced now will need a build patch to fix The custom track backup archive has one file per track and no ordering information: hgSession wrote the files into a directory and tar walks it in readdir order, which is not the order they were written. hubtools globbed them, and a hub track without a priority is shown in alphabetical order of its label, so an imported session came out sorted by label. hgSession now writes the priority hgTracks orders the tracks by into each track line of the archive, either the one the browser assigned when it loaded the track or the cart variable that a drag-and-drop reorder left behind. A track line that names its own priority is left alone. hubtools sorts on that priority and numbers the hub stanzas, continuing the count across assemblies so two genome stanzas cannot interleave. Archives made before this have no priority and fall back to file name order. diff --git src/hg/hgSession/backup.c src/hg/hgSession/backup.c index aceba145466..b01efebecf3 100644 --- src/hg/hgSession/backup.c +++ src/hg/hgSession/backup.c @@ -921,30 +921,50 @@ struct wiggleDataStream *wds = NULL; int operations = wigFetchAscii; operations = wigFetchAscii; wds = wiggleDataStreamNew(); wds->getData(wds, database, table, operations); wds->asciiOut(wds, database, outFile, TRUE, FALSE); wiggleDataStreamFree(&wds); } +static boolean trackLineHasSetting(char *trackLine, char *setting) +/* Does the var=value list of a custom track line set this variable? Parses a copy, + * hashVarLine() chops up the line it is given. */ +{ +char *copy = cloneString(trackLine); +char *pLine = copy; +nextWord(&pLine); +pLine = skipLeadingSpaces(pLine); +if (isEmpty(pLine)) // a bare "track" line sets nothing + { + freeMem(copy); + return FALSE; + } +struct hash *vars = hashVarLine(pLine, 1); +boolean gotIt = (hashFindVal(vars, setting) != NULL); +freeHashAndVals(&vars); +freeMem(copy); +return gotIt; +} + void makeDownloadSessionCtData(char *param1, char *backgroundProgress) /* Download tables and data to save save in compressed archive. */ { char query[512]; char **row = NULL; struct sqlResult *sr = NULL; // Initialize .progress channel file struct dyString *dyProg = dyStringNew(256); dyStringPrintf(dyProg, "please wait, dumping data to archive ...<br>\n"); updateProgessFile(backgroundProgress, dyProg); lazarusLives(20 * 60); htmlOpen("Preparing Custom Tracks Backup Archive for Download"); @@ -1083,30 +1103,45 @@ dyStringPrintf(dyProg, "%s <br>\n", extra->name); updateProgessFile(backgroundProgress, dyProg); lazarusLives(20 * 60); char outNameCt[2014]; safef(outNameCt, sizeof outNameCt, "%s/%s.ct", outDbDir, extra->name); FILE *fct = mustOpen(outNameCt, "w"); // write the track header if (extra->browserLines) fprintf(fct, "%s", extra->browserLines); // should have ; converted \n already if (!extra->trackLine) errAbort("origTrackLine is NULL!"); + // The archive holds one file per track and tar walks the directory in + // whatever order the file system hands out, so the order of the session + // is lost unless the track line itself carries it. The original track + // line rarely has a priority, the one hgTracks orders the tracks by + // does, so write that one out with it: the tdb priority the browser + // assigned when it loaded the track, or the cart variable that a + // drag-and-drop reorder left behind. A track line that names its own + // priority is left alone, the user asked for that one. A reader that + // does not care about the order is unaffected, priority is optional. + char prioVar[256]; + safef(prioVar, sizeof prioVar, "%s.priority", track->tdb->track); + double priority = cartUsualDouble(cart, prioVar, track->tdb->priority); + if (priority != 0 && !trackLineHasSetting(extra->trackLine, "priority")) + fprintf(fct, "%s priority='%g'\n", extra->trackLine, priority); + else fprintf(fct, "%s\n", extra->trackLine); if (!extra->bigDataUrl) { // handle wiggle cts which have an additional wig binary // wibFile='../trash/ct/hgtct_genome_542_dc1750.wib' // wibFile='../trash/ct/ct_hgwdev_galt_e83f_3892d0.maf' // wibFile='../trash/ct/ct_hgwdev_galt_4ba3_415cc0.vcf' // symlink for speed, file should not change if (track->wibFile) { if (endsWith(track->wibFile, ".wib")) {