Example #1
0
    def run_command(self, args, config):
        workspace_path = get_workspace_path()
        manifest = get_workspace_manifest()

        manifest_repo = find_source_manifest_repo(manifest, config['cfg_file'],
                                                  config['user_cfg_file'],
                                                  args.source_manifest_repo)
        cfg, user_cfg, conflicts = list_available_manifest_repos(
            config['cfg_file'], config['user_cfg_file'])
        if manifest_repo in cfg:
            manifest_repo_path = config['cfg_file'].manifest_repo_abs_path(
                manifest_repo)
        elif manifest_repo in user_cfg:
            manifest_repo_path = config[
                'user_cfg_file'].manifest_repo_abs_path(manifest_repo)
        else:
            manifest_repo_path = None

        pin_path = self.__get_pin_path(args, workspace_path,
                                       manifest_repo_path, manifest)
        pin = ManifestXml(pin_path)
        manifest_sources = manifest.get_repo_sources(
            manifest.general_config.current_combo)
        check_dirty_repos(manifest, workspace_path)
        for source in manifest_sources:
            local_path = os.path.join(workspace_path, source.root)
            repo = Repo(local_path)
            origin = repo.remotes.origin
            origin.fetch()
        self.__pin_matches_project(pin, manifest, workspace_path)
        sparse_enabled = sparse_checkout_enabled(workspace_path,
                                                 manifest_sources)
        if sparse_enabled:
            ui_functions.print_info_msg(SPARSE_RESET, header=False)
            reset_sparse_checkout(workspace_path, manifest_sources)
        submodule_combo = pin.general_config.current_combo
        try:
            deinit_full(workspace_path, manifest, args.verbose)
        except Exception as e:
            ui_functions.print_error_msg(SUBMODULE_DEINIT_FAILED, header=False)
            if args.verbose:
                ui_functions.print_error_msg(e, header=False)
        pin_repo_sources = pin.get_repo_sources(
            pin.general_config.current_combo)
        try:
            checkout_repos(args.verbose, args.override, pin_repo_sources,
                           workspace_path, manifest)
            manifest.write_current_combo(humble.PIN_COMBO.format(args.pinfile))
        finally:
            cache_path = None
            cache_obj = get_repo_cache_obj(config)
            if cache_obj is not None:
                cache_path = cache_obj.get_cache_path(
                    SUBMODULE_CACHE_REPO_NAME)
            maintain_submodules(workspace_path, pin, submodule_combo,
                                args.verbose, cache_path)
            if sparse_enabled:
                ui_functions.print_info_msg(SPARSE_CHECKOUT, header=False)
                sparse_checkout(workspace_path, pin_repo_sources, manifest)
Example #2
0
def maintain_submodules(workspace,
                        manifest,
                        combo_name,
                        verbose=False,
                        cache_path=None):
    """
    Updates the submodules for a specific repo.

        workspace  - Path to the current workspace.
        manifest   - The manifest parser object for the project.
        combo_name - The combination name to use for submodule maintenance.
        verbose    - Enable verbose messages.
        cache_path - Path to the submodule cache repo.  A value of None indicates that no cache repo exists.
    """
    # Process each repo that may have submodules enabled
    ui_functions.print_info_msg(strings.SUBMOD_INIT_UPDATE)
    repo_sources = manifest.get_repo_sources(combo_name)
    for source in repo_sources:
        # Open the repo and process submodules
        try:
            repo = git.Repo(os.path.join(workspace, source.root))
        except Exception as repo_error:
            if args.verbose:
                ui_functions.print_error_msg(
                    strings.SUBMOD_EXCEPTION.format(repo_error))
            continue

        # Collect the submodule initialization data from manifest as well as if submodules
        # should be processed for the repo.
        repo_subs = manifest.get_submodule_init_paths(source.remote_name,
                                                      combo_name)
        repo_subs_enabled = _get_submodule_enable(manifest, source.remote_name,
                                                  combo_name)
        if not repo_subs_enabled:
            continue

        # Initialize submodules
        if len(repo_subs) > 0:
            _init(repo, repo_subs, verbose)

        # Perform sync/update
        if len(repo_subs) == 0:
            _update(repo, None, verbose, cache_path=cache_path)
        else:
            _update(repo, repo_subs, verbose, cache_path=cache_path)
    def run_command(self, args, config):

        # Configure git long path support
        ui_functions.print_info_msg(humble.LONGPATH_CONFIG, header=False)
        set_long_path_support()
        print()

        # Remove unneeded instead of entries from git global config
        ui_functions.print_info_msg(humble.CLEAN_INSTEAD_OFS, header=False)
        print()

        # If in a valid workspace run the following for each repo:
        # git reflog --expire, git gc, git remote prune origin
        try:
            workspace_path = get_workspace_path()
        except EdkrepoWorkspaceInvalidException:
            workspace_path = None
            ui_functions.print_error_msg(humble.NO_WOKKSPACE, header=False)
            print()

        if workspace_path:
            manifest = get_workspace_manifest()
            repos_to_maintain = manifest.get_repo_sources(
                manifest.general_config.current_combo)
            for repo_to_maintain in repos_to_maintain:
                local_repo_path = os.path.join(workspace_path,
                                               repo_to_maintain.root)
                repo = Repo(local_repo_path)
                ui_functions.print_info_msg(humble.REPO_MAINTENANCE.format(
                    repo_to_maintain.root),
                                            header=False)
                ui_functions.print_info_msg(humble.REFLOG_EXPIRE, header=False)
                repo.git.reflog('expire', '--expire=now', '--all')
                ui_functions.print_info_msg(humble.GC_AGGRESSIVE, header=False)
                repo.git.gc('--aggressive', '--prune=now')
                ui_functions.print_info_msg(humble.REMOTE_PRUNE, header=False)
                repo.git.remote('prune', 'origin')
                print()
