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, |
|
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