git, a learn by doing attempt
Some things in life are best learnt by practise and git happens to be one such thing. With the current day internet and the plethora of resources available learning has never been more easy. This article introduces you how to learn while having fun.
What is this article about?
This article is my attempt at solving the 23 exercises from https://gitexercises.fracz.com/. While some of the solutions do match exactly with the solutions shown after one successfully solves the exercises, the following collection of solutions are bit detailed in their descriptions along with some links pointing to resources which I found to be helpful in learning and solving the exercises.
Exercise 23 is the most interesting one and is also one of the solution I have spent some time working with gitpython module in developing an alternate method to find bug, in contrast to the git bisect solution presented on the main website.
Exercise 7: save-your-work
To learn about stashing and cleaning https://git-scm.com/book/en/v2/Git-Tools-Stashing-and-Cleaning
$> git stash
# or
$> git stash push
$> nano bug.txt # in the text editor delete the bud line
$> git commit -am "remove bug"
$> git stash apply
# or
$> git stash apply stash@{0}
$> nano bug.txt # in the text editor add the line "Finally, finished it!" to the end
$> git commit -am "finish"
$> git verify
Exercise 8: change-branch-history
To learn about git rebase https://git-scm.com/docs/git-rebase
$> git checkout change-branch-history
$> git rebase hot-bugfix
$> git verify
Exercise 9: remove-ignored
Solution and explanation https://stackoverflow.com/questions/1274057/how-to-make-git-forget-about-a-file-that-was-tracked-but-is-now-in-gitignore
$> git rm --cached ignored.txt
$> git commit -am "untrack ignored.txt"
$> git verify
Exercise 17: executable
Under the hood details https://stackoverflow.com/questions/40978921/how-to-add-chmod-permissions-to-file-in-git
$> git update-index --chmod=+x script.sh
$> git commit -m "make executable"
$> git verify
Exercise 18: commit-parts
$> git add --patch file.txt
# split the hunks with 's'
# Stage this hunk [y,n,q,a,d,/,j,J,g,s,e,?]?
#
# Here is a description of each option:
#
# y stage this hunk for the next commit
# n do not stage this hunk for the next commit
# q quit; do not stage this hunk or any of the remaining hunks
# a stage this hunk and all later hunks in the file
# d do not stage this hunk or any of the later hunks in the file
# g select a hunk to go to
# / search for a hunk matching the given regex
# j leave this hunk undecided, see next undecided hunk
# J leave this hunk undecided, see next hunk
# k leave this hunk undecided, see previous undecided hunk
# K leave this hunk undecided, see previous hunk
# s split the current hunk into smaller hunks
# e manually edit the current hunk
# ? print hunk help
# select each hunk with 'y' or 'n'
$> git commit -m "task 1 related"
$> git commit -am "rest of the content"
$> git verify
Exercise 19: pick-your-features
# get an idea of the logs currently and know the SHA-1's needed
$> git log --oneline --decorate --graph --all -8
$> git checkout pick-your-features
$> git cherry-pick feature-a
# or
$> git cherry pick SHA-1 of feature-a commit
$> git cherry-pick feature-b
# or
$> git cherry pick SHA-1 of feature-b commit
$> git merge --squash feature-c
# resolve conflict
$> git commit -am "Complete Feature C"
$> git verify
Exercise 20: reabse-complex
Explanation from git-book https://git-scm.com/book/en/v2/Git-Branching-Rebasing
$> git rebase --onto your-master issue-555 rebase-complex
# This basically says, “Take the rebase-complex branch, figure out the patches since it diverged from the issue-555 branch,
# and replay these patches in the rebase-complex branch as if it was based directly off the your-master branch instead.”
$> git verify
Exercise 22: find-swearwords
$> git log -S shit
# make a note of the commits where a word "shit" was introduced
$> git rebase -i HEAD~105
# replace 'pick' with 'edit' for those commits
# check which files were modified
$> git log -p -1
# replace 'shit' with 'flower' in list.txt
$> git add list.txt
$> git commit --amend
$> git rebase --continue
# check which files were modified
$> git log -p -1
# replace 'shit' with 'flower' in words.txt
$> git add words.txt
$> git commit --amend
$> git rebase --continue
# check which files were modified
$> git log -p -1
# replace 'shit' with 'flower' in words.txt
$> git add words.txt
$> git commit --amend
$> git rebase --continue
$> git verify
Exercise 23: find-bug
- First method using git bisect
$> git checkout find-bug $> git bisect start $> git bisect bad $> git bisect good 1.0 # the grep documentation for -v flag doesn't make sense with what the author fracz mentioned and also # I couldn't see the binary output of '1' or '0' as given in the hints $> git bisect run sh -c "openssl enc -base64 -A -d < home-screen-text.txt | grep -v jackass" $> git push origin 4d2725ac4c874dbb207770001def27aed48e9ddb:find-bug
Second method using gitpython
a. Brute-force method iterate through the commits one-by-one from oldest to the newest and break when the word 'jackass' got introduced
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from git import Repo
import base64
# create a repo object to query
repo = Repo("./exercises/")
# switch to the required branch
repo.git.checkout('find-bug')
# get all commits between HEAD and tag 1.0
commits = repo.iter_commits('HEAD...1.0')
for x in reversed(list(commits)):
# move the head to the commit 'x'
repo.head.set_reference(x)
# get a tree object and query the blog based on the file name and read the content from it's data stream
read_this = repo.tree(x)['home-screen-text.txt'].data_stream.read()
# reader = repo.config_reader()
# read_this = open('./exercises/home-screen-text.txt')
# decode the base64 bytes
content = base64.b64decode(read_this).decode('ascii')
# check if 'jackass' is present in the file, if yes, print the SHA-1 and exit
if 'jackass' in content:
print("The commit where the bug 'jackass' was introduced is:", x.hexsha)
break
b. Binary search method (newest first), very similar to what the *git bisect* command does
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from git import Repo
import base64
# create a repo object to query
repo = Repo("./exercises/")
# switch to the required branch
repo.git.checkout('find-bug')
# get all commits between HEAD and tag 1.0
commits = repo.iter_commits('HEAD...1.0')
commits = list(commits)
search_length = len(commits)
idx = search_length//2
last_idx = 0
found_at = None
for i in range(0, round(math.log2(search_length))):
repo.head.set_reference(commits[idx])
read_this = repo.tree(commits[idx])['home-screen-text.txt'].data_stream.read()
content = base64.b64decode(read_this).decode('ascii')
if 'jackass' in content:
found_at = idx
print(commits[idx].hexsha)
if i == 0:
update = (search_length - idx)//2
else:
update = (last_idx - idx)//2
last_idx = idx
idx += abs(update)
else:
update = (idx - last_idx)//2
last_idx = idx
idx -= abs(update)
print("The commit where the bug 'jackass' was introduced is:", commits[found_at].hexsha)