diff --git a/app/src/main/java/com/zeapo/pwdstore/git/BreakOutOfDetached.kt b/app/src/main/java/com/zeapo/pwdstore/git/BreakOutOfDetached.kt new file mode 100644 index 00000000..31c65704 --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/git/BreakOutOfDetached.kt @@ -0,0 +1,80 @@ +package com.zeapo.pwdstore.git + +import android.app.Activity +import android.app.AlertDialog +import com.zeapo.pwdstore.R +import org.eclipse.jgit.api.Git +import org.eclipse.jgit.api.GitCommand +import org.eclipse.jgit.api.PushCommand +import org.eclipse.jgit.api.RebaseCommand +import java.io.File + +class BreakOutOfDetached(fileDir: File, callingActivity: Activity) : GitOperation(fileDir, callingActivity) { + private lateinit var commands: List> + + /** + * Sets the command + * + * @return the current object + */ + fun setCommands(): BreakOutOfDetached { + val git = Git(repository) + val branchName = "conflicting-master-${System.currentTimeMillis()}" + + this.commands = listOf( + // abort the rebase + git.rebase().setOperation(RebaseCommand.Operation.ABORT), + // git checkout -b conflict-branch + git.checkout().setCreateBranch(true).setName(branchName), + // push the changes + git.push().setRemote("origin"), + // switch back to master + git.checkout().setName("master") + ) + return this + } + + override fun execute() { + val git = Git(repository) + if (!git.repository.repositoryState.isRebasing) { + AlertDialog.Builder(callingActivity) + .setTitle(callingActivity.resources.getString(R.string.git_abort_and_push_title)) + .setMessage("The repository is not rebasing, no need to push to another branch") + .setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> + callingActivity.finish() + }.show() + return + } + + if (this.provider != null) { + // set the credentials for push command + this.commands.forEach { cmd -> + if (cmd is PushCommand) { + cmd.setCredentialsProvider(this.provider) + } + } + } + GitAsyncTask(callingActivity, false, true, this) + .execute(*this.commands.toTypedArray()) + } + + override fun onError(errorMessage: String) { + AlertDialog.Builder(callingActivity) + .setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title)) + .setMessage("Error occurred when checking out another branch operation $errorMessage") + .setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> + callingActivity.finish() + }.show() + } + + override fun onSuccess() { + AlertDialog.Builder(callingActivity) + .setTitle(callingActivity.resources.getString(R.string.git_abort_and_push_title)) + .setMessage("There was a conflict when trying to rebase. " + + "Your local master branch was pushed to another branch named conflicting-master-....\n" + + "Use this branch to resolve conflict on your computer") + .setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> + callingActivity.finish() + }.show() + } +} diff --git a/app/src/main/java/com/zeapo/pwdstore/git/GitActivity.java b/app/src/main/java/com/zeapo/pwdstore/git/GitActivity.java index e945c973..eb9b48ab 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/GitActivity.java +++ b/app/src/main/java/com/zeapo/pwdstore/git/GitActivity.java @@ -14,6 +14,7 @@ import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; +import android.widget.Button; import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; @@ -48,13 +49,13 @@ public class GitActivity extends AppCompatActivity { public static final int REQUEST_SYNC = 106; public static final int REQUEST_CREATE = 107; public static final int EDIT_GIT_CONFIG = 108; + public static final int BREAK_OUT_OF_DETACHED = 109; private static final String TAG = "GitAct"; private static final String emailPattern = "^[^@]+@[^@]+$"; private Activity activity; private Context context; private String protocol; private String connectionMode; - private File localDir; private String hostname; private SharedPreferences settings; private SshApiSessionFactory.IdentityBuilder identityBuilder; @@ -479,6 +480,7 @@ public class GitActivity extends AppCompatActivity { // init the server information final EditText git_user_name = findViewById(R.id.git_user_name); final EditText git_user_email = findViewById(R.id.git_user_email); + final Button abort = findViewById(R.id.git_abort_rebase); git_user_name.setText(settings.getString("git_config_user_name", "")); git_user_email.setText(settings.getString("git_config_user_email", "")); @@ -492,6 +494,9 @@ public class GitActivity extends AppCompatActivity { Ref ref = repo.getRef("refs/heads/master"); String head = ref.getObjectId().equals(objectId) ? ref.getName() : "DETACHED"; git_commit_hash.setText(String.format("%s (%s)", objectId.abbreviate(8).name(), head)); + + // enable the abort button only if we're rebasing + abort.setEnabled(repo.getRepositoryState().isRebasing()); } catch (Exception e) { // ignore } @@ -532,23 +537,7 @@ public class GitActivity extends AppCompatActivity { } public void abortRebase(View view) { - final Repository repo = PasswordRepository.getRepository(PasswordRepository.getRepositoryDirectory(getApplicationContext())); - if (repo != null) { - new GitOperation(PasswordRepository.getRepositoryDirectory(activity), activity) { - @Override - public void execute() { - Log.d(TAG, "Resetting the repository"); - assert repository != null; - GitAsyncTask tasks = new GitAsyncTask(activity, false, true, this); - tasks.execute(new Git(repo).rebase().setOperation(RebaseCommand.Operation.ABORT)); - } - - @Override - public void onSuccess() { - showGitConfig(); - } - }.execute(); - } + launchGitOperation(BREAK_OUT_OF_DETACHED); } /** @@ -558,7 +547,7 @@ public class GitActivity extends AppCompatActivity { if (PasswordRepository.getRepository(null) == null) { PasswordRepository.initialize(this); } - localDir = PasswordRepository.getRepositoryDirectory(context); + File localDir = PasswordRepository.getRepositoryDirectory(context); if (!saveConfiguration()) return; @@ -649,6 +638,7 @@ public class GitActivity extends AppCompatActivity { */ protected void launchGitOperation(int operation) { GitOperation op; + File localDir = PasswordRepository.getRepositoryDirectory(context); try { @@ -685,6 +675,10 @@ public class GitActivity extends AppCompatActivity { op = new SyncOperation(localDir, activity).setCommands(); break; + case BREAK_OUT_OF_DETACHED: + op = new BreakOutOfDetached(localDir, activity).setCommands(); + break; + case GitOperation.GET_SSH_KEY_FROM_CLONE: op = new CloneOperation(localDir, activity).setCommand(hostname); break; diff --git a/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.java b/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.java index 79234a32..52f5b1b7 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.java +++ b/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.java @@ -8,7 +8,10 @@ import com.zeapo.pwdstore.PasswordStore; import com.zeapo.pwdstore.R; import org.eclipse.jgit.api.CommitCommand; import org.eclipse.jgit.api.GitCommand; +import org.eclipse.jgit.api.PullCommand; +import org.eclipse.jgit.api.PullResult; import org.eclipse.jgit.api.PushCommand; +import org.eclipse.jgit.api.RebaseResult; import org.eclipse.jgit.api.StatusCommand; import org.eclipse.jgit.transport.PushResult; import org.eclipse.jgit.transport.RemoteRefUpdate; @@ -50,6 +53,14 @@ public class GitAsyncTask extends AsyncTask { // the previous status will eventually be used to avoid a commit if (nbChanges == null || nbChanges > 0) command.call(); + } else if (command instanceof PullCommand) { + final PullResult result = ((PullCommand) command).call(); + final RebaseResult rr = result.getRebaseResult(); + + if (rr.getStatus() == RebaseResult.Status.STOPPED) { + return activity.getString(R.string.git_pull_fail_error); + } + } else if (command instanceof PushCommand) { for (final PushResult result : ((PushCommand) command).call()) { // Code imported (modified) from Gerrit PushOp, license Apache v2 diff --git a/app/src/main/java/com/zeapo/pwdstore/git/SyncOperation.java b/app/src/main/java/com/zeapo/pwdstore/git/SyncOperation.java index 4e7f037d..266dd096 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/SyncOperation.java +++ b/app/src/main/java/com/zeapo/pwdstore/git/SyncOperation.java @@ -58,9 +58,9 @@ public class SyncOperation extends GitOperation { new AlertDialog.Builder(callingActivity). setTitle(callingActivity.getResources().getString(R.string.jgit_error_dialog_title)). setMessage("Error occured during the sync operation, " + + "\nPlease check the FAQ for possible reasons why this error might occur." + callingActivity.getResources().getString(R.string.jgit_error_dialog_text) - + errorMessage - + "\nPlease check the FAQ for possible reasons why this error might occur."). + + errorMessage). setPositiveButton(callingActivity.getResources().getString(R.string.dialog_ok), (dialogInterface, i) -> callingActivity.finish()).show(); } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 675c5ca9..52f631c1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -238,6 +238,7 @@ Screenshot of accessibility services Screenshot of toggle in accessibility services Screenshot of autofill service in action + Pull has failed, you\'re in a detached head. Using "settings > git utils", save your changes to the remote in a new branch and resolve the conflict on your computer. Push was rejected by remote, run pull before pushing again. You can use Synchronize rather than pull/push as it implements both Push was rejected by remote, reason: Remote rejected non-fast-forward push. Check receive.denyNonFastForwards variable in config file of destination repository. @@ -246,7 +247,7 @@ Clear saved preference for HOTP incrementing Remember the passphrase in the app configuration (insecure) Hackish tools - Abort rebase + Abort rebase and push new branch Commit hash p@ssw0rd! username: something other extra content @@ -258,4 +259,5 @@ Unknown SSH API Error SD-Card root selected You have selected the root of your sdcard for the store. This is extremely dangerous and you will lose your data as its content will, eventually, be deleted + Abort and Push