Beispiel #1
0
 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
Beispiel #2
0
 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
Beispiel #4
0
    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)
Beispiel #5
0
    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)
Beispiel #6
0
    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))
Beispiel #7
0
    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))
Beispiel #8
0
    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))
Beispiel #9
0
    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)
Beispiel #10
0
    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)
Beispiel #11
0
    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
Beispiel #12
0
    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)
Beispiel #13
0
 def set_parent_remote(self, name):
     value = config.set('branch.%s.remote' % self.__name, name)
Beispiel #14
0
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
Beispiel #15
0
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
Beispiel #16
0
 def __set_parent_remote(self, remote):
     value = config.set('branch.%s.remote' % self.get_name(), remote)
Beispiel #17
0
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
Beispiel #18
0
 def set_parent_remote(self, name):
     config.set('branch.%s.remote' % self.name, name)
Beispiel #19
0
 def __set_parent_remote(self, remote):
     config.set('branch.%s.remote' % self.get_name(), remote)
Beispiel #20
0
 def set_description(self, line):
     if line:
         config.set(self.__branch_descr(), line)
     else:
         config.unset(self.__branch_descr())
Beispiel #21
0
 def protect(self):
     config.set(self.__branch_protect(), 'true')
Beispiel #22
0
 def set_parent_remote(self, name):
     value = config.set('branch.%s.remote' % self.__name, name)
Beispiel #23
0
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
Beispiel #24
0
 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)
Beispiel #25
0
 def set_parent_remote(self, name):
     config.set(self._remote_key, name)
Beispiel #26
0
 def set_description(self, description):
     if description:
         config.set('branch.%s.description' % self.name, description)
     else:
         config.unset('branch.%s.description' % self.name)
Beispiel #27
0
 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)
Beispiel #28
0
 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)
Beispiel #29
0
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)
Beispiel #30
0
 def set_description(self, line):
     if line:
         config.set(self.__branch_descr(), line)
     else:
         config.unset(self.__branch_descr())
Beispiel #31
0
 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)
Beispiel #32
0
 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)
Beispiel #33
0
    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))
Beispiel #34
0
 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)
Beispiel #35
0
 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)
Beispiel #36
0
 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)
Beispiel #37
0
 def set_format_version(v):
     out.info('Upgraded branch %s to format version %d' % (branch, v))
     config.set(key, '%d' % v)