From b0b5404f6d62d9799193c959344f955507228a8c Mon Sep 17 00:00:00 2001 From: Alec Dorrington Date: Mon, 26 Oct 2026 15:11:08 +0100 Subject: [PATCH 1/2] [sync] Deleted a file whose last change came from the source, however it first arrived. Co-Authored-By: Claude Opus 5.5 --- README.md | 10 +++++++--- scripts/merge-files.sh | 33 ++++++++++++++++----------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index bdc6e2c..aa3aef6 100644 --- a/README.md +++ b/README.md @@ -209,11 +209,15 @@ Defaults to `true`. ``` As the target may have files of its own which happen to share a name with one deleted from the source, -a file is only deleted if the history of both repositories shows that it's a copy of the one which was deleted. +a file is only deleted if the history of both repositories shows that its last change in the target came from the source: +that it's still a version which the source once had, and was last changed before the source deleted it. In particular, a file is kept if: -- **It didn't originate from the source.** For example, the target had its own version of the file, which was later overwritten by the source. -- **It was re-added after the deletion.** For example, the deletion was synced, but the target later restored the file or created a new one with the same name. - **It was modified in the target.** That is, it doesn't match any version which the source ever had. +- **It was changed or re-added after the deletion.** For example, the deletion was synced, but the target later restored the file or created a new one with the same name. + +Only the file's last change counts, not how it first arrived. +So a file which the target had first, but which the source then overwrote, is the source's from then on. +Likewise for one which another source first added, before the current one took it over, as when a repository is moved beneath a new parent. Any file which is kept for one of these reasons is reported in the workflow logs. A renamed file is treated as a deletion of the old name, plus an addition of the new one. diff --git a/scripts/merge-files.sh b/scripts/merge-files.sh index b4f4da7..9057a3c 100644 --- a/scripts/merge-files.sh +++ b/scripts/merge-files.sh @@ -101,13 +101,14 @@ done # 2. Delete from the target repository all files which were synced from the source repository, # but which have since been deleted from the source repository. # Files in the ignore list or outside of SOURCE_ROOT are skipped. -# As the target may have files of its own with the same names, a file is only deleted if: -# a) It originated from the source. -# i.e. When it was last added to the target, it matched a version which the source once had. -# b) It hasn't been re-added to the target since being deleted from the source. -# i.e. It was last added to the target before it was deleted from the source. -# c) It hasn't been modified in the target. +# As the target may have files of its own with the same names, a file is only deleted if +# its last change in the target came from the source, before the source deleted it: +# a) It hasn't been modified in the target. # i.e. It currently matches a version which the source once had. +# b) It hasn't been changed or re-added in the target since being deleted from the source. +# i.e. It was last changed in the target before it was deleted from the source. +# So a file which the target had first, but which the source then overwrote, is the source's +# from then on, as is one which an earlier source first added: only its last change counts. # ================================================================================================= if [[ "$SYNC_DELETIONS" != "true" ]]; then @@ -147,21 +148,19 @@ for file in "${!DELETED_AT[@]}"; do dest=$(target_relative_path "$file") current=$(git -C target rev-parse -q --verify "HEAD:$dest") || continue - # Find when the file was last added to the target, and with which version. - added_at="" added_blob="" + # Find when the file was last changed in the target, whether added or modified. + changed_at="" while IFS= read -r -d '' timestamp && IFS= read -r -d '' blob && IFS= read -r -d '' status && IFS= read -r -d '' path; do if [[ "$path" == "$dest" ]]; then - added_at="$timestamp" added_blob="$blob" + changed_at="$timestamp" break fi - done < <(file_history target --diff-filter=A -- ":(literal)$dest") - - if [[ -z "$added_at" || -z "${SOURCE_VERSIONS["$added_blob:$file"]+_}" ]]; then - echo "Kept: $file (didn't originate from the source)" - elif (( added_at >= deleted_at )); then - echo "Kept: $file (re-added after being deleted from the source)" - elif [[ -z "${SOURCE_VERSIONS["$current:$file"]+_}" ]]; then - echo "Kept: $file (modified after being synced from the source)" + done < <(file_history target --diff-filter=AM -- ":(literal)$dest") + + if [[ -z "${SOURCE_VERSIONS["$current:$file"]+_}" ]]; then + echo "Kept: $file (doesn't match any version which the source had)" + elif [[ -z "$changed_at" ]] || (( changed_at >= deleted_at )); then + echo "Kept: $file (changed or re-added after being deleted from the source)" else rm "target/$dest" echo "Deleted: $file" From 36d4d80858db2263108092ffe57acca4ddaa300c Mon Sep 17 00:00:00 2001 From: Alec Dorrington Date: Mon, 26 Oct 2026 15:21:58 +0100 Subject: [PATCH 2/2] [sync] Read when a file last changed in the target with one git log. Co-Authored-By: Claude Opus 5.5 --- scripts/merge-files.sh | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/scripts/merge-files.sh b/scripts/merge-files.sh index 9057a3c..815de21 100644 --- a/scripts/merge-files.sh +++ b/scripts/merge-files.sh @@ -148,14 +148,8 @@ for file in "${!DELETED_AT[@]}"; do dest=$(target_relative_path "$file") current=$(git -C target rev-parse -q --verify "HEAD:$dest") || continue - # Find when the file was last changed in the target, whether added or modified. - changed_at="" - while IFS= read -r -d '' timestamp && IFS= read -r -d '' blob && IFS= read -r -d '' status && IFS= read -r -d '' path; do - if [[ "$path" == "$dest" ]]; then - changed_at="$timestamp" - break - fi - done < <(file_history target --diff-filter=AM -- ":(literal)$dest") + # When the file was last changed in the target, dated by when the change landed on the branch. + changed_at=$(git -C target log -1 --first-parent --format=%ct -- ":(literal)$dest") if [[ -z "${SOURCE_VERSIONS["$current:$file"]+_}" ]]; then echo "Kept: $file (doesn't match any version which the source had)"