@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at upstream/main 116 lines 3.5 kB view raw
1<?php 2 3final class DiffusionGitBranch extends Phobject { 4 5 const DEFAULT_GIT_REMOTE = 'origin'; 6 7 /** 8 * Parse the output of 'git branch -r --verbose --no-abbrev' or similar into 9 * a map. For instance: 10 * 11 * array( 12 * 'origin/master' => '99a9c082f9a1b68c7264e26b9e552484a5ae5f25', 13 * ); 14 * 15 * If you specify $only_this_remote, branches will be filtered to only those 16 * on the given remote, **and the remote name will be stripped**. For example: 17 * 18 * array( 19 * 'master' => '99a9c082f9a1b68c7264e26b9e552484a5ae5f25', 20 * ); 21 * 22 * @param string $stdout stdout of git branch command. 23 * @param string $only_this_remote (optional) Filter branches to those on a 24 * specific remote. 25 * @return map Map of 'branch' or 'remote/branch' to hash at HEAD. 26 */ 27 public static function parseRemoteBranchOutput( 28 $stdout, 29 $only_this_remote = null) { 30 $map = array(); 31 32 $lines = array_filter(explode("\n", $stdout)); 33 foreach ($lines as $line) { 34 $matches = null; 35 if (preg_match('/^ (\S+)\s+-> (\S+)$/', $line, $matches)) { 36 // This is a line like: 37 // 38 // origin/HEAD -> origin/master 39 // 40 // ...which we don't currently do anything interesting with, although 41 // in theory we could use it to automatically choose the default 42 // branch. 43 continue; 44 } 45 if (!preg_match('/^ *(\S+)\s+([a-z0-9]{40})/', $line, $matches)) { 46 throw new Exception( 47 pht( 48 'Failed to parse %s!', 49 $line)); 50 } 51 52 $remote_branch = $matches[1]; 53 $branch_head = $matches[2]; 54 55 if (strpos($remote_branch, 'HEAD') !== false) { 56 // let's assume that no one will call their remote or branch HEAD 57 continue; 58 } 59 60 if ($only_this_remote) { 61 $matches = null; 62 if (!preg_match('#^([^/]+)/(.*)$#', $remote_branch, $matches)) { 63 throw new Exception( 64 pht( 65 "Failed to parse remote branch '%s'!", 66 $remote_branch)); 67 } 68 $remote_name = $matches[1]; 69 $branch_name = $matches[2]; 70 if ($remote_name != $only_this_remote) { 71 continue; 72 } 73 $map[$branch_name] = $branch_head; 74 } else { 75 $map[$remote_branch] = $branch_head; 76 } 77 } 78 79 return $map; 80 } 81 82 /** 83 * Parse the output of 'git branch --verbose --no-abbrev' or similar into a 84 * map. As parseRemoteBranchOutput but no `-r`. Used for bare repositories. 85 * 86 * @return map Map of branch name (string or int) and its hash (string). 87 */ 88 public static function parseLocalBranchOutput($stdout) { 89 $map = array(); 90 91 $lines = array_filter(explode("\n", $stdout)); 92 $regex = '/^[* ]*(\(no branch\)|\S+)\s+([a-z0-9]{40})/'; 93 foreach ($lines as $line) { 94 $matches = null; 95 if (!preg_match($regex, $line, $matches)) { 96 throw new Exception( 97 pht( 98 'Failed to parse %s!', 99 $line)); 100 } 101 102 $branch = $matches[1]; 103 $branch_head = $matches[2]; 104 if ($branch == '(no branch)') { 105 continue; 106 } 107 // Note: If the $branch name string is numeric containing only decimal 108 // ints and does not start with 0, PHP will cast it from string to int: 109 // https://www.php.net/manual/en/language.types.array.php#language.types.array.syntax 110 $map[$branch] = $branch_head; 111 } 112 113 return $map; 114 } 115 116}