313 self._via_quiet = False |
313 self._via_quiet = False |
314 self._dryrun = False |
314 self._dryrun = False |
315 self._common_opts = {} |
315 self._common_opts = {} |
316 self._opt_mutator = {} |
316 self._opt_mutator = {} |
317 |
317 |
|
318 self.reg_option_mutator( "strip", lambda opts : dict( {"rev" : [],}, **opts ) ) |
|
319 self.reg_option_mutator( "graft", lambda opts : dict( {"rev" : [], "continue" : False,}, **opts ) ) |
|
320 self.reg_option_mutator( "log", lambda opts : dict( {"date" : None, "user" : None, "rev" : None,}, **opts ) ) |
|
321 |
318 |
322 |
319 |
323 |
320 def __getattr__( self, name ) : |
324 def __getattr__( self, name ) : |
321 """ |
325 """ |
322 Typical invocation of mercurial commands is in the form: commands.name( ... ). |
326 Typical invocation of mercurial commands is in the form: commands.name( ... ). |
346 elif (cmd == "rebase") : where = extensions.find( "rebase" ) |
351 elif (cmd == "rebase") : where = extensions.find( "rebase" ) |
347 else : where = mercurial.commands |
352 else : where = mercurial.commands |
348 |
353 |
349 kwarg = self._mutate_options( where, self._cmd, kwarg ) |
354 kwarg = self._mutate_options( where, self._cmd, kwarg ) |
350 |
355 |
351 for key, value in kwarg.items() : |
356 for key, value in sorted( kwarg.items(), reverse = True ) : |
352 if (value in [None, ""]) : |
357 if (value in [None, "", False]) : |
353 continue |
358 continue |
354 |
359 |
355 # If the command is `hg commit --message <commit-hint> --force-editor [other-options...]`, we will drop the |
360 # If the command is `hg commit --message <commit-hint> --force-editor [other-options...]`, we will drop the |
356 # `--message <commit-hint> --force-editor` part from the command string because `--force-editor` is not a command |
361 # `--message <commit-hint> --force-editor` part from the command string because `--force-editor` is not a command |
357 # option (it avails only programmatically). |
362 # option (it avails only programmatically). |
476 |
490 |
477 def use_quiet_channel( self, via_quiet = True ) : |
491 def use_quiet_channel( self, via_quiet = True ) : |
478 """ |
492 """ |
479 Print the history to the I{quiet} channel, where text will be displayed even when user does not specify the |
493 Print the history to the I{quiet} channel, where text will be displayed even when user does not specify the |
480 C{--verbose} option. |
494 C{--verbose} option. |
|
495 |
|
496 @type via_quiet: C{bool} |
|
497 @param via_quiet: To turn off using the "quiet" channel for history printing, you can call this function like: |
|
498 C{use_quiet_channel( False )}. |
481 """ |
499 """ |
482 self._via_quiet = via_quiet |
500 self._via_quiet = via_quiet |
483 |
501 |
484 |
502 |
485 |
503 |
486 def use_verbose_channel( self, via_verbose = True ) : |
504 def use_verbose_channel( self, via_verbose = True ) : |
487 """ |
505 """ |
488 Print the history to the I{verbose} channel, where text will be display only when user specify the C{--verbose} option. |
506 Print the history to the I{verbose} channel, where text will be display only when user specify the C{--verbose} option. |
|
507 |
|
508 @type via_verbose: C{bool} |
|
509 @param via_verbose: To turn off using the "verbose" channel for history printing (you will be using the "quiet" |
|
510 channel instead), you can call this function like: C{use_verbose_channel( False )}. |
489 """ |
511 """ |
490 self._via_quiet = not via_verbose |
512 self._via_quiet = not via_verbose |
491 |
513 |
492 |
514 |
493 |
515 |
547 same object as in C{STREAM}. If not, create and return a new C{stream} object. If the new object is not in the standard |
573 same object as in C{STREAM}. If not, create and return a new C{stream} object. If the new object is not in the standard |
548 streams, an C{AbnormalStream} exception will be thrown. One can catch the exception and call its C{stream} method to |
574 streams, an C{AbnormalStream} exception will be thrown. One can catch the exception and call its C{stream} method to |
549 get the object. |
575 get the object. |
550 |
576 |
551 @type name : C{str} |
577 @type name : C{str} |
552 @param name : Name of the stream |
578 @param name : Name of the stream. It can be a complex stream name, e.g., "develop/spring:release". |
553 @type check: C{boolean} |
579 @type check: C{boolean} |
554 @param check: If true and the stream is not a standard one, the function will check if the trunk of the stream exists |
580 @param check: If true and the stream is not a standard one, the function will check if the trunk of the stream exists |
555 or not and (if exists) open or not. |
581 or not and (if exists) open or not. |
556 |
582 |
557 @raise AbortFlow : When C{check} is true and the trunk of the stream doesn't exist or is closed |
583 @raise AbortFlow : When C{check} is true and the trunk of the stream doesn't exist or is closed |
558 @raise AbnormalStream: When the stream is not in any of the standard streams |
584 @raise AbnormalStream: When the stream is not in any of the standard streams |
559 """ |
585 """ |
|
586 source = None |
|
587 tokens = name.split( ':' ) |
|
588 n = len( tokens ) |
|
589 if (n == 2) : |
|
590 source, name = tokens[0], tokens[1] |
|
591 if (n > 2 or not name) : |
|
592 raise AbortFlow( "Invalid stream syntax: '%s'" % stream ) |
|
593 |
560 for e in STREAM.values() : |
594 for e in STREAM.values() : |
561 if (name == e.name()) : |
595 if (name == e.name()) : |
562 return e |
596 if (source) : |
563 |
597 stream = copy.copy( e ) |
564 rootstream_name = name.split( '/', 1 )[0] |
598 break |
565 is_normalstream = True |
599 else : |
566 if (rootstream_name in STREAM) : |
600 return e |
567 trunk = name.replace( rootstream_name + '/', STREAM[rootstream_name].prefix(), 1 ) |
|
568 stream = Stream( ui, repo, name, trunk = trunk ) |
|
569 else : |
601 else : |
570 stream = Stream( ui, repo, name, trunk = name ) |
602 rootstream_name = name.split( '/', 1 )[0] |
571 is_normalstream = False |
603 is_normalstream = True |
572 if (check) : |
604 |
573 try : |
605 if (rootstream_name in STREAM) : |
574 trunk = stream.trunk() |
606 trunk = name.replace( rootstream_name + '/', STREAM[rootstream_name].prefix(), 1 ) |
575 except error.RepoLookupError : |
607 stream = Stream( ui, repo, name, trunk = trunk ) |
576 misspelling = difflib.get_close_matches( stream.name(), STREAM.keys(), 3, 0.7 ) |
608 else : |
577 note = "Did you mean: %s?" % " or ".join( misspelling ) if (misspelling) else None |
609 stream = Stream( ui, repo, name, trunk = name ) |
578 raise AbortFlow( "Stream not found: %s" % stream, note = note ) |
610 is_normalstream = False |
579 if (trunk.is_closed()) : |
611 |
580 raise AbortFlow( "%s has been closed." % stream ) |
612 if (check) : |
581 if (not is_normalstream) : |
613 try : |
582 raise AbnormalStream( stream = Stream( ui, repo, name ) ) |
614 trunk = stream.trunk() |
|
615 except error.RepoLookupError , e : |
|
616 misspelling = difflib.get_close_matches( stream.name(), STREAM.keys(), 3, 0.7 ) |
|
617 note = "Did you mean: %s?" % " or ".join( misspelling ) if (misspelling) else None |
|
618 raise AbortFlow( "Stream not found: %s" % stream, note = note ) |
|
619 if (trunk.is_closed()) : |
|
620 raise AbortFlow( "%s has been closed." % stream ) |
|
621 |
|
622 # It seems we never really mind abnormal streams. So comment this out. |
|
623 #if (not is_normalstream) : |
|
624 # raise AbnormalStream( stream = Stream( ui, repo, name ) ) |
|
625 |
|
626 if (source) : |
|
627 source = Stream.gen( ui, repo, source, check = True ) |
|
628 stream = copy.copy( stream ) |
|
629 stream._source = source |
|
630 for i, e in enumerate( stream.destin() ) : |
|
631 if (source in e) : |
|
632 stream._destin[i] = source |
|
633 break |
|
634 else : |
|
635 stream._destin = [source] |
|
636 |
583 return stream |
637 return stream |
584 |
638 |
585 |
639 |
586 |
640 |
587 def __init__( self, ui, repo, name, **kwarg ) : |
641 def __init__( self, ui, repo, name, **kwarg ) : |
666 Return the trunk of this stream. If it has no trunk, return C{None} or the trunk of the source stream depending on the |
720 Return the trunk of this stream. If it has no trunk, return C{None} or the trunk of the source stream depending on the |
667 C{trace} parameter. |
721 C{trace} parameter. |
668 |
722 |
669 @type trace: C{boolean} |
723 @type trace: C{boolean} |
670 @param trace: If true and this stream has no trunk, return the trunk of the source stream, and do this recursively |
724 @param trace: If true and this stream has no trunk, return the trunk of the source stream, and do this recursively |
671 until a trunk is found. If false, this function will return C{None}. |
725 until a trunk is found. If false and this stream has no trunk, this function will return C{None}. |
672 |
726 |
673 @return: A C{Branch} object or C{None} |
727 @return: A C{Branch} object or C{None} |
674 """ |
728 """ |
675 if (self._tcache) : |
729 if (self._tcache) : |
676 return self._tcache |
730 return self._tcache |
677 |
731 |
678 trunk = Branch( self.ui, self.repo, self._trunk ) if (self._trunk) else None |
732 trunk = Branch( self.ui, self.repo, self._trunk ) if (self._trunk) else None |
679 if (not trunk and trace) : |
733 if (not trunk and trace) : |
680 return self.source().trunk( True ) |
734 return self.source().trunk( True ) |
681 self._tcache = trunk |
735 self._tcache = trunk |
682 return trunk |
736 return trunk |
749 closed branches in this stream; if C{"all"}, returns all open and closed branches in this stream. |
803 closed branches in this stream; if C{"all"}, returns all open and closed branches in this stream. |
750 """ |
804 """ |
751 if (openclosed not in ["open", "closed", "all",]) : |
805 if (openclosed not in ["open", "closed", "all",]) : |
752 raise ValueError( "Invalid value for `openclosed` parameter: %s" % openclosed ) |
806 raise ValueError( "Invalid value for `openclosed` parameter: %s" % openclosed ) |
753 |
807 |
|
808 all_branches = [] |
754 if (openclosed == "open") : |
809 if (openclosed == "open") : |
755 all_branches = [Branch( self.ui, self.repo, head[0] ) for branch_fullname, head in self.repo.branchmap().items() |
810 for branch_fullname, heads in self.repo.branchmap().items() : |
756 if (not self.repo[head[0]].extra().get( "close", False ) )] |
811 all_branches += [Branch( self.ui, self.repo, head ) for head in heads |
|
812 if (not self.repo[head].extra().get( "close", False ))] |
757 elif (openclosed == "closed") : |
813 elif (openclosed == "closed") : |
758 all_branches = [Branch( self.ui, self.repo, head[0] ) for branch_fullname, head in self.repo.branchmap().items() |
814 for branch_fullname, heads in self.repo.branchmap().items() : |
759 if (self.repo[head[0]].extra().get( "close", False ) )] |
815 all_branches += [Branch( self.ui, self.repo, head ) for head in heads |
|
816 if (self.repo[head].extra().get( "close", False ))] |
760 else : |
817 else : |
761 all_branches = [Branch( self.ui, self.repo, head[0] ) for branch_fullname, head in self.repo.branchmap().items()] |
818 for branch_fullname, heads in self.repo.branchmap().items() : |
|
819 all_branches += [Branch( self.ui, self.repo, head ) for head in heads] |
762 |
820 |
763 return sorted( [e for e in all_branches if (e in self)] ) |
821 return sorted( [e for e in all_branches if (e in self)] ) |
764 |
822 |
765 |
823 |
766 |
824 |
809 """ |
867 """ |
810 Return the basename relative to the C{stream}. If C{stream} is C{None}, return the shortest possible basename (will |
868 Return the basename relative to the C{stream}. If C{stream} is C{None}, return the shortest possible basename (will |
811 not contain any '/'s). |
869 not contain any '/'s). |
812 Return the string "trunk" if this branch is the trunk of the C{stream}. |
870 Return the string "trunk" if this branch is the trunk of the C{stream}. |
813 |
871 |
814 @type stream: C{Stream} or C{None} |
872 @type stream: C{Stream} or C{None} |
815 @param stream: Stream to which the basename is relative |
873 @param stream: Stream to which the basename is relative |
|
874 @type should_quote: C{bool} |
|
875 @param should_quote: The returned string will be wrapped with single quotes ' if this parameter's value is true. |
816 """ |
876 """ |
817 if (stream) : |
877 if (stream) : |
818 if (self._fullname == stream._trunk) : |
878 if (self._fullname == stream._trunk) : |
819 return "trunk" |
879 return "trunk" |
820 ret = self._fullname[len( stream.prefix() ):] |
880 ret = self._fullname[len( stream.prefix() ):] |
821 else : |
881 else : |
822 ret = self._fullname.rsplit( '/', 1 )[-1] |
882 ret = self._fullname.rsplit( '/', 1 )[-1] |
823 if (should_quote) : |
883 if (should_quote) : |
824 ret = "'%s'" % ret |
884 ret = "'%s'" % ret |
825 return ret |
885 return ret |
826 |
886 |
|
887 |
|
888 |
|
889 def rev_node( self ) : |
|
890 """ |
|
891 Return a string showing this branch's head's revision number and short node ID in the format of "<rev>:<node-ID>", |
|
892 e.g., "9:db14bf692069". |
|
893 """ |
|
894 return "%s:%s" % (self.ctx.rev(), short( self.ctx.node() ),) |
|
895 |
827 |
896 |
828 |
897 |
829 def is_closed( self ) : |
898 def is_closed( self ) : |
830 """ |
899 """ |
831 Return true if this branch is closed; or false if it is open. |
900 Return true if this branch is closed; or false if it is open. |
889 STREAM = {} # key = stream name, value = `Stream` object. Will be set by `Flow.__init__`. |
958 STREAM = {} # key = stream name, value = `Stream` object. Will be set by `Flow.__init__`. |
890 |
959 |
891 |
960 |
892 |
961 |
893 class Flow( object ) : |
962 class Flow( object ) : |
|
963 |
|
964 ACTION_NAME = ["start", "finish", "push", "publish", "pull", "list", "log", "abort", "promote", "rebase", "rename",] |
|
965 |
894 def __init__( self, ui, repo, init = False ) : |
966 def __init__( self, ui, repo, init = False ) : |
895 """ |
967 """ |
896 Construct a C{Flow} instance that will execute the workflow. |
968 Construct a C{Flow} instance that will execute the workflow. |
897 Construction will fail if the C{flow} extension has not been initialized for the repository. |
969 Construction will fail if the C{flow} extension has not been initialized for the repository. |
898 A warning message will be issued if the repository has uncommitted changes. |
970 A warning message will be issued if the repository has uncommitted changes. |
944 hotfix = cfg.get( CONFIG_SECTION_BRANCHNAME, "hotfix" ) |
1016 hotfix = cfg.get( CONFIG_SECTION_BRANCHNAME, "hotfix" ) |
945 support = cfg.get( CONFIG_SECTION_BRANCHNAME, "support" ) |
1017 support = cfg.get( CONFIG_SECTION_BRANCHNAME, "support" ) |
946 except Exception, e : |
1018 except Exception, e : |
947 self._error( str( e ) ) |
1019 self._error( str( e ) ) |
948 self._error( "Flow has not been initialized properly for this repository." ) |
1020 self._error( "Flow has not been initialized properly for this repository." ) |
949 self._note ( "You can use command `hg flow init -f` to reinitialize for this repository.", via_quiet = True ) |
1021 self._note ( "You can use command `hg flow init -f` to reinitialize for this repository.", |
|
1022 via_quiet = True ) |
950 sys.exit( 1 ) |
1023 sys.exit( 1 ) |
951 else : |
1024 else : |
952 self._error( "Flow has not been initialized for this repository: %s file is missing." % CONFIG_BASENAME ) |
1025 self._error( "Flow has not been initialized for this repository: %s file is missing." % CONFIG_BASENAME ) |
953 self._note ( "You can use command `hg flow init` to initialize for this repository.", via_quiet = True ) |
1026 self._note ( "You can use command `hg flow init` to initialize for this repository.", via_quiet = True ) |
954 sys.exit( 1 ) |
1027 sys.exit( 1 ) |
1140 C{hg import <patch_filename> --no-commit} |
1213 C{hg import <patch_filename> --no-commit} |
1141 C{hg qdelete <patchname>} |
1214 C{hg qdelete <patchname>} |
1142 where <patchname> follows the pattern: flow/<branch_fullname>.pch, which was previously created by flow's shelving. |
1215 where <patchname> follows the pattern: flow/<branch_fullname>.pch, which was previously created by flow's shelving. |
1143 |
1216 |
1144 @type basename: C{str} |
1217 @type basename: C{str} |
1145 @param basename: Base name of the shelved patch file. Default is the name of current workspace branch. |
1218 @param basename: Basename of the path of the shelved patch file. Default is the name of current workspace branch. |
1146 """ |
1219 """ |
1147 if (self.autoshelve or kwarg.get( "force" )) : |
1220 if (self.autoshelve or kwarg.get( "force" )) : |
1148 basename = basename if (basename) else self.curr_workspace.fullname() |
1221 basename = basename if (basename) else self.curr_workspace.fullname() |
1149 shelve_name = "flow/" + basename + ".pch" |
1222 shelve_name = "flow/" + basename + ".pch" |
1150 patch_fname = self.repo.join( "patches/" + shelve_name ) |
1223 patch_fname = self.repo.join( "patches/" + shelve_name ) |
1173 @param openclosed: If C{"open"}, return all open branches; if C{"closed"}, return all closed branches; if C{"all"}, |
1246 @param openclosed: If C{"open"}, return all open branches; if C{"closed"}, return all closed branches; if C{"all"}, |
1174 return all branches. |
1247 return all branches. |
1175 """ |
1248 """ |
1176 if (openclosed not in ["open", "closed", "all",]) : |
1249 if (openclosed not in ["open", "closed", "all",]) : |
1177 raise ValueError( "Invalid value for openclosed parameter: %s" % openclosed ) |
1250 raise ValueError( "Invalid value for openclosed parameter: %s" % openclosed ) |
|
1251 |
|
1252 all_branches = [] |
1178 if (openclosed == "open") : |
1253 if (openclosed == "open") : |
1179 all_branches = [Branch( self.ui, self.repo, head[0] ) for branch_fullname, head in self.repo.branchmap().items() |
1254 for branch_fullname, heads in self.repo.branchmap().items() : |
1180 if (not self.repo[head[0]].extra().get( "close", False ) )] |
1255 all_branches += [Branch( self.ui, self.repo, head ) for head in heads |
|
1256 if (not self.repo[head].extra().get( "close", False ))] |
1181 elif (openclosed == "closed") : |
1257 elif (openclosed == "closed") : |
1182 all_branches = [Branch( self.ui, self.repo, head[0] ) for branch_fullname, head in self.repo.branchmap().items() |
1258 for branch_fullname, heads in self.repo.branchmap().items() : |
1183 if (self.repo[head[0]].extra().get( "close", False ))] |
1259 all_branches += [Branch( self.ui, self.repo, head ) for head in heads |
|
1260 if (self.repo[head].extra().get( "close", False ))] |
1184 else : |
1261 else : |
1185 all_branches = [Branch( self.ui, self.repo, head[0] ) for branch_fullname, head in self.repo.branchmap().items()] |
1262 for branch_fullname, heads in self.repo.branchmap().items() : |
|
1263 all_branches += [Branch( self.ui, self.repo, head ) for head in heads] |
|
1264 |
1186 return all_branches |
1265 return all_branches |
1187 |
1266 |
1188 |
1267 |
1189 |
1268 |
1190 def _find_branch( self, fullname ) : |
1269 def _find_branch( self, fullname ) : |
1191 """ |
1270 """ |
1192 Return true if a branch C{fullname} is open. |
1271 Try to find a branch of name: C{fullname}. If it exists, return a C{Branch} object of this branch and a boolean value |
|
1272 indicating if it's open (C{True}) or closed (C{False}). If it does not exists, return C{(None, None)}. |
1193 |
1273 |
1194 @type fullname: C{str} |
1274 @type fullname: C{str} |
1195 @param fullname: Fullname of branch that you want to know whether it is open |
1275 @param fullname: Fullname of the branch to find |
1196 """ |
1276 """ |
1197 try : |
1277 try : |
1198 Branch( self.ui, self.repo, fullname ) |
1278 branch = Branch( self.ui, self.repo, fullname ) |
1199 return True |
1279 return branch, branch.is_open() |
1200 except error.RepoLookupError : |
1280 except error.RepoLookupError : |
1201 return False |
1281 return None, None |
1202 |
1282 |
1203 |
1283 |
1204 |
1284 |
1205 def latest_master_tags( self ) : |
1285 def latest_master_tags( self ) : |
1206 """ |
1286 """ |
1261 try : |
1341 try : |
1262 basename = arg[1] |
1342 basename = arg[1] |
1263 except IndexError : |
1343 except IndexError : |
1264 raise AbortFlow( "You must specify a name for the new branch to start." ) |
1344 raise AbortFlow( "You must specify a name for the new branch to start." ) |
1265 |
1345 |
1266 rev = kwarg.pop( "rev", None ) |
1346 rev = kwarg.pop( "rev", None ) |
1267 msg = kwarg.pop( "message", "" ) |
1347 msg = kwarg.pop( "message", "" ) |
1268 dirty = kwarg.pop( "dirty", None ) |
1348 dirty = kwarg.pop( "dirty", None ) |
1269 fullname = stream.get_fullname( basename ) |
1349 fullname = stream.get_fullname( basename ) |
1270 if (self._find_branch( fullname )) : |
1350 br, is_open = self._find_branch( fullname ) |
1271 self._warn( "An open branch named '%s' already exists in %s." % (basename, stream,) ) |
1351 if (br) : |
|
1352 self._error( "A branch named '%s' already exists in %s: '%s'." % (basename, stream, fullname,) ) |
|
1353 if (not is_open) : |
|
1354 self._note( "Branch '%s' is currently closed." % fullname, via_quiet = True ) |
1272 else : |
1355 else : |
1273 shelvedpatch_basename = self.curr_workspace.fullname() |
1356 shelvedpatch_basename = self.curr_workspace.fullname() |
1274 if (rev is None) : |
1357 if (rev is None) : |
1275 from_branch = stream.source().trunk() |
1358 from_branch = stream.source().trunk() |
1276 self._shelve( force = dirty ) |
1359 self._shelve( force = dirty ) |
1341 tags = (", latest tags: %s" % ", ".join( tags )) if (tags) else "" |
1424 tags = (", latest tags: %s" % ", ".join( tags )) if (tags) else "" |
1342 self._print( "%s trunk: %s%s" % (stream, trunk, tags,) ) |
1425 self._print( "%s trunk: %s%s" % (stream, trunk, tags,) ) |
1343 if (open_branches) : |
1426 if (open_branches) : |
1344 self._print( "Open %s branches:" % stream ) |
1427 self._print( "Open %s branches:" % stream ) |
1345 for e in open_branches : |
1428 for e in open_branches : |
1346 marker = "#" if (self._is_shelved( e ) ) else "" |
1429 marker = "#" if (self._is_shelved( e ) ) else "" |
1347 marker += "*" if (e == self.curr_workspace) else "" |
1430 marker += "*" if (e == self.curr_workspace) else "" |
|
1431 marker += " %s" % e.rev_node() |
1348 self._print( str( e ) + marker, prefix = " " ) |
1432 self._print( str( e ) + marker, prefix = " " ) |
1349 else : |
1433 else : |
1350 self._print( "No open %s branches" % stream ) |
1434 self._print( "No open %s branches" % stream ) |
1351 if (kwarg.get( "closed" )) : |
1435 if (kwarg.get( "closed" )) : |
1352 closed_branches = stream.branches( "closed" ) |
1436 closed_branches = stream.branches( "closed" ) |
1353 if (closed_branches) : |
1437 if (closed_branches) : |
1354 self._print( "Closed %s branches:" % stream ) |
1438 self._print( "Closed %s branches:" % stream ) |
1355 closed_branches.sort( lambda x, y : y.ctx.rev() - x.ctx.rev() ) |
1439 closed_branches.sort( lambda x, y : y.ctx.rev() - x.ctx.rev() ) |
1356 for e in closed_branches : |
1440 for e in closed_branches : |
1357 self.ui.write( "%-31s" % e.basename( stream ), label = "branches.closed" ) |
1441 self.ui.write( "%-31s" % e.basename( stream ), label = "branches.closed" ) |
1358 self.ui.write( " %5s:%s" % (e.ctx.rev(), short( e.ctx.node() ),), label = "log.changeset" ) |
1442 self.ui.write( " %18s" % e.rev_node(), label = "log.changeset" ) |
1359 self.ui.write( " %s\n" % util.datestr( e.ctx.date(), format = "%Y-%m-%d %a %H:%M %1" ), |
1443 self.ui.write( " %s\n" % util.datestr( e.ctx.date(), format = "%Y-%m-%d %a %H:%M %1" ), |
1360 label = "log.date" ) |
1444 label = "log.date" ) |
1361 bn = str( e ) |
1445 bn = str( e ) |
1362 p1 = e.ctx |
1446 p1 = e.ctx |
1363 while (p1.branch() == bn) : |
1447 while (p1.branch() == bn) : |
1462 Abort the workspace branch. |
1545 Abort the workspace branch. |
1463 |
1546 |
1464 @type stream: C{Stream} |
1547 @type stream: C{Stream} |
1465 @param stream: Stream where the branch which you want to abort is |
1548 @param stream: Stream where the branch which you want to abort is |
1466 """ |
1549 """ |
|
1550 arg = arg[1:] |
1467 msg = kwarg.pop( "message", "" ) |
1551 msg = kwarg.pop( "message", "" ) |
1468 should_erase = kwarg.pop( "erase", False ) |
1552 should_erase = kwarg.pop( "erase", False ) |
1469 onstream = kwarg.pop( "onstream", False ) |
1553 onstream = kwarg.pop( "onstream", False ) |
1470 curr_workspace = self.curr_workspace |
1554 curr_workspace = self.curr_workspace |
1471 if (msg) : |
1555 if (msg) : |
1472 msg = "%s\n" % msg |
1556 msg = "%s\n" % msg |
1473 if (curr_workspace.is_develop_trunk()) : |
1557 if (curr_workspace.is_develop_trunk()) : |
1474 raise AbortFlow( "You cannot abort the <develop> trunk." ) |
1558 raise AbortFlow( "You cannot abort the <develop> trunk." ) |
|
1559 if (arg) : |
|
1560 if (len( arg ) > 1 or curr_workspace.basename() != arg[0]) : |
|
1561 raise AbortFlow( "hgflow intentionally forbids aborting a non-workspace branch." ) |
1475 if (onstream) : |
1562 if (onstream) : |
1476 branches = stream.branches() |
1563 branches = stream.branches() |
1477 if (stream == STREAM["develop"]) : |
1564 if (stream == STREAM["develop"]) : |
1478 branches.remove( stream.trunk() ) |
1565 branches.remove( stream.trunk() ) |
1479 elif (stream._trunk) : |
1566 elif (stream._trunk) : |
1480 branches.append( stream.trunk() ) |
1567 branches.append( stream.trunk() ) |
1481 for branch in branches : |
1568 for branch in branches : |
1482 if (should_erase) : |
1569 if (should_erase) : |
1483 self._strip( rev = ["branch('%s')" % branch,] ) |
1570 self._strip( branch, self.repo.revs( "min(branch('%s'))" % branch )[0] ) |
1484 else : |
1571 else : |
1485 self._update( branch ) |
1572 self._update( branch ) |
1486 self._commit( close_branch = True, message = "%s%sAborted %s %s." % |
1573 self._commit( close_branch = True, message = "%s%sAborted %s %s." % |
1487 (msg, self.msg_prefix, stream, branch.basename( stream, should_quote = True ),) ) |
1574 (msg, self.msg_prefix, stream, branch.basename( stream, should_quote = True ),) ) |
1488 if (self.curr_workspace != self.orig_workspace and self._orig_workspace not in branches) : |
1575 if (self.curr_workspace != self.orig_workspace and self._orig_workspace not in branches) : |
1495 ) |
1582 ) |
1496 if (curr_workspace not in stream) : |
1583 if (curr_workspace not in stream) : |
1497 raise AbortFlow( "Your workspace is '%s' branch, which is not in %s." % (curr_workspace, stream,), |
1584 raise AbortFlow( "Your workspace is '%s' branch, which is not in %s." % (curr_workspace, stream,), |
1498 "To abort a %s branch, you must first update to it." % stream ) |
1585 "To abort a %s branch, you must first update to it." % stream ) |
1499 if (should_erase) : |
1586 if (should_erase) : |
1500 self._strip( rev = ["branch('%s')" % curr_workspace,] ) |
1587 self._strip( curr_workspace, self.repo.revs( "min(branch('%s'))" % curr_workspace )[0] ) |
1501 else : |
1588 else : |
1502 self._commit( close_branch = True, message = "%s%sAborted %s '%s'." % |
1589 self._commit( close_branch = True, message = "%s%sAborted %s '%s'." % |
1503 (msg, self.msg_prefix, stream, curr_workspace.basename( stream ),) ) |
1590 (msg, self.msg_prefix, stream, curr_workspace.basename( stream ),) ) |
1504 self._update( stream.trunk( trace = True ) ) |
1591 self._update( stream.trunk( trace = True ) ) |
1505 self._unshelve() |
1592 self._unshelve() |
1511 Promote the workspace branch to its destination stream(s). If there are uncommitted changes in the current branch, |
1598 Promote the workspace branch to its destination stream(s). If there are uncommitted changes in the current branch, |
1512 they will be automatically shelved before rebasing and unshelved afterwards. |
1599 they will be automatically shelved before rebasing and unshelved afterwards. |
1513 |
1600 |
1514 @type stream: C{Stream} |
1601 @type stream: C{Stream} |
1515 @param stream: Stream where the branch which you want to rebase is |
1602 @param stream: Stream where the branch which you want to rebase is |
1516 @type rev: C{str} |
1603 @type rev : C{str} |
1517 @param rev: If provided, promote this revision instead of the head. The specified revision must be in the workspace |
1604 @param rev : If provided, promote this revision instead of the head. The specified revision must be in the workspace |
1518 branch. |
1605 branch. |
1519 """ |
1606 """ |
1520 rev = kwarg.pop( "rev", None ) |
1607 rev = kwarg.pop( "rev", None ) |
1521 tag_name = kwarg.pop( "tag", None ) |
1608 tag_name = kwarg.pop( "tag", None ) |
1522 message = kwarg.pop( "message", None ) |
1609 message = kwarg.pop( "message", None ) |
1588 Rebase the workspace branch to its parent branch. If there are uncommitted changes in the current branch, they will be |
1675 Rebase the workspace branch to its parent branch. If there are uncommitted changes in the current branch, they will be |
1589 automatically shelved before rebasing and unshelved afterwards. |
1676 automatically shelved before rebasing and unshelved afterwards. |
1590 |
1677 |
1591 @type stream: C{Stream} |
1678 @type stream: C{Stream} |
1592 @param stream: Stream where the branch which you want to rebase is |
1679 @param stream: Stream where the branch which you want to rebase is |
1593 @type dest: C{str} |
1680 @type dest : C{str} |
1594 @param dest: If provided, use its value as the destination of rebasing. The value must be a changeset of the parent |
1681 @param dest : If provided, use its value as the destination of rebasing. The value must be a changeset of the parent |
1595 branch, otherwise it will trigger an error. If not provided, use the tip of the parent branch as the |
1682 branch, otherwise it will trigger an error. If not provided, use the tip of the parent branch as the |
1596 destination of rebasing. |
1683 destination of rebasing. |
1597 """ |
1684 """ |
1598 dest = kwarg.get( "dest" ) |
1685 dest = kwarg.get( "dest" ) |
1599 onstream = kwarg.pop( "onstream", False ) |
1686 onstream = kwarg.pop( "onstream", False ) |
1625 else : |
1712 else : |
1626 self._check_rebase() |
1713 self._check_rebase() |
1627 self._shelve() |
1714 self._shelve() |
1628 self._rebase( base = curr_workspace, dest = dest, keepbranches = True ) |
1715 self._rebase( base = curr_workspace, dest = dest, keepbranches = True ) |
1629 self._unshelve() |
1716 self._unshelve() |
|
1717 |
|
1718 |
|
1719 |
|
1720 def _action_rename( self, stream, *arg, **kwarg ) : |
|
1721 """ |
|
1722 Rename the workspace branch to a new basename. If there are uncommitted changes in the current branch, they will be |
|
1723 automatically shelved before renaming and unshelved afterwards. |
|
1724 Under the hood this action will create a new branch and copy (or graft) all commits in the workspace branch to the new |
|
1725 branch and then erase the workspace branch. |
|
1726 |
|
1727 @type stream: C{Stream} |
|
1728 @param stream: Stream where the branch which you want to rename is |
|
1729 @type to : C{str} |
|
1730 @param to : Its value should be the new basename of the workspace branch. |
|
1731 """ |
|
1732 new_branch_name = kwarg.pop( "to", None ) |
|
1733 curr_workspace = self.curr_workspace |
|
1734 if (not new_branch_name) : |
|
1735 raise AbortFlow( "Please specify the new base name of this branch via the `-t` option." ) |
|
1736 if (curr_workspace not in stream) : |
|
1737 raise AbortFlow( "Your workspace is '%s' branch, which is not in %s." % (curr_workspace, stream,), |
|
1738 "To rename a %s branch, you must first update to it." % stream ) |
|
1739 if (curr_workspace.is_trunk( stream )) : |
|
1740 raise AbortFlow( "You cannot rename the trunk of %s." % stream ) |
|
1741 if (new_branch_name == curr_workspace.basename( stream )) : |
|
1742 self._warn( "No effects because the supposed new basename turns out to be the same as the current one." ) |
|
1743 else : |
|
1744 cfn = curr_workspace.fullname() |
|
1745 brn = "branch(%s)" % cfn |
|
1746 rev = "min(%s)" % brn |
|
1747 ctx = self.repo[self.repo.revs( rev )[0]] |
|
1748 nfn = stream.get_fullname( new_branch_name ) |
|
1749 msg = ctx.description() |
|
1750 msg = msg.replace( cfn, nfn ) |
|
1751 self._shelve() |
|
1752 self._update( self.repo.revs( "%s^" % rev )[0] ) |
|
1753 self._create_branch( nfn, msg, user = ctx.user(), date = util.datestr( ctx.date() ) ) |
|
1754 self._graft( curr_workspace, **kwarg ) |
|
1755 self._unshelve( cfn ) |
|
1756 self._strip( curr_workspace, int( ctx ) ) |
1630 |
1757 |
1631 |
1758 |
1632 |
1759 |
1633 def _update_workspace( self, stream, branch, verbose = True ) : |
1760 def _update_workspace( self, stream, branch, verbose = True ) : |
1634 """ |
1761 """ |
1666 branch = stream.get_branch( name ) |
1794 branch = stream.get_branch( name ) |
1667 if (branch.is_closed()) : |
1795 if (branch.is_closed()) : |
1668 self._warn( "%s '%s' has been closed." % (stream, name,) ) |
1796 self._warn( "%s '%s' has been closed." % (stream, name,) ) |
1669 self._update_workspace( stream, branch ) |
1797 self._update_workspace( stream, branch ) |
1670 except error.RepoLookupError : |
1798 except error.RepoLookupError : |
1671 misspelling = difflib.get_close_matches( name, ["start", "finish", "push", "publish", "pull", |
1799 misspelling = difflib.get_close_matches( name, Flow.ACTION_NAME, 3, 0.7 ) |
1672 "list", "log", "abort", "promote", "rebase",], 3, 0.7 ) |
|
1673 note = ("Did you mean: %s?" % " or ".join( misspelling )) if (misspelling ) else None |
1800 note = ("Did you mean: %s?" % " or ".join( misspelling )) if (misspelling ) else None |
1674 note = ("Did you mean: finish or abort?") if ("close" == name) else note |
1801 note = ("Did you mean: finish or abort?") if ("close" == name) else note |
1675 note = ("If you meant to create a new branch called '%s' in %s" % (name, stream,), |
1802 if (stream != STREAM["master"]) : |
1676 "try command:", " hg flow %s start %s" % (stream.name(), name,),) if (not note) else note |
1803 note = ("If you meant to create a new branch called '%s' in %s" % (name, stream,), |
|
1804 "try command:", " hg flow %s start %s" % (stream.name(), name,),) if (not note) else note |
1677 raise AbortFlow( "Invalid action or unknown branch in %s: '%s'" % (stream, name,), note = note ) |
1805 raise AbortFlow( "Invalid action or unknown branch in %s: '%s'" % (stream, name,), note = note ) |
1678 |
1806 |
1679 |
1807 |
1680 |
1808 |
1681 def _commit_change( self, opt, commit_hint, is_erasing = False ) : |
1809 def _commit_change( self, opt, commit_hint, is_erasing = False ) : |
1738 onstream = kwarg.pop( "onstream", False ) |
1866 onstream = kwarg.pop( "onstream", False ) |
1739 should_erase = kwarg.pop( "erase", False ) |
1867 should_erase = kwarg.pop( "erase", False ) |
1740 curr_workspace = self.curr_workspace |
1868 curr_workspace = self.curr_workspace |
1741 curr_stream = curr_workspace.stream() |
1869 curr_stream = curr_workspace.stream() |
1742 name = curr_workspace.basename( stream, should_quote = True ) |
1870 name = curr_workspace.basename( stream, should_quote = True ) |
|
1871 tag_name_orig = tag_name |
1743 tag_name = tag_name if (tag_name) else (self.version_prefix + name[1:-1]) |
1872 tag_name = tag_name if (tag_name) else (self.version_prefix + name[1:-1]) |
1744 develop_stream = STREAM["develop"] |
1873 develop_stream = STREAM["develop"] |
1745 |
1874 |
1746 if (should_erase) : |
1875 if (should_erase) : |
1747 if (onstream ) : raise AbortFlow( "'--erase' cannot be used together with '--onstream'." ) |
1876 if (onstream ) : raise AbortFlow( "'--erase' cannot be used together with '--onstream'." ) |
1795 # This is particularly needed for dry run. |
1924 # This is particularly needed for dry run. |
1796 if (not is_commit_done and self._has_uncommitted_changes()) : |
1925 if (not is_commit_done and self._has_uncommitted_changes()) : |
1797 raise AbortFlow( "Cannot finish '%s' branch because it has uncommitted changes." % curr_workspace ) |
1926 raise AbortFlow( "Cannot finish '%s' branch because it has uncommitted changes." % curr_workspace ) |
1798 |
1927 |
1799 # For destin streams without trunks, we need to create a branch in each of these destin streams. |
1928 # For destin streams without trunks, we need to create a branch in each of these destin streams. |
1800 # Each newly created branch will be named after the pattern: <stream-prefix>/<current-branch-basename> and will be from |
1929 # Each newly created branch will be from the current branch and named after the pattern: |
1801 # the current branch. The current branch will be closed. Note that there is no need to merge the current branch because |
1930 # <stream-prefix>/<current-branch-basename>. Afterwards, the current branch will be closed. |
1802 # a new branch has been created from it. |
1931 # Note that there is no need to merge the current branch because the new branch is created from it. |
1803 for s in destin_without_trunk : |
1932 for s in destin_without_trunk : |
1804 trunk = s.trunk() |
1933 trunk = s.trunk() |
1805 so_name = "" if ("trunk" == curr_workspace.basename( stream )) else ("/" + curr_workspace.basename( stream )) |
1934 so_name = "" if ("trunk" == curr_workspace.basename( stream )) else ("/" + curr_workspace.basename( stream )) |
1806 so = Stream( self.ui, self.repo, stream.name() + so_name, trunk = curr_workspace.fullname() ) |
1935 so = Stream ( self.ui, self.repo, stream.name() + so_name, trunk = curr_workspace.fullname() ) |
1807 so = "%s:%s" % (so.name(), s.name(),) |
1936 so = Stream.gen( self.ui, self.repo, "%s:%s" % (so.name(), s.name(),), check = True ) |
1808 basename = curr_workspace.basename() |
1937 basename = curr_workspace.basename() |
1809 self.action( so, "start", basename ) |
1938 self.action( so, "start", basename ) |
1810 final_branch = s.get_fullname( basename ) |
1939 final_branch = s.get_fullname( basename ) |
1811 |
1940 |
1812 if (destin_with_trunk or destin_without_trunk) : |
1941 if (destin_with_trunk or destin_without_trunk) : |
1819 self._update( curr_workspace ) |
1948 self._update( curr_workspace ) |
1820 self._commit( close_branch = True, message = "%sClosed %s %s." % (self.msg_prefix, stream, name,), **kwarg ) |
1949 self._commit( close_branch = True, message = "%sClosed %s %s." % (self.msg_prefix, stream, name,), **kwarg ) |
1821 final_branch = STREAM["master"].trunk() |
1950 final_branch = STREAM["master"].trunk() |
1822 else : |
1951 else : |
1823 self._print( "All open branches in %s are finished and merged to its trunk." % stream ) |
1952 self._print( "All open branches in %s are finished and merged to its trunk." % stream ) |
|
1953 |
|
1954 if (tag_name_orig and (STREAM["master"] not in destin_with_trunk)) : |
|
1955 self._warn( "You specified a tag name, but it has effect only when the workspace branch is merged to <master>." ) |
|
1956 |
1824 for s in destin_with_trunk : |
1957 for s in destin_with_trunk : |
1825 trunk = s.trunk() |
1958 trunk = s.trunk() |
1826 self._update( trunk ) |
1959 self._update( trunk ) |
1827 self._merge ( curr_workspace ) |
1960 self._merge ( curr_workspace ) |
1828 self._commit( message = "%sMerged %s %s to %s ('%s')." % (self.msg_prefix, stream, name, s, trunk,), **kwarg ) |
1961 self._commit( message = "%sMerged %s %s to %s ('%s')." % (self.msg_prefix, stream, name, s, trunk,), **kwarg ) |
1829 if (s == STREAM["master"]) : |
1962 if (s == STREAM["master"]) : |
1830 self._tag( tag_name ) |
1963 self._tag( tag_name, force = True ) |
1831 elif (s in develop_stream and s is not develop_stream) : |
1964 elif (s in develop_stream and s is not develop_stream) : |
1832 tr_stream = trunk.stream() |
1965 tr_stream = trunk.stream() |
1833 for ss in tr_stream.destin() : |
1966 for ss in tr_stream.destin() : |
1834 if (ss == develop_stream) : |
1967 if (ss == develop_stream) : |
1835 dvtrunk = develop_stream.trunk() |
1968 dvtrunk = develop_stream.trunk() |
1878 "list" : self._action_list, |
2011 "list" : self._action_list, |
1879 "log" : self._action_log, |
2012 "log" : self._action_log, |
1880 "abort" : self._action_abort, |
2013 "abort" : self._action_abort, |
1881 "promote" : self._action_promote, |
2014 "promote" : self._action_promote, |
1882 "rebase" : self._action_rebase, |
2015 "rebase" : self._action_rebase, |
|
2016 "rename" : self._action_rename, |
1883 "other" : self._action_other, |
2017 "other" : self._action_other, |
1884 } |
2018 } |
1885 |
2019 |
1886 custom_action_func = kwarg.pop( "action_func", {} ) |
2020 custom_action_func = kwarg.pop( "action_func", {} ) |
1887 action_func.update( custom_action_func ) |
2021 action_func.update( custom_action_func ) |
1891 |
2025 |
1892 |
2026 |
1893 def action( self, stream, *arg, **kwarg ) : |
2027 def action( self, stream, *arg, **kwarg ) : |
1894 """ |
2028 """ |
1895 Execute action on the stream. |
2029 Execute action on the stream. |
|
2030 |
|
2031 @type stream: C{Stream} |
|
2032 @param stream: Stream where we will execute the action |
1896 """ |
2033 """ |
1897 if (len( arg ) > 0) : |
2034 if (len( arg ) > 0) : |
1898 action = arg[0] |
2035 action = arg[0] |
1899 if (stream == STREAM["master"]) : |
2036 if (stream == STREAM["master"]) : |
1900 if (action in ["start", "finish", "abort", "rebase",]) : |
2037 if (action in ["start", "finish", "abort", "rebase",]) : |
1901 raise AbortFlow( "Invalid action for <master>" ) |
2038 raise AbortFlow( "Invalid action for <master>" ) |
1902 else : |
2039 else : |
1903 self._update_workspace( stream, stream.trunk(), verbose = False ) |
2040 trunk = stream.trunk() |
|
2041 self._update_workspace( stream, trunk, verbose = False ) |
1904 self._execute_action( stream, *arg, **kwarg ) |
2042 self._execute_action( stream, *arg, **kwarg ) |
1905 |
2043 |
1906 |
2044 |
1907 |
2045 |
1908 def print_version( self, *arg, **kwarg ) : |
2046 def print_version( self, *arg, **kwarg ) : |
1931 """ |
2069 """ |
1932 self._print( "Currently open branches:" ) |
2070 self._print( "Currently open branches:" ) |
1933 curr_workspace = self.curr_workspace |
2071 curr_workspace = self.curr_workspace |
1934 stream_names = ["master", "develop", "feature", "release", "hotfix", "support",] |
2072 stream_names = ["master", "develop", "feature", "release", "hotfix", "support",] |
1935 all_branches = self._branches() |
2073 all_branches = self._branches() |
|
2074 name_branches = {} |
|
2075 for branch in all_branches : |
|
2076 name_branches.setdefault( branch.fullname(), [] ).append( branch ) |
|
2077 name_branches = sorted( name_branches.items() ) |
1936 for sn in stream_names : |
2078 for sn in stream_names : |
1937 stream = STREAM[sn] |
2079 stream = STREAM[sn] |
1938 trunk = stream.trunk() |
2080 trunk = stream.trunk() |
1939 open_branches_in_stream = [] |
2081 open_branches_in_stream = [] |
1940 remaining_branches = [] |
2082 for name, heads in name_branches : |
1941 for e in all_branches : |
2083 e = heads[0] |
1942 if (e in stream) : |
2084 if (e in stream) : |
1943 open_branches_in_stream.append( e ) |
2085 open_branches_in_stream.append( e ) |
1944 else : |
|
1945 remaining_branches.append( e ) |
|
1946 all_branches = remaining_branches |
|
1947 if (trunk is None and not open_branches_in_stream) : |
2086 if (trunk is None and not open_branches_in_stream) : |
1948 continue |
2087 continue |
1949 self._print( "%-9s: " % stream, newline = False ) |
2088 self._print( "%-9s: " % stream, newline = False ) |
1950 if (trunk) : |
2089 if (trunk) : |
1951 marker = "#" if (self._is_shelved( trunk )) else "" |
2090 marker = "#" if (self._is_shelved( trunk )) else "" |
1958 for e in open_branches_in_stream : |
2097 for e in open_branches_in_stream : |
1959 marker = "#" if (self._is_shelved( e )) else "" |
2098 marker = "#" if (self._is_shelved( e )) else "" |
1960 marker += "*" if (e == curr_workspace ) else "" |
2099 marker += "*" if (e == curr_workspace ) else "" |
1961 self.ui.write( "%s%s " % (e, marker,) ) |
2100 self.ui.write( "%s%s " % (e, marker,) ) |
1962 self.ui.write( "\n" ) |
2101 self.ui.write( "\n" ) |
1963 |
2102 if (sum( [len( heads ) - 1 for name, heads in name_branches] )) : |
|
2103 self._print( "\n", newline = False ) |
|
2104 self._print( "Multihead branches:" ) |
|
2105 for name, heads in name_branches : |
|
2106 if (len( heads ) > 1) : |
|
2107 self._print( " %s" % name ) |
|
2108 for head in heads : |
|
2109 self._print( " %s" % head.rev_node() ) |
|
2110 |
1964 |
2111 |
1965 |
2112 |
1966 def init( self, *arg, **kwarg ) : |
2113 def init( self, *arg, **kwarg ) : |
1967 """ |
2114 """ |
1968 Initialize flow. |
2115 Initialize flow. |
2035 if (branch == workspace) : |
2182 if (branch == workspace) : |
2036 self._warn( " " + branch.fullname() + " (active)" ) |
2183 self._warn( " " + branch.fullname() + " (active)" ) |
2037 else : |
2184 else : |
2038 self._warn( " %s" % branch.fullname() ) |
2185 self._warn( " %s" % branch.fullname() ) |
2039 print |
2186 print |
2040 |
2187 |
2041 # 'status' method returns a 7-member tuple: |
2188 # 'status' method returns a 7-member tuple: |
2042 # 0 modified, 1 added, 2 removed, 3 deleted, 4 unknown(?), 5 ignored, and 6 clean |
2189 # 0 modified, 1 added, 2 removed, 3 deleted, 4 unknown(?), 5 ignored, and 6 clean |
2043 orig_repo_status = self.repo.status()[:4] |
2190 orig_repo_status = self.repo.status()[:4] |
2044 for e in orig_repo_status : |
2191 for e in orig_repo_status : |
2045 try : |
2192 try : |
2098 "release = %s" % release_stream, |
2245 "release = %s" % release_stream, |
2099 "hotfix = %s" % hotfix_stream, |
2246 "hotfix = %s" % hotfix_stream, |
2100 "support = %s" % support_stream,] |
2247 "support = %s" % support_stream,] |
2101 def write_config() : |
2248 def write_config() : |
2102 # Writes the configuration in the current branch. |
2249 # Writes the configuration in the current branch. |
2103 with open( config_fname, "w" ) as fh : |
2250 if (not commands.dryrun()) : |
2104 print >> fh, "\n".join( cfg_contents ) |
2251 with open( config_fname, "w" ) as fh : |
|
2252 print >> fh, "\n".join( cfg_contents ) |
2105 repo_status = self.repo.status( unknown = True ) |
2253 repo_status = self.repo.status( unknown = True ) |
2106 if (CONFIG_BASENAME in repo_status[0]) : |
2254 if (CONFIG_BASENAME in repo_status[0]) : |
2107 self._commit( config_fname, message = "flow initialization: Modified configuration file." ) |
2255 self._commit( config_fname, message = "flow initialization: Modified configuration file." ) |
2108 elif (CONFIG_BASENAME in repo_status[4]) : |
2256 elif (CONFIG_BASENAME in repo_status[4]) : |
2109 self._add ( config_fname ) |
2257 self._add ( config_fname ) |
2110 self._commit( config_fname, message = "flow initialization: Added configuration file." ) |
2258 self._commit( config_fname, message = "flow initialization: Added configuration file." ) |
2111 |
2259 |
2112 write_config() |
2260 write_config() |
2113 |
2261 |
|
2262 master_trunk, is_open = self._find_branch( master_stream ) |
|
2263 if (master_trunk and not is_open) : |
|
2264 self._warn( "Branch \"%s\" is currently closed." % master_stream ) |
|
2265 self._warn( "Will reopen and use it as <master> trunk." ) |
|
2266 branches.append( master_trunk ) |
|
2267 |
|
2268 develop_trunk, is_open = self._find_branch( develop_stream ) |
|
2269 if (develop_trunk and not is_open) : |
|
2270 self._warn( "Branch \"%s\" is currently closed." % develop_stream ) |
|
2271 self._warn( "Will reopen and use it as <develop> trunk." ) |
|
2272 branches.append( develop_trunk ) |
|
2273 |
2114 # Writes the configuration in all the other branches. |
2274 # Writes the configuration in all the other branches. |
2115 self.autoshelve = True |
2275 self.autoshelve = True |
2116 self._shelve() |
2276 self._shelve() |
2117 |
2277 |
2118 if (len( branches ) > 1) : |
2278 if (len( branches ) > 1) : |
2121 self._update( branch ) |
2281 self._update( branch ) |
2122 write_config() |
2282 write_config() |
2123 self._update( workspace ) |
2283 self._update( workspace ) |
2124 |
2284 |
2125 # Creates 'master' and 'develop' streams if they don't yet exist. |
2285 # Creates 'master' and 'develop' streams if they don't yet exist. |
2126 if (not self._find_branch( master_stream )) : |
2286 if (master_trunk is None) : |
2127 self._create_branch( master_stream, "flow initialization: Created <master> trunk: %s." % master_stream ) |
2287 self._create_branch( master_stream, "flow initialization: Created <master> trunk: %s." % master_stream ) |
2128 if (not self._find_branch( develop_stream )) : |
2288 if (develop_trunk is None) : |
2129 self._create_branch( develop_stream, "flow initialization: Created <develop> trunk: %s." % develop_stream ) |
2289 self._create_branch( develop_stream, "flow initialization: Created <develop> trunk: %s." % develop_stream ) |
|
2290 |
2130 self._update( workspace ) |
2291 self._update( workspace ) |
2131 self._unshelve() |
2292 self._unshelve() |
2132 |
2293 |
2133 |
2294 |
2134 |
2295 |
2135 def upgrade( self, *arg, **kwarg ) : |
2296 def upgrade( self, *arg, **kwarg ) : |
2136 """ |
2297 """ |
2137 Upgrade older version to the latest version. |
2298 Upgrade older version to the latest version. |
2138 """ |
2299 """ |
2165 - pull Pull from the remote repository and update workspace branch. |
2326 - pull Pull from the remote repository and update workspace branch. |
2166 - list List all open branches in the stream. |
2327 - list List all open branches in the stream. |
2167 - log Show revision history of branch. |
2328 - log Show revision history of branch. |
2168 - promote Merge workspace to other branches. (not closing any branches.) |
2329 - promote Merge workspace to other branches. (not closing any branches.) |
2169 - rebase Rebase workspace branch to its parent branch. |
2330 - rebase Rebase workspace branch to its parent branch. |
|
2331 - rename Rename workspace branch to a new basename. |
2170 - abort Abort branch. Close branch without merging. |
2332 - abort Abort branch. Close branch without merging. |
2171 |
2333 |
2172 If no action is specified by user, the action will default to `list`. If a |
2334 If no action is specified by user, the action will default to `list`. If a |
2173 branch name (instead of action) is given after the stream name, Flow will |
2335 branch name (instead of action) is given after the stream name, Flow will |
2174 switch the current workspace to the branch. |
2336 switch the current workspace to the branch. |
2218 |
2380 |
2219 try : |
2381 try : |
2220 # Constructs a `Stream' objects. |
2382 # Constructs a `Stream' objects. |
2221 # This will also check the validity of the part of user's input that is supposed to specify a stream. |
2383 # This will also check the validity of the part of user's input that is supposed to specify a stream. |
2222 if (isinstance( stream, str )) : |
2384 if (isinstance( stream, str )) : |
2223 source = None |
2385 stream = Stream.gen( ui, repo, stream, check = True ) |
2224 tokens = stream.split( ':' ) |
2386 |
2225 n = len( tokens ) |
|
2226 if (n == 2) : |
|
2227 source, stream = tokens[0], tokens[1] |
|
2228 if (n > 2 or not stream) : |
|
2229 raise AbortFlow( "Invalid stream syntax: '%s'" % stream ) |
|
2230 try : |
|
2231 stream = Stream.gen( ui, repo, stream, check = True ) |
|
2232 except AbnormalStream, e : |
|
2233 stream = e.stream() |
|
2234 if (source) : |
|
2235 try : |
|
2236 source = Stream.gen( ui, repo, source, check = True ) |
|
2237 except AbnormalStream, e : |
|
2238 source = e.stream() |
|
2239 stream = copy.copy( stream ) |
|
2240 stream._source = source |
|
2241 for i, e in enumerate( stream.destin() ) : |
|
2242 if (source in e ) : |
|
2243 stream._destin[i] = source |
|
2244 break |
|
2245 else : |
|
2246 stream._destin = [source] |
|
2247 |
|
2248 # Checks the options for all commands and actions. |
2387 # Checks the options for all commands and actions. |
2249 kwarg = _getopt( ui, cmd, kwarg ) |
2388 kwarg = _getopt( ui, cmd, kwarg ) |
2250 stamp = kwarg.pop( "stamp", None ) |
2389 stamp = kwarg.pop( "stamp", None ) |
2251 if (stamp) : |
2390 if (stamp) : |
2252 def stamp_commit_message( opts ) : |
2391 def stamp_commit_message( opts ) : |
2262 except AbortFlow, e : |
2401 except AbortFlow, e : |
2263 errmsg = e.error_message() |
2402 errmsg = e.error_message() |
2264 _error( ui, *errmsg ) |
2403 _error( ui, *errmsg ) |
2265 if (getattr( e, "note", None )) : |
2404 if (getattr( e, "note", None )) : |
2266 _note( ui, *((e.note,) if (isinstance( e.note, str )) else e.note), via_quiet = True ) |
2405 _note( ui, *((e.note,) if (isinstance( e.note, str )) else e.note), via_quiet = True ) |
|
2406 elif (errmsg[0].startswith( "Stream not found" )) : |
|
2407 misspelling = difflib.get_close_matches( stream, ["init", "upgrade", "unshelve", "help", "version",], 3, 0.7 ) |
|
2408 note = ("Did you mean the command: %s?" % " or ".join( misspelling )) if (misspelling ) else None |
|
2409 note = ("Did you mean the command: init?") if ("install" == stream) else note |
|
2410 note = ("Did you mean the command: upgrade?") if ("update" == stream) else note |
|
2411 if (note) : |
|
2412 _note( ui, note, via_quiet = True ) |
|
2413 |
2267 if (ui.tracebackflag) : |
2414 if (ui.tracebackflag) : |
2268 if (hasattr( e, "traceback" )) : |
2415 if (hasattr( e, "traceback" )) : |
2269 ei = e.traceback |
2416 ei = e.traceback |
2270 sys.excepthook( ei[0], ei[1], ei[2] ) |
2417 sys.excepthook( ei[0], ei[1], ei[2] ) |
2271 print |
2418 print |
2610 option: |
2761 option: |
2611 -p --stamp TEXT Append TEXT to all commit messages. |
2762 -p --stamp TEXT Append TEXT to all commit messages. |
2612 |
2763 |
2613 The workspace branch must be in <stream>. If the destination revision is not |
2764 The workspace branch must be in <stream>. If the destination revision is not |
2614 specified, it will default to the source branch of the workspace branch. |
2765 specified, it will default to the source branch of the workspace branch. |
|
2766 """, |
|
2767 |
|
2768 "@version" : """ |
|
2769 Show version of the flow extension. |
|
2770 |
|
2771 syntax: |
|
2772 {{{hg flow version}}} |
|
2773 """, |
|
2774 |
|
2775 "@rename" : """ |
|
2776 Rename the workspace branch to a new basename. Under the hood, this action |
|
2777 will create a new branch with the new basename and copy/graft the commits in |
|
2778 the workspace branch to the new branch, and then erase the workspace branch. |
|
2779 All the user, date, and commit-message information will be copied to the |
|
2780 new branch. |
|
2781 |
|
2782 N.B.: The workspace branch should be a simple linear branch. This means: |
|
2783 (1) It has not merged to other branches; |
|
2784 (2) It has no subbranches; |
|
2785 (3) No other branches has merged to this branch. |
|
2786 |
|
2787 syntax: |
|
2788 {{{hg flow <stream> rename [-t <basename>]}}} |
|
2789 |
|
2790 option: |
|
2791 -t --to NAME Rename the basename of the workspace branch to NAME. |
|
2792 |
|
2793 The workspace branch must be in <stream>. |
2615 """, |
2794 """, |
2616 |
2795 |
2617 "@version" : """ |
2796 "@version" : """ |
2618 Show version of the flow extension. |
2797 Show version of the flow extension. |
2619 |
2798 |
2801 _print( self.ui, """Supported topics are the following: |
2980 _print( self.ui, """Supported topics are the following: |
2802 @all - Show detailed help for all supported topics. |
2981 @all - Show detailed help for all supported topics. |
2803 @<stream> - Show help about a particular stream, e.g., @feature, @master. |
2982 @<stream> - Show help about a particular stream, e.g., @feature, @master. |
2804 @<action> - Show help about an action, e.g., @finish, @log. |
2983 @<action> - Show help about an action, e.g., @finish, @log. |
2805 @<command> - Show help about a command, e.g., @help, @unshelve. |
2984 @<command> - Show help about a command, e.g., @help, @unshelve. |
2806 @<option> - Show help about an option, e.g., @-F, @--history. |
2985 @terms - Show explanations of terminologies used in hgflow. |
2807 @examples - Show a few command examples. |
2986 @examples - Show a few command examples. |
2808 @deprecated - Show a list of deprecated features. |
2987 @deprecated - Show a list of deprecated features. |
2809 """ ) |
2988 """ ) |
2810 |
2989 |
2811 |
2990 |
2818 "list" : ("closed",), |
2997 "list" : ("closed",), |
2819 "log" : ("file", "date", "user", "keyword", "patch", "git", "limit", "graph", "closed", "onstream",), |
2998 "log" : ("file", "date", "user", "keyword", "patch", "git", "limit", "graph", "closed", "onstream",), |
2820 "abort" : ("erase", "message", "stamp", "onstream",), |
2999 "abort" : ("erase", "message", "stamp", "onstream",), |
2821 "promote" : ("rev", "message", "stamp", "tag", "date", "user", "onstream",), |
3000 "promote" : ("rev", "message", "stamp", "tag", "date", "user", "onstream",), |
2822 "rebase" : ("dest", "onstream", "stamp",), |
3001 "rebase" : ("dest", "onstream", "stamp",), |
|
3002 "rename" : ("to",), |
2823 } |
3003 } |
2824 |
3004 |
2825 OPT_CONFLICT = { |
3005 OPT_CONFLICT = { |
2826 "dest" : ("-d", '', ), # (short-form-of-option, default-value,) |
3006 "dest" : ("-d", '', ), # (short-form-of-option, default-value,) |
2827 "date" : ("-d", '', ), |
3007 "date" : ("-d", '', ), |
2828 "default" : ("-d", False,), |
3008 "default" : ("-d", False,), |
2829 "closed" : ("-c", False,), |
3009 "closed" : ("-c", False,), |
2830 "commit" : ("-c", False,), |
3010 "commit" : ("-c", False,), |
2831 "stamp" : ("-p", '' ), |
3011 "stamp" : ("-p", '' ), |
2832 "patch" : ("-p", False ), |
3012 "patch" : ("-p", False ), |
|
3013 "tag" : ("-t", '' ), |
|
3014 "to" : ("-t", '' ), |
2833 } |
3015 } |
2834 |
3016 |
2835 def _getopt( ui, key, opt ) : |
3017 def _getopt( ui, key, opt ) : |
2836 """ |
3018 """ |
2837 Return user-specified options. |
3019 Return user-specified options. |
2882 bad_opt = [e.replace( "_", "-" ) for e in bad_opt] |
3064 bad_opt = [e.replace( "_", "-" ) for e in bad_opt] |
2883 if (key is None) : |
3065 if (key is None) : |
2884 raise AbortFlow( "Unrecognized option%s for `hg flow`: %s." % |
3066 raise AbortFlow( "Unrecognized option%s for `hg flow`: %s." % |
2885 ("" if (len( bad_opt ) == 1) else "s", "--" + (", --".join( bad_opt )),), |
3067 ("" if (len( bad_opt ) == 1) else "s", "--" + (", --".join( bad_opt )),), |
2886 note = "`hg flow` should take no options." ) |
3068 note = "`hg flow` should take no options." ) |
2887 else : |
3069 elif (key in Flow.ACTION_NAME) : |
2888 raise AbortFlow( "Unrecognized option%s for `%s`: %s." % |
3070 raise AbortFlow( "Unrecognized option%s for `%s`: %s." % |
2889 ("" if (len( bad_opt ) == 1) else "s", key, "--" + (", --".join( bad_opt )),), |
3071 ("" if (len( bad_opt ) == 1) else "s", key, "--" + (", --".join( bad_opt )),), |
2890 note = "Execute `hg flow help @%s` to see available options for `%s`." % (key, key,) ) |
3072 note = "Execute `hg flow help @%s` to see available options for `%s`." % (key, key,) ) |
|
3073 else : |
|
3074 raise AbortFlow( "Unrecognized option%s: %s." % |
|
3075 ("" if (len( bad_opt ) == 1) else "s", "--" + (", --".join( bad_opt )),) ) |
2891 |
3076 |
2892 return ret |
3077 return ret |
2893 |
3078 |
2894 |
3079 |
2895 |
3080 |
2919 ("r", "rev", '', _("Revision to start a new branch from. [start]"), _('REV'), ), |
3104 ("r", "rev", '', _("Revision to start a new branch from. [start]"), _('REV'), ), |
2920 ("r", "rev", '', _("Revision to promote to other branches. [promote]"), _('REV'), ), |
3105 ("r", "rev", '', _("Revision to promote to other branches. [promote]"), _('REV'), ), |
2921 ("s", "onstream", False, _("Act on stream. [finish, rebase, log, abort]"), ), |
3106 ("s", "onstream", False, _("Act on stream. [finish, rebase, log, abort]"), ), |
2922 ("t", "tag", '', _("Tag the merging changeset with NAME. [promote]"), _('NAME'),), |
3107 ("t", "tag", '', _("Tag the merging changeset with NAME. [promote]"), _('NAME'),), |
2923 ("t", "tag", '', _("Tag the <master> trunk with NAME after merging. [finish]"), _('NAME'),), |
3108 ("t", "tag", '', _("Tag the <master> trunk with NAME after merging. [finish]"), _('NAME'),), |
|
3109 ("t", "to", '', _("Rename the branch to NAME. [rename]"), _('NAME'),), |
2924 ("u", "user", '', _("Use specified user as committer. [init, upgrade, start, finish," |
3110 ("u", "user", '', _("Use specified user as committer. [init, upgrade, start, finish," |
2925 " promote]"), _('USER'),), |
3111 " promote]"), _('USER'),), |
2926 ("u", "user", '', _("Show revisions committed by specified user. [log]"), _('USER'),), |
3112 ("u", "user", '', _("Show revisions committed by specified user. [log]"), _('USER'),), |
2927 ], |
3113 ], |
2928 "hg flow {<stream> [<action> [<arg>]] | <command>} [<option>...]", |
3114 "hg flow {<stream> [<action> [<arg>]] | <command>} [<option>...]", |