93a3129444064c4d19b4cb49e24b6de4bd0e06a6 markd Fri Oct 2 21:41:15 2026 -0700 Restore the superTrack nesting fix. refs #38460 Taking this off master was over-cautious. It is wanted on its own: #38460 is a real bug, a superTrack given a parent passes tdbQuery -check -strict and is then silently dropped at load, and the fix should get into a release rather than wait on a trackDb change that uses it. Nothing on master nests yet, and the fix is behaviour-identical where nothing does: trackDb built with it and with the released hgTrackDb is byte-identical on hg38, hs1 and mm39, and hgTracks renders the same rows and supertrack controls as dev. The TSS trackDb change that uses this stays reverted, on superTrackNesting, until this ships. Whoever releases it: trackDbToTxt has to be rebuilt too, or the curated hub it writes names a parent stanza it did not emit. diff --git src/hg/makeDb/hgTrackDb/hgTrackDb.c src/hg/makeDb/hgTrackDb/hgTrackDb.c index bd8d9563bef..19687b0b51c 100644 --- src/hg/makeDb/hgTrackDb/hgTrackDb.c +++ src/hg/makeDb/hgTrackDb/hgTrackDb.c @@ -699,32 +699,36 @@ { struct trackDb *tdb; for (tdb = tdbList; tdb != NULL; tdb = tdb->next) { trackDbPolish(tdb); rPolish(tdb->subtracks); } } static void polishSupers(struct trackDb *tdbList) /* Run polish on supertracks. */ { struct trackDb *tdb; for (tdb = tdbList; tdb != NULL; tdb = tdb->next) { - struct trackDb *parent = tdb->parent; - if (parent != NULL) + /* Walk the whole chain of parents, not just the immediate one: a supertrack + * inside another supertrack is reached only through its child, and without + * its fields filled in from settings it reaches the write with a NULL + * shortLabel. */ + struct trackDb *parent; + for (parent = tdb->parent; parent != NULL; parent = parent->parent) { trackDbFieldsFromSettings(parent); trackDbPolish(parent); } } } static struct trackDb *buildTrackDb(char *org, char *database, char *hgRoot, boolean strict) /* Build trackDb objects from files. */ { struct hash *trackHash = newHash(0); char rootDir[PATH_LEN], orgDir[PATH_LEN], asmDir[PATH_LEN]; /* Create track list from hgRoot and hgRoot/org and hgRoot/org/assembly * ra format database. */ @@ -773,41 +777,44 @@ return tdbList; } static struct trackDb *flatten(struct trackDb *tdbForest) /* Convert our peculiar forest back to a list. * This for now rescues superTracks from the heavens. */ { struct hash *superTrackHash = hashNew(0); struct slRef *ref, *refList = trackDbListGetRefsToDescendants(tdbForest); struct trackDb *tdbList = NULL; for (ref = refList; ref != NULL; ref = ref->next) { struct trackDb *tdb = ref->val; - struct trackDb *parent = tdb->parent; - if (parent != NULL && tdbIsSuperTrack(parent)) + /* Walk up the chain of floating supertrack parents, not just the immediate + * one: a supertrack may itself sit inside another supertrack, and only the + * innermost is reachable from a track in the forest. */ + struct trackDb *parent; + for (parent = tdb->parent; parent != NULL && tdbIsSuperTrack(parent); + parent = parent->parent) { /* The supertrack may appear as a 'floating' parent for multiple tracks. * Only put it on the list once. */ - if (!hashLookup(superTrackHash, parent->track)) - { + if (hashLookup(superTrackHash, parent->track)) + break; hashAdd(superTrackHash, parent->track, parent); slAddHead(&tdbList, parent); } - } slAddHead(&tdbList, tdb); } slFreeList(&refList); hashFree(&superTrackHash); slReverse(&tdbList); return tdbList; } static int findMaxCartVersion(struct trackDb *tdbList) /* Search the track list for the maximum cartVersion. */ { struct trackDb *tdb; int maxVal = 0; for (tdb = tdbList; tdb != NULL; tdb = tdb->next)