Beispiel #1
0
        def process_workingcopy():
            #command = ['hg', 'status', '--subrepos', '-q', '.']
            command = ['hg', 'status', '--subrepos', '-q', '-C', '.']
            output = subprocess.Popen(
                command,
                stdout=subprocess.PIPE).communicate()[0].decode("utf-8")
            lines = fixup_renames(output.split('\n'))

            # decorate entries based on any staging information

            orphaned_tag = '^'
            orphaned_count = 0

            root = find_hg_root()

            stage_path = Stage.StageIO().get_staging_root(root, options)
            if os.path.exists(stage_path):
                stage_names = os.listdir(stage_path)

                # if len(stage_names) and len(lines) == 0:
                #     msg = 'ERROR: Orphaned staged entries found in the following areas:\n'
                #     for stage_name in stage_names:
                #         msg += '  [%s]\n' % stage_name
                #     msg += '\nUse "unstage --erase" to clear them.'
                #     self.message = msg
                #     return False

                for stage_name in stage_names:

                    # reference_count = 0
                    # capture_count = 0

                    stage_db_path = os.path.join(stage_path, stage_name)
                    stage_db_file = os.path.join(stage_db_path, 'stage.db')
                    if not os.path.exists(stage_db_file):
                        continue  # odd... should probably print a message

                    stage_db = Stage.StageIO().load_stage_db(stage_db_file)

                    # (yes, yes, I know...quadratic complexity: I don't care :)
                    for key in stage_db:
                        staged_entry = stage_db[key]
                        found = False
                        for i in range(len(lines)):
                            if key == lines[i][2:]:
                                # reference_count += 1 if staged_entry.snapshot is None else 0
                                # capture_count += 1 if staged_entry.snapshot is not None else 0

                                snap = Stage.StageIO().get_staged_entry_tag(
                                    stage_db_path, staged_entry, key)

                                lines[i] = '%s [%s] %s (%s)' % (
                                    lines[i][:1], stage_name, key, snap)

                                found = True
                                break
                        if not found:
                            snap = Stage.StageIO().get_staged_entry_tag(
                                stage_db_path, staged_entry, key)
                            # if this is a refernce, it's orphaned
                            orphaned = ''
                            if staged_entry.snapshot is None:
                                # reference_count += 1
                                orphaned = orphaned_tag
                                orphaned_count += 1
                            lines.append('%s [%s] %s%s (%s)' %
                                         (staged_entry.state, stage_name,
                                          orphaned, key, snap))

            self.process_lines(lines, options)

            if orphaned_count != 0:
                print(
                    '\n(Use the "staged" command to purge %sorphaned references)'
                    % orphaned_tag)

            return True
