Issue 256: fix parsing branch list when branch name(s) contains spaces
Since branch names may contain space, we must use custom template
to get a a robust machine readable output.
However, this is supported since Mercurial 3.5 (including) so for
older Mercurials, we still use old code as a courtesy to users who
may have old Mercurials. Las lomng as branch has no spaces, it should
just work.
--- a/mercurial/HGCommand.st Tue Jan 08 09:35:11 2019 +0000
+++ b/mercurial/HGCommand.st Thu Mar 07 12:22:12 2019 +0000
@@ -27,6 +27,7 @@
HGVersionIsGreaterOrEqualThan_2_4
HGVersionIsGreaterOrEqualThan_2_5
HGVersionIsGreaterOrEqualThan_3_3
+ HGVersionIsGreaterOrEqualThan_3_5
HGVersionIsGreaterOrEqualThan_4_1
HGVersionIsGreaterOrEqualThan_4_8'
poolDictionaries:'HGDebugFlags'
@@ -294,8 +295,8 @@
HGCommandString := command notNil ifTrue:[ command asString ] ifFalse:[ nil ].
HGExecutable := HGExecutableArguments := HGVersion := nil.
HGVersionIsGreaterOrEqualThan_2_4 := HGVersionIsGreaterOrEqualThan_2_5 :=
- HGVersionIsGreaterOrEqualThan_3_3 := HGVersionIsGreaterOrEqualThan_4_1 :=
- HGVersionIsGreaterOrEqualThan_4_8 := nil.
+ HGVersionIsGreaterOrEqualThan_3_3 := HGVersionIsGreaterOrEqualThan_3_5 :=
+ HGVersionIsGreaterOrEqualThan_4_1 := HGVersionIsGreaterOrEqualThan_4_8 := nil.
"
@@ -306,7 +307,7 @@
"
"Created: / 19-11-2012 / 21:49:02 / Jan Vrany <jan.vrany@fit.cvut.cz>"
- "Modified: / 10-01-2019 / 14:43:02 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+ "Modified: / 09-03-2019 / 08:13:56 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
hgCommandValidate: command
@@ -457,6 +458,16 @@
"Modified: / 25-03-2016 / 17:23:13 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
+hgVersionIsGreaterOrEqualThan_3_5
+ "/ 3.5 released 2015-07-31
+ HGVersionIsGreaterOrEqualThan_3_5 isNil ifTrue:[
+ HGVersionIsGreaterOrEqualThan_3_5 := self hgVersionIsGreaterOrEqualThan:#( 3 5 ).
+ ].
+ ^ HGVersionIsGreaterOrEqualThan_3_5
+
+ "Created: / 09-03-2019 / 08:13:28 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
hgVersionIsGreaterOrEqualThan_4_1
HGVersionIsGreaterOrEqualThan_4_1 isNil ifTrue:[
HGVersionIsGreaterOrEqualThan_4_1 := self hgVersionIsGreaterOrEqualThan:#( 4 1 ).
@@ -1438,7 +1449,15 @@
stream nextPut:'--closed'
].
+ self class hgVersionIsGreaterOrEqualThan_3_5 ifTrue:[
+ stream
+ nextPut:'--template';
+ nextPut:HGCommandParser templateBranches.
+ ]
+
"Created: / 27-11-2012 / 19:54:08 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+ "Modified: / 09-03-2019 / 08:16:17 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+ "Modified (format): / 12-03-2019 / 11:58:40 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
parseError:stream
@@ -1451,9 +1470,14 @@
"Parses output of 'hg' command, i.e. commit, log, update, checkout,
etc."
- ^ (self parserOn:stream) parseCommandBranches
+ self class hgVersionIsGreaterOrEqualThan_3_5 ifTrue:[
+ ^ (self parserOn:stream) parseCommandBranches
+ ] ifFalse:[
+ ^ (self parserOn:stream) parseCommandBranchesOld
+ ]
"Created: / 27-11-2012 / 19:55:00 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+ "Modified: / 09-03-2019 / 08:18:55 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !
!HGCommand::branches methodsFor:'queries'!
--- a/mercurial/HGCommandParser.st Tue Jan 08 09:35:11 2019 +0000
+++ b/mercurial/HGCommandParser.st Thu Mar 07 12:22:12 2019 +0000
@@ -76,6 +76,12 @@
!HGCommandParser class methodsFor:'templates'!
+templateBranches
+ ^ '{branch}\0{rev}:{node}\0{ifeq(closed,''True'',''C'',ifeq(active,''False'',''I'',''A''))}\n'
+
+ "Created: / 07-03-2019 / 11:02:46 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
templateHeads
^'{rev}:{node}\n'
@@ -229,63 +235,127 @@
parseBranches
"Parse output of 'hg branches' command. Return collection
of orphaned HGBranch"
-
- | branches branch |
+
+ | branches branch |
branches := OrderedCollection new.
- stream atEnd ifFalse:[
- branch := self parseBranchesEntryAllowForInvalidBranchheadsMessage: true.
- branch notNil ifTrue:[
- branches add: branch.
+ stream atEnd ifFalse:[
+ branch := self
+ parseBranchesEntryAllowForInvalidBranchheadsMessageEntryAllowForInvalidBranchheadsMessage:true.
+ branch notNil ifTrue:[
+ branches add:branch.
].
].
[ stream atEnd ] whileFalse:[
- branch := self parseBranchesEntryAllowForInvalidBranchheadsMessage: false.
- branches add: branch.
+ branch := self
+ parseBranchesEntryAllowForInvalidBranchheadsMessageEntryAllowForInvalidBranchheadsMessage:false.
+ branches add:branch.
+ ].
+ ^ branches
+
+ "Created: / 09-03-2019 / 08:25:55 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+parseBranchesEntryAllowForInvalidBranchheadsMessageEntryAllowForInvalidBranchheadsMessage:allowForInvalidBrancheadsMessage
+ | name branch state |
+
+ name := self parseNameUpToNullOrNewLine.
+ allowForInvalidBrancheadsMessage ifTrue:[
+ stream peek == Character null ifFalse:[
+ | message |
+
+ message := name , ' ' , stream nextLine.
+ self notify:message.
+ name := self parseNameUpToNullOrNewLine.
+ ].
].
- ^branches
+ branch := HGBranch new.
+ branch setName:name.
+ self expect:Character null.
+ self parseNodeId.
+ self expect:Character null.
+ state := stream next.
+ state == $A ifTrue:[
+ branch setActive:true
+ ] ifFalse:[
+ state == $C ifTrue:[
+ branch setClosed:true
+ ] ifFalse:[
+ state == $I ifTrue:[
+ branch setActive:false
+ ] ifFalse:[
+ self error:'Invalid branch state: ' , state
+ ]
+ ]
+ ].
+ self expectLineEnd.
+ ^ branch
+
+ "Created: / 09-03-2019 / 08:26:01 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+parseBranchesOld
+ "Parse output of 'hg branches' command. Return collection
+ of orphaned HGBranch"
+
+ | branches branch |
+
+ branches := OrderedCollection new.
+ stream atEnd ifFalse:[
+ branch := self parseBranchesOldEntryAllowForInvalidBranchheadsMessage:true.
+ branch notNil ifTrue:[
+ branches add:branch.
+ ].
+ ].
+ [ stream atEnd ] whileFalse:[
+ branch := self parseBranchesOldEntryAllowForInvalidBranchheadsMessage:false.
+ branches add:branch.
+ ].
+ ^ branches
"Created: / 27-11-2012 / 20:20:56 / Jan Vrany <jan.vrany@fit.cvut.cz>"
"Modified: / 08-10-2014 / 20:56:29 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
-parseBranchesEntryAllowForInvalidBranchheadsMessage: allowForInvalidBrancheadsMessage
- | name branch |
+parseBranchesOldEntryAllowForInvalidBranchheadsMessage:allowForInvalidBrancheadsMessage
+ | name branch |
name := self parseName.
stream skipSeparators.
- allowForInvalidBrancheadsMessage ifTrue:[
- stream peek isDigit ifFalse:[
+ allowForInvalidBrancheadsMessage ifTrue:[
+ stream peek isDigit ifFalse:[
| message |
- message := name , ' ', stream nextLine.
- self notify: message.
+ message := name , ' ' , stream nextLine.
+ self notify:message.
name := self parseName.
- stream skipSeparators.
+ stream skipSeparators.
].
- ].
+ ].
branch := HGBranch new.
- branch setName: name.
-
+ branch setName:name.
self parseNodeId.
stream peek == Character space ifTrue:[
stream next.
- stream peek == $( ifFalse:[self error:'''('' expected but ''' , stream peek , ''' found'].
+ stream peek == $( ifFalse:[
+ self error:'''('' expected but ''' , stream peek , ''' found'
+ ].
stream next.
stream peek == $i ifTrue:[
self expect:'inactive)'.
- branch setActive: false.
+ branch setActive:false.
] ifFalse:[
stream peek == $c ifTrue:[
self expect:'closed)'.
- branch setClosed: true.
+ branch setClosed:true.
] ifFalse:[
- self error:'Unexpected branch attribute (only ''closed'' and ''inactive'' supported)'''
+ self
+ error:'Unexpected branch attribute (only ''closed'' and ''inactive'' supported)'''
]
].
].
self expectLineEnd.
- ^branch
+ ^ branch
"Created: / 08-10-2014 / 20:54:46 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
@@ -509,6 +579,7 @@
].
"Created: / 27-11-2012 / 20:21:27 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+ "Modified: / 07-03-2019 / 11:17:52 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
parseNameList
@@ -527,6 +598,33 @@
"Created: / 27-11-2012 / 20:30:02 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
+parseNameUpTo: terminator1 or: terminator2
+ ^String streamContents:[:out|
+ | c |
+ [ c := stream peek. c == terminator1 or:[ c == terminator2] ] whileFalse:[
+ out nextPut:stream next
+ ]
+ ].
+
+ "Created: / 07-03-2019 / 12:16:26 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+parseNameUpToNullOrNewLine
+ ^ self parseNameUpTo: Character null or: Character cr.
+
+ "Created: / 07-03-2019 / 12:16:53 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+parseNameZ
+ ^String streamContents:[:out|
+ [ stream peek == Character null ] whileFalse:[
+ out nextPut:stream next
+ ]
+ ].
+
+ "Created: / 07-03-2019 / 11:18:24 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
parseNodeId
"Parses node id from stream and returns it. Support both,
short and full node ids"
@@ -737,8 +835,17 @@
parseCommandBranches
"Parse output of 'hg branches' command. Return collection
of orphaned HGBranch"
+
+ ^ self parseBranches
- ^self parseBranches
+ "Created: / 09-03-2019 / 08:19:26 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+parseCommandBranchesOld
+ "Parse output of 'hg branches' command. Return collection
+ of orphaned HGBranch"
+
+ ^ self parseBranchesOld
"Created: / 27-11-2012 / 19:16:12 / Jan Vrany <jan.vrany@fit.cvut.cz>"
"Modified: / 27-11-2012 / 20:21:15 / Jan Vrany <jan.vrany@fit.cvut.cz>"
--- a/mercurial/HGCommandParserTests.st Tue Jan 08 09:35:11 2019 +0000
+++ b/mercurial/HGCommandParserTests.st Thu Mar 07 12:22:12 2019 +0000
@@ -110,10 +110,10 @@
| branches |
- branches := (HGCommandParser on: 'default 5:f22945219f9be25a1fe436d81afece07b89330be
-branch1 4:5bd21fb5eea8a7cb4adf45bccfea76cda11df84a (inactive)
-branch2 3:32d32dee719fb422a69cfa6f7f8c1d8e299de2df (closed)
-') parseCommandBranches.
+ branches := (HGCommandParser on: (('default|5:f22945219f9be25a1fe436d81afece07b89330be|A
+branch1|4:5bd21fb5eea8a7cb4adf45bccfea76cda11df84a|I
+branch2|3:32d32dee719fb422a69cfa6f7f8c1d8e299de2df|C
+' copyReplaceAll: $| with: Character null))) parseCommandBranches.
self assert: branches size == 3.
@@ -130,16 +130,17 @@
self assert: branches third isClosed.
"Created: / 27-11-2012 / 19:00:30 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+ "Modified: / 07-03-2019 / 12:12:20 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
test_cmd_branches_02
| branches |
- branches := (HGCommandParser on: 'invalid branchheads cache (visible): tip differs
-default 5694:756610fa329d48cd8b225524016713485aefbb95
-jv 5684:2c32b6c5d3543cd0381f9b346d62bfeabb95e6c6
-') parseCommandBranches.
+ branches := (HGCommandParser on: (('invalid branchheads cache (visible): tip differs
+default|5694:756610fa329d48cd8b225524016713485aefbb95|A
+jv|5684:2c32b6c5d3543cd0381f9b346d62bfeabb95e6c6|A
+')copyReplaceAll: $| with: Character null)) parseCommandBranches.
self assert: branches size == 2.
@@ -152,6 +153,29 @@
self assert: branches second isClosed not.
"Created: / 08-10-2014 / 20:39:33 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+ "Modified: / 07-03-2019 / 12:08:26 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+test_cmd_branches_issue256b
+
+ | branches |
+
+ branches := (HGCommandParser on: (('default|6:6b8bef68bf9c6b8bef686b8bef68bf9c6b8bef68|A
+test branches issue256b |5:9db0e56bb86e6b8bef686b8bef68bf9c6b8bef68|I
+') copyReplaceAll: $| with: Character null)) parseCommandBranches.
+
+ self assert: branches size == 2.
+
+ self assert: branches first name = 'default'.
+ self assert: branches first isActive.
+ self assert: branches first isClosed not.
+
+ self assert: branches second name = 'test branches issue256b '.
+ self assert: branches second isActive not.
+ self assert: branches second isClosed not.
+
+ "Created: / 07-03-2019 / 10:21:57 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+ "Modified: / 07-03-2019 / 12:07:31 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
test_cmd_err_branches_01
--- a/mercurial/HGTests.st Tue Jan 08 09:35:11 2019 +0000
+++ b/mercurial/HGTests.st Thu Mar 07 12:22:12 2019 +0000
@@ -479,6 +479,45 @@
self assert:(repo branches contains: [:b|b name = 'test branches issue256a']).
"Created: / 07-01-2019 / 22:42:24 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+test_branches_issue256b
+ "
+ Change branch with spaces & commit. Check whether commited changeset
+ has the branch set.
+ "
+
+ | repo wc |
+
+ "/ Older Mercurials do not support templates so we cannot reliably support
+ "/ branches with spaces in there. Hence we skip the test.
+ self skipIf: HGCommand hgVersionIsGreaterOrEqualThan_3_5 not description: 'Not supported on hg < 3.5'.
+
+ repo := self repositoryNamed:'test_repo_01'.
+ wc := repo workingCopy.
+ self assert: repo branches size == 1.
+ wc branch: 'test branches issue256b'.
+
+ "Modify some file"
+ (wc / 'f1.txt') writingFileDo:[:s | s nextPutAll:'modified...' ].
+ wc commit: testSelector , ' - commit 01'.
+
+ self assert: wc changeset branches size == 1.
+ self assert: wc changeset branches anElement name = 'test branches issue256b'.
+ self assert: repo branches size == 2.
+ self assert:(repo branches contains: [:b|b name = 'default']).
+ self assert:(repo branches contains: [:b|b name = 'test branches issue256b']).
+
+ wc branch: 'default'.
+ "Modify some file"
+ (wc / 'f1.txt') writingFileDo:[:s | s nextPutAll:'modified_default...' ].
+ wc commit: testSelector , ' - commit default branch 01'.
+
+ self assert: wc changeset branches size == 1.
+ self assert: wc changeset branches anElement name = 'default'.
+
+ "Created: / 06-03-2019 / 10:32:50 / svestkap"
+ "Modified (format): / 12-03-2019 / 11:58:14 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !
!HGTests methodsFor:'tests - changesets'!