4 * Records a push to a hosted repository. This allows us to store metadata
5 * about who pushed commits, when, and from where. We can also record the
6 * history of branches and tags, which is not normally persisted outside of
9 * This log is written by commit hooks installed into hosted repositories.
10 * See @{class:DiffusionCommitHookEngine}.
12 final class PhabricatorRepositoryPushLog
13 extends PhabricatorRepositoryDAO
14 implements PhabricatorPolicyInterface
{
16 const REFTYPE_BRANCH
= 'branch';
17 const REFTYPE_TAG
= 'tag';
18 const REFTYPE_BOOKMARK
= 'bookmark';
19 const REFTYPE_COMMIT
= 'commit';
20 const REFTYPE_REF
= 'ref';
21 const REFTYPE_MAINTENANCE
= 'maintenance';
23 const CHANGEFLAG_ADD
= 1;
24 const CHANGEFLAG_DELETE
= 2;
25 const CHANGEFLAG_APPEND
= 4;
26 const CHANGEFLAG_REWRITE
= 8;
27 const CHANGEFLAG_DANGEROUS
= 16;
28 const CHANGEFLAG_ENORMOUS
= 32;
29 const CHANGEFLAG_OVERSIZED
= 64;
30 const CHANGEFLAG_TOUCHES
= 128;
31 const CHANGEFLAG_MAINTENANCE
= 256;
33 const REJECT_ACCEPT
= 0;
34 const REJECT_DANGEROUS
= 1;
35 const REJECT_HERALD
= 2;
36 const REJECT_EXTERNAL
= 3;
37 const REJECT_BROKEN
= 4;
38 const REJECT_ENORMOUS
= 5;
39 const REJECT_OVERSIZED
= 6;
40 const REJECT_TOUCHES
= 7;
42 protected $repositoryPHID;
44 protected $pusherPHID;
45 protected $pushEventPHID;
46 protected $devicePHID;
48 protected $refNameHash;
49 protected $refNameRaw;
50 protected $refNameEncoding;
54 protected $changeFlags;
56 private $dangerousChangeDescription = self
::ATTACHABLE
;
57 private $pushEvent = self
::ATTACHABLE
;
58 private $repository = self
::ATTACHABLE
;
60 public static function initializeNewLog(PhabricatorUser
$viewer) {
61 return id(new PhabricatorRepositoryPushLog())
62 ->setPusherPHID($viewer->getPHID());
65 public static function getFlagDisplayNames() {
67 self
::CHANGEFLAG_ADD
=> pht('Create'),
68 self
::CHANGEFLAG_DELETE
=> pht('Delete'),
69 self
::CHANGEFLAG_APPEND
=> pht('Append'),
70 self
::CHANGEFLAG_REWRITE
=> pht('Rewrite'),
71 self
::CHANGEFLAG_DANGEROUS
=> pht('Dangerous'),
72 self
::CHANGEFLAG_ENORMOUS
=> pht('Enormous'),
73 self
::CHANGEFLAG_OVERSIZED
=> pht('Oversized'),
74 self
::CHANGEFLAG_TOUCHES
=> pht('Touches Too Many Paths'),
75 self
::CHANGEFLAG_MAINTENANCE
=> pht('Maintenance'),
79 public static function getRejectCodeDisplayNames() {
81 self
::REJECT_ACCEPT
=> pht('Accepted'),
82 self
::REJECT_DANGEROUS
=> pht('Rejected: Dangerous'),
83 self
::REJECT_HERALD
=> pht('Rejected: Herald'),
84 self
::REJECT_EXTERNAL
=> pht('Rejected: External Hook'),
85 self
::REJECT_BROKEN
=> pht('Rejected: Broken'),
86 self
::REJECT_ENORMOUS
=> pht('Rejected: Enormous'),
87 self
::REJECT_OVERSIZED
=> pht('Rejected: Oversized File'),
88 self
::REJECT_TOUCHES
=> pht('Rejected: Touches Too Many Paths'),
92 public static function getHeraldChangeFlagConditionOptions() {
94 self
::CHANGEFLAG_ADD
=>
95 pht('change creates ref'),
96 self
::CHANGEFLAG_DELETE
=>
97 pht('change deletes ref'),
98 self
::CHANGEFLAG_REWRITE
=>
99 pht('change rewrites ref'),
100 self
::CHANGEFLAG_DANGEROUS
=>
101 pht('dangerous change'),
105 protected function getConfiguration() {
107 self
::CONFIG_AUX_PHID
=> true,
108 self
::CONFIG_TIMESTAMPS
=> false,
109 self
::CONFIG_BINARY
=> array(
110 'refNameRaw' => true,
112 self
::CONFIG_COLUMN_SCHEMA
=> array(
113 'refType' => 'text12',
114 'refNameHash' => 'bytes12?',
115 'refNameRaw' => 'bytes?',
116 'refNameEncoding' => 'text16?',
117 'refOld' => 'text40?',
118 'refNew' => 'text40',
119 'mergeBase' => 'text40?',
120 'changeFlags' => 'uint32',
121 'devicePHID' => 'phid?',
123 self
::CONFIG_KEY_SCHEMA
=> array(
124 'key_repository' => array(
125 'columns' => array('repositoryPHID'),
128 'columns' => array('repositoryPHID', 'refNew'),
131 'columns' => array('repositoryPHID', 'refNameHash'),
133 'key_event' => array(
134 'columns' => array('pushEventPHID'),
136 'key_pusher' => array(
137 'columns' => array('pusherPHID'),
139 'key_epoch' => array(
140 'columns' => array('epoch'),
143 ) + parent
::getConfiguration();
146 public function generatePHID() {
147 return PhabricatorPHID
::generateNewPHID(
148 PhabricatorRepositoryPushLogPHIDType
::TYPECONST
);
151 public function attachPushEvent(PhabricatorRepositoryPushEvent
$push_event) {
152 $this->pushEvent
= $push_event;
156 public function getPushEvent() {
157 return $this->assertAttached($this->pushEvent
);
160 public function getRefName() {
161 if ($this->getRefNameRaw() === null) {
164 return $this->getUTF8StringFromStorage(
165 $this->getRefNameRaw(),
166 $this->getRefNameEncoding());
169 public function setRefName($ref_raw) {
170 $this->setRefNameRaw($ref_raw);
171 $this->setRefNameHash(PhabricatorHash
::digestForIndex($ref_raw));
172 $this->setRefNameEncoding($this->detectEncodingForStorage($ref_raw));
177 public function getRefOldShort() {
178 if ($this->getRepository()->isSVN()) {
179 return $this->getRefOld();
181 if ($this->getRefOld() === null) {
184 return substr($this->getRefOld(), 0, 12);
187 public function getRefNewShort() {
188 if ($this->getRepository()->isSVN()) {
189 return $this->getRefNew();
191 return substr($this->getRefNew(), 0, 12);
194 public function hasChangeFlags($mask) {
195 return ($this->changeFlags
& $mask);
198 public function attachDangerousChangeDescription($description) {
199 $this->dangerousChangeDescription
= $description;
203 public function getDangerousChangeDescription() {
204 return $this->assertAttached($this->dangerousChangeDescription
);
207 public function attachRepository(PhabricatorRepository
$repository) {
208 // NOTE: Some gymnastics around this because of object construction order
209 // in the hook engine. Particularly, web build the logs before we build
211 $this->repository
= $repository;
215 public function getRepository() {
216 if ($this->repository
== self
::ATTACHABLE
) {
217 return $this->getPushEvent()->getRepository();
219 return $this->assertAttached($this->repository
);
223 /* -( PhabricatorPolicyInterface )----------------------------------------- */
226 public function getCapabilities() {
228 PhabricatorPolicyCapability
::CAN_VIEW
,
232 public function getPolicy($capability) {
233 // NOTE: We're passing through the repository rather than the push event
234 // mostly because we need to do policy checks in Herald before we create
235 // the event. The two approaches are equivalent in practice.
236 return $this->getRepository()->getPolicy($capability);
239 public function hasAutomaticCapability($capability, PhabricatorUser
$viewer) {
240 return $this->getRepository()->hasAutomaticCapability($capability, $viewer);
243 public function describeAutomaticCapability($capability) {
245 "A repository's push logs are visible to users who can see the ".