<commit_before>(* Unison file synchronizer: src/recon.ml *)
(* Copyright 1999-2018, <NAME>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*)


open Common

(* ------------------------------------------------------------------------- *)
(*                     Handling of prefer/force                              *)
(* ------------------------------------------------------------------------- *)
let debug = Trace.debug "recon"

let setDirection ri dir force =
  match ri.replicas with
    Different
      ({rc1 = rc1; rc2 = rc2; direction = d; default_direction = default } as diff)
          when force=`Force || isConflict default ->
      if dir=`Replica1ToReplica2 then
        diff.direction <- Replica1ToReplica2
      else if dir=`Replica2ToReplica1 then
        diff.direction <- Replica2ToReplica1
      else if dir=`Merge then begin
        if Globals.shouldMerge ri.path1 then diff.direction <- Merge
      end else begin  (* dir = `Older or dir = `Newer *)
        match rc1.status, rc2.status with
          `Deleted, _ ->
            if isConflict default then
              diff.direction <- Replica2ToReplica1
        | _, `Deleted ->
            if isConflict default then
              diff.direction <- Replica1ToReplica2
        | _ ->
            let comp = Props.time rc1.desc -. Props.time rc2.desc in
            let comp = if dir=`Newer then -. comp else comp in
            if comp<0.0 then
              diff.direction <- Replica1ToReplica2
            else
              diff.direction <- Replica2ToReplica1
      end
  | _ ->
      ()

let revertToDefaultDirection ri =
  match ri.replicas with
    Different diff -> diff.direction <- diff.default_direction
  | _              -> ()

(* Find out which direction we need to propagate changes if we want to       *)
(* consider the given root to be the "truth"                                 *)
(* --                                                                        *)
(*   root := "older" | "newer" | <one of the two roots>                      *)
(*   return value := 'Older  | 'Newer  | 'Replica1ToReplica2 |               *)
(*                   'Replica2ToReplica1                                     *)
(* --                                                                        *)
let root2direction root =
  if      root="older" then `Older
  else if root="newer" then `Newer
  else
    let (r1, r2) = Globals.rawRootPair () in
    debug (fun() ->
       Printf.eprintf "root2direction called to choose %s from %s and %s\n"
         root r1 r2);
    if r1 = root then `Replica1ToReplica2 else
    if r2 = root then `Replica2ToReplica1 else
    raise (Util.Fatal (Printf.sprintf
     "%s (given as argument to 'prefer' or 'force' preference)\nis not one of \
      the current roots:\n  %s\n  %s" root r1 r2))

let forceRoot: string Prefs.t =
  Prefs.createString "force" ""
    "!force changes from this replica to the other"
    ("Including the preference \\texttt{-force \\ARG{root}} causes Unison to "
     ^ "resolve all differences (even non-conflicting changes) in favor of "
     ^ "\\ARG{root}.  "
     ^ "This effectively changes Unison from a synchronizer into a mirroring "
     ^ "utility.  \n\n"
     ^ "You can also specify \\verb|-force newer| (or \\verb|-force older|) "
     ^ "to force Unison to choose the file with the later (earlier) "
     ^ "modtime.  In this case, the \\verb|-times| preference must also "
     ^ "be enabled.\n\n"
     ^ "This preference is overridden by the \\verb|forcepartial| preference.\n\n"
     ^ "This preference should be used only if you are {\\em sure} you "
     ^ "know what you are doing!")

let forceRootPartial: Pred.t =
  Pred.create "forcepartial" ~advanced:true
    ("Including the preference \\texttt{forcepartial = \\ARG{PATHSPEC} -> \\ARG{root}} causes Unison to "
     ^ "resolve all differences (even non-conflicting changes) in favor of "
     ^ "\\ARG{root} for the files in \\ARG{PATHSPEC} (see \\sectionref{pathspec}{Path Specification} "
     ^ "for more information).  "
     ^ "This effectively changes Unison from a synchronizer into a mirroring "
     ^ "utility.  \n\n"
     ^ "You can also specify \\verb|forcepartial PATHSPEC -> newer| "
     ^ "(or \\verb|forcepartial PATHSPEC older|) "
     ^ "to force Unison to choose the file with the later (earlier) "
     ^ "modtime.  In this case, the \\verb|-times| preference must also "
     ^ "be enabled.\n\n"
     ^ "This preference should be used only if you are {\\em sure} you "
     ^ "know what you are doing!")

let preferRoot: string Prefs.t =
  Prefs.createString "prefer" ""
    "!choose this replica's version for conflicting changes"
    ("Including the preference \\texttt{-prefer \\ARG{root}} causes Unison always to "
     ^ "resolve conflicts in favor of \\ARG{root}, rather than asking for "
     ^ "guidance from the user.  (The syntax of \\ARG{root} is the same as "
     ^ "for the \\verb|root| preference, plus the special values "
     ^ "\\verb|newer| and \\verb|older|.)  \n\n"<commit_msg>Recon: warn that the preference merge overrides prefer<commit_after>(* Unison file synchronizer: src/recon.ml *)
(* Copyright 1999-2018, <NAME>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*)


open Common

(* ------------------------------------------------------------------------- *)
(*                     Handling of prefer/force                              *)
(* ------------------------------------------------------------------------- *)
let debug = Trace.debug "recon"

let setDirection ri dir force =
  match ri.replicas with
    Different
      ({rc1 = rc1; rc2 = rc2; direction = d; default_direction = default } as diff)
          when force=`Force || isConflict default ->
      if dir=`Replica1ToReplica2 then
        diff.direction <- Replica1ToReplica2
      else if dir=`Replica2ToReplica1 then
        diff.direction <- Replica2ToReplica1
      else if dir=`Merge then begin
        if Globals.shouldMerge ri.path1 then diff.direction <- Merge
      end else begin  (* dir = `Older or dir = `Newer *)
        match rc1.status, rc2.status with
          `Deleted, _ ->
            if isConflict default then
              diff.direction <- Replica2ToReplica1
        | _, `Deleted ->
            if isConflict default then
              diff.direction <- Replica1ToReplica2
        | _ ->
            let comp = Props.time rc1.desc -. Props.time rc2.desc in
            let comp = if dir=`Newer then -. comp else comp in
            if comp<0.0 then
              diff.direction <- Replica1ToReplica2
            else
              diff.direction <- Replica2ToReplica1
      end
  | _ ->
      ()

