2 # TopGit merging utility functions
3 # Copyright (C) 2015,2016,2017,2018,2019,2021 Kyle J. McKay <mackyle@gmail.com>
7 # git_topmerge will need this even on success and since it might otherwise
8 # be called many times do it just the once here and now
10 v_get_show_toplevel repotoplvl
12 # If HEAD is a symref to "$1" detach it at its current value
13 detach_symref_head_on_branch
() {
14 _hsr
="$(git symbolic-ref -q HEAD --)" && [ -n "$_hsr" ] ||
return 0
15 _hrv
="$(git rev-parse --quiet --verify HEAD --)" && [ -n "$_hrv" ] ||
16 die
"cannot detach_symref_head_on_branch from unborn branch $_hsr"
17 git update-ref
--no-deref -m "detaching HEAD from $_hsr to safely update it" HEAD
"$_hrv"
20 # Run an in-tree recursive merge but make sure we get the desired version of
21 # any .topdeps and .topmsg files. The $auhopt and --no-stat options are
22 # always implicitly in effect. If successful, a new commit is performed on HEAD
23 # unless the optional --no-commit option has been given.
25 # The "git merge-recursive" tool (and others) must be run to get the desired
26 # result. And --no-ff is always implicitly in effect as well.
28 # NOTE: [optional] arguments MUST appear in the order shown
29 # [optional] '-v' varname => optional variable to return original HEAD hash in
30 # [optional] '--no-commit' => update worktree and index but do not commit
31 # [optional] '--merge', '--theirs' or '--remove' to alter .topfile handling
32 # [optional] '--name' <name-for-ours> [--name <name-for-theirs>]
33 # $1 => '-m' MUST be '-m'
34 # $2 => commit message
35 # $3 => commit-ish to merge as "theirs"
39 [ "$1" != "-v" ] ||
[ $# -lt 2 ] ||
[ -z "$2" ] ||
{ _ovar
="$2"; shift 2; }
41 [ "$1" != "--no-commit" ] ||
{ _ncmode
=1; shift; }
43 case "$1" in --theirs|
--remove|
--merge) _mmode
="${1#--}"; shift; esac
46 if [ "$1" = "--name" ] && [ $# -ge 2 ]; then
49 if [ "$1" = "--name" ] && [ $# -ge 2 ]; then
54 : "${_nameours:=HEAD}"
55 [ "$#" -eq 3 ] && [ "$1" = "-m" ] && [ -n "$2" ] && [ -n "$3" ] ||
56 die
"programmer error: invalid arguments to git_topmerge: $*"
57 _ours
="$(git rev-parse --verify HEAD^0)" || die
"git rev-parse failed"
58 _theirs
="$(git rev-parse --verify "$3^
0")" || die
"git rev-parse failed"
59 [ -z "$_ovar" ] ||
eval "$_ovar="'"$_ours"'
60 eval "GITHEAD_$_ours="'"$_nameours"' && eval export "GITHEAD_$_ours"
61 if [ -n "$_nametheirs" ]; then
62 eval "GITHEAD_$_theirs="'"$_nametheirs"' && eval export "GITHEAD_$_theirs"
65 if [ "$_mmode" = "merge" ]; then
66 TG_L1
="$_nameours" && export TG_L1
67 TG_L2
="merged common ancestors" && export TG_L2
68 TG_L3
="${_nametheirs:-$3}" && export TG_L3
69 _mdriver
='git merge-file -L "$TG_L1" -L "$TG_L2" -L "$TG_L3" --marker-size=%L %A %O %B'
73 _mb
="$(git merge-base --all "$_ours" "$_theirs")" && [ -n "$_mb" ] ||
74 { _mt
=1; _mb
="$(git mktree < /dev/null)"; }
75 # any .topdeps or .topmsg output needs to be stripped from stdout
76 tmpstdout
="$tg_tmp_dir/stdout.$$"
78 git
-c "merge.ours.driver=$_mdriver" merge-recursive \
79 $_mb -- "$_ours" "$_theirs" >"$tmpstdout" || _ret
=$?
80 # success or failure is not relevant until after fixing up the
81 # .topdeps and .topmsg files and running rerere unless _ret >= 126
82 [ $_ret -lt 126 ] ||
return $_ret
83 if [ "$_mmode" = "merge" ]; then
87 theirs
) _source
="$_theirs";;
93 _newinfo
="$(git cat-file --batch-check="%(objecttype
) %(objectname
)$tab%(rest
)" <<-EOT |
94 $_source:.topdeps .topdeps
95 $_source:.topmsg .topmsg
97 sed -n 's/^blob /100644 /p'
99 [ -z "$_newinfo" ] || _newinfo
="$lf$_newinfo"
100 git update-index
--index-info <<-EOT ||
101 0 $nullsha$tab.topdeps
102 0 $nullsha$tab.topmsg$_newinfo
104 die
"git update-index failed"
105 if [ "$_mmode" = "remove" ] &&
106 { [ -e "$repotoplvl/.topdeps" ] ||
[ -e "$repotoplvl/.topmsg" ]; }
108 rm -r -f "$repotoplvl/.topdeps" "$repotoplvl/.topmsg" >/dev
/null
2>&1 ||
:
110 for zapbad
in "$repotoplvl/.topdeps" "$repotoplvl/.topmsg"; do
111 if [ -e "$zapbad" ] && { [ -L "$zapbad" ] ||
[ ! -f "$zapbad" ]; }; then
115 # Since Git v2.30.1, even with "-q" checkout-index can spuriously fail!
116 # It must only be called with the names of files actually in the index to avoid that.
117 idxtopfiles
="$(git ls-files --full-name -- :/.topdeps :/.topmsg)" ||
:
118 [ -z "$idxtopfiles" ] ||
119 (cd "$repotoplvl" && git checkout-index
-q -f -u -- $idxtopfiles) ||
120 die
"git checkout-index failed"
122 # dump output without any .topdeps or .topmsg messages
123 sed -e '/ \.topdeps/d' -e '/ \.topmsg/d' <"$tmpstdout"
125 # rerere will be a nop unless rerere.enabled is true, but might complete the merge!
126 eval git
"${setautoupdate:+-c rerere.autoupdate=1}" rerere ||
:
127 git ls-files
--unmerged --full-name --abbrev :/ >"$tmpstdout" 2>&1 ||
128 die
"git ls-files failed"
129 if [ -s "$tmpstdout" ]; then
130 [ "$_ret" != "0" ] || _ret
=1
134 if [ $_ret -ne 0 ]; then
135 # merge failed, spit out message, enter "merge" mode and return
137 printf '%s\n\n# Conflicts:\n' "$_msg"
138 sed -n "/$tab/s/^[^$tab]*/#/p" <"$tmpstdout" |
sort -u
139 } >"$git_dir/MERGE_MSG"
140 git update-ref MERGE_HEAD
"$_theirs" ||
:
141 echo 'Automatic merge failed; fix conflicts and then commit the result.'
145 if [ -n "$_ncmode" ]; then
146 # merge succeeded, but --no-commit requested, enter "merge" mode and return
147 printf '%s\n' "$_msg" >"$git_dir/MERGE_MSG"
148 git update-ref MERGE_HEAD
"$_theirs" ||
:
149 echo 'Automatic merge went well; stopped before committing as requested.'
153 # commit time at last!
154 thetree
="$(git write-tree)" || die
"git write-tree failed"
155 # avoid an extra "already up-to-date" commit (can't happen if _mt though)
158 origtree
="$(git rev-parse --quiet --verify "$_ours^
{tree
}" --)" &&
160 } || die
"git rev-parse failed"
161 if [ "$origtree" != "$thetree" ] ||
! contained_by
"$_theirs" "$_ours"; then
162 thecommit
="$(git commit-tree -p "$_ours" -p "$_theirs" -m "$_msg" "$thetree")" &&
163 [ -n "$thecommit" ] || die
"git commit-tree failed"
164 git update-ref
-m "$_msg" HEAD
"$thecommit" || die
"git update-ref failed"
166 # mention how the merge was made
167 echo "Merge made by the 'recursive' strategy."
172 # run git_topmerge with the passed in arguments (it always does --no-stat)
173 # then return the exit status of git_topmerge
174 # if the returned exit status is no error show a shortstat before
175 # returning assuming the merge was done into the previous HEAD but exclude
176 # .topdeps and .topmsg info from the stat unless doing a --merge
177 # if the first argument is --merge or --theirs or --remove handle .topmsg/.topdeps
179 # (default) .topmsg and .topdeps always keep ours
180 # --merge a normal merge takes place
181 # --theirs .topmsg and .topdeps always keep theirs
182 # --remove .topmsg and .topdeps are removed from the result and working tree
183 # note this function should only be called after attempt_index_merge fails as
184 # it implicity always does --no-ff (except for --merge which will --ff)
187 git_topmerge
-v _oldhead
"$@" || _ret
=$?
188 if [ "$1" != "--no-commit" ] && [ "$_ret" = "0" ]; then
190 [ "$1" = "--merge" ] || _exclusions
=":/ :!/.topdeps :!/.topmsg"
191 git
--no-pager diff-tree
--shortstat "$_oldhead" HEAD^
0 -- $_exclusions
196 # $1 => .topfile handling ([--]merge, [--]theirs, [--]remove or else do ours)
197 # $2 => current "HEAD"
198 # $3 => proposed fast-forward-to "HEAD"
199 # result is success if fast-forward satisfies $1
203 # merge and theirs will always be correct
206 # okay if both blobs are "missing" in $3
207 printf '%s\n' "$3:.topdeps" "$3:.topmsg" |
208 git cat-file
--batch-check="%(objectname) %(objecttype)" |
212 [ "$_tdt" = "missing" ] &&
213 [ "$_tmt" = "missing" ]
218 # okay if both blobs are the same (same hash or missing)
219 printf '%s\n' "$2:.topdeps" "$2:.topmsg" "$3:.topdeps" "$3:.topmsg" |
220 git cat-file
--batch-check="%(objectname) %(objecttype)" |
226 { [ "$_td1t" = "$_td2t" ] &&
227 { [ "$_td1o" = "$_td2o" ] ||
228 [ "$_td1t" = "missing" ]; }; } &&
229 { [ "$_tm1t" = "$_tm2t" ] &&
230 { [ "$_tm1o" = "$_tm2o" ] ||
231 [ "$_tm1t" = "missing" ]; }; }
238 # similar to git_merge but operates exclusively using a separate index and temp dir
239 # only trivial aggressive automatic (i.e. simple) merges are supported
241 # [optional] '--no-auto' to suppress "automatic" merging, merge fails instead
242 # [optional] '--merge', '--theirs' or '--remove' to alter .topfile handling
243 # $1 => '' to discard result, 'refs/?*' to update the specified ref or a varname
244 # $2 => '-m' MUST be '-m'
245 # $3 => commit message AND, if $1 matches refs/?* the update-ref message
246 # $4 => commit-ish to merge as "ours"
247 # $5 => commit-ish to merge as "theirs"
248 # [$6...] => more commit-ishes to merge as "theirs" in octopus
250 # all merging is done in a separate index (or temporary files for simple merges)
251 # if successful the ref or var is updated with the result
252 # otherwise everything is left unchanged and a silent failure occurs
253 # if successful and $1 matches refs/?* it WILL BE UPDATED to a new commit using the
254 # message and appropriate parents AND HEAD WILL BE DETACHED first if it's a symref
256 # otherwise if $1 does not match refs/?* and is not empty the named variable will
257 # be set to contain the resulting commit from the merge
258 # the working tree and index ARE LEFT COMPLETELY UNTOUCHED no matter what
259 v_attempt_index_merge
() {
261 if [ "$1" = "--no-auto" ]; then
266 [ "$1" = "--merge" ] || _exclusions
=":/ :!/.topdeps :!/.topmsg"
268 if [ "$1" = "--merge" ] ||
[ "$1" = "--theirs" ] ||
[ "$1" = "--remove" ]; then
271 if [ "$_mmode" = "merge" ] ||
[ "$_mmode" = "theirs" ]; then
272 _mstyle
="-top$_mmode"
275 [ "$#" -ge 5 ] && [ "$2" = "-m" ] && [ -n "$3" ] && [ -n "$4" ] && [ -n "$5" ] ||
276 die
"programmer error: invalid arguments to v_attempt_index_merge: $*"
281 rh
="$(git rev-parse --quiet --verify "$_head^
0" --)" && [ -n "$rh" ] ||
return 1
290 if [ $# -gt 1 ]; then
291 if [ "$_mmode" = "merge" ] ||
[ "$_mmode" = "theirs" ]; then
292 die
"programmer error: invalid octopus .topfile strategy to v_attempt_index_merge: --$_mode"
294 ihl
="$(git merge-base --independent "$@
")" ||
return 1
296 [ $# -ge 1 ] && [ -n "$1" ] ||
return 1
298 [ $# -eq 1 ] || _octo
=1
299 mb
="$(git merge-base ${_octo:+--octopus} "$rh" "$@
")" && [ -n "$mb" ] ||
{
300 mb
="$(git mktree < /dev/null)"
303 if [ -z "$_mt" ]; then
304 if [ -n "$_octo" ]; then
305 while [ $# -gt 1 ] && mbh
="$(git merge-base "$rh" "$1")" && [ -n "$mbh" ]; do
306 if [ "$rh" = "$mbh" ]; then
307 if topff_ok
"$_mmode" "$rh" "$1"; then
308 _mmsg
="Fast-forward (no commit created)"
315 elif [ "$1" = "$mbh" ]; then
321 if [ $# -eq 1 ]; then
323 mb
="$(git merge-base "$rh" "$1")" && [ -n "$mb" ] ||
return 1
326 if [ -z "$_octo" ]; then
327 r1
="$(git rev-parse --quiet --verify "$1^
0" --)" && [ -n "$r1" ] ||
return 1
330 if [ "$rh" = "$mb" ]; then
331 if topff_ok
"$_mmode" "$rh" "$r1"; then
332 _mmsg
="Fast-forward (no commit created)"
337 elif [ "$r1" = "$mb" ]; then
338 [ -n "$_mmsg" ] || _mmsg
="Already up-to-date!"
346 if [ -z "$newc" ]; then
347 if [ "$_mmode" = "theirs" ] && [ -z "$oth" ]; then
348 oth
="$(git rev-parse --quiet --verify "$1^
0" --)" && [ -n "$oth" ] ||
return 1
351 inew
="$tg_tmp_dir/index.$$"
352 ! [ -e "$inew" ] ||
rm -f "$inew"
353 itmp
="$tg_tmp_dir/output.$$"
354 imrg
="$tg_tmp_dir/auto.$$"
355 [ -z "$_octo" ] ||
>"$imrg"
360 if [ -n "$_parents" ]; then
361 if contained_by
"$1" "$_newrh"; then
366 GIT_INDEX_FILE
="$inew" git read-tree
-m --aggressive -i "$mb" "$rh" "$1" ||
{ rm -f "$inew" "$imrg"; return 1; }
367 GIT_INDEX_FILE
="$inew" git ls-files
--unmerged --full-name --abbrev :/ >"$itmp" 2>&1 ||
{ rm -f "$inew" "$itmp" "$imrg"; return 1; }
368 ! [ -s "$itmp" ] ||
{
369 if ! GIT_INDEX_FILE
="$inew" TG_TMP_DIR
="$tg_tmp_dir" git merge-index
-q "$TG_INST_CMDDIR/tg--index-merge-one-file$_mstyle" -a >"$itmp" 2>&1; then
370 rm -f "$inew" "$itmp" "$imrg"
373 if [ -s "$itmp" ]; then
374 if [ -n "$_noauto" ]; then
375 rm -f "$inew" "$itmp" "$imrg"
378 if [ -n "$_octo" ]; then
379 cat "$itmp" >>"$imrg"
388 _parents
="${_parents:+$_parents }-p $1"
389 if [ $# -gt 1 ]; then
390 newt
="$(GIT_INDEX_FILE="$inew" git write-tree)" && [ -n "$newt" ] ||
{ rm -f "$inew" "$imrg"; return 1; }
397 if [ "$_mmode" != "merge" ]; then
399 theirs
) _source
="$oth";;
405 _newinfo
="$(git cat-file --batch-check="%(objecttype
) %(objectname
)$tab%(rest
)" <<-EOT |
406 $_source:.topdeps .topdeps
407 $_source:.topmsg .topmsg
409 sed -n 's/^blob /100644 /p'
411 [ -z "$_newinfo" ] || _newinfo
="$lf$_newinfo"
412 GIT_INDEX_FILE
="$inew" git update-index
--index-info <<-EOT || { rm -f "$inew" "$imrg"; return 1; }
413 0 $nullsha$tab.topdeps
414 0 $nullsha$tab.topmsg$_newinfo
417 newt
="$(GIT_INDEX_FILE="$inew" git write-tree)" && [ -n "$newt" ] ||
{ rm -f "$inew" "$imrg"; return 1; }
418 [ -z "$_octo" ] ||
sort -u <"$imrg"
419 rm -f "$inew" "$imrg"
420 newc
="$(git commit-tree -p "$orh" $_parents -m "$_msg" "$newt")" && [ -n "$newc" ] ||
return 1
421 _mmsg
="Merge made by the 'trivial aggressive$_auto${_octo:+ octopus}' strategy."
425 if [ -n "$_same" ]; then
427 if rv
="$(git rev-parse --quiet --verify "$_var" --)" && [ "$rv" = "$newc" ]; then
431 if [ -z "$_same" ]; then
432 detach_symref_head_on_branch
"$_var" ||
return 1
433 # git update-ref returns 0 even on failure :(
434 git update-ref
-m "$_msg" "$_var" "$newc" ||
return 1
438 eval "$_var="'"$newc"'
442 [ -n "$_nodt" ] || git
--no-pager diff-tree
--shortstat "$orh" "$newc" -- $_exclusions
446 # shortcut that passes $3 as a preceding argument (which must match refs/?*)
447 attempt_index_merge
() {
450 if [ "$1" = "--no-auto" ]; then
454 if [ "$1" = "--merge" ] ||
[ "$1" = "--theirs" ] ||
[ "$1" = "--remove" ]; then
458 case "$3" in refs
/?
*);;*)
459 die
"programmer error: invalid arguments to attempt_index_merge: $*"
461 v_attempt_index_merge
$_noauto $_mmode "$3" "$@"
464 # write empty blob and store its hash in the variable named by $1 (if not empty)
466 __gitmtblob
="$(git hash-object -t blob -w --stdin </dev/null 2>/dev/null)" ||
:
467 [ -n "$__gitmtblob" ] || die
"could not write empty blob to object database"
468 [ -z "$1" ] ||
eval "$1=\"\$__gitmtblob\""
472 # $1 is variable name to receive written tree (may be empty)
473 # $2 is full blob hash to assign to single "blob" file in created tree
474 v_write_blob_tree
() {
475 _mktreet
="$(git mktree <<EOT 2>/dev/null
476 100644 blob $2${tab}blob
479 [ -n "$_mktreet" ] ||
return 1
480 [ -z "$1" ] ||
eval "$1=\"\$_mktreet\""
484 # $1 is variable name to receive type of object (may be empty)
485 # $2 is variable name to receive hash of object (may be empty)
486 # $3 is a suitable object name for cat-file --batch-check
487 # $4 [optional] if non-empty will be joined to $3 with a ':'
488 # if object does not exist status return is not 0
489 v_get_object_type
() {
492 read -r _goth _gott
<<EOT || :
493 $(git cat-file --batch-check="%(objectname) %(objecttype)" 2>/dev/null <<EOD
498 case "$_gott" in *"missing")
501 [ -n "$_goth" ] && [ -n "$_gott" ] ||
return 1
502 [ -z "$1" ] ||
eval "$1=\"\$_gott\""
503 [ -z "$2" ] ||
eval "$2=\"\$_goth\""
507 # attempt a 3-way index merge of a single file and return the resulting blob
508 # similar to v_attempt_index_merge except there are always exactly two heads
509 # and only the specified file will be merged (and if successful a blob created)
511 # optional arguments MUST be given in the order shown
513 # [optional] '--use-empty' use empty blob if file does not exist in either head
514 # [optional] '--non-blob-empty' use empty blob if non-blob found
515 # [optional] '--auh' allow unrelated histories (use empty tree if no merge base)
516 # $1 => '' to discard result or a varname to receive the blob hash
517 # $2 => commit-ish to merge as "ours"
518 # $3 => commit-ish to merge as "theirs"
519 # $4 => full path to file to merge (as shown by ls-tree -rt --full-tree <tree>)
520 # $5 => [optional] full path of file in "theirs" (defaults to $4)
521 # $6 => [optional] full path of file in merge base (defaults to $4)
523 # if either tree $2 and/or tree $3 is invalid a silent failure always occurs
524 # if merge-base $2 $3 fails and --auh has NOT been given a silent failure occurs
525 # if either $2 and/or $3 has a non-blob at the specified path, a silent failure
526 # occurs unless --non-blob-empty has been given
527 # if either $2 and/or $3 has nothing at the specified path, a silent failure
528 # occurs unless --use-empty has been given
530 # With `--auh` $2 and/or $3 can be a tree rather than a commit in which case
531 # the merge base will be an empty tree
533 # $6 will always be ignored if no merge base has been found which will also
534 # always be a silent failure unless `--auh` has been given
536 # If the "ours" blob is the same as the "theirs" blob it's returned early and
537 # the check for a merge base is skipped (--auh is also irrelevant in this case)
539 # all merging is done in a separate index (or temporary files for simple merges)
540 # if successful the var $1 (if not empty) is updated with the result blob hash
541 # otherwise everything is left unchanged and a silent failure occurs
542 # the working tree and index ARE LEFT COMPLETELY UNTOUCHED no matter what
543 # on success (regardless of whether $1 is empty) the new blob will always be
544 # written to the object database even if it's not returned ($1 is empty)
545 v_attempt_index_merge_file
() {
549 [ "$1" != "--use-empty" ] ||
{ _usemt
=1; shift; }
550 [ "$1" != "--non-blob-empty" ] ||
{ _nonmt
=1; shift; }
551 [ "$1" != "--auh" ] ||
{ _useuh
=1; shift; }
552 [ $# -eq 4 ] ||
[ $# -eq 5 ] ||
[ $# -eq 6 ] ||
553 die
"programmer error: wrong number of args ($#) to v_attempt_index_merge_file"
554 [ -n "$4" ] || die
"programmer error: empty path for arg \$2 to v_attempt_index_merge_file"
555 _treeo
="$(git rev-parse --verify --quiet "$2^
{tree
}" -- 2>/dev/null)" ||
:
556 [ -n "$_treeo" ] ||
return 1
557 _treet
="$(git rev-parse --verify --quiet "$3^
{tree
}" -- 2>/dev/null)" ||
:
558 [ -n "$_treet" ] ||
return 1
560 if ! v_get_object_type _blobot _bloboh
"$_treeo" "$4"; then
561 [ -n "$_usemt" ] ||
return 1
562 [ -n "$_mtblob" ] || v_write_mt_blob _mtblob
566 if [ "$_blobot" != "blob" ]; then
567 [ -n "$_nonmt" ] ||
return 1
568 [ -n "$_mtblob" ] || v_write_mt_blob _mtblob
572 if ! v_get_object_type _blobtt _blobth
"$_treet" "${5:-$4}"; then
573 [ -n "$_usemt" ] ||
return 1
574 [ -n "$_mtblob" ] || v_write_mt_blob _mtblob
578 if [ "$_blobtt" != "blob" ]; then
579 [ -n "$_nonmt" ] ||
return 1
580 [ -n "$_mtblob" ] || v_write_mt_blob _mtblob
584 if [ "$_bloboh" = "$_blobth" ]; then
585 # nothing to do, blobs are the same
586 [ -z "$1" ] ||
eval "$1=\"\$_bloboh\""
589 _mbf
="$(git merge-base "$2" "$3" 2>/dev/null)" ||
:
590 [ -n "$_mbf" ] ||
[ -n "$_useuh" ] ||
return 1
592 if [ -n "$_mbf" ]; then
593 _treeb
="$(git rev-parse --verify --quiet "$_mbf^
{tree
}" -- 2>/dev/null)" ||
:
594 [ -n "$_treeb" ] ||
return 1 # somehow the commit doesn't have a tree
601 ! v_get_object_type _blobbt _blobbh
"$_treeb" "${6:-$4}"
603 [ -n "$_mbtmt" ] ||
[ -n "$_usemt" ] ||
return 1
604 [ -n "$_mtblob" ] || v_write_mt_blob _mtblob
608 if [ "$_blobbt" != "blob" ]; then
609 [ -n "$_nonmt" ] ||
return 1
610 [ -n "$_mtblob" ] || v_write_mt_blob _mtblob
614 v_write_blob_tree _treeob
"$_bloboh" ||
return 1
615 v_write_blob_tree _treetb
"$_blobth" ||
return 1
616 v_write_blob_tree _treebb
"$_blobbh" ||
return 1
617 inew
="$tg_tmp_dir/index.$$"
618 ! [ -e "$inew" ] ||
rm -f "$inew"
619 GIT_INDEX_FILE
="$inew" \
620 git read-tree
-m --aggressive -i "$_treebb" "$_treeob" "$_treetb" >/dev
/null
2>&1 ||
{
624 _unmerged
="$(GIT_INDEX_FILE="$inew" \
625 git ls-files --unmerged --full-name --abbrev -- :/ 2>/dev/null)" ||
{
629 if [ -n "$_unmerged" ]; then
630 # try an automatic merge
631 GIT_INDEX_FILE
="$inew" TG_TMP_DIR
="$tg_tmp_dir" \
632 git merge-index
-q "$TG_INST_CMDDIR/tg--index-merge-one-file" -a >/dev
/null
2>&1 ||
{
636 _unmerged
="$(GIT_INDEX_FILE="$inew" \
637 git ls-files --unmerged --full-name --abbrev -- :/ 2>/dev/null)" ||
{
642 [ -z "$_unmerged" ] ||
{
651 read -r _rsltm _rslth _rslts _rsltf
<<EOT || :
652 $(GIT_INDEX_FILE="$inew" git ls-files --full-name -s -- :/)
656 [ "$_rsltm" = "100644" ] && [ -n "$_rslth" ] &&
657 [ "$_rslts" = "0" ] && [ "$_rsltf" = "blob" ]
660 [ -z "$1" ] ||
eval "$1=\"\$_rslth\""