コード例 #1
0
def do_rebase(tracker, parent, child):
    # type: (BranchTracker, str, str) -> None
    bases = tracker.bases_for_branch(child)

    if len(bases) == 2:
        first_base, second_base = bases
        has_first_base = does_branch_contain_commit(child, first_base)
        has_second_base = does_branch_contain_commit(child, second_base)
        # Should have at least one of the two bases
        assert has_first_base or has_second_base
        if has_first_base and has_second_base:
            # Choose the newer one. The older one will be the merge base of the two
            older_base = git("merge-base %s %s" % (first_base, second_base))
            first_is_newer = older_base == second_base
            base = first_base if first_is_newer else second_base
        else:
            # Only has one, choose the one that it does have
            base = bases[0] if has_first_base else bases[1]
        tracker.finish_rebase(child, base)
    else:
        base = bases[0]

    parent_rev = hash_for(parent)

    tracker.start_rebase(child, parent_rev)
    git("rebase --onto %s %s %s" % (parent, base, child))
    tracker.finish_rebase(child, parent_rev)
コード例 #2
0
def do_rebase(tracker, parent, child):
    # type: (BranchTracker, str, str) -> None
    bases = tracker.bases_for_branch(child)

    if len(bases) == 2:
        first_base, second_base = bases
        has_first_base = does_branch_contain_commit(child, first_base)
        has_second_base = does_branch_contain_commit(child, second_base)
        # Should have at least one of the two bases
        assert has_first_base or has_second_base
        if has_first_base and has_second_base:
            # Choose the newer one. The older one will be the merge base of the two
            older_base = git("merge-base {} {}".format(first_base,
                                                       second_base))
            first_is_newer = older_base == second_base
            base = first_base if first_is_newer else second_base
        else:
            # Only has one, choose the one that it does have
            base = bases[0] if has_first_base else bases[1]
        tracker.finish_rebase(child, base)
    else:
        base = bases[0]

    parent_rev = hash_for(parent)

    tracker.start_rebase(child, parent_rev)
    git("rebase --onto {} {} {}".format(parent, base, child))
    tracker.finish_rebase(child, parent_rev)
コード例 #3
0
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)
コード例 #4
0
def do_rebase(tracker, parent, child):
    # type: (BranchTracker, Text, Text) -> None
    base = tracker.base_for_branch(child)
    parent_rev = hash_for(parent)
    if base == parent_rev:
        return

    tracker.start_rebase(child, parent_rev)
    git("rebase --onto {} {} {}".format(parent, base, child))
    tracker.finish_rebase(child, parent_rev)
コード例 #5
0
def do_rebase(tracker, parent, child, extra_args):
    # type: (BranchTracker, Text, Text, Text) -> None
    base = tracker.base_for_branch(child)
    parent_rev = hash_for(parent)
    if base == parent_rev:
        return

    tracker.start_rebase(child, parent_rev)
    # Can't use the `git` function since we don't want to capture/return output text.
    command = "rebase --onto {} {} {}{}".format(parent, base, child,
                                                extra_args)
    run_command_expecting_failure(subprocess.check_call, "git", command)
    tracker.finish_rebase(child, parent_rev)
コード例 #6
0
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)
コード例 #7
0
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)
コード例 #8
0
def _integration_test(target_directory):
    # type: (str) -> None
    target_directory = os.path.expanduser(target_directory)
    target_container = os.path.dirname(target_directory)
    assert not os.path.exists(target_directory)
    assert os.path.isdir(target_container)

    with run_test(target_directory):
        # Initialize a repo and add a first commit so we can tell what branch we're on.
        print "Initializing repo"
        git("init")
        open("hello.txt", "w").close()
        git("add .")
        git("commit -am initial_commit")

        assert get_current_branch() == "master"
        original_commit = hash_for("HEAD")

        # Create all the branches
        make_child_branch("first_branch")
        assert get_current_branch() == "first_branch"

        make_child_branch("second_branch")
        assert get_current_branch() == "second_branch"

        make_child_branch("third_branch")
        assert get_current_branch() == "third_branch"

        # Rename a branch
        rename_current_branch("third_branch_renamed")
        assert get_current_branch() == "third_branch_renamed"

        # Rename it back
        rename_current_branch("third_branch")
        assert get_current_branch() == "third_branch"

        # This should be sibling to second_branch, on top of first_branch
        git("checkout first_branch")
        make_child_branch("sibling_branch")
        assert get_current_branch() == "sibling_branch"

        # Make the first "real" commit, in master
        print "First commit"
        git("checkout master")
        with open(os.path.join(target_directory, "hello.txt"), "w") as f:
            f.write("Hello!")
        # Avoiding spaces because of how we break up args in the `git` function
        git("commit -am first_commit_message")
        first_commit = hash_for("HEAD")
        assert original_commit != first_commit

        # Do the recursive rebase
        print "Rebase first_branch and its children on top of master."
        git("checkout first_branch")
        rebase_children(True)
        assert first_commit == hash_for("first_branch")
        assert first_commit == hash_for("second_branch")
        assert first_commit == hash_for("third_branch")
        assert first_commit == hash_for("sibling_branch")

        # Make a second commit, this time in first_branch
        print "Make a second commit in first_branch"
        git("checkout first_branch")
        with open(os.path.join(target_directory, "hello.txt"), "w") as f:
            f.write("Hello there!")
        git("commit -am second_commit_message")
        second_commit = hash_for("HEAD")
        assert original_commit != second_commit and first_commit != second_commit

        # Rebase just second_branch. This should update third_branch but shouldn't touch
        # sibling_branch.
        print "Doing second rebase"
        git("checkout second_branch")
        rebase_children(True)
        assert second_commit == hash_for("first_branch")
        assert second_commit == hash_for("second_branch")
        assert second_commit == hash_for("third_branch")
        assert first_commit == hash_for("sibling_branch")

        print "Make a merge conflict in sibling_branch"
        # Add a new commit to the sibling branch, delete the branch, and re-create it at the
        # revision it was at.
        git("checkout sibling_branch")
        # Make a conflicting change on sibling so that we can test rebasing it later.
        with open(os.path.join(target_directory, "hello.txt"), "w") as f:
            f.write("Hello conflict")
        git("commit -am conflicting_change_message")
        sibling_conflicting_commit = hash_for('HEAD')

        print "Test deleting branch"
        # See that removing fails since it's not merged
        _assert_fails(lambda: remove_branch(force_remove=False))
        assert get_current_branch() == 'sibling_branch'
        assert sibling_conflicting_commit == hash_for('HEAD')
        remove_branch(force_remove=True)
        assert get_current_branch() == 'first_branch'
        assert second_commit == hash_for('HEAD')

        print "Test creating branch at specific revision"
        make_child_branch('sibling_branch', sibling_conflicting_commit)
        assert get_current_branch() == 'sibling_branch'
        assert sibling_conflicting_commit == hash_for('HEAD')

        # This should throw since the rebase has conflicts
        print "Testing merge conflicts"
        _assert_fails(lambda: rebase_children(True))

        # Abort the rebase and try again
        git("rebase --abort")
        # It should fail for the same reason
        print "Testing merge conflicts again"
        _assert_fails(lambda: rebase_children(True))

        print "Resolving the merge conflict"
        with open(os.path.join(target_directory, "hello.txt"), "w") as f:
            f.write("Hello merge")
        git("add hello.txt")
        git("rebase --continue")

        # This should effectively no-op
        print "Doing no-op rebase"
        current_commit = hash_for("HEAD")
        rebase_children(True)
        assert current_commit == hash_for("HEAD")

        assert get_branch_structure_string(False) == UNARCHIVED_PRINT_STRUCTURE

        set_archived(True, "second_branch")
        assert get_branch_structure_string(False) == ARCHIVED_PRINT_STRUCTURE
        assert get_branch_structure_string(True) == UNARCHIVED_PRINT_STRUCTURE

        git("checkout second_branch")
        set_archived(False)
        assert get_branch_structure_string(False) == UNARCHIVED_PRINT_STRUCTURE
コード例 #9
0
def _unit_tests(target_directory):
    # type: (str) -> None
    target_directory = os.path.expanduser(target_directory)
    target_container = os.path.dirname(target_directory)
    assert not os.path.exists(target_directory)
    assert os.path.isdir(target_container)

    with run_test(target_directory):
        # Initialize a repo and add a first commit so we can tell what branch we're on.
        print "Initializing repo"
        git("init")
        open("hello.txt", "w").close()
        git("add .")
        git("commit -am initial_commit")

        assert get_current_branch() == "master"
        original_commit = hash_for("HEAD")

        # Create all the branches
        make_child_branch("first_branch")
        assert get_current_branch() == "first_branch"

        make_child_branch("second_branch")
        assert get_current_branch() == "second_branch"

        make_child_branch("third_branch")
        assert get_current_branch() == "third_branch"

        # Rename a branch
        rename_current_branch("third_branch_renamed")
        assert get_current_branch() == "third_branch_renamed"

        # Rename it back
        rename_current_branch("third_branch")
        assert get_current_branch() == "third_branch"

        # This should be sibling to second_branch, on top of first_branch
        git("checkout first_branch")
        make_child_branch("sibling_branch")
        assert get_current_branch() == "sibling_branch"

        # Make the first "real" commit, in master
        print "First commit"
        git("checkout master")
        with open(os.path.join(target_directory, "hello.txt"), "w") as f:
            f.write("Hello!")
        # Avoiding spaces because of how we break up args in the `git` function
        git("commit -am first_commit_message")
        first_commit = hash_for("HEAD")
        assert original_commit != first_commit

        # Do the recursive rebase
        print "Rebase first_branch and its children on top of master."
        git("checkout first_branch")
        rebase_children(True)
        assert first_commit == hash_for("first_branch")
        assert first_commit == hash_for("second_branch")
        assert first_commit == hash_for("third_branch")
        assert first_commit == hash_for("sibling_branch")

        # Make a second commit, this time in first_branch
        print "Make a second commit in first_branch"
        git("checkout first_branch")
        with open(os.path.join(target_directory, "hello.txt"), "w") as f:
            f.write("Hello there!")
        git("commit -am second_commit_message")
        second_commit = hash_for("HEAD")
        assert original_commit != second_commit and first_commit != second_commit

        # Rebase just second_branch. This should update third_branch but shouldn't touch sibling_branch.
        print "Doing second rebase"
        git("checkout second_branch")
        rebase_children(True)
        assert second_commit == hash_for("first_branch")
        assert second_commit == hash_for("second_branch")
        assert second_commit == hash_for("third_branch")
        assert first_commit == hash_for("sibling_branch")

        print "Make a merge conflict in sibling_branch"
        git("checkout sibling_branch")
        with open(os.path.join(target_directory, "hello.txt"), "w") as f:
            f.write("Hello conflict")
        git("commit -am conflicting_change_message")

        # This should throw since the rebase has conflicts
        print "Testing merge conflicts"
        _assert_fails(lambda: rebase_children(True))

        # Abort the rebase and try again
        git("rebase --abort")
        # It should fail for the same reason
        print "Testing merge conflicts again"
        _assert_fails(lambda: rebase_children(True))

        print "Resolving the merge conflict"
        with open(os.path.join(target_directory, "hello.txt"), "w") as f:
            f.write("Hello merge")
        git("add hello.txt")
        git("rebase --continue")

        # This should effectively no-op
        print "Doing no-op rebase"
        current_commit = hash_for("HEAD")
        rebase_children(True)
        assert current_commit == hash_for("HEAD")

        assert get_branch_structure_string(False) == UNARCHIVED_PRINT_STRUCTURE

        set_archived(True, "second_branch")
        assert get_branch_structure_string(False) == ARCHIVED_PRINT_STRUCTURE
        assert get_branch_structure_string(True) == UNARCHIVED_PRINT_STRUCTURE

        git("checkout second_branch")
        set_archived(False)
        assert get_branch_structure_string(False) == UNARCHIVED_PRINT_STRUCTURE
