Kiln » Kiln Extensions
Clone URL:  
Pushed to 2 repositories · View In Graph Contained in tip

3.0.61

Changeset 50302f954e2f

Parent eb3f0219a412

by Profile picture of User 276Kevin Gessner <kevin@fogcreek.com>

Changes to 6 files · Browse files at 50302f954e2f Showing diff from parent eb3f0219a412 Diff from another changeset...

Change 1 of 7 Show Entire File big-push.py Stacked
 
1
 
2
3
4
 
21
22
23
 
24
25
26
 
34
35
36
 
37
38
39
 
51
52
53
 
54
55
56
 
59
60
61
 
62
63
64
 
99
100
101
102
 
103
104
105
106
107
 
108
109
110
 
113
114
115
 
116
117
118
119
 
120
121
122
123
 
124
125
 
 
1
2
3
4
 
21
22
23
24
25
26
27
 
35
36
37
38
39
40
41
 
53
54
55
56
57
58
59
 
62
63
64
65
66
67
68
 
103
104
105
 
106
107
108
109
110
 
111
112
113
114
 
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
@@ -1,4 +1,4 @@
-# Copyright (C) 2009-2011 Fog Creek Software. All rights reserved. +# Copyright (C) 2009-2013 Fog Creek Software. All rights reserved.  #  # This program is free software; you can redistribute it and/or modify  # it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@
   max_push_size = 1000   +  def findoutgoing(repo, other):   try:   # Mercurial 1.6 through 1.8 @@ -34,6 +35,7 @@
  # Mercurial 1.5 and lower   return repo.findoutgoing(other, force=False)   +  def prepush(repo, other, force, revs):   try:   from mercurial import discovery @@ -51,6 +53,7 @@
  # Mercurial 1.5 and lower   return repo.prepush(other, False, revs)   +  def remoteui(repo, opts):   if hasattr(cmdutil, 'remoteui'):   # Mercurial 1.5 and lower @@ -59,6 +62,7 @@
  # Mercurial 1.6 and higher   return hg.remoteui(repo, opts)   +  def bigpush(push_fn, ui, repo, dest=None, *files, **opts):   '''Pushes this repository to a target repository.   @@ -99,12 +103,12 @@
  ui.debug('pushing: %d\n' % current_push_size)   # force the push, because we checked above that by the time the whole push is done, we'll have merged back to one head   remote_heads = repo.push(other, force=True, revs=outgoing[:current_push_size]) - if remote_heads: # push succeeded + if remote_heads: # push succeeded   outgoing = outgoing[current_push_size:]   ui.debug('pushed %d ok\n' % current_push_size)   if push_size < max_push_size:   push_size *= 2 - else: # push failed; try again with a smaller size + else: # push failed; try again with a smaller size   push_size /= 2   ui.debug('failed, trying %d\n' % current_push_size)   if push_size == 0: @@ -113,13 +117,16 @@
  ui.status(_('unable to push changeset %s\n') % outgoing[0])   ui.debug('done\n')   +  def parseurl(source):   '''wrap hg.parseurl to work on 1.3 -> 1.5'''   return hg.parseurl(source, None)[:2]   +  def uisetup(ui):   push_cmd = extensions.wrapcommand(commands.table, 'push', bigpush)   push_cmd[1].extend([('', 'chunked', None, 'push large repository in chunks')])   +  class UnpushableChangesetError(Exception):   pass
Change 1 of 15 Show Entire File gestalt.py Stacked
 
1
 
2
3
4
 
66
67
68
 
69
70
71
 
79
80
81
 
82
83
84
 
96
97
98
 
99
100
101
 
111
112
113
 
114
115
116
 
122
123
124
 
125
126
127
128
129
130
 
131
132
133
 
139
140
141
 
142
143
144
 
150
151
152
 
153
154
155
 
194
195
196
 
197
198
199
 
206
207
208
 
209
210
211
212
 
213
214
215
 
219
220
221
 
222
223
224
 
235
236
237
238
 
239
240
241
242
243
244
 
245
246
247
 
316
317
318
 
319
320
321
 
336
337
338
 
339
340
341
 
357
358
359
360
 
361
362
 
 
1
2
3
4
 
66
67
68
69
70
71
72
 
80
81
82
83
84
85
86
 
98
99
100
101
102
103
104
 
114
115
116
117
118
119
120
 
126
127
128
129
130
131
132
133
134
135
136
137
138
139
 
145
146
147
148
149
150
151
 
