1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.internal.storage.reftree;
12
13 import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
14 import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
15 import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
16 import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_NONFASTFORWARD;
17 import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
18 import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE;
19 import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE_NONFASTFORWARD;
20
21 import java.io.IOException;
22 import java.text.MessageFormat;
23 import java.util.ArrayList;
24 import java.util.List;
25
26 import org.eclipse.jgit.annotations.Nullable;
27 import org.eclipse.jgit.internal.JGitText;
28 import org.eclipse.jgit.lib.BatchRefUpdate;
29 import org.eclipse.jgit.lib.CommitBuilder;
30 import org.eclipse.jgit.lib.NullProgressMonitor;
31 import org.eclipse.jgit.lib.ObjectId;
32 import org.eclipse.jgit.lib.ObjectInserter;
33 import org.eclipse.jgit.lib.ObjectReader;
34 import org.eclipse.jgit.lib.PersonIdent;
35 import org.eclipse.jgit.lib.ProgressMonitor;
36 import org.eclipse.jgit.lib.Ref;
37 import org.eclipse.jgit.lib.Repository;
38 import org.eclipse.jgit.revwalk.RevCommit;
39 import org.eclipse.jgit.revwalk.RevWalk;
40 import org.eclipse.jgit.transport.ReceiveCommand;
41
42
43 class RefTreeBatch extends BatchRefUpdate {
44 private final RefTreeDatabase refdb;
45 private Ref src;
46 private ObjectId parentCommitId;
47 private ObjectId parentTreeId;
48 private RefTree tree;
49 private PersonIdent author;
50 private ObjectId newCommitId;
51
52 RefTreeBatch(RefTreeDatabase refdb) {
53 super(refdb);
54 this.refdb = refdb;
55 }
56
57
58 @Override
59 public void execute(RevWalk rw, ProgressMonitor monitor)
60 throws IOException {
61 List<Command> todo = new ArrayList<>(getCommands().size());
62 for (ReceiveCommand c : getCommands()) {
63 if (!isAllowNonFastForwards()) {
64 if (c.getType() == UPDATE) {
65 c.updateType(rw);
66 }
67 if (c.getType() == UPDATE_NONFASTFORWARD) {
68 c.setResult(REJECTED_NONFASTFORWARD);
69 if (isAtomic()) {
70 ReceiveCommand.abort(getCommands());
71 return;
72 }
73 continue;
74 }
75 }
76 todo.add(new Command(rw, c));
77 }
78 init(rw);
79 execute(rw, todo);
80 }
81
82 void init(RevWalk rw) throws IOException {
83 src = refdb.getBootstrap().exactRef(refdb.getTxnCommitted());
84 if (src != null && src.getObjectId() != null) {
85 RevCommit c = rw.parseCommit(src.getObjectId());
86 parentCommitId = c;
87 parentTreeId = c.getTree();
88 tree = RefTree.read(rw.getObjectReader(), c.getTree());
89 } else {
90 parentCommitId = ObjectId.zeroId();
91 try (ObjectInserter.Formatter fmt = new ObjectInserter.Formatter()) {
92 parentTreeId = fmt.idFor(OBJ_TREE, new byte[] {});
93 }
94 tree = RefTree.newEmptyTree();
95 }
96 }
97
98 @Nullable
99 Ref exactRef(ObjectReader reader, String name) throws IOException {
100 return tree.exactRef(reader, name);
101 }
102
103
104
105
106
107
108
109
110
111
112
113 void execute(RevWalk rw, List<Command> todo) throws IOException {
114 for (Command c : todo) {
115 if (c.getResult() != NOT_ATTEMPTED) {
116 Command.abort(todo, null);
117 return;
118 }
119 if (refdb.conflictsWithBootstrap(c.getRefName())) {
120 c.setResult(REJECTED_OTHER_REASON, MessageFormat
121 .format(JGitText.get().invalidRefName, c.getRefName()));
122 Command.abort(todo, null);
123 return;
124 }
125 }
126
127 if (apply(todo) && newCommitId != null) {
128 commit(rw, todo);
129 }
130 }
131
132 private boolean apply(List<Command> todo) throws IOException {
133 if (!tree.apply(todo)) {
134
135 return false;
136 }
137
138 Repository repo = refdb.getRepository();
139 try (ObjectInserter ins = repo.newObjectInserter()) {
140 CommitBuilder b = new CommitBuilder();
141 b.setTreeId(tree.writeTree(ins));
142 if (parentTreeId.equals(b.getTreeId())) {
143 for (Command c : todo) {
144 c.setResult(OK);
145 }
146 return true;
147 }
148 if (!parentCommitId.equals(ObjectId.zeroId())) {
149 b.setParentId(parentCommitId);
150 }
151
152 author = getRefLogIdent();
153 if (author == null) {
154 author = new PersonIdent(repo);
155 }
156 b.setAuthor(author);
157 b.setCommitter(author);
158 b.setMessage(getRefLogMessage());
159 newCommitId = ins.insert(b);
160 ins.flush();
161 }
162 return true;
163 }
164
165 private void commit(RevWalk rw, List<Command> todo) throws IOException {
166 ReceiveCommand commit = new ReceiveCommand(
167 parentCommitId, newCommitId,
168 refdb.getTxnCommitted());
169 updateBootstrap(rw, commit);
170
171 if (commit.getResult() == OK) {
172 for (Command c : todo) {
173 c.setResult(OK);
174 }
175 } else {
176 Command.abort(todo, commit.getResult().name());
177 }
178 }
179
180 private void updateBootstrap(RevWalk rw, ReceiveCommand commit)
181 throws IOException {
182 BatchRefUpdate u = refdb.getBootstrap().newBatchUpdate();
183 u.setAllowNonFastForwards(true);
184 u.setPushCertificate(getPushCertificate());
185 if (isRefLogDisabled()) {
186 u.disableRefLog();
187 } else {
188 u.setRefLogIdent(author);
189 u.setRefLogMessage(getRefLogMessage(), false);
190 }
191 u.addCommand(commit);
192 u.execute(rw, NullProgressMonitor.INSTANCE);
193 }
194 }