tg-annihilate.sh: autostash and support --stash and --no-stash
[topgit/pro.git] / tg--index-merge-one-file.sh
blob7937e7766851e674ff1aff71d44eb7ee8252d428
1 #!/bin/sh
3 # tg--index-merge-one-file -- auto merge a file without touching working tree
4 # Copyright (C) 2017 Kyle J. McKay
5 # All rights reserved.
6 # License GPLv2+
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
15 # $6 => stage 2 mode
16 # $7 => stage 3 mode
18 nullsha="0000000000000000000000000000000000000000"
19 tab=' '
21 if [ "$1" = "-h" ] && [ $# -eq 1 ]; then
22 echo "\
23 usage: ${tgname:-tg} index-merge-one-file [--<mergetop>] <s1_hash> <s2_hash> <s3_hash> <path> <s1_mode> <s2_mode> <s3_mode>"
24 exit 0
26 if [ -z "$tg_index_mergetop_behavior" ]; then case "$1" in
27 --merge|--theirs|--remove|--ours)
28 tg_index_mergetop_behavior="${1#--}"
29 shift
30 esac; fi
32 [ $# -eq 7 ] || exit 1
34 # The read-tree --aggressive option handles three cases that may end up in
35 # here:
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
46 # in here as well.
48 newhash=
49 newmode="${6:-0}"
51 if [ "$tg_index_mergetop_behavior" != "merge" ] &&
52 { [ "$4" = ".topdeps" ] || [ "$4" = ".topmsg" ]; }
53 then
54 # Handle --ours, --theirs and --remove for .topdeps and .topmsg
56 if [ "$tg_index_mergetop_behavior" = "remove" ]; then
57 newhash="$nullsha"
58 elif [ "$tg_index_mergetop_behavior" = "theirs" ]; then
59 newhash="${3:-$nullsha}"
60 newmode="${7:-0}"
61 else
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
71 newhash="$nullsha"
72 newmode="0"
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 129' HUP
108 trap 'exit 130' INT
109 trap 'exit 131' QUIT
110 trap 'exit 134' ABRT
111 trap 'exit 141' PIPE
112 trap 'exit 143' TERM
113 git cat-file blob "$1" >"$basef" 2>/dev/null || exit 1
114 git cat-file blob "$2" >"$oursf" 2>/dev/null || exit 1
115 git cat-file blob "$3" >"$thrsf" 2>/dev/null || exit 1
116 git merge-file --quiet "$oursf" "$basef" "$thrsf" >/dev/null 2>&1 || exit 1
117 printf '%s\n' "Auto-merging $4"
118 newhash="$(git hash-object -w --stdin <"$oursf" 2>/dev/null)" || exit 1
121 [ -n "$newhash" ] && [ -n "$newmode" ] || exit 1
122 [ "$newhash" != "$nullsha" ] || newmode="0"
123 git update-index --index-info >/dev/null 2>&1 <<EOT
124 0 $nullsha$tab$4
125 $newmode $newhash$tab$4