Exemple #1
0
 def get_redirect_url(self, maintplan_name):
     maintplan = get_object_or_404(MaintenancePlan, name=maintplan_name)
     release = Release.get_current(maintplan)
     if not release:
         raise Exception('No releases defined for maintenance plan %s' %
                         maintplan.name)
     milestone = Milestone.get_current(release)
     if not milestone:
         raise Exception('No milestones defined for release %s' %
                         release.name)
     return reverse('rrs_recipes',
                    args=(maintplan.name, release.name, milestone.name))
Exemple #2
0
 def get_redirect_url(self):
     maintplan = MaintenancePlan.objects.first()
     if not maintplan:
         raise Exception('No maintenance plans defined')
     release = Release.get_current(maintplan)
     if not release:
         raise Exception('No releases defined for maintenance plan %s' %
                         maintplan.name)
     milestone = Milestone.get_current(release)
     if not milestone:
         raise Exception('No milestones defined for release %s' %
                         release.name)
     return reverse('rrs_recipes',
                    args=(maintplan.name, release.name, milestone.name))
Exemple #3
0
 def save_model(self, request, obj, form, change):
     # For new maintenance plans, copy releases from the first plan
     if obj.pk is None:
         copyfrom_mp = MaintenancePlan.objects.all().first()
     else:
         copyfrom_mp = None
     super().save_model(request, obj, form, change)
     if copyfrom_mp:
         for release in copyfrom_mp.release_set.all():
             release.pk = None
             release.plan = obj
             release.save()
             milestone = Milestone(release=release)
             milestone.name = 'Default'
             milestone.start_date = release.start_date
             milestone.end_date = release.end_date
             milestone.save()
Exemple #4
0
    def get_context_data(self, **kwargs):
        context = super(MaintainerListView, self).get_context_data(**kwargs)

        context['this_url_name'] = resolve(self.request.path_info).url_name

        context['all_maintplans'] = MaintenancePlan.objects.all()
        context['maintplan_name'] = self.maintplan_name
        maintplan = get_object_or_404(MaintenancePlan,
                                      name=self.maintplan_name)
        context['release_name'] = self.release_name
        context['all_releases'] = Release.objects.filter(
            plan=maintplan).order_by('-end_date')
        context['milestone_name'] = self.milestone_name
        context['all_milestones'] = Milestone.get_by_release_name(
            maintplan, self.release_name)

        context['recipes_percentage'] = self.milestone_statistics['percentage']
        context['recipes_all_upgraded'] = self.milestone_statistics[
            'all_upgraded']
        context['recipes_all_not_upgraded'] = self.milestone_statistics[
            'all_not_upgraded']
        context['recipes_up_to_date'] = self.milestone_statistics['up_to_date']
        context['recipes_not_updated'] = self.milestone_statistics[
            'not_updated']
        context['recipes_cant_be_updated'] = self.milestone_statistics[
            'cant_be_updated']
        context['recipes_unknown'] = self.milestone_statistics['unknown']
        context['recipes_percentage_up_to_date'] = \
            self.milestone_statistics['percentage_up_to_date']
        context['recipes_percentage_not_updated'] = \
            self.milestone_statistics['percentage_not_updated']
        context['recipes_percentage_cant_be_updated'] = \
            self.milestone_statistics['percentage_cant_be_updated']
        context['recipes_percentage_unknown'] = \
            self.milestone_statistics['percentage_unknown']

        context['maintainer_count'] = self.maintainer_count
        context['intervals'] = self.intervals
        context['interval_range'] = range(len(self.intervals))
        if hasattr(self, 'current_interval'):
            context['current_interval'] = self.current_interval

        return context