Example #4
0
    def run_command(self, args, config):
        workspace_path = get_workspace_path()
        initial_manifest = get_workspace_manifest()
        current_combo = initial_manifest.general_config.current_combo
        initial_sources = initial_manifest.get_repo_sources(current_combo)
        initial_hooks = initial_manifest.repo_hooks
        initial_combo = current_combo

        try:
            pull_workspace_manifest_repo(initial_manifest, config['cfg_file'],
                                         config['user_cfg_file'],
                                         args.source_manifest_repo, False)
        except:
            pull_all_manifest_repos(config['cfg_file'],
                                    config['user_cfg_file'], False)
        source_global_manifest_repo = find_source_manifest_repo(
            initial_manifest, config['cfg_file'], config['user_cfg_file'],
            args.source_manifest_repo)
        cfg_manifest_repos, user_cfg_manifest_repos, conflicts = list_available_manifest_repos(
            config['cfg_file'], config['user_cfg_file'])
        if source_global_manifest_repo in cfg_manifest_repos:
            global_manifest_directory = config[
                'cfg_file'].manifest_repo_abs_path(source_global_manifest_repo)
            verify_single_manifest(config['cfg_file'],
                                   source_global_manifest_repo,
                                   get_workspace_manifest_file(), args.verbose)
        elif source_global_manifest_repo in user_cfg_manifest_repos:
            global_manifest_directory = config[
                'user_cfg_file'].manifest_repo_abs_path(
                    source_global_manifest_repo)
            verify_single_manifest(config['user_cfg_file'],
                                   source_global_manifest_repo,
                                   get_workspace_manifest_file(), args.verbose)
        else:
            global_manifest_directory = None

        if global_manifest_directory is not None:
            update_editor_config(config, global_manifest_directory)

        if not args.update_local_manifest:
            self.__check_for_new_manifest(args, config, initial_manifest,
                                          workspace_path,
                                          global_manifest_directory)
        check_dirty_repos(initial_manifest, workspace_path)

        # Determine if sparse checkout needs to be disabled for this operation
        sparse_settings = initial_manifest.sparse_settings
        sparse_enabled = sparse_checkout_enabled(workspace_path,
                                                 initial_sources)
        sparse_reset_required = False
        if sparse_settings is None:
            sparse_reset_required = True
        elif args.update_local_manifest:
            sparse_reset_required = True
        if sparse_enabled and sparse_reset_required:
            ui_functions.print_info_msg(SPARSE_RESET, header=False)
            reset_sparse_checkout(workspace_path, initial_sources)

        # Get the latest manifest if requested
        if args.update_local_manifest:  # NOTE: hyphens in arg name replaced with underscores due to argparse
            self.__update_local_manifest(args, config, initial_manifest,
                                         workspace_path,
                                         global_manifest_directory)
        manifest = get_workspace_manifest()
        if args.update_local_manifest:
            try:
                repo_sources_to_sync = manifest.get_repo_sources(current_combo)
            except ValueError:
                # The manifest file was updated and the initial combo is no longer present so use the default combo
                current_combo = manifest.general_config.default_combo
                repo_sources_to_sync = manifest.get_repo_sources(current_combo)
        else:
            repo_sources_to_sync = manifest.get_repo_sources(current_combo)
        manifest.write_current_combo(current_combo)

        # At this point both new and old manifest files are ready so we can deinit any
        # submodules that are removed due to a manifest update.
        if not args.skip_submodule:
            deinit_submodules(workspace_path, initial_manifest, initial_combo,
                              manifest, current_combo, args.verbose)

        sync_error = False
        # Calculate the hooks which need to be updated, added or removed for the sync
        if args.update_local_manifest:
            new_hooks = manifest.repo_hooks
            hooks_add = set(new_hooks).difference(set(initial_hooks))
            hooks_update = set(initial_hooks).intersection(set(new_hooks))
            hooks_uninstall = set(initial_hooks).difference(set(new_hooks))
        else:
            hooks_add = None
            hooks_update = initial_hooks
            hooks_uninstall = None
        # Update submodule configuration
        if not args.update_local_manifest:  #Performance optimization, __update_local_manifest() will do this
            self.__check_submodule_config(workspace_path, manifest,
                                          repo_sources_to_sync)
        clean_git_globalconfig()
        for repo_to_sync in repo_sources_to_sync:
            local_repo_path = os.path.join(workspace_path, repo_to_sync.root)
            # Update any hooks
            if global_manifest_directory is not None:
                update_hooks(hooks_add, hooks_update, hooks_uninstall,
                             local_repo_path, repo_to_sync, config,
                             global_manifest_directory)
            repo = Repo(local_repo_path)
            #Fetch notes
            repo.remotes.origin.fetch("refs/notes/*:refs/notes/*")
            if repo_to_sync.commit is None and repo_to_sync.tag is None:
                local_commits = False
                initial_active_branch = repo.active_branch
                repo.remotes.origin.fetch(
                    "refs/heads/{0}:refs/remotes/origin/{0}".format(
                        repo_to_sync.branch))
                #The new branch may not exist in the heads list yet if it is a new branch
                repo.git.checkout(repo_to_sync.branch)
                if not args.fetch:
                    ui_functions.print_info_msg(SYNCING.format(
                        repo_to_sync.root, repo.active_branch),
                                                header=False)
                else:
                    ui_functions.print_info_msg(FETCHING.format(
                        repo_to_sync.root, repo.active_branch),
                                                header=False)
                try:
                    repo.remotes.origin.fetch()
                except GitCommandError as e:
                    prune_needed = False
                    prune_needed_heuristic_str = "error: some local refs could not be updated"
                    if e.stdout.strip().find(prune_needed_heuristic_str) != -1:
                        prune_needed = True
                    if e.stderr.strip().find(prune_needed_heuristic_str) != -1:
                        prune_needed = True
                    if prune_needed:
                        time.sleep(1.0)
                        repo.git.remote('prune', 'origin')
                        time.sleep(1.0)
                        repo.remotes.origin.fetch()
                    else:
                        raise
                if has_primary_repo_remote(repo, args.verbose):
                    fetch_from_primary_repo(repo, repo_to_sync, args.verbose)
                if not args.override and not repo.is_ancestor(
                        ancestor_rev='HEAD',
                        rev='origin/{}'.format(repo_to_sync.branch)):
                    ui_functions.print_info_msg(SYNC_COMMITS_ON_TARGET.format(
                        repo_to_sync.branch, repo_to_sync.root),
                                                header=False)
                    local_commits = True
                    sync_error = True
                if not args.fetch and (not local_commits or args.override):
                    repo.head.reset(commit='origin/{}'.format(
                        repo_to_sync.branch),
                                    working_tree=True)

                # Check to see if mirror is up to date
                if not in_sync_with_primary(repo, repo_to_sync, args.verbose):
                    ui_functions.print_info_msg(MIRROR_BEHIND_PRIMARY_REPO,
                                                header=False)

                # Switch back to the initially active branch before exiting
                repo.heads[initial_active_branch.name].checkout()

                # Warn user if local branch is behind target branch
                try:
                    latest_sha = get_latest_sha(repo, repo_to_sync.branch)
                    commit_count = int(
                        repo.git.rev_list('--count',
                                          '{}..HEAD'.format(latest_sha)))
                    branch_origin = next(
                        itertools.islice(repo.iter_commits(), commit_count,
                                         commit_count + 1))
                    behind_count = int(
                        repo.git.rev_list(
                            '--count', '{}..{}'.format(branch_origin.hexsha,
                                                       latest_sha)))
                    if behind_count:
                        ui_functions.print_info_msg(SYNC_NEEDS_REBASE.format(
                            behind_count=behind_count,
                            target_remote='origin',
                            target_branch=repo_to_sync.branch,
                            local_branch=initial_active_branch.name,
                            repo_folder=repo_to_sync.root),
                                                    header=False)
                except:
                    ui_functions.print_error_msg(SYNC_REBASE_CALC_FAIL,
                                                 header=False)
            elif args.verbose:
                ui_functions.print_warning_msg(NO_SYNC_DETACHED_HEAD.format(
                    repo_to_sync.root),
                                               header=False)

            # Update commit message templates
            if global_manifest_directory is not None:
                update_repo_commit_template(workspace_path, repo, repo_to_sync,
                                            config, global_manifest_directory)

        if sync_error:
            ui_functions.print_error_msg(SYNC_ERROR, header=False)

        # Initialize submodules
        if not args.skip_submodule:
            cache_path = None
            cache_obj = get_repo_cache_obj(config)
            if cache_obj is not None:
                cache_path = cache_obj.get_cache_path(
                    SUBMODULE_CACHE_REPO_NAME)
            maintain_submodules(workspace_path, manifest, current_combo,
                                args.verbose, cache_path)

        # Restore sparse checkout state
        if sparse_enabled:
            ui_functions.print_info_msg(SPARSE_CHECKOUT, header=False)
            sparse_checkout(workspace_path, repo_sources_to_sync, manifest)
