Skip to content

Commit

Permalink
Edit all messages of consecutive squashed commits in one go
Browse files Browse the repository at this point in the history
Closes #77
  • Loading branch information
krobelus committed May 18, 2021
1 parent e27bc16 commit 13278f3
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 7 deletions.
53 changes: 46 additions & 7 deletions gitrevise/todo.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import re
from enum import Enum
from typing import List, Optional
from typing import List, Optional, Tuple

from .odb import Commit, Repository, MissingObject
from .utils import run_editor, run_sequence_editor, edit_commit_message, cut_commit
Expand Down Expand Up @@ -240,19 +240,58 @@ def edit_todos(
return result


def is_fixup(todo: Step) -> bool:
return todo.kind in (StepKind.FIXUP, StepKind.SQUASH)


def squash_message_template(
target_message: bytes, fixups: List[Tuple[StepKind, bytes]]
) -> bytes:
fused = (
b"# This is a combination of %d commits.\n" % (len(fixups) + 1)
+ b"# This is the 1st commit message:\n"
+ b"\n"
+ target_message
)

for i, (kind, message) in enumerate(fixups):
fused += b"\n"
if kind == StepKind.FIXUP:
fused += (
b"# The commit message #%d will be skipped:\n" % (i + 2)
+ b"\n"
+ b"".join(b"# " + line for line in message.splitlines(keepends=True))
)
else:
assert kind == StepKind.SQUASH
fused += b"# This is the commit message #%d:\n\n%s" % (i + 2, message)

return fused


def apply_todos(current: Commit, todos: List[Step], reauthor: bool = False) -> Commit:
for step in todos:
fixups: List[Tuple[StepKind, bytes]] = []

for i, step in enumerate(todos):
rebased = step.commit.rebase(current).update(message=step.message)
if step.kind == StepKind.PICK:
current = rebased
elif step.kind == StepKind.FIXUP:
elif is_fixup(step):
if not fixups:
fixup_target_message = current.message
fixups.append((step.kind, rebased.message))
current = current.update(tree=rebased.tree())
is_last_fixup = i + 1 == len(todos) or not is_fixup(todos[i + 1])
if is_last_fixup and any(
kind == StepKind.SQUASH for kind, message in fixups
):
current = current.update(
message=squash_message_template(fixup_target_message, fixups)
)
current = edit_commit_message(current)
fixups.clear()
elif step.kind == StepKind.REWORD:
current = edit_commit_message(rebased)
elif step.kind == StepKind.SQUASH:
fused = current.message + b"\n\n" + rebased.message
current = current.update(tree=rebased.tree(), message=fused)
current = edit_commit_message(current)
elif step.kind == StepKind.CUT:
current = cut_commit(rebased)
elif step.kind == StepKind.INDEX:
Expand Down
87 changes: 87 additions & 0 deletions tests/test_fixup.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,3 +375,90 @@ def test_autosquash_multiline_summary(repo):
new = repo.get_commit("HEAD")
assert old != new, "commit was modified"
assert old.parents() == new.parents(), "parents are unchanged"


def test_autosquash_multiple_squashes(repo):
bash(
"""
git commit --allow-empty -m 'initial commit'
git commit --allow-empty -m 'target'
git commit --allow-empty --squash :/^target --no-edit
git commit --allow-empty --squash :/^target --no-edit
"""
)

with editor_main([":/^init", "--autosquash"], input=b"") as ed:
with ed.next_file() as f:
assert f.startswith_dedent(
"""\
# This is a combination of 3 commits.
# This is the 1st commit message:
target
# This is the commit message #2:
squash! target
# This is the commit message #3:
squash! target
"""
)
f.replace_dedent("two squashes")


def test_autosquash_multiple_squashes_with_fixup(repo):
bash(
"""
git commit --allow-empty -m 'initial commit'
git commit --allow-empty -m 'target'
git commit --allow-empty --squash :/^target --no-edit
git commit --allow-empty --fixup :/^target --no-edit
git commit --allow-empty --squash :/^target --no-edit
git commit --allow-empty -m 'unrelated'
"""
)
with editor_main([":/^init", "--autosquash"], input=b"") as ed:
with ed.next_file() as f:
assert f.startswith_dedent(
"""\
# This is a combination of 4 commits.
# This is the 1st commit message:
target
# This is the commit message #2:
squash! target
# The commit message #3 will be skipped:
# fixup! target
# This is the commit message #4:
squash! target
"""
)
f.replace_dedent("squashes + fixup")


def test_autosquash_multiple_independent_squashes(repo):
bash(
"""
git commit --allow-empty -m 'initial commit'
git commit --allow-empty -m 'target1'
git commit --allow-empty -m 'target2'
git commit --allow-empty --squash :/^target1 --no-edit
git commit --allow-empty --squash :/^target2 --no-edit
"""
)

with editor_main([":/^init", "--autosquash"], input=b"") as ed:
with ed.next_file() as f:
assert f.startswith_dedent("# This is a combination of 2 commits.")
f.replace_dedent("squash 1")
with ed.next_file() as f:
assert f.startswith_dedent("# This is a combination of 2 commits.")
f.replace_dedent("squash 2")

0 comments on commit 13278f3

Please sign in to comment.