157
158
159
160
161
162
163
 
202
203
204
205
206
207
208
 
215
216
217
218
219
220
221
222
223
224
225
226
 
230
231
232
233
234
235
236
 
247
248
249
 
250
251
252
253
254
255
256
257
258
259
260
 
329
330
331
332
333
334
335
 
350
351
352
353
354
355
356
 
372
373
374
 
375
376
377
@@ -1,4 +1,4 @@
-# Copyright (C) 2009-2012 Fog Creek Software. All rights reserved. +# Copyright (C) 2009-2013 Fog Creek Software. All rights reserved.  #  # To enable the "gestalt" extension put these lines in your ~/.hgrc:  # [extensions] @@ -66,6 +66,7 @@
  return repo.changelog.findmissing(common)  demandimport.enable()   +  @apply  def _HG_VERSION():   '''return the mercurial version as a tuple rather than a string @@ -79,6 +80,7 @@
  version[i] = part   return tuple(version)   +  def parseurl(source):   '''wrap hg.parseurl to work on 1.5 and 1.6   @@ -96,6 +98,7 @@
  hashbranch = branches and branches[0] or None   return uri, hashbranch   +  def addbranchrevs(lrepo, repo, hashbranch):   '''wrap hg.addbranchrevs to work on 1.5 and 1.6 and returns the   first value (revs) only @@ -111,6 +114,7 @@
  revs, checkout = hg.addbranchrevs(lrepo, repo, branches, None)   return revs   +  ##  # Private utility functions for determining advice output  def _isrepo(ui, repo, files, opts): @@ -122,12 +126,14 @@
  return True   return False   +  def _ismerging(ui, repo, files, opts):   if repo.dirstate.parents()[1] != node.nullid:   ui.status(_('It appears there is a merge in progress.\n'))   return True   return False   +  def _haschanges(ui, repo, files, opts):   changed = any(repo.status())   if changed: @@ -139,6 +145,7 @@
  return True   return False   +  def _shouldmerge(ui, repo, files, opts):   heads = repo.branchheads(closed=False)   if len(heads) > 1: @@ -150,6 +157,7 @@
  return True   return False   +  def _shouldsync(ui, repo, files, opts):   source, hashbranch = parseurl(ui.expandpath('default'))   @@ -194,6 +202,7 @@
  return True   return False   +  def _istip(ui, repo, files, opts):   tip = repo['tip']   cwd = repo['.'] @@ -206,10 +215,12 @@
  return True   return False   +  def _shouldwritemorecode(ui, *ignored):   ui.status(_('Everything is up-to-date. Write more code!\n'))   return True   +  ##  # General utility methods  def outgoing(repo, origin): @@ -219,6 +230,7 @@
  out = repo.changelog.nodesbetween(out, None)[0]   return out   +  def incoming(repo, origin, revs):   '''return a list of incoming changesets'''   if revs: @@ -235,13 +247,14 @@
  cg = origin.changegroup(incoming, 'incoming')   else:   cg = origin.changegroupsubset(incoming, revs, 'incoming') - fname = changegroup.writebundle(cg, None, "HG10UN") + fname = changegroup.writebundle(cg, None, "HG10UN")   origin = bundlerepo.bundlerepository(repo.ui, repo.root, fname)   incoming = origin.changelog.findmissing(common, revs)   if hasattr(origin, 'close'):   origin.close()   return incoming   +  ##  # commands  def overview(ui, repo, source=None, **opts): @@ -316,6 +329,7 @@
  for l in status.splitlines():   print ' %s' % l   +  def advice(ui, repo, *files, **opts):   '''provides a suggestion of your next step   @@ -336,6 +350,7 @@
  if fun(ui, repo, files, opts):   return   +  def next(ui, repo, *files, **opts):   '''provides an overview and explanation of what to do next   @@ -357,6 +372,6 @@
  (next,   [('d', 'detail', None, _('provide verbose output'))],   _('hg next [OPTION] [REMOTE REPOSITORY]')), - } +}    commands.optionalrepo += 'wtf advice overview next'
Change 1 of 45 Show Entire File kiln.py Stacked
 
1
 
2
3
4
 
40
41
42
 
43
44
45
 
52
53
54
55
 
56
57
58
 
73
74
75
 
76
77
78
79
80
 
81
82
83
84
85
86
 
87
88
89
 
95
96
97
 
98
99
100
 
119
120
121
122
 
123
124
125
126
127
128
129
 
130
131
132
133
134
135
 
136
137
138
 
 
139
140
141
 
 
142
143
144
 
145
146
147
 
152
153
154
 
155
156
157
 
168
169
170
 
171
172
173
174
175
176
 
 
 
 
177
178
179
 
182
183
184
 
185
186
187
 
188
189
190
 
206
207
208
209
 
 
210
211
212
213
 
 
214
215
216
 
217
218
219
 
225
226
227
 
228
229
230
 
236
237
238
 
239
240
241
 
246
247
248
 
249
250
251
 
253
254
255
 
256
257
258
 
282
283
284
 
285
286
287
 
289
290
291
 
292
293
294
 
297
298
299
 
300
301
302
303
304
305
 
306
307
308
 
316
317
318
 
319
320
321
 
332
333
334
 
335
336
337
 
355
356
357
 
358
359
360
 
369
370
371
 
372
373
374
375
376
377
 
378
379
380
 
396
397
398
 
399
400
401
402
403
404
 
 
405
406
 
 
407
408
409
 
428
429
430
 
431
432
433
 
446
447
448
 
449
450
451
 
465
466
467
468
469
470
 
 
 
471
472
473
474
 
 
475
476
477
 
478
479
480
481
482
483
484
485
 
 
 
 
 
486
487
488
 
503
504
505
 
506
507
508
 
513
514
515
 
516
517
518
 
536
537
538
539
 
540
541
 
542
543
544
 
549
550
551
 
552
553
554
 
560
561
562
 
563
564
565
 
570
571
572
 
573
574
575
 
578
579
580
 
581
582
583
 
585
586
587
588
 
589
590
591
 
614
615
616
 
617
618
619
 
634
635
636
 
637
638
639
640
 
641
642
643
644
 
645
646
647
648
 
649
650
651
652
 
653
654
655
656
657
658
 
659
660
661
662
663
 
664
665
666
 
669
670
671
 
672
673
674
 
770
771
772
773
 
774
775
776
 
778
779
780
 
781
782
783
 
786
787
788
 
789
790
791
 
795
796
797
 
798
799
800
 
812
813
814
 
815
816
817
 
894
895
896
 
897
898
899
 
902
903
904
 
905
906
907
 
913
914
915
 
916
917
918
 
943
944
945
946
 
 
 
1
2
3
4
 
40
41
42
43
44
45
46
 
53
54
55
 
56
57
58
59
 
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
 
99
100
101
102
103
104
105
 
124
125
126
 
127
128
129
130
131
132
133
 
134
135
136
137
138
139
 
140
141
142
 
143
144
145
146
 
147
148
149
150
151
152
153
154
155
 
160
161
162
163
164
165
166
 
177
178
179
180
181
182
 
 
 
 
183
184
185
186
187
188
189
 
192
193
194
195
196
197
198
199
200
201
202
 
218
219
220
 
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
 
241
242
243
244
245
246
247
 
253
254
255
256
257
258
259
 
264
265
266
267
268
269
270
 
272
273
274
275
276
277
278
 
302
303
304
305
306
307
308
 
310
311
312
313
314
315
316
 
319
320
321
322
323
324
325
326
327
328
329
330
331
332
 
340
341
342
343
344
345
346
 
357
358
359
360
361
362
363
 
381
382
383
384
385
386
387
 
396
397
398
399
400
401
402
403
404
405
406
407
408
409
 
425
426
427
428
429
430
431
432
433
 
434
435
436
 
437
438
439
440
441
 
460
461
462
463
464
465
466
 
479
480
481
482
483
484
485
 
499
500
501
 
 
 
502
503
504
505
506
 
 
507
508
509
510
 
511
512
513
514
 
 
 
 
 
515
516
517
518
519
520
521
522
 
537
538
539
540
541
542
543
 
548
549
550
551
552
553
554
 
572
573
574
 
575
576
577
578
579
580
581
 
586
587
588
589
590
591
592
 
598
599
600
601
602
603
604
 
609
610
611
612
613
614
615
 
618
619
620
621
622
623
624
 
626
627
628
 
629
630
631
632
 
655
656
657
658
659
660
661
 
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
 
718
719
720
721
722
723
724
 
820
821
822
 
823
824
825
826
 
828
829
830
831
832
833
834
 
837
838
839
840
841
842
843
 
847
848
849
850
851
852
853
 
865
866
867
868
869
870
871
 
948
949
950
951
952
953
954
 
957
958
959
960
961
962
963
 
969
970
971
972
973
974
975
 
1000
1001
1002
 
1003
@@ -1,4 +1,4 @@
-# Copyright (C) 2011, 2012 Fog Creek Software. All rights reserved. +# Copyright (C) 2011-2013 Fog Creek Software. All rights reserved.  #  # To enable the "kiln" extension put these lines in your ~/.hgrc:  # [extensions] @@ -40,6 +40,7 @@
 pushing to Kiln. See :hg:`help push` and  http://kiln.stackexchange.com/questions/4679/ for more information.  ''' +  import itertools  import os  import re @@ -52,7 +53,7 @@
 from cookielib import MozillaCookieJar  from hashlib import md5  from mercurial import (commands, cmdutil, demandimport, extensions, hg, - localrepo, match, util) + localrepo, match, util)  from mercurial import ui as hgui  from mercurial import url as hgurl  from mercurial.error import RepoError @@ -73,17 +74,20 @@
   try:   import webbrowser +   def browse(url):   webbrowser.open(escape_reserved(url))  except ImportError:   if os.name == 'nt':   import win32api +   def browse(url):   win32api.ShellExecute(0, 'open', escape_reserved(url), None, None, 0)  demandimport.enable()    _did_version_check = False   +  class APIError(Exception):   def __init__(self, obj):   '''takes a json object for debugging @@ -95,6 +99,7 @@
  def __str__(self):   return '\n'.join('%s: %s' % (k, v) for k, v in self.errors.items())   +  class Review(dict):   def __init__(self, json, ui=None, token=None, baseurl=None):   self.ui = ui @@ -119,29 +124,32 @@
  'token': self.token,   'ixBug': self.key,   'revs': revs, - } + }   call_api(self.ui, self.baseurl, 'Api/1.0/Repo/%d/CaseAssociation/Create' % ixRepo, params, post=True)   else:   params = {   'token': self.token,   'revs': revs,   'ixRepo': ixRepo, - } + }   call_api(self.ui, self.baseurl, 'Api/2.0/Review/%s/Association/Create' % self.key, params, post=True)   return urljoin(self.baseurl, 'Review', self.key)     @classmethod   def get_reviews(klass, ui, token, baseurl, ixRepo): - review_lists = call_api(ui, baseurl, 'Api/2.0/Reviews', dict(token=token)) + review_lists = call_api(ui, baseurl, 'Api/2.0/Reviews', dict(token=token, fReviewed="false", fAwaitingReview="false", nDaysActive=14))   reviews = {}   for key, review_list in review_lists.iteritems(): - if not key.startswith('reviews'): continue + if not key.startswith('reviews'): + continue   for review in review_list:   review = Review(review, ui, token, baseurl) - if not review.belongs_to(ixRepo): continue + if not review.belongs_to(ixRepo): + continue   reviews[review.key.lower()] = review   return reviews   +  def urljoin(*components):   url = components[0]   for next in components[1:]: @@ -152,6 +160,7 @@
  url += next   return url   +  def _baseurl(ui, path):   try:   url = str(util.url(util.removeauth(path))) @@ -168,12 +177,13 @@
  else:   return None   +  def escape_reserved(path):   reserved = re.compile( - r'^(((com[1-9]|lpt[1-9]|con|prn|aux)(\..*)?)|web\.config' + - r'|clock\$|app_data|app_code|app_browsers' + - r'|app_globalresources|app_localresources|app_themes' + - r'|app_webreferences|bin|.*\.(cs|vb)html?)$', re.IGNORECASE) + r'^(((com[1-9]|lpt[1-9]|con|prn|aux)(\..*)?)|web\.config' + + r'|clock\$|app_data|app_code|app_browsers' + + r'|app_globalresources|app_localresources|app_themes' + + r'|app_webreferences|bin|.*\.(cs|vb)html?|.*\.(svc|xamlx|xoml|rules))$', re.IGNORECASE)   p = path.split('?')   path = p[0]   query = '?' + p[1] if len(p) > 1 else '' @@ -182,9 +192,11 @@
  else part   for part in path.split('/')) + query   +  def normalize_name(s):   return s.lower().replace(' ', '-')   +  def call_api(ui, baseurl, urlsuffix, params, post=False):   '''returns the json object for the url and the data dictionary   @@ -206,14 +218,18 @@
  raise util.Abort(_('kiln: an error occurred while trying to reach %s') % url)     if isinstance(obj, dict) and 'errors' in obj: - if 'token' in params and obj['errors'][0]['codeError'] == 'InvalidToken': + error_code = obj['errors'][0]['codeError'] + if 'token' in params and error_code == 'InvalidToken':   token = login(ui, baseurl)   add_kilnapi_token(ui, baseurl, token)   params['token'] = token   return call_api(ui, baseurl, urlsuffix, params, post) + elif error_code == 'BadAuthentication': + raise util.Abort(_('authorization failed'))   raise APIError(obj)   return obj   +  def login(ui, url):   ui.write(_('realm: %s\n') % url)   user = ui.prompt('username:') @@ -225,6 +241,7 @@
  return token   raise util.Abort(_('authorization failed'))   +  def get_domain(url):   temp = url[url.find('://') + len('://'):]   domain = temp[:temp.find('/')] @@ -236,6 +253,7 @@
    return domain   +  def _get_path(path):   if os.name == 'nt':   ret = os.path.expanduser('~\\_' + path) @@ -246,6 +264,7 @@
  ret = re.sub(r'([A-Za-z]):', r'\1:\\', ret)   return ret   +  def _upgradecheck(ui, repo):   global _did_version_check   if _did_version_check or not ui.configbool('kiln', 'autoupdate', True): @@ -253,6 +272,7 @@
  _did_version_check = True   _upgrade(ui, repo)   +  def _upgrade(ui, repo):   ext_dir = os.path.dirname(os.path.abspath(__file__))   ui.debug(_('kiln: checking for extensions upgrade for %s\n') % ext_dir) @@ -282,6 +302,7 @@
  ui.debug(_('kiln: error updating extensions: %s\n') % e)   ui.debug(_('kiln: traceback: %s\n') % traceback.format_exc())   +  def is_dest_a_path(ui, dest):   paths = ui.configitems('paths')   for pathname, path in paths: @@ -289,6 +310,7 @@
  return True   return False   +  def is_dest_a_scheme(ui, dest):   destscheme = dest[:dest.find('://')]   if destscheme: @@ -297,12 +319,14 @@
  return True   return False   +  def create_match_list(matchlist):   ret = ''   for m in matchlist:   ret += ' ' + m + '\n'   return ret   +  def get_username(url):   url = re.sub(r'https?://', '', url)   url = re.sub(r'/.*', '', url) @@ -316,6 +340,7 @@
  # Didn't find anything...   return ''   +  def get_dest(ui):   from mercurial.dispatch import _parse   try: @@ -332,6 +357,7 @@
  dest = 'default'   return ui.expandpath(dest)   +  def check_kilnapi_token(ui, url):   tokenpath = _get_path('hgkiln')   @@ -355,6 +381,7 @@
  fp.close()   return ret   +  def add_kilnapi_token(ui, url, fbToken):   if not fbToken:   return @@ -369,12 +396,14 @@
  fp.write(domain + ' ' + userhash + ' ' + fbToken + '\n')   fp.close()   +  def delete_kilnapi_tokens():   # deletes the hgkiln file   tokenpath = _get_path('hgkiln')   if os.path.exists(tokenpath) and not os.path.isdir(tokenpath):   os.remove(tokenpath)   +  def check_kilnauth_token(ui, url):   cookiepath = _get_path('hgcookies')   if (not os.path.exists(cookiepath)) or (not os.path.isdir(cookiepath)): @@ -396,14 +425,17 @@
  if cookie.name == 'fbToken':   return cookie.value   +  def remember_path(ui, repo, path, value):   '''appends the path to the working copy's hgrc and backs up the original'''     paths = dict(ui.configitems('paths'))   # This should never happen. - if path in paths: return + if path in paths: + return   # ConfigParser only cares about these three characters. - if re.search(r'[:=\s]', path): return + if re.search(r'[:=\s]', path): + return     try:   audit_path = scmutil.pathauditor(repo.root) @@ -428,6 +460,7 @@
  except IOError:   return   +  def unremember_path(ui, repo):   '''restores the working copy's hgrc'''   @@ -446,6 +479,7 @@
  else:   os.remove(hgrc)   +  def guess_kilnpath(orig, ui, repo, dest=None, **opts):   if not dest:   return orig(ui, repo, **opts) @@ -465,24 +499,24 @@
    if (ndest.count('/') == 0 and   (ntarget[0] == ndest or - ntarget[1] == ndest or - ntarget[2] == ndest or - ndest in aliases)): + ntarget[1] == ndest or + ntarget[2] == ndest or + ndest in aliases)):   matches.append(url)   elif (ndest.count('/') == 1 and - '/'.join(ntarget[0:2]) == ndest or - '/'.join(ntarget[1:3]) == ndest): + '/'.join(ntarget[0:2]) == ndest or + '/'.join(ntarget[1:3]) == ndest):   matches.append(url)   elif (ndest.count('/') == 2 and - '/'.join(ntarget[0:3]) == ndest): + '/'.join(ntarget[0:3]) == ndest):   matches.append(url)     if (ntarget[0].startswith(ndest) or - ntarget[1].startswith(ndest) or - ntarget[2].startswith(ndest) or - '/'.join(ntarget[0:2]).startswith(ndest) or - '/'.join(ntarget[1:3]).startswith(ndest) or - '/'.join(ntarget[0:3]).startswith(ndest)): + ntarget[1].startswith(ndest) or + ntarget[2].startswith(ndest) or + '/'.join(ntarget[0:2]).startswith(ndest) or + '/'.join(ntarget[1:3]).startswith(ndest) or + '/'.join(ntarget[0:3]).startswith(ndest)):   prefixmatches.append(url)     if len(matches) == 0: @@ -503,6 +537,7 @@
  finally:   unremember_path(ui, repo)   +  def get_tails(repo):   tails = []   for rev in xrange(repo['tip'].rev() + 1): @@ -513,6 +548,7 @@
  raise util.Abort(_('Path guessing is only enabled for non-empty repositories.'))   return tails   +  def get_targets(repo):   def get_kiln_repo_url_prefix(default_prefix):   '''Checks repo paths and returns server url for ssh:. For http(s) falls back to default_prefix.''' @@ -536,9 +572,10 @@
  related_repo['sProjectSlug'],   related_repo['sGroupSlug'],   related_repo['sSlug'], - related_repo.get('rgAliases', [])] for related_repo in related_repos]) + [a['sSlug'] for a in related_repo.get('rgAliases', [])]] for related_repo in related_repos])   return targets   +  def display_targets(repo):   targets = get_targets(repo)   repo.ui.write(_('The following Kiln targets are available for this repository:\n\n')) @@ -549,6 +586,7 @@
  alias_text = ''   repo.ui.write(' %s/%s/%s/%s%s\n' % (target[0], target[1], target[2], target[3], alias_text))   +  def get_token(ui, url):   '''Checks for an existing API token. If none, returns a new valid token.'''   token = check_kilnapi_token(ui, url) @@ -560,6 +598,7 @@
  add_kilnapi_token(ui, url, token)   return token   +  def get_api_url(url):   '''Given a URL, returns the URL of the Kiln installation.'''   if '/kiln/' in url.lower(): @@ -570,6 +609,7 @@
  baseurl = url   return baseurl   +  class HTTPNoRedirectHandler(urllib2.HTTPRedirectHandler):   def http_error_302(self, req, fp, code, msg, headers):   # Doesn't allow multiple redirects so repo alias URLs will not @@ -578,6 +618,7 @@
    http_error_301 = http_error_303 = http_error_307 = http_error_302   +  def get_repo_record(repo, url, token=None):   '''Returns a Kiln repository record that corresponds to the given repo.'''   baseurl = get_api_url(url) @@ -585,7 +626,7 @@
  token = get_token(repo.ui, baseurl)     try: - data = urllib.urlencode({ 'token': token }, doseq=True) + data = urllib.urlencode({'token': token}, doseq=True)   opener = urllib2.build_opener(HTTPNoRedirectHandler)   urllib2.install_opener(opener)   fd = urllib2.urlopen(url + '?' + data) @@ -614,6 +655,7 @@
  repo = find_slug(repo, group, 'repos')   return repo   +  def new_branch(repo, url, name):   '''Creates a new, decentralized branch off of the specified repo.'''   baseurl = get_api_url(url) @@ -634,33 +676,40 @@
  return   raise   +  def normalize_user(s):   '''Takes a Unicode string and returns an ASCII string.'''   return unicodedata.normalize('NFKD', s).encode('ASCII', 'ignore')   +  def encode_out(s):   '''Takes a Unicode string and returns a string encoded for output.'''   return s.encode(sys.stdout.encoding, 'ignore')   +  def record_base(ui, repo, node, **kwargs):   '''Stores the first changeset committed in the repo UI so we do not need to expensively recalculate.'''   repo.ui.setconfig('kiln', 'node', node)   +  def walk(repo, revs):   '''Returns revisions in repo specified by the string revs'''   return cmdutil.walkchangerevs(repo, match.always(repo.root, None), {'rev': [revs.encode('ascii', 'ignore')]}, lambda *args: None)   +  def print_list(ui, l, header):   '''Prints a list l to ui using list notation, with header being the first line'''   ui.write(_('%s\n' % header))   for item in l:   ui.write(_('- %s\n') % item)   +  def wrap_push(orig, ui, repo, dest=None, **opts):   '''Wraps `hg push' so a review will be created after path guessing and a successful push.'''   guess_kilnpath(orig, ui, repo, dest, **opts)   review(ui, repo, dest, opts)   +  def add_unique_reviewer(ui, reviewer, reviewers, name_to_ix, ix_to_name):   '''Adds a reviewer to reviewers if it is not already added. Otherwise, print an error.'''   if name_to_ix[reviewer] in reviewers: @@ -669,6 +718,7 @@
  reviewers.append(name_to_ix[reviewer])   print_list(ui, [ix_to_name[r] for r in reviewers], 'reviewers:')   +  def review(ui, repo, dest, opts):   '''Associates the pushed changesets with a new or existing Kiln review.'''   if not opts['review'] or not repo.ui.config('kiln', 'node'): @@ -770,7 +820,7 @@
  'ixReviewers': reviewers,   'sTitle': '(Multiple changesets)' if len(revs) > 1 else repo[revs[0]].description(),   'sDescription': 'Review created from push.' - } + }   r = Review(call_api(repo.ui, baseurl, 'Api/2.0/Review/Create', params, post=True))   ui.write(_('new review created: %s\n' % urljoin(baseurl, 'Review', r.key)))   else: @@ -778,6 +828,7 @@
  url = reviews[choice].associate(kiln_repo['ixRepo'], revs)   ui.write(_('updated review: %s\n' % url))   +  def dummy_command(ui, repo, dest=None, **opts):   '''dummy command to pass to guess_path() for hg kiln   @@ -786,6 +837,7 @@
  '''   return opts['path'] != dest and dest or None   +  def _standin_expand(paths):   '''given a sequence of filenames, returns a set of filenames --   relative to the current working directory! -- prefixed with all @@ -795,6 +847,7 @@
  choices = [[p, os.path.join('.kbf', p), os.path.join('.hglf', p)] for p in paths]   return set(itertools.chain(*choices))   +  def _filename_match(repo, ctx, paths):   '''returns a set of filenames contained in both paths and the   ctx's manifest, accounting for standins''' @@ -812,6 +865,7 @@
  haystacks = set(map(os.path.normpath, haystacks))   return needles.intersection(haystacks)   +  def kiln(ui, repo, **opts):   '''show the relevant page of the repository in Kiln   @@ -894,6 +948,7 @@
  if default or opts['changes']:   browse(url)   +  def uisetup(ui):   extensions.wrapcommand(commands.table, 'outgoing', guess_kilnpath)   extensions.wrapcommand(commands.table, 'pull', guess_kilnpath) @@ -902,6 +957,7 @@
  # Add --review as a valid flag to push's command table   push_cmd[1].extend([('', 'review', None, 'associate changesets with Kiln review')])   +  def reposetup(ui, repo):   try:   from mercurial.httprepo import httprepository @@ -913,6 +969,7 @@
  _upgradecheck(ui, repo)   repo.ui.setconfig('hooks', 'outgoing.kilnreview', 'python:kiln.record_base')   +  def extsetup(ui):   try:   g = extensions.find('gestalt') @@ -943,4 +1000,4 @@
  ('n', 'new-branch', '', _('asynchronously create a new branch from the current repository')),   ('', 'logout', None, _('log out of Kiln sessions'))],   _('hg kiln [-p url] [-r rev|-a file|-f file|-c|-o|-s|-t|-n branchName|--logout]')) - } +}
Change 1 of 1 Show Entire File kilnfilecache.py Stacked
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@@ -0,0 +1,59 @@
+# Copyright (C) 2011, 2012 Fog Creek Software. All rights reserved. +# +# This extension is used internally by Kiln Importer. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +'''fix a _filecache invalidation bug so hg convert works + +See + + * http://our.fogbugz.com/f/cases/2338914/importer-does-not-work-with-hg-2-3-1 and + * http://www.selenic.com/pipermail/mercurial-devel/2012-November/046159.html + + for more details. Until Mercurial fixes this bug or accepts my patch, +this bug affects any tags in this revset: + + * hg log -r 'descendants(9f94358) and tag()' --template '{tags}\n' + +As of writing, that's every 2.3 release and 2.4-rc and 2.4. +''' + +from mercurial import util + +def reposetup(ui, repo): + if util.version()[:3] < '2.3': + # This bug was first introduced in 9f94358f9f93, which + # occurred between 2.3-rc and 2.3. + return + + from mercurial import localrepo + if not issubclass(repo.__class__, localrepo.localrepository): + return + + class kilnfilecacherepo(repo.__class__): + def destroyed(self, *args, **kwargs): + # By saving _filecache before it's cleared, we can + # restore it and then call invalidate() afterward so + # localrepo can delattr() the relevant attributes off + # the repo object. See my mercurial-devel patch for + # more details. + oldfilecache = dict(self._filecache) + super(kilnfilecacherepo, self).destroyed(*args, **kwargs) + self._filecache = oldfilecache + self.invalidate() + + repo.__class__ = kilnfilecacherepo
Change 1 of 5 Show Entire File kilnpath.py Stacked
 
