├── .gitignore ├── AUTHORS ├── LICENSE ├── README.md ├── _00_test_imediff_env.sh ├── _01_test_upload.sh ├── _02_test_dowload.sh ├── _03_pypi_upload.sh ├── _04_pypi_dowload.sh ├── debian ├── changelog ├── check_version ├── control ├── copyright ├── gbp.conf ├── imediff.docs ├── imediff.install ├── imediff.manpages ├── rules ├── salsa-ci.yml ├── source │ ├── format │ ├── options │ └── patch-header ├── tests │ ├── check-script │ └── control ├── upstream │ └── metadata └── watch ├── pyproject.toml ├── sample ├── 00_local_imediff_diff3.sh ├── README.md ├── curses_baseline.py ├── file_a2 ├── file_b2 ├── file_c2 ├── file_out_diff3 ├── file_out_imediff ├── test_argparse.py ├── test_curses00.py ├── test_curses01.py ├── test_curses02.py ├── test_curses03.py ├── test_curses04.py ├── test_logic_function.py ├── test_readlines.py └── test_report.py ├── src └── imediff │ ├── README.md │ ├── __init__.py │ ├── cli.py │ ├── config.py │ ├── data │ ├── git-ime.1 │ ├── git-ime.in │ ├── imediff │ └── imediff.1 │ ├── diff3lib.py │ ├── initialize_args.py │ ├── initialize_confs.py │ ├── install.py │ ├── lines2lib.py │ ├── main.py │ ├── safe_curses.py │ ├── tui.py │ └── utils.py ├── test ├── 00_local_bash.sh ├── 00_local_clean.sh ├── 00_local_git_ime.sh ├── 00_local_imediff.sh ├── 00_local_imediff_cfgs.sh ├── 10_local_imediff_abcdfg.sh ├── 10_local_imediff_diff23.sh ├── 10_local_imediff_doctest.sh ├── 80_local_imediff_log.sh ├── 80_local_imediff_test_dfg.sh ├── 90_local_git_ime_tests.sh ├── 90_local_imediff_tests.sh ├── 90_local_imediff_unittest.sh ├── README.md ├── __init__.py ├── _diff23.py ├── _imediff.py ├── file_a ├── file_a0 ├── file_a1 ├── file_b ├── file_b0 ├── file_b1 ├── file_c ├── file_c0 ├── file_c1 ├── test_unittest_all.py ├── z_diff23.ref ├── z_imediff-log.ref ├── z_imediff.conf.ref ├── z_imediff2.ref ├── z_imediff2_a.ref ├── z_imediff2_b.ref ├── z_imediff2_d.ref ├── z_imediff2_f.ref ├── z_imediff3.ref ├── z_imediff3_a.ref ├── z_imediff3_b.ref ├── z_imediff3_c.ref ├── z_imediff3_d.ref ├── z_imediff3_f.ref └── z_imediff3_g.ref └── usr ├── bin └── git-ime.in ├── lib └── git-core │ └── mergetools │ └── imediff └── share └── man └── man1 ├── Makefile ├── git-ime.1 ├── git-ime.xml ├── imediff.1 └── imediff.xml /.gitignore: -------------------------------------------------------------------------------- 1 | imediff/__pycache__ 2 | test/imediff.log 3 | # general things to ignore 4 | build/ 5 | dist/ 6 | *.egg-info/ 7 | *.egg 8 | *.py[cod] 9 | __pycache__/ 10 | *.so 11 | *~ 12 | 13 | # due to using tox and pytest 14 | .tox 15 | .cache 16 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Jarno Elonen 2 | Osamu Aoki 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IMEDIFF - 2-way/3-way merge tool (CLI, Ncurses) 2 | 3 | * Copyright (C) 2003,2004 Jarno Elonen 4 | * Copyright (C) 2018-2024 Osamu Aoki 5 | 6 | Released under the GNU General Public License, 2.0+. 7 | See LICENSE for details 8 | 9 | The latest upstream source: https://github.com/osamuaoki/imediff 10 | 11 | This package provides commands for use on unix-like systems. 12 | 13 | * `imediff` -- 2-way/3-way merge tool for a file 14 | * `git-ime` -- git commit unsquasher from `HEAD^` to `HEAD` 15 | * `imediff_install` -- (wheel only) installer of `git-ime` 16 | 17 | The Debian package tracker: https://tracker.debian.org/pkg/imediff 18 | 19 | ## Tutorial (deb) 20 | 21 | Please install the `imediff` package from the APT repository if you are on Debian/Ubuntu. 22 | 23 | For `imediff`, type at the console command line prompt: 24 | 25 | * "`imediff`" to read the tutorial 26 | * "`imediff -h`" to get all the command line options 27 | * "`imediff -o output older newer`" to merge 2 files 28 | * "`imediff -o output yours base theirs`" to merge 3 files 29 | * During its interactive execution, type "`h`" and "`H`" for usage instructions. 30 | 31 | For `git ime`: 32 | 33 | * Execute it in a git repository. 34 | 35 | See below "[Tutorial (wheel)](#tutorial-wheel)" for the non-system program installation. 36 | 37 | ## What is `imediff` 38 | 39 | The `imediff` command helps you to merge 2 slightly different files with an 40 | optional base file interactively or non-interactively with MACRO. (-Mw) 41 | 42 | For non-interactive 2-way diff operation, this can express diffs with ordinary 43 | diff-format. 44 | 45 | ```console 46 | $ imediff -Mw -n older.txt newer.txt -o diff.txt 47 | ``` 48 | 49 | For non-interactive 2-way diff operation, this can also express diffs with 50 | wdiff format 51 | 52 | ```console 53 | $ imediff -Mw -n -f older.txt newer.txt -o wdiff.txt 54 | ``` 55 | 56 | For non-interactive 3-way merge operation, this can not only express conflicts 57 | with ordinary diff3-format but also 3-way wdiff format 58 | 59 | ```console 60 | $ imediff -Mw -n myfile.txt oldfile.txt yourfile.txt -o merged.txt 61 | $ imediff -Mw -n -f myfile.txt oldfile.txt yourfile.txt -o wdiff-merged.txt 62 | ``` 63 | Here, this 3-way-merge logic is smarter than "diff3 -m". 64 | 65 | For interactive operation, this uses the in-place alternating display of the 66 | changed content on a single-pane full screen terminal user interface. The 67 | source of line is clearly identified by the color of the line or the identifier 68 | character at the first column. The advantage of this user interface is the 69 | minimal movement of the line of sight for the user. 70 | 71 | For interactive 2-way pick operation, this can select each section from one of 72 | the input files. 73 | 74 | ```console 75 | $ imediff older.txt newer.txt -o picked.txt 76 | ``` 77 | 78 | For interactive 3-way merge operation, this can select sections from input 79 | files. 80 | 81 | ```console 82 | $ imediff yours.txt base.txt theirs.txt -o merged.txt 83 | ``` 84 | 85 | The line matching logic of the `imediff` command has been improved to ignore 86 | whitespaces and use partial line matches to provide the best presentation with 87 | small chunk of lines. 88 | 89 | The automatic 3 way merge logic of the `imediff` command operates not only on 90 | the difference by line but on the difference by character. This is another 91 | great feature of the `imediff` command. So for the non-overlapping changes, it 92 | always yields the clean merge. 93 | 94 | You can also use the `imediff` command non-interactively from CLI with --macro 95 | option. 96 | 97 | ## What is `git-ime` 98 | 99 | The `git ime` command helps you to unsquash 2 consecutive commits from `HEAD^` 100 | to `HEAD` in a git repository. The "`git rebase -i `" and "`gitk`" 101 | can be used to organize unsquashed changes. 102 | 103 | If any staged changes or local uncommitted changes are found in the git 104 | repository, `git ime` immediately exits without changes to be on the safe side. 105 | 106 | If the latest commit involves multiple files, `git ime` splits this big commit 107 | into multiple smaller commits involving a single file for each commit. Unless 108 | `-k` option is used with `git ime`, the original commit message is discarded. 109 | 110 | If the latest commit involves only a single file, the splitting process is 111 | managed interactively by `imediff` with the interactive TUI. (You can force 112 | the non-interactive splitting by `-a` option.) 113 | 114 | This `git ime` is not only useful at the checked out branch head but also at 115 | "edit" prompt during the interactive execution of "`git rebase -i `". 116 | Execute `git ime` after committing the pending commit. 117 | 118 | ## Tutorial (wheel) 119 | 120 | You can install this program in the wheel package into a python virtual 121 | environment without contaminating your main system. 122 | 123 | ```console 124 | $ mkdir -p path/to/ 125 | $ cd path/to/ 126 | $ python3 -m venv venv_imediff 127 | $ source venv_imediff/bin/activate 128 | $ pip install -U imediff 129 | Collecting imediff 130 | Downloading imediff-2.9-py3-none-any.whl (62 kB) 131 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 62.9/62.9 kB 1.8 MB/s eta 0:00:00 132 | Installing collected packages: imediff 133 | Successfully installed imediff-2.9 134 | (venv_imediff) $ imediff_install 135 | I: successfully installed: /home/osamu/tmp/venv_imediff/bin/git-ime 136 | I: manual page for imediff can be found at: /home/osamu/tmp/venv_imediff/lib/python3.11/site-packages/imediff/imediff.1 137 | I: manual page for git-ime can be found at: /home/osamu/tmp/venv_imediff/lib/python3.11/site-packages/imediff/git-ime.1 138 | I: script for git-mergetool(1) can be found at: /home/osamu/tmp/venv_imediff/lib/python3.11/site-packages/imediff/imediff 139 | I: For more, see the upstream source site. 140 | I: * https://github.com/osamuaoki/imediff 141 | ``` 142 | 143 | Here, the `imediff_install` command installs the `git-ime` shell command to the 144 | pertinent python virtual environment etc. 145 | 146 | ## Note to developer (myself) 147 | 148 | * Both deb-package and wheel-package are build from "`main`" branch. 149 | * The upstream tarball for deb-package is made from a tagged commit of "`upstream/`" off "`main`" branch after "`rm -rf debian`" using "`git deborig`" (quasi-native). 150 | * The git repository is patch-applied one. 151 | 152 | ### build and test this program using the local wheel package 153 | 154 | You must have a relatively new system with python 3.9 equivalent of Debian 12.0 155 | (bookworm) released on June 10th, 2023 with: 156 | 157 | * https://github.com/pypa/setuptools (>=61.0.0) 158 | * https://github.com/pypa/installer/ 159 | * https://github.com/pypa/build 160 | * https://github.com/hukkin/tomli 161 | 162 | ```console 163 | $ cd /path/to 164 | $ python3 -m venv venv_imediff 165 | $ source venv_imediff/bin/activate 166 | $ git clone https://github.com/osamuaoki/imediff.git 167 | $ cd imediff 168 | ... hack 169 | $ python3 -m build 170 | $ cd build 171 | $ pip install imediff-*.whl 172 | ``` 173 | This is a good way to build and test program without contaminating the whole system. 174 | 175 | The building of rpm is not supported as out-of-box now (patch welcome). 176 | 177 | Code is not written for Windows compatibility in mind, yet. Gettext support was 178 | intentionally dropped in favor of better compatibility across various systems. 179 | 180 | The `git-ime` command requires you to have a POSIX shell and the `git` 181 | command access. 182 | 183 | ### making Debian package 184 | 185 | You can make your own Debian package as: 186 | 187 | ```console 188 | $ git clone https://github.com/osamuaoki/imediff.git 189 | $ cd imediff 190 | $ git checkout main 191 | ... hack source 192 | $ git commit -a 193 | $ rm -rf debian 194 | $ git add -A . 195 | $ git commit 196 | $ git tag 2.5 197 | $ git reset --hard HEAD^ 198 | $ git deborig # to make ../*.orig.tar.xz 199 | $ sbuild 200 | $ cd .. 201 | $ sudo dpkg -i imediff_2.5-1_all.deb 202 | ``` 203 | 204 | Here, we assume the upstream version to be 2.5, and the Debian revision to be 205 | 1. 206 | 207 | If you have bug fixes or feature enhancement propose changes to me via "pull 208 | request" 209 | 210 | ### updating source code 211 | 212 | Please make sure to fit each code below 80-88 chars. (Run `black` on the python 213 | code and `shfmt` on the shell code) In case if reformat errors, check its syntax by: 214 | 215 | ```console 216 | $ python3 -m py_compile program.py 217 | $ dash -n usr/bin/git-ime 218 | ``` 219 | 220 | ### updating manpages 221 | 222 | Manpages need to be updated from XML files with `make` first in the `doc/` 223 | directory when you edit it. 224 | 225 | If you wish to update manpages from XML, `docbook-xsl` and `xsltproc` are 226 | needed for building manpages from xml sources then manually touch up details. 227 | 228 | ### testing python source 229 | 230 | Whenever you make changes, please test them. 231 | 232 | To test the code in the module without installing it, invoke the test script as: 233 | 234 | ```console 235 | $ cd /path/to/source-root 236 | $ export PYTHONPATH=$(pwd)/src/ 237 | $ python3 test/test_unittest_all.py -v 238 | ``` 239 | 240 | To test the installed module, invoke the test script as: 241 | 242 | ```console 243 | $ cd /path/to/source-root 244 | $ python3 test/test_unittest_all.py -v 245 | ``` 246 | 247 | ### History and features 248 | 249 | This was originally written by Jarno Elonen in Python2. The latest original 250 | upstream version was 1.1.2 released on 2007-8-18. 251 | 252 | The original author's website was https://elonen.iki.fi/code/imediff/ . Now it 253 | redirects to this site https://github.com/osamuaoki/imediff . 254 | 255 | Osamu Aoki made a minor patched release for Debian buster in Oct 2018. 256 | 257 | * No more surprise hitting `q`. You will be asked. (Fix Debian bug #799865) 258 | * Fix manpage generation issue (Fix Debian bug #860351) 259 | * New `git ime` wrapper script to unsquash a big squashed commit into many 260 | commits for each file 261 | * You can customize key bindings. 262 | 263 | Osamu also wanted to add some features: 264 | 265 | * Use of Python3 with `pyproject.toml` and `setuptools` to organize the source 266 | into a module with multiple source files. 267 | * Use standard libraries for the flexible customization (`argparse`, 268 | `configparser`, `logging`) 269 | * Addition of the diff3 merge capability 270 | * Addition of the wdiff capability 271 | * Addition of the cursor location display capability 272 | * Make it manually edit highlighted section for manual resolution 273 | * Make its TUI more friendly under monochrome terminal 274 | * Use curses.wrapper() 275 | * CLI and logging interface for easy self-testing/debugging 276 | * Add decent test cases 277 | * Include a simple tutorial within `imediff`. 278 | * Add `git-mergetool` integration. 279 | * Add `git ime` to help making partial patch series commits to git. 280 | * Good CJK wide character support with East_Asian_Width on console. 281 | * The use of GNU gettext for `imediff` is removed for the sake of portability via wheel. 282 | 283 | This was accomplished by practically a whole rewrite of the source code 284 | originally released as `imediff2` by Jarno Elonen after consulting with him 285 | around November-December 2018. Now program name is `imediff` without `2`, 286 | since it supports diff for not only 2 files but also 3 files. The version 287 | number is bumped to 2.0. In version 2.5, line matching rules of `imediff` are 288 | updated to produce better diff presentation. In version 2.10-2.11, `git ime` 289 | was rewitten to cope with commits involving rename and delete of files. 290 | In version 3.0, TUI was rewritten to cope with curses library limitations. 291 | 292 | 293 | ### Note on Debian package links 294 | 295 | * imediff2 (based on older python2 source for and before stretch) 296 | * https://tracker.debian.org/pkg/imediff2 297 | * https://packages.debian.org/source/sid/imediff2 (source package in Debian) 298 | * https://packages.debian.org/sid/imediff2 (binary package in Debian) 299 | * https://bugs.debian.org/cgi-bin/pkgreport.cgi?repeatmerged=0;src=imediff2 (BTS) 300 | 301 | * imediff: (based on newer python3 source for buster) 302 | * https://tracker.debian.org/pkg/imediff 303 | * https://packages.debian.org/source/sid/imediff (source package in Debian) 304 | * https://packages.debian.org/sid/imediff (binary package in Debian) 305 | * https://bugs.debian.org/cgi-bin/pkgreport.cgi?repeatmerged=0;src=imediff (BTS) 306 | 307 | This is written and updated by Osamu Aoki on February 2024. 308 | 309 | -------------------------------------------------------------------------------- /_00_test_imediff_env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # 4 | # test script used before uploading wheel package to pypi.org 5 | # 6 | type imediff 7 | type git-ime 8 | git reset --hard HEAD 9 | git clean -dfx 10 | python3 -m build 11 | echo -n "I: starting test environment at pwd = " 12 | pwd 13 | python3 -m venv venv 14 | . venv/bin/activate 15 | echo "I: starting test environment" 16 | pip install dist/imediff-*-any.whl 17 | imediff_install 18 | type imediff 19 | type git-ime 20 | echo "I: imediff installed with helper script" 21 | imediff test/file_a test/file_b test/file_c -o test/file_out 22 | echo "I: starting subshell to continue. Type ^D to exit" 23 | bash -i 24 | echo "I: exit test environment" 25 | -------------------------------------------------------------------------------- /_01_test_upload.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # 4 | # script for uploading wheel package to test.pypi.org 5 | # 6 | git reset --hard HEAD 7 | git clean -dfx 8 | python3 -m build 9 | echo -n "I: starting test upload environment at pwd = " 10 | pwd 11 | python3 -m venv venv 12 | . venv/bin/activate 13 | python3 -m pip install --upgrade twine 14 | python3 -m twine upload --repository testpypi dist/* 15 | echo "I: check upload at https://test.pypi.org/project/imediff" 16 | echo "I: exit test environment" 17 | -------------------------------------------------------------------------------- /_02_test_dowload.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # 4 | # script for downloading wheel package from test.pypi.org 5 | # 6 | type imediff 7 | type git-ime 8 | git reset --hard HEAD 9 | git clean -dfx 10 | echo -n "I: starting test download environment at pwd = " 11 | pwd 12 | python3 -m venv venv 13 | . venv/bin/activate 14 | python3 -m pip install --index-url https://test.pypi.org/simple/ --no-deps imediff 15 | type imediff 16 | type git-ime 17 | echo "I: starting subshell to continue. Type ^D to exit" 18 | bash -i 19 | echo "I: exit test environment" 20 | -------------------------------------------------------------------------------- /_03_pypi_upload.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # 4 | # script for uploading wheel package to official pypi.org 5 | # 6 | git reset --hard HEAD 7 | git clean -dfx 8 | python3 -m build 9 | echo -n "I: starting upload environment at pwd = " 10 | pwd 11 | python3 -m venv venv 12 | . venv/bin/activate 13 | python3 -m pip install --upgrade twine 14 | python3 -m twine upload dist/* 15 | echo "I: check upload at https://pypi.org/project/imediff" 16 | echo "I: exit test environment" 17 | -------------------------------------------------------------------------------- /_04_pypi_dowload.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # 4 | # script for downloading wheel package from official pypi.org 5 | # 6 | type imediff 7 | type git-ime 8 | git reset --hard HEAD 9 | git clean -dfx 10 | echo -n "I: starting test download environment at pwd = " 11 | pwd 12 | python3 -m venv venv 13 | . venv/bin/activate 14 | type imediff 15 | python3 -m pip install --no-deps imediff 16 | type imediff 17 | type git-ime 18 | echo "I: starting subshell to continue. Type ^D to exit" 19 | bash -i 20 | echo "I: exit test environment" 21 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | imediff (3.4.0-1) unstable; urgency=medium 2 | 3 | * Redesign logging of imediff as an optional behavior to address 4 | https://github.com/osamuaoki/imediff/issues/11 5 | * Redesign short command options of imediff for consistency. 6 | * Update documentation (tutorial, manpage). 7 | * Update test scripts. 8 | * Fixed bug for END/9. 9 | 10 | -- Osamu Aoki Tue, 04 Feb 2025 20:05:11 +0900 11 | 12 | imediff (3.3.2-1) unstable; urgency=medium 13 | 14 | * Update git-ime since TUI has no line limitation. 15 | 16 | -- Osamu Aoki Thu, 14 Nov 2024 23:53:30 +0900 17 | 18 | imediff (3.3.1-1) unstable; urgency=medium 19 | 20 | * Fix python warning for \s etc. Closes: #1087234 21 | 22 | -- Osamu Aoki Sun, 10 Nov 2024 21:24:16 +0900 23 | 24 | imediff (3.3-1) unstable; urgency=medium 25 | 26 | * Program version bump. 27 | 28 | -- Osamu Aoki Sun, 10 Nov 2024 17:21:15 +0900 29 | 30 | imediff (3.2-1) unstable; urgency=medium 31 | 32 | * UI consistency with SPACE (scroll or exit). 33 | 34 | -- Osamu Aoki Sun, 10 Nov 2024 17:08:21 +0900 35 | 36 | imediff (3.1-1) unstable; urgency=medium 37 | 38 | * Fixed imediff minor bugs 39 | * Add pypi-support scripts 40 | * Add test/00_local_imediff.sh dev-helper scripts 41 | * Add sample/00_local_imediff_diff3.sh and results 42 | * Drop outdated reminder on file size limitation 43 | * Add /WHITE support feature 44 | * Reasonable -a, -b, -c, -d starting behavior for diff3 45 | * Refine Tutorial text. 46 | 47 | -- Osamu Aoki Sat, 09 Nov 2024 21:39:48 +0900 48 | 49 | imediff (3.0-1) unstable; urgency=medium 50 | 51 | * Embed version in git-ime. 52 | * Rewite code to avoid hitting curses library limitations. 53 | 54 | -- Osamu Aoki Thu, 07 Nov 2024 22:13:19 +0900 55 | 56 | imediff (2.11-1) unstable; urgency=medium 57 | 58 | * Fix major git-ime regression for changes on a single file. 59 | * Improve git-ime to cope with renames and deletes. 60 | * Stop using relative path import for imediff and clean up code. 61 | * Simplify Debian packaging using quasi-native with auto-commit. 62 | 63 | -- Osamu Aoki Sun, 06 Oct 2024 17:03:25 +0900 64 | 65 | imediff (2.10-1) unstable; urgency=medium 66 | 67 | * Update to new upstream version 2.10. 68 | * revise documentation 69 | * remove unused function in git-ime 70 | 71 | -- Osamu Aoki Wed, 14 Aug 2024 14:18:48 +0900 72 | 73 | imediff (2.9-1) unstable; urgency=medium 74 | 75 | * Updated git-ime 76 | 77 | -- Osamu Aoki Wed, 14 Aug 2024 00:33:17 +0900 78 | 79 | imediff (2.8-1) unstable; urgency=medium 80 | 81 | * Update src/imediff/* 82 | * Update test/* 83 | * Shuffle files for Linux 84 | * pyproject.toml migration 85 | * Update debian/* 86 | * Don't use setuptools_scm for Debian 87 | * Remove imediff_install for Debian 88 | 89 | -- Osamu Aoki Sat, 10 Feb 2024 23:26:09 +0900 90 | 91 | imediff (2.7-1) unstable; urgency=medium 92 | 93 | * More logging before curses-related crash 94 | * Update README.md to address curses-related crash 95 | * Enhance git-ime to use --auto for large file to avoid crash 96 | * Add and enhance test cases (word diff) 97 | * Add pybuild-plugin-pyproject 98 | 99 | -- Osamu Aoki Sun, 28 Jan 2024 17:44:25 +0900 100 | 101 | imediff (2.6-1) unstable; urgency=medium 102 | 103 | * Fix bug in git-ime for diff split a file into many files ( >8) 104 | 105 | -- Osamu Aoki Sun, 06 Feb 2022 08:25:12 +0900 106 | 107 | imediff (2.5-1) unstable; urgency=medium 108 | 109 | * Match lines with whitespace changes. 110 | * Match lines with partial head or tail string. 111 | * Fix bug in 3-way merge. 112 | * Better debug logging. 113 | * Drop unused i18n/gettext code for the ease of portability 114 | 115 | -- Osamu Aoki Tue, 26 Oct 2021 16:00:06 +0900 116 | 117 | imediff (2.4-6) unstable; urgency=medium 118 | 119 | * Disable automatic tagging during git-rebase. 120 | 121 | -- Osamu Aoki Tue, 14 Sep 2021 14:15:49 +0900 122 | 123 | imediff (2.4-5) unstable; urgency=medium 124 | 125 | * More CI-test codes. 126 | * Automatic tagging for git-ime. 127 | 128 | -- Osamu Aoki Tue, 14 Sep 2021 13:41:05 +0900 129 | 130 | imediff (2.4-4) unstable; urgency=medium 131 | 132 | * Update description of git-ime. 133 | * Update documentation for non-Debian/Ubuntu system. 134 | 135 | -- Osamu Aoki Sun, 15 Aug 2021 16:49:12 +0900 136 | 137 | imediff (2.4-3) unstable; urgency=medium 138 | 139 | * Fix regression caused by refactoring 1810b81 (2021-06-03) 140 | commit which caused broken -t option. 141 | 142 | -- Osamu Aoki Wed, 21 Jul 2021 10:57:53 +0900 143 | 144 | imediff (2.4-2) unstable; urgency=medium 145 | 146 | * Fix main.py -> __main__.py change left over. 147 | 148 | -- Osamu Aoki Sat, 03 Jul 2021 22:32:58 +0900 149 | 150 | imediff (2.4-1) unstable; urgency=medium 151 | 152 | * Fix git-ime bug for the handling of moved file. 153 | 154 | -- Osamu Aoki Sat, 03 Jul 2021 16:39:55 +0900 155 | 156 | imediff (2.3-1) unstable; urgency=medium 157 | 158 | [ Debian Janitor ] 159 | * Trim trailing whitespace. 160 | * Set upstream metadata fields: Bug-Database, Bug-Submit. 161 | * Update standards version to 4.5.0, no changes needed. 162 | 163 | [ Osamu Aoki ] 164 | * Bump upstream version to 2.3. 165 | * dgit compatible git branch tree. 166 | * Modern static setup.cfg and pyproject.toml with setuptools. 167 | * Move module source under src/ 168 | * imediff: factor out create_template 169 | * imediff: remove tailing spaces in imediff/* 170 | * imediff: refine UI for non-clean save etc. 171 | * imediff: update test scripts 172 | * git-ime: lint with shellcheck 173 | * git-ime: reorganized to address commit with multiple files 174 | * doc: update manpage, update README.md 175 | * Update standards version to 4.5.1 and debhelper-compat to 13. 176 | 177 | -- Osamu Aoki Tue, 29 Jun 2021 13:02:40 +0900 178 | 179 | imediff (2.2-1.1) unstable; urgency=medium 180 | 181 | * Non maintainer upload by the Reproducible Builds team. 182 | * No source change upload to rebuild on buildd with .buildinfo files. 183 | 184 | -- Holger Levsen Sun, 03 Jan 2021 17:52:53 +0100 185 | 186 | imediff (2.2-1) unstable; urgency=medium 187 | 188 | * Refactor code and refine "g" key command. 189 | * Use REVERSE for mode "g" for readability. 190 | * Ensure clean save and add --sloppy option. 191 | * Bump policy to 4.3.0 and compat to 12. 192 | 193 | -- Osamu Aoki Sun, 03 Mar 2019 21:15:46 +0900 194 | 195 | imediff (2.1-1) unstable; urgency=medium 196 | 197 | * Adjust package dependency for smooth transition from imediff2. 198 | 199 | -- Osamu Aoki Sat, 16 Feb 2019 13:08:00 +0900 200 | 201 | imediff (2.0-1) unstable; urgency=medium 202 | 203 | * New upstream release in python3 with diff3 support etc. as imediff. 204 | Closes: #920810 205 | * Add autopkgtest support. 206 | 207 | -- Osamu Aoki Wed, 30 Jan 2019 00:01:23 +0900 208 | 209 | imediff2 (1.1.2.1-2) unstable; urgency=medium 210 | 211 | * Ship git-ime and git-ime.1. 212 | 213 | -- Osamu Aoki Sun, 21 Oct 2018 02:45:03 +0900 214 | 215 | imediff2 (1.1.2.1-1) unstable; urgency=medium 216 | 217 | * New upstream release by DD with configurable key bindings. 218 | * Salvage upload. 219 | * Add safe guards to the 'q' command. Closes: #799865 220 | * Update manpage XML source and rebuild it. Closes: #860351 221 | * Use source/format 3.0 (quilt) 222 | * Use "dh $@" in debian/rules. 223 | 224 | -- Osamu Aoki Sun, 21 Oct 2018 01:54:49 +0900 225 | 226 | imediff2 (1.1.2-3) unstable; urgency=medium 227 | 228 | * Add dh-python, python-setuptools and python-all-dev to Build-Depends. 229 | * Correct typo in bug number reference in previous version (1.1.2-2) 230 | of changelog. Closes: #831966. 231 | 232 | -- Kevin Coyner Wed, 12 Oct 2016 16:22:53 -0400 233 | 234 | imediff2 (1.1.2-2) unstable; urgency=medium 235 | 236 | * debian/rules: 237 | + Changed build architecture line to fix RC bug FTBFS binary build with no 238 | binary artifacts found. Closes: #831966. 239 | + Changed call to dh_clean -k to dh_prep. 240 | + Added build commands to build-arch build-indep. 241 | * debian/control: 242 | + Bumped Standards-Version to 3.9.8. No changes. 243 | + Bumped debhelper to >= 9. 244 | + Add ${misc:Depends}. 245 | * Bumped debian/compat to 9. 246 | 247 | -- Kevin Coyner Tue, 11 Oct 2016 06:46:12 -0400 248 | 249 | imediff2 (1.1.2-1.1) unstable; urgency=low 250 | 251 | * Non-maintainer upload. 252 | * Convert to dh_python2 (Closes: #616847). 253 | * Define homepage field (Closes: #615420). 254 | 255 | -- Luca Falavigna Thu, 30 May 2013 20:25:45 +0200 256 | 257 | imediff2 (1.1.2-1) unstable; urgency=low 258 | 259 | * New upstream release. 260 | * New maintainer. Closes: #425240. 261 | * Changed debian/copyright to include standard paragraph about GPL. 262 | * debian/control: 263 | + Bumped Standards-Version to 3.7.2. No changes. 264 | + Bumped debhelper to >= 5.0.38. 265 | + Added homepage. 266 | + Changed Build-Depends-Indep to Build-Depends. 267 | + Added Build-Depends on python-central and python-all-dev. 268 | + Added XB-Python-Version and XS-Python-Version. 269 | * Bumped debian/compat to 5. 270 | * Edited debian/watch to look for current versioning style in upstream. Also 271 | updated to version 3. 272 | * debian/rules: 273 | + Changed to dh_pycentral. 274 | + Removed dh_python. 275 | + Cleaned up cruft. 276 | * Added debian/pycompat. 277 | 278 | -- Kevin Coyner Thu, 16 Aug 2007 21:02:40 -0400 279 | 280 | imediff2 (1.1.1-2) unstable; urgency=low 281 | 282 | * Orphaning the package. 283 | 284 | -- Jarno Elonen Fri, 18 May 2007 12:18:09 +0300 285 | 286 | imediff2 (1.1.1-1) unstable; urgency=low 287 | 288 | * New upstream release 289 | - Applied some patches by Wolfram Sang. Thank you! 290 | + Adds '--new-file' option (like 'diff') 291 | + Better error messages 292 | + KEY_BACKSPACE goes to previous chunk 293 | + '-o file' is not required anymore (for preview, no saving) 294 | + help screen shows which color means which file 295 | 296 | -- Jarno Elonen Fri, 10 Feb 2006 19:13:57 +0200 297 | 298 | imediff2 (1.1.0+20041113-1) unstable; urgency=low 299 | 300 | * New upstream release (from SVN) 301 | - typo correction in help file 302 | 303 | * Changed watch file to point to Alioth 304 | 305 | -- Jarno Elonen Sat, 13 Nov 2004 01:18:42 +0200 306 | 307 | imediff2 (1.1.0-1) unstable; urgency=low 308 | 309 | * New upstream release 310 | - launch external editor with 'e' 311 | - more effective keyboard commands 312 | - documentation license changed from 313 | GFDL to Public Domain 314 | 315 | -- Jarno Elonen Fri, 22 Oct 2004 22:27:04 +0300 316 | 317 | imediff2 (1.0.2-1) unstable; urgency=low 318 | 319 | * New upstream release 320 | 321 | * Added a watch file 322 | 323 | -- Jarno Elonen Sun, 10 Oct 2004 02:44:39 +0300 324 | 325 | imediff2 (1.0.1-2) unstable; urgency=low 326 | 327 | * Added a watch file 328 | 329 | -- Jarno Elonen Sun, 10 Oct 2004 01:57:17 +0300 330 | 331 | imediff2 (1.0.1-1) unstable; urgency=low 332 | 333 | * New upstream release. 334 | - Fix some screen (re)size problems (closes: #250627) 335 | 336 | * _Really_ changed maintainer email address 337 | 338 | -- Jarno Elonen Mon, 24 May 2004 21:16:53 +0300 339 | 340 | imediff2 (1.0-3) unstable; urgency=low 341 | 342 | * Copyright update 343 | * Changed maintainer email address 344 | 345 | -- Jarno Elonen Fri, 16 Jan 2004 11:41:41 +0200 346 | 347 | imediff2 (1.0-2) unstable; urgency=low 348 | 349 | * Support for the alternatives system ('/usr/bin/merge2') 350 | 351 | * Use the packaging automation provided by dh_python 352 | 353 | * First upload to Main (closes: #221135) 354 | 355 | -- Jarno Elonen Sat, 15 Nov 2003 14:46:34 +0200 356 | 357 | imediff2 (1.0-1) unstable; urgency=low 358 | 359 | * Initial Release. 360 | 361 | -- Jarno Elonen Sun, 27 Apr 2003 02:11:31 +0300 362 | -------------------------------------------------------------------------------- /debian/check_version: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | UPSTREAM_VERSION="$(sed -n -e 's/__version__ *= *"\([0-9.]*\)"/\1/p' src/imediff/__init__.py)" 3 | DEB_VERSION="$(dpkg-parsechangelog -S version | sed -e 's/-.[^-]*$//')" 4 | echo "I: $UPSTREAM_VERSION -- upstream version in src/imediff/__init__.py" 5 | echo "I: $DEB_VERSION -- debian version in debian/changelog" 6 | if [ "$UPSTREAM_VERSION" = "$DEB_VERSION" ]; then 7 | echo "I: VERSION MATCHED -- GOOD" 8 | else 9 | echo "E: VERSION NON-MATCH -- BAD" 10 | echo "E: >>> Fix debian/changelog file with 'dch'" 11 | exit 1 12 | fi 13 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: imediff 2 | Section: text 3 | Priority: optional 4 | Maintainer: Osamu Aoki 5 | Build-Depends: debhelper-compat (= 13), 6 | dh-python, 7 | pybuild-plugin-pyproject, 8 | python3-all, 9 | python3-setuptools 10 | Standards-Version: 4.7.0 11 | VCS-Git: https://github.com/osamuaoki/imediff.git -b main 12 | Vcs-Browser: https://github.com/osamuaoki/imediff 13 | Homepage: https://github.com/osamuaoki/imediff 14 | 15 | Package: imediff 16 | Architecture: all 17 | Depends: vim | emacs | editor, ${misc:Depends}, ${python3:Depends} 18 | Recommends: git 19 | Provides: imediff2 20 | Breaks: imediff2 (<< 1.1.2.1-3) 21 | Replaces: imediff2 (<< 1.1.2.1-3) 22 | Description: interactive full screen 2/3-way merge tool 23 | The imediff command helps you to merge 2 slightly different files with 24 | an optional base file interactively using the in-place alternating 25 | display of the changed content on a single-pane full screen terminal 26 | user interface. 27 | . 28 | The source of line is clearly identified by the color of the line or the 29 | identifier character at the first column. 30 | . 31 | The advantage of this user interface is the minimal movement of the line 32 | of sight for the user. 33 | . 34 | The line matching logic of imediff has been improved to ignore whitespaces 35 | and use partial line matches to provide the best presentation with small 36 | chunk of lines. 37 | . 38 | The automatic 3 way merge logic of the imediff command operates not only on 39 | the difference by line but on the difference by character. This is another 40 | great feature of the imediff command. So for the non-overlapping changes, 41 | it always yields the clean merge. 42 | . 43 | The "git ime" command helps you to unsquash 2 consecutive commits (`HEAD^`, 44 | `HEAD`) of a git repository. The "git rebase -i " and "gitk" can 45 | be used to organize unsquashed changes. 46 | . 47 | If any staged changes or local uncommitted changes are found in the git 48 | repository, "git ime" immediately exits without changes to be on the 49 | safe side. 50 | . 51 | If the latest commit involves multiple files, "git ime" splits this big 52 | commit by the file into multiple smaller commits involving a single file 53 | for each commit. 54 | . 55 | If the latest commit involves only a single file, the commit is split 56 | into multiple smaller commits involving a set of minimal partial 57 | changes by imediff to be managed interactively later. 58 | . 59 | This "git ime" is not only useful at the checked out branch head but 60 | also at "edit" prompt during the interactive execution of "git rebase -i 61 | ". Execute "git ime" after committing the pending commit. 62 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: imediff 3 | Source: https://github.com/osamuaoki/imediff 4 | 5 | Files: * 6 | Copyright: 2003-2004 Jarno Elonen 7 | 2015-2024 Osamu Aoki 8 | License: GPL-2.0+ 9 | This program is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU General Public License as 11 | published by the Free Software Foundation; either version 2 of 12 | the License, or (at your option) any later version. 13 | . 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | . 19 | You should have received a copy of the GNU General Public 20 | License along with this program; if not, write to the Free 21 | Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 | Boston, MA 02110-1301, USA. 23 | . 24 | On Debian systems, the complete text of the GNU General Public License 25 | Version 2 can be found in `/usr/share/common-licenses/GPL-2'. 26 | -------------------------------------------------------------------------------- /debian/gbp.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | debian-branch=main 3 | -------------------------------------------------------------------------------- /debian/imediff.docs: -------------------------------------------------------------------------------- 1 | README.md 2 | -------------------------------------------------------------------------------- /debian/imediff.install: -------------------------------------------------------------------------------- 1 | usr/lib/git-core/mergetools/imediff 2 | -------------------------------------------------------------------------------- /debian/imediff.manpages: -------------------------------------------------------------------------------- 1 | usr/share/man/man1/git-ime.1 2 | usr/share/man/man1/imediff.1 3 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # vim: set ts=8 noet sw=8 ai si: 3 | # uncomment to enable verbose mode for debhelper 4 | #DH_VERBOSE = 1 5 | VERSION=$(shell dpkg-parsechangelog -S version) 6 | 7 | %: 8 | dh $@ --with python3 --buildsystem=pybuild 9 | 10 | override_dh_auto_clean: 11 | debian/check_version 12 | dh_auto_clean 13 | 14 | override_dh_auto_install: 15 | dh_auto_install -O--buildsystem=pybuild 16 | # dh-compat for all 13, 14, 15 17 | if [ -e debian/imediff ]; then \ 18 | rm -f debian/imediff/usr/bin/imediff_install ; \ 19 | sed -e "s/@@version@@/$(VERSION)/" usr/bin/git-ime.in > debian/imediff/usr/bin/git-ime ; \ 20 | rm -f debian/imediff/usr/bin/git-ime.in ; \ 21 | elif [ -e debian/tmp ]; then \ 22 | rm -f debian/tmp/usr/bin/imediff_install ; \ 23 | sed -e "s/@@version@@/$(VERSION)/" usr/bin/git-ime.in > debian/tmp/usr/bin/git-ime ; \ 24 | rm -f debian/tmp/usr/bin/git-ime.in ; \ 25 | else \ 26 | echo "E: missing installed imediff_install" ; \ 27 | exit ; \ 28 | fi 29 | -------------------------------------------------------------------------------- /debian/salsa-ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | include: 3 | - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml 4 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /debian/source/options: -------------------------------------------------------------------------------- 1 | # use debian/patches/debian-changes as automatic patch 2 | single-debian-patch 3 | # commit patch automatically to quilt queue 4 | auto-commit 5 | -------------------------------------------------------------------------------- /debian/source/patch-header: -------------------------------------------------------------------------------- 1 | The Debian packaging of imediff is maintained in git, using the merging 2 | workflow described in dgit-maint-merge(7) tweaked as quasi-native Debian 3 | packaging style. 4 | 5 | Upstream tarball is generated using "git deborig". 6 | 7 | See https://github.com/osamuaoki/imediff.git (debian, dgit/sid branch) 8 | 9 | -------------------------------------------------------------------------------- /debian/tests/check-script: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # Test code for programs installed to the system-wide 3 | # Used by autopkgtest 4 | # 5 | # (This is not for python venv) 6 | # 7 | # check syntax of code browser start script 8 | echo "Simple check of diff23lib library" 9 | unset PYTHONPATH 10 | python3 test/test_unittest_all.py -v 2>&1 11 | 12 | echo "Copy imediff to here to avoid contaminating system " 13 | cp /usr/bin/imediff ./ 14 | unset PYTHONPATH 15 | echo "Syntax check of installed imediff" 16 | python3 -m py_compile ./imediff 17 | rm -f ./imediff 18 | rm -rf ./__pycache__ 19 | 20 | echo "Syntax check of installed git-ime" 21 | sh -n /usr/bin/git-ime 22 | 23 | echo "Simple invocation check of imediff" 24 | /usr/bin/imediff -h >/dev/null 25 | 26 | echo "Simple invocation check of git-ime" 27 | /usr/bin/git-ime -h >/dev/null 28 | echo "============================================================" 29 | echo 30 | echo "Test results: OK" 31 | echo 32 | -------------------------------------------------------------------------------- /debian/tests/control: -------------------------------------------------------------------------------- 1 | Tests: check-script 2 | Depends: @ 3 | -------------------------------------------------------------------------------- /debian/upstream/metadata: -------------------------------------------------------------------------------- 1 | Repository: https://github.com/osamuaoki/imediff 2 | Repository-Browse: https://github.com/osamuaoki/imediff 3 | Bug-Database: https://github.com/osamuaoki/imediff/issues 4 | Bug-Submit: https://github.com/osamuaoki/imediff/issues/new 5 | -------------------------------------------------------------------------------- /debian/watch: -------------------------------------------------------------------------------- 1 | version=4 2 | opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%imediff-$1.tar.gz%, pgpmode=none" \ 3 | https://github.com/osamuaoki/imediff/tags \ 4 | (?:.*?/)?v?(\d[\d.]*)\.tar\.gz debian uupdate 5 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | # This is the name of your project. The first time you publish this 3 | # package, this name will be registered for you. It will determine how 4 | # users can install this project, e.g.: 5 | # 6 | # $ pip install sampleproject 7 | # 8 | # And where it will live on PyPI: https://pypi.org/project/sampleproject/ 9 | # 10 | # There are some restrictions on what makes a valid project name 11 | # specification here: 12 | # https://packaging.python.org/specifications/core-metadata/#name 13 | name = "imediff" # Required 14 | 15 | # Versions should comply with PEP 440: 16 | # https://www.python.org/dev/peps/pep-0440/ 17 | # 18 | # For a discussion on single-sourcing the version, see 19 | # https://packaging.python.org/guides/single-sourcing-package-version/ 20 | dynamic = ["version"] 21 | 22 | # This is a one-line description or tagline of what your project does. This 23 | # corresponds to the "Summary" metadata field: 24 | # https://packaging.python.org/specifications/core-metadata/#summary 25 | description = "2-way/3-way merge tool (CLI, Ncurses)" 26 | 27 | # This is an optional longer description of your project that represents 28 | # the body of text which users will see when they visit PyPI. 29 | # 30 | # Often, this is the same as your README, so you can just read it in from 31 | # that file directly (as we have already done above) 32 | # 33 | # This field corresponds to the "Description" metadata field: 34 | # https://packaging.python.org/specifications/core-metadata/#description-optional 35 | readme = {file = "README.md", content-type = "text/markdown"} 36 | 37 | # Specify which Python versions you support. In contrast to the 38 | # 'Programming Language' classifiers above, 'pip install' will check this 39 | # and refuse to install the project if the version does not match. See 40 | # https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires 41 | requires-python = ">=3.9" 42 | 43 | # This is either text indicating the license for the distribution, or a file 44 | # that contains the license 45 | # https://packaging.python.org/en/latest/specifications/core-metadata/#license 46 | 47 | ## The following needed to be commented out to work with "twine check" 48 | license = {file = "LICENSE"} 49 | 50 | # This field adds keywords for your project which will appear on the 51 | # project page. What does your project relate to? 52 | # 53 | # Note that this is a list of additional keywords, separated 54 | # by commas, to be used to assist searching for the distribution in a 55 | # larger catalog. 56 | keywords = ["imediff", "merge", "diff", "cli", "ncurses", "git-ime", "ime"] # Optional 57 | 58 | # This should be your name or the name of the organization who originally 59 | # authored the project, and a valid email address corresponding to the name 60 | # listed. 61 | authors = [ 62 | {name = "Osamu Aoki", email = "osamu@debian.org" }, 63 | {name = "Jarno Elonen", email = "elonen@iki.fi" } 64 | ] 65 | 66 | # This should be your name or the names of the organization who currently 67 | # maintains the project, and a valid email address corresponding to the name 68 | # listed. 69 | maintainers = [ 70 | {name = "Osamu Aoki", email = "osamu@debian.org" } 71 | ] 72 | 73 | # Classifiers help users find your project by categorizing it. 74 | # 75 | # For a list of valid classifiers, see https://pypi.org/classifiers/ 76 | classifiers = [ # Optional 77 | # How mature is this project? Common values are 78 | # 3 - Alpha 79 | # 4 - Beta 80 | # 5 - Production/Stable 81 | "Development Status :: 5 - Production/Stable", 82 | 83 | # Indicate who your project is intended for 84 | "Intended Audience :: Developers", 85 | "Topic :: Text Processing :: General", 86 | 87 | # Pick your license as you wish 88 | "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)", 89 | 90 | # Specify the Python versions you support here. In particular, ensure 91 | # that you indicate you support Python 3. These classifiers are *not* 92 | # checked by "pip install". See instead "python_requires" below. 93 | "Programming Language :: Python :: 3", 94 | "Programming Language :: Python :: 3.9", 95 | "Programming Language :: Python :: 3.10", 96 | "Programming Language :: Python :: 3.11", 97 | "Programming Language :: Python :: 3.12", 98 | "Programming Language :: Python :: 3.13", 99 | "Programming Language :: Python :: 3 :: Only", 100 | # Others 101 | "Environment :: Console :: Curses", 102 | "Operating System :: POSIX :: Linux", 103 | ] 104 | 105 | # This field lists other packages that your project depends on to run. 106 | # Any package you put here will be installed by pip when your project is 107 | # installed, so they must be valid existing projects. 108 | # 109 | # For an analysis of this field vs pip's requirements files see: 110 | # https://packaging.python.org/discussions/install-requires-vs-requirements/ 111 | #dependencies = [ # Optional 112 | # "peppercorn" 113 | #] 114 | 115 | # List additional groups of dependencies here (e.g. development 116 | # dependencies). Users will be able to install these using the "extras" 117 | # syntax, for example: 118 | # 119 | # $ pip install sampleproject[dev] 120 | # 121 | # Similar to `dependencies` above, these must be valid existing 122 | # projects. 123 | [project.optional-dependencies] # Optional 124 | dev = ["check-manifest"] 125 | #test = ["coverage"] 126 | ##### test_suite = test.test_diff23lib 127 | 128 | # List URLs that are relevant to your project 129 | # 130 | # This field corresponds to the "Project-URL" and "Home-Page" metadata fields: 131 | # https://packaging.python.org/specifications/core-metadata/#project-url-multiple-use 132 | # https://packaging.python.org/specifications/core-metadata/#home-page-optional 133 | # 134 | # Examples listed include a pattern for specifying where the package tracks 135 | # issues, where the source is hosted, where to say thanks to the package 136 | # maintainers, and where to support the project financially. The key is 137 | # what's used to render the link text on PyPI. 138 | [project.urls] # Optional 139 | "Homepage" = "https://github.com/osamuaoki/imediff" 140 | "Bug Reports" = "https://github.com/osamuaoki/imediff/issues" 141 | ##"Funding" = "https://donate.pypi.org" 142 | ##"Say Thanks!" = "http://saythanks.io/to/example" 143 | "Source" = "https://github.com/osamuaoki/imediff/" 144 | 145 | # The following would provide a command line executable called `sample` 146 | # which executes the function `main` from this package when invoked. 147 | [project.scripts] # Optional 148 | imediff = "imediff:main.main" 149 | imediff_install = "imediff:install.install" 150 | 151 | # This is configuration specific to the `setuptools` build backend. 152 | # If you are using a different build backend, you will need to change this. 153 | [tool.setuptools] 154 | 155 | [tool.setuptools.package-data] 156 | # If there are data files included in your packages that need to be 157 | # installed, specify them here for each module. 158 | # https://setuptools.pypa.io/en/latest/userguide/datafiles.html#package-data 159 | # regex https://git-scm.com/docs/gitignore 160 | imediff = ["data/*"] 161 | 162 | [tool.setuptools.dynamic] 163 | # https://packaging.python.org/en/latest/guides/single-sourcing-package-version/#single-sourcing-the-version 164 | # Use setuptools >=61.0.0 165 | version = {attr = "imediff.__version__"} 166 | 167 | [build-system] 168 | # https://peps.python.org/pep-0518/ 169 | # https://github.com/pypa/setuptools/pull/3056 (drop wheel) 170 | requires = ["setuptools>=61.0.0"] 171 | # https://peps.python.org/pep-0517/ 172 | build-backend = "setuptools.build_meta" 173 | 174 | -------------------------------------------------------------------------------- /sample/00_local_imediff_diff3.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # This is invoked from source tree to test python codes 4 | 5 | 6 | PYTHONPATH=$(pwd)/../src 7 | PYTHONPATH=$(realpath $PYTHONPATH) 8 | echo "I: Setting PYTHONPATH=$PYTHONPATH" 9 | export PYTHONPATH 10 | 11 | # ignore ~/.imediff 12 | python3 ../test/_imediff.py -C none -n -Mw file_a2 file_b2 file_c2 -o file_out_imediff 13 | diff3 -m file_a2 file_b2 file_c2 > file_out_diff3 14 | 15 | rm -f imediff.log 16 | -------------------------------------------------------------------------------- /sample/README.md: -------------------------------------------------------------------------------- 1 | ## Snippets for checking corner cases 2 | 3 | Here is memo for this directory. 4 | 5 | ## diff3 vs. imediff 6 | 7 | See 00_local_imediff_diff3.sh and its results 8 | 9 | ## Tailing NL 10 | 11 | See `test_readlines.py` 12 | 13 | * `FOOLINE.split("\n")` -- drop NL 14 | * `FOOLINE.splitlines(keepends=True)` -- keep NL 15 | * `fp.readlines()` -- keep NL, list of lines 16 | 17 | 18 | ## Subclass and overriding method 19 | 20 | See `test_report.py` 21 | 22 | ## Curses operations 23 | 24 | There are many subtle points to use curses library. 25 | 26 | The initial motivation was the internal integer overflow of curses library. So dumping all the data into a textpad needed to be avoided and copy only a limited portion of data into curses. 27 | 28 | As I checked 29 | [Window Objects](https://docs.python.org/3/library/curses.html#window-objects) 30 | in "The Python Standard Library » Generic Operating System Services » curses — Terminal handling for character-cell displays of displays", 31 | above issue is well known issue. 32 | 33 | * [Calling insch in corner instead of addch worked for me](https://stackoverflow.com/questions/21594778/how-to-fill-to-lower-right-corner-in-python-curses) 34 | * [exception usage example](https://stackoverflow.com/questions/36387625/curses-fails-when-calling-addch-on-the-bottom-right-corner) 35 | * [mvprintw or insch usage with example](https://stackoverflow.com/questions/6574836/printing-to-right-side-or-bottom-side-of-terminal-using-ncurses) 36 | * [addstr + insstr solution example](https://stackoverflow.com/questions/6574836/printing-to-right-side-or-bottom-side-of-terminal-using-ncurses) 37 | 38 | The use of `win.clrtoeol` may be useful to avoid garbage stays in the screen. 39 | 40 | ### many `test_curses0*.py` programs: 41 | 42 | Tried many coding styles to decide coding style 43 | 44 | Issues and thoughts 45 | 46 | * `newpad` is complicated for refresh and requires longer string copy. 47 | * `addstr` faces overflow problems 48 | * `addstr`+`insch` looked good but not good enough for non-alphanumeric 49 | * `try: ...` is the only reasonable way 50 | * `getch` must be used directly with curses 51 | * color setting needs to be reconsidered. 52 | * single row window is the best way to prevent overflow to next line. 53 | * bad cursor move actually leave cursor position as is. 54 | * application of wdiff should be single line case 55 | 56 | ### `curses_baseline.py`: 57 | 58 | Final baseline for TUI. 59 | 60 | * passing line is done by tuple: `(line,i_start,i_end)` 61 | * single row derwin to contain overflow 62 | * BS erases character 63 | * NULL cause error 64 | * HT(TAB) is 8 char only 65 | * VT is no effect on screen 66 | 67 | 68 | For imediff TUI, let me stick to `newwin` to work with 3 area ... something like: 69 | 70 | * stdscr (terminal size) 60 min x 12 min 71 | * text_area 60 min x 9 min 72 | * status_area 60 min x 1 73 | * popup (active area) 3 x 5 min 74 | * help text data 76 columns or less desirable 75 | 76 | ### Resources 77 | 78 | https://www.google.com/search?q=python+curses+window+newwin+refresh 79 | 80 | * https://stackoverflow.com/questions/3170406/python-curses-newwin-not-working 81 | * https://stackoverflow.com/questions/67021267/python-curses-newwin-not-being-displayed-if-stdscr-hasnt-been-refreshed-first 82 | * https://stackoverflow.com/questions/9653688/how-to-refresh-curses-window-correctly 83 | * https://stackoverflow.com/questions/57241649/continuously-update-string-in-python-curses 84 | * https://stackoverflow.com/questions/16763701/python-ncurses-doesnt-show-screen-until-first-key-press-even-though-refresh-i 85 | * https://www.reddit.com/r/learnpython/comments/12xqdlm/curses_for_a_split_screen_terminal_application/ 86 | 87 | Basic tutorials: 88 | 89 | * https://www.ibm.com/docs/sl/aix/7.2?topic=library-manipulating-window-data-curses 90 | * https://sceweb.sce.uhcl.edu/helm/WEBPAGE-Python/documentation/howto/curses/node5.html 91 | 92 | Also: 93 | 94 | * `getch` --- return integer 95 | * `getkey` -- return string 96 | 97 | ### functions to remember 98 | 99 | * `curses.raw()` / `curses.noraw()` 100 | * `curses.def_prog_mode()`/`curses.reset_prog_mode()` 101 | * `curses.def_shell_mode()`/`curses.reset_shell_mode()` 102 | * `curses.curs_set(visibility)` 103 | * visibility=0 invisible 104 | * visibility=1 normal 105 | * visibility=2 very visible 106 | * `window.leaveok(flag)` -- True no cursor change 107 | * `window.refresh([pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol])` 108 | * parameters needed for "pad" (virtual screen) 109 | * pminrow and pmincol: the upper left-hand corner of the rectangle to be displayed in the pad 110 | * sminrow, smincol, smaxrow, smaxcol: the edges of the rectangle to be displayed on the screen 111 | 112 | -------------------------------------------------------------------------------- /sample/file_a2: -------------------------------------------------------------------------------- 1 | 00 wsx 2 | 01 qaz same 3 | 02 adf 4 | -------------------------------------------------------------------------------- /sample/file_b2: -------------------------------------------------------------------------------- 1 | 00 wsx 2 | 01 qaz 3 | 02 adf 4 | -------------------------------------------------------------------------------- /sample/file_c2: -------------------------------------------------------------------------------- 1 | 00 wsx 2 | 01 qaz same 3 | 02 adf 4 | -------------------------------------------------------------------------------- /sample/file_out_diff3: -------------------------------------------------------------------------------- 1 | 00 wsx 2 | <<<<<<< file_b2 3 | 01 qaz 4 | ======= 5 | 01 qaz same 6 | >>>>>>> file_c2 7 | 02 adf 8 | -------------------------------------------------------------------------------- /sample/file_out_imediff: -------------------------------------------------------------------------------- 1 | 00 wsx 2 | 01 qaz same 3 | 02 adf 4 | -------------------------------------------------------------------------------- /sample/test_argparse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # vim:se tw=79 sts=4 ts=4 et ai fileencoding=utf-8 : 3 | 4 | import argparse 5 | 6 | # This allows different reporting style for TUI/CLI 7 | pa = argparse.ArgumentParser( 8 | description="SIMPLE example", 9 | epilog="See README.md for more", 10 | ) 11 | group = pa.add_mutually_exclusive_group() 12 | group.add_argument( 13 | "-a", action="store_true", help="Start with all chunks to use file_a" 14 | ) 15 | group.add_argument( 16 | "-b", action="store_true", help="Start with all chunks to use file_b" 17 | ) 18 | group.add_argument( 19 | "-c", 20 | action="store_true", 21 | help="Start with all chunks to use file_c (only for diff3)", 22 | ) 23 | pa.add_argument( 24 | "--rule-filter", 25 | "-r", 26 | action="store", 27 | default=2, 28 | help="Fuzzy match line filtering rule (0,1,2,3,10,11,12,13)", 29 | ) 30 | args = pa.parse_args() 31 | 32 | print("a={}, b={}, c={}".format(args.a, args.b, args.c)) 33 | print("rule-filter={}".format(args.rule_filter)) 34 | -------------------------------------------------------------------------------- /sample/test_curses01.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # vim:se tw=79 sts=4 ts=4 et ai fileencoding=utf-8 : 3 | 4 | """ 5 | Copyright (C) 2024 Osamu Aoki 6 | """ 7 | 8 | import sys 9 | import curses 10 | import logging 11 | 12 | logger = logging.getLogger(__name__) 13 | logging.basicConfig(filename="example.log", level=logging.DEBUG) 14 | exception = None 15 | # See https://docs.python.org/3/library/curses.html 16 | # https://docs.python.org/3/howto/curses.html 17 | # logger.debug('This message should go to the log file') 18 | # logger.info('So should this') 19 | # logger.warning('And this, too') 20 | # logger.error('And non-ASCII stuff, too, like Øresund and Malmö') 21 | 22 | 23 | def test_text(char, height, width): 24 | test_text = "" 25 | for col in range(0, width): 26 | test_text += "{:1}".format(col % 10) 27 | test_text += "\n\n\n" 28 | for col in range(0, width): 29 | test_text += "{:1}".format((col // 10) % 10) 30 | test_text += "\n\n\n" 31 | for row in range(6, (height // 3) - 6): 32 | test_text += (char[:1] * 32 + " {:4} ".format(row) + char[:1] * 40) * ( 33 | width // 80 34 | ) + "\n\n\n" 35 | for row in range(height - (height // 3) * 3, height): 36 | test_text += "{:1}\n".format(char[:1] * width) 37 | return test_text 38 | 39 | 40 | #################################################################### 41 | # Externally used main method and effective main method gui_loop 42 | #################################################################### 43 | def main(): # for curses wrapper 44 | global exception 45 | logger.debug("start CURSES TEST") 46 | curses.wrapper(gui_main) 47 | if exception is not None: 48 | print("E: {} {}".format(type(exception).__name__, exception)) 49 | logger.debug("end CURSES TEST") 50 | return 51 | 52 | 53 | def gui_main(stdscr): # for curses TUI (core) 54 | global exception 55 | # Clear screen 56 | try: 57 | curses.curs_set(0) # cursor off 58 | except Exception as e: 59 | exception = e 60 | # https://stackoverflow.com/questions/3170406/python-curses-newwin-not-working 61 | stdscr.clear() 62 | curses.curs_set(1) # cursor on 63 | curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK) 64 | curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK) 65 | curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK) 66 | curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK) 67 | curses.init_pair(5, curses.COLOR_GREEN, curses.COLOR_BLACK) 68 | curses.init_pair(6, curses.COLOR_CYAN, curses.COLOR_BLACK) 69 | curses.init_pair(7, curses.COLOR_MAGENTA, curses.COLOR_BLACK) 70 | curses.init_pair(8, curses.COLOR_BLACK, curses.COLOR_WHITE) 71 | # This raises ZeroDivisionError when i == 10. 72 | term_row_max, term_col_max = stdscr.getmaxyx() 73 | textarea = stdscr.derwin(term_row_max - 1, term_col_max, 0, 0) 74 | textarea_row_max, textarea_col_max = textarea.getmaxyx() 75 | statusarea = stdscr.derwin(1, term_col_max, term_row_max - 1, 0) 76 | statusarea_row_max, statusarea_col_max = statusarea.getmaxyx() 77 | test_lines = test_text("#", term_row_max - 1, term_col_max) 78 | for i, test_line in enumerate(test_lines.split("\n")): 79 | if i < term_row_max - 1: 80 | try: 81 | textarea.addstr(i, 0, test_line[:term_col_max]) 82 | except Exception as e: 83 | print("E: {}".format(e)) 84 | pass 85 | statusarea.addstr( 86 | 0, 87 | 0, 88 | "STEP 0: stdscr={}:{}, textarea={}:{} statusarea={}:{}".format( 89 | term_row_max, 90 | term_col_max, 91 | textarea_row_max, 92 | textarea_col_max, 93 | statusarea_row_max, 94 | statusarea_col_max, 95 | ), 96 | curses.color_pair(6), 97 | ) 98 | stdscr.refresh() 99 | textarea.refresh() 100 | statusarea.refresh() 101 | stdscr.getkey() 102 | ##################################################################################### 103 | stdscr.clear() 104 | stdscr.addstr(term_row_max - 1, 0, "STEP 1: ") 105 | stdscr.refresh() 106 | stdscr.getkey() 107 | ##################################################################################### 108 | box_row = 12 109 | box_col = 60 110 | margin_row = get_center_margin(textarea_row_max, box_row, 5) 111 | margin_col = get_center_margin(textarea_col_max, box_col, 5) 112 | outbox = textarea.derwin(box_row, box_col, margin_row, margin_col) 113 | outbox_row_max, outbox_col_max = outbox.getmaxyx() 114 | outbox.border() 115 | stdscr.addstr(term_row_max - 1, 0, "STEP 2: ", curses.color_pair(2)) 116 | outbox.refresh() 117 | stdscr.getkey() 118 | ##################################################################################### 119 | inbox = outbox.derwin(6, 36, 1, 2) 120 | inbox_row_max, inbox_col_max = inbox.getmaxyx() 121 | inbox.addstr( 122 | 0, 0, "Hello, world! ################################\n" * 2, curses.A_REVERSE 123 | ) 124 | stdscr.addstr(term_row_max - 1, 0, "STEP 3: ", curses.color_pair(3)) 125 | inbox.refresh() 126 | stdscr.getkey() 127 | ##################################################################################### 128 | # intext = textarea.newpad(1000,300) # BAD 129 | intext = curses.newpad(1000, 300) 130 | intext.addstr(0, 0, test_lines) 131 | intext.refresh(0, 0, 5, 5, 30, 60) 132 | stdscr.addstr( 133 | term_row_max - 1, 134 | 0, 135 | "STEP 4: stdscr={}:{}, outbox={}:{}, inbox={}:{} END of example".format( 136 | term_row_max, 137 | term_col_max, 138 | outbox_row_max, 139 | outbox_col_max, 140 | inbox_row_max, 141 | inbox_col_max, 142 | ), 143 | curses.color_pair(4), 144 | ) 145 | stdscr.refresh() 146 | stdscr.getkey() 147 | ##################################################################################### 148 | 149 | 150 | def get_center_margin(span_out, span_in, margin_min): 151 | span_in_real = min(span_out - 2 * margin_min, span_in) 152 | margin = (span_out - span_in_real) // 2 153 | return margin 154 | 155 | 156 | if __name__ == "__main__": 157 | # run program 158 | main() 159 | # finish 160 | sys.exit(0) 161 | -------------------------------------------------------------------------------- /sample/test_curses02.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # vim:se tw=79 sts=4 ts=4 et ai fileencoding=utf-8 : 3 | 4 | """ 5 | Copyright (C) 2024 Osamu Aoki 6 | """ 7 | 8 | import sys 9 | import curses 10 | import logging 11 | 12 | logger = logging.getLogger(__name__) 13 | logging.basicConfig(filename="example.log", level=logging.DEBUG) 14 | exception = None 15 | # See https://docs.python.org/3/library/curses.html 16 | # https://docs.python.org/3/howto/curses.html 17 | # logger.debug('This message should go to the log file') 18 | # logger.info('So should this') 19 | # logger.warning('And this, too') 20 | # logger.error('And non-ASCII stuff, too, like Øresund and Malmö') 21 | 22 | 23 | def test_text(char, height, width): 24 | test_text = "" 25 | for col in range(0, width): 26 | test_text += "{:1}".format(col % 10) 27 | test_text += "\n\n\n" 28 | for col in range(0, width): 29 | test_text += "{:1}".format((col // 10) % 10) 30 | test_text += "\n\n\n" 31 | for row in range(6, (height // 3) - 6): 32 | test_text += (char[:1] * 32 + " {:4} ".format(row) + char[:1] * 40) * ( 33 | width // 80 34 | ) + "\n\n\n" 35 | for row in range(height - (height // 3) * 3, height): 36 | test_text += "{:1}\n".format(char[:1] * width) 37 | return test_text 38 | 39 | 40 | #################################################################### 41 | # Externally used main method and effective main method gui_loop 42 | #################################################################### 43 | def main(): # for curses wrapper 44 | global exception 45 | debug("start CURSES TEST") 46 | curses.wrapper(gui_main) 47 | if exception is not None: 48 | print("E: {} {}".format(type(exception).__name__, exception)) 49 | debug("end CURSES TEST") 50 | return 51 | 52 | 53 | def gui_main(stdscr): # for curses TUI (core) 54 | global exception 55 | # Clear screen 56 | try: 57 | curses.curs_set(0) # cursor off 58 | except Exception as e: 59 | exception = e 60 | # https://stackoverflow.com/questions/3170406/python-curses-newwin-not-working 61 | stdscr.clear() 62 | # curses.curs_set(1) # cursor on 63 | curses.curs_set(0) # cursor off 64 | curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK) 65 | curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK) 66 | curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK) 67 | curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK) 68 | curses.init_pair(5, curses.COLOR_GREEN, curses.COLOR_BLACK) 69 | curses.init_pair(6, curses.COLOR_CYAN, curses.COLOR_BLACK) 70 | curses.init_pair(7, curses.COLOR_MAGENTA, curses.COLOR_BLACK) 71 | curses.init_pair(8, curses.COLOR_BLACK, curses.COLOR_WHITE) 72 | # This raises ZeroDivisionError when i == 10. 73 | term_row_max, term_col_max = stdscr.getmaxyx() 74 | stdscr.insstr(term_row_max - 1, term_col_max - 1, "@") 75 | stdscr.refresh() 76 | stdscr.getkey() 77 | textarea = stdscr.derwin(term_row_max - 1, term_col_max, 0, 0) 78 | textarea_row_max, textarea_col_max = textarea.getmaxyx() 79 | textarea.insstr(textarea_row_max - 1, textarea_col_max - 1, "*") 80 | textarea.refresh() 81 | textarea.getkey() 82 | statusarea = stdscr.derwin(1, term_col_max, term_row_max - 1, 0) 83 | statusarea_row_max, statusarea_col_max = statusarea.getmaxyx() 84 | statusarea.insstr(statusarea_row_max - 1, statusarea_col_max - 1, "%") 85 | statusarea.refresh() 86 | statusarea.getkey() 87 | statusarea = stdscr.derwin(1, term_col_max, term_row_max - 1, 0) 88 | statusarea_row_max, statusarea_col_max = statusarea.getmaxyx() 89 | try: 90 | statusarea.addstr(statusarea_row_max - 1, statusarea_col_max - 1, "$") 91 | except curses.error as _: 92 | pass 93 | statusarea.refresh() 94 | # getkey is noy window specific 95 | # statusarea.getkey() 96 | # textarea.getkey() 97 | stdscr.getkey() 98 | stdscr.clear() 99 | stdscr.border() 100 | stdscr.refresh() 101 | stdscr.getkey() 102 | for row in range(0, textarea_row_max): 103 | color = row % 6 104 | textarea.insstr(row, 0, "&" * (textarea_col_max), color) 105 | textarea.addstr(textarea_row_max - 1, 10, "!" * 10) 106 | textarea.clrtoeol() 107 | textarea.refresh() 108 | stdscr.getkey() 109 | offset_col = 10 110 | stdscr.addstr(term_row_max - 1, offset_col, "Z" * (term_col_max - 1 - offset_col)) 111 | stdscr.insch("E") 112 | stdscr.refresh() 113 | stdscr.getkey() 114 | 115 | ##################################################################################### 116 | 117 | 118 | def get_center_margin(span_out, span_in, margin_min): 119 | span_in_real = min(span_out - 2 * margin_min, span_in) 120 | margin = (span_out - span_in_real) // 2 121 | return margin 122 | 123 | 124 | def get_sanitized(x, x_min, x_max): 125 | if x_min > x_max: 126 | print("ERROR min={}, max={}".format(x_min, x_max)) 127 | sys.exit(1) 128 | elif x < x_min: 129 | x = x_min 130 | elif x >= x_max: 131 | x = x_max 132 | else: 133 | pass # sanity OK 134 | return x 135 | 136 | 137 | #################################################################### 138 | # Internally used utility methods (popup display via curses) 139 | #################################################################### 140 | def tui_popup_text(stdscr, text): 141 | curses.curs_set(0) 142 | term_row_max, term_col_max = stdscr.getmaxyx() 143 | stdscr.border() 144 | for line_index, line_text in enumerate(text.split("\n")): # no \n 145 | if line_index < term_row_max - 5: 146 | stdscr.addstr( 147 | line_index + 1, 2, line_text[: term_col_max - 5], curses.A_BOLD 148 | ) 149 | stdscr.refresh() 150 | stdscr.getch() 151 | return 152 | 153 | 154 | # debug message 155 | def debug(msg): 156 | logger.debug(msg) 157 | return 158 | 159 | 160 | if __name__ == "__main__": 161 | # run program 162 | main() 163 | # finish 164 | sys.exit(0) 165 | -------------------------------------------------------------------------------- /sample/test_logic_function.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # vim:se tw=79 sts=4 ts=4 et ai fileencoding=utf-8 : 3 | 4 | import sys 5 | import termios 6 | 7 | 8 | def getch(): 9 | old_settings = termios.tcgetattr(0) 10 | new_settings = old_settings[:] 11 | # Enable canonical mode. See termios(3) 12 | new_settings[3] &= ~termios.ICANON 13 | try: 14 | # Change attributes immediately. 15 | termios.tcsetattr(0, termios.TCSANOW, new_settings) 16 | ch = sys.stdin.read(1) 17 | finally: 18 | termios.tcsetattr(0, termios.TCSANOW, old_settings) 19 | return ch 20 | 21 | 22 | def yn0(): 23 | print("\n0: enter 'y' or 'n': ", end="") 24 | ch = getch() 25 | if ch == "y": 26 | r = True 27 | else: 28 | r = False 29 | return r 30 | 31 | 32 | def yn1(): 33 | print("\n1: enter 'y' or 'n': ", end="") 34 | ch = getch() 35 | r = ch == "y" 36 | return r 37 | 38 | 39 | # NO `return ch == "y"` like C 40 | 41 | if yn0(): 42 | print("\nTRUE\n") 43 | else: 44 | print("\nFALSE\n") 45 | 46 | if yn1(): 47 | print("\nTRUE\n") 48 | else: 49 | print("\nFALSE\n") 50 | -------------------------------------------------------------------------------- /sample/test_readlines.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # vim:se tw=79 sts=4 ts=4 et ai fileencoding=utf-8 : 3 | fp = open("report.py", "r") 4 | line_list = fp.readlines() 5 | # 6 | # list of string containing \n at the end of each item 7 | # for line in range(len(line_list)): 8 | for line in line_list: 9 | print("line='{}'".format(line)) 10 | 11 | bigline = "".join(line_list) 12 | print("================================================") 13 | print(bigline) 14 | print("================================================") 15 | 16 | FOOLINE = "a\nb\nc\n" 17 | print(FOOLINE.split()) 18 | print("================================================") 19 | print(FOOLINE.splitlines(keepends=True)) 20 | -------------------------------------------------------------------------------- /sample/test_report.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # vim:se tw=79 sts=4 ts=4 et ai fileencoding=utf-8 : 3 | 4 | 5 | class Test: 6 | """TEST CODE""" 7 | 8 | def __init__(self): 9 | pass 10 | return 11 | 12 | def report(self, foo): # To Be Overriden 13 | print(foo) 14 | return 15 | 16 | def main(self): 17 | self.report("HELLO") 18 | return 19 | 20 | 21 | T = Test() 22 | 23 | print("=====") 24 | T.main() 25 | print("=====") 26 | T.report("OOPS") 27 | print("=====") 28 | 29 | # This allows different reporting style for TUI/CLI 30 | -------------------------------------------------------------------------------- /src/imediff/README.md: -------------------------------------------------------------------------------- 1 | # Note on delta, diff2, diff3, merge, ... 2 | 3 | I am no expert on this subject of computing delta. 4 | 5 | I collected readily available information on the web for my reference and 6 | summarized as my reminders. 7 | 8 | I also documented history and feature of `imediff`. 9 | 10 | Here, I use shorthand as follows: 11 | 12 | * diff2: delta of 2 data 13 | * diff3: delta of 3 data 14 | 15 | ## Computing deltas 16 | 17 | Algorithm of computing delta seems to be non-trivial one since it is a NP-hard 18 | problem. 19 | 20 | There seem to be several algorithms for computing deltas: 21 | 22 | * https://en.wikipedia.org/wiki/Diff 23 | 24 | ## Python difflib for diff2 25 | 26 | The python offers `diflib` as a standard library for computing deltas 27 | 28 | * https://docs.python.org/3/library/difflib.html 29 | 30 | This is based on an extended Ratcliff and Obershelp algorithm. The idea is to 31 | find the longest contiguous matching subsequence that contains no “junk” 32 | element. As for longest contiguous matching subsequence, see: 33 | 34 | * https://en.wikipedia.org/wiki/Longest_common_subsequence_problem 35 | * John W. Ratcliff and David Metzener, Pattern Matching: The Gestalt 36 | Approach, Dr. Dobb's Journal, page 46, July 1988. 37 | * https://xlinux.nist.gov/dads/HTML/ratcliffObershelp.html 38 | * PATTERN MATCHING: THE GESTALT APPROACH: https://collaboration.cmc.ec.gc.ca/science/rpn/biblio/ddj/Website/articles/DDJ/1988/8807/8807c/8807c.htm 39 | 40 | ## Alternative algorithms for diff2 41 | 42 | I found several interesting blogs and source codes examples. 43 | 44 | * susisu 45 | * 差分検出アルゴリズム三種盛り (javascript) 46 | https://susisu.hatenablog.com/entry/2017/10/09/134032 47 | * Wikibooks: Algorithm Implementation 48 | * Levenshtein distance 49 | https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance 50 | * Longest common subsequence 51 | https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Longest_common_subsequence 52 | * Tociyuki (perl `diff3` author) 53 | * Heckel 法による diff プログラム 54 | https://tociyuki.hatenablog.jp/entry/20141209/1418117214 55 | * Hirschberg 法 - Myers による O(ND) テキスト差分 56 | https://tociyuki.hatenablog.jp/entry/20150330/1427683909 57 | * またもや Heckel 法による diff プログラム 58 | https://tociyuki.hatenablog.jp/entry/20151201/1448942936 59 | * Hirschberg-Wu 法による diff プログラム 60 | https://tociyuki.hatenablog.jp/entry/20151203/1449158483 61 | * Hirschberg-Wu 法による diff プログラム改良版 62 | https://tociyuki.hatenablog.jp/entry/20151207/1449474412 63 | * テキスト差分 Wu によるO(NP) 法の Hirschberg 法化まとめ 64 | https://tociyuki.hatenablog.jp/entry/20170526/1495801840 65 | * Simple variant of diff(1) program 66 | https://gist.github.com/tociyuki/09eae66a6650b5f7defc 67 | * word based unified color differences between two texts (The BSD 3-Clause) 68 | https://github.com/tociyuki/udiff-cxx11 69 | * Neil Bowers 70 | * Text-Diff-1.45 12 ++ / Text::Diff (License: perl_5) 71 | https://metacpan.org/pod/Text::Diff 72 | https://github.com/neilb/Text-Diff 73 | * Tye McQueen 74 | * Algorithm-Diff-1.1903 19 ++ / Algorithm::Diff 75 | https://metacpan.org/pod/Algorithm::Diff 76 | * Paul Heckel -- see next section 77 | * http://wiki.c2.com/?DiffAlgorithm 78 | 79 | These were too much to digest but there seems to be newer algorithm than one 80 | used in Python library for diff2. 81 | 82 | * unified format diff between texts line by line with Wu's O(NP) and Hirschberg's linear space method in C++11 83 | https://gist.github.com/tociyuki/acedd33ca4913f1ab8e9 84 | 85 | Addition of this may be TODO for post 2.0 sources. But for now, too much work. 86 | python standard library is fast enough. 87 | 88 | ## Paul Heckel's diff algorithm for diff2 89 | 90 | * Paul Heckel (April 1978). "A technique for isolating differences between files" 91 | * http://documents.scribd.com/docs/10ro9oowpo1h81pgh1as.pdf 92 | * https://gist.github.com/ndarville/3166060 93 | * https://stackoverflow.com/questions/42755035/difficulty-understanding-paul-heckels-diff-algorithm 94 | * https://www.npmjs.com/package/heckel-diff 95 | * https://github.com/mcudich/HeckelDiff/blob/master/Source/Diff.swift 96 | * https://johnresig.com/projects/javascript-diff-algorithm/ 97 | * https://johnresig.com/files/jsdiff.js 98 | * https://github.com/ndarville/jsdiff/blob/master/jsdiff.js 99 | 100 | ## Algorithms for diff3 101 | 102 | When I searched for `diff3` implemented in python, I found `diff3.py`. 103 | 104 | * https://github.com/cmake-basis/BASIS/blob/master/src/utilities/python/diff3.py 105 | * https://github.com/schuhschuh/cmake-basis/blob/master/src/utilities/python/diff3.py 106 | * http://www.nmr.mgh.harvard.edu/~you2/dramms/dramms-1.4.3-source/build/bundle/src/BASIS/src/utilities/python/diff3.py 107 | 108 | All these are based on Perl code by MIZUTANI Tociyuki 109 | 110 | * https://metacpan.org/pod/Text::Diff3 111 | MIZUTANI, Tociyuki / Text-Diff3-0.10 ++ / Text::Diff3 112 | 113 | I initially tried to use above `diff3.py`. I may have broke code when adopting 114 | it, but I didn't get right answer when delta existed at the tail ends. This 115 | code was transcoded from perl to python and used one-dimensional array `d2` to 116 | hold 2-dimensional data. This odd python code was difficult to understand and 117 | debug. I gave up fixing this initial trial. 118 | 119 | ``` 120 | d2 = (diff(origtext, yourtext), diff(origtext, theirtext)) 121 | ``` 122 | 123 | From `diff3.py`, I took the basic idea of combining 2 diff2 data to make diff3 124 | data. After my second trial to write my own diff3, I made decent success 125 | verified with accompanied test codes. I released `diff3lib.py` as a part of 126 | `imediff` version 2.0. My `diff3lib.py` code is not a derivative work of 127 | `diff3.py`. (I suspect these may be mostly logically equivalent. But I didn't 128 | check again.) 129 | 130 | Now that I wrote working code, the following paper started to make some sense. 131 | It may be good idea to reread this. (I wrote my code before I found this 132 | paper.) 133 | 134 | * http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf (paper) 135 | 136 | ## Improved merge algorithms for diff3 137 | 138 | The merge logic of `imediff` works on both line and character in 2-stage 139 | action. I think this is one most important improvement of `imediff`. 140 | 141 | The merge logic working only on character can merge non-overlapping change on 142 | the same line cleanly. But it wastes CPU resource and may pick up accidental 143 | match in totally wrong position of code. 144 | 145 | This 2-stage merge action provides a practical improvement of clean merge for 146 | non-overlapping change on the same line without regression over the traditional 147 | merge logic which works only on line as seen in `diff3 -m` and `git`'s internal 148 | merge. 149 | 150 | When implementing this 2-stage merge action, `imediff` uses the same code twice 151 | by taking advantage of the python *iterable* concept. 152 | 153 | ## Problem of GNU diff3 154 | 155 | MIZUTANI Tociyuki published a blog for his diff3 perl 156 | code. 157 | 158 | * https://tociyuki.hatenablog.jp/entry/20051019/1129742409 Algorithm::Diff3改めText::Diff3へ 159 | 160 | At the bottom, he has a section with its Japanese titled meaning "Funny 161 | behavior of `diff3 -m`" and wonder if this funny behavior is an intestinal 162 | feature or a bug after mentioning "If the 2 lines in the original file are 163 | removed in both party, `diff3 -m` yields merged output like a conflict case." 164 | 165 | > ポート元のGNU utilsの挙動で、一つ不思議なものがあります。オリジナルのファイル 166 | > のある連続行を、2つの側で同時に削除するとコンフリクト発生とみなすようです。 167 | > 違いますよね?私のmerge.plでは、コンフリクトではなく双方刷り合わせ上での正常 168 | > 削除とみなしています。当初、これは、diffのアルゴリズムの違いでdiffの出力が 169 | > 違っているのだろうと考えたのですが、途中結果を出力できるようにして、つきあわ 170 | > せてみると一緒。diff3の変更箇所ブロック出力まで一緒なので、この手のパターンを 171 | > コンフリクトとみなしているのは、mergeを出力する部分のようです。なんかC言語の 172 | > ソースを読んでも、例によってなぜそれをコンフリクト扱いにしているか、わからな 173 | > いのです。仕様でそうしているのか、それともバグなのか。 174 | 175 | NOTE: I verified that my `diff3lib.py` functions like MIZUTANI Tociyuki's 176 | `merge.pl` to produce a clean merge result. 177 | 178 | Osamu / 2019-01-31 179 | 180 | 181 | -------------------------------------------------------------------------------- /src/imediff/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # vim:se tw=79 sts=4 ts=4 et ai fileencoding=utf-8 : 3 | 4 | """ 5 | IMEDIFF - Interactive Merge Editor for DIFF2 and DIFF3 6 | Curses based single-pane fullscreen interactive tool 7 | and CLI based non-interactive tool with --macro 8 | 9 | Copyright (C) 2003, 2004 Jarno Elonen 10 | Copyright (C) 2018--2024 Osamu Aoki 11 | 12 | This program is free software; you can redistribute it and/or 13 | modify it under the terms of the GNU General Public License as 14 | published by the Free Software Foundation; either version 2 of 15 | the License, or (at your option) any later version. 16 | 17 | This program is distributed in the hope that it will be useful, 18 | but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | GNU General Public License for more details. 21 | 22 | You should have received a copy of the GNU General Public 23 | License along with this program; if not, write to the Free 24 | Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 25 | Boston, MA 02110-1301, USA. 26 | """ 27 | __version__ = "3.4.0" 28 | __package__ = "imediff" 29 | 30 | __all__ = ["__version__", "__package__"] 31 | -------------------------------------------------------------------------------- /src/imediff/config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # vim:se tw=79 sts=4 ts=4 et ai fileencoding=utf-8 : 3 | 4 | """ 5 | IMEDIFF - Interactive Merge Editor for DIFF2 and DIFF3 6 | Curses based single-pane fullscreen interactive tool 7 | and CLI based non-interactive tool with --macro 8 | 9 | Copyright (C) 2003, 2004 Jarno Elonen 10 | Copyright (C) 2018--2024 Osamu Aoki 11 | 12 | This program is free software; you can redistribute it and/or 13 | modify it under the terms of the GNU General Public License as 14 | published by the Free Software Foundation; either version 2 of 15 | the License, or (at your option) any later version. 16 | 17 | This program is distributed in the hope that it will be useful, 18 | but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | GNU General Public License for more details. 21 | 22 | You should have received a copy of the GNU General Public 23 | License along with this program; if not, write to the Free 24 | Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 25 | Boston, MA 02110-1301, USA. 26 | """ 27 | 28 | import os 29 | import sys 30 | import io 31 | import logging 32 | 33 | logger = logging.getLogger(__name__) 34 | 35 | # Update version below only when configuration API changes 36 | 37 | config_template = """\ 38 | # imediff configuration file 39 | # Please edit right side of assignment to customize 40 | # ini-type syntax where # starts comment 41 | # Don't create duplicate definition 42 | # Key side is case insensitive. 43 | 44 | [config] 45 | version = 3.1 # DON'T EDIT THIS. This is for future upgrade tracking. 46 | confirm_exit = True # Set as "False" to save and exit without pause 47 | confirm_quit = True # Set as "False" to quit without pause 48 | #editor = vim # Set this to override /usr/bin/editor and $EDITOR 49 | 50 | # key remapping is used only for TUI user input. MACRO need to use the 51 | # original bindings. 52 | [key] # action invoked by the selected key 53 | select_a = a # set mode a to select 'a' buffer 54 | select_b = b # set mode b to select 'b' buffer 55 | select_c = c # set mode c to select 'c' buffer (diff3) 56 | select_d = d # set mode d to select diff content if possible 57 | select_e = e # set mode e to select editor buffer if possible 58 | select_f = f # set mode f to select wdiff content if possible 59 | select_g = g # set good merge (diff3/wdiff3) if possible 60 | select_h = h # move left for the display scope 61 | select_i = i 62 | select_j = j # move down for the display scope 63 | select_k = k # move up for the display scope 64 | select_l = l # move right for the display scope 65 | select_m = m # start editor to modify content / M reset 66 | select_n = n # move active selection to next usr_chunk 67 | select_o = o 68 | select_p = p # move active selection to previous usr_chunk 69 | select_q = q # quit imediff without saving the result 70 | select_r = r 71 | select_s = s 72 | select_t = t # display tutorial 73 | select_u = u 74 | select_v = v 75 | select_w = w # write result and e_x_it program 76 | select_x = x # write result and e_x_it program 77 | select_y = y # key for "Yes" answer 78 | select_z = z 79 | select_SPACE = SPACE 80 | select_! = ! 81 | select_" = " 82 | select_HASH = HASH 83 | select_$ = $ 84 | select_PERCENT = PERCENT 85 | select_& = & 86 | select_' = ' 87 | select_( = ( 88 | select_) = ) 89 | select_* = * 90 | select_+ = + 91 | select_, = , 92 | select_- = - 93 | select_. = . 94 | select_/ = / 95 | select_0 = 0 # move active selection to the first usr_chunk 96 | select_1 = 1 # alias for a 97 | select_2 = 2 # alias for b 98 | select_3 = 3 # alias for c 99 | select_4 = 4 # alias for d 100 | select_5 = 5 # alias for e 101 | select_6 = 6 # alias for f 102 | select_7 = 7 # alias for g 103 | select_8 = 8 104 | select_9 = 9 # move active selection to the last usr_chunk 105 | select_COLON = COLON 106 | select_; = ; 107 | select_< = > 108 | select_EQUAL = EQUAL 109 | select_> = > 110 | select_? = ? 111 | select_@ = @ 112 | select_LBRACKET = LBRACKET # MACRO block start 113 | select_BACKSLASH = BACKSLASH 114 | select_RBRACKET = RBRACKET # MACRO block end 115 | select_^ = ^ 116 | select__ = _ 117 | select_` = ` 118 | select_{ = { 119 | select_| = | 120 | select_} = } 121 | select_~ = ~ 122 | select_DEL = DEL 123 | select_TAB = TAB 124 | select_BTAB = BTAB 125 | select_ENTER = ENTER 126 | select_UP = UP 127 | select_DOWN = DOWN 128 | select_LEFT = LEFT 129 | select_RIGHT = RIGHT 130 | select_INSERT = INSERT 131 | select_DELETE = DELETE 132 | select_HOME = HOME 133 | select_END = END 134 | select_PAGEUP = PAGEUP 135 | select_PAGEDOWN = PAGEDOWN 136 | select_BACKSPACE = BACKSPACE 137 | select_F1 = F1 138 | select_F2 = F2 139 | select_F3 = F3 140 | select_F4 = F4 141 | select_F5 = F5 142 | select_F6 = F6 143 | select_F7 = F7 144 | select_F8 = F8 145 | select_F9 = F9 146 | select_F10 = F10 147 | select_F11 = F11 148 | select_F12 = F11 149 | # = ^--------- customized key setting 150 | # ^--------- reference key setting 151 | # Upper case ASCII-keys are mapped following lower case keys 152 | 153 | # Color with /WHITE uses white background 154 | [attrib] 155 | color_merge_ab = WHITE,NORMAL # diff2 = 156 | color_merge_abc = WHITE,NORMAL # diff3 = 157 | color_merge_ac = WHITE,NORMAL # diff 3 # 158 | color_merge_a = GREEN,NORMAL # diff 3 A 159 | color_merge_c = YELLOW,NORMAL # diff 3 C 160 | color_merge_wdiff = WHITE,NORMAL # diff 3 G wdiff 161 | color_a = GREEN,BOLD # diff23 a 162 | color_a_focus = GREEN,BOLD,REVERSE 163 | #color_a_focus = GREEN/WHITE 164 | color_b2 = YELLOW,BOLD # diff2 b (wdiff2) 165 | color_b2_focus = YELLOW,BOLD,REVERSE 166 | #color_b2_focus = YELLOW/WHITE 167 | color_b3 = MAGENTA,BOLD # diff 3 b (wdiff3) 168 | color_b3_focus = MAGENTA,BOLD,REVERSE 169 | # color_b3_focus = MAGENTA/WHITE 170 | color_c = YELLOW,BOLD # diff 3 c 171 | color_c_focus = YELLOW,BOLD,REVERSE 172 | # color_c_focus = YELLOW/WHITE 173 | color_editor = CYAN,BOLD # editor-buffer-line 174 | color_editor_focus = CYAN,BOLD,REVERSE 175 | #color_editor_focus = CYAN/WHITE 176 | color_diff_marker = BLUE,NORMAL # marker-diff-line 177 | color_diff_marker_focus = BLUE,NORMAL,REVERSE 178 | color_wdiff_abc = WHITE,NORMAL # wdiff3 179 | color_wdiff_abc_focus = WHITE,BOLD,REVERSE 180 | color_wdiff_ac = WHITE,DIM # wdiff3 181 | color_wdiff_ac_focus = WHITE,BOLD,REVERSE 182 | color_wdiff_ab = WHITE,NORMAL # wdiff2 183 | color_wdiff_ab_focus = WHITE,BOLD,REVERSE 184 | color_wdiff_marker = BLUE,NORMAL 185 | color_wdiff_marker_focus = BLUE,NORMAL,REVERSE 186 | 187 | color_status = WHITE,NORMAL 188 | color_status_focus = WHITE,BOLD,REVERSE 189 | color_white_bold = WHITE,BOLD 190 | color_white = WHITE,NORMAL 191 | color_white_reverse = WHITE,BOLD,REVERSE 192 | color_warn = RED,BOLD 193 | color_eof = BLUE,DIM,REVERSE 194 | color_mono = WHITE,NORMAL 195 | 196 | 197 | [line_separator] # diff output formatting strings 198 | # diff2 uses ls0, ls2, ls3 199 | # diff3 uses ls0, ls1, ls2, ls3 200 | # File name added ls0, ls1, ls3 201 | ls0 = <<<<<<< 202 | ls1 = ||||||| 203 | ls2 = ======= 204 | ls3 = >>>>>>> 205 | 206 | [word_separator] # wdiff output formatting strings 207 | # wdiff2 uses ws0, ws1, ws3 208 | # wdiff3 uses ws0, ws1, ws2, ws3 209 | ws0 = { 210 | ws1 = | 211 | ws2 = | 212 | ws3 = } 213 | # alternative1 for UTF-8 terminal 214 | #ws0 = «🅐 215 | #ws1 = 🅑 216 | #ws2 = 🅒 217 | #ws3 = » 218 | # alternative2 for UTF-8 terminal 219 | #ws0 = « 220 | #ws1 = ╪ 221 | #ws2 = ╫ 222 | #ws3 = » 223 | """ 224 | 225 | 226 | def create_template(conf): 227 | config_file = os.path.expanduser(conf) 228 | if not os.path.exists(config_file): 229 | logger.debug("create configuration file: {}".format(conf)) 230 | try: 231 | with open(config_file, mode="w", buffering=io.DEFAULT_BUFFER_SIZE) as ofp: 232 | ofp.write(config_template) 233 | except IOError: 234 | logger.error("Error in creating configuration file: {}".format(conf)) 235 | sys.exit(2) 236 | else: 237 | logger.error("Erase {} before 'imediff -t'".format(conf)) 238 | sys.exit(2) 239 | return 240 | 241 | 242 | # Generate template file: TEMPLATE.imediff 243 | if __name__ == "__main__": 244 | create_template("TEMPLATE.imediff") 245 | -------------------------------------------------------------------------------- /src/imediff/data/git-ime.1: -------------------------------------------------------------------------------- 1 | ../../../usr/share/man/man1/git-ime.1 -------------------------------------------------------------------------------- /src/imediff/data/git-ime.in: -------------------------------------------------------------------------------- 1 | ../../../usr/bin/git-ime.in -------------------------------------------------------------------------------- /src/imediff/data/imediff: -------------------------------------------------------------------------------- 1 | ../../../usr/lib/git-core/mergetools/imediff -------------------------------------------------------------------------------- /src/imediff/data/imediff.1: -------------------------------------------------------------------------------- 1 | ../../../usr/share/man/man1/imediff.1 -------------------------------------------------------------------------------- /src/imediff/initialize_args.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # vim:se tw=79 sts=4 ts=4 et ai fileencoding=utf-8 : 3 | 4 | """ 5 | IMEDIFF - Interactive Merge Editor for DIFF2 and DIFF3 6 | Curses based single-pane fullscreen interactive tool 7 | and CLI based non-interactive tool with --macro 8 | 9 | Copyright (C) 2003, 2004 Jarno Elonen 10 | Copyright (C) 2018--2024 Osamu Aoki 11 | 12 | This program is free software; you can redistribute it and/or 13 | modify it under the terms of the GNU General Public License as 14 | published by the Free Software Foundation; either version 2 of 15 | the License, or (at your option) any later version. 16 | 17 | This program is distributed in the hope that it will be useful, 18 | but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | GNU General Public License for more details. 21 | 22 | You should have received a copy of the GNU General Public 23 | License along with this program; if not, write to the Free 24 | Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 25 | Boston, MA 02110-1301, USA. 26 | """ 27 | import argparse 28 | 29 | # NO LOGGING YET 30 | 31 | 32 | def initialize_args(): 33 | """ 34 | Parse command line options and arguments 35 | 36 | Input: 37 | commandline 38 | 39 | Return: 40 | args argument values 41 | """ 42 | pa = argparse.ArgumentParser( 43 | description="Interactive Merge Editor for 2 or 3 different files", 44 | epilog='Start "imediff" without any arguments to see the tutorial.', 45 | ) 46 | group = pa.add_mutually_exclusive_group() 47 | group.add_argument( 48 | "-a", action="store_true", help="Start with all chunks to use file_a" 49 | ) 50 | group.add_argument( 51 | "-b", action="store_true", help="Start with all chunks to use file_b" 52 | ) 53 | group.add_argument( 54 | "-c", 55 | action="store_true", 56 | help="Start with all chunks to use file_c (only for diff3)", 57 | ) 58 | group.add_argument( 59 | "-d", action="store_true", help="Start with all chunks to use diff" 60 | ) 61 | group.add_argument( 62 | "-f", action="store_true", help="Start with all chunks to use wdiff" 63 | ) 64 | group.add_argument( 65 | "-g", action="store_true", help="Start with good merge mode (only for diff3)" 66 | ) 67 | pa.add_argument( 68 | "-o", 69 | "--output", 70 | help="Write output to the given file. If this is missing, STDERR is used", 71 | ) 72 | pa.add_argument( 73 | "-l", 74 | "--force-logging", 75 | action="store_true", 76 | help='Force LOGFILE="imediff.log" and LOGLEVEL="INFO"', 77 | ) 78 | pa.add_argument( 79 | "-n", 80 | "--non-interactive", 81 | action="store_true", 82 | help="Use non-interactive CLI instead of normal TUI", 83 | ) 84 | pa.add_argument( 85 | "-s", 86 | "--sloppy", 87 | action="store_true", 88 | help="Allow one to save unresolved contents", 89 | ) 90 | pa.add_argument( 91 | "-t", 92 | "--template", 93 | action="store_true", 94 | help='Create a template configuration file "~/.imediff"', 95 | ) 96 | pa.add_argument( 97 | "-v", "--version", action="store_true", help="Show version and license" 98 | ) 99 | pa.add_argument( 100 | "-C", 101 | "--conf", 102 | default="~/.imediff", 103 | help='Specify configuration file to use. (default="~/.imediff", set this to "none" to use internal configuration only)', 104 | ) 105 | pa.add_argument("-M", "--macro", default="", help="Set MACRO string. E.g.: Abw") 106 | pa.add_argument( 107 | "-F", 108 | "--logfile", 109 | default="/dev/null", 110 | help='Enable logging to LOGFILE (default logging disabled as LOGFILE="/dev/null").', 111 | ) 112 | pa.add_argument( 113 | "-L", 114 | "--loglevel", 115 | default="WARNING", 116 | help='Set LOGLEVEL to "ERROR", "WARNING" (default), "INFO", or "DEBUG"', 117 | ) 118 | pa.add_argument( 119 | "-J", 120 | "--isjunk", 121 | action="store_true", 122 | help="Force isjunk to None instead of the default list", 123 | ) 124 | pa.add_argument( 125 | "-R", 126 | "--line-rule", 127 | action="store", 128 | default=2, 129 | help="Fuzzy match line filtering rule (0,1,2,3,10,11,12,13)", 130 | ) 131 | pa.add_argument( 132 | "-I", 133 | "--line-min", 134 | action="store", 135 | default=3, 136 | help="Fuzzy match minimum partial line length", 137 | ) 138 | pa.add_argument( 139 | "-A", 140 | "--line-max", 141 | action="store", 142 | default=80, 143 | help="Fuzzy match maximum partial line length", 144 | ) 145 | pa.add_argument( 146 | "-X", 147 | "--line-factor", 148 | action="store", 149 | default=8, 150 | help="Fuzzy match (partial line length shortening factor/2-depth) x 10, default 8", 151 | ) 152 | pa.add_argument("file_a", nargs="?", help="file for OLDER(diff2), MYFILE(diff3)") 153 | pa.add_argument( 154 | "file_b", nargs="?", help="file for NEWER(diff2), OLDFILE=BASE(diff3)" 155 | ) 156 | pa.add_argument( 157 | "file_c", 158 | nargs="?", 159 | help="file for ------------, YOURFILE(diff3) (only for diff3)", 160 | ) 161 | # pa.add_argument("--poke", "-p", nargs="?", default=None, help=argparse.SUPPRESS) 162 | args = pa.parse_args() 163 | args.macro_buffer = args.macro 164 | if args.file_c is not None: 165 | args.diff_mode = 3 166 | elif args.file_b is not None: 167 | args.diff_mode = 2 168 | elif args.file_a is not None: 169 | # undocumented help for diff3 170 | args.diff_mode = 1 # help for imediff for 3 files 171 | else: 172 | # undocumented help for diff2 173 | args.diff_mode = 0 # help for imediff for 2 files 174 | # 175 | # help for imediff for 3 files 176 | if args.a: 177 | args.default_action = "a" 178 | elif args.b: 179 | args.default_action = "b" 180 | elif args.c and args.diff_mode == 3: 181 | args.default_action = "c" 182 | elif args.d: 183 | args.default_action = "d" 184 | elif args.f: 185 | args.default_action = "f" 186 | elif args.g and args.diff_mode == 3: 187 | args.default_action = "g" 188 | elif args.diff_mode == 3: 189 | args.default_action = "g" 190 | else: # diff2 191 | args.default_action = "d" 192 | # 193 | # override for logging 194 | if args.force_logging: 195 | if args.loglevel == "WARNING": 196 | args.loglevel = "INFO" 197 | if args.logfile == "/dev/null": 198 | args.logfile = "imediff.log" 199 | 200 | # if args.poke is not None: 201 | # print("I: +++ hidden -p/--poke option is used with '{}' +++".format(args.poke)) 202 | 203 | return args 204 | -------------------------------------------------------------------------------- /src/imediff/initialize_confs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # vim:se tw=79 sts=4 ts=4 et ai fileencoding=utf-8 : 3 | 4 | """ 5 | IMEDIFF - Interactive Merge Editor for DIFF2 and DIFF3 6 | Curses based single-pane fullscreen interactive tool 7 | and CLI based non-interactive tool with --macro 8 | 9 | Copyright (C) 2003, 2004 Jarno Elonen 10 | Copyright (C) 2018--2024 Osamu Aoki 11 | 12 | This program is free software; you can redistribute it and/or 13 | modify it under the terms of the GNU General Public License as 14 | published by the Free Software Foundation; either version 2 of 15 | the License, or (at your option) any later version. 16 | 17 | This program is distributed in the hope that it will be useful, 18 | but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | GNU General Public License for more details. 21 | 22 | You should have received a copy of the GNU General Public 23 | License along with this program; if not, write to the Free 24 | Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 25 | Boston, MA 02110-1301, USA. 26 | """ 27 | from imediff.config import config_template 28 | 29 | import configparser 30 | import os 31 | import sys 32 | import logging 33 | 34 | logger = logging.getLogger(__name__) 35 | 36 | 37 | def initialize_confs(conf): 38 | """Process configuration file""" 39 | config_file = os.path.expanduser(conf) 40 | # Allow inline comment with # 41 | confs_i = configparser.ConfigParser(inline_comment_prefixes=("#")) 42 | confs_i.read_string(config_template) 43 | confs_f = configparser.ConfigParser(inline_comment_prefixes=("#")) 44 | if config_file == "none": 45 | confs = confs_i 46 | elif os.path.exists(config_file): 47 | confs_f.read(config_file) 48 | if ( 49 | "version" in confs_f["config"].keys() 50 | and confs_f["config"]["version"] == confs_i["config"]["version"] 51 | ): 52 | confs = confs_f 53 | else: 54 | logger.error( 55 | '''\ 56 | Error in {0}: version mismatch 57 | the current version: {1} 58 | the required version: {2} 59 | 60 | Rename {0} to {0}.bkup and make the new {0} by 61 | editing the template obtained by "imediff -t"'''.format( 62 | conf, confs_f["config"]["version"], confs_i["config"]["version"] 63 | ) 64 | ) 65 | # enhanced visibility 66 | print( 67 | '''\ 68 | Error in {0}: version mismatch 69 | the current version: {1} 70 | the required version: {2} 71 | 72 | Rename {0} to {0}.bkup and make the new {0} by 73 | editing the template obtained by "imediff -t"'''.format( 74 | conf, confs_f["config"]["version"], confs_i["config"]["version"] 75 | ) 76 | ) 77 | sys.exit(2) 78 | else: 79 | confs = confs_i 80 | logger.debug("confs: end with len(confs)={}".format(len(confs))) 81 | return confs 82 | -------------------------------------------------------------------------------- /src/imediff/install.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # vim:se tw=79 sts=4 ts=4 et ai fileencoding=utf-8 : 3 | 4 | """ 5 | IMEDIFF - Interactive Merge Editor for DIFF2 and DIFF3 6 | Curses based single-pane fullscreen interactive tool 7 | and CLI based non-interactive tool with --macro 8 | 9 | Copyright (C) 2003, 2004 Jarno Elonen 10 | Copyright (C) 2018--2024 Osamu Aoki 11 | 12 | This program is free software; you can redistribute it and/or 13 | modify it under the terms of the GNU General Public License as 14 | published by the Free Software Foundation; either version 2 of 15 | the License, or (at your option) any later version. 16 | 17 | This program is distributed in the hope that it will be useful, 18 | but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | GNU General Public License for more details. 21 | 22 | You should have received a copy of the GNU General Public 23 | License along with this program; if not, write to the Free 24 | Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 25 | Boston, MA 02110-1301, USA. 26 | """ 27 | 28 | from imediff import __version__ 29 | import os 30 | import sys 31 | import subprocess 32 | 33 | 34 | def install(): 35 | """ 36 | Entry point for imediff_install command 37 | 38 | Exit value 39 | 0 normal exit 40 | 1 error exit after an internal error 41 | """ 42 | 43 | if os.getuid == 0: 44 | print("E: This imediff_install needs to be run from non-root user") 45 | print( 46 | "I: For system-wide install, use the Debian package or the upstream source." 47 | ) 48 | print("I: * https://tracker.debian.org/pkg/imediff") 49 | print("I: * https://github.com/osamuaoki/imediff") 50 | sys.exit(1) 51 | # check install path of imediff module for site-package 52 | src_dir = os.path.dirname(os.path.abspath(__file__)) 53 | # possible values for this_file 54 | # ~/.local/lib/python3.11/site-packages/imediff -- normal user-mode 55 | # ~/.venv/lib/python3.11/site-packages/imediff -- after "python3 -m venv" 56 | python_dir = os.path.dirname(os.path.dirname(src_dir)) 57 | lib_dir = os.path.dirname(python_dir) 58 | dest_dir = os.path.dirname(lib_dir) 59 | fp = open(dest_dir + "/bin/git-ime", "w") 60 | subprocess.run(["mkdir", "-p", dest_dir + "/bin"]) 61 | subprocess.run( 62 | [ 63 | "sed", 64 | "-e", 65 | "s/@@version@@/" + __version__ + "/", 66 | src_dir + "/data/git-ime.in", 67 | ], 68 | stdout=fp, 69 | ) 70 | subprocess.run(["rm", "-f", src_dir + "/data/git-ime.in"]) 71 | subprocess.run(["chmod", "755", dest_dir + "/bin/git-ime"]) 72 | print("I: successfully installed: " + dest_dir + "/bin/git-ime") 73 | print("I: manual page for imediff can be found at: " + src_dir + "/imediff.1") 74 | print("I: manual page for git-ime can be found at: " + src_dir + "/git-ime.1") 75 | print("I: script for git-mergetool(1) can be found at: " + src_dir + "/imediff") 76 | print("I: For more, see the upstream source site.") 77 | print("I: * https://github.com/osamuaoki/imediff") 78 | # subprocess.run(['mkdir', '-p', usr_dir + "/lib/git-core/mergetools"]) 79 | # subprocess.run(['cp', '-f', src_dir + "/imediff", usr_dir + "/lib/git-core/mergetools"]) 80 | # subprocess.run(['mkdir -p', usr_dir + "/share/man/man1/"]) 81 | # subprocess.run(['cp', '-f', src_dir + "/imediff.1", usr_dir + "/share/man/man1/"]) 82 | # subprocess.run(['cp', '-f', src_dir + "/git-ime.1", usr_dir + "/share/man/man1/"]) 83 | 84 | sys.exit(0) 85 | 86 | 87 | if __name__ == "__main__": 88 | install() 89 | -------------------------------------------------------------------------------- /src/imediff/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # vim:se tw=79 sts=4 ts=4 et ai fileencoding=utf-8 : 3 | 4 | """ 5 | IMEDIFF - Interactive Merge Editor for DIFF2 and DIFF3 6 | Curses based single-pane fullscreen interactive tool 7 | and CLI based non-interactive tool with --macro 8 | 9 | Copyright (C) 2003, 2004 Jarno Elonen 10 | Copyright (C) 2018--2024 Osamu Aoki 11 | 12 | This program is free software; you can redistribute it and/or 13 | modify it under the terms of the GNU General Public License as 14 | published by the Free Software Foundation; either version 2 of 15 | the License, or (at your option) any later version. 16 | 17 | This program is distributed in the hope that it will be useful, 18 | but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | GNU General Public License for more details. 21 | 22 | You should have received a copy of the GNU General Public 23 | License along with this program; if not, write to the Free 24 | Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 25 | Boston, MA 02110-1301, USA. 26 | """ 27 | # utility imediff functions 28 | from imediff import __version__, __package__ 29 | from imediff.utils import read_lines 30 | from imediff.config import create_template 31 | from imediff.cli import TextData 32 | from imediff.tui import TextPad 33 | from imediff.initialize_confs import initialize_confs 34 | from imediff.initialize_args import initialize_args 35 | 36 | import locale 37 | import os 38 | import sys 39 | import shutil 40 | import logging 41 | 42 | logger = logging.getLogger(__name__) 43 | 44 | PACKAGE = __package__ 45 | version = __version__ 46 | 47 | version_string = "".join( 48 | filter( 49 | None, 50 | ( 51 | """\ 52 | {p} (version {v}) 53 | """.format( 54 | p=PACKAGE, v=version 55 | ), 56 | __doc__, 57 | ), 58 | ) 59 | ) 60 | 61 | opening = """\ 62 | =============================================================================== 63 | {p} (version {v}) 64 | 65 | The imediff command interactively merges 2 slightly different files with an 66 | optional base file using the in-place alternating display of the changed 67 | content on a single-pane full screen terminal user interface (TUI). 68 | 69 | Start this command from the shell command prompt as: 70 | * "imediff" to read the tutorial, 71 | * "imediff -h" to get all the command line options, 72 | * "imediff -o output OLDER NEWER" to merge 2 files, and 73 | * "imediff -o output MYFILE OLDFILE YOURFILE" to merge 3 files. 74 | 75 | In the interactive TUI, you can also type "t" to read the tutorial and type "/" 76 | to display the list of key commands. 77 | 78 | * Copyright (C) 2003,2004 Jarno Elonen 79 | * Copyright (C) 2018-2025 Osamu Aoki 80 | 81 | License: GPL 2.0+ 82 | =============================================================================== 83 | 84 | Type "q" to quit this program. 85 | """.format( 86 | p=PACKAGE, v=version 87 | ) 88 | 89 | 90 | ############################################################################## 91 | def main(): 92 | """ 93 | Entry point for imediff command 94 | 95 | Exit value 96 | 0 program exits normally after saving data 97 | 1 program quits without saving 98 | 2 program terminates after an internal error 99 | """ 100 | 101 | # preparation and arguments 102 | locale.setlocale(locale.LC_ALL, "") 103 | args = initialize_args() 104 | logging.basicConfig( 105 | format="%(levelname)s: %(filename)s: %(funcName)s: %(message)s", 106 | filename=args.logfile, 107 | level=getattr(logging, args.loglevel.upper(), logging.WARNING), 108 | ) 109 | logger.debug( 110 | "============================== start of main ==============================" 111 | ) 112 | if args.template: 113 | create_template(args.conf) 114 | sys.exit(0) 115 | 116 | # configuration 117 | confs = initialize_confs(args.conf) 118 | for section in confs.sections(): 119 | for key, value in confs[section].items(): 120 | logger.debug( 121 | "confs['{}'] >>> key='{}' value='{}'".format(section, key, value) 122 | ) 123 | editor = "editor" 124 | if "EDITOR" in os.environ: 125 | editor = os.environ["EDITOR"] 126 | if "editor" in confs["config"].keys(): 127 | editor = confs["config"]["editor"] 128 | args.edit_cmd = shutil.which(editor) 129 | if args.edit_cmd is None: 130 | args.edit_cmd = "/usr/bin/editor" # safe fall back 131 | logger.debug("external editor {} found as {}".format(editor, args.edit_cmd)) 132 | 133 | # normalize and process non-standard situation 134 | if args.version: 135 | print(version_string) 136 | sys.exit(0) 137 | 138 | if args.diff_mode == 0: # argument contains only zero file 139 | list_a = (opening).splitlines(keepends=True) 140 | list_b = list_a 141 | list_c = [] 142 | args.file_a = "" 143 | args.file_b = "" 144 | args.diff_mode = 2 145 | args.macro = "t" 146 | confs["config"]["confirm_quit"] = "False" 147 | confs["config"]["confirm_exit"] = "False" 148 | logger.debug("=== diff0 === Tutorial for diff2 ===") 149 | elif args.diff_mode == 1: # argument contains only 1 file 150 | list_a = (opening).splitlines(keepends=True) 151 | list_b = list_a 152 | list_c = list_a 153 | args.file_a = "" 154 | args.file_b = "" 155 | args.file_c = "" 156 | args.diff_mode = 3 157 | args.macro = "t" 158 | confs["config"]["confirm_quit"] = "False" 159 | confs["config"]["confirm_exit"] = "False" 160 | logger.debug("=== diff1 === Tutorial for diff3 ===") 161 | elif args.diff_mode == 2: 162 | # diff2 163 | list_a = read_lines(args.file_a) 164 | list_b = read_lines(args.file_b) 165 | list_c = None 166 | logger.debug( 167 | "=== diff2 === default_action='{}' non_interactive={} ===".format( 168 | args.default_action, args.non_interactive 169 | ), 170 | ) 171 | elif args.diff_mode == 3: 172 | list_a = read_lines(args.file_a) 173 | list_b = read_lines(args.file_b) 174 | list_c = read_lines(args.file_c) 175 | logger.debug( 176 | "=== diff3 === default_action='{}' non_interactive={} ===".format( 177 | args.default_action, args.non_interactive 178 | ), 179 | ) 180 | else: 181 | logger.error("imediff normally takes 2 or 3 files") 182 | sys.exit(2) 183 | 184 | if not args.non_interactive: 185 | display_instance = TextPad(list_a, list_b, list_c, args, confs) 186 | # set textpad size 187 | display_instance.main() 188 | del display_instance 189 | else: # non-interactive 190 | text_instance = TextData(list_a, list_b, list_c, args, confs) 191 | text_instance.main() 192 | del text_instance 193 | logger.debug("end of main") 194 | sys.exit(0) 195 | -------------------------------------------------------------------------------- /src/imediff/safe_curses.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # vim:se tw=79 sts=4 ts=4 et ai fileencoding=utf-8 : 3 | 4 | """ 5 | curses_pc: safe curses wrapper 6 | 7 | Copyright (C) 2018--2024 Osamu Aoki 8 | 9 | This program is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU General Public License as 11 | published by the Free Software Foundation; either version 2 of 12 | the License, or (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public 20 | License along with this program; if not, write to the Free 21 | Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 | Boston, MA 02110-1301, USA. 23 | """ 24 | 25 | import logging 26 | import curses 27 | 28 | logger = logging.getLogger(__name__) 29 | 30 | 31 | def get_keycode(keyname): 32 | if len(keyname) == 1: 33 | keycode = ord(keyname) 34 | elif keyname == "TAB": 35 | keycode = 9 36 | elif keyname == "BTAB": 37 | keycode = curses.KEY_BTAB 38 | elif keyname == "ENTER": 39 | keycode = 10 40 | elif keyname == "ESCAPE": 41 | keycode = 27 42 | elif keyname == "SPACE": 43 | keycode = ord(" ") 44 | elif keyname == "HASH": 45 | keycode = ord("#") 46 | elif keyname == "COLON": 47 | keycode = ord(":") 48 | elif keyname == "EQUAL": 49 | keycode = ord("=") 50 | elif keyname == "LBRACKET": 51 | keycode = ord("[") 52 | elif keyname == "\\": 53 | keycode = ord("=") 54 | elif keyname == "BACKSLASH": 55 | keycode = ord("]") 56 | elif keyname == "RBRACKET": 57 | keycode = ord("=") 58 | elif keyname == "UP": 59 | keycode = curses.KEY_UP 60 | elif keyname == "DOWN": 61 | keycode = curses.KEY_DOWN 62 | elif keyname == "LEFT": 63 | keycode = curses.KEY_LEFT 64 | elif keyname == "RIGHT": 65 | keycode = curses.KEY_RIGHT 66 | elif keyname == "INSERT": 67 | keycode = curses.KEY_IC 68 | elif keyname == "DELETE": 69 | keycode = curses.KEY_DC 70 | elif keyname == "HOME": 71 | keycode = curses.KEY_HOME 72 | elif keyname == "END": 73 | keycode = curses.KEY_END 74 | elif keyname == "PAGEUP": 75 | keycode = curses.KEY_PPAGE 76 | elif keyname == "PAGEDOWN": 77 | keycode = curses.KEY_BACKSPACE 78 | elif keyname == "BACKSPACE": 79 | keycode = curses.KEY_NPAGE 80 | elif keyname == "F1": 81 | keycode = curses.KEY_F1 82 | elif keyname == "F2": 83 | keycode = curses.KEY_F2 84 | elif keyname == "F3": 85 | keycode = curses.KEY_F3 86 | elif keyname == "F4": 87 | keycode = curses.KEY_F4 88 | elif keyname == "F5": 89 | keycode = curses.KEY_F5 90 | elif keyname == "F6": 91 | keycode = curses.KEY_F6 92 | elif keyname == "F7": 93 | keycode = curses.KEY_F7 94 | elif keyname == "F8": 95 | keycode = curses.KEY_F8 96 | elif keyname == "F9": 97 | keycode = curses.KEY_F9 98 | elif keyname == "F10": 99 | keycode = curses.KEY_F10 100 | elif keyname == "F11": 101 | keycode = curses.KEY_F11 102 | elif keyname == "F12": 103 | keycode = curses.KEY_F12 104 | else: 105 | keycode = ord(" ") 106 | return keycode 107 | 108 | 109 | def get_keyname(keycode): 110 | if keycode == 9: 111 | keyname = "TAB" 112 | elif keycode == curses.KEY_BTAB: 113 | keyname = "BTAB" 114 | elif keycode == 10: 115 | keyname = "ENTER" 116 | elif keycode == 27: 117 | keyname = "ESCAPE" 118 | elif keycode == ord(" "): 119 | keyname = "SPACE" 120 | elif keycode == ord("#"): 121 | keyname = "HASH" 122 | elif keycode == ord("%"): 123 | keyname = "PERCENT" 124 | elif keycode == ord(":"): 125 | keyname = "COLON" 126 | elif keycode == ord("="): 127 | keyname = "EQUAL" 128 | elif keycode == ord("["): 129 | keyname = "LBRACKET" 130 | elif keycode == ord("\\"): 131 | keyname = "BACKSLASH" 132 | elif keycode == ord("]"): 133 | keyname = "RBRACKET" 134 | elif keycode == curses.KEY_UP: 135 | keyname = "UP" 136 | elif keycode == curses.KEY_DOWN: 137 | keyname = "DOWN" 138 | elif keycode == curses.KEY_LEFT: 139 | keyname = "LEFT" 140 | elif keycode == curses.KEY_RIGHT: 141 | keyname = "RIGHT" 142 | elif keycode == curses.KEY_IC: 143 | keyname = "INSERT" 144 | elif keycode == curses.KEY_DC: 145 | keyname = "DELETE" 146 | elif keycode == curses.KEY_HOME: 147 | keyname = "HOME" 148 | elif keycode == curses.KEY_END: 149 | keyname = "END" 150 | elif keycode == curses.KEY_PPAGE: 151 | keyname = "PAGEUP" 152 | elif keycode == curses.KEY_NPAGE: 153 | keyname = "PAGEDOWN" 154 | elif keycode == curses.KEY_BACKSPACE: 155 | keyname = "BACKSPACE" 156 | elif keycode == curses.KEY_F1: 157 | keyname = "F1" 158 | elif keycode == curses.KEY_F2: 159 | keyname = "F2" 160 | elif keycode == curses.KEY_F3: 161 | keyname = "F3" 162 | elif keycode == curses.KEY_F4: 163 | keyname = "F4" 164 | elif keycode == curses.KEY_F5: 165 | keyname = "F5" 166 | elif keycode == curses.KEY_F6: 167 | keyname = "F6" 168 | elif keycode == curses.KEY_F7: 169 | keyname = "F7" 170 | elif keycode == curses.KEY_F8: 171 | keyname = "F8" 172 | elif keycode == curses.KEY_F9: 173 | keyname = "F9" 174 | elif keycode == curses.KEY_F10: 175 | keyname = "F10" 176 | elif keycode == curses.KEY_F11: 177 | keyname = "F11" 178 | elif keycode == curses.KEY_F12: 179 | keyname = "F12" 180 | elif keycode < ord(" "): 181 | logging.warning( 182 | "W: suspicous keycode below ASCII characters (control code?): {}".format( 183 | keycode 184 | ) 185 | ) 186 | keyname = "q" 187 | elif keycode >= 127: 188 | logging.warning( 189 | "W: suspicous keycode above ASCII characters (function keys?): {}".format( 190 | keycode 191 | ) 192 | ) 193 | keyname = "q" 194 | else: 195 | keyname = chr(keycode) 196 | return keyname 197 | 198 | 199 | #################################################################### 200 | def display_content(win, row, col, content, clrtoeol=False): 201 | # win: parent window (curses.stdscr for full screen) 202 | # row: row to put starting cursor in parent window 203 | # if negative or None, continue to use current cursor position 204 | # col: col to put starting cursor in parent window 205 | # if negative or None, continue to use current cursor position 206 | # content: [(line, i_b, i_e, attrib), ...] 207 | # line: input line (to be sliced) 208 | # i_b: input line begin point index (=) 209 | # i_e: input line end point index (<) 210 | # attrib: curses attribute 211 | # 212 | # rationale behind not-doing line[i_b, i_e] on the calling side 213 | # Python does slice-by-copy, meaning every time you slice (except for 214 | # very trivial slices, such as a[:]), it copies all of the data into 215 | # a new string object. 216 | # https://stackoverflow.com/questions/5722006/does-python-do-slice-by-reference-on-strings 217 | # 218 | win_row_max, win_col_max = win.getmaxyx() 219 | # win.leaveok(False) 220 | # set starting cursor position robustly 221 | if row is None or row < 0: 222 | try: 223 | row, _ = win.getyx() 224 | except curses.error as _: 225 | logger.warning("win.getyx() caused error") 226 | row = 0 227 | if col is None or col < 0: 228 | try: 229 | _, col = win.getyx() 230 | except curses.error as _: 231 | logger.warning("win.getyx() caused error") 232 | col = 0 233 | if row < win_row_max: 234 | try: 235 | win.move(row, 0) 236 | except curses.error as _: 237 | pass 238 | # single row window 239 | win1row = win.derwin(1, win_col_max, row, 0) 240 | if col is not None: 241 | try: 242 | win1row.move(0, col) 243 | except curses.error as _: 244 | pass 245 | for line, i_b, i_e, attrib in content: 246 | # use always positive reasonable index range to slice 247 | # line[i_b:i_e] 248 | if i_b is None: 249 | i_b = 0 250 | if i_b >= len(line): 251 | i_b = 0 252 | i_e = 0 253 | if i_e is None or i_e >= len(line): 254 | i_e = len(line) 255 | else: 256 | i_e = max(0, min(i_e, i_b + win_col_max, len(line))) 257 | try: 258 | win1row.addstr(line[i_b:i_e], attrib) 259 | except curses.error as _: 260 | pass 261 | try: 262 | _, col = win1row.getyx() 263 | except curses.error as _: 264 | col = 0 265 | if clrtoeol: 266 | win1row.clrtoeol() 267 | try: 268 | win.move(row, col) 269 | except curses.error as _: 270 | pass 271 | else: 272 | # not in writable cursor position 273 | pass 274 | 275 | 276 | def test_key_input(stdscr): 277 | curses.start_color() 278 | stdscr.clear() 279 | while True: 280 | content0 = [ 281 | ( 282 | "Please type key for number:", 283 | 0, 284 | None, 285 | curses.color_pair(0) | curses.A_BOLD, 286 | ) 287 | ] 288 | display_content(stdscr, 0, 0, content0, clrtoeol=True) 289 | stdscr.refresh() 290 | code = stdscr.getch() 291 | content1 = [ 292 | ("code={}".format(code), 0, None, curses.color_pair(0) | curses.A_REVERSE) 293 | ] 294 | display_content(stdscr, 1, 0, content1, clrtoeol=True) 295 | stdscr.refresh() 296 | content2 = [ 297 | ( 298 | "Please type key for string:", 299 | 0, 300 | None, 301 | curses.color_pair(0) | curses.A_BOLD, 302 | ) 303 | ] 304 | display_content(stdscr, 2, 0, content2, clrtoeol=True) 305 | stdscr.refresh() 306 | key = stdscr.getkey() 307 | content3 = [ 308 | ("key='{}'".format(key), 0, None, curses.color_pair(0) | curses.A_REVERSE) 309 | ] 310 | display_content(stdscr, 3, 0, content3, clrtoeol=True) 311 | stdscr.refresh() 312 | content4 = [ 313 | ( 314 | "Please type key for PC keyname:", 315 | 0, 316 | None, 317 | curses.color_pair(0) | curses.A_BOLD, 318 | ) 319 | ] 320 | display_content(stdscr, 4, 0, content4, clrtoeol=True) 321 | stdscr.refresh() 322 | code = stdscr.getch() 323 | content5 = [ 324 | # ("PC keyname={}".format(code), 0, None, curses.color_pair(0)|curses.A_REVERSE) 325 | ( 326 | "PC keyname={}".format(get_keyname(code)), 327 | 0, 328 | None, 329 | curses.color_pair(0) | curses.A_REVERSE, 330 | ) 331 | ] 332 | display_content(stdscr, 5, 0, content5, clrtoeol=True) 333 | stdscr.refresh() 334 | content9 = [ 335 | ( 336 | "Type any key to start again, 'q' to quit. escape_delay={}".format( 337 | curses.get_escdelay() 338 | ), 339 | 0, 340 | None, 341 | curses.color_pair(0) | curses.A_REVERSE, 342 | ) 343 | ] 344 | display_content(stdscr, 9, 0, content9, clrtoeol=True) 345 | code = stdscr.getch() 346 | stdscr.refresh() 347 | if code == ord("q"): 348 | break 349 | return 350 | 351 | 352 | if __name__ == "__main__": 353 | import sys 354 | 355 | # stdscr = curses.initscr() 356 | curses.wrapper(test_key_input) 357 | sys.exit(0) 358 | -------------------------------------------------------------------------------- /src/imediff/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # vim:se tw=79 sts=4 ts=4 et ai fileencoding=utf-8 : 3 | 4 | """ 5 | IMEDIFF - Interactive Merge Editor for DIFF2 and DIFF3 6 | Curses based single-pane fullscreen interactive tool 7 | and CLI based non-interactive tool with --macro 8 | 9 | Copyright (C) 2003, 2004 Jarno Elonen 10 | Copyright (C) 2018--2024 Osamu Aoki 11 | 12 | This program is free software; you can redistribute it and/or 13 | modify it under the terms of the GNU General Public License as 14 | published by the Free Software Foundation; either version 2 of 15 | the License, or (at your option) any later version. 16 | 17 | This program is distributed in the hope that it will be useful, 18 | but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | GNU General Public License for more details. 21 | 22 | You should have received a copy of the GNU General Public 23 | License along with this program; if not, write to the Free 24 | Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 25 | Boston, MA 02110-1301, USA. 26 | """ 27 | 28 | import io 29 | import sys 30 | import logging 31 | 32 | logger = logging.getLogger(__name__) 33 | 34 | # Utility functions 35 | # Console width with Zenkaku=2, Hankaku=1 (Hankaku=ASCII etc.) 36 | # Latin-1, CJK focus simplification applied. 37 | # (For Arabic, Hebrew etc., we need some refinement) 38 | # def console_width(text): 39 | # width = 0 40 | # for c in text: 41 | # if c == "\t": # TAB = 8 42 | # width += 8 43 | # elif unicodedata.east_asian_width(c) in "FWA": # Zenkaku 44 | # width += 2 45 | # else: 46 | # width += 1 47 | # return width 48 | 49 | 50 | # file read 51 | def read_lines(filename): 52 | logger.debug("read_lines filename = '{}'".format(filename)) 53 | if filename is None or filename == "": 54 | lines = [] 55 | else: 56 | try: 57 | with open(filename, buffering=io.DEFAULT_BUFFER_SIZE) as fp: 58 | lines = fp.readlines() # read into list with tailing \n 59 | except Exception as _: 60 | lines = [] 61 | return lines 62 | 63 | 64 | # file output 65 | def write_file(filename, output): 66 | logger.debug("write_file filename = '{}'".format(filename)) 67 | if filename is None or filename == "-" or filename == "": 68 | sys.stderr.write(output) 69 | else: 70 | try: 71 | with open(filename, mode="w", buffering=io.DEFAULT_BUFFER_SIZE) as fp: 72 | fp.write(output) 73 | except OSError as err: 74 | logger.error("Error {} in creating output file: {}".format(err, filename)) 75 | sys.exit(2) 76 | return 77 | 78 | 79 | def s_number(number): 80 | if number is None: 81 | ret = "*" 82 | else: 83 | ret = "{}".format(number) 84 | return ret 85 | -------------------------------------------------------------------------------- /test/00_local_bash.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # This is invoked from source tree to test python codes 4 | 5 | rm -f imediff.log 6 | 7 | PYTHONPATH=$(pwd)/../src 8 | PYTHONPATH=$(realpath $PYTHONPATH) 9 | echo "I: Setting PYTHONPATH=$PYTHONPATH" 10 | export PYTHONPATH 11 | 12 | "$@" 13 | -------------------------------------------------------------------------------- /test/00_local_clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # This is invoked from source tree to test git-ime codes 4 | # Use installed imediff (deb or wheel) 5 | # 6 | # working toss-away repo location 7 | THISFILE="$(realpath $0)" 8 | THISDIR="${THISFILE%/*}" 9 | REPO_DIR="${THISDIR}/repo" 10 | 11 | set -x 12 | rm -f *.new *.out 13 | rm -rf "$REPO_DIR" 14 | rm -f imediff.log 15 | echo "===== CLEAN ALL =====" 16 | -------------------------------------------------------------------------------- /test/00_local_git_ime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # This is invoked from source tree to test git-ime codes 4 | # Use installed imediff (deb or wheel) 5 | # 6 | # working toss-away repo location 7 | THISFILE="$(realpath $0)" 8 | THISDIR="${THISFILE%/*}" 9 | NEWPATH="$(realpath ${THISDIR}/../usr/bin)" 10 | GIT_IME="$(realpath $THISDIR/../usr/bin/git-ime.in)" 11 | PATH="$THISDIR/../usr/bin:$PATH" 12 | # echo GIT_IME 13 | # echo $GIT_IME 14 | # echo THISDIR 15 | # echo $THISDIR 16 | # echo $NEWPATH 17 | # echo $PATH 18 | $GIT_IME "$@" 19 | echo "===== SUCCESS ALL =====" 20 | -------------------------------------------------------------------------------- /test/00_local_imediff.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # This is invoked from source tree to test python codes 4 | 5 | rm -f imediff.log 6 | 7 | PYTHONPATH=$(pwd)/../src 8 | PYTHONPATH=$(realpath $PYTHONPATH) 9 | echo "I: Setting PYTHONPATH=$PYTHONPATH" 10 | export PYTHONPATH 11 | 12 | # ignore ~/.imediff and output log 13 | python3 _imediff.py -l -C none "$@" 14 | -------------------------------------------------------------------------------- /test/00_local_imediff_cfgs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # This is invoked from source tree to test python codes 4 | 5 | rm -f imediff.log z_imediff.conf.new 6 | 7 | PYTHONPATH=$(pwd)/../src 8 | PYTHONPATH=$(realpath $PYTHONPATH) 9 | echo "I: Setting PYTHONPATH=$PYTHONPATH" 10 | export PYTHONPATH 11 | 12 | # ignore ~/.imediff 13 | python3 _imediff.py -l -C z_imediff.conf.new -t 14 | echo 15 | for f in *.new; do 16 | g="${f%.new}.ref" 17 | echo " ==== COMPARE: $g vs. $f ===" 18 | if diff -u "$g" "$f"; then 19 | echo " -> = NO_DIFF: $g" 20 | else 21 | echo " -> ! DIFF: $g" 22 | EXITSTATUS=false 23 | fi 24 | echo 25 | done 26 | $EXITSTATUS 27 | -------------------------------------------------------------------------------- /test/10_local_imediff_abcdfg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # This is invoked from source tree to test python codes 4 | # 5 | # This is the same as test_unittest_all.py but easier to modify to identify issues from console 6 | # This is not for unittest 7 | # 8 | rm -f imediff.log 9 | 10 | PYTHONPATH=$(pwd)/../src 11 | PYTHONPATH=$(realpath $PYTHONPATH) 12 | echo "I: Setting PYTHONPATH=$PYTHONPATH" 13 | export PYTHONPATH 14 | 15 | COMMAND="python3 _imediff.py -l" 16 | EXITSTATUS=true 17 | # test 2 file diff 18 | $COMMAND --macro=w -C none -n file_a file_b -o z_imediff2.new 19 | # test 2 file diff 20 | $COMMAND --macro=w -C none -n -f file_a file_b -o z_imediff2_f.new 21 | # test 2 file diff 22 | $COMMAND --macro=w -C none -n -a file_a file_b -o z_imediff2_a.new 23 | # test 2 file diff 24 | $COMMAND --macro=w -C none -n -b file_a file_b -o z_imediff2_b.new 25 | # test 2 file diff 26 | $COMMAND --macro=w -C none -n -d file_a file_b -o z_imediff2_d.new 27 | # test 3 file diff with merge 28 | $COMMAND --macro=w -C none -n file_a file_b file_c -o z_imediff3.new 29 | # test 3 file diff with extra word diff merge 30 | $COMMAND --macro=w -C none -n -a file_a file_b file_c -o z_imediff3_a.new 31 | # test 3 file diff with extra word diff merge 32 | $COMMAND --macro=w -C none -n -b file_a file_b file_c -o z_imediff3_b.new 33 | # test 3 file diff with extra word diff merge 34 | $COMMAND --macro=w -C none -n -c file_a file_b file_c -o z_imediff3_c.new 35 | # test 3 file diff with extra word diff merge 36 | $COMMAND --macro=w -C none -n -d file_a file_b file_c -o z_imediff3_d.new 37 | # test 3 file diff with extra word diff merge 38 | $COMMAND --macro=w -C none -n -f file_a file_b file_c -o z_imediff3_f.new 39 | # test 3 file diff with extra word diff merge 40 | $COMMAND --macro=w -C none -n -g file_a file_b file_c -o z_imediff3_g.new 41 | echo 42 | ls -l *.new 43 | for f in *.new; do 44 | g="${f%.new}.ref" 45 | echo " ==== COMPARE: $g vs. $f ===" 46 | if diff -u "$g" "$f"; then 47 | echo " -> = NO_DIFF: $g" 48 | else 49 | echo " -> ! DIFF: $g" 50 | EXITSTATUS=false 51 | fi 52 | echo 53 | done 54 | $EXITSTATUS 55 | -------------------------------------------------------------------------------- /test/10_local_imediff_diff23.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # This is invoked from source tree to test python codes 4 | # 5 | # This is the same as test_unittest_all.py but easier to modify to identify issues from console 6 | # This is not for unittest 7 | # 8 | 9 | PYTHONPATH=$(pwd)/../src 10 | PYTHONPATH=$(realpath $PYTHONPATH) 11 | echo "I: Setting PYTHONPATH=$PYTHONPATH" 12 | export PYTHONPATH 13 | 14 | EXITSTATUS=true 15 | 16 | echo 17 | # doctest for diff23.py 18 | python3 _diff23.py >z_diff23.new 19 | for f in *.new; do 20 | g="${f%.new}.ref" 21 | echo " ==== COMPARE: $g vs. $f ===" 22 | if diff -u "$g" "$f"; then 23 | echo " -> = NO_DIFF: $g" 24 | else 25 | echo " -> ! DIFF: $g" 26 | EXITSTATUS=false 27 | fi 28 | echo 29 | done 30 | $EXITSTATUS 31 | -------------------------------------------------------------------------------- /test/10_local_imediff_doctest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # This is invoked from source tree to test python codes 4 | # 5 | # This is the same as test_unittest_all.py but easier to modify to identify issues from console 6 | # This is not for unittest 7 | # 8 | 9 | PYTHONPATH=$(pwd)/../src 10 | PYTHONPATH=$(realpath $PYTHONPATH) 11 | echo "I: Setting PYTHONPATH=$PYTHONPATH" 12 | export PYTHONPATH 13 | 14 | EXITSTATUS=true 15 | 16 | echo 17 | # doctest 18 | python3 ../src/imediff/diff3lib.py 19 | echo "I: success for doctest on src/imediff/diff3lib.py" 20 | echo 21 | python3 ../src/imediff/lines2lib.py 22 | echo "I: success for doctest on src/imediff/lines2lib.py" 23 | echo 24 | -------------------------------------------------------------------------------- /test/80_local_imediff_log.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # This is invoked from source tree to test python codes 4 | # This is redundant so this is not part of standard build check 5 | # See 00_local_imediff_cfgs.sh 6 | # 7 | rm -f imediff.log 8 | 9 | PYTHONPATH=$(pwd)/../src 10 | PYTHONPATH=$(realpath $PYTHONPATH) 11 | echo "I: Setting PYTHONPATH=$PYTHONPATH" 12 | export PYTHONPATH 13 | 14 | # ignore ~/.imediff 15 | python3 _imediff.py -l -C none -L DEBUG -n --macro=q 16 | sed -e '/external editor/,$d' imediff.log >z_imediff-log.new 17 | 18 | 19 | for f in *.new; do 20 | g="${f%.new}.ref" 21 | echo " ==== COMPARE: $g vs. $f ===" 22 | if diff -u "$g" "$f"; then 23 | echo " -> = NO_DIFF: $g" 24 | else 25 | echo " -> ! DIFF: $g" 26 | EXITSTATUS=false 27 | fi 28 | echo 29 | done 30 | $EXITSTATUS 31 | -------------------------------------------------------------------------------- /test/80_local_imediff_test_dfg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # This is invoked from source tree to test python codes 4 | # 5 | # This is redundant so this is not part of standard build check 6 | # See 10_local_imediff_abcdfg.sh 7 | # 8 | rm -f imediff.log 9 | 10 | PYTHONPATH=$(pwd)/../src 11 | PYTHONPATH=$(realpath $PYTHONPATH) 12 | echo "I: Setting PYTHONPATH=$PYTHONPATH" 13 | export PYTHONPATH 14 | 15 | EXITSTATUS=true 16 | COMMAND="python3 _imediff.py -l" 17 | #COMMAND="imediff" 18 | EXITSTATUS=true 19 | # clean 20 | set -x 21 | rm -f *.new 22 | rm -rf "$REPO_DIR" 23 | rm -f imediff.log 24 | echo "===== CLEAN ALL =====" 25 | # test 3 file diff with merge 26 | set +x 27 | $COMMAND --macro=w -C none -n file_a file_b file_c -o z_imediff3.new 28 | # test 3 file diff with extra word diff merge 29 | $COMMAND --macro=w -C none -n -d file_a file_b file_c -o z_imediff3_d.new 30 | # test 3 file diff with extra word diff merge 31 | $COMMAND --macro=w -C none -n -f file_a file_b file_c -o z_imediff3_f.new 32 | # test 3 file diff with extra word diff merge 33 | $COMMAND --macro=w -C none -n -g file_a file_b file_c -o z_imediff3_g.new 34 | # doctest for diff23.py 35 | 36 | echo 37 | # doctest 38 | for f in *.new; do 39 | g="${f%.new}.ref" 40 | echo " ==== COMPARE: $g vs. $f ===" 41 | if diff -u "$g" "$f"; then 42 | echo " -> = NO_DIFF: $g" 43 | else 44 | echo " -> ! DIFF: $g" 45 | EXITSTATUS=false 46 | fi 47 | echo 48 | done 49 | $EXITSTATUS 50 | -------------------------------------------------------------------------------- /test/90_local_git_ime_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # This is invoked from source tree to test git-ime codes 4 | # Use installed imediff (deb or wheel) 5 | # 6 | # working toss-away repo location 7 | THISFILE="$(realpath $0)" 8 | THISDIR="${THISFILE%/*}" 9 | REPO_DIR="${THISDIR}/repo" 10 | GIT_IME=$THISDIR/../usr/bin/git-ime.in 11 | 12 | if [ "$1" = "-c" ]; then 13 | rm -rf "$REPO_DIR" 14 | exit 15 | fi 16 | 17 | create_repo() { 18 | git init 19 | git add . 20 | git commit -m "initial" 21 | git tag "initial" 22 | } 23 | commit_data() { 24 | git add -A . 25 | git commit -m "data changed" 26 | } 27 | base_data() { 28 | for i in $(seq 10 29); do 29 | : >"FILE_$i" 30 | for j in $(seq 10 29); do 31 | echo "CONTENT $i -- $j" >>"FILE_$i" 32 | done 33 | done 34 | } 35 | change_data() { 36 | for i in $(seq 10 29); do 37 | sed -i 's/[257]/X/g' "FILE_$i" 38 | done 39 | } 40 | change_a_file() { 41 | mv FILE_10 FILE_99 42 | sed -i "s/9/X/g" "FILE_99" 43 | git add FILE_99 44 | } 45 | drop_data() { 46 | for i in $(seq 10 3 29); do 47 | rm "FILE_$i" 48 | done 49 | } 50 | add_data() { 51 | for i in $(seq 30 39); do 52 | : >"FILE_$i" 53 | for j in $(seq 10 29); do 54 | echo "CONTENT $i -- $j" >>"FILE_$i" 55 | done 56 | done 57 | } 58 | junk1_data() { 59 | for i in $(seq 40 2 45); do 60 | echo "CONTENT $i" >"FILE_$i" 61 | done 62 | } 63 | junk2_data() { 64 | for i in $(seq 11 39); do 65 | if [ -e "FILE_$i" ]; then 66 | sed -i 's/^/RANDOM /' "FILE_$i" 67 | fi 68 | done 69 | } 70 | 71 | test_good1() { 72 | echo "--- split multiple file commits" 73 | rm -rf "$REPO_DIR" 74 | test -d "$REPO_DIR" || mkdir -p "$REPO_DIR" 75 | cd "$REPO_DIR" 76 | base_data 77 | create_repo 78 | change_data 79 | commit_data 80 | echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" 81 | # shellcheck disable=SC2086 82 | $GIT_IME -a 83 | N_CHANGED="$(git log --pretty=oneline | wc -l)" 84 | echo "I: >>> changed $N_CHANGED" 85 | if [ "$N_CHANGED" = "21" ]; then 86 | echo "NO_ERROR at test_good1 (expected)" 87 | else 88 | echo "ERROR at test_good1" 89 | false 90 | fi 91 | cd .. 92 | } 93 | 94 | test_good2() { 95 | echo "--- split a commit with multiple files with changes by file" 96 | rm -rf "$REPO_DIR" 97 | test -d "$REPO_DIR" || mkdir -p "$REPO_DIR" 98 | cd "$REPO_DIR" 99 | base_data 100 | create_repo 101 | drop_data 102 | add_data 103 | commit_data 104 | echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" 105 | $GIT_IME -a 106 | N_CHANGED="$(git log --pretty=oneline | wc -l)" 107 | echo "I: >>> changed $N_CHANGED" # 31 = 30 (changes)+ 1(initial) 108 | if [ "$N_CHANGED" = "18" ]; then 109 | echo "NO_ERROR at test_good2 (expected)" 110 | else 111 | echo "ERROR at test_good2" 112 | false 113 | fi 114 | cd .. 115 | } 116 | 117 | test_good3() { 118 | echo "--- split a commit with a moved file" 119 | rm -rf "$REPO_DIR" 120 | test -d "$REPO_DIR" || mkdir -p "$REPO_DIR" 121 | cd "$REPO_DIR" 122 | base_data 123 | create_repo 124 | change_a_file 125 | commit_data 126 | echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" 127 | $GIT_IME -a 128 | git log --pretty=oneline 129 | N_CHANGED="$(git log --pretty=oneline | wc -l)" 130 | echo "I: >>> changed $N_CHANGED" 131 | if [ "$N_CHANGED" = "3" ]; then 132 | echo "NO_ERROR at test_good3 (expected)" 133 | else 134 | echo "ERROR at test_good3" 135 | false 136 | fi 137 | cd .. 138 | } 139 | 140 | test_bad1() { 141 | echo "--- revert contents to HEAD^ after commit" 142 | rm -rf "$REPO_DIR" 143 | test -d "$REPO_DIR" || mkdir -p "$REPO_DIR" 144 | cd "$REPO_DIR" 145 | base_data 146 | create_repo 147 | change_data 148 | drop_data 149 | add_data 150 | commit_data 151 | base_data 152 | echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" 153 | # shellcheck disable=SC2086 154 | if $GIT_IME $OPTQ; then 155 | echo "NO_ERROR at test_bad1" 156 | false 157 | else 158 | echo "ERROR at test_bad1 (expected)" 159 | fi 160 | cd .. 161 | } 162 | 163 | test_bad2() { 164 | echo "--- add bogus empty directory after commit" 165 | rm -rf "$REPO_DIR" 166 | test -d "$REPO_DIR" || mkdir -p "$REPO_DIR" 167 | cd "$REPO_DIR" 168 | base_data 169 | create_repo 170 | change_data 171 | drop_data 172 | add_data 173 | commit_data 174 | mkdir -p bogus 175 | echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" 176 | # shellcheck disable=SC2086 177 | if $GIT_IME $OPTQ; then 178 | echo "NO_ERROR at test_bad2" 179 | false 180 | else 181 | echo "ERROR at test_bad2 (expected)" 182 | fi 183 | cd .. 184 | } 185 | 186 | test_bad3() { 187 | echo "--- add junk files after commit" 188 | rm -rf "$REPO_DIR" 189 | test -d "$REPO_DIR" || mkdir -p "$REPO_DIR" 190 | cd "$REPO_DIR" 191 | base_data 192 | create_repo 193 | change_data 194 | drop_data 195 | add_data 196 | commit_data 197 | junk1_data 198 | echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" 199 | # shellcheck disable=SC2086 200 | if $GIT_IME $OPTQ; then 201 | echo "NO_ERROR at test_bad3" 202 | false 203 | else 204 | echo "ERROR at test_bad3 (expected)" 205 | fi 206 | cd .. 207 | } 208 | 209 | test_bad4() { 210 | echo "--- add junk changes after commit" 211 | rm -rf "$REPO_DIR" 212 | test -d "$REPO_DIR" || mkdir -p "$REPO_DIR" 213 | cd "$REPO_DIR" 214 | base_data 215 | create_repo 216 | change_data 217 | drop_data 218 | add_data 219 | commit_data 220 | junk2_data 221 | echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" 222 | # shellcheck disable=SC2086 223 | if $GIT_IME $OPTQ; then 224 | echo "NO_ERROR at test_bad4" 225 | false 226 | else 227 | echo "ERROR at test_bad4 (expected)" 228 | fi 229 | cd .. 230 | } 231 | 232 | echo "===== START =====" 233 | echo "=== vvv SHOULD WORK vvv === 1" 234 | test_good1 235 | echo 236 | echo "=== vvv SHOULD WORK vvv === 2" 237 | test_good2 238 | echo 239 | echo "=== vvv SHOULD WORK vvv === 3" 240 | test_good3 241 | echo 242 | echo "=== vvv SHOULD FAIL vvv === 1" 243 | test_bad1 244 | echo 245 | echo "=== vvv SHOULD FAIL vvv === 2" 246 | test_bad2 247 | echo 248 | echo "=== vvv SHOULD FAIL vvv === 3" 249 | test_bad3 250 | echo 251 | echo "=== vvv SHOULD FAIL vvv === 4" 252 | test_bad4 253 | echo 254 | echo 255 | rm -rf "$REPO_DIR" 256 | echo "===== SUCCESS ALL =====" 257 | -------------------------------------------------------------------------------- /test/90_local_imediff_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # This is invoked from source tree to test python codes 4 | # 5 | # This is the same as test_unittest_all.py but easier to modify to identify issues from console 6 | # This is not for unittest 7 | # 8 | rm -f imediff.log 9 | 10 | PYTHONPATH=$(pwd)/../src 11 | PYTHONPATH=$(realpath $PYTHONPATH) 12 | echo "I: Setting PYTHONPATH=$PYTHONPATH" 13 | export PYTHONPATH 14 | 15 | EXITSTATUS=true 16 | COMMAND="python3 _imediff.py -l" 17 | #COMMAND="imediff" 18 | EXITSTATUS=true 19 | # test 2 file diff 20 | $COMMAND --macro=w -C none -n file_a file_b -o z_imediff2.new 21 | # test 2 file diff 22 | $COMMAND --macro=w -C none -n -f file_a file_b -o z_imediff2_f.new 23 | # test 2 file diff 24 | $COMMAND --macro=w -C none -n -a file_a file_b -o z_imediff2_a.new 25 | # test 2 file diff 26 | $COMMAND --macro=w -C none -n -b file_a file_b -o z_imediff2_b.new 27 | # test 2 file diff 28 | $COMMAND --macro=w -C none -n -d file_a file_b -o z_imediff2_d.new 29 | # test 3 file diff with merge 30 | $COMMAND --macro=w -C none -n file_a file_b file_c -o z_imediff3.new 31 | # test 3 file diff with extra word diff merge 32 | $COMMAND --macro=w -C none -n -a file_a file_b file_c -o z_imediff3_a.new 33 | # test 3 file diff with extra word diff merge 34 | $COMMAND --macro=w -C none -n -b file_a file_b file_c -o z_imediff3_b.new 35 | # test 3 file diff with extra word diff merge 36 | $COMMAND --macro=w -C none -n -c file_a file_b file_c -o z_imediff3_c.new 37 | # test 3 file diff with extra word diff merge 38 | $COMMAND --macro=w -C none -n -d file_a file_b file_c -o z_imediff3_d.new 39 | # test 3 file diff with extra word diff merge 40 | $COMMAND --macro=w -C none -n -f file_a file_b file_c -o z_imediff3_f.new 41 | # test 3 file diff with extra word diff merge 42 | $COMMAND --macro=w -C none -n -g file_a file_b file_c -o z_imediff3_g.new 43 | # doctest for diff23.py 44 | python3 _diff23.py >z_diff23.new 45 | # check configuration initializer default values 46 | python3 _imediff.py -C z_imediff.conf.new -t 47 | 48 | echo 49 | # doctest 50 | python3 ../src/imediff/diff3lib.py 51 | echo "I: success for doctest on src/imediff/diff3lib.py" 52 | echo 53 | python3 ../src/imediff/lines2lib.py 54 | echo "I: success for doctest on src/imediff/lines2lib.py" 55 | echo 56 | for f in *.new; do 57 | g="${f%.new}.ref" 58 | echo " ==== COMPARE: $g vs. $f ===" 59 | if diff -u "$g" "$f"; then 60 | echo " -> = NO_DIFF: $g" 61 | else 62 | echo " -> ! DIFF: $g" 63 | EXITSTATUS=false 64 | fi 65 | echo 66 | done 67 | $EXITSTATUS 68 | -------------------------------------------------------------------------------- /test/90_local_imediff_unittest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | # vim: set sw=2 et sw=2 sts=2: 3 | # This is invoked from source tree to test python codes 4 | 5 | rm -f imediff.log 6 | 7 | PYTHONPATH=$(pwd)/../src 8 | PYTHONPATH=$(realpath $PYTHONPATH) 9 | echo "I: Setting PYTHONPATH=$PYTHONPATH" 10 | export PYTHONPATH 11 | 12 | python3 test_unittest_all.py -v 13 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Test suite for imediff and git-ime 2 | 3 | This directory contains files to test imediff and git-ime. 4 | 5 | ## Test code used by dh_test and autopkgtest 6 | 7 | * `test_unittest_all.py` 8 | * Run a set of test to see its diff logics are valid 9 | * `./_diff23.py` -- test with files (file_a, file_b file_c) 10 | * `./_imediff.py` -- test with imediff in this source. 11 | * `../src/imediff/diff3lib.py` -- doctest 12 | * `../src/imediff/lines2lib.py` -- doctest 13 | 14 | ## Test codes manually run as you write and update codes 15 | 16 | * `./90_local_imediff_tests.sh` 17 | * Run a set of test to see its diff logic are valid 18 | * `./_diff23.py` -- test with files (file_a, file_b file_c) 19 | * `./_imediff.py` -- test with imediff in this source. 20 | * Run a subset of the test normally run by unittest via 21 | `./test_unittest_all.py` without using unittest. 22 | * Python path is adjusted as: `export PYTHONPATH=../src` 23 | * `./90_local_git_ime_tests.sh` 24 | * Run a set of test to see its git ime are valid 25 | * Use local shell code `../usr/bin/git-ime.in` 26 | * `90_local_imediff_unittest.sh` 27 | * Run `./test_unittest_all.py` using of the local python module code. 28 | * `./00_local_imediff.sh` 29 | * Run the local python module code with user provided arguments 30 | * Effectively the same test run by unittest via `./test_unittest_all.py` 31 | * `./00_local_git_ime.sh` 32 | * Run the local shell code `../usr/bin/git-ime` with user provided arguments 33 | * Path is adjusted as: `export PATH=../usr/bin/:$PATH` 34 | 35 | These are meant to be executed in this directory. 36 | 37 | ## NOTE 38 | 39 | For Debian package building, dh_auto_test isn't run from here. It looks like a 40 | copy of test/ directory as test and a copy of src/imediff directory as imediff 41 | are created at .pybuild/cpython3_*.*/ where the current directory is moved to. 42 | 43 | Notable thing is that when test/ directories are copied, symlinks are copied as files. 44 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osamuaoki/imediff/eb2e17b4b328f95eeefaab244aed5415339a071c/test/__init__.py -------------------------------------------------------------------------------- /test/_diff23.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # vim:se tw=79 sts=4 ts=4 et ai: 3 | """ 4 | Script to test diff2 and diff3 logic 5 | 6 | The diff3 logic is build on top of diff2 logic. 7 | 8 | Current diff2 uses Python standard library difflib which uses a variant of 9 | longest contiguous matching subsequence algorithm by Ratcliff and Obershelp 10 | developed in the late 1980's. If I update this imediff program to use more 11 | modern algorithm, this test may yield slightly different result in some 12 | corner cases. 13 | """ 14 | 15 | import os 16 | import sys 17 | import difflib 18 | import imediff.diff3lib 19 | 20 | 21 | def diff23(a, b, c, name=""): 22 | print("# Test type: " + name) 23 | print(" a='%s' -> %i" % (a, len(a))) 24 | print(" b='%s' -> %i" % (b, len(b))) 25 | print(" c='%s' -> %i" % (c, len(c))) 26 | sa = difflib.SequenceMatcher(None, a, b) 27 | print("$ diff2 A B") 28 | for tag, i1, i2, j1, j2 in sa.get_opcodes(): 29 | print( 30 | ( 31 | " %s a[%d:%d] (%s) / b[%d:%d] (%s)" 32 | % (tag, i1, i2, a[i1:i2], j1, j2, b[j1:j2]) 33 | ) 34 | ) 35 | print("$ diff2 C B") 36 | sc = difflib.SequenceMatcher(None, c, b) 37 | for tag, i1, i2, j1, j2 in sc.get_opcodes(): 38 | print( 39 | ( 40 | " %s c[%d:%d] (%s) / b[%d:%d] (%s)" 41 | % (tag, i1, i2, c[i1:i2], j1, j2, b[j1:j2]) 42 | ) 43 | ) 44 | s = imediff.diff3lib.SequenceMatcher3(a, b, c, 0, None, True) 45 | print("$ diff3 A B C") 46 | for tag, i1, i2, j1, j2, k1, k2 in s.get_opcodes(): 47 | print( 48 | ( 49 | " %s a[%d:%d] (%s) / b[%d:%d] (%s) / c[%d:%d] (%s)" 50 | % (tag, i1, i2, a[i1:i2], j1, j2, b[j1:j2], k1, k2, c[k1:k2]) 51 | ) 52 | ) 53 | print("===========================================================") 54 | return 55 | 56 | 57 | print("\nI: _diff23 start >>>", file=sys.stderr) 58 | cwd_dir = os.getcwd() 59 | print("I: cwd_dir = '{}'".format(cwd_dir), file=sys.stderr) 60 | print("I: test_file = '{}' (active)".format(__file__), file=sys.stderr) 61 | test_dir = os.path.dirname(os.path.abspath(__file__)) 62 | print("I: test_dir = '{}' (active)".format(test_dir), file=sys.stderr) 63 | if "PYTHONPATH" in os.environ: 64 | print("I: PYTHONPATH = '{}'".format(os.environ["PYTHONPATH"]), file=sys.stderr) 65 | else: 66 | print("I: PYTHONPATH = ", file=sys.stderr) 67 | print("I: _diff23 end <<<", file=sys.stderr) 68 | 69 | diff23("12345", "12345", "12345", "same") 70 | 71 | diff23("a12345z", "1245", "1245", "add a side") 72 | 73 | diff23("a12345z", "1245", "a12345z", "add same") 74 | 75 | diff23("1245", "1245", "a12345z", "add c side") 76 | 77 | diff23("24", "12345", "12345", "delete a side") 78 | 79 | diff23("24", "12345", "24", "delete same") 80 | 81 | diff23("12345", "12345", "24", "delete c side") 82 | 83 | diff23("a2b4c", "12345", "x2y4z", "conflict both ends") 84 | 85 | diff23("1a2b4c", "12345", "x2y45z", "conflict skewed") 86 | -------------------------------------------------------------------------------- /test/_imediff.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # vim:se tw=0 sts=4 ts=4 et ai: 3 | """ 4 | Script to test imediff.main module 5 | 6 | Use as: 7 | 8 | $ python3 _imediff ... 9 | 10 | """ 11 | import os 12 | import sys 13 | import imediff.main 14 | 15 | print("\nI: _imediff start >>>", file=sys.stderr) 16 | cwd_dir = os.getcwd() 17 | print("I: cwd_dir = '{}'".format(cwd_dir), file=sys.stderr) 18 | print("I: test_file = '{}' (active)".format(__file__), file=sys.stderr) 19 | test_dir = os.path.dirname(os.path.abspath(__file__)) 20 | print("I: test_dir = '{}' (active)".format(test_dir), file=sys.stderr) 21 | if "PYTHONPATH" in os.environ: 22 | print("I: PYTHONPATH = '{}'".format(os.environ["PYTHONPATH"]), file=sys.stderr) 23 | else: 24 | print("I: PYTHONPATH = ", file=sys.stderr) 25 | print("I: _imediff end <<<", file=sys.stderr) 26 | # 27 | sys.exit(imediff.main.main()) 28 | -------------------------------------------------------------------------------- /test/file_a: -------------------------------------------------------------------------------- 1 | EXTRA-LINE 2 | 2-way OLDER_FILE / 3-way MYFILE 3 | 00 wsx 4 | 01 qaz 5 | 02 adf 6 | 03 zcv aaa [G] 7 | 04 adv 8 | 05 tqazwsxyu 9 | 06 ghj 10 | 07 ujj 11 | 08 qaz 12 | 09 wsx 13 | 10 tte 14 | 11 iao 15 | 12 njk 16 | 13 mop 17 | 14 wdd 18 | 16 ijb 19 | 17 wsc 20 | 18 ugv 21 | 19 ijp 22 | 21 iao 23 | 22 njk 24 | 24 wdd 25 | 25 edv 26 | 26 ijssgsgab 27 | 27 wsc 28 | 28 ugv 29 ijp LESS LINE 29 | -------------------------------------------------------------------------------- /test/file_a0: -------------------------------------------------------------------------------- 1 | 2-way OLDER_FILE / 3-way MYFILE 2 | 00 wsx 3 | 01 qaz 4 | 02 adf 5 | 03 bbb zcv 6 | 04 adv 7 | 05 tyu 8 | 06 qwerrttt 9 | 07 ujj 10 | 08 qaz 11 | 09 ***** 12 | 10 tte 13 | 11 iao 14 | 13 mop 15 | 14 wdd 16 | 15 dsadsdedv 17 | 16 ijb 18 | 17 wsc 19 | 12 njk 20 | 18 ugv 21 | 19 ijp 22 | 20 tte 23 | 21 iao 24 | 22 njk 25 | 23 mop 26 | 24 wdd 27 | 25 edv 28 | 26 ijb 29 | 27 wsc 30 | 28 ugv 31 | 29 ijp 32 | -------------------------------------------------------------------------------- /test/file_a1: -------------------------------------------------------------------------------- 1 | 2-way OLDER_FILE / 3-way MYFILE 2 | 3 | lOREM IPSUM doLOR SIT AMET, CONSEcteTUR ADIPISCINg elit, SED DO EIUSMOD teMPOR 4 | incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis 5 | nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consEQUat. 6 | Duis aute irure dolor in reprehenderit in voluptate velit esse cillum doloRE eu 7 | fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sUNT in 8 | culpa qui officia deserunt mollit anim id est laborum. 9 | -------------------------------------------------------------------------------- /test/file_b: -------------------------------------------------------------------------------- 1 | 2-way NEWER_FILE / 3-way OLDFILE 2 | 00 wsx 3 | 01 qaz 4 | 02 adf 5 | 03 bbb zcv 6 | 04 adv 7 | 05 tyu 8 | 06 qwerrttt 9 | 07 ujj 10 | 08 qaz 11 | 09 ***** 12 | 10 tte 13 | 11 iao 14 | 13 mop 15 | 14 wdd 16 | 15 dsadsdedv 17 | 16 ijb 18 | 17 wsc 19 | 12 njk 20 | 18 ugv 21 | 19 ijp 22 | 20 tte 23 | 21 iao 24 | 22 njk 25 | 23 mop 26 | 24 wdd 27 | 25 edv 28 | 26 ijb 29 | 27 wsc 30 | 28 ugv 31 | 29 ijp 32 | -------------------------------------------------------------------------------- /test/file_b0: -------------------------------------------------------------------------------- 1 | 2-way NEWER_FILE / 3-way OLDFILE 2 | 00 wsx 3 | 01 qaz 4 | 02 adf 5 | 03 bbb zcv 6 | 04 adv 7 | 05 tyu 8 | 06 qwerrttt 9 | 07 ujj 10 | 08 qaz 11 | 09 ***** 12 | 10 tte 13 | 11 iao 14 | 13 mop 15 | 14 wdd 16 | 15 dsadsdedv 17 | 16 ijb 18 | 17 wsc 19 | 12 njk 20 | 18 ugv 21 | 19 ijp 22 | 20 tte 23 | 21 iao 24 | 22 njk 25 | 23 mop 26 | 24 wdd 27 | 25 edv 28 | 26 ijb 29 | 27 wsc 30 | 28 ugv 31 | 29 ijp 32 | -------------------------------------------------------------------------------- /test/file_b1: -------------------------------------------------------------------------------- 1 | 2-way NEWER_FILE / 3-way OLDFILE 2 | 3 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor 4 | incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis 5 | nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 6 | Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu 7 | fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in 8 | culpa qui officia deserunt mollit anim id est laborum. 9 | -------------------------------------------------------------------------------- /test/file_c: -------------------------------------------------------------------------------- 1 | 3-way YOURFILE 2 | 01 qaz 00 is missing only in C (above) 3 | 02 adf 4 | ccc 03 zcv 5 | 04 adv 6 | 05 tdfgyu 7 | 06 ghj 8 | 07 ujj 9 | 08 qaz 10 | 09 wsx 11 | 10 tte 12 | 11 iao 13 | 12 njk 14 | 13 mop 15 | 14 wdd 16 | 15 edfsdsfdsfvsdf 17 | 16 ijb 18 | 17 wsc 19 | 18 ugv 20 | 19 ijp 21 | 21 iao 22 | 22 njk 23 | 23 mop 24 | 24 wdd 25 | 25 edv 26 | 26asdsdadadjdb 27 | 27 wsc 28 | 28 ugv 29 | 29 ijp 30 | EXTRA LINE 31 | -------------------------------------------------------------------------------- /test/file_c0: -------------------------------------------------------------------------------- 1 | 3-way YOURFILE 2 | 00 wsx 3 | 01 qaz 4 | 02 adf CCCCCCCCCCCCCCCCCC 5 | 03 zcv 6 | 04 adv 7 | zz 05 asdfg tyu 8 | 06 ghj 9 | 0x7 CONFLICTuj 10 | 08 qaz 11 | 09 wsx 12 | 10 tte 13 | 11 iao 14 | 12 njk 15 | 13 mop 16 | 14 wdd 17 | 15 edv ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 18 | 16ihjb 19 | s1d7 wrstc 20 | z18 uhgjv 21 | 19 ijp 22 | 20 tte 23 | 21 iao 24 | 22 njk 25 | 23 mop 26 | 24 wdd 27 | 25 edv 28 | 26 ijb 29 | 27 wsc 30 | 28 ugv 31 | 29 ijp 32 | -------------------------------------------------------------------------------- /test/file_c1: -------------------------------------------------------------------------------- 1 | 3-way YOURFILE 2 | 3 | Lorem IPSUM dolor SIT amET, CONSECTetur adipiscing ELIT, sed DO EIUSMod tempor 4 | incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis 5 | nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 6 | Duis aute irure dolor in REPREHENDERIT IN Voluptate velit esse cillum dolore eu 7 | fugiat nulla paRIATUR. Excepteur sint occaecat CUPIDATAT Non proident, sunt in 8 | culpa qui officia deserunt mollit anim id est laborum. 9 | -------------------------------------------------------------------------------- /test/test_unittest_all.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # vim:se tw=79 sts=4 ts=4 et ai: 3 | """ 4 | Test many aspect of imediff package via unittest. 5 | 6 | pybuild(1) 7 | https://docs.python.org/3/library/unittest.html#test-discovery 8 | In order to be compatible with test discovery, all of the test files must be 9 | modules or packages importable from the top-level directory of the project 10 | 11 | https://stackoverflow.com/questions/3295386/python-unittest-and-discovery 12 | The unittest filename needs to be test_*.py 13 | 14 | To test the in-source-tree module, invoke this script in this directory as: 15 | 16 | $ export PYTHONPATH=$(pwd)/../src 17 | $ python3 test_unittest_all.py -v 18 | 19 | To test the system installed module, invoke this script directly in this 20 | directory as: 21 | 22 | $ python3 test_unittest_all.py -v 23 | 24 | Current diff2 uses Python standard library difflib which uses a variant of 25 | longest contiguous matching sub-sequence algorithm by Ratcliff and Obershelp 26 | developed in the late 1980's. If I update this imediff program to use more 27 | modern algorithm, this test may yield slightly different result. 28 | """ 29 | import unittest 30 | import subprocess 31 | import os 32 | import os.path 33 | import imediff.diff3lib 34 | 35 | # Deb package build dh_test 36 | # 37 | # test/ and imediff/ are copied to ../build/ directory and cwd is set to there 38 | # test_dir = cwd_dir + "/test" 39 | # cwd_dir + "/imediff" -- exist 40 | # PYTHONPATH = 'PROJECT_ROOT/debian/tmp/usr/lib/python3.11/dist-packages:PROJECT_ROOT/.pybuild/cpython3_3.11/build' 41 | # doctest_dir = "../src/imediff/" 42 | 43 | ### TEST installed system module 44 | # Deb autopkgtest 45 | # 46 | # The cwd of each test is guaranteed to be the root of the source package, 47 | # which will have been unpacked but not built. However note that the tests must 48 | # test the installed version of the package, as opposed to programs or any 49 | # other file from the built tree. Tests may not modify the source tree (and may 50 | # not have write access to it). 51 | # test_dir = cwd_dir + "/test" 52 | # cwd_dir + "/debian" -- exist 53 | # cwd_dir + "/src" -- exist 54 | # cwd_dir + "/src/imediff" -- exist 55 | # doctest_dir = "imediff/" 56 | 57 | # Direct invocation from test_dir 58 | # test_dir == cwd_dir 59 | # doctest_dir = "imediff/" 60 | 61 | # Direct invocation from PROJECT_ROOT 62 | # test_dir = cwd_dir + "/test" 63 | # cwd_dir + "/debian" -- exist 64 | # cwd_dir + "/src" -- exist 65 | # cwd_dir + "/src/imediff" -- exist 66 | # doctest_dir = "imediff/" 67 | 68 | ##### Build time 69 | # This is run from copied files with PYTHONPATH set 70 | # I: PYTHONPATH = 'PROJECT_ROOT/debian/tmp/usr/lib/python3.11/dist-packages:PROJECT_ROOT/.pybuild/cpython3_3.11/build' 71 | ##### path to test directory 72 | ####print("I: test_dir ='{}'".format(test_dir)) 73 | ##### I: test_dir ='PROJECT_ROOT/.pybuild/cpython3_3.11/build/test' 74 | #### 75 | ##### path to current working directory 76 | ####print("I: cwd_dir ='{}'".format(cwd_dir)) 77 | ##### I: cwd_dir ='PROJECT_ROOT/.pybuild/cpython3_3.11/build' 78 | ##### <<< This is where imediff module is copied and located 79 | #### 80 | ##### --- subprocess.run(["ls", "-laR"]) 81 | 82 | cwd_dir = os.getcwd() 83 | print("I: cwd_dir = '{}'".format(cwd_dir)) 84 | print("I: test_file = '{}' (active)".format(__file__)) 85 | test_dir = os.path.dirname(os.path.abspath(__file__)) 86 | print("I: test_dir = '{}' (active)".format(test_dir)) 87 | base_dir = os.path.dirname(test_dir) 88 | print("I: base_dir = '{}'".format(base_dir)) 89 | if os.path.isdir(base_dir + "/src"): 90 | print("I: test in the original source tree invoked") 91 | doctest_dir = base_dir + "/src/imediff" 92 | elif os.path.isdir(base_dir + "/imediff"): 93 | print("I: test in the copied source tree in Debian pybuild invoked") 94 | doctest_dir = base_dir + "/imediff" 95 | else: 96 | print("I: test in an unknown environment invoked") 97 | exit(1) 98 | print("I: doctest_dir = '{}'".format(doctest_dir)) 99 | if "PYTHONPATH" in os.environ: 100 | print("I: PYTHONPATH = '{}'".format(os.environ["PYTHONPATH"])) 101 | else: 102 | print("I: PYTHONPATH = ") 103 | 104 | 105 | class TestImediff(unittest.TestCase): 106 | a = "a12b345c6789d" 107 | b = "123456789" 108 | c = "a1234b567c89d" 109 | 110 | def test_diff3lib_abc(self): 111 | a = "a12b345c6789d" 112 | b = "123456789" 113 | c = "a1234b567c89d" 114 | self.assertEqual( 115 | imediff.diff3lib.SequenceMatcher3(a, b, c, 0, None, True).get_opcodes(), 116 | [ 117 | ("e", 0, 1, 0, 0, 0, 1), 118 | ("E", 1, 3, 0, 2, 1, 3), 119 | ("A", 3, 4, 2, 2, 3, 3), 120 | ("E", 4, 6, 2, 4, 3, 5), 121 | ("C", 6, 6, 4, 4, 5, 6), 122 | ("E", 6, 7, 4, 5, 6, 7), 123 | ("A", 7, 8, 5, 5, 7, 7), 124 | ("E", 8, 10, 5, 7, 7, 9), 125 | ("C", 10, 10, 7, 7, 9, 10), 126 | ("E", 10, 12, 7, 9, 10, 12), 127 | ("e", 12, 13, 9, 9, 12, 13), 128 | ], 129 | ) 130 | return 131 | 132 | def test_lines2lib_doctest(self): 133 | result = subprocess.call( 134 | "python3 " + doctest_dir + "/lines2lib.py", 135 | shell=True, 136 | ) 137 | self.assertEqual(result, 0) 138 | return 139 | 140 | def test_diff3lib_doctest(self): 141 | result = subprocess.call( 142 | "python3 " + doctest_dir + "/diff3lib.py", 143 | shell=True, 144 | ) 145 | self.assertEqual(result, 0) 146 | return 147 | 148 | def test_diff23(self): 149 | result = subprocess.call( 150 | "cd " + test_dir + ";python3 _diff23.py >z_diff23.out", shell=True 151 | ) 152 | result = subprocess.call( 153 | "cd " + test_dir + ";diff z_diff23.out z_diff23.ref >/dev/null", shell=True 154 | ) 155 | self.assertEqual(result, 0) 156 | return 157 | 158 | def test_imediff2(self): 159 | result = subprocess.call( 160 | "cd " 161 | + test_dir 162 | + ";python3 _imediff.py -l -C none --macro=w -n file_a file_b -o z_imediff2.out", 163 | shell=True, 164 | ) 165 | result = subprocess.call( 166 | "cd " + test_dir + ";diff z_imediff2.out z_imediff2.ref >/dev/null", 167 | shell=True, 168 | ) 169 | self.assertEqual(result, 0) 170 | return 171 | 172 | def test_imediff2_a(self): 173 | result = subprocess.call( 174 | "cd " 175 | + test_dir 176 | + ";python3 _imediff.py -l -a -C none --macro=w -n file_a file_b -o z_imediff2_a.out", 177 | shell=True, 178 | ) 179 | result = subprocess.call( 180 | "cd " + test_dir + ";diff z_imediff2_a.out z_imediff2_a.ref >/dev/null", 181 | shell=True, 182 | ) 183 | self.assertEqual(result, 0) 184 | return 185 | 186 | def test_imediff2_b(self): 187 | result = subprocess.call( 188 | "cd " 189 | + test_dir 190 | + ";python3 _imediff.py -l -b -C none --macro=w -n file_a file_b -o z_imediff2_b.out", 191 | shell=True, 192 | ) 193 | result = subprocess.call( 194 | "cd " + test_dir + ";diff z_imediff2_b.out z_imediff2_b.ref >/dev/null", 195 | shell=True, 196 | ) 197 | self.assertEqual(result, 0) 198 | return 199 | 200 | def test_imediff2_d(self): 201 | result = subprocess.call( 202 | "cd " 203 | + test_dir 204 | + ";python3 _imediff.py -l -d -C none --macro=w -n file_a file_b -o z_imediff2_d.out", 205 | shell=True, 206 | ) 207 | result = subprocess.call( 208 | "cd " + test_dir + ";diff z_imediff2_d.out z_imediff2_d.ref >/dev/null", 209 | shell=True, 210 | ) 211 | self.assertEqual(result, 0) 212 | return 213 | 214 | def test_imediff2_f(self): 215 | result = subprocess.call( 216 | "cd " 217 | + test_dir 218 | + ";python3 _imediff.py -l -f -C none --macro=w -n file_a file_b -o z_imediff2_f.out", 219 | shell=True, 220 | ) 221 | result = subprocess.call( 222 | "cd " + test_dir + ";diff z_imediff2_f.out z_imediff2_f.ref >/dev/null", 223 | shell=True, 224 | ) 225 | self.assertEqual(result, 0) 226 | return 227 | 228 | def test_imediff3(self): 229 | result = subprocess.call( 230 | "cd " 231 | + test_dir 232 | + ";python3 _imediff.py -l -C none --macro=w -n file_a file_b file_c -o z_imediff3.out", 233 | shell=True, 234 | ) 235 | result = subprocess.call( 236 | "cd " + test_dir + ";diff z_imediff3.out z_imediff3.ref >/dev/null", 237 | shell=True, 238 | ) 239 | self.assertEqual(result, 0) 240 | return 241 | 242 | def test_imediff3_a(self): 243 | result = subprocess.call( 244 | "cd " 245 | + test_dir 246 | + ";python3 _imediff.py -l -a -C none --macro=w -n file_a file_b file_c -o z_imediff3_a.out", 247 | shell=True, 248 | ) 249 | result = subprocess.call( 250 | "cd " + test_dir + ";diff z_imediff3_a.out z_imediff3_a.ref >/dev/null", 251 | shell=True, 252 | ) 253 | self.assertEqual(result, 0) 254 | return 255 | 256 | def test_imediff3_b(self): 257 | result = subprocess.call( 258 | "cd " 259 | + test_dir 260 | + ";python3 _imediff.py -l -b -C none --macro=w -n file_a file_b file_c -o z_imediff3_b.out", 261 | shell=True, 262 | ) 263 | result = subprocess.call( 264 | "cd " + test_dir + ";diff z_imediff3_b.out z_imediff3_b.ref >/dev/null", 265 | shell=True, 266 | ) 267 | self.assertEqual(result, 0) 268 | return 269 | 270 | def test_imediff3_c(self): 271 | result = subprocess.call( 272 | "cd " 273 | + test_dir 274 | + ";python3 _imediff.py -l -c -C none --macro=w -n file_a file_b file_c -o z_imediff3_c.out", 275 | shell=True, 276 | ) 277 | result = subprocess.call( 278 | "cd " + test_dir + ";diff z_imediff3_c.out z_imediff3_c.ref >/dev/null", 279 | shell=True, 280 | ) 281 | self.assertEqual(result, 0) 282 | return 283 | 284 | def test_imediff3_d(self): 285 | result = subprocess.call( 286 | "cd " 287 | + test_dir 288 | + ";python3 _imediff.py -l -d -C none --macro=w -n file_a file_b file_c -o z_imediff3_d.out", 289 | shell=True, 290 | ) 291 | result = subprocess.call( 292 | "cd " + test_dir + ";diff z_imediff3_d.out z_imediff3_d.ref >/dev/null", 293 | shell=True, 294 | ) 295 | self.assertEqual(result, 0) 296 | return 297 | 298 | def test_imediff3_f(self): 299 | result = subprocess.call( 300 | "cd " 301 | + test_dir 302 | + ";python3 _imediff.py -l -f -C none --macro=w -n file_a file_b file_c -o z_imediff3_f.out", 303 | shell=True, 304 | ) 305 | result = subprocess.call( 306 | "cd " + test_dir + ";diff z_imediff3_f.out z_imediff3_f.ref >/dev/null", 307 | shell=True, 308 | ) 309 | self.assertEqual(result, 0) 310 | return 311 | 312 | def test_imediff3_g(self): 313 | result = subprocess.call( 314 | "cd " 315 | + test_dir 316 | + ";python3 _imediff.py -l -g -C none --macro=w -n file_a file_b file_c -o z_imediff3_g.out", 317 | shell=True, 318 | ) 319 | result = subprocess.call( 320 | "cd " + test_dir + ";diff z_imediff3_g.out z_imediff3_g.ref >/dev/null", 321 | shell=True, 322 | ) 323 | self.assertEqual(result, 0) 324 | return 325 | 326 | def tearDown(self): 327 | _ = subprocess.call("cd " + test_dir + ";rm -f z_*.out", shell=True) 328 | return 329 | 330 | 331 | if __name__ == "__main__": 332 | unittest.main() 333 | -------------------------------------------------------------------------------- /test/z_diff23.ref: -------------------------------------------------------------------------------- 1 | # Test type: same 2 | a='12345' -> 5 3 | b='12345' -> 5 4 | c='12345' -> 5 5 | $ diff2 A B 6 | equal a[0:5] (12345) / b[0:5] (12345) 7 | $ diff2 C B 8 | equal c[0:5] (12345) / b[0:5] (12345) 9 | $ diff3 A B C 10 | E a[0:5] (12345) / b[0:5] (12345) / c[0:5] (12345) 11 | =========================================================== 12 | # Test type: add a side 13 | a='a12345z' -> 7 14 | b='1245' -> 4 15 | c='1245' -> 4 16 | $ diff2 A B 17 | delete a[0:1] (a) / b[0:0] () 18 | equal a[1:3] (12) / b[0:2] (12) 19 | delete a[3:4] (3) / b[2:2] () 20 | equal a[4:6] (45) / b[2:4] (45) 21 | delete a[6:7] (z) / b[4:4] () 22 | $ diff2 C B 23 | equal c[0:4] (1245) / b[0:4] (1245) 24 | $ diff3 A B C 25 | A a[0:1] (a) / b[0:0] () / c[0:0] () 26 | E a[1:3] (12) / b[0:2] (12) / c[0:2] (12) 27 | A a[3:4] (3) / b[2:2] () / c[2:2] () 28 | E a[4:6] (45) / b[2:4] (45) / c[2:4] (45) 29 | A a[6:7] (z) / b[4:4] () / c[4:4] () 30 | =========================================================== 31 | # Test type: add same 32 | a='a12345z' -> 7 33 | b='1245' -> 4 34 | c='a12345z' -> 7 35 | $ diff2 A B 36 | delete a[0:1] (a) / b[0:0] () 37 | equal a[1:3] (12) / b[0:2] (12) 38 | delete a[3:4] (3) / b[2:2] () 39 | equal a[4:6] (45) / b[2:4] (45) 40 | delete a[6:7] (z) / b[4:4] () 41 | $ diff2 C B 42 | delete c[0:1] (a) / b[0:0] () 43 | equal c[1:3] (12) / b[0:2] (12) 44 | delete c[3:4] (3) / b[2:2] () 45 | equal c[4:6] (45) / b[2:4] (45) 46 | delete c[6:7] (z) / b[4:4] () 47 | $ diff3 A B C 48 | e a[0:1] (a) / b[0:0] () / c[0:1] (a) 49 | E a[1:3] (12) / b[0:2] (12) / c[1:3] (12) 50 | e a[3:4] (3) / b[2:2] () / c[3:4] (3) 51 | E a[4:6] (45) / b[2:4] (45) / c[4:6] (45) 52 | e a[6:7] (z) / b[4:4] () / c[6:7] (z) 53 | =========================================================== 54 | # Test type: add c side 55 | a='1245' -> 4 56 | b='1245' -> 4 57 | c='a12345z' -> 7 58 | $ diff2 A B 59 | equal a[0:4] (1245) / b[0:4] (1245) 60 | $ diff2 C B 61 | delete c[0:1] (a) / b[0:0] () 62 | equal c[1:3] (12) / b[0:2] (12) 63 | delete c[3:4] (3) / b[2:2] () 64 | equal c[4:6] (45) / b[2:4] (45) 65 | delete c[6:7] (z) / b[4:4] () 66 | $ diff3 A B C 67 | C a[0:0] () / b[0:0] () / c[0:1] (a) 68 | E a[0:2] (12) / b[0:2] (12) / c[1:3] (12) 69 | C a[2:2] () / b[2:2] () / c[3:4] (3) 70 | E a[2:4] (45) / b[2:4] (45) / c[4:6] (45) 71 | C a[4:4] () / b[4:4] () / c[6:7] (z) 72 | =========================================================== 73 | # Test type: delete a side 74 | a='24' -> 2 75 | b='12345' -> 5 76 | c='12345' -> 5 77 | $ diff2 A B 78 | insert a[0:0] () / b[0:1] (1) 79 | equal a[0:1] (2) / b[1:2] (2) 80 | insert a[1:1] () / b[2:3] (3) 81 | equal a[1:2] (4) / b[3:4] (4) 82 | insert a[2:2] () / b[4:5] (5) 83 | $ diff2 C B 84 | equal c[0:5] (12345) / b[0:5] (12345) 85 | $ diff3 A B C 86 | A a[0:0] () / b[0:1] (1) / c[0:1] (1) 87 | E a[0:1] (2) / b[1:2] (2) / c[1:2] (2) 88 | A a[1:1] () / b[2:3] (3) / c[2:3] (3) 89 | E a[1:2] (4) / b[3:4] (4) / c[3:4] (4) 90 | A a[2:2] () / b[4:5] (5) / c[4:5] (5) 91 | =========================================================== 92 | # Test type: delete same 93 | a='24' -> 2 94 | b='12345' -> 5 95 | c='24' -> 2 96 | $ diff2 A B 97 | insert a[0:0] () / b[0:1] (1) 98 | equal a[0:1] (2) / b[1:2] (2) 99 | insert a[1:1] () / b[2:3] (3) 100 | equal a[1:2] (4) / b[3:4] (4) 101 | insert a[2:2] () / b[4:5] (5) 102 | $ diff2 C B 103 | insert c[0:0] () / b[0:1] (1) 104 | equal c[0:1] (2) / b[1:2] (2) 105 | insert c[1:1] () / b[2:3] (3) 106 | equal c[1:2] (4) / b[3:4] (4) 107 | insert c[2:2] () / b[4:5] (5) 108 | $ diff3 A B C 109 | e a[0:0] () / b[0:1] (1) / c[0:0] () 110 | E a[0:1] (2) / b[1:2] (2) / c[0:1] (2) 111 | e a[1:1] () / b[2:3] (3) / c[1:1] () 112 | E a[1:2] (4) / b[3:4] (4) / c[1:2] (4) 113 | e a[2:2] () / b[4:5] (5) / c[2:2] () 114 | =========================================================== 115 | # Test type: delete c side 116 | a='12345' -> 5 117 | b='12345' -> 5 118 | c='24' -> 2 119 | $ diff2 A B 120 | equal a[0:5] (12345) / b[0:5] (12345) 121 | $ diff2 C B 122 | insert c[0:0] () / b[0:1] (1) 123 | equal c[0:1] (2) / b[1:2] (2) 124 | insert c[1:1] () / b[2:3] (3) 125 | equal c[1:2] (4) / b[3:4] (4) 126 | insert c[2:2] () / b[4:5] (5) 127 | $ diff3 A B C 128 | C a[0:1] (1) / b[0:1] (1) / c[0:0] () 129 | E a[1:2] (2) / b[1:2] (2) / c[0:1] (2) 130 | C a[2:3] (3) / b[2:3] (3) / c[1:1] () 131 | E a[3:4] (4) / b[3:4] (4) / c[1:2] (4) 132 | C a[4:5] (5) / b[4:5] (5) / c[2:2] () 133 | =========================================================== 134 | # Test type: conflict both ends 135 | a='a2b4c' -> 5 136 | b='12345' -> 5 137 | c='x2y4z' -> 5 138 | $ diff2 A B 139 | replace a[0:1] (a) / b[0:1] (1) 140 | equal a[1:2] (2) / b[1:2] (2) 141 | replace a[2:3] (b) / b[2:3] (3) 142 | equal a[3:4] (4) / b[3:4] (4) 143 | replace a[4:5] (c) / b[4:5] (5) 144 | $ diff2 C B 145 | replace c[0:1] (x) / b[0:1] (1) 146 | equal c[1:2] (2) / b[1:2] (2) 147 | replace c[2:3] (y) / b[2:3] (3) 148 | equal c[3:4] (4) / b[3:4] (4) 149 | replace c[4:5] (z) / b[4:5] (5) 150 | $ diff3 A B C 151 | N a[0:1] (a) / b[0:1] (1) / c[0:1] (x) 152 | E a[1:2] (2) / b[1:2] (2) / c[1:2] (2) 153 | N a[2:3] (b) / b[2:3] (3) / c[2:3] (y) 154 | E a[3:4] (4) / b[3:4] (4) / c[3:4] (4) 155 | N a[4:5] (c) / b[4:5] (5) / c[4:5] (z) 156 | =========================================================== 157 | # Test type: conflict skewed 158 | a='1a2b4c' -> 6 159 | b='12345' -> 5 160 | c='x2y45z' -> 6 161 | $ diff2 A B 162 | equal a[0:1] (1) / b[0:1] (1) 163 | delete a[1:2] (a) / b[1:1] () 164 | equal a[2:3] (2) / b[1:2] (2) 165 | replace a[3:4] (b) / b[2:3] (3) 166 | equal a[4:5] (4) / b[3:4] (4) 167 | replace a[5:6] (c) / b[4:5] (5) 168 | $ diff2 C B 169 | replace c[0:1] (x) / b[0:1] (1) 170 | equal c[1:2] (2) / b[1:2] (2) 171 | replace c[2:3] (y) / b[2:3] (3) 172 | equal c[3:5] (45) / b[3:5] (45) 173 | delete c[5:6] (z) / b[5:5] () 174 | $ diff3 A B C 175 | C a[0:1] (1) / b[0:1] (1) / c[0:1] (x) 176 | A a[1:2] (a) / b[1:1] () / c[1:1] () 177 | E a[2:3] (2) / b[1:2] (2) / c[1:2] (2) 178 | N a[3:4] (b) / b[2:3] (3) / c[2:3] (y) 179 | E a[4:5] (4) / b[3:4] (4) / c[3:4] (4) 180 | A a[5:6] (c) / b[4:5] (5) / c[4:5] (5) 181 | C a[6:6] () / b[5:5] () / c[5:6] (z) 182 | =========================================================== 183 | -------------------------------------------------------------------------------- /test/z_imediff-log.ref: -------------------------------------------------------------------------------- 1 | DEBUG: main.py: main: ============================== start of main ============================== 2 | DEBUG: initialize_confs.py: initialize_confs: confs: end with len(confs)=6 3 | DEBUG: main.py: main: confs['config'] >>> key='version' value='3.1' 4 | DEBUG: main.py: main: confs['config'] >>> key='confirm_exit' value='True' 5 | DEBUG: main.py: main: confs['config'] >>> key='confirm_quit' value='True' 6 | DEBUG: main.py: main: confs['key'] >>> key='select_a' value='a' 7 | DEBUG: main.py: main: confs['key'] >>> key='select_b' value='b' 8 | DEBUG: main.py: main: confs['key'] >>> key='select_c' value='c' 9 | DEBUG: main.py: main: confs['key'] >>> key='select_d' value='d' 10 | DEBUG: main.py: main: confs['key'] >>> key='select_e' value='e' 11 | DEBUG: main.py: main: confs['key'] >>> key='select_f' value='f' 12 | DEBUG: main.py: main: confs['key'] >>> key='select_g' value='g' 13 | DEBUG: main.py: main: confs['key'] >>> key='select_h' value='h' 14 | DEBUG: main.py: main: confs['key'] >>> key='select_i' value='i' 15 | DEBUG: main.py: main: confs['key'] >>> key='select_j' value='j' 16 | DEBUG: main.py: main: confs['key'] >>> key='select_k' value='k' 17 | DEBUG: main.py: main: confs['key'] >>> key='select_l' value='l' 18 | DEBUG: main.py: main: confs['key'] >>> key='select_m' value='m' 19 | DEBUG: main.py: main: confs['key'] >>> key='select_n' value='n' 20 | DEBUG: main.py: main: confs['key'] >>> key='select_o' value='o' 21 | DEBUG: main.py: main: confs['key'] >>> key='select_p' value='p' 22 | DEBUG: main.py: main: confs['key'] >>> key='select_q' value='q' 23 | DEBUG: main.py: main: confs['key'] >>> key='select_r' value='r' 24 | DEBUG: main.py: main: confs['key'] >>> key='select_s' value='s' 25 | DEBUG: main.py: main: confs['key'] >>> key='select_t' value='t' 26 | DEBUG: main.py: main: confs['key'] >>> key='select_u' value='u' 27 | DEBUG: main.py: main: confs['key'] >>> key='select_v' value='v' 28 | DEBUG: main.py: main: confs['key'] >>> key='select_w' value='w' 29 | DEBUG: main.py: main: confs['key'] >>> key='select_x' value='x' 30 | DEBUG: main.py: main: confs['key'] >>> key='select_y' value='y' 31 | DEBUG: main.py: main: confs['key'] >>> key='select_z' value='z' 32 | DEBUG: main.py: main: confs['key'] >>> key='select_space' value='SPACE' 33 | DEBUG: main.py: main: confs['key'] >>> key='select_!' value='!' 34 | DEBUG: main.py: main: confs['key'] >>> key='select_"' value='"' 35 | DEBUG: main.py: main: confs['key'] >>> key='select_hash' value='HASH' 36 | DEBUG: main.py: main: confs['key'] >>> key='select_$' value='$' 37 | DEBUG: main.py: main: confs['key'] >>> key='select_percent' value='PERCENT' 38 | DEBUG: main.py: main: confs['key'] >>> key='select_&' value='&' 39 | DEBUG: main.py: main: confs['key'] >>> key='select_'' value=''' 40 | DEBUG: main.py: main: confs['key'] >>> key='select_(' value='(' 41 | DEBUG: main.py: main: confs['key'] >>> key='select_)' value=')' 42 | DEBUG: main.py: main: confs['key'] >>> key='select_*' value='*' 43 | DEBUG: main.py: main: confs['key'] >>> key='select_+' value='+' 44 | DEBUG: main.py: main: confs['key'] >>> key='select_,' value=',' 45 | DEBUG: main.py: main: confs['key'] >>> key='select_-' value='-' 46 | DEBUG: main.py: main: confs['key'] >>> key='select_.' value='.' 47 | DEBUG: main.py: main: confs['key'] >>> key='select_/' value='/' 48 | DEBUG: main.py: main: confs['key'] >>> key='select_0' value='0' 49 | DEBUG: main.py: main: confs['key'] >>> key='select_1' value='1' 50 | DEBUG: main.py: main: confs['key'] >>> key='select_2' value='2' 51 | DEBUG: main.py: main: confs['key'] >>> key='select_3' value='3' 52 | DEBUG: main.py: main: confs['key'] >>> key='select_4' value='4' 53 | DEBUG: main.py: main: confs['key'] >>> key='select_5' value='5' 54 | DEBUG: main.py: main: confs['key'] >>> key='select_6' value='6' 55 | DEBUG: main.py: main: confs['key'] >>> key='select_7' value='7' 56 | DEBUG: main.py: main: confs['key'] >>> key='select_8' value='8' 57 | DEBUG: main.py: main: confs['key'] >>> key='select_9' value='9' 58 | DEBUG: main.py: main: confs['key'] >>> key='select_colon' value='COLON' 59 | DEBUG: main.py: main: confs['key'] >>> key='select_;' value=';' 60 | DEBUG: main.py: main: confs['key'] >>> key='select_<' value='>' 61 | DEBUG: main.py: main: confs['key'] >>> key='select_equal' value='EQUAL' 62 | DEBUG: main.py: main: confs['key'] >>> key='select_>' value='>' 63 | DEBUG: main.py: main: confs['key'] >>> key='select_?' value='?' 64 | DEBUG: main.py: main: confs['key'] >>> key='select_@' value='@' 65 | DEBUG: main.py: main: confs['key'] >>> key='select_lbracket' value='LBRACKET' 66 | DEBUG: main.py: main: confs['key'] >>> key='select_backslash' value='BACKSLASH' 67 | DEBUG: main.py: main: confs['key'] >>> key='select_rbracket' value='RBRACKET' 68 | DEBUG: main.py: main: confs['key'] >>> key='select_^' value='^' 69 | DEBUG: main.py: main: confs['key'] >>> key='select__' value='_' 70 | DEBUG: main.py: main: confs['key'] >>> key='select_`' value='`' 71 | DEBUG: main.py: main: confs['key'] >>> key='select_{' value='{' 72 | DEBUG: main.py: main: confs['key'] >>> key='select_|' value='|' 73 | DEBUG: main.py: main: confs['key'] >>> key='select_}' value='}' 74 | DEBUG: main.py: main: confs['key'] >>> key='select_~' value='~' 75 | DEBUG: main.py: main: confs['key'] >>> key='select_del' value='DEL' 76 | DEBUG: main.py: main: confs['key'] >>> key='select_tab' value='TAB' 77 | DEBUG: main.py: main: confs['key'] >>> key='select_btab' value='BTAB' 78 | DEBUG: main.py: main: confs['key'] >>> key='select_enter' value='ENTER' 79 | DEBUG: main.py: main: confs['key'] >>> key='select_up' value='UP' 80 | DEBUG: main.py: main: confs['key'] >>> key='select_down' value='DOWN' 81 | DEBUG: main.py: main: confs['key'] >>> key='select_left' value='LEFT' 82 | DEBUG: main.py: main: confs['key'] >>> key='select_right' value='RIGHT' 83 | DEBUG: main.py: main: confs['key'] >>> key='select_insert' value='INSERT' 84 | DEBUG: main.py: main: confs['key'] >>> key='select_delete' value='DELETE' 85 | DEBUG: main.py: main: confs['key'] >>> key='select_home' value='HOME' 86 | DEBUG: main.py: main: confs['key'] >>> key='select_end' value='END' 87 | DEBUG: main.py: main: confs['key'] >>> key='select_pageup' value='PAGEUP' 88 | DEBUG: main.py: main: confs['key'] >>> key='select_pagedown' value='PAGEDOWN' 89 | DEBUG: main.py: main: confs['key'] >>> key='select_backspace' value='BACKSPACE' 90 | DEBUG: main.py: main: confs['key'] >>> key='select_f1' value='F1' 91 | DEBUG: main.py: main: confs['key'] >>> key='select_f2' value='F2' 92 | DEBUG: main.py: main: confs['key'] >>> key='select_f3' value='F3' 93 | DEBUG: main.py: main: confs['key'] >>> key='select_f4' value='F4' 94 | DEBUG: main.py: main: confs['key'] >>> key='select_f5' value='F5' 95 | DEBUG: main.py: main: confs['key'] >>> key='select_f6' value='F6' 96 | DEBUG: main.py: main: confs['key'] >>> key='select_f7' value='F7' 97 | DEBUG: main.py: main: confs['key'] >>> key='select_f8' value='F8' 98 | DEBUG: main.py: main: confs['key'] >>> key='select_f9' value='F9' 99 | DEBUG: main.py: main: confs['key'] >>> key='select_f10' value='F10' 100 | DEBUG: main.py: main: confs['key'] >>> key='select_f11' value='F11' 101 | DEBUG: main.py: main: confs['key'] >>> key='select_f12' value='F11' 102 | DEBUG: main.py: main: confs['attrib'] >>> key='color_merge_ab' value='WHITE,NORMAL' 103 | DEBUG: main.py: main: confs['attrib'] >>> key='color_merge_abc' value='WHITE,NORMAL' 104 | DEBUG: main.py: main: confs['attrib'] >>> key='color_merge_ac' value='WHITE,NORMAL' 105 | DEBUG: main.py: main: confs['attrib'] >>> key='color_merge_a' value='GREEN,NORMAL' 106 | DEBUG: main.py: main: confs['attrib'] >>> key='color_merge_c' value='YELLOW,NORMAL' 107 | DEBUG: main.py: main: confs['attrib'] >>> key='color_merge_wdiff' value='WHITE,NORMAL' 108 | DEBUG: main.py: main: confs['attrib'] >>> key='color_a' value='GREEN,BOLD' 109 | DEBUG: main.py: main: confs['attrib'] >>> key='color_a_focus' value='GREEN,BOLD,REVERSE' 110 | DEBUG: main.py: main: confs['attrib'] >>> key='color_b2' value='YELLOW,BOLD' 111 | DEBUG: main.py: main: confs['attrib'] >>> key='color_b2_focus' value='YELLOW,BOLD,REVERSE' 112 | DEBUG: main.py: main: confs['attrib'] >>> key='color_b3' value='MAGENTA,BOLD' 113 | DEBUG: main.py: main: confs['attrib'] >>> key='color_b3_focus' value='MAGENTA,BOLD,REVERSE' 114 | DEBUG: main.py: main: confs['attrib'] >>> key='color_c' value='YELLOW,BOLD' 115 | DEBUG: main.py: main: confs['attrib'] >>> key='color_c_focus' value='YELLOW,BOLD,REVERSE' 116 | DEBUG: main.py: main: confs['attrib'] >>> key='color_editor' value='CYAN,BOLD' 117 | DEBUG: main.py: main: confs['attrib'] >>> key='color_editor_focus' value='CYAN,BOLD,REVERSE' 118 | DEBUG: main.py: main: confs['attrib'] >>> key='color_diff_marker' value='BLUE,NORMAL' 119 | DEBUG: main.py: main: confs['attrib'] >>> key='color_diff_marker_focus' value='BLUE,NORMAL,REVERSE' 120 | DEBUG: main.py: main: confs['attrib'] >>> key='color_wdiff_abc' value='WHITE,NORMAL' 121 | DEBUG: main.py: main: confs['attrib'] >>> key='color_wdiff_abc_focus' value='WHITE,BOLD,REVERSE' 122 | DEBUG: main.py: main: confs['attrib'] >>> key='color_wdiff_ac' value='WHITE,DIM' 123 | DEBUG: main.py: main: confs['attrib'] >>> key='color_wdiff_ac_focus' value='WHITE,BOLD,REVERSE' 124 | DEBUG: main.py: main: confs['attrib'] >>> key='color_wdiff_ab' value='WHITE,NORMAL' 125 | DEBUG: main.py: main: confs['attrib'] >>> key='color_wdiff_ab_focus' value='WHITE,BOLD,REVERSE' 126 | DEBUG: main.py: main: confs['attrib'] >>> key='color_wdiff_marker' value='BLUE,NORMAL' 127 | DEBUG: main.py: main: confs['attrib'] >>> key='color_wdiff_marker_focus' value='BLUE,NORMAL,REVERSE' 128 | DEBUG: main.py: main: confs['attrib'] >>> key='color_status' value='WHITE,NORMAL' 129 | DEBUG: main.py: main: confs['attrib'] >>> key='color_status_focus' value='WHITE,BOLD,REVERSE' 130 | DEBUG: main.py: main: confs['attrib'] >>> key='color_white_bold' value='WHITE,BOLD' 131 | DEBUG: main.py: main: confs['attrib'] >>> key='color_white' value='WHITE,NORMAL' 132 | DEBUG: main.py: main: confs['attrib'] >>> key='color_white_reverse' value='WHITE,BOLD,REVERSE' 133 | DEBUG: main.py: main: confs['attrib'] >>> key='color_warn' value='RED,BOLD' 134 | DEBUG: main.py: main: confs['attrib'] >>> key='color_eof' value='BLUE,DIM,REVERSE' 135 | DEBUG: main.py: main: confs['attrib'] >>> key='color_mono' value='WHITE,NORMAL' 136 | DEBUG: main.py: main: confs['line_separator'] >>> key='ls0' value='<<<<<<<' 137 | DEBUG: main.py: main: confs['line_separator'] >>> key='ls1' value='|||||||' 138 | DEBUG: main.py: main: confs['line_separator'] >>> key='ls2' value='=======' 139 | DEBUG: main.py: main: confs['line_separator'] >>> key='ls3' value='>>>>>>>' 140 | DEBUG: main.py: main: confs['word_separator'] >>> key='ws0' value='{' 141 | DEBUG: main.py: main: confs['word_separator'] >>> key='ws1' value='|' 142 | DEBUG: main.py: main: confs['word_separator'] >>> key='ws2' value='|' 143 | DEBUG: main.py: main: confs['word_separator'] >>> key='ws3' value='}' 144 | -------------------------------------------------------------------------------- /test/z_imediff.conf.ref: -------------------------------------------------------------------------------- 1 | # imediff configuration file 2 | # Please edit right side of assignment to customize 3 | # ini-type syntax where # starts comment 4 | # Don't create duplicate definition 5 | # Key side is case insensitive. 6 | 7 | [config] 8 | version = 3.1 # DON'T EDIT THIS. This is for future upgrade tracking. 9 | confirm_exit = True # Set as "False" to save and exit without pause 10 | confirm_quit = True # Set as "False" to quit without pause 11 | #editor = vim # Set this to override /usr/bin/editor and $EDITOR 12 | 13 | # key remapping is used only for TUI user input. MACRO need to use the 14 | # original bindings. 15 | [key] # action invoked by the selected key 16 | select_a = a # set mode a to select 'a' buffer 17 | select_b = b # set mode b to select 'b' buffer 18 | select_c = c # set mode c to select 'c' buffer (diff3) 19 | select_d = d # set mode d to select diff content if possible 20 | select_e = e # set mode e to select editor buffer if possible 21 | select_f = f # set mode f to select wdiff content if possible 22 | select_g = g # set good merge (diff3/wdiff3) if possible 23 | select_h = h # move left for the display scope 24 | select_i = i 25 | select_j = j # move down for the display scope 26 | select_k = k # move up for the display scope 27 | select_l = l # move right for the display scope 28 | select_m = m # start editor to modify content / M reset 29 | select_n = n # move active selection to next usr_chunk 30 | select_o = o 31 | select_p = p # move active selection to previous usr_chunk 32 | select_q = q # quit imediff without saving the result 33 | select_r = r 34 | select_s = s 35 | select_t = t # display tutorial 36 | select_u = u 37 | select_v = v 38 | select_w = w # write result and e_x_it program 39 | select_x = x # write result and e_x_it program 40 | select_y = y # key for "Yes" answer 41 | select_z = z 42 | select_SPACE = SPACE 43 | select_! = ! 44 | select_" = " 45 | select_HASH = HASH 46 | select_$ = $ 47 | select_PERCENT = PERCENT 48 | select_& = & 49 | select_' = ' 50 | select_( = ( 51 | select_) = ) 52 | select_* = * 53 | select_+ = + 54 | select_, = , 55 | select_- = - 56 | select_. = . 57 | select_/ = / 58 | select_0 = 0 # move active selection to the first usr_chunk 59 | select_1 = 1 # alias for a 60 | select_2 = 2 # alias for b 61 | select_3 = 3 # alias for c 62 | select_4 = 4 # alias for d 63 | select_5 = 5 # alias for e 64 | select_6 = 6 # alias for f 65 | select_7 = 7 # alias for g 66 | select_8 = 8 67 | select_9 = 9 # move active selection to the last usr_chunk 68 | select_COLON = COLON 69 | select_; = ; 70 | select_< = > 71 | select_EQUAL = EQUAL 72 | select_> = > 73 | select_? = ? 74 | select_@ = @ 75 | select_LBRACKET = LBRACKET # MACRO block start 76 | select_BACKSLASH = BACKSLASH 77 | select_RBRACKET = RBRACKET # MACRO block end 78 | select_^ = ^ 79 | select__ = _ 80 | select_` = ` 81 | select_{ = { 82 | select_| = | 83 | select_} = } 84 | select_~ = ~ 85 | select_DEL = DEL 86 | select_TAB = TAB 87 | select_BTAB = BTAB 88 | select_ENTER = ENTER 89 | select_UP = UP 90 | select_DOWN = DOWN 91 | select_LEFT = LEFT 92 | select_RIGHT = RIGHT 93 | select_INSERT = INSERT 94 | select_DELETE = DELETE 95 | select_HOME = HOME 96 | select_END = END 97 | select_PAGEUP = PAGEUP 98 | select_PAGEDOWN = PAGEDOWN 99 | select_BACKSPACE = BACKSPACE 100 | select_F1 = F1 101 | select_F2 = F2 102 | select_F3 = F3 103 | select_F4 = F4 104 | select_F5 = F5 105 | select_F6 = F6 106 | select_F7 = F7 107 | select_F8 = F8 108 | select_F9 = F9 109 | select_F10 = F10 110 | select_F11 = F11 111 | select_F12 = F11 112 | # = ^--------- customized key setting 113 | # ^--------- reference key setting 114 | # Upper case ASCII-keys are mapped following lower case keys 115 | 116 | # Color with /WHITE uses white background 117 | [attrib] 118 | color_merge_ab = WHITE,NORMAL # diff2 = 119 | color_merge_abc = WHITE,NORMAL # diff3 = 120 | color_merge_ac = WHITE,NORMAL # diff 3 # 121 | color_merge_a = GREEN,NORMAL # diff 3 A 122 | color_merge_c = YELLOW,NORMAL # diff 3 C 123 | color_merge_wdiff = WHITE,NORMAL # diff 3 G wdiff 124 | color_a = GREEN,BOLD # diff23 a 125 | color_a_focus = GREEN,BOLD,REVERSE 126 | #color_a_focus = GREEN/WHITE 127 | color_b2 = YELLOW,BOLD # diff2 b (wdiff2) 128 | color_b2_focus = YELLOW,BOLD,REVERSE 129 | #color_b2_focus = YELLOW/WHITE 130 | color_b3 = MAGENTA,BOLD # diff 3 b (wdiff3) 131 | color_b3_focus = MAGENTA,BOLD,REVERSE 132 | # color_b3_focus = MAGENTA/WHITE 133 | color_c = YELLOW,BOLD # diff 3 c 134 | color_c_focus = YELLOW,BOLD,REVERSE 135 | # color_c_focus = YELLOW/WHITE 136 | color_editor = CYAN,BOLD # editor-buffer-line 137 | color_editor_focus = CYAN,BOLD,REVERSE 138 | #color_editor_focus = CYAN/WHITE 139 | color_diff_marker = BLUE,NORMAL # marker-diff-line 140 | color_diff_marker_focus = BLUE,NORMAL,REVERSE 141 | color_wdiff_abc = WHITE,NORMAL # wdiff3 142 | color_wdiff_abc_focus = WHITE,BOLD,REVERSE 143 | color_wdiff_ac = WHITE,DIM # wdiff3 144 | color_wdiff_ac_focus = WHITE,BOLD,REVERSE 145 | color_wdiff_ab = WHITE,NORMAL # wdiff2 146 | color_wdiff_ab_focus = WHITE,BOLD,REVERSE 147 | color_wdiff_marker = BLUE,NORMAL 148 | color_wdiff_marker_focus = BLUE,NORMAL,REVERSE 149 | 150 | color_status = WHITE,NORMAL 151 | color_status_focus = WHITE,BOLD,REVERSE 152 | color_white_bold = WHITE,BOLD 153 | color_white = WHITE,NORMAL 154 | color_white_reverse = WHITE,BOLD,REVERSE 155 | color_warn = RED,BOLD 156 | color_eof = BLUE,DIM,REVERSE 157 | color_mono = WHITE,NORMAL 158 | 159 | 160 | [line_separator] # diff output formatting strings 161 | # diff2 uses ls0, ls2, ls3 162 | # diff3 uses ls0, ls1, ls2, ls3 163 | # File name added ls0, ls1, ls3 164 | ls0 = <<<<<<< 165 | ls1 = ||||||| 166 | ls2 = ======= 167 | ls3 = >>>>>>> 168 | 169 | [word_separator] # wdiff output formatting strings 170 | # wdiff2 uses ws0, ws1, ws3 171 | # wdiff3 uses ws0, ws1, ws2, ws3 172 | ws0 = { 173 | ws1 = | 174 | ws2 = | 175 | ws3 = } 176 | # alternative1 for UTF-8 terminal 177 | #ws0 = «🅐 178 | #ws1 = 🅑 179 | #ws2 = 🅒 180 | #ws3 = » 181 | # alternative2 for UTF-8 terminal 182 | #ws0 = « 183 | #ws1 = ╪ 184 | #ws2 = ╫ 185 | #ws3 = » 186 | -------------------------------------------------------------------------------- /test/z_imediff2.ref: -------------------------------------------------------------------------------- 1 | <<<<<<< file_a 2 | EXTRA-LINE 3 | ======= 4 | >>>>>>> file_b 5 | <<<<<<< file_a 6 | 2-way OLDER_FILE / 3-way MYFILE 7 | ======= 8 | 2-way NEWER_FILE / 3-way OLDFILE 9 | >>>>>>> file_b 10 | 00 wsx 11 | 01 qaz 12 | 02 adf 13 | <<<<<<< file_a 14 | 03 zcv aaa [G] 15 | ======= 16 | 03 bbb zcv 17 | >>>>>>> file_b 18 | 04 adv 19 | <<<<<<< file_a 20 | 05 tqazwsxyu 21 | ======= 22 | 05 tyu 23 | >>>>>>> file_b 24 | <<<<<<< file_a 25 | 06 ghj 26 | ======= 27 | 06 qwerrttt 28 | >>>>>>> file_b 29 | 07 ujj 30 | 08 qaz 31 | <<<<<<< file_a 32 | 09 wsx 33 | ======= 34 | 09 ***** 35 | >>>>>>> file_b 36 | 10 tte 37 | 11 iao 38 | <<<<<<< file_a 39 | 12 njk 40 | ======= 41 | >>>>>>> file_b 42 | 13 mop 43 | 14 wdd 44 | <<<<<<< file_a 45 | ======= 46 | 15 dsadsdedv 47 | >>>>>>> file_b 48 | 16 ijb 49 | 17 wsc 50 | <<<<<<< file_a 51 | ======= 52 | 12 njk 53 | >>>>>>> file_b 54 | 18 ugv 55 | 19 ijp 56 | <<<<<<< file_a 57 | ======= 58 | 20 tte 59 | >>>>>>> file_b 60 | 21 iao 61 | 22 njk 62 | <<<<<<< file_a 63 | ======= 64 | 23 mop 65 | >>>>>>> file_b 66 | 24 wdd 67 | 25 edv 68 | <<<<<<< file_a 69 | 26 ijssgsgab 70 | ======= 71 | 26 ijb 72 | >>>>>>> file_b 73 | 27 wsc 74 | <<<<<<< file_a 75 | 28 ugv 29 ijp LESS LINE 76 | ======= 77 | 28 ugv 78 | >>>>>>> file_b 79 | <<<<<<< file_a 80 | ======= 81 | 29 ijp 82 | >>>>>>> file_b 83 | -------------------------------------------------------------------------------- /test/z_imediff2_a.ref: -------------------------------------------------------------------------------- 1 | EXTRA-LINE 2 | 2-way OLDER_FILE / 3-way MYFILE 3 | 00 wsx 4 | 01 qaz 5 | 02 adf 6 | 03 zcv aaa [G] 7 | 04 adv 8 | 05 tqazwsxyu 9 | 06 ghj 10 | 07 ujj 11 | 08 qaz 12 | 09 wsx 13 | 10 tte 14 | 11 iao 15 | 12 njk 16 | 13 mop 17 | 14 wdd 18 | 16 ijb 19 | 17 wsc 20 | 18 ugv 21 | 19 ijp 22 | 21 iao 23 | 22 njk 24 | 24 wdd 25 | 25 edv 26 | 26 ijssgsgab 27 | 27 wsc 28 | 28 ugv 29 ijp LESS LINE 29 | -------------------------------------------------------------------------------- /test/z_imediff2_b.ref: -------------------------------------------------------------------------------- 1 | 2-way NEWER_FILE / 3-way OLDFILE 2 | 00 wsx 3 | 01 qaz 4 | 02 adf 5 | 03 bbb zcv 6 | 04 adv 7 | 05 tyu 8 | 06 qwerrttt 9 | 07 ujj 10 | 08 qaz 11 | 09 ***** 12 | 10 tte 13 | 11 iao 14 | 13 mop 15 | 14 wdd 16 | 15 dsadsdedv 17 | 16 ijb 18 | 17 wsc 19 | 12 njk 20 | 18 ugv 21 | 19 ijp 22 | 20 tte 23 | 21 iao 24 | 22 njk 25 | 23 mop 26 | 24 wdd 27 | 25 edv 28 | 26 ijb 29 | 27 wsc 30 | 28 ugv 31 | 29 ijp 32 | -------------------------------------------------------------------------------- /test/z_imediff2_d.ref: -------------------------------------------------------------------------------- 1 | <<<<<<< file_a 2 | EXTRA-LINE 3 | ======= 4 | >>>>>>> file_b 5 | <<<<<<< file_a 6 | 2-way OLDER_FILE / 3-way MYFILE 7 | ======= 8 | 2-way NEWER_FILE / 3-way OLDFILE 9 | >>>>>>> file_b 10 | 00 wsx 11 | 01 qaz 12 | 02 adf 13 | <<<<<<< file_a 14 | 03 zcv aaa [G] 15 | ======= 16 | 03 bbb zcv 17 | >>>>>>> file_b 18 | 04 adv 19 | <<<<<<< file_a 20 | 05 tqazwsxyu 21 | ======= 22 | 05 tyu 23 | >>>>>>> file_b 24 | <<<<<<< file_a 25 | 06 ghj 26 | ======= 27 | 06 qwerrttt 28 | >>>>>>> file_b 29 | 07 ujj 30 | 08 qaz 31 | <<<<<<< file_a 32 | 09 wsx 33 | ======= 34 | 09 ***** 35 | >>>>>>> file_b 36 | 10 tte 37 | 11 iao 38 | <<<<<<< file_a 39 | 12 njk 40 | ======= 41 | >>>>>>> file_b 42 | 13 mop 43 | 14 wdd 44 | <<<<<<< file_a 45 | ======= 46 | 15 dsadsdedv 47 | >>>>>>> file_b 48 | 16 ijb 49 | 17 wsc 50 | <<<<<<< file_a 51 | ======= 52 | 12 njk 53 | >>>>>>> file_b 54 | 18 ugv 55 | 19 ijp 56 | <<<<<<< file_a 57 | ======= 58 | 20 tte 59 | >>>>>>> file_b 60 | 21 iao 61 | 22 njk 62 | <<<<<<< file_a 63 | ======= 64 | 23 mop 65 | >>>>>>> file_b 66 | 24 wdd 67 | 25 edv 68 | <<<<<<< file_a 69 | 26 ijssgsgab 70 | ======= 71 | 26 ijb 72 | >>>>>>> file_b 73 | 27 wsc 74 | <<<<<<< file_a 75 | 28 ugv 29 ijp LESS LINE 76 | ======= 77 | 28 ugv 78 | >>>>>>> file_b 79 | <<<<<<< file_a 80 | ======= 81 | 29 ijp 82 | >>>>>>> file_b 83 | -------------------------------------------------------------------------------- /test/z_imediff2_f.ref: -------------------------------------------------------------------------------- 1 | <<<<<<< file_a 2 | EXTRA-LINE 3 | ======= 4 | >>>>>>> file_b 5 | 2-way {OLD|NEW}ER_FILE / 3-way {MY|OLD}FILE 6 | 00 wsx 7 | 01 qaz 8 | 02 adf 9 | 03{| bbb} zcv{ aaa [G]|} 10 | 04 adv 11 | 05 t{qazwsx|}yu 12 | 06 {ghj|qwerrttt} 13 | 07 ujj 14 | 08 qaz 15 | 09 {wsx|*****} 16 | 10 tte 17 | 11 iao 18 | <<<<<<< file_a 19 | 12 njk 20 | ======= 21 | >>>>>>> file_b 22 | 13 mop 23 | 14 wdd 24 | <<<<<<< file_a 25 | ======= 26 | 15 dsadsdedv 27 | >>>>>>> file_b 28 | 16 ijb 29 | 17 wsc 30 | <<<<<<< file_a 31 | ======= 32 | 12 njk 33 | >>>>>>> file_b 34 | 18 ugv 35 | 19 ijp 36 | <<<<<<< file_a 37 | ======= 38 | 20 tte 39 | >>>>>>> file_b 40 | 21 iao 41 | 22 njk 42 | <<<<<<< file_a 43 | ======= 44 | 23 mop 45 | >>>>>>> file_b 46 | 24 wdd 47 | 25 edv 48 | 26 ij{ssgsga|}b 49 | 27 wsc 50 | 28 ugv{ 29 ijp LESS LINE|} 51 | <<<<<<< file_a 52 | ======= 53 | 29 ijp 54 | >>>>>>> file_b 55 | -------------------------------------------------------------------------------- /test/z_imediff3.ref: -------------------------------------------------------------------------------- 1 | <<<<<<< file_a 2 | EXTRA-LINE 3 | 2-way OLDER_FILE / 3-way MYFILE 4 | ||||||| file_b 5 | 2-way NEWER_FILE / 3-way OLDFILE 6 | ======= 7 | 3-way YOURFILE 8 | >>>>>>> file_c 9 | 01 qaz 00 is missing only in C (above) 10 | 02 adf 11 | ccc 03 zcv aaa [G] 12 | 04 adv 13 | <<<<<<< file_a 14 | 05 tqazwsxyu 15 | ||||||| file_b 16 | 05 tyu 17 | ======= 18 | 05 tdfgyu 19 | >>>>>>> file_c 20 | 06 ghj 21 | 07 ujj 22 | 08 qaz 23 | 09 wsx 24 | 10 tte 25 | 11 iao 26 | 12 njk 27 | 13 mop 28 | 14 wdd 29 | <<<<<<< file_a 30 | ||||||| file_b 31 | 15 dsadsdedv 32 | ======= 33 | 15 edfsdsfdsfvsdf 34 | >>>>>>> file_c 35 | 16 ijb 36 | 17 wsc 37 | 18 ugv 38 | 19 ijp 39 | 21 iao 40 | 22 njk 41 | 24 wdd 42 | 25 edv 43 | <<<<<<< file_a 44 | 26 ijssgsgab 45 | ||||||| file_b 46 | 26 ijb 47 | ======= 48 | 26asdsdadadjdb 49 | >>>>>>> file_c 50 | 27 wsc 51 | 28 ugv 29 ijp LESS LINE 52 | EXTRA LINE 53 | -------------------------------------------------------------------------------- /test/z_imediff3_a.ref: -------------------------------------------------------------------------------- 1 | file_a -------------------------------------------------------------------------------- /test/z_imediff3_b.ref: -------------------------------------------------------------------------------- 1 | file_b -------------------------------------------------------------------------------- /test/z_imediff3_c.ref: -------------------------------------------------------------------------------- 1 | file_c -------------------------------------------------------------------------------- /test/z_imediff3_d.ref: -------------------------------------------------------------------------------- 1 | <<<<<<< file_a 2 | EXTRA-LINE 3 | 2-way OLDER_FILE / 3-way MYFILE 4 | ||||||| file_b 5 | 2-way NEWER_FILE / 3-way OLDFILE 6 | ======= 7 | 3-way YOURFILE 8 | >>>>>>> file_c 9 | 01 qaz 00 is missing only in C (above) 10 | 02 adf 11 | <<<<<<< file_a 12 | 03 zcv aaa [G] 13 | ||||||| file_b 14 | 03 bbb zcv 15 | ======= 16 | ccc 03 zcv 17 | >>>>>>> file_c 18 | 04 adv 19 | <<<<<<< file_a 20 | 05 tqazwsxyu 21 | ||||||| file_b 22 | 05 tyu 23 | ======= 24 | 05 tdfgyu 25 | >>>>>>> file_c 26 | 06 ghj 27 | 07 ujj 28 | 08 qaz 29 | 09 wsx 30 | 10 tte 31 | 11 iao 32 | 12 njk 33 | 13 mop 34 | 14 wdd 35 | <<<<<<< file_a 36 | ||||||| file_b 37 | 15 dsadsdedv 38 | ======= 39 | 15 edfsdsfdsfvsdf 40 | >>>>>>> file_c 41 | 16 ijb 42 | 17 wsc 43 | 18 ugv 44 | 19 ijp 45 | 21 iao 46 | 22 njk 47 | 24 wdd 48 | 25 edv 49 | <<<<<<< file_a 50 | 26 ijssgsgab 51 | ||||||| file_b 52 | 26 ijb 53 | ======= 54 | 26asdsdadadjdb 55 | >>>>>>> file_c 56 | 27 wsc 57 | 28 ugv 29 ijp LESS LINE 58 | EXTRA LINE 59 | -------------------------------------------------------------------------------- /test/z_imediff3_f.ref: -------------------------------------------------------------------------------- 1 | <<<<<<< file_a 2 | EXTRA-LINE 3 | 2-way OLDER_FILE / 3-way MYFILE 4 | ||||||| file_b 5 | 2-way NEWER_FILE / 3-way OLDFILE 6 | ======= 7 | 3-way YOURFILE 8 | >>>>>>> file_c 9 | 01 qaz 00 is missing only in C (above) 10 | 02 adf 11 | ccc 03 zcv aaa [G] 12 | 04 adv 13 | 05 t{qazwsx||dfg}yu 14 | 06 ghj 15 | 07 ujj 16 | 08 qaz 17 | 09 wsx 18 | 10 tte 19 | 11 iao 20 | 12 njk 21 | 13 mop 22 | 14 wdd 23 | <<<<<<< file_a 24 | ||||||| file_b 25 | 15 dsadsdedv 26 | ======= 27 | 15 edfsdsfdsfvsdf 28 | >>>>>>> file_c 29 | 16 ijb 30 | 17 wsc 31 | 18 ugv 32 | 19 ijp 33 | 21 iao 34 | 22 njk 35 | 24 wdd 36 | 25 edv 37 | 26asdsdadadj{ssgsga||d}b 38 | 27 wsc 39 | 28 ugv 29 ijp LESS LINE 40 | EXTRA LINE 41 | -------------------------------------------------------------------------------- /test/z_imediff3_g.ref: -------------------------------------------------------------------------------- 1 | <<<<<<< file_a 2 | EXTRA-LINE 3 | 2-way OLDER_FILE / 3-way MYFILE 4 | ||||||| file_b 5 | 2-way NEWER_FILE / 3-way OLDFILE 6 | ======= 7 | 3-way YOURFILE 8 | >>>>>>> file_c 9 | 01 qaz 00 is missing only in C (above) 10 | 02 adf 11 | ccc 03 zcv aaa [G] 12 | 04 adv 13 | <<<<<<< file_a 14 | 05 tqazwsxyu 15 | ||||||| file_b 16 | 05 tyu 17 | ======= 18 | 05 tdfgyu 19 | >>>>>>> file_c 20 | 06 ghj 21 | 07 ujj 22 | 08 qaz 23 | 09 wsx 24 | 10 tte 25 | 11 iao 26 | 12 njk 27 | 13 mop 28 | 14 wdd 29 | <<<<<<< file_a 30 | ||||||| file_b 31 | 15 dsadsdedv 32 | ======= 33 | 15 edfsdsfdsfvsdf 34 | >>>>>>> file_c 35 | 16 ijb 36 | 17 wsc 37 | 18 ugv 38 | 19 ijp 39 | 21 iao 40 | 22 njk 41 | 24 wdd 42 | 25 edv 43 | <<<<<<< file_a 44 | 26 ijssgsgab 45 | ||||||| file_b 46 | 26 ijb 47 | ======= 48 | 26asdsdadadjdb 49 | >>>>>>> file_c 50 | 27 wsc 51 | 28 ugv 29 ijp LESS LINE 52 | EXTRA LINE 53 | -------------------------------------------------------------------------------- /usr/lib/git-core/mergetools/imediff: -------------------------------------------------------------------------------- 1 | diff_cmd () { 2 | "$merge_tool_path" "$LOCAL" "$REMOTE" 3 | } 4 | 5 | merge_cmd () { 6 | if $base_present 7 | then 8 | "$merge_tool_path" --output="$MERGED" \ 9 | "$LOCAL" "$BASE" "$REMOTE" 10 | else 11 | "$merge_tool_path" --output="$MERGED" \ 12 | "$LOCAL" "$REMOTE" 13 | fi 14 | } 15 | 16 | -------------------------------------------------------------------------------- /usr/share/man/man1/Makefile: -------------------------------------------------------------------------------- 1 | # vim:set noet ts=8 2 | # All Linux/unix specific data installation by "make -C src/imediff/unix-data" 3 | 4 | all: manpages 5 | 6 | 7 | # Generate manpage from DocBook XML 8 | # 9 | # Require xsltproc 10 | # For Debian, xsltproc binary package. 11 | # For others, xsltproc is available from the libxsl source which uses libxml2. 12 | # See http://xmlsoft.org/xslt/ for more. 13 | # 14 | # In order to have flexibility for the manpage, I commit generated manpage in advance. 15 | # If you make eny manual touch up, please add "sed -i ..." script here etc. 16 | # 17 | # This approach should make build process more robust against some xslt upstream change etc. 18 | # 19 | MANPAGES = imediff.1 git-ime.1 20 | 21 | %.1: %.xml 22 | xsltproc --novalid /usr/share/sgml/docbook/stylesheet/xsl/nwalsh/manpages/docbook.xsl $? 23 | 24 | # This is used by the upstream 25 | manpages: $(MANPAGES) 26 | 27 | distclean: clean 28 | 29 | clean: 30 | rm -f $(MANPAGES) 31 | 32 | -------------------------------------------------------------------------------- /usr/share/man/man1/git-ime.1: -------------------------------------------------------------------------------- 1 | '\" t 2 | .\" Title: git-ime 3 | .\" Author: Osamu Aoki 4 | .\" Generator: DocBook XSL Stylesheets vsnapshot 5 | .\" Date: 2021-06-04 6 | .\" Manual: git-ime User Manual 7 | .\" Source: git-ime 8 | .\" Language: English 9 | .\" 10 | .TH "GIT\-IME" "1" "2021\-06\-04" "git-ime" "git-ime User Manual" 11 | .\" ----------------------------------------------------------------- 12 | .\" * Define some portability stuff 13 | .\" ----------------------------------------------------------------- 14 | .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 15 | .\" http://bugs.debian.org/507673 16 | .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html 17 | .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 18 | .ie \n(.g .ds Aq \(aq 19 | .el .ds Aq ' 20 | .\" ----------------------------------------------------------------- 21 | .\" * set default formatting 22 | .\" ----------------------------------------------------------------- 23 | .\" disable hyphenation 24 | .nh 25 | .\" disable justification (adjust text to left margin only) 26 | .ad l 27 | .\" ----------------------------------------------------------------- 28 | .\" * MAIN CONTENT STARTS HERE * 29 | .\" ----------------------------------------------------------------- 30 | .SH "NAME" 31 | git-ime \- An interactive git commit split helper tool 32 | .SH "SYNOPSIS" 33 | .HP \w'\fBgit\-ime\fR\ 'u 34 | \fBgit\-ime\fR [\fB\-a\fR] [\fB\-\-auto\fR] [\fB\-n\fR] [\fB\-\-notag\fR] [\fB\-v\fR] [\fB\-\-verbose\fR] [\fB\-h\fR] [\fB\-\-help\fR] 35 | .SH "DESCRIPTION" 36 | .PP 37 | This 38 | \fBgit ime\fR 39 | is a simple shell wrapper script on 40 | \fBgit\fR 41 | and 42 | \fBimediff\fR 43 | to split the latest commit from HEAD^ to HEAD on the current git repository into multiple commits\&. 44 | .PP 45 | If any staged changes or local uncommitted changes are found in the git repository, 46 | \fBgit ime\fR 47 | immediately exits without changes to be on the safe side\&. 48 | .PP 49 | If the latest commit involves multiple files, 50 | \fBgit ime\fR 51 | splits this big commit into multiple smaller commits involving a single file for each commit\&. When the first line of the commit message is "\-", each split commit message becomes just filename only\&. 52 | .PP 53 | If the latest commit involves only a single file, the commit is split into multiple smaller commits involving a set of meaningful partial changes selected by 54 | \fBimediff\fR 55 | interactively\&. The log of 56 | \fBimediff\fR 57 | is stored in the \&.git/imediff\&.log file 58 | .PP 59 | This 60 | \fBgit\-ime\fR 61 | is not only useful at the checked out branch head but also at "edit" prompt during the interactive execution of 62 | \fBgit rebase \-i \fR\fB\fItreeish\fR\fR\&. Execute 63 | \fBgit\-ime\fR 64 | after committing the pending commit\&. 65 | .PP 66 | To preserve good accessibility to the original state, this 67 | \fBgit\-ime\fR 68 | tags the original commit with "git\-ime\-aYYYYMMDD\-HHMMSS" if it hasn\*(Aqt been tagged\&. Also this 69 | \fBgit\-ime\fR 70 | tags its final commit with "git\-ime\-zYYYYMMDD\-HHMMSS" for the convenience of subsequent operations such as 71 | \fBgit\-rebase\fR\&. This is not applicable during 72 | \fBgit\-rebase\fR 73 | \&. 74 | .PP 75 | Please note this 76 | \fBgit\-ime\fR 77 | creates or overwrites 78 | \fIfile\fR\&.tmp_a and 79 | \fIfile\fR\&.tmp_b files\&. So they should not exist before execution of this command\&. 80 | .SH "OPTIONS" 81 | .PP 82 | \fB\-a\fR, \fB\-\-auto\fR 83 | .RS 4 84 | When operating on a single file change commit, the 85 | \fBgit ime\fR 86 | command executes 87 | \fBimediff\fR 88 | with 89 | \fB\-\-macro "Abw"\fR 90 | to select a series of minimum partial changes automatically and repeatedly to create a series of commits, instead\&. 91 | .RE 92 | .PP 93 | \fB\-v\fR, \fB\-\-verbose\fR 94 | .RS 4 95 | The 96 | \fBgit ime\fR 97 | command is executed with verbose console outputs\&. 98 | .RE 99 | .PP 100 | \fB\-n\fR, \fB\-\-notag\fR 101 | .RS 4 102 | Disable automatic tagging feature\&. Since the use of this option is desirable if this 103 | \fBgit ime\fR 104 | command is used during the 105 | \fBgit rebase\fR 106 | operation\&. In such case, this is automatically turned on as the default behavior\&. 107 | .RE 108 | .PP 109 | \fB\-h\fR, \fB\-\-help\fR 110 | .RS 4 111 | The 112 | \fBgit ime\fR 113 | outputs help message\&. 114 | .RE 115 | .SH "SEE ALSO" 116 | .PP 117 | imediff(1), git(1), and git\-rebase(1)\&. 118 | .SH "COPYRIGHT" 119 | .PP 120 | This manual page as well as the program itself was written by 121 | Osamu Aoki 122 | ()\&. This document has been placed into the Public Domain\&. 123 | .SH "AUTHOR" 124 | .PP 125 | \fBOsamu Aoki\fR 126 | .RS 4 127 | Original author of this script\&. 128 | .RE 129 | .SH "COPYRIGHT" 130 | .br 131 | Copyright \(co 2018-2021 Osamu Aoki 132 | .br 133 | -------------------------------------------------------------------------------- /usr/share/man/man1/git-ime.xml: -------------------------------------------------------------------------------- 1 | 4 | Osamu"> 5 | Aoki"> 6 | 2021-06-04"> 7 | 1"> 8 | osamu@debian.org"> 9 | 10 | git-ime"> 11 | 12 | 13 | 14 | 15 | GNU"> 16 | ]> 17 | 18 | 19 | 20 | &dhtitle; 21 | &dhpackage; 22 |
23 | &dhemail; 24 |
25 | 26 | &dhfirstname; 27 | &dhsurname; 28 | Original author of this script. 29 | 30 | 31 | 2018-2021 32 | &dhusername; &dhemail; 33 | 34 | &dhdate; 35 |
36 | 37 | &dhucpackage; 38 | 39 | &dhsection; 40 | 41 | 42 | &dhpackage; 43 | An interactive git commit split helper tool 44 | 45 | 46 | 47 | &dhpackage; 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | DESCRIPTION 61 | 62 | This git ime is a simple shell wrapper script on 63 | git and imediff to split the latest 64 | commit from HEAD^ to HEAD on the current git repository into multiple 65 | commits. 66 | 67 | If any staged changes or local uncommitted changes are found in the git 68 | repository, git ime immediately exits without changes to be 69 | on the safe side. 70 | 71 | If the latest commit involves multiple files, git ime 72 | splits this big commit into multiple smaller commits involving a single file 73 | for each commit. When the first line of the commit message is "-", each split 74 | commit message becomes just filename only. 75 | 76 | If the latest commit involves only a single file, the commit is split 77 | into multiple smaller commits involving a set of meaningful partial changes 78 | selected by imediff interactively. The log of 79 | imediff is stored in the .git/imediff.log file 80 | 81 | This git-ime is not only useful at the checked out 82 | branch head but also at "edit" prompt during the interactive execution of 83 | git rebase -i treeish. Execute 84 | git-ime after committing the pending commit. 85 | 86 | To preserve good accessibility to the original state, this 87 | git-ime tags the original commit with 88 | "git-ime-aYYYYMMDD-HHMMSS" if it hasn't been tagged. Also this 89 | git-ime tags its final commit with 90 | "git-ime-zYYYYMMDD-HHMMSS" for the convenience of subsequent operations such 91 | as git-rebase. This is not applicable during 92 | git-rebase . 93 | 94 | Please note this git-ime creates or overwrites 95 | file.tmp_a and 96 | file.tmp_b files. So they should not exist before 97 | execution of this command. 98 | 99 | 100 | 101 | 102 | OPTIONS 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | When operating on a single file change commit, the git 111 | ime command executes imediff with 112 | --macro "Abw" to select a series of minimum partial changes 113 | automatically and repeatedly to create a series of commits, instead. 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | The git ime command is executed with verbose console 123 | outputs. 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | Disable automatic tagging feature. Since the use of this option is 133 | desirable if this git ime command is used during the 134 | git rebase operation. In such case, this is automatically 135 | turned on as the default behavior. 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | The git ime outputs help message. 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | SEE ALSO 154 | 155 | imediff(1), git(1), and git-rebase(1). 156 | 157 | 158 | 159 | COPYRIGHT 160 | 161 | This manual page as well as the program itself was written by 162 | &dhfirstname; &dhsurname; (&dhemail;). This document has been 163 | placed into the Public Domain. 164 | 165 | 166 |
167 | -------------------------------------------------------------------------------- /usr/share/man/man1/imediff.1: -------------------------------------------------------------------------------- 1 | '\" t 2 | .\" Title: imediff 3 | .\" Author: Jarno Elonen 4 | .\" Generator: DocBook XSL Stylesheets vsnapshot 5 | .\" Date: 2018-12-11 6 | .\" Manual: imediff User Manual 7 | .\" Source: imediff 8 | .\" Language: English 9 | .\" 10 | .TH "IMEDIFF" "1" "2018\-12\-11" "imediff" "imediff User Manual" 11 | .\" ----------------------------------------------------------------- 12 | .\" * Define some portability stuff 13 | .\" ----------------------------------------------------------------- 14 | .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 15 | .\" http://bugs.debian.org/507673 16 | .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html 17 | .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 18 | .ie \n(.g .ds Aq \(aq 19 | .el .ds Aq ' 20 | .\" ----------------------------------------------------------------- 21 | .\" * set default formatting 22 | .\" ----------------------------------------------------------------- 23 | .\" disable hyphenation 24 | .nh 25 | .\" disable justification (adjust text to left margin only) 26 | .ad l 27 | .\" ----------------------------------------------------------------- 28 | .\" * MAIN CONTENT STARTS HERE * 29 | .\" ----------------------------------------------------------------- 30 | .SH "NAME" 31 | imediff \- An interactive fullscreen merge tool for 2 or 3 files 32 | .SH "SYNOPSIS" 33 | .HP \w'\fBimediff\fR\ 'u 34 | \fBimediff\fR [\fB\-h\fR] [\fB\-v\fR] [\fB\-abcdfg\fR] [\fB\-o\ \fR\fB\fIOUTPUT\fR\fR] [\fB\-m\fR] [\fB\-n\fR] [\fB\-s\fR] [\fB\-t\fR] [\fB\-l\fR] [\fB\-\&.\&.\&.\fR] {\fIfile_a\fR} {\fIfile_b\fR} [\fIfile_c\fR] 35 | .SH "DESCRIPTION" 36 | .PP 37 | Merge two (slightly different) files with an optional common base file interactively with a user friendly fullscreen interface in the full screen text terminal mode\&. 38 | .PP 39 | For diff2 case, these files are: 40 | .sp 41 | .RS 4 42 | .ie n \{\ 43 | \h'-04'\(bu\h'+03'\c 44 | .\} 45 | .el \{\ 46 | .sp -1 47 | .IP \(bu 2.3 48 | .\} 49 | \fIfile_a\fR 50 | == 51 | \fIOLDER\fR 52 | .RE 53 | .sp 54 | .RS 4 55 | .ie n \{\ 56 | \h'-04'\(bu\h'+03'\c 57 | .\} 58 | .el \{\ 59 | .sp -1 60 | .IP \(bu 2.3 61 | .\} 62 | \fIfile_b\fR 63 | == 64 | \fINEWER\fR 65 | .RE 66 | .PP 67 | For diff3 case, these files are: 68 | .sp 69 | .RS 4 70 | .ie n \{\ 71 | \h'-04'\(bu\h'+03'\c 72 | .\} 73 | .el \{\ 74 | .sp -1 75 | .IP \(bu 2.3 76 | .\} 77 | \fIfile_a\fR 78 | == 79 | \fIMYFILE\fR 80 | .RE 81 | .sp 82 | .RS 4 83 | .ie n \{\ 84 | \h'-04'\(bu\h'+03'\c 85 | .\} 86 | .el \{\ 87 | .sp -1 88 | .IP \(bu 2.3 89 | .\} 90 | \fIfile_b\fR 91 | == 92 | \fIOLDFILE\fR 93 | (base file) 94 | .RE 95 | .sp 96 | .RS 4 97 | .ie n \{\ 98 | \h'-04'\(bu\h'+03'\c 99 | .\} 100 | .el \{\ 101 | .sp -1 102 | .IP \(bu 2.3 103 | .\} 104 | \fIfile_c\fR 105 | == 106 | \fIYOURFILE\fR 107 | .RE 108 | .PP 109 | \fBimediff\fR 110 | shows the differences of given two files (in color if the terminal supports them), lets you scroll the file and toggle changes between older and newer version for each chunk of difference\&. You can also select line diff and character wdiff mode for display\&. If you wish to make manual changes, you can edit each chunk with editor\&. The result of merge operation is saved in 111 | \fIOUTPUT\fR\&. If this is missing, STDERR is used\&. 112 | .PP 113 | Additionally, you can customize your choice of editor via 114 | \fI$EDITOR\fR 115 | or 116 | ~/\&.imediff\&. The default editor is 117 | \fB/usr/bin/editor\fR\&. 118 | .PP 119 | Removed lines are shown in reversed \*(Aq???\*(Aq as a placeholder, so you see that and you can select them\&. While it takes one line on display, it is naturally not saved into the output file\&. 120 | .PP 121 | To see the key binding, hit 122 | h 123 | in the main 124 | \fBimediff\fR 125 | screen\&. 126 | .PP 127 | To see the tutorial, hit 128 | H 129 | in the main 130 | \fBimediff\fR 131 | screen or simply start the 132 | \fBimediff\fR 133 | command without arguments\&. 134 | .PP 135 | The program exits with status 0 if the changes were saved, 1 if the merging was aborted and 2 if the parameters were invalid\&. 136 | .SH "OPTIONS" 137 | .PP 138 | \fB\-h\fR, \fB\-\-help\fR 139 | .RS 4 140 | Show summary of options and exit\&. 141 | .RE 142 | .PP 143 | \fB\-a\fR 144 | .RS 4 145 | Start with all chunks to use 146 | \fIfile_a\fR\&. 147 | .RE 148 | .PP 149 | \fB\-b\fR 150 | .RS 4 151 | Start with all chunks to use 152 | \fIfile_b\fR\&. 153 | .RE 154 | .PP 155 | \fB\-c\fR 156 | .RS 4 157 | Start with all chunks to use 158 | \fIfile_c\fR 159 | (only for diff3)\&. 160 | .RE 161 | .PP 162 | \fB\-d\fR 163 | .RS 4 164 | Start with all chunks to use diff\&. 165 | .RE 166 | .PP 167 | \fB\-f\fR 168 | .RS 4 169 | Start with all chunks to use wdiff\&. 170 | .RE 171 | .PP 172 | \fB\-g\fR 173 | .RS 4 174 | Start with good merge mode\&. (only for diff3) 175 | .RE 176 | .PP 177 | \fB\-o \fR\fB\fIOUTPUT\fR\fR, \fB\-\-output=\fR\fB\fIfile_output\fR\fR 178 | .RS 4 179 | Write output to a given file\&. If this is missing, STDERR is used\&. 180 | .RE 181 | .PP 182 | \fB\-l\fR, \fB\-\-force\-logging\fR 183 | .RS 4 184 | Force LOGFILE="imediff\&.log" and LOGLEVEL="INFO"\&. 185 | .RE 186 | .PP 187 | \fB\-n\fR, \fB\-\-non\-interactive\fR 188 | .RS 4 189 | Use non\-interactive CLI instead of normal TUI\&. 190 | .RE 191 | .PP 192 | \fB\-s\fR, \fB\-\-sloppy\fR 193 | .RS 4 194 | Allow one to save unresolved contents\&. 195 | .RE 196 | .PP 197 | \fB\-t\fR, \fB\-\-template\fR 198 | .RS 4 199 | Create a template configuration file 200 | ~/\&.imediff 201 | \&. 202 | .RE 203 | .PP 204 | \fB\-v\fR, \fB\-\-version\fR 205 | .RS 4 206 | Show version and license\&. 207 | .RE 208 | .PP 209 | \fB\-\&.\&.\&.\fR 210 | .RS 4 211 | For other options, see the tutorial by starting "imediff" without any arguments\&. 212 | .RE 213 | .SH "SEE ALSO" 214 | .PP 215 | sdiff(1), diff(1), merge(1), diff3(1)\&. 216 | .SH "COPYRIGHT" 217 | .PP 218 | This manual page as well as the program itself was written by 219 | Jarno Elonen 220 | 221 | and 222 | Osamu Aoki 223 | \&. Unlike the program itself, which is licensed under the GNU General Public License (GPL) version 2 (or any later version, at your option), this document has been placed into the Public Domain\&. 224 | .SH "AUTHORS" 225 | .PP 226 | \fBJarno Elonen\fR <\&elonen@iki\&.fi\&> 227 | .RS 4 228 | Original author for 2 file merge in python2 229 | .RE 230 | .PP 231 | \fBOsamu Aoki\fR <\&osamu@debian\&.org\&> 232 | .RS 4 233 | Rewrite author for 2 and 3 file merge in python3 234 | .RE 235 | .SH "COPYRIGHT" 236 | .br 237 | Copyright \(co 2003-2006, 2018 Jarno Elonen , Osamu Aoki 238 | .br 239 | -------------------------------------------------------------------------------- /usr/share/man/man1/imediff.xml: -------------------------------------------------------------------------------- 1 | 4 | Jarno 5 | Elonen elonen@iki.fi"> 6 | Osamu 7 | Aoki osamu@debian.org"> 8 | GNU"> 9 | ]> 10 | 11 | 12 | 13 | imediff User Manual 14 | imediff 15 | 2018-12-11 16 | 17 | &holder1; 18 | Original author for 2 file merge in python2 19 | 20 | 21 | &holder2; 22 | Rewrite author for 2 and 3 file merge in python3 23 | 24 | 25 | 2003-2006 &holder1; 26 | 2018 &holder2; 27 | 28 | 29 | 30 | imediff 1 31 | 32 | 33 | imediff 34 | An interactive fullscreen merge tool for 2 or 3 files 35 | 36 | 37 | 38 | imediff 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | file_a 50 | file_b 51 | file_c 52 | 53 | 54 | 55 | DESCRIPTION 56 | 57 | Merge two (slightly different) files with an optional common base file 58 | interactively with a user friendly fullscreen interface in the full screen 59 | text terminal mode. 60 | 61 | For diff2 case, these files are: 62 | 63 | file_a == OLDER 64 | file_b == NEWER 65 | 66 | 67 | For diff3 case, these files are: 68 | 69 | file_a == MYFILE 70 | file_b == OLDFILE (base file) 71 | file_c == YOURFILE 72 | 73 | 74 | imediff shows the differences of given two 75 | files (in color if the terminal supports them), lets you scroll the file and 76 | toggle changes between older and newer version for each chunk of difference. 77 | You can also select line diff and character wdiff mode for display. If you 78 | wish to make manual changes, you can edit each chunk with editor. The 79 | result of merge operation is saved in 80 | OUTPUT. If this is missing, STDERR is used. 81 | 82 | Additionally, you can customize your choice of editor via 83 | $EDITOR or ~/.imediff. The default 84 | editor is /usr/bin/editor. 85 | 86 | Removed lines are shown in reversed '???' as a 87 | placeholder, so you see that and you can select them. While it takes one 88 | line on display, it is naturally not saved into the output file. 89 | 90 | To see the key binding, hit h in the main 91 | imediff screen. 92 | 93 | To see the tutorial, hit H in the main 94 | imediff screen or simply start the 95 | imediff command without arguments. 96 | 97 | The program exits with status 0 if the changes were saved, 1 if the 98 | merging was aborted and 2 if the parameters were invalid. 99 | 100 | 101 | 102 | 103 | OPTIONS 104 | 105 | 106 | 107 | 108 | , 109 | 110 | Show summary of options and exit. 111 | 112 | 113 | 114 | 115 | 116 | 117 | Start with all chunks to use file_a. 118 | 119 | 120 | 121 | 122 | 123 | 124 | Start with all chunks to use file_b. 125 | 126 | 127 | 128 | 129 | 130 | 131 | Start with all chunks to use file_c (only for diff3). 132 | 133 | 134 | 135 | 136 | 137 | 138 | Start with all chunks to use diff. 139 | 140 | 141 | 142 | 143 | 144 | 145 | Start with all chunks to use wdiff. 146 | 147 | 148 | 149 | 150 | 151 | 152 | Start with good merge mode. (only for diff3) 153 | 154 | 155 | 156 | 157 | 158 | 159 | Write output to a given file. If this is missing, STDERR is used. 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | Force LOGFILE="imediff.log" and LOGLEVEL="INFO". 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | Use non-interactive CLI instead of normal TUI. 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | Allow one to save unresolved contents. 184 | 185 | 186 | 187 | 188 | , 189 | 190 | Create a template configuration file ~/.imediff . 191 | 192 | 193 | 194 | 195 | , 196 | 197 | Show version and license. 198 | 199 | 200 | 201 | 202 | 203 | 204 | For other options, see the tutorial by starting "imediff" without any arguments. 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | SEE ALSO 213 | 214 | sdiff(1), diff(1), merge(1), diff3(1). 215 | 216 | 217 | 218 | COPYRIGHT 219 | 220 | This manual page as well as the program itself was written 221 | by &holder1; and &holder2;. 222 | Unlike the program itself, which is licensed under the GNU 223 | General Public License (GPL) version 2 (or any later version, 224 | at your option), this document has been placed into the 225 | Public Domain. 226 | 227 | 228 | 229 | 230 | --------------------------------------------------------------------------------