Allow the user to push local master to a new branch in case of conflict (#508)

* detect that we're in a detached head and show the message

* add a new way to clean local repo in case of failed rebase
This commit is contained in:
Mohamed Zenadi 2019-05-15 11:22:07 +02:00 committed by GitHub
parent 127a8b8c8a
commit 064a3fad99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 109 additions and 22 deletions

View file

@ -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<GitCommand<out Any>>
/**
* 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()
}
}

View file

@ -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;

View file

@ -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<GitCommand, Integer, String> {
// 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

View file

@ -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();
}
}

View file

@ -238,6 +238,7 @@
<string name="autofill_ins_1_hint">Screenshot of accessibility services</string>
<string name="autofill_ins_2_hint">Screenshot of toggle in accessibility services</string>
<string name="autofill_ins_3_hint">Screenshot of autofill service in action</string>
<string name="git_pull_fail_error">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.</string>
<string name="git_push_nff_error">Push was rejected by remote, run pull before pushing again. You can use Synchronize rather than pull/push as it implements both</string>
<string name="git_push_generic_error">Push was rejected by remote, reason:</string>
<string name="git_push_other_error">Remote rejected non-fast-forward push. Check receive.denyNonFastForwards variable in config file of destination repository.</string>
@ -246,7 +247,7 @@
<string name="hotp_remember_clear_choice">Clear saved preference for HOTP incrementing</string>
<string name="remember_the_passphrase">Remember the passphrase in the app configuration (insecure)</string>
<string name="hackish_tools">Hackish tools</string>
<string name="abort_rebase">Abort rebase</string>
<string name="abort_rebase">Abort rebase and push new branch</string>
<string name="commit_hash">Commit hash</string>
<string name="crypto_password_edit_hint" translatable="false">p@ssw0rd!</string>
<string name="crypto_extra_edit_hint">username: something other extra content</string>
@ -258,4 +259,5 @@
<string name="ssh_api_unknown_error">Unknown SSH API Error</string>
<string name="sdcard_root_warning_title">SD-Card root selected</string>
<string name="sdcard_root_warning_message">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</string>
<string name="git_abort_and_push_title">Abort and Push</string>
</resources>