[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#924245: unblock: imediff/2.2-1



Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock

Please unblock package imediff

(explain the reason for the unblock here)
 * This improves default color situation a lot.
 * This also include upstream improvement to ensure clean merge result.
 * Most line changes are documentation.
 * Most code changes are refactoring to clean up initialization.
 * File merge codes are tested during build extensively.

(include/attach the debdiff against the package in testing)

$ debdiff imediff_2.1-1.dsc imediff_2.2-1.dsc
diff -Nru imediff-2.1/debian/changelog imediff-2.2/debian/changelog
--- imediff-2.1/debian/changelog	2019-02-16 13:08:00.000000000 +0900
+++ imediff-2.2/debian/changelog	2019-03-03 21:15:46.000000000 +0900
@@ -1,3 +1,12 @@
+imediff (2.2-1) unstable; urgency=medium
+
+  * Refactor code and refine "g" key command.
+  * Use REVERSE for mode "g" for readability.
+  * Ensure clean save and add --sloppy option.
+  * Bump policy to 4.3.0 and compat to 12.
+
+ -- Osamu Aoki <osamu@debian.org>  Sun, 03 Mar 2019 21:15:46 +0900
+
 imediff (2.1-1) unstable; urgency=medium
 
   * Adjust package dependency for smooth transition from imediff2.
diff -Nru imediff-2.1/debian/compat imediff-2.2/debian/compat
--- imediff-2.1/debian/compat	2019-02-11 18:23:56.000000000 +0900
+++ imediff-2.2/debian/compat	1970-01-01 09:00:00.000000000 +0900
@@ -1 +0,0 @@
-11
diff -Nru imediff-2.1/debian/control imediff-2.2/debian/control
--- imediff-2.1/debian/control	2019-02-11 18:30:03.000000000 +0900
+++ imediff-2.2/debian/control	2019-03-03 21:15:46.000000000 +0900
@@ -2,14 +2,14 @@
 Section: text
 Priority: optional
 Maintainer: Osamu Aoki <osamu@debian.org>
-Build-Depends: debhelper (>= 11~),
+Build-Depends: debhelper-compat (= 12),
                dh-python,
                docbook-xsl,
                python3-all,
                python3-distutils-extra,
                python3-setuptools,
                xsltproc
-Standards-Version: 4.2.1
+Standards-Version: 4.3.0
 VCS-Git: https://github.com/osamuaoki/imediff.git -b master
 Vcs-Browser: https://github.com/osamuaoki/imediff
 Homepage: https://github.com/osamuaoki/imediff
diff -Nru imediff-2.1/doc/imediff.1 imediff-2.2/doc/imediff.1
--- imediff-2.1/doc/imediff.1	2019-02-11 18:23:20.000000000 +0900
+++ imediff-2.2/doc/imediff.1	2019-03-03 22:31:51.000000000 +0900
@@ -82,6 +82,11 @@
 Force monochrome display\&.
 .RE
 .PP
+\fB\-\-sloppy\fR
+.RS 4
+Allow one to save unresolved contents\&.
+.RE
+.PP
 \fB\-n\fR
 .RS 4
 Non\-interactive mode\&.
@@ -110,19 +115,19 @@
 .RS 4
 Start with diff by line between version A and version B for 2 files\&. (default for 2 files)
 .sp
-Start with diff by line between version A and version C for 3 files\&.
+Start with diff by line between version A and version C while using version B as base version for 3 files\&.
 .RE
 .PP
 \fB\-f\fR
 .RS 4
 Start with wdiff by character between version A and version B for 2 files\&.
 .sp
-Start with wdiff by character between version A and version C for 3 files\&.
+Start with wdiff by character between version A and version C while using version B as base version for 3 files\&.
 .RE
 .PP
 \fB\-g\fR
 .RS 4
-Set good mode from (a,b,c,e,f) if merged cleanly, or set mode (d) in case of conflicts (default for 3 files)\&.
+This is the default starting mode for 3 files\&. For the cleanly merged resolved portion, display wdiff by character between version A and version C while using version B as base version\&. For the unresolved portion, display diff by line, instead\&.
 .RE
 .PP
 \fB\-h\fR, \fB\-\-help\fR
@@ -147,7 +152,11 @@
 .PP
 x
 .RS 4
-Save and exit\&.
+Save and exit if merge is resolved\&. (default behavior)
+.sp
+Save and exit unconditionally\&. (when
+\fB\-\-sloppy\fR
+specified)
 .RE
 .PP
 q, ctrl+c
@@ -157,7 +166,7 @@
 .PP
 home, t, end, z
 .RS 4
-, Jump to start or end of chunks\&.
+Jump to start or end of chunks\&.
 .RE
 .PP
 h
@@ -202,97 +211,103 @@
 .PP
 N, tab
 .RS 4
-Jump to next changed chunk in diff mode (d)\&.
+Jump to next unresolved chunk (mode "d" or "f")\&.
 .RE
 .PP
-p , backspace
+p, backspace
 .RS 4
 Jump to previous changed chunk\&.
 .RE
 .PP
-P , back\-tab
+P, back\-tab
 .RS 4
-Jump to previous changed chunk in diff mode (d)\&.
+Jump to previous unresolved chunk (mode "d" or "f")\&.
 .RE
 .PP
 a
 .RS 4
-Set mode of the current chunk to display version A\&.
+Set mode of the current chunk to "a" to display version A\&.
 .RE
 .PP
 b
 .RS 4
-Set mode of the current chunk to display version B\&.
+Set mode of the current chunk to "b" to display version B\&.
 .RE
 .PP
 c
 .RS 4
-Set mode of the current chunk to display version C\&. (if available)
+Set mode of the current chunk to "c" to display version C for 3 files\&.
 .RE
 .PP
 d
 .RS 4
-Set mode of the current chunk to display diff by line\&.
+Set mode of the current chunk to "d" to display diff by line\&.
 .RE
 .PP
 e
 .RS 4
-Set mode of the current chunk to display editor buffer\&.
+Set mode of the current chunk to "e" to display editor buffer if available\&.
 .RE
 .PP
 f
 .RS 4
-Set mode of the current chunk to display wdiff by character\&.
+Set mode of the current chunk to "f" to display wdiff by character\&. If cleanly merged, mode is set to "g" instead\&.
 .RE
 .PP
 g
 .RS 4
-Set good mode from (a,b,c,e,f) if merged cleanly, or set mode (d) in case of conflicts for the current chunk\&.
+This causes automatic merge efforts on a chunk for 3 files in the following order\&.
+.sp
+If the editor result buffer has content, mode is set to "e"\&.
+.sp
+If a chunk is resolved cleanly, mode is set to "a", "c", or "g"\&. This overrides previous manual settings such as "a", "b", or "c"\&.
+.sp
+If a chunk isn\*(Aqt resolved cleanly, mode is left as mode "g" or "f"\&.
 .RE
 .PP
 shift+a
 .RS 4
-Set mode of all chunks to display version A\&.
+Apply "a" to all chunks\&.
 .RE
 .PP
 shift+b
 .RS 4
-Set mode of all chunks to display version B\&.
+Apply "b" to all chunks\&.
 .RE
 .PP
 shift+c
 .RS 4
-Set mode of all chunks to display version C\&. (if available)
+Apply "c" to all chunks\&.
 .RE
 .PP
 shift+d
 .RS 4
-Set mode of all chunks to display diff by line\&.
+Apply "d" to all chunks\&.
 .RE
 .PP
 shift+e
 .RS 4
-Set mode of all chunks to display editor buffer\&.
+Apply "e" to all chunks\&.
 .RE
 .PP
 shift+f
 .RS 4
-Set mode of all chunks to display wdiff by character\&.
+Apply "f" to all chunks\&.
 .RE
 .PP
 shift+g
 .RS 4
-Set good mode from (a,b,c,e,f) if merged cleanly, or set mode (d) in case of conflicts for all chunks\&.
+Apply "g" to all chunks\&.
 .RE
 .PP
 m
 .RS 4
-Launch external editor on the current chunk\&.
+Launch external editor on the current chunk, save its result to the editor buffer, and set its mode to "e"\&.
 .RE
 .PP
-shift+f
+shift+m
 .RS 4
-Remove display editor buffer of the current chunk\&.
+Remove the editor buffer of the current chunk when its mode is "e"\&.
 .RE
 .SH "FILE"
 .PP
diff -Nru imediff-2.1/doc/imediff.xml imediff-2.2/doc/imediff.xml
--- imediff-2.1/doc/imediff.xml	2019-02-11 18:23:20.000000000 +0900
+++ imediff-2.2/doc/imediff.xml	2019-03-03 22:31:51.000000000 +0900
@@ -107,14 +107,14 @@
     <variablelist>
 
       <varlistentry>
-        <term><option>-o <replaceable>file_output</replaceable></option>, <option>--output=<replaceable>file_output</replaceable></option></term>
+        <term><option>-o <replaceable>file_output</replaceable></option></term> <term><option>--output=<replaceable>file_output</replaceable></option></term>
         <listitem>
           <para>Write output to a given file. If missing, STDERR is used.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>-m</option>, <option>--mode</option></term>
+        <term><option>-m</option></term> <term><option>--mode</option></term>
         <listitem>
           <para>Display mode column.</para>
         </listitem>
@@ -128,6 +128,13 @@
       </varlistentry>
 
       <varlistentry>
+        <term><option>--sloppy</option></term>
+        <listitem>
+          <para>Allow one to save unresolved contents.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>-n</option></term>
         <listitem>
           <para>Non-interactive mode.</para>
@@ -159,7 +166,7 @@
         <term><option>-d</option></term>
         <listitem>
           <para>Start with diff by line between version A and version B for 2 files. (default for 2 files)</para>
-          <para>Start with diff by line between version A and version C for 3 files.</para>
+          <para>Start with diff by line between version A and version C while using version B as base version for 3 files.</para>
         </listitem>
       </varlistentry>
 
@@ -167,14 +174,15 @@
         <term><option>-f</option></term>
         <listitem>
           <para>Start with wdiff by character between version A and version B for 2 files.</para>
-          <para>Start with wdiff by character between version A and version C for 3 files.</para>
+          <para>Start with wdiff by character between version A and version C while using version B as base version for 3 files.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>-g</option></term>
         <listitem>
-          <para>Set good mode from (a,b,c,e,f) if merged cleanly, or set mode (d) in case of conflicts (default for 3 files).</para>
+          <para>This is the default starting mode for 3 files.  For the cleanly merged resolved portion, display wdiff by character between version A and version C while using version B as base version.
+          For the unresolved portion, display diff by line, instead.</para>
         </listitem>
       </varlistentry>
 
@@ -213,19 +221,20 @@
       <varlistentry>
         <term><keysym>x</keysym></term>
         <listitem>
-          <para>Save and exit.</para>
+          <para>Save and exit if merge is resolved. (default behavior)</para>
+          <para>Save and exit unconditionally. (when <option>--sloppy</option> specified)</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><keysym>q</keysym>, <keycombo action='simul'><keycap>ctrl</keycap><keysym>c</keysym></keycombo></term>
+        <term><keysym>q</keysym></term> <term><keycombo action='simul'><keycap>ctrl</keycap><keysym>c</keysym></keycombo></term>
         <listitem>
           <para>Exit without saving.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><keycap>home</keycap>, <keycap>t</keycap></term>, <term><keycap>end</keycap>, <keycap>z</keycap></term>
+        <term><keycap>home</keycap></term> <term><keycap>t</keycap></term> <term><keycap>end</keycap></term> <term><keycap>z</keycap></term>
         <listitem>
           <para>Jump to start or end of chunks.</para>
         </listitem>
@@ -246,28 +255,28 @@
       </varlistentry>
 
       <varlistentry>
-        <term><keysym>s</keysym>, <keysym>?</keysym></term>
+        <term><keysym>s</keysym></term> <term><keysym>?</keysym></term>
         <listitem>
           <para>Show the state of the merge.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><keycap>down</keycap>, <keycap>j</keycap>, <keycap>up</keycap>, <keycap>k</keycap></term>
+        <term><keycap>down</keycap></term> <term><keycap>j</keycap></term> <term><keycap>up</keycap></term> <term><keycap>k</keycap></term>
         <listitem>
           <para>Move scope of the display for 1 line.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><keycap>left</keycap>, <keycap>right</keycap></term>
+        <term><keycap>left</keycap></term> <term><keycap>right</keycap></term>
         <listitem>
           <para>Move scope of the display for 1 column.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><keycap>page up</keycap>, <keycap>page down</keycap></term>
+        <term><keycap>page up</keycap></term> <term><keycap>page down</keycap></term>
         <listitem>
           <para>Move scope of the display for screenfull lines.</para>
         </listitem>
@@ -281,128 +290,132 @@
       </varlistentry>
 
       <varlistentry>
-        <term><keysym>n</keysym>, <keycap>space</keycap></term>
+        <term><keysym>n</keysym></term> <term><keycap>space</keycap></term>
         <listitem>
           <para>Jump to next changed chunk.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><keysym>N</keysym>, <keycap>tab</keycap></term>
+        <term><keysym>N</keysym></term> <term><keycap>tab</keycap></term>
         <listitem>
-          <para>Jump to next changed chunk in diff mode (d).</para>
+          <para>Jump to next unresolved chunk (mode "d" or "f").</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><keysym>p</keysym> , <keycap>backspace</keycap></term>
+        <term><keysym>p</keysym></term> <term><keycap>backspace</keycap></term>
         <listitem>
           <para>Jump to previous changed chunk.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><keysym>P</keysym> , <keycap>back-tab</keycap></term>
+        <term><keysym>P</keysym></term> <term><keycap>back-tab</keycap></term>
         <listitem>
-          <para>Jump to previous changed chunk in diff mode (d).</para>
+          <para>Jump to previous unresolved chunk (mode "d" or "f").</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
         <term><keysym>a</keysym></term>
         <listitem>
-          <para>Set mode of the current chunk to display version A.</para>
+          <para>Set mode of the current chunk to "a" to display version A.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
         <term><keysym>b</keysym></term>
         <listitem>
-          <para>Set mode of the current chunk to display version B.</para>
+          <para>Set mode of the current chunk to "b" to display version B.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
         <term><keysym>c</keysym></term>
         <listitem>
-          <para>Set mode of the current chunk to display version C. (if available)</para>
+          <para>Set mode of the current chunk to "c" to display version C for 3 files.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
         <term><keysym>d</keysym></term>
         <listitem>
-          <para>Set mode of the current chunk to display diff by line.</para>
+          <para>Set mode of the current chunk to "d" to display diff by line.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
         <term><keysym>e</keysym></term>
         <listitem>
-          <para>Set mode of the current chunk to display editor buffer.</para>
+          <para>Set mode of the current chunk to "e" to display editor buffer if available.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
         <term><keysym>f</keysym></term>
         <listitem>
-          <para>Set mode of the current chunk to display wdiff by character.</para>
+          <para>Set mode of the current chunk to "f" to display wdiff by character.  If cleanly merged, mode is set to "g" instead.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
         <term><keysym>g</keysym></term>
         <listitem>
-          <para>Set good mode from (a,b,c,e,f) if merged cleanly, or set mode (d) in case of conflicts for the current chunk.</para>
+          <para>This causes automatic merge efforts on a chunk for 3 files in the following order.</para>
+          <para>If the editor result buffer has content, mode is set to "e".</para>
+          <para>If a chunk is resolved cleanly, mode is set to "a", "c", or "g".
+                This overrides previous manual settings such as "a", "b", or "c".</para>
+          <para>If a chunk isn't resolved cleanly, mode is left as mode "g" or "f".</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
         <term><keycombo action='simul'><keycap>shift</keycap><keysym>a</keysym></keycombo></term>
         <listitem>
-          <para>Set mode of all chunks to display version A.</para>
+          <para>Apply "a" to all chunks.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
         <term><keycombo action='simul'><keycap>shift</keycap><keysym>b</keysym></keycombo></term>
         <listitem>
-          <para>Set mode of all chunks to display version B.</para>
+          <para>Apply "b" to all chunks.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
         <term><keycombo action='simul'><keycap>shift</keycap><keysym>c</keysym></keycombo></term>
         <listitem>
-          <para>Set mode of all chunks to display version C. (if available)</para>
+          <para>Apply "c" to all chunks.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
         <term><keycombo action='simul'><keycap>shift</keycap><keysym>d</keysym></keycombo></term>
         <listitem>
-          <para>Set mode of all chunks to display diff by line.</para>
+          <para>Apply "d" to all chunks.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
         <term><keycombo action='simul'><keycap>shift</keycap><keysym>e</keysym></keycombo></term>
         <listitem>
-          <para>Set mode of all chunks to display editor buffer.</para>
+          <para>Apply "e" to all chunks.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
         <term><keycombo action='simul'><keycap>shift</keycap><keysym>f</keysym></keycombo></term>
         <listitem>
-          <para>Set mode of all chunks to display wdiff by character.</para>
+          <para>Apply "f" to all chunks.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
         <term><keycombo action='simul'><keycap>shift</keycap><keysym>g</keysym></keycombo></term>
         <listitem>
-          <para>Set good mode from (a,b,c,e,f) if merged cleanly, or set mode (d) in case of conflicts for all chunks.</para>
+          <para>Apply "g" to all chunks.</para>
         </listitem>
       </varlistentry>
 
@@ -410,14 +423,14 @@
       <varlistentry>
         <term><keysym>m</keysym></term>
         <listitem>
-          <para>Launch external editor on the current chunk.</para>
+          <para>Launch external editor on the current chunk, save its result to the editor buffer, and set its mode to "e".</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><keycombo action='simul'><keycap>shift</keycap><keysym>f</keysym></keycombo></term>
+        <term><keycombo action='simul'><keycap>shift</keycap><keysym>m</keysym></keycombo></term>
         <listitem>
-          <para>Remove display editor buffer of the current chunk.</para>
+          <para>Remove the editor buffer of the current chunk when its mode is "e".</para>
         </listitem>
       </varlistentry>
 
diff -Nru imediff-2.1/imediff/cli.py imediff-2.2/imediff/cli.py
--- imediff-2.1/imediff/cli.py	2019-02-11 18:23:20.000000000 +0900
+++ imediff-2.2/imediff/cli.py	2019-03-03 22:31:51.000000000 +0900
@@ -35,38 +35,6 @@
 from imediff.diff3lib import *
 
 
-def _new_mode2(tag, bf, new_mode):
-    if new_mode in "abdf":
-        mode = new_mode
-    elif new_mode in "eg":
-        if bf is None:
-            mode = "d"
-        else:
-            mode = "e"
-    else:  # even for "c" hidden command
-        mode = "d"
-    return mode
-
-
-def _new_mode3(tag, bf, new_mode):
-    if new_mode in "abcdf":
-        mode = new_mode
-    elif new_mode == "e":
-        if bf is not None:
-            mode = "e"
-        else:
-            mode = "d"
-    elif tag == "E" or tag == "e" or tag == "A":  # new_mode = 'g'
-        mode = "a"
-    elif tag == "C":  # new_mode = 'g'
-        mode = "c"
-    elif bf is not None:  # new_mode = 'g'
-        mode = "e"
-    else:  # new_mode = 'g'
-        mode = "g"
-    return mode
-
-
 class TextData:  # Non-TUI data
     """Non curses class to handle diff data for 2 or 3 lines"""
 
@@ -83,6 +51,7 @@
         self.list_a = list_a
         self.list_b = list_b
         self.list_c = list_c
+        self.sloppy = args.sloppy
         self.isjunk = args.isjunk
         self.edit_cmd = args.edit_cmd
         self.macro = args.macro
@@ -122,36 +91,32 @@
             sequence = SequenceMatcher2(isjunk, list_a, list_b, None)
             chunks = sequence.get_chunks()
             k1 = k2 = None
+            # Set initial mode to "a" or "d"
             self.chunks = [
-                (tag, i1, i2, j1, j2, k1, k2, _new_mode2(tag, bf, new_mode), row, bf)
+                (tag, i1, i2, j1, j2, k1, k2, "a" if tag == "E" else "d", row, bf)
                 for (tag, i1, i2, j1, j2) in chunks
             ]
             self.actives = [
-                j for j, (tag, i1, i2, j1, j2) in enumerate(chunks) if tag != "E"
+                j for j, (tag, i1, i2, j1, j2) in enumerate(chunks) if tag == "N"
             ]
         else:
             sequence = SequenceMatcher3(isjunk, list_a, list_b, list_c)
             chunks = sequence.get_chunks()
+            # Set initial mode to "a" or "d"
             self.chunks = [
-                (tag, i1, i2, j1, j2, k1, k2, _new_mode3(tag, bf, new_mode), row, bf)
+                (tag, i1, i2, j1, j2, k1, k2, "a" if tag in "Ee" else "d", row, bf)
                 for (tag, i1, i2, j1, j2, k1, k2) in chunks
             ]
-            for i, chunk in enumerate(self.chunks):
-                (tag, i1, i2, j1, j2, k1, k2, mode, row, bf) = chunk
-                if mode == "g":
-                    (clean_merge, content) = self.merge_wdiff3(i)
-                    if clean_merge:
-                        self.set_mode(i, "f")
-                    else:
-                        self.set_mode(i, "d")
             self.actives = [
                 j
                 for j, (tag, i1, i2, j1, j2, k1, k2) in enumerate(chunks)
-                if (tag != "E" and tag != "e")
+                if tag not in "Ee"
             ]
         # self.actives[i] = j -> self.rev_actives[j] = i
         self.rev_actives = dict()
         for i, j in enumerate(self.actives):
+            if new_mode != "d":
+                self.set_mode(j, new_mode)
             self.rev_actives[j] = i
         if len(self.actives) == 0:
             self.active = None
@@ -278,6 +243,11 @@
                 content = self.merge_wdiff2(i)
             else:  # self.diff_mode == 3
                 (clean_merge, content) = self.merge_wdiff3(i)
+        elif mode == "g":
+            if self.diff_mode == 2:
+                content = self.merge_diff(i)
+            else:  # self.diff_mode == 3
+                (clean_merge, content) = self.merge_wdiff3(i)
         else:
             error_exit("Bad mode='{}'\n".format(mode))
         # content is at least [] (at least empty list)
@@ -287,38 +257,48 @@
 
     def set_mode(self, i, new_mode):
         (tag, i1, i2, j1, j2, k1, k2, mode, row, bf) = self.chunks[i]
-        if self.diff_mode == 2:
-            self.chunks[i] = (
-                tag,
-                i1,
-                i2,
-                j1,
-                j2,
-                k1,
-                k2,
-                _new_mode2(tag, bf, new_mode),
-                row,
-                bf,
-            )
-        elif tag == "N" and new_mode == "g":
-            (clean_merge, content) = self.merge_wdiff3(i)
-            if clean_merge:
-                self.chunks[i] = (tag, i1, i2, j1, j2, k1, k2, "f", row, bf)
-            else:
-                self.chunks[i] = (tag, i1, i2, j1, j2, k1, k2, "d", row, bf)
-        else:
-            self.chunks[i] = (
-                tag,
-                i1,
-                i2,
-                j1,
-                j2,
-                k1,
-                k2,
-                _new_mode3(tag, bf, new_mode),
-                row,
-                bf,
-            )
+        if new_mode in "abd":
+            mode = new_mode
+        elif self.diff_mode == 2:  # mode is always in "abdef"
+            if new_mode == "c":
+                mode = "d"  # hidden alias
+            elif new_mode == "f":
+                mode = "f"
+            else:  # for new_mode in "eg"
+                if bf is not None:
+                    mode = "e"
+                else:  #  for mode in "abdef"
+                    pass
+        else:  # self.diff_mode == 3
+            if new_mode == "c":
+                mode = "c"
+            elif new_mode == "f":
+                (clean_merge, content) = self.merge_wdiff3(i)
+                if clean_merge:
+                    mode = "g"
+                else:
+                    mode = "f"
+            elif new_mode == "e":
+                if bf is not None:
+                    mode = "e"
+                else:  # for mode in "abcdefg"
+                    pass
+            else:  # new_mode == "g":
+                if bf is not None:
+                    mode = "e"
+                elif tag == "A":
+                    mode = "a"
+                elif tag == "C":
+                    mode = "c"
+                elif mode in "abceg":
+                    pass
+                else:  # for mode in "df" and tag == "N"
+                    (clean_merge, content) = self.merge_wdiff3(i)
+                    if clean_merge:
+                        mode = "g"
+                    else:
+                        pass  # mode to "d" or "f"
+        self.chunks[i] = (tag, i1, i2, j1, j2, k1, k2, mode, row, bf)
         self.update_textpad = True
         return
 
@@ -328,7 +308,7 @@
             self.set_mode(i, new_mode)
         return
 
-    def set_row(self, i, new_row):
+    def set_row(self, i, new_row):  # used by TUI
         (tag, i1, i2, j1, j2, k1, k2, mode, row, bf) = self.chunks[i]
         # row is passed by value but chunk is passed by reference !
         self.chunks[i] = (tag, i1, i2, j1, j2, k1, k2, mode, new_row, bf)
@@ -427,7 +407,7 @@
         """Count 'd' mode chunks"""
         count = 0
         for j, i in enumerate(self.actives):
-            if self.get_mode(i) == "d":
+            if self.get_mode(i) in "df":
                 count += 1
         return count
 
@@ -437,7 +417,7 @@
         active = None
         for j in range(self.active + 1, len(self.actives)):
             i = self.actives[j]
-            if self.get_mode(i) == "d":
+            if self.get_mode(i) in "df":
                 active = j
                 break
         if active is not None:
@@ -454,7 +434,7 @@
         active = None
         for j in range(self.active - 1, -1, -1):
             i = self.actives[j]
-            if self.get_mode(i) == "d":
+            if self.get_mode(i) in "df":
                 active = j
                 break
         if active is not None:
@@ -469,7 +449,7 @@
         active = None
         for j in range(0, self.active):
             i = self.actives[j]
-            if self.get_mode(i) == "d":
+            if self.get_mode(i) in "df":
                 active = j
                 break
         if active is not None:
@@ -484,7 +464,7 @@
         active = None
         for j in range(len(self.actives) - 1, self.active, -1):
             i = self.actives[j]
-            if self.get_mode(i) == "d":
+            if self.get_mode(i) in "df":
                 active = j
                 break
         if active is not None:
diff -Nru imediff-2.1/imediff/main.py imediff-2.2/imediff/main.py
--- imediff-2.1/imediff/main.py	2019-02-11 18:23:20.000000000 +0900
+++ imediff-2.2/imediff/main.py	2019-03-03 22:31:51.000000000 +0900
@@ -126,6 +126,9 @@
     pa.add_argument("--mode", "-m", action="store_true", help="Display mode column")
     pa.add_argument("--mono", action="store_true", help="Force monochrome display")
     pa.add_argument(
+        "--sloppy", action="store_true", help="Allow one to save unresolved contents"
+    )
+    pa.add_argument(
         "--version", "-V", action="store_true", help="Show version and license"
     )
     pa.add_argument(
@@ -147,7 +150,7 @@
     )
     pa.add_argument(
         "--macro", "-M", default="", help=argparse.SUPPRESS
-    )  # hidden option for debug and selftest
+    )  # hidden option for debug and self test
     pa.add_argument(
         "--template",
         "-t",
@@ -176,14 +179,14 @@
     elif args.b:
         args.default_mode = "b"
     elif args.c and args.diff_mode == 2:
-        args.default_mode = "d"  # hidden backward compatibility -c
+        args.default_mode = "d"  # backward compatibility -c
     elif args.c and args.diff_mode == 3:
         args.default_mode = "c"
     elif args.d or args.u:
         args.default_mode = "d"  # hidden backward compatibility -u
     elif args.f:
         args.default_mode = "f"
-    else:  # default
+    else:  # default=None or "g"
         if args.diff_mode == 2:
             args.default_mode = "d"  # default diff2
         else:
@@ -281,7 +284,7 @@
         editor = confs["config"]["editor"]
     args.edit_cmd = shutil.which(editor)
     if args.edit_cmd is None:
-        args.edit_cmd = "/usr/bin/editor" # safe fall back
+        args.edit_cmd = "/usr/bin/editor"  # safe fall back
     logger.debug("external editor {} found as {}".format(editor, args.edit_cmd))
 
     # normalize and process non-standard situation
diff -Nru imediff-2.1/imediff/tui.py imediff-2.2/imediff/tui.py
--- imediff-2.1/imediff/tui.py	2019-02-11 18:23:20.000000000 +0900
+++ imediff-2.2/imediff/tui.py	2019-03-03 22:31:51.000000000 +0900
@@ -79,8 +79,8 @@
  * mode d: display diff3(a,b,c) by line       in {color_d}
  * mode e: display editor result buffer       in {color_e}
  * mode f: display wdiff3(a,b,c) by character in {color_f}
- * mode g: set good mode from (a,b,c,e,f) if merged cleanly, or
-           set mode (d) in case of conflicts
+ * mode g: set good mode from (a,b,c,e,g) if merged cleanly, or
+           set mode (df) in case of conflicts
 
 key commands          induced actions
 {x:c}                     save and exit
@@ -148,7 +148,7 @@
 The automatic merge logic of the imediff command operates not only on the
 difference by line but on the difference by character.  This is another
 great feature of the imediff command. So for the non-overlapping changes, it
-always yields the clean merge.
+always yields the clean merge (mode "g").
 
 Merge with 2 files
 ==================
@@ -171,26 +171,31 @@
 single key command.  Pressing "a" displays the "file_a" content.  Pressing
 "b" displays the "file_b" content.
 
-By alternating "a" and "b" keys, you can easily see the difference very
-intuitively with the constant line of sight.  (This is the same great
+By alternating "a" and "b" keys, you can see the difference in place which
+is easy on you with the constant line of sight.  (This is the same great
 feature inherited from the original imediff2 program.)  
 
 You can display both the "file_a" content and the "file_b" content with 2
 key commands.  Pressing "d" displays 2 blocks of lines organized somewhat
-like "diff -u".  Pressing "f" displays intermixed 1 block of lines organized
-somewhat like "wdiff".
+like "diff -u" (mode "d").  Pressing "f" displays intermixed 1 block of
+lines organized somewhat like "wdiff" (mode "f").
 
 Pressing "m" starts an editor to edit the focused chunk from any modes to
 create a manually merged content.  Upon exiting the editor, its result is
-kept in the editor result buffer.  Even after pressing "a", "b", "d" or "f",
-the content of the editor result buffer can be recalled and displayed by
-pressing "e".
-
-When you press one of the upper case "A", "B", "D", "E", "F", this sets all
-chunks to the corresponding lower case mode.
-
-Once you are satisfied with the merge result on screen, type "x" to save the
-displayed content to "file_o" and exit the imediff program.
+kept in the editor result buffer.  Even after pressing "a", "b", "d", or 
+"f", the content of the editor result buffer can be recalled and displayed
+by pressing "e".
+
+Pressing "M" in mode "e" removes the editor result buffer.
+
+When you press one of the upper case "A", "B", "D", "E", "F", this sets
+all chunks to the corresponding lower case mode.
+
+Once you are satisfied with the cleanly merge result on screen, type "x" to
+save the displayed content to "file_o" and exit the imediff program.  Here,
+the editor buffer content is always treated as cleanly merged.  (This
+requirement of the cleanly merge result can be disabled by specifying the
+"--sloppy" option.)
 
 Although the imediff program is practically WYSIWYG, there is one notable
 exception.  For the deleted content in mode "a" or "b", the imediff program
@@ -226,10 +231,15 @@
 
 The key binding for "Merge with 3 files" is almost the same as that for
 "Merge with 2 files".  There are 2 notable extensions.  Pressing "c"
-displays the "file_c" content.  Pressing "g" sets the chunk to the new
-automatic merge starting mode.  Naturally, you need to use alternating "a"
-and "c" keys to see the difference, instead.  The rests are the same as
-"Merge with 2 files".
+displays the "file_c" content.  Pressing "g" causes automatic merge efforts
+on a chunk for 3 files in the following order:
+ * If the editor result buffer has content, mode is set to "e".
+ * If a chunk is resolved cleanly, mode is set to "a", "c", or "g".
+   This overrides previous manual settings such as "a", "b", or "c".
+ * If a chunk isn't resolved cleanly, mode is left as mode "g" or "f".
+
+By alternating "a" and "c" keys, you can see the difference in place.
+The rests are mostly the same as "Merge with 2 files".
 
 Terminal
 ========
@@ -246,8 +256,8 @@
    (Under the color terminal, this is displayed in white/normal.)
  * "#" for a un-selectable section means matched and changed files: a==c and
    a!=b.  (Under the color terminal, this is displayed in white/bold.)
- * "a", "b", "c", "d", "e", and "f" for selectable unmatched chunk are the
-   display mode of each chunk.  (Under the color terminal, these are
+ * "a", "b", "c", "d", "e", "f", and "g" for selectable unmatched chunk are
+   the display mode of each chunk.  (Under the color terminal, these are
    displayed in different colors.)
 
 Customization
@@ -264,6 +274,10 @@
 Note
 ====
 
+Some keys are aliased for "Merge with 2 files" for your convenience:
+ * "c" works as "d"
+ * "g" works as "e"
+
 The "diff3 -m" has an odd feature for the merge when "file_a" and "file_c"
 undergo identical changes from "file_b".  This imediff program results in a
 more intuitive merge result."""
@@ -418,14 +432,23 @@
                 c = self.getch_translated()
             ch = chr(c)
             if ch == "x" or c == curses.KEY_EXIT or c == curses.KEY_SAVE:
-                if not self.confirm_exit or self.popup(
-                    _("Do you save and exit? (Press '{y:c}' to exit)").format(
-                        y=self.rkc["y"]
-                    )
+                if self.sloppy or (
+                    not self.sloppy and self.get_unresolved_count() == 0
                 ):
-                    output = self.get_output()
-                    write_file(self.file_o, output)
-                    break
+                    if not self.confirm_exit or self.popup(
+                        _("Do you save and exit? (Press '{y:c}' to exit)").format(
+                            y=self.rkc["y"]
+                        )
+                    ):
+                        output = self.get_output()
+                        write_file(self.file_o, output)
+                        break
+                else:
+                    self.popup(
+                        _(
+                            "Unresolved contents exist. (Press '{y:c}' to continue)"
+                        ).format(y=self.rkc["y"])
+                    )
             elif ch == "q":
                 if not self.confirm_exit or self.popup(
                     _("Do you quit without saving? (Press '{y:c}' to quit)").format(
@@ -518,7 +541,7 @@
                         self.set_mode(self.actives[self.active], "a")
                 elif ch == "m":
                     self.editor(self.actives[self.active])
-                elif ch == "M":
+                elif ch == "M" and mode == "e":
                     self.del_editor(self.actives[self.active])
                 elif ch == "n" or c == curses.KEY_NEXT or ch == " ":
                     self.active_next()
@@ -610,14 +633,19 @@
             decor = curses.A_BOLD
             color_pair = 5
             prefix = mode + " "
-        else:  # 'f': # wdiff
+        elif mode == "f":  # wdiff
+            decor = curses.A_BOLD
+            color_pair = 6
+            prefix = mode + " "
+        else:  # 'g': # wdiff, cleanly merged
             decor = curses.A_BOLD
+            decor |= curses.A_REVERSE
             color_pair = 6
             prefix = mode + " "
         row = self.get_row(i)
         content = self.get_content(i)  # list()
         # Decorative "???" for deleted lines only for display
-        if len(content) == 0 and not (tag == "E" or tag == "e"):
+        if len(content) == 0 and tag not in "Ee":
             content = ["???"]  # override []
             if self.mono:
                 decor |= curses.A_REVERSE
diff -Nru imediff-2.1/test/test_diff23lib.py imediff-2.2/test/test_diff23lib.py
--- imediff-2.1/test/test_diff23lib.py	2019-02-11 18:23:20.000000000 +0900
+++ imediff-2.2/test/test_diff23lib.py	2019-03-03 22:31:51.000000000 +0900
@@ -28,6 +28,7 @@
 # path to test directory
 pwd = os.path.dirname(os.path.abspath(__file__))
 
+
 class TestImediff(unittest.TestCase):
 
     a = "a12b345c6789d"



unblock imediff/2.2-1

-- System Information:
Debian Release: buster/sid
  APT prefers testing
  APT policy: (500, 'testing'), (10, 'unstable')
Architecture: amd64 (x86_64)

Kernel: Linux 4.19.0-2-amd64 (SMP w/4 CPU cores)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE=en_US:en (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled


Reply to: