pathRelative function

String pathRelative(
  1. String base,
  2. String target
)

Relative path from base to target. Roadmap #163. Audited: 2026-06-12 11:26 EDT

Implementation

String pathRelative(String base, String target) {
  // Normalize then drop empty segments so leading/trailing/double slashes do not
  // create phantom segments that would throw off the common-prefix match below.
  final List<String> b = pathNormalize(base).split('/').where((String s) => s.isNotEmpty).toList();
  final List<String> t = pathNormalize(
    target,
  ).split('/').where((String s) => s.isNotEmpty).toList();
  // Advance through the shared leading segments; i ends at the first point where
  // base and target diverge (or the end of the shorter path).
  int i = 0;
  while (i < b.length && i < t.length && b[i] == t[i]) {
    i++;
  }
  // Climb out of every base segment past the divergence with "..", then descend
  // into target's remaining segments — that concatenation is the relative path.
  final List<String> ups = List<String>.filled(b.length - i, '..');
  final List<String> rest = t.sublist(i);
  // Join directly rather than via pathJoin: pathJoin treats a leading '..' as a
  // pop with nothing above it and discards it, which would erase the very
  // climb-out segments computed above (the bug where 'a/b/c' -> 'a/b/d' yielded
  // 'd' instead of '../d'). The segments are already clean — target was
  // normalized, base segments were consumed, and ups are literal '..' — so no
  // pathJoin re-normalization is needed. Identical paths leave both lists empty,
  // which joins to '' (the documented same-location result).
  return <String>[...ups, ...rest].join('/');
}