1 /*
2 * #%L
3 * JavaHg
4 * %%
5 * Copyright (C) 2011 aragost Trifork ag
6 * %%
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 * #L%
25 */
26 package com.aragost.javahg.commands;
27
28 import java.io.IOException;
29
30 import com.aragost.javahg.Changeset;
31 import com.aragost.javahg.Repository;
32 import com.aragost.javahg.commands.flags.GraftCommandFlags;
33 import com.aragost.javahg.internals.HgInputStream;
34 import com.aragost.javahg.internals.RuntimeIOException;
35 import com.aragost.javahg.merge.GraftContext;
36
37 /**
38 * Command class for executing <tt>hg graft</tt>. Set flags from
39 * {@link GraftCommandFlags} and call the {@link #execute} method.
40 */
41 public class GraftCommand extends GraftCommandFlags {
42
43 private boolean hasConflicts = false;
44 private boolean somethingSkipped = false;
45
46 /**
47 * @param repository
48 * the repository associated with this command.
49 */
50 public GraftCommand(Repository repository) {
51 super(repository);
52 cmdAppend("-y");
53 cmdAppend("--tool", "internal:fail");
54 }
55
56 @Override
57 public boolean isSuccessful() {
58 return super.isSuccessful() || this.hasConflicts || this.somethingSkipped;
59 }
60
61 /**
62 * Run <tt>hg graft</tt> with the specified revisions.
63 * <p>
64 * If the grafting is succesful null is return. Otherwise a
65 * MergeContext is return where the conflicts can be inspected and
66 * resolved.
67 *
68 * @param changeset
69 * the changeset to graft
70 */
71 public GraftContext execute(Changeset changeset) {
72 Repository repo = getRepository();
73 repo.lock();
74 HgInputStream stream = launchStream(changeset.getNode());
75 try {
76 if (stream.match("grafting revision ".getBytes())) {
77 int rev = stream.revisionUpTo('\n');
78 GraftContext ctx = new GraftContext(this, rev);
79 if (!stream.isEof()) {
80 ctx.processStream(stream, true);
81 }
82 stream.consumeAll();
83 boolean flagOrKeepDeleteConflicts = !ctx.getFlagConflicts().isEmpty()
84 || !ctx.getKeepDeleteConflicts().isEmpty();
85 if (getReturnCode() == -1 && this.hasConflicts || flagOrKeepDeleteConflicts) {
86 if (flagOrKeepDeleteConflicts && ctx.getMergeConflicts().isEmpty()) {
87 // graft has actually made a new changeset,
88 // but
89 // there is actually flag conflict or
90 // keep/delete
91 // conflict. So rollback the changeset.
92 Changeset rollbackChangeset = repo.tip();
93 if (!changeset.getNode().equals(rollbackChangeset.getExtra().getString("source"))) {
94 throw new IllegalStateException("Current tip is not grafted from expected changeset");
95 }
96 RollbackCommand.on(repo).execute();
97 ctx.setRollbackChangeset(rollbackChangeset);
98 }
99 return ctx;
100 } else {
101 return null;
102 }
103 } else {
104 return null;
105 }
106 } catch (IOException e) {
107 throw new RuntimeIOException(e);
108 } finally {
109 repo.unlock();
110 }
111 }
112
113 /**
114 * Execute the graft command with the continue flag.
115 *
116 * @return true if a new changeset was created, false if for some
117 * reason graft did not succeed
118 */
119 public boolean executeContinue() {
120 launchString("--continue");
121 return getReturnCode() == 0;
122 }
123
124 @Override
125 protected void doneHook() {
126 String errorString = getErrorString();
127 this.hasConflicts = errorString.indexOf("abort: unresolved conflicts, can't continue") >= 0
128 || errorString.indexOf("abort: unresolved merge conflicts (see hg help resolve)") >= 0;
129 this.somethingSkipped = errorString.indexOf("skipping ancestor revision ") >= 0;
130 }
131
132 @Override
133 protected void clear() {
134 super.clear();
135 this.hasConflicts = false;
136 this.somethingSkipped = false;
137 }
138
139 }