Exemple #5
0
def _get_recipe_upgrade_detail(maintplan, recipe_upgrade):
    release_name = ''
    milestone_name = ''
    recipe_maintainer_history = None

    release = Release.get_by_date(maintplan, recipe_upgrade.commit_date)
    if release:
        release_name = release.name
        milestone = Milestone.get_by_release_and_date(
            release, recipe_upgrade.commit_date)
        if milestone:
            milestone_name = milestone.name
            recipe_maintainer_history = RecipeMaintainerHistory.get_by_end_date(
                recipe_upgrade.recipesymbol.layerbranch, milestone.end_date)

    is_recipe_maintainer = False
    maintainer_name = ''
    if not recipe_upgrade.maintainer is None:
        maintainer_name = recipe_upgrade.maintainer.name

        if not recipe_maintainer_history is None and \
            RecipeMaintainer.objects.filter(maintainer__name
            = maintainer_name, history = recipe_maintainer_history) \
            .count() > 0:
            is_recipe_maintainer = True

    commit_date = recipe_upgrade.commit_date.date().isoformat()
    commit = recipe_upgrade.sha1[:10]
    commit_url = recipe_upgrade.recipesymbol.layerbranch.commit_url(
        recipe_upgrade.sha1)

    rud = RecipeUpgradeDetail(recipe_upgrade.title, recipe_upgrade.version, \
            maintplan.name, release_name, milestone_name, commit_date, maintainer_name, \
            is_recipe_maintainer, commit, commit_url, recipe_upgrade.upgrade_type, \
            recipe_upgrade.group)

    return rud
def upgrade_history(options, logger):
    from rrs.models import MaintenancePlan, RecipeUpgrade, Release, Milestone

    if options.plan:
        maintplans = MaintenancePlan.objects.filter(id=int(options.plan))
        if not maintplans.exists():
            logger.error('No maintenance plan with ID %s found' % options.plan)
            sys.exit(1)
    else:
        maintplans = MaintenancePlan.objects.filter(updates_enabled=True)
        if not maintplans.exists():
            logger.error('No enabled maintenance plans found')
            sys.exit(1)

    lockfn = os.path.join(fetchdir, "layerindex.lock")
    lockfile = utils.lock_file(lockfn)
    if not lockfile:
        logger.error("Layer index lock timeout expired")
        sys.exit(1)
    try:
        for maintplan in maintplans:
            # Check releases and milestones
            current = date.today()
            current_release = Release.get_by_date(maintplan, current)
            if current_release:
                current_milestone = Milestone.get_by_release_and_date(
                    current_release, current)
                if not current_milestone:
                    logger.warning(
                        '%s: there is no milestone defined in the latest release (%s) that covers the current date, so up-to-date data will not be visible in the web interface.'
                        % (maintplan, current_release))
            else:
                logger.warning(
                    '%s: there is no release defined that covers the current date, so up-to-date data will not be visible in the web interface.'
                    % maintplan)

            for maintplanbranch in maintplan.maintenanceplanlayerbranch_set.all(
            ):
                layerbranch = maintplanbranch.layerbranch
                if options.fullreload and not options.dry_run:
                    RecipeUpgrade.objects.filter(
                        recipe__layerbranch=layerbranch).delete()
                layer = layerbranch.layer
                urldir = layer.get_fetch_dir()
                repodir = os.path.join(fetchdir, urldir)
                layerdir = os.path.join(repodir, layerbranch.vcs_subdir)

                if options.commit:
                    initial = False
                    since = options.commit
                    since_option = '%s^..%s' % (options.commit, options.commit)
                elif maintplanbranch.upgrade_rev and not options.fullreload:
                    initial = False
                    since = maintplanbranch.upgrade_date
                    since_option = '%s..origin/master' % maintplanbranch.upgrade_rev
                else:
                    initial = True
                    since = options.since
                    since_option = '--since="%s" origin/master' % since

                repo = git.Repo(repodir)
                assert repo.bare == False

                commits = utils.runcmd(
                    "git log %s --format='%%H %%ct' --reverse" % since_option,
                    repodir,
                    logger=logger)
                commit_list = commits.split('\n')

                bitbake_map = {}
                # Filter out some bad commits
                bitbake_commits = utils.runcmd(
                    "git rev-list fef18b445c0cb6b266cd939b9c78d7cbce38663f^..39780b1ccbd76579db0fc6fb9369c848a3bafa9d^",
                    bitbakepath,
                    logger=logger)
                bitbake_commit_list = bitbake_commits.splitlines()
                for commit in bitbake_commit_list:
                    bitbake_map[
                        commit] = '39780b1ccbd76579db0fc6fb9369c848a3bafa9d'

                if initial:
                    logger.debug("Adding initial upgrade history ....")

                    ct, ctepoch = commit_list.pop(0).split()
                    ctdate = datetime.fromtimestamp(int(ctepoch))
                    run_internal(maintplanbranch,
                                 ct,
                                 ctdate,
                                 options,
                                 logger,
                                 bitbake_map,
                                 initial=True)

                if layerbranch.vcs_subdir:
                    layersubdir_start = layerbranch.vcs_subdir
                    if not layersubdir_start.endswith('/'):
                        layersubdir_start += '/'
                else:
                    layersubdir_start = ''
                logger.debug("Adding upgrade history from %s to %s ..." %
                             (since, datetime.today().strftime("%Y-%m-%d")))
                for item in commit_list:
                    if item:
                        ct, ctepoch = item.split()
                        ctdate = datetime.fromtimestamp(int(ctepoch))
                        commitobj = repo.commit(ct)
                        touches_recipe = False
                        for parent in commitobj.parents:
                            diff = parent.diff(commitobj)
                            for diffitem in diff:
                                if layersubdir_start and not (
                                        diffitem.a_path.startswith(
                                            layersubdir_start) or diffitem.
                                        b_path.startswith(layersubdir_start)):
                                    # Not in this layer, skip it
                                    continue
                                if diffitem.a_path.endswith(
                                    ('.bb',
                                     '.inc')) or diffitem.b_path.endswith(
                                         ('.bb', '.inc')):
                                    # We need to look at this commit
                                    touches_recipe = True
                                    break
                            if touches_recipe:
                                break
                        if not touches_recipe:
                            # No recipes in the layer changed in this commit
                            # NOTE: Whilst it's possible that a change to a class might alter what's
                            # in the recipe, we can ignore that since we are only concerned with actual
                            # upgrades which would always require some sort of change to the recipe
                            # or an include file, so we can safely skip commits that don't do that
                            logger.debug("Skipping commit %s" % ct)
                            continue
                        logger.debug("Analysing commit %s ..." % ct)
                        run_internal(maintplanbranch, ct, ctdate, options,
                                     logger, bitbake_map)
                        if not options.dry_run:
                            maintplanbranch.upgrade_rev = ct
                            maintplanbranch.upgrade_date = ctdate
                            maintplanbranch.save()
    finally:
        utils.unlock_file(lockfile)
