Git Integration Guidelines - Moonshine-IDE/Moonshine-IDE GitHub Wiki

Git Integration Guidelines

Table of contents generated with markdown-toc

Repositories

Detecting repository

Right now Moonshine IDE supports only repositories cloned from the remote location. My recommendation is to include support for local repositories. To detect whether the project is part of a git repository or not I suggest using:

git rev-parse --is-inside-work-tree

Based on the result of this command we should enable or disable parts of git menu:

False True

Detecting Remote Repository

To detect whether the repository is local only or connected with a remote I suggest using:

git remote

Local Repo Remote Repo

Managing Repositories

For managing repositories I suggest using Path as well as Remote.

Remote address should be obtained using the command:

git remote -v

It should be displayed only when:

  • There is only one remote
  • Fetch Url is the same as Push Url
  • Both use https:// (instead of ssh://)

Otherwise remote should be blank. We may also want to show some kind of error.

Detecting state of a repository

As far as I know Git repository can be in the following states:

  • Normal
  • MERGING
  • REBASING
  • BISECTING

BISECTING is not very interesting and it shouldn’t interfere with other commands so I hope we can ignore it.

MERGING is important from our perspective. During merging we should block most of the UI and leave only the possibility to resolve merge conflicts.

Although we’re not letting the users rebase directly from our UI it would be beneficial to detect REBASING state and also block most of Git UI.

Detecting REBASING state:

(test -d "$(git rev-parse --git-path rebase-merge)" || test -d "$(git rev-parse --git-path rebase-apply)") && echo REBASING

Detecting MERGING state:

(test -f "$(git rev-parse --git-path MERGE_HEAD)") && echo MERGING

Normal REBASING MERGING

File Operations Using Simplified and Faithful Approach

