Function p4get-changedescription {param([Parameter(Mandatory=$true)][int]$changelist,[string]$arrayKey,[int]$depotFileLimit) $verbosePrefs = $verbosePreference #store the original console verbosity preference $verbosePreference = "continue" #set it to continue for messaging. $m = "NoteProperty" #default member type to use for the emitted object if (!$arrayKey) { $arrayKey = "depotFile" #this is the key that will be used to create our multi dimensional array. It needs to be one of the always present multi-object returned names. We are using depotFile because every change in p4 affects a depot file. } if (!$depotFileLimit) { $depotFileLimit = 500 #depot file limit prevents the return of a humongous changelist. if you're querying more than 500 files, it's probably best to use a differnt tool than this function. it's overridable though } $depotFileLimitExitCounter = 0 #set the depot file limit flag 0. it is required for exiting the parse loop at the right point. write-verbose "retrieving changelist description from server" $rawChange = p4get "describe $changelist" #grab the change that pulls in multiple chunks and a broken description. if (!$rawChange) #if the change doesn't return content { write-warning "No changelist found for ID $changelist" break } $allProperties = $rawChange | %{$_ | gm} | ?{$_.MemberType -eq "NoteProperty"} | %{$_.Name} #collect every property in the object. This list is dynamic and can be different from one record set to the next. ##create a number of array indexes in order to extract the objects in the correct order. $changeDescription = new-object psobject #create an object type foreach ($property in ($allProperties | ?{$_ -notmatch "[0-9]"})) #all these properties occur once. these properties returned from ztag output ending without numeral are always unique. iterate through all properties in object. { foreach ($chunk in $rawChange) #the change object is returned broken. Normally, it shows up in two chunks but maybe there could be more. iterate through the chunks. { $value = $chunk | gm | ?{$_.name -eq $property} | %{$_.name} | sort-object | %{$chunk.$_} #return the values from all of the chunks that do not have a regex [0-9] match. if ($value) #if there's a value on the return do something with it. { if ($property -eq "desc") #if the description property, extract the text with a different parser {#need to do some goofy shit to parse description correctly. otherwise it returns just the first line. I am not a regex genius so I do this crap because I suck. add-member -inputobject $changeDescription -membertype $m -name $property -value (p4get-change $changelist).description #push it into the object with a nice blob of text. } else { add-member -inputobject $changeDescription -membertype $m -name $property -value $value #otherwise, just parse the remaining properties and push into the object. } } } } if ($changeDescription.time) #fix the datetime on the object. { $changeDescription.time = convertUnixTime $changeDescription.time#return the object. } $objectItems = ($allProperties | ?{$_ -match "[0-9]"} | sort-object) #index all the properties returned from ztag format that are multi-object returns. #That is; items that should be in an array and are indexed as such, but just in a big blob right now. for example depotFile0,depotFile1,etc. This glob of properties is dynamic and needs to be addressed as such. $multiPropertyNames = $objectItems -replace '[0-9]' | Sort-Object -Unique #return all the unique matching names from the multi-object return array. We will use this to uniquely identify our arrays of arrays. add-member -inputobject $changeDescription -membertype $m -name $arrayKey -value (New-Object System.Collections.ArrayList) #start by creating a .net array object with the key name. $i=0 #start a pointer counter. This will be used to ensure we do not create any more objects than $depotFileLimit foreach ($item in $objectItems)#now iterate through all variants of the arraykey items in the multi object blob. p4 always returns a square valueset for any multi-object items. That is, if there are 7 "depotFile" objects, there will be 7 "filesize","rev","type", etc objects as well. It might be a good idea to make a checker that validates the array, so the return is known as corrupted or not. #maybe something like: if ((foreach (array in arrays){(i = array.count + i)/arrays.count)).notequals(1)){ write-error "array corrupt!"} { if ($item -match $arraykey) #if the item from the multi-object blob string matches the name of the array key { if ($i -ge $depotFileLimit) { break } $depotFileProperties = new-object psobject #generate a new empty psobject $changeDescription.$arrayKey.Add($depotFileProperties) | out-null #add the empty psobject to the array held in the master object. $i++ } } #now insert properties into the array foreach ($propertyName in $multiPropertyNames) #this is each unique multi-item property name we're going to search for { $i=0 #start a pointer counter. This assumption can be made because p4 returns a square valueset foreach ($item in $objectItems) #iterate over each item in the property-value array glob { if ($item -match $propertyName) #so, each time we iterate through the property value array glob, if we match a unique multi item property name, insert as a new property into the arraylist specific for this change description. { if ($i -ge $depotFileLimit) #keep track of the depotfilelimit. This prevents processing of gigantic changes that might just eat up memory, or otherwise be unnecessary. { $depotFileLimitExitCounter++ #increment the exit counter on each loop. We want to loop through and process the last depotFile before exiting. if ($depotFileLimitExitCounter -ge $multiPropertyNames.count) #if the counter matches the number of properties we're including in the object, THEN exit. { add-member -inputobject $changeDescription -membertype $m -name incompleteFileList -value $true #slide in a little telltale to notate that there were more files on the list but not included. $changeDescription #return the object. write-warning "DepotFileLimit[$depotFileLimit] Reached. Exiting early." #throw a warning return #then exit from function. } break #if we haven't reached the depotfilelimitexitcounter, then just break from the foreach loop without adding any more items to the list. This allows the $i pointer to increment until the limitexitcounter is reached, which ensures a whole object is processed without nulls in it. } $changeDescription.$arrayKey[$i] | add-member -membertype $m -name $propertyName -value ($rawChange.$item[1]) #directly address each array element with our array pointer ($i) and insert the second [1] value from $rawchange.$item because the first element [0] is a null. $i++ #increment the pointer if ($i -gt ($depotFileLimit / 2)) { write-progress -id 25 -activity "Processing $depotFileLimit file objects from the change description." -status ($propertyname + ": " + $i +" of " + $depotFileLimit + ".") } } } } $changeDescription $verbosePreference = $verbosePrefs #return verbosity preferences to the configured. }