def _test_delete_archived_branches(target_directory): # type: (Text) -> None with _run_test(target_directory): _initialize_repo() # Create all the branches make_child_branch("first_branch") assert get_current_branch() == "first_branch" git("checkout master") make_child_branch("second_branch") assert get_current_branch() == "second_branch" make_child_branch("second_branch_child_one") assert get_current_branch() == "second_branch_child_one" make_child_branch("second_branch_child_two") assert get_current_branch() == "second_branch_child_two" git("checkout master") with get_branch_tracker() as tracker: tracker.set_is_archived("first_branch", True) tracker.set_is_archived("second_branch_child_two", True) # Test delete_archived_branches() with get_branch_tracker() as tracker: assert not tracker.is_branch_tracked("first_branch") assert tracker.is_branch_tracked("second_branch") assert tracker.is_branch_tracked("second_branch_child_one") assert not tracker.is_branch_tracked("second_branch_child_two")
def rebase_children(is_recursive, extra_git_rebase_args=()): # type: (bool, Sequence[Text]) -> None current_branch = get_current_branch() if extra_git_rebase_args: # If the first extra arg starts with "-", "--" must also have been passed, and # argparse doesn't remove it for us if extra_git_rebase_args[0] == "--": extra_git_rebase_args = extra_git_rebase_args[1:] extra_args = " " + " ".join(extra_git_rebase_args) else: extra_args = "" with get_branch_tracker() as tracker: do_rebase(tracker, tracker.parent_for_child(current_branch), current_branch, extra_args) if is_recursive: to_rebase_onto = [current_branch] while to_rebase_onto: parent = to_rebase_onto.pop() children = tracker.children_for_parent(parent) for child in children: do_rebase(tracker, parent, child, extra_args) to_rebase_onto.append(child) # Go back to where we started. git("checkout {}".format(current_branch))
def run_command(self, args): # type: (Namespace) -> None extra_arc_land_options = args.arc_land_args if extra_arc_land_options: # If the first extra arg starts with "-", "--" must also have been passed, and # argparse doesn't remove it for us if extra_arc_land_options[0] == "--": del extra_arc_land_options[0] extra_args = " " + " ".join(extra_arc_land_options) else: extra_args = "" current_branch = get_current_branch() with get_branch_tracker() as tracker: parent = tracker.parent_for_child(current_branch) fail_if_not_rebased(current_branch, parent, tracker) if parent != "master": should_land = raw_input( "Are you sure you want to land onto non-master branch " "'{}'? [y/N] ".format(parent)) should_land = should_land.lower() if should_land not in ("y", "yes"): print "Aborting land" exit() arc("land --onto {}{}".format(parent, extra_args)) # Successfully landed, replace ourselves with our parent tracker.collapse_and_remove_parent(current_branch)
def rename_current_branch(new_branch_name): # type: (str) -> None current_branch = get_current_branch() with get_branch_tracker() as tracker: git("checkout -b %s" % new_branch_name) git("branch -d %s" % current_branch) tracker.rename_branch(current_branch, new_branch_name)
def rename_current_branch(new_branch_name): # type: (Text) -> None current_branch = get_current_branch() with get_branch_tracker() as tracker: git("checkout -b {}".format(new_branch_name)) git("branch -d {}".format(current_branch)) tracker.rename_branch(current_branch, new_branch_name)
def _test_clean_branches(target_directory): # type: (Text) -> None with _run_test(target_directory): _initialize_repo() # Create all the branches make_child_branch("valid_branch") assert get_current_branch() == "valid_branch" git("checkout master") make_child_branch("ghost_branch_childless") assert get_current_branch() == "ghost_branch_childless" git("checkout master") make_child_branch("ghost_branch_with_children") assert get_current_branch() == "ghost_branch_with_children" make_child_branch("child_of_ghost_branch") assert get_current_branch() == "child_of_ghost_branch" git("checkout master") print("Deleting branches from git") git("branch -D ghost_branch_childless") git("branch -D ghost_branch_with_children") print("Test cleaning by archiving") clean_invalid_branches(dry_run=False, archive=True, upstream=False) with get_branch_tracker() as tracker: assert not tracker.is_archived("valid_branch") assert tracker.is_archived("ghost_branch_childless") assert tracker.is_archived("ghost_branch_with_children") print("Clear archived flags for next test") tracker.set_is_archived("ghost_branch_childless", False) tracker.set_is_archived("ghost_branch_with_children", False) print("Test cleaning by deleting") clean_invalid_branches(dry_run=False, archive=False, upstream=False) with get_branch_tracker() as tracker: assert tracker.is_branch_tracked("valid_branch") assert not tracker.is_branch_tracked("ghost_branch_childless") assert tracker.is_branch_tracked("ghost_branch_with_children")
def rename_current_branch(new_branch_name, force): # type: (Text, bool) -> None current_branch = get_current_branch() with get_branch_tracker() as tracker: git("checkout -b {}".format(new_branch_name)) flag = "-D" if force else "-d" git("branch {} {}".format(flag, current_branch)) tracker.rename_branch(current_branch, new_branch_name)
def run_command(self, args): # type: (Namespace) -> None new_parent = args.new_parent current_branch = get_current_branch() with get_branch_tracker() as tracker: tracker.set_parent(current_branch, new_parent) print "You may want to rebase on top of the new parent to make sure its changes are " \ "visible in this branch."
def make_child_branch(new_branch_name): # type: (str) -> None current_branch = get_current_branch() current_rev = hash_for(current_branch) with get_branch_tracker() as tracker: # Add the child using the current branch as the parent. tracker.add_child_for_parent(current_branch, new_branch_name, current_rev) # Make the new branch, also where the current branch is git("checkout -b %s" % new_branch_name)
def delete_archived_branches(): # type: () -> None with get_branch_tracker() as tracker: for branch in tracker.linearized_branches(): if tracker.is_branch_tracked(branch) and tracker.is_archived( branch): if tracker.children_for_parent(branch): print( "Skipping deletion of archived branch {} because it has children" .format(branch)) else: print("Deleting {}".format(branch)) tracker.remove_child_leaf(branch)
def main(force_remove): # type: (bool) -> None current_branch = get_current_branch() with get_branch_tracker() as tracker: parent = tracker.parent_for_child(current_branch) children = tracker.children_for_parent(current_branch) assert not children, "Child branch should not have any children, found %s child(ren)" % len(children) git("checkout %s" % parent) # This will fail if we're not forcing the remove and the branch isn't merged in. delete_flag = "-D" if force_remove else "-d" git("branch %s %s" % (delete_flag, current_branch)) tracker.remove_child_leaf(current_branch)
def clean_invalid_branches(dry_run, archive, upstream): # type: (bool, bool, bool) -> None if upstream: # Make sure we have latest remote info git("fetch --prune") with get_branch_tracker() as tracker: for branch in tracker.linearized_branches(): if _is_branch_invalid(tracker, branch, upstream): if archive: _archive_invalid_branch(dry_run, tracker, branch) else: _delete_invalid_branch_if_possible(dry_run, tracker, branch, upstream)
def get_branch_info(branch, use_null_delimiter): # type: (Optional[Text], bool) -> Text if branch is None: branch = get_current_branch() with get_branch_tracker() as tracker: if not tracker.has_parent(branch): sys.exit("Branch does not have a parent: {}".format(branch)) parent = tracker.parent_for_child(branch) base = tracker.base_for_branch(branch) if use_null_delimiter: return "{}\0{}".format(parent, base) else: return "Parent branch: {}; Base revision: {}".format(parent, base)
def print_branch_structure(): # type: () -> None current_branch = get_current_branch() with get_branch_tracker() as tracker: roots = [] for parent in tracker.get_all_parents(): if not tracker.has_parent(parent): roots.append(parent) first = True for root in roots: if not first: print "" first = False print format_node(current_branch, root) print_tree(tracker, current_branch, root, "")
def run_command(self, args): # type: (Namespace) -> None extra_arc_diff_options = args.arc_diff_args if extra_arc_diff_options: # If the first extra arg starts with "-", "--" must also have been passed, and # argparse doesn't remove it for us if extra_arc_diff_options[0] == "--": del extra_arc_diff_options[0] extra_args = " " + " ".join(extra_arc_diff_options) else: extra_args = "" current_branch = get_current_branch() with get_branch_tracker() as tracker: base = tracker.base_for_branch(current_branch) arc("diff {}{}".format(base, extra_args))
def get_branch_structure_string(show_all): # type: (bool) -> str current_branch = get_current_branch() structure_parts = [] # type: List[str] with get_branch_tracker() as tracker: roots = [] for parent in tracker.get_all_parents(): if not tracker.has_parent(parent): roots.append(parent) skipped_archived = _get_branch_structure_parts_internal( tracker, current_branch, roots, structure_parts, show_all) if skipped_archived: structure_parts.append("(not displaying archived branches, run with --all to see them)") return "\n".join(structure_parts)
def rebase_children(is_recursive): # type: (bool) -> None current_branch = get_current_branch() with get_branch_tracker() as tracker: do_rebase(tracker, tracker.parent_for_child(current_branch), current_branch) if is_recursive: to_rebase_onto = [current_branch] while to_rebase_onto: parent = to_rebase_onto.pop() children = tracker.children_for_parent(parent) for child in children: do_rebase(tracker, parent, child) to_rebase_onto.append(child) # Go back to where we started. git("checkout %s" % current_branch)
def rebase_children(is_recursive): # type: (bool) -> None current_branch = get_current_branch() with get_branch_tracker() as tracker: do_rebase(tracker, tracker.parent_for_child(current_branch), current_branch) if is_recursive: to_rebase_onto = [current_branch] while to_rebase_onto: parent = to_rebase_onto.pop() children = tracker.children_for_parent(parent) for child in children: do_rebase(tracker, parent, child) to_rebase_onto.append(child) # Go back to where we started. git("checkout {}".format(current_branch))
def main(): # type: () -> None current_branch = get_current_branch() with get_branch_tracker() as tracker: parent = tracker.parent_for_child(current_branch) fail_if_not_rebased(current_branch, parent, tracker) if parent != "master": should_land = raw_input("Are you sure you want to land onto non-master branch '%s'? [y/N] " % parent) should_land = should_land.lower() if should_land not in ("y", "yes"): print "Aborting land" exit() arc("land --onto %s" % parent) # Successfully landed, replace ourselves with our parent tracker.collapse_and_remove_parent(current_branch)
def get_branch_structure_string(show_all): # type: (bool) -> Text current_branch = get_current_branch() structure_parts = [] # type: List[Text] with get_branch_tracker() as tracker: roots = [] for parent in tracker.get_all_parents(): if not tracker.has_parent(parent): roots.append(parent) roots = sorted(roots) skipped_archived = _get_branch_structure_parts_internal( tracker, current_branch, roots, structure_parts, show_all) if skipped_archived: structure_parts.append("(not displaying archived branches, run with --all to see them)") return "\n".join(structure_parts)
def remove_branch(force_remove): # type: (bool) -> None current_branch = get_current_branch() current_commit = hash_for(current_branch) with get_branch_tracker() as tracker: parent = tracker.parent_for_child(current_branch) children = tracker.children_for_parent(current_branch) assert not children, \ "Child branch should not have any children, found {} child(ren)".format(len(children)) merged_into_parent = [ line[2:] for line in git("branch --merged {}".format(parent)).split('\n') if line ] if current_branch in merged_into_parent: print("Removing merged branch {!r} (was at commit {})".format( current_branch, current_commit)) elif force_remove: print("Force removing unmerged branch {!r} (was at commit {})". format( current_branch, current_commit, )) else: print("") print("!!!!!!!!") print("!!! Trying to remove branch {!r} not merged into its parent. Re-run with" \ "".format(current_branch)) print( "!!! '--force' if you want to force the deletion of this branch." ) print("!!!") print("!!! WARNING: Running with '--force' may cause data loss") print("!!!!!!!!") print("") exit(1) git("checkout {}".format(parent)) # This will fail if we're not forcing the remove and the branch isn't merged in. delete_flag = "-D" if force_remove else "-d" git("branch {} {}".format(delete_flag, current_branch)) tracker.remove_child_leaf(current_branch)
def make_child_branch(new_branch_name, revision=None): # type: (Text, Optional[Text]) -> None parent = get_current_branch() if revision is None: # Use the current revision as the base base_rev = hash_for("HEAD") else: # Use the merge-base of the given revision and the parent branch as the base base_rev = git("merge-base {} {}".format(parent, revision)).strip() with get_branch_tracker() as tracker: # Add the child using the current branch as the parent. tracker.add_child_for_parent(parent, new_branch_name, base_rev) # Make the new branch, either where the current branch is or at the specified revision if revision is None: command = "checkout -b {}".format(new_branch_name) else: command = "checkout -b {} {}".format(new_branch_name, revision) git(command)
def set_archived(archived, branch_name=None): # type: (bool, Optional[str]) -> None if branch_name is None: branch_name = get_current_branch() with get_branch_tracker() as tracker: tracker.set_is_archived(branch_name, archived)
def run_command(self, args): # type: (Namespace) -> None with get_branch_tracker() as tracker: remove_branch(tracker, branch_name=args.branch_name or get_current_branch(), force_remove=args.force)
def main(new_parent): # type: (str) -> None current_branch = get_current_branch() with get_branch_tracker() as tracker: tracker.set_parent(current_branch, new_parent) print "You may want to rebase on top of the new parent to make sure its changes are visible in this branch."