add a changeset tag if a merge conflict occurred (#9636)

This commit is contained in:
Kyℓe Hensel
2025-03-05 22:38:32 +11:00
committed by GitHub
parent c7e177fee4
commit 7beace6698
4 changed files with 52 additions and 2 deletions
+2
View File
@@ -45,6 +45,7 @@ _Breaking developer changes, which may affect downstream projects or sites that
* Render `*_link` roads narrower than their "regular" counterpart ([#10722])
* Remove `noexit=yes` and `fixme=continue` when you continue a line ([#9634], thanks [@k-yle])
* Don't zoom out when interacting with notes ([#10807], thanks [@hlfan])
* Add a changeset tag if a merge conflict occurred ([#9636], thanks [@k-yle])
#### :scissors: Operations
* Fix splitting of closed ways (or areas) when two or more split-points are selected
* Keep `red_turn:left/right` tags unchanged when reversing a way ([#10737], thanks [@burrscurr])
@@ -84,6 +85,7 @@ _Breaking developer changes, which may affect downstream projects or sites that
[#9586]: https://github.com/openstreetmap/iD/pull/9586
[#9634]: https://github.com/openstreetmap/iD/pull/9634
[#9635]: https://github.com/openstreetmap/iD/pull/9635
[#9636]: https://github.com/openstreetmap/iD/pull/9636
[#10003]: https://github.com/openstreetmap/iD/pull/10003
[#10618]: https://github.com/openstreetmap/iD/pull/10618
[#10646]: https://github.com/openstreetmap/iD/pull/10646
+16 -2
View File
@@ -11,6 +11,7 @@ import { t } from '../core/localizer';
import { utilArrayUnion, utilArrayUniq, utilDisplayName, utilDisplayType, utilRebind } from '../util';
/** @param {iD.Context} context */
export function coreUploader(context) {
var dispatch = d3_dispatch(
@@ -30,6 +31,7 @@ export function coreUploader(context) {
var _isSaving = false;
let _anyConflictsAutomaticallyResolved = false;
var _conflicts = [];
var _errors = [];
var _origChanges;
@@ -72,6 +74,7 @@ export function coreUploader(context) {
var history = context.history();
_anyConflictsAutomaticallyResolved = false;
_conflicts = [];
_errors = [];
@@ -251,7 +254,10 @@ export function coreUploader(context) {
history.replace(merge);
var mergeConflicts = merge.conflicts();
if (!mergeConflicts.length) return; // merged safely
if (!mergeConflicts.length) {
_anyConflictsAutomaticallyResolved = true;
return; // merged safely
}
var forceLocal = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_local');
var forceRemote = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_remote');
@@ -273,7 +279,7 @@ export function coreUploader(context) {
}
function upload(changeset) {
async function upload(changeset) {
var osm = context.connection();
if (!osm) {
_errors.push({ msg: 'No OSM Service' });
@@ -286,6 +292,11 @@ export function coreUploader(context) {
didResultInErrors();
} else {
if (_anyConflictsAutomaticallyResolved) {
// add a changeset tag to aid reviewers
changeset.tags.merge_conflict_resolved = 'automatically';
await osm.updateChangesetTags(changeset);
}
var history = context.history();
var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
if (changes.modified.length || changes.created.length || changes.deleted.length) {
@@ -339,6 +350,9 @@ export function coreUploader(context) {
function didResultInConflicts(changeset) {
// add a changeset tag to aid reviewers
changeset.tags.merge_conflict_resolved = 'manually';
context.connection().updateChangesetTags(changeset);
_conflicts.sort(function(a, b) { return b.id.localeCompare(a.id); });
+10
View File
@@ -876,6 +876,16 @@ export default {
}
},
/** updates the tags on an existing unclosed changeset */
// PUT /api/0.6/changeset/#id
updateChangesetTags: (changeset) => {
return oauth.fetch(`${oauth.options().apiUrl}/api/0.6/changeset/${changeset.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'text/xml' },
body: JXON.stringify(changeset.asJXON())
});
},
// Load multiple users in chunks
// (note: callback may be called multiple times)
+24
View File
@@ -79,6 +79,7 @@ describe('iD.actionMergeRemoteChanges', function () {
var result = action(localGraph);
expect(result).to.eql(localGraph);
expect(action.conflicts()).toHaveLength(1);
});
it('doesn\'t merge tags if conflict (local change, remote delete)', function () {
@@ -92,6 +93,7 @@ describe('iD.actionMergeRemoteChanges', function () {
result = action(localGraph);
expect(result).to.eql(localGraph);
expect(action.conflicts()).toHaveLength(1);
});
it('doesn\'t merge tags if conflict (local delete, remote change)', function () {
@@ -105,6 +107,7 @@ describe('iD.actionMergeRemoteChanges', function () {
result = action(localGraph);
expect(result).to.eql(localGraph);
expect(action.conflicts()).toHaveLength(1);
});
it('doesn\'t merge tags if conflict (local add, remote add)', function () {
@@ -118,6 +121,7 @@ describe('iD.actionMergeRemoteChanges', function () {
result = action(localGraph);
expect(result).to.eql(localGraph);
expect(action.conflicts()).toHaveLength(1);
});
it('merges tags if no conflict (remote delete)', function () {
@@ -133,6 +137,7 @@ describe('iD.actionMergeRemoteChanges', function () {
expect(result.entity('a').version).to.eql('2');
expect(result.entity('a').tags).to.eql(mergedTags);
expect(action.conflicts()).toHaveLength(0);
});
it('merges tags if no conflict (local delete)', function () {
@@ -148,6 +153,7 @@ describe('iD.actionMergeRemoteChanges', function () {
expect(result.entity('a').version).to.eql('2');
expect(result.entity('a').tags).to.eql(mergedTags);
expect(action.conflicts()).toHaveLength(0);
});
});
@@ -184,6 +190,7 @@ describe('iD.actionMergeRemoteChanges', function () {
expect(result.entity('a').version).to.eql('2');
expect(result.entity('a').tags).to.eql(mergedTags);
expect(result.entity('a').loc).to.eql([2, 2]);
expect(action.conflicts()).toHaveLength(0);
});
});
@@ -202,6 +209,7 @@ describe('iD.actionMergeRemoteChanges', function () {
expect(result.entity('w1').version).to.eql('2');
expect(result.entity('w1').tags).to.eql(mergedTags);
expect(action.conflicts()).toHaveLength(0);
});
it('merges ways if nodelist changed only remotely', function () {
@@ -222,6 +230,7 @@ describe('iD.actionMergeRemoteChanges', function () {
expect(result.entity('w1').nodes).to.eql(remoteNodes);
expect(result.hasEntity('r2')).to.eql(r2);
expect(result.hasEntity('r3')).to.eql(r3);
expect(action.conflicts()).toHaveLength(0);
});
it('merges ways if nodelist changed only locally', function () {
@@ -240,6 +249,7 @@ describe('iD.actionMergeRemoteChanges', function () {
expect(result.entity('w1').version).to.eql('2');
expect(result.entity('w1').tags).to.eql(mergedTags);
expect(result.entity('w1').nodes).to.eql(localNodes);
expect(action.conflicts()).toHaveLength(0);
});
it('merges ways if nodelist changes don\'t overlap', function () {
@@ -261,6 +271,7 @@ describe('iD.actionMergeRemoteChanges', function () {
expect(result.entity('w1').nodes).to.eql(mergedNodes);
expect(result.hasEntity('r3')).to.eql(r3);
expect(result.hasEntity('r4')).to.eql(r4);
expect(action.conflicts()).toHaveLength(0);
});
it('doesn\'t merge ways if nodelist changes overlap', function () {
@@ -276,6 +287,7 @@ describe('iD.actionMergeRemoteChanges', function () {
result = action(localGraph);
expect(result).to.eql(localGraph);
expect(action.conflicts()).toHaveLength(1);
});
it('merges ways if childNode location is same', function () {
@@ -290,6 +302,7 @@ describe('iD.actionMergeRemoteChanges', function () {
expect(result.entity('p1').version).to.eql('2');
expect(result.entity('p1').loc).to.eql(remoteLoc);
expect(action.conflicts()).toHaveLength(0);
});
it('doesn\'t merge ways if childNode location is different', function () {
@@ -303,6 +316,7 @@ describe('iD.actionMergeRemoteChanges', function () {
result = action(localGraph);
expect(result).to.eql(localGraph);
expect(action.conflicts()).toHaveLength(1);
});
});
@@ -321,6 +335,7 @@ describe('iD.actionMergeRemoteChanges', function () {
result = action(localGraph);
expect(result).to.eql(localGraph);
expect(action.conflicts()).toHaveLength(1);
});
it('merges relations if members are same and changed tags don\'t conflict', function () {
@@ -338,6 +353,7 @@ describe('iD.actionMergeRemoteChanges', function () {
expect(result.entity('r').version).to.eql('2');
expect(result.entity('r').tags).to.eql(mergedTags);
expect(action.conflicts()).toHaveLength(0);
});
});
@@ -377,6 +393,7 @@ describe('iD.actionMergeRemoteChanges', function () {
expect(result.entity('a').version).to.eql('2');
expect(result.entity('a').tags).to.eql(localTags);
expect(result.entity('a').loc).to.eql(localLoc);
expect(action.conflicts()).toHaveLength(0);
});
it('merges nodes with \'force_remote\' option', function () {
@@ -394,6 +411,7 @@ describe('iD.actionMergeRemoteChanges', function () {
expect(result.entity('a').version).to.eql('2');
expect(result.entity('a').tags).to.eql(remoteTags);
expect(result.entity('a').loc).to.eql(remoteLoc);
expect(action.conflicts()).toHaveLength(0);
});
});
@@ -414,6 +432,7 @@ describe('iD.actionMergeRemoteChanges', function () {
expect(result.entity('w1').version).to.eql('2');
expect(result.entity('w1').tags).to.eql(localTags);
expect(result.entity('w1').nodes).to.eql(localNodes);
expect(action.conflicts()).toHaveLength(0);
});
it('merges ways with \'force_remote\' option', function () {
@@ -433,6 +452,7 @@ describe('iD.actionMergeRemoteChanges', function () {
expect(result.entity('w1').nodes).to.eql(remoteNodes);
expect(result.hasEntity('r3')).to.eql(r3);
expect(result.hasEntity('r4')).to.eql(r4);
expect(action.conflicts()).toHaveLength(0);
});
it('merges way childNodes with \'force_local\' option', function () {
@@ -447,6 +467,7 @@ describe('iD.actionMergeRemoteChanges', function () {
expect(result.entity('p1').version).to.eql('2');
expect(result.entity('p1').loc).to.eql(localLoc);
expect(action.conflicts()).toHaveLength(0);
});
it('merges way childNodes with \'force_remote\' option', function () {
@@ -477,6 +498,7 @@ describe('iD.actionMergeRemoteChanges', function () {
expect(result.entity('w1').nodes).to.eql(remoteNodes);
expect(result.hasEntity('r1')).to.eql(localr1);
expect(result.hasEntity('r2')).to.be.not.ok;
expect(action.conflicts()).toHaveLength(0);
});
});
@@ -497,6 +519,7 @@ describe('iD.actionMergeRemoteChanges', function () {
expect(result.entity('r').version).to.eql('2');
expect(result.entity('r').tags).to.eql(localTags);
expect(result.entity('r').members).to.eql(localMembers);
expect(action.conflicts()).toHaveLength(0);
});
it('merges relations with \'force_remote\' option', function () {
@@ -514,6 +537,7 @@ describe('iD.actionMergeRemoteChanges', function () {
expect(result.entity('r').version).to.eql('2');
expect(result.entity('r').tags).to.eql(remoteTags);
expect(result.entity('r').members).to.eql(remoteMembers);
expect(action.conflicts()).toHaveLength(0);
});
});
});