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 parentTreeId = new ObjectInserter.Formatter()
92 .idFor(OBJ_TREE, new byte[] {});
93 tree = RefTree.newEmptyTree();
94 }
95 }
96
97 @Nullable
98 Ref exactRef(ObjectReader reader, String name) throws IOException {
99 return tree.exactRef(reader, name);
100 }
101
102
103
104
105
106
107
108
109
110
111
112 void execute(RevWalk rw, List<Command> todo) throws IOException {
113 for (Command c : todo) {
114 if (c.getResult() != NOT_ATTEMPTED) {
115 Command.abort(todo, null);
116 return;
117 }
118 if (refdb.conflictsWithBootstrap(c.getRefName())) {
119 c.setResult(REJECTED_OTHER_REASON, MessageFormat
120 .format(JGitText.get().invalidRefName, c.getRefName()));
121 Command.abort(todo, null);
122 return;
123 }
124 }
125
126 if (apply(todo) && newCommitId != null) {
127 commit(rw, todo);
128 }
129 }
130
131 private boolean apply(List<Command> todo) throws IOException {
132 if (!tree.apply(todo)) {
133
134 return false;
135 }
136
137 Repository repo = refdb.getRepository();
138 try (ObjectInserter ins = repo.newObjectInserter()) {
139 CommitBuilder b = new CommitBuilder();
140 b.setTreeId(tree.writeTree(ins));
141 if (parentTreeId.equals(b.getTreeId())) {
142 for (Command c : todo) {
143 c.setResult(OK);
144 }
145 return true;
146 }
147 if (!parentCommitId.equals(ObjectId.zeroId())) {
148 b.setParentId(parentCommitId);
149 }
150
151 author = getRefLogIdent();
152 if (author == null) {
153 author = new PersonIdent(repo);
154 }
155 b.setAuthor(author);
156 b.setCommitter(author);
157 b.setMessage(getRefLogMessage());
158 newCommitId = ins.insert(b);
159 ins.flush();
160 }
161 return true;
162 }
163
164 private void commit(RevWalk rw, List<Command> todo) throws IOException {
165 ReceiveCommand commit = new ReceiveCommand(
166 parentCommitId, newCommitId,
167 refdb.getTxnCommitted());
168 updateBootstrap(rw, commit);
169
170 if (commit.getResult() == OK) {
171 for (Command c : todo) {
172 c.setResult(OK);
173 }
174 } else {
175 Command.abort(todo, commit.getResult().name());
176 }
177 }
178
179 private void updateBootstrap(RevWalk rw, ReceiveCommand commit)
180 throws IOException {
181 BatchRefUpdate u = refdb.getBootstrap().newBatchUpdate();
182 u.setAllowNonFastForwards(true);
183 u.setPushCertificate(getPushCertificate());
184 if (isRefLogDisabled()) {
185 u.disableRefLog();
186 } else {
187 u.setRefLogIdent(author);
188 u.setRefLogMessage(getRefLogMessage(), false);
189 }
190 u.addCommand(commit);
191 u.execute(rw, NullProgressMonitor.INSTANCE);
192 }
193 }