Beispiel #2
0
    def get_staged_entries(self, options):
        working_dir = os.getcwd()

        root = find_hg_root()
        if root:
            os.chdir(root)
            os.chdir("..")

        if not os.path.exists('.hg'):
            os.chdir(working_dir)
            self.message = 'ERROR: Must be in root of working copy to stage.'
            return []

        command = ['hg', 'status', '-q', '-C', '.']
        output = subprocess.Popen(
            command, stdout=subprocess.PIPE).communicate()[0].decode("utf-8")
        output_lines = fixup_renames(output.split('\n'))

        stage_name = options.stage_name

        self.stage_path = super(Staged, self).get_staging_root(root, options)
        if not os.path.exists(self.stage_path):
            os.mkdir(self.stage_path)
        stage_names = []
        if stage_name is None:
            # gather up all staging area names
            for entry in os.listdir(self.stage_path):
                stage_names.append(entry)
        else:
            stage_names.append(stage_name)
        staged_entries = {}
        for stage_name in stage_names:
            stage_db_path = os.path.join(self.stage_path, stage_name)
            stage_db_file = os.path.join(stage_db_path, 'stage.db')
            if not os.path.exists(stage_db_file):
                continue  # odd... should probably print a message

            stage_db = super(Staged, self).load_stage_db(stage_db_file)

            reference_count = 0
            capture_count = 0

            entries = []
            bad_keys = []
            for key in stage_db:
                staged_entry = stage_db[key]

                reference_count += 1 if staged_entry.snapshot is None else 0
                capture_count += 1 if staged_entry.snapshot is not None else 0

                found = False
                for line in output_lines:
                    if (key in line) or (staged_entry.snapshot is not None):
                        snapshot_path = None
                        snap = super(Staged, self).get_staged_entry_tag(
                            stage_db_path, staged_entry, key)
                        entries.append('%s (%s)' % (line, snap))
                        found = True
                        break

                if not found:
                    if staged_entry.snapshot is None:
                        bad_keys.append(key)
                    else:
                        # note: snapshots are independent of the state of their source files
                        snap = super(Staged, self).get_staged_entry_tag(
                            stage_db_path, staged_entry, key)
                        entries.append('%s %s (%s)' %
                                       (staged_entry.state, key, snap))

            if len(bad_keys):
                # all 'bad_keys' are references
                for key in bad_keys:
                    del stage_db[key]

            if len(stage_db):
                # save the corrected database
                super(Staged, self).save_stage_db(stage_db, stage_db_file)
                staged_entries[stage_name] = entries
            else:
                if (len(output_lines)
                        == 0) and (reference_count != 0) and (capture_count
                                                              == 0):
                    print('WARNING: Purging orphaned staging area "%s".' %
                          stage_name)

                # if (len(output_lines) == 0) and (reference_count != 0) and (capture_count == 0):
                #     # orphaned references found
                #     os.chdir(working_dir)
                #     msg = 'ERROR: Orphaned reference entries found in the following staging areas:\n'
                #     for stage_name in stage_names:
                #         msg += '  [%s]\n' % stage_name
                #     msg += '\nUse "unstage --erase" to clear them.'
                #     self.message = msg
                #     return []

                if os.path.exists(stage_db_path):
                    shutil.rmtree(stage_db_path)

        os.chdir(working_dir)

        return staged_entries
Beispiel #3
0
    def __init__(self, options):
        super(Unstage, self).__init__()

        if not options.branch:
            print('ERROR: Could not determine branch.', file=sys.stderr)
            sys.exit(1)

        root = find_hg_root()
        if root:
            os.chdir(root)
            os.chdir("..")

        if not os.path.exists('.hg'):
            os.chdir(working_dir)
            print('ERROR: Must be in root of working copy to stage.',
                  file=sys.stderr)
            sys.exit(1)

        stage_name = "default" if options.stage_name is None else options.stage_name

        stage_path = super(Unstage, self).get_staging_root(root, options)
        if not os.path.exists(stage_path):
            os.mkdir(stage_path)
        stage_db_path = os.path.join(stage_path, stage_name)
        stage_db_file = os.path.join(stage_db_path, 'stage.db')
        if not os.path.exists(stage_db_path):
            if options.erase_cache:
                return  # nothing more to do
            print(
                'ERROR: No modifications are currently staged in "%s" for committing.'
                % stage_name,
                file=sys.stderr)
            sys.exit(1)

        if options.erase_cache:
            print('All entries in the "%s" staging area were cleared.' %
                  stage_name)
            shutil.rmtree(stage_db_path)
            return

        if len(options.args) == 0:
            print('ERROR: No filter(s) specified for unstaging.',
                  file=sys.stderr)
            sys.exit(1)

        stage_db = {}
        if os.path.exists(stage_db_file):
            stage_db = super(Unstage, self).load_stage_db(stage_db_file)

        command = ['hg', 'status', '-q', '-C', '.']
        output = subprocess.Popen(
            command, stdout=subprocess.PIPE).communicate()[0].decode("utf-8")
        output_lines = fixup_renames(output.split('\n'))

        bad_keys = []
        for key in stage_db:
            for item in options.args:
                if item in key:
                    bad_keys.append(key)

        if len(bad_keys) == 0:
            print(
                'ERROR: Your specified filter(s) did not match any entries in the "%s" staging area.'
                % stage_name,
                file=sys.stderr)
            sys.exit(1)

        unstaged_entries = []
        for key in bad_keys:
            for line in output_lines:
                if key in line:
                    unstaged_entries.append(line)

        for key in bad_keys:
            if stage_db[key].snapshot is not None:
                # it's a snapshot, delete it as well
                snapshot_file = os.path.join(stage_db_path,
                                             stage_db[key].snapshot)
                os.remove(snapshot_file)
            del stage_db[key]

        if len(stage_db) == 0:
            if os.path.exists(stage_db_path):
                shutil.rmtree(stage_db_path)
        else:
            # save the new database
            super(Unstage, self).save_stage_db(stage_db, stage_db_file)

            if len(unstaged_entries):
                print(
                    'The following existing entries were removed from the "%s" staging area:'
                    % stage_name)
                s = Info.Status()
                s.process_lines(unstaged_entries, options)
            else:
                print(
                    'No unique entries were removed from the "%s" staging area.'
                    % stage_name)
Beispiel #4
0
    def __init__(self, options):
        super(Stage, self).__init__()

        def generate_snapshot(options, stage_db_path, file_path, entry):
            if (entry is None) or (entry.snapshot is None):
                if (not options.snapshot) or (not os.path.exists(file_path)):
                    return StageEntry(None, entry.state)
            if entry.state != 'M':
                print(
                    "ERROR: Only (M)odified files can be captured by snapshot.",
                    file=sys.stderr)
                sys.exit(1)
            ss = entry.snapshot
            if ss is None:
                ss = str(uuid.uuid4()).replace('-', '')
            snapshot_file_name = os.path.join(stage_db_path, ss)
            if os.path.exists(snapshot_file_name):
                os.remove(snapshot_file_name)  # we are refreshing the snapshot
            if not os.path.exists(stage_db_path):
                os.mkdir(stage_db_path)
            # use copy2() to make sure the snapshot shares the timestamp of
            # the source file at the time of creation
            shutil.copy2(file_path, snapshot_file_name)
            return StageEntry(ss, entry.state)

        if not options.branch:
            print('ERROR: Could not determine branch.', file=sys.stderr)
            sys.exit(1)

        root = find_hg_root()
        if root:
            os.chdir(root)
            os.chdir("..")

        if not os.path.exists('.hg'):
            os.chdir(working_dir)
            print('ERROR: Must be in root of working copy to stage.',
                  file=sys.stderr)
            sys.exit(1)

        stage_name = "default" if options.stage_name is None else options.stage_name

        stage_db = {}
        stage_path = super(Stage, self).get_staging_root(root, options)
        if not os.path.exists(stage_path):
            os.mkdir(stage_path)
        stage_db_path = os.path.join(stage_path, stage_name)
        stage_db_file = os.path.join(stage_db_path, 'stage.db')
        if os.path.exists(stage_db_file):
            if options.erase_cache:
                print('All staged entries in "%s" cleared.' % stage_name)
                shutil.rmtree(stage_db_path)
            else:
                stage_db = super(Stage, self).load_stage_db(stage_db_file)

        if options.erase_cache:
            return  # nothing else to do

        if (len(options.args) == 0):
            capture_count = 0
            for key in stage_db:
                if stage_db[key].snapshot is not None:
                    capture_count += 1
            if capture_count:
                try:
                    print(
                        'You are about to refresh snapshot entries in the "%s" staging area.'
                        % stage_name)
                    input(
                        'Press ENTER if this is the intent (or press Ctrl-C to abort):'
                    )
                except SyntaxError:
                    pass

        command = ['hg', 'status', '-q', '-C', '.']
        output = subprocess.Popen(
            command, stdout=subprocess.PIPE).communicate()[0].decode("utf-8")
        output_lines = fixup_renames(output.split('\n'))

        lines = []
        if len(options.args):
            newlines = []
            # they are specifying files to be staged...filter 'lines'
            # based on them
            for item in options.args:
                found = -1
                for i in range(len(output_lines)):
                    if item in output_lines[i]:
                        newlines.append(output_lines[i][2:])
                        break

            if len(newlines) == 0:
                print(
                    "ERROR: Your specified filter(s) did not match any pending changes in the working copy.",
                    file=sys.stderr)
                sys.exit(1)

            lines = newlines
        else:
            # strip off the status bit
            for i in range(len(output_lines)):
                lines.append(output_lines[i][2:])

        if len(lines) == 0:
            print("ERROR: No files have been selected for staging.",
                  file=sys.stderr)
            sys.exit(1)

        # filter out duplicate entries
        status_db = {}
        for path in output_lines:
            key = path[2:].strip()
            status_db[key] = StageEntry(None, path[:1])

        added_files = []
        refreshed_files = []

        # all the files in lines[] are to be added to the staging database
        for path in lines:
            if path in stage_db:
                stage_db[path] = generate_snapshot(options, stage_db_path,
                                                   path, stage_db[path])
                refreshed_files.append('%s %s' % (stage_db[path].state, path))
            else:
                for l in output_lines:
                    if path in l:
                        added_files.append(l)
                        break
                entry = StageEntry()
                if path in status_db:
                    entry = status_db[path]
                stage_db[path] = generate_snapshot(options, stage_db_path,
                                                   path, entry)

        bad_keys = []
        for key in stage_db:
            if key not in status_db:
                # this file had been staged, but is now no longer modified
                bad_keys.append(key)

        for key in bad_keys:
            if stage_db[key].snapshot is not None:
                # it's a snapshot, delete it as well
                snapshot_file = os.path.join(stage_db_path,
                                             stage_db[key].snapshot)
                os.remove(snapshot_file)
            del status_db[key]

        # save the new database
        super(Stage, self).save_stage_db(stage_db, stage_db_file)

        if len(added_files) or len(refreshed_files):
            s = Info.Status()
            if len(added_files):
                print(
                    'The following new %s entries were added to the "%s" staging area:'
                    % ('snapshot' if options.snapshot else 'reference',
                       stage_name))
                s.process_lines(added_files, options)
            if len(refreshed_files):
                print(
                    'The following snapshot entries were refreshed in the "%s" staging area:'
                    % stage_name)
                s.process_lines(refreshed_files, options)
        else:
            print('No unique entries were added to the "%s" staging area.' %
                  stage_name)
Beispiel #5
0
    def execute(self, options, quiet=False, **kwargs):
        if not options.branch:
            self.message = 'ERROR: Could not determine branch.'
            return False

        working_dir = os.getcwd()

        root = find_hg_root()
        if root:
            os.chdir(root)
            os.chdir("..")

        stage_path = StageIO().get_staging_root(root, options)
        if os.path.exists(stage_path):
            stages = os.listdir(stage_path)
            stage_path = os.path.join(".hg",
                                      "stage") if len(stages) != 0 else None
        else:
            stage_path = None

        if not os.path.exists('.hg'):
            os.chdir(working_dir)
            self.message = 'ERROR: Must be in root of working copy to shelf.'
            return False

        command = ['hg', 'status', '-q', '-C']
        if (options.include_filter is None) and (len(options.exclude_filter)
                                                 == 0):
            command.append(options.use_path)

        output = subprocess.Popen(
            command, stdout=subprocess.PIPE).communicate()[0].decode("utf-8")
        if len(output) > 0:
            lines = fixup_renames(output.split('\n'))

            shelf_name = 'shelf'
            if len(options.shelf_name) != 0:
                shelf_name = options.shelf_name
            shelf_name_unquoted = shelf_name
            shelf_name = quote(shelf_name, '')

            if 'mb_root' in kwargs:
                root = kwargs['mb_root']
            else:
                root = find_mb_root(
                )  # this will not return if we can't find a working location

            manifest_version = 0
            manifest = []
            manifest_name = os.path.join(root, '%s.manifest' % shelf_name)
            manifest_archive = os.path.join(root, '%s.7z' % shelf_name)
            manifest_comment = ''

            timestamp = hex(int(time.time()))[2:]
            if os.path.exists(manifest_name):
                # grab the previous comment
                manifest_lines = open(manifest_name).readlines()
                if manifest_lines[0].startswith('version '):
                    manifest_version = int(manifest_lines[0][8:])
                    if manifest_version >= 1:
                        manifest_comment = manifest_lines[1].rstrip()
                else:
                    manifest_comment = manifest_lines[0].rstrip()
                manifest_lines = None
                try:
                    os.rename(manifest_name,
                              '%s.%s' % (manifest_name, timestamp))
                except:
                    os.chdir(working_dir)
                    self.message = 'ERROR: Could not back up previous shelf.'
                    return False
            if os.path.exists(manifest_archive):
                try:
                    os.rename(manifest_archive,
                              '%s.%s' % (manifest_archive, timestamp))
                except:
                    os.chdir(working_dir)
                    self.message = 'ERROR: Could not back up previous shelf.'
                    return False

            if len(options.comment):
                manifest_comment = options.comment

            shelve_command = [
                options.seven_zip, 'a', manifest_archive,
                '@%s.list' % shelf_name
            ]

            for line in lines:
                line = line.strip()
                if not len(line):
                    continue

                if (options.include_filter is not None) or len(
                        options.exclude_filter):
                    if options.include_filter is not None:
                        if options.include_filter in line:
                            manifest.append(line)
                    if len(options.exclude_filter):
                        exclude = [
                            f for f in options.exclude_filter if f in line
                        ]
                        if len(exclude) == 0:
                            manifest.append(line)
                else:
                    manifest.append(line)

            if options.ide_state:
                # find all the .suo files and add them to the archive
                # (the Visual Studio .suo file maintains a record of all files that
                # were last open in the IDE)
                if os.path.exists('.vs'):  # VS2017+
                    for root, dirs, files in os.walk('.vs'):
                        if '.suo' in files:
                            manifest.append('X %s' %
                                            os.path.join(root, '.suo'))
                            options.extra_files.append(
                                os.path.join(root, '.suo'))
                else:  # +VS2013
                    files = glob.glob('*.suo')
                    if len(files):
                        options.extra_files += files
                        for file in files:
                            manifest.append('X %s' % file)

            lines_written = 0
            with open('%s.list' % shelf_name, 'w') as f:
                if stage_path is not None:
                    f.write('%s\n' %
                            stage_path)  # capture current staging metadata
                if isinstance(options.extra_files,
                              (list, tuple)) and len(options.extra_files):
                    # should be a path relative to the root of the working copy
                    f.write('%s\n' % '\n'.join(options.extra_files))
                for line in manifest:
                    action = line[0]
                    if action == 'M' or action == 'A':
                        f.write('%s\n' % line[2:])
                        lines_written += 1
                    if action == 'V':
                        # rename; place the current file into the backup in case it holds changes
                        f.write('%s\n' % line[2:].split(',')[1])
                        lines_written += 1

            if lines_written:
                output = subprocess.Popen(
                    shelve_command,
                    stdout=subprocess.PIPE).communicate()[0].decode("utf-8")
                lines = output.split('\n')

                something_went_wrong = True
                for line in lines:
                    line = line.rstrip()
                    if line == 'Everything is Ok':
                        something_went_wrong = False
                        break

                if something_went_wrong:
                    os.chdir(working_dir)
                    self.message = 'ERROR: Failed to construct shelf archive:\n%s' % output
                    return False

            os.remove('%s.list' % shelf_name)

            if (options.include_filter is not None) or len(
                    options.exclude_filter):
                for line in manifest:
                    action = line[0]
                    filename = line[2:]
                    if action == 'V':
                        filename = line[2:].split(',')[1]
                    command = ['hg', 'revert', filename]
                    output = subprocess.Popen(
                        command,
                        stdout=subprocess.PIPE).communicate()[0].decode(
                            "utf-8")
            elif not options.no_revert:
                command = ['hg', 'revert', '--all']
                if options.use_path != '.':
                    command.append(options.use_path)
                output = subprocess.Popen(
                    command,
                    stdout=subprocess.PIPE).communicate()[0].decode("utf-8")
                if stage_path is not None:
                    shutil.rmtree(
                        stage_path)  # remove current staging metadata

            with open(manifest_name, 'w') as f:
                f.write('version %d\n' % MANIFEST_VERSION)
                f.write('%s\n' % manifest_comment)
                for line in manifest:
                    action = line[0]
                    if os.name == 'nt':
                        file_name = line[2:].replace('/', '\\')
                    else:
                        file_name = line[2:].replace('\\', '/')
                    #timestamp = 0.0
                    changeset = ''

                    if action == 'M':
                        changeset = hashlib.md5(open(file_name,
                                                     'rb').read()).hexdigest()

                    elif action == 'V':
                        # the revert above may have left the renamed file in place
                        # we are nice, and clean it up for them...
                        from_name, to_name = file_name.split(',')
                        if os.path.exists(to_name):
                            try:
                                os.remove(to_name)
                            except:
                                self.message = 'ERROR: Failed to remove renamed file "%s"!' % to_name
                                return False

                        changeset = hashlib.md5(open(from_name,
                                                     'rb').read()).hexdigest()

                    f.write('%s?%s?%s\n' % (action, file_name, changeset))

            batch_text = ''
            if os.name == 'nt':
                batch_text = '@echo off\n'
                batch_text += 'set FG=%_fg\n'
                batch_text += 'set BG=%_bg\n'

            manifest = colorize_status(manifest)

            if not quiet:
                print('Shelved the following state as microbranch "%s":' %
                      shelf_name_unquoted)
                for line in manifest:
                    if options.ansi_color:
                        if options.ansi_color_requires_batch:
                            batch_text += 'echo %s\n' % line[3]
                        else:
                            print(line[3])
                    else:
                        print(line[0], line[1])

                if options.ansi_color:
                    if options.ansi_color_requires_batch:
                        if os.name == 'nt':
                            batch_text += 'color %FG on %BG\n'
                        open(options.batch_file_name, 'w').write(batch_text)
                        os.system(options.batch_file_name)
                if options.no_revert:
                    print(
                        '\nAs requested, changes have been left in the working copy.'
                    )
        else:
            if not quiet:
                print('Nothing to shelve.')

        os.chdir(working_dir)
        return True
