Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
1566e9a
Collapse ObjectChanges when merging with dependency graph
arthanson Nov 17, 2025
6369f74
Collapse ObjectChanges when merging simplified
arthanson Nov 18, 2025
5fa597d
tests
arthanson Nov 18, 2025
c19d82d
fix tests
arthanson Nov 18, 2025
c717166
update tests
arthanson Nov 18, 2025
44af813
dependency graph
arthanson Nov 18, 2025
dca51ea
fix ruff
arthanson Nov 18, 2025
f80fed9
truncate delete
arthanson Nov 19, 2025
9e94a21
Improved dependency merge
arthanson Nov 19, 2025
86a2e89
fix ruff
arthanson Nov 19, 2025
4b316ad
revert changes with collapse
arthanson Nov 19, 2025
50df037
revert tests and fixes
arthanson Nov 19, 2025
14a9e6c
revert tests and fixes
arthanson Nov 20, 2025
177a978
fix tests
arthanson Nov 20, 2025
c2dfa0b
fix tests
arthanson Nov 20, 2025
5bea48b
retain old code
arthanson Nov 20, 2025
874a263
refactor
arthanson Nov 20, 2025
dafd988
add selection for merge strategy
arthanson Nov 20, 2025
f7e5360
cleanup, simplify revert, fix merge ordering for delete
arthanson Nov 20, 2025
506d56d
dependency checking fixes
arthanson Nov 21, 2025
20b32a5
dependency checking fixes
arthanson Nov 21, 2025
53a48da
cleanup
arthanson Nov 21, 2025
a92f101
cleanup
arthanson Nov 21, 2025
39c1930
cleanup
arthanson Nov 21, 2025
3739d00
job timeout
arthanson Nov 21, 2025
c43d1a7
fix circuit termination
arthanson Nov 22, 2025
e65304f
fix circuit termination
arthanson Nov 22, 2025
01780c6
cleanup
arthanson Nov 24, 2025
7286c2c
cleanup
arthanson Nov 24, 2025
a5e59ba
cleanup
arthanson Nov 24, 2025
e55d714
refactor to separate file and add class methods for apply, undo
arthanson Nov 26, 2025
6def45d
collapse_changes to merge_strategy
arthanson Nov 26, 2025
722585e
ruff fixes
arthanson Nov 26, 2025
71a3232
move to merge strategies
arthanson Nov 26, 2025
944382f
remove Django comment from migration
arthanson Nov 26, 2025
0696909
fixes
arthanson Nov 26, 2025
1f5d1f5
cleanup
arthanson Nov 26, 2025
f08a421
cleanup
arthanson Nov 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion netbox_branching/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ def merge(self, request, pk):
job = MergeBranchJob.enqueue(
instance=branch,
user=request.user,
commit=commit
commit=commit,
job_timeout=MergeBranchJob.Meta.job_timeout
)

return Response(JobSerializer(job, context={'request': request}).data)
Expand Down
10 changes: 10 additions & 0 deletions netbox_branching/choices.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ class BranchStatusChoices(ChoiceSet):
)


class BranchMergeStrategyChoices(ChoiceSet):
ITERATIVE = 'iterative'
SQUASH = 'squash'

CHOICES = (
(ITERATIVE, _('Iterative')),
(SQUASH, _('Squash')),
)


class BranchEventTypeChoices(ChoiceSet):
PROVISIONED = 'provisioned'
SYNCED = 'synced'
Expand Down
675 changes: 675 additions & 0 deletions netbox_branching/collapse_merge.py

Large diffs are not rendered by default.

13 changes: 12 additions & 1 deletion netbox_branching/forms/misc.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django import forms
from django.utils.translation import gettext_lazy as _

from netbox_branching.choices import BranchMergeStrategyChoices
from netbox_branching.models import ChangeDiff

