Make a weekly git GUI client [4] diff et al.

What was the weekly publication

Summary up to the last time

[\ 1 ] stage / unstage basic knowledge

5 things to do for line-by-line stage / unstage

1. Output diff as the original patch 2. Select the line you want to stage / unstage 3. Edit the body of hunk`` 4. Recalculate the hunk header`` 5. Apply the final patch

[\ 2 ] stage / unstage incomplete capture

It was impossible to use JGit, so I talked about using git.exe.

[\ 3 ] stage / unstage patch editing

3. Edit the body of hunk`` 4. Recalculate the hunk header``

This time

1. Output diff as the original patch

diff is not enough

It's easy to get a diff.

git diff -p
git diff -p --cached

This will not include the newly added untracked files. So it takes a little effort

git ls-files -o --exclude-standard

You can get the list with> and get the diff with the following command.

git diff --no-index /dev/null [untracked file name]

Rest assured that Windows will also compare with / dev / null. If you just want to see the difference, it's a new file, so you can look at the file itself, In the current context, what I want is a patch-style diff that can be applied, so I introduced it here.

Parse diff to get (do not) file name

However, not only the diff itself, but also the file name is needed in various situations.

Get with command

git diff --name-status 
git diff --name-status --cached

It is convenient to know the status (new addition, change, rename, delete) --Name-status is better than the --name-only option.

The filename is included in the diff itself, but it can be difficult.

[M] Change

The beginning of the diff looks like this

diff --git a/target.txt b/target.txt
index 0b669b6..5c30060 100644
--- a/target.txt
+++ b/target.txt

This seems easy to understand even from diff.

[A] New addition

In case of new addition, it looks like this.

diff --git a/end_with_linebreak/with space.txt b/end_with_linebreak/with space.txt
new file mode 100644
index 0000000..093dc04
--- /dev/null
+++ b/end_with_linebreak/with space.txt	

As you can see in this example, if there are spaces in the filename, it's a bit annoying to get from the first line. So it's easier to use the filename on the +++ b / line.

[R] Rename

Renaming is like this, and it's an easy win because there is more information.

diff --git a/names.txt b/names_.txt
similarity index 96%
rename from names.txt
rename to names_.txt
index bcfb685..f915764 100644
--- a/names.txt
+++ b/names_.txt

Rename basically appears as a diff of the file in index. Files on worktree that are not listed in the index, that is, with the git diff command

Appears in the form of. If you specify two file names and compare them, you may be able to get a diff that is treated as a rename, I don't know how to find those two files. For git diff --cached, git will find it for you.

By the way, it looks like this in binary. It is said that rename from rename to is not fixed and depends on the language environment, We have not confirmed the specifications in particular.

diff --git a/src/main/resources/icon.png b/src/main/resources/icon_white.png
similarity index 100%
rename from src/main/resources/icon.png
rename to src/main/resources/icon_white.png

[D] Delete

In the case of deletion, it looks like this. Finally the file name is only the first line.

diff --git a/src/main/java/NewView.java b/src/main/java/NewView.java
deleted file mode 100644
index e69de29..0000000

in short

That's why I don't want to twist my head, so I get the file name quietly.

Stage / unstage not line by line

What I want to create is an application that can be staged / unstaged line by line. However, it is difficult to do everything line by line.

stage / unstage in hunk units

If it is a hunk unit, extract the diff hunk and git apply. This is for line-by-line stage / unstage

3. Edit the body of hunk`` 4. Recalculate the hunk header``

Is just omitted, so there shouldn't be any problem. As a GUI, we have prepared the function from the beginning.

進捗.gif

With this gif, Click on the hunk header (black background)

Each is stage / unstage.

File-based stage / unstage

When developing while dogfooding in the git repository of this application itself, I didn't notice it, If it is a new project, the number of files will be reasonable at first, In most cases, you want to stage the entire newly added file. You will want to stage each file or all at once.

So, use the file name you got earlier. It ’s a normal command, but

git add -- [file paths]
git reset -- [file paths]

The file name may contain spaces, so Always put each file name between double quotes " ".

Digression

There are several phases to working with git.

I don't (too much) think that one application should handle all the operations of git. Also, what unit should the application (or a part of it) cut out the function? I think it's a difficult problem (and in general).

Although, Stage / unstage should be done in one application, including those that are not line-by-line.

image

I hope I can get a nice diff, but it seems to be difficult, so At the very least, I want to simply display and arrange the images.

ImageIO.read seemed easy in Java, so I'm using it. You can get a BufferedImage by passing an InputStream or File.

Non-existent file

If it currently exists as a file, simply File for that file is fine.

However, if you change it or delete the image, The image before modification or deletion does not exist as a file.

The fact that git is detecting changes or deletions means that Since it exists as a git object, you can get it if you know the hash. And the hash is contained in the diff, so use that.

git cat-file -p [hash]

This command is for browsing git objects (compressed) With the -p option, the contents of the file (extracted) will be obtained as is.

I'm hitting the git command from Java like this, I just passed the InputStreme obtained by this process.getInputStream () to ImageIO.read.

final Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(command, null, workingDir.toFile());

similarity index 100%

However, in the case of a simple rename, the hash is not included in the diff for some reason. The following is the same diff as in the previous example.

diff --git a/src/main/resources/icon.png b/src/main/resources/icon_white.png
similarity index 100%
rename from src/main/resources/icon.png
rename to src/main/resources/icon_white.png

Therefore, use the following command to get the hash from the file name and then get the image.

git ls-files -s [file path]

Because it exists as a git object after and before renaming It should be okay to use either name. (For some reason I used the renamed name.)

Memory cache

For example, the image does not change before and after stage / unstage, so I want to cache it. Of course, the basis for caching is the hash. Except for renaming, you should always be able to get the hash from diff.

But was it confused, was it a remnant of something, or was he not thinking about anything? In my code, I'm getting the hash for a newly added file. For reference, I will introduce the command.

git hash-object [file path]

For applications under development The specification is to "display only the diff of the selected file". So if you don't use the cache, the image will be loaded and created each time you select a file.

In the environment at hand, if you remove the cache, The difference is that you can see the hourglass that is displayed when waiting for an external command. Depending on the performance of the machine and the number of images, I have the impression that it will be difficult without caching.

progress

The screen is under development. It may differ from the actual specifications.

進捗_2.gif

Afterword

next time

Maybe it will be 2. Select the line you want to stage / unstage. I've forgotten a lot, so I want to write it early.

Swing

By the way, I wrote it in Swing.

Initially I used JList for the display and selection part of Diff, but It seems that JList cannot be updated on a cell-by-cell basis, so I changed it to JTable. After loading the image, I wanted to change the display and height of only the image cells.

It was a bit of a hassle to make the display and event processing that was done in JList exactly the same in JTable, Fortunately, the JTable version was able to eliminate the flicker-like display flicker that accompanies the image display on the JList. (Because of the waste and anxiety, I also made it possible to switch to the JList version with one flag. I just checked the operation again. )

RxJava/RxSwing

Also, I'm an Rx man, so I also use RxJava. I didn't have RxSwing for RxJava2.0, so I made only Scheduler by myself. A Scheduler that runs on EDT, the Swing UI thread. I referred to RxSwing for RxJava1.0 and RxAndroid.

Recommended Posts

Make a weekly git GUI client [4] diff et al.
Make a weekly git GUI client [2] stage / unstage incomplete capture
Create a weekly git GUI client [5] First desktop app
Now, git GUI client self-made is very popular!