Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 7cf86cd8bc
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

executable file 403 lines (325 sloc) 9.743 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
#!/bin/bash
. shellopts.sh
set -e

create()
{
echo "$1" >"$1"
git add "$1"
}

check()
{
echo
echo "check:" "$@"
if "$@"; then
echo ok
return 0
else
echo FAILED
exit 1
fi
}

check_not()
{
echo
echo "check: NOT " "$@"
if "$@"; then
echo FAILED
exit 1
else
echo ok
return 0
fi
}

check_equal()
{
echo
echo "check a:" "{$1}"
echo " b:" "{$2}"
if [ "$1" = "$2" ]; then
return 0
else
echo FAILED
exit 1
fi
}

fixnl()
{
t=""
while read x; do
t="$t$x "
done
echo $t
}

multiline()
{
while read x; do
set -- $x
for d in "$@"; do
echo "$d"
done
done
}

undo()
{
git reset --hard HEAD~
}

last_commit_message()
{
git log --pretty=format:%s -1
}

rm -rf mainline subproj
mkdir mainline subproj

cd subproj
git init

create sub1
git commit -m 'sub1'
git branch sub1
git branch -m master subproj
check true

create sub2
git commit -m 'sub2'
git branch sub2

create sub3
git commit -m 'sub3'
git branch sub3

cd ../mainline
git init
create main4
git commit -m 'main4'
git branch -m master mainline
git branch subdir

git fetch ../subproj sub1
git branch sub1 FETCH_HEAD

# check if --message works for add
check_not git subtree merge --prefix=subdir sub1
check_not git subtree pull --prefix=subdir ../subproj sub1
git subtree add --prefix=subdir --message="Added subproject" sub1
check_equal "$(last_commit_message)" "Added subproject"
undo

# check if --message works as -m and --prefix as -P
git subtree add -P subdir -m "Added subproject using git subtree" sub1
check_equal "$(last_commit_message)" "Added subproject using git subtree"
undo

# check if --message works with squash too
git subtree add -P subdir -m "Added subproject with squash" --squash sub1
check_equal "$(last_commit_message)" "Added subproject with squash"
undo

git subtree add --prefix=subdir/ FETCH_HEAD
check_equal "$(last_commit_message)" "Add 'subdir/' from commit '$(git rev-parse sub1)'"

# this shouldn't actually do anything, since FETCH_HEAD is already a parent
git merge -m 'merge -s -ours' -s ours FETCH_HEAD

create subdir/main-sub5
git commit -m 'main-sub5'

create main6
git commit -m 'main6 boring'

create subdir/main-sub7
git commit -m 'main-sub7'

git fetch ../subproj sub2
git branch sub2 FETCH_HEAD

# check if --message works for merge
git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2
check_equal "$(last_commit_message)" "Merged changes from subproject"
undo

# check if --message for merge works with squash too
git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2
check_equal "$(last_commit_message)" "Merged changes from subproject using squash"
undo

git subtree merge --prefix=subdir FETCH_HEAD
git branch pre-split
check_equal "$(last_commit_message)" "Merge commit '$(git rev-parse sub2)' into mainline"

# Check that prefix argument is required for split (exits with warning and exit status = 1)
! result=$(git subtree split 2>&1)
check_equal "You must provide the --prefix option." "$result"

# Check that the <prefix> exists for a split.
! result=$(git subtree split --prefix=non-existent-directory 2>&1)
check_equal "'non-existent-directory' does not exist; use 'git subtree add'" \
  "$result"

# check if --message works for split+rejoin
spl1=$(git subtree split --annotate='*' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)
echo "spl1={$spl1}"
git branch spl1 "$spl1"
check_equal "$(last_commit_message)" "Split & rejoin"
undo

# check split with --branch
git subtree split --annotate='*' --prefix subdir --onto FETCH_HEAD --branch splitbr1
check_equal "$(git rev-parse splitbr1)" "$spl1"

# check split with --branch for an existing branch
git branch splitbr2 sub1
git subtree split --annotate='*' --prefix subdir --onto FETCH_HEAD --branch splitbr2
check_equal "$(git rev-parse splitbr2)" "$spl1"

# check split with --branch for an incompatible branch
result=$(git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir || echo "caught error")
check_equal "$result" "caught error"


git subtree split --annotate='*' --prefix subdir --onto FETCH_HEAD --rejoin
check_equal "$(last_commit_message)" "Split 'subdir/' into commit '$spl1'"

create subdir/main-sub8
git commit -m 'main-sub8'

cd ../subproj
git fetch ../mainline spl1
git branch spl1 FETCH_HEAD
git merge FETCH_HEAD

create sub9
git commit -m 'sub9'

cd ../mainline
split2=$(git subtree split --annotate='*' --prefix subdir/ --rejoin)
git branch split2 "$split2"

create subdir/main-sub10
git commit -m 'main-sub10'

spl3=$(git subtree split --annotate='*' --prefix subdir --rejoin)
git branch spl3 "$spl3"

cd ../subproj
git fetch ../mainline spl3
git branch spl3 FETCH_HEAD
git merge FETCH_HEAD
git branch subproj-merge-spl3

