##// END OF EJS Templates
mercurial: Add option to close a branch before merging
Mathieu Cantin -
r2055:5bbc6873 default
parent child Browse files
Show More
@@ -533,6 +533,7 b' get_repo_settings'
533 "hooks_outgoing_pull_logger": true,
533 "hooks_outgoing_pull_logger": true,
534 "phases_publish": "True",
534 "phases_publish": "True",
535 "rhodecode_hg_use_rebase_for_merging": true,
535 "rhodecode_hg_use_rebase_for_merging": true,
536 "rhodecode_hg_close_branch_before_merging": false,
536 "rhodecode_pr_merge_enabled": true,
537 "rhodecode_pr_merge_enabled": true,
537 "rhodecode_use_outdated_comments": true
538 "rhodecode_use_outdated_comments": true
538 }
539 }
@@ -420,7 +420,7 b' class BaseRepository(object):'
420
420
421 def merge(self, target_ref, source_repo, source_ref, workspace_id,
421 def merge(self, target_ref, source_repo, source_ref, workspace_id,
422 user_name='', user_email='', message='', dry_run=False,
422 user_name='', user_email='', message='', dry_run=False,
423 use_rebase=False):
423 use_rebase=False, close_branch=False):
424 """
424 """
425 Merge the revisions specified in `source_ref` from `source_repo`
425 Merge the revisions specified in `source_ref` from `source_repo`
426 onto the `target_ref` of this repository.
426 onto the `target_ref` of this repository.
@@ -445,6 +445,7 b' class BaseRepository(object):'
445 :param dry_run: If `True` the merge will not take place.
445 :param dry_run: If `True` the merge will not take place.
446 :param use_rebase: If `True` commits from the source will be rebased
446 :param use_rebase: If `True` commits from the source will be rebased
447 on top of the target instead of being merged.
447 on top of the target instead of being merged.
448 :param close_branch: If `True` branch will be close before merging it
448 """
449 """
449 if dry_run:
450 if dry_run:
450 message = message or 'dry_run_merge_message'
451 message = message or 'dry_run_merge_message'
@@ -465,7 +466,7 b' class BaseRepository(object):'
465 return self._merge_repo(
466 return self._merge_repo(
466 shadow_repository_path, target_ref, source_repo,
467 shadow_repository_path, target_ref, source_repo,
467 source_ref, message, user_name, user_email, dry_run=dry_run,
468 source_ref, message, user_name, user_email, dry_run=dry_run,
468 use_rebase=use_rebase)
469 use_rebase=use_rebase, close_branch=close_branch)
469 except RepositoryError:
470 except RepositoryError:
470 log.exception(
471 log.exception(
471 'Unexpected failure when running merge, dry-run=%s',
472 'Unexpected failure when running merge, dry-run=%s',
@@ -475,7 +476,8 b' class BaseRepository(object):'
475
476
476 def _merge_repo(self, shadow_repository_path, target_ref,
477 def _merge_repo(self, shadow_repository_path, target_ref,
477 source_repo, source_ref, merge_message,
478 source_repo, source_ref, merge_message,
478 merger_name, merger_email, dry_run=False, use_rebase=False):
479 merger_name, merger_email, dry_run=False,
480 use_rebase=False, close_branch=False):
479 """Internal implementation of merge."""
481 """Internal implementation of merge."""
480 raise NotImplementedError
482 raise NotImplementedError
481
483
@@ -849,7 +849,7 b' class GitRepository(BaseRepository):'
849 def _merge_repo(self, shadow_repository_path, target_ref,
849 def _merge_repo(self, shadow_repository_path, target_ref,
850 source_repo, source_ref, merge_message,
850 source_repo, source_ref, merge_message,
851 merger_name, merger_email, dry_run=False,
851 merger_name, merger_email, dry_run=False,
852 use_rebase=False):
852 use_rebase=False, close_branch=False):
853 if target_ref.commit_id != self.branches[target_ref.name]:
853 if target_ref.commit_id != self.branches[target_ref.name]:
854 return MergeResponse(
854 return MergeResponse(
855 False, False, None, MergeFailureReason.TARGET_IS_NOT_HEAD)
855 False, False, None, MergeFailureReason.TARGET_IS_NOT_HEAD)
@@ -646,6 +646,28 b' class MercurialRepository(BaseRepository'
646 self._remote.update(clean=True)
646 self._remote.update(clean=True)
647 raise
647 raise
648
648
649 def _local_close(self, target_ref, user_name, user_email,
650 source_ref, close_message=''):
651 """
652 Close the branch of the given source_revision
653
654 Returns the commit id of the close and a boolean indicating if the
655 commit needs to be pushed.
656 """
657 self._update(target_ref.commit_id)
658 message = close_message or "Closing branch"
659 try:
660 self._remote.commit(
661 message=safe_str(message),
662 username=safe_str('%s <%s>' % (user_name, user_email)),
663 close_branch=True)
664 self._remote.invalidate_vcs_cache()
665 return self._identify(), True
666 except RepositoryError:
667 # Cleanup any commit leftovers
668 self._remote.update(clean=True)
669 raise
670
649 def _is_the_same_branch(self, target_ref, source_ref):
671 def _is_the_same_branch(self, target_ref, source_ref):
650 return (
672 return (
651 self._get_branch_name(target_ref) ==
673 self._get_branch_name(target_ref) ==
@@ -679,7 +701,7 b' class MercurialRepository(BaseRepository'
679 def _merge_repo(self, shadow_repository_path, target_ref,
701 def _merge_repo(self, shadow_repository_path, target_ref,
680 source_repo, source_ref, merge_message,
702 source_repo, source_ref, merge_message,
681 merger_name, merger_email, dry_run=False,
703 merger_name, merger_email, dry_run=False,
682 use_rebase=False):
704 use_rebase=False, close_branch=False):
683 if target_ref.commit_id not in self._heads():
705 if target_ref.commit_id not in self._heads():
684 return MergeResponse(
706 return MergeResponse(
685 False, False, None, MergeFailureReason.TARGET_IS_NOT_HEAD)
707 False, False, None, MergeFailureReason.TARGET_IS_NOT_HEAD)
@@ -712,26 +734,40 b' class MercurialRepository(BaseRepository'
712 merge_ref = None
734 merge_ref = None
713 merge_failure_reason = MergeFailureReason.NONE
735 merge_failure_reason = MergeFailureReason.NONE
714
736
715 try:
737 if close_branch and not use_rebase:
716 merge_commit_id, needs_push = shadow_repo._local_merge(
738 try:
717 target_ref, merge_message, merger_name, merger_email,
739 close_commit_id, needs_push = shadow_repo._local_close(
718 source_ref, use_rebase=use_rebase)
740 target_ref, merger_name, merger_email, source_ref)
741 target_ref.commit_id = close_commit_id
742 merge_possible = True
743 except RepositoryError:
744 log.exception('Failure when doing close branch on hg shadow repo')
745 merge_possible = False
746 merge_failure_reason = MergeFailureReason.MERGE_FAILED
747 else:
719 merge_possible = True
748 merge_possible = True
720
749
721 # Set a bookmark pointing to the merge commit. This bookmark may be
750 if merge_possible:
722 # used to easily identify the last successful merge commit in the
751 try:
723 # shadow repository.
752 merge_commit_id, needs_push = shadow_repo._local_merge(
724 shadow_repo.bookmark('pr-merge', revision=merge_commit_id)
753 target_ref, merge_message, merger_name, merger_email,
725 merge_ref = Reference('book', 'pr-merge', merge_commit_id)
754 source_ref, use_rebase=use_rebase)
726 except SubrepoMergeError:
755 merge_possible = True
727 log.exception(
756
728 'Subrepo merge error during local merge on hg shadow repo.')
757 # Set a bookmark pointing to the merge commit. This bookmark may be
729 merge_possible = False
758 # used to easily identify the last successful merge commit in the
730 merge_failure_reason = MergeFailureReason.SUBREPO_MERGE_FAILED
759 # shadow repository.
731 except RepositoryError:
760 shadow_repo.bookmark('pr-merge', revision=merge_commit_id)
732 log.exception('Failure when doing local merge on hg shadow repo')
761 merge_ref = Reference('book', 'pr-merge', merge_commit_id)
733 merge_possible = False
762 except SubrepoMergeError:
734 merge_failure_reason = MergeFailureReason.MERGE_FAILED
763 log.exception(
764 'Subrepo merge error during local merge on hg shadow repo.')
765 merge_possible = False
766 merge_failure_reason = MergeFailureReason.SUBREPO_MERGE_FAILED
767 except RepositoryError:
768 log.exception('Failure when doing local merge on hg shadow repo')
769 merge_possible = False
770 merge_failure_reason = MergeFailureReason.MERGE_FAILED
735
771
736 if merge_possible and not dry_run:
772 if merge_possible and not dry_run:
737 if needs_push:
773 if needs_push:
@@ -388,7 +388,9 b' class _BaseVcsSettingsForm(formencode.Sc'
388 extensions_largefiles = v.StringBoolean(if_missing=False)
388 extensions_largefiles = v.StringBoolean(if_missing=False)
389 extensions_evolve = v.StringBoolean(if_missing=False)
389 extensions_evolve = v.StringBoolean(if_missing=False)
390 phases_publish = v.StringBoolean(if_missing=False)
390 phases_publish = v.StringBoolean(if_missing=False)
391
391 rhodecode_hg_use_rebase_for_merging = v.StringBoolean(if_missing=False)
392 rhodecode_hg_use_rebase_for_merging = v.StringBoolean(if_missing=False)
393 rhodecode_hg_close_branch_before_merging = v.StringBoolean(if_missing=False)
392
394
393 # git
395 # git
394 vcs_git_lfs_enabled = v.StringBoolean(if_missing=False)
396 vcs_git_lfs_enabled = v.StringBoolean(if_missing=False)
@@ -552,6 +552,7 b' class PullRequestModel(BaseModel):'
552
552
553 workspace_id = self._workspace_id(pull_request)
553 workspace_id = self._workspace_id(pull_request)
554 use_rebase = self._use_rebase_for_merging(pull_request)
554 use_rebase = self._use_rebase_for_merging(pull_request)
555 close_branch = self._close_branch_before_merging(pull_request)
555
556
556 callback_daemon, extras = prepare_callback_daemon(
557 callback_daemon, extras = prepare_callback_daemon(
557 extras, protocol=vcs_settings.HOOKS_PROTOCOL,
558 extras, protocol=vcs_settings.HOOKS_PROTOCOL,
@@ -565,7 +566,8 b' class PullRequestModel(BaseModel):'
565 merge_state = target_vcs.merge(
566 merge_state = target_vcs.merge(
566 target_ref, source_vcs, pull_request.source_ref_parts,
567 target_ref, source_vcs, pull_request.source_ref_parts,
567 workspace_id, user_name=user.username,
568 workspace_id, user_name=user.username,
568 user_email=user.email, message=message, use_rebase=use_rebase)
569 user_email=user.email, message=message, use_rebase=use_rebase,
570 close_branch=close_branch)
569 return merge_state
571 return merge_state
570
572
571 def _comment_and_close_pr(self, pull_request, user, merge_state):
573 def _comment_and_close_pr(self, pull_request, user, merge_state):
@@ -1249,9 +1251,11 b' class PullRequestModel(BaseModel):'
1249 workspace_id = self._workspace_id(pull_request)
1251 workspace_id = self._workspace_id(pull_request)
1250 source_vcs = pull_request.source_repo.scm_instance()
1252 source_vcs = pull_request.source_repo.scm_instance()
1251 use_rebase = self._use_rebase_for_merging(pull_request)
1253 use_rebase = self._use_rebase_for_merging(pull_request)
1254 close_branch = self._close_branch_before_merging(pull_request)
1252 merge_state = target_vcs.merge(
1255 merge_state = target_vcs.merge(
1253 target_reference, source_vcs, pull_request.source_ref_parts,
1256 target_reference, source_vcs, pull_request.source_ref_parts,
1254 workspace_id, dry_run=True, use_rebase=use_rebase)
1257 workspace_id, dry_run=True, use_rebase=use_rebase,
1258 close_branch=close_branch)
1255
1259
1256 # Do not store the response if there was an unknown error.
1260 # Do not store the response if there was an unknown error.
1257 if merge_state.failure_reason != MergeFailureReason.UNKNOWN:
1261 if merge_state.failure_reason != MergeFailureReason.UNKNOWN:
@@ -1416,14 +1420,21 b' class PullRequestModel(BaseModel):'
1416 return vcs_diff
1420 return vcs_diff
1417
1421
1418 def _is_merge_enabled(self, pull_request):
1422 def _is_merge_enabled(self, pull_request):
1423 return self._get_general_setting(
1424 pull_request, 'rhodecode_pr_merge_enabled')
1425
1426 def _use_rebase_for_merging(self, pull_request):
1427 return self._get_general_setting(
1428 pull_request, 'rhodecode_hg_use_rebase_for_merging')
1429
1430 def _close_branch_before_merging(self, pull_request):
1431 return self._get_general_setting(
1432 pull_request, 'rhodecode_hg_close_branch_before_merging')
1433
1434 def _get_general_setting(self, pull_request, settings_key, default=False):
1419 settings_model = VcsSettingsModel(repo=pull_request.target_repo)
1435 settings_model = VcsSettingsModel(repo=pull_request.target_repo)
1420 settings = settings_model.get_general_settings()
1436 settings = settings_model.get_general_settings()
1421 return settings.get('rhodecode_pr_merge_enabled', False)
1437 return settings.get(settings_key, default)
1422
1423 def _use_rebase_for_merging(self, pull_request):
1424 settings_model = VcsSettingsModel(repo=pull_request.target_repo)
1425 settings = settings_model.get_general_settings()
1426 return settings.get('rhodecode_hg_use_rebase_for_merging', False)
1427
1438
1428 def _log_audit_action(self, action, action_data, user, pull_request):
1439 def _log_audit_action(self, action, action_data, user, pull_request):
1429 audit_logger.store(
1440 audit_logger.store(
@@ -408,7 +408,8 b' class VcsSettingsModel(object):'
408 GENERAL_SETTINGS = (
408 GENERAL_SETTINGS = (
409 'use_outdated_comments',
409 'use_outdated_comments',
410 'pr_merge_enabled',
410 'pr_merge_enabled',
411 'hg_use_rebase_for_merging')
411 'hg_use_rebase_for_merging',
412 'hg_close_branch_before_merging')
412
413
413 HOOKS_SETTINGS = (
414 HOOKS_SETTINGS = (
414 ('hooks', 'changegroup.repo_size'),
415 ('hooks', 'changegroup.repo_size'),
@@ -161,6 +161,14 b''
161 <span class="help-block">${_('Use rebase instead of creating a merge commit when merging via web interface.')}</span>
161 <span class="help-block">${_('Use rebase instead of creating a merge commit when merging via web interface.')}</span>
162 </div>
162 </div>
163
163
164 <div class="checkbox">
165 ${h.checkbox('rhodecode_hg_close_branch_before_merging' + suffix, 'True', **kwargs)}
166 <label for="rhodecode_hg_close_branch_before_merging{suffix}">${_('Close branch before merging it')}</label>
167 </div>
168 <div class="label">
169 <span class="help-block">${_('Close branch before merging it into destination branch. No effect when rebase strategy is use.')}</span>
170 </div>
171
164 </div>
172 </div>
165 </div>
173 </div>
166 % endif
174 % endif
General Comments 0
You need to be logged in to leave comments. Login now