Beispiel #6
0
    def __init__(self, options):
        if not options.branch:
            return

        command = ['hg', 'log', '-v', '--removed']
        if len(options.log_rev) != 0:
            command += ['-r', options.log_rev]
        elif len(options.log_user):
            command += ['-u', options.log_user]
        elif len(options.log_date):
            command += ['-d', options.log_date]
        elif len(options.log_keyword):
            command += ['-k', options.log_keyword]

        if options.log_limit != 0:
            command += ['-l', options.log_limit]

        if len(options.log_rev) == 0:
            if len(options.log_branch):
                command += ['-b', options.log_branch]
            else:
                command += ['-b', options.branch]

        if options.log_no_merges:
            command.append('-M')

        if options.detailed:
            command.append('--debug')

        if len(options.log_template):
            command += ['-T', options.log_template]
            output = subprocess.Popen(
                command,
                stdout=subprocess.PIPE).communicate()[0].decode("utf-8")
            print(output)
        else:
            output = subprocess.Popen(
                command,
                stdout=subprocess.PIPE).communicate()[0].decode("utf-8")
            if len(output) == 0:
                print("ERROR: Invalid revision provided", file=sys.stderr)
                sys.exit(1)

            lines = output.split('\n')

            blank_count = 0
            has_file_changes = False
            id = ''

            i = 0
            while i < len(lines):
                line = lines[i]
                if line.startswith('changeset: '):
                    if len(id) != 0:
                        print('-' * 40)
                    id = line.split(' ')[-1]
                    if ':' in id:
                        id = id.split(':')[1]
                    print('%s%s%s' %
                          (Colors['BrightGreen'], line, Colors['Reset']))
                    has_file_changes = False
                elif line.startswith('description:'):
                    print(line)

                    blank_count = 0
                    i += 1
                    while i < len(lines):
                        if len(lines[i].strip()) == 0:
                            blank_count += 1
                        else:
                            if blank_count:
                                print('\n' * blank_count)
                                blank_count = 0
                            print('%s%s%s' % (Colors['BrightYellow'], lines[i],
                                              Colors['Reset']))

                        i += 1
                        try:
                            if (lines[i].startswith('changeset: ')):
                                i -= 1
                                break
                        except IndexError:
                            pass  # we've exceeded the array bounds

                    if has_file_changes:
                        print('changes:')
                        command = ['hg', 'status', '-C', '--change', id]
                        output = subprocess.Popen(
                            command,
                            stdout=subprocess.PIPE).communicate()[0].decode(
                                "utf-8")
                        if len(output) == 0:
                            print("ERROR: Invalid revision provided",
                                  file=sys.stderr)
                            sys.exit(1)

                        change_lines = fixup_renames(output.split('\n'))
                        Status().process_lines(change_lines, options)

                    print(Colors['Reset'])
                elif line.startswith('files:'):
                    has_file_changes = True
                else:
                    print(line)

                i += 1