Exemple #7
0
    def get_context_data(self, **kwargs):
        context = super(RecipeDetailView, self).get_context_data(**kwargs)
        recipe = self.get_object()
        if not recipe:
            raise django.http.Http404

        maintplan = get_object_or_404(MaintenancePlan,
                                      name=self.maintplan_name)
        context['maintplan_name'] = maintplan.name
        context['maintplan'] = maintplan
        release = Release.get_current(maintplan)
        context['release_name'] = release.name
        milestone = Milestone.get_current(release)
        context['milestone_name'] = milestone.name

        context['upstream_status'] = ''
        context['upstream_version'] = ''
        context['upstream_no_update_reason'] = ''
        recipe_upstream_history = RecipeUpstreamHistory.get_last_by_date_range(
            recipe.layerbranch, milestone.start_date, milestone.end_date)
        if recipe_upstream_history:
            recipe_upstream = RecipeUpstream.get_by_recipe_and_history(
                recipe, recipe_upstream_history)
            if recipe_upstream:
                if recipe_upstream.status == 'N' and recipe_upstream.no_update_reason:
                    recipe_upstream.status = 'C'
                elif recipe_upstream.status == 'D':
                    recipe_upstream.status = 'U'
                context['upstream_status'] = \
                    RecipeUpstream.RECIPE_UPSTREAM_STATUS_CHOICES_DICT[recipe_upstream.status]
                context['upstream_version'] = recipe_upstream.version
                context[
                    'upstream_no_update_reason'] = recipe_upstream.no_update_reason

        self.recipe_maintainer_history = RecipeMaintainerHistory.get_last(
            recipe.layerbranch)
        recipe_maintainer = RecipeMaintainer.objects.filter(
            recipe=recipe, history=self.recipe_maintainer_history)
        if recipe_maintainer:
            maintainer = recipe_maintainer[0].maintainer
            context['maintainer_name'] = maintainer.name
        else:
            context['maintainer_name'] = 'No maintainer'

        context['recipe_upgrade_details'] = []
        for ru in RecipeUpgrade.objects.filter(
                recipe=recipe).order_by('-commit_date'):
            context['recipe_upgrade_details'].append(
                _get_recipe_upgrade_detail(maintplan, ru))
        context['recipe_upgrade_detail_count'] = len(
            context['recipe_upgrade_details'])

        context['recipe_layer_branch_url'] = _get_layer_branch_url(
            recipe.layerbranch.branch.name, recipe.layerbranch.layer.name)

        context['recipe_provides'] = []
        for p in recipe.provides.split():
            context['recipe_provides'].append(p)

        context['recipe_depends'] = StaticBuildDep.objects.filter(
            recipes__id=recipe.id).values_list('name', flat=True)

        context['recipe_distros'] = RecipeDistro.get_distros_by_recipe(recipe)

        return context