コード例 #10
0
def _integration_test(target_directory):
    # type: (Text) -> None
    with _run_test(target_directory):
        _initialize_repo()
        original_commit = hash_for("HEAD")

        # Create all the branches
        make_child_branch("first_branch")
        assert get_current_branch() == "first_branch"

        make_child_branch("second_branch")
        assert get_current_branch() == "second_branch"

        make_child_branch("third_branch")
        assert get_current_branch() == "third_branch"

        # Rename a branch
        rename_current_branch("third_branch_renamed", force=False)
        assert get_current_branch() == "third_branch_renamed"

        # Rename it back
        rename_current_branch("third_branch", force=False)
        assert get_current_branch() == "third_branch"

        # This should be sibling to second_branch, on top of first_branch
        git("checkout first_branch")
        make_child_branch("sibling_branch")
        assert get_current_branch() == "sibling_branch"

        # Make the first "real" commit, in master
        print("First commit")
        git("checkout master")
        with open(os.path.join(target_directory, "hello.txt"), "wb") as f:
            f.write(b"Hello!")
        # Avoiding spaces because of how we break up args in the `git` function
        git("commit -am first_commit_message")
        first_commit = hash_for("HEAD")
        assert original_commit != first_commit

        # Do the recursive rebase
        print("Rebase first_branch and its children on top of master.")
        git("checkout first_branch")
        rebase_children(True)
        assert first_commit == hash_for("first_branch")
        assert first_commit == hash_for("second_branch")
        assert first_commit == hash_for("third_branch")
        assert first_commit == hash_for("sibling_branch")

        # Make a second commit, this time in first_branch
        print("Make a second commit in first_branch")
        git("checkout first_branch")
        with open(os.path.join(target_directory, "hello.txt"), "wb") as f:
            f.write(b"Hello there!")
        git("commit -am second_commit_message")
        second_commit = hash_for("HEAD")
        assert original_commit != second_commit and first_commit != second_commit

        # Rebase just second_branch. This should update third_branch but shouldn't touch
        # sibling_branch.
        print("Doing second rebase")
        git("checkout second_branch")
        rebase_children(True)
        assert second_commit == hash_for("first_branch")
        assert second_commit == hash_for("second_branch")
        assert second_commit == hash_for("third_branch")
        assert first_commit == hash_for("sibling_branch")

        print("Make a merge conflict in sibling_branch")
        # Add a new commit to the sibling branch, delete the branch, and re-create it at the
        # revision it was at.
        git("checkout sibling_branch")
        # Make a conflicting change on sibling so that we can test rebasing it later.
        with open(os.path.join(target_directory, "hello.txt"), "wb") as f:
            f.write(b"Hello conflict")
        git("commit -am conflicting_change_message")
        sibling_conflicting_commit = hash_for("HEAD")

        print("Test deleting branch")
        # See that removing fails since it's not merged
        _assert_fails(lambda: _command_with_args(GitRemoveLeafBranch, []))
        assert get_current_branch() == "sibling_branch"
        assert sibling_conflicting_commit == hash_for("HEAD")
        _command_with_args(GitRemoveLeafBranch, ["--force"])
        assert get_current_branch() == "first_branch"
        assert second_commit == hash_for("HEAD")

        print("Test creating branch at specific revision")
        make_child_branch("sibling_branch", sibling_conflicting_commit)
        assert get_current_branch() == "sibling_branch"
        assert sibling_conflicting_commit == hash_for("HEAD")

        # This should throw since the rebase has conflicts
        print("Testing merge conflicts")
        _assert_fails(lambda: rebase_children(True))

        # Abort the rebase and try again
        git("rebase --abort")
        # It should fail for the same reason
        print("Testing merge conflicts again")
        _assert_fails(lambda: rebase_children(True))

        print("Resolving the merge conflict")
        with open(os.path.join(target_directory, "hello.txt"), "wb") as f:
            f.write(b"Hello merge")
        git("add hello.txt")
        git("rebase --continue")

        # This should effectively no-op
        print("Doing no-op rebase")
        current_commit = hash_for("HEAD")
        rebase_children(True)
        assert current_commit == hash_for("HEAD")

        unarchived_print_structure_sibling_branch = UNARCHIVED_PRINT_STRUCTURE.replace(
            "sibling_branch", make_green("sibling_branch")
        )
        archived_print_structure_sibling_branch = ARCHIVED_PRINT_STRUCTURE.replace(
            "sibling_branch", make_green("sibling_branch")
        )
        assert get_branch_structure_string(False) == unarchived_print_structure_sibling_branch

        set_archived(True, "second_branch")
        assert get_branch_structure_string(False) == archived_print_structure_sibling_branch
        assert get_branch_structure_string(True) == unarchived_print_structure_sibling_branch

        git("checkout second_branch")
        set_archived(False)
        assert get_branch_structure_string(False) == UNARCHIVED_PRINT_STRUCTURE.replace(
            "second_branch", make_green("second_branch")
        )

        current_commit = hash_for("HEAD")
        assert get_branch_info(
            branch=None, use_null_delimiter=False
        ) == "Parent branch: first_branch; Base revision: {}".format(current_commit)
        assert get_branch_info(branch=None, use_null_delimiter=True) == "first_branch\0{}".format(current_commit)
        try:
            get_branch_info(branch="master", use_null_delimiter=False)
            assert False, "Should not get here"
        except SystemExit as e:
            assert str(e) == "Branch does not have a parent: master"