When designing Git integrations you can go for Simplified Approach (pretend Git staging area doesn't exist) or Faithful Approach (show staging area and let the user manipulate it). Currently we're doing the Simplified Approach with the exception of showing the user complex file statuses like MM, AD, etc.

Both approaches have their pros and cons. For now, I'm focusing only on the Simplified Approach, because it's a lot closer to what we already have, but if at any point you decide that Faithful Approach would be better then I can also describe how to switch to the Faithful Approach.

The chosen approach will have impact on file statuses, committing and restoring. I think it should not have impact on merging and resolving conflicts (if we block the commit and restore options during conflicts).

A. The Simplified Approach

A1. File Statuses

When using git status --porcelain you get a Git File Status for every file that changed. Here's how you can simplify these statuses for users not familiar with staging area:

Moonshine Status:

  • ADDED - file is not present in head but is present in working tree
  • MODIFIED - file is present in head in different version than in working tree
  • DELETED - file is present in head but is not present in working tree
  • ??? - file is not present in both head and working tree but was manually added to staging area. Yes, it's a hacky status for situations that should be edge cases. No matter if user decides to commit the change or restore the change the result should be the same - file disappears from the staging area. Neither TortoiseGit nor Github Desktop handle this case in satisfactory manner. If you have a better idea how to name this status, please let me know.

Statuses that doesn't require manual console work:

Git Status Moonshine Status
?? ADDED
_M MODIFIED
_D DELETED

Statuses that require manual console work:

Git Status Moonshine Status
A_ ADDED
AM ADDED
AD ???
M_ MODIFIED
MM MODIFIED
MD DELETED
D_ DELETED
R_ Both:
  • oldFile - DELETED
  • newFile - ADDED
RM Both:
  • oldFile - DELETED
  • newFile - ADDED
RD Both:
  • oldFile - DELETED
  • newFile - ???

Conflict Only Statuses: UU, AA, DU, UD, DD, AU, UA - no need to show in commit/restore window, they will be shown in resolve conflicts window.

I believe all other statuses are impossible to get. If I'm wrong let me know.

A2. Committing

I think we should be preselecting all files, including untracked files.

When user clicks commit button we should:

  1. Build a pathspec of all selected files
  2. Run git add -- pathspec
  3. After adding all changes it may happen that there are no changes to commit (added changes nullified themselves). We should take that into account.
  4. Run git commit -- pathspec

A3. Restoring files

At the moment this menu item is named 'Status'. We should consider if we want to include more status information in the future:

  • Current branch name
  • Is in detached head mode
  • Is branch in sync with remote
  • In in MERGING|REBASING state

If not, maybe we should rename this item to 'Restore Files' because at the moment it’s all that it does.

How to restore a file based on its status:

  • Moonshine status ADDED
    • Command: git rm -f -- pathspec || git clean -f -- pathspec
    • Removes a file from both the index (if staged) and working tree
    • I believe it should work for all Git statuses: ??, A_, AM, R_ (newFile), RM (newFile)
  • Moonshine status MODIFIED
    • Command: git checkout head -- pathspec
    • Gets version of a file from head, updates the index with it, then updates working tree
    • I believe it should work for all Git statuses: M, M, MM
  • Moonshine status DELETED
    • Command: git checkout head -- pathspec
    • Gets version of a file from head, restores it to the index, then restores it to the working tree
    • I believe it should work for all Git statuses: D, MD, D, R_ (oldFile), RM (oldFile), RD (oldFile)
  • Moonshine status ???
    • Command: git rm -f -- pathspec
    • Removes file from index
    • Should work for both Git statuses: AD and RD (newfile)

The confirmation button should be renamed from 'Revert Selected' to 'Restore Selected'

Pros and Cons of Simplified Approach

Pros:

  • Great for users who start with Git and don't know how to handle Staging Area
  • Very close to what we already have

Cons:

  • Need some gymnastic to simplify statuses.
  • Need to split staging area renames into two statuses
  • Hacky ??? status
  • Less flexibility than being able to control staging area

Working with branches

Right now Moonshine IDE shows only remote branches. I recommend showing all branches.

I will add this section later.

Conflicts

Resolving Merge Conflicts

After merging and having conflicts we should end up in MERGING state. When in MERGING state, Git menu should look like this:

After clicking on 'Resolve Conflicts' a new window should appear:

How to find conflict files

Run git status --porcelain and look for these conflict statuses:

Status Conflict Resolve Dropdown Menu
UU Both modified
  • Open in Moonshine IDE
  • Open in default editor
AA Both added
  • Open in Moonshine IDE
  • Open in default editor
DU Deleted by us, modified by them
  • Delete
  • Keep theirs
UD Modified by us, deleted by them
  • Keep ours
  • Delete
DD Both deleted*
  • Delete (but make user select this option)
AU Added by us*
  • Keep ours
  • Delete
UA Added by them*
  • Delete
  • Keep theirs

*How to get statuses DD, AU and UA? Imagine you have a file named “OldFile.txt”. On one branch you rename OldFile.txt -> NewFileA.txt, on the other branch you rename OldFile.txt -> NewFileB.txt. After merge you should get:

  • DD -- OldFile.txt
  • AU -- NewFileA.txt
  • UA -- NewFileB.txt

Conflict types

As you can see there are two major conflict types:

  • Conflicts in the content of the file: UU, AA. There are git conflict markers inside the files themselves. Those conflict can be handled in external editors like Notepad, VSCode or KDiff3. When dealing with these conflicts it's hard to tell when they are resolved. One of the ways to do so, is to look inside the files for conflict markers.
  • Deciding whether the file should be kept or deleted: DU, UD, DD, AU, UA. Those should be resolved inside Moonshine IDE, because Moonshine IDE performed the merge. When dealing with these conflicts we can turn the conflict marker from red to green as soon as the user chooses to keep or delete the file.

Conflict resolution scenario

Step 1

User renames OldFile.txt -> NewFileA.txt. She also modifies SomeFile.txt and OtherFile.txt. She commits her changes and merges other branch using Moonshine IDE. The console informs her she got conflicts.

Step 2

She goes to Git Menu and chooses 'Resolve Conflicts'. This is what she sees:

Step 3

Looking at the conflicts she realizes that while she renamed OldFile.txt -> NewFileA.txt someone else renamed the same file OldFile.txt -> NewFileB.txt. She decides to stick with her changes, so she chooses 'Keep Ours', 'Delete' and 'Delete' from the dropdowns and sees this:

Because all keep/delete conflicts are now resolved the 'Continue' button is now enabled.

The commands we should run when the user chooses the resolution from dropdowns:

  • Keep Ours - git checkout head -- file
  • Keep Theris - git checkout merge_head -- file
  • Delete - git rm -f -- file

You can safely keep switching between different resolutions multiple times.

Step 4

She decides to also resolve file content conflicts. She clicks on the dropdowns to manually edit the files SomeFile.txt and OtherFile.txt.

She resolves the conflicts in Moonshine IDE editor or any other editor. When she comes back to the 'Resolve Conflicts' window the conflict markers are still red. She needs to press the 'Refresh' button. This performs regex search for conflict markers in the conflicted files. Now she sees:

So she continues with the merge.

⚠️ **GitHub.com Fallback** ⚠️