cogito: understand permissions written as "100755"
[cogito.git] / cg-Xmergefile
blob1219d1f7d4d5151af890d7f0bb8674ecdabd3651
1 #!/usr/bin/env bash
3 # Merge two revisions of a file
4 # Copyright (c) Linus Torvalds, 2005
5 # Copyright (c) Petr Baudis, 2005, 2006
7 # This is the git per-file merge script, called with
9 # $1 - original file SHA1 (or empty)
10 # $2 - file in branch1 SHA1 (or empty)
11 # $3 - file in branch2 SHA1 (or empty)
12 # $4 - pathname in repository
13 # $5 - original file mode (or empty)
14 # $6 - file in branch1 mode (or empty)
15 # $7 - file in branch2 mode (or empty)
17 # We are designed to merge $3 _to_ $2, so we will give it
18 # a preference.
20 # FIXME: Make at least some effort to protect against checking in
21 # the conflicts.
23 # FIXME: What if a symlink replaces a regular file? What if symlinks conflict?
24 # What about directory-regularfile conflicts?
26 # ENVIRONMENT
27 # -----------
28 # _CG_MERGING_LOCAL::
29 # If set, we are merging local changes, not "real" three trees.
31 . "${COGITO_LIB}"cg-Xlib || exit 1
33 id0="${ARGS[0]}"
34 id1="${ARGS[1]}"
35 id2="${ARGS[2]}"
36 file="${ARGS[3]}"
37 mode0="${ARGS[4]}"
38 mode1="${ARGS[5]}"
39 mode2="${ARGS[6]}"
41 error()
43 echo "MERGE ERROR: $@" >&2
44 return 1
47 warning()
49 echo "MERGE WARNING: $@" >&2
52 # Returns stuff in $_pmxmc
53 # PlusMinus+XModeChar ;-)
54 pmxmc()
56 case "$1" in
57 *755)
58 _pmxmc=+;;
60 _pmxmc=-;;
61 esac
64 # Set ways[] to the symbolic names of the particular ways
65 load_unescaped_sym_ways()
67 if [ "$_CG_MERGING_LOCAL" ]; then
68 ways=("original" "local" "target")
69 else
70 ways=("merge base" "$_git_head" "$(cat "$_git/cg-merge-state/merging-sym")")
73 load_sym_ways()
75 load_unescaped_sym_ways
76 ways[0]="${ways[0]// //}"
77 ways=("${ways[@]//\//~}")
81 case "${id0:-.}${id1:-.}${id2:-.}" in
83 # Deleted in both or deleted in one and unchanged in the other
85 "$id0.." | "$id0.$id0" | "$id0$id0.")
86 #echo "Removing $file"
87 if test -f "$file"; then
88 rm -f -- "$file"
89 fi &&
90 exec git-update-index --remove -- "$file"
94 # Deleted in one and changed in the other
96 "$id0$id1." | "$id0.$id2")
97 #echo "Removing $file"
98 filev="$file"
99 load_sym_ways
100 if [ "$id1" ]; then
101 num=1; mode="$mode1"; id="$id1"
102 else
103 num=2; mode="$mode2"; id="$id2"
105 while [ -e "$filev~${ways[0]}" ] || [ -e "$filev~${ways[$num]}" ]; do
106 filev="$filev~"
107 done
108 error "File $file removed in one branch but modified in the other!"
109 error "The original version saved as '$filev~${ways[0]}', the modified one as '$filev~${ways[$num]}'."
110 git-update-index --add --cacheinfo "$mode0" "$id0" "$file" &&
111 git-checkout-index -u -f -- "$file" &&
112 mv "$file" "$filev~${ways[0]}" ||
113 error "Cannot create '$filev~${ways[0]}'"
114 git-update-index --add --cacheinfo "$mode" "$id" "$file" &&
115 git-checkout-index -u -f -- "$file" &&
116 mv "$file" "$filev~${ways[$num]}" ||
117 error "Cannot create '$filev~${ways[$num]}'"
118 git-update-index --force-remove "$file"
121 # Added in one.
123 ".$id1." | "..$id2" )
124 #echo "Adding $file"
125 git-update-index --add --cacheinfo "$mode1$mode2" "$id1$id2" "$file" &&
126 exec git-checkout-index -u -f -- "$file"
130 # Added same in both (check for same permissions).
132 ".$id2$id1")
133 #echo "Adding $file"
134 git-update-index --add --cacheinfo "$mode1" "$id1" "$file" &&
135 git-checkout-index -u -f -- "$file"
136 ret=$?
137 if [ "$mode1" != "$mode2" ]; then
138 pmxmc "$mode2"; chmod ${_pmxmc}x "$file"
139 error "$file: added in both branches, permissions conflict $mode1->$mode2 (defaulting to $mode2)"
140 exit 1
142 exit $ret
146 # Added in both (different in each).
148 ".$id1$id2")
149 #echo "Adding $file"
150 filev="$file"
151 load_sym_ways
152 while [ -e "$filev~${ways[1]}" ] || [ -e "$filev~${ways[2]}" ]; do
153 filev="$filev~"
154 done
155 error "File $file added in both branches, but different in each!"
156 error "Conflicting versions saved as '$filev~${ways[1]}' and '$filev~${ways[2]}'."
157 git-update-index --add --cacheinfo "$mode1" "$id1" "$file" &&
158 git-checkout-index -u -f -- "$file" &&
159 mv "$file" "$filev~${ways[1]}" ||
160 error "Cannot create '$filev~${ways[1]}'"
161 git-update-index --add --cacheinfo "$mode2" "$id2" "$file" &&
162 git-checkout-index -u -f -- "$file" &&
163 mv "$file" "$filev~${ways[2]}" ||
164 error "Cannot create '$filev~${ways[2]}'"
165 exit 1
169 # Modified in both, but differently.
171 "$id0$id1$id2")
172 echo "... Auto-merging $file"
173 orig=$(git-unpack-file $id0)
174 src2=$(git-unpack-file $id2)
175 merged=$(mktemp .mergeresult-XXXXXX)
177 load_unescaped_sym_ways
178 # We reset the index to the first branch, making
179 # git-diff-file useful
180 git-update-index --add --cacheinfo "$mode1" "$id1" "$file"
181 git-checkout-index -u -f -- "$file" &&
182 { diff3 -m -E -L "${ways[1]}" -L "${ways[0]}" -L "${ways[2]}" "$file" "$orig" "$src2" >"$merged"; }
183 ret=$?
184 rm -f -- "$orig" "$src2"
185 cat "$merged" >"$file"
186 rm "$merged"
188 if [ "$mode1" != "$mode2" ]; then
189 if [ "$mode0" = "$mode1" ]; then
190 moded="$mode2"
191 else
192 moded="$mode1"
194 pmxmc "$moded"; chmod ${_pmxmc}x "$file"
195 error "Permissions conflict: $mode0->$mode1,$mode2 (defaulting to $moded)"
196 ret=1
199 if [ $ret -ne 0 ]; then
200 echo " CONFLICTS during merge." >&2
201 exit 1
203 exec git-update-index -- "$file"
207 error "$file: Not handling case: ${id0:-empty} -> ${id1:-empty} -> ${id2:-empty} (this would be a Cogito bug, please report it)"
209 esac
210 exit 1