1
 
2
3
4
 
18
19
20
21
 
22
23
24
 
31
32
33
 
 
 
 
34
35
36
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
39
40
 
43
44
45
 
46
47
48
49
50
 
51
52
53
 
70
71
72
 
73
74
75
76
77
78
 
 
1
2
3
4
 
18
19
20
 
21
22
23
24
 
31
32
33
34
35
36
37
38
39
 
 
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
 
69
70
71
72
73
74
75
76
77
78
79
80
81
 
98
99
100
101
102
103
104
105
106
 
@@ -1,4 +1,4 @@
-# Copyright (C) 2009-2011 Fog Creek Software. All rights reserved. +# Copyright (C) 2009-2013 Fog Creek Software. All rights reserved.  #  # To enable the "kilnpath" extension put these lines in your ~/.hgrc:  # [extensions] @@ -18,7 +18,7 @@
 # along with this program; if not, write to the Free Software  # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.   -'''Lets users identify repository paths by custom schemes like kiln://Project/Group/Repository. +'''allow users identify repository paths by custom schemes like kiln://Project/Group/Repository (DEPRECATED)    This extensions knows how to expand Kiln repository paths from  kiln://Project/Group/Repository to @@ -31,10 +31,36 @@
 To specify the Kiln path prefix, add a [kiln_scheme] section to your hg config like so:  [kiln_scheme]  kiln = https://your.fogbugz.com/kiln/Repo + +This extensions is deprecated, and does not work in Mercurial 2.4 and later. +You should use the built-in `schemes` extension instead instead. Type `hg help schemes` +for more information.  '''   -import os -from mercurial import extensions, commands, hg, ui +import re + +from mercurial import hg, ui, util, __version__ + + +@apply +def _HG_VERSION(): + '''return the mercurial version as a tuple rather than a string + + Python does the right thing when comparing tuples, so the return + value can be used to compare and detect versions. + ''' + version = [0, 0, 0] + parts = [re.match(r'\d+', v).group(0) for v in __version__.version.split('.')[:3]] + for i, part in enumerate(map(int, parts)): + version[i] = part + return tuple(version) + + +if _HG_VERSION >= (2, 3, 0): + raise util.Abort( + 'kilnpath is deprecated, and does not work in Mercurial 2.3 or higher. ' + 'Use the official schemes extension instead') +    def urlcombine(pre, suff):   if pre.endswith("/"): @@ -43,11 +69,13 @@
  suff = "/" + suff   return pre + suff   +  def add_schemes(ui):   schemes = ui.configitems('kiln_scheme')   for scheme, prefix in schemes:   hg.schemes[scheme] = KilnRepo(scheme, prefix)   +  class KilnRepo(object):   def __init__(self, scheme, prefix):   self.scheme = scheme @@ -70,9 +98,9 @@
  # Mercurial <= 1.8   return hg._lookup(path_full).instance(ui, path_full, create)   +  def extsetup(*args):   if len(args) > 0:   add_schemes(args[0])   else:   add_schemes(ui.ui()) -
Change 1 of 1 Show Entire File setup.py Stacked
 
3
4
5
6
 
 
7
8
9
 
3
4
5
 
6
7
8
9
10
@@ -3,7 +3,8 @@
 import zipfile    def compile_extensions(): - compileall.compile_dir(os.path.dirname(__file__), force=1) + curdir = os.path.dirname(os.path.abspath(__file__)) + compileall.compile_dir(curdir, force=True)    def walk(dir_root):   for dirpath, dirnames, filenames in os.walk(dir_root):