mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Delete the SnapshotRepositoryBuilder and WLBridgedProject abominations. Set accessed time after update and push.
This commit is contained in:
parent
c459cd57af
commit
dd5694104d
63 changed files with 1383 additions and 260 deletions
|
@ -3,6 +3,8 @@ package uk.ac.ic.wlgitbridge.bridge;
|
|||
import com.google.api.client.auth.oauth2.Credential;
|
||||
import org.eclipse.jgit.transport.ServiceMayNotContinueException;
|
||||
import uk.ac.ic.wlgitbridge.bridge.db.DBStore;
|
||||
import uk.ac.ic.wlgitbridge.bridge.db.ProjectState;
|
||||
import uk.ac.ic.wlgitbridge.bridge.lock.LockGuard;
|
||||
import uk.ac.ic.wlgitbridge.bridge.lock.ProjectLock;
|
||||
import uk.ac.ic.wlgitbridge.bridge.repo.RepoStore;
|
||||
import uk.ac.ic.wlgitbridge.bridge.resource.ResourceCache;
|
||||
|
@ -30,6 +32,8 @@ import uk.ac.ic.wlgitbridge.snapshot.push.exception.*;
|
|||
import uk.ac.ic.wlgitbridge.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
|
@ -110,62 +114,109 @@ public class Bridge {
|
|||
swapJob.start();
|
||||
}
|
||||
|
||||
/* TODO: Remove these when WLBridged is moved into RepoStore */
|
||||
public void lockForProject(String projectName) {
|
||||
lock.lockForProject(projectName);
|
||||
}
|
||||
|
||||
public void unlockForProject(String projectName) {
|
||||
lock.unlockForProject(projectName);
|
||||
}
|
||||
|
||||
public boolean repositoryExists(Credential oauth2, String projectName)
|
||||
throws ServiceMayNotContinueException, GitUserException {
|
||||
lockForProject(projectName);
|
||||
GetDocRequest getDocRequest = new GetDocRequest(oauth2, projectName);
|
||||
getDocRequest.request();
|
||||
try {
|
||||
public boolean projectExists(
|
||||
Credential oauth2,
|
||||
String projectName
|
||||
) throws ServiceMayNotContinueException,
|
||||
GitUserException {
|
||||
try (LockGuard __ = lock.lockGuard(projectName)) {
|
||||
GetDocRequest getDocRequest = new GetDocRequest(
|
||||
oauth2,
|
||||
projectName
|
||||
);
|
||||
getDocRequest.request();
|
||||
getDocRequest.getResult().getVersionID();
|
||||
return true;
|
||||
} catch (InvalidProjectException e) {
|
||||
return false;
|
||||
} finally {
|
||||
unlockForProject(projectName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void getWritableRepositories(
|
||||
public void updateRepository(
|
||||
Credential oauth2,
|
||||
ProjectRepo repo
|
||||
) throws IOException,
|
||||
GitUserException {
|
||||
Log.info("[{}] Fetching", repo.getProjectName());
|
||||
updateProjectWithName(oauth2, repo);
|
||||
) throws IOException, GitUserException {
|
||||
String projectName = repo.getProjectName();
|
||||
try (LockGuard __ = lock.lockGuard(projectName)) {
|
||||
Log.info("[{}] Updating", projectName);
|
||||
updateRepositoryCritical(oauth2, repo);
|
||||
}
|
||||
}
|
||||
|
||||
public void
|
||||
putDirectoryContentsToProjectWithName(Credential oauth2,
|
||||
String projectName,
|
||||
RawDirectory directoryContents,
|
||||
RawDirectory oldDirectoryContents,
|
||||
String hostname)
|
||||
throws SnapshotPostException, IOException, ForbiddenException {
|
||||
lock.lockForProject(projectName);
|
||||
CandidateSnapshot candidate = null;
|
||||
try {
|
||||
Log.info("[{}] Pushing", projectName);
|
||||
String postbackKey = postbackManager.makeKeyForProject(projectName);
|
||||
Log.info(
|
||||
"[{}] Created postback key: {}",
|
||||
private void updateRepositoryCritical(
|
||||
Credential oauth2,
|
||||
ProjectRepo repo
|
||||
) throws IOException, GitUserException {
|
||||
String projectName = repo.getProjectName();
|
||||
ProjectState state = dbStore.getProjectState(projectName);
|
||||
switch (state) {
|
||||
case NOT_PRESENT:
|
||||
repo.initRepo(repoStore);
|
||||
break;
|
||||
case SWAPPED:
|
||||
swapJob.restore(projectName);
|
||||
/* Fallthrough */
|
||||
default:
|
||||
repo.useExistingRepository(repoStore);
|
||||
}
|
||||
updateProject(oauth2, repo);
|
||||
dbStore.setLastAccessedTime(
|
||||
projectName,
|
||||
Timestamp.valueOf(LocalDateTime.now())
|
||||
);
|
||||
}
|
||||
|
||||
public void putDirectoryContentsToProjectWithName(
|
||||
Credential oauth2,
|
||||
String projectName,
|
||||
RawDirectory directoryContents,
|
||||
RawDirectory oldDirectoryContents,
|
||||
String hostname
|
||||
) throws SnapshotPostException, IOException, ForbiddenException {
|
||||
try (LockGuard __ = lock.lockGuard(projectName)) {
|
||||
pushToProjectCritical(
|
||||
oauth2,
|
||||
projectName,
|
||||
postbackKey
|
||||
directoryContents,
|
||||
oldDirectoryContents
|
||||
);
|
||||
candidate =
|
||||
createCandidateSnapshot(
|
||||
projectName,
|
||||
directoryContents,
|
||||
oldDirectoryContents
|
||||
);
|
||||
} catch (SevereSnapshotPostException e) {
|
||||
Log.warn("[" + projectName + "] Failed to put to Overleaf", e);
|
||||
throw e;
|
||||
} catch (SnapshotPostException e) {
|
||||
/* Stack trace should be printed further up */
|
||||
Log.warn(
|
||||
"[{}] Exception when waiting for postback: {}",
|
||||
projectName,
|
||||
e.getClass().getSimpleName()
|
||||
);
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
Log.warn("[{}] IOException on put", projectName);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void pushToProjectCritical(
|
||||
Credential oauth2,
|
||||
String projectName,
|
||||
RawDirectory directoryContents,
|
||||
RawDirectory oldDirectoryContents
|
||||
) throws IOException, ForbiddenException, SnapshotPostException {
|
||||
Log.info("[{}] Pushing", projectName);
|
||||
String postbackKey = postbackManager.makeKeyForProject(projectName);
|
||||
Log.info(
|
||||
"[{}] Created postback key: {}",
|
||||
projectName,
|
||||
postbackKey
|
||||
);
|
||||
try (
|
||||
CandidateSnapshot candidate = createCandidateSnapshot(
|
||||
projectName,
|
||||
directoryContents,
|
||||
oldDirectoryContents
|
||||
);
|
||||
) {
|
||||
Log.info(
|
||||
"[{}] Candindate snapshot created: {}",
|
||||
projectName,
|
||||
|
@ -197,6 +248,10 @@ public class Bridge {
|
|||
projectName,
|
||||
versionID
|
||||
);
|
||||
dbStore.setLastAccessedTime(
|
||||
projectName,
|
||||
Timestamp.valueOf(LocalDateTime.now())
|
||||
);
|
||||
} else {
|
||||
Log.warn(
|
||||
"[{}] Went out of date while waiting for push",
|
||||
|
@ -204,31 +259,6 @@ public class Bridge {
|
|||
);
|
||||
throw new OutOfDateException();
|
||||
}
|
||||
} catch (SevereSnapshotPostException e) {
|
||||
Log.warn("[" + projectName + "] Failed to put to Overleaf", e);
|
||||
throw e;
|
||||
} catch (SnapshotPostException e) {
|
||||
/* Stack trace should be printed further up */
|
||||
Log.warn(
|
||||
"[{}] Exception when waiting for postback: {}",
|
||||
projectName,
|
||||
e.getClass().getSimpleName()
|
||||
);
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
Log.warn("[{}] IOException on put", projectName);
|
||||
throw e;
|
||||
} finally {
|
||||
if (candidate != null) {
|
||||
candidate.deleteServletFiles();
|
||||
} else {
|
||||
Log.error(
|
||||
"[{}] Candidate snapshot was null: " +
|
||||
"this should never happen.",
|
||||
projectName
|
||||
);
|
||||
}
|
||||
lock.unlockForProject(projectName);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,7 +298,7 @@ public class Bridge {
|
|||
|
||||
/* PRIVATE */
|
||||
|
||||
private void updateProjectWithName(
|
||||
private void updateProject(
|
||||
Credential oauth2,
|
||||
ProjectRepo repo
|
||||
) throws IOException, GitUserException {
|
||||
|
|
|
@ -1,33 +1,36 @@
|
|||
package uk.ac.ic.wlgitbridge.bridge;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.lib.PersonIdent;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
|
||||
import uk.ac.ic.wlgitbridge.bridge.repo.RepoStore;
|
||||
import uk.ac.ic.wlgitbridge.data.filestore.GitDirectoryContents;
|
||||
import uk.ac.ic.wlgitbridge.data.filestore.RawFile;
|
||||
import uk.ac.ic.wlgitbridge.git.exception.GitUserException;
|
||||
import uk.ac.ic.wlgitbridge.git.util.RepositoryObjectTreeWalker;
|
||||
import uk.ac.ic.wlgitbridge.util.Log;
|
||||
import uk.ac.ic.wlgitbridge.util.Project;
|
||||
import uk.ac.ic.wlgitbridge.util.Util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Created by winston on 20/08/2016.
|
||||
*/
|
||||
public class GitProjectRepo implements ProjectRepo {
|
||||
|
||||
private final Repository repository;
|
||||
private final String projectName;
|
||||
private Optional<Repository> repository;
|
||||
|
||||
public GitProjectRepo(Repository repository, String projectName) {
|
||||
this.repository = repository;
|
||||
public GitProjectRepo(String projectName) {
|
||||
Preconditions.checkArgument(Project.isValidProjectName(projectName));
|
||||
this.projectName = projectName;
|
||||
repository = Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -35,11 +38,34 @@ public class GitProjectRepo implements ProjectRepo {
|
|||
return projectName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initRepo(
|
||||
RepoStore repoStore
|
||||
) throws IOException {
|
||||
initRepositoryField(repoStore);
|
||||
Preconditions.checkState(repository.isPresent());
|
||||
Repository repo = this.repository.get();
|
||||
Preconditions.checkState(!repo.getObjectDatabase().exists());
|
||||
repo.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void useExistingRepository(
|
||||
RepoStore repoStore
|
||||
) throws IOException {
|
||||
initRepositoryField(repoStore);
|
||||
Preconditions.checkState(repository.isPresent());
|
||||
Preconditions.checkState(
|
||||
repository.get().getObjectDatabase().exists()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, RawFile> getFiles()
|
||||
throws IOException, GitUserException {
|
||||
Preconditions.checkState(repository.isPresent());
|
||||
return new RepositoryObjectTreeWalker(
|
||||
repository
|
||||
repository.get()
|
||||
).getDirectoryContents().getFileTable();
|
||||
}
|
||||
|
||||
|
@ -54,13 +80,33 @@ public class GitProjectRepo implements ProjectRepo {
|
|||
}
|
||||
}
|
||||
|
||||
public Repository getJGitRepository() {
|
||||
return repository.get();
|
||||
}
|
||||
|
||||
private void initRepositoryField(RepoStore repoStore) throws IOException {
|
||||
Preconditions.checkNotNull(repoStore);
|
||||
Preconditions.checkArgument(Project.isValidProjectName(projectName));
|
||||
Preconditions.checkState(!repository.isPresent());
|
||||
repository = Optional.of(createJGitRepository(repoStore, projectName));
|
||||
}
|
||||
|
||||
private Repository createJGitRepository(
|
||||
RepoStore repoStore,
|
||||
String projName
|
||||
) throws IOException {
|
||||
File repoDir = new File(repoStore.getRootDirectory(), projName);
|
||||
return new FileRepositoryBuilder().setWorkTree(repoDir).build();
|
||||
}
|
||||
|
||||
private Collection<String> doCommitAndGetMissing(
|
||||
GitDirectoryContents contents
|
||||
) throws IOException, GitAPIException {
|
||||
Preconditions.checkState(repository.isPresent());
|
||||
String name = getProjectName();
|
||||
Log.info("[{}] Writing commit", name);
|
||||
contents.write();
|
||||
Git git = new Git(repository);
|
||||
Git git = new Git(repository.get());
|
||||
Log.info("[{}] Getting missing files", name);
|
||||
Set<String> missingFiles = git.status().call().getMissing();
|
||||
for (String missing : missingFiles) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package uk.ac.ic.wlgitbridge.bridge;
|
||||
|
||||
import uk.ac.ic.wlgitbridge.bridge.repo.RepoStore;
|
||||
import uk.ac.ic.wlgitbridge.data.filestore.GitDirectoryContents;
|
||||
import uk.ac.ic.wlgitbridge.data.filestore.RawFile;
|
||||
import uk.ac.ic.wlgitbridge.git.exception.GitUserException;
|
||||
|
@ -15,6 +16,14 @@ public interface ProjectRepo {
|
|||
|
||||
String getProjectName();
|
||||
|
||||
void initRepo(
|
||||
RepoStore repoStore
|
||||
) throws IOException;
|
||||
|
||||
void useExistingRepository(
|
||||
RepoStore repoStore
|
||||
) throws IOException;
|
||||
|
||||
Map<String, RawFile> getFiles() throws IOException, GitUserException;
|
||||
|
||||
Collection<String> commitAndGetMissing(
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
package uk.ac.ic.wlgitbridge.bridge;
|
||||
|
||||
import com.google.api.client.auth.oauth2.Credential;
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.transport.ServiceMayNotContinueException;
|
||||
import uk.ac.ic.wlgitbridge.git.exception.GitUserException;
|
||||
import uk.ac.ic.wlgitbridge.snapshot.getdoc.exception.InvalidProjectException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by Winston on 05/11/14.
|
||||
*/
|
||||
public class WLBridgedProject {
|
||||
|
||||
private final Repository repository;
|
||||
private final String name;
|
||||
private final Bridge bridgeAPI;
|
||||
|
||||
public WLBridgedProject(Repository repository, String name, Bridge bridgeAPI) {
|
||||
this.repository = repository;
|
||||
this.name = name;
|
||||
this.bridgeAPI = bridgeAPI;
|
||||
}
|
||||
|
||||
public void buildRepository(Credential oauth2) throws RepositoryNotFoundException, ServiceMayNotContinueException, GitUserException {
|
||||
bridgeAPI.lockForProject(name);
|
||||
try {
|
||||
if (repository.getObjectDatabase().exists()) {
|
||||
updateRepositoryFromSnapshots(oauth2, repository);
|
||||
} else {
|
||||
buildRepositoryFromScratch(oauth2, repository);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
throw new ServiceMayNotContinueException(e);
|
||||
} finally {
|
||||
bridgeAPI.unlockForProject(name);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRepositoryFromSnapshots(
|
||||
Credential oauth2,
|
||||
Repository repository
|
||||
) throws RepositoryNotFoundException,
|
||||
ServiceMayNotContinueException,
|
||||
GitUserException {
|
||||
try {
|
||||
bridgeAPI.getWritableRepositories(
|
||||
oauth2,
|
||||
new GitProjectRepo(repository, name)
|
||||
);
|
||||
} catch (InvalidProjectException e) {
|
||||
throw new RepositoryNotFoundException(name);
|
||||
} catch (IOException e) {
|
||||
throw new ServiceMayNotContinueException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildRepositoryFromScratch(Credential oauth2, Repository repository) throws RepositoryNotFoundException, ServiceMayNotContinueException, GitUserException {
|
||||
if (!bridgeAPI.repositoryExists(oauth2, name)) {
|
||||
throw new RepositoryNotFoundException(name);
|
||||
}
|
||||
try {
|
||||
repository.create();
|
||||
} catch (IOException e) {
|
||||
throw new ServiceMayNotContinueException(e);
|
||||
}
|
||||
updateRepositoryFromSnapshots(oauth2, repository);
|
||||
}
|
||||
|
||||
}
|
|
@ -26,6 +26,8 @@ public interface DBStore {
|
|||
|
||||
int getNumUnswappedProjects();
|
||||
|
||||
ProjectState getProjectState(String projectName);
|
||||
|
||||
/**
|
||||
* Sets the last accessed time for the given project name.
|
||||
* @param projectName the project's name
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package uk.ac.ic.wlgitbridge.bridge.db;
|
||||
|
||||
/**
|
||||
* Created by winston on 24/08/2016.
|
||||
*/
|
||||
public enum ProjectState {
|
||||
|
||||
NOT_PRESENT,
|
||||
PRESENT,
|
||||
SWAPPED
|
||||
|
||||
}
|
|
@ -2,6 +2,7 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite;
|
|||
|
||||
import uk.ac.ic.wlgitbridge.bridge.db.DBInitException;
|
||||
import uk.ac.ic.wlgitbridge.bridge.db.DBStore;
|
||||
import uk.ac.ic.wlgitbridge.bridge.db.ProjectState;
|
||||
import uk.ac.ic.wlgitbridge.bridge.db.sqlite.query.*;
|
||||
import uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.create.*;
|
||||
import uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.delete.DeleteFilesForProjectSQLUpdate;
|
||||
|
@ -91,6 +92,11 @@ public class SqliteDBStore implements DBStore {
|
|||
return query(new GetNumUnswappedProjects());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectState getProjectState(String projectName) {
|
||||
return query(new GetProjectState(projectName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastAccessedTime(
|
||||
String projectName,
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package uk.ac.ic.wlgitbridge.bridge.db.sqlite.query;
|
||||
|
||||
import uk.ac.ic.wlgitbridge.bridge.db.ProjectState;
|
||||
import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Created by winston on 24/08/2016.
|
||||
*/
|
||||
public class GetProjectState implements SQLQuery<ProjectState> {
|
||||
|
||||
private static final String GET_PROJECT_STATE =
|
||||
"SELECT `last_accessed`\n" +
|
||||
" FROM `swap_table`\n" +
|
||||
" WHERE `project_name` = ?";
|
||||
|
||||
private final String projectName;
|
||||
|
||||
public GetProjectState(String projectName) {
|
||||
this.projectName = projectName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSQL() {
|
||||
return GET_PROJECT_STATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectState processResultSet(ResultSet resultSet) throws SQLException {
|
||||
while (resultSet.next()) {
|
||||
if (resultSet.getTimestamp("last_accessed") == null) {
|
||||
return ProjectState.SWAPPED;
|
||||
}
|
||||
return ProjectState.PRESENT;
|
||||
}
|
||||
return ProjectState.NOT_PRESENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addParametersToStatement(PreparedStatement statement) throws SQLException {
|
||||
statement.setString(1, projectName);
|
||||
}
|
||||
|
||||
}
|
|
@ -18,12 +18,12 @@ import static uk.ac.ic.wlgitbridge.util.Util.deleteInDirectoryApartFrom;
|
|||
/**
|
||||
* Created by winston on 20/08/2016.
|
||||
*/
|
||||
public class FSRepoStore implements RepoStore {
|
||||
public class FSGitRepoStore implements RepoStore {
|
||||
|
||||
private final String repoStorePath;
|
||||
private final File rootDirectory;
|
||||
|
||||
public FSRepoStore(String repoStorePath) {
|
||||
public FSGitRepoStore(String repoStorePath) {
|
||||
this.repoStorePath = repoStorePath;
|
||||
rootDirectory = initRootGitDirectory(repoStorePath);
|
||||
}
|
|
@ -17,7 +17,7 @@ import java.util.Map.Entry;
|
|||
/**
|
||||
* Created by Winston on 16/11/14.
|
||||
*/
|
||||
public class CandidateSnapshot {
|
||||
public class CandidateSnapshot implements AutoCloseable {
|
||||
|
||||
private final String projectName;
|
||||
private final int currentVersion;
|
||||
|
@ -25,14 +25,22 @@ public class CandidateSnapshot {
|
|||
private final List<String> deleted;
|
||||
private File attsDirectory;
|
||||
|
||||
public CandidateSnapshot(String projectName, int currentVersion, RawDirectory directoryContents, RawDirectory oldDirectoryContents) {
|
||||
public CandidateSnapshot(
|
||||
String projectName,
|
||||
int currentVersion,
|
||||
RawDirectory directoryContents,
|
||||
RawDirectory oldDirectoryContents
|
||||
) {
|
||||
this.projectName = projectName;
|
||||
this.currentVersion = currentVersion;
|
||||
files = diff(directoryContents, oldDirectoryContents);
|
||||
deleted = deleted(directoryContents, oldDirectoryContents);
|
||||
}
|
||||
|
||||
private List<ServletFile> diff(RawDirectory directoryContents, RawDirectory oldDirectoryContents) {
|
||||
private List<ServletFile> diff(
|
||||
RawDirectory directoryContents,
|
||||
RawDirectory oldDirectoryContents
|
||||
) {
|
||||
List<ServletFile> files = new LinkedList<ServletFile>();
|
||||
Map<String, RawFile> fileTable = directoryContents.getFileTable();
|
||||
Map<String, RawFile> oldFileTable = oldDirectoryContents.getFileTable();
|
||||
|
@ -43,10 +51,16 @@ public class CandidateSnapshot {
|
|||
return files;
|
||||
}
|
||||
|
||||
private List<String> deleted(RawDirectory directoryContents, RawDirectory oldDirectoryContents) {
|
||||
private List<String> deleted(
|
||||
RawDirectory directoryContents,
|
||||
RawDirectory oldDirectoryContents
|
||||
) {
|
||||
List<String> deleted = new LinkedList<String>();
|
||||
Map<String, RawFile> fileTable = directoryContents.getFileTable();
|
||||
for (Entry<String, RawFile> entry : oldDirectoryContents.getFileTable().entrySet()) {
|
||||
for (
|
||||
Entry<String, RawFile> entry :
|
||||
oldDirectoryContents.getFileTable().entrySet()
|
||||
) {
|
||||
String path = entry.getKey();
|
||||
RawFile newFile = fileTable.get(path);
|
||||
if (newFile == null) {
|
||||
|
@ -57,7 +71,10 @@ public class CandidateSnapshot {
|
|||
}
|
||||
|
||||
public void writeServletFiles(File rootGitDirectory) throws IOException {
|
||||
attsDirectory = new File(rootGitDirectory, ".wlgb/atts/" + projectName);
|
||||
attsDirectory = new File(
|
||||
rootGitDirectory,
|
||||
".wlgb/atts/" + projectName
|
||||
);
|
||||
for (ServletFile file : files) {
|
||||
if (file.isChanged()) {
|
||||
file.writeToDisk(attsDirectory);
|
||||
|
@ -90,11 +107,18 @@ public class CandidateSnapshot {
|
|||
return filesArray;
|
||||
}
|
||||
|
||||
private JsonObject getFileAsJson(ServletFile file, String projectURL, String postbackKey) {
|
||||
private JsonObject getFileAsJson(
|
||||
ServletFile file,
|
||||
String projectURL,
|
||||
String postbackKey
|
||||
) {
|
||||
JsonObject jsonFile = new JsonObject();
|
||||
jsonFile.addProperty("name", file.getPath());
|
||||
if (file.isChanged()) {
|
||||
jsonFile.addProperty("url", projectURL + "/" + file.getPath() + "?key=" + postbackKey);
|
||||
jsonFile.addProperty(
|
||||
"url",
|
||||
projectURL + "/" + file.getPath() + "?key=" + postbackKey
|
||||
);
|
||||
}
|
||||
return jsonFile;
|
||||
}
|
||||
|
@ -119,4 +143,9 @@ public class CandidateSnapshot {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
deleteServletFiles();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,11 +33,13 @@ public class ProjectLockImpl implements ProjectLock {
|
|||
setWaiter(waiter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lockForProject(String projectName) {
|
||||
getLockForProjectName(projectName).lock();
|
||||
rlock.lock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlockForProject(String projectName) {
|
||||
getLockForProjectName(projectName).unlock();
|
||||
rlock.unlock();
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
package uk.ac.ic.wlgitbridge.data;
|
||||
|
||||
import com.google.api.client.auth.oauth2.Credential;
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
|
||||
import org.eclipse.jgit.transport.ServiceMayNotContinueException;
|
||||
import uk.ac.ic.wlgitbridge.bridge.Bridge;
|
||||
import uk.ac.ic.wlgitbridge.bridge.WLBridgedProject;
|
||||
import uk.ac.ic.wlgitbridge.git.exception.GitUserException;
|
||||
import uk.ac.ic.wlgitbridge.snapshot.push.exception.InternalErrorException;
|
||||
import uk.ac.ic.wlgitbridge.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by Winston on 03/11/14.
|
||||
*/
|
||||
public class SnapshotRepositoryBuilder {
|
||||
|
||||
private final Bridge bridgeAPI;
|
||||
|
||||
public SnapshotRepositoryBuilder(Bridge bridgeAPI) {
|
||||
this.bridgeAPI = bridgeAPI;
|
||||
}
|
||||
|
||||
public Repository getRepositoryWithNameAtRootDirectory(String name, File rootDirectory, Credential oauth2) throws RepositoryNotFoundException, ServiceMayNotContinueException, GitUserException {
|
||||
if (!bridgeAPI.repositoryExists(oauth2, name)) {
|
||||
throw new RepositoryNotFoundException(name);
|
||||
}
|
||||
File repositoryDirectory = new File(rootDirectory, name);
|
||||
|
||||
Repository repository = null;
|
||||
try {
|
||||
repository = new FileRepositoryBuilder().setWorkTree(repositoryDirectory).build();
|
||||
new WLBridgedProject(repository, name, bridgeAPI).buildRepository(oauth2);
|
||||
} catch (IOException e) {
|
||||
Log.warn(
|
||||
"IOException when trying to get repo: " +
|
||||
name +
|
||||
", at: "
|
||||
+ rootDirectory.getAbsolutePath(),
|
||||
e
|
||||
);
|
||||
throw new ServiceMayNotContinueException(
|
||||
new InternalErrorException().getDescriptionLines().get(0)
|
||||
);
|
||||
}
|
||||
return repository;
|
||||
}
|
||||
|
||||
}
|
|
@ -7,35 +7,48 @@ import org.eclipse.jgit.transport.ServiceMayNotContinueException;
|
|||
import org.eclipse.jgit.transport.resolver.RepositoryResolver;
|
||||
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
|
||||
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
|
||||
import uk.ac.ic.wlgitbridge.data.SnapshotRepositoryBuilder;
|
||||
import uk.ac.ic.wlgitbridge.bridge.Bridge;
|
||||
import uk.ac.ic.wlgitbridge.bridge.GitProjectRepo;
|
||||
import uk.ac.ic.wlgitbridge.git.exception.GitUserException;
|
||||
import uk.ac.ic.wlgitbridge.git.exception.InvalidRootDirectoryPathException;
|
||||
import uk.ac.ic.wlgitbridge.server.Oauth2Filter;
|
||||
import uk.ac.ic.wlgitbridge.snapshot.base.ForbiddenException;
|
||||
import uk.ac.ic.wlgitbridge.util.Log;
|
||||
import uk.ac.ic.wlgitbridge.util.Util;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by Winston on 02/11/14.
|
||||
*/
|
||||
public class WLRepositoryResolver implements RepositoryResolver<HttpServletRequest> {
|
||||
public class WLRepositoryResolver
|
||||
implements RepositoryResolver<HttpServletRequest> {
|
||||
|
||||
private File rootGitDirectory;
|
||||
private SnapshotRepositoryBuilder snapshotRepositoryBuilder;
|
||||
private final Bridge bridge;
|
||||
|
||||
public WLRepositoryResolver(String rootGitDirectoryPath, SnapshotRepositoryBuilder repositorySource) throws InvalidRootDirectoryPathException {
|
||||
this.snapshotRepositoryBuilder = repositorySource;
|
||||
initRootGitDirectory(rootGitDirectoryPath);
|
||||
public WLRepositoryResolver(Bridge bridge) {
|
||||
this.bridge = bridge;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Repository open(HttpServletRequest httpServletRequest, String name) throws RepositoryNotFoundException, ServiceNotAuthorizedException, ServiceNotEnabledException, ServiceMayNotContinueException {
|
||||
Credential oauth2 = (Credential) httpServletRequest.getAttribute(Oauth2Filter.ATTRIBUTE_KEY);
|
||||
public Repository open(
|
||||
HttpServletRequest httpServletRequest,
|
||||
String name
|
||||
) throws RepositoryNotFoundException,
|
||||
ServiceNotAuthorizedException,
|
||||
ServiceNotEnabledException,
|
||||
ServiceMayNotContinueException {
|
||||
Credential oauth2 = (Credential) httpServletRequest.getAttribute(
|
||||
Oauth2Filter.ATTRIBUTE_KEY
|
||||
);
|
||||
String projName = Util.removeAllSuffixes(name, "/", ".git");
|
||||
try {
|
||||
return snapshotRepositoryBuilder.getRepositoryWithNameAtRootDirectory(Util.removeAllSuffixes(name, "/", ".git"), rootGitDirectory, oauth2);
|
||||
if (!bridge.projectExists(oauth2, projName)) {
|
||||
throw new RepositoryNotFoundException(projName);
|
||||
}
|
||||
GitProjectRepo repo = new GitProjectRepo(projName);
|
||||
bridge.updateRepository(oauth2, repo);
|
||||
return repo.getJGitRepository();
|
||||
} catch (RepositoryNotFoundException e) {
|
||||
Log.info("Repository not found: " + name);
|
||||
throw e;
|
||||
|
@ -45,7 +58,8 @@ public class WLRepositoryResolver implements RepositoryResolver<HttpServletReque
|
|||
} catch (ServiceNotEnabledException e) {
|
||||
cannot occur
|
||||
*/
|
||||
} catch (ServiceMayNotContinueException e) { /* Such as FailedConnectionException */
|
||||
} catch (ServiceMayNotContinueException e) {
|
||||
/* Such as FailedConnectionException */
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
Log.warn("Runtime exception when trying to open repo", e);
|
||||
|
@ -54,16 +68,9 @@ public class WLRepositoryResolver implements RepositoryResolver<HttpServletReque
|
|||
throw new ServiceNotAuthorizedException();
|
||||
} catch (GitUserException e) {
|
||||
throw new ServiceMayNotContinueException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void initRootGitDirectory(String rootGitDirectoryPath) throws InvalidRootDirectoryPathException {
|
||||
rootGitDirectory = new File(rootGitDirectoryPath);
|
||||
/* throws SecurityException */
|
||||
rootGitDirectory.mkdirs();
|
||||
rootGitDirectory.getAbsolutePath();
|
||||
if (!rootGitDirectory.isDirectory()) {
|
||||
throw new InvalidRootDirectoryPathException();
|
||||
} catch (IOException e) {
|
||||
Log.warn("IOException when trying to open repo", e);
|
||||
throw new ServiceMayNotContinueException("Internal server error.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,12 +2,11 @@ package uk.ac.ic.wlgitbridge.git.servlet;
|
|||
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jgit.http.server.GitServlet;
|
||||
import uk.ac.ic.wlgitbridge.bridge.Bridge;
|
||||
import uk.ac.ic.wlgitbridge.git.exception.InvalidRootDirectoryPathException;
|
||||
import uk.ac.ic.wlgitbridge.git.handler.WLReceivePackFactory;
|
||||
import uk.ac.ic.wlgitbridge.git.handler.WLRepositoryResolver;
|
||||
import uk.ac.ic.wlgitbridge.git.handler.WLUploadPackFactory;
|
||||
import uk.ac.ic.wlgitbridge.data.SnapshotRepositoryBuilder;
|
||||
import uk.ac.ic.wlgitbridge.bridge.Bridge;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
|
@ -16,11 +15,14 @@ import javax.servlet.ServletException;
|
|||
*/
|
||||
public class WLGitServlet extends GitServlet {
|
||||
|
||||
public WLGitServlet(ServletContextHandler servletContextHandler, Bridge bridgeAPI, String rootGitDirectoryPath) throws ServletException, InvalidRootDirectoryPathException {
|
||||
setRepositoryResolver(new WLRepositoryResolver(rootGitDirectoryPath, new SnapshotRepositoryBuilder(bridgeAPI)));
|
||||
setReceivePackFactory(new WLReceivePackFactory(bridgeAPI));
|
||||
public WLGitServlet(
|
||||
ServletContextHandler ctxHandler,
|
||||
Bridge bridge
|
||||
) throws ServletException, InvalidRootDirectoryPathException {
|
||||
setRepositoryResolver(new WLRepositoryResolver(bridge));
|
||||
setReceivePackFactory(new WLReceivePackFactory(bridge));
|
||||
setUploadPackFactory(new WLUploadPackFactory());
|
||||
init(new WLGitServletConfig(servletContextHandler));
|
||||
init(new WLGitServletConfig(ctxHandler));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ public class WLGitServletConfig implements ServletConfig {
|
|||
|
||||
private ServletContext servletContext;
|
||||
|
||||
public WLGitServletConfig(ServletContextHandler servletContextHandler) {
|
||||
servletContext = servletContextHandler.getServletContext();
|
||||
public WLGitServletConfig(ServletContextHandler ctxHandler) {
|
||||
servletContext = ctxHandler.getServletContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -11,7 +11,7 @@ import uk.ac.ic.wlgitbridge.application.jetty.NullLogger;
|
|||
import uk.ac.ic.wlgitbridge.bridge.Bridge;
|
||||
import uk.ac.ic.wlgitbridge.bridge.db.DBStore;
|
||||
import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SqliteDBStore;
|
||||
import uk.ac.ic.wlgitbridge.bridge.repo.FSRepoStore;
|
||||
import uk.ac.ic.wlgitbridge.bridge.repo.FSGitRepoStore;
|
||||
import uk.ac.ic.wlgitbridge.bridge.repo.RepoStore;
|
||||
import uk.ac.ic.wlgitbridge.bridge.swap.store.SwapStore;
|
||||
import uk.ac.ic.wlgitbridge.git.exception.InvalidRootDirectoryPathException;
|
||||
|
@ -51,7 +51,7 @@ public class GitBridgeServer {
|
|||
org.eclipse.jetty.util.log.Log.setLog(new NullLogger());
|
||||
this.port = config.getPort();
|
||||
this.rootGitDirectoryPath = config.getRootGitDirectory();
|
||||
RepoStore repoStore = new FSRepoStore(rootGitDirectoryPath);
|
||||
RepoStore repoStore = new FSGitRepoStore(rootGitDirectoryPath);
|
||||
DBStore dbStore = new SqliteDBStore(
|
||||
Paths.get(
|
||||
repoStore.getRootDirectory().getAbsolutePath()
|
||||
|
@ -143,8 +143,7 @@ public class GitBridgeServer {
|
|||
new ServletHolder(
|
||||
new WLGitServlet(
|
||||
servletContextHandler,
|
||||
bridgeAPI,
|
||||
rootGitDirectoryPath
|
||||
bridgeAPI
|
||||
)
|
||||
),
|
||||
"/*"
|
||||
|
|
|
@ -3,6 +3,7 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import uk.ac.ic.wlgitbridge.bridge.db.ProjectState;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Timestamp;
|
||||
|
@ -147,4 +148,29 @@ public class SqliteDBStoreTest {
|
|||
assertEquals(0, dbStore.getNumUnswappedProjects());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void projectStateIsNotPresentIfNotInDBAtAll() {
|
||||
assertEquals(
|
||||
ProjectState.NOT_PRESENT,
|
||||
dbStore.getProjectState("asdf")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void projectStateIsPresentIfProjectHasLastAccessed() {
|
||||
dbStore.setLatestVersionForProject("asdf", 1);
|
||||
dbStore.setLastAccessedTime(
|
||||
"asdf",
|
||||
Timestamp.valueOf(LocalDateTime.now())
|
||||
);
|
||||
assertEquals(ProjectState.PRESENT, dbStore.getProjectState("asdf"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void projectStateIsSwappedIfLastAccessedIsNull() {
|
||||
dbStore.setLatestVersionForProject("asdf", 1);
|
||||
dbStore.setLastAccessedTime("asdf", null);
|
||||
assertEquals(ProjectState.SWAPPED, dbStore.getProjectState("asdf"));
|
||||
}
|
||||
|
||||
}
|
|
@ -19,7 +19,7 @@ import static org.junit.Assert.assertEquals;
|
|||
/**
|
||||
* Created by winston on 23/08/2016.
|
||||
*/
|
||||
public class FSRepoStoreTest {
|
||||
public class FSGitRepoStoreTest {
|
||||
|
||||
public static File makeTempRepoDir(
|
||||
TemporaryFolder tmpFolder,
|
||||
|
@ -28,14 +28,14 @@ public class FSRepoStoreTest {
|
|||
File tmp = tmpFolder.newFolder(name);
|
||||
Path rootdir = Paths.get(
|
||||
"src/test/resources/uk/ac/ic/wlgitbridge/"
|
||||
+ "bridge/repo/FSRepoStoreTest/rootdir"
|
||||
+ "bridge/repo/FSGitRepoStoreTest/rootdir"
|
||||
);
|
||||
FileUtils.copyDirectory(rootdir.toFile(), tmp);
|
||||
Files.renameAll(tmp, "DOTgit", ".git");
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private FSRepoStore repoStore;
|
||||
private FSGitRepoStore repoStore;
|
||||
private File original;
|
||||
|
||||
@Before
|
||||
|
@ -45,7 +45,7 @@ public class FSRepoStoreTest {
|
|||
File tmp = makeTempRepoDir(tmpFolder, "rootdir");
|
||||
original = tmpFolder.newFolder("original");
|
||||
FileUtils.copyDirectory(tmp, original);
|
||||
repoStore = new FSRepoStore(tmp.getAbsolutePath());
|
||||
repoStore = new FSGitRepoStore(tmp.getAbsolutePath());
|
||||
}
|
||||
|
||||
@Test
|
|
@ -6,8 +6,8 @@ import org.junit.rules.TemporaryFolder;
|
|||
import uk.ac.ic.wlgitbridge.bridge.db.DBStore;
|
||||
import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SqliteDBStore;
|
||||
import uk.ac.ic.wlgitbridge.bridge.lock.ProjectLock;
|
||||
import uk.ac.ic.wlgitbridge.bridge.repo.FSRepoStore;
|
||||
import uk.ac.ic.wlgitbridge.bridge.repo.FSRepoStoreTest;
|
||||
import uk.ac.ic.wlgitbridge.bridge.repo.FSGitRepoStore;
|
||||
import uk.ac.ic.wlgitbridge.bridge.repo.FSGitRepoStoreTest;
|
||||
import uk.ac.ic.wlgitbridge.bridge.repo.RepoStore;
|
||||
import uk.ac.ic.wlgitbridge.bridge.swap.store.InMemorySwapStore;
|
||||
import uk.ac.ic.wlgitbridge.bridge.swap.store.SwapStore;
|
||||
|
@ -39,8 +39,8 @@ public class SwapJobImplTest {
|
|||
TemporaryFolder tmpFolder = new TemporaryFolder();
|
||||
tmpFolder.create();
|
||||
lock = new ProjectLockImpl();
|
||||
repoStore = new FSRepoStore(
|
||||
FSRepoStoreTest.makeTempRepoDir(
|
||||
repoStore = new FSGitRepoStore(
|
||||
FSGitRepoStoreTest.makeTempRepoDir(
|
||||
tmpFolder,
|
||||
"repostore"
|
||||
).getAbsolutePath()
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Main
|
|
@ -0,0 +1 @@
|
|||
ref: refs/heads/master
|
|
@ -0,0 +1,7 @@
|
|||
[core]
|
||||
repositoryformatversion = 0
|
||||
filemode = true
|
||||
bare = false
|
||||
logallrefupdates = true
|
||||
ignorecase = true
|
||||
precomposeunicode = true
|
|
@ -0,0 +1 @@
|
|||
Unnamed repository; edit this file 'description' to name the repository.
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to check the commit log message taken by
|
||||
# applypatch from an e-mail message.
|
||||
#
|
||||
# The hook should exit with non-zero status after issuing an
|
||||
# appropriate message if it wants to stop the commit. The hook is
|
||||
# allowed to edit the commit message file.
|
||||
#
|
||||
# To enable this hook, rename this file to "applypatch-msg".
|
||||
|
||||
. git-sh-setup
|
||||
commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
|
||||
test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
|
||||
:
|
|
@ -0,0 +1,24 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to check the commit log message.
|
||||
# Called by "git commit" with one argument, the name of the file
|
||||
# that has the commit message. The hook should exit with non-zero
|
||||
# status after issuing an appropriate message if it wants to stop the
|
||||
# commit. The hook is allowed to edit the commit message file.
|
||||
#
|
||||
# To enable this hook, rename this file to "commit-msg".
|
||||
|
||||
# Uncomment the below to add a Signed-off-by line to the message.
|
||||
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
|
||||
# hook is more suited to it.
|
||||
#
|
||||
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
|
||||
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
|
||||
|
||||
# This example catches duplicate Signed-off-by lines.
|
||||
|
||||
test "" = "$(grep '^Signed-off-by: ' "$1" |
|
||||
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
|
||||
echo >&2 Duplicate Signed-off-by lines.
|
||||
exit 1
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to prepare a packed repository for use over
|
||||
# dumb transports.
|
||||
#
|
||||
# To enable this hook, rename this file to "post-update".
|
||||
|
||||
exec git update-server-info
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to verify what is about to be committed
|
||||
# by applypatch from an e-mail message.
|
||||
#
|
||||
# The hook should exit with non-zero status after issuing an
|
||||
# appropriate message if it wants to stop the commit.
|
||||
#
|
||||
# To enable this hook, rename this file to "pre-applypatch".
|
||||
|
||||
. git-sh-setup
|
||||
precommit="$(git rev-parse --git-path hooks/pre-commit)"
|
||||
test -x "$precommit" && exec "$precommit" ${1+"$@"}
|
||||
:
|
|
@ -0,0 +1,49 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to verify what is about to be committed.
|
||||
# Called by "git commit" with no arguments. The hook should
|
||||
# exit with non-zero status after issuing an appropriate message if
|
||||
# it wants to stop the commit.
|
||||
#
|
||||
# To enable this hook, rename this file to "pre-commit".
|
||||
|
||||
if git rev-parse --verify HEAD >/dev/null 2>&1
|
||||
then
|
||||
against=HEAD
|
||||
else
|
||||
# Initial commit: diff against an empty tree object
|
||||
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
|
||||
fi
|
||||
|
||||
# If you want to allow non-ASCII filenames set this variable to true.
|
||||
allownonascii=$(git config --bool hooks.allownonascii)
|
||||
|
||||
# Redirect output to stderr.
|
||||
exec 1>&2
|
||||
|
||||
# Cross platform projects tend to avoid non-ASCII filenames; prevent
|
||||
# them from being added to the repository. We exploit the fact that the
|
||||
# printable range starts at the space character and ends with tilde.
|
||||
if [ "$allownonascii" != "true" ] &&
|
||||
# Note that the use of brackets around a tr range is ok here, (it's
|
||||
# even required, for portability to Solaris 10's /usr/bin/tr), since
|
||||
# the square bracket bytes happen to fall in the designated range.
|
||||
test $(git diff --cached --name-only --diff-filter=A -z $against |
|
||||
LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
|
||||
then
|
||||
cat <<\EOF
|
||||
Error: Attempt to add a non-ASCII file name.
|
||||
|
||||
This can cause problems if you want to work with people on other platforms.
|
||||
|
||||
To be portable it is advisable to rename the file.
|
||||
|
||||
If you know what you are doing you can disable this check using:
|
||||
|
||||
git config hooks.allownonascii true
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If there are whitespace errors, print the offending file names and fail.
|
||||
exec git diff-index --check --cached $against --
|
|
@ -0,0 +1,53 @@
|
|||
#!/bin/sh
|
||||
|
||||
# An example hook script to verify what is about to be pushed. Called by "git
|
||||
# push" after it has checked the remote status, but before anything has been
|
||||
# pushed. If this script exits with a non-zero status nothing will be pushed.
|
||||
#
|
||||
# This hook is called with the following parameters:
|
||||
#
|
||||
# $1 -- Name of the remote to which the push is being done
|
||||
# $2 -- URL to which the push is being done
|
||||
#
|
||||
# If pushing without using a named remote those arguments will be equal.
|
||||
#
|
||||
# Information about the commits which are being pushed is supplied as lines to
|
||||
# the standard input in the form:
|
||||
#
|
||||
# <local ref> <local sha1> <remote ref> <remote sha1>
|
||||
#
|
||||
# This sample shows how to prevent push of commits where the log message starts
|
||||
# with "WIP" (work in progress).
|
||||
|
||||
remote="$1"
|
||||
url="$2"
|
||||
|
||||
z40=0000000000000000000000000000000000000000
|
||||
|
||||
while read local_ref local_sha remote_ref remote_sha
|
||||
do
|
||||
if [ "$local_sha" = $z40 ]
|
||||
then
|
||||
# Handle delete
|
||||
:
|
||||
else
|
||||
if [ "$remote_sha" = $z40 ]
|
||||
then
|
||||
# New branch, examine all commits
|
||||
range="$local_sha"
|
||||
else
|
||||
# Update to existing branch, examine new commits
|
||||
range="$remote_sha..$local_sha"
|
||||
fi
|
||||
|
||||
# Check for WIP commit
|
||||
commit=`git rev-list -n 1 --grep '^WIP' "$range"`
|
||||
if [ -n "$commit" ]
|
||||
then
|
||||
echo >&2 "Found WIP commit in $local_ref, not pushing"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,169 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2006, 2008 Junio C Hamano
|
||||
#
|
||||
# The "pre-rebase" hook is run just before "git rebase" starts doing
|
||||
# its job, and can prevent the command from running by exiting with
|
||||
# non-zero status.
|
||||
#
|
||||
# The hook is called with the following parameters:
|
||||
#
|
||||
# $1 -- the upstream the series was forked from.
|
||||
# $2 -- the branch being rebased (or empty when rebasing the current branch).
|
||||
#
|
||||
# This sample shows how to prevent topic branches that are already
|
||||
# merged to 'next' branch from getting rebased, because allowing it
|
||||
# would result in rebasing already published history.
|
||||
|
||||
publish=next
|
||||
basebranch="$1"
|
||||
if test "$#" = 2
|
||||
then
|
||||
topic="refs/heads/$2"
|
||||
else
|
||||
topic=`git symbolic-ref HEAD` ||
|
||||
exit 0 ;# we do not interrupt rebasing detached HEAD
|
||||
fi
|
||||
|
||||
case "$topic" in
|
||||
refs/heads/??/*)
|
||||
;;
|
||||
*)
|
||||
exit 0 ;# we do not interrupt others.
|
||||
;;
|
||||
esac
|
||||
|
||||
# Now we are dealing with a topic branch being rebased
|
||||
# on top of master. Is it OK to rebase it?
|
||||
|
||||
# Does the topic really exist?
|
||||
git show-ref -q "$topic" || {
|
||||
echo >&2 "No such branch $topic"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Is topic fully merged to master?
|
||||
not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
|
||||
if test -z "$not_in_master"
|
||||
then
|
||||
echo >&2 "$topic is fully merged to master; better remove it."
|
||||
exit 1 ;# we could allow it, but there is no point.
|
||||
fi
|
||||
|
||||
# Is topic ever merged to next? If so you should not be rebasing it.
|
||||
only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
|
||||
only_next_2=`git rev-list ^master ${publish} | sort`
|
||||
if test "$only_next_1" = "$only_next_2"
|
||||
then
|
||||
not_in_topic=`git rev-list "^$topic" master`
|
||||
if test -z "$not_in_topic"
|
||||
then
|
||||
echo >&2 "$topic is already up-to-date with master"
|
||||
exit 1 ;# we could allow it, but there is no point.
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
|
||||
/usr/bin/perl -e '
|
||||
my $topic = $ARGV[0];
|
||||
my $msg = "* $topic has commits already merged to public branch:\n";
|
||||
my (%not_in_next) = map {
|
||||
/^([0-9a-f]+) /;
|
||||
($1 => 1);
|
||||
} split(/\n/, $ARGV[1]);
|
||||
for my $elem (map {
|
||||
/^([0-9a-f]+) (.*)$/;
|
||||
[$1 => $2];
|
||||
} split(/\n/, $ARGV[2])) {
|
||||
if (!exists $not_in_next{$elem->[0]}) {
|
||||
if ($msg) {
|
||||
print STDERR $msg;
|
||||
undef $msg;
|
||||
}
|
||||
print STDERR " $elem->[1]\n";
|
||||
}
|
||||
}
|
||||
' "$topic" "$not_in_next" "$not_in_master"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
################################################################
|
||||
|
||||
This sample hook safeguards topic branches that have been
|
||||
published from being rewound.
|
||||
|
||||
The workflow assumed here is:
|
||||
|
||||
* Once a topic branch forks from "master", "master" is never
|
||||
merged into it again (either directly or indirectly).
|
||||
|
||||
* Once a topic branch is fully cooked and merged into "master",
|
||||
it is deleted. If you need to build on top of it to correct
|
||||
earlier mistakes, a new topic branch is created by forking at
|
||||
the tip of the "master". This is not strictly necessary, but
|
||||
it makes it easier to keep your history simple.
|
||||
|
||||
* Whenever you need to test or publish your changes to topic
|
||||
branches, merge them into "next" branch.
|
||||
|
||||
The script, being an example, hardcodes the publish branch name
|
||||
to be "next", but it is trivial to make it configurable via
|
||||
$GIT_DIR/config mechanism.
|
||||
|
||||
With this workflow, you would want to know:
|
||||
|
||||
(1) ... if a topic branch has ever been merged to "next". Young
|
||||
topic branches can have stupid mistakes you would rather
|
||||
clean up before publishing, and things that have not been
|
||||
merged into other branches can be easily rebased without
|
||||
affecting other people. But once it is published, you would
|
||||
not want to rewind it.
|
||||
|
||||
(2) ... if a topic branch has been fully merged to "master".
|
||||
Then you can delete it. More importantly, you should not
|
||||
build on top of it -- other people may already want to
|
||||
change things related to the topic as patches against your
|
||||
"master", so if you need further changes, it is better to
|
||||
fork the topic (perhaps with the same name) afresh from the
|
||||
tip of "master".
|
||||
|
||||
Let's look at this example:
|
||||
|
||||
o---o---o---o---o---o---o---o---o---o "next"
|
||||
/ / / /
|
||||
/ a---a---b A / /
|
||||
/ / / /
|
||||
/ / c---c---c---c B /
|
||||
/ / / \ /
|
||||
/ / / b---b C \ /
|
||||
/ / / / \ /
|
||||
---o---o---o---o---o---o---o---o---o---o---o "master"
|
||||
|
||||
|
||||
A, B and C are topic branches.
|
||||
|
||||
* A has one fix since it was merged up to "next".
|
||||
|
||||
* B has finished. It has been fully merged up to "master" and "next",
|
||||
and is ready to be deleted.
|
||||
|
||||
* C has not merged to "next" at all.
|
||||
|
||||
We would want to allow C to be rebased, refuse A, and encourage
|
||||
B to be deleted.
|
||||
|
||||
To compute (1):
|
||||
|
||||
git rev-list ^master ^topic next
|
||||
git rev-list ^master next
|
||||
|
||||
if these match, topic has not merged in next at all.
|
||||
|
||||
To compute (2):
|
||||
|
||||
git rev-list master..topic
|
||||
|
||||
if this is empty, it is fully merged to "master".
|
|
@ -0,0 +1,36 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to prepare the commit log message.
|
||||
# Called by "git commit" with the name of the file that has the
|
||||
# commit message, followed by the description of the commit
|
||||
# message's source. The hook's purpose is to edit the commit
|
||||
# message file. If the hook fails with a non-zero status,
|
||||
# the commit is aborted.
|
||||
#
|
||||
# To enable this hook, rename this file to "prepare-commit-msg".
|
||||
|
||||
# This hook includes three examples. The first comments out the
|
||||
# "Conflicts:" part of a merge commit.
|
||||
#
|
||||
# The second includes the output of "git diff --name-status -r"
|
||||
# into the message, just before the "git status" output. It is
|
||||
# commented because it doesn't cope with --amend or with squashed
|
||||
# commits.
|
||||
#
|
||||
# The third example adds a Signed-off-by line to the message, that can
|
||||
# still be edited. This is rarely a good idea.
|
||||
|
||||
case "$2,$3" in
|
||||
merge,)
|
||||
/usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
|
||||
|
||||
# ,|template,)
|
||||
# /usr/bin/perl -i.bak -pe '
|
||||
# print "\n" . `git diff --cached --name-status -r`
|
||||
# if /^#/ && $first++ == 0' "$1" ;;
|
||||
|
||||
*) ;;
|
||||
esac
|
||||
|
||||
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
|
||||
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
|
|
@ -0,0 +1,128 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to block unannotated tags from entering.
|
||||
# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
|
||||
#
|
||||
# To enable this hook, rename this file to "update".
|
||||
#
|
||||
# Config
|
||||
# ------
|
||||
# hooks.allowunannotated
|
||||
# This boolean sets whether unannotated tags will be allowed into the
|
||||
# repository. By default they won't be.
|
||||
# hooks.allowdeletetag
|
||||
# This boolean sets whether deleting tags will be allowed in the
|
||||
# repository. By default they won't be.
|
||||
# hooks.allowmodifytag
|
||||
# This boolean sets whether a tag may be modified after creation. By default
|
||||
# it won't be.
|
||||
# hooks.allowdeletebranch
|
||||
# This boolean sets whether deleting branches will be allowed in the
|
||||
# repository. By default they won't be.
|
||||
# hooks.denycreatebranch
|
||||
# This boolean sets whether remotely creating branches will be denied
|
||||
# in the repository. By default this is allowed.
|
||||
#
|
||||
|
||||
# --- Command line
|
||||
refname="$1"
|
||||
oldrev="$2"
|
||||
newrev="$3"
|
||||
|
||||
# --- Safety check
|
||||
if [ -z "$GIT_DIR" ]; then
|
||||
echo "Don't run this script from the command line." >&2
|
||||
echo " (if you want, you could supply GIT_DIR then run" >&2
|
||||
echo " $0 <ref> <oldrev> <newrev>)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
|
||||
echo "usage: $0 <ref> <oldrev> <newrev>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Config
|
||||
allowunannotated=$(git config --bool hooks.allowunannotated)
|
||||
allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
|
||||
denycreatebranch=$(git config --bool hooks.denycreatebranch)
|
||||
allowdeletetag=$(git config --bool hooks.allowdeletetag)
|
||||
allowmodifytag=$(git config --bool hooks.allowmodifytag)
|
||||
|
||||
# check for no description
|
||||
projectdesc=$(sed -e '1q' "$GIT_DIR/description")
|
||||
case "$projectdesc" in
|
||||
"Unnamed repository"* | "")
|
||||
echo "*** Project description file hasn't been set" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# --- Check types
|
||||
# if $newrev is 0000...0000, it's a commit to delete a ref.
|
||||
zero="0000000000000000000000000000000000000000"
|
||||
if [ "$newrev" = "$zero" ]; then
|
||||
newrev_type=delete
|
||||
else
|
||||
newrev_type=$(git cat-file -t $newrev)
|
||||
fi
|
||||
|
||||
case "$refname","$newrev_type" in
|
||||
refs/tags/*,commit)
|
||||
# un-annotated tag
|
||||
short_refname=${refname##refs/tags/}
|
||||
if [ "$allowunannotated" != "true" ]; then
|
||||
echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
|
||||
echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/tags/*,delete)
|
||||
# delete tag
|
||||
if [ "$allowdeletetag" != "true" ]; then
|
||||
echo "*** Deleting a tag is not allowed in this repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/tags/*,tag)
|
||||
# annotated tag
|
||||
if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
|
||||
then
|
||||
echo "*** Tag '$refname' already exists." >&2
|
||||
echo "*** Modifying a tag is not allowed in this repository." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/heads/*,commit)
|
||||
# branch
|
||||
if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
|
||||
echo "*** Creating a branch is not allowed in this repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/heads/*,delete)
|
||||
# delete branch
|
||||
if [ "$allowdeletebranch" != "true" ]; then
|
||||
echo "*** Deleting a branch is not allowed in this repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/remotes/*,commit)
|
||||
# tracking branch
|
||||
;;
|
||||
refs/remotes/*,delete)
|
||||
# delete tracking branch
|
||||
if [ "$allowdeletebranch" != "true" ]; then
|
||||
echo "*** Deleting a tracking branch is not allowed in this repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
# Anything else (is there anything else?)
|
||||
echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# --- Finished
|
||||
exit 0
|
Binary file not shown.
|
@ -0,0 +1,6 @@
|
|||
# git ls-files --others --exclude-from=.git/info/exclude
|
||||
# Lines that start with '#' are comments.
|
||||
# For a project mostly in C, the following would be a good set of
|
||||
# exclude patterns (uncomment them if you want to use them):
|
||||
# *.[oa]
|
||||
# *~
|
|
@ -0,0 +1 @@
|
|||
0000000000000000000000000000000000000000 e5fc0d2678ec7b9bacf0bf514bac035fa371cb6e Winston Li <git@winston.li> 1471957665 +0100 commit (initial): Main
|
|
@ -0,0 +1 @@
|
|||
0000000000000000000000000000000000000000 e5fc0d2678ec7b9bacf0bf514bac035fa371cb6e Winston Li <git@winston.li> 1471957665 +0100 commit (initial): Main
|
Binary file not shown.
|
@ -0,0 +1,2 @@
|
|||
x•ÍA
|
||||
1@Q×=Eö‚4ÛZ™èÚuì$pZ#s}AOàò/¿öyVŒec3HÈp™ƒx‰—é@B·€Yx"fJ„•ÙÑÛ}<7D>«¶—õg…ã]m\½{ê pŸ±ÄœR„Gï]ýÎŒÿdîBÚÜëþ4&
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
e5fc0d2678ec7b9bacf0bf514bac035fa371cb6e
|
|
@ -0,0 +1 @@
|
|||
Main
|
|
@ -0,0 +1 @@
|
|||
ref: refs/heads/master
|
|
@ -0,0 +1,7 @@
|
|||
[core]
|
||||
repositoryformatversion = 0
|
||||
filemode = true
|
||||
bare = false
|
||||
logallrefupdates = true
|
||||
ignorecase = true
|
||||
precomposeunicode = true
|
|
@ -0,0 +1 @@
|
|||
Unnamed repository; edit this file 'description' to name the repository.
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to check the commit log message taken by
|
||||
# applypatch from an e-mail message.
|
||||
#
|
||||
# The hook should exit with non-zero status after issuing an
|
||||
# appropriate message if it wants to stop the commit. The hook is
|
||||
# allowed to edit the commit message file.
|
||||
#
|
||||
# To enable this hook, rename this file to "applypatch-msg".
|
||||
|
||||
. git-sh-setup
|
||||
commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
|
||||
test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
|
||||
:
|
|
@ -0,0 +1,24 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to check the commit log message.
|
||||
# Called by "git commit" with one argument, the name of the file
|
||||
# that has the commit message. The hook should exit with non-zero
|
||||
# status after issuing an appropriate message if it wants to stop the
|
||||
# commit. The hook is allowed to edit the commit message file.
|
||||
#
|
||||
# To enable this hook, rename this file to "commit-msg".
|
||||
|
||||
# Uncomment the below to add a Signed-off-by line to the message.
|
||||
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
|
||||
# hook is more suited to it.
|
||||
#
|
||||
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
|
||||
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
|
||||
|
||||
# This example catches duplicate Signed-off-by lines.
|
||||
|
||||
test "" = "$(grep '^Signed-off-by: ' "$1" |
|
||||
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
|
||||
echo >&2 Duplicate Signed-off-by lines.
|
||||
exit 1
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to prepare a packed repository for use over
|
||||
# dumb transports.
|
||||
#
|
||||
# To enable this hook, rename this file to "post-update".
|
||||
|
||||
exec git update-server-info
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to verify what is about to be committed
|
||||
# by applypatch from an e-mail message.
|
||||
#
|
||||
# The hook should exit with non-zero status after issuing an
|
||||
# appropriate message if it wants to stop the commit.
|
||||
#
|
||||
# To enable this hook, rename this file to "pre-applypatch".
|
||||
|
||||
. git-sh-setup
|
||||
precommit="$(git rev-parse --git-path hooks/pre-commit)"
|
||||
test -x "$precommit" && exec "$precommit" ${1+"$@"}
|
||||
:
|
|
@ -0,0 +1,49 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to verify what is about to be committed.
|
||||
# Called by "git commit" with no arguments. The hook should
|
||||
# exit with non-zero status after issuing an appropriate message if
|
||||
# it wants to stop the commit.
|
||||
#
|
||||
# To enable this hook, rename this file to "pre-commit".
|
||||
|
||||
if git rev-parse --verify HEAD >/dev/null 2>&1
|
||||
then
|
||||
against=HEAD
|
||||
else
|
||||
# Initial commit: diff against an empty tree object
|
||||
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
|
||||
fi
|
||||
|
||||
# If you want to allow non-ASCII filenames set this variable to true.
|
||||
allownonascii=$(git config --bool hooks.allownonascii)
|
||||
|
||||
# Redirect output to stderr.
|
||||
exec 1>&2
|
||||
|
||||
# Cross platform projects tend to avoid non-ASCII filenames; prevent
|
||||
# them from being added to the repository. We exploit the fact that the
|
||||
# printable range starts at the space character and ends with tilde.
|
||||
if [ "$allownonascii" != "true" ] &&
|
||||
# Note that the use of brackets around a tr range is ok here, (it's
|
||||
# even required, for portability to Solaris 10's /usr/bin/tr), since
|
||||
# the square bracket bytes happen to fall in the designated range.
|
||||
test $(git diff --cached --name-only --diff-filter=A -z $against |
|
||||
LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
|
||||
then
|
||||
cat <<\EOF
|
||||
Error: Attempt to add a non-ASCII file name.
|
||||
|
||||
This can cause problems if you want to work with people on other platforms.
|
||||
|
||||
To be portable it is advisable to rename the file.
|
||||
|
||||
If you know what you are doing you can disable this check using:
|
||||
|
||||
git config hooks.allownonascii true
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If there are whitespace errors, print the offending file names and fail.
|
||||
exec git diff-index --check --cached $against --
|
|
@ -0,0 +1,53 @@
|
|||
#!/bin/sh
|
||||
|
||||
# An example hook script to verify what is about to be pushed. Called by "git
|
||||
# push" after it has checked the remote status, but before anything has been
|
||||
# pushed. If this script exits with a non-zero status nothing will be pushed.
|
||||
#
|
||||
# This hook is called with the following parameters:
|
||||
#
|
||||
# $1 -- Name of the remote to which the push is being done
|
||||
# $2 -- URL to which the push is being done
|
||||
#
|
||||
# If pushing without using a named remote those arguments will be equal.
|
||||
#
|
||||
# Information about the commits which are being pushed is supplied as lines to
|
||||
# the standard input in the form:
|
||||
#
|
||||
# <local ref> <local sha1> <remote ref> <remote sha1>
|
||||
#
|
||||
# This sample shows how to prevent push of commits where the log message starts
|
||||
# with "WIP" (work in progress).
|
||||
|
||||
remote="$1"
|
||||
url="$2"
|
||||
|
||||
z40=0000000000000000000000000000000000000000
|
||||
|
||||
while read local_ref local_sha remote_ref remote_sha
|
||||
do
|
||||
if [ "$local_sha" = $z40 ]
|
||||
then
|
||||
# Handle delete
|
||||
:
|
||||
else
|
||||
if [ "$remote_sha" = $z40 ]
|
||||
then
|
||||
# New branch, examine all commits
|
||||
range="$local_sha"
|
||||
else
|
||||
# Update to existing branch, examine new commits
|
||||
range="$remote_sha..$local_sha"
|
||||
fi
|
||||
|
||||
# Check for WIP commit
|
||||
commit=`git rev-list -n 1 --grep '^WIP' "$range"`
|
||||
if [ -n "$commit" ]
|
||||
then
|
||||
echo >&2 "Found WIP commit in $local_ref, not pushing"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,169 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2006, 2008 Junio C Hamano
|
||||
#
|
||||
# The "pre-rebase" hook is run just before "git rebase" starts doing
|
||||
# its job, and can prevent the command from running by exiting with
|
||||
# non-zero status.
|
||||
#
|
||||
# The hook is called with the following parameters:
|
||||
#
|
||||
# $1 -- the upstream the series was forked from.
|
||||
# $2 -- the branch being rebased (or empty when rebasing the current branch).
|
||||
#
|
||||
# This sample shows how to prevent topic branches that are already
|
||||
# merged to 'next' branch from getting rebased, because allowing it
|
||||
# would result in rebasing already published history.
|
||||
|
||||
publish=next
|
||||
basebranch="$1"
|
||||
if test "$#" = 2
|
||||
then
|
||||
topic="refs/heads/$2"
|
||||
else
|
||||
topic=`git symbolic-ref HEAD` ||
|
||||
exit 0 ;# we do not interrupt rebasing detached HEAD
|
||||
fi
|
||||
|
||||
case "$topic" in
|
||||
refs/heads/??/*)
|
||||
;;
|
||||
*)
|
||||
exit 0 ;# we do not interrupt others.
|
||||
;;
|
||||
esac
|
||||
|
||||
# Now we are dealing with a topic branch being rebased
|
||||
# on top of master. Is it OK to rebase it?
|
||||
|
||||
# Does the topic really exist?
|
||||
git show-ref -q "$topic" || {
|
||||
echo >&2 "No such branch $topic"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Is topic fully merged to master?
|
||||
not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
|
||||
if test -z "$not_in_master"
|
||||
then
|
||||
echo >&2 "$topic is fully merged to master; better remove it."
|
||||
exit 1 ;# we could allow it, but there is no point.
|
||||
fi
|
||||
|
||||
# Is topic ever merged to next? If so you should not be rebasing it.
|
||||
only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
|
||||
only_next_2=`git rev-list ^master ${publish} | sort`
|
||||
if test "$only_next_1" = "$only_next_2"
|
||||
then
|
||||
not_in_topic=`git rev-list "^$topic" master`
|
||||
if test -z "$not_in_topic"
|
||||
then
|
||||
echo >&2 "$topic is already up-to-date with master"
|
||||
exit 1 ;# we could allow it, but there is no point.
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
|
||||
/usr/bin/perl -e '
|
||||
my $topic = $ARGV[0];
|
||||
my $msg = "* $topic has commits already merged to public branch:\n";
|
||||
my (%not_in_next) = map {
|
||||
/^([0-9a-f]+) /;
|
||||
($1 => 1);
|
||||
} split(/\n/, $ARGV[1]);
|
||||
for my $elem (map {
|
||||
/^([0-9a-f]+) (.*)$/;
|
||||
[$1 => $2];
|
||||
} split(/\n/, $ARGV[2])) {
|
||||
if (!exists $not_in_next{$elem->[0]}) {
|
||||
if ($msg) {
|
||||
print STDERR $msg;
|
||||
undef $msg;
|
||||
}
|
||||
print STDERR " $elem->[1]\n";
|
||||
}
|
||||
}
|
||||
' "$topic" "$not_in_next" "$not_in_master"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
################################################################
|
||||
|
||||
This sample hook safeguards topic branches that have been
|
||||
published from being rewound.
|
||||
|
||||
The workflow assumed here is:
|
||||
|
||||
* Once a topic branch forks from "master", "master" is never
|
||||
merged into it again (either directly or indirectly).
|
||||
|
||||
* Once a topic branch is fully cooked and merged into "master",
|
||||
it is deleted. If you need to build on top of it to correct
|
||||
earlier mistakes, a new topic branch is created by forking at
|
||||
the tip of the "master". This is not strictly necessary, but
|
||||
it makes it easier to keep your history simple.
|
||||
|
||||
* Whenever you need to test or publish your changes to topic
|
||||
branches, merge them into "next" branch.
|
||||
|
||||
The script, being an example, hardcodes the publish branch name
|
||||
to be "next", but it is trivial to make it configurable via
|
||||
$GIT_DIR/config mechanism.
|
||||
|
||||
With this workflow, you would want to know:
|
||||
|
||||
(1) ... if a topic branch has ever been merged to "next". Young
|
||||
topic branches can have stupid mistakes you would rather
|
||||
clean up before publishing, and things that have not been
|
||||
merged into other branches can be easily rebased without
|
||||
affecting other people. But once it is published, you would
|
||||
not want to rewind it.
|
||||
|
||||
(2) ... if a topic branch has been fully merged to "master".
|
||||
Then you can delete it. More importantly, you should not
|
||||
build on top of it -- other people may already want to
|
||||
change things related to the topic as patches against your
|
||||
"master", so if you need further changes, it is better to
|
||||
fork the topic (perhaps with the same name) afresh from the
|
||||
tip of "master".
|
||||
|
||||
Let's look at this example:
|
||||
|
||||
o---o---o---o---o---o---o---o---o---o "next"
|
||||
/ / / /
|
||||
/ a---a---b A / /
|
||||
/ / / /
|
||||
/ / c---c---c---c B /
|
||||
/ / / \ /
|
||||
/ / / b---b C \ /
|
||||
/ / / / \ /
|
||||
---o---o---o---o---o---o---o---o---o---o---o "master"
|
||||
|
||||
|
||||
A, B and C are topic branches.
|
||||
|
||||
* A has one fix since it was merged up to "next".
|
||||
|
||||
* B has finished. It has been fully merged up to "master" and "next",
|
||||
and is ready to be deleted.
|
||||
|
||||
* C has not merged to "next" at all.
|
||||
|
||||
We would want to allow C to be rebased, refuse A, and encourage
|
||||
B to be deleted.
|
||||
|
||||
To compute (1):
|
||||
|
||||
git rev-list ^master ^topic next
|
||||
git rev-list ^master next
|
||||
|
||||
if these match, topic has not merged in next at all.
|
||||
|
||||
To compute (2):
|
||||
|
||||
git rev-list master..topic
|
||||
|
||||
if this is empty, it is fully merged to "master".
|
|
@ -0,0 +1,36 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to prepare the commit log message.
|
||||
# Called by "git commit" with the name of the file that has the
|
||||
# commit message, followed by the description of the commit
|
||||
# message's source. The hook's purpose is to edit the commit
|
||||
# message file. If the hook fails with a non-zero status,
|
||||
# the commit is aborted.
|
||||
#
|
||||
# To enable this hook, rename this file to "prepare-commit-msg".
|
||||
|
||||
# This hook includes three examples. The first comments out the
|
||||
# "Conflicts:" part of a merge commit.
|
||||
#
|
||||
# The second includes the output of "git diff --name-status -r"
|
||||
# into the message, just before the "git status" output. It is
|
||||
# commented because it doesn't cope with --amend or with squashed
|
||||
# commits.
|
||||
#
|
||||
# The third example adds a Signed-off-by line to the message, that can
|
||||
# still be edited. This is rarely a good idea.
|
||||
|
||||
case "$2,$3" in
|
||||
merge,)
|
||||
/usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
|
||||
|
||||
# ,|template,)
|
||||
# /usr/bin/perl -i.bak -pe '
|
||||
# print "\n" . `git diff --cached --name-status -r`
|
||||
# if /^#/ && $first++ == 0' "$1" ;;
|
||||
|
||||
*) ;;
|
||||
esac
|
||||
|
||||
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
|
||||
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
|
|
@ -0,0 +1,128 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to block unannotated tags from entering.
|
||||
# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
|
||||
#
|
||||
# To enable this hook, rename this file to "update".
|
||||
#
|
||||
# Config
|
||||
# ------
|
||||
# hooks.allowunannotated
|
||||
# This boolean sets whether unannotated tags will be allowed into the
|
||||
# repository. By default they won't be.
|
||||
# hooks.allowdeletetag
|
||||
# This boolean sets whether deleting tags will be allowed in the
|
||||
# repository. By default they won't be.
|
||||
# hooks.allowmodifytag
|
||||
# This boolean sets whether a tag may be modified after creation. By default
|
||||
# it won't be.
|
||||
# hooks.allowdeletebranch
|
||||
# This boolean sets whether deleting branches will be allowed in the
|
||||
# repository. By default they won't be.
|
||||
# hooks.denycreatebranch
|
||||
# This boolean sets whether remotely creating branches will be denied
|
||||
# in the repository. By default this is allowed.
|
||||
#
|
||||
|
||||
# --- Command line
|
||||
refname="$1"
|
||||
oldrev="$2"
|
||||
newrev="$3"
|
||||
|
||||
# --- Safety check
|
||||
if [ -z "$GIT_DIR" ]; then
|
||||
echo "Don't run this script from the command line." >&2
|
||||
echo " (if you want, you could supply GIT_DIR then run" >&2
|
||||
echo " $0 <ref> <oldrev> <newrev>)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
|
||||
echo "usage: $0 <ref> <oldrev> <newrev>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Config
|
||||
allowunannotated=$(git config --bool hooks.allowunannotated)
|
||||
allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
|
||||
denycreatebranch=$(git config --bool hooks.denycreatebranch)
|
||||
allowdeletetag=$(git config --bool hooks.allowdeletetag)
|
||||
allowmodifytag=$(git config --bool hooks.allowmodifytag)
|
||||
|
||||
# check for no description
|
||||
projectdesc=$(sed -e '1q' "$GIT_DIR/description")
|
||||
case "$projectdesc" in
|
||||
"Unnamed repository"* | "")
|
||||
echo "*** Project description file hasn't been set" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# --- Check types
|
||||
# if $newrev is 0000...0000, it's a commit to delete a ref.
|
||||
zero="0000000000000000000000000000000000000000"
|
||||
if [ "$newrev" = "$zero" ]; then
|
||||
newrev_type=delete
|
||||
else
|
||||
newrev_type=$(git cat-file -t $newrev)
|
||||
fi
|
||||
|
||||
case "$refname","$newrev_type" in
|
||||
refs/tags/*,commit)
|
||||
# un-annotated tag
|
||||
short_refname=${refname##refs/tags/}
|
||||
if [ "$allowunannotated" != "true" ]; then
|
||||
echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
|
||||
echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/tags/*,delete)
|
||||
# delete tag
|
||||
if [ "$allowdeletetag" != "true" ]; then
|
||||
echo "*** Deleting a tag is not allowed in this repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/tags/*,tag)
|
||||
# annotated tag
|
||||
if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
|
||||
then
|
||||
echo "*** Tag '$refname' already exists." >&2
|
||||
echo "*** Modifying a tag is not allowed in this repository." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/heads/*,commit)
|
||||
# branch
|
||||
if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
|
||||
echo "*** Creating a branch is not allowed in this repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/heads/*,delete)
|
||||
# delete branch
|
||||
if [ "$allowdeletebranch" != "true" ]; then
|
||||
echo "*** Deleting a branch is not allowed in this repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/remotes/*,commit)
|
||||
# tracking branch
|
||||
;;
|
||||
refs/remotes/*,delete)
|
||||
# delete tracking branch
|
||||
if [ "$allowdeletebranch" != "true" ]; then
|
||||
echo "*** Deleting a tracking branch is not allowed in this repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
# Anything else (is there anything else?)
|
||||
echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# --- Finished
|
||||
exit 0
|
Binary file not shown.
|
@ -0,0 +1,6 @@
|
|||
# git ls-files --others --exclude-from=.git/info/exclude
|
||||
# Lines that start with '#' are comments.
|
||||
# For a project mostly in C, the following would be a good set of
|
||||
# exclude patterns (uncomment them if you want to use them):
|
||||
# *.[oa]
|
||||
# *~
|
|
@ -0,0 +1 @@
|
|||
0000000000000000000000000000000000000000 6c12c073e5702530a9d06b83840d62f8a6621764 Winston Li <git@winston.li> 1471957694 +0100 commit (initial): Main
|
|
@ -0,0 +1 @@
|
|||
0000000000000000000000000000000000000000 6c12c073e5702530a9d06b83840d62f8a6621764 Winston Li <git@winston.li> 1471957694 +0100 commit (initial): Main
|
|
@ -0,0 +1,2 @@
|
|||
x•Í=
|
||||
1@aëœbzAfH&! ²ÐÚ:¿:à&°Žx}AO`ùŠ<C3B9>W溊qÜéÖ„nƒg×r÷EϘr.<2E>k¶‘m&F¤JÙ¤—ÞçWO<>ÎÇ›èòþõá!' (rðÑÁ Ñ”ïLÛŸÌ\’ó_ö2ý
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
6c12c073e5702530a9d06b83840d62f8a6621764
|
|
@ -1 +0,0 @@
|
|||
Subproject commit e5fc0d2678ec7b9bacf0bf514bac035fa371cb6e
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 6c12c073e5702530a9d06b83840d62f8a6621764
|
Loading…
Reference in a new issue