# Dev CLI Design Document ## 1. Overview ### 1.1 Purpose The Dev CLI is a patch management system for Chromium development that acts like git but specifically for managing patches against the Chromium codebase. It enables developers to: - Extract patches from git commits in a Chromium repository - Store patches as individual file diffs - Track which files belong to which features - Apply patches to new Chromium versions with conflict resolution - Manage the upgrade process when moving to newer Chromium versions ### 1.2 Key Design Principles - **Simplicity**: Mirror git's command structure and workflow - **Transparency**: Store patches as readable diff files - **Flexibility**: Support both individual file and feature-based operations - **Robustness**: Handle conflicts gracefully during Chromium upgrades ## 2. Architecture ### 2.1 Directory Structure ``` nxtscape/ ├── build/ │ ├── dev.py # Main CLI entry point │ ├── context.py # Shared BuildContext (used by both build.py and dev.py) │ └── modules/ │ └── dev_cli/ # Dev CLI modules │ ├── __init__.py │ ├── extract.py # Extract commands (extract, extract-range) │ ├── apply.py # Apply commands (apply --all, apply --feature) │ ├── feature.py # Feature management (add, list, show, generate-patch) │ └── utils.py # Shared utilities (diff parsing, git commands) ├── chromium_src/ # Individual file patches (mirrors Chromium structure) │ ├── chrome/ │ │ ├── app/ │ │ │ └── chrome_command_ids.h.patch │ │ └── browser/ │ │ └── ui/ │ │ ├── ui_features.h.patch │ │ └── views/ │ │ └── side_panel/ │ │ └── third_party_llm/ │ │ ├── third_party_llm_panel_coordinator.cc.patch │ │ └── third_party_llm_view.cc.patch # New file (diff against /dev/null) │ └── ... ├── features.yaml # Feature to file mapping └── patches/ # Legacy patches directory (existing) └── browseros/ └── llm-chat.patch # Combined patch for reference ``` ### 2.2 Module Structure Each module in `build/modules/dev_cli/` is self-contained and registers its own Click commands: #### 2.2.1 Module Organization - **extract.py**: Handles extracting patches from git commits - **apply.py**: Handles applying patches with conflict resolution - **feature.py**: Manages feature-to-file mappings - **utils.py**: Shared utilities for git operations, diff parsing, etc. #### 2.2.2 BuildContext Integration The Dev CLI reuses the existing `BuildContext` from `build/context.py` with additional methods: ```python # In context.py - extended for dev CLI class BuildContext: # ... existing code ... def get_dev_patches_dir(self) -> Path: """Get individual patches directory (chromium_src/)""" return join_paths(self.root_dir, "chromium_src") def get_features_yaml_path(self) -> Path: """Get features.yaml file path""" return join_paths(self.root_dir, "features.yaml") def get_patch_path_for_file(self, file_path: str) -> Path: """Convert a chromium file path to patch file path""" return join_paths(self.get_dev_patches_dir(), f"{file_path}.patch") ``` ### 2.2 File Formats #### 2.2.1 Patch Files (.patch) Standard unified diff format (git diff output): ```diff --- a/chrome/app/chrome_command_ids.h +++ b/chrome/app/chrome_command_ids.h @@ -290,6 +290,8 @@ #define IDC_SHOW_HISTORY_SIDE_PANEL 40293 #define IDC_OPEN_GLIC 40294 #define IDC_FIND_EXTENSIONS 40295 +#define IDC_SHOW_THIRD_PARTY_LLM_SIDE_PANEL 40296 +#define IDC_CYCLE_THIRD_PARTY_LLM_PROVIDER 40297 ``` New files use /dev/null as the source: ```diff --- /dev/null +++ b/chrome/browser/ui/views/side_panel/third_party_llm/third_party_llm_view.cc @@ -0,0 +1,11 @@ +// Copyright 2024 The Chromium Authors +// ... file contents ... ``` #### 2.2.2 Features YAML ```yaml version: 1.0 features: llm-chat: description: "LLM chat integration in side panel" files: - chrome/app/chrome_command_ids.h - chrome/browser/ui/ui_features.h - chrome/browser/ui/views/side_panel/third_party_llm/third_party_llm_panel_coordinator.cc - chrome/browser/ui/views/side_panel/third_party_llm/third_party_llm_view.cc another-feature: description: "Another feature description" files: - chrome/app/chrome_command_ids.h # Note: can be in multiple features - chrome/browser/some_other_file.cc ``` ## 3. Implementation Structure ### 3.1 Main Entry Point (build/dev.py) The main entry point uses Click's group mechanism to register subcommands from modules: ```python #!/usr/bin/env python3 """Dev CLI for Chromium patch management""" import click from pathlib import Path from context import BuildContext from modules.dev_cli import extract, apply, feature from utils import log_info, log_error @click.group() @click.option('--chromium-src', '-S', type=click.Path(exists=True, path_type=Path), help='Path to Chromium source directory') @click.pass_context def cli(ctx, chromium_src): """Dev CLI - Chromium patch management tool""" # Store chromium_src in context for subcommands ctx.ensure_object(dict) ctx.obj['chromium_src'] = chromium_src or None # Register subcommand groups from modules cli.add_command(extract.extract_group) cli.add_command(apply.apply_group) cli.add_command(feature.feature_group) if __name__ == '__main__': cli() ``` ### 3.2 Module Implementation Examples #### 3.2.1 Extract Module (build/modules/dev_cli/extract.py) ```python import click from pathlib import Path from context import BuildContext from modules.dev_cli.utils import run_git_command, parse_diff_output from utils import log_info, log_error, log_success @click.group(name='extract') def extract_group(): """Extract patches from git commits""" pass @extract_group.command(name='commit') @click.argument('commit') @click.pass_context def extract_commit(ctx, commit): """Extract patches from a single commit""" chromium_src = ctx.obj.get('chromium_src') if not chromium_src: log_error("--chromium-src is required") return # Create BuildContext build_ctx = BuildContext( root_dir=Path.cwd(), chromium_src=chromium_src, architecture="", # Not needed for patch operations build_type="debug" # Not needed for patch operations ) if extract_single_commit(build_ctx, commit): log_success(f"Successfully extracted patches from {commit}") else: log_error(f"Failed to extract patches from {commit}") @extract_group.command(name='range') @click.argument('base_commit') @click.argument('head_commit') @click.pass_context def extract_range(ctx, base_commit, head_commit): """Extract patches from a range of commits""" # Similar implementation... def extract_single_commit(ctx: BuildContext, commit_hash: str) -> bool: """Implementation of single commit extraction""" # Implementation as detailed in section 4 pass ``` #### 3.2.2 Apply Module (build/modules/dev_cli/apply.py) ```python import click from pathlib import Path from context import BuildContext from modules.dev_cli.utils import apply_single_patch from utils import log_info, log_error, log_success @click.group(name='apply') def apply_group(): """Apply patches to Chromium source""" pass @apply_group.command(name='all') @click.option('--commit-each', is_flag=True, help='Create git commit after each patch') @click.option('--dry-run', is_flag=True, help='Test patches without applying') @click.pass_context def apply_all(ctx, commit_each, dry_run): """Apply all patches from chromium_src/""" chromium_src = ctx.obj.get('chromium_src') if not chromium_src: log_error("--chromium-src is required") return build_ctx = BuildContext( root_dir=Path.cwd(), chromium_src=chromium_src, architecture="", build_type="debug" ) apply_all_patches(build_ctx, commit_each, dry_run) @apply_group.command(name='feature') @click.argument('feature_name') @click.option('--commit-each', is_flag=True, help='Create git commit after each patch') @click.option('--dry-run', is_flag=True, help='Test patches without applying') @click.pass_context def apply_feature(ctx, feature_name, commit_each, dry_run): """Apply patches for a specific feature""" # Implementation... ``` #### 3.2.3 Feature Module (build/modules/dev_cli/feature.py) ```python import click import yaml from pathlib import Path from context import BuildContext from utils import log_info, log_error, log_success @click.group(name='feature') def feature_group(): """Manage feature-to-file mappings""" pass @feature_group.command(name='add') @click.argument('feature_name') @click.argument('commit') @click.pass_context def add_feature(ctx, feature_name, commit): """Add files from a commit to a feature""" # Implementation... @feature_group.command(name='list') @click.pass_context def list_features(ctx): """List all features""" build_ctx = BuildContext( root_dir=Path.cwd(), chromium_src=Path.cwd(), # Not used for listing architecture="", build_type="debug" ) features = load_features_yaml(build_ctx) for name, data in features.items(): file_count = len(data.get('files', [])) description = data.get('description', 'No description') log_info(f" {name} ({file_count} files) - {description}") @feature_group.command(name='show') @click.argument('feature_name') @click.pass_context def show_feature(ctx, feature_name): """Show details of a specific feature""" # Implementation... ``` ## 4. Command Specifications ### 4.1 Extract Commands #### 4.1.1 `extract commit` - Extract patches from a single commit ```bash dev-cli --chromium-src extract commit # or dev-cli extract commit # if chromium-src is in config ``` **Implementation Steps:** 1. Validate commit exists in the repository 2. Get diff for the commit: `git diff ^..` 3. Parse diff output to identify changed files 4. For each changed file: - Extract the file path relative to chromium src - Detect if file is new (doesn't exist in parent commit) or modified - Create directory structure in `chromium_src/` mirroring the file path - Write individual patch file with `.patch` extension 5. Log summary of extracted patches **Code Structure:** ```python def extract_single_commit(commit_hash: str, chromium_src: Path) -> bool: # Step 1: Validate commit if not validate_commit_exists(commit_hash, chromium_src): return False # Step 2: Get diff diff_output = run_git_command(['git', 'diff', f'{commit_hash}^..{commit_hash}'], cwd=chromium_src) # Step 3: Parse diff into file patches file_patches = parse_diff_output(diff_output) # Step 4: Write individual patches for file_path, patch_content in file_patches.items(): write_patch_file(file_path, patch_content) # Step 5: Log summary log_extraction_summary(file_patches) return True ``` #### 4.1.2 `extract range` - Extract patches from a range of commits ```bash dev-cli --chromium-src extract range ``` **Implementation Steps:** 1. Validate both commits exist 2. Get cumulative diff: `git diff ..` 3. Parse diff to identify all changed files 4. For each file: - Detect if new or modified (check if exists at base commit) - Create/update patch file in `chromium_src/` 5. Handle file deletions (create `.deleted` marker files) 6. Log summary with statistics **Code Structure:** ```python def extract_commit_range(base_commit: str, head_commit: str, chromium_src: Path) -> bool: # Step 1: Validate commits if not validate_commit_exists(base_commit, chromium_src): return False if not validate_commit_exists(head_commit, chromium_src): return False # Step 2: Get cumulative diff diff_output = run_git_command(['git', 'diff', f'{base_commit}..{head_commit}'], cwd=chromium_src) # Step 3-5: Process diff file_patches = parse_diff_output(diff_output) for file_path, patch_content in file_patches.items(): if patch_content is None: # File was deleted create_deletion_marker(file_path) else: write_patch_file(file_path, patch_content) # Step 6: Log summary log_extraction_summary(file_patches) return True ``` ### 4.2 Apply Commands #### 4.2.1 `apply all` - Apply all patches ```bash dev-cli --chromium-src apply all [--commit-each] [--dry-run] ``` **Implementation Steps:** 1. Validate chromium_src is a git repository 2. If `--dry-run`, check which patches would apply cleanly 3. Recursively find all `.patch` files in `chromium_src/` directory 4. Sort patches alphabetically by path (deterministic order) 5. For each patch: - Apply using `git apply -p1 ` - If conflict, handle based on conflict resolution flow - If `--commit-each`, create git commit after successful application 6. Report summary of applied/failed patches **Conflict Resolution Flow:** ```python def handle_patch_conflict(patch_path: Path, chromium_src: Path) -> bool: print(f"CONFLICT: {patch_path}") print("Options:") print(" 1) Fix manually and continue") print(" 2) Skip this patch") print(" 3) Abort all remaining patches") while True: choice = input("Enter choice (1-3): ") if choice == "1": input("Fix the conflicts and press Enter to continue...") return True # Continue with next patch elif choice == "2": print(f"Skipping {patch_path}") return True # Skip but continue elif choice == "3": return False # Abort ``` #### 4.2.2 `apply feature` - Apply patches for a specific feature ```bash dev-cli --chromium-src apply feature [--commit-each] [--dry-run] ``` **Implementation Steps:** 1. Load `features.yaml` file 2. Validate feature exists in the YAML 3. Get list of files for the feature 4. For each file in the feature: - Construct patch file path in `chromium_src/` - Apply patch using same logic as `apply --all` 5. Handle conflicts with same resolution flow 6. Report feature-specific summary **Code Structure:** ```python def apply_feature_patches(feature_name: str, chromium_src: Path, commit_each: bool = False) -> bool: # Step 1-2: Load and validate features = load_features_yaml() if feature_name not in features: print(f"Feature '{feature_name}' not found") return False # Step 3: Get file list file_list = features[feature_name]['files'] # Step 4-5: Apply patches success_count = 0 fail_count = 0 for file_path in file_list: patch_path = construct_patch_path(file_path) if apply_single_patch(patch_path, chromium_src): success_count += 1 if commit_each: create_git_commit(f"Apply {feature_name}: {file_path}") else: fail_count += 1 # Step 6: Report print(f"Feature '{feature_name}': {success_count} applied, {fail_count} failed") return fail_count == 0 ``` ### 4.3 Feature Management Commands #### 4.3.1 `feature add` - Add files from a commit to a feature ```bash dev-cli --chromium-src feature add ``` **Implementation Steps:** 1. Get list of files changed in the commit 2. Load or create `features.yaml` 3. Add/update feature entry with file list 4. Detect duplicates and warn 5. Save updated YAML **Code Structure:** ```python def add_feature_from_commit(feature_name: str, commit_hash: str, chromium_src: Path) -> bool: # Step 1: Get changed files changed_files = get_commit_changed_files(commit_hash, chromium_src) # Step 2: Load features features = load_features_yaml() # Step 3-4: Update feature if feature_name in features: existing_files = set(features[feature_name]['files']) new_files = set(changed_files) - existing_files if new_files: features[feature_name]['files'].extend(new_files) print(f"Added {len(new_files)} new files to '{feature_name}'") else: print(f"All files already in '{feature_name}'") else: features[feature_name] = { 'description': f"Feature added from commit {commit_hash}", 'files': changed_files } print(f"Created new feature '{feature_name}' with {len(changed_files)} files") # Step 5: Save save_features_yaml(features) return True ``` #### 4.3.2 `feature list` - List all features ```bash dev-cli feature list ``` **Implementation Steps:** 1. Load `features.yaml` 2. Display formatted list of features with file counts 3. Show features sorted alphabetically **Output Example:** ``` Features: llm-chat (4 files) - LLM chat integration in side panel another-feature (2 files) - Another feature description Total: 2 features, 6 unique files ``` #### 4.3.3 `feature show` - Show details of a feature ```bash dev-cli feature show ``` **Implementation Steps:** 1. Load `features.yaml` 2. Validate feature exists 3. Display feature details and file list 4. Check patch status for each file **Output Example:** ``` Feature: llm-chat Description: LLM chat integration in side panel Files (4): ✓ chrome/app/chrome_command_ids.h ✓ chrome/browser/ui/ui_features.h ✗ chrome/browser/ui/views/side_panel/third_party_llm/third_party_llm_panel_coordinator.cc (patch missing) ✓ chrome/browser/ui/views/side_panel/third_party_llm/third_party_llm_view.cc ``` #### 4.3.4 `feature generate-patch` - Generate combined patch for a feature ```bash dev-cli feature generate-patch [--output ] ``` **Implementation Steps:** 1. Load feature file list from YAML 2. Concatenate all individual patches in order 3. Generate unified patch header 4. Write to output file or stdout ## 4. Core Modules Implementation ### 4.1 Diff Parser Module ```python # modules/diff_parser.py def parse_diff_output(diff_output: str) -> Dict[str, Optional[str]]: """ Parse git diff output into individual file patches. Returns: Dict mapping file path to patch content. None value indicates file was deleted. """ patches = {} current_file = None current_patch = [] for line in diff_output.splitlines(): if line.startswith('diff --git'): # Save previous patch if exists if current_file: patches[current_file] = '\n'.join(current_patch) # Parse new file path parts = line.split() current_file = parts[2][2:] if parts[2].startswith('a/') else parts[2] current_patch = [line] elif line.startswith('deleted file'): patches[current_file] = None # Mark as deleted elif current_file: current_patch.append(line) # Save last patch if current_file: patches[current_file] = '\n'.join(current_patch) if current_patch else None return patches ``` ### 4.2 Patch Writer Module ```python # modules/patch_writer.py def write_patch_file(file_path: str, patch_content: str) -> bool: """ Write a patch file to chromium_src directory structure. """ # Construct output path output_path = Path('chromium_src') / file_path output_path = output_path.with_suffix(output_path.suffix + '.patch') # Create directory structure output_path.parent.mkdir(parents=True, exist_ok=True) # Write patch content output_path.write_text(patch_content) print(f" Written: {output_path}") return True def create_deletion_marker(file_path: str) -> bool: """ Create a marker file for deleted files. """ marker_path = Path('chromium_src') / file_path marker_path = marker_path.with_suffix(marker_path.suffix + '.deleted') marker_path.parent.mkdir(parents=True, exist_ok=True) marker_path.write_text(f"File deleted in patch\n") print(f" Marked deleted: {marker_path}") return True ``` ### 4.3 Patch Application Module ```python # modules/patch_apply.py def apply_single_patch(patch_path: Path, chromium_src: Path, interactive: bool = True) -> bool: """ Apply a single patch file to chromium source. """ if not patch_path.exists(): print(f"Patch file not found: {patch_path}") return False # Try standard apply cmd = ['git', 'apply', '-p1', str(patch_path)] result = run_command(cmd, cwd=chromium_src, capture=True) if result.returncode == 0: print(f" ✓ Applied: {patch_path.name}") return True # Try 3-way merge cmd.append('--3way') result = run_command(cmd, cwd=chromium_src, capture=True) if result.returncode == 0: print(f" ✓ Applied (3-way): {patch_path.name}") return True # Handle conflict if interactive: return handle_patch_conflict(patch_path, chromium_src) else: print(f" ✗ Failed: {patch_path.name}") return False ``` ### 4.4 Feature Manager Module ```python # modules/feature_manager.py def load_features_yaml() -> Dict: """Load features.yaml file.""" features_path = Path('features.yaml') if not features_path.exists(): return {'version': '1.0', 'features': {}} with open(features_path) as f: data = yaml.safe_load(f) return data.get('features', {}) def save_features_yaml(features: Dict) -> None: """Save features to YAML file.""" features_path = Path('features.yaml') data = { 'version': '1.0', 'features': features } with open(features_path, 'w') as f: yaml.dump(data, f, default_flow_style=False, sort_keys=False) ``` ## 5. Error Handling ### 5.1 Common Error Scenarios 1. **Invalid Chromium Source Path** - Check if path exists and is a git repository - Check for presence of chromium markers (BUILD.gn, chrome/ directory) 2. **Git Command Failures** - Wrap all git commands in try-except - Provide clear error messages with git stderr output - Suggest fixes (e.g., "commit not found, check if you're in the right branch") 3. **Patch Conflicts** - Detect via git apply return code - Offer interactive resolution - Allow skipping problematic patches 4. **File Permission Issues** - Check write permissions before operations - Provide clear error messages about permission requirements 5. **Malformed YAML** - Validate YAML structure on load - Provide line numbers for syntax errors - Create backup before modifying ### 5.2 Error Recovery ```python def safe_operation(operation_func, *args, **kwargs): """ Wrapper for safe execution with rollback capability. """ backup_created = False try: # Create backup if modifying files if kwargs.get('create_backup', True): create_backup() backup_created = True # Execute operation result = operation_func(*args, **kwargs) # Clean up backup on success if backup_created: cleanup_backup() return result except Exception as e: print(f"Error: {e}") if backup_created: if prompt_yes_no("Restore from backup?"): restore_backup() raise ``` ## 6. Configuration ### 6.1 CLI Configuration File (.dev-cli.yaml) ```yaml # Optional configuration file defaults: chromium_src: /path/to/chromium/src auto_commit: false interactive: true aliases: # Short aliases for common operations up: "extract-range HEAD~1 HEAD" fix: "apply --all --commit-each" ``` ### 6.2 Environment Variables ```bash DEV_CLI_CHROMIUM_SRC=/path/to/chromium/src DEV_CLI_AUTO_COMMIT=true DEV_CLI_INTERACTIVE=false ``` ## 7. Testing Strategy ### 7.1 Unit Tests - Test diff parsing with various git diff outputs - Test patch file writing and directory creation - Test YAML loading/saving - Test conflict detection ### 7.2 Integration Tests - Create test repository with known commits - Test extract commands with various commit scenarios - Test apply commands with clean and conflicting patches - Test feature management operations ### 7.3 End-to-End Tests - Full workflow: extract → apply → fix conflicts → re-extract - Chromium version upgrade simulation - Multi-feature patch management ## 8. Implementation Phases ### Phase 1: Core Infrastructure (Foundation) 1. Create main CLI entry point with argument parsing 2. Implement configuration loading (CLI args, config file, env vars) 3. Create basic project structure and modules 4. Implement logging and error handling framework ### Phase 2: Extract Commands 1. Implement git command execution wrapper 2. Create diff parser module 3. Implement `extract` command for single commits 4. Implement `extract-range` command for commit ranges 5. Add patch file writer with directory structure creation ### Phase 3: Apply Commands 1. Implement patch discovery (finding .patch files) 2. Create patch application logic with git apply 3. Add conflict detection and resolution flow 4. Implement `apply --all` command 5. Add `--commit-each` support 6. Implement `--dry-run` mode ### Phase 4: Feature Management 1. Create YAML file handler for features.yaml 2. Implement `feature add` command 3. Implement `feature list` command 4. Implement `feature show` command 5. Add `apply --feature` command 6. Implement `feature generate-patch` command ### Phase 5: Polish and Optimization 1. Add progress bars for long operations 2. Implement caching for repeated operations 3. Add verbose and quiet modes 4. Create comprehensive help documentation 5. Add shell completion scripts ## 9. CLI Usage Examples ### 9.1 Initial Setup ```bash # Set chromium source path (can also be in config) export CHROMIUM_SRC=~/chromium/src # Extract patches from existing work dev-cli --chromium-src $CHROMIUM_SRC extract range chromium-base HEAD # Create feature from recent commit dev-cli --chromium-src $CHROMIUM_SRC feature add llm-chat HEAD~1 ``` ### 9.2 Daily Development ```bash # Make changes in chromium repo, commit them cd ~/chromium/src git add -A git commit -m "Fix: Updated LLM chat UI" # Extract the changes to patches dev-cli --chromium-src . extract commit HEAD # Or update patches from a range dev-cli --chromium-src . extract range main HEAD ``` ### 9.3 Chromium Upgrade Workflow ```bash # Check what will apply cleanly dev-cli --chromium-src $CHROMIUM_SRC apply all --dry-run # Apply all patches with commit tracking dev-cli --chromium-src $CHROMIUM_SRC apply all --commit-each # If conflicts occur: # 1. Fix conflicts in chromium/src # 2. Press Enter to continue applying remaining patches # 3. Build and test # 4. Extract all changes to update patches dev-cli --chromium-src $CHROMIUM_SRC extract range chromium-base HEAD ``` ### 9.4 Feature-based Operations ```bash # Apply only LLM chat feature dev-cli --chromium-src $CHROMIUM_SRC apply feature llm-chat # See what's in a feature dev-cli feature show llm-chat # Generate combined patch for sharing dev-cli feature generate-patch llm-chat --output llm-chat-combined.patch ``` ## 10. Future Enhancements (Not in MVP) 1. **Patch Dependencies**: Track which patches depend on others 2. **Automatic Conflict Resolution**: Use AI/heuristics for simple conflicts 3. **Patch Versioning**: Track patch history across Chromium versions 4. **Collaboration Features**: Share patches via git/cloud 5. **Build Integration**: Trigger builds after successful patch application 6. **Patch Validation**: Pre-check if patches will build before applying 7. **Bisect Support**: Find which patch broke the build 8. **Patch Statistics**: Track success rates across versions ## 11. Success Criteria The dev CLI will be considered successful when: 1. Developers can extract patches from any git commit or range 2. Patches can be applied to clean Chromium with clear conflict resolution 3. Features can be tracked and managed independently 4. The Chromium upgrade process is streamlined and predictable 5. The tool requires minimal manual intervention for common operations 6. Error messages are clear and actionable 7. The tool is faster than manual patch management ## 12. Non-Goals The following are explicitly NOT goals for this tool: 1. Managing chromium source code fetching/syncing (use depot_tools) 2. Building chromium (use existing build system) 3. Managing git branches (use git directly) 4. Automatic merge conflict resolution (manual resolution required) 5. Patch optimization or combination (maintain 1:1 file mapping)