The sound distributed version control system

#668 apply (or push or pull) should be able to fail when a conflict occurs

Opened by apm on April 12, 2022
apm on April 12, 2022

Scenario: I have two repositories (a user repository and a master repository) with two patches in conflict.

Pull case:

# from the master repo to the user repo
$ pijul pull ../master

There were conflicts:

  - Order conflict in "a" starting on line 2

Everything is ok. A conflict has been detected, and it is locally marked: I can fix it.

Push case:

# from the user repo to the master repo
$ pijul push ../master
Uploading changes [==================================================] 1/1

Everything seems ok but a conflict has occurred on the master repository. Some master’s files now exhibit conflict markers, and I do not know it.

Proposal

Follow the darcs way, which allows to refuse to apply (or pull or push) a patch which would lead to a conflict:

from darcs apply --help:

Usage: darcs apply [OPTION]... <PATCHFILE>
Apply a patch bundle created by `darcs send'.

Options:
[...]
      --no-resolve-conflicts       fail if there are patches that would create conflicts [DEFAULT]

And like darcs, by default, do not allow to push or apply a patch which will leads to a conflict:

$ darcs push ../master
We have conflicts in the following files:
./a
Refusing to apply patches leading to conflicts.
If you would rather apply the patch and mark the conflicts,
use the --mark-conflicts or --allow-conflicts options to apply
These can set as defaults by adding
 apply mark-conflicts
to _darcs/prefs/defaults in the target repo. 
Apply failed!
apm on April 13, 2022

Related observation: another way to introduce a conflict without notification is by unrecording a patch.

# Everything OK
$ cat a
1
3

# Unrecord a patch
$ pijul unrecord --reset NB37SGCYG6UNUBQFFQKQUIT7KJZWPVUWMN64PKNFS75GQX24PE5QC

# Everything seems fine, but a conflict has appeared in a file, and I have no mean to know it...
$ cat a
1
>>>>>>> 1 [F2VHVRPD]
3
======= 1 [FXJQP5CV]
2
<<<<<<< 1
spacefrogg on April 26, 2022

I understand the confusion, but I have several remarks regarding your proposition.

First, you must accept that conflicting changes usually remain part of the code base, even after they have been resolved. The reason is, that “conflict” is a normal state for a pijul working tree. It may be an unwanted state and it will break the semantics of your working tree files, but it is just a normal state for a repository to be in. To fix this semantically unwanted state, you fix your files to your liking and record a fixing change. This change will always be the fixing change for this particular conflict (which arose from specific set of conflicting changes). It, too, will become part of your code base forever. Whenever you unrecord the fixing change, you will introduce this particular conflict again. Also, you can decide to leave the conflict alone (e.g., when it affects parts of the code base which is not interesting for you, like docs or locales) and add new changes to the “conflicting” state.

This is radically different from all other VCS’s I know of. In those, being in a “conflict” means, you have broken your repository (in a controlled way) and need to change one of the conflicting parties (commits, patches, whatever the VCS calls them) to bring it back into a healthy state. It is syntactically broken (and semantically as well). You cannot go on from this state, cannot commit or branch or do anything other than immediately decide to fix one of the conflicting parties.

Now, it might be worthwhile to output a warning, should you end up in a conflicting state after a push/pull.

Regarding the unrecord, it looks strange to me that you unrecorded a change that solved a conflict. You must have known that it solved the conflict and/or the change author should have stated so clearly in the change message. (Because, obviously, it is important to know when a change fixes a conflict.) So, how exactly could you make that happen? Did you artificially create a conflict and resolve it to show that pijul does not print a warning message?

EDIT Additional remarks. pijul apply outputs a conflict message. Also, pijul diff should be changed to output conflicts, as well.

apm on April 27, 2022

I understand Pijul’s innovative way to handle conflicts and I find it great. However, it is not because it is possible to have a repository exhibiting conflicts as a “normal state” that such a state is always desirable.

Consider the following scenario:

  • developer A pushes a change to the master repository, a conflict occurs which breaks the build, all other developers have to wait that A pushes a fixing change before they be able to go back to work (variation: a developer A sends a change to some CI pipeline, which calls pijul apply, and we want to fail as soon as a conflict is detected).

The “darcs way” seems a sane default to me:

  • developer A pushes a change to the master repository, a conflict occurs, and the push fails. A pulls changes, solves the conflict locally (either by unrecording a local patch, amending a patch, rollbacking a patch pulled from the master (when pijul rollback will be here), or creating a new one), then pushes the appropriate change: no other developers will be impacted.

Something like pijul push --allow-conflicts or pijul apply --allow-conflicts would allow to share conflicted state with other deliberately, only if it is the true intent of the user.

Regarding the unrecord, it looks strange to me that you unrecorded a change that solved a conflict.

Yes, I saw this when I made some tests, it is not a real-life scenario.

EDIT Additional remarks. pijul apply outputs a conflict message. Also, pijul diff should be changed to output conflicts, as well.

Great for apply! I agree with diff, and such a conflict message is missing with pijul push and pijul unrecord.