Exemple #8
0
    def get_context_data(self, **kwargs):
        context = super(RecipeListView, self).get_context_data(**kwargs)

        context['this_url_name'] = resolve(self.request.path_info).url_name

        context['all_maintplans'] = MaintenancePlan.objects.all()
        context['maintplan_name'] = self.maintplan_name
        maintplan = get_object_or_404(MaintenancePlan,
                                      name=self.maintplan_name)
        context['maintplan'] = maintplan
        context['release_name'] = self.release_name
        context['all_releases'] = Release.objects.filter(
            plan=maintplan).order_by('-end_date')
        context['milestone_name'] = self.milestone_name
        context['all_milestones'] = Milestone.get_by_release_name(
            maintplan, self.release_name)

        current = date.today()
        current_release = Release.get_by_date(maintplan, current)
        if current_release:
            current_milestone = Milestone.get_by_release_and_date(
                current_release, current)
            if not current_milestone:
                messages.error(
                    self.request,
                    'There is no milestone defined in the latest release (%s) that covers the current date, so data shown here is not up-to-date. The administrator will need to create a milestone in order to fix this.'
                    % current_release)
        else:
            messages.error(
                self.request,
                'There is no release defined that covers the current date, so data shown here is not up-to-date. The administrator will need to create a release (and corresponding milestones) in order to fix this.'
            )

        context['recipes_percentage'] = self.milestone_statistics['percentage']
        context['recipes_all_upgraded'] = self.milestone_statistics[
            'all_upgraded']
        context['recipes_all_not_upgraded'] = self.milestone_statistics[
            'all_not_upgraded']
        context['recipes_up_to_date'] = self.milestone_statistics['up_to_date']
        context['recipes_not_updated'] = self.milestone_statistics[
            'not_updated']
        context['recipes_cant_be_updated'] = self.milestone_statistics[
            'cant_be_updated']
        context['recipes_unknown'] = self.milestone_statistics['unknown']
        context['recipes_percentage_up_to_date'] = \
            self.milestone_statistics['percentage_up_to_date']
        context['recipes_percentage_not_updated'] = \
            self.milestone_statistics['percentage_not_updated']
        context['recipes_percentage_cant_be_updated'] = \
            self.milestone_statistics['percentage_cant_be_updated']
        context['recipes_percentage_unknown'] = \
            self.milestone_statistics['percentage_unknown']

        context['recipe_list_count'] = self.recipe_list_count

        context['upstream_status'] = self.upstream_status
        ruch = RecipeUpstream.RECIPE_UPSTREAM_STATUS_CHOICES_DICT
        context['upstream_status_set_choices'] = [ruch['A']]
        context['upstream_status_choices'] = [
            ruch['N'], ruch['C'], ruch['Y'], ruch['U']
        ]

        context['maintainer_name'] = self.maintainer_name
        context['set_maintainers'] = ['All', 'No maintainer']
        all_maintainers = []
        for layerbranch_id, rmh in self.recipe_maintainer_history.items():
            for rm in RecipeMaintainer.objects.filter(
                    history=rmh).values('maintainer__name').distinct(
                    ).order_by('maintainer__name'):
                if rm['maintainer__name'] in context['set_maintainers']:
                    continue
                all_maintainers.append(rm['maintainer__name'])
        context['all_maintainers'] = all_maintainers

        context['search'] = self.search

        return context
Exemple #9
0
    def get_context_data(self, **kwargs):
        context = super(RecipeDetailView, self).get_context_data(**kwargs)
        recipesymbol = self.get_object()
        if not recipesymbol:
            raise django.http.Http404

        recipe = recipesymbol.layerbranch.recipe_set.filter(
            pn=recipesymbol.pn, layerbranch=recipesymbol.layerbranch).last()
        context['recipe'] = recipe

        maintplan = get_object_or_404(MaintenancePlan,
                                      name=self.maintplan_name)
        context['maintplan_name'] = maintplan.name
        context['maintplan'] = maintplan
        release = Release.get_current(maintplan)
        context['release_name'] = release.name
        milestone = Milestone.get_current(release)
        context['milestone_name'] = milestone.name

        context['upstream_status'] = ''
        context['upstream_version'] = ''
        context['upstream_no_update_reason'] = ''
        recipe_upstream_history = RecipeUpstreamHistory.get_last_by_date_range(
            recipesymbol.layerbranch, milestone.start_date, milestone.end_date)
        if recipe_upstream_history:
            recipe_upstream = RecipeUpstream.get_by_recipe_and_history(
                recipesymbol, recipe_upstream_history)
            if recipe_upstream:
                if recipe_upstream.status == 'N' and recipe_upstream.no_update_reason:
                    recipe_upstream.status = 'C'
                elif recipe_upstream.status == 'D':
                    recipe_upstream.status = 'U'
                context['upstream_status'] = \
                    RecipeUpstream.RECIPE_UPSTREAM_STATUS_CHOICES_DICT[recipe_upstream.status]
                context['upstream_version'] = recipe_upstream.version
                context[
                    'upstream_no_update_reason'] = recipe_upstream.no_update_reason

        self.recipe_maintainer_history = RecipeMaintainerHistory.get_last(
            recipesymbol.layerbranch)
        recipe_maintainer = RecipeMaintainer.objects.filter(
            recipesymbol=recipesymbol, history=self.recipe_maintainer_history)
        if recipe_maintainer:
            maintainer = recipe_maintainer[0].maintainer
            context['maintainer_name'] = maintainer.name
        else:
            context['maintainer_name'] = 'No maintainer'

        details = []
        multigroup = False
        lastgroup = ''  # can't use None here
        for ru in RecipeUpgrade.objects.filter(
                recipesymbol=recipesymbol).exclude(upgrade_type='M').order_by(
                    'group', '-commit_date', '-id'):
            details.append(_get_recipe_upgrade_detail(maintplan, ru))
            if not multigroup:
                if lastgroup == '':
                    lastgroup = ru.group
                elif ru.group != lastgroup:
                    multigroup = True
        details.sort(key=lambda s: RecipeUpgradeGroupSortItem(s.group),
                     reverse=True)
        context['multigroup'] = multigroup
        context['recipe_upgrade_details'] = details
        context['recipe_upgrade_detail_count'] = len(details)

        if not recipe:
            ru = RecipeUpgrade.objects.filter(
                recipesymbol=recipesymbol).order_by('-commit_date',
                                                    '-id').first()
            if ru:
                context['last_filepath'] = ru.filepath

        context['recipe_layer_branch_url'] = _get_layer_branch_url(
            recipesymbol.layerbranch.branch.name,
            recipesymbol.layerbranch.layer.name)

        context['recipe_provides'] = []
        if recipe:
            for p in recipe.provides.split():
                context['recipe_provides'].append(p)

            context['recipe_depends'] = StaticBuildDep.objects.filter(
                recipes__id=recipe.id).values_list('name', flat=True)

            context['recipe_distros'] = RecipeDistro.get_distros_by_recipe(
                recipe)
        else:
            context['recipe_depends'] = []
            context['recipe_distros'] = []

        context['otherbranch_recipes'] = Recipe.objects.filter(
            layerbranch__layer=recipesymbol.layerbranch.layer,
            layerbranch__branch__comparison=False,
            pn=recipesymbol.pn).order_by('layerbranch__branch__sort_priority')

        return context
