1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 package org.eclipse.jgit.lib;
49
50 import java.io.BufferedOutputStream;
51 import java.io.File;
52 import java.io.FileNotFoundException;
53 import java.io.FileOutputStream;
54 import java.io.IOException;
55 import java.net.URISyntaxException;
56 import java.text.MessageFormat;
57 import java.util.Collection;
58 import java.util.Collections;
59 import java.util.HashMap;
60 import java.util.HashSet;
61 import java.util.LinkedList;
62 import java.util.List;
63 import java.util.Map;
64 import java.util.Set;
65 import java.util.concurrent.atomic.AtomicInteger;
66
67 import org.eclipse.jgit.dircache.DirCache;
68 import org.eclipse.jgit.errors.AmbiguousObjectException;
69 import org.eclipse.jgit.errors.CorruptObjectException;
70 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
71 import org.eclipse.jgit.errors.MissingObjectException;
72 import org.eclipse.jgit.errors.NoWorkTreeException;
73 import org.eclipse.jgit.errors.RevisionSyntaxException;
74 import org.eclipse.jgit.events.IndexChangedEvent;
75 import org.eclipse.jgit.events.IndexChangedListener;
76 import org.eclipse.jgit.events.ListenerList;
77 import org.eclipse.jgit.events.RepositoryEvent;
78 import org.eclipse.jgit.internal.JGitText;
79 import org.eclipse.jgit.revwalk.RevBlob;
80 import org.eclipse.jgit.revwalk.RevCommit;
81 import org.eclipse.jgit.revwalk.RevObject;
82 import org.eclipse.jgit.revwalk.RevTree;
83 import org.eclipse.jgit.revwalk.RevWalk;
84 import org.eclipse.jgit.transport.RefSpec;
85 import org.eclipse.jgit.transport.RemoteConfig;
86 import org.eclipse.jgit.treewalk.TreeWalk;
87 import org.eclipse.jgit.util.FS;
88 import org.eclipse.jgit.util.FileUtils;
89 import org.eclipse.jgit.util.IO;
90 import org.eclipse.jgit.util.RawParseUtils;
91 import org.eclipse.jgit.util.SystemReader;
92 import org.eclipse.jgit.util.io.SafeBufferedOutputStream;
93
94
95
96
97
98
99
100
101
102 public abstract class Repository implements AutoCloseable {
103 private static final ListenerList globalListeners = new ListenerList();
104
105
106 public static ListenerList getGlobalListenerList() {
107 return globalListeners;
108 }
109
110 private final AtomicInteger useCnt = new AtomicInteger(1);
111
112
113 private final File gitDir;
114
115
116 private final FS fs;
117
118 private final ListenerList myListeners = new ListenerList();
119
120
121 private final File workTree;
122
123
124 private final File indexFile;
125
126
127
128
129
130
131
132 protected Repository(final BaseRepositoryBuilder options) {
133 gitDir = options.getGitDir();
134 fs = options.getFS();
135 workTree = options.getWorkTree();
136 indexFile = options.getIndexFile();
137 }
138
139
140 public ListenerList getListenerList() {
141 return myListeners;
142 }
143
144
145
146
147
148
149
150
151
152
153 public void fireEvent(RepositoryEvent<?> event) {
154 event.setRepository(this);
155 myListeners.dispatch(event);
156 globalListeners.dispatch(event);
157 }
158
159
160
161
162
163
164
165
166
167
168 public void create() throws IOException {
169 create(false);
170 }
171
172
173
174
175
176
177
178
179
180
181
182 public abstract void create(boolean bare) throws IOException;
183
184
185 public File getDirectory() {
186 return gitDir;
187 }
188
189
190
191
192 public abstract ObjectDatabase getObjectDatabase();
193
194
195 public ObjectInserter newObjectInserter() {
196 return getObjectDatabase().newInserter();
197 }
198
199
200 public ObjectReader newObjectReader() {
201 return getObjectDatabase().newReader();
202 }
203
204
205 public abstract RefDatabase getRefDatabase();
206
207
208
209
210 public abstract StoredConfig getConfig();
211
212
213
214
215 public FS getFS() {
216 return fs;
217 }
218
219
220
221
222
223
224 public boolean hasObject(AnyObjectId objectId) {
225 try {
226 return getObjectDatabase().has(objectId);
227 } catch (IOException e) {
228
229 return false;
230 }
231 }
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247 public ObjectLoader open(final AnyObjectId objectId)
248 throws MissingObjectException, IOException {
249 return getObjectDatabase().open(objectId);
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274 public ObjectLoader open(AnyObjectId objectId, int typeHint)
275 throws MissingObjectException, IncorrectObjectTypeException,
276 IOException {
277 return getObjectDatabase().open(objectId, typeHint);
278 }
279
280
281
282
283
284
285
286
287
288
289
290
291
292 public RefUpdate updateRef(final String ref) throws IOException {
293 return updateRef(ref, false);
294 }
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310 public RefUpdate updateRef(final String ref, final boolean detach) throws IOException {
311 return getRefDatabase().newUpdate(ref, detach);
312 }
313
314
315
316
317
318
319
320
321
322
323
324
325
326 public RefRename renameRef(final String fromRef, final String toRef) throws IOException {
327 return getRefDatabase().newRename(fromRef, toRef);
328 }
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379 public ObjectId resolve(final String revstr)
380 throws AmbiguousObjectException, IncorrectObjectTypeException,
381 RevisionSyntaxException, IOException {
382 try (RevWalk rw = new RevWalk(this)) {
383 Object resolved = resolve(rw, revstr);
384 if (resolved instanceof String) {
385 final Ref ref = getRef((String)resolved);
386 return ref != null ? ref.getLeaf().getObjectId() : null;
387 } else {
388 return (ObjectId) resolved;
389 }
390 }
391 }
392
393
394
395
396
397
398
399
400
401
402
403
404 public String simplify(final String revstr)
405 throws AmbiguousObjectException, IOException {
406 try (RevWalk rw = new RevWalk(this)) {
407 Object resolved = resolve(rw, revstr);
408 if (resolved != null)
409 if (resolved instanceof String)
410 return (String) resolved;
411 else
412 return ((AnyObjectId) resolved).getName();
413 return null;
414 }
415 }
416
417 private Object resolve(final RevWalk rw, final String revstr)
418 throws IOException {
419 char[] revChars = revstr.toCharArray();
420 RevObject rev = null;
421 String name = null;
422 int done = 0;
423 for (int i = 0; i < revChars.length; ++i) {
424 switch (revChars[i]) {
425 case '^':
426 if (rev == null) {
427 if (name == null)
428 if (done == 0)
429 name = new String(revChars, done, i);
430 else {
431 done = i + 1;
432 break;
433 }
434 rev = parseSimple(rw, name);
435 name = null;
436 if (rev == null)
437 return null;
438 }
439 if (i + 1 < revChars.length) {
440 switch (revChars[i + 1]) {
441 case '0':
442 case '1':
443 case '2':
444 case '3':
445 case '4':
446 case '5':
447 case '6':
448 case '7':
449 case '8':
450 case '9':
451 int j;
452 rev = rw.parseCommit(rev);
453 for (j = i + 1; j < revChars.length; ++j) {
454 if (!Character.isDigit(revChars[j]))
455 break;
456 }
457 String parentnum = new String(revChars, i + 1, j - i
458 - 1);
459 int pnum;
460 try {
461 pnum = Integer.parseInt(parentnum);
462 } catch (NumberFormatException e) {
463 throw new RevisionSyntaxException(
464 JGitText.get().invalidCommitParentNumber,
465 revstr);
466 }
467 if (pnum != 0) {
468 RevCommit commit = (RevCommit) rev;
469 if (pnum > commit.getParentCount())
470 rev = null;
471 else
472 rev = commit.getParent(pnum - 1);
473 }
474 i = j - 1;
475 done = j;
476 break;
477 case '{':
478 int k;
479 String item = null;
480 for (k = i + 2; k < revChars.length; ++k) {
481 if (revChars[k] == '}') {
482 item = new String(revChars, i + 2, k - i - 2);
483 break;
484 }
485 }
486 i = k;
487 if (item != null)
488 if (item.equals("tree")) {
489 rev = rw.parseTree(rev);
490 } else if (item.equals("commit")) {
491 rev = rw.parseCommit(rev);
492 } else if (item.equals("blob")) {
493 rev = rw.peel(rev);
494 if (!(rev instanceof RevBlob))
495 throw new IncorrectObjectTypeException(rev,
496 Constants.TYPE_BLOB);
497 } else if (item.equals("")) {
498 rev = rw.peel(rev);
499 } else
500 throw new RevisionSyntaxException(revstr);
501 else
502 throw new RevisionSyntaxException(revstr);
503 done = k;
504 break;
505 default:
506 rev = rw.peel(rev);
507 if (rev instanceof RevCommit) {
508 RevCommit commit = ((RevCommit) rev);
509 if (commit.getParentCount() == 0)
510 rev = null;
511 else
512 rev = commit.getParent(0);
513 } else
514 throw new IncorrectObjectTypeException(rev,
515 Constants.TYPE_COMMIT);
516 }
517 } else {
518 rev = rw.peel(rev);
519 if (rev instanceof RevCommit) {
520 RevCommit commit = ((RevCommit) rev);
521 if (commit.getParentCount() == 0)
522 rev = null;
523 else
524 rev = commit.getParent(0);
525 } else
526 throw new IncorrectObjectTypeException(rev,
527 Constants.TYPE_COMMIT);
528 }
529 done = i + 1;
530 break;
531 case '~':
532 if (rev == null) {
533 if (name == null)
534 if (done == 0)
535 name = new String(revChars, done, i);
536 else {
537 done = i + 1;
538 break;
539 }
540 rev = parseSimple(rw, name);
541 name = null;
542 if (rev == null)
543 return null;
544 }
545 rev = rw.peel(rev);
546 if (!(rev instanceof RevCommit))
547 throw new IncorrectObjectTypeException(rev,
548 Constants.TYPE_COMMIT);
549 int l;
550 for (l = i + 1; l < revChars.length; ++l) {
551 if (!Character.isDigit(revChars[l]))
552 break;
553 }
554 int dist;
555 if (l - i > 1) {
556 String distnum = new String(revChars, i + 1, l - i - 1);
557 try {
558 dist = Integer.parseInt(distnum);
559 } catch (NumberFormatException e) {
560 throw new RevisionSyntaxException(
561 JGitText.get().invalidAncestryLength, revstr);
562 }
563 } else
564 dist = 1;
565 while (dist > 0) {
566 RevCommit commit = (RevCommit) rev;
567 if (commit.getParentCount() == 0) {
568 rev = null;
569 break;
570 }
571 commit = commit.getParent(0);
572 rw.parseHeaders(commit);
573 rev = commit;
574 --dist;
575 }
576 i = l - 1;
577 done = l;
578 break;
579 case '@':
580 if (rev != null)
581 throw new RevisionSyntaxException(revstr);
582 if (i + 1 < revChars.length && revChars[i + 1] != '{')
583 continue;
584 int m;
585 String time = null;
586 for (m = i + 2; m < revChars.length; ++m) {
587 if (revChars[m] == '}') {
588 time = new String(revChars, i + 2, m - i - 2);
589 break;
590 }
591 }
592 if (time != null) {
593 if (time.equals("upstream")) {
594 if (name == null)
595 name = new String(revChars, done, i);
596 if (name.equals(""))
597
598
599 name = Constants.HEAD;
600 if (!Repository.isValidRefName("x/" + name))
601 throw new RevisionSyntaxException(revstr);
602 Ref ref = getRef(name);
603 name = null;
604 if (ref == null)
605 return null;
606 if (ref.isSymbolic())
607 ref = ref.getLeaf();
608 name = ref.getName();
609
610 RemoteConfig remoteConfig;
611 try {
612 remoteConfig = new RemoteConfig(getConfig(),
613 "origin");
614 } catch (URISyntaxException e) {
615 throw new RevisionSyntaxException(revstr);
616 }
617 String remoteBranchName = getConfig()
618 .getString(
619 ConfigConstants.CONFIG_BRANCH_SECTION,
620 Repository.shortenRefName(ref.getName()),
621 ConfigConstants.CONFIG_KEY_MERGE);
622 List<RefSpec> fetchRefSpecs = remoteConfig
623 .getFetchRefSpecs();
624 for (RefSpec refSpec : fetchRefSpecs) {
625 if (refSpec.matchSource(remoteBranchName)) {
626 RefSpec expandFromSource = refSpec
627 .expandFromSource(remoteBranchName);
628 name = expandFromSource.getDestination();
629 break;
630 }
631 }
632 if (name == null)
633 throw new RevisionSyntaxException(revstr);
634 } else if (time.matches("^-\\d+$")) {
635 if (name != null)
636 throw new RevisionSyntaxException(revstr);
637 else {
638 String previousCheckout = resolveReflogCheckout(-Integer
639 .parseInt(time));
640 if (ObjectId.isId(previousCheckout))
641 rev = parseSimple(rw, previousCheckout);
642 else
643 name = previousCheckout;
644 }
645 } else {
646 if (name == null)
647 name = new String(revChars, done, i);
648 if (name.equals(""))
649 name = Constants.HEAD;
650 if (!Repository.isValidRefName("x/" + name))
651 throw new RevisionSyntaxException(revstr);
652 Ref ref = getRef(name);
653 name = null;
654 if (ref == null)
655 return null;
656
657
658 if (ref.isSymbolic())
659 ref = ref.getLeaf();
660 rev = resolveReflog(rw, ref, time);
661 }
662 i = m;
663 } else
664 throw new RevisionSyntaxException(revstr);
665 break;
666 case ':': {
667 RevTree tree;
668 if (rev == null) {
669 if (name == null)
670 name = new String(revChars, done, i);
671 if (name.equals(""))
672 name = Constants.HEAD;
673 rev = parseSimple(rw, name);
674 name = null;
675 }
676 if (rev == null)
677 return null;
678 tree = rw.parseTree(rev);
679 if (i == revChars.length - 1)
680 return tree.copy();
681
682 TreeWalk tw = TreeWalk.forPath(rw.getObjectReader(),
683 new String(revChars, i + 1, revChars.length - i - 1),
684 tree);
685 return tw != null ? tw.getObjectId(0) : null;
686 }
687 default:
688 if (rev != null)
689 throw new RevisionSyntaxException(revstr);
690 }
691 }
692 if (rev != null)
693 return rev.copy();
694 if (name != null)
695 return name;
696 if (done == revstr.length())
697 return null;
698 name = revstr.substring(done);
699 if (!Repository.isValidRefName("x/" + name))
700 throw new RevisionSyntaxException(revstr);
701 if (getRef(name) != null)
702 return name;
703 return resolveSimple(name);
704 }
705
706 private static boolean isHex(char c) {
707 return ('0' <= c && c <= '9')
708 || ('a' <= c && c <= 'f')
709 || ('A' <= c && c <= 'F');
710 }
711
712 private static boolean isAllHex(String str, int ptr) {
713 while (ptr < str.length()) {
714 if (!isHex(str.charAt(ptr++)))
715 return false;
716 }
717 return true;
718 }
719
720 private RevObject parseSimple(RevWalk rw, String revstr) throws IOException {
721 ObjectId id = resolveSimple(revstr);
722 return id != null ? rw.parseAny(id) : null;
723 }
724
725 private ObjectId resolveSimple(final String revstr) throws IOException {
726 if (ObjectId.isId(revstr))
727 return ObjectId.fromString(revstr);
728
729 if (Repository.isValidRefName("x/" + revstr)) {
730 Ref r = getRefDatabase().getRef(revstr);
731 if (r != null)
732 return r.getObjectId();
733 }
734
735 if (AbbreviatedObjectId.isId(revstr))
736 return resolveAbbreviation(revstr);
737
738 int dashg = revstr.indexOf("-g");
739 if ((dashg + 5) < revstr.length() && 0 <= dashg
740 && isHex(revstr.charAt(dashg + 2))
741 && isHex(revstr.charAt(dashg + 3))
742 && isAllHex(revstr, dashg + 4)) {
743
744 String s = revstr.substring(dashg + 2);
745 if (AbbreviatedObjectId.isId(s))
746 return resolveAbbreviation(s);
747 }
748
749 return null;
750 }
751
752 private String resolveReflogCheckout(int checkoutNo)
753 throws IOException {
754 ReflogReader reader = getReflogReader(Constants.HEAD);
755 if (reader == null) {
756 return null;
757 }
758 List<ReflogEntry> reflogEntries = reader.getReverseEntries();
759 for (ReflogEntry entry : reflogEntries) {
760 CheckoutEntry checkout = entry.parseCheckout();
761 if (checkout != null)
762 if (checkoutNo-- == 1)
763 return checkout.getFromBranch();
764 }
765 return null;
766 }
767
768 private RevCommit resolveReflog(RevWalk rw, Ref ref, String time)
769 throws IOException {
770 int number;
771 try {
772 number = Integer.parseInt(time);
773 } catch (NumberFormatException nfe) {
774 throw new RevisionSyntaxException(MessageFormat.format(
775 JGitText.get().invalidReflogRevision, time));
776 }
777 assert number >= 0;
778 ReflogReader reader = getReflogReader(ref.getName());
779 if (reader == null) {
780 throw new RevisionSyntaxException(
781 MessageFormat.format(JGitText.get().reflogEntryNotFound,
782 Integer.valueOf(number), ref.getName()));
783 }
784 ReflogEntry entry = reader.getReverseEntry(number);
785 if (entry == null)
786 throw new RevisionSyntaxException(MessageFormat.format(
787 JGitText.get().reflogEntryNotFound,
788 Integer.valueOf(number), ref.getName()));
789
790 return rw.parseCommit(entry.getNewId());
791 }
792
793 private ObjectId resolveAbbreviation(final String revstr) throws IOException,
794 AmbiguousObjectException {
795 AbbreviatedObjectId id = AbbreviatedObjectId.fromString(revstr);
796 try (ObjectReader reader = newObjectReader()) {
797 Collection<ObjectId> matches = reader.resolve(id);
798 if (matches.size() == 0)
799 return null;
800 else if (matches.size() == 1)
801 return matches.iterator().next();
802 else
803 throw new AmbiguousObjectException(id, matches);
804 }
805 }
806
807
808 public void incrementOpen() {
809 useCnt.incrementAndGet();
810 }
811
812
813 public void close() {
814 if (useCnt.decrementAndGet() == 0) {
815 doClose();
816 }
817 }
818
819
820
821
822
823
824 protected void doClose() {
825 getObjectDatabase().close();
826 getRefDatabase().close();
827 }
828
829 @SuppressWarnings("nls")
830 public String toString() {
831 String desc;
832 if (getDirectory() != null)
833 desc = getDirectory().getPath();
834 else
835 desc = getClass().getSimpleName() + "-"
836 + System.identityHashCode(this);
837 return "Repository[" + desc + "]";
838 }
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857 public String getFullBranch() throws IOException {
858 Ref head = getRef(Constants.HEAD);
859 if (head == null)
860 return null;
861 if (head.isSymbolic())
862 return head.getTarget().getName();
863 if (head.getObjectId() != null)
864 return head.getObjectId().name();
865 return null;
866 }
867
868
869
870
871
872
873
874
875
876
877
878
879
880 public String getBranch() throws IOException {
881 String name = getFullBranch();
882 if (name != null)
883 return shortenRefName(name);
884 return name;
885 }
886
887
888
889
890
891
892
893
894
895
896
897 public Set<ObjectId> getAdditionalHaves() {
898 return Collections.emptySet();
899 }
900
901
902
903
904
905
906
907
908
909
910
911 public Ref getRef(final String name) throws IOException {
912 return getRefDatabase().getRef(name);
913 }
914
915
916
917
918 public Map<String, Ref> getAllRefs() {
919 try {
920 return getRefDatabase().getRefs(RefDatabase.ALL);
921 } catch (IOException e) {
922 return new HashMap<String, Ref>();
923 }
924 }
925
926
927
928
929
930
931 public Map<String, Ref> getTags() {
932 try {
933 return getRefDatabase().getRefs(Constants.R_TAGS);
934 } catch (IOException e) {
935 return new HashMap<String, Ref>();
936 }
937 }
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952 public Ref peel(final Ref ref) {
953 try {
954 return getRefDatabase().peel(ref);
955 } catch (IOException e) {
956
957
958
959 return ref;
960 }
961 }
962
963
964
965
966 public Map<AnyObjectId, Set<Ref>> getAllRefsByPeeledObjectId() {
967 Map<String, Ref> allRefs = getAllRefs();
968 Map<AnyObjectId, Set<Ref>> ret = new HashMap<AnyObjectId, Set<Ref>>(allRefs.size());
969 for (Ref ref : allRefs.values()) {
970 ref = peel(ref);
971 AnyObjectId target = ref.getPeeledObjectId();
972 if (target == null)
973 target = ref.getObjectId();
974
975 Set<Ref> oset = ret.put(target, Collections.singleton(ref));
976 if (oset != null) {
977
978 if (oset.size() == 1) {
979
980 oset = new HashSet<Ref>(oset);
981 }
982 ret.put(target, oset);
983 oset.add(ref);
984 }
985 }
986 return ret;
987 }
988
989
990
991
992
993
994
995 public File getIndexFile() throws NoWorkTreeException {
996 if (isBare())
997 throw new NoWorkTreeException();
998 return indexFile;
999 }
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019 public DirCache readDirCache() throws NoWorkTreeException,
1020 CorruptObjectException, IOException {
1021 return DirCache.read(this);
1022 }
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043 public DirCache lockDirCache() throws NoWorkTreeException,
1044 CorruptObjectException, IOException {
1045
1046
1047 IndexChangedListener l = new IndexChangedListener() {
1048
1049 public void onIndexChanged(IndexChangedEvent event) {
1050 notifyIndexChanged();
1051 }
1052 };
1053 return DirCache.lock(this, l);
1054 }
1055
1056 static byte[] gitInternalSlash(byte[] bytes) {
1057 if (File.separatorChar == '/')
1058 return bytes;
1059 for (int i=0; i<bytes.length; ++i)
1060 if (bytes[i] == File.separatorChar)
1061 bytes[i] = '/';
1062 return bytes;
1063 }
1064
1065
1066
1067
1068 public RepositoryState getRepositoryState() {
1069 if (isBare() || getDirectory() == null)
1070 return RepositoryState.BARE;
1071
1072
1073 if (new File(getWorkTree(), ".dotest").exists())
1074 return RepositoryState.REBASING;
1075 if (new File(getDirectory(), ".dotest-merge").exists())
1076 return RepositoryState.REBASING_INTERACTIVE;
1077
1078
1079 if (new File(getDirectory(),"rebase-apply/rebasing").exists())
1080 return RepositoryState.REBASING_REBASING;
1081 if (new File(getDirectory(),"rebase-apply/applying").exists())
1082 return RepositoryState.APPLY;
1083 if (new File(getDirectory(),"rebase-apply").exists())
1084 return RepositoryState.REBASING;
1085
1086 if (new File(getDirectory(),"rebase-merge/interactive").exists())
1087 return RepositoryState.REBASING_INTERACTIVE;
1088 if (new File(getDirectory(),"rebase-merge").exists())
1089 return RepositoryState.REBASING_MERGE;
1090
1091
1092 if (new File(getDirectory(), Constants.MERGE_HEAD).exists()) {
1093
1094 try {
1095 if (!readDirCache().hasUnmergedPaths()) {
1096
1097 return RepositoryState.MERGING_RESOLVED;
1098 }
1099 } catch (IOException e) {
1100
1101
1102
1103 }
1104 return RepositoryState.MERGING;
1105 }
1106
1107 if (new File(getDirectory(), "BISECT_LOG").exists())
1108 return RepositoryState.BISECTING;
1109
1110 if (new File(getDirectory(), Constants.CHERRY_PICK_HEAD).exists()) {
1111 try {
1112 if (!readDirCache().hasUnmergedPaths()) {
1113
1114 return RepositoryState.CHERRY_PICKING_RESOLVED;
1115 }
1116 } catch (IOException e) {
1117
1118 }
1119
1120 return RepositoryState.CHERRY_PICKING;
1121 }
1122
1123 if (new File(getDirectory(), Constants.REVERT_HEAD).exists()) {
1124 try {
1125 if (!readDirCache().hasUnmergedPaths()) {
1126
1127 return RepositoryState.REVERTING_RESOLVED;
1128 }
1129 } catch (IOException e) {
1130
1131 }
1132
1133 return RepositoryState.REVERTING;
1134 }
1135
1136 return RepositoryState.SAFE;
1137 }
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150 public static boolean isValidRefName(final String refName) {
1151 final int len = refName.length();
1152 if (len == 0)
1153 return false;
1154 if (refName.endsWith(".lock"))
1155 return false;
1156
1157
1158
1159 try {
1160 SystemReader.getInstance().checkPath(refName);
1161 } catch (CorruptObjectException e) {
1162 return false;
1163 }
1164
1165 int components = 1;
1166 char p = '\0';
1167 for (int i = 0; i < len; i++) {
1168 final char c = refName.charAt(i);
1169 if (c <= ' ')
1170 return false;
1171 switch (c) {
1172 case '.':
1173 switch (p) {
1174 case '\0': case '/': case '.':
1175 return false;
1176 }
1177 if (i == len -1)
1178 return false;
1179 break;
1180 case '/':
1181 if (i == 0 || i == len - 1)
1182 return false;
1183 if (p == '/')
1184 return false;
1185 components++;
1186 break;
1187 case '{':
1188 if (p == '@')
1189 return false;
1190 break;
1191 case '~': case '^': case ':':
1192 case '?': case '[': case '*':
1193 case '\\':
1194 case '\u007F':
1195 return false;
1196 }
1197 p = c;
1198 }
1199 return components > 1;
1200 }
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210 public static String stripWorkDir(File workDir, File file) {
1211 final String filePath = file.getPath();
1212 final String workDirPath = workDir.getPath();
1213
1214 if (filePath.length() <= workDirPath.length() ||
1215 filePath.charAt(workDirPath.length()) != File.separatorChar ||
1216 !filePath.startsWith(workDirPath)) {
1217 File absWd = workDir.isAbsolute() ? workDir : workDir.getAbsoluteFile();
1218 File absFile = file.isAbsolute() ? file : file.getAbsoluteFile();
1219 if (absWd == workDir && absFile == file)
1220 return "";
1221 return stripWorkDir(absWd, absFile);
1222 }
1223
1224 String relName = filePath.substring(workDirPath.length() + 1);
1225 if (File.separatorChar != '/')
1226 relName = relName.replace(File.separatorChar, '/');
1227 return relName;
1228 }
1229
1230
1231
1232
1233 public boolean isBare() {
1234 return workTree == null;
1235 }
1236
1237
1238
1239
1240
1241
1242
1243
1244 public File getWorkTree() throws NoWorkTreeException {
1245 if (isBare())
1246 throw new NoWorkTreeException();
1247 return workTree;
1248 }
1249
1250
1251
1252
1253
1254
1255 public abstract void scanForRepoChanges() throws IOException;
1256
1257
1258
1259
1260 public abstract void notifyIndexChanged();
1261
1262
1263
1264
1265
1266
1267 public static String shortenRefName(String refName) {
1268 if (refName.startsWith(Constants.R_HEADS))
1269 return refName.substring(Constants.R_HEADS.length());
1270 if (refName.startsWith(Constants.R_TAGS))
1271 return refName.substring(Constants.R_TAGS.length());
1272 if (refName.startsWith(Constants.R_REMOTES))
1273 return refName.substring(Constants.R_REMOTES.length());
1274 return refName;
1275 }
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285 public String shortenRemoteBranchName(String refName) {
1286 for (String remote : getRemoteNames()) {
1287 String remotePrefix = Constants.R_REMOTES + remote + "/";
1288 if (refName.startsWith(remotePrefix))
1289 return refName.substring(remotePrefix.length());
1290 }
1291 return null;
1292 }
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302 public String getRemoteName(String refName) {
1303 for (String remote : getRemoteNames()) {
1304 String remotePrefix = Constants.R_REMOTES + remote + "/";
1305 if (refName.startsWith(remotePrefix))
1306 return remote;
1307 }
1308 return null;
1309 }
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319 public abstract ReflogReader getReflogReader(String refName)
1320 throws IOException;
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334 public String readMergeCommitMsg() throws IOException, NoWorkTreeException {
1335 return readCommitMsgFile(Constants.MERGE_MSG);
1336 }
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350 public void writeMergeCommitMsg(String msg) throws IOException {
1351 File mergeMsgFile = new File(gitDir, Constants.MERGE_MSG);
1352 writeCommitMsg(mergeMsgFile, msg);
1353 }
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368 public String readCommitEditMsg() throws IOException, NoWorkTreeException {
1369 return readCommitMsgFile(Constants.COMMIT_EDITMSG);
1370 }
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384 public void writeCommitEditMsg(String msg) throws IOException {
1385 File commiEditMsgFile = new File(gitDir, Constants.COMMIT_EDITMSG);
1386 writeCommitMsg(commiEditMsgFile, msg);
1387 }
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402 public List<ObjectId> readMergeHeads() throws IOException, NoWorkTreeException {
1403 if (isBare() || getDirectory() == null)
1404 throw new NoWorkTreeException();
1405
1406 byte[] raw = readGitDirectoryFile(Constants.MERGE_HEAD);
1407 if (raw == null)
1408 return null;
1409
1410 LinkedList<ObjectId> heads = new LinkedList<ObjectId>();
1411 for (int p = 0; p < raw.length;) {
1412 heads.add(ObjectId.fromString(raw, p));
1413 p = RawParseUtils
1414 .nextLF(raw, p + Constants.OBJECT_ID_STRING_LENGTH);
1415 }
1416 return heads;
1417 }
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430 public void writeMergeHeads(List<? extends ObjectId> heads) throws IOException {
1431 writeHeadsFile(heads, Constants.MERGE_HEAD);
1432 }
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445 public ObjectId readCherryPickHead() throws IOException,
1446 NoWorkTreeException {
1447 if (isBare() || getDirectory() == null)
1448 throw new NoWorkTreeException();
1449
1450 byte[] raw = readGitDirectoryFile(Constants.CHERRY_PICK_HEAD);
1451 if (raw == null)
1452 return null;
1453
1454 return ObjectId.fromString(raw, 0);
1455 }
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468 public ObjectId readRevertHead() throws IOException, NoWorkTreeException {
1469 if (isBare() || getDirectory() == null)
1470 throw new NoWorkTreeException();
1471
1472 byte[] raw = readGitDirectoryFile(Constants.REVERT_HEAD);
1473 if (raw == null)
1474 return null;
1475 return ObjectId.fromString(raw, 0);
1476 }
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487 public void writeCherryPickHead(ObjectId head) throws IOException {
1488 List<ObjectId> heads = (head != null) ? Collections.singletonList(head)
1489 : null;
1490 writeHeadsFile(heads, Constants.CHERRY_PICK_HEAD);
1491 }
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502 public void writeRevertHead(ObjectId head) throws IOException {
1503 List<ObjectId> heads = (head != null) ? Collections.singletonList(head)
1504 : null;
1505 writeHeadsFile(heads, Constants.REVERT_HEAD);
1506 }
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516 public void writeOrigHead(ObjectId head) throws IOException {
1517 List<ObjectId> heads = head != null ? Collections.singletonList(head)
1518 : null;
1519 writeHeadsFile(heads, Constants.ORIG_HEAD);
1520 }
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533 public ObjectId readOrigHead() throws IOException, NoWorkTreeException {
1534 if (isBare() || getDirectory() == null)
1535 throw new NoWorkTreeException();
1536
1537 byte[] raw = readGitDirectoryFile(Constants.ORIG_HEAD);
1538 return raw != null ? ObjectId.fromString(raw, 0) : null;
1539 }
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553 public String readSquashCommitMsg() throws IOException {
1554 return readCommitMsgFile(Constants.SQUASH_MSG);
1555 }
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569 public void writeSquashCommitMsg(String msg) throws IOException {
1570 File squashMsgFile = new File(gitDir, Constants.SQUASH_MSG);
1571 writeCommitMsg(squashMsgFile, msg);
1572 }
1573
1574 private String readCommitMsgFile(String msgFilename) throws IOException {
1575 if (isBare() || getDirectory() == null)
1576 throw new NoWorkTreeException();
1577
1578 File mergeMsgFile = new File(getDirectory(), msgFilename);
1579 try {
1580 return RawParseUtils.decode(IO.readFully(mergeMsgFile));
1581 } catch (FileNotFoundException e) {
1582
1583 return null;
1584 }
1585 }
1586
1587 private void writeCommitMsg(File msgFile, String msg) throws IOException {
1588 if (msg != null) {
1589 FileOutputStream fos = new FileOutputStream(msgFile);
1590 try {
1591 fos.write(msg.getBytes(Constants.CHARACTER_ENCODING));
1592 } finally {
1593 fos.close();
1594 }
1595 } else {
1596 FileUtils.delete(msgFile, FileUtils.SKIP_MISSING);
1597 }
1598 }
1599
1600
1601
1602
1603
1604
1605
1606
1607 private byte[] readGitDirectoryFile(String filename) throws IOException {
1608 File file = new File(getDirectory(), filename);
1609 try {
1610 byte[] raw = IO.readFully(file);
1611 return raw.length > 0 ? raw : null;
1612 } catch (FileNotFoundException notFound) {
1613 return null;
1614 }
1615 }
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627 private void writeHeadsFile(List<? extends ObjectId> heads, String filename)
1628 throws FileNotFoundException, IOException {
1629 File headsFile = new File(getDirectory(), filename);
1630 if (heads != null) {
1631 BufferedOutputStream bos = new SafeBufferedOutputStream(
1632 new FileOutputStream(headsFile));
1633 try {
1634 for (ObjectId id : heads) {
1635 id.copyTo(bos);
1636 bos.write('\n');
1637 }
1638 } finally {
1639 bos.close();
1640 }
1641 } else {
1642 FileUtils.delete(headsFile, FileUtils.SKIP_MISSING);
1643 }
1644 }
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660 public List<RebaseTodoLine> readRebaseTodo(String path,
1661 boolean includeComments)
1662 throws IOException {
1663 return new RebaseTodoFile(this).readRebaseTodo(path, includeComments);
1664 }
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679 public void writeRebaseTodoFile(String path, List<RebaseTodoLine> steps,
1680 boolean append)
1681 throws IOException {
1682 new RebaseTodoFile(this).writeRebaseTodoFile(path, steps, append);
1683 }
1684
1685
1686
1687
1688
1689 public Set<String> getRemoteNames() {
1690 return getConfig()
1691 .getSubsections(ConfigConstants.CONFIG_REMOTE_SECTION);
1692 }
1693 }