Skip to content

Instantly share code, notes, and snippets.

@theaspect
Last active August 29, 2015 14:14
Show Gist options
  • Save theaspect/09d0c0e516d41bfcdb7f to your computer and use it in GitHub Desktop.
Save theaspect/09d0c0e516d41bfcdb7f to your computer and use it in GitHub Desktop.
Demo history editing in git
#!/bin/bash
#
# Constantine Linnick <theaspect@gmail.com>
# https://gist.github.com/theaspect/09d0c0e516d41bfcdb7f
set -e
BASE="$PWD"
ORIGIN="$PWD/origin"
ALICE="$PWD/alice"
BOB="$PWD/bob"
# Skip curling funny request messages from whatthecommit.com and waiting after echo
FAST=false
FUNNY=false
function check {
if ! hash $1 2>/dev/null; then
echo $1 require
exit 1
fi
}
check git
if $FUNNY; then check curl; fi;
function wait { if ! $FAST; then read -s -n1; fi; }
function msg { if $FUNNY; then curl http://whatthecommit.com/index.txt 2>/dev/null; else echo "Message $RANDOM" ; fi; }
function command { echo -e "\033[93m$@\033[0m"; }
function header { echo -e "\033[1;32m$@\033[0m\n"; }
function say { echo -e "\033[32m$@\033[0m\n"; }
function sayw { echo -e "\033[32m$@\033[0m\n"; wait; }
function git_origin { cd $ORIGIN ; command "git $@"; git --no-pager "$@"; echo -e "\n"; }
function git_alice { cd $ALICE ; command "git $@"; git --no-pager "$@"; echo -e "\n"; }
function git_bob { cd $BOB ; command "git $@"; git --no-pager "$@"; echo -e "\n"; }
function log_origin { git_origin log --date-order --pretty=format:"%C(yellow)%h%Creset%C(green)%Creset%C(auto)%d%Creset %s %C(magenta)<%an>" --decorate --all --graph $@; }
function log_alice { git_alice log --date-order --pretty=format:"%C(yellow)%h%Creset%C(green)%Creset%C(auto)%d%Creset %s %C(magenta)<%an>" --decorate --all --graph $@; }
function log_bob { git_bob log --date-order --pretty=format:"%C(yellow)%h%Creset%C(green)%Creset%C(auto)%d%Creset %s %C(magenta)<%an>" --decorate --all --graph $@; }
function reflog_alice { log_alice "$(git rev-list --reflog --all)"; }
function reflog_bob { log_bob "$(git rev-list --reflog --all)"; }
function change_alice { cd $ALICE; if [ ! -z "$1" ] ; then echo $RANDOM >> "$1.txt"; echo -e "\033[96mAlice edited $1.txt\033[0m"; else echo $RANDOM >> alice.txt; echo -e "\033[96mAlice edited alice.txt\033[0m"; fi; }
function change_bob { cd $BOB; if [ ! -z "$1" ] ; then echo $RANDOM >> "$1.txt"; echo -e "\033[96mBob edited $1.txt\033[0m" ; else echo $RANDOM >> bob.txt ; echo -e "\033[96mBob edited bob.txt\033[0m" ; fi; }
function commit_alice { cmsg="$(msg)"; change_alice "$@"; command "git add -A; git --no-pager diff --cached; git commit -m \"$cmsg\""; git add -A; git --no-pager diff --cached; git commit --author="alice <alice@example.com>" -m "$cmsg"; echo -e "\n"; }
function commit_bob { cmsg="$(msg)"; change_bob "$@"; command "git add -A; git --no-pager diff --cached; git commit -m \"$cmsg\""; git add -A; git --no-pager diff --cached; git commit --author="bob <bob@example.com>" -m "$cmsg"; echo -e "\n"; }
header Init
say Cleanup dirs
rm -rf "$ORIGIN" "$ALICE" "$BOB"
mkdir $ORIGIN
mkdir $ALICE
mkdir $BOB
say Init repos
# We cannot to push to non bare repository
git_origin init --bare
git_alice clone $ORIGIN .
git_bob clone $ORIGIN .
sayw Branches is simply labels, and nothing disappleras without a traces."\n"\
To better understand this look at cool visualisation "\n"\
at http://tonsky.me/blog/reinventing-git-interface/ "\n"\
"\n"\
Everything is ready, press any key to continue or ^c to stop. "\n"\
During pauses feel free to play with repos and inspect its state
header 1. Restore lost commits
say Alice did some commits...
commit_alice
commit_alice
commit_alice
say and now have small tree
log_alice
sayw Somehow she droped first two commits
git_alice reset --hard HEAD^^
log_alice
sayw OH MY GOD, where is whole day work
sayw Well, in ref-log. We can collect all revs from ref-log...
git_alice rev-list --reflog --all
sayw and pass it to git log with '$(git rev-list --reflog --all)'
reflog_alice
sayw We can checkout to lost commits but HEAD will be detached
# Sorting order are not stable even with --topo-order
git_alice checkout "$(git rev-list --reflog --topo-order --all | sed -n '1p')"
log_alice
sayw we can even commit to detached head
commit_alice
reflog_alice
sayw To get commits back, we can create branch...
git_alice checkout -b lost_commits
log_alice
sayw checkout master...
git_alice checkout master
sayw and merge it...
git_alice merge lost_commits
sayw Let\'s check
log_alice
sayw Drop unnecessary branch
git_alice branch -d lost_commits
git_alice push origin
header 2. Remove single pushed commit
sayw Alice commited something and pushed to origin
commit_alice
git_alice push origin
log_alice
sayw But minute after she wanted to revert last commit in origin
sayw If Bob not yet pulled, just reset --hard...
git_alice reset HEAD^ --hard
log_alice
sayw and push --force
git_alice push origin --force
log_alice
header 3. Drop non last shared commit
sayw Alice did some work
# If file does not exists before it can raise Deleted-Unmerged state and automatic rebase will fail
commit_alice bob
commit_alice bob
git_alice push origin
sayw Bob pull origin, push commit, and continue his work
git_bob pull origin
commit_bob
git_bob push origin
commit_bob
sayw Alice pull Bob\'s changes
git_alice pull origin
say Alice\'s repo
log_alice
say Bob\'s repo
log_bob
sayw Alice wants to drop last changes in Bob\'s file using interactive rebase
# Weird way to comment first (NR==1) line in passed todo file by git, during rebase you obviuously do this by hand
# See http://git.kernel.org/cgit/git/git.git/tree/git-rebase--interactive.sh?id=821881d88d3012a64a52ece9a8c2571ca00c35cd#n164
# Also without || true all scipt will fail
GIT_SEQUENCE_EDITOR="awk '{line=(NR==1)?\"#\" \$0:\$0; print line; print line >> (FILENAME \"~\") }END{system (\"mv \" FILENAME \"~ \" FILENAME)}'" git_alice rebase -i HEAD^^ || true
sayw Obviuously we have conflict
git_alice diff bob.txt
sayw We resolve it...
cd $ALICE && sed -i -e "2d;3d;4d;6d" bob.txt
git_alice add -A
git_alice commit -m "Resolve rebase conflict"
sayw and continue rebase
# If we do not add -m message editor will popup
git_alice rebase --continue
sayw Alice\'s history now splitted...
log_alice
sayw and Alice push force this to origin
git_alice push origin --force
sayw Bob fetch origin...
git_bob fetch origin
sayw and see
log_bob
sayw Bob simply pulls master without conflicts...
git_bob pull origin
sayw and see
log_bob
sayw Note, nothing dropped, and old commits still in reflog
reflog_bob
header The end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment