~ubuntu-branches/ubuntu/precise/mercurial/precise-updates

« back to all changes in this revision

Viewing changes to hgext/mq.py

  • Committer: Bazaar Package Importer
  • Author(s): Javi Merino
  • Date: 2011-03-06 16:01:58 UTC
  • mto: (28.1.2 sid) (1.1.14)
  • mto: This revision was merged to the branch mainline in revision 32.
  • Revision ID: james.westby@ubuntu.com-20110306160158-y94pzpmtd7b1xgjk
Tags: upstream-1.8
ImportĀ upstreamĀ versionĀ 1.8

Show diffs side-by-side

added added

removed removed

Lines of Context:
86
86
        parent = None
87
87
        format = None
88
88
        subject = None
 
89
        branch = None
 
90
        nodeid = None
89
91
        diffstart = 0
90
92
 
91
93
        for line in file(pf):
106
108
                    date = line[7:]
107
109
                elif line.startswith("# Parent "):
108
110
                    parent = line[9:]
 
111
                elif line.startswith("# Branch "):
 
112
                    branch = line[9:]
 
113
                elif line.startswith("# Node ID "):
 
114
                    nodeid = line[10:]
109
115
                elif not line.startswith("# ") and line:
110
116
                    message.append(line)
111
117
                    format = None
134
140
 
135
141
        eatdiff(message)
136
142
        eatdiff(comments)
 
143
        # Remember the exact starting line of the patch diffs before consuming
 
144
        # empty lines, for external use by TortoiseHg and others
 
145
        self.diffstartline = len(comments)
137
146
        eatempty(message)
138
147
        eatempty(comments)
139
148
 
147
156
        self.user = user
148
157
        self.date = date
149
158
        self.parent = parent
 
159
        # nodeid and branch are for external use by TortoiseHg and others
 
160
        self.nodeid = nodeid
 
161
        self.branch = branch
150
162
        self.haspatch = diffstart > 1
151
163
        self.plainmode = plainmode
152
164
 
239
251
        try:
240
252
            fh = open(os.path.join(path, 'patches.queue'))
241
253
            cur = fh.read().rstrip()
 
254
            fh.close()
242
255
            if not cur:
243
256
                curpath = os.path.join(path, 'patches')
244
257
            else:
269
282
    @util.propertycache
270
283
    def applied(self):
271
284
        if os.path.exists(self.join(self.status_path)):
272
 
            def parse(l):
273
 
                n, name = l.split(':', 1)
274
 
                return statusentry(bin(n), name)
 
285
            def parselines(lines):
 
286
                for l in lines:
 
287
                    entry = l.split(':', 1)
 
288
                    if len(entry) > 1:
 
289
                        n, name = entry
 
290
                        yield statusentry(bin(n), name)
 
291
                    elif l.strip():
 
292
                        self.ui.warn(_('malformated mq status line: %s\n') % entry)
 
293
                    # else we ignore empty lines
275
294
            lines = self.opener(self.status_path).read().splitlines()
276
 
            return [parse(l) for l in lines]
 
295
            return list(parselines(lines))
277
296
        return []
278
297
 
279
298
    @util.propertycache
793
812
            return top, patch
794
813
        return None, None
795
814
 
 
815
    def check_substate(self, repo):
 
816
        '''return list of subrepos at a different revision than substate.
 
817
        Abort if any subrepos have uncommitted changes.'''
 
818
        inclsubs = []
 
819
        wctx = repo[None]
 
820
        for s in wctx.substate:
 
821
            if wctx.sub(s).dirty(True):
 
822
                raise util.Abort(
 
823
                    _("uncommitted changes in subrepository %s") % s)
 
824
            elif wctx.sub(s).dirty():
 
825
                inclsubs.append(s)
 
826
        return inclsubs
 
827
 
796
828
    def check_localchanges(self, repo, force=False, refresh=True):
797
829
        m, a, r, d = repo.status()[:4]
798
830
        if (m or a or r or d) and not force:
826
858
                                 % patchfn)
827
859
            else:
828
860
                raise util.Abort(_('patch "%s" already exists') % patchfn)
 
861
 
 
862
        inclsubs = self.check_substate(repo)
 
863
        if inclsubs:
 
864
            inclsubs.append('.hgsubstate')
829
865
        if opts.get('include') or opts.get('exclude') or pats:
 
866
            if inclsubs:
 
