Last active
November 25, 2020 05:49
Revisions
-
drmingdrmer revised this gist
Nov 25, 2020 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -743,6 +743,7 @@ while getopts "b:m:p:s:orh" opname; do exit 0 ;; [?]) echo "unknown option: ($opname)" usage exit 1 ;; -
drmingdrmer revised this gist
Mar 27, 2019 . 1 changed file with 66 additions and 29 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -71,8 +71,11 @@ Options: -r Remove <fns> from original history. Without -r, the original history will not be modified. -m Merge back action: "" for doing nothing; "merge": merge the new branch to commmit "<to>"; "rebase": rebase "<to>" onto the new branch. non empty -m implies -r. -s <prefix> Strip <prefix> dir segments from paths that are splitted out. E.g.: @@ -712,18 +715,19 @@ fn_match() } new_branch=tmpbranch merge="" remove=0 orphan=0 to_strip=0 while getopts "b:m:p:s:orh" opname; do case $opname in b) new_branch=$OPTARG ;; m) merge=$OPTARG remove=1 ;; o) orphan=1 @@ -811,7 +815,7 @@ extract() commit=$(git_copy_commit "$c" "$tree" "-p $parent") dd "new commit:" $commit git reset --hard $commit -- parent=$commit done @@ -829,7 +833,7 @@ build_new_branch() git commit --allow-empty -m "init-root" || die commit init-root else info reset $new_branch to $base git reset --hard $base -- fi git clean -dxf || die clean -dxf @@ -871,50 +875,83 @@ discard_changes() info "== END ==" } merge_or_rebase() { local action=$1 if [ "$action" == "merge" ]; then info "== merge splitted branch back to $tip ==" \ && \ if [ ".$new_branch" = "tmpbranch" ]; then git merge --commit --no-edit --no-ff $new_hash else git merge --commit --no-edit --no-ff $new_branch fi \ && merged_hash=$(git_hash HEAD) \ && info "merged hash: $merged_hash" elif [ "$action" == "rebase" ]; then info "== rebase $tip onto $new_branch ($new_hash) ==" git rebase $new_hash || return 1 merged_hash=$(git_hash HEAD) info "rebased hash: $merged_hash" else die "invalid action: ($action)" fi } is_same_as_original() { info "== diff with original $tip ==" git diff $tip || return 1 ok "Current HEAD is same as $tip" } update_tip_ref() { info "remove tmp branch: refs/original/HEAD" git update-ref -d refs/original/HEAD # if it is a reference(branch, tag), not hash or HEAD if [ ".$tip" = ".HEAD" ]; then info "git update-ref $tip $merged_hash" git update-ref $tip $merged_hash \ && info "== $tip set to $merged_hash" fi if git rev-parse refs/heads/$tip >/dev/null 2>/dev/null; then info "git update-ref refs/heads/$tip $merged_hash" git update-ref refs/heads/$tip $merged_hash \ && info "== $tip set to $merged_hash" fi } info "split $fns from $range" build_new_branch || die build_new_branch if [ "$remove" = "0" ]; then exit 0; fi discard_changes || die discard_changes if [ "$merge" = "" ]; then exit 0 fi merge_or_rebase "$merge" || die merge_or_rebase is_same_as_original || die is_same_as_original update_tip_ref || die update_tip_ref git checkout $tip >/dev/null 2>/dev/null || die git checkout $tip info "== Done ==" -
drmingdrmer revised this gist
Mar 25, 2019 . 1 changed file with 31 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -84,6 +84,36 @@ END clr() { # clr light read blabla local black=0 local white=7 local red=1 local green=2 local brown=3 local blue=4 local purple=5 local cyan=6 local light="" local color=$1 shift if [ "$color" == "light" ]; then light="tput bold; " color=$1 shift fi local code=$(eval 'echo $'$color) local cmd="${light}tput setaf $code" local color_str="$(eval "$cmd")" echo $color_str"$@""$(tput sgr0)" } shlib_init_colors() { Black="$( tput setaf 0)" @@ -113,6 +143,7 @@ shlib_init_colors() NC="$( tput sgr0)" # No Color } screen_width() { local chr="${1--}" -
drmingdrmer revised this gist
Feb 12, 2019 . 1 changed file with 82 additions and 83 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,87 @@ #!/bin/bash usage() { cat <<-END Split one git history into two. One of them only contains changes of specified files. The other contains other changes(or optionally contains full history). Usage: git-split [ -b <to-new-branch> ] [-r] [-m] [-s <prefix> ] <from>..[<to>] -- <fns> Synoposis: Initial: * 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 -m 82554be..HEAD -- html/ Results in: * 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 Arguments: <from> and <to> specifies history range to split. E.g. to split the latest 3 commit: git-split master~3..master "--" must present as a separator. <fns> are several file patterns. E.g.: git-split master~3..master -- src/foo.* deps/config.yaml Options: -b <new-branch> Create a new branch. When absent, create a temporary branch "tmpbranch". -o Create an orphan branch instead of one based on <from>. -r Remove <fns> from original history. Without -r, the original history will not be modified. -m Merge back the new branch to commmit "<to>". -m works only when -r is specified. -s <prefix> Strip <prefix> dir segments from paths that are splitted out. E.g.: git-split -s 3 base..master -- my/foo/dir/bar.txt removes "my/foo/dir". END } shlib_init_colors() @@ -598,89 +680,6 @@ fn_match() return 1 } new_branch=tmpbranch merge=0 remove=0 -
drmingdrmer revised this gist
Feb 12, 2019 . 1 changed file with 0 additions and 20 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -1,7 +1,5 @@ #!/bin/bash shlib_init_colors() @@ -530,24 +528,7 @@ git_diff_ln_new() }' } os_detect() @@ -616,7 +597,6 @@ fn_match() esac return 1 } usage() { -
drmingdrmer created this gist
Feb 12, 2019 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,910 @@ #!/bin/bash #include 'shlib.sh' begin #!/bin/sh shlib_init_colors() { Black="$( tput setaf 0)" BlackBG="$( tput setab 0)" DarkGrey="$( tput bold; tput setaf 0)" LightGrey="$( tput setaf 7)" LightGreyBG="$( tput setab 7)" White="$( tput bold; tput setaf 7)" Red="$( tput setaf 1)" RedBG="$( tput setab 1)" LightRed="$( tput bold; tput setaf 1)" Green="$( tput setaf 2)" GreenBG="$( tput setab 2)" LightGreen="$( tput bold; tput setaf 2)" Brown="$( tput setaf 3)" BrownBG="$( tput setab 3)" Yellow="$( tput bold; tput setaf 3)" Blue="$( tput setaf 4)" BlueBG="$( tput setab 4)" LightBlue="$( tput bold; tput setaf 4)" Purple="$( tput setaf 5)" PurpleBG="$( tput setab 5)" Pink="$( tput bold; tput setaf 5)" Cyan="$( tput setaf 6)" CyanBG="$( tput setab 6)" LightCyan="$( tput bold; tput setaf 6)" NC="$( tput sgr0)" # No Color } screen_width() { local chr="${1--}" chr="${chr:0:1}" local width=$(tput cols 2||echo 80) width="${COLUMNS:-$width}" echo $width } hr() { # generate a full screen width horizontal ruler local width=$(screen_width) printf -vl "%${width}s\n" && echo ${l// /$chr}; } remove_color() { # remove color control chars from stdin or first argument local sed=gsed which -s $sed || sed=sed local s="$1" if [ -z "$s" ]; then $sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" else echo "$s" | remove_color fi } text_hr() { # generate a full screen width sperator line with text. # text_hr "-" "a title" # > a title ----------------------------------------- # # variable LR=l|m|r controls alignment local chr="$1" shift local bb="$(echo "$@" | remove_color)" local text_len=${#bb} local width=$(screen_width) let width=width-text_len local lr=${LR-m} case $lr in m) let left=width/2 let right=width-left echo "$(printf -vl "%${left}s\n" && echo ${l// /$chr})$@$(printf -vl "%${right}s\n" && echo ${l// /$chr})" ;; r) echo "$(printf -vl "%${width}s\n" && echo ${l// /$chr})$@" ;; *) # l by default echo "$@$(printf -vl "%${width}s\n" && echo ${l// /$chr})" ;; esac } SHLIB_LOG_VERBOSE=1 SHLIB_LOG_FORMAT='[$(date +"%Y-%m-%d %H:%M:%S")] $level $title $mes' die() { err "$@" >&2 exit 1 } die_empty() { if test -z "$1" then shift die empty: "$@" fi } set_verbose() { SHLIB_LOG_VERBOSE=${1-1} } log() { local color="$1" local title="$2" local level="$_LOG_LEVEL" shift shift local mes="$@" local NC="$(tput sgr0)" if [ -t 1 ]; then title="${color}${title}${NC}" level="${color}${level}${NC}" fi eval "echo \"$SHLIB_LOG_FORMAT\"" } dd() { debug "$@" } debug() { if [ ".$SHLIB_LOG_VERBOSE" = ".1" ]; then local LightCyan="$(tput bold ; tput setaf 6)" _LOG_LEVEL=DEBUG log "$LightCyan" "$@" >&2 fi } info() { local Brown="$(tput setaf 3)" _LOG_LEVEL=" INFO" log "$Brown" "$@" } ok() { local Green="$(tput setaf 2)" _LOG_LEVEL=" OK" log "${Green}" "$@" } err() { local Red="$(tput setaf 1)" _LOG_LEVEL="ERROR" log "${Red}" "$@" } git_hash() { git rev-parse $1 \ || die "'git_hash $@'" } git_is_merge() { test $(git cat-file -p "$1" | grep "^parent " | wc -l) -gt 1 } git_parents() { git rev-list --parents -n 1 ${1-HEAD} | { read self parents; echo $parents; } } git_rev_list() { # --parents # print parent in this form: # <commit> <parent-1> <parent-2> .. git rev-list \ --reverse \ --topo-order \ --default HEAD \ --simplify-merges \ "$@" \ || die "'git rev-list $@'" } git_tree_hash() { git rev-parse "$1^{tree}" } git_ver() { local git_version=$(git --version | awk '{print $NF}') local git_version_1=${git_version%%.*} local git_version_2=${git_version#*.} git_version_2=${git_version_2%%.*} printf "%03d%03d" $git_version_1 $git_version_2 } git_working_root() { git rev-parse --show-toplevel } git_rev_exist() { git rev-parse --verify --quiet "$1" >/dev/null } git_branch_default_remote() { local branchname=$1 git config --get branch.${branchname}.remote } git_branch_default_upstream_ref() { local branchname=$1 git config --get branch.${branchname}.merge } git_branch_default_upstream() { git rev-parse --abbrev-ref --symbolic-full-name "$1"@{upstream} # OR # git_branch_default_upstream_ref "$@" | sed 's/^refs\/heads\///' } git_branch_exist() { git_rev_exist "refs/heads/$1" } git_head_branch() { git symbolic-ref --short HEAD } git_commit_date() { # git_commit_date author|commit <ref> [date-format] # by default output author-date local what_date="%ad" if [ "$1" = "commit" ]; then # commit date instead of author date what_date="%cd" fi shift local ref=$1 shift local fmt="%Y-%m-%d %H:%M:%S" if [ "$#" -gt 0 ]; then fmt="$1" fi shift git log -n1 --format="$what_date" --date=format:"$fmt" "$ref" } git_commit_copy() { # We're going to set some environment vars here, so # do it in a subshell to get rid of them safely later dd copy_commit "{$1}" "{$2}" "{$3}" git log -1 --pretty=format:'%an%n%ae%n%ad%n%cn%n%ce%n%cd%n%s%n%n%b' "$1" | ( read GIT_AUTHOR_NAME read GIT_AUTHOR_EMAIL read GIT_AUTHOR_DATE read GIT_COMMITTER_NAME read GIT_COMMITTER_EMAIL read GIT_COMMITTER_DATE export GIT_AUTHOR_NAME \ GIT_AUTHOR_EMAIL \ GIT_AUTHOR_DATE \ GIT_COMMITTER_NAME \ GIT_COMMITTER_EMAIL \ GIT_COMMITTER_DATE # (echo -n "$annotate"; cat ) | git commit-tree "$2" $3 # reads the rest of stdin ) || die "Can't copy commit $1" } git_object_type() { # $0 ref|hash # output "commit", "tree" etc git cat-file -t "$@" 2>/dev/null } git_object_add_by_commit_path() { # add an blob or tree object to target_path in index # the object to add is specified by commit and path local target_path="$1" local src_commit="$2" local src_path="$3" local src_dir="$(dirname "$src_path")/" local src_name="$(basename "$src_path")" local src_treeish="$(git rev-parse "$src_commit:$src_dir")" git_object_add_by_tree_name "$target_path" "$src_treeish" "$src_name" } git_object_add_by_tree_name() { # add an blob or tree object to target_path in index local target_path="$1" local src_treeish="$2" local src_name="$3" dd "arg: target_path: ($target_path) src_treeish: ($src_treeish) src_name: ($src_name)" local target_dir="$(dirname $target_path)/" local target_fn="$(basename $target_path)" local treeish if [ -z "$src_name" ] || [ "$src_name" = "." ] || [ "$src_name" = "./" ]; then treeish="$src_treeish" else treeish=$(git ls-tree "$src_treeish" "$src_name" | awk '{print $3}') if [ -z "$treeish" ]; then die "source treeish not found: in tree: ($src_treeish) name: ($src_name)" fi fi dd "hash of object to add is: $treeish" if [ "$(git_object_type $treeish)" = "blob" ]; then # the treeish imported is a file, not a dir # first create a wrapper tree or replace its containing tree dd "object to add is blob" local dir_treeish local target_dir_treeish="$(git rev-parse "HEAD:$target_dir")" if [ -n "target_dir_treeish" ]; then dir_treeish="$(git rev-parse "HEAD:$target_dir")" dd "target dir presents: $target_dir" else dd "target dir absent" dir_treeish="" fi treeish=$(git_tree_add_blob "$dir_treeish" "$target_fn" $src_treeish $src_name) || die create wrapper treeish target_path="$target_dir" dd "wrapper treeish: $treeish" dd "target_path set to: $target_path" else dd "object to add is tree" fi git_treeish_add_to_prefix "$target_path" "$treeish" } git_treeish_add_to_prefix() { local target_path="$1" local treeish="$2" dd treeish content: git ls-tree $treeish git rm "$target_path" -r --cached || dd removing target "$target_path" if [ "$target_path" = "./" ]; then git read-tree "$treeish" \ || die "read-tree $target_path $treeish" else git read-tree --prefix="$target_path" "$treeish" \ || die "read-tree $target_path $treeish" fi } git_tree_add_tree() { # output new tree hash in stdout # treeish can be empty local treeish="$1" local target_fn="$2" local item_hash="$3" local item_name="$4" { if [ -n "$treeish" ]; then git ls-tree "$treeish" \ | fgrep -v " $item_name" fi cat "040000 tree $item_hash $target_fn" } | git mktree } git_tree_add_blob() { # output new tree hash in stdout # treeish can be empty local treeish="$1" local target_fn="$2" local blob_treeish="$3" local blob_name="$4" { if [ -n "$treeish" ]; then git ls-tree "$treeish" \ | fgrep -v " $target_fn" fi git ls-tree "$blob_treeish" "$blob_name" \ | awk -v target_fn="$target_fn" -F" " '{print $1" "target_fn}' } | git mktree } git_workdir_save() { local index_hash=$(git write-tree) # add modified file to index and read index tree git add -u local working_hash=$(git write-tree) # restore index tree git read-tree $index_hash echo $index_hash $working_hash } git_workdir_load() { local index_hash=$1 local working_hash=$2 git_object_type $index_hash || die "invalid index hash: $index_hash" git_object_type $working_hash || die "invalid workdir hash: $working_hash" # First create a temp commit to restore working tree. # # git-read-index to index and git-reset does not work because deleted file in # index does not apply to working tree. # # But there is an issue with this: # git checkout --orphan br # git_workdir_load # would fails, because ORIG_HEAD is not a commit. local working_commit=$(echo "x" | git commit-tree $working_hash) || die get working commit git reset --hard $working_commit || die reset to tmp commit git reset --soft ORIG_HEAD || die reset to ORIG_HEAD git read-tree $index_hash || die "load saved index tree from $index_hash" } git_workdir_is_clean() { local untracked="$1" if [ "$untracked" == "untracked" ]; then [ -z "$(git status --porcelain)" ] else [ -z "$(git status --porcelain --untracked-files=no)" ] fi } git_copy_commit() { git_commit_copy "$@" } git_diff_ln_new() { # output changed line number of a file: <from> <end>; inclusive: # 27 28 # 307 309 # 350 350 # # Usage: # # diff working tree with HEAD: # git_diff_ln_new HEAD -- <fn> # # diff working tree with staged: # git_diff_ln_new -- <fn> # # diff staged(cached) with HEAD: # git_diff_ln_new --cached -- <fn> # # in git-diff output: # for add lines: # @@ -53 +72,8 # # for remove lines: # @@ -155 +179,0 git diff -U0 "$@" \ | grep '^@@' \ | awk '{ # @@ -155 +179,0 # $1 $2 $3 l = $3 gsub("^+", "", l) # add default offset: ",1" split(l",1", x, ",") # inclusive line range: x[2] = x[1] + x[2] - 1 # line remove format: @@ -155, +179,0 # do need to output line range for removed. if (x[2] >= x[1]) { print x[1] " " x[2] } }' } # test: # # file to root # git reset --hard HEAD # git_object_add_by_commit_path a HEAD dist/shlib.sh # git status # # dir to root # git reset --hard HEAD # git_object_add_by_commit_path a HEAD dist # git status # # file to folder # git reset --hard HEAD # git_object_add_by_commit_path a/b/c HEAD dist/shlib.sh # git status # # dir to folder # git reset --hard HEAD # git_object_add_by_commit_path a/b/c HEAD dist # git status os_detect() { local os case $(uname -s) in Linux) os=linux ;; *[bB][sS][dD]) os=bsd ;; Darwin) os=mac ;; *) os=unix ;; esac echo $os } mac_ac_power_connection() { # Connected: (Yes|No) system_profiler SPPowerDataType \ | sed '1,/^ *AC Charger Information:/d' \ | grep Connected: } mac_power() { # $0 is-battery exit code 0 if using battery. # $0 is-ac-power exit code 0 if using ac power. local cmd="$1" local os=$(os_detect) if [ "$os" != "mac" ]; then err "not mac but: $os" return 1 fi case $cmd in is-battery) mac_ac_power_connection | grep -q No ;; is-ac-power) mac_ac_power_connection | grep -q Yes ;; *) err "invalid cmd: $cmd" return 1 ;; esac } fn_match() { # $0 a.txt *.txt case "$1" in $2) return 0 ;; esac return 1 } #include 'shlib.sh' end usage() { cat <<-END Split one git history into two. One of them only contains changes of specified files. The other contains other changes(or optionally contains full history). Usage: git-split [ -b <to-new-branch> ] [-r] [-m] [-s <prefix> ] <from>..[<to>] -- <fns> Synoposis: Initial: * 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 -m 82554be..HEAD -- html/ Results in: * 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 Arguments: <from> and <to> specifies history range to split. E.g. to split the latest 3 commit: git-split master~3..master "--" must present as a separator. <fns> are several file patterns. E.g.: git-split master~3..master -- src/foo.* deps/config.yaml Options: -b <new-branch> Create a new branch. When absent, create a temporary branch "tmpbranch". -o Create an orphan branch instead of one based on <from>. -r Remove <fns> from original history. Without -r, the original history will not be modified. -m Merge back the new branch to commmit "<to>". -m works only when -r is specified. -s <prefix> Strip <prefix> dir segments from paths that are splitted out. E.g.: git-split -s 3 base..master -- my/foo/dir/bar.txt removes "my/foo/dir". END } new_branch=tmpbranch merge=0 remove=0 orphan=0 to_strip=0 while getopts "b:p:s:morh" opname; do case $opname in b) new_branch=$OPTARG ;; m) merge=1 ;; o) orphan=1 ;; r) remove=1 ;; s) to_strip="$OPTARG" ;; h) usage exit 0 ;; [?]) usage exit 1 ;; esac done shift $(($OPTIND - 1)) if [ ".$2" != ".--" ]; then usage exit 1 fi range=$1 shift shift fns="$@" base=${range%..*} if [ ".$base" = ".$range" ]; then usage exit 1 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=$(git_hash $tip) new_hash= merged_hash= extract() { local parent=$1 local tree= local commit= for c in $(git log --reverse $base..$tip_hash --format="%H" -- $fns); do info "== to pick $fns from:" git --no-pager log --color --stat --decorate --pretty=oneline --abbrev-commit -M -n1 $c info "== END ==" # mes=$(git log -n1 --format="%B" $c) # force prefix when diff, since .gitconfig could hide prefix git diff --src-prefix="" --dst-prefix="" --binary $c~ $c -- $fns | git apply -p $to_strip --index info "== after patch $c ==" git status --short info "== END ==" # manually commit index tree=$(git write-tree) dd "tree to commit: $tree" git ls-tree -r $tree dd "end of tree" dd parent: $parent commit=$(git_copy_commit "$c" "$tree" "-p $parent") dd "new commit:" $commit git reset --hard $commit parent=$commit done return 0 } build_new_branch() { info "== build_new_branch ==" git checkout --orphan $new_branch || die checkout --orpahn $new_branch if [ "$orphan" = "1" ]; then git reset --hard git commit --allow-empty -m "init-root" || die commit init-root else info reset $new_branch to $base git reset --hard $base fi git clean -dxf || die clean -dxf info "HEAD hash: $(git_hash HEAD)" extract $(git_hash HEAD) \ && new_hash=$(git_hash HEAD) \ && \ if [ "$new_branch" = "tmpbranch" ]; then git checkout --detach HEAD \ && git branch -d tmpbranch else : fi \ && info "== new branch $new_branch hash is $new_hash == " } discard_changes() { info "== discard_changes ==" # git checkout would fail if one of the files does not exists. # thus newly added file impedes the whole process. # git checkout ab12345 -- nonexistent # local script='for f in '"$fns"'; do git rm -r -f $f; git checkout '"$base"' -- $f; done' local script='for f in '"$fns"'; do echo "== $f =="; git rm -r -f $f; git status -- $f >&2; git checkout '"$base"' -- $f; git status -- $f >&2; done' git checkout --detach $tip_hash \ && info "== 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 ] \ && info "== confirmed that no changes through $base..HEAD" git status --short info "== END ==" info "== history ==" git --no-pager log --color --stat --decorate --pretty=oneline --abbrev-commit -M $base..HEAD info "== END ==" } merge_them() { info "== merge splitted branch back to $tip ==" \ && \ if [ ".$new_branch" = "tmpbranch" ]; then git merge --commit --no-edit --no-ff $new_hash else git merge --commit --no-edit --no-ff $new_branch fi \ && merged_hash=$(git_hash) \ && info "merged hash: $merged_hash" } is_same_as_original() { info "== 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 \ && info "== $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 \ && info "== $tip set to $merged_hash" fi } info "split $fns from $range" \ && build_new_branch \ && { [ "$remove" = "0" ] && exit 0 || ``; } \ && discard_changes \ && { [ "$merge" = "0" ] && exit 0 || ``; } \ && merge_them \ && is_same_as_original \ && update_tip_ref \ && git checkout $tip >/dev/null 2>/dev/null \ && info "== Done =="