Re: treating some directories atomically, and ignoring socket files
Julian Squires <[email protected]>
| Newsgroups | gmane.network.unison.devel |
|---|---|
| Message-ID | <CAEbLoBKobC2P=XBfjdOPj4r9=A4uAt6fu+YdStfPBYuDweM8BA@mail.gmail.com> |
Thanks for the feedback, everyone. I like the idea of the "up" action in the text UI, but implementing it right now was outside the scope of what I was working on. I've attached patches that implement an atomic option, so you can specify, e.g., "-atomic Name\ .git -atomic Name\ .svn", and these directories will be treated indivisibly. Presumably one can use the pattern matching facilities of this kind of predicate to match packages on OS X but I haven't investigated it. I have no doubt there are improvements that can be made to how I have implemented this, so I welcome your comments. -- Julian Squires _______________________________________________ Unison-hackers mailing list [email protected] http://lists.seas.upenn.edu/mailman/listinfo/unison-hackers
0001-Add-atomic-option-as-a-global-predicate.patch
(text/x-diff, 1.4 KB)
From 58a04f261b1dc2a2362d8dd859c5f952fd3f8ba9 Mon Sep 17 00:00:00 2001 From: Julian Squires <[email protected]> Date: Tue, 13 Jan 2015 07:44:42 -0500 Subject: [PATCH 1/3] Add atomic option as a global predicate --- src/globals.ml | 6 +++++- src/globals.mli | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/globals.ml b/src/globals.ml index a601e49..382c2e4 100644 --- a/src/globals.ml +++ b/src/globals.ml @@ -258,7 +258,11 @@ let ignorenotPred = and then using {\\tt ignorenot} to select particular paths to be synchronized will not work. Instead, you should use the {\\tt path} preference to choose particular paths to synchronize.") - + +let atomic = Pred.create "atomic" ~advanced:true + ("This preference specifies paths for directories whose \ + contents will be considered as a group rather than individually.") + let shouldIgnore p = let p = Path.toString p in (Pred.test ignorePred p) && not (Pred.test ignorenotPred p) diff --git a/src/globals.mli b/src/globals.mli index f542bf5..b7e7f73 100644 --- a/src/globals.mli +++ b/src/globals.mli @@ -80,6 +80,7 @@ val shouldIgnore : 'a Path.path -> bool val shouldMerge : 'a Path.path -> bool val ignorePred : Pred.t val ignorenotPred : Pred.t +val atomic : Pred.t (* Be careful calling this to add new patterns to be ignored: Its value does NOT persist when a new profile is loaded, so it has to -- 2.1.3
0002-Add-acceptance-test-for-git-directory-atomicity.patch
(text/x-diff, 2.5 KB)
From f9e2a32290ad94a5e37e490f1a49ca327cdfadea Mon Sep 17 00:00:00 2001 From: Julian Squires <[email protected]> Date: Thu, 18 Dec 2014 18:15:16 -0500 Subject: [PATCH 2/3] Add acceptance test for git directory atomicity The client would like the contents of .git directories to be copied as a whole, not merged. --- src/test.ml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/test.ml b/src/test.ml index 2f02f5b..453cace 100644 --- a/src/test.ml +++ b/src/test.ml @@ -322,6 +322,40 @@ let test() = running fast enough that the whole thing happens within a second, then the update will be missed! *) + (* Test that .git is treated atomically. *) + runtest "Atomicity of certain directories 1" ["atomic = Name .git"; + "force = newer"] (fun() -> + let orig = (Dir ["foo", Dir [".git", Dir ["a", File "foo"; + "b", File "bar"; + "c", File "baz"]]]) in + put R1 orig; put R2 orig; sync(); + let expected = (Dir ["foo", Dir [".git", Dir ["a", File "modified on R1"; + "b", File "bar"; + "c", File "modified on R1"]]]) in + put R2 (Dir ["foo", Dir [".git", + Dir ["a", File "foo"; + "b", File "modified on R2"; + "c", File "modified on R2"]]]); + put R1 expected; + sync (); + check "1" R2 expected; + check "2" R1 expected + ); + + runtest "Atomicity of certain directories 2" ["atomic = Name .git"] (fun() -> + let a = (Dir ["foo", Dir [".git", Dir ["a", File "foo"; + "b", File "bar"; + "c", File "baz"; + "d", File "quux"]]]) in + let b = (Dir ["foo", Dir [".git", Dir ["a", File "foo"; + "b", File "bar"; + "c", File "baz"; + "e", File "quux"]]]) in + put R1 a; put R2 b; sync(); + check "1" R1 a; + check "2" R2 b + ); + (* Check for the bug reported by Ralf Lehmann *) if not bothRootsLocal then runtest "backups 1 (remote)" ["backup = Name *"] (fun() -> -- 2.1.3
0003-Add-handling-of-atomic-directories.patch
(text/x-diff, 4.6 KB)
From 51daee4d2577bbb2a3a76a78948f0f9890c277a7 Mon Sep 17 00:00:00 2001 From: Julian Squires <[email protected]> Date: Wed, 14 Jan 2015 08:18:00 -0500 Subject: [PATCH 3/3] Add handling of atomic directories If there have been any updates of a directory we consider atomic, we mark it in conflict so that it will be dealt with as a single recon item encapsulating all the necessary update items. --- src/recon.ml | 76 +++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 31 deletions(-) diff --git a/src/recon.ml b/src/recon.ml index e4f42bb..519d8e2 100644 --- a/src/recon.ml +++ b/src/recon.ml @@ -580,39 +580,53 @@ let rec reconcile (add_equal counter equals (Absent, Absent), unequals) | (Updates (Dir (desc1, children1, propsChanged1, _) as uc1, prevState1), Updates (Dir (desc2, children2, propsChanged2, _) as uc2, prevState2)) -> - (* See if the directory itself should have a reconItem *) - let dirResult = - if propsChanged1 = PropsSame && propsChanged2 = PropsSame then - (equals, unequals) - else if Props.similar desc1 desc2 then - let uc1 = Dir (desc1, [], PropsSame, false) in - let uc2 = Dir (desc2, [], PropsSame, false) in - (add_equal counter equals (uc1, uc2), unequals) - else - let action = - if propsChanged1 = PropsSame then Replica2ToReplica1 - else if propsChanged2 = PropsSame then Replica1ToReplica2 - else Conflict "properties changed on both sides" in - (equals, - Tree.add unequals - (Different - {rc1 = update2replicaContent path false ui1 [] uc1 `DIRECTORY; - rc2 = update2replicaContent path false ui2 [] uc2 `DIRECTORY; + let is_atomic_case = match path |> Path.finalName with + | Some n -> Pred.test Globals.atomic (Name.toString n) + | None -> false + in + if is_atomic_case then + let action = Conflict "atomic directory" in + (equals, + Tree.add unequals + (Different + {rc1 = update2replicaContent path true ui1 [] uc1 `DIRECTORY; + rc2 = update2replicaContent path true ui2 [] uc2 `DIRECTORY; direction = action; default_direction = action; errors1 = []; errors2 = []})) - in - (* Apply reconcile on children. *) - Safelist.fold_left - (fun (equals, unequals) (name1,ui1,name2,ui2) -> - let (eq, uneq) = - reconcile - allowPartial (Path.child path name1) ui1 [] ui2 [] counter - (Tree.enter equals (name1, name2)) - (Tree.enter unequals (name1, name2)) - in - (Tree.leave eq, Tree.leave uneq)) - dirResult - (combineChildren children1 children2) + else + (* See if the directory itself should have a reconItem *) + let dirResult = + if propsChanged1 = PropsSame && propsChanged2 = PropsSame then + (equals, unequals) + else if Props.similar desc1 desc2 then + let uc1 = Dir (desc1, [], PropsSame, false) in + let uc2 = Dir (desc2, [], PropsSame, false) in + (add_equal counter equals (uc1, uc2), unequals) + else + let action = + if propsChanged1 = PropsSame then Replica2ToReplica1 + else if propsChanged2 = PropsSame then Replica1ToReplica2 + else Conflict "properties changed on both sides" in + (equals, + Tree.add unequals + (Different + {rc1 = update2replicaContent path false ui1 [] uc1 `DIRECTORY; + rc2 = update2replicaContent path false ui2 [] uc2 `DIRECTORY; + direction = action; default_direction = action; + errors1 = []; errors2 = []})) + in + (* Apply reconcile on children. *) + Safelist.fold_left + (fun (equals, unequals) (name1,ui1,name2,ui2) -> + let (eq, uneq) = + reconcile + allowPartial (Path.child path name1) ui1 [] ui2 [] counter + (Tree.enter equals (name1, name2)) + (Tree.enter unequals (name1, name2)) + in + (Tree.leave eq, Tree.leave uneq)) + dirResult + (combineChildren children1 children2) | (Updates (File (desc1,contentsChanged1) as uc1, prev), Updates (File (desc2,contentsChanged2) as uc2, _)) -> begin match contentsChanged1, contentsChanged2 with -- 2.1.3