Compare commits

...

15 commits

6 changed files with 161 additions and 30 deletions

View file

@ -14,7 +14,6 @@ jobs:
matrix:
os: [ubuntu-22.04, macos-15]
ghc:
- "9.6"
- "9.12"
steps:
- uses: actions/checkout@v4

View file

@ -1,5 +0,0 @@
# Revision history for werge
## 0.1.0.0 -- YYYY-mm-dd
* First version. Released on an unsuspecting world.

View file

@ -385,4 +385,4 @@ main = catch go bad
go = parseOpts >>= uncurry (flip runCmd)
bad e = do
hPutStrLn stderr $ "fatal: " ++ displayException (e :: IOException)
exitWith (ExitFailure 2)
exitWith (ExitFailure 129)

149
README.md
View file

@ -21,21 +21,52 @@ Separate `diff`&`patch` functionality is provided too for sending
token-granularity patches. (The patches are similar to what `git diff
--word-diff` produces, but can be applied to files.)
## Installation
- To build from source, clone the repo and run `cabal install` in the directory
(you need [a way to compile Haskell](https://www.haskell.org/downloads/)).
- [Releases](https://github.com/exaexa/werge/releases) come with prebuilt
binaries that you may download and run as-is on many Linuxes and Macs.
Running of `werge` requires working installations of `diff` and `patch`
compatible with the ones from [GNU
diffutils](https://www.gnu.org/software/diffutils/):
- Most Linux distributions contain the correct diffutils
- On BSDs you should be able to install these from Ports
([FreeBSD](https://cgit.freebsd.org/ports/tree/textproc/diffutils),
[OpenBSD](https://openports.eu/ports/textproc/gdiff))
- On Macs, install [diffutils from
brew](https://formulae.brew.sh/formula/diffutils).
In any other case, you may set up a path to any compatible `diff` and `patch`
(or suitable wrapper scripts) via environment variables `WERGE_DIFF` and
`WERGE_PATCH`. (If required, the same applies for `WERGE_GIT`.)
### Editor integration
There's a `vim` syntax highlighting file in `vim/werge.vim`. To install, simply
copy it to your local `vim` syntax configuration directory (usually to
`~/.vim/syntax/werge.vim`). Then, you can activate the syntax in vim with:
```
:set syn=werge
```
## Demo
Original (`old` file):
##### Original (`old` file):
```
Roses are red. Violets are blue.
Patch is quite hard. I cannot rhyme.
```
Local changes (`my` file):
##### Local changes (`my` file):
```
Roses are red. Violets are blue.
Patching is hard. I still cannot rhyme.
```
Remote changes (`your` file):
##### Remote changes (`your` file):
```
Roses are red.
Violets are blue.
@ -43,18 +74,23 @@ Patch is quite hard.
I cannot do verses.
```
Token-merged version with `werge merge my orig your` (conflicts on the space
change that is too close to the disappearing "still" token):
##### Token-merged version
This is produced with `werge merge my old your` (conflicts on the space change
that is too close to the disappearing "still" token):
```
Roses are red.
Violets are blue.
Patching is hard.<<<<< I still||||| I=====
I>>>>> cannot do verses.
```
(NOTE: option `-G` gives nicely colored output that is much easier to read.)
(NOTE: option `-G` gives nicely colored output that is much easier to read.
Alternatively you can install the syntax highlighting for `vim`.)
Token-merged version with separate space resolution using `-s` (conflicts get
fixed separately):
##### Merge with separate space resultion
Adding option `-s` to `werge merge` causes it to resolve space conflicts
separately, usually helping many cases that would be easily resolvable by a
human:
```
Roses are red.
Violets are blue.
@ -62,7 +98,8 @@ Patching is hard.
I still cannot do verses.
```
A harder-conflicting file (`theirs`):
##### Mixing in unresolvable conflict
A harder-conflicting file (`their`):
```
Roses are red.
Violets are blue.
@ -70,7 +107,7 @@ Merging is quite hard.
I cannot do verses.
```
`werge merge mine orig theirs -s` highlights the actual unmergeable change:
`werge merge my old their -s` highlights the actual unmergeable change:
```
Roses are red.
Violets are blue.
@ -134,18 +171,9 @@ because the approach was too complex. Before that, the issue was tackled by
Arek Antoniewicz on MFF CUNI, who used regex-edged DFAs (REDFAs) to construct
user-specifiable tokenizers in a pretty cool way.
## Installation
## Integration with `git`
```sh
cabal install
```
Running of `werge` requires a working installation of `diff` compatible
with the one from [GNU diffutils](https://www.gnu.org/software/diffutils/). You
may set up a path to such `diff` (or a wrapper script) via environment variable
`WERGE_DIFF`.
## Use with `git`
### Automerging conflicts
`werge` can automatically process files that are marked in `git` as merge
conflicts:
@ -161,6 +189,85 @@ settings it runs `git add` on them. The current changes in the files are
replaced by the merged (or partially merged) state; backups are written
automatically to `filename.werge-backup`.
Optionally, you can specify exact files to be automerged. That is useful for
cases when only some of the conflicting files should be processed by `werge`:
```sh
$ werge git my/conflicting/file.txt
```
Support for merging complex types of changes (deletes, directory moves,
symlinks, ...) via this interface is currently limited. `werge` can be used as
a mergetool or a merge driver to ameliorate that.
### Use as `git difftool` and `git mergetool`
The `git` config below allows direct use of `werge` as `git difftool -t werge`
and `git mergetool -t werge`:
```ini
[difftool "werge"]
cmd = werge diff -G $LOCAL $REMOTE
[mergetool "werge"]
cmd = werge merge $LOCAL $BASE $REMOTE > $MERGED
trustExitCode = true
# variant for separate resolution of space (solves more conflicts):
[mergetool "spacewerge"]
cmd = werge merge -s $LOCAL $BASE $REMOTE > $MERGED
trustExitCode = true
```
One issue with `git` mergetools is that they are supposed to be interactive,
and thus `git` expects them to always produce a completely merged, conflictless
result. In turn, if the auto-merging with `git mergetool -t werge` fails with
conflicts, `git` assumes a complete failure and restores the original version
from the backup. To enable a more useful behavior, use `werge` as a merge
driver (see below).
### Use as a `git` merge driver
Add this to your git config:
```ini
[merge "werge"]
name = werge
driver = werge merge %A %O %B > %P
recursive = binary
```
Then, specify that the "werge" driver should be used for certain files in your
repository's `.gitattributes`:
```
*.md merge=werge
*.tex merge=werge
# ... etc
```
With this in place, `git merge` will automatically run `werge` to merge the
marked files in the repository. On conflict, you will have the files marked
with the usual (werge's usual) conflict markers, and you will be able to
resolve them just as with the normal merging workflow.
**Hint:** As with `spacewerge` mergetool above, it is beneficial to add a few
conflict-resolving options such as `-s` to the `driver`, in order to help the
automerges pass nicely.
### Use with `git rebase`
The merge driver and mergetools as configured above will also automatically
work with `git rebase` that runs in the "merge mode" (which is the default).
As a possible source of confusion, the "my" and "your" versions are somewhat swapped (as implied by semantics):
- With `git checkout mybranch; git merge otherbranch`, the conflicts will look
roughly like this:
```
<<<<< mybranch version ||||| merge base ===== otherbranch version >>>>>
```
- With `git checkout mybranch; git rebase otherbranch`, the logic is reversed:
```
<<<<< otherbranch version ||||| common base ===== mybranch version >>>>>
```
## Current `--help` and features
```

29
vim/werge.vim Normal file
View file

@ -0,0 +1,29 @@
" Vim syntax file
" Language: werge
" Maintainer: Mirek Kratochvil
" Last Change: Oct 14, 2025
" Version: 1
" URL: https://github.com/exaexa/werge
" quit if a syntax file was already loaded
if exists("b:current_syntax")
finish
endif
" syntax for the werge files
syntax region wergeHunk start=/<<<<</ end=/>>>>>/ contains=wergeRm,wergeDiffAdd,wergeConflictOrigAdd
syntax region wergeRm start=/<<<<</hs=e+1 end=/|||||/he=s-1,me=s-1 contained
syntax match wergeDiffAdd /|||||\(\_[^>=]\|>\{1,4\}>\@!\|=\{1,4\}=\@!\)*>>>>>/ms=s+5,me=e-5 contained
syntax match wergeConflictOrigAdd /|||||\(\_[^=>]\|=\{1,4\}=\@!\|>\{1,4\}>\@!\)*=====\(\_[^=>]\|=\{1,4\}=\@!\|>\{1,4\}>\@!\)*>>>>>/me=e-5 contained contains=wergeConflictOrig,wergeconflictAdd
syntax region wergeConflictOrig start=/|||||/hs=e+1 end=/=====/he=s-1,me=s-1 contained
syntax region wergeConflictAdd start=/=====/hs=e+1 end=/>>>>>/he=s-1,me=s-1 contained
" color specification
highlight default link wergeHunk Comment
highlight default link wergeRm Removed
highlight default link wergeDiffAdd Added
highlight default link wergeConflictOrigAdd Comment
highlight default link wergeConflictOrig Changed
highlight default link wergeConflictAdd Added
let b:current_syntax = "werge"

View file

@ -1,16 +1,17 @@
cabal-version: 3.0
name: werge
version: 0.2.0.0
version: 0.2.1.0
synopsis: mergetool for mangled-up bite-size changes
license: GPL-3.0-or-later
license-file: LICENSE
copyright: (c) 2025 Mirek Kratochvil
author: Mirek Kratochvil
maintainer: exa.exa@gmail.com
-- copyright:
category: Text
build-type: Simple
extra-doc-files: CHANGELOG.md
extra-doc-files: README.md vim/werge.vim
-- extra-source-files: