#!/usr/bin/env bash # # Merge two revisions of a file. # Copyright (c) Linus Torvalds, 2005 # # This is the git per-file merge script, called with # # $1 - original file SHA1 (or empty) # $2 - file in branch1 SHA1 (or empty) # $3 - file in branch2 SHA1 (or empty) # $4 - pathname in repository # $5 - orignal file mode (or empty) # $6 - file in branch1 mode (or empty) # $7 - file in branch2 mode (or empty) # # We are designed to merge $3 _to_ $2, so we will give it # a preference. # # FIXME: Make at least some effort to protect against checking in # the conflicts. # # FIXME: What if a symlink replaces a regular file? What if symlinks conflict? # What about directory-regularfile conflicts? . "${COGITO_LIB}"cg-Xlib || exit 1 id0="${ARGS[0]}" id1="${ARGS[1]}" id2="${ARGS[2]}" file="${ARGS[3]}" mode0="${ARGS[4]}" mode1="${ARGS[5]}" mode2="${ARGS[6]}" error() { echo "MERGE ERROR: $@" >&2 return 1 } warning() { echo "MERGE WARNING: $@" >&2 } # Returns stuff in $_pmxmc # PlusMinus+XModeChar ;-) pmxmc() { if [ "$1" = "755" ]; then _pmxmc=+ else _pmxmc=- fi } # Set ways[] to the symbolic names of the particular ways load_unescaped_sym_ways() { ways=("merge base" "$_git_head" "$(cat "$_git/merging-sym")") } load_sym_ways() { load_unescaped_sym_ways ways[0]="merge/base" ways=("${ways[@]//\//~}") } case "${id0:-.}${id1:-.}${id2:-.}" in # # Deleted in both or deleted in one and unchanged in the other # "$id0.." | "$id0.$id0" | "$id0$id0.") #echo "Removing $file" if test -f "$file"; then rm -f -- "$file" fi && exec git-update-index --remove -- "$file" ;; # # Deleted in one and changed in the other # "$id0$id1." | "$id0.$id2") #echo "Removing $file" filev="$file" load_sym_ways if [ "$id1" ]; then num=1; mode="$mode1"; id="$id1" else num=2; mode="$mode2"; id="$id2" fi while [ -e "$filev~${ways[0]}" ] || [ -e "$filev~${ways[$num]}" ]; do filev="$filev~" done error "File $file removed in one branch but modified in the other!" error "The original version saved as '$filev~${ways[0]}', the modified one as '$filev~${ways[$num]}'." git-update-index --add --cacheinfo "$mode0" "$id0" "$file" && git-checkout-index -u -f -- "$file" && mv "$file" "$filev~${ways[0]}" || error "Cannot create '$filev~${ways[0]}'" git-update-index --add --cacheinfo "$mode" "$id" "$file" && git-checkout-index -u -f -- "$file" && mv "$file" "$filev~${ways[$num]}" || error "Cannot create '$filev~${ways[$num]}'" ;; # # Added in one. # ".$id1." | "..$id2" ) #echo "Adding $file" git-update-index --add --cacheinfo "$mode1$mode2" "$id1$id2" "$file" && exec git-checkout-index -u -f -- "$file" ;; # # Added same in both (check for same permissions). # ".$id2$id1") #echo "Adding $file" git-update-index --add --cacheinfo "$mode1" "$id1" "$file" && git-checkout-index -u -f -- "$file" ret=$? if [ "$mode1" != "$mode2" ]; then pmxmc "$mode2"; chmod ${_pmxmc}x "$file" error "$file: added in both branches, permissions conflict $mode1->$mode2 (defaulting to $mode2)" exit 1 fi exit $ret ;; # # Added in both (different in each). # ".$id1$id2") #echo "Adding $file" filev="$file" load_sym_ways while [ -e "$filev~${ways[1]}" ] || [ -e "$filev~${ways[2]}" ]; do filev="$filev~" done error "File $file added in both branches, but different in each!" error "Conflicting versions saved as '$filev~${ways[1]}' and '$filev~${ways[2]}'." git-update-index --add --cacheinfo "$mode1" "$id1" "$file" && git-checkout-index -u -f -- "$file" && mv "$file" "$filev~${ways[1]}" || error "Cannot create '$filev~${ways[1]}'" git-update-index --add --cacheinfo "$mode2" "$id2" "$file" && git-checkout-index -u -f -- "$file" && mv "$file" "$filev~${ways[2]}" || error "Cannot create '$filev~${ways[2]}'" exit 1 ;; # # Modified in both, but differently. # "$id0$id1$id2") echo "... Auto-merging $file" orig=$(git-unpack-file $id0) src2=$(git-unpack-file $id2) load_unescaped_sym_ways # We reset the index to the first branch, making # git-diff-file useful git-update-index --add --cacheinfo "$mode1" "$id1" "$file" git-checkout-index -u -f -- "$file" && merge -L "${ways[1]}" -L "${ways[0]}" -L "${ways[2]}" "$file" "$orig" "$src2" ret=$? rm -f -- "$orig" "$src2" if [ "$mode1" != "$mode2" ]; then if [ "$mode0" = "$mode1" ]; then moded="$mode2" else moded="$mode1" fi pmxmc "$moded"; chmod ${_pmxmc}x "$file" error "Permissions conflict: $mode0->$mode1,$mode2 (defaulting to $moded)" ret=1 fi if [ $ret -ne 0 ]; then # The user already gets the warning from merge itself and # from merge-cache too. This is too much. #error "Auto-merge failed" exit 1 fi exec git-update-index -- "$file" ;; *) error "$file: Not handling case: ${id0:-empty} -> ${id1:-empty} -> ${id2:-empty} (this would be a Cogito bug, please report it)" ;; esac exit 1