let revertToDefaultDirection ri =
  match ri.replicas with
    Different diff -> diff.direction <- diff.default_direction
  | _              -> ()

(* Find out which direction we need to propagate changes if we want to       *)
(* consider the given root to be the "truth"                                 *)
(* --                                                                        *)
(*   root := "older" | "newer" | <one of the two roots>                      *)
(*   return value := 'Older  | 'Newer  | 'Replica1ToReplica2 |               *)
(*                   'Replica2ToReplica1                                     *)
(* --                                                                        *)
let root2direction root =
  if      root="older" then `Older
  else if root="newer" then `Newer
  else
    let (r1, r2) = Globals.rawRootPair () in
    debug (fun() ->
       Printf.eprintf "root2direction called to choose %s from %s and %s\n"
         root r1 r2);
    if r1 = root then `Replica1ToReplica2 else
    if r2 = root then `Replica2ToReplica1 else
    raise (Util.Fatal (Printf.sprintf
     "%s (given as argument to 'prefer' or 'force' preference)\nis not one of \
      the current roots:\n  %s\n  %s" root r1 r2))

let forceRoot: string Prefs.t =
  Prefs.createString "force" ""
    "!force changes from this replica to the other"
    ("Including the preference \\texttt{-force \\ARG{root}} causes Unison to "
     ^ "resolve all differences (even non-conflicting changes) in favor of "
     ^ "\\ARG{root}.  "
     ^ "This effectively changes Unison from a synchronizer into a mirroring "
     ^ "utility.  \n\n"
     ^ "You can also specify \\verb|-force newer| (or \\verb|-force older|) "
     ^ "to force Unison to choose the file with the later (earlier) "
     ^ "modtime.  In this case, the \\verb|-times| preference must also "
     ^ "be enabled.\n\n"
     ^ "This preference is overridden by the \\verb|forcepartial| preference.\n\n"
     ^ "This preference should be used only if you are {\\em sure} you "
     ^ "know what you are doing!")

let forceRootPartial: Pred.t =
  Pred.create "forcepartial" ~advanced:true
    ("Including the preference \\texttt{forcepartial = \\ARG{PATHSPEC} -> \\ARG{root}} causes Unison to "
     ^ "resolve all differences (even non-conflicting changes) in favor of "
     ^ "\\ARG{root} for the files in \\ARG{PATHSPEC} (see \\sectionref{pathspec}{Path Specification} "
     ^ "for more information).  "
     ^ "This effectively changes Unison from a synchronizer into a mirroring "
     ^ "utility.  \n\n"
     ^ "You can also specify \\verb|forcepartial PATHSPEC -> newer| "
     ^ "(or \\verb|forcepartial PATHSPEC older|) "
     ^ "to force Unison to choose the file with the later (earlier) "
     ^ "modtime.  In this case, the \\verb|-times| preference must also "
     ^ "be enabled.\n\n"
     ^ "This preference should be used only if you are {\\em sure} you "
     ^ "know what you are doing!")

let preferRoot: string Prefs.t =
  Prefs.createString "prefer" ""
    "!choose this replica's version for conflicting changes"
    ("Including the preference \\texttt{-prefer \\ARG{root}} causes Unison always to "
     ^ "resolve conflicts in favor of \\ARG{root}, rather than asking for "
     ^ "guidance from the user, except for paths marked by the preference "
     ^ "\\texttt{merge}.  (The syntax of \\ARG{root} is the same as "
     ^ "for the \\verb|root| preference, plus the special values "
     ^ "\\verb|newer| and \\verb|older|.)  \n\n"
{'fraction_non_alphanumeric': 0.12355110642781876, 'fraction_numerical': 0.009483667017913594, 'mean_word_length': 1.9790740256343187, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 14, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3654282', 'n_tokens_mistral': 3224, 'n_tokens_neox': 3150, 'n_words': 1172}