Reject disconnected ancestries when creating changes
If we are asked to create new changes in a project, require that the new commit objects be connected to the existing commits already in the refs/heads/ or refs/tags/ namespaces of the destination. This is a simple check to catch users who mistype the URL on their push command line and send objects into the wrong repository by mistake. Suggested-in: http://groups.google.com/group/repo-discuss/browse_thread/thread/61bdc30dbc2c3d87 Signed-off-by: Shawn O. Pearce <sop@google.com> CC: Simon Wilkinson <simon@sxw.org.uk>
This commit is contained in:
parent
ebb4e64cfc
commit
5169e14558
@ -67,6 +67,7 @@ import org.spearce.jgit.lib.RefUpdate;
|
|||||||
import org.spearce.jgit.revwalk.FooterKey;
|
import org.spearce.jgit.revwalk.FooterKey;
|
||||||
import org.spearce.jgit.revwalk.FooterLine;
|
import org.spearce.jgit.revwalk.FooterLine;
|
||||||
import org.spearce.jgit.revwalk.RevCommit;
|
import org.spearce.jgit.revwalk.RevCommit;
|
||||||
|
import org.spearce.jgit.revwalk.RevFlag;
|
||||||
import org.spearce.jgit.revwalk.RevObject;
|
import org.spearce.jgit.revwalk.RevObject;
|
||||||
import org.spearce.jgit.revwalk.RevSort;
|
import org.spearce.jgit.revwalk.RevSort;
|
||||||
import org.spearce.jgit.revwalk.RevTag;
|
import org.spearce.jgit.revwalk.RevTag;
|
||||||
@ -195,7 +196,10 @@ final class Receive extends AbstractGitCommand {
|
|||||||
public void onPreReceive(final ReceivePack arg0,
|
public void onPreReceive(final ReceivePack arg0,
|
||||||
final Collection<ReceiveCommand> commands) {
|
final Collection<ReceiveCommand> commands) {
|
||||||
parseCommands(commands);
|
parseCommands(commands);
|
||||||
createNewChanges();
|
if (newChange != null
|
||||||
|
&& newChange.getResult() == ReceiveCommand.Result.NOT_ATTEMPTED) {
|
||||||
|
createNewChanges();
|
||||||
|
}
|
||||||
appendPatchSets();
|
appendPatchSets();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -527,6 +531,60 @@ final class Receive extends AbstractGitCommand {
|
|||||||
reject(cmd, "branch " + n + " not found");
|
reject(cmd, "branch " + n + " not found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate that the new commits are connected with the existing heads
|
||||||
|
// or tags of this repository. If they aren't, we want to abort. We do
|
||||||
|
// this check by coloring the tip CONNECTED and letting a RevWalk push
|
||||||
|
// that color through the graph until it reaches at least one of our
|
||||||
|
// already existing heads or tags. We then test to see if that color
|
||||||
|
// made it back onto that set.
|
||||||
|
//
|
||||||
|
try {
|
||||||
|
final RevWalk walk = rp.getRevWalk();
|
||||||
|
final RevFlag CONNECTED = walk.newFlag("CONNECTED");
|
||||||
|
walk.carry(CONNECTED);
|
||||||
|
walk.reset();
|
||||||
|
walk.sort(RevSort.TOPO);
|
||||||
|
walk.sort(RevSort.REVERSE, true);
|
||||||
|
|
||||||
|
final RevCommit tip = walk.parseCommit(newChange.getNewId());
|
||||||
|
tip.add(CONNECTED);
|
||||||
|
walk.markStart(tip);
|
||||||
|
|
||||||
|
final List<RevCommit> heads = new ArrayList<RevCommit>();
|
||||||
|
for (final Ref r : rp.getAdvertisedRefs().values()) {
|
||||||
|
if (isHead(r) || isTag(r)) {
|
||||||
|
try {
|
||||||
|
final RevCommit h = walk.parseCommit(r.getObjectId());
|
||||||
|
walk.markUninteresting(h);
|
||||||
|
heads.add(h);
|
||||||
|
} catch (IOException e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!heads.isEmpty()) {
|
||||||
|
while (walk.next() != null) {
|
||||||
|
/* nothing, just pump the RevWalk until its done */
|
||||||
|
}
|
||||||
|
boolean isConnected = false;
|
||||||
|
for (final RevCommit c : heads) {
|
||||||
|
if (c.has(CONNECTED)) {
|
||||||
|
isConnected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isConnected) {
|
||||||
|
reject(newChange, "no common ancestry");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
newChange.setResult(Result.REJECTED_MISSING_OBJECT);
|
||||||
|
log.error("Invalid pack upload; one or more objects weren't sent", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseNewPatchSetCommand(final ReceiveCommand cmd,
|
private void parseNewPatchSetCommand(final ReceiveCommand cmd,
|
||||||
@ -572,11 +630,6 @@ final class Receive extends AbstractGitCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void createNewChanges() {
|
private void createNewChanges() {
|
||||||
if (newChange == null
|
|
||||||
|| newChange.getResult() != ReceiveCommand.Result.NOT_ATTEMPTED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<RevCommit> toCreate = new ArrayList<RevCommit>();
|
final List<RevCommit> toCreate = new ArrayList<RevCommit>();
|
||||||
final RevWalk walk = rp.getRevWalk();
|
final RevWalk walk = rp.getRevWalk();
|
||||||
walk.reset();
|
walk.reset();
|
||||||
@ -603,6 +656,8 @@ final class Receive extends AbstractGitCommand {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!validCommitter(newChange, c)) {
|
if (!validCommitter(newChange, c)) {
|
||||||
|
// Not a change the user can propose? Abort as early as possible.
|
||||||
|
//
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
toCreate.add(c);
|
toCreate.add(c);
|
||||||
@ -1254,6 +1309,10 @@ final class Receive extends AbstractGitCommand {
|
|||||||
cmd.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, why);
|
cmd.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, why);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isTag(final Ref ref) {
|
||||||
|
return ref.getName().startsWith(Constants.R_TAGS);
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isTag(final ReceiveCommand cmd) {
|
private static boolean isTag(final ReceiveCommand cmd) {
|
||||||
return cmd.getRefName().startsWith(Constants.R_TAGS);
|
return cmd.getRefName().startsWith(Constants.R_TAGS);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user