Dialog with an intern, or, alternately, some random unixy tips and tricks

August 08, 2016

mmeehan: actually i may be able to ask it on here if u have a moment

jfo: sure

mmeehan: so for the [work task related] thing, I added another output field [task thing stuff] and then when i recompile, i get a GET request error for adding that [thing]

jfo: ah

mmeehan: and i wasn’t sure if i was missing some other step?

jfo: show me the diff?

mmeehan: sure…

mmeehan: this is kind of difficult to read, without the syntax highlighting…

jfo: ooohhh pro tip!

mmeehan: ok

jfo: how did you generate this

mmeehan: git diff commit#..commit# | gist

jfo: cool!

jfo: so, gist has a type param

jfo: gist -t diff or whatever the filetype is…

jfo: which will syntax highlight for you

mmeehan: [url to syntax highlighted gist of a diff]

jfo: cool!

mmeehan: oO)o0

mmeehan: thank u!!!

jfo: want another related awesome trick?

mmeehan: YES

mmeehan: i like pro tips

jfo: this is a good one

jfo: this will blow people’s minds

mmeehan: ok i want to blow all the minds gimme

jfo: we think of diffs as just a git thing probably

jfo: but they are not

mmeehan: lol

jfo: man diff to read about the diff tool, which is separate

mmeehan: woooah

jfo: diff is actually a like, really specific file format that originated in perl land, I think

well, actually, yeah… patch was written by Larry Wall in 1985, who later went on to write perl, but diff was a unix utility before that, authored in the early 70’s. patch was capable of taking the output from diff and applying the changes to a file or a group of files.

jfo: here’s another one…

jfo: man patch

jfo: which you can use to apply a patch file

jfo: a “patch” is what git diff produces and it’s what git apply applies; a “patchfile” contains the plaintext diff.

Here’s a thing to try! Run some commands in an empty directory (not a git repo just an empty directory…)

printf 'hi mom\nhi dad\n' > 1.txt
printf 'hi mom\nhi dad\nhello world\n' > 2.txt
cat 1.txt
cat 2.txt

Just makin’ some files! How are they different?

diff 1.txt 2.txt
diff 2.txt 1.txt
diff 1.txt 2.txt > look_a_patch_file_omg
cat look_a_patch_file_omg

We saved those differences into the patchfile… notice the name is inconsequential, it doesn’t need a suffix to describe a filetype.

Let’s apply that patch, then!

patch 1 < look_a_patch_file_omg


patch 2 < look_a_patch_file_omg
cat 1.txt
cat 2.txt

This is basically all git diff and git apply do, but the diff is taken from what a file looked like at a specified ref (a ref[erence] is a commit hash, or a branch name, or one of a few other things…), rather than another file on disk like above. I do not know why git apply isn’t git patch.

jfo: it’s the same stuff

jfo: just wrapped into the git plumbing

jfo: so this is all to say that you can apply a diff directly to a working tree

jfo: with git apply patch.diff or whatever

jfo: which is what I’ll do to “checkout” your changes in a really lightweight way

mmeehan: O0o0o0o i get it woow ahh thats so helpful! So whenever you want to see what someone else’s diff does on your own machine you just git apply patch.diff

jfo: yup! or at least that is one way to do it. It’s outside of a lot of the git tracking and checking out and branches and stuff… it’s as if you typed all the changes in yourself. It doesn’t commit anything for you, which means you can get back to a clean head with a git reset --hard if that is what you want

jfo: but you might have already known that…

mmeehan: i didn’t know about that

jfo: oh good!

jfo: well want more? theres another level that’s even more cool

jfo: I’m on a pedagogical roll here

mmeehan: go for it hahah

jfo: well, git apply reads a patchfile, just like patch does. As far as I can tell they pretty much do the same thing basically, just with different invocation syntax and I am sure there are more subtle differences but the main point is they both apply a patch file.

jfo: but

jfo: what about [url to raw gist of diff on github]

jfo: that is the diff we want

jfo: in plaintext… just that it’s living on the server right now.

jfo: so

jfo: curl [url]

jfo: would fetch the diff from the server and print it to standard out.

jfo: so from there…

jfo: you could curl [url] > /tmp/file.diff, which redirects the diff into a temporary file,

omg did you know about /tmp? It’s the best. Basically the whole directory gets cleaned out by the OS every so often, so for ephemeral crap that you just want to hold onto for a little while (tmporarily?) you can just put it in /tmp and you will get cleaned up after.

jfo: and then git apply /tmp/file.diff in the working tree

