Exemple #1
0
    def log_patch(self, patch, message, notes = None):
        """Generate a log commit for a patch
        """
        top = git.get_commit(patch.get_top())
        old_log = patch.get_log()

        if message is None:
            # replace the current log entry
            if not old_log:
                raise StackException('No log entry to annotate for patch "%s"'
                                     % patch.get_name())
            replace = True
            log_commit = git.get_commit(old_log)
            msg = log_commit.get_log().split('\n')[0]
            log_parent = log_commit.get_parent()
            if log_parent:
                parents = [log_parent]
            else:
                parents = []
        else:
            # generate a new log entry
            replace = False
            msg = '%s\t%s' % (message, top.get_id_hash())
            if old_log:
                parents = [old_log]
            else:
                parents = []

        if notes:
            msg += '\n\n' + notes

        log = git.commit(message = msg, parents = parents,
                         cache_update = False, tree_id = top.get_tree(),
                         allowempty = True)
        patch.set_log(log)
Exemple #2
0
def func(parser, options, args):
    """Assimilate a number of patches.
    """

    def nothing_to_do():
        out.info('No commits to assimilate')

    top_patch = crt_series.get_current_patch()
    if not top_patch:
        return nothing_to_do()

    victims = []
    victim = git.get_commit(git.get_head())
    while victim.get_id_hash() != top_patch.get_top():
        victims.append(victim)
        parents = victim.get_parents()
        if not parents:
            raise CmdException, 'Commit %s has no parents, aborting' % victim
        elif len(parents) > 1:
            raise CmdException, 'Commit %s is a merge, aborting' % victim
        victim = git.get_commit(parents[0])

    if not victims:
        return nothing_to_do()

    if crt_series.get_protected():
        raise CmdException(
            'This branch is protected. Modification is not permitted')

    patch2name = {}
    name2patch = {}

    def name_taken(name):
        return name in name2patch or crt_series.patch_exists(name)

    for victim in victims:
        patchname = make_patch_name(victim.get_log(), name_taken)
        patch2name[victim] = patchname
        name2patch[patchname] = victim

    victims.reverse()
    for victim in victims:
        out.info('Creating patch "%s" from commit %s'
                 % (patch2name[victim], victim))
        aname, amail, adate = name_email_date(victim.get_author())
        cname, cmail, cdate = name_email_date(victim.get_committer())
        crt_series.new_patch(
            patch2name[victim],
            can_edit = False, before_existing = False, commit = False,
            top = victim.get_id_hash(), bottom = victim.get_parent(),
            message = victim.get_log(),
            author_name = aname, author_email = amail, author_date = adate,
            committer_name = cname, committer_email = cmail)
Exemple #3
0
    def empty_patch(self, name):
        """Returns True if the patch is empty
        """
        self.__patch_name_valid(name)
        patch = self.get_patch(name)
        bottom = patch.get_bottom()
        top = patch.get_top()

        if bottom == top:
            return True
        elif git.get_commit(top).get_tree() == git.get_commit(bottom).get_tree():
            return True

        return False
Exemple #4
0
def show_log(log, options):
    """List the patch changelog
    """
    commit = git.get_commit(log)
    diff_str = ''
    while commit:
        log = commit.get_log().split('\n')

        cmd_rev = log[0].split()
        if len(cmd_rev) >= 2:
            cmd = cmd_rev[0]
            rev = cmd_rev[1]
        elif len(cmd_rev) == 1:
            cmd = cmd_rev[0]
            rev = ''
        else:
            cmd = rev = ''

        if options.patch:
            if cmd in ['refresh', 'undo', 'sync']:
                diff_str = '%s%s\n' % (diff_str,
                                       git.pretty_commit(commit.get_id_hash()))
        else:
            if len(log) >= 3:
                notes = log[2]
            else:
                notes = ''
            author_name, author_email, author_date = \
                         name_email_date(commit.get_author())
            secs, tz = author_date.split()
            date = '%s %s' % (time.ctime(int(secs)), tz)

            if options.full:
                out.stdout('%-7s %-40s %s' % (cmd[:7], rev[:40], date))
            else:
                out.stdout('%-8s [%-7s] %-28s  %s' % \
                           (rev[:8], cmd[:7], notes[:28], date))

        parent = commit.get_parent()
        if parent:
            commit = git.get_commit(parent)
        else:
            commit = None

    if options.patch and diff_str:
        pager(diff_str.rstrip())
Exemple #5
0
 def set_top(self, value, backup=False):
     if backup:
         curr_top = self.get_top()
         self._set_field('top.old', curr_top)
         self._set_field(
             'bottom.old',
             git.get_commit(curr_top).get_parent(),
         )
     self._update_top_ref(value)
Exemple #6
0
    def log_patch(self, patch, message, notes=None):
        """Generate a log commit for a patch
        """
        top = git.get_commit(patch.get_top())
        old_log = patch.get_log()

        if message is None:
            # replace the current log entry
            if not old_log:
                raise StackException, \
                      'No log entry to annotate for patch "%s"' \
                      % patch.get_name()
            replace = True
            log_commit = git.get_commit(old_log)
            msg = log_commit.get_log().split('\n')[0]
            log_parent = log_commit.get_parent()
            if log_parent:
                parents = [log_parent]
            else:
                parents = []
        else:
            # generate a new log entry
            replace = False
            msg = '%s\t%s' % (message, top.get_id_hash())
            if old_log:
                parents = [old_log]
            else:
                parents = []

        if notes:
            msg += '\n\n' + notes

        log = git.commit(message=msg,
                         parents=parents,
                         cache_update=False,
                         tree_id=top.get_tree(),
                         allowempty=True)
        patch.set_log(log)
Exemple #7
0
    def log_patch(self, patch, message):
        """Generate a log commit for a patch
        """
        top = git.get_commit(patch.get_top())
        old_log = patch.get_log()

        # generate a new log entry
        msg = '%s\t%s' % (message, top.get_id_hash())
        if old_log:
            parents = [old_log]
        else:
            parents = []

        log = git.commit(
            message=msg,
            parents=parents,
            cache_update=False,
            tree_id=top.get_tree(),
            allowempty=True,
        )
        patch.set_log(log)
Exemple #8
0
    def new_patch(self, name, message = None, can_edit = True,
                  unapplied = False, show_patch = False,
                  top = None, bottom = None, commit = True,
                  author_name = None, author_email = None, author_date = None,
                  committer_name = None, committer_email = None,
                  before_existing = False, sign_str = None):
        """Creates a new patch, either pointing to an existing commit object,
        or by creating a new commit object.
        """

        assert commit or (top and bottom)
        assert not before_existing or (top and bottom)
        assert not (commit and before_existing)
        assert (top and bottom) or (not top and not bottom)
        assert commit or (not top or (bottom == git.get_commit(top).get_parent()))

        if name is not None:
            self.__patch_name_valid(name)
            if self.patch_exists(name):
                raise StackException('Patch "%s" already exists' % name)

        # TODO: move this out of the stgit.stack module, it is really
        # for higher level commands to handle the user interaction
        def sign(msg):
            return add_sign_line(msg, sign_str,
                                 committer_name or git.committer().name,
                                 committer_email or git.committer().email)
        if not message and can_edit:
            descr = edit_file(
                self, sign(''),
                'Please enter the description for the patch above.',
                show_patch)
        else:
            descr = sign(message)

        head = git.get_head()

        if name is None:
            name = make_patch_name(descr, self.patch_exists)

        patch = self.get_patch(name)
        patch.create()

        patch.set_description(descr)
        patch.set_authname(author_name)
        patch.set_authemail(author_email)
        patch.set_authdate(author_date)
        patch.set_commname(committer_name)
        patch.set_commemail(committer_email)

        if before_existing:
            insert_string(self.__applied_file, patch.get_name())
        elif unapplied:
            patches = [patch.get_name()] + self.get_unapplied()
            write_strings(self.__unapplied_file, patches)
            set_head = False
        else:
            append_string(self.__applied_file, patch.get_name())
            set_head = True

        if commit:
            if top:
                top_commit = git.get_commit(top)
            else:
                bottom = head
                top_commit = git.get_commit(head)

            # create a commit for the patch (may be empty if top == bottom);
            # only commit on top of the current branch
            assert(unapplied or bottom == head)
            commit_id = git.commit(message = descr, parents = [bottom],
                                   cache_update = False,
                                   tree_id = top_commit.get_tree(),
                                   allowempty = True, set_head = set_head,
                                   author_name = author_name,
                                   author_email = author_email,
                                   author_date = author_date,
                                   committer_name = committer_name,
                                   committer_email = committer_email)
            # set the patch top to the new commit
            patch.set_top(commit_id)
        else:
            patch.set_top(top)

        self.log_patch(patch, 'new')

        return patch
Exemple #9
0
    def refresh_patch(self, files = None, message = None, edit = False,
                      empty = False,
                      show_patch = False,
                      cache_update = True,
                      author_name = None, author_email = None,
                      author_date = None,
                      committer_name = None, committer_email = None,
                      backup = True, sign_str = None, log = 'refresh',
                      notes = None, bottom = None):
        """Generates a new commit for the topmost patch
        """
        patch = self.get_current_patch()
        if not patch:
            raise StackException('No patches applied')

        descr = patch.get_description()
        if not (message or descr):
            edit = True
            descr = ''
        elif message:
            descr = message

        # TODO: move this out of the stgit.stack module, it is really
        # for higher level commands to handle the user interaction
        if not message and edit:
            descr = edit_file(self, descr.rstrip(), \
                              'Please edit the description for patch "%s" ' \
                              'above.' % patch.get_name(), show_patch)

        if not author_name:
            author_name = patch.get_authname()
        if not author_email:
            author_email = patch.get_authemail()
        if not committer_name:
            committer_name = patch.get_commname()
        if not committer_email:
            committer_email = patch.get_commemail()

        descr = add_sign_line(descr, sign_str, committer_name, committer_email)

        if not bottom:
            bottom = patch.get_bottom()

        if empty:
            tree_id = git.get_commit(bottom).get_tree()
        else:
            tree_id = None

        commit_id = git.commit(files = files,
                               message = descr, parents = [bottom],
                               cache_update = cache_update,
                               tree_id = tree_id,
                               set_head = True,
                               allowempty = True,
                               author_name = author_name,
                               author_email = author_email,
                               author_date = author_date,
                               committer_name = committer_name,
                               committer_email = committer_email)

        patch.set_top(commit_id, backup = backup)
        patch.set_description(descr)
        patch.set_authname(author_name)
        patch.set_authemail(author_email)
        patch.set_authdate(author_date)
        patch.set_commname(committer_name)
        patch.set_commemail(committer_email)

        if log:
            self.log_patch(patch, log, notes)

        return commit_id
Exemple #10
0
 def set_top(self, value, backup = False):
     if backup:
         curr_top = self.get_top()
         self._set_field('top.old', curr_top)
         self._set_field('bottom.old', git.get_commit(curr_top).get_parent())
     self.__update_top_ref(value)
Exemple #11
0
 def get_bottom(self):
     return git.get_commit(self.get_top()).get_parent()
Exemple #12
0
 def __update_top_ref(self, ref):
     git.set_ref(self.__top_ref, ref)
     self._set_field('top', ref)
     self._set_field('bottom', git.get_commit(ref).get_parent())
Exemple #13
0
 def __update_top_ref(self, ref):
     git.set_ref(self.__top_ref, ref)
     self._set_field("top", ref)
     self._set_field("bottom", git.get_commit(ref).get_parent())
Exemple #14
0
 def __get_commit(self):
     if not self.__commit:
         self.__commit = git.get_commit(self.id)
     return self.__commit
Exemple #15
0
    def forward_patches(self, names):
        """Try to fast-forward an array of patches.

        On return, patches in names[0:returned_value] have been pushed on the
        stack. Apply the rest with push_patch
        """
        unapplied = self.get_unapplied()

        forwarded = 0
        top = git.get_head()

        for name in names:
            assert(name in unapplied)

            patch = self.get_patch(name)

            head = top
            bottom = patch.get_bottom()
            top = patch.get_top()

            # top != bottom always since we have a commit for each patch
            if head == bottom:
                # reset the backup information. No logging since the
                # patch hasn't changed
                patch.set_top(top, backup=True)

            else:
                head_tree = git.get_commit(head).get_tree()
                bottom_tree = git.get_commit(bottom).get_tree()
                if head_tree == bottom_tree:
                    # We must just reparent this patch and create a new commit
                    # for it
                    descr = patch.get_description()
                    author_name = patch.get_authname()
                    author_email = patch.get_authemail()
                    author_date = patch.get_authdate()
                    committer_name = patch.get_commname()
                    committer_email = patch.get_commemail()

                    top_tree = git.get_commit(top).get_tree()

                    top = git.commit(
                        message=descr,
                        parents=[head],
                        cache_update=False,
                        tree_id=top_tree,
                        allowempty=True,
                        author_name=author_name,
                        author_email=author_email,
                        author_date=author_date,
                        committer_name=committer_name,
                        committer_email=committer_email,
                    )

                    patch.set_top(top, backup=True)

                    self.log_patch(patch, 'push(f)')
                else:
                    top = head
                    # stop the fast-forwarding, must do a real merge
                    break

            forwarded += 1
            unapplied.remove(name)

        if forwarded == 0:
            return 0

        git.switch(top)

        append_strings(self.__applied_file, names[0:forwarded])
        write_strings(self.__unapplied_file, unapplied)

        return forwarded
Exemple #16
0
    def new_patch(
        self,
        name,
        message=None,
        can_edit=True,
        unapplied=False,
        show_patch=False,
        top=None,
        bottom=None,
        commit=True,
        author_name=None,
        author_email=None,
        author_date=None,
        committer_name=None,
        committer_email=None,
        sign_str=None,
    ):
        """Creates a new patch, either pointing to an existing commit object,
        or by creating a new commit object.
        """

        assert commit or (top and bottom)
        assert (top and bottom) or (not top and not bottom)
        assert commit or (
            not top
            or bottom == git.get_commit(top).get_parent()
        )

        if name is not None:
            self.__patch_name_valid(name)
            if self.patch_exists(name):
                raise StackException('Patch "%s" already exists' % name)

        # TODO: move this out of the stgit.stack module, it is really
        # for higher level commands to handle the user interaction
        def sign(msg):
            return add_sign_line(msg, sign_str,
                                 committer_name or git.committer().name,
                                 committer_email or git.committer().email)
        if not message and can_edit:
            descr = edit_file(
                self, sign(''),
                'Please enter the description for the patch above.',
                show_patch)
        else:
            descr = sign(message)

        head = git.get_head()

        if name is None:
            name = make_patch_name(descr, self.patch_exists)

        patch = self.get_patch(name)
        patch.create()

        patch.set_description(descr)
        patch.set_authname(author_name)
        patch.set_authemail(author_email)
        patch.set_authdate(author_date)
        patch.set_commname(committer_name)
        patch.set_commemail(committer_email)

        if unapplied:
            patches = [patch.get_name()] + self.get_unapplied()
            write_strings(self.__unapplied_file, patches)
            set_head = False
        else:
            append_string(self.__applied_file, patch.get_name())
            set_head = True

        if commit:
            if top:
                top_commit = git.get_commit(top)
            else:
                bottom = head
                top_commit = git.get_commit(head)

            # create a commit for the patch (may be empty if top == bottom);
            # only commit on top of the current branch
            assert(unapplied or bottom == head)
            commit_id = git.commit(
                message=descr,
                parents=[bottom],
                cache_update=False,
                tree_id=top_commit.get_tree(),
                allowempty=True,
                set_head=set_head,
                author_name=author_name,
                author_email=author_email,
                author_date=author_date,
                committer_name=committer_name,
                committer_email=committer_email,
            )
            # set the patch top to the new commit
            patch.set_top(commit_id)
        else:
            patch.set_top(top)

        self.log_patch(patch, 'new')

        return patch
Exemple #17
0
    def refresh_patch(
        self,
        files=None,
        message=None,
        edit=False,
        empty=False,
        show_patch=False,
        cache_update=True,
        author_name=None,
        author_email=None,
        author_date=None,
        committer_name=None,
        committer_email=None,
        backup=True,
        sign_str=None,
        log='refresh',
        notes=None,
        bottom=None
    ):
        """Generates a new commit for the topmost patch
        """
        patch = self.get_current_patch()
        if not patch:
            raise StackException('No patches applied')

        descr = patch.get_description()
        if not (message or descr):
            edit = True
            descr = ''
        elif message:
            descr = message

        # TODO: move this out of the stgit.stack module, it is really
        # for higher level commands to handle the user interaction
        if not message and edit:
            descr = edit_file(
                self,
                descr.rstrip(),
                'Please edit the description for patch "%s" above.' % (
                    patch.get_name(),
                    show_patch,
                )
            )

        if not author_name:
            author_name = patch.get_authname()
        if not author_email:
            author_email = patch.get_authemail()
        if not committer_name:
            committer_name = patch.get_commname()
        if not committer_email:
            committer_email = patch.get_commemail()

        descr = add_sign_line(descr, sign_str, committer_name, committer_email)

        if not bottom:
            bottom = patch.get_bottom()

        if empty:
            tree_id = git.get_commit(bottom).get_tree()
        else:
            tree_id = None

        commit_id = git.commit(
            files=files,
            message=descr,
            parents=[bottom],
            cache_update=cache_update,
            tree_id=tree_id,
            set_head=True,
            allowempty=True,
            author_name=author_name,
            author_email=author_email,
            author_date=author_date,
            committer_name=committer_name,
            committer_email=committer_email
        )

        patch.set_top(commit_id, backup=backup)
        patch.set_description(descr)
        patch.set_authname(author_name)
        patch.set_authemail(author_email)
        patch.set_authdate(author_date)
        patch.set_commname(committer_name)
        patch.set_commemail(committer_email)

        if log:
            self.log_patch(patch, log, notes)

        return commit_id
Exemple #18
0
 def get_bottom(self):
     return git.get_commit(self.get_top()).get_parent()
Exemple #19
0
 def __update_top_ref(self, ref):
     git.set_ref(self.__top_ref, ref)
     self._set_field('top', ref)
     self._set_field('bottom', git.get_commit(ref).get_parent())
Exemple #20
0
    def forward_patches(self, names):
        """Try to fast-forward an array of patches.

        On return, patches in names[0:returned_value] have been pushed on the
        stack. Apply the rest with push_patch
        """
        unapplied = self.get_unapplied()

        forwarded = 0
        top = git.get_head()

        for name in names:
            assert(name in unapplied)

            patch = self.get_patch(name)

            head = top
            bottom = patch.get_bottom()
            top = patch.get_top()

            # top != bottom always since we have a commit for each patch
            if head == bottom:
                # reset the backup information. No logging since the
                # patch hasn't changed
                patch.set_top(top, backup = True)

            else:
                head_tree = git.get_commit(head).get_tree()
                bottom_tree = git.get_commit(bottom).get_tree()
                if head_tree == bottom_tree:
                    # We must just reparent this patch and create a new commit
                    # for it
                    descr = patch.get_description()
                    author_name = patch.get_authname()
                    author_email = patch.get_authemail()
                    author_date = patch.get_authdate()
                    committer_name = patch.get_commname()
                    committer_email = patch.get_commemail()

                    top_tree = git.get_commit(top).get_tree()

                    top = git.commit(message = descr, parents = [head],
                                     cache_update = False,
                                     tree_id = top_tree,
                                     allowempty = True,
                                     author_name = author_name,
                                     author_email = author_email,
                                     author_date = author_date,
                                     committer_name = committer_name,
                                     committer_email = committer_email)

                    patch.set_top(top, backup = True)

                    self.log_patch(patch, 'push(f)')
                else:
                    top = head
                    # stop the fast-forwarding, must do a real merge
                    break

            forwarded+=1
            unapplied.remove(name)

        if forwarded == 0:
            return 0

        git.switch(top)

        append_strings(self.__applied_file, names[0:forwarded])
        write_strings(self.__unapplied_file, unapplied)

        return forwarded
Exemple #21
0
def func(parser, options, args):
    """Repair inconsistencies in StGit metadata."""

    orig_applied = crt_series.get_applied()
    orig_unapplied = crt_series.get_unapplied()
    orig_hidden = crt_series.get_hidden()

    if crt_series.get_protected():
        raise CmdException(
            'This branch is protected. Modification is not permitted.')

    # Find commits that aren't patches, and applied patches.
    head = git.get_commit(git.get_head()).get_id_hash()
    commits, patches = read_commit_dag(crt_series.get_name())
    c = commits[head]
    patchify = []  # commits to definitely patchify
    maybe_patchify = []  # commits to patchify if we find a patch below them
    applied = []
    while len(c.parents) == 1:
        parent, = c.parents
        if c.patch:
            applied.append(c)
            patchify.extend(maybe_patchify)
            maybe_patchify = []
        else:
            maybe_patchify.append(c)
        c = parent
    applied.reverse()
    patchify.reverse()

    # Find patches hidden behind a merge.
    merge = c
    todo = set([c])
    seen = set()
    hidden = set()
    while todo:
        c = todo.pop()
        seen.add(c)
        todo |= c.parents - seen
        if c.patch:
            hidden.add(c)
    if hidden:
        out.warn(('%d patch%s are hidden below the merge commit' %
                  (len(hidden), ['es', ''][len(hidden) == 1])),
                 '%s,' % merge.id, 'and will be considered unapplied.')

    # Make patches of any linear sequence of commits on top of a patch.
    names = set(p.patch for p in patches)

    def name_taken(name):
        return name in names

    if applied and patchify:
        out.start('Creating %d new patch%s' %
                  (len(patchify), ['es', ''][len(patchify) == 1]))
        for p in patchify:
            name = make_patch_name(p.commit.get_log(), name_taken)
            out.info('Creating patch %s from commit %s' % (name, p.id))
            aname, amail, adate = name_email_date(p.commit.get_author())
            cname, cmail, cdate = name_email_date(p.commit.get_committer())
            parent, = p.parents
            crt_series.new_patch(
                name,
                can_edit=False,
                commit=False,
                top=p.id,
                bottom=parent.id,
                message=p.commit.get_log(),
                author_name=aname,
                author_email=amail,
                author_date=adate,
                committer_name=cname,
                committer_email=cmail,
            )
            p.patch = name
            applied.append(p)
            names.add(name)
        out.done()

    # Figure out hidden
    orig_patches = orig_applied + orig_unapplied + orig_hidden
    orig_applied_name_set = set(orig_applied)
    orig_unapplied_name_set = set(orig_unapplied)
    orig_hidden_name_set = set(orig_hidden)
    orig_patches_name_set = set(orig_patches)
    hidden = [p for p in patches if p.patch in orig_hidden_name_set]

    # Write the applied/unapplied files.
    out.start('Checking patch appliedness')
    unapplied = patches - set(applied) - set(hidden)
    applied_name_set = set(p.patch for p in applied)
    unapplied_name_set = set(p.patch for p in unapplied)
    hidden_name_set = set(p.patch for p in hidden)
    patches_name_set = set(p.patch for p in patches)
    for name in orig_patches_name_set - patches_name_set:
        out.info('%s is gone' % name)
    for name in applied_name_set - orig_applied_name_set:
        out.info('%s is now applied' % name)
    for name in unapplied_name_set - orig_unapplied_name_set:
        out.info('%s is now unapplied' % name)
    for name in hidden_name_set - orig_hidden_name_set:
        out.info('%s is now hidden' % name)
    orig_order = dict(zip(orig_patches, range(len(orig_patches))))

    def patchname_key(p):
        i = orig_order.get(p, len(orig_order))
        return i, p

    crt_series.set_applied(p.patch for p in applied)
    crt_series.set_unapplied(sorted(unapplied_name_set, key=patchname_key))
    crt_series.set_hidden(sorted(hidden_name_set, key=patchname_key))
    out.done()
Exemple #22
0
 def __get_commit(self):
     if not self.__commit:
         self.__commit = git.get_commit(self.id)
     return self.__commit
Exemple #23
0
def func(parser, options, args):
    """Repair inconsistencies in StGit metadata."""

    orig_applied = crt_series.get_applied()
    orig_unapplied = crt_series.get_unapplied()
    orig_hidden = crt_series.get_hidden()

    if crt_series.get_protected():
        raise CmdException(
            'This branch is protected. Modification is not permitted.')

    # Find commits that aren't patches, and applied patches.
    head = git.get_commit(git.get_head()).get_id_hash()
    commits, patches = read_commit_dag(crt_series.get_name())
    c = commits[head]
    patchify = []       # commits to definitely patchify
    maybe_patchify = [] # commits to patchify if we find a patch below them
    applied = []
    while len(c.parents) == 1:
        parent, = c.parents
        if c.patch:
            applied.append(c)
            patchify.extend(maybe_patchify)
            maybe_patchify = []
        else:
            maybe_patchify.append(c)
        c = parent
    applied.reverse()
    patchify.reverse()

    # Find patches hidden behind a merge.
    merge = c
    todo = set([c])
    seen = set()
    hidden = set()
    while todo:
        c = todo.pop()
        seen.add(c)
        todo |= c.parents - seen
        if c.patch:
            hidden.add(c)
    if hidden:
        out.warn(('%d patch%s are hidden below the merge commit'
                  % (len(hidden), ['es', ''][len(hidden) == 1])),
                 '%s,' % merge.id, 'and will be considered unapplied.')

    # Make patches of any linear sequence of commits on top of a patch.
    names = set(p.patch for p in patches)
    def name_taken(name):
        return name in names
    if applied and patchify:
        out.start('Creating %d new patch%s'
                  % (len(patchify), ['es', ''][len(patchify) == 1]))
        for p in patchify:
            name = make_patch_name(p.commit.get_log(), name_taken)
            out.info('Creating patch %s from commit %s' % (name, p.id))
            aname, amail, adate = name_email_date(p.commit.get_author())
            cname, cmail, cdate = name_email_date(p.commit.get_committer())
            parent, = p.parents
            crt_series.new_patch(
                name, can_edit = False, commit = False,
                top = p.id, bottom = parent.id, message = p.commit.get_log(),
                author_name = aname, author_email = amail, author_date = adate,
                committer_name = cname, committer_email = cmail)
            p.patch = name
            applied.append(p)
            names.add(name)
        out.done()

    # Figure out hidden
    orig_patches = orig_applied + orig_unapplied + orig_hidden
    orig_applied_name_set = set(orig_applied)
    orig_unapplied_name_set = set(orig_unapplied)
    orig_hidden_name_set = set(orig_hidden)
    orig_patches_name_set = set(orig_patches)
    hidden = [p for p in patches if p.patch in orig_hidden_name_set]

    # Write the applied/unapplied files.
    out.start('Checking patch appliedness')
    unapplied = patches - set(applied) - set(hidden)
    applied_name_set = set(p.patch for p in applied)
    unapplied_name_set = set(p.patch for p in unapplied)
    hidden_name_set = set(p.patch for p in hidden)
    patches_name_set = set(p.patch for p in patches)
    for name in orig_patches_name_set - patches_name_set:
        out.info('%s is gone' % name)
    for name in applied_name_set - orig_applied_name_set:
        out.info('%s is now applied' % name)
    for name in unapplied_name_set - orig_unapplied_name_set:
        out.info('%s is now unapplied' % name)
    for name in hidden_name_set - orig_hidden_name_set:
        out.info('%s is now hidden' % name)
    orig_order = dict(zip(orig_patches, xrange(len(orig_patches))))
    def patchname_cmp(p1, p2):
        i1 = orig_order.get(p1, len(orig_order))
        i2 = orig_order.get(p2, len(orig_order))
        return cmp((i1, p1), (i2, p2))
    crt_series.set_applied(p.patch for p in applied)
    crt_series.set_unapplied(sorted(unapplied_name_set, cmp = patchname_cmp))
    crt_series.set_hidden(sorted(hidden_name_set, cmp = patchname_cmp))
    out.done()
