diff --git a/org.archicontribs.modelrepository.tests/src/org/archicontribs/modelrepository/grafico/GitExecutorTest.java b/org.archicontribs.modelrepository.tests/src/org/archicontribs/modelrepository/grafico/GitExecutorTest.java
new file mode 100644
index 0000000..5636563
--- /dev/null
+++ b/org.archicontribs.modelrepository.tests/src/org/archicontribs/modelrepository/grafico/GitExecutorTest.java
@@ -0,0 +1,184 @@
+package org.archicontribs.modelrepository.grafico;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.logging.Logger;
+
+import org.junit.jupiter.api.Test;
+
+public class GitExecutorTest {
+
+ private static final Logger LOGGER = Logger.getLogger(GitExecutorTest.class.getName());
+
+ private GitExecutor underTest;
+
+ public GitExecutorTest() throws GitExecutionException {
+ underTest = new GitExecutor(TestData.GIT_PATH, TestData.GIT_REPO);
+ }
+
+ @Test
+ public void canFindGitOnPath() throws IOException, InterruptedException {
+ Process process = Runtime.getRuntime().exec(new String[] { "which", "git" });
+ assertEquals(0, process.waitFor());
+ }
+
+ @Test
+ public void canGitVersion() throws GitExecutionException {
+ GitExecutionResult res = underTest.version();
+ LOGGER.finest(res.outputLine());
+ assertEquals(0, res.exitCode());
+ }
+
+ @Test
+ public void canGitReset() throws GitExecutionException {
+ GitExecutionResult res = underTest.reset(true, TestData.GIT_HISTORICAL_COMMIT_ID);
+ LOGGER.finest(res.outputLine());
+ assertEquals(0, res.exitCode());
+ }
+
+ @Test
+ public void canGitClean() throws GitExecutionException, IOException {
+ File f = new File(TestData.GIT_FOLDER, "cleanMe");
+ assertTrue(f.createNewFile());
+ assertTrue(f.exists());
+
+ GitExecutionResult res = underTest.clean();
+ LOGGER.finest(res.outputLine());
+ assertEquals(0, res.exitCode());
+
+ assertFalse(f.exists());
+ }
+
+ private void resetTestScenario() {
+ try {
+ canGitReset();
+ canGitClean();
+ } catch (GitExecutionException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void canGitFetch() throws GitExecutionException {
+ GitExecutionResult res = underTest.fetch();
+ LOGGER.finest(res.outputLine());
+ assertEquals(0, res.exitCode());
+ }
+
+
+ @Test
+ public void canGitCommit() throws GitExecutionException {
+ resetTestScenario(); // arrange
+
+ GitExecutionResult res = underTest.commit("empty commit", false, true, false);
+ LOGGER.finest(res.outputLine());
+ assertEquals(0, res.exitCode());
+ }
+
+ @Test
+ public void canGitPull() throws GitExecutionException {
+ resetTestScenario(); // arrange
+
+ GitExecutionResult res = underTest.pull(); // FF_ONLY
+ LOGGER.finest(res.outputLine());
+ assertEquals(0, res.exitCode());
+ }
+
+ @Test
+ public void canGitPullDetectConflict() throws GitExecutionException {
+ resetTestScenario(); // arrange
+
+ GitExecutionResult resCommit = underTest.commit("empty commit", false, true, false);
+ LOGGER.finest(resCommit.outputLine());
+
+ GitExecutionResult res = underTest.pull();
+ LOGGER.finest(res.outputLine());
+ assertEquals(128, res.exitCode());
+ }
+
+ @Test
+ public void canGitPullRebase() throws GitExecutionException {
+ resetTestScenario(); // arrange
+
+ GitExecutionResult resCommit = underTest.commit("empty commit", false, true, false);
+ LOGGER.finest(resCommit.outputLine());
+
+ GitExecutionResult res = underTest.pull(GitExecutor.PullMode.REBASE_MERGE);
+ LOGGER.finest(res.outputLine());
+ assertEquals(0, res.exitCode());
+ }
+
+ @Test
+ public void canGitRebaseAbort() throws GitExecutionException, IOException {
+ resetTestScenario(); // arrange
+
+ File targetF = Paths.get(TestData.GIT_FOLDER.getPath(), TestData.GIT_FILE.getName()).toFile();
+ Files.move(TestData.GIT_FILE.toPath(), targetF.toPath(),
+ StandardCopyOption.REPLACE_EXISTING);
+
+ GitExecutionResult resAdd = underTest.add(TestData.GIT_FILE);
+ assertEquals(0, resAdd.exitCode());
+
+ GitExecutionResult resCommit = underTest.commit("file deleted accidentaly oopsy");
+ LOGGER.finest(resCommit.outputLine());
+
+ GitExecutionResult res = underTest.rebase(TestData.GIT_HISTORICAL_ONTO_COMMIT_ID);
+ LOGGER.finest(res.outputLine());
+ assertEquals(1, res.exitCode());
+
+ GitExecutionResult abortResult = underTest.rebaseAbort();
+ assertEquals(0, abortResult.exitCode());
+ }
+
+ @Test
+ public void canDetectUncommittedFile() throws GitExecutionException, IOException {
+ resetTestScenario(); // arrange
+
+ assertFalse(underTest.hasChanges());
+
+ Files.move(TestData.GIT_FILE.toPath(), Paths.get(TestData.GIT_FOLDER.getPath(), TestData.GIT_FILE.getName()),
+ StandardCopyOption.REPLACE_EXISTING);
+
+ assertTrue(underTest.hasChanges());
+ }
+
+ @Test
+ public void canStageAllFiles() throws GitExecutionException, IOException {
+ resetTestScenario(); // arrange
+
+ Files.move(TestData.GIT_FILE.toPath(), Paths.get(TestData.GIT_FOLDER.getPath(), TestData.GIT_FILE.getName()),
+ StandardCopyOption.REPLACE_EXISTING);
+
+ assertEquals(0, underTest.addAll().exitCode());
+ }
+
+ @Test
+ public void canStageFile() throws GitExecutionException, IOException {
+ resetTestScenario(); // arrange
+
+ File targetF = Paths.get(TestData.GIT_FOLDER.getPath(), TestData.GIT_FILE.getName()).toFile();
+ assertTrue(targetF.createNewFile());
+
+ assertEquals(0, underTest.add(targetF).exitCode());
+
+ File nonExistentF = Paths.get(TestData.GIT_FOLDER.getPath(), "nonExistentFile").toFile();
+ assertFalse(nonExistentF.exists());
+
+ assertNotEquals(0, underTest.add(nonExistentF).exitCode());
+ }
+
+ @Test
+ public void canRemoteSshCredentials() {
+ // TODO
+ }
+}
diff --git a/org.archicontribs.modelrepository.tests/src/org/archicontribs/modelrepository/grafico/TestData.java b/org.archicontribs.modelrepository.tests/src/org/archicontribs/modelrepository/grafico/TestData.java
new file mode 100644
index 0000000..c0647c9
--- /dev/null
+++ b/org.archicontribs.modelrepository.tests/src/org/archicontribs/modelrepository/grafico/TestData.java
@@ -0,0 +1,14 @@
+package org.archicontribs.modelrepository.grafico;
+
+import java.io.File;
+
+interface TestData {
+
+ final File GIT_REPO = new File("/home/jan/projs/egit");
+ final File GIT_FILE = new File(GIT_REPO, "pom.xml");
+ final File GIT_FOLDER = new File(GIT_REPO, "icons");
+ final File GIT_PATH = new File("/usr/local/bin/git");
+ final String GIT_HISTORICAL_COMMIT_ID = "e90d864edca6eb34d0b7a1f0dcc767bcd4970bb5";
+ final String GIT_HISTORICAL_ONTO_COMMIT_ID = "cd8c66d521371cbd1163b136f991a9598055d84a";
+
+}
\ No newline at end of file
diff --git a/org.archicontribs.modelrepository.tests/src/org/archicontribs/modelrepository/grafico/launchers/GitExecutorTest.launch b/org.archicontribs.modelrepository.tests/src/org/archicontribs/modelrepository/grafico/launchers/GitExecutorTest.launch
new file mode 100644
index 0000000..2fc9b02
--- /dev/null
+++ b/org.archicontribs.modelrepository.tests/src/org/archicontribs/modelrepository/grafico/launchers/GitExecutorTest.launch
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.archicontribs.modelrepository.tests/src/org/archicontribs/modelrepository/grafico/launchers/archi.product.launch b/org.archicontribs.modelrepository.tests/src/org/archicontribs/modelrepository/grafico/launchers/archi.product.launch
new file mode 100644
index 0000000..16401d8
--- /dev/null
+++ b/org.archicontribs.modelrepository.tests/src/org/archicontribs/modelrepository/grafico/launchers/archi.product.launch
@@ -0,0 +1,240 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.archicontribs.modelrepository/.classpath b/org.archicontribs.modelrepository/.classpath
index 735ab3b..bdd4c5f 100644
--- a/org.archicontribs.modelrepository/.classpath
+++ b/org.archicontribs.modelrepository/.classpath
@@ -4,7 +4,11 @@
-
+
+
+
+
+
diff --git a/org.archicontribs.modelrepository/META-INF/MANIFEST.MF b/org.archicontribs.modelrepository/META-INF/MANIFEST.MF
index c290385..e5476f2 100644
--- a/org.archicontribs.modelrepository/META-INF/MANIFEST.MF
+++ b/org.archicontribs.modelrepository/META-INF/MANIFEST.MF
@@ -3,12 +3,12 @@ Bundle-ManifestVersion: 2
Bundle-Name: coArchi
Bundle-SymbolicName: org.archicontribs.modelrepository;singleton:=true
Bundle-Localization: plugin
-Bundle-Version: 0.9.2.qualifier
+Bundle-Version: 0.9.3.qualifier
Bundle-Vendor: Archi
Require-Bundle: org.eclipse.help.ui,
com.archimatetool.editor;bundle-version="4.9.0"
Bundle-ActivationPolicy: lazy
-Bundle-RequiredExecutionEnvironment: JavaSE-11
+Bundle-RequiredExecutionEnvironment: JavaSE-17
Eclipse-BundleShape: dir
Bundle-Activator: org.archicontribs.modelrepository.ModelRepositoryPlugin
Export-Package: org.archicontribs.modelrepository,
diff --git a/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/actions/AbstractModelAction.java b/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/actions/AbstractModelAction.java
index 9899daf..45af81f 100644
--- a/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/actions/AbstractModelAction.java
+++ b/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/actions/AbstractModelAction.java
@@ -18,6 +18,7 @@
import org.archicontribs.modelrepository.grafico.IArchiRepository;
import org.archicontribs.modelrepository.grafico.IRepositoryListener;
import org.archicontribs.modelrepository.grafico.RepositoryListenerManager;
+import org.archicontribs.modelrepository.grafico.ShellArchiRepository;
import org.archicontribs.modelrepository.preferences.IPreferenceConstants;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.dialogs.MessageDialog;
@@ -35,6 +36,7 @@
public abstract class AbstractModelAction extends Action implements IGraficoModelAction {
private IArchiRepository fRepository;
+ private ShellArchiRepository shellRepository;
protected IWorkbenchWindow fWindow;
@@ -46,14 +48,27 @@ protected AbstractModelAction(IWorkbenchWindow window) {
public void setRepository(IArchiRepository repository) {
fRepository = repository;
setEnabled(shouldBeEnabled());
+ setShellRepository(new ShellArchiRepository(repository.getLocalRepositoryFolder()));
}
@Override
public IArchiRepository getRepository() {
return fRepository;
}
+
+ public boolean isShellModeAvailable() {
+ return this.shellRepository != null;
+ }
- @Override
+ public void setShellRepository(ShellArchiRepository shellRepository) {
+ this.shellRepository = shellRepository;
+ }
+
+ public ShellArchiRepository getShellRepository() {
+ return shellRepository;
+ }
+
+ @Override
public void update() {
setEnabled(shouldBeEnabled());
}
@@ -124,8 +139,12 @@ protected boolean offerToCommitChanges() {
boolean amend = commitDialog.getAmend();
try {
- getRepository().commitChanges(commitMessage, amend);
-
+ if (isShellModeAvailable()) {
+ getShellRepository().commit(commitMessage, amend);
+ } else {
+ getRepository().commitChanges(commitMessage, amend);
+
+ }
// Save the checksum
getRepository().saveChecksum();
}
diff --git a/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/actions/RefreshModelAction.java b/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/actions/RefreshModelAction.java
index bb7466a..253c7ab 100644
--- a/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/actions/RefreshModelAction.java
+++ b/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/actions/RefreshModelAction.java
@@ -8,6 +8,7 @@
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.security.GeneralSecurityException;
+import java.util.logging.Logger;
import org.archicontribs.modelrepository.IModelRepositoryImages;
import org.archicontribs.modelrepository.authentication.ProxyAuthenticator;
@@ -18,6 +19,7 @@
import org.archicontribs.modelrepository.grafico.GraficoModelLoader;
import org.archicontribs.modelrepository.grafico.GraficoUtils;
import org.archicontribs.modelrepository.grafico.IRepositoryListener;
+import org.archicontribs.modelrepository.grafico.ShellArchiRepository.PullOutcome;
import org.archicontribs.modelrepository.merge.MergeConflictHandler;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
@@ -51,7 +53,7 @@
* @author Phillip Beauvoir
*/
public class RefreshModelAction extends AbstractModelAction {
-
+
protected static final int PULL_STATUS_ERROR = -1;
protected static final int PULL_STATUS_OK = 0;
protected static final int PULL_STATUS_UP_TO_DATE = 1;
@@ -59,6 +61,8 @@ public class RefreshModelAction extends AbstractModelAction {
protected static final int USER_OK = 0;
protected static final int USER_CANCEL = 1;
+
+ private static final Logger LOGGER = Logger.getLogger(RefreshModelAction.class.getName());
public RefreshModelAction(IWorkbenchWindow window) {
super(window);
@@ -66,11 +70,12 @@ public RefreshModelAction(IWorkbenchWindow window) {
setText(Messages.RefreshModelAction_0);
setToolTipText(Messages.RefreshModelAction_0);
}
-
+
public RefreshModelAction(IWorkbenchWindow window, IArchimateModel model) {
this(window);
+
if(model != null) {
- setRepository(new ArchiRepository(GraficoUtils.getLocalRepositoryFolderForModel(model)));
+ setRepository(new ArchiRepository(GraficoUtils.getLocalRepositoryFolderForModel(model)));
}
}
@@ -169,7 +174,8 @@ protected int init() throws IOException, GitAPIException {
getRepository().exportModelToGraficoFiles();
// Then offer to Commit
- if(getRepository().hasChangesToCommit()) {
+ if((super.isShellModeAvailable() && super.getShellRepository().hasChanges())
+ || getRepository().hasChangesToCommit()) {
if(!offerToCommitChanges()) {
return USER_CANCEL;
}
@@ -186,7 +192,21 @@ protected int pull(UsernamePassword npw, ProgressMonitorDialog pmDialog) throws
Display.getCurrent().readAndDispatch(); // update dialog
try {
- pullResult = getRepository().pullFromRemote(npw, new ProgressMonitorWrapper(pmDialog.getProgressMonitor()));
+ if (super.isShellModeAvailable()) {
+ PullOutcome pullOutcome = super.getShellRepository().pullFromRemote(npw);
+ switch(pullOutcome) {
+ case ALREADY_UP_TO_DATE:
+ return PULL_STATUS_UP_TO_DATE;
+ case PULLED_SUCCESSFULLY:
+ // places loaded in model in IEditorModelManager
+ new GraficoModelLoader(getRepository()).loadModel();
+ return PULL_STATUS_OK;
+ case PULL_INCOMPLETE:
+ LOGGER.warning("Shell mode run into trouble, falling back to jgit handlings.");
+ }
+ }
+
+ pullResult = getRepository().pullFromRemote(npw, new ProgressMonitorWrapper(pmDialog.getProgressMonitor()));
}
catch(Exception ex) {
// If this exception is thrown then the remote doesn't have the ref which can happen when pulling on a branch,
@@ -289,7 +309,6 @@ protected int pull(UsernamePassword npw, ProgressMonitorDialog pmDialog) throws
commitMessage += "\n\n" + Messages.RefreshModelAction_3 + "\n" + restoredObjects; //$NON-NLS-1$ //$NON-NLS-2$
}
- // TODO - not sure if amend should be false or true here?
getRepository().commitChanges(commitMessage, false);
}
diff --git a/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/grafico/GitExecutionException.java b/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/grafico/GitExecutionException.java
new file mode 100644
index 0000000..f057ba2
--- /dev/null
+++ b/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/grafico/GitExecutionException.java
@@ -0,0 +1,11 @@
+package org.archicontribs.modelrepository.grafico;
+
+public class GitExecutionException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public GitExecutionException(Throwable cause) {
+ super(cause);
+ }
+
+}
\ No newline at end of file
diff --git a/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/grafico/GitExecutionResult.java b/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/grafico/GitExecutionResult.java
new file mode 100644
index 0000000..d703cd1
--- /dev/null
+++ b/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/grafico/GitExecutionResult.java
@@ -0,0 +1,4 @@
+package org.archicontribs.modelrepository.grafico;
+
+public record GitExecutionResult(int exitCode, String outputLine) {
+}
\ No newline at end of file
diff --git a/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/grafico/GitExecutor.java b/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/grafico/GitExecutor.java
new file mode 100644
index 0000000..e911464
--- /dev/null
+++ b/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/grafico/GitExecutor.java
@@ -0,0 +1,151 @@
+package org.archicontribs.modelrepository.grafico;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * GitExecutor allows direct access to git-cli.
+ *
+ * Majorly inspired by
+ * https://github.com/JetBrains/intellij-community/tree/8b6e5ebc0cfccaad14323e47337ddac47c8347aa/plugins/git4idea/src/git4idea
+ *
+ * @author Jan Esser
+ */
+public class GitExecutor {
+
+ public static enum PullMode {
+ FF_ONLY, REBASE_MERGE
+ }
+
+ private static final String[] from(String command, String... commandArgs) {
+ List commandStrings = new ArrayList(1 + (commandArgs != null ? commandArgs.length : 0));
+ commandStrings.add(command);
+
+ for (String commandArg : commandArgs)
+ if (commandArg != null && commandArg != "")
+ commandStrings.add(commandArg);
+
+ return commandStrings.toArray(new String[0]);
+ }
+
+ private final File gitPath;
+ private final File gitRepo;
+
+ public GitExecutor(File localRepoFolder) throws GitExecutionException {
+ this(new File("git"), localRepoFolder);
+ }
+
+ public GitExecutor(File gitPath, File gitRepo) throws GitExecutionException {
+ this.gitPath = gitPath;
+ this.gitRepo = gitRepo;
+
+ version(); // assure git is operational
+ }
+
+ private GitExecutionResult gitExec(File gitPath, File workingDir, String... gitCommands)
+ throws IOException, InterruptedException {
+ String[] commandStrings = from(gitPath.getPath(), gitCommands);
+
+ ProcessBuilder pb = new ProcessBuilder(commandStrings);
+ pb.redirectErrorStream(true);
+ pb.directory(workingDir);
+ Process p = pb.start();
+ BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
+ // TODO add supplier -> consumer her to also process structured/multi-line
+ String line = br.readLine();
+ return new GitExecutionResult(p.waitFor(), line);
+ }
+
+ private GitExecutionResult gitExec(String gitCommand, String... gitCommandArgs) throws GitExecutionException {
+ try {
+ return gitExec(gitPath, gitRepo, from(gitCommand, gitCommandArgs));
+ } catch (IOException | InterruptedException ex) {
+ throw new GitExecutionException(ex);
+ }
+ }
+
+ public GitExecutionResult version() throws GitExecutionException {
+ return gitExec("--version");
+ }
+
+ public GitExecutionResult fetch() throws GitExecutionException {
+ return gitExec("fetch");
+ }
+
+ public GitExecutionResult pull() throws GitExecutionException {
+ return pull(PullMode.FF_ONLY);
+ }
+
+ public GitExecutionResult pull(PullMode mode) throws GitExecutionException {
+ String pullArg;
+ switch (mode) {
+ case FF_ONLY:
+ pullArg = "--ff-only";
+ break;
+ case REBASE_MERGE:
+ pullArg = "--rebase=merges";
+ break;
+ default:
+ throw new RuntimeException("NOT IMPLEMENTED");
+ }
+
+ return gitExec("pull", pullArg);
+ }
+
+ public GitExecutionResult reset(boolean hard, String commitId) throws GitExecutionException {
+ return gitExec("reset", hard ? "--hard" : "", commitId);
+ }
+
+ public GitExecutionResult commit(String message) throws GitExecutionException {
+ return commit(message, false, false, false);
+ }
+
+ public GitExecutionResult commit(String message, boolean amend, boolean allowEmpty, boolean commitAll)
+ throws GitExecutionException {
+ if (commitAll)
+ this.addAll();
+
+ return gitExec("commit", //
+ "-m", message, //
+ amend ? "--amend" : "", //
+ allowEmpty ? "--allow-empty" : "" //
+ );
+ }
+
+ public GitExecutionResult rebase(String onto) throws GitExecutionException {
+ return gitExec("rebase", "--onto", onto);
+ }
+
+ public GitExecutionResult rebaseAbort() throws GitExecutionException {
+ return gitExec("rebase", "--abort");
+ }
+
+ public boolean hasChanges() throws GitExecutionException {
+ // https://stackoverflow.com/a/3879077
+ GitExecutionResult refreshIndexResult = gitExec("update-index", "--refresh");
+ if (refreshIndexResult.exitCode() != 0)
+ return true;
+
+ GitExecutionResult diffIndexResult = gitExec("diff-index", "--quiet", "HEAD");
+ if (diffIndexResult.exitCode() != 0)
+ return true;
+
+ return false; // otherwise
+ }
+
+ public GitExecutionResult addAll() throws GitExecutionException {
+ return gitExec("add", "-A", ":/");
+ }
+
+ public GitExecutionResult add(File f) throws GitExecutionException {
+ return gitExec("add", "-A", this.gitRepo.toPath().relativize(f.toPath()).toString());
+ }
+
+ public GitExecutionResult clean() throws GitExecutionException {
+ return gitExec("clean", "-fd", ":/");
+ }
+}
\ No newline at end of file
diff --git a/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/grafico/ShellArchiRepository.java b/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/grafico/ShellArchiRepository.java
new file mode 100644
index 0000000..200a215
--- /dev/null
+++ b/org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/grafico/ShellArchiRepository.java
@@ -0,0 +1,66 @@
+package org.archicontribs.modelrepository.grafico;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.archicontribs.modelrepository.authentication.UsernamePassword;
+import org.archicontribs.modelrepository.grafico.GitExecutor.PullMode;
+
+import com.archimatetool.editor.actions.AbstractModelAction;
+
+/**
+ * ShellArchiRepository challenges ArchiRepository with alternative GIT
+ * back-end.
+ *
+ * Hexagonal force field: driven by: descendants of {@link AbstractModelAction}, driver: git-executor
+ */
+public class ShellArchiRepository {
+
+ public static enum PullOutcome {
+ ALREADY_UP_TO_DATE, PULLED_SUCCESSFULLY, PULL_INCOMPLETE
+ }
+
+ private final GitExecutor executor;
+
+ public ShellArchiRepository(File localRepoFolder) {
+ try {
+ this.executor = new GitExecutor(new File("/snap/eclipse-pde/current/usr/bin/git"), localRepoFolder);
+ } catch (GitExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public PullOutcome pullFromRemote(UsernamePassword npw) throws IOException {
+ try {
+ GitExecutionResult result = executor.pull(PullMode.REBASE_MERGE);
+ switch (result.exitCode()) {
+ case 0:
+ if (result.outputLine().endsWith("."))
+ return PullOutcome.ALREADY_UP_TO_DATE;
+ else
+ return PullOutcome.PULLED_SUCCESSFULLY;
+ default:
+ return PullOutcome.PULL_INCOMPLETE;
+ }
+ } catch (GitExecutionException e) {
+ throw new IOException(e);
+ }
+ }
+
+ public boolean hasChanges() throws IOException {
+ try {
+ return executor.hasChanges();
+ } catch (GitExecutionException e) {
+ throw new IOException(e);
+ }
+ }
+
+ public boolean commit(String commitMessage, boolean amend) throws IOException {
+ try {
+ return 0 == executor.commit(commitMessage, amend, false, true).exitCode();
+ } catch (GitExecutionException e) {
+ throw new IOException(e);
+ }
+ }
+
+}