jfo: (the suffixes are inconsequential btw, just conventional, you don’t have to call it .diff, it could be whatever you want)

jfo: but doesn’t it seem like you should be able to do that in one step?

mmeehan: yes it sure does

jfo: well you sure can!

jfo: in at least TWO different ways!

mmeehan: !!


mmeehan: hahaha

jfo: you can pipe it like curl [url] | git apply

mmeehan: so piping sends the output of the first command to the second command?

jfo: basically yes, when used like that the stdout stream coming from the left side command feeds into the stdin stream of the right side command. You can chain things as much as you want, btw, it’s the unix way tm. Want to know how many README files are on your whole computer?

tree / | grep README | wc -l

jfo: That’s a terrible and inefficient way to do that, but it would totally work. I always forget the flag for multi pattern grepping and end up piping things through multiple instances of grep instead, like a wally.

mmeehan: what is curl exactly?

mmeehan: i feel like i have seen it in a few different contexts…

jfo: curl is a command line http client

jfo: you can do all sorts of stuff with it,

jfo: omg

jfo: this is so fun

jfo: check this out

jfo: open a terminal window

jfo: and run nc -l localhost 5000

mmeehan: what does nc do?

jfo: it’s short for ‘netcat’, which is a simple little utility that reads and writes data across networks.

jfo: ok ready?

mmeehan: yea

jfo: then open another terminal window

mmeehan: ok

jfo: and run curl localhost:5000

mmeehan: ok ok

mmeehan: what is happening

jfo: haha did it work?

mmeehan: o0o0o i see i wasn’t sure what nc is

mmeehan: cool

jfo: curl is the most basic client, it sends simple get request headers and then prints the response to std out

If the commands worked you should have seen something like

GET / HTTP/1.1
Host: localhost:5000
User-Agent: curl/7.43.0
Accept: */*

Appear in the netcat window. Those are the ‘headers’ I’m referring to.

mmeehan: that’s crazy

jfo: you can also post with it, not sure of the flags off the top of my head…

jfo: :sparkles: networking :sparkles:

jfo: notice too, that

jfo: the window you ran curl in

mmeehan: yeah

jfo: is waiting on a response

mmeehan: yeah it’s so smart!

jfo: it will never get one, because it sent the request to netcat, which isn’t a server that knows how to handle that request, and it also isn’t piping to anything that responds, either.

mmeehan: this is supa cool

jfo: but you could wire up like, the dumbest server ever, using pipes and random utilities, probably

mmeehan: ooo right i see

jfo: but the last thing I was going to say

jfo: is this weird unix operator that I learned about last year

jfo: <(arbitrary commands or whatever)

jfo: run echo <(cat /dev/random)

mmeehan: ok

mmeehan: hmm i don’t understand the return

jfo: you get a path, right? to a device file!

jfo: which is weird, right?

jfo: :D

mmeehan: yeah i got a path … where is it comin from? random? lol

jfo: under the hood, *nix is implementing interfaces to processes as device files, which represent streams in some form or another!

jfo: a usual path represents what we think of as data on disk

jfo: but a stream can come from anywhere… like a stream of audio data could be coming in through an analog to digital converter, which is an external device, that the OS interfaces with through a device file!

jfo: what that <(...) command does, it exposes the internal device file representation of the process you run inside of it

jfo: so you can access the return of that process like any other file

jfo: that is to say

jfo: that this will also work:

jfo: git apply <(curl [url to raw diff])

jfo: and essentially it does the same thing as writing the curl output to a file with curl [url] > /tmp/filename and then reading it with git apply

jfo: but it skips the writing to disk step

jfo: computers! :D

jfo: now, I will actually look at the diff

mmeehan: this all sounds cool but i would have to read more into device files and streams and such to fully grasp the awesomeness …

mmeehan: lol

jfo: yeah it’s a little mind bendy!

jfo: sorry for getting excited about it!

mmeehan: y r u sry i am glad to learn !!

jfo: you are right I am not sorry I am just being polite in case you hate this

mmeehan: i am excited to understand it better … under the terminal hood

jfo: I should write this all up in a blog post or something

mmeehan: yeah !

jfo: oh yeah last tip for now and this is a small one

jfo: you can put ‘.diff’ on the end of any github pull request url

jfo: like https://github.com/username/reponame/pull/#####.diff

jfo: and you’ll get the raw diff of the whole changeset

jfo: try it!

jfo: or .patch too

jfo: either will work

mmeehan: right wow!

jfo: lol

jfo: ok back to this ticket

jfo and mmeehan work on a javascript bug until the heat death of the universe in the year 41201 AD. Scene.