def get_format_version(): """Return the integer format version number, or None if the branch doesn't have any StGIT metadata at all, of any version.""" fv = config.get(self.format_version_key()) ofv = config.get('branch.%s.stgitformatversion' % self.get_name()) if fv: # Great, there's an explicitly recorded format version # number, which means that the branch is initialized and # of that exact version. return int(fv) elif ofv: # Old name for the version info, upgrade it config.set(self.format_version_key(), ofv) config.unset('branch.%s.stgitformatversion' % self.get_name()) return int(ofv) elif os.path.isdir(os.path.join(branch_dir, 'patches')): # There's a .git/patches/<branch>/patches dirctory, which # means this is an initialized version 1 branch. return 1 elif os.path.isdir(branch_dir): # There's a .git/patches/<branch> directory, which means # this is an initialized version 0 branch. return 0 else: # The branch doesn't seem to be initialized at all. return None
def get_format_version(): """Return the integer format version number, or None if the branch doesn't have any StGit metadata at all, of any version.""" fv = config.get(key) ofv = config.get(old_key) if fv: # Great, there's an explicitly recorded format version # number, which means that the branch is initialized and # of that exact version. return int(fv) elif ofv: # Old name for the version info: upgrade it. config.set(key, ofv) config.unset(old_key) return int(ofv) elif os.path.isdir(os.path.join(branch_dir, 'patches')): # There's a .git/patches/<branch>/patches dirctory, which # means this is an initialized version 1 branch. return 1 elif os.path.isdir(branch_dir): # There's a .git/patches/<branch> directory, which means # this is an initialized version 0 branch. return 0 else: # The branch doesn't seem to be initialized at all. return None
def clone(self, clone_name, msg): clone = self.create( self.repository, name=clone_name, msg=msg, create_at=self.base, parent_remote=self.parent_remote, parent_branch=self.name, ) for pn in self.patchorder.all_visible: clone.patches.new(pn, self.patches[pn], 'clone from %s' % self.name) clone.patchorder.set_order( applied=[], unapplied=self.patchorder.all_visible, hidden=[], ) prefix = 'branch.%s.' % self.name clone_prefix = 'branch.%s.' % clone_name for k, v in list(config.getstartswith(prefix)): clone_key = k.replace(prefix, clone_prefix, 1) config.set(clone_key, v) self.repository.refs.set( clone.state_ref, self.repository.refs.get(self.state_ref), msg=msg, ) return clone
def initialise(cls, repository, name=None, switch_to=False): """Initialise a Git branch to handle patch series. @param repository: The L{Repository} where the L{Stack} will be created @param name: The name of the L{Stack} """ if not name: name = repository.current_branch_name # make sure that the corresponding Git branch exists branch = Branch(repository, name) dir = os.path.join(repository.directory, cls._repo_subdir, name) if os.path.exists(dir): raise StackException('%s: branch already initialized' % name) if switch_to: branch.switch_to() # create the stack directory and files utils.create_dirs(dir) compat_dir = os.path.join(dir, 'patches') utils.create_dirs(compat_dir) PatchOrder.create(dir) config.set(stackupgrade.format_version_key(name), str(stackupgrade.FORMAT_VERSION)) return repository.get_stack(name)
def initialise(cls, repository, name=None, msg='initialise', switch_to=False): """Initialise a Git branch to handle patch series. @param repository: The L{Repository} where the L{Stack} will be created @param name: The name of the L{Stack} """ if not name: name = repository.current_branch_name # make sure that the corresponding Git branch exists branch = Branch(repository, name) stack_state_ref = _stack_state_ref(name) if repository.refs.exists(stack_state_ref): raise StackException('%s: stack already initialized' % name) dir = os.path.join(repository.directory, cls._repo_subdir, name) if os.path.exists(dir): raise StackException('%s: branch already initialized' % name) if switch_to: branch.switch_to() # create the stack directory and files utils.create_dirs(dir) compat_dir = os.path.join(dir, 'patches') utils.create_dirs(compat_dir) PatchOrder.create(dir) config.set( stackupgrade.format_version_key(name), str(stackupgrade.FORMAT_VERSION) ) state_commit = log.StackState( repository, prev=None, head=branch.head, applied=[], unapplied=[], hidden=[], patches={}, message=msg, ).commit_state() repository.refs.set(stack_state_ref, state_commit, msg) return repository.get_stack(name)
def init(self, create_at=False, parent_remote=None, parent_branch=None): """Initialises the stgit series """ if self.is_initialised(): raise StackException, "%s already initialized" % self.get_name() for d in [self._dir()]: if os.path.exists(d): raise StackException, "%s already exists" % d if create_at != False: git.create_branch(self.get_name(), create_at) os.makedirs(self.__patch_dir) self.set_parent(parent_remote, parent_branch) self.create_empty_field("applied") self.create_empty_field("unapplied") config.set(stackupgrade.format_version_key(self.get_name()), str(stackupgrade.FORMAT_VERSION))
def init(self, create_at=False, parent_remote=None, parent_branch=None): """Initialises the stgit series """ if self.is_initialised(): raise StackException('%s already initialized' % self.get_name()) for d in [self._dir()]: if os.path.exists(d): raise StackException('%s already exists' % d) if create_at is not False: git.create_branch(self.get_name(), create_at) os.makedirs(self.__patch_dir) self.set_parent(parent_remote, parent_branch) self.create_empty_field('applied') self.create_empty_field('unapplied') config.set(stackupgrade.format_version_key(self.get_name()), text(stackupgrade.FORMAT_VERSION))
def init(self, create_at=False, parent_remote=None, parent_branch=None): """Initialises the stgit series """ if self.is_initialised(): raise StackException, '%s already initialized' % self.get_name() for d in [self._dir(), self.__refs_dir]: if os.path.exists(d): raise StackException, '%s already exists' % d if (create_at != False): git.create_branch(self.get_name(), create_at) os.makedirs(self.__patch_dir) self.set_parent(parent_remote, parent_branch) self.create_empty_field('applied') self.create_empty_field('unapplied') os.makedirs(self.__refs_dir) self._set_field('orig-base', git.get_head()) config.set(self.format_version_key(), str(FORMAT_VERSION))
def clone(self, target_series): """Clones a series """ try: # allow cloning of branches not under StGIT control base = self.get_base() except BaseException: base = git.get_head() Series(target_series).init(create_at=base) new_series = Series(target_series) # generate an artificial description file new_series.set_description('clone of "%s"' % self.get_name()) # clone self's entire series as unapplied patches try: # allow cloning of branches not under StGIT control applied = self.get_applied() unapplied = self.get_unapplied() patches = applied + unapplied patches.reverse() except BaseException: patches = applied = unapplied = [] for p in patches: patch = self.get_patch(p) newpatch = new_series.new_patch( p, message=patch.get_description(), can_edit=False, unapplied=True, bottom=patch.get_bottom(), top=patch.get_top(), author_name=patch.get_authname(), author_email=patch.get_authemail(), author_date=patch.get_authdate(), ) if patch.get_log(): out.info('Setting log to %s' % patch.get_log()) newpatch.set_log(patch.get_log()) else: out.info('No log for %s' % p) # fast forward the cloned series to self's top new_series.forward_patches(applied) # Clone parent informations value = config.get('branch.%s.remote' % self.get_name()) if value: config.set('branch.%s.remote' % target_series, value) value = config.get('branch.%s.merge' % self.get_name()) if value: config.set('branch.%s.merge' % target_series, value) value = config.get('branch.%s.stgit.parentbranch' % self.get_name()) if value: config.set('branch.%s.stgit.parentbranch' % target_series, value)
def clone(self, target_series): """Clones a series """ try: # allow cloning of branches not under StGIT control base = self.get_base() except: base = git.get_head() Series(target_series).init(create_at=base) new_series = Series(target_series) # generate an artificial description file new_series.set_description('clone of "%s"' % self.get_name()) # clone self's entire series as unapplied patches try: # allow cloning of branches not under StGIT control applied = self.get_applied() unapplied = self.get_unapplied() patches = applied + unapplied patches.reverse() except: patches = applied = unapplied = [] for p in patches: patch = self.get_patch(p) newpatch = new_series.new_patch( p, message=patch.get_description(), can_edit=False, unapplied=True, bottom=patch.get_bottom(), top=patch.get_top(), author_name=patch.get_authname(), author_email=patch.get_authemail(), author_date=patch.get_authdate(), ) if patch.get_log(): out.info("Setting log to %s" % patch.get_log()) newpatch.set_log(patch.get_log()) else: out.info("No log for %s" % p) # fast forward the cloned series to self's top new_series.forward_patches(applied) # Clone parent informations value = config.get("branch.%s.remote" % self.get_name()) if value: config.set("branch.%s.remote" % target_series, value) value = config.get("branch.%s.merge" % self.get_name()) if value: config.set("branch.%s.merge" % target_series, value) value = config.get("branch.%s.stgit.parentbranch" % self.get_name()) if value: config.set("branch.%s.stgit.parentbranch" % target_series, value)
def get_format_version(): """Return the integer format version number. :returns: the format version number or None if the branch does not have any StGit metadata at all, of any version """ mfv = get_meta_file_version() if mfv is not None and mfv >= 4: # Modern-era format version found in branch meta blob. return mfv # Older format versions were stored in the Git config. fv = config.get(old_format_key) ofv = config.get(older_format_key) if fv: # Great, there's an explicitly recorded format version # number, which means that the branch is initialized and # of that exact version. return int(fv) elif ofv: # Old name for the version info: upgrade it. config.set(old_format_key, ofv) config.unset(older_format_key) return int(ofv) elif os.path.isdir(os.path.join(branch_dir, 'patches')): # There's a .git/patches/<branch>/patches dirctory, which # means this is an initialized version 1 branch. return 1 elif os.path.isdir(branch_dir): # There's a .git/patches/<branch> directory, which means # this is an initialized version 0 branch. return 0 else: # The branch doesn't seem to be initialized at all. return None
def initialise(cls, repository, name = None): """Initialise a Git branch to handle patch series. @param repository: The L{Repository} where the L{Stack} will be created @param name: The name of the L{Stack} """ if not name: name = repository.current_branch_name # make sure that the corresponding Git branch exists git.Branch(repository, name) dir = os.path.join(repository.directory, cls.__repo_subdir, name) compat_dir = os.path.join(dir, 'patches') if os.path.exists(dir): raise StackException('%s: branch already initialized' % name) # create the stack directory and files utils.create_dirs(dir) utils.create_dirs(compat_dir) PatchOrder.create(dir) config.set(stackupgrade.format_version_key(name), text(stackupgrade.FORMAT_VERSION)) return repository.get_stack(name)
def set_parent_remote(self, name): value = config.set('branch.%s.remote' % self.__name, name)
def update_to_current_format_version(repository, branch): """Update a potentially older StGit directory structure to the latest version. Note: This function should depend as little as possible on external functions that may change during a format version bump, since it must remain able to process older formats. """ patches_dir = os.path.join(repository.directory, 'patches') branch_dir = os.path.join(patches_dir, branch) old_format_key = format_version_key(branch) older_format_key = 'branch.%s.stgitformatversion' % branch def get_meta_file_version(): """Get format version from the ``meta`` file in the stack log branch.""" stack_ref = 'refs/heads/%s.stgit:meta' % branch try: lines = (repository.run(['git', 'show', stack_ref ]).discard_stderr().output_lines()) except RunException: return None for line in lines: if line.startswith('Version: '): return int(line.split('Version: ', 1)[1]) else: return None def get_format_version(): """Return the integer format version number. :returns: the format version number or None if the branch does not have any StGit metadata at all, of any version """ mfv = get_meta_file_version() if mfv is not None and mfv >= 4: # Modern-era format version found in branch meta blob. return mfv # Older format versions were stored in the Git config. fv = config.get(old_format_key) ofv = config.get(older_format_key) if fv: # Great, there's an explicitly recorded format version # number, which means that the branch is initialized and # of that exact version. return int(fv) elif ofv: # Old name for the version info: upgrade it. config.set(old_format_key, ofv) config.unset(older_format_key) return int(ofv) elif os.path.isdir(os.path.join(branch_dir, 'patches')): # There's a .git/patches/<branch>/patches dirctory, which # means this is an initialized version 1 branch. return 1 elif os.path.isdir(branch_dir): # There's a .git/patches/<branch> directory, which means # this is an initialized version 0 branch. return 0 else: # The branch doesn't seem to be initialized at all. return None def set_format_version_in_config(v): out.info('Upgraded branch %s to format version %d' % (branch, v)) config.set(old_format_key, '%d' % v) def mkdir(d): if not os.path.isdir(d): os.makedirs(d) def rm(f): if os.path.exists(f): os.remove(f) def rm_ref(ref): if repository.refs.exists(ref): repository.refs.delete(ref) # Update 0 -> 1. if get_format_version() == 0: mkdir(os.path.join(branch_dir, 'trash')) patch_dir = os.path.join(branch_dir, 'patches') mkdir(patch_dir) refs_base = 'refs/patches/%s' % branch with open(os.path.join(branch_dir, 'unapplied')) as f: patches = f.readlines() with open(os.path.join(branch_dir, 'applied')) as f: patches.extend(f.readlines()) for patch in patches: patch = patch.strip() os.rename(os.path.join(branch_dir, patch), os.path.join(patch_dir, patch)) topfield = os.path.join(patch_dir, patch, 'top') if os.path.isfile(topfield): top = read_string(topfield) else: top = None if top: repository.refs.set( refs_base + '/' + patch, repository.get_commit(top), 'StGit upgrade', ) set_format_version_in_config(1) # Update 1 -> 2. if get_format_version() == 1: desc_file = os.path.join(branch_dir, 'description') if os.path.isfile(desc_file): desc = read_string(desc_file) if desc: config.set('branch.%s.description' % branch, desc) rm(desc_file) rm(os.path.join(branch_dir, 'current')) rm_ref('refs/bases/%s' % branch) set_format_version_in_config(2) # Update 2 -> 3 if get_format_version() == 2: protect_file = os.path.join(branch_dir, 'protected') if os.path.isfile(protect_file): config.set('branch.%s.stgit.protect' % branch, 'true') os.remove(protect_file) set_format_version_in_config(3) # compatibility with the new infrastructure. The changes here do not # affect the compatibility with the old infrastructure (format version 2) if get_format_version() == 3: hidden_file = os.path.join(branch_dir, 'hidden') if not os.path.isfile(hidden_file): create_empty_file(hidden_file) applied_file = os.path.join(branch_dir, 'applied') unapplied_file = os.path.join(branch_dir, 'unapplied') applied = read_strings(applied_file) unapplied = read_strings(unapplied_file) hidden = read_strings(hidden_file) state_ref = 'refs/heads/%s.stgit' % branch head = repository.refs.get('refs/heads/%s' % branch) parents = [head] meta_lines = [ 'Version: 4', 'Previous: None', 'Head: %s' % head.sha1, ] patches_tree = {} for patch_list, title in [ (applied, 'Applied'), (unapplied, 'Unapplied'), (hidden, 'Hidden'), ]: meta_lines.append('%s:' % title) for i, pn in enumerate(patch_list): patch_ref = 'refs/patches/%s/%s' % (branch, pn) commit = repository.refs.get(patch_ref) meta_lines.append(' %s: %s' % (pn, commit.sha1)) if title != 'Applied' or i == len(patch_list) - 1: if commit not in parents: parents.append(commit) cd = commit.data patch_meta = '\n'.join([ 'Bottom: %s' % cd.parent.data.tree.sha1, 'Top: %s' % cd.tree.sha1, 'Author: %s' % cd.author.name_email, 'Date: %s' % cd.author.date, '', cd.message_str, ]).encode('utf-8') patches_tree[pn] = repository.commit(BlobData(patch_meta)) meta_lines.append('') meta = '\n'.join(meta_lines).encode('utf-8') tree = repository.commit( TreeData({ 'meta': repository.commit(BlobData(meta)), 'patches': repository.commit(TreeData(patches_tree)), })) state_commit = repository.commit( CommitData( tree=tree, message='stack upgrade to version 4', parents=parents, )) repository.refs.set(state_ref, state_commit, 'stack upgrade to v4') for patch_list in [applied, unapplied, hidden]: for pn in patch_list: patch_log_ref = 'refs/patches/%s/%s.log' % (branch, pn) if repository.refs.exists(patch_log_ref): repository.refs.delete(patch_log_ref) config.unset(old_format_key) shutil.rmtree(branch_dir) try: # .git/patches will be removed after the last stack is converted os.rmdir(patches_dir) except OSError: pass out.info('Upgraded branch %s to format version %d' % (branch, 4)) # Make sure we're at the latest version. fv = get_format_version() if fv not in [None, FORMAT_VERSION]: raise StackException('Branch %s is at format version %d, expected %d' % (branch, fv, FORMAT_VERSION)) return fv is not None # true if branch is initialized
def update_to_current_format_version(repository, branch): """Update a potentially older StGit directory structure to the latest version. Note: This function should depend as little as possible on external functions that may change during a format version bump, since it must remain able to process older formats.""" branch_dir = os.path.join(repository.directory, 'patches', branch) key = format_version_key(branch) old_key = 'branch.%s.stgitformatversion' % branch def get_format_version(): """Return the integer format version number, or None if the branch doesn't have any StGit metadata at all, of any version.""" fv = config.get(key) ofv = config.get(old_key) if fv: # Great, there's an explicitly recorded format version # number, which means that the branch is initialized and # of that exact version. return int(fv) elif ofv: # Old name for the version info: upgrade it. config.set(key, ofv) config.unset(old_key) return int(ofv) elif os.path.isdir(os.path.join(branch_dir, 'patches')): # There's a .git/patches/<branch>/patches dirctory, which # means this is an initialized version 1 branch. return 1 elif os.path.isdir(branch_dir): # There's a .git/patches/<branch> directory, which means # this is an initialized version 0 branch. return 0 else: # The branch doesn't seem to be initialized at all. return None def set_format_version(v): out.info('Upgraded branch %s to format version %d' % (branch, v)) config.set(key, '%d' % v) def mkdir(d): if not os.path.isdir(d): os.makedirs(d) def rm(f): if os.path.exists(f): os.remove(f) def rm_ref(ref): if repository.refs.exists(ref): repository.refs.delete(ref) # Update 0 -> 1. if get_format_version() == 0: mkdir(os.path.join(branch_dir, 'trash')) patch_dir = os.path.join(branch_dir, 'patches') mkdir(patch_dir) refs_base = 'refs/patches/%s' % branch for patch in (file(os.path.join(branch_dir, 'unapplied')).readlines() + file(os.path.join(branch_dir, 'applied')).readlines()): patch = patch.strip() os.rename(os.path.join(branch_dir, patch), os.path.join(patch_dir, patch)) topfield = os.path.join(patch_dir, patch, 'top') if os.path.isfile(topfield): top = utils.read_string(topfield, False) else: top = None if top: repository.refs.set(refs_base + '/' + patch, repository.get_commit(top), 'StGit upgrade') set_format_version(1) # Update 1 -> 2. if get_format_version() == 1: desc_file = os.path.join(branch_dir, 'description') if os.path.isfile(desc_file): desc = utils.read_string(desc_file) if desc: config.set('branch.%s.description' % branch, desc) rm(desc_file) rm(os.path.join(branch_dir, 'current')) rm_ref('refs/bases/%s' % branch) set_format_version(2) # compatibility with the new infrastructure. The changes here do not # affect the compatibility with the old infrastructure (format version 2) if get_format_version() == 2: hidden_file = os.path.join(branch_dir, 'hidden') if not os.path.isfile(hidden_file): utils.create_empty_file(hidden_file) # Make sure we're at the latest version. fv = get_format_version() if not fv in [None, FORMAT_VERSION]: raise StackException('Branch %s is at format version %d, expected %d' % (branch, fv, FORMAT_VERSION)) return fv != None # true if branch is initialized
def __set_parent_remote(self, remote): value = config.set('branch.%s.remote' % self.get_name(), remote)
def update_to_current_format_version(repository, branch): """Update a potentially older StGit directory structure to the latest version. Note: This function should depend as little as possible on external functions that may change during a format version bump, since it must remain able to process older formats. """ patches_dir = os.path.join(repository.directory, 'patches') branch_dir = os.path.join(patches_dir, branch) old_format_key = _format_version_key(branch) older_format_key = 'branch.%s.stgitformatversion' % branch def get_meta_file_version(): """Get format version from the ``meta`` file in the stack log branch.""" new_version = get_stack_json_file_version() if new_version is not None: return new_version old_version = get_old_meta_file_version() if old_version is not None: return old_version def get_old_meta_file_version(): """Get format version from the ``meta`` file in the stack log branch.""" stack_ref = 'refs/heads/%s.stgit:meta' % branch try: lines = ( repository.run(['git', 'show', stack_ref]) .discard_stderr() .output_lines() ) except RunException: return None for line in lines: if line.startswith('Version: '): return int(line.split('Version: ', 1)[1]) else: return None def get_stack_json_file_version(): stack_ref = 'refs/stacks/%s:stack.json' % branch try: data = ( repository.run(['git', 'show', stack_ref]) .decoding('utf-8') .discard_stderr() .raw_output() ) except RunException: return None try: stack_json = json.loads(data) except json.JSONDecodeError: return None else: return stack_json.get('version') def get_format_version(): """Return the integer format version number. :returns: the format version number or None if the branch does not have any StGit metadata at all, of any version """ mfv = get_meta_file_version() if mfv is not None and mfv >= 4: # Modern-era format version found in branch meta blob. return mfv # Older format versions were stored in the Git config. fv = config.get(old_format_key) ofv = config.get(older_format_key) if fv: # Great, there's an explicitly recorded format version # number, which means that the branch is initialized and # of that exact version. return int(fv) elif ofv: # Old name for the version info: upgrade it. config.set(old_format_key, ofv) config.unset(older_format_key) return int(ofv) elif os.path.isdir(os.path.join(branch_dir, 'patches')): # There's a .git/patches/<branch>/patches dirctory, which # means this is an initialized version 1 branch. return 1 elif os.path.isdir(branch_dir): # There's a .git/patches/<branch> directory, which means # this is an initialized version 0 branch. return 0 else: # The branch doesn't seem to be initialized at all. return None def set_format_version_in_config(v): out.info('Upgraded branch %s to format version %d' % (branch, v)) config.set(old_format_key, '%d' % v) def rm_ref(ref): if repository.refs.exists(ref): repository.refs.delete(ref) # Update 0 -> 1. if get_format_version() == 0: os.makedirs(os.path.join(branch_dir, 'trash'), exist_ok=True) patch_dir = os.path.join(branch_dir, 'patches') os.makedirs(patch_dir, exist_ok=True) refs_base = 'refs/patches/%s' % branch with open(os.path.join(branch_dir, 'unapplied')) as f: patches = f.readlines() with open(os.path.join(branch_dir, 'applied')) as f: patches.extend(f.readlines()) for patch in patches: patch = patch.strip() os.rename(os.path.join(branch_dir, patch), os.path.join(patch_dir, patch)) topfield = os.path.join(patch_dir, patch, 'top') if os.path.isfile(topfield): top = _read_string(topfield) else: top = None if top: repository.refs.set( refs_base + '/' + patch, repository.get_commit(top), 'StGit upgrade', ) set_format_version_in_config(1) # Update 1 -> 2. if get_format_version() == 1: desc_file = os.path.join(branch_dir, 'description') if os.path.isfile(desc_file): desc = _read_string(desc_file) if desc: config.set('branch.%s.description' % branch, desc) _try_rm(desc_file) _try_rm(os.path.join(branch_dir, 'current')) rm_ref('refs/bases/%s' % branch) set_format_version_in_config(2) # Update 2 -> 3 if get_format_version() == 2: protect_file = os.path.join(branch_dir, 'protected') if os.path.isfile(protect_file): config.set('branch.%s.stgit.protect' % branch, 'true') os.remove(protect_file) set_format_version_in_config(3) # compatibility with the new infrastructure. The changes here do not # affect the compatibility with the old infrastructure (format version 2) if get_format_version() == 3: os.makedirs(branch_dir, exist_ok=True) hidden_file = os.path.join(branch_dir, 'hidden') if not os.path.isfile(hidden_file): open(hidden_file, 'w+', encoding='utf-8').close() applied_file = os.path.join(branch_dir, 'applied') unapplied_file = os.path.join(branch_dir, 'unapplied') applied = _read_strings(applied_file) unapplied = _read_strings(unapplied_file) hidden = _read_strings(hidden_file) state_ref = 'refs/heads/%s.stgit' % branch head = repository.refs.get('refs/heads/%s' % branch) parents = [head] meta_lines = [ 'Version: 4', 'Previous: None', 'Head: %s' % head.sha1, ] patches_tree = {} for patch_list, title in [ (applied, 'Applied'), (unapplied, 'Unapplied'), (hidden, 'Hidden'), ]: meta_lines.append('%s:' % title) for i, pn in enumerate(patch_list): patch_ref = 'refs/patches/%s/%s' % (branch, pn) commit = repository.refs.get(patch_ref) meta_lines.append(' %s: %s' % (pn, commit.sha1)) if title != 'Applied' or i == len(patch_list) - 1: if commit not in parents: parents.append(commit) cd = commit.data patch_meta = '\n'.join( [ 'Bottom: %s' % cd.parent.data.tree.sha1, 'Top: %s' % cd.tree.sha1, 'Author: %s' % cd.author.name_email, 'Date: %s' % cd.author.date, '', cd.message_str, ] ).encode('utf-8') patches_tree[pn] = repository.commit(BlobData(patch_meta)) meta_lines.append('') meta = '\n'.join(meta_lines).encode('utf-8') tree = repository.commit( TreeData( { 'meta': repository.commit(BlobData(meta)), 'patches': repository.commit(TreeData(patches_tree)), } ) ) state_commit = repository.commit( CommitData( tree=tree, message='stack upgrade to version 4', parents=parents, ) ) repository.refs.set(state_ref, state_commit, 'stack upgrade to v4') for patch_list in [applied, unapplied, hidden]: for pn in patch_list: patch_log_ref = 'refs/patches/%s/%s.log' % (branch, pn) if repository.refs.exists(patch_log_ref): repository.refs.delete(patch_log_ref) config.unset(old_format_key) shutil.rmtree(branch_dir) try: # .git/patches will be removed after the last stack is converted os.rmdir(patches_dir) except OSError: pass out.info('Upgraded branch %s to format version %d' % (branch, 4)) # Metadata moves from refs/heads/<branch>.stgit to refs/stacks/<branch>. # Also, metadata file format is JSON instead of custom format. if get_format_version() == 4: old_state_ref = 'refs/heads/%s.stgit' % branch old_state = repository.refs.get(old_state_ref) old_meta = old_state.data.tree.data['meta'][1].data.bytes lines = old_meta.decode('utf-8').splitlines() if not lines[0].startswith('Version: 4'): raise StackException('Malformed metadata (expected version 4)') parsed = {} key = None for line in lines: if line.startswith(' '): assert key is not None parsed[key].append(line.strip()) else: key, val = [x.strip() for x in line.split(':', 1)] if val: parsed[key] = val else: parsed[key] = [] head = repository.refs.get('refs/heads/%s' % branch) new_meta = dict( version=5, prev=parsed['Previous'], head=head.sha1, applied=[], unapplied=[], hidden=[], patches=dict(), ) if parsed['Head'] != new_meta['head']: raise StackException('Unexpected head mismatch') for patch_list_name in ['Applied', 'Unapplied', 'Hidden']: for entry in parsed[patch_list_name]: pn, sha1 = [x.strip() for x in entry.split(':')] new_patch_list_name = patch_list_name.lower() new_meta[new_patch_list_name].append(pn) new_meta['patches'][pn] = dict(oid=sha1) meta_bytes = json.dumps(new_meta, indent=2).encode('utf-8') tree = repository.commit( TreeData( { 'stack.json': repository.commit(BlobData(meta_bytes)), 'patches': old_state.data.tree.data['patches'], } ) ) repository.refs.set( 'refs/stacks/%s' % branch, repository.commit( CommitData( tree=tree, message='stack upgrade to version 5', parents=[head], ) ), 'stack upgrade to v5', ) repository.refs.delete(old_state_ref) out.info('Upgraded branch %s to format version %d' % (branch, 5)) # Make sure we're at the latest version. fv = get_format_version() if fv not in [None, FORMAT_VERSION]: raise StackException( 'Branch %s is at format version %d, expected %d' % (branch, fv, FORMAT_VERSION) ) return fv is not None # true if branch is initialized
def set_parent_remote(self, name): config.set('branch.%s.remote' % self.name, name)
def __set_parent_remote(self, remote): config.set('branch.%s.remote' % self.get_name(), remote)
def set_description(self, line): if line: config.set(self.__branch_descr(), line) else: config.unset(self.__branch_descr())
def protect(self): config.set(self.__branch_protect(), 'true')
def update_to_current_format_version(repository, branch): """Update a potentially older StGit directory structure to the latest version. Note: This function should depend as little as possible on external functions that may change during a format version bump, since it must remain able to process older formats.""" branch_dir = os.path.join(repository.directory, 'patches', branch) key = format_version_key(branch) old_key = 'branch.%s.stgitformatversion' % branch def get_format_version(): """Return the integer format version number, or None if the branch doesn't have any StGit metadata at all, of any version.""" fv = config.get(key) ofv = config.get(old_key) if fv: # Great, there's an explicitly recorded format version # number, which means that the branch is initialized and # of that exact version. return int(fv) elif ofv: # Old name for the version info: upgrade it. config.set(key, ofv) config.unset(old_key) return int(ofv) elif os.path.isdir(os.path.join(branch_dir, 'patches')): # There's a .git/patches/<branch>/patches dirctory, which # means this is an initialized version 1 branch. return 1 elif os.path.isdir(branch_dir): # There's a .git/patches/<branch> directory, which means # this is an initialized version 0 branch. return 0 else: # The branch doesn't seem to be initialized at all. return None def set_format_version(v): out.info('Upgraded branch %s to format version %d' % (branch, v)) config.set(key, '%d' % v) def mkdir(d): if not os.path.isdir(d): os.makedirs(d) def rm(f): if os.path.exists(f): os.remove(f) def rm_ref(ref): if repository.refs.exists(ref): repository.refs.delete(ref) # Update 0 -> 1. if get_format_version() == 0: mkdir(os.path.join(branch_dir, 'trash')) patch_dir = os.path.join(branch_dir, 'patches') mkdir(patch_dir) refs_base = 'refs/patches/%s' % branch with open(os.path.join(branch_dir, 'unapplied')) as f: patches = f.readlines() with open(os.path.join(branch_dir, 'applied')) as f: patches.extend(f.readlines()) for patch in patches: patch = patch.strip() os.rename(os.path.join(branch_dir, patch), os.path.join(patch_dir, patch)) topfield = os.path.join(patch_dir, patch, 'top') if os.path.isfile(topfield): top = utils.read_string(topfield) else: top = None if top: repository.refs.set( refs_base + '/' + patch, repository.get_commit(top), 'StGit upgrade', ) set_format_version(1) # Update 1 -> 2. if get_format_version() == 1: desc_file = os.path.join(branch_dir, 'description') if os.path.isfile(desc_file): desc = utils.read_string(desc_file) if desc: config.set('branch.%s.description' % branch, desc) rm(desc_file) rm(os.path.join(branch_dir, 'current')) rm_ref('refs/bases/%s' % branch) set_format_version(2) # Update 2 -> 3 if get_format_version() == 2: protect_file = os.path.join(branch_dir, 'protected') if os.path.isfile(protect_file): config.set('branch.%s.stgit.protect' % branch, 'true') os.remove(protect_file) set_format_version(3) # compatibility with the new infrastructure. The changes here do not # affect the compatibility with the old infrastructure (format version 2) if get_format_version() == 3: hidden_file = os.path.join(branch_dir, 'hidden') if not os.path.isfile(hidden_file): utils.create_empty_file(hidden_file) # Make sure we're at the latest version. fv = get_format_version() if fv not in [None, FORMAT_VERSION]: raise StackException('Branch %s is at format version %d, expected %d' % (branch, fv, FORMAT_VERSION)) return fv is not None # true if branch is initialized
def set_format_version(v): out.info('Upgraded branch %s to format version %d' % (self.get_name(), v)) config.set(self.format_version_key(), '%d' % v)
def set_parent_remote(self, name): config.set(self._remote_key, name)
def set_description(self, description): if description: config.set('branch.%s.description' % self.name, description) else: config.unset('branch.%s.description' % self.name)
def set_parent_branch(self, name): if config.get("branch.%s.remote" % self.__name): # Never set merge if remote is not set to avoid # possibly-erroneous lookups into 'origin' config.set("branch.%s.merge" % self.__name, name)
def set_parent_branch(self, name): if config.get(self._remote_key): # Never set merge if remote is not set to avoid # possibly-erroneous lookups into 'origin' config.set('branch.%s.merge' % self.name, name)
def func(parser, options, args): repository = directory.repository if options.create: if len(args) == 0 or len(args) > 2: parser.error('incorrect number of arguments') branch_name = args[0] committish = None if len(args) < 2 else args[1] if committish: check_local_changes(repository) check_conflicts(repository.default_iw) try: stack = repository.get_stack() except (DetachedHeadException, StackException): pass else: check_head_top_equal(stack) stack = __create_branch(branch_name, committish) out.info('Branch "%s" created' % branch_name) log.log_entry(stack, 'branch --create %s' % stack.name) return elif options.clone: cur_branch = Branch(repository, repository.current_branch_name) if len(args) == 0: clone_name = cur_branch.name + time.strftime('-%C%y%m%d-%H%M%S') elif len(args) == 1: clone_name = args[0] else: parser.error('incorrect number of arguments') check_local_changes(repository) check_conflicts(repository.default_iw) try: stack = repository.current_stack except StackException: stack = None base = repository.refs.get(repository.head_ref) else: check_head_top_equal(stack) base = stack.base out.start('Cloning current branch to "%s"' % clone_name) clone = Stack.create( repository, name=clone_name, create_at=base, parent_remote=cur_branch.parent_remote, parent_branch=cur_branch.name, ) if stack: for pn in stack.patchorder.all_visible: patch = stack.patches.get(pn) clone.patches.new(pn, patch.commit, 'clone %s' % stack.name) clone.patchorder.set_order(applied=[], unapplied=stack.patchorder.all_visible, hidden=[]) trans = StackTransaction(clone, 'clone') try: for pn in stack.patchorder.applied: trans.push_patch(pn) except TransactionHalted: pass trans.run() prefix = 'branch.%s.' % cur_branch.name new_prefix = 'branch.%s.' % clone.name for n, v in list(config.getstartswith(prefix)): config.set(n.replace(prefix, new_prefix, 1), v) clone.set_description('clone of "%s"' % cur_branch.name) clone.switch_to() out.done() log.copy_log(log.default_repo(), cur_branch.name, clone.name, 'branch --clone') return elif options.delete: if len(args) != 1: parser.error('incorrect number of arguments') __delete_branch(args[0], options.force) log.delete_log(log.default_repo(), args[0]) return elif options.cleanup: if not args: name = repository.current_branch_name elif len(args) == 1: name = args[0] else: parser.error('incorrect number of arguments') __cleanup_branch(name, options.force) log.delete_log(log.default_repo(), name) return elif options.list: if len(args) != 0: parser.error('incorrect number of arguments') branch_names = sorted( ref.replace('refs/heads/', '', 1) for ref in repository.refs if ref.startswith('refs/heads/') and not ref.endswith('.stgit')) if branch_names: out.info('Available branches:') max_len = max(len(name) for name in branch_names) for branch_name in branch_names: __print_branch(branch_name, max_len) else: out.info('No branches') return elif options.protect: if len(args) == 0: branch_name = repository.current_branch_name elif len(args) == 1: branch_name = args[0] else: parser.error('incorrect number of arguments') try: stack = repository.get_stack(branch_name) except StackException: raise CmdException('Branch "%s" is not controlled by StGIT' % branch_name) out.start('Protecting branch "%s"' % branch_name) stack.protected = True out.done() return elif options.rename: if len(args) == 1: stack = repository.current_stack new_name = args[0] elif len(args) == 2: stack = repository.get_stack(args[0]) new_name = args[1] else: parser.error('incorrect number of arguments') old_name = stack.name stack.rename(new_name) out.info('Renamed branch "%s" to "%s"' % (old_name, new_name)) log.rename_log(repository, old_name, new_name, 'branch --rename') return elif options.unprotect: if len(args) == 0: branch_name = repository.current_branch_name elif len(args) == 1: branch_name = args[0] else: parser.error('incorrect number of arguments') try: stack = repository.get_stack(branch_name) except StackException: raise CmdException('Branch "%s" is not controlled by StGIT' % branch_name) out.info('Unprotecting branch "%s"' % branch_name) stack.protected = False out.done() return elif options.description is not None: if len(args) == 0: branch_name = repository.current_branch_name elif len(args) == 1: branch_name = args[0] else: parser.error('incorrect number of arguments') Branch(repository, branch_name).set_description(options.description) return elif len(args) == 1: branch_name = args[0] if branch_name == repository.current_branch_name: raise CmdException('Branch "%s" is already the current branch' % branch_name) if not options.merge: check_local_changes(repository) check_conflicts(repository.default_iw) try: stack = repository.get_stack() except StackException: pass else: check_head_top_equal(stack) out.start('Switching to branch "%s"' % branch_name) Branch(repository, branch_name).switch_to() out.done() return # default action: print the current branch if len(args) != 0: parser.error('incorrect number of arguments') out.stdout(directory.repository.current_branch_name)
def protected(self, protect): protect_key = 'branch.%s.stgit.protect' % self.name if protect: config.set(protect_key, 'true') elif self.protected: config.unset(protect_key)
def __set_parent_branch(self, name): if config.get('branch.%s.remote' % self.get_name()): # Never set merge if remote is not set to avoid # possibly-erroneous lookups into 'origin' config.set('branch.%s.merge' % self.get_name(), name) config.set('branch.%s.stgit.parentbranch' % self.get_name(), name)
def update_to_current_format_version(self): """Update a potentially older StGIT directory structure to the latest version. Note: This function should depend as little as possible on external functions that may change during a format version bump, since it must remain able to process older formats.""" branch_dir = os.path.join(self._basedir(), 'patches', self.get_name()) def get_format_version(): """Return the integer format version number, or None if the branch doesn't have any StGIT metadata at all, of any version.""" fv = config.get(self.format_version_key()) ofv = config.get('branch.%s.stgitformatversion' % self.get_name()) if fv: # Great, there's an explicitly recorded format version # number, which means that the branch is initialized and # of that exact version. return int(fv) elif ofv: # Old name for the version info, upgrade it config.set(self.format_version_key(), ofv) config.unset('branch.%s.stgitformatversion' % self.get_name()) return int(ofv) elif os.path.isdir(os.path.join(branch_dir, 'patches')): # There's a .git/patches/<branch>/patches dirctory, which # means this is an initialized version 1 branch. return 1 elif os.path.isdir(branch_dir): # There's a .git/patches/<branch> directory, which means # this is an initialized version 0 branch. return 0 else: # The branch doesn't seem to be initialized at all. return None def set_format_version(v): out.info('Upgraded branch %s to format version %d' % (self.get_name(), v)) config.set(self.format_version_key(), '%d' % v) def mkdir(d): if not os.path.isdir(d): os.makedirs(d) def rm(f): if os.path.exists(f): os.remove(f) # Update 0 -> 1. if get_format_version() == 0: mkdir(os.path.join(branch_dir, 'trash')) patch_dir = os.path.join(branch_dir, 'patches') mkdir(patch_dir) refs_dir = os.path.join(self._basedir(), 'refs', 'patches', self.get_name()) mkdir(refs_dir) for patch in ( file(os.path.join(branch_dir, 'unapplied')).readlines() + file(os.path.join(branch_dir, 'applied')).readlines()): patch = patch.strip() os.rename(os.path.join(branch_dir, patch), os.path.join(patch_dir, patch)) Patch(patch, patch_dir, refs_dir).update_top_ref() set_format_version(1) # Update 1 -> 2. if get_format_version() == 1: desc_file = os.path.join(branch_dir, 'description') if os.path.isfile(desc_file): desc = read_string(desc_file) if desc: config.set('branch.%s.description' % self.get_name(), desc) rm(desc_file) rm(os.path.join(branch_dir, 'current')) rm(os.path.join(self._basedir(), 'refs', 'bases', self.get_name())) set_format_version(2) # Make sure we're at the latest version. if not get_format_version() in [None, FORMAT_VERSION]: raise StackException( 'Branch %s is at format version %d, expected %d' % (self.get_name(), get_format_version(), FORMAT_VERSION))
def set_parents(self, remote, branch): if remote: self.set_parent_remote(remote) if branch: self.set_parent_branch(branch) config.set('branch.%s.stgit.parentbranch' % self.name, branch)
def set_format_version_in_config(v): out.info('Upgraded branch %s to format version %d' % (branch, v)) config.set(old_format_key, '%d' % v)
def set_format_version(v): out.info('Upgraded branch %s to format version %d' % (branch, v)) config.set(key, '%d' % v)