#! /usr/bin/env python3.3 """Convert between a Perforce changelist number and N git-fast-import marks. We used to have a 1:1 correspondence, used the Perforce changelist number as the git-fast-import mark. But now that a single Perforce changelist can intersect multiple Git Fusion branch views, a single Perforce changelist can map to multiple, distinct, Git commits. So we have to decouple 1:1 and go to 1:n. Internal storage is an ordered list of N changelist numbers. Sorted lists of scalars can be passed to bisect.bisect_left() for O(ln(N)) fast lookup """ import bisect from p4gf_l10n import _ class MarkList: """Associate one or more git-fast-import mark numbers with a single Perforce changelist number. Returned marks are always integers not strings. Input changelists may be integers or strings. Always stored internally and returned as ints. """ def __init__(self): self.change_list = [] # change_list[i] is the changelist number # assigned to mark (i+1) def assign(self, changelist_number): """Assign and return a new mark number for a changelist.""" # Force to integer type so we can do numeric checks for bad input. changelist_number_int = int(changelist_number) # Reject bad input. if changelist_number_int < 1: raise RuntimeError(_('Changelist numbers must be positive integers.')) if self.change_list and (changelist_number_int < self.change_list[-1]): raise RuntimeError(_('Changelist numbers must repeat or increase,' ' must not decrease.')) next_mark = 1 + len(self.change_list) self.change_list.append(changelist_number_int) return next_mark def cl_to_mark_list(self, changelist_number): """Return a list of all previously assign()ed mark numbers for a changelist. Return empty list if we never assign()ed a mark for this changelist. """ changelist_number_int = int(changelist_number) lower_bound = bisect.bisect_left(self.change_list, changelist_number_int) result = [] for i in range(lower_bound, len(self.change_list)): if changelist_number_int == self.change_list[i]: result.append(i+1) else: break return result def mark_to_cl(self, mark_number): """Return the one and only changelist to which this mark was assign()ed. Return None if mark_number was never assign()ed. """ mark_number_int = int(mark_number) if ( (mark_number_int < 1) or (1 + len(self.change_list) <= mark_number_int)): return None return self.change_list[mark_number_int-1]