chkm="main4 main6"
chkms="main-sub10 main-sub5 main-sub7 main-sub8"
chkms_sub=$(echo $chkms | multiline | sed 's,^,subdir/,' | fixnl)
chks="sub1 sub2 sub3 sub9"
chks_sub=$(echo $chks | multiline | sed 's,^,subdir/,' | fixnl)

# make sure exactly the right set of files ends up in the subproj
subfiles=$(git ls-files | fixnl)
check_equal "$subfiles" "$chkms $chks"

# make sure the subproj history *only* contains commits that affect the subdir.
allchanges=$(git log --name-only --pretty=format:'' | sort | fixnl)
check_equal "$allchanges" "$chkms $chks"

cd ../mainline
git fetch ../subproj subproj-merge-spl3
git branch subproj-merge-spl3 FETCH_HEAD
git subtree pull --prefix=subdir ../subproj subproj-merge-spl3

# make sure exactly the right set of files ends up in the mainline
mainfiles=$(git ls-files | fixnl)
check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"

# make sure each filename changed exactly once in the entire history.
# 'main-sub??' and '/subdir/main-sub??' both change, because those are the
# changes that were split into their own history. And 'subdir/sub??' never
# change, since they were *only* changed in the subtree branch.
allchanges=$(git log --name-only --pretty=format:'' | sort | fixnl)
check_equal "$allchanges" "$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"

# make sure the --rejoin commits never make it into subproj
check_equal "$(git log --pretty=format:'%s' HEAD^2 | grep -i split)" ""

# make sure no 'git subtree' tagged commits make it into subproj. (They're
# meaningless to subproj since one side of the merge refers to the mainline)
check_equal "$(git log --pretty=format:'%s%n%b' HEAD^2 | grep 'git-subtree.*:')" ""


# check if split can find proper base without --onto
# prepare second pair of repositories
mkdir test2
cd test2

mkdir main
cd main
git init
create main1
git commit -m "main1"

cd ..
mkdir sub
cd sub
git init
create sub2
git commit -m "sub2"

cd ../main
git fetch ../sub master
git branch sub2 FETCH_HEAD
git subtree add --prefix subdir sub2

cd ../sub
create sub3
git commit -m "sub3"

cd ../main
git fetch ../sub master
git branch sub3 FETCH_HEAD
git subtree merge --prefix subdir sub3

create subdir/main-sub4
git commit -m "main-sub4"
git subtree split --prefix subdir --branch mainsub4

# at this point, the new commit's parent should be sub3
# if it's not, something went wrong (the "newparent" of "master~" commit should have been sub3,
# but it wasn't, because it's cache was not set to itself)
check_equal "$(git log --pretty=format:%P -1 mainsub4)" "$(git rev-parse sub3)"

mkdir subdir2
create subdir2/main-sub5
git commit -m "main-sub5"
git subtree split --prefix subdir2 --branch mainsub5

# also test that we still can split out an entirely new subtree
# if the parent of the first commit in the tree isn't empty,
# then the new subtree has accidently been attached to something
check_equal "$(git log --pretty=format:%P -1 mainsub5)" ""


# make sure no patch changes more than one file. The original set of commits
# changed only one file each. A multi-file change would imply that we pruned
# commits too aggressively.
joincommits()
{
commit=
all=
while read x y; do
echo "{$x}" >&2
if [ -z "$x" ]; then
continue
elif [ "$x" = "commit:" ]; then
if [ -n "$commit" ]; then
echo "$commit $all"
all=
fi
commit="$y"
else
all="$all $y"
fi
done
echo "$commit $all"
}
x=
git log --pretty=format:'commit: %H' | joincommits |
( while read commit a b; do
echo "Verifying commit $commit"
check_equal "$b" ""
x=1
done
check_equal "$x" 1
) || exit 1

# Return to mainline
cd ../..


# from-submodule

make_submodule()
{
cd ..
local prefix=$1

local submodule_path=$(mktemp -d submodule.XXX)
cd $submodule_path
local submodule_abs_path=$(pwd)

git init > /dev/null
create "sub-file"
git commit -m "sub-commit" > /dev/null
local submodule_sha=$(git rev-parse HEAD)
cd ../mainline

git submodule add $submodule_abs_path $prefix > /dev/null
git submodule update --init > /dev/null
git commit -m "Added $prefix." > /dev/null

echo $submodule_sha
}

# Unfortunately the following submodule tests were broken when I found them:
#
# subA_sha=$(make_submodule submodules/subA)
#
# git subtree from-submodule --prefix submodules/subA
#
# check_equal "$(last_commit_message)" "Add 'submodules/subA/' from commit '${subA_sha}'"
# # Submodule should be gone.
# check_equal "$(git submodule status)" ""
#
# rm -rf ../submodule.*


# Simple subtree add-and-immediately-push test:
echo
echo '*** TESTING add subtree and immediately push'
echo

cd .. # return to test dir top level
rm -rf mainline2
git init mainline2
cd mainline2
date > mainline2.txt && git add . && git commit -m "initial mainline2 commit"
git subtree add --prefix=subproj ../subproj subproj
git subtree push --prefix=subproj ../subproj subproj
result=$?
cd ..
check_equal 0 "$result"

echo
echo '*** PASSED add subtree and immediately push'
echo

echo '*** ALL TESTS PASSED ***'
Something went wrong with that request. Please try again.