45 ms·
Things I wish everyone knew about Git (Part II)
- mg 4y ago"It is really hard to lose stuff" Indeed. This also means that garbage keeps piling up in git repos. This is how I make sure, I do lose stuff eventually: https://github.com/no-gravity/git-gc-all-repos.sh https://github.com/no-gravity/git-gc-all-repos.sh A script that goes through all my repos and performs garbage collection.
- donatj 4y agoDisk space is cheaper than lost data.
- bloopernova 4y agoTrue, but wouldn't the reduction in size make it a little quicker to transfer over slow networks?
- Arnavion 4y agoI know your link says you don't want to do `gc --aggressive --prune all`, but for anyone who does: a) That still leaves objects referenced by the reflog, so you may want to `reflog expire --expire=now --all` first if your reflog is full of rebases etc that you don't need, and b) you may want to run gc twice because the second time compacts a tiny bit more than the first.
- Slade_ 4y agoYou might be interested in the `git maintenance` subcommand that was introduced in Git 2.31. It can do per-repository automatic background garbage collection on a configurable schedule, among other things.
- donatj 4y ago> It is really hard to lose stuff Furthermore I recommend turning off automatic garbage collection. Turning it off makes it even harder to lose things, and unless you have an insanely massive amount of churn, you really don’t need it. https://donatstudios.com/yagni-git-gc https://donatstudios.com/yagni-git-gc
- Groxx 4y agoOr committing large binaries frequently, but yes. Totally agreed. GC manually unless you're dealing with scale where you know precisely how much it helps you. Otherwise it's safer and easier to not do it, and the disk space is utterly inconsequential (and trivial to find and `git gc` if it proves otherwise). You can afford a few more megabytes every year or so.
- MBlume 4y ago"It is really hard to lose stuff" It is really hard to lose stuff that you have ever committed. It is really hard to lose stuff as long as you commit early and often. Uncommitted work is pretty easy to lose with 'git reset'.
- perlgeek 4y ago... or with `git checkout` or with `rm` or `git clean` (for files not yet under version control).
- tsimionescu 4y ago... or git merge / git pull (but not git rebase) ...
- TheChaplain 4y agoDepends. I use an IDE from Jetbrains, their "Local History" feature makes it fairly difficult to lose anything, even between changes on uncommitted files.
- stu2b50 4y agoI don’t think that counts as a “depends” if it involves the use of another tool.
- atom_arranger 4y agoThis has saved me many times. Memory is cheap these days, this should probably be a more common editor feature, on by default.
- brenainn 4y agoA simple one is being able to write multiple messages (subject and body), e.g. git commit -m "subject" -m "a longer body" or just running 'git commit' and using your text editor (subject first line, body after). I've worked on too many repos with messages like "fixed the thing" where a few more sentences of context would've prevented headaches when trying to debug or change something.
- rjmunro 4y agoI always tell people the `-m` flag of `git commit` is only ever to be used in scripts. Let git open an editor and write your multi-line message there. Look at the comments telling you which files have been changed. Even better than using `git add` and `git commit` is to use `git gui` to add and commit files. The (standard) GUI is not to help beginners, it is to make you a more effective user of git. Note there are other apps you can use with more features, for example I like gitx on MacOS, but there are also terrible apps out there that do try to be easy for beginners, and they should be avoided. The most obvious is github desktop (I haven't tried it for a while - it might have slightly improved, but I still won't go near it)
- pfarrell 4y agoGood points. I occasionally use gitk to browse recent commits. A gui definitely has its place. I like using the terminal, so my workflow is to use `git add -p`. That makes me look at each change, in small chunks,as I stage them. As a bonus, it helps me keep commits small, or decide if there’s some logical separation in my changes that would be better expressed in two or more commits.
- cassianoleal 4y agoWhat's `git gui`? $ git gui git: 'gui' is not a git command. See 'git --help'. The most similar commands are ci gc grep init lg80 pull push
- greggyb 4y agoYour package manager seems not to include it. `git gui` is available in the standard Windows installer for git. The package on Ubuntu 20.04 seems to not include the gui. As noted on git website, there are two GUIs that are in-tree -- native components of the project. https://git-scm.com/downloads/guis https://git-scm.com/downloads/guis
- stinos 4y ago> But the old commit is still in there, pristine, forever. > (Git will eventually throw away lost and unused snapshots, but typically not anything you have used in the last 90 days.) Without having exlplained what a 'snapshot' is and what 'typically' means, it's pretty unclear whether the first statement is affected by the second, and if it were the first is incorrect, so I feel this clarification should be added to the list of things you want people to know about git.
- dale_glass 4y ago> Without having exlplained what a 'snapshot' A commit is effectively a snapshot of the repository at a given point in time. This is unlike some other revision control systems where a commit is a diff. In git, a commit contains whole files, and the diff is generated if needed. > and what 'typically' means Pretty much everything is configurable, and you can invoke the cleanup process by hand if you really want to. > it's pretty unclear whether the first statement is affected by the second, and if it were the first is incorrect Yes, I think it's incorrect and shouldn't be relied on. Things floating around unconnected to a branch can be removed by git eventually. There's plenty time to fix any mistake, but it's not "forever".
- deleted 4y ago[deleted]
- pjerem 4y agoWell, the Part I have en entire chapter enticing you to read Git From The Bottom Up [0] before continuing. So the author can expect that you know what is a snapshot. [0] : https://jwiegley.github.io/git-from-the-bottom-up/ https://jwiegley.github.io/git-from-the-bottom-up/
- rob74 4y agoMy thoughts on this: - even after years of working with git, I'm still not familiar with all the dark corners I can get myself into by running the wrong command at the wrong time. Yes, most of the time my changes are still there, but the way back to the state I actually want for my repository may be long, complicated and require lots of googling/asking around. - as the article mentions, git cares a lot about stuff that has been committed, but not so much about the files in your working directory. Because of that, IDEs that keep a local history of your file system changes (like the JetBrains products) are really helpful. "Commit early, commit often" may save you from this, but produces more noise in the repo (which you can reduce by squashing commits, but that's extra effort) and may make it harder to find things - and I guess that goes double if you follow the suggestion of committing changes automatically "every few minutes". The local history is there when you need it and gets out of the way when you don't.
- rocqua 4y agoFYI The committing every few minutes idea commits to a separate branch to prevent this clutter.
- deleted 4y ago[deleted]
- colonwqbang 4y agoI don't think it's so strange that we need to rework the patch series a bit before sending it for review. At least not if we see readability as a main goal. Not many great writers can sit down and write a perfect script on the first try. They write a first draft and then rework it, move things around etc. In the same way the git user goes over the patch series with rebase and amend to massage it into something that flows nicely and is pleasant to read. I find that it's actually easier to make a good patch series if you start from many small commits, rather than a few big ones. The tools in git that merge patches together are faster to use than those that pick patches apart.
- papito 4y agoI can't tell you how many times IDEA's "local history" saved my ass. I use it extensively. Instead of stashing local changes and going around messing with my working tree, what I did 30 minutes ago is just a right-click away. (Also, if you right-click on the top project folder itself, you can restore the entire project to a previous snapshot, if something was working before and you heroically went down the wrong path).
- praptak 4y agoI love this quote from Part I: "Git has an elegant and powerful underlying model based on a few simple concepts: 1. Commits are immutable snapshots of the repository 2. Branches are named sequences of commits 3. Every object has a unique ID, derived from its content Built atop this elegant system is a flaming trash pile. "
- eulenteufel 4y agoThe quote is kinda wrong in that point 1 and 2 are mixed up. Branches are just pointers to commits. Commits contain a reference to their history. It's only kind of incorrect because in praxis branches are used to refer to a history (a sequence of commits). But it's also misleading once you have to do anything more complicated than just commiting/merging. When I started out using git I was working with the same assumptions, but I was perpetually confused. Git became a lot easier to use once that misunderstanding cleared up. Maybe it's just like that for me but I think we might be doing newcomers to git a disservice by explaining the basics of git in this simplified manner.
- deleted 4y ago[deleted]
- sophacles 4y agoDo you have an actual example of this? I've never encountered a situation where those three points are incorrect.
- badpun 4y agoGit branch is actually very similar to git tag[1]. For example, there are commands to point branch to completely different commit in different section of the commit graph, just like you can do it with tags. [1] The main difference perhaps is that, if you create a new commit, the branch pointer will actually be moved to point to that latest commit (while tag stays fixed).
- kleinsch 4y agoIf you're in a detached head state and create a new commit, you have a commit that isn't on a branch. The commit still has a parent, so it's not the branch that's tracking the sequence, it's the commit. Branches are just pointers that get updated as you add commits.
- wokwokwok 4y agoHrm… can you really recover branches and tags once you delete them? I was under the impression commits were retained but removing labels (tags, branches, remotes) was unrecoverable and immediate.
- shikoba 4y agoYou can't recover where your references were but all the objects (anything with a SHA1) are still there.
- formerly_proven 4y agoreflog tells you when you created/changed a label and what ID it had, so you can recover them.
- wdkrnls 4y agoHas anyone written a tool to actually does this? I want to add that to my post-rewrite hook immediately so that I can gain peace of mind I will always be able to switch to a branch where I can study the previous reality after rebasing a new one.
- arxanas 4y agoMy project https://github.com/arxanas/git-branchless https://github.com/arxanas/git-branchless does this. Use `git undo -i` to get a graphical view of where branches were at an arbitrary previous point in time. It's worth noting that, in principle, there are cases where the reflog will have never observed a branch move. The reflog we usually look at is that of HEAD; if HEAD did not have the branch checked out when it moved, then that information will be lost. (For example, if you run `git branch -f my-branch abc123`, the branch will be forcibly reassigned without having been checked out.) See https://github.com/arxanas/git-branchless/wiki/Architecture#comparison-with-the-reflog https://github.com/arxanas/git-branchless/wiki/Architecture#... for more details.
- OJFord 4y agoIf I could just convince colleagues to `pull.rebase`, so CI isn't constantly building 'Merge branch master' on the master branch, I'd be happy enough.
- formerly_proven 4y agoMaybe it would have been good if the git defaults were changed from the defaults used for the kernel development workflow to the git workflow ~everyone else is using, even though git was created for the kernel people by the kernel people - most users are not kernel people. "git pull" in the kernel workflow is for integrating downstream changes into your own upstream repository. Meanwhile, "git pull" in the everyman workflow is for synchronizing your local changes with upstream changes (you know, re-basing your patches on upstream or something). Even the merge commits left behind by default git pull reveal this. "Merge branch master of ssh://yourorg-git/upstream" - completely backwards for what you are doing - you're merging the upstream into the downstream? What, your local repo is the boss now? Doesn't make any sense.
- shikoba 4y agoThe idea of "merging into" has no meaning with git. You're merging two things into one. But saying that there are a master thing and a slave thing has no meanings.
- andreareina 4y agogit does privilege the first parent[1] in a merge in a bunch of contexts [1] i.e. the branch you did the merge from
- deleted 4y ago[deleted]
- IshKebab 4y agoNot true because commit parents are ordered. One of them is first - the main parent.
- codingdave 4y ago> It is really hard to lose stuff If that stuff is committed. Maybe I'm stating the obvious but conversations seem to revolve so much around finding stuff and merging stuff that my favorite use of git isn't talked about as much -- throwing stuff away. A commit is your save point - once made, you can basically code risk-free, knowing you can always go back to that save point. Try new things, thrash your code, don't stress even if you break everything - because you can always just revert back to that last commit. So I'd say that it is really hard to lose the stuff you explicitly declared you wanted to keep. And easy to lose the rest.
- lelandfe 4y agoAs a corollary: cheap branches are wonderful. The workflow I teach the newbies at my job is to just do a quick `git checkout -b tempbranch` when trying out: complicated rebasing, cherry-picking commits, whatever. Do your thing on that temporary branch. Did the thing work? Hell yeah. `git checkout - && git reset --hard tempbranch`. Presto magic, your old branch is now a perfect replica of tempbranch. It's as if you did the whole thing on your original branch to start. Did it fail miserably? Oh well. `git checkout - && git branch --delete tempbranch`. It vanishes and no one has to know that the rebase fell apart. Your working branch is in pristine condition still.
- stu2b50 4y agoThose are pretty much the same thing. Branches work for that purpose because commits are immutable - the branch just saves you from having to remember the hash. A tag would also work in this case.
- lelandfe 4y ago> just saves you from having to remember the hash You're underselling it :) That is the entire point. Don't tell me you enjoy using reflog. This "just saves you" in the same way that a rebase "just saves you" from having to write out enormous cherry-pick statements. While "the plumbing is the same" is a nice bit of trivia, the porcelain is all that we should care about when we're recommending behaviors.
- lelandfe 4y ago> A few things can be lost forever! > The dangerous commands are git-reset and git-checkout Don't forget about `git clean`, which will happily gobble up all your important ignored files (API keys, IDE configurations, etc)
- TacticalCoder 4y agoBut git clean requires confirmation, unless you configured Git to not ask your permission to trash the .gitignored files. It even shows you which files are going to be deleted. Now, on that topic of "important but (.git)ignored files", I don't know what the best practices are but I like to symlink my IDE config files / test API keys etc. that aren't to be committed to Git. So in case I'm not cautious I'm only deleting the symlink but not the file itself. To me it's the best of every world: I don't need to take any special care of "not backuping" these infos were they shouldn't be backed because I don't backup my Git repo with anything but Git (and they're .gitignore'd so I'm good), I don't risk losing them even if in engage in reckless behavior (like a git clean or some "rm -rf ..." the whole repo and re- git clone, you guys know what I mean) and I can centralize my secret / personal config files into a specifir dir which I know is important and which I can securely backup (say doing encrypted backups). If I delete the symlink (and git clean only deletes the symlink, as expected), I can just re-create it.
- beeforpork 4y agoThat! Also, 'git reset' is only dangerous in some modes, e.g., --hard. It's a user experience nightmare to have the same command be nice in one case and dangerous in another, depending on an option parameter. It is a very useful command, so it's likely that you accidentally lose your local changes the one day when you're a bit tired, maybe. Also, 'git checkout' is used for many good things like, well, checkout. To abuse it also for reverting local changes is a user experience nightmare, too. It should have a different command for that functionality.
- WorldMaker 4y ago> It should have a different command for that functionality. It does in today's git. Rather than using `git checkout` you can migrate to using the two split commands `git switch` (switches branches, generally safe) and `git restore` (restores files, generally "dangerous"). `git restore` also has the benefit that the some of the most dangerous commands at that point have similar names: restore, revert, and reset. It will take a while for people's `git checkout` muscle memory to be replaced with `git switch` and `git restore` (and `git restore` is still marked as "experimental" despite being stable for quite a few git releases now, though that "experimental" suggestion seems to be mostly earmarked for people thinking to use `git restore` in automated scripts than it is for human users), but the command split is a very good thing for git user experience. (I've mostly gotten used to `git switch` at least and have stopped using `git checkout` in my own work.)
- emacs28 4y agoI made a simple script below I find useful that commits current changes then automatically rebases and squashes it with the previous commit, preserving the previous commit message. I find it useful for a way to quickly save current changes before switching branches, or for easily backing up work before its worth making a new separate commit: git add --all git commit --fixup=HEAD~1 GIT_SEQUENCE_EDITOR="sed -i -re 's/^pick (.*)fixup! (.*)/fixup \1\2/'" git rebase -i --autosquash --autostash --keep-empty HEAD~2
- lelandfe 4y agoHN italics markup is mangling your code – indent the code block to fix :) like so! > save current changes before switching branches Also: why this over a stash? Stashing fundamentally just creates a hidden little commit with all your unstaged work on it. It does have one advantage over your approach, which is easily applying the work to another branch.
- emacs28 4y ago> indent the code block to fix :) Thanks! > Also: why this over a stash? I just like this better because with stash you have to do one more command (stash pop) to get the changes back after switching back to the first branch, but this way the changes are already there when you switch back.
- rjmunro 4y agoIsn't this the same as something like: git commit --all --amend With maybe a --no-edit thrown in?
- emacs28 4y agoYou know what you're right, the command git commit --all --amend --no-edit seems to do essentially the exact same thing, thanks!
- awaisraad 4y agoa colleague accidentally force pushed a stale branch to master and a weeks worth of work was potentially lost. That day git reflog helped and saved our jobs as we were contractors for a big Germany based client.
- papito 4y agoAlways disable force pushes to trunk (if that feature is available, as it is on Github).
- deleted 4y ago[deleted]
- LgWoodenBadger 4y agoI hope you(they) are backing up their source code as well, which would have helped you if reflog did not.
- onpensionsterm 4y ago>Some people automate this: they have a process that runs every few minutes and commits the current working tree to a special branch that they look at only in case of disaster. This sounds nice. Does anyone have a concrete example? I'd like to try with a cronjob but it would be useful to avoid having to create the job manually for each repo & I don't want a job repeatedly scanning all my disks. Is there anything like a global post-clone hook? Additionally, I'd be worried about race-conditions from saving work during the job.
- mabbo 4y agoI said this in another comment recently about git, but I find it odd that even after a decade of using git as a mandatory part of my professional life, I'm still learning new things. That's maybe not a good sign about the usability of git. That `@{'3 days ago'}` thing? That's incredible. Why didn't I know about that 8 years ago? Why wasn't it obvious, intuitive to me that this was possible? git is brilliant and I love it. But there are very few affordances that make it obvious what to do next, what is possible. There's few patterns in it where I can apply what I already know. The thing that replaces git will have most of the power of git, but an intuitive interface that makes learning everything about it easy.
- NKCSS 4y agoGIT is just the new Regex. Everybody uses it, most have no clue how it works and just copy/pasts stuff from other places/repeats the same little trick
- scrollaway 4y agoNo, that’s terrifyingly untrue. Git is more like… the new car. It’s complicated and most people have no idea how it works, but they know how to steer very simply and can drive it with a bit of training. Experienced people however, who know how cars work, can do really cool things with them and have no problem repairing them on their own. Still, cars are useful. A bike would be better of course.
- foobarian 4y agoI like it! I'll do you one further. Git is more like... someone ripped out all the controls from your car and installed a Boeing 787 dashboard. You'd probably figure out how to start it and make it go forward, left and right, but you'd miss out on a lot of functionality and likely destroy something while playing with unknown buttons.
- stackbutterflow 4y agoI find regex easier to write than to read. I never copy them from stackoverflow because I'm worried it does some weird magic that I don't want. Instead I build them against one or more examples.
- raju 4y ago> The dangerous commands are git-reset and git-checkout Don't forget about `git-restore` which replaces `git reset -- filename`
- WorldMaker 4y agoIt's also designed to replace `git checkout branch -- filename` and related complicated `git checkout` uses. (It was added alongside `git switch` and between `git switch` and `git restore` you can entirely avoid `git checkout`.)
- pif 4y agoFrom Part I: > "I don't need to know how it works. I just want to know which commands to run." > with Git, this does not work.* Well, so what is your post about? Make it work, then call back.
- sdrwefgfvb 4y agoUnrelated to git, but I LOVE how fast and user-friendly this page is.
- jwilk 4y agoPart I: https://news.ycombinator.com/item?id=31923429 https://news.ycombinator.com/item?id=31923429 (191 comments)
- joe8756438 4y agoLearning git is not easy. There is one thing that will almost guarantee no work is ever lost: commit. if something is commit, even if it’s not pushed it can be retrieved. One other thing that has nothing to do with how it works, but makes all the difference in usability: write commit messages describing _why_. To learn git is to use git, reading about it has not helped me.
- shikoba 4y ago> write commit messages describing _why_. Could you provide a concrete example? I don't understand your point.
- joe8756438 4y agoAs in: "changed this property in the config _because_ it will extinguish all evil from planet earth" vs. "changed this property in the config". I can see _what_ somebody did well enough from the code diff itself -- reading an uninterrupted stream of messages explaining _why_ in a git log is a wonderful experience.
- shikoba 4y agoThank you, I think I get your idea.
- yogeshg 4y ago["Git from the Bottom Up"](https://jwiegley.github.io/git-from-the-bottom-up/ https://jwiegley.github.io/git-from-the-bottom-up/) changed my life. Someone recommended this to me, and I recommend this to anyone seeking more information about git. People often just want to know "which commands to run", which is a fair expectation, until they reach a threshold of what they can do without knowing the internals. [When should someone read "Git from the Bottom Up"? What are your git recommendations?](https://forms.gle/aqFy52uvTp8CUvkF9 https://forms.gle/aqFy52uvTp8CUvkF9)
- js2 4y ago> What if you can't find it? Funny enough, I just wrote "git-lost-and-found" last week because I'd added a new file (`git add`) but before I created a commit I did a `git reset --hard` which deleted the file. It wouldn't have been in the reflog since there was never a commit created. But I knew it would be under .git/objects until the next time a `git gc` runs, so the trick was to just take a look at a few recent blob objects till I found what I was looking for: https://gist.github.com/jaysoffian/f42f4b1806f65158b404088144fca227 https://gist.github.com/jaysoffian/f42f4b1806f65158b40408814... Put that in your PATH as `git-lost-and-found` and then you can call it as `git lost-and-found` from inside any repo. Edit: oh, MJD beat me to it by 6 years but I hadn't seen this when I wrote the gist above: https://blog.plover.com/prog/git-reset-disaster.html https://blog.plover.com/prog/git-reset-disaster.html
- js2 4y agoI guess I could use `git fsck --lost-found` too. https://git-scm.com/docs/git-fsck https://git-scm.com/docs/git-fsck
- deleted 4y ago[deleted]
- wdkrnls 4y agoI wish that instead of focusing on this API view of git that everyone seems to have run with that something akin to a plan9/fuse file server was the main interface everyone learned. I would imagine it would work like a souped up version of RepoFS: https://github.com/AUEB-BALab/RepoFS https://github.com/AUEB-BALab/RepoFS There is a paper to go along with the package which showed some convincing examples that much of the git tooling could just go away in favor of already existing shell commands: e.g. no need for `git grep` when `grep` will do the job.