mirror of
https://github.com/git/git.git
synced 2024-11-19 15:34:51 +01:00
Merge branch 'master' of git://github.com/psionides/git-subtree
* 'master' of git://github.com/psionides/git-subtree: improved rev_is_descendant_of_branch() function added temporary test dirs to gitignore added tests for recent changes fixed bug in commit message for split changed alias for --prefix from -p to -P fix for subtree split not finding proper base for new commits allow using --branch with existing branches if it makes sense added -m/--message option for setting merge commit message added -p alias for --prefix
This commit is contained in:
commit
b2f166e4e2
3 changed files with 164 additions and 17 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
||||||
*~
|
*~
|
||||||
git-subtree.xml
|
git-subtree.xml
|
||||||
git-subtree.1
|
git-subtree.1
|
||||||
|
mainline
|
||||||
|
subproj
|
||||||
|
|
|
@ -16,7 +16,8 @@ git subtree split --prefix=<prefix> <commit...>
|
||||||
h,help show the help
|
h,help show the help
|
||||||
q quiet
|
q quiet
|
||||||
d show debug messages
|
d show debug messages
|
||||||
prefix= the name of the subdir to split out
|
P,prefix= the name of the subdir to split out
|
||||||
|
m,message= use the given message as the commit message for the merge commit
|
||||||
options for 'split'
|
options for 'split'
|
||||||
annotate= add a prefix to commit message of new commits
|
annotate= add a prefix to commit message of new commits
|
||||||
b,branch= create a new branch from the split subtree
|
b,branch= create a new branch from the split subtree
|
||||||
|
@ -40,6 +41,7 @@ rejoin=
|
||||||
ignore_joins=
|
ignore_joins=
|
||||||
annotate=
|
annotate=
|
||||||
squash=
|
squash=
|
||||||
|
message=
|
||||||
|
|
||||||
debug()
|
debug()
|
||||||
{
|
{
|
||||||
|
@ -76,7 +78,8 @@ while [ $# -gt 0 ]; do
|
||||||
--annotate) annotate="$1"; shift ;;
|
--annotate) annotate="$1"; shift ;;
|
||||||
--no-annotate) annotate= ;;
|
--no-annotate) annotate= ;;
|
||||||
-b) branch="$1"; shift ;;
|
-b) branch="$1"; shift ;;
|
||||||
--prefix) prefix="$1"; shift ;;
|
-P) prefix="$1"; shift ;;
|
||||||
|
-m) message="$1"; shift ;;
|
||||||
--no-prefix) prefix= ;;
|
--no-prefix) prefix= ;;
|
||||||
--onto) onto="$1"; shift ;;
|
--onto) onto="$1"; shift ;;
|
||||||
--no-onto) onto= ;;
|
--no-onto) onto= ;;
|
||||||
|
@ -158,6 +161,20 @@ rev_exists()
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rev_is_descendant_of_branch()
|
||||||
|
{
|
||||||
|
newrev="$1"
|
||||||
|
branch="$2"
|
||||||
|
branch_hash=$(git rev-parse $branch)
|
||||||
|
match=$(git rev-list -1 $branch_hash ^$newrev)
|
||||||
|
|
||||||
|
if [ -z "$match" ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# if a commit doesn't have a parent, this might not work. But we only want
|
# if a commit doesn't have a parent, this might not work. But we only want
|
||||||
# to remove the parent from the rev-list, and since it doesn't exist, it won't
|
# to remove the parent from the rev-list, and since it doesn't exist, it won't
|
||||||
# be there anyway, so do nothing in that case.
|
# be there anyway, so do nothing in that case.
|
||||||
|
@ -266,8 +283,13 @@ add_msg()
|
||||||
dir="$1"
|
dir="$1"
|
||||||
latest_old="$2"
|
latest_old="$2"
|
||||||
latest_new="$3"
|
latest_new="$3"
|
||||||
|
if [ -n "$message" ]; then
|
||||||
|
commit_message="$message"
|
||||||
|
else
|
||||||
|
commit_message="Add '$dir/' from commit '$latest_new'"
|
||||||
|
fi
|
||||||
cat <<-EOF
|
cat <<-EOF
|
||||||
Add '$dir/' from commit '$latest_new'
|
$commit_message
|
||||||
|
|
||||||
git-subtree-dir: $dir
|
git-subtree-dir: $dir
|
||||||
git-subtree-mainline: $latest_old
|
git-subtree-mainline: $latest_old
|
||||||
|
@ -275,13 +297,27 @@ add_msg()
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
add_squashed_msg()
|
||||||
|
{
|
||||||
|
if [ -n "$message" ]; then
|
||||||
|
echo "$message"
|
||||||
|
else
|
||||||
|
echo "Merge commit '$1' as '$2'"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
rejoin_msg()
|
rejoin_msg()
|
||||||
{
|
{
|
||||||
dir="$1"
|
dir="$1"
|
||||||
latest_old="$2"
|
latest_old="$2"
|
||||||
latest_new="$3"
|
latest_new="$3"
|
||||||
|
if [ -n "$message" ]; then
|
||||||
|
commit_message="$message"
|
||||||
|
else
|
||||||
|
commit_message="Split '$dir/' into commit '$latest_new'"
|
||||||
|
fi
|
||||||
cat <<-EOF
|
cat <<-EOF
|
||||||
Split '$dir/' into commit '$latest_new'
|
$commit_message
|
||||||
|
|
||||||
git-subtree-dir: $dir
|
git-subtree-dir: $dir
|
||||||
git-subtree-mainline: $latest_old
|
git-subtree-mainline: $latest_old
|
||||||
|
@ -441,7 +477,7 @@ cmd_add()
|
||||||
|
|
||||||
if [ -n "$squash" ]; then
|
if [ -n "$squash" ]; then
|
||||||
rev=$(new_squash_commit "" "" "$rev") || exit $?
|
rev=$(new_squash_commit "" "" "$rev") || exit $?
|
||||||
commit=$(echo "Merge commit '$rev' as '$dir'" |
|
commit=$(add_squashed_msg "$rev" "$dir" |
|
||||||
git commit-tree $tree $headp -p "$rev") || exit $?
|
git commit-tree $tree $headp -p "$rev") || exit $?
|
||||||
else
|
else
|
||||||
commit=$(add_msg "$dir" "$headrev" "$rev" |
|
commit=$(add_msg "$dir" "$headrev" "$rev" |
|
||||||
|
@ -454,10 +490,6 @@ cmd_add()
|
||||||
|
|
||||||
cmd_split()
|
cmd_split()
|
||||||
{
|
{
|
||||||
if [ -n "$branch" ] && rev_exists "refs/heads/$branch"; then
|
|
||||||
die "Branch '$branch' already exists."
|
|
||||||
fi
|
|
||||||
|
|
||||||
debug "Splitting $dir..."
|
debug "Splitting $dir..."
|
||||||
cache_setup || exit $?
|
cache_setup || exit $?
|
||||||
|
|
||||||
|
@ -488,7 +520,8 @@ cmd_split()
|
||||||
eval "$grl" |
|
eval "$grl" |
|
||||||
while read rev parents; do
|
while read rev parents; do
|
||||||
revcount=$(($revcount + 1))
|
revcount=$(($revcount + 1))
|
||||||
say -n "$revcount/$revmax ($createcount)
"
|
say -n "$revcount/$revmax ($createcount)
|
||||||
|
"
|
||||||
debug "Processing commit: $rev"
|
debug "Processing commit: $rev"
|
||||||
exists=$(cache_get $rev)
|
exists=$(cache_get $rev)
|
||||||
if [ -n "$exists" ]; then
|
if [ -n "$exists" ]; then
|
||||||
|
@ -505,7 +538,10 @@ cmd_split()
|
||||||
|
|
||||||
# ugly. is there no better way to tell if this is a subtree
|
# ugly. is there no better way to tell if this is a subtree
|
||||||
# vs. a mainline commit? Does it matter?
|
# vs. a mainline commit? Does it matter?
|
||||||
[ -z $tree ] && continue
|
if [ -z $tree ]; then
|
||||||
|
cache_set $rev $rev
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
|
newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
|
||||||
debug " newrev is: $newrev"
|
debug " newrev is: $newrev"
|
||||||
|
@ -526,9 +562,16 @@ cmd_split()
|
||||||
$latest_new >&2 || exit $?
|
$latest_new >&2 || exit $?
|
||||||
fi
|
fi
|
||||||
if [ -n "$branch" ]; then
|
if [ -n "$branch" ]; then
|
||||||
git update-ref -m 'subtree split' "refs/heads/$branch" \
|
if rev_exists "refs/heads/$branch"; then
|
||||||
$latest_new "" || exit $?
|
if ! rev_is_descendant_of_branch $latest_new $branch; then
|
||||||
say "Created branch '$branch'"
|
die "Branch '$branch' is not an ancestor of commit '$latest_new'."
|
||||||
|
fi
|
||||||
|
action='Updated'
|
||||||
|
else
|
||||||
|
action='Created'
|
||||||
|
fi
|
||||||
|
git update-ref -m 'subtree split' "refs/heads/$branch" $latest_new || exit $?
|
||||||
|
say "$action branch '$branch'"
|
||||||
fi
|
fi
|
||||||
echo $latest_new
|
echo $latest_new
|
||||||
exit 0
|
exit 0
|
||||||
|
@ -561,7 +604,7 @@ cmd_merge()
|
||||||
rev="$new"
|
rev="$new"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
git merge -s subtree $rev
|
git merge -s subtree --message="$message" $rev
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_pull()
|
cmd_pull()
|
||||||
|
|
106
test.sh
106
test.sh
|
@ -53,6 +53,16 @@ multiline()
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
undo()
|
||||||
|
{
|
||||||
|
git reset --hard HEAD~
|
||||||
|
}
|
||||||
|
|
||||||
|
last_commit_message()
|
||||||
|
{
|
||||||
|
git log --format=%s -1
|
||||||
|
}
|
||||||
|
|
||||||
rm -rf mainline subproj
|
rm -rf mainline subproj
|
||||||
mkdir mainline subproj
|
mkdir mainline subproj
|
||||||
|
|
||||||
|
@ -82,7 +92,24 @@ git branch subdir
|
||||||
|
|
||||||
git fetch ../subproj sub1
|
git fetch ../subproj sub1
|
||||||
git branch sub1 FETCH_HEAD
|
git branch sub1 FETCH_HEAD
|
||||||
|
|
||||||
|
# check if --message works for add
|
||||||
|
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
|
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
|
# this shouldn't actually do anything, since FETCH_HEAD is already a parent
|
||||||
git merge -m 'merge -s -ours' -s ours FETCH_HEAD
|
git merge -m 'merge -s -ours' -s ours FETCH_HEAD
|
||||||
|
@ -98,13 +125,44 @@ git commit -m 'main-sub7'
|
||||||
|
|
||||||
git fetch ../subproj sub2
|
git fetch ../subproj sub2
|
||||||
git branch sub2 FETCH_HEAD
|
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 subtree merge --prefix=subdir FETCH_HEAD
|
||||||
git branch pre-split
|
git branch pre-split
|
||||||
|
check_equal "$(last_commit_message)" "Merge commit '$(git rev-parse sub2)' into mainline"
|
||||||
|
|
||||||
spl1=$(git subtree split --annotate='*' \
|
# check if --message works for split+rejoin
|
||||||
--prefix subdir --onto FETCH_HEAD --rejoin)
|
spl1=$(git subtree split --annotate='*' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)
|
||||||
echo "spl1={$spl1}"
|
echo "spl1={$spl1}"
|
||||||
git branch 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
|
create subdir/main-sub8
|
||||||
git commit -m 'main-sub8'
|
git commit -m 'main-sub8'
|
||||||
|
@ -170,6 +228,50 @@ check_equal "$(git log --pretty=format:'%s' HEAD^2 | grep -i split)" ""
|
||||||
# meaningless to subproj since one side of the merge refers to the mainline)
|
# 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_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 --format=%P -1 mainsub4)" "$(git rev-parse sub3)"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# make sure no patch changes more than one file. The original set of commits
|
# 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
|
# changed only one file each. A multi-file change would imply that we pruned
|
||||||
# commits too aggressively.
|
# commits too aggressively.
|
||||||
|
|
Loading…
Reference in a new issue