def upgrade_history(options, logger):
    from rrs.models import MaintenancePlan, RecipeUpgrade, Release, Milestone, RecipeUpgradeGroupRule

    logger.debug('=== BEGIN; cmdline: %s' % (' '.join(sys.argv)))

    if options.plan:
        maintplans = MaintenancePlan.objects.filter(id=int(options.plan))
        if not maintplans.exists():
            logger.error('No maintenance plan with ID %s found' % options.plan)
            sys.exit(1)
    else:
        maintplans = MaintenancePlan.objects.filter(updates_enabled=True)
        if not maintplans.exists():
            logger.error('No enabled maintenance plans found')
            sys.exit(1)

    if options.regroup:
        for maintplan in maintplans:
            for maintplanbranch in maintplan.maintenanceplanlayerbranch_set.all(
            ):
                layerbranch = maintplanbranch.layerbranch
                if RecipeUpgradeGroupRule.objects.filter(
                        layerbranch=layerbranch).exists():
                    for ru in RecipeUpgrade.objects.filter(
                            recipesymbol__layerbranch=layerbranch):
                        if ru.regroup():
                            ru.save()
        sys.exit(0)

    lockfn = os.path.join(fetchdir, "layerindex.lock")
    lockfile = utils.lock_file(lockfn)
    if not lockfile:
        logger.error("Layer index lock timeout expired")
        sys.exit(1)
    try:
        for maintplan in maintplans:
            # Check releases and milestones
            current = date.today()
            current_release = Release.get_by_date(maintplan, current)
            if current_release:
                current_milestone = Milestone.get_by_release_and_date(
                    current_release, current)
                if not current_milestone:
                    logger.warning(
                        '%s: there is no milestone defined in the latest release (%s) that covers the current date, so up-to-date data will not be visible in the web interface.'
                        % (maintplan, current_release))
            else:
                logger.warning(
                    '%s: there is no release defined that covers the current date, so up-to-date data will not be visible in the web interface.'
                    % maintplan)

            for maintplanbranch in maintplan.maintenanceplanlayerbranch_set.all(
            ):
                layerbranch = maintplanbranch.layerbranch
                if options.fullreload and not options.dry_run:
                    logger.debug('fullreload: deleting upgrade objects')
                    if options.filter_files:
                        RecipeUpgrade.objects.filter(
                            recipesymbol__layerbranch=layerbranch,
                            filepath__startswith=options.filter_files).delete(
                            )
                    else:
                        RecipeUpgrade.objects.filter(
                            recipesymbol__layerbranch=layerbranch).delete()
                layer = layerbranch.layer
                urldir = layer.get_fetch_dir()
                repodir = os.path.join(fetchdir, urldir)
                layerdir = os.path.join(repodir, layerbranch.vcs_subdir)

                if options.commit:
                    initial = False
                    since = options.commit
                    since_option = [
                        '%s^..%s' % (options.commit, options.commit)
                    ]
                elif maintplanbranch.upgrade_rev and not options.fullreload:
                    initial = False
                    since = maintplanbranch.upgrade_date
                    since_option = [
                        '%s..origin/master' % maintplanbranch.upgrade_rev
                    ]
                else:
                    initial = True
                    since = options.since
                    since_option = ['--since=%s' % since, 'origin/master']

                repo = git.Repo(repodir)
                if repo.bare:
                    logger.error('Repository %s is bare, not supported' %
                                 repodir)
                    continue

                commits = utils.runcmd(['git', 'log'] + since_option +
                                       ['--format=%H %ct', '--reverse'],
                                       repodir,
                                       logger=logger)
                commit_list = commits.split('\n')

                bitbake_map = {}

                def remap_range(start, end, replacewith=None):
                    if replacewith is None:
                        replacewith = end
                    bitbake_commits = utils.runcmd(
                        ['git', 'rev-list',
                         '%s^..%s^' % (start, end)],
                        bitbakepath,
                        logger=logger)
                    bitbake_commit_list = bitbake_commits.splitlines()
                    for commit in bitbake_commit_list:
                        bitbake_map[commit] = replacewith

                # Filter out some bad commits
                remap_range('fef18b445c0cb6b266cd939b9c78d7cbce38663f',
                            '39780b1ccbd76579db0fc6fb9369c848a3bafa9d')
                remap_range('5796ed550d127853808f38257f8dcc8c1cf59342',
                            '547128731e62b36d2271c4390b3fee2b16c535dc')

                if options.stop_commit and (options.stop_commit not in [
                        x.split()[0] for x in commit_list
                ]):
                    logger.error('Stop commit %s is not in repository %s' %
                                 (options.stop_commit, repodir))
                    sys.exit(1)

                if initial:
                    logger.debug("Adding initial upgrade history ....")

                    ct, ctepoch = commit_list.pop(0).split()
                    ctdate = datetime.fromtimestamp(int(ctepoch))
                    run_internal(maintplanbranch,
                                 ct,
                                 ctdate,
                                 options,
                                 logger,
                                 bitbake_map,
                                 initial=True)

                if layerbranch.vcs_subdir:
                    layersubdir_start = layerbranch.vcs_subdir
                    if not layersubdir_start.endswith('/'):
                        layersubdir_start += '/'
                else:
                    layersubdir_start = ''
                logger.debug("Adding upgrade history from %s to %s ..." %
                             (since, datetime.today().strftime("%Y-%m-%d")))
                for item in commit_list:
                    if item:
                        ct, ctepoch = item.split()
                        if ct == options.stop_commit:
                            logger.debug('Stopping at requested commit %s' %
                                         ct)
                            break
                        ctdate = datetime.fromtimestamp(int(ctepoch))
                        commitobj = repo.commit(ct)
                        touches_recipe = False
                        for parent in commitobj.parents:
                            diff = parent.diff(commitobj)
                            for diffitem in diff:
                                if layersubdir_start and not (
                                        diffitem.a_path.startswith(
                                            layersubdir_start) or diffitem.
                                        b_path.startswith(layersubdir_start)):
                                    # Not in this layer, skip it
                                    continue
                                if options.filter_files and not (
                                        diffitem.a_path.startswith(
                                            options.filter_files)
                                        or diffitem.b_path.startswith(
                                            options.filter_files)):
                                    # Doesn't match path filter
                                    continue
                                if diffitem.a_path.endswith(
                                    ('.bb',
                                     '.inc')) or diffitem.b_path.endswith(
                                         ('.bb', '.inc')):
                                    # We need to look at this commit
                                    touches_recipe = True
                                    break
                            if touches_recipe:
                                break
                        if not touches_recipe:
                            # No recipes in the layer changed in this commit
                            # NOTE: Whilst it's possible that a change to a class might alter what's
                            # in the recipe, we can ignore that since we are only concerned with actual
                            # upgrades which would always require some sort of change to the recipe
                            # or an include file, so we can safely skip commits that don't do that
                            logger.debug("Skipping commit %s" % ct)
                            continue
                        logger.debug("Analysing commit %s ..." % ct)
                        run_internal(maintplanbranch, ct, ctdate, options,
                                     logger, bitbake_map)
                        if not (options.dry_run or options.filter_files):
                            maintplanbranch.upgrade_rev = ct
                            maintplanbranch.upgrade_date = ctdate
                            maintplanbranch.save()
    finally:
        utils.unlock_file(lockfile)