Example #5
0
    def __update_local_manifest(self, args, config, initial_manifest,
                                workspace_path, global_manifest_directory):
        #if the manifest repository for the current manifest was not found then there is no project with the manifest
        #specified project name in the index file for any of the manifest repositories
        if global_manifest_directory is None:
            raise EdkrepoManifestNotFoundException(
                SOURCE_MANIFEST_REPO_NOT_FOUND.format(
                    initial_manifest.project_info.codename))

        local_manifest_dir = os.path.join(workspace_path, 'repo')
        current_combo = initial_manifest.general_config.current_combo
        initial_sources = initial_manifest.get_repo_sources(current_combo)
        # Do a fetch for each repo in the initial to ensure that newly created upstream branches are available
        for initial_repo in initial_sources:
            local_repo_path = os.path.join(workspace_path, initial_repo.root)
            repo = Repo(local_repo_path)
            origin = repo.remotes.origin
            try:
                origin.fetch()
            except GitCommandError as e:
                prune_needed = False
                prune_needed_heuristic_str = "error: some local refs could not be updated"
                if e.stdout.strip().find(prune_needed_heuristic_str) != -1:
                    prune_needed = True
                if e.stderr.strip().find(prune_needed_heuristic_str) != -1:
                    prune_needed = True
                if prune_needed:
                    # The sleep is to give the operating system time to close all the file handles that Git has open
                    time.sleep(1.0)
                    repo.git.remote('prune', 'origin')
                    time.sleep(1.0)
                    origin.fetch()
                else:
                    raise

        #see if there is an entry in CiIndex.xml that matches the prject name of the current manifest
        index_path = os.path.join(global_manifest_directory, 'CiIndex.xml')
        ci_index_xml = CiIndexXml(index_path)
        if initial_manifest.project_info.codename not in ci_index_xml.project_list:
            raise EdkrepoManifestNotFoundException(
                SYNC_MANIFEST_NOT_FOUND.format(
                    initial_manifest.project_info.codename))
        initial_manifest_remotes = {
            name: url
            for name, url in initial_manifest.remotes
        }
        ci_index_xml_rel_path = os.path.normpath(
            ci_index_xml.get_project_xml(
                initial_manifest.project_info.codename))
        global_manifest_path = os.path.join(global_manifest_directory,
                                            ci_index_xml_rel_path)
        new_manifest_to_check = ManifestXml(global_manifest_path)

        # Does the current combo exist in the new manifest? If not check to see if you can use the repo sources from
        # the default combo
        initial_combos = combinations_in_manifest(initial_manifest)
        new_combos = combinations_in_manifest(new_manifest_to_check)
        if current_combo not in new_combos:
            new_sources_for_current_combo = new_manifest_to_check.get_repo_sources(
                new_manifest_to_check.general_config.default_combo)
            new_sources = new_sources_for_current_combo
        else:
            new_sources_for_current_combo = new_manifest_to_check.get_repo_sources(
                current_combo)
            new_sources = new_manifest_to_check.get_repo_sources(current_combo)

        remove_included_config(initial_manifest.remotes,
                               initial_manifest.submodule_alternate_remotes,
                               local_manifest_dir)
        write_included_config(
            new_manifest_to_check.remotes,
            new_manifest_to_check.submodule_alternate_remotes,
            local_manifest_dir)

        self.__check_submodule_config(workspace_path, new_manifest_to_check,
                                      new_sources_for_current_combo)

        # Check that the repo sources lists are the same. If they are not the same and the override flag is not set, throw an exception.
        if not args.override and set(initial_sources) != set(new_sources):
            raise EdkrepoManifestChangedException(
                SYNC_REPO_CHANGE.format(
                    initial_manifest.project_info.codename))
        elif args.override and set(initial_sources) != set(new_sources):
            #get a set of repo source tuples that are not in both the new and old manifest
            uncommon_sources = []
            initial_common = []
            new_common = []
            for initial in initial_sources:
                common = False
                for new in new_sources:
                    if initial.root == new.root:
                        if initial.remote_name == new.remote_name:
                            if initial.remote_url == new.remote_url:
                                # If the source is unchanged between the old and the new manifest,
                                # add it to the common lists
                                common = True
                                initial_common.append(initial)
                                new_common.append(new)
                                break
                # If the source is different between the old and the new manifest, add it to the uncommon list
                if not common:
                    uncommon_sources.append(initial)
            for new in new_sources:
                common = False
                for initial in initial_sources:
                    if new.root == initial.root:
                        if new.remote_name == initial.remote_name:
                            if new.remote_url == initial.remote_url:
                                common = True
                                break
                # If the source is different between the old and the new manifest, add it to the uncommon list
                if not common:
                    uncommon_sources.append(new)
            uncommon_sources = set(uncommon_sources)
            initial_common = set(initial_common)
            new_common = set(new_common)
            sources_to_move = []
            sources_to_remove = []
            sources_to_clone = []
            for source in uncommon_sources:
                found_source = False
                for source_to_check in initial_sources:
                    if source_to_check.root == source.root:
                        if source_to_check.remote_name == source.remote_name:
                            if source_to_check.remote_url == source.remote_url:
                                found_source = True
                                break
                # If the source that is different came from the old manifest, then it is now outdated and either needs
                # to be deleted or moved to an archival location.
                if found_source:
                    roots = [s.root for s in new_sources]
                    # If there is a source in the new manifest that goes into the same folder name as a source in the
                    # old manifest, then we need to move that old folder to an archival location.
                    if source.root in roots:
                        sources_to_move.append(source)
                    else:
                        # If it doesn't exist at all in the new manifest, tell the user it is old and no longer used.
                        sources_to_remove.append(source)
                else:
                    # If the source that is different came from the new manifest, then we need to clone that new
                    # Git repository.
                    sources_to_clone.append(source)
            # Move the obsolete Git repositories to archival locations.
            for source in sources_to_move:
                old_dir = os.path.join(workspace_path, source.root)
                new_dir = generate_name_for_obsolete_backup(old_dir)
                ui_functions.print_warning_msg(SYNC_SOURCE_MOVE_WARNING.format(
                    source.root, new_dir),
                                               header=False)
                new_dir = os.path.join(workspace_path, new_dir)
                try:
                    shutil.move(old_dir, new_dir)
                except:
                    ui_functions.print_error_msg(SYNC_MOVE_FAILED.format(
                        initial_dir=source.root, new_dir=new_dir),
                                                 header=False)
                    raise
            # Tell the user about any Git repositories that are no longer used.
            if len(sources_to_remove) > 0:
                ui_functions.print_warning_msg(SYNC_REMOVE_WARNING,
                                               header=False)
            for source in sources_to_remove:
                path_to_source = os.path.join(workspace_path, source.root)
                ui_functions.print_warning_msg(path_to_source, header=False)
            if len(sources_to_remove) > 0:
                ui_functions.print_warning_msg(SYNC_REMOVE_LIST_END_FORMATTING,
                                               header=False)
            # Clone any new Git repositories
            clone_repos(args, workspace_path, sources_to_clone,
                        new_manifest_to_check.repo_hooks, config,
                        new_manifest_to_check)
            # Make a list of and only checkout repos that were newly cloned. Sync keeps repos on their initial active branches
            # cloning the entire combo can prevent existing repos from correctly being returned to their proper branch
            repos_to_checkout = []
            if sources_to_clone:
                for new_source in new_sources_for_current_combo:
                    for source in sources_to_clone:
                        if source.root == new_source.root:
                            repos_to_checkout.append(source)
            repos_to_checkout.extend(
                self.__check_combo_sha_tag_branch(workspace_path,
                                                  initial_common, new_common))
            if repos_to_checkout:
                checkout_repos(args.verbose, args.override, repos_to_checkout,
                               workspace_path, new_manifest_to_check)

        #remove the old manifest file and copy the new one
        ui_functions.print_info_msg(UPDATING_MANIFEST, header=False)
        local_manifest_path = os.path.join(local_manifest_dir, 'Manifest.xml')
        os.remove(local_manifest_path)
        shutil.copy(global_manifest_path, local_manifest_path)

        # Update the source manifest repository tag in the local copy of the manifest XML
        new_manifest = ManifestXml(local_manifest_path)
        try:
            if 'source_manifest_repo' in vars(args).keys():
                find_source_manifest_repo(new_manifest, config['cfg_file'],
                                          config['user_cfg_file'],
                                          args.source_manifest_repo)
            else:
                find_source_manifest_repo(new_manifest, config['cfg_file'],
                                          config['user_cfg_file'], None)
        except EdkrepoManifestNotFoundException:
            pass