3 # tg--index-merge-one-file -- auto merge a file without touching working tree
4 # Copyright (C) 2017 Kyle J. McKay
8 # $TG_TMP_DIR => location to store temporary files
9 # $tg_index_mergetop_behavior => "" (ours), "theirs", "merge", "remove"
10 # $1 => stage 1 hash or empty
11 # $2 => stage 2 hash or empty
12 # $3 => stage 3 hash or empty
13 # $4 => full pathname in index
14 # $5 => stage 1 mode (6 octal digits) or empty
18 nullsha
="0000000000000000000000000000000000000000"
21 if [ "$1" = "-h" ] && [ $# -eq 1 ]; then
23 usage: ${tgname:-tg} index-merge-one-file [--<mergetop>] <s1_hash> <s2_hash> <s3_hash> <path> <s1_mode> <s2_mode> <s3_mode>"
26 if [ -z "$tg_index_mergetop_behavior" ]; then case "$1" in
27 --merge|
--theirs|
--remove|
--ours)
28 tg_index_mergetop_behavior
="${1#--}"
32 [ $# -eq 7 ] ||
exit 1
34 # The read-tree --aggressive option handles three cases that may end up in
37 # 1. One side removes a file but the other leaves it unchanged (remove)
38 # 2. Both sides remove a file (remove)
39 # 3. Both sides add a path identically (use it)
41 # The problem is (1). When resolving .topdeps and .topmsg files using either
42 # --ours or --theirs the normal resolution of (1) to remove may be incorrect.
44 # But that means in order to get that case to come to us all three cases must
45 # be handled properly for non-topfile files since they will therefore end up
51 if [ "$tg_index_mergetop_behavior" != "merge" ] &&
52 { [ "$4" = ".topdeps" ] ||
[ "$4" = ".topmsg" ]; }
54 # Handle --ours, --theirs and --remove for .topdeps and .topmsg
56 if [ "$tg_index_mergetop_behavior" = "remove" ]; then
58 elif [ "$tg_index_mergetop_behavior" = "theirs" ]; then
59 newhash
="${3:-$nullsha}"
62 # --ours is the default mode
63 newhash
="${2:-$nullsha}"
66 # .topmsg and .topdeps are never executable
67 [ "$newmode" != "100755" ] || newmode
="100644"
69 if [ "$newmode" != "100644" ] && [ "$newhash" != "$nullsha" ]; then
70 # .topmsg and .topdeps are only allowed to be blobs
76 if [ -z "$newhash" ]; then
77 # Check for the "--aggressive" and "--trivial" things:
79 # a) all three hashes are the same (handled same as next case)
80 # b) $2 and $3 are the same (and their modes)
81 # c) $1 and $2 are the same (and their modes)
82 # d) $1 and $3 are the same (and their modes)
84 if [ "$2" = "$3" ] && [ "$6" = "$7" ] ; then
85 newhash
="${2:-$nullsha}"
86 elif [ "$1" = "$2" ] && [ "$5" = "$6" ]; then
87 newhash
="${3:-$nullsha}" newmode
="${7:-0}"
88 elif [ "$1" = "$3" ] && [ "$5" = "$7" ]; then
89 newhash
="${2:-$nullsha}"
93 if [ -z "$newhash" ]; then
94 # We only handle auto merging existing files with the same mode
95 case "${1:-:}${2:-:}${3:-:}${4:-:}${5:-:}${6:-:}${7:-:}" in *":"*) exit 1; esac
96 [ "$5" = "$6" ] && [ "$6" = "$7" ] ||
exit 1
98 # mode must match 100\o\o\o
99 case "$6" in 100[0-7][0-7][0-7]);;*) exit 1; esac
101 # perform a "simple" 3-way merge
102 tg_tmp_dir
="${TG_TMP_DIR:-/tmp}"
103 basef
="$tg_tmp_dir/tgmerge_$$_base"
104 oursf
="$tg_tmp_dir/tgmerge_$$_ours"
105 thrsf
="$tg_tmp_dir/tgmerge_$$_thrs"
106 trap 'rm -f "$basef" "$oursf" "$thrsf"' EXIT
107 trap 'exit' HUP INT QUIT ABRT PIPE TERM
108 git cat-file blob
"$1" >"$basef" 2>/dev
/null ||
exit 1
109 git cat-file blob
"$2" >"$oursf" 2>/dev
/null ||
exit 1
110 git cat-file blob
"$3" >"$thrsf" 2>/dev
/null ||
exit 1
111 git merge-file
--quiet "$oursf" "$basef" "$thrsf" >/dev
/null
2>&1 ||
exit 1
112 printf '%s\n' "Auto-merging $4"
113 newhash
="$(git hash-object -w --stdin <"$oursf" 2>/dev/null)" ||
exit 1
116 [ -n "$newhash" ] && [ -n "$newmode" ] ||
exit 1
117 [ "$newhash" != "$nullsha" ] || newmode
="0"
118 git update-index
--index-info >/dev
/null
2>&1 <<EOT
120 $newmode $newhash$tab$4