Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like GitRepository often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use GitRepository, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
11 | class GitRepository implements IGit |
||
12 | { |
||
13 | /** |
||
14 | * @var string |
||
15 | */ |
||
16 | protected $repository; |
||
17 | |||
18 | /** |
||
19 | * @var string|NULL @internal |
||
20 | */ |
||
21 | protected $cwd; |
||
22 | |||
23 | |||
24 | /** |
||
25 | * @param string |
||
26 | * @throws GitException |
||
27 | */ |
||
28 | public function __construct($repository) |
||
40 | |||
41 | |||
42 | /** |
||
43 | * @return string |
||
44 | */ |
||
45 | public function getRepositoryPath() |
||
49 | |||
50 | |||
51 | /** |
||
52 | * Creates a tag. |
||
53 | * `git tag <name>` |
||
54 | * |
||
55 | * @param string |
||
56 | * @param array|NULL |
||
57 | * @throws GitException |
||
58 | * @return self |
||
59 | */ |
||
60 | public function createTag($name, $options = null) |
||
66 | |||
67 | |||
68 | /** |
||
69 | * Removes tag. |
||
70 | * `git tag -d <name>` |
||
71 | * |
||
72 | * @param string |
||
73 | * @throws GitException |
||
74 | * @return self |
||
75 | */ |
||
76 | public function removeTag($name) |
||
86 | |||
87 | |||
88 | /** |
||
89 | * Renames tag. |
||
90 | * `git tag <new> <old>` |
||
91 | * `git tag -d <old>` |
||
92 | * |
||
93 | * @param string |
||
94 | * @param string |
||
95 | * @throws GitException |
||
96 | * @return self |
||
97 | */ |
||
98 | public function renameTag($oldName, $newName) |
||
108 | |||
109 | |||
110 | /** |
||
111 | * Returns list of tags in repo. |
||
112 | * |
||
113 | * @return string[]|NULL NULL => no tags |
||
114 | * @throws GitException |
||
115 | */ |
||
116 | public function getTags() |
||
120 | |||
121 | |||
122 | /** |
||
123 | * Merges branches. |
||
124 | * `git merge <options> <name>` |
||
125 | * |
||
126 | * @param string |
||
127 | * @param array|NULL |
||
128 | * @throws GitException |
||
129 | * @return self |
||
130 | */ |
||
131 | public function merge($branch, $options = null) |
||
137 | |||
138 | |||
139 | /** |
||
140 | * Creates new branch. |
||
141 | * `git branch <name>` |
||
142 | * (optionaly) `git checkout <name>` |
||
143 | * |
||
144 | * @param string |
||
145 | * @param bool |
||
146 | * @throws GitException |
||
147 | * @return self |
||
148 | */ |
||
149 | public function createBranch($name, $checkout = false) |
||
162 | |||
163 | |||
164 | /** |
||
165 | * Removes branch. |
||
166 | * `git branch -d <name>` |
||
167 | * |
||
168 | * @param string |
||
169 | * @throws GitException |
||
170 | * @return self |
||
171 | */ |
||
172 | public function removeBranch($name) |
||
182 | |||
183 | |||
184 | /** |
||
185 | * Gets name of current branch |
||
186 | * `git branch` + magic |
||
187 | * |
||
188 | * @return string |
||
189 | * @throws GitException |
||
190 | */ |
||
191 | public function getCurrentBranchName() |
||
213 | |||
214 | |||
215 | /** |
||
216 | * Returns list of all (local & remote) branches in repo. |
||
217 | * |
||
218 | * @return string[]|NULL NULL => no branches |
||
219 | * @throws GitException |
||
220 | */ |
||
221 | public function getBranches() |
||
229 | |||
230 | |||
231 | /** |
||
232 | * Returns list of remote branches in repo. |
||
233 | * |
||
234 | * @return string[]|NULL NULL => no branches |
||
235 | * @throws GitException |
||
236 | */ |
||
237 | public function getRemoteBranches() |
||
245 | |||
246 | |||
247 | /** |
||
248 | * Returns list of local branches in repo. |
||
249 | * |
||
250 | * @return string[]|NULL NULL => no branches |
||
251 | * @throws GitException |
||
252 | */ |
||
253 | public function getLocalBranches() |
||
261 | |||
262 | |||
263 | /** |
||
264 | * Checkout branch. |
||
265 | * `git checkout <branch>` |
||
266 | * |
||
267 | * @param string |
||
268 | * @throws GitException |
||
269 | * @return self |
||
270 | */ |
||
271 | public function checkout($name) |
||
277 | |||
278 | |||
279 | /** |
||
280 | * Removes file(s). |
||
281 | * `git rm <file>` |
||
282 | * |
||
283 | * @param string|string[] |
||
284 | * @throws GitException |
||
285 | * @return self |
||
286 | */ |
||
287 | public function removeFile($file) |
||
302 | |||
303 | |||
304 | /** |
||
305 | * Adds file(s). |
||
306 | * `git add <file>` |
||
307 | * |
||
308 | * @param string|string[] |
||
309 | * @throws GitException |
||
310 | * @return self |
||
311 | */ |
||
312 | public function addFile($file) |
||
335 | |||
336 | |||
337 | /** |
||
338 | * Adds all created, modified & removed files. |
||
339 | * `git add --all` |
||
340 | * |
||
341 | * @throws GitException |
||
342 | * @return self |
||
343 | */ |
||
344 | public function addAllChanges() |
||
350 | |||
351 | |||
352 | /** |
||
353 | * Renames file(s). |
||
354 | * `git mv <file>` |
||
355 | * |
||
356 | * @param string|string[] from: array('from' => 'to', ...) || (from, to) |
||
357 | * @param string|NULL |
||
358 | * @throws GitException |
||
359 | * @return self |
||
360 | */ |
||
361 | public function renameFile($file, $to = null) |
||
379 | |||
380 | |||
381 | /** |
||
382 | * Commits changes |
||
383 | * `git commit <params> -m <message>` |
||
384 | * |
||
385 | * @param string |
||
386 | * @param string[] param => value |
||
387 | * @throws GitException |
||
388 | * @return self |
||
389 | */ |
||
390 | public function commit($message, $params = null) |
||
404 | |||
405 | |||
406 | /** |
||
407 | * Returns last commit ID on current branch |
||
408 | * `git log --pretty=format:"%H" -n 1` |
||
409 | * |
||
410 | * @return string|NULL |
||
411 | * @throws GitException |
||
412 | */ |
||
413 | public function getLastCommitId() |
||
423 | |||
424 | |||
425 | /** |
||
426 | * Exists changes? |
||
427 | * `git status` + magic |
||
428 | * |
||
429 | * @return bool |
||
430 | * @throws GitException |
||
431 | */ |
||
432 | public function hasChanges() |
||
442 | |||
443 | |||
444 | /** |
||
445 | * @deprecated |
||
446 | * @throws GitException |
||
447 | */ |
||
448 | public function isChanges() |
||
452 | |||
453 | |||
454 | /** |
||
455 | * Pull changes from a remote |
||
456 | * |
||
457 | * @param string|NULL |
||
458 | * @param array|NULL |
||
459 | * @return self |
||
460 | * @throws GitException |
||
461 | */ |
||
462 | View Code Duplication | public function pull($remote = null, array $params = null) |
|
472 | |||
473 | |||
474 | /** |
||
475 | * Push changes to a remote |
||
476 | * |
||
477 | * @param string|NULL |
||
478 | * @param array|NULL |
||
479 | * @return self |
||
480 | * @throws GitException |
||
481 | */ |
||
482 | View Code Duplication | public function push($remote = null, array $params = null) |
|
492 | |||
493 | |||
494 | /** |
||
495 | * Run fetch command to get latest branches |
||
496 | * |
||
497 | * @param string|NULL |
||
498 | * @param array|NULL |
||
499 | * @return self |
||
500 | * @throws GitException |
||
501 | */ |
||
502 | View Code Duplication | public function fetch($remote = null, array $params = null) |
|
512 | |||
513 | |||
514 | /** |
||
515 | * Adds new remote repository |
||
516 | * |
||
517 | * @param string |
||
518 | * @param string |
||
519 | * @param array|NULL |
||
520 | * @return self |
||
521 | * @throws GitException |
||
522 | */ |
||
523 | public function addRemote($name, $url, array $params = null) |
||
529 | |||
530 | |||
531 | /** |
||
532 | * Renames remote repository |
||
533 | * |
||
534 | * @param string |
||
535 | * @param string |
||
536 | * @return self |
||
537 | * @throws GitException |
||
538 | */ |
||
539 | public function renameRemote($oldName, $newName) |
||
545 | |||
546 | |||
547 | /** |
||
548 | * Removes remote repository |
||
549 | * |
||
550 | * @param string |
||
551 | * @return self |
||
552 | * @throws GitException |
||
553 | */ |
||
554 | public function removeRemote($name) |
||
560 | |||
561 | |||
562 | /** |
||
563 | * Changes remote repository URL |
||
564 | * |
||
565 | * @param string |
||
566 | * @param string |
||
567 | * @param array|NULL |
||
568 | * @return self |
||
569 | * @throws GitException |
||
570 | */ |
||
571 | public function setRemoteUrl($name, $url, array $params = null) |
||
577 | |||
578 | |||
579 | /** |
||
580 | * @param string|string[] |
||
581 | * @return string[] returns output |
||
582 | * @throws GitException |
||
583 | */ |
||
584 | public function execute($cmd) |
||
603 | |||
604 | |||
605 | /** |
||
606 | * @return self |
||
607 | */ |
||
608 | protected function begin() |
||
618 | |||
619 | |||
620 | /** |
||
621 | * @return self |
||
622 | */ |
||
623 | protected function end() |
||
632 | |||
633 | |||
634 | /** |
||
635 | * @param string |
||
636 | * @param callback|NULL |
||
637 | * @return string[]|NULL |
||
638 | * @throws GitException |
||
639 | */ |
||
640 | protected function extractFromCommand($cmd, $filter = null) |
||
677 | |||
678 | |||
679 | /** |
||
680 | * Runs command. |
||
681 | * |
||
682 | * @param string|array |
||
683 | * @return self |
||
684 | * @throws GitException |
||
685 | */ |
||
686 | protected function run($cmd/*, $options = NULL*/) |
||
698 | |||
699 | |||
700 | protected static function processCommand(array $args) |
||
727 | |||
728 | |||
729 | /** |
||
730 | * Init repo in directory |
||
731 | * |
||
732 | * @param string |
||
733 | * @param array|NULL |
||
734 | * @return self |
||
735 | * @throws GitException |
||
736 | */ |
||
737 | public static function init($directory, array $params = null) |
||
769 | |||
770 | |||
771 | /** |
||
772 | * Clones GIT repository from $url into $directory |
||
773 | * |
||
774 | * @param string |
||
775 | * @param string|NULL |
||
776 | * @param array|NULL |
||
777 | * @return self |
||
778 | * @throws GitException |
||
779 | */ |
||
780 | public static function cloneRepository($url, $directory = null, array $params = null) |
||
855 | |||
856 | |||
857 | /** |
||
858 | * @param string |
||
859 | * @param array|NULL |
||
860 | * @return bool |
||
861 | */ |
||
862 | public static function isRemoteUrlReadable($url, array $refs = null) |
||
888 | |||
889 | |||
890 | /** |
||
891 | * @param string /path/to/repo.git | host.xz:foo/.git | ... |
||
892 | * @return string repo | foo | ... |
||
893 | */ |
||
894 | public static function extractRepositoryNameFromUrl($url) |
||
911 | |||
912 | |||
913 | /** |
||
914 | * Is path absolute? |
||
915 | * Method from Nette\Utils\FileSystem |
||
916 | * |
||
917 | * @link https://github.com/nette/nette/blob/master/Nette/Utils/FileSystem.php |
||
918 | * @return bool |
||
919 | */ |
||
920 | public static function isAbsolute($path) |
||
924 | |||
925 | |||
926 | /** |
||
927 | * Returns commit message from specific commit |
||
928 | * `git log -1 --format={%s|%B} )--pretty=format:'%H' -n 1` |
||
929 | * |
||
930 | * @param string commit ID |
||
931 | * @param bool use %s instead of %B if TRUE |
||
932 | * @return string |
||
933 | * @throws GitException |
||
934 | */ |
||
935 | public function getCommitMessage($commit, $oneline = false) |
||
942 | |||
943 | |||
944 | /** |
||
945 | * Returns array of commit metadata from specific commit |
||
946 | * `git show --raw <sha1>` |
||
947 | * |
||
948 | * @param string commit ID |
||
949 | * @return array |
||
950 | * @throws GitException |
||
951 | */ |
||
952 | public function getCommitData($commit) |
||
990 | |||
991 | } |
||
992 |