Created
April 27, 2012 07:52
-
-
Save james-dowling/2507154 to your computer and use it in GitHub Desktop.
Unformatted notes from my quick-fire rundown on what I learned from Ben H's Workshow
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 characters
These are my notes from my hands-on talk entitled "I went to Ben H's Git Workshop and all I got was this stupid Knowledge" | |
Lots of people asked me to tweet them, even thought they are just a mix of notes-to-self, things to say, and things to type. | |
Enjoy FWIF | |
BEFORE ANYTHING, CLONE RAILS AND CREATE A COMMIT | |
So, all git info is kept in the .git folder in the root folder of the repo | |
let's look in there….. | |
cd .git | |
ls -la | |
The first file we'll look at is HEAD | |
if we cat that, we get a link to a ref file | |
that file will be in .git/refs | |
refs has a file for every branch | |
If we cat the file referenced in HEAD, we get a pointer to the commit we've got checkout out right now | |
That commit will be in objects. | |
cd to .git/objects | |
ls | |
Now, we can't see any pointers to tell us where to go next… so what we'll do is look at the first two digits of the SHA in master | |
and we cd there! | |
and our filename is not the whole name of the sha (that would be redundant), but instead, the rest of the sha | |
CAT THE COMMIT | |
So, that's no good to us. The reason it's all garbled is that it's a zip file. | |
TRY TO UNZIP THE FILE (Doesn't work) cat filename | gunzip | |
So, to save space, git removes the headers on the zip file and this breaks gunzip. | |
I've thrown together a command line alias called "gitgrow" | |
DEMO | |
alias gitgrow='ruby -rzlib -e '\''print Zlib::Inflate.new.inflate(STDIN.read)'\'' < ' | |
basically, the tool has no real-world use :P | |
Now, what's very interesting here is that the sha representing the commit is not random, but in fact a sha1 digest of the contents: | |
gitgrow *commit_file_name* | openssl dgst -sha1 | |
So, once the commit is created, git creates a folder named after the first two letters of that sha1, and then leaves the commit itself zipped up, with no headers in there with the name of the remainder of that sha1 | |
MAGIC! | |
Ok, so we see in the commit that its parents are listed in the commit (only one here) | |
So, if you follow me, the name of a commit is dependent on the name of it's parent, because its parent's name helped build the sha that it is named after. And so on….. all the way down the git tree. | |
CREATE A NEW BRANCH - SHOW IT'S REFERENCE IN HEADS | |
SWITCH BACK TO MASTER | |
CREATE A NEW COMMIT | |
SHOW MASTER POINTING TO A NEW COMMIT, and the new branch's head pointing to the old commit | |
'NOW LET's TALK ABOUT MERGE OPTIONS" | |
AND ON THE WAY THERE<,I'LL SHOW HOW MAN PAGES WORK FOR GIT COMMANDS | |
man git-merge | |
FIND --no-ff (using `/`) | |
NOW LETS TALK ABOUT fast forwarding | |
go: | |
git checkout -b new_branch | |
touch hello | |
git add hello | |
git commit -m "a commit on the new branch" | |
git checkout master | |
git merge new_branch | |
git status | |
`say omfg` - no merge commit because it "fast forwarded" | |
this is only good if you're the only person working on the project, and you never have more than one feature branch going at one time. | |
So, use --no-ff pretty-much always | |
Still, if you ever start a branch, throw in a few commits, then realise you didn't need a feature branch, you can explicitly specify a --ff-only merge. So that'll only work if nobody has merged in anything since you created the branch. | |
MORE ON MERGES: | |
octopus merges allow merges of multiples heads in the some commit. | |
when this happens, it starts to become more obvious what the point of ^ and ~ is | |
^ goes to the first parent | |
~ goes to the next parent at the same level | |
so you can checkout any one of those with the refspec HEAD^2~3 | |
Does anybody actually do this? Yes. Linus Torvalds. The end. I don't even know why he does it. | |
I guess to amalgamate a release …. or something | |
If you go through the repo for git, you'll see plenty of these. | |
GIT LOG | |
git log SHOWS A LIST OF COMMITS | |
git log -p ALSO SHOWS THE CONTENTS OF EACH COMMIT | |
git log --graph | |
git smart-log SHOWS A GRAPH OF COMMITS JUST LIKE GLEN MADDERN LIKES IT | |
IT DOES THIS BY USING THE --pretty FLAG AND SPECIFYING A FORMAT | |
SHOW THEM THE COMMAND AT THE TOP | |
git-smart always shows you which git command it used. | |
REFLOG | |
git reflog shows everything you've done ever | |
It's a really useful tool if you want to know what the hell you just did, other than what commits you've made | |
another way to do that is to use 'git log -g' | |
IS THE REFLOG JUST A SIMPLIFIED REPOSITORY?? | |
Best thing about the reflog is that you can `reset --hard` to wherever head was at before a history changing commit like a reflog | |
Do a | |
git reset --hard HEAD@{5} | |
ORIG_HEAD | |
special file in .git/ | |
Any history "changing" action will keep a record of where HEAD was pointing at before it created a whole new treelet | |
So instead of going | |
git reset --hard HEAD@{0} | |
you can go | |
git reset --hard ORIG_HEAD | |
Admittedly, this is a less relevant tool, given the advent of the reflog | |
REBASING | |
Ben hates to rebase. It's basically not good. | |
….because it duplicates commits. (more on that later) | |
If you say, branched off a feature branch, and then merge it all together, all the duplicates end up on the master branch at the end usually causing conflicts. | |
So, only use it for pull-type scenarios! | |
COMPARING TWO BRANCHES | |
*********************************** | |
git diff master..production | |
SHOWS THE DIFFERENCES BETWEEN MASTER AND PRODUCTION | |
========================= | |
git log master..production | |
SHOWS WHAT COMMITS ARE ON PRODUCTION BUT NOT ON MASTER | |
*********************************** | |
GIT HAS GARBAGE COLLECTION | |
this is the only way in which commits can get deleted! | |
It happens when dangling commits get dropped off the end of the reflog | |
git gc --auto HAPPENS WHENEVER YOU DO A COMMIT | |
checks whether git thinks any garbage collection is necessary. | |
git gc HAPPENS RIGHT AWAY | |
What does it do? | |
it does a pack on the objects in the objects folder | |
This deletes duplicates, and mostly just clears out the objects folder | |
if you want to learn about git pack files, check out these two sites: | |
gitref.org | |
progit.org | |
There is a bad thing about pack files | |
Pushes are sometimes bigger than they have to be, as they may contain redundant code | |
Whenever you do a pull from github, you usually just download a single pack file. | |
Neat trick…… branch from a commit | |
git checkout -b branchname SHA | |
can use ~ and ^ with this | |
INTERACTIVE REBASES | |
git rebase -i SHA | |
opens editors for each of these commits | |
These gives us a whole bunch of optional keywords to use. | |
eg | |
pick: use this commit as is | |
reword: change message, but use commit | |
edit: we'll ammend the commit before commiting | |
lots more | |
You can even reorder the commit's order in the tree! | |
Don't tell anybody, but you can actually alter your history this way! | |
A good way to make Glen Maddern cry | |
If you want to rebase your entire current branch this way…. | |
just choose the commit **before** the earliest commit you want to rebase | |
SO, DO REBASES REWRITE HISTORY? | |
Complicated question actually. | |
In fact, you'll find that the reflog will keep the original history, for a time :) | |
You'll remember that all commits' shas are products of their parents shas? | |
That's why a rebase is the only way to seriously fuck with your history more than one commit ago. New shas, influencing their childrens' shas need to be created for all descendants of the interactively changed commit. | |
As a side note, git doesn't delete the old commits, just creates the new ones, and points your branch's ref file to the latest of the new commits. So, again, history is immutable. if you ever want to go back how it was before, you can just check your reflog for the sha of the commit before the rebase happened. | |
If you're going to do interactive rebases to rewrite history, make sure the commits you're rewriting aren't referenced from other branches. As you can imagine, this creates scary duplication in the repo, and thus causes incredibly stressful merges. | |
GIT STATUS | |
this is actually verbose by default | |
use -s to make it short. | |
GIT ADD | |
git add -u | |
same as git add --update | |
like an add for a whole directory, but never adds new files | |
also, does a `git rm` on any files which have been removed | |
git add -A | |
So, does the same thing as --update | |
Obviously, will add new files too | |
git log SHA | |
SHOWS THE GIT LOG *AT THE TIME THAT COMMIT WAS MADE* | |
TRACKING REMOTE BRANCHES | |
A remote tracking branch is one which you will pull or fetch from automatically, without having to specify the branch in the command line. Just go `git pull` or `git fetch` | |
THE GRAPH | |
Just the overall git tree. | |
A TREELET | |
Stupid name imo | |
The tree which lead to a single refspec | |
A refspec can be a SHA, The first 8 digits of a SHA, or HEAD~ etc | |
GIT SMART-PULL | |
will do a merge on occasion! | |
If there are no local commits, then it'll merge, otherwise it'll do a rebase -p | |
GIT PULL --REBASE | |
Never ever do this | |
For one thing, it's actually broken - it can delete your work from the graph occasionally! | |
Also, it doesn't do a -p with the rebase, so merges get squashed!!! | |
The bad thing about merges getting squashed, is when there are multiple commits which have been merged in, you can just revert the merge. But when they're all in the same thread of commits, you have to revert them all individually. Pretty crap to have to do on a saturday morning on-call | |
GIT RESET | |
--soft : RESETS THE HEAD TO THE COMMIT SPECIFIED. ALL LATER CHANGES ARE "staged" | |
--hard : SAME AS --soft, BUT DISCARDS CHANGES LATER THAN THE SPECIFIED COMMIT | |
GIT CLEAN | |
does nothing by default | |
only works if you specify `-f` | |
it deletes untracked files | |
if you'd like to do a dry-run, you can use `-n` instead of `-f` | |
be very careful to specify the correct paths!!!! | |
if you use -x instead of -f, it will even delete file which match lines in your .gitignore! | |
Ben says that it's basically used by psychopaths on their prod machines | |
ooorrrr…….. for getting rid of bullshit logs, test generated attachments etc | |
basically nuts. never do this | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment