Beispiel #1
0
    def decide(self, row):
        """Main entry point for RowDecider."""
        self._reset()
        self.row = row

        self._raise_if_symlink_in_gdest_path()

        self.gdest_cell      = row.cell_if_col(self.m._gdest_column)
        self.p4jitfp_cell    = row.cell_if_col(self.m._p4jitfp_column)
        self.git_delta_cell  = row.cell_if_col(self.m.git_delta_column)
        self.integ_dest_cell = row.cell_if_col(self.m._integ_dest_column)

        if LOG.isEnabledFor(logging.DEBUG2):
            LOG.debug2('RowDecider.decide() GDEST={gdest}'
                       ' P4JITFP={p4jitfp} git_delta={git_delta}'
                       ' pop_fm={pop_fm} row={row}'
                       .format(
                          row       = row.gwt_path
                        , gdest     = self._col_index(self.m._gdest_column)
                        , pop_fm    = self.m._populate_from_column.index
                                      if self.m._populate_from_column else None
                        , p4jitfp   = self._col_index(self.m._p4jitfp_column)
                        , git_delta = self._col_index(self.m.git_delta_column)
                        ))

        self._decide_populate_from(exists_in_git = self.row.exists_in_git())

                        # Git LFS file? Then it's too complex to try to integ
                        # it from any GPAR(fp)N at the same time we integ from
                        # LFS de-dupe storage. Ignore/bypass GPAR(fp)N. The
                        # only integ we might decide() for an LFS file is a JIT
                        # integ from P4JITFP.
        lfs_oid = None
        if self.m.ctx.is_lfs_enabled:
            lfs_oid = _lfs_oid(self.gdest_cell)
        if not lfs_oid:
                        # Integrate from each Git parent commit, and possibly
                        # from any fully populated bases for those parent
                        # commits. Using a queue so that we can add GPARFPN
                        # columns later if we need to.
            self.integ_work_queue = \
                deque(col for col in Column.of_col_type( self.m.columns, Column.GPARN))
            common.debug3('Row.decide() initial integ_work_queue={col}'
                          , col=[col.index for col in self.integ_work_queue])
            while self.integ_work_queue:
                column = self.integ_work_queue.pop()    # GPARN or GPARFPN
                self._decide_integ_from_column(column)

        self._decide_gdest_have_delta()

                        # Git says Add/Modify/Delete? Do so.
        if not self.has_integ:
                        # Apply Git's requested add/edit/delete.
            self._apply_git_delta()

                        # If add/edit/deleting, do we also need to JIT-branch?
            self._decide_jit_if()

        self._remove_duplicate_integ()
Beispiel #2
0
    def decide(self, row):
        '''
        Main entry point for RowDecider.
        '''
        self._reset()
        self.row = row

        self._raise_if_symlink_in_gdest_path()

        self.gdest_cell      = row.cell_if_col(self.m._gdest_column)
        self.p4jitfp_cell    = row.cell_if_col(self.m._p4jitfp_column)
        self.git_delta_cell  = row.cell_if_col(self.m.git_delta_column)
        self.integ_dest_cell = row.cell_if_col(self.m._integ_dest_column)

        if LOG.isEnabledFor(logging.DEBUG2):
            LOG.debug2('RowDecider.decide() GDEST={gdest}'
                       ' P4JITFP={p4jitfp} git_delta={git_delta}'
                       ' pop_fm={pop_fm} row={row}'
                       .format(
                          row       = row.gwt_path
                        , gdest     = self._col_index(self.m._gdest_column)
                        , pop_fm    = self.m._populate_from_column.index
                                      if self.m._populate_from_column else None
                        , p4jitfp   = self._col_index(self.m._p4jitfp_column)
                        , git_delta = self._col_index(self.m.git_delta_column)
                        ))

        self._decide_populate_from(exists_in_git = self.row.exists_in_git())

                        # Integrate from each Git parent commit, and possibly
                        # from any fully populated bases for those parent
                        # commits. Using a queue so that we can add GPARFPN
                        # columns later if we need to.
        self.integ_work_queue = \
                deque(col for col in Column.of_col_type( self.m.columns
                                                       , Column.GPARN))
        common.debug3('Row.decide() initial integ_work_queue={col}'
                , col=[col.index for col in self.integ_work_queue])
        while self.integ_work_queue:
            column = self.integ_work_queue.pop()    # GPARN or GPARFPN
            self._decide_integ_from_column(column)

                        # Git says Add/Modify/Delete? Do so.
        if self.git_delta_cell and self.git_delta_cell.discovered:
                        # Apply Git's requested add/edit/delete.
            self._apply_git_delta()

                        # If add/edit/deleting, do we also need to JIT-branch?
            self._decide_jit_if()

        self._remove_duplicate_integ()
Beispiel #3
0
    def _decide_populate_from(self, exists_in_git):
        """If discovery marked a column for "populate this branch from this
        column", do so.

        Infrequent. Occurs only on first changelist on a new Perforce branch.
        """
        if not self._want_populate_from(exists_in_git):
            return

        # Yep, we can indeed populate this row's GWT from the
        # populate_from column.
        common.debug3( '_decide_populate_from() col={col} integ -Rbd resolve -at'
               , col=self.m._populate_from_column.index )
        self.populate_from_cell.decided \
                                    = Decided( integ_flags      = '-Rbd'
                                             , resolve_flags    = '-at'
                                             , on_integ_failure = Decided.RAISE
                                             , integ_input      = 'populate_from')
        self.has_integ = True
Beispiel #4
0
    def _after_dest_fp(self, src_cell):
        """Does src_cell integrate from a Perforce file revision at or after our
        destination's fully populated basis?

        Always True if destination has no basis.
        """
        assert src_cell
        # Even if lightweight, if we have no basis, then all revisions
        # are permitted as integration sources.
        if not self.p4jitfp_cell:
            common.debug3('_after_dest_fp() True no FP basis. ')
            return True

        # Integrating from some Perforce path other than our basis?
        # Go right ahead.
        fp  = self.p4jitfp_cell.discovered    # for less typing
        src = src_cell       .discovered
        fp_from_file  = _from_depot_file(fp)
        src_from_file = _from_depot_file(src)
        if (   (not fp_from_file)
            or (not src_from_file)
            or fp_from_file != src_from_file):
            common.debug3('_after_dest_fp() True src={src} not from'
                    ' JIT FP basis={fp}. '
                    .format(src=src_from_file
                           ,fp =fp_from_file))
            return True

        # Integrating a later revision of our basis?
        fp_rev  = self.p4jitfp_cell.rev()
        src_rev = src_cell.rev()
        if ( not fp_rev
             or not src_rev
             or int(fp_rev) < int(src_rev)):
            common.debug3('_after_dest_fp() True src={src} > '
                    ' JIT FP basis={fp}. '
                    .format( src=src_rev
                           , fp =fp_rev))
            return True

        # Sorry, you're trying to integrate from a source revision that is at
        # or before lightweight GDEST's fully populated basis P4JITFP's revision
        # for this file. Can't do that: that would make our lightweight branch
        # unnecessarily heavyweight.
        common.debug3('_after_dest_fp() False src={src_d}#{src_r} <= '
                ' JIT FP basis={fp_d}#{fp_r}. '
                .format( src_d=src.get('depotFile')
                       , src_r=src_rev
                       , fp_d =fp .get('depotFile')
                       , fp_r =fp_rev
                       ))
        return False
    def _decide_populate_from(self, exists_in_git):
        '''
        If discovery marked a column for "populate this branch from this
        column", do so.

        Infrequent. Occurs only on first changelist on a new Perforce branch.
        '''
        if not self._want_populate_from(exists_in_git):
            return

        # Yep, we can indeed populate this row's GWT from the
        # populate_from column.
        common.debug3( '_decide_populate_from() col={col} integ -Rbd resolve -at'
               , col=self.m._populate_from_column.index )
        self.populate_from_cell.decided \
                                    = Decided( integ_flags      = '-Rbd'
                                             , resolve_flags    = '-at'
                                             , on_integ_failure = Decided.RAISE
                                             , integ_input      = 'populate_from')
        self.has_integ = True
    def _after_dest_fp(self, src_cell):
        '''
        Does src_cell integrate from a Perforce file revision at or after our
        destination's fully populated basis?

        Always True if destination has no basis.
        '''
        assert src_cell
        # Even if lightweight, if we have no basis, then all revisions
        # are permitted as integration sources.
        if not self.p4jitfp_cell:
            common.debug3('_after_dest_fp() True no FP basis. ')
            return True

        # Integrating from some Perforce path other than our basis?
        # Go right ahead.
        fp  = self.p4jitfp_cell.discovered    # for less typing
        src = src_cell       .discovered
        fp_from_file  = _from_depot_file(fp)
        src_from_file = _from_depot_file(src)
        if (   (not fp_from_file)
            or (not src_from_file)
            or fp_from_file != src_from_file):
            common.debug3('_after_dest_fp() True src={src} not from'
                    ' JIT FP basis={fp}. '
                    .format(src=src_from_file
                           ,fp =fp_from_file))
            return True

        # Integrating a later revision of our basis?
        if ( 'rev' not in fp
             or 'rev' not in src
             or int(fp['rev']) < int(src['rev'])):
            common.debug3('_after_dest_fp() True src={src} > '
                    ' JIT FP basis={fp}. '
                    .format( src=src.get('rev')
                           , fp =fp .get('rev')))
            return True

        # Sorry, you're trying to integrate from a source revision that is at
        # or before lightweight GDEST's fully populated basis P4JITFP's revision
        # for this file. Can't do that: that would make our lightweight branch
        # unnecessarily heavyweight.
        common.debug3('_after_dest_fp() True src={src_d}#{src_r} <= '
                ' JIT FP basis={fp_d}#{fp_d}. '
                .format( src_d=src.get('depotFile')
                       , src_r=src.get('rev')
                       , fp_d =fp .get('depotFile')
                       , fp_r =fp .get('rev')
                       ))
        return False
Beispiel #7
0
    def _decide_jit_if(self):
        '''
        Branch file from fully populated basis if necessary and possible.

        This code runs after any _decide_integ_from_column() and
        _apply_git_delta(). Prefer p4 'branch' file actions from actual Git
        parent branches over JIT branch file actions.

        Store the JIT action we WOULD run if we later decide JIT is required.

        ### BUG: integ matrix prohibits JIT-for-delete, but we
                 require JIT-for-delete. Make JIT-for-delete work.
        '''

        # Not worth attempting to JIT-branch a file that we've already decided
        # to integrate from at least one other branch. We've got files and
        # content coming from other integ source branches, and that's good
        # enough to hold the incoming commit's results.
        if self.has_integ:
            common.debug3('_decide_jit_if() no: has_integ')
            return

        # If we've got no Git action to apply, there's no reason to
        # JIT-branch this file.
        if not (    self.git_delta_cell
                and self.git_delta_cell.decided ):
            common.debug3('_decide_jit_if() no: no git delta from prev commit')
            return

        # No need to JIT-branch a file that already exists in destination.
        if common.gdest_cell_exists_in_p4(self.gdest_cell):
            common.debug3('_decide_jit_if() no: no already exists in p4 destination')
            return

        # Cannot JIT-branch a file that has no source.
        if not self._p4jitfp_exists():
            common.debug3('_decide_jit_if() no: does not exist in JIT FP basis')
            return


        if self.m.symlink_in_path(self.row.gwt_path):
            common.debug3('_decide_jit_if() no: symlink in path')
            return

        # Look up the correct integ, resolve, and fallback action
        # to take for this file. Sometimes is "do nothing" and that's okay.
        ri = p4gf_g2p_matrix_integ.to_input( row             = self.row
                                           , integ_src_cell  = self.p4jitfp_cell
                                           , integ_dest_cell = self.integ_dest_cell
                                           , git_delta_cell  = self.git_delta_cell
                                           , gdest_cell      = self.gdest_cell )
        r = p4gf_g2p_matrix_integ.find_row(ri)
        if (   not r
            or not ((r.integ_flags != None) or r.fallback) ):
            common.debug3('_decide_jit_if() no: integ matrix returned'
                    ' no integ or fallback')
            return

        self.p4jitfp_cell.decided = Decided.from_integ_matrix_row(r, ri)
        common.debug3('_decide_jit_if() JIT: {}'.format(self.p4jitfp_cell.decided))
        if r.integ_flags != None:
            self.has_integ = True
Beispiel #8
0
    def _decide_integ_from_column(self, column):
        '''
        If this column has an integration source that we _discover_branches()
        decide should be used, decide how.

        If column is GPARN with nothing to integrate, but backed by a GPARFPN,
        schedule GPARFP as next work_queue item so that we can check to see
        if it holds something to integrate.

        This code runs before _decide_jit_if(). Prefer p4 'branch' file
        actions from actual Git parent branches over JIT branch file actions.
        '''
        common.debug3('Row._decide_integ_from_column() {col}', col=column.index)
        # Skip any already-integrated population source
        if column is self.m._populate_from_column:
            common.debug3( 'Row._decide_integ_from_column() {col} == pop_fm. Skipping.'
                   , col=column.index )
            return
        # Or from our destination branch.
        if column.branch == self.m.current_branch:
            common.debug3( 'Row._decide_integ_from_column() {col} == curr. Skipping.'
                   , col=column.index )
            return

        # Nothing to integ?
        src_cell = self.row.cells[column.index]
        if not src_cell or not src_cell.discovered:
            # Even if nothing to integ from GPARN, check its GPARFPN
            # basis for something to integ. If so, prepend to work queue
            # to check for something to integ.
            if (    column.col_type == Column.GPARN
                and column.fp_counterpart
                and self.row.cells[column.fp_counterpart.index]):
                self.integ_work_queue.appendleft(column.fp_counterpart)
            common.debug3( 'Row._decide_integ_from_column() {col} no src disc.'
                     ' Skipping.'
                    , col=column.index )
            return

        # Nothing to integ.
        #
        # 'endFromRev'-not-'rev' OK here, indicates _some_ integ action
        # that 'rev' does not.
        if not 'endFromRev' in src_cell.discovered:
            common.debug3( 'Row._decide_integ_from_column() {col} no src endFromRev.'
                     ' Skipping.'
                    , col=column.index )
            return

        # Don't integ duplicate branch or delete actions.
        cur_is = _p4_branch_or_delete(src_cell.discovered)
        if self.is_integ_branch_or_delete and cur_is:
            common.debug3( 'Row._decide_integ_from_column() {col}'
                     ' double-delete/branch. Skipping.'
                   , col=column.index )
            return
        # Or integs that treat a symlink file as a directory.
        if self.m.symlink_in_path(self.row.gwt_path):
            common.debug3( 'Row._decide_integ_from_column() {col} symlink ancestor.'
                     ' Skipping.'
                    , col=column.index)
            return
        # Or source file revisions at or before the destination's
        # fully populated basis
        # common.debug3('### Row._decide_integ_from_column() {col}'
        #         ' checking FP basis...', col=column.index)
        if not self._after_dest_fp(src_cell):
            common.debug3( 'Row._decide_integ_from_column() {col} Not after'
                     ' JIT basis. Skipping.'
                    , col=column.index )
            return

        # Look up the correct integ, resolve, and fallback action
        # to take for this file. Sometimes is "do nothing" and that's okay.
        # common.debug3('### Row._decide_integ_from_column() {col}'
        #         ' checking integ matrix...', col=column.index)
        ri = p4gf_g2p_matrix_integ.to_input( row             = self.row
                                           , integ_src_cell  = src_cell
                                           , integ_dest_cell = self.integ_dest_cell
                                           , git_delta_cell  = self.git_delta_cell
                                           , gdest_cell      = self.gdest_cell )
        r = p4gf_g2p_matrix_integ.find_row(ri)
        if (   not r
            or not ((r.integ_flags != None) or r.fallback) ):
            common.debug3('Row._decide_integ_from_column() {col} {ri} matrix returned'
                    ' no action. Skipping.'
                   , col=column.index
                   , ri = p4gf_g2p_matrix_integ.deb(ri)
                   )
            return

        # I don't _think_ there's a way to gt here on a column we already
        # decided (such as self._poulate_from_column). assert() to be sure.
        assert not src_cell.decided

        src_cell.decided = Decided.from_integ_matrix_row(r, ri)

        common.debug3( 'Row._decide_integ_from_column() col={col} decided={decided}'
               , col     = column.index
               , decided = src_cell.decided )

        if r.integ_flags != None:
            self.has_integ = True
            self.is_integ_branch_or_delete |= cur_is
Beispiel #9
0
    def _decide_integ_from_column(self, column):
        """If this column has an integration source that we _discover_branches()
        decide should be used, decide how.

        If column is GPARN with nothing to integrate, but backed by a GPARFPN,
        schedule GPARFP as next work_queue item so that we can check to see
        if it holds something to integrate.

        This code runs before _decide_jit_if(). Prefer p4 'branch' file
        actions from actual Git parent branches over JIT branch file actions.
        """
        common.debug3('Row._decide_integ_from_column() {col}', col=column.index)
        # Skip any already-integrated population source
        if column is self.m._populate_from_column:
            common.debug3( 'Row._decide_integ_from_column() {col} == pop_fm. Skipping.'
                   , col=column.index )
            return
        # Or from our destination branch.
        if column.branch == self.m.current_branch:
            common.debug3( 'Row._decide_integ_from_column() {col} == curr. Skipping.'
                   , col=column.index )
            return

        # Nothing to integ?
        src_cell = self.row.cells[column.index]
        if not src_cell or not src_cell.discovered:
            # Even if nothing to integ from GPARN, check its GPARFPN
            # basis for something to integ. If so, prepend to work queue
            # to check for something to integ.
            if (    column.col_type == Column.GPARN
                and column.fp_counterpart
                and self.row.cells[column.fp_counterpart.index]):
                self.integ_work_queue.appendleft(column.fp_counterpart)
            common.debug3( 'Row._decide_integ_from_column() {col} no src disc.'
                     ' Skipping.'
                    , col=column.index )
            return

        # Skip integ sources that do not exist at all.
        if 'depotFile' not in src_cell.discovered:
            common.debug3('Row._decide_integ_from_column() {col} no src depotFile.'
                          ' Skipping.'
                         , col=column.index )
            return

        # Don't integ duplicate branch or delete actions.
        ### Zig warns: now that we 'p4 fstat' instead of 'p4 integ', a 'branch'
        ### action here means "head action on source depot file is 'branch'"
        ### not "want to branch file from source to dest. This use of
        ### _p4_branch_or_delete() no longer defends against multiple branch
        ### actions in a single dest file, single changelist. This appears to
        ### be okay, because we've never seen multiple branch actions cause a
        ### problem. It's only multiple deletes that fail.
        cur_is = _p4_branch_or_delete(src_cell.discovered)
        if self.is_integ_branch_or_delete and cur_is:
            common.debug3( 'Row._decide_integ_from_column() {col}'
                     ' double-delete/branch. Skipping.'
                   , col=column.index )
            return
        # Or integs that treat a symlink file as a directory.
        if self.m.symlink_in_path(self.row.gwt_path):
            common.debug3( 'Row._decide_integ_from_column() {col} symlink ancestor.'
                     ' Skipping.'
                    , col=column.index)
            return
        # Or source file revisions at or before the destination's
        # fully populated basis
        # common.debug3('### Row._decide_integ_from_column() {col}'
        #         ' checking FP basis...', col=column.index)
        if not self._after_dest_fp(src_cell):
            common.debug3( 'Row._decide_integ_from_column() {col} Not after'
                     ' JIT basis. Skipping.'
                    , col=column.index )
            return

        # Look up the correct integ, resolve, and fallback action
        # to take for this file. Sometimes is "do nothing" and that's okay.
        # common.debug3('### Row._decide_integ_from_column() {col}'
        #         ' checking integ matrix...', col=column.index)
        ri = p4gf_g2p_matrix_integ.to_input( row             = self.row
                                           , integ_src_cell  = src_cell
                                           , integ_dest_cell = self.integ_dest_cell
                                           , git_delta_cell  = self.git_delta_cell
                                           , gdest_cell      = self.gdest_cell )
        r = p4gf_g2p_matrix_integ.find_row(ri)
        if (   not r
            or not ((r.integ_flags is not None) or r.fallback) ):
            common.debug3('Row._decide_integ_from_column() {col} {ri} matrix returned'
                    ' no action. Skipping.'
                   , col=column.index
                   , ri = p4gf_g2p_matrix_integ.deb(ri)
                   )
            return

        # I don't _think_ there's a way to get here on a column we already
        # decided (such as self._poulate_from_column). assert() to be sure.
        assert not (src_cell.decided and src_cell.decided.has_p4_action())

        src_cell.decided = Decided.from_integ_matrix_row(r, ri)

        common.debug3( 'Row._decide_integ_from_column() col={col} decided={decided}'
               , col     = column.index
               , decided = src_cell.decided )

        if r.integ_flags is not None:
            self.has_integ = True
            self.is_integ_branch_or_delete |= cur_is