Git Internals, Techniques, and Rewriting History
blog.isquaredsoftware.com
blog.isquaredsoftware.com
But whenever I try to explain Git to someone I try to step away from a computer and just work out the problem on a whiteboard. Using post-it's for branches/HEAD/tags and marker to write down the commits and the commit-tree. Preferrably permanent marker to reflect the permanent nature of commits with regards to branches (eg: rebasing keeps the original commits around). Also I ditch files in favor of a picture of a cat there changes are attributes like added body parts or toys since most people like cats better than files.
Taking it away from a computer really helps to reduce the complexity involved and thinking about what manipulations you make to the Git 'database' visually really helps understanding the concepts imho. Just going through it step by step as if you where the Git binary making the changes, and after a while the Git commands turn from hard to remember trivia to tools in your toolbox.
People used to talk about "CASE tools": Computer Aided Software Engineering, by analogy with the CAD tools that replaced physical drawing tables once the technology got good enough. I joke that certain tools are "computer impaired software engineering", and git can certainly belong in that category at times.
What we have built is a tool with a good internal model but a user interface that it's easier for you to not use a computer, to step away to the whiteboard, in order to work out what to do.
We don't tolerate this for, say, word processors; people don't go to a piece of paper to lay out their headings and only then work out the incantations necessary to achieve that result.
(Has anyone attempted to make a "transparent porcelain" GUI which represents the "intuitive" internals in an actually explanatory way? The success of which could be measured by the reduced number of mistakes and amount of apprehension experienced by non-expert users?)
To me, this is a sensible decision. The target users of git will use it day in and day out for decades, in this case it makes sense to prioritise experienced usage. For onboarding new users there are plenty of good tutorials, and I think in the long run it makes sense to put the legwork in with those, rather than switch to a more intuitive but less efficient porcelain.
But that implies that a trade-off has to be there, which it does not. If you design your features well, it can both be quite intuitive for newcomers while still affording efficiency for experts.
A great example is the staging area in git. It is a horrible feature, that causes lots of interactions with other commands that are inconsistent and unclear, especially for newcomers. How to do it better? Just make the staging area a full-fledged commit. Considering that it's already pretty easy to edit a commit anyways, promoting the workflow of just polishing a commit before publication is more intuitive for users, and it means questions like "how do I see what the diff of the staging area by itself is?" is really just "how do I see what the diff of a commit by itself is?" One of those questions I already know the answer to, the other I don't.
Except it's not. It's an ad-hoc accretion of overlapping functions with unintuitive naming. An attempt is being made to rectify this with the "switch" command, but it's a long way from being optimized for anything at all.
Hm.. replace a few words and you will get a paragraph about git.
In LaTeX (or Markdown, or some other markup), it is pretty easy to enforce common style: font name, font size, indentation, space around figures, and so on.
In Word, not so much -- one bad copy-paste, and your document has a few words in unusual font, or "11.8" pt characters, or "1.17" line spacing. This is especially bad if you are working together, and some of your collaborators are using non-English language -- you see a mix of "Heading 1" and "Titre 1", each configured separately.
I am pretty happy that now that I am not longer in academia, I don't need to deal with either Word or LaTeX -- it's all Markdown, JIRA's markup or, in the extreme cases, Google Docs. Still, if I had to produce a fancy looking document today, I'd probably go for LaTeX with a GUI like Lyx -- good discoverability, and pretty clean .tex files on output.
There's a reasonably significant set of people that do this sort of thing. The backlash against word processors in favor of simpler focused writing tools or even simpler word processors is what I'd offer as evidence. Scott Hanselmann's video series on "How to REALLY use Word." is another example. (Word has offered powerful stylesheet features since at least 1991 and very few people actually use them, even those that could benefit.)
Have a look at Gitless[0]. It's a layer on top op Git which simplifies/rearranges some of the concepts and commands of Git while still manipulating the Git DB in the same way (you can switch back and forth between Git and Gitless seamlessly).
Don't expect this to hit critical mass by now, but happy to answer questions on the rewriting thing if anyone has any.
I'll try to find time to go through and attribute things. Thanks for the heads-up!
- I'm a lot more comfortable with Python than I am with shell scripting
- The Python+libgit2 approach _absolutely_ saved time. Being able to iterate through commits in 4-ish hours vs 24 hours for a complete run was huge, and yes, I did a lot of partial runs to test how the process was going.
- Yes, the code rewriting portion ended up as the bulk of the time. I could do a no-op iteration of the commit history in about 30 minutes, and the final processing was about 4.5 hours.
If I ever needed to do this again, yes, I'd use basically the same approach, just with the advantage of applying the knowledge I learned right away.
I'm wondering why you went through the pain of making an HTTP server to process JS files, instead of say putting the temporary file over a ramdisk and running the formatters in parallel over multiple files. Plus, even if it's slow, you only have to run it a few times right?
And no, I was re-running this _dozens_ of times to get it right, especially when I was in the "run -> transform fails on broken JS code -> debug -> write regex -> rerun" portion.
"App Repo Size Issues:" yes, this is the Achilles heel. By being a full distributed system where every client has to carry a full copy of the history of everything, some common practices become unsustainable. I've had two employers that checked build artefacts into SVN, for example: do that with git and the repo becomes unusably large very quickly. Vendoring dependencies, a useful practice if your project is slow-moving, will also bloat the repo. They should be using "shallow clone" for Jenkins, but even that can be surprisingly large.
I've also been through the "apply BFG to repo" phase (very time-consuming, and blocks all commits while you're doing it!)
> New idea: run the formatter against every commit in the history, so that it looked as if the code was "always formatted the right way".
This is actually brilliant, for the reason they give - keeping "credit" assigned correctly to original commits.
> Determined it was okay if older commits were potentially "broken", as long as the latest commit runs and has all of our changes as of late 2018
I'm less OK with this, as the chances of an automatically introduced horrible bug which you can't trace seem rather high and you've wrecked any chance of using git-bisect! But if the "tip" is the only supported released version, I suppose it's less critical.
It's also interesting that most of the speed issues are addressed by re-architecting to avoid syncing to disk. If there was an easy Windows RAMdisk this might have made almost as much of a difference.
We happen to be in a situation where we have very long development cycles, and also rarely need to go back and rebuild older versions. So, in our case we could get away with this.
And yes, the speed improvements came from a combination of iterating over Git commits in a single Python process vs running Git as an external command, and avoiding touching disk for everything except reading and writing blobs and commits.
I’m looking at the long-term picture here, rather than how you interact with it when in the process of rewriting history.
Clarifying that you’re replacing history rather than modifying it is useful, but I certainly have no beef with the expression “rewriting history”.
Anyway, personally, I’d pick on the other word sometimes, and suggest we don’t call it rewriting ‘history’ when rebasing local changes before push. Maybe it’d be better to define “history” to mean things that are pushed and shared with others, and call commits that haven’t been shared something like “local changes”. Might be moot in this case since the article was mainly about rewriting the entire repo, so “history” is totally appropriate, but still perhaps nice to use terminology to distinguish between commits that have been shared from commits that haven’t, since that is the line across which one should be more careful with any kind of rewrite.
This requires keeping some state around. libgit2 is a nice pointer, I'll check that out.
(hg users will recognize this as "restacking.")
e.g.
$ echo echo hello world > ~/bin/git-hello-world
$ chmod +x !$
$ git hello-world
hello world
You'll even get typo recognition: $ git hello-worl
git: 'hello-worl' is not a git command. See 'git --help'.
The most similar command is
hello-world [alias]
lol = !git --no-pager log --graph --decorate --abbrev-commit --all --date=local --pretty=format:\"%C(auto)%h%d %C(blue)%an %C(green)%cd %C(red)%GG %C(reset)%s\"
Then you can run "git lol" and get that cool output. That's one way to "extend" git.custom shell scripts
use a Git library in your language of choice and write something
"git diff --cached" = staging vs. HEAD
"git diff" = workspace vs. staging (but when nothing is staged, staging == HEAD)
(I think?)
I skimmed over git diff, as when I use git diff I constantly re-figure out for what it precisely applies again. I just care to remember "it diffs".
The complaints about it not being readable on mobile are legit. If I could wave a magic wand and make it all magically responsive, I would.
But, my original goal was simply to actually make the slides for the presentation, and show them during my talk. I do specifically use the Spectacle React/JS toolkit, as I _want_ to publish the slides online later for viewing on the web (one of several reasons why I don't make them in PowerPoint). But, part of the reason I can get away with that is that publishing them on my blog is just a matter of uploading the built assets and moving on, vs having to convert them from Powerpoint by hand or something.
I've got a ton of other priorities and tasks to deal with, and figuring out what's needed to make the slides well-formatted on mobile realistically isn't anywhere on that list. Honestly, this thread is the first time anyone's actually complained about that.
I'm not sure how much of the formatting issue is due to Spectacle's own styles, vs the typical slide layout that I have in there (which is mostly flexboxes with two items side-by-side). If anyone has some specific suggestions on how to alter the styles to make them work better, I could try to apply those and rebuild it.
https://blog.isquaredsoftware.com/2018/11/git-js-history-rew...