From aae7863535696feb75ad7b4ccda85ae367f169df Mon Sep 17 00:00:00 2001 From: Constantine Plotnikov Date: Tue, 29 Sep 2009 19:59:27 +0400 Subject: [PATCH] git4idea: Implemeted forced merge/rebase and stash options for update operation --- plugins/git4idea/src/git4idea/GitUtil.java | 49 +- plugins/git4idea/src/git4idea/GitVcs.java | 2 +- .../src/git4idea/actions/GitRepositoryAction.java | 6 +- .../src/git4idea/changes/GitChangeUtils.java | 42 +- .../src/git4idea/config/GitVcsSettings.java | 20 + .../src/git4idea/i18n/GitBundle.properties | 786 +++++++++++---------- .../src/git4idea/update/GitStashUtils.java | 45 ++ .../src/git4idea/update/GitUpdateConfigurable.java | 85 +++ .../src/git4idea/update/GitUpdateEnvironment.java | 233 +++++- .../src/git4idea/update/GitUpdateOptionsPanel.form | 59 ++ .../src/git4idea/update/GitUpdateOptionsPanel.java | 113 +++ 11 files changed, 1023 insertions(+), 417 deletions(-) create mode 100644 plugins/git4idea/src/git4idea/update/GitStashUtils.java create mode 100644 plugins/git4idea/src/git4idea/update/GitUpdateConfigurable.java create mode 100644 plugins/git4idea/src/git4idea/update/GitUpdateOptionsPanel.form create mode 100644 plugins/git4idea/src/git4idea/update/GitUpdateOptionsPanel.java diff --git a/plugins/git4idea/src/git4idea/GitUtil.java b/plugins/git4idea/src/git4idea/GitUtil.java index 79bf892dff..5dd6488f1e 100644 --- a/plugins/git4idea/src/git4idea/GitUtil.java +++ b/plugins/git4idea/src/git4idea/GitUtil.java @@ -49,7 +49,27 @@ import java.util.*; * Git utility/helper methods */ public class GitUtil { + /** + * The logger instance + */ private final static Logger LOG = Logger.getInstance("#git4idea.GitUtil"); + /** + * Comparator for virtual files by name + */ + public static final Comparator VIRTUAL_FILE_COMPARATOR = new Comparator() { + public int compare(final VirtualFile o1, final VirtualFile o2) { + if (o1 == null && o2 == null) { + return 0; + } + if (o1 == null) { + return -1; + } + if (o2 == null) { + return 1; + } + return o1.getPresentableUrl().compareTo(o2.getPresentableUrl()); + } + }; /** * A private constructor to suppress instance creation @@ -269,8 +289,8 @@ public class GitUtil { * @return timestamp as {@link Date} object */ public static Date parseTimestamp(String value) { - return new Date(Long.parseLong(value.trim()) * 1000); - } + return new Date(Long.parseLong(value.trim()) * 1000); + } /** * Get git roots from content roots @@ -629,8 +649,10 @@ public class GitUtil { return getPossibleBase(file, n - 1, path); } - public static List getLocalCommittedChanges(final Project project, final VirtualFile root, - final Consumer parametersSpecifier) throws VcsException { + public static List getLocalCommittedChanges(final Project project, + final VirtualFile root, + final Consumer parametersSpecifier) + throws VcsException { final List rc = new ArrayList(); GitSimpleHandler h = new GitSimpleHandler(project, root, GitHandler.LOG); @@ -650,4 +672,23 @@ public class GitUtil { } return rc; } + + /** + * Cast or wrap exception into a vcs exception, errors and runtime exceptions are just thrown throw. + * + * @param t an exception to throw + * @return a wrapped exception + */ + public static VcsException rethrowVcsException(Throwable t) { + if (t instanceof Error) { + throw (Error)t; + } + if (t instanceof RuntimeException) { + throw (RuntimeException)t; + } + if (t instanceof VcsException) { + return (VcsException)t; + } + return new VcsException(t.getMessage(), t); + } } diff --git a/plugins/git4idea/src/git4idea/GitVcs.java b/plugins/git4idea/src/git4idea/GitVcs.java index f51cbfef32..60e1c3dd88 100644 --- a/plugins/git4idea/src/git4idea/GitVcs.java +++ b/plugins/git4idea/src/git4idea/GitVcs.java @@ -193,7 +193,7 @@ public class GitVcs extends AbstractVcs { myRollbackEnvironment = gitRollbackEnvironment; myRevSelector = new GitRevisionSelector(); myConfigurable = new GitVcsConfigurable(mySettings, myProject); - myUpdateEnvironment = new GitUpdateEnvironment(myProject); + myUpdateEnvironment = new GitUpdateEnvironment(myProject, this, mySettings); myMergeProvider = new GitMergeProvider(myProject); myCommittedChangeListProvider = new GitCommittedChangeListProvider(myProject); myOutgoingChangesProvider = new GitOutgoingChangesProvider(myProject); diff --git a/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java b/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java index d585e7f81f..d402a8a6e2 100644 --- a/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java +++ b/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java @@ -70,11 +70,7 @@ public abstract class GitRepositoryAction extends AnAction { GitBundle.getString("repository.action.missing.roots.title")); return; } - Collections.sort(roots, new Comparator() { - public int compare(final VirtualFile o1, final VirtualFile o2) { - return o1.getPresentableUrl().compareTo(o2.getPresentableUrl()); - } - }); + Collections.sort(roots, GitUtil.VIRTUAL_FILE_COMPARATOR); // get default root final VirtualFile[] vFiles = e.getData(PlatformDataKeys.VIRTUAL_FILE_ARRAY); VirtualFile defaultRootVar = null; diff --git a/plugins/git4idea/src/git4idea/changes/GitChangeUtils.java b/plugins/git4idea/src/git4idea/changes/GitChangeUtils.java index ab35c2c97e..38dd46da2e 100644 --- a/plugins/git4idea/src/git4idea/changes/GitChangeUtils.java +++ b/plugins/git4idea/src/git4idea/changes/GitChangeUtils.java @@ -8,6 +8,7 @@ import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; import com.intellij.openapi.vcs.versionBrowser.CommittedChangeListImpl; +import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import git4idea.GitContentRevision; import git4idea.GitRevisionNumber; @@ -39,6 +40,45 @@ public class GitChangeUtils { * Parse changes from lines * * @param project the context project + * @param root the git root + * @return a set of unmerged files + * @throws VcsException if the input format does not matches expected format + */ + public static List unmergedFiles(Project project, VirtualFile root) throws VcsException { + HashSet unmerged = new HashSet(); + String rootPath = root.getPath(); + GitSimpleHandler h = new GitSimpleHandler(project, root, GitHandler.LS_FILES); + h.setNoSSH(true); + h.setSilent(true); + h.addParameters("--unmerged"); + LocalFileSystem lfs = LocalFileSystem.getInstance(); + for (StringScanner s = new StringScanner(h.run()); s.hasMoreData();) { + if (s.isEol()) { + s.nextLine(); + continue; + } + s.boundedToken('\t'); + final String relative = s.line(); + String path = rootPath + "/" + GitUtil.unescapePath(relative); + VirtualFile file = lfs.refreshAndFindFileByPath(path); + assert file != null : "The unmerged file is not found " + path; + file.refresh(false, false); + unmerged.add(file); + } + if(unmerged.size() == 0) { + return Collections.emptyList(); + } else { + ArrayList rc = new ArrayList(unmerged.size()); + rc.addAll(unmerged); + Collections.sort(rc, GitUtil.VIRTUAL_FILE_COMPARATOR); + return rc; + } + } + + /** + * Parse changes from lines + * + * @param project the context project * @param vcsRoot the git root * @param thisRevision the current revision * @param parentRevision the parent revision for this change list @@ -57,7 +97,7 @@ public class GitChangeUtils { StringScanner sc = new StringScanner(s); parseChanges(project, vcsRoot, thisRevision, parentRevision, sc, changes, ignoreNames); if (sc.hasMoreData()) { - throw new IllegalStateException("Uknown file status: " + sc.line()); + throw new IllegalStateException("Unknown file status: " + sc.line()); } } diff --git a/plugins/git4idea/src/git4idea/config/GitVcsSettings.java b/plugins/git4idea/src/git4idea/config/GitVcsSettings.java index d5cb7cd81c..00c23dcc66 100644 --- a/plugins/git4idea/src/git4idea/config/GitVcsSettings.java +++ b/plugins/git4idea/src/git4idea/config/GitVcsSettings.java @@ -87,6 +87,14 @@ public class GitVcsSettings implements PersistentStateComponent * IDEA SSH should be used instead of native SSH. */ public SshExecutable SSH_EXECUTABLE = DEFAULT_SSH; + /** + * True if stash/unstash operation should be performed before update. + */ + public boolean UPDATE_STASH = true; + /** + * The type of update operation to perform + */ + public UpdateType UPDATE_TYPE = UpdateType.BRANCH_DEFAULT; /** * Save an author of the commit and make it the first one. If amount of authors exceeds the limit, remove least recently selected author. @@ -200,4 +208,16 @@ public class GitVcsSettings implements PersistentStateComponent * Naive SSH. */ NATIVE_SSH, } + + /** + * The type of update to perform + */ + public enum UpdateType { + /** Use default specified in the config file for the branch */ + BRANCH_DEFAULT, + /** Merge fetched commits with local branch */ + MERGE, + /** Rebase local commits upon the fetched branch*/ + REBASE + } } diff --git a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties index 16c6dccf97..3f48652672 100644 --- a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties +++ b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties @@ -1,385 +1,401 @@ -action.text.show.all.submitted=Show all files changed by this commit -add.action.name=Add -addrefspec.button=Add -addrefspec.get.references.tooltip=Get remote tag and branch references (depending on checkbox state). -addrefspec.get.references=&Get References -addrefspec.getting.references.title=Getting remote references for {0} -addrefspec.include.branches.tooltip=The next update references wil retrieve remote branches. -addrefspec.include.branches=Include &branches -addrefspec.include.tags.tooltip=The next update references wil retrieve remote tags. -addrefspec.include.tags=Include &tags -addrefspec.node.branches=Branches -addrefspec.node.tags=Tags -addrefspec.reference.chooser.tooltip=Select remote branch and tag references -addrefspec.title=Add Reference(s) -annotate.action.name=Annotate -annotation.tool.tip=commit {0}\nAuthor: {1}\nDate: {2}\n\n{3} -checking.out=Checkout {0} -checkout.action.name=Checkout -checkout.branch.label=&Checkout -checkout.branch.name.exists=Branch with the same name exists. Use override option to force branch creation. -checkout.branch=Checkout -checkout.create.ref.log.tooltip=If checked the reference log is created for the branch. It allows time based searches. (the option \"-l\") -checkout.create.ref.log=Create ref &log -checkout.include.tags.tooltip=If checked, the combobox will inclue tags names as well as branches. -checkout.include.tags=&Include tags -checkout.invalid.new.branch.name=Invalid new branch name -checkout.new.branch.tooltip=Enter name of new branch here. Leave blank if you do not want to create a new branch. -checkout.new.branch=As &new branch -checkout.override.branch=&Override -checkout.override.tooltip=If ckecked a branch will be created even if a branch with the same name already exists. The previous branch will be deleted. -checkout.ref.tooltip=Select branch, tag, or enter commit reference -checkout.retrieving.branches.and.tags=Retrieving branches and tags -checkout.track.branch=&Track branch -checkout.track.tooltip=If checked, the newly created branch will track original branch during pull. -checkout.validate.tooltip=Use this button to validate reference expression if entered manually. -checkout.validate=&Validate -checkout.validation.failed=Source revision validation failed -clone.button=Clone -clone.destination.directory.description=Select a parent directory destination directory for clone -clone.destination.directory.title=Parent Directory -clone.destination.exists.error=The path {0} exists. The Git cannot clone to existing directory. -clone.dir.name=Directory &Name -clone.invalid.origin=The origin name is in invalid format. -clone.invalid.url=Repository URL is a malformed URL or non-existent directory. -clone.origin=&Origin Name -clone.parent.dir=&Parent Directory -clone.parent.missing.error=The parent path {0} must exist. -clone.repository.url=Git &Repository URL -clone.test.failed.error=Repository test has failed. -clone.test.success.message=Connection to repository {0} has been successful. -clone.test.success=Connection successful -clone.test=&Test -clone.testing=Testing repository {0} -clone.title=Clone Repository -clone.url.is.not.directory.error=Repository URL is not a directory. -cloning.repository=Cloning source repository {0} -command.cancelled=The command was cancelled. -commit.action.name=Commit -commit.author.tooltip=Specify a commit author here if it is different from committer. The author should be in the format: Author Name <author@ema.il> -commit.author=&Author: -commit.partial.merge.message=The following files are not included in commit.
    {0}
Perform commit with all files included? -commit.partial.merge.title=Partial Merge Commit -commit.push.changes.tooltip=When this option is enabled, changes are pushed to the server after commit. -commit.push.changes=&Push Changes -common.current.branch.tooltip=The currently checked out branch. -common.current.branch=Current Branch: -common.git.root.tooltip=Select git vcs root -common.git.root=Git &Root: -common.no.active.branch= -computing.annotation=Computing annotation for {0} -current.branch.action.name=CurrentBranch -current.branch.change.tracked=Change Tracked Branch -current.branch.message=Checked out branch: {0} -current.branch.title=Current Branch -current.branch.tracked.branch.none= -current.branch.tracked.branch.tooltip=Select the branch to track -current.branch.tracked.branch=&Branch: -current.branch.tracked.remote.none= -current.branch.tracked.remote.this= -current.branch.tracked.repository.tooltip=Select repository to track -current.branch.tracked.repository=Re&pository: -current.branch.tracked.title=Tracked Branch -debug.git.exec=DEBUG: work-dir: [{0}] exec: [{1}] -delete.action.name=Delete -diff.find.error=Finding revision for diff: {0} -error.dialog.title=Error -error.list.title={0} Error: -error.occurred.during=Error occurred during ''{0}'' -fetch.action.name=Fetch -fetch.button=Fetch -fetch.force.references.update.tooltip=Forces update of branch references for which update is not forced in refrence mapping. -fetch.force.references.update=Force references &update -fetch.remote.label=Re&mote: -fetch.remote.tooltip=Remote name or url for fetch -fetch.tags.label=Fetch &tags: -fetch.tags.policy.all=All -fetch.tags.policy.for.fetched.commits=For fetched commits -fetch.tags.policy.none=None -fetch.tags.tooltip=Select fetched commits policy
  • For fetched commits means tags associated with commits on fetched branches are fetched.
  • All means that all tags and reference commits are fetched
  • None means that no tags are fetched.
-fetch.title=Git Fetch -fetching.tags.title=Updating tags from {0} -fetching.title=Fetching from {0} -find.git.description=Select path to git executable -find.git.error.title=Error Running git -find.git.success.title=Git Executed Successfully -find.git.title=Git Configuration -find.git.unsupported.message={0}
This version is unsupported, and some plugin functionality could fail to work.
The minimal supported version is {1}. -fix.roots.button=Accept -fix.roots.list.tooltip=The suggested list of Git VCS roots, new roots are marked as bold, removed roots are marked as overstriked. -fix.roots.message=The following Git vcs roots will be used instead of the current Git VCS roots. -fix.roots.title=Fix Git VCS Roots -fix.roots.valid.message=The invalid Git roots have been fixed already. -fix.roots.valid.title=All Git Roots Are Valid -getting.history=Getting history for {0} -git.default.commit.message=\n# Brief commit description here\n\n# Full commit description here (comment lines starting with '#' will not be included)\n\n -git.error.exit=The git process exited with the code {0} -git.running=Running: {0} -git.vcs.config.path.label=Path to &git executable: -git.vcs.config.ssh.mode.idea=IDEA ssh -git.vcs.config.ssh.mode.native=Native -git.vcs.config.ssh.mode.tooltip=Specify which version of SSH to be used with git.
  • Native means that the native ssh will be used.
  • IDEA SSH means that the implementation provided by the IDEA will be used.
The native implementation might cause hangups on some platforms. In native case, you also might need to configure ssh-askpass for your platform to recieve GUI promopts for passwords. -git.vcs.config.ssh.mode=&SSH executable: -git.vcs.config.test.label=Test -git.vcs.config.test=&Test -index.file.error=Updating file in the index failed -init.add.root.message=Do you want to add this directory as vcs root? -init.add.root.title=Git Init -init.destination.directory.description=Select directory where the new Git repository will be created. -init.destination.directory.title=Select directory for git init -init.error.already.under.git=The selected directory {0} is already under git. The submodules are not yet supported. -init.error.title=git init Error -merge.action.name=Merge -merge.add.log.information.tooltip=Add log information to the commit message (\"--log\" option) -merge.add.log.information=Add &log information -merge.branch.button=Merge -merge.branch.message=Select branch to merge into this one ({0}) -merge.branch.title=Merge Branches -merge.branches.tooltip=The chooser for braches. Select one or more braches to merge (already merged branches are not shown). -merge.branches=&Branches to merge: -merge.commit.message.tooltip=The commit message to use in case if merge results in commit (\"-m\" option) -merge.commit.message=Commit &Message -merge.default.strategy= -merge.is.not.needed=Merge is not needed for the file {0} -merge.load.files=Loading files for merge -merge.no.commit.tooltip=Force no commit for merge (the \"--no-commit\" option) -merge.no.commit=No &commit -merge.no.fast.forward.tooltip=Disable fast forward (\"--no-ff\" option) -merge.no.fast.forward=No &fast forward -merge.retrieving.branches=Retrieving unmerged branches -merge.squash.commit=S&quash commit -merge.squash.tooltip=Squash all changes into the single commit (\"--squash\" option) -merge.strategy.tooltip=The merge strategy to use (\"-s\" option) -merge.strategy=&Strategy: -merge.tool.action.name=MergeTool -merge.tool.column.status.deleted=Deleted -merge.tool.column.status.modified=Modified -merge.tool.column.theirs.status=Theirs -merge.tool.column.yours.status=Yours -merging.branch=Merging branch {0} -merging.title=Merging changes to {0} -paths.affected.title=Paths affected in commit {0} -pull.action.name=Pull -pull.button=Pull -pull.force.reference.update=Force reference &update -pull.get.branches.tooltip=Get branch names from remote repository. Otherwise a locally cached information is used. -pull.get.branches=&Get Branches -pull.getting.remote.branches=Getting remote branches -pull.remote.tooltip=Registered remote name or URL. -pull.remote=Re&mote: -pull.retrieving.remotes=Retrieving remotes -pull.title=Pull changes -pull.url.message=Enter remote repository URL to pull/merge (empty for default): -pull.url.title=Pull URL -pulling.title=Pulling changes from {0} -push.action.name=Push -push.branches.tooltip=Select branches to push -push.branches=&Branches: -push.button=Push -push.force.update.tooltip=If selected remote ref is updated even if they are is not an ancestor of the local ref. -push.force.update=&Force update -push.policy.all=All -push.policy.default=Default -push.policy.mirror=Mirror -push.policy.selected=Selected branches -push.policy.tooltip=Select push policy:
  • Default - the push is done according to the configuration of the remote.
  • Selected branches - selected branches are pushed.
  • All - all references under refs/heads are pushed.
  • Mirror - all references under refs/ are pushed including tags and remotes.
-push.policy=&Push: -push.remote.tooltip=Specify remote repository URL or remote name where to push changes. -push.remote=Re&mote: -push.show.tags.tooltip=If selected, the tags are shown in branch chooser as well. -push.show.tags=Show ta&gs -push.tags.tooltip=All tag refereneces are pushed in addition to references specified in references section. -push.tags=Push &tags -push.title=Push Changes -push.use.thin.pack.tooltip=If this option is selected, the push will spend extra CPU cycles to minimize amount of data transferred (use it for slow connections) -push.use.thin.pack=&Use thin pack -pushing.all.changes=Pushing all commited changes, refs & tags to remote repos -rebase.abort.action.name=Abort Rebasing -rebase.action.error=Git Rebase Error -rebase.action.message=Mulitple git roots have unfinished rebase process, please select root to perform action on. -rebase.action.name=Rebase -rebase.action.no.root=There is no rebase operation in progress in the project -rebase.branch.tooltip=Select branch to rebase (if branch is different from the current branch, it will be checked out first) -rebase.branch=&Branch: -rebase.button=Rebase -rebase.continue.action.name=Continue Rebasing -rebase.editor.action.column=Action -rebase.editor.button=Start Rebasing -rebase.editor.comment.column=Comment -rebase.editor.commit.column=Commit -rebase.editor.invalid.entryset=No commits found to rebase -rebase.editor.invalid.squash=The first non-skip commint could not be marked as squashed since squash merges commit with the previous commit. -rebase.editor.message=Reorder and edit &rebased commits -rebase.editor.move.down.tooltip=Move commit down (commit will be applied later) -rebase.editor.move.down=Move &Down -rebase.editor.move.up.tooltip=Move commit up in the list (commit will be applied earlier) -rebase.editor.move.up=Move &Up -rebase.editor.title=Rebasing commits -rebase.editor.view.tooltip=View commit contents -rebase.editor.view=&View -rebase.from.tooltip=Sepecify actual base for the branch. Leave blank to onto. -rebase.from=&From: -rebase.in.progress=Interactive rebase has been already started for this git root. -rebase.interactive.tooltip=If selected, the interactive rebase will be preformed. -rebase.interactive=&Interactive -rebase.invalid.from=\"From\" reference expression is invalid. -rebase.invalid.onto=\"Onto\" reference expression is invalid. -rebase.merge.strategy.tooltip=Select merge strategy to use -rebase.merge.strategy=Merge &Strategy: -rebase.no.merge.tooltip=If selected, no merge strategies will be applied during the rebase. -rebase.no.merge=&Do not use merge strategies -rebase.onto.tooltip=The reference that will become a new base for selected branch. -rebase.onto.validate=&Validate -rebase.onto=&Onto: -rebase.preserve.merges.tooltip=Preserve merges during rebease instead of squashing them. -rebase.preserve.merges=&Preserve Merges -rebase.result.amend.title=Rebase Suspended -rebase.result.amend=Stopped for amending commit while rebasing ({0}/{1}).\nAmmed commit and continue the rebase process. -rebase.result.cancelled.title=Rebase Cancelled -rebase.result.cancelled=The rebase process was cancelled. -rebase.result.conflict.title=Rebase Suspended -rebase.result.conflict=Stopped rebasing because of conflict ({0}/{1}).\nResolve conflicts and continue the rebase process. -rebase.result.error.title=Rebase Error -rebase.result.error=Stopped rebasing because of error while rebasing ({0}/{1}).\nCheck vcs console for details. -rebase.show.remote.branches.tooltip=If selected, remote branches are shown in drop down as well. -rebase.show.remote.branches=Show Re&mote Branches -rebase.show.tags.tooltip=Show tags in \"from\" and \"onto\" dropdowns. -rebase.skip.action.name=Skip Commit in Rebasing -rebase.title=Rebase branch -rebase.unstructured.editor.button=Resume Rebasing -rebase.unstructured.editor.git.root=Git Root: -rebase.unstructured.editor.message=Git rebase operation requested additional &information through the editor: -rebase.unstructured.editor.title=Addtional Rebase Input -rebase.unstructured.editor.tooltip=Edit this text according to instructions provided inline -rebase.valdate.onto.tooltip=Valdate "onto" reference. -rebase.validate.from.tooltip=Validate \"from\" reference -rebase.validate.from=Va&lidate -rebasing.title=Rebasing... -refspec.add.all.branches.tooltip=Add refspec that maps all remote branches by glob spec. -refspec.add.all.branches=Add A&ll Branches -refspec.add.all.tags.tooltip=Adds mapping entry for all tags -refspec.add.all.tags=Add All Ta&gs -refspec.add.ref.tooltip=Add branch or tag by name -refspec.add.ref=&Add... -refspec.branch.prefix.tooltip=Specify default branch prefix for the mapping. -refspec.branch.prefix=Remote &Name: -refspec.column.force=Force -refspec.column.local=Local -refspec.column.remote=Remote -refspec.default.tooltip=Restore default reference mapping -refspec.default=&Default -refspec.remove.tooltip=Delete refspec entry -refspec.remove=Remo&ve -refspec.title=Reference mapping -refspec.validation.remote.invalid=The invalid local name for remote -refspec.validation.remote.is.blank=The local name for remote is blank -regase.show.tags=Show &tags -repository.action.missing.roots.misconfigured=Neither of configured git roots are is under git. The configured directory or some of its ancestors must have ".git" directory in it. -repository.action.missing.roots.title=No git roots -repository.action.missing.roots.unconfigured.message=No git roots are configured for the project. -reset.action.name=Reset Head ... -reset.button=Reset -reset.commit.invalid=The specified commit expression did not pass validation. -reset.commit.label=To &Commit: -reset.commit.tooltip=The commit that will become the current HEAD\n as result of reset operation. -reset.title=Reset Head -reset.type.hard=Hard -reset.type.mixed=Mixed -reset.type.soft=Soft -reset.type.tooltip=The reset type (see also git reset man page):
  • Mixed resets index but not working tree
  • Soft leaves index and working tree, just moves head pointer
  • Hard resets index and working tree.Changes in the working tree will be lost
-reset.type=Reset &Type: -reset.validate.tooltip=Validate the commit pointer and check the content of referenced commit. -reset.validate=&Validate -resetting.title=Resetting HEAD... -revert.action.name=Revert -revision.graph=RevisionGraph -root.tracker.message=Some configured Git VCS roots are not under Git or have Git repositories in subdirectories without configured VCS root. -select.branch.to.checkout=Select branch to checkout -show.all.paths.affected.action.name=Show All Affected Paths -ssh.ask.passphrase.title=SSH Key Passphrase -ssh.askPassphrase.message=Please enter passphrase for the private key {0} (the user name is {1}) -ssh.changed.host.key=The server host key for the host {0}:{1} has changed to {2} (type {3}).\nDo you want to accept the changed key? -ssh.confirm.key.titile=Confirm SSH Server Key -ssh.error.title=Authentication error -ssh.keyboard.interactive.title=SSH Keyboard Interactive: {0} -ssh.keyboard.interactive.username=Username: -ssh.new.host.key=Connecting to a new host {0}:{1} that has the key {2} (type {3}).\nDo you want to add this host to known hosts database? -ssh.password.message=Please enter password for user {0} -ssh.password.title=SSH Password Login -sshmain.failed.to.verify.key=Failed to verify key: {0} -sshmain.forwarding.failed=Forwarding {0} failed: {1} -sshmain.invald.host.key=Invald host key {0} {1} -sshmain.invalid.amount.of.arguments=Invalid amount of arguments: {0} -sshmain.invalidpassphrase=Invalid passphrase for the key {0} -sshmain.keyboard.interactive.failed=Keyboard interactive authentication failed. -sshmain.password.failed=Invalid user or password. -sshmain.pk.authenitication.failed=Authentication using key {0} failed. -sshmain.too.mush.passphrase.guesses=Invalid passphrase for the key {0}({1} attempts) -stash.action.name=Stash -stash.button=Create Stash -stash.keep.index.tooltip=If this checkbox is selected, indexed changes are kept in the index. -stash.keep.index=Keep &index -stash.message.tooltip=Enter stash message here. -stash.message=&Message: -stash.title=Stash -stashing.title=Stashing changes... -tag.action.name=Tag -tag.button=Create Tag -tag.commit.label=&Commit: -tag.commit.tooltip=Enter name of commit or object to tag or leave blank to use HEAD. -tag.error.creating.message.file.message=Unable to create message file: {0} -tag.error.creating.message.file.title=Error creating message file -tag.error.invalid.commit=The commit or object name is invalid. -tag.error.tag.exists=The tag with the same name exists. -tag.force.tooltip=Force creation of the text even if tag with such name already exists. -tag.force=&Force -tag.getting.existing.tags=Getting existing tags... -tag.message.label=&Message: -tag.message.tooltip=If the message is not empty, an annotated tag is created. -tag.message=Specify tag name: -tag.name.label=Tag &Name: -tag.name.tooltip=Enter the new tag name here. -tag.nonvcs.error.message=ERROR: Files not tagged, not all are under VCS root! -tag.nonvcs.error.title=Tag Result -tag.title=Tag -tag.validate.tooltip=Click this button to the validate commit to be tagged. -tag.validate=&Validate -tagging.title=Tagging files... -unindexed.files.changlelist.name=Unindexed Files -unstash.action.name=UnStash -unstash.branch.label\:=As new &branch: -unstash.branch.tooltip=If non-empty name is entered, the stash is checked out as a new branch. -unstash.button.apply=Apply Stash -unstash.button.branch=Branch -unstash.button.pop=Pop Stash -unstash.clear.tooltip=Delete all stashes in the repository. -unstash.clear=&Clear -unstash.clearing.stashes=Clearing stashes... -unstash.drop.tooltip=Delete selected stash -unstash.drop=&Drop -unstash.dropping.stash=Dropping stash {0}... -unstash.error.branch.exists=The specified branch already exists -unstash.error.invalid.branch.name=Invalid branch name -unstash.pop.stash.tooltip=If selected the stash is dropped after it is applied. -unstash.pop.stash=&Pop stash -unstash.reinstate.index.tooltip=Attempt to reinstate index as well as working tree changes. -unstash.reinstate.index=Reinstate &index -unstash.stashes.item={0}:{1}:{2} -unstash.stashes.tooltip=Select a stash to operate for. -unstash.stashes=&Stashes: -unstash.title=UnStash Changes -unstash.view.tooltip=View selected stash -unstash.view=&View -unstashing.title=UnStashing changes... -util.remote.renderer.default={0}({1}) -util.remote.renderer.none=None -util.remote.renderer.normal={0}({1}) -util.remote.renderer.self=Current repository -vcs.unable.to.run.git=Unable to run git: {0} ({1}) -vcs.unsupported.version=Unsupported version of git configured: {0}. The minimal supported version is {1} -vfs.listener.add.single.prompt=Do you want to add the following file to Git?\n{0}\n\nIf you say NO, you can still add it later manually. -vfs.listener.add.single.title=Add file to Git -vfs.listener.add.title=Add files to Git -vfs.listener.delete.single.prompt=Do you want to delete the following file from Git?\n{0}\n\nIf you say NO, you can still delete it later manually. -vfs.listener.delete.single.title=Delete file from Git -vfs.listener.delete.title=Delete files from Git +action.text.show.all.submitted=Show all files changed by this commit +add.action.name=Add +addrefspec.button=Add +addrefspec.get.references.tooltip=Get remote tag and branch references (depending on checkbox state). +addrefspec.get.references=&Get References +addrefspec.getting.references.title=Getting remote references for {0} +addrefspec.include.branches.tooltip=The next update references wil retrieve remote branches. +addrefspec.include.branches=Include &branches +addrefspec.include.tags.tooltip=The next update references wil retrieve remote tags. +addrefspec.include.tags=Include &tags +addrefspec.node.branches=Branches +addrefspec.node.tags=Tags +addrefspec.reference.chooser.tooltip=Select remote branch and tag references +addrefspec.title=Add Reference(s) +annotate.action.name=Annotate +annotation.tool.tip=commit {0}\nAuthor: {1}\nDate: {2}\n\n{3} +checking.out=Checkout {0} +checkout.action.name=Checkout +checkout.branch.label=&Checkout +checkout.branch.name.exists=Branch with the same name exists. Use override option to force branch creation. +checkout.branch=Checkout +checkout.create.ref.log.tooltip=If checked the reference log is created for the branch. It allows time based searches. (the option \"-l\") +checkout.create.ref.log=Create ref &log +checkout.include.tags.tooltip=If checked, the combobox will inclue tags names as well as branches. +checkout.include.tags=&Include tags +checkout.invalid.new.branch.name=Invalid new branch name +checkout.new.branch.tooltip=Enter name of new branch here. Leave blank if you do not want to create a new branch. +checkout.new.branch=As &new branch +checkout.override.branch=&Override +checkout.override.tooltip=If ckecked a branch will be created even if a branch with the same name already exists. The previous branch will be deleted. +checkout.ref.tooltip=Select branch, tag, or enter commit reference +checkout.retrieving.branches.and.tags=Retrieving branches and tags +checkout.track.branch=&Track branch +checkout.track.tooltip=If checked, the newly created branch will track original branch during pull. +checkout.validate.tooltip=Use this button to validate reference expression if entered manually. +checkout.validate=&Validate +checkout.validation.failed=Source revision validation failed +clone.button=Clone +clone.destination.directory.description=Select a parent directory destination directory for clone +clone.destination.directory.title=Parent Directory +clone.destination.exists.error=The path {0} exists. The Git cannot clone to existing directory. +clone.dir.name=Directory &Name +clone.invalid.origin=The origin name is in invalid format. +clone.invalid.url=Repository URL is a malformed URL or non-existent directory. +clone.origin=&Origin Name +clone.parent.dir=&Parent Directory +clone.parent.missing.error=The parent path {0} must exist. +clone.repository.url=Git &Repository URL +clone.test.failed.error=Repository test has failed. +clone.test.success.message=Connection to repository {0} has been successful. +clone.test.success=Connection successful +clone.test=&Test +clone.testing=Testing repository {0} +clone.title=Clone Repository +clone.url.is.not.directory.error=Repository URL is not a directory. +cloning.repository=Cloning source repository {0} +command.cancelled=The command was cancelled. +commit.action.name=Commit +commit.author.tooltip=Specify a commit author here if it is different from committer. The author should be in the format: Author Name <author@ema.il> +commit.author=&Author: +commit.partial.merge.message=The following files are not included in commit.
    {0}
Perform commit with all files included? +commit.partial.merge.title=Partial Merge Commit +commit.push.changes.tooltip=When this option is enabled, changes are pushed to the server after commit. +commit.push.changes=&Push Changes +common.current.branch.tooltip=The currently checked out branch. +common.current.branch=Current Branch: +common.git.root.tooltip=Select git vcs root +common.git.root=Git &Root: +common.no.active.branch= +computing.annotation=Computing annotation for {0} +current.branch.action.name=CurrentBranch +current.branch.change.tracked=Change Tracked Branch +current.branch.message=Checked out branch: {0} +current.branch.title=Current Branch +current.branch.tracked.branch.none= +current.branch.tracked.branch.tooltip=Select the branch to track +current.branch.tracked.branch=&Branch: +current.branch.tracked.remote.none= +current.branch.tracked.remote.this= +current.branch.tracked.repository.tooltip=Select repository to track +current.branch.tracked.repository=Re&pository: +current.branch.tracked.title=Tracked Branch +debug.git.exec=DEBUG: work-dir: [{0}] exec: [{1}] +delete.action.name=Delete +diff.find.error=Finding revision for diff: {0} +error.dialog.title=Error +error.list.title={0} Error: +error.occurred.during=Error occurred during ''{0}'' +fetch.action.name=Fetch +fetch.button=Fetch +fetch.force.references.update.tooltip=Forces update of branch references for which update is not forced in refrence mapping. +fetch.force.references.update=Force references &update +fetch.remote.label=Re&mote: +fetch.remote.tooltip=Remote name or url for fetch +fetch.tags.label=Fetch &tags: +fetch.tags.policy.all=All +fetch.tags.policy.for.fetched.commits=For fetched commits +fetch.tags.policy.none=None +fetch.tags.tooltip=Select fetched commits policy
  • For fetched commits means tags associated with commits on fetched branches are fetched.
  • All means that all tags and reference commits are fetched
  • None means that no tags are fetched.
+fetch.title=Git Fetch +fetching.tags.title=Updating tags from {0} +fetching.title=Fetching from {0} +find.git.description=Select path to git executable +find.git.error.title=Error Running git +find.git.success.title=Git Executed Successfully +find.git.title=Git Configuration +find.git.unsupported.message={0}
This version is unsupported, and some plugin functionality could fail to work.
The minimal supported version is {1}. +fix.roots.button=Accept +fix.roots.list.tooltip=The suggested list of Git VCS roots, new roots are marked as bold, removed roots are marked as overstriked. +fix.roots.message=The following Git vcs roots will be used instead of the current Git VCS roots. +fix.roots.title=Fix Git VCS Roots +fix.roots.valid.message=The invalid Git roots have been fixed already. +fix.roots.valid.title=All Git Roots Are Valid +getting.history=Getting history for {0} +git.default.commit.message=\n# Brief commit description here\n\n# Full commit description here (comment lines starting with '#' will not be included)\n\n +git.error.exit=The git process exited with the code {0} +git.running=Running: {0} +git.vcs.config.path.label=Path to &git executable: +git.vcs.config.ssh.mode.idea=IDEA ssh +git.vcs.config.ssh.mode.native=Native +git.vcs.config.ssh.mode.tooltip=Specify which version of SSH to be used with git.
  • Native means that the native ssh will be used.
  • IDEA SSH means that the implementation provided by the IDEA will be used.
The native implementation might cause hangups on some platforms. In native case, you also might need to configure ssh-askpass for your platform to recieve GUI promopts for passwords. +git.vcs.config.ssh.mode=&SSH executable: +git.vcs.config.test.label=Test +git.vcs.config.test=&Test +index.file.error=Updating file in the index failed +init.add.root.message=Do you want to add this directory as vcs root? +init.add.root.title=Git Init +init.destination.directory.description=Select directory where the new Git repository will be created. +init.destination.directory.title=Select directory for git init +init.error.already.under.git=The selected directory {0} is already under git. The submodules are not yet supported. +init.error.title=git init Error +merge.action.name=Merge +merge.add.log.information.tooltip=Add log information to the commit message (\"--log\" option) +merge.add.log.information=Add &log information +merge.branch.button=Merge +merge.branch.message=Select branch to merge into this one ({0}) +merge.branch.title=Merge Branches +merge.branches.tooltip=The chooser for braches. Select one or more braches to merge (already merged branches are not shown). +merge.branches=&Branches to merge: +merge.commit.message.tooltip=The commit message to use in case if merge results in commit (\"-m\" option) +merge.commit.message=Commit &Message +merge.default.strategy= +merge.is.not.needed=Merge is not needed for the file {0} +merge.load.files=Loading files for merge +merge.no.commit.tooltip=Force no commit for merge (the \"--no-commit\" option) +merge.no.commit=No &commit +merge.no.fast.forward.tooltip=Disable fast forward (\"--no-ff\" option) +merge.no.fast.forward=No &fast forward +merge.retrieving.branches=Retrieving unmerged branches +merge.squash.commit=S&quash commit +merge.squash.tooltip=Squash all changes into the single commit (\"--squash\" option) +merge.strategy.tooltip=The merge strategy to use (\"-s\" option) +merge.strategy=&Strategy: +merge.tool.action.name=MergeTool +merge.tool.column.status.deleted=Deleted +merge.tool.column.status.modified=Modified +merge.tool.column.theirs.status=Theirs +merge.tool.column.yours.status=Yours +merging.branch=Merging branch {0} +merging.title=Merging changes to {0} +paths.affected.title=Paths affected in commit {0} +pull.action.name=Pull +pull.button=Pull +pull.force.reference.update=Force reference &update +pull.get.branches.tooltip=Get branch names from remote repository. Otherwise a locally cached information is used. +pull.get.branches=&Get Branches +pull.getting.remote.branches=Getting remote branches +pull.remote.tooltip=Registered remote name or URL. +pull.remote=Re&mote: +pull.retrieving.remotes=Retrieving remotes +pull.title=Pull changes +pull.url.message=Enter remote repository URL to pull/merge (empty for default): +pull.url.title=Pull URL +pulling.title=Pulling changes from {0} +push.action.name=Push +push.branches.tooltip=Select branches to push +push.branches=&Branches: +push.button=Push +push.force.update.tooltip=If selected remote ref is updated even if they are is not an ancestor of the local ref. +push.force.update=&Force update +push.policy.all=All +push.policy.default=Default +push.policy.mirror=Mirror +push.policy.selected=Selected branches +push.policy.tooltip=Select push policy:
  • Default - the push is done according to the configuration of the remote.
  • Selected branches - selected branches are pushed.
  • All - all references under refs/heads are pushed.
  • Mirror - all references under refs/ are pushed including tags and remotes.
+push.policy=&Push: +push.remote.tooltip=Specify remote repository URL or remote name where to push changes. +push.remote=Re&mote: +push.show.tags.tooltip=If selected, the tags are shown in branch chooser as well. +push.show.tags=Show ta&gs +push.tags.tooltip=All tag refereneces are pushed in addition to references specified in references section. +push.tags=Push &tags +push.title=Push Changes +push.use.thin.pack.tooltip=If this option is selected, the push will spend extra CPU cycles to minimize amount of data transferred (use it for slow connections) +push.use.thin.pack=&Use thin pack +pushing.all.changes=Pushing all commited changes, refs & tags to remote repos +rebase.abort.action.name=Abort Rebasing +rebase.action.error=Git Rebase Error +rebase.action.message=Mulitple git roots have unfinished rebase process, please select root to perform action on. +rebase.action.name=Rebase +rebase.action.no.root=There is no rebase operation in progress in the project +rebase.branch.tooltip=Select branch to rebase (if branch is different from the current branch, it will be checked out first) +rebase.branch=&Branch: +rebase.button=Rebase +rebase.continue.action.name=Continue Rebasing +rebase.editor.action.column=Action +rebase.editor.button=Start Rebasing +rebase.editor.comment.column=Comment +rebase.editor.commit.column=Commit +rebase.editor.invalid.entryset=No commits found to rebase +rebase.editor.invalid.squash=The first non-skip commint could not be marked as squashed since squash merges commit with the previous commit. +rebase.editor.message=Reorder and edit &rebased commits +rebase.editor.move.down.tooltip=Move commit down (commit will be applied later) +rebase.editor.move.down=Move &Down +rebase.editor.move.up.tooltip=Move commit up in the list (commit will be applied earlier) +rebase.editor.move.up=Move &Up +rebase.editor.title=Rebasing commits +rebase.editor.view.tooltip=View commit contents +rebase.editor.view=&View +rebase.from.tooltip=Sepecify actual base for the branch. Leave blank to onto. +rebase.from=&From: +rebase.in.progress=Interactive rebase has been already started for this git root. +rebase.interactive.tooltip=If selected, the interactive rebase will be preformed. +rebase.interactive=&Interactive +rebase.invalid.from=\"From\" reference expression is invalid. +rebase.invalid.onto=\"Onto\" reference expression is invalid. +rebase.merge.strategy.tooltip=Select merge strategy to use +rebase.merge.strategy=Merge &Strategy: +rebase.no.merge.tooltip=If selected, no merge strategies will be applied during the rebase. +rebase.no.merge=&Do not use merge strategies +rebase.onto.tooltip=The reference that will become a new base for selected branch. +rebase.onto.validate=&Validate +rebase.onto=&Onto: +rebase.preserve.merges.tooltip=Preserve merges during rebease instead of squashing them. +rebase.preserve.merges=&Preserve Merges +rebase.result.amend.title=Rebase Suspended +rebase.result.amend=Stopped for amending commit while rebasing ({0}/{1}).\nAmmed commit and continue the rebase process. +rebase.result.cancelled.title=Rebase Cancelled +rebase.result.cancelled=The rebase process was cancelled. +rebase.result.conflict.title=Rebase Suspended +rebase.result.conflict=Stopped rebasing because of conflict ({0}/{1}).\nResolve conflicts and continue the rebase process. +rebase.result.error.title=Rebase Error +rebase.result.error=Stopped rebasing because of error while rebasing ({0}/{1}).\nCheck vcs console for details. +rebase.show.remote.branches.tooltip=If selected, remote branches are shown in drop down as well. +rebase.show.remote.branches=Show Re&mote Branches +rebase.show.tags.tooltip=Show tags in \"from\" and \"onto\" dropdowns. +rebase.skip.action.name=Skip Commit in Rebasing +rebase.title=Rebase branch +rebase.unstructured.editor.button=Resume Rebasing +rebase.unstructured.editor.git.root=Git Root: +rebase.unstructured.editor.message=Git rebase operation requested additional &information through the editor: +rebase.unstructured.editor.title=Addtional Rebase Input +rebase.unstructured.editor.tooltip=Edit this text according to instructions provided inline +rebase.valdate.onto.tooltip=Valdate "onto" reference. +rebase.validate.from.tooltip=Validate \"from\" reference +rebase.validate.from=Va&lidate +rebasing.title=Rebasing... +refspec.add.all.branches.tooltip=Add refspec that maps all remote branches by glob spec. +refspec.add.all.branches=Add A&ll Branches +refspec.add.all.tags.tooltip=Adds mapping entry for all tags +refspec.add.all.tags=Add All Ta&gs +refspec.add.ref.tooltip=Add branch or tag by name +refspec.add.ref=&Add... +refspec.branch.prefix.tooltip=Specify default branch prefix for the mapping. +refspec.branch.prefix=Remote &Name: +refspec.column.force=Force +refspec.column.local=Local +refspec.column.remote=Remote +refspec.default.tooltip=Restore default reference mapping +refspec.default=&Default +refspec.remove.tooltip=Delete refspec entry +refspec.remove=Remo&ve +refspec.title=Reference mapping +refspec.validation.remote.invalid=The invalid local name for remote +refspec.validation.remote.is.blank=The local name for remote is blank +regase.show.tags=Show &tags +repository.action.missing.roots.misconfigured=Neither of configured git roots are is under git. The configured directory or some of its ancestors must have ".git" directory in it. +repository.action.missing.roots.title=No git roots +repository.action.missing.roots.unconfigured.message=No git roots are configured for the project. +reset.action.name=Reset Head ... +reset.button=Reset +reset.commit.invalid=The specified commit expression did not pass validation. +reset.commit.label=To &Commit: +reset.commit.tooltip=The commit that will become the current HEAD\n as result of reset operation. +reset.title=Reset Head +reset.type.hard=Hard +reset.type.mixed=Mixed +reset.type.soft=Soft +reset.type.tooltip=The reset type (see also git reset man page):
  • Mixed resets index but not working tree
  • Soft leaves index and working tree, just moves head pointer
  • Hard resets index and working tree.Changes in the working tree will be lost
+reset.type=Reset &Type: +reset.validate.tooltip=Validate the commit pointer and check the content of referenced commit. +reset.validate=&Validate +resetting.title=Resetting HEAD... +revert.action.name=Revert +revision.graph=RevisionGraph +root.tracker.message=Some configured Git VCS roots are not under Git or have Git repositories in subdirectories without configured VCS root. +select.branch.to.checkout=Select branch to checkout +show.all.paths.affected.action.name=Show All Affected Paths +ssh.ask.passphrase.title=SSH Key Passphrase +ssh.askPassphrase.message=Please enter passphrase for the private key {0} (the user name is {1}) +ssh.changed.host.key=The server host key for the host {0}:{1} has changed to {2} (type {3}).\nDo you want to accept the changed key? +ssh.confirm.key.titile=Confirm SSH Server Key +ssh.error.title=Authentication error +ssh.keyboard.interactive.title=SSH Keyboard Interactive: {0} +ssh.keyboard.interactive.username=Username: +ssh.new.host.key=Connecting to a new host {0}:{1} that has the key {2} (type {3}).\nDo you want to add this host to known hosts database? +ssh.password.message=Please enter password for user {0} +ssh.password.title=SSH Password Login +sshmain.failed.to.verify.key=Failed to verify key: {0} +sshmain.forwarding.failed=Forwarding {0} failed: {1} +sshmain.invald.host.key=Invald host key {0} {1} +sshmain.invalid.amount.of.arguments=Invalid amount of arguments: {0} +sshmain.invalidpassphrase=Invalid passphrase for the key {0} +sshmain.keyboard.interactive.failed=Keyboard interactive authentication failed. +sshmain.password.failed=Invalid user or password. +sshmain.pk.authenitication.failed=Authentication using key {0} failed. +sshmain.too.mush.passphrase.guesses=Invalid passphrase for the key {0}({1} attempts) +stash.action.name=Stash +stash.button=Create Stash +stash.keep.index.tooltip=If this checkbox is selected, indexed changes are kept in the index. +stash.keep.index=Keep &index +stash.message.tooltip=Enter stash message here. +stash.message=&Message: +stash.title=Stash +stashing.title=Stashing changes... +tag.action.name=Tag +tag.button=Create Tag +tag.commit.label=&Commit: +tag.commit.tooltip=Enter name of commit or object to tag or leave blank to use HEAD. +tag.error.creating.message.file.message=Unable to create message file: {0} +tag.error.creating.message.file.title=Error creating message file +tag.error.invalid.commit=The commit or object name is invalid. +tag.error.tag.exists=The tag with the same name exists. +tag.force.tooltip=Force creation of the text even if tag with such name already exists. +tag.force=&Force +tag.getting.existing.tags=Getting existing tags... +tag.message.label=&Message: +tag.message.tooltip=If the message is not empty, an annotated tag is created. +tag.message=Specify tag name: +tag.name.label=Tag &Name: +tag.name.tooltip=Enter the new tag name here. +tag.nonvcs.error.message=ERROR: Files not tagged, not all are under VCS root! +tag.nonvcs.error.title=Tag Result +tag.title=Tag +tag.validate.tooltip=Click this button to the validate commit to be tagged. +tag.validate=&Validate +tagging.title=Tagging files... +unindexed.files.changlelist.name=Unindexed Files +unstash.action.name=UnStash +unstash.branch.label\:=As new &branch: +unstash.branch.tooltip=If non-empty name is entered, the stash is checked out as a new branch. +unstash.button.apply=Apply Stash +unstash.button.branch=Branch +unstash.button.pop=Pop Stash +unstash.clear.tooltip=Delete all stashes in the repository. +unstash.clear=&Clear +unstash.clearing.stashes=Clearing stashes... +unstash.drop.tooltip=Delete selected stash +unstash.drop=&Drop +unstash.dropping.stash=Dropping stash {0}... +unstash.error.branch.exists=The specified branch already exists +unstash.error.invalid.branch.name=Invalid branch name +unstash.pop.stash.tooltip=If selected the stash is dropped after it is applied. +unstash.pop.stash=&Pop stash +unstash.reinstate.index.tooltip=Attempt to reinstate index as well as working tree changes. +unstash.reinstate.index=Reinstate &index +unstash.stashes.item={0}:{1}:{2} +unstash.stashes.tooltip=Select a stash to operate for. +unstash.stashes=&Stashes: +unstash.title=UnStash Changes +unstash.view.tooltip=View selected stash +unstash.view=&View +unstashing.title=UnStashing changes... +update.options.display.name=Git Update Settings +update.options.no.commit=No &Commit +update.options.stash.tooltip=If selected, the current changes will be stashed before update, and than restored after update. +update.options.stash=&Stash changes before update +update.options.type.default=Branch Default +update.options.type.merge=Force Merge +update.options.type.rebase=Force Rebase +update.options.type.tooltip=Force update type (merge or rebase) or use branch default configuration +update.options.type=&Type: +update.rebase.no.change.cancel=Cancel Update +update.rebase.no.change.retry=Retry Continue +update.rebase.no.change.skip=Skip Commit +update.rebase.no.change.title=No Changes Staged +update.rebase.no.change=No changes detected {0}.
  • Skip Commit - skips current commit in the rebase process
  • Retry Continue - do it after you add changes to commit manually
  • Or cancel cancels update process for this root.
+update.rebase.unmerged.title=Unresolved Rebase Conflicts +update.rebase.unmerged={0} file(s) are not yet merged. Repeat merge operation (no cancels update for root {1}? +util.remote.renderer.default={0}({1}) +util.remote.renderer.none=None +util.remote.renderer.normal={0}({1}) +util.remote.renderer.self=Current repository +vcs.unable.to.run.git=Unable to run git: {0} ({1}) +vcs.unsupported.version=Unsupported version of git configured: {0}. The minimal supported version is {1} +vfs.listener.add.single.prompt=Do you want to add the following file to Git?\n{0}\n\nIf you say NO, you can still add it later manually. +vfs.listener.add.single.title=Add file to Git +vfs.listener.add.title=Add files to Git +vfs.listener.delete.single.prompt=Do you want to delete the following file from Git?\n{0}\n\nIf you say NO, you can still delete it later manually. +vfs.listener.delete.single.title=Delete file from Git +vfs.listener.delete.title=Delete files from Git diff --git a/plugins/git4idea/src/git4idea/update/GitStashUtils.java b/plugins/git4idea/src/git4idea/update/GitStashUtils.java new file mode 100644 index 0000000000..e7e9754033 --- /dev/null +++ b/plugins/git4idea/src/git4idea/update/GitStashUtils.java @@ -0,0 +1,45 @@ +package git4idea.update; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vfs.VirtualFile; +import git4idea.commands.GitHandler; +import git4idea.commands.GitSimpleHandler; +import org.jetbrains.annotations.NotNull; + +/** + * The class contains utilities for creating and removing stashes. + */ +public class GitStashUtils { + private GitStashUtils() { + } + + /** + * Create stash for later use + * + * @param project the project to use + * @param root the root + * @param message the message for the stash + * @return true if the stash was created, false otherwise + */ + public static boolean saveStash(@NotNull Project project, @NotNull VirtualFile root, final String message) throws VcsException { + GitSimpleHandler handler = new GitSimpleHandler(project, root, GitHandler.STASH); + handler.setNoSSH(true); + handler.addParameters("save", message); + String output = handler.run(); + return !output.startsWith("No local changes to save"); + } + + /** + * Create stash for later use + * + * @param project the project to use + * @param root the root + */ + public static void popLastStash(@NotNull Project project, @NotNull VirtualFile root) throws VcsException { + GitSimpleHandler handler = new GitSimpleHandler(project, root, GitHandler.STASH); + handler.setNoSSH(true); + handler.addParameters("pop"); + handler.run(); + } +} diff --git a/plugins/git4idea/src/git4idea/update/GitUpdateConfigurable.java b/plugins/git4idea/src/git4idea/update/GitUpdateConfigurable.java new file mode 100644 index 0000000000..107b6a79ad --- /dev/null +++ b/plugins/git4idea/src/git4idea/update/GitUpdateConfigurable.java @@ -0,0 +1,85 @@ +package git4idea.update; + +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurationException; +import git4idea.config.GitVcsSettings; +import git4idea.i18n.GitBundle; +import org.jetbrains.annotations.Nls; + +import javax.swing.*; + +/** + * Configurable for the update session + */ +public class GitUpdateConfigurable implements Configurable { + /** The vcs settings for the configurable */ + private final GitVcsSettings mySettings; + /** The options panel */ + private GitUpdateOptionsPanel myPanel; + + /** + * The constructor + * @param settings the settings object + */ + public GitUpdateConfigurable(GitVcsSettings settings) { + mySettings = settings; + } + + /** + * {@inheritDoc} + */ + @Nls + public String getDisplayName() { + return GitBundle.getString("update.options.display.name"); + } + + /** + * {@inheritDoc} + */ + public Icon getIcon() { + return null; + } + + /** + * {@inheritDoc} + */ + public String getHelpTopic() { + return null; + } + + /** + * {@inheritDoc} + */ + public JComponent createComponent() { + myPanel = new GitUpdateOptionsPanel(); + return myPanel.getPanel(); //To change body of implemented methods use File | Settings | File Templates. + } + + /** + * {@inheritDoc} + */ + public boolean isModified() { + return myPanel.isModified(mySettings); + } + + /** + * {@inheritDoc} + */ + public void apply() throws ConfigurationException { + myPanel.applyTo(mySettings); + } + + /** + * {@inheritDoc} + */ + public void reset() { + myPanel.updateFrom(mySettings); + } + + /** + * {@inheritDoc} + */ + public void disposeUIResources() { + myPanel = null; + } +} diff --git a/plugins/git4idea/src/git4idea/update/GitUpdateEnvironment.java b/plugins/git4idea/src/git4idea/update/GitUpdateEnvironment.java index 1082f54ab9..b44968699d 100644 --- a/plugins/git4idea/src/git4idea/update/GitUpdateEnvironment.java +++ b/plugins/git4idea/src/git4idea/update/GitUpdateEnvironment.java @@ -19,7 +19,10 @@ import com.intellij.openapi.options.Configurable; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vcs.AbstractVcsHelper; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.update.SequentialUpdatesContext; @@ -27,13 +30,15 @@ import com.intellij.openapi.vcs.update.UpdateEnvironment; import com.intellij.openapi.vcs.update.UpdateSession; import com.intellij.openapi.vcs.update.UpdatedFiles; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.ui.UIUtil; import git4idea.GitBranch; import git4idea.GitRevisionNumber; import git4idea.GitUtil; -import git4idea.commands.GitHandler; -import git4idea.commands.GitHandlerUtil; -import git4idea.commands.GitLineHandler; -import git4idea.config.GitConfigUtil; +import git4idea.GitVcs; +import git4idea.changes.GitChangeUtils; +import git4idea.commands.*; +import git4idea.config.GitVcsSettings; +import git4idea.i18n.GitBundle; import git4idea.merge.MergeChangeCollector; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -42,6 +47,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; /** * Git update environment implementation. The environment does just a simple @@ -49,17 +55,27 @@ import java.util.List; */ public class GitUpdateEnvironment implements UpdateEnvironment { /** + * The vcs instance + */ + private GitVcs myVcs; + /** * The context project */ private final Project myProject; + /** + * The project settings + */ + private final GitVcsSettings mySettings; /** * A constructor from settings * * @param project a project */ - public GitUpdateEnvironment(@NotNull Project project) { + public GitUpdateEnvironment(@NotNull Project project, @NotNull GitVcs vcs, GitVcsSettings settings) { + myVcs = vcs; myProject = project; + mySettings = settings; } /** @@ -79,33 +95,131 @@ public class GitUpdateEnvironment implements UpdateEnvironment { @NotNull Ref sequentialUpdatesContextRef) throws ProcessCanceledException { List exceptions = new ArrayList(); - for (VirtualFile root : GitUtil.gitRoots(Arrays.asList(filePaths))) { + for (final VirtualFile root : GitUtil.gitRoots(Arrays.asList(filePaths))) { try { // check if there is a remote for the branch final GitBranch branch = GitBranch.current(myProject, root); if (branch == null) { continue; } - final String value = GitConfigUtil.getValue(myProject, root, "branch." + branch.getName() + ".remote"); + final String value = branch.getTrackedRemoteName(myProject, root); if (value == null || value.length() == 0) { continue; } - // remember the current position - GitRevisionNumber before = GitRevisionNumber.resolve(myProject, root, "HEAD"); - // do pull - GitLineHandler h = new GitLineHandler(myProject, root, GitHandler.PULL); - // ignore merge failure for the pull - h.ignoreErrorCode(1); - h.addParameters("--no-stat"); - h.addParameters("-v"); + boolean stashCreated = + mySettings.UPDATE_STASH && GitStashUtils.saveStash(myProject, root, "Uncommitted changes before update operation"); try { - GitHandlerUtil.doSynchronouslyWithExceptions(h, progressIndicator); + // remember the current position + GitRevisionNumber before = GitRevisionNumber.resolve(myProject, root, "HEAD"); + // do pull + GitLineHandler h = new GitLineHandler(myProject, root, GitHandler.PULL); + // ignore merge failure for the pull + h.ignoreErrorCode(1); + switch (mySettings.UPDATE_TYPE) { + case REBASE: + h.addParameters("--rebase"); + break; + case MERGE: + h.addParameters("--no-rebase"); + break; + case BRANCH_DEFAULT: + // use default for the branch + break; + default: + assert false : "Unknown update type: " + mySettings.UPDATE_TYPE; + } + h.addParameters("--no-stat"); + h.addParameters("-v"); + final Ref cancelled = new Ref(false); + try { + RebaseConflictDetector rebaseConflictDetector = new RebaseConflictDetector(); + h.addLineListener(rebaseConflictDetector); + try { + GitHandlerUtil.doSynchronouslyWithExceptions(h, progressIndicator); + } + finally { + if (!rebaseConflictDetector.isRebaseConflict()) { + exceptions.addAll(h.errors()); + } + } + do { + final Ref ex = new Ref(); + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + public void run() { + try { + List affectedFiles = GitChangeUtils.unmergedFiles(myProject, root); + while (affectedFiles.size() != 0) { + AbstractVcsHelper.getInstance(myProject).showMergeDialog(affectedFiles, myVcs.getMergeProvider()); + affectedFiles = GitChangeUtils.unmergedFiles(myProject, root); + if (affectedFiles.size() != 0) { + int result = Messages.showYesNoDialog(myProject, GitBundle.message("update.rebase.unmerged", affectedFiles.size(), + root.getPresentableUrl()), + GitBundle.getString("update.rebase.unmerged.title"), Messages.getErrorIcon()); + if (result != 0) { + cancelled.set(true); + return; + } + } + } + } + catch (Throwable t) { + ex.set(t); + } + } + }); + //noinspection ThrowableResultOfMethodCallIgnored + if (ex.get() != null) { + //noinspection ThrowableResultOfMethodCallIgnored + throw GitUtil.rethrowVcsException(ex.get()); + } + if (cancelled.get()) { + break; + } + doRebase(progressIndicator, root, rebaseConflictDetector, "--continue"); + final Ref result = new Ref(); + noChangeLoop: + while (rebaseConflictDetector.isNoChange()) { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + public void run() { + int rc = Messages.showDialog(myProject, GitBundle.message("update.rebase.no.change", root.getPresentableUrl()), + GitBundle.getString("update.rebase.no.change.title"), + new String[]{GitBundle.getString("update.rebase.no.change.skip"), + GitBundle.getString("update.rebase.no.change.retry"), + GitBundle.getString("update.rebase.no.change.cancel")}, 0, Messages.getErrorIcon()); + result.set(rc); + } + }); + switch (result.get()) { + case 0: + doRebase(progressIndicator, root, rebaseConflictDetector, "--skip"); + continue noChangeLoop; + case 1: + continue noChangeLoop; + case 2: + cancelled.set(true); + break noChangeLoop; + } + } + } + while (rebaseConflictDetector.isRebaseConflict() && !cancelled.get()); + if (cancelled.get()) { + //noinspection ThrowableInstanceNeverThrown + exceptions.add(new VcsException("The update process was cancelled for " + root.getPresentableUrl())); + doRebase(progressIndicator, root, rebaseConflictDetector, "--abort"); + } + } + finally { + if (!cancelled.get()) { + // find out what have changed + MergeChangeCollector collector = new MergeChangeCollector(myProject, root, before, updatedFiles); + collector.collect(exceptions); + } + } } finally { - exceptions.addAll(h.errors()); - // find out what have changed - MergeChangeCollector collector = new MergeChangeCollector(myProject, root, before, updatedFiles); - collector.collect(exceptions); + if (stashCreated) { + GitStashUtils.popLastStash(myProject, root); + } } } catch (VcsException ex) { @@ -115,6 +229,26 @@ public class GitUpdateEnvironment implements UpdateEnvironment { return new GitUpdateSession(exceptions); } + /** + * Do rebase operation as part of update operator + * + * @param progressIndicator the progress indicator for the update + * @param root the vcs root + * @param rebaseConflictDetector the detector of conflicts in rebase operation + * @param action the rebase action to execute + */ + private void doRebase(ProgressIndicator progressIndicator, + VirtualFile root, + RebaseConflictDetector rebaseConflictDetector, + final String action) { + GitLineHandler rh = new GitLineHandler(myProject, root, GitHandler.REBASE); + // ignore failure for abort + rh.ignoreErrorCode(1); + rh.addParameters(action); + rebaseConflictDetector.reset(); + rh.addLineListener(rebaseConflictDetector); + GitHandlerUtil.doSynchronouslyWithExceptions(rh, progressIndicator); + } /** * {@inheritDoc} @@ -133,6 +267,63 @@ public class GitUpdateEnvironment implements UpdateEnvironment { */ @Nullable public Configurable createConfigurable(Collection files) { - return null; + return new GitUpdateConfigurable(mySettings); + } + + /** + * The detector of conflict conditions for rebase operation + */ + static class RebaseConflictDetector extends GitLineHandlerAdapter { + /** + * The line that indicates that there is a rebase conflict. + */ + private final static String REBASE_CONFLICT_INDICATOR = "When you have resolved this problem run \"git rebase --continue\"."; + /** + * The line that indicates "no change" condition. + */ + private static final String REBASE_NO_CHANGE_INDICATOR = "No changes - did you forget to use 'git add'?"; + /** + * if true, the rebase conflict happened + */ + AtomicBoolean rebaseConflict = new AtomicBoolean(false); + /** + * if true, the no changes were detected in the rebase operations + */ + AtomicBoolean noChange = new AtomicBoolean(false); + + /** + * Reset detector before new operation + */ + public void reset() { + rebaseConflict.set(false); + noChange.set(false); + } + + /** + * @return true if "no change" condition was detected during the operation + */ + public boolean isNoChange() { + return noChange.get(); + } + + /** + * @return true if conflict during rebase was detected + */ + public boolean isRebaseConflict() { + return rebaseConflict.get(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onLineAvailable(String line, Key outputType) { + if (line.startsWith(REBASE_CONFLICT_INDICATOR)) { + rebaseConflict.set(true); + } + if (line.startsWith(REBASE_NO_CHANGE_INDICATOR)) { + noChange.set(true); + } + } } } diff --git a/plugins/git4idea/src/git4idea/update/GitUpdateOptionsPanel.form b/plugins/git4idea/src/git4idea/update/GitUpdateOptionsPanel.form new file mode 100644 index 0000000000..1609d2dc87 --- /dev/null +++ b/plugins/git4idea/src/git4idea/update/GitUpdateOptionsPanel.form @@ -0,0 +1,59 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/plugins/git4idea/src/git4idea/update/GitUpdateOptionsPanel.java b/plugins/git4idea/src/git4idea/update/GitUpdateOptionsPanel.java new file mode 100644 index 0000000000..feb3af53bc --- /dev/null +++ b/plugins/git4idea/src/git4idea/update/GitUpdateOptionsPanel.java @@ -0,0 +1,113 @@ +package git4idea.update; + +import git4idea.config.GitVcsSettings; +import git4idea.config.GitVcsSettings.UpdateType; +import git4idea.i18n.GitBundle; + +import javax.swing.*; + +/** + * Update options + */ +public class GitUpdateOptionsPanel { + /** + * The merge policy + */ + private static final String MERGE = GitBundle.getString("update.options.type.merge"); + /** + * The rebase policy + */ + private static final String REBASE = GitBundle.getString("update.options.type.rebase"); + /** + * The rebase policy + */ + private static final String DEFAULT = GitBundle.getString("update.options.type.default"); + /** + * The type combobox + */ + private JComboBox myTypeComboBox; + /** + * The stash changes checkbox + */ + private JCheckBox myStashChangesCheckBox; + /** + * The root panel + */ + private JPanel myPanel; + + /** + * A constructor + */ + public GitUpdateOptionsPanel() { + myTypeComboBox.addItem(DEFAULT); + myTypeComboBox.addItem(REBASE); + myTypeComboBox.addItem(MERGE); + } + + /** + * @return the panel component + */ + public JComponent getPanel() { + return myPanel; + } + + /** + * Check if the panel is modified relatively to settings + * @param settings the settings to compare to + * @return true if the UI modified the settings + */ + public boolean isModified(GitVcsSettings settings) { + UpdateType type = getUpdateType(); + return type != settings.UPDATE_TYPE || myStashChangesCheckBox.isSelected() != settings.UPDATE_STASH; + } + + /** + * @return get the currently selected update type + */ + private UpdateType getUpdateType() { + UpdateType type = null; + String typeVal = (String)myTypeComboBox.getSelectedItem(); + if (REBASE.equals(typeVal)) { + type = UpdateType.REBASE; + } + else if (MERGE.equals(typeVal)) { + type = UpdateType.MERGE; + } else if(DEFAULT.equals(typeVal)) { + type = UpdateType.BRANCH_DEFAULT; + } + assert type != null; + return type; + } + + /** + * Save configuration to settings object + * @param settings the settings to save to + */ + public void applyTo(GitVcsSettings settings) { + settings.UPDATE_TYPE = getUpdateType(); + settings.UPDATE_STASH = myStashChangesCheckBox.isSelected(); + } + + /** + * Update panel according to settings + * @param settings the settings to use + */ + public void updateFrom(GitVcsSettings settings) { + myStashChangesCheckBox.setSelected(settings.UPDATE_STASH); + String value = null; + switch(settings.UPDATE_TYPE) { + case REBASE: + value = REBASE; + break; + case MERGE: + value = MERGE; + break; + case BRANCH_DEFAULT: + value = DEFAULT; + break; + default: + assert false : "Unknown value of update type: "+settings.UPDATE_TYPE; + } + myTypeComboBox.setSelectedItem(value); + } +} -- 2.11.4.GIT