867
                pats = list(pats or []) + inclsubs
830
868
            match = cmdutil.match(repo, pats, opts)
831
869
            # detect missing files in pats
832
870
            def badfn(f, msg):
833
 
                raise util.Abort('%s: %s' % (f, msg))
 
871
                if f != '.hgsubstate': # .hgsubstate is auto-created
 
872
                    raise util.Abort('%s: %s' % (f, msg))
834
873
            match.bad = badfn
835
874
            m, a, r, d = repo.status(match=match)[:4]
836
875
        else:
837
876
            m, a, r, d = self.check_localchanges(repo, force=True)
838
 
            match = cmdutil.matchfiles(repo, m + a + r)
 
877
            match = cmdutil.matchfiles(repo, m + a + r + inclsubs)
839
878
        if len(repo[None].parents()) > 1:
840
879
            raise util.Abort(_('cannot manage merge changesets'))
841
880
        commitfiles = m + a + r
1006
1045
        raise util.Abort(_("patch %s not in series") % patch)
1007
1046
 
1008
1047
    def push(self, repo, patch=None, force=False, list=False,
1009
 
             mergeq=None, all=False, move=False):
 
1048
             mergeq=None, all=False, move=False, exact=False):
1010
1049
        diffopts = self.diffopts()
1011
1050
        wlock = repo.wlock()
1012
1051
        try:
1015
1054
                heads += ls
1016
1055
            if not heads:
1017
1056
                heads = [nullid]
1018
 
            if repo.dirstate.parents()[0] not in heads:
 
1057
            if repo.dirstate.parents()[0] not in heads and not exact:
1019
1058
                self.ui.status(_("(working directory not at a head)\n"))
1020
1059
 
1021
1060
            if not self.series:
1029
1068
            # go backwards with qpush)
1030
1069
            if patch:
1031
1070
                info = self.isapplied(patch)
1032
 
                if info:
1033
 
                    if info[0] < len(self.applied) - 1:
1034
 
                        raise util.Abort(
1035
 
                            _("cannot push to a previous patch: %s") % patch)
 
1071
                if info and info[0] >= len(self.applied) - 1:
1036
1072
                    self.ui.warn(
1037
1073
                        _('qpush: %s is already at the top\n') % patch)
1038
1074
                    return 0
 
1075
 
1039
1076
                pushable, reason = self.pushable(patch)
1040
 
                if not pushable:
 
1077
                if pushable:
 
1078
                    if self.series.index(patch) < self.series_end():
 
1079
                        raise util.Abort(
 
1080
                            _("cannot push to a previous patch: %s") % patch)
 
1081
                else:
1041
1082
                    if reason:
1042
1083
                        reason = _('guarded by %r') % reason
1043
1084
                    else:
1062
1103
            if not force:
1063
1104
                self.check_localchanges(repo)
1064
1105
 
 
1106
            if exact:
 
1107
                if move:
 
1108
                    raise util.Abort(_("cannot use --exact and --move together"))
 
1109
                if self.applied:
 
1110
                    raise util.Abort(_("cannot push --exact with applied patches"))
 
1111
                root = self.series[start]
 
1112
                target = patchheader(self.join(root), self.plainmode).parent
 
1113
                if not target:
 
1114
                    raise util.Abort(_("%s does not have a parent recorded" % root))
 
1115
                if not repo[target] == repo['.']:
 
1116
                    hg.update(repo, target)
 
1117
 
1065
1118
            if move:
1066
1119
                if not patch:
1067
 
                    raise  util.Abort(_("please specify the patch to move"))
 
1120
                    raise util.Abort(_("please specify the patch to move"))
1068
1121
                for i, rpn in enumerate(self.full_series[start:]):
1069
1122
                    # strip markers for patch guards
1070
1123
                    if self.guard_re.split(rpn, 1)[0] == patch:
1102
1155
                for f in all_files:
1103
1156
                    if f not in repo.dirstate:
1104
1157
                        try:
1105
 
                            util.unlink(repo.wjoin(f))
 
1158
                            util.unlinkpath(repo.wjoin(f))
1106
1159
                        except OSError, inst:
1107
1160
                            if inst.errno != errno.ENOENT:
1108
1161
                                raise
1196
1249
                    raise util.Abort(_("deletions found between repo revs"))
1197
1250
                for f in a:
1198
1251
                    try:
1199
 
                        util.unlink(repo.wjoin(f))
 
1252
                        util.unlinkpath(repo.wjoin(f))
1200
1253
                    except OSError, e:
1201
1254
                        if e.errno != errno.ENOENT:
1202
1255
                            raise
1247
1300
            if repo.changelog.heads(top) != [top]:
1248
1301
                raise util.Abort(_("cannot refresh a revision with children"))
1249
1302
 
 
1303
            inclsubs = self.check_substate(repo)
 
1304
 
1250
1305
            cparents = repo.changelog.parents(top)
1251
1306
            patchparent = self.qparents(repo, top)
1252
1307
            ph = patchheader(self.join(patchfn), self.plainmode)
1270
1325
            # and then commit.
1271
1326
            #
1272
1327
            # this should really read:
1273
 
            #   mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
 
1328
            #   mm, dd, aa = repo.status(top, patchparent)[:3]
1274
1329
            # but we do it backwards to take advantage of manifest/chlog
1275
1330
            # caching against the next repo.status call
1276
 
            mm, aa, dd, aa2 = repo.status(patchparent, top)[:4]
 
1331
            mm, aa, dd = repo.status(patchparent, top)[:3]
1277
1332
            changes = repo.changelog.read(top)
1278
1333
            man = repo.manifest.read(changes[0])
1279
1334
            aaa = aa[:]
1289
1344
            else:
1290
1345
                match = cmdutil.matchall(repo)
1291
1346
            m, a, r, d = repo.status(match=match)[:4]
 
1347
            mm = set(mm)
 
1348
            aa = set(aa)
 
1349
            dd = set(dd)
1292
1350
 
1293
1351
            # we might end up with files that were added between
1294
1352
            # qtip and the dirstate parent, but then changed in the
1295
1353
            # local dirstate. in this case, we want them to only
1296
1354
            # show up in the added section
1297
1355
            for x in m:
1298
 
                if x == '.hgsub' or x == '.hgsubstate':
1299
 
                    self.ui.warn(_('warning: not refreshing %s\n') % x)
1300
 
                    continue
1301
1356
                if x not in aa:
1302
 
                    mm.append(x)
 
1357
                    mm.add(x)
1303
1358
            # we might end up with files added by the local dirstate that
1304
1359
            # were deleted by the patch.  In this case, they should only
1305
1360
            # show up in the changed section.
1306
1361
            for x in a:
1307
 
                if x == '.hgsub' or x == '.hgsubstate':
1308
 
                    self.ui.warn(_('warning: not adding %s\n') % x)
1309
 
                    continue
1310
1362
                if x in dd:
1311
 
                    del dd[dd.index(x)]
1312
 
                    mm.append(x)
 
1363
                    dd.remove(x)
 
1364
                    mm.add(x)
1313
1365
                else:
1314
 
                    aa.append(x)
 
1366
                    aa.add(x)
1315
1367
            # make sure any files deleted in the local dirstate
1316
1368
            # are not in the add or change column of the patch
1317
1369
            forget = []
1318
1370
            for x in d + r:
1319
 
                if x == '.hgsub' or x == '.hgsubstate':
1320
 
                    self.ui.warn(_('warning: not removing %s\n') % x)
1321
 
                    continue
1322
1371
                if x in aa:
1323
 
                    del aa[aa.index(x)]
 
1372
                    aa.remove(x)
1324
1373
                    forget.append(x)
1325
1374
                    continue
1326
 
                elif x in mm:
1327
 
                    del mm[mm.index(x)]
1328
 
                dd.append(x)
 
1375
                else:
 
1376
                    mm.discard(x)
 
1377
                dd.add(x)
1329
1378
 
1330
 
            m = list(set(mm))
1331
 
            r = list(set(dd))
1332
 
            a = list(set(aa))
 
1379
            m = list(mm)
 
1380
            r = list(dd)
 
1381
            a = list(aa)
1333
1382
            c = [filter(matchfn, l) for l in (m, a, r)]
1334
 
            match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
 
1383
            match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs))
1335
1384
            chunks = patch.diff(repo, patchparent, match=match,
1336
1385
                                changes=c, opts=diffopts)
1337
1386
            for chunk in chunks:
1529
1578
                l = line.rstrip()
1530
1579
                l = l[10:].split(' ')
1531
1580
                qpp = [bin(x) for x in l]
1532
 
            elif datastart != None:
 
1581
            elif datastart is not None:
1533
1582
                l = line.rstrip()
1534
1583
                n, name = l.split(':', 1)
1535
1584
                if n:
1739
1788
                                _('need --name to import a patch from -'))
1740
1789
                        text = sys.stdin.read()
1741
1790
                    else:
1742
 
                        text = url.open(self.ui, filename).read()
 
1791
                        fp = url.open(self.ui, filename)
 
1792
                        text = fp.read()
 
1793
                        fp.close()
1743
1794
                except (OSError, IOError):
1744
1795
                    raise util.Abort(_("unable to read file %s") % filename)
1745
1796
                if not patchname:
1748
1799
                checkfile(patchname)
1749
1800
                patchf = self.opener(patchname, "w")
1750
1801
                patchf.write(text)
 
1802
                patchf.close()
1751
1803
            if not force:
1752
1804
                checkseries(patchname)
1753
1805
            if patchname not in self.series:
1759
1811
            self.added.append(patchname)
1760
1812
            patchname = None
1761
1813
 
 
1814
        self.removeundo(repo)
 
1815
 
1762
1816
def delete(ui, repo, *patches, **opts):
1763
1817
    """remove patches from queue
1764
1818
 
2344
2398
        mergeq = queue(ui, repo.join(""), newpath)
2345
2399
        ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2346
2400
    ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2347
 
                 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'))
 
2401
                 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
 
2402
                 exact=opts.get('exact'))
2348
2403
    return ret
2349
2404
 
2350
2405
def pop(ui, repo, patch=None, **opts):
2744
2799
        try:
2745
2800
            fh = repo.opener(_allqueues, 'r')
2746
2801
            queues = [queue.strip() for queue in fh if queue.strip()]
 
2802
            fh.close()
2747
2803
            if current not in queues:
2748
2804
                queues.append(current)
2749
2805
        except IOError:
2878
2934
            return super(mqrepo, self).commit(text, user, date, match, force,
2879
2935
                                              editor, extra)
2880
2936
 
2881
 
        def push(self, remote, force=False, revs=None, newbranch=False):
 
2937
        def checkpush(self, force, revs):
2882
2938
            if self.mq.applied and not force:
2883
2939
                haspatches = True
2884
2940
                if revs:
2889
2945
                    haspatches = bool([n for n in revs if n in applied])
2890
2946
                if haspatches:
2891
2947
                    raise util.Abort(_('source has mq patches applied'))
2892
 
            return super(mqrepo, self).push(remote, force, revs, newbranch)
 
2948
            super(mqrepo, self).checkpush(force, revs)
2893
2949
 
2894
2950
        def _findtags(self):
2895
2951
            '''augment tags from base class with patch tags'''
2901
2957
 
2902
2958
            mqtags = [(patch.node, patch.name) for patch in q.applied]
2903
2959
 
2904
 
            if mqtags[-1][0] not in self.changelog.nodemap:
 
2960
            try:
 
2961
                r = self.changelog.rev(mqtags[-1][0])
 
2962
            except error.RepoLookupError:
2905
2963
                self.ui.warn(_('mq status file refers to unknown node %s\n')
2906
2964
                             % short(mqtags[-1][0]))
2907
2965
                return result
2926
2984
 
2927
2985
            cl = self.changelog
2928
2986
            qbasenode = q.applied[0].node
2929
 
            if qbasenode not in cl.nodemap:
 
2987
            try:
 
2988
                qbase = cl.rev(qbasenode)
 
2989
            except error.LookupError:
2930
2990
                self.ui.warn(_('mq status file refers to unknown node %s\n')
2931
2991
                             % short(qbasenode))
2932
2992
                return super(mqrepo, self)._branchtags(partial, lrev)
2933
2993
 
2934
 
            qbase = cl.rev(qbasenode)
2935
2994
            start = lrev + 1
2936
2995
            if start < qbase:
2937
2996
                # update the cache (excluding the patches) and save it
3120
3179
    "^qpush":
3121
3180
        (push,
3122
3181
         [('f', 'force', None, _('apply on top of local changes')),
 
3182
          ('e', 'exact', None, _('apply the target patch to its recorded parent')),
3123
3183
          ('l', 'list', None, _('list patch name in commit text')),
3124
3184
          ('a', 'all', None, _('apply all patches')),
3125
3185
          ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),