File: git_prune_all.py

package info (click to toggle)
perfetto 54.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 133,812 kB
  • sloc: cpp: 338,350; python: 74,464; sql: 46,895; ansic: 18,340; javascript: 2,557; java: 2,160; sh: 1,444; yacc: 776; xml: 563; makefile: 226
file content (162 lines) | stat: -rwxr-xr-x 5,560 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#!/usr/bin/env python3
import argparse
import sys
import os
from typing import Dict, List, Optional, Set

# Allow importing of root-relative modules.
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(os.path.join(ROOT_DIR))

#pylint: disable=wrong-import-position
from python.tools.git_utils import (
    get_all_branches,
    get_branch_children,
    get_current_branch,
    run_git_command,
    topological_sort_branches,
    MAINLINE_BRANCHES,
)
#pylint: enable=wrong-import-position


def main():
  parser = argparse.ArgumentParser(
      description='Deletes local branches identical (no diff) to effective parent. Updates children.',
      formatter_class=argparse.RawTextHelpFormatter)
  parser.add_argument(
      '--dry-run',
      action='store_true',
      help="Show actions without making changes.")
  args = parser.parse_args()

  if args.dry_run:
    print("--- DRY RUN MODE ---")

  # --- Phase 1: Check and Map ---
  print("Analyzing branch structure...")
  sorted_branches: List[str] = []
  parent_graph: Dict[str, Optional[str]] = {}
  try:
    sorted_branches, parent_graph = topological_sort_branches()
  except ValueError as e:
    print(f"Error: {e}", file=sys.stderr)
    sys.exit(1)
  except Exception as e:
    print(f"Dependency analysis error: {e}", file=sys.stderr)
    sys.exit(1)

  if not sorted_branches:
    print("No branches with parent configurations found.")
    sys.exit(0)

  remap: Dict[str, str] = {}  # {pruned_branch: effective_parent_it_matched}
  branches_to_prune: Set[str] = set()
  all_local_branches = set(get_all_branches())

  print("Checking branches against effective parents...")
  for branch in sorted_branches:
    original_parent = parent_graph.get(branch)
    if original_parent is None:
      continue
    effective_parent = remap.get(original_parent, original_parent)
    if not effective_parent:
      continue
    if effective_parent not in all_local_branches and effective_parent not in MAINLINE_BRANCHES:
      continue

    diff_cmd = ['diff', '--quiet', effective_parent, branch]
    try:
      diff_result = run_git_command(diff_cmd, check=False)
      is_identical = (diff_result.returncode == 0)
    except Exception as e:
      print(
          f"Warning: Error diffing '{effective_parent}'..'{branch}': {e}. Skipping.",
          file=sys.stderr)
      continue

    if is_identical:
      print(
          f"- Found: '{branch}' identical to effective parent '{effective_parent}'. Marking for prune."
      )
      branches_to_prune.add(branch)
      remap[branch] = effective_parent

  if not branches_to_prune:
    print("\nNo branches found to be pruned.")
    sys.exit(0)

  # --- Phase 2: Perform Actions ---
  print("\n--- Actions ---")
  if args.dry_run:
    print("Dry Run - Would perform:")
    for branch_to_prune in sorted(list(branches_to_prune)):
      final_parent = remap.get(branch_to_prune, "???")
      print(
          f" - Delete branch '{branch_to_prune}' (identical to '{final_parent}')"
      )
      children = get_branch_children(branch_to_prune, list(all_local_branches))
      children_to_reparent = [c for c in children if c not in branches_to_prune]
      if children_to_reparent:
        print(
            f"   - Re-parent children ({', '.join(children_to_reparent)}) to '{final_parent}'"
        )
  else:
    print("Performing re-parenting and deletions...")
    current_checked_out_branch = get_current_branch()
    all_local_branches_list = list(all_local_branches)
    reparent_errors = False
    processed_children = set()

    print("Updating parent config for children...")
    for branch_to_prune in branches_to_prune:
      new_parent = remap.get(branch_to_prune)
      if not new_parent:
        print(
            f"Error: No remap parent for '{branch_to_prune}'. Skipping children.",
            file=sys.stderr)
        reparent_errors = True
        continue
      children = get_branch_children(branch_to_prune, all_local_branches_list)
      for child in children:
        if child not in branches_to_prune and child not in processed_children:
          try:
            print(
                f" - Setting parent of '{child}' to '{new_parent}' (was '{branch_to_prune}')"
            )
            run_git_command(['config', f'branch.{child}.parent', new_parent])
            processed_children.add(child)
          except Exception as e:
            print(f"Error updating config for '{child}': {e}", file=sys.stderr)
            reparent_errors = True

    print("Deleting branches...")
    delete_errors = False
    for branch_to_prune in sorted(list(branches_to_prune)):
      if branch_to_prune == current_checked_out_branch:
        print(f"Skipping delete of '{branch_to_prune}' (checked out).")
        continue
      print(f" - Deleting branch '{branch_to_prune}'...")
      try:
        # Use -D for force delete, as branch might not appear merged
        run_git_command(['branch', '-D', branch_to_prune])
      except SystemExit:
        delete_errors = True  # Report error but continue deleting others
      except Exception as e:
        print(
            f"Unexpected error deleting '{branch_to_prune}': {e}",
            file=sys.stderr)
        delete_errors = True

    if reparent_errors or delete_errors:
      print("\nWarning: Errors occurred.", file=sys.stderr)
      sys.exit(1)

  print(f"\n--- Pruning process finished ---")
  print(
      f"Branches {'would have been' if args.dry_run else 'were'} pruned: {len(branches_to_prune)}"
  )


if __name__ == "__main__":
  main()