View Javadoc

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   * Base class for a context where conflicts are resolved.
22   * <p>
23   * That is for example after a merge or graft.
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       * Read input from the stream record conflicts.
92       * <p>
93       * 
94       * @param in
95       * @param whenUnknowReturn
96       * @throws IOException
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                     // If there is merge conflicts it will be lazy
128                     // intialized
129                 }
130             } else if (in.match(USE_RESOLVE_EXPLANATION)) {
131                 // ignore
132             } else if (in.match(USE_RESOLVE_SHORT_EXPLANATION)) {
133                 // ignore
134             } else if (in.match(BRANCH_MERGE_EXPLANATION)) {
135                 // ignore
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 }