From patchwork Mon Apr 29 07:07:31 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [1, of, 1, STABLE, V2] merge: increase safety of parallel updating/removing on icasefs From: Katsunori FUJIWARA X-Patchwork-Id: 1494 Message-Id: To: mercurial-devel@selenic.com Date: Mon, 29 Apr 2013 16:07:31 +0900 # HG changeset patch # User FUJIWARA Katsunori # Date 1367218695 -32400 # Mon Apr 29 15:58:15 2013 +0900 # Branch stable # Node ID b1dccec8c4a05fd7e8531fc8f4dcb954ab2aca78 # Parent f01a351db79106ba96ac6d527cf69944fd98e665 merge: increase safety of parallel updating/removing on icasefs "merge.applyupdates()" sorts "actions" in removal first order, and "workeractions" derived from it should be also sorted. If each actions in "workeractions" are executed in serial, this sorting ensures that merging/updating process is collision free, because updating the file in target context is always executed after removing the existing file which causes case-folding collision against the former. In the other hand, if each actions are executed in parallel, updating on a worker process may be executed before removing on another worker process, because "worker.partition()" partitions list of actions regardless of type of each actions. This patch divides "workeractions" into removing and updating, and executes the former first. This patch still scans "actions"/"workeractions" some times for ease of patch review, even though large list may cost much in this way. (total cost should be as same as before) This also changes some tests, because dividing "workeractions" affects progress indication. diff --git a/mercurial/merge.py b/mercurial/merge.py --- a/mercurial/merge.py +++ b/mercurial/merge.py @@ -455,8 +455,10 @@ numupdates = len(actions) workeractions = [a for a in actions if a[1] in 'gr'] - updated = len([a for a in workeractions if a[1] == 'g']) - removed = len([a for a in workeractions if a[1] == 'r']) + updateactions = [a for a in workeractions if a[1] == 'g'] + updated = len(updateactions) + removeactions = [a for a in workeractions if a[1] == 'r'] + removed = len(removeactions) actions = [a for a in actions if a[1] not in 'gr'] hgsub = [a[1] for a in workeractions if a[0] == '.hgsubstate'] @@ -465,7 +467,13 @@ z = 0 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite), - workeractions) + removeactions) + for i, item in prog: + z += i + repo.ui.progress(_('updating'), z, item=item, total=numupdates, + unit=_('files')) + prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite), + updateactions) for i, item in prog: z += i repo.ui.progress(_('updating'), z, item=item, total=numupdates, diff --git a/tests/test-issue672.t b/tests/test-issue672.t --- a/tests/test-issue672.t +++ b/tests/test-issue672.t @@ -37,6 +37,7 @@ 1: other deleted -> r 1a: remote created -> g removing 1 + updating: 1 1/2 files (50.00%) getting 1a updating: 1a 2/2 files (100.00%) 1 files updated, 0 files merged, 1 files removed, 0 files unresolved diff --git a/tests/test-rename-dir-merge.t b/tests/test-rename-dir-merge.t --- a/tests/test-rename-dir-merge.t +++ b/tests/test-rename-dir-merge.t @@ -46,6 +46,7 @@ b/b: remote created -> g removing a/a removing a/b + updating: a/b 2/5 files (40.00%) getting b/a getting b/b updating: b/b 4/5 files (80.00%) diff --git a/tests/test-rename-merge2.t b/tests/test-rename-merge2.t --- a/tests/test-rename-merge2.t +++ b/tests/test-rename-merge2.t @@ -290,6 +290,7 @@ rev: versions differ -> m preserving rev for resolve of rev removing a + updating: a 1/3 files (33.33%) getting b updating: b 2/3 files (66.67%) updating: rev 3/3 files (100.00%) diff --git a/tests/test-update-reverse.t b/tests/test-update-reverse.t --- a/tests/test-update-reverse.t +++ b/tests/test-update-reverse.t @@ -73,6 +73,7 @@ main: remote created -> g removing side1 removing side2 + updating: side2 2/3 files (66.67%) getting main updating: main 3/3 files (100.00%) 1 files updated, 0 files merged, 2 files removed, 0 files unresolved