Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show specific dialog when we get stashed files git errors #10053

Merged
merged 15 commits into from
Jul 8, 2020

Conversation

rafeca
Copy link
Contributor

@rafeca rafeca commented Jun 22, 2020

Closes #8197

⚠️ This PR is based on #10077. Please do not merge before #10077 has been reviewed and merged.

Steps to test this PR locally before a new dugite version with desktop/dugite#403 gets released:~~

  • Clone the dugite repository
  • checkout Detect unstashed files errors dugite#403
  • npm install && npm run build
  • yarn link
  • Go back to the folder where Desktop is checked out and execute yarn link dugite from the app/ folder.

(this PR is expected to fail until desktop/dugite#403 gets shipped).

edit: The info above ☝️ is not relevant anymore, since a new dugite version has already been shipped.

Description

This PR adds a new error handler for 3 dugite errors (LocalChangesOverwritten, MergeWithLocalChanges and RebaseWithLocalChanges) that get triggered when a git command cannot be executed due to existing local changes.

When this error gets intercepted a generic modal gets displayed with an option to "Stash the Changes and Continue", which internally will stash the local changes and retry the action that caused the error:

Screen Shot 2020-06-22 at 15 02 37

Edge cases

retryAction

In order to retry the action, this PR makes use of the retry mechanism that's incorporated in some git calls. Since not all git calls currently have retry logic, this modal will only get shown whenever the error contains retry logic (in any other case the generic error modal will get shown, as it was done before).

Currently, Merge operation don't have retry logic so we can add it to them in order to get this dialog also when merging branches.

Existing stash

If there's already an existing stash, the dialog won't offer the option to stash changes:

Screen Shot 2020-06-22 at 15 18 38

I think that we can improve this by showing a clear warning message in this scenario that tells the user that the current stash will get overwritten and allow the user to stash the changes. I initially tried to trigger the standard overwrite stash warning popup when this happens, but turns out that opening a popup within another popup creates a few edges cases.

I'm going to experiment with different ways to show that warning within the modal (cc @ampinsk and @billygriffin if you have any suggestion here).

Release notes

Notes: [Added] Suggest to stash changes when trying to do an operation that requires a clean working directory.

@rafeca rafeca requested a review from niik June 22, 2020 13:49
@rafeca rafeca changed the title Show special dialog when we get stashed files git errors Show specific dialog when we get stashed files git errors Jun 22, 2020
@rafeca rafeca requested a review from outofambit June 22, 2020 13:53
Comment on lines 1316 to 1317
const onDismissed = this.getOnPopupDismissedFn(popup.type)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed offline with @niik, we're creating a memoized version of onPopupDismissed that passes the popupType that gets opened, so when dismissing a modal it will only close itself and not other modals that could have got opened afterwards.

For now I've decided to only use this method on the new modal. It would be good to use it for every other modal but that would require extensive testing since it's possible that some modals are relying on the fact that currently onDismissed closes any other modal.

@tierninho
Copy link
Contributor

(this PR is expected to fail until desktop/dugite#403 gets shipped).

I plan to wait to test this PR unless you suggest otherwise. Thanks!

@rafeca rafeca changed the base branch from development to upgrade-dugite June 26, 2020 17:15
@rafeca rafeca marked this pull request as ready for review June 26, 2020 17:18
Base automatically changed from upgrade-dugite to development July 2, 2020 16:21
@niik
Copy link
Member

niik commented Jul 2, 2020

I plan to wait to test this PR unless you suggest otherwise. Thanks!

@tierninho Feel free to test to your heart's content!

@niik niik requested review from tierninho and removed request for outofambit July 2, 2020 16:26
@niik niik self-assigned this Jul 2, 2020
Copy link
Member

@niik niik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall the code looks great. I noticed that when I attempt to rebase with local changes I'm presented with a generic dialog

image

It's not clear to me whether this is something that this PR is intending to solve but from looking at the code it seems like it should. It seems the only reason the dialog isn't shown when rebasing is because we don' have a retry action for rebase. Could/should we add one?

@rafeca
Copy link
Contributor Author

rafeca commented Jul 6, 2020

It's not clear to me whether this is something that this PR is intending to solve but from looking at the code it seems like it should. It seems the only reason the dialog isn't shown when rebasing is because we don' have a retry action for rebase. Could/should we add one?

Good point! I tried the merge workflow but totally missed that there's a rebase workflow (I thought rebases were only done when pulling based on the pull.rebase config option). I'm going to address that

… submitting

When the user chooses to stash changes and retry, the dialog can flicker since hasExistingStash will momentarily be true.
This is to overcome the limitation that jest uses the commonJS version of npm dependencies, which in most cases (like memoize-one, classnames or moment) don't expose a default keyword to make it interoperable with default imports.

While this technically is not ES module compliant, it's a quick fix that's been already widely used in this codebase
@@ -123,6 +123,8 @@ import { CreateTag } from './create-tag'
import { DeleteTag } from './delete-tag'
import { ChooseForkSettings } from './choose-fork-settings'
import { DiscardSelection } from './discard-changes/discard-selection-dialog'
import { LocalChangesOverwrittenDialog } from './local-changes-overwritten/local-changes-overwritten-dialog'
import * as memoizeOne from 'memoize-one'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using import * from memoizeOne here to overcome the limitation that jest uses the commonJS version of npm dependencies, which in most cases (like memoize-one, classnames or moment) don't expose a default attribute to make it interoperable with default imports.

While this technically is not ES module compliant, it's a quick fix that's been already widely used in this codebase (for moment and classnames).

I'm going to create a follow-up PR to fix these in a way that makes our codebase compatible with ES6 module syntax.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the star-import trick didn't quite do the trick here (pun intended) 😢

[at-loader] ./app/src/ui/app.tsx:196:35 
   TS2349: This expression is not callable.
 Type 'typeof import("/Users/distiller/Desktop/desktop/node_modules/@types/memoize-one/index")' >  has no call signatures.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Damn it! It looks like the TypeScript definitions for memoize-one are stricter than for the other libraries and don't allow this trick.

I'm going to remove the memoization from this module for now and create a follow-up PR to try to fix the madness when importing npm packages that expose both commonJS and ES6 modules

@rafeca rafeca requested a review from niik July 6, 2020 16:12
niik
niik previously approved these changes Jul 6, 2020
Copy link
Member

@niik niik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes look great but unfortunately it seems the build is still broken

@@ -123,6 +123,8 @@ import { CreateTag } from './create-tag'
import { DeleteTag } from './delete-tag'
import { ChooseForkSettings } from './choose-fork-settings'
import { DiscardSelection } from './discard-changes/discard-selection-dialog'
import { LocalChangesOverwrittenDialog } from './local-changes-overwritten/local-changes-overwritten-dialog'
import * as memoizeOne from 'memoize-one'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the star-import trick didn't quite do the trick here (pun intended) 😢

[at-loader] ./app/src/ui/app.tsx:196:35 
   TS2349: This expression is not callable.
 Type 'typeof import("/Users/distiller/Desktop/desktop/node_modules/@types/memoize-one/index")' >  has no call signatures.

@tierninho tierninho removed their request for review July 7, 2020 20:19
@tierninho
Copy link
Contributor

Happy to test this in beta as it already has an approval and I can avoid the yarn errors after running yarn && yarn build:dev && yarn start:

✖ 「atl」: Checking finished with 1 errors
(node:53756) Warning: a promise was rejected with a non-error: [object Object]
[WEBPACK] Build failed after 33.396 seconds
(node:53756) Warning: a promise was rejected with a non-error: [object Object]
(node:53756) Warning: a promise was rejected with a non-error: [object Object]
[WEBPACK] Errors building ask-pass.js
[at-loader] ./app/src/ui/app.tsx:196:35
    TS2349: This expression is not callable.
  Type 'typeof import("/Users/tierninho/github/desktop/node_modules/@types/memoize-one/index")' has no call signatures.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
✖ 「atl」: Checking finished with 1 errors
➜  desktop git:(prompt-to-stash) ✖ 「atl」: Checking finished with 1 errors
✖ 「atl」: Checking finished with 1 errors
✖ 「atl」: Checking finished with 1 errors

Feel free to merge when ready.

There's a problem with  ts-jest that prevents jest from importing correctly commonJS modules that I'm going to fix in a separate PR
Copy link
Member

@niik niik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we clean up the remaining pieces of the memoized specific popup dismissal in favor of the forthcoming PR?

Comment on lines +190 to +198
/**
* Returns a memoized instance of onPopupDismissed() bound to the
* passed popupType, so it can be used in render() without creating
* multiple instances when the component gets re-rendered.
*/
private getOnPopupDismissedFn = (popupType: PopupType) => {
return () => this.onPopupDismissed(popupType)
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we drop this in favor of reintroducing it properly in the memoization PR?

Suggested change
/**
* Returns a memoized instance of onPopupDismissed() bound to the
* passed popupType, so it can be used in render() without creating
* multiple instances when the component gets re-rendered.
*/
private getOnPopupDismissedFn = (popupType: PopupType) => {
return () => this.onPopupDismissed(popupType)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this check, in some situations the dismissal of this popup will cause other popups to get closed: I've just tested it and when merging the conflicts modal will get automatically closed after stashing the changes.

Anyways, I'm planning to send a PR with the memoization very soon, so in either case we'll fix the bug or the performance issue very soon 😃

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that's great context that I was missing. Sounds great, we can live with the performance issue for a brief period of time I think.

app/src/ui/app.tsx Show resolved Hide resolved
@rafeca rafeca requested a review from niik July 8, 2020 13:56
@niik niik merged commit eee3cf8 into development Jul 8, 2020
@niik niik deleted the prompt-to-stash branch July 8, 2020 15:13
@WALLOUD

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow users to stash changes without switching branches
4 participants