Exemple #24
0
    def new_patch(self,
                  name,
                  message=None,
                  can_edit=True,
                  unapplied=False,
                  show_patch=False,
                  top=None,
                  bottom=None,
                  commit=True,
                  author_name=None,
                  author_email=None,
                  author_date=None,
                  committer_name=None,
                  committer_email=None,
                  before_existing=False):
        """Creates a new patch
        """

        if name != None:
            self.__patch_name_valid(name)
            if self.patch_exists(name):
                raise StackException, 'Patch "%s" already exists' % name

        if not message and can_edit:
            descr = edit_file(
                self, None,
                'Please enter the description for the patch above.',
                show_patch)
        else:
            descr = message

        head = git.get_head()

        if name == None:
            name = make_patch_name(descr, self.patch_exists)

        patch = Patch(name, self.__patch_dir, self.__refs_dir)
        patch.create()

        if not bottom:
            bottom = head
        if not top:
            top = head

        patch.set_bottom(bottom)
        patch.set_top(top)
        patch.set_description(descr)
        patch.set_authname(author_name)
        patch.set_authemail(author_email)
        patch.set_authdate(author_date)
        patch.set_commname(committer_name)
        patch.set_commemail(committer_email)

        if before_existing:
            insert_string(self.__applied_file, patch.get_name())
            # no need to commit anything as the object is already
            # present (mainly used by 'uncommit')
            commit = False
        elif unapplied:
            patches = [patch.get_name()] + self.get_unapplied()
            write_strings(self.__unapplied_file, patches)
            set_head = False
        else:
            append_string(self.__applied_file, patch.get_name())
            set_head = True

        if commit:
            # create a commit for the patch (may be empty if top == bottom);
            # only commit on top of the current branch
            assert (unapplied or bottom == head)
            top_commit = git.get_commit(top)
            commit_id = git.commit(message=descr,
                                   parents=[bottom],
                                   cache_update=False,
                                   tree_id=top_commit.get_tree(),
                                   allowempty=True,
                                   set_head=set_head,
                                   author_name=author_name,
                                   author_email=author_email,
                                   author_date=author_date,
                                   committer_name=committer_name,
                                   committer_email=committer_email)
            # set the patch top to the new commit
            patch.set_top(commit_id)

        self.log_patch(patch, 'new')

        return patch