1#    quilt.py -- Quilt patch handling
2#    Copyright (C) 2011 Canonical Ltd.
3#    Copyright (C) 2019 Jelmer Verooij <jelmer@jelmer.uk>
4#
5#    Breezy is free software; you can redistribute it and/or modify
6#    it under the terms of the GNU General Public License as published by
7#    the Free Software Foundation; either version 2 of the License, or
8#    (at your option) any later version.
9#
10#    Breezy is distributed in the hope that it will be useful,
11#    but WITHOUT ANY WARRANTY; without even the implied warranty of
12#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13#    GNU General Public License for more details.
14#
15#    You should have received a copy of the GNU General Public License
16#    along with Breezy; if not, write to the Free Software
17#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18#
19
20"""Quilt patch handling."""
21
22import shutil
23import tempfile
24
25from ...i18n import gettext
26from ...mutabletree import MutableTree
27from ...revisiontree import RevisionTree
28from ... import (
29    errors,
30    merge as _mod_merge,
31    trace,
32    )
33
34from .quilt import (
35    QuiltPatches,
36)
37
38
39class NoUnapplyingMerger(_mod_merge.Merge3Merger):
40
41    _no_quilt_unapplying = True
42
43
44def tree_unapply_patches(orig_tree, orig_branch=None, force=False):
45    """Return a tree with patches unapplied.
46
47    :param orig_tree: Tree from which to unapply quilt patches
48    :param orig_branch: Related branch (optional)
49    :return: Tuple with tree and temp path.
50        The tree is a tree with unapplied patches; either a checkout of
51        tree or tree itself if there were no patches
52    """
53    if orig_branch is None:
54        orig_branch = orig_tree.branch
55    quilt = QuiltPatches.find(orig_tree)
56    if quilt is None:
57        return orig_tree, None
58    applied_patches = quilt.applied()
59    if not applied_patches:
60        # No quilt patches
61        return orig_tree, None
62
63    target_dir = tempfile.mkdtemp()
64    try:
65        if isinstance(orig_tree, MutableTree):
66            tree = orig_branch.create_checkout(
67                target_dir, lightweight=True,
68                revision_id=orig_tree.last_revision(),
69                accelerator_tree=orig_tree)
70            merger = _mod_merge.Merger.from_uncommitted(tree, orig_tree)
71            merger.merge_type = NoUnapplyingMerger
72            merger.do_merge()
73        elif isinstance(orig_tree, RevisionTree):
74            tree = orig_branch.create_checkout(
75                target_dir, lightweight=True,
76                accelerator_tree=orig_tree, revision_id=orig_tree.get_revision_id())
77        else:
78            trace.mutter("Not sure how to create copy of %r", orig_tree)
79            shutil.rmtree(target_dir)
80            return orig_tree, None
81        trace.mutter("Applying quilt patches for %r in %s", orig_tree, target_dir)
82        quilt = QuiltPatches.find(tree)
83        if quilt is not None:
84            quilt.pop_all(force=force)
85        return tree, target_dir
86    except BaseException:
87        shutil.rmtree(target_dir)
88        raise
89
90
91def post_process_quilt_patches(tree, old_patches, policy):
92    """(Un)apply patches after a merge.
93
94    :param tree: Working tree to work in
95    :param old_patches: List of patches applied before the operation (usually a merge)
96    """
97    quilt = QuiltPatches.find(tree)
98    if quilt is None:
99        return
100    new_patches = quilt.series()
101    applied_patches = quilt.applied()
102    if policy == "applied":
103        to_apply = []
104        for p in new_patches:
105            if p in old_patches:
106                continue
107            if p not in applied_patches:
108                to_apply.append(p)
109        if to_apply == []:
110            return
111        trace.note(gettext("Applying %d quilt patches."), len(to_apply))
112        for p in to_apply:
113            quilt.push(p)
114    elif policy == "unapplied":
115        to_unapply = []
116        for p in new_patches:
117            if p in old_patches:
118                continue
119            if p in applied_patches:
120                to_unapply.append(p)
121        if to_unapply == []:
122            return
123        trace.note(gettext("Unapplying %d quilt patches."), len(to_unapply))
124        for p in to_unapply:
125            quilt.pop(p)
126
127
128def start_commit_quilt_patches(tree, policy):
129    quilt = QuiltPatches.find(tree)
130    if quilt is None:
131        return
132    applied_patches = quilt.applied()
133    unapplied_patches = quilt.unapplied()
134    if policy is None:
135        # No policy set - just warn about having both applied and unapplied
136        # patches.
137        if applied_patches and unapplied_patches:
138            trace.warning(
139                gettext("Committing with %d patches applied and %d patches unapplied."),
140                len(applied_patches), len(unapplied_patches))
141    elif policy == "applied":
142        quilt.push_all()
143    elif policy == "unapplied":
144        quilt.pop_all()
145