Get attribute value of XML
I believe the reason you're getting these results is because the namespace for the xml is not the same as in the select statements.
I've altered your example to briefly explain each case. 

declare @xmlMessage xml

set @xmlMessage ='&lt;pmsg:Messages xmlns:pmsg="pmsg"&gt;
    &lt;pmsg:Message Info="blah"/&gt;
    &lt;pmsg:Message Info="blah"/&gt;
&lt;/pmsg:Messages&gt;'

;WITH XMLNAMESPACES('pmsg' as pmsg)
SELECT msgs.msg.value('@Info', 'nvarchar(max)') FROM @xmlMessage.nodes('pmsg:Messages/pmsg:Message') msgs(msg); 
-- return twos rows 'blah'


The nodes() expression provides a set of two pmsg:Message nodes, and the select reads the value of @Info from each row.

;WITH XMLNAMESPACES('pmsg' as pmsg)
SELECT @xmlMessage.value('(/pmsg:Messages/pmsg:Message/@Info)[1]', 'nvarchar(max)'); 
--Returns a single rowset of one row containing 'blah'


This will only return a single row 'blah' because the [1] in the query restricts to the first item found.  Actually in this form the [1] is required. SQL will throw exeception of you dont include it becuase the expression must evaluate to a single value.

;WITH XMLNAMESPACES('pmsg' as pmsg)
SELECT t.x.value('(/pmsg:Messages/pmsg:Message/@Info)[1]', 'nvarchar(max)') as INFO FROM @xmlMessage.nodes('/*') t(x);
--Returns a rowset of one row containing 'blah'


Like the previous example, the xpath query evaluates to a single item and the nodes() expresson provides a row set on the top level node: