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 public String getFullBranch() throws IOException {
857 Ref head = getRef(Constants.HEAD);
858 if (head == null)
859 return null;
860 if (head.isSymbolic())
861 return head.getTarget().getName();
862 if (head.getObjectId() != null)
863 return head.getObjectId().name();
864 return null;
865 }
866
867
868
869
870
871
872
873
874
875
876
877
878 public String getBranch() throws IOException {
879 String name = getFullBranch();
880 if (name != null)
881 return shortenRefName(name);
882 return name;
883 }
884
885
886
887
888
889
890
891
892
893
894
895 public Set<ObjectId> getAdditionalHaves() {
896 return Collections.emptySet();
897 }
898
899
900
901
902
903
904
905
906
907
908
909 public Ref getRef(final String name) throws IOException {
910 return getRefDatabase().getRef(name);
911 }
912
913
914
915
916 public Map<String, Ref> getAllRefs() {
917 try {
918 return getRefDatabase().getRefs(RefDatabase.ALL);
919 } catch (IOException e) {
920 return new HashMap<String, Ref>();
921 }
922 }
923
924
925
926
927
928
929 public Map<String, Ref> getTags() {
930 try {
931 return getRefDatabase().getRefs(Constants.R_TAGS);
932 } catch (IOException e) {
933 return new HashMap<String, Ref>();
934 }
935 }
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950 public Ref peel(final Ref ref) {
951 try {
952 return getRefDatabase().peel(ref);
953 } catch (IOException e) {
954
955
956
957 return ref;
958 }
959 }
960
961
962
963
964 public Map<AnyObjectId, Set<Ref>> getAllRefsByPeeledObjectId() {
965 Map<String, Ref> allRefs = getAllRefs();
966 Map<AnyObjectId, Set<Ref>> ret = new HashMap<AnyObjectId, Set<Ref>>(allRefs.size());
967 for (Ref ref : allRefs.values()) {
968 ref = peel(ref);
969 AnyObjectId target = ref.getPeeledObjectId();
970 if (target == null)
971 target = ref.getObjectId();
972
973 Set<Ref> oset = ret.put(target, Collections.singleton(ref));
974 if (oset != null) {
975
976 if (oset.size() == 1) {
977
978 oset = new HashSet<Ref>(oset);
979 }
980 ret.put(target, oset);
981 oset.add(ref);
982 }
983 }
984 return ret;
985 }
986
987
988
989
990
991
992
993 public File getIndexFile() throws NoWorkTreeException {
994 if (isBare())
995 throw new NoWorkTreeException();
996 return indexFile;
997 }
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017 public DirCache readDirCache() throws NoWorkTreeException,
1018 CorruptObjectException, IOException {
1019 return DirCache.read(this);
1020 }
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041 public DirCache lockDirCache() throws NoWorkTreeException,
1042 CorruptObjectException, IOException {
1043
1044
1045 IndexChangedListener l = new IndexChangedListener() {
1046
1047 public void onIndexChanged(IndexChangedEvent event) {
1048 notifyIndexChanged();
1049 }
1050 };
1051 return DirCache.lock(this, l);
1052 }
1053
1054 static byte[] gitInternalSlash(byte[] bytes) {
1055 if (File.separatorChar == '/')
1056 return bytes;
1057 for (int i=0; i<bytes.length; ++i)
1058 if (bytes[i] == File.separatorChar)
1059 bytes[i] = '/';
1060 return bytes;
1061 }
1062
1063
1064
1065
1066 public RepositoryState getRepositoryState() {
1067 if (isBare() || getDirectory() == null)
1068 return RepositoryState.BARE;
1069
1070
1071 if (new File(getWorkTree(), ".dotest").exists())
1072 return RepositoryState.REBASING;
1073 if (new File(getDirectory(), ".dotest-merge").exists())
1074 return RepositoryState.REBASING_INTERACTIVE;
1075
1076
1077 if (new File(getDirectory(),"rebase-apply/rebasing").exists())
1078 return RepositoryState.REBASING_REBASING;
1079 if (new File(getDirectory(),"rebase-apply/applying").exists())
1080 return RepositoryState.APPLY;
1081 if (new File(getDirectory(),"rebase-apply").exists())
1082 return RepositoryState.REBASING;
1083
1084 if (new File(getDirectory(),"rebase-merge/interactive").exists())
1085 return RepositoryState.REBASING_INTERACTIVE;
1086 if (new File(getDirectory(),"rebase-merge").exists())
1087 return RepositoryState.REBASING_MERGE;
1088
1089
1090 if (new File(getDirectory(), Constants.MERGE_HEAD).exists()) {
1091
1092 try {
1093 if (!readDirCache().hasUnmergedPaths()) {
1094
1095 return RepositoryState.MERGING_RESOLVED;
1096 }
1097 } catch (IOException e) {
1098
1099
1100
1101 }
1102 return RepositoryState.MERGING;
1103 }
1104
1105 if (new File(getDirectory(), "BISECT_LOG").exists())
1106 return RepositoryState.BISECTING;
1107
1108 if (new File(getDirectory(), Constants.CHERRY_PICK_HEAD).exists()) {
1109 try {
1110 if (!readDirCache().hasUnmergedPaths()) {
1111
1112 return RepositoryState.CHERRY_PICKING_RESOLVED;
1113 }
1114 } catch (IOException e) {
1115
1116 }
1117
1118 return RepositoryState.CHERRY_PICKING;
1119 }
1120
1121 if (new File(getDirectory(), Constants.REVERT_HEAD).exists()) {
1122 try {
1123 if (!readDirCache().hasUnmergedPaths()) {
1124
1125 return RepositoryState.REVERTING_RESOLVED;
1126 }
1127 } catch (IOException e) {
1128
1129 }
1130
1131 return RepositoryState.REVERTING;
1132 }
1133
1134 return RepositoryState.SAFE;
1135 }
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148 public static boolean isValidRefName(final String refName) {
1149 final int len = refName.length();
1150 if (len == 0)
1151 return false;
1152 if (refName.endsWith(".lock"))
1153 return false;
1154
1155
1156
1157 try {
1158 SystemReader.getInstance().checkPath(refName);
1159 } catch (CorruptObjectException e) {
1160 return false;
1161 }
1162
1163 int components = 1;
1164 char p = '\0';
1165 for (int i = 0; i < len; i++) {
1166 final char c = refName.charAt(i);
1167 if (c <= ' ')
1168 return false;
1169 switch (c) {
1170 case '.':
1171 switch (p) {
1172 case '\0': case '/': case '.':
1173 return false;
1174 }
1175 if (i == len -1)
1176 return false;
1177 break;
1178 case '/':
1179 if (i == 0 || i == len - 1)
1180 return false;
1181 if (p == '/')
1182 return false;
1183 components++;
1184 break;
1185 case '{':
1186 if (p == '@')
1187 return false;
1188 break;
1189 case '~': case '^': case ':':
1190 case '?': case '[': case '*':
1191 case '\\':
1192 case '\u007F':
1193 return false;
1194 }
1195 p = c;
1196 }
1197 return components > 1;
1198 }
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208 public static String stripWorkDir(File workDir, File file) {
1209 final String filePath = file.getPath();
1210 final String workDirPath = workDir.getPath();
1211
1212 if (filePath.length() <= workDirPath.length() ||
1213 filePath.charAt(workDirPath.length()) != File.separatorChar ||
1214 !filePath.startsWith(workDirPath)) {
1215 File absWd = workDir.isAbsolute() ? workDir : workDir.getAbsoluteFile();
1216 File absFile = file.isAbsolute() ? file : file.getAbsoluteFile();
1217 if (absWd == workDir && absFile == file)
1218 return "";
1219 return stripWorkDir(absWd, absFile);
1220 }
1221
1222 String relName = filePath.substring(workDirPath.length() + 1);
1223 if (File.separatorChar != '/')
1224 relName = relName.replace(File.separatorChar, '/');
1225 return relName;
1226 }
1227
1228
1229
1230
1231 public boolean isBare() {
1232 return workTree == null;
1233 }
1234
1235
1236
1237
1238
1239
1240
1241
1242 public File getWorkTree() throws NoWorkTreeException {
1243 if (isBare())
1244 throw new NoWorkTreeException();
1245 return workTree;
1246 }
1247
1248
1249
1250
1251
1252
1253 public abstract void scanForRepoChanges() throws IOException;
1254
1255
1256
1257
1258 public abstract void notifyIndexChanged();
1259
1260
1261
1262
1263
1264
1265 public static String shortenRefName(String refName) {
1266 if (refName.startsWith(Constants.R_HEADS))
1267 return refName.substring(Constants.R_HEADS.length());
1268 if (refName.startsWith(Constants.R_TAGS))
1269 return refName.substring(Constants.R_TAGS.length());
1270 if (refName.startsWith(Constants.R_REMOTES))
1271 return refName.substring(Constants.R_REMOTES.length());
1272 return refName;
1273 }
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283 public String shortenRemoteBranchName(String refName) {
1284 for (String remote : getRemoteNames()) {
1285 String remotePrefix = Constants.R_REMOTES + remote + "/";
1286 if (refName.startsWith(remotePrefix))
1287 return refName.substring(remotePrefix.length());
1288 }
1289 return null;
1290 }
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300 public String getRemoteName(String refName) {
1301 for (String remote : getRemoteNames()) {
1302 String remotePrefix = Constants.R_REMOTES + remote + "/";
1303 if (refName.startsWith(remotePrefix))
1304 return remote;
1305 }
1306 return null;
1307 }
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317 public abstract ReflogReader getReflogReader(String refName)
1318 throws IOException;
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332 public String readMergeCommitMsg() throws IOException, NoWorkTreeException {
1333 return readCommitMsgFile(Constants.MERGE_MSG);
1334 }
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348 public void writeMergeCommitMsg(String msg) throws IOException {
1349 File mergeMsgFile = new File(gitDir, Constants.MERGE_MSG);
1350 writeCommitMsg(mergeMsgFile, msg);
1351 }
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366 public String readCommitEditMsg() throws IOException, NoWorkTreeException {
1367 return readCommitMsgFile(Constants.COMMIT_EDITMSG);
1368 }
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382 public void writeCommitEditMsg(String msg) throws IOException {
1383 File commiEditMsgFile = new File(gitDir, Constants.COMMIT_EDITMSG);
1384 writeCommitMsg(commiEditMsgFile, msg);
1385 }
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400 public List<ObjectId> readMergeHeads() throws IOException, NoWorkTreeException {
1401 if (isBare() || getDirectory() == null)
1402 throw new NoWorkTreeException();
1403
1404 byte[] raw = readGitDirectoryFile(Constants.MERGE_HEAD);
1405 if (raw == null)
1406 return null;
1407
1408 LinkedList<ObjectId> heads = new LinkedList<ObjectId>();
1409 for (int p = 0; p < raw.length;) {
1410 heads.add(ObjectId.fromString(raw, p));
1411 p = RawParseUtils
1412 .nextLF(raw, p + Constants.OBJECT_ID_STRING_LENGTH);
1413 }
1414 return heads;
1415 }
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428 public void writeMergeHeads(List<? extends ObjectId> heads) throws IOException {
1429 writeHeadsFile(heads, Constants.MERGE_HEAD);
1430 }
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443 public ObjectId readCherryPickHead() throws IOException,
1444 NoWorkTreeException {
1445 if (isBare() || getDirectory() == null)
1446 throw new NoWorkTreeException();
1447
1448 byte[] raw = readGitDirectoryFile(Constants.CHERRY_PICK_HEAD);
1449 if (raw == null)
1450 return null;
1451
1452 return ObjectId.fromString(raw, 0);
1453 }
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466 public ObjectId readRevertHead() throws IOException, NoWorkTreeException {
1467 if (isBare() || getDirectory() == null)
1468 throw new NoWorkTreeException();
1469
1470 byte[] raw = readGitDirectoryFile(Constants.REVERT_HEAD);
1471 if (raw == null)
1472 return null;
1473 return ObjectId.fromString(raw, 0);
1474 }
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485 public void writeCherryPickHead(ObjectId head) throws IOException {
1486 List<ObjectId> heads = (head != null) ? Collections.singletonList(head)
1487 : null;
1488 writeHeadsFile(heads, Constants.CHERRY_PICK_HEAD);
1489 }
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500 public void writeRevertHead(ObjectId head) throws IOException {
1501 List<ObjectId> heads = (head != null) ? Collections.singletonList(head)
1502 : null;
1503 writeHeadsFile(heads, Constants.REVERT_HEAD);
1504 }
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514 public void writeOrigHead(ObjectId head) throws IOException {
1515 List<ObjectId> heads = head != null ? Collections.singletonList(head)
1516 : null;
1517 writeHeadsFile(heads, Constants.ORIG_HEAD);
1518 }
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531 public ObjectId readOrigHead() throws IOException, NoWorkTreeException {
1532 if (isBare() || getDirectory() == null)
1533 throw new NoWorkTreeException();
1534
1535 byte[] raw = readGitDirectoryFile(Constants.ORIG_HEAD);
1536 return raw != null ? ObjectId.fromString(raw, 0) : null;
1537 }
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551 public String readSquashCommitMsg() throws IOException {
1552 return readCommitMsgFile(Constants.SQUASH_MSG);
1553 }
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567 public void writeSquashCommitMsg(String msg) throws IOException {
1568 File squashMsgFile = new File(gitDir, Constants.SQUASH_MSG);
1569 writeCommitMsg(squashMsgFile, msg);
1570 }
1571
1572 private String readCommitMsgFile(String msgFilename) throws IOException {
1573 if (isBare() || getDirectory() == null)
1574 throw new NoWorkTreeException();
1575
1576 File mergeMsgFile = new File(getDirectory(), msgFilename);
1577 try {
1578 return RawParseUtils.decode(IO.readFully(mergeMsgFile));
1579 } catch (FileNotFoundException e) {
1580
1581 return null;
1582 }
1583 }
1584
1585 private void writeCommitMsg(File msgFile, String msg) throws IOException {
1586 if (msg != null) {
1587 FileOutputStream fos = new FileOutputStream(msgFile);
1588 try {
1589 fos.write(msg.getBytes(Constants.CHARACTER_ENCODING));
1590 } finally {
1591 fos.close();
1592 }
1593 } else {
1594 FileUtils.delete(msgFile, FileUtils.SKIP_MISSING);
1595 }
1596 }
1597
1598
1599
1600
1601
1602
1603
1604
1605 private byte[] readGitDirectoryFile(String filename) throws IOException {
1606 File file = new File(getDirectory(), filename);
1607 try {
1608 byte[] raw = IO.readFully(file);
1609 return raw.length > 0 ? raw : null;
1610 } catch (FileNotFoundException notFound) {
1611 return null;
1612 }
1613 }
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625 private void writeHeadsFile(List<? extends ObjectId> heads, String filename)
1626 throws FileNotFoundException, IOException {
1627 File headsFile = new File(getDirectory(), filename);
1628 if (heads != null) {
1629 BufferedOutputStream bos = new SafeBufferedOutputStream(
1630 new FileOutputStream(headsFile));
1631 try {
1632 for (ObjectId id : heads) {
1633 id.copyTo(bos);
1634 bos.write('\n');
1635 }
1636 } finally {
1637 bos.close();
1638 }
1639 } else {
1640 FileUtils.delete(headsFile, FileUtils.SKIP_MISSING);
1641 }
1642 }
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658 public List<RebaseTodoLine> readRebaseTodo(String path,
1659 boolean includeComments)
1660 throws IOException {
1661 return new RebaseTodoFile(this).readRebaseTodo(path, includeComments);
1662 }
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677 public void writeRebaseTodoFile(String path, List<RebaseTodoLine> steps,
1678 boolean append)
1679 throws IOException {
1680 new RebaseTodoFile(this).writeRebaseTodoFile(path, steps, append);
1681 }
1682
1683
1684
1685
1686
1687 public Set<String> getRemoteNames() {
1688 return getConfig()
1689 .getSubsections(ConfigConstants.CONFIG_REMOTE_SECTION);
1690 }
1691 }