1 package com.aragost.javahg.merge;
2
3 import java.io.IOException;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.List;
7
8 import com.aragost.javahg.Changeset;
9 import com.aragost.javahg.Repository;
10 import com.aragost.javahg.WorkingCopy;
11 import com.aragost.javahg.commands.LogCommand;
12 import com.aragost.javahg.commands.ResolveCommand;
13 import com.aragost.javahg.commands.ResolveStatusLine;
14 import com.aragost.javahg.internals.AbstractCommand;
15 import com.aragost.javahg.internals.HgInputStream;
16 import com.aragost.javahg.internals.UnexpectedCommandOutputException;
17 import com.aragost.javahg.internals.Utils;
18 import com.google.common.collect.Lists;
19
20
21
22
23
24
25 public abstract class ConflictResolvingContext {
26
27 private static final byte[] USE_RESOLVE_EXPLANATION = "use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon\n".getBytes();
28 private static final byte[] USE_RESOLVE_SHORT_EXPLANATION = "use 'hg resolve' to retry unresolved file merges\n".getBytes();
29 private static final byte[] BRANCH_MERGE_EXPLANATION = "(branch merge, don't forget to commit)\n".getBytes();
30 private static final byte[] FILES_REMOVED = " files removed, ".getBytes();
31 private static final byte[] FILES_MERGED = " files merged, ".getBytes();
32 private static final byte[] FILES_UPDATED = " files updated, ".getBytes();
33 private static final byte[] LOCAL_CHANGED_AFTER = " which remote deleted\nuse (c)hanged version or (d)elete? c\n".getBytes();
34 private static final byte[] LOCAL_CHANGED = " local changed ".getBytes();
35 private static final byte[] REMOVED_CHANGE_AFTER = " which local deleted\nuse (c)hanged version or leave (d)eleted? c\n".getBytes();
36 private static final byte[] REMOTE_CHANGED = "remote changed ".getBytes();
37 private static final byte[] CONFLICTING_FLAGS = " conflicting flags for ".getBytes();
38 private static final String NONE_EXEC_SYMLINK = "(n)one, e(x)ec or sym(l)ink? n\n";
39
40 private AbstractCommand command;
41
42 private WorkingCopy workingCopy;
43
44 private List<KeepDeleteConflict> keepDeleteConflicts = Lists.newArrayList();
45
46 private List<FlagConflict> flagConflicts = Lists.newArrayList();
47
48 private List<MergeConflict> mergeConflicts;
49
50 private Changeset base;
51
52 public ConflictResolvingContext(AbstractCommand command) {
53 this.command = command;
54 this.workingCopy = this.command.getRepository().workingCopy();
55 }
56
57 public WorkingCopy getWorkingCopy() {
58 return workingCopy;
59 }
60
61 public Collection<FlagConflict> getFlagConflicts() {
62 return flagConflicts;
63 }
64
65 public List<KeepDeleteConflict> getKeepDeleteConflicts() {
66 return keepDeleteConflicts;
67 }
68
69 public synchronized List<MergeConflict> getMergeConflicts() {
70 if (this.mergeConflicts == null) {
71 List<ResolveStatusLine> lines = ResolveCommand.on(getRepository()).list();
72 List<MergeConflict> conflicts = Lists.newArrayListWithCapacity(lines.size());
73 for (ResolveStatusLine line : lines) {
74 MergeConflict mergeConflict = new MergeConflict(this, line.getFileName());
75 conflicts.add(mergeConflict);
76 }
77 this.mergeConflicts = conflicts;
78 }
79 return mergeConflicts;
80 }
81
82 public Repository getRepository() {
83 return this.command.getRepository();
84 }
85
86 public AbstractCommand getCommand() {
87 return command;
88 }
89
90
91
92
93
94
95
96
97
98 public void processStream(HgInputStream in, boolean whenUnknowReturn) throws IOException {
99 while (!in.isEof()) {
100 byte[] endPattern = null;
101 KeepDeleteConflict.State keepState = null;
102 if (in.match(REMOTE_CHANGED)) {
103 endPattern = REMOVED_CHANGE_AFTER;
104 keepState = KeepDeleteConflict.State.DELETE;
105 } else if (in.match(LOCAL_CHANGED)) {
106 endPattern = LOCAL_CHANGED_AFTER;
107 keepState = KeepDeleteConflict.State.KEEP;
108 } else if (in.match(CONFLICTING_FLAGS)) {
109 String fileName = in.textUpTo('\n');
110 in.mustMatch(NONE_EXEC_SYMLINK.getBytes());
111 FlagConflict flagConflict = new FlagConflict(this, fileName);
112 this.flagConflicts.add(flagConflict);
113
114 } else if ((in.readDecimal()) != null) {
115 in.mustMatch(FILES_UPDATED);
116 Integer merged = in.readDecimal();
117 in.mustMatch(FILES_MERGED);
118 Integer removed = in.readDecimal();
119 in.mustMatch(FILES_REMOVED);
120 Integer unresolved = in.readDecimal();
121 in.mustMatch(" files unresolved\n".getBytes());
122 if (merged == null || removed == null || unresolved == null) {
123 throw new UnexpectedCommandOutputException(this.command, null);
124 }
125 if (unresolved.intValue() == 0) {
126 this.mergeConflicts = Collections.emptyList();
127
128
129 }
130 } else if (in.match(USE_RESOLVE_EXPLANATION)) {
131
132 } else if (in.match(USE_RESOLVE_SHORT_EXPLANATION)) {
133
134 } else if (in.match(BRANCH_MERGE_EXPLANATION)) {
135
136 } else {
137 if (whenUnknowReturn) {
138 return;
139 } else {
140 throw new UnexpectedCommandOutputException(Utils.readStream(in,
141 command.getRepository().newDecoder()));
142 }
143 }
144 if (endPattern != null) {
145 String fileName = in.textUpTo(endPattern);
146 KeepDeleteConflict keepRemoveConflict = new KeepDeleteConflict(this, fileName, keepState);
147 this.keepDeleteConflicts.add(keepRemoveConflict);
148 }
149 }
150 }
151
152 public Changeset getLocal() {
153 return getWorkingCopy().getParent1();
154 }
155
156 public Changeset getRemote() {
157 return getWorkingCopy().getParent2();
158 }
159
160 public synchronized Changeset getBase() {
161 if (base == null) {
162 String ancestorRev = "ancestor(" + getLocal().getNode() + "," + getRemote().getNode() + ")";
163 this.base = LogCommand.on(getRepository()).rev(ancestorRev).single();
164 }
165 return this.base;
166 }
167
168 }