Last active
November 22, 2015 17:57
-
-
Save drmingdrmer/dcc194f3977e362457ac to your computer and use it in GitHub Desktop.
split files out of commits to build a new branch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# * 358aee4 (HEAD) fix css | |
# | css/site.css | 13 ++--- | |
# | | |
# * f869f40 init site | |
# | html/index.html | 12 ++-- | |
# | html/header.html | 34 +----- | |
# | css/site.css | 61 +++++++------ | |
# | | |
# * 82554be fix doc of Build | |
# | |
# | |
# # to split folder html into a separate branch: | |
# > git-split 82554be..HEAD -- html/ | |
# | |
# | |
# * 948149a Merge commit 'aa76f4de5be5ddbb2908ba145fea5697c7baf728' into HEAD | |
# |\ | |
# | * aa76f4d init site | |
# | | html/index.html | 12 ++-- | |
# | | html/header.html | 34 +----- | |
# | | | |
# * | 088e903 fix css | |
# | | css/site.css | 13 ++--- | |
# | | | |
# * | 0b606c2 init site | |
# | | css/site.css | 61 +++++++------ | |
# |/ | |
# * 82554be fix doc of Build | |
usage() | |
{ | |
echo 'split files out of commits to build a new branch' | |
echo 'git-split [ -b <to-new-branch> ] <from>..[<to>] -- <fns>' | |
exit 1 | |
} | |
mes() | |
{ | |
local Brown="$(tput setaf 3)" | |
local NC="$(tput sgr0)" | |
echo $Brown"$@"$NC | |
} | |
githash() | |
{ | |
git log -n1 --format="%H" $1 | |
} | |
new_branch= | |
while getopts "b:" opname; do | |
case $opname in | |
b) | |
new_branch=$OPTARG | |
;; | |
[?]) | |
usage | |
;; | |
esac | |
done | |
shift $(($OPTIND - 1)) | |
if [ ".$2" != ".--" ]; then | |
usage | |
fi | |
range=$1 | |
shift | |
shift | |
fns="$@" | |
base=${range%..*} | |
if [ ".$base" = ".$range" ]; then | |
usage | |
fi | |
tip=${range#*..} | |
if [ ".$tip" = "." ]; then | |
tip=HEAD | |
fi | |
head=$(git symbolic-ref --short HEAD 2>/dev/null) | |
if [ ".$head" = "." ]; then | |
head=$(git rev-parse HEAD) | |
fi | |
tip_hash=$(githash $tip) | |
new_hash= | |
merged_hash= | |
extract() | |
{ | |
for c in $(git log --reverse $base..$tip_hash --format="%H" -- $fns); do | |
mes "== to pick $fns from:" | |
git log --color --stat --decorate --pretty=oneline --abbrev-commit -M -n1 $c | |
mes=$(git log -n1 --format="%B" $c) | |
p=$(git diff $c~ $c -- $fns) | |
git apply --index <<<"$p" | |
git status | |
git commit -am "$mes" | |
done | |
return 0 | |
} | |
build_new_branch() | |
{ | |
git checkout --detach $base \ | |
&& mes "== build new branch ==" \ | |
&& extract \ | |
&& new_hash=$(githash) \ | |
&& \ | |
if [ ".$new_branch" != "." ]; then | |
git branch "$new_branch" | |
fi \ | |
&& mes "== new branch $new_branch hash is $new_hash == " | |
} | |
discard_changes() | |
{ | |
local script="git rm -r -f $fns 2>/dev/null; git checkout $base -- $fns 2>/dev/null; git status >/dev/null" | |
git checkout --detach $tip_hash \ | |
&& mes "== discard from $tip ==" \ | |
&& git filter-branch -f --prune-empty --tree-filter "$script" $base..HEAD \ | |
&& [ $(git log $base..HEAD -- "$fns" | wc -l | awk '{print $1}') = 0 ] \ | |
&& mes "== confirmed that no changes through $base..HEAD" | |
} | |
merge_them() | |
{ | |
mes "== merge splitted branch back to $tip ==" \ | |
&& \ | |
if [ ".$new_branch" = "." ]; then | |
git merge --commit --no-edit --no-ff $new_hash | |
else | |
git merge --commit --no-edit --no-ff $new_branch | |
fi \ | |
&& merged_hash=$(githash) \ | |
&& mes "merged hash: $merged_hash" | |
} | |
is_same_as_original() | |
{ | |
mes "== diff with original $tip ==" \ | |
&& git diff $tip | |
} | |
update_tip_ref() | |
{ | |
git update-ref -d refs/original/HEAD | |
# if it is a reference(branch, tag), not hash or HEAD | |
if [ ".$tip" = ".HEAD" ]; then | |
git update-ref $tip $merged_hash \ | |
&& mes "== $tip set to $merged_hash" | |
fi | |
if git rev-parse refs/heads/$tip >/dev/null 2>/dev/null; then | |
git update-ref refs/heads/$tip $merged_hash \ | |
&& mes "== $tip set to $merged_hash" | |
fi | |
} | |
mes "split $fns from $range" \ | |
&& build_new_branch \ | |
&& discard_changes \ | |
&& merge_them \ | |
&& is_same_as_original \ | |
&& update_tip_ref \ | |
&& git checkout $tip >/dev/null 2>/dev/null \ | |
&& mes "== Done ==" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment