Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 35 additions & 39 deletions manic/checkout.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,34 @@ def commandline_arguments(args=None):
options = parser.parse_args()
return options


def _dirty_local_repo_msg(program_name, config_file):
return """The external repositories labeled with 'M' above are not in a clean state.
The following are four options for how to proceed:
(1) Go into each external that is not in a clean state and issue either a 'git status' or
an 'svn status' command (depending on whether the external is managed by git or
svn). Either revert or commit your changes so that all externals are in a clean
state. (To revert changes in git, follow the instructions given when you run 'git
status'.) (Note, though, that it is okay to have untracked files in your working
directory.) Then rerun {program_name}.
(2) Alternatively, you do not have to rely on {program_name}. Instead, you can manually
update out-of-sync externals (labeled with 's' above) as described in the
configuration file {config_file}. (For example, run 'git fetch' and 'git checkout'
commands to checkout the appropriate tags for each external, as given in
{config_file}.)
(3) You can also use {program_name} to manage most, but not all externals: You can specify
one or more externals to ignore using the '-x' or '--exclude' argument to
{program_name}. Excluding externals labeled with 'M' will allow {program_name} to
update the other, non-excluded externals.
(4) As a last resort, if you are confident that there is no work that needs to be saved
from a given external, you can remove that external (via "rm -rf [directory]") and
then rerun the {program_name} tool. This option is mainly useful as a workaround for
issues with this tool (such as https://github.com/ESMCI/manage_externals/issues/157).
The external repositories labeled with '?' above are not under version
control using the expected protocol. If you are sure you want to switch
protocols, and you don't have any work you need to save from this
directory, then run "rm -rf [directory]" before rerunning the
{program_name} tool.
""".format(program_name=program_name, config_file=config_file)
# ---------------------------------------------------------------------
#
# main
Expand Down Expand Up @@ -380,8 +407,12 @@ def main(args):
comp, args.externals))

source_tree = SourceTree(root_dir, external, svn_ignore_ancestry=args.svn_ignore_ancestry)
printlog('Checking status of externals: ', end='')
tree_status = source_tree.status()
if args.components:
components_str = 'specified components'
else:
components_str = 'required & optional components'
printlog('Checking local status of ' + components_str + ': ', end='')
tree_status = source_tree.status(print_progress=True)
printlog('')

if args.status:
Expand All @@ -396,43 +427,8 @@ def main(args):
for comp in sorted(tree_status):
tree_status[comp].log_status_message(args.verbose)
# exit gracefully
msg = """The external repositories labeled with 'M' above are not in a clean state.

The following are four options for how to proceed:

(1) Go into each external that is not in a clean state and issue either a 'git status' or
an 'svn status' command (depending on whether the external is managed by git or
svn). Either revert or commit your changes so that all externals are in a clean
state. (To revert changes in git, follow the instructions given when you run 'git
status'.) (Note, though, that it is okay to have untracked files in your working
directory.) Then rerun {program_name}.

(2) Alternatively, you do not have to rely on {program_name}. Instead, you can manually
update out-of-sync externals (labeled with 's' above) as described in the
configuration file {config_file}. (For example, run 'git fetch' and 'git checkout'
commands to checkout the appropriate tags for each external, as given in
{config_file}.)

(3) You can also use {program_name} to manage most, but not all externals: You can specify
one or more externals to ignore using the '-x' or '--exclude' argument to
{program_name}. Excluding externals labeled with 'M' will allow {program_name} to
update the other, non-excluded externals.

(4) As a last resort, if you are confident that there is no work that needs to be saved
from a given external, you can remove that external (via "rm -rf [directory]") and
then rerun the {program_name} tool. This option is mainly useful as a workaround for
issues with this tool (such as https://github.com/ESMCI/manage_externals/issues/157).


The external repositories labeled with '?' above are not under version
control using the expected protocol. If you are sure you want to switch
protocols, and you don't have any work you need to save from this
directory, then run "rm -rf [directory]" before rerunning the
{program_name} tool.
""".format(program_name=program_name, config_file=args.externals)

printlog('-' * 70)
printlog(msg)
printlog(_dirty_local_repo_msg(program_name, args.externals))
printlog('-' * 70)
else:
if not args.components:
Expand Down
16 changes: 14 additions & 2 deletions manic/externals_description.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,10 @@ def read_gitmodules_file(root_dir, file_name):
def create_externals_description(
model_data, model_format='cfg', components=None, exclude=None, parent_repo=None):
"""Create the a externals description object from the provided data

components: list of component names to include, None to include all. If a
name isn't found, it is silently omitted from the return value.
exclude: list of component names to skip.
"""
externals_description = None
if model_format == 'dict':
Expand Down Expand Up @@ -357,8 +361,9 @@ class ExternalsDescription(dict):
input value.

"""
# keywords defining the interface into the externals description data
EXTERNALS = 'externals'
# keywords defining the interface into the externals description data; these
# are brought together by the schema below.
EXTERNALS = 'externals' # path to externals file.
BRANCH = 'branch'
SUBMODULE = 'from_submodule'
HASH = 'hash'
Expand All @@ -384,6 +389,8 @@ class ExternalsDescription(dict):
_V1_BRANCH = 'BRANCH'
_V1_REQ_SOURCE = 'REQ_SOURCE'

# Dictionary keys are component names. The corresponding values are laid out
# according to this schema.
_source_schema = {REQUIRED: True,
PATH: 'string',
EXTERNALS: 'string',
Expand Down Expand Up @@ -760,6 +767,8 @@ def __init__(self, model_data, components=None, exclude=None, parent_repo=None):
"""Convert the config data into a standardized dict that can be used to
construct the source objects

components: list of component names to include, None to include all.
exclude: list of component names to skip.
"""
ExternalsDescription.__init__(self, parent_repo=parent_repo)
self._schema_major = 1
Expand All @@ -783,6 +792,9 @@ def _remove_metadata(model_data):

def _parse_cfg(self, cfg_data, components=None, exclude=None):
"""Parse a config_parser object into a externals description.

components: list of component names to include, None to include all.
exclude: list of component names to skip.
"""
def list_to_dict(input_list, convert_to_lower_case=True):
"""Convert a list of key-value pairs into a dictionary.
Expand Down
30 changes: 15 additions & 15 deletions manic/externals_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ class ExternalStatus(object):
transactions (e.g. add, remove, rename, untracked files).

"""
DEFAULT = '-'
# sync_state and clean_state can be one of the following:
DEFAULT = '-' # aka not set yet.
UNKNOWN = '?'
EMPTY = 'e'
MODEL_MODIFIED = 's' # a.k.a. out-of-sync
DIRTY = 'M'

STATUS_OK = ' '
MODEL_MODIFIED = 's' # repo version != externals (sync_state only)
DIRTY = 'M' # repo is dirty (clean_state only)
STATUS_OK = ' ' # repo is clean/matches externals.
STATUS_ERROR = '!'

# source types
# source_type can be one of the following:
OPTIONAL = 'o'
STANDALONE = 's'
MANAGED = ' '
Expand All @@ -55,19 +55,21 @@ def __init__(self):
def log_status_message(self, verbosity):
"""Write status message to the screen and log file
"""
self._default_status_message()
printlog(self._default_status_message())
if verbosity >= VERBOSITY_VERBOSE:
self._verbose_status_message()
printlog(self._verbose_status_message())
if verbosity >= VERBOSITY_DUMP:
self._dump_status_message()
printlog(self._dump_status_message())

def __repr__(self):
return self._default_status_message()

def _default_status_message(self):
"""Return the default terse status message string
"""
msg = '{sync}{clean}{src_type} {path}'.format(
return '{sync}{clean}{src_type} {path}'.format(
sync=self.sync_state, clean=self.clean_state,
src_type=self.source_type, path=self.path)
printlog(msg)

def _verbose_status_message(self):
"""Return the verbose status message string
Expand All @@ -82,14 +84,12 @@ def _verbose_status_message(self):
if self.sync_state != self.STATUS_OK:
sync_str = '{current} --> {expected}'.format(
current=self.current_version, expected=self.expected_version)
msg = ' {clean}, {sync}'.format(clean=clean_str, sync=sync_str)
printlog(msg)
return ' {clean}, {sync}'.format(clean=clean_str, sync=sync_str)

def _dump_status_message(self):
"""Return the dump status message string
"""
msg = indent_string(self.status_output, 12)
printlog(msg)
return indent_string(self.status_output, 12)

def safe_to_update(self):
"""Report if it is safe to update a repository. Safe is defined as:
Expand Down
1 change: 1 addition & 0 deletions manic/repository_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def create_repository(component_name, repo_info, svn_ignore_ancestry=False):
"""Determine what type of repository we have, i.e. git or svn, and
create the appropriate object.

Can return None (e.g. if protocol is 'externals_only').
"""
protocol = repo_info[ExternalsDescription.PROTOCOL].lower()
if protocol == 'git':
Expand Down
Loading