__all__ = (
Expand All @@ -20,14 +21,24 @@ class BranchActionForm(forms.Form):
label=_('Commit changes'),
help_text=_('Leave unchecked to perform a dry run')
)
merge_strategy = forms.ChoiceField(
choices=BranchMergeStrategyChoices,
initial=BranchMergeStrategyChoices.ITERATIVE,
label=_('Merge Strategy'),
help_text=_('Strategy to use when merging changes.')
)

def __init__(self, branch, *args, allow_commit=True, **kwargs):
def __init__(self, branch, *args, allow_commit=True, action=None, **kwargs):
self.branch = branch
super().__init__(*args, **kwargs)

if not allow_commit:
self.fields['commit'].disabled = True

# Only show merge_strategy for merge operations, not revert
if action == 'revert':
del self.fields['merge_strategy']

def clean(self):
super().clean()

Expand Down
18 changes: 18 additions & 0 deletions netbox_branching/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ class SyncBranchJob(JobRunner):
"""
class Meta:
name = 'Sync branch'
job_timeout = 3600 # 1 hour - increased for large syncs

@property
def job_timeout(self):
"""Return the job timeout from Meta."""
return getattr(self.Meta, 'job_timeout', None)

def _disconnect_signal_receivers(self):
"""
Expand Down Expand Up @@ -101,6 +107,12 @@ class MergeBranchJob(JobRunner):
"""
class Meta:
name = 'Merge branch'
job_timeout = 3600 # 1 hour (in seconds) - increased from default 300s for large merges

@property
def job_timeout(self):
"""Return the job timeout from Meta."""
return getattr(self.Meta, 'job_timeout', None)

def run(self, commit=True, *args, **kwargs):
# Initialize logging
Expand All @@ -122,6 +134,12 @@ class RevertBranchJob(JobRunner):
"""
class Meta:
name = 'Revert branch'
job_timeout = 3600 # 1 hour - increased for large reverts

@property
def job_timeout(self):
"""Return the job timeout from Meta."""
return getattr(self.Meta, 'job_timeout', None)

def run(self, commit=True, *args, **kwargs):
# Initialize logging
Expand Down
14 changes: 14 additions & 0 deletions netbox_branching/merge_strategies/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""
Merge strategy implementations for branch operations.
"""
from .strategy import MergeStrategy, get_merge_strategy
from .iterative import IterativeMergeStrategy
from .squash import SquashMergeStrategy


__all__ = (
'MergeStrategy',
'IterativeMergeStrategy',
'SquashMergeStrategy',
'get_merge_strategy',
)
58 changes: 58 additions & 0 deletions netbox_branching/merge_strategies/iterative.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""
Iterative merge strategy implementation.
"""
from django.db import DEFAULT_DB_ALIAS

from netbox.context_managers import event_tracking
from utilities.exceptions import AbortTransaction

from .strategy import MergeStrategy


__all__ = (
'IterativeMergeStrategy',
)


class IterativeMergeStrategy(MergeStrategy):
"""
Iterative merge strategy that applies/reverts changes one at a time in chronological order.
"""

def merge(self, branch, changes, request, commit, logger):
"""
Apply changes iteratively in chronological order.
"""
models = set()

for change in changes:
models.add(change.changed_object_type.model_class())
with event_tracking(request):
request.id = change.request_id
request.user = change.user
change.apply(branch, using=DEFAULT_DB_ALIAS, logger=logger)

if not commit:
raise AbortTransaction()

branch._cleanup(models)

def revert(self, branch, changes, request, commit, logger):
"""
Undo changes iteratively (one at a time) in reverse chronological order.
"""
models = set()

# Undo each change from the Branch
for change in changes:
models.add(change.changed_object_type.model_class())
with event_tracking(request):
request.id = change.request_id
request.user = change.user
change.undo(branch, logger=logger)

if not commit:
raise AbortTransaction()

# Perform cleanup tasks
branch._cleanup(models)
Loading
Loading