-
-
Notifications
You must be signed in to change notification settings - Fork 455
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
Rewrite DiffParser #399
Merged
Merged
Rewrite DiffParser #399
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
When I added the DiffEditor.has_selection() method, I missed the fact that QTextCursor has a hasSelection() method. Simplify DiffEditor.has_selection() by reimplmenting it in terms of QTextCursor.hasSelection(). Signed-off-by: Daniel Harding <dharding@living180.net>
Previously, the DiffParser.process_diff_selection() method took the contents of the text selection and searched for it in the output of git diff to determine which subset of the diff to process. Most of the time this worked, but if only a small portion of the diff was selected, such that the selection appeared multiple times in the diff, the wrong part of the diff might be processed. This is especially bad when performing a Revert operation, because the user can lose data if reverting the wrong lines. This commit provides a complete rewrite of the cola.diffparse module such that the Stage/Unstage/Revert operations should (modulo any bugs) always process exactly the line(s) selected by the user. Following is a summary of the changes, starting from the DiffParser class and working outward: Simplify the purpose of the DiffParser class. It no longer knows anything about models or how to apply changes to the index or the worktree. Nor does it call out to git anymore to load any data about the diff. The initializer now takes just two parameters: the filename (or relative path) of the file being diffed, and the actual text of the diff (without the diff header). The initializer uses a new standalone function _parse_diff() to convert the diff text to a list of _DiffHunk objects, which are stored on the instance in the hunks attribute. DiffParser now has just two methods, the first of which is generate_patch(). It takes the indexes of the first and last lines of the diff to include in the patch, along with an optional boolean flag indicating if a reverse patch should be generated. It returns a string containing the patch (including the diff header, generated using the filename passed to the initializer), or None if the resulting patch would not change anything. The second method is generate_hunk_patch(), which is a simple wrapper around the generate_hunk() method. It takes a single line index along with a reverse flag, and generates patch using only the hunk containing the specified line. Next is the ApplyDiffSelection command class, which is the sole user of the DiffParser class. Update the initializer to replace the offset and selection_text parameters (indicating the start and contents of the selection, respectively), with three parameters indicating the indexes of the first and last selected lines (or of the line containing the cursor if there is no selection), along with a boolean indicating if there is actually any text selected or not (to differentiate between a selection on a single line versus no selection at all). Merge the functionality of the old DiffParser.process_diff_selection() method into the do() method. This method now first constructs a DiffParser instance, passing it the filename and diff text from the model. It then uses the has_selection attribute to call either the generate_patch() or the generate_hunk_patch() method of the DiffParser instance, depending on whether or not there is a selection. Finally, if the resulting patch is not None, it writes it to a temporary file which is passed to either the model's apply_diff_to_worktree() or apply_diff() methods, depending on the operation being performed (Revert vs. Stage/Unstage). It then logs the status and updates the model as before. Lastly, update the DiffEditor widget class. Add a selected_lines() method, which returns the indexes of the first and last selected lines (or of the line containing the cursor if there is no selection). Update the process_diff_selection() method to use this new method along with the has_selection() method to pass the appropriate line indexes and the has_selection flag when constructing the ApplyDiffSelection command. Also, to reflect the change to ApplyDiffSelection, replace the staged parameter with a reverse parameter, and update the callers of the process_diff_selection() method appropriately. In addition, update the tests to reflect these changes, and remove the diff-*-reverse.txt fixtures as they are no longer needed (the code now converts the forward diff to a reverse diff when necessary). Signed-off-by: Daniel Harding <dharding@living180.net>
This is definitely one of the more gnarly parts of the code base. Thank you for improving it! |
davvid
added a commit
that referenced
this pull request
Dec 25, 2014
diff: rewrite DiffParser Signed-off-by: David Aguilar <davvid@gmail.com>
Glad to! I've been iterating over this for a number of weeks, so I'm happy I finally got to a point of having something I felt good about pushing up for merging. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR contains a full rewrite of the
cola.diffparser
module. The primary reason for this is to eliminate the ambiguity noted by @davvid in his comment on PR #329:Finding the text from the GUI selection in the diff is no longer done, removing any ambiguity. Instead, since the Stage/Unstage/Revert operations all operate on lines as a whole, rework the code to operate in terms of lines. The
DiffEditor
widget has a new method,selected_lines()
, which returns the indexes of the first and last selected lines (or of the line containing the cursor, if there is no selection). TheApplyDiffSelection
command class and theDiffParser
class were rewritten to operate in terms of these line indexes. To ensure that the correct lines are always included in the generated patch,DiffParser
no longer calls out togit
to get the diff, but instead uses the diff text from the model (otherwise if the file had changed on disk since git-cola refreshed its model, we can't be sure that the line numbers from theDiffEditor
widget would correspond to the line numbers fromgit diff
).In addition, the rewritten
DiffParser
class has been made purely functional: its state is set in the initializer, and its methods operate in a purely functional manner on that state. All the imperative functionality (creating temporary files and applying the diff) has been moved into thedo()
method of theApplyDiffSelection
command class.