Hacker News

Top stories

Live mirror
30 storiesupdated just nowView source snapshot
  1. Yes, Claude can do Nine Loops (anthropic.com)
    2comments
  2. Platform-Independent SIMD in Go (go.dev)
    100comments
  3. Ollaya – Ollama for open-source, Jev-style decision models (ollaya.dev)
    1comments
  4. Git-bug: Distributed, offline-first bug tracker embedded in Git (github.com/git-bug)
    77comments
  5. First Principles Thinking (sunilsadasivan.com)
    55comments
  6. Alan Kay: Shannon gave us a way of dealing with noisy channels [video] (youtube.com)
    1comments
  7. Pentium II at 600Mhz with Voodoo 3 Emulated on 86Box with M6 Mac Mini (nyaa.sh)
    96comments
  8. F-Droid 2.0 (f-droid.org)
    404comments
  9. Show HN: Doom or Bloom, map your AI worldview with Jev (doom-or-bloom.com)
    5comments
  10. Classified Estimates Show the NSA Is Paying Billions to Test AI Models (washingtonsun.com)
    73comments
  11. Ink and Switch Interactive Homepage (inkandswitch.com)
    24comments
  12. Dutch governments builds alternative for Microsoft based on NixOS (dawo.community)
    515comments
  13. U.S. appeals court upholds designation of Anthropic as supply chain risk (cnbc.com)
    336comments
  14. Factorio that you can touch (factorio.com)
    34comments
  15. Amiga Screens: A Primer (datagubbe.se)
    22comments
  16. Show HN: Make cursed fonts like Times New Bastard (mitpit.com)
    121comments
  17. What happens when you analyze your favorite college football team like the CIA? (cultivatelabs.com)
    6comments
  18. Gravity Seems Holographic. What Does That Mean for Reality? (quantamagazine.org)
    59comments
  19. Show HN: Whiteboard (YC W26) – An open-source IDE for thoughtful software design (github.com/devdotfast)
    126comments
  20. Boards of Casio (ambionix.com)
    25comments
  21. CVE-2025-13032: Entering and Breaking the Avast Antivirus Sandbox Part 2 (safateam.com)
    26comments
  22. What About Rails? (jardo.dev)
    154comments
  23. Why is the liver so weirdly regenerative? (dynomight.substack.com)
    264comments
  24. Jevmem – automatic project memory for Claude Code, built on Jev (github.com/avinash-jetwani)
    37comments
  25. Allow Carriers on Planes (jefftk.com)
    208comments
  26. 2DWillNeverDie (2dwillneverdie.com)
    77comments
  27. Rails World 2026 Opening Keynote [video] (youtube.com)
    447comments
  28. The Test (tante.cc)
    58comments
  29. Toyota is taking the Corolla electric (electrek.co)
    760comments
  30. Opus 5.5 is good at explainer videos (launchvideo.io)
    209comments

Lesser known Git commands

262 pointsby 10y agohackernoon.com
139 comments
10y agoHN ↗

Warning: not actually git commands, rather TFAA's aliases for possibly useful combinations of switches and/or commands.

10y agoHN ↗

See, switches in git are there because git maintains backward compatibility. Sometimes a switch really would logically be better as a whole new command. But it's a common refrain to hear that git hides useful things in the switches.

This article is a great way to find those hidden things.

10y agoHN ↗

See, switches in git are there because git maintains backward compatibility.

Switches are there because git commands tend to greatly leak the abstraction and group behaviour in terms of the underlying plumbing, and because Git maintainers most likely would rather add new switches to existing commands than new commands intersecting with existing ones.

Sometimes a switch really would logically be better as a whole new command.

Which could trivially be done without BC issues and thus can't be an explanation why existing commands keep growing new switches.

This article is a great way to find those hidden things.

It would have been a useful article if it had been about showcasing interesting or useful git switches.

10y agoHN ↗

Alternatively, read the documentation and know how to use your tools instead of reading blog posts that lists a few things one person has found useful.

10y agoHN ↗

Agreed - these aren't lesser known git commands at all.

The switches given to git commands in the article might be lesser-known, but title could be clearer that this is:

    > Some suggestions for git aliases
10y agoHN ↗

I should really read more of the git doc but at least I learned about aliasing in the gitconfig from this even if, I too, was expecting actual git commands

10y agoHN ↗

Some of the aliases are funny but could be renamed for brevity.

Also grog is making me feel extremely groggy.

10y agoHN ↗

"commend" seems especially bad, it's both unclear and longer than "amend" would be.

My aliases list is pretty small, aside from amend I have

* get (pull --ff-only --all -p), basically a hard sync with all remotes, updating the current branch if there are no conflicts or divergences

* summary (log --oneline --no-merges), given a range from git fetch/pull, provides an overview of the commits you just fetched

* original (!git show $(cat .git/rebase-apply/original-commit)) during a rebase conflict, shows the original commit whose application failed (it can be difficult to untangle a rebase conflict, this sometimes helps a lot)

10y agoHN ↗

It make me think of pirates drinking their grog.

I would have named it "git rum"

10y agoHN ↗

I like the `git grog` command, I might actually use that.

The idea of `git please` is not bad but it made me wonder if it could be possible to go even further.. a variant on --force that only force-pushes IF NOT ONE HAS PULLED YET.

I can see why this wouldn't be built into git core however, as it's quite stateful and depends on tracking other people's fetches, which probably isn't easy.

10y agoHN ↗

My personal favorite two:

    jschroeder@omniscience:~$ git config alias.up
    pull --rebase
    jschroeder@omniscience:~$ git config alias.down
    push

They can be used thusly:

   git up && git down
10y agoHN ↗

While these aliases are useful, I'm confused why "down" means "push".

10y agoHN ↗

Well, if you think of yourself of the ruler of the repo and "pushing down" to the plebs, it makes sense ;)

10y agoHN ↗

I love the implication that the upstream repo is the enemy, so you mentally put it down. Way to go Ender.

10y agoHN ↗

I would name them "down" and "up" instead.

10y agoHN ↗

"Here let me UPload the local repo to our git server" git down

"Great now let me DOWNload the changes to another branch" git up

What?

10y agoHN ↗

I think the 'up' is as in 'update' rather than 'upload', but I agree that the use of 'down' makes that confusing.

10y agoHN ↗

Years ago... I was a pretty prolific svn user. Coming from svn, I knew that it would fetch changes from the central server (remote in git parlance) and then rebase them with whatever you had in your local tree. This is where git up came from, literally an equivalent of svn up.

10y agoHN ↗

I like where your head's at and added a little something to help me here at work:

    void:~% git config alias.funky
    ! git up && git submodule update

And speaking of lesser known commands, I've been using 'git worktree' recently at work and I'm a big fan. Admittedly, it's probably mostly unknown because it's a new feature.

It's a little rough around the edges, but it has made things like customer support context switching much easier since I can leave my current development tree alone and just create a new worktree for the current escalation.

10y agoHN ↗

Could you share your workflow with worktree a little? What kind of development do you do and where does it fit in with your workflow?

I tried using it in a JS project for those times when I'm in the middle of work but someone asks "hey is master working fine on your system?" - eventually though, having to install all the node and bower modules and rebuild everything on the worktree didn't seem much smoother than just stashing my local changes.

10y agoHN ↗

Well, admittedly my current situation lends itself well to 'worktree' where normally a stash is probably just fine.

At the moment I'm doing a big rework (porting our software from a 10 year-old OS to CentOS 7), so I have many, many modified files (all tiny mods to fix new compiler warnings from new toolchains) and untracked and obsoleted files.

Basically, it's too much for me to keep track of if I change anything, so it's a case of "whatever you do, don't touch" that pushes me toward the worktree solution.

I've been pulled in to work on customer escalations on released code (one instance from 2 releases ago), so it's just easier for me to create a whole new worktree.

I imagine that once I'm back into a normal dev cycle that I'll be back to stashing.

10y agoHN ↗

I would name them the other way around since I'm pulling "down" and pushing "up" ....

10y agoHN ↗

I always do this together, so I just use `git push-pull`

  $ git help push-pull
  `git push-pull' is aliased to `!git pull --rebase && git push'
10y agoHN ↗

same functionality, my alias is "rush"

$ git rush

10y agoHN ↗

I have up aliased to

  pull --rebase --stat

That gives me an idea of what has changed, Though I often follow with:

  git log @\{1\}..
10y agoHN ↗

Didn't know about those stash options. I've always used 'git stash save -u' to bring along the untracked files in the stash, that's the more common scenario.

10y agoHN ↗

I just usually do something silly like 'git add . && git stash', i had no idea it was built in...

10y agoHN ↗

My most useful bash alias (when you go back to master and want to start at the most up to date everything before creating a new branch):

  alias grm="git fetch origin && git reset --hard origin/master"
10y agoHN ↗

same here but I switch to master first:

resync = !git reset --hard HEAD && git checkout master && git reset --hard origin/master && git pull

or with a parameter to do the same with any branch:

resync = "!f() { git reset --hard HEAD && git checkout $1 && git reset --hard @{u} && git pull ; }; f"

10y agoHN ↗

I use a very similar version of this, and people are often surprised that I'm working directly on master. Well, when it's ready to go, `git checkout -b feature/name; git push origin HEAD; git resync`.

10y agoHN ↗

so it’s good practice to create an empty commit as your repository root

While I'm aware of issues with rebasing the root commit, I've never heard this advice before and it seems unnecessary.

10y agoHN ↗

There's also the `--root` flag for rebase, which solves this problem without needing the dummy commit. I think this section of the article is out of date.

10y agoHN ↗

I actually like to make an empty root commit just as a place to describe what I intend to do with a repo. I'm uncomfortable doing it as a README commit because that lends it an undue amount of permanence once work begins.

Occasionally on repos that are really just for me I even do this as a 'root' commit for work branches, but the way rebase rolls over empty commits makes that kind of problematic in the long run.

10y agoHN ↗

It also helps if you want to do an octopus merge of branches that otherwise have no common ancestor. If it comes to it, you can use git replace to provide a false parent for the root commit.

10y agoHN ↗

I've seen more stash accidents than any other kind with git. Stashing is more dangerous than committing or branching, and to me it doesn't seem to provide any advantages... do people actually find stashing easier than branching? Is it just because when you branch you have to name it, and that causes friction? I stay away from stash.

Right from the man page: "If you mistakenly drop or clear stashes, they cannot be recovered through the normal safety mechanisms."

https://git-scm.com/docs/git-stash

--

I do like shorty. My version:

  alias gits='git status -sb'

--

My next fave is rebase the current branch against it's upstream branch point:

  [alias]
    rearrange = "!git rebase -i $(git merge-base HEAD @{u})"

git rearrange ftw.

10y agoHN ↗

I use stash when I forget to branch. So I might be working and then realize I never branched. So I stash my changes, create a new branch, and then pop my changes on the new branch.

10y agoHN ↗

Makes sense, but fwiw, there's a safer alternative: branch immediately, then go back to the source branch and reset.

There's nothing wrong with your workflow, btw, stash works in that case, as long as you immediately pop & don't get interrupted by lunch or a phone call.

Stashing is obviously providing some value that people like, it's very popular. But considering that it has extra danger, and that it's really easy to work without stash, I'm still confused why it's so popular.

And fwiw, most of the stash accidents I've seen are fairly simple scenarios like you described, and some teeny tiny twist or interruption causes someone to get confused and lose their stash, or think they lost their stash, and panic.

10y agoHN ↗

I'm not a git expert (yet), but I agree. I've read the stash man page a few times and tried to imagine when I'd want to use it over the alternatives, but I never do.

10y agoHN ↗

Because you might be currently not at the place where you intended to branch off. For example trying to make a bugfix on an experimental feature branch, but then you realize you should branch it off master.

10y agoHN ↗

So branch to a temporary branch, commit, checkout the branch you wanted to be on, cherry-pick the temporary branch to grab its head commit, then delete the temporary branch once you're sure you don't need it.

This isn't completely safe because deleting a branch also deletes its reflogs, but it does avoid use of the stash command at all, which I find good for my own safety (because it reduces cognitive load). And it scales obviously to making multiple commits onto the temporary branch, etc.

10y agoHN ↗

Or even just branch, commit, rebase onto the desired branch.

10y agoHN ↗

My #1 use of stash is as the quickest way to express "get rid of all this crap". I rarely intend to ever retrieve it.

10y agoHN ↗

git checkout -- .

Also works well for this use case

10y agoHN ↗

Doesn't do the same thing, without some more options. Stash is shorter.

10y agoHN ↗

Now here's one I actually like. As an alternative to git reset, a git stash is safer, and gives you an easier way than reflog to recover if you dropped a commit you want back.

10y agoHN ↗

For one of my projects, the 'build process' requires that I use the git archive command (basically archive to .tar.gz without any git-related files/directories). git archive on its own is fine for final builds because it's based off of HEAD for the current branch, but if I want to test a build with working changes, I would either need to commit all the changes _or_ use git stash and then archive based on the state I just stashed.

So in this case git stash works a lot better for a 'temporary' commit, kind of deal.

It might not be optimal but I've found the process to work pretty well for this particular project's situation.

10y agoHN ↗

If you're stashing your changes, that means they're just in the working copy and haven't been committed to the incorrect branch.

In this case, simply create the branch and switch to it (git checkout -b branchname) and you're good. It's like it never happened ;) (remember that branches are just labels - until you commit it doesn't really matter).

10y agoHN ↗

Typically once I've noticed I haven't branched is after I've already added and committed.

10y agoHN ↗

Then just create the branch where you are, and afterwards go back and reset the original branch

10y agoHN ↗

and make sure to patch up the upstream reference

10y agoHN ↗

I use `legit` aliases for this. It allows me to do `git sprout <new branch name>`, where it stashes my untracked changes, create a new branch, pop the stash and applies it. Pretty handy!

legit: https://github.com/kennethreitz/legit [1]

[Ignore the pretentious "for humans" tagline though...]

10y agoHN ↗

`git sprout <branch-name>` looks identical to `git checkout -b <branch-name>`.

10y agoHN ↗

While it's true that they can't be recovered through the "normal" safety mechanisms, they can certainly be recovered - that too up to 30 days in most cases.

`git fsck` can help here [0]. Also when a stash is dropped, it's SHA is printed to the console. If the console buffer hasn't been cleared and you realise it soon enough, recovery can be quite straightforward.

[0]: http://stackoverflow.com/questions/89332/how-to-recover-a-dr...

10y agoHN ↗

Yep true, and to be fair to the man page, that's the next sentence after the one I quoted.

But, fsck is a big hammer, and most people I've had to help recover their stashes completely glaze over when they see commands named fsck and reflog. Some people when faced with fsck opt to give up and recreate the lost changes, it's that intimidating.

And why not just avoid accidents? git's role in life is to be a safety net. Stash is hanging halfway over the edge of the net. Yes it's still possible to recover from accidents, but it's harder. That alone makes it more dangerous. It should be hard to screw up, not hard to recover.

10y agoHN ↗

You're right of course, but whereas for a lot of purposes I understand the impulse of "I don't have time to learn this". For so many things that's the right call, but for a developer using git I would like to insert the brain worm that maybe it isn't.

I consider git to be on the short list of career-long tools. The rest of the list is linux, bash and vim. Knowledge of these things has a long half life and will serve me across jobs. Even a language I've used for 10+ years in my daily work like Ruby is more likely to become obsolete within my career than git. And the thought of using git without understanding reflog is anxiety producing. The beauty of git is not in memorizing its arcane syntax, it's in understanding the elegant data model.

That's my case for why every developer should at least know git-reflog. git-fsck I have to admit never having used :)

10y agoHN ↗

personally I rarely stash and often it's just to get rid of a bunch of crap. rather than googling reset --hardhatheadhenry, I stash and drop.

Total hack I know, but whatever.

10y agoHN ↗

do people actually find stashing easier than branching?

Branching wouldn't be enough, you'd need to commit your entire working copy to approximate a stash.

I'd say it's generally a bug-prone mistake any time you commit your entire working copy (rather than staging lines/hunks on a one-commit-per-feature/bug basis).

So branching and committing and pushing into a `backupForTomorrow` branch is fine, but you have to be sure to get rid of that commit via something like a mixed reset when you start working on it again.

If that temporary backup commit somehow stays around in the final feature branch, a stash would be much preferred.

10y agoHN ↗

Committing your entire working copy to a separate branch can work just like a safe stash:

    $ git branch
      master
    * cool-feature-1
    $ git checkout -b stash-2016.09.30T01.10
    $ git add -A; git commit -m "stashing WIP for later; maybe add some commentary to remind myself what I'm doing"
    $ git branch
      master
      cool-feature-1
    * stash-2016.09.30T01.10
    $ git checkout master
    $ git branch
    * master
      cool-feature-1
      stash-2016.09.30T01.10
    # do other things
    # later...
    $ git checkout cool-feature-1
    $ git merge --no-commit --no-ff stash-2016.09.30T01.10
    Automatic merge went well; stopped before committing as requested
    $ git reset
    $ git status
    On branch cool-feature-1
    Untracked files:
     ...

And now your old WIP is back, ready to keep working, and in the meantime you could have pushed up your "stash" to a remote and grabbed it back.

There might be something terribly wrong with this, but if there's not, then I don't understand why git stash doesn't do something similar to this by default.

10y agoHN ↗

Branching wouldn't be enough, you'd need to commit ...

Yes, you're right. I mentioned both committing I branching. I imagine commits only as the alternative to a single stash/pop, and commits & branching as the alternative to using multiple stashes.

I'd say it's generally a bug-prone mistake any time you commit your entire working copy

The workflow I'd use instead of stash is to commit, do things, then reset --soft. Exact same effect as stash, except that it's less bug-prone, there's an easier & safer recovery mechanism in case of problems.

If that temporary backup commit somehow stays around in the final feature branch, a stash would be much preferred.

You're comparing making a small mess to losing work. Your opinion is your own, I'm not here to argue, but in my book the downside of losing work while using a revision control system is much larger than the downside of accidentally preserving imperfect commit refactoring. You're preferring something superficially nice over the actual safety mechanisms that is git's sole purpose.

10y agoHN ↗

I use stash in one of the two workflows below, neither of which have ever caused problems. Stash's advantage is in not needing to create branches, commit, or un-commit the code back into your working copy. Very quick, and I've never had problems.

- Paired operations `git stash` / `git stash pop` to quickly get rid of everything then reapply it a few minutes later.

- Stash as a "I probably don't want this code, but may as well keep a stashed copy" alternative to a hard reset. These stashes all then show up in my SourceTree sidebar to easily peruse or apply if needed (or copy/paste individual lines). Not a big deal if lost.

If I'm actually backing up code that I'll likely use, or need a remote backup of my current working copy to protect against laptop theft or HDD failure, I'll branch/commit/push the backup. Then use a mixed reset later to get rid of the commit itself and return everything to the working copy.

10y agoHN ↗

there's no sense arguing. to many git users I have met, the software isn't the product, the git repository is. I have met someone who will happily spend an entire day refactoring all the commit boundaries by teasing apart and combining diffs so that they match some retroactive narrative that looks prettier.

you must be one of those squash haters

10y agoHN ↗

I mostly only stash when I want to rebase -i because rebase -i seems to complain when I have uncommitted changes.

10y agoHN ↗

That's a good example of what I'd consider to be a mildly unsafe use of stash, since if anything goes wrong you can lose unsaved changes. It's easier to forget what you're doing if you have merge conflicts during your rebase, or end up stashing something else while you rebase.

The safer alternative workflow is:

  git commit -am "WIP"           # Just throw your uncommitted changes into a commit
  git rebase -i origin/master    # you might want to checkout my git rearrange command above
  git reset --soft HEAD^         # now un-stage your uncommitted changes and keep working

The advantage of this is getting your uncommitted changes into a commit that can be easily recovered if anything goes wrong. Even though you get rid of the commit, this process is all stored in the reflog, and you can undo any of it easily and without resorting to fsck.

10y agoHN ↗

You might check out "git rebase --autostash" if you haven't already.

10y agoHN ↗

In regards to stash, yes, loss is problematic, but one can simulate it by saving the diff to a file:

  git diff > my-stash.diff

Then you can even commit these if you like, or copy them to a safer directory. Applying them is just an invocation of `patch`.

10y agoHN ↗

Why did you write your rearrange with the ! form? The whole point of the leading ! is to say "don't prefix my alias with `git`", but then you go ahead and prefix your alias with `git`. You should be able to write that as

  rearrange = "rebase -i $(git merge-base HEAD @{u})"
10y agoHN ↗

Ha! You're right, that's silly, thanks! I didn't even notice. I don't remember why, it's been a long time, but I think I had a tricky pipe command to figure out the upstream branch-point a long time ago before I learned about merge-base and @{u}.

10y agoHN ↗

I was under the impression that you needed to use !-form if you wanted git to use the shell itself, and that not using it basically just does an exec on the subcommand? Is that inaccurate?

10y agoHN ↗

Hmm, maybe you're right. I don't know anymore. I thought it just prefixed aliases with "git " and otherwise ran them as normal, but I admit I haven't researched this topic in a long time.

10y agoHN ↗

Stashing is great

Because some things are only possible while there are no changes. (I changed this one-off file for testing or debugging something, need to pull latest changes, so I stash)

Stashing comes from a tool called quilt https://savannah.nongnu.org/projects/quilt

10y agoHN ↗

My IDE stashes things behind the scenes quite often, allowing me to do many operations on "dirty" workspaces.

10y agoHN ↗

Do people really use stash for things they value longer than a few minutes?

I've only ever used stash as a way to store changes that I made on the wrong branch.

  git checkout my_old_branch (wrong branch not realized yet)
   - work work work...
  git status
   - "oh, I'm on the wrong branch"
  git stash
  git checkout the_correct_branch
  git pull
  git stash apply (or git pop - but you lose what was on the top of the stash)
10y agoHN ↗

Do people really use stash for things they value longer than a few minutes?

You'd be surprised. ;) Even a few minutes can get you into trouble though. The very last person I helped out of a stash crisis two days ago did exactly what you outlined, fully intending to stash pop immediately, and couldn't find his change and panicked.

What actually happened was he'd unknowingly stashed again during his branch switch, and his first stash was there, but not on the top of the stack. GitHub desktop was auto-stashing or something. (GitHub desktop has made several very strange choices...) Anyway when my friend applied an empty stash, he thought he'd lost his changes. He called for help on a Saturday, and I tried to point him in the right direction over email, but by the time I got to a terminal to see what happened, he'd already rewritten most of his change, having decided it was a loss.

OK, FWIW here are some safer alternatives to switching branches without stash:

- First thing to know is that you can switch branches with uncommitted changes in your working tree. You don't have to do anything else, git will protect against conflicts, so why not always try this first? It's less work most of the time.

If git complains, you can:

  git commit -am "WIP for the_correct_branch"
  git checkout the_correct_branch
  git cherry-pick my_old_branch
  git reset --soft HEAD^  # Optional, if you want to unstage
  ... Later ...
  git checkout my_old_branch
  git log
  ... Notice the leftover commit ...
  git reset --hard HEAD^

Or here's an alternative that doesn't leave the dangling commit for later:

  git commit -am "WIP"
  git branch work         # you can always do this with uncommitted changes
  git reset --hard HEAD^  # still on my_old_branch here
  git checkout the_correct_branch
  git cherry-pick work
  git branch -d work

If anything goes wrong with these workflows, my changes have been dropped in two branches, they'll be easy to find in the reflog, and the gc timer doesn't even start yet because they're still referenced. It's really hard to lose the changes this way.

The only option with the stash flow if the stash gets lost while switching branches is to use fsck, and the gc timer starts immediately because the stash is not referenced.

I mention the gc timer because some of the accidents I've seen are people popping stashes and only realizing later that they popped a different change than they thought they did.

They way this happens is people who are beginners to git. Once you know git, it's hard to imagine popping the wrong stash and not knowing it, but I've seen it happen multiple times. This is why I advise everyone to avoid stash - for beginners, it looks deceptively and attractively simple, but is easy to get in hot water. It's also another git subsystem to learn, as if git wasn't hard enough to learn. For git experts, it's so easy to avoid using stash, there are always safer alternatives.

10y agoHN ↗

I tend to stash debug code, so for example I'm working on a feature, and while developing I've added a bunch of console.log()s (or pprint.pprint()s, print_r()s, whatever). Then I can do:

    git add --patch  # include only the code I want comitted
    git stash --keep-index
    # now I can do some tests without my debug cruft
    git commit

And then ship the feature to staging/production, if there are any remaining issues with it I still have my debug cruft in the stash. It's rarely needed, so I don't want the overhead of a branch for it. If I lose it it's also easy enough to just add the debug code I need at that moment in case the stash was cleared.

10y agoHN ↗

do people actually find stashing easier than branching?

Absolutely. I stash things all the time. I find feature branches only useful for pull requests. My own code never uses feature branches unless I'm unsure of where I'm headed with the feature—I'll need to continually come back to it and build it a little at a time. But that's very rare.

Right from the man page: "If you mistakenly drop or clear stashes, they cannot be recovered through the normal safety mechanisms."

Then don't ever use those commands. I don't. Checking one of my repos:

    $ git stash list | wc -l
          46

I just let the stash grow unbounded. Usually it's for cleaning up unfinished features or debugging when manipulating the repo (in which case it gets popped almost immediately). But often I'll just hit a point where I don't care about what I have too much, but I think it might have promise in the future and so I'll just stash and start over. One of the wonderful git commands is "git stash list -p", letting me search the diffs for code I know I've written at some point.

10y agoHN ↗

One of the hardest thing about git is the multitude of options and workflows. It makes it powerful but also intimidating for newbies - so many commands and flags!

Having several years of git experience, I've set up on the following workflow, which avoids stashing and mixed resets. I think it's quite safe and simple.

1. using feature branches for everything (except when I'm the only developer in a given repo); I have `goto` and `forkto` and aliases for git checkout / git checkout -b

2. frequent, small commits - for backups, and to have "recovery point" for this scenario when you change one line and everything's on fire and you can't find out what's going on for next one hour

3. try to never do partial commits, always commit everything ( git add -A . && git commit ). This teaches you a discipline to do one thing at a time. If you have a sudden need to do something else, git branch, git commit, git checkout previous-branch. Or git commit, continue working, git rebase -i to reorder/squash commits later if needed.

4. for "work in progress, will come back later" commit I have an alias "wip" = "git commit -A . -m 'wip'"

5. for permanently throwing away stuff, "git reset --hard" (used rarely)

6. since I do lots of small commits, at the end of development I clean up with `git rebase -i`, squash stuff, improve commit messages if needed, maybe reorder etc.

Due to 2 and 3 I basically don't use stash at all. I think however not everyone is so keen on interactive rebase as much as I do :)

10y agoHN ↗

I find feature branches only useful for pull requests

I wasn't super clear in my top post, sorry. Mentioned a couple times in the thread, but as an alternative to a single stash, I would normally commit, not branch. Consider whether a WIP commit is that much easier than a stash, given that a commit is harder to lose.

I mentioned branches because that's the alternative to a multi-stash workflow, or to having a large time gap with a lot of work in-between stash & pop. Otherwise, my question is how much harder, really, is git commit & git reset --soft, compared to git stash & git stash pop?

Then don't ever use those commands. I don't.

That seems like a great way to use stash safely. A couple others here mentioned using stash as an alternative to git reset. I'm in favor of that kind of stash usage, it's even safer & easer than losing something to a premature reset.

10y agoHN ↗

I have more than 400 stashes… it turns out it's quite often that I find my current approach completely wrong and want to do a hard reset. But I don't do a hard reset but I stash it so during my next attempt I can still see it.

10y agoHN ↗

yeah, I find myself more doing a local commit with message WIP. I switch branches to do something else, come back, look at the log and if WIP, roll back the last commit

10y agoHN ↗

In Chromium (well, actually depot_tools[1], the closest thing Chromium has to a developer SDK) we provide `git freeze`[2] and `git thaw`[3]. These behave similarly to 'stash', but instead of taking your content and putting it who-knows-where, they commit it on top of your current branch in a special FREEZE commit (or multiple commits, if you have both staged and unstaged changes) which they then know how to thaw out again. Our other tools (such as `git rebase-update`[4]) also know how to deal with FREEZE commits, and can automatically thaw them for you when you go back to working on that branch.

[1] https://chromium.googlesource.com/chromium/tools/depot_tools... [2] https://commondatastorage.googleapis.com/chrome-infra-docs/f... [3] https://commondatastorage.googleapis.com/chrome-infra-docs/f... [4] https://commondatastorage.googleapis.com/chrome-infra-docs/f...

10y agoHN ↗

Brilliant! I wonder why git stash wasn't implemented similarly in the first place.

10y agoHN ↗

My hypothesis: git was implemented assuming that—at least for large projects—you would have a local on-disk bare clone of the repo, and then multiple workdir checkouts of that bare clone sitting on different branches. The setup with a single clone containing a workdir with a .git dir inside it is a hack that's nice for "on-the-go" use-cases, but wasn't intended for serious developers contributing many changes to the same codebase.

git-stash, then, is only for the use-cases that are not better solved by just leaving your WIP workdir as it is and checking out another, concurrent workdir for your new feature. Such as, for example, taking code you were accidentally writing on one branch, and "shifting it over" onto some other branch; or "moving your changes out of the way" to run some tool that is broken by them, then putting them back in place, because you really still are working on them.

10y agoHN ↗

Er... but then you have (N+1) checkouts of the repository where N is the number of workdirs, no? How does this even work with submodules? Will I have a copy of the submodules tree in each workdir? Our submodules tree is like 4Gb at this point, I can't very well have 5 of them. Disks are large, but not yet infinite.

10y agoHN ↗

This is how you have to deal with branching in almost every scm. Changing branch might require you to rebuild something slow and then it's good to have multiple working directories. If you have monorepos you might also want to test component a on branch x vs component b on branch y etc.

If you are only working serially then switching branch in one working dir is obviously easier and to be preferred but some times you really need multiple checkouts of the same repo.

10y agoHN ↗

Honestly I stash only cause I don't fully understand how to branch and bring my changes with me when I realize I should have branched earlier

10y agoHN ↗

This is a great answer, honestly, and you're not alone.

FWIW, here's how. Since stash only captures uncommitted changes, I assume you're working on master and want to create a feature branch, and you've already made some changes for the branch.

  git checkout -b myFeature

That's it. Uncommitted changes aren't in a branch yet, so they don't have to be stashed before you make a new one. You can always start a branch from wherever you are, and switch to it, and your changes will be right there. Now you can commit, and your commit will be on the branch myFeature.

BTW, you can switch branches without stashing too. Git will prevent you from messing up your uncommitted changes. If that happens, then you can commit or stash first. The safest course of action is always to commit any changes first before you do anything.

It's also easy to build your feature branch if you have both committed and uncommitted changes already, or if you want to base your feature branch off a different branch than the one you're working in. I'm already spamming you, so just say so if you'd like me to outline those.

10y agoHN ↗

Thank you for explaining this! I didn't know that and will start incorporating that into my workflow :) thanks for teaching me something new!

10y agoHN ↗

I frequently swap between branches, for code review, or to move temporarily to something else for a short time.

I decide whether to commit what I have done or whether to stash it for now, based on what state it is in.

If I am still in the exploratory stage of the work, I usually wont commit, I will just stash and come back to it later.

Its a useful thing to be able to do at time when i am not yet ready to COMMIT to the changes I have made.

10y agoHN ↗

Of course I can't tell what your comfort level with git it. The more expert you are, the more it doesn't matter, and you can use stash safely.

I am not judging anyone or their workflows. It's the stash command that has the problem, because it circumvents git.

That said, my reaction to your comment is that you're taking "commit" too seriously. ;) Nothing in git is a commitment until you push. Nothing. If you haven't heard the phrase "commit early, commit often", consider that and take it to heart. If you have work you care about and even might want to save, then you should commit it. Git was literally made for the purpose of managing multiple versions of exploratory work -- to not use commits and branches for that purpose is to not use git to its full potential.

Stashing things you care about for some later time strikes me as the kind of use of stash that is the most prone to data loss. But again, if you're comfortable using fsck, none of this applies to you.

10y agoHN ↗

If code is important enough to me that I care whether or not I lose it, I commit and push.

I prefer to commit discrete change sets (not features, or stories, just a discrete set of changes that achieve a piece of what I am wanting, dont break the code base and dont break the build).

I have heard that phrase, I live by it and encourage others to do the same. it is especially important when working as a part of a bigger team on the same area of the code.

Teams that commit early and often are happy teams :)

OTOH I use stash constantly, multiple times every day, when switching between branches, because sometimes its just handy to be able to put stuff aside to look at something else.

I dont recall ever losing code because of it, but it is certainly possible and its not hard to see how it could happen.

10y agoHN ↗

Heres one I like to use sdiff = diff --ignore-space-change

10y agoHN ↗

If in doubt, the long one (git staaash) will always restore your worktree to what looks like a fresh clone of your repository.

Not necessarily. This depends on what branch you're on as well as whether or not it's up to date with the remote.

10y agoHN ↗

I'm no Git wizard, but switching from "merge" to "rebase" and from "add <file>" to "add -p" cleaned up my repos quite a bit.

10y agoHN ↗

One reason I like git so much is that right there. The author is interested in reasoning about what branch certain changes came from, but I have to wonder why. Once changes are squashed and ff-merged, it's pretty clear where they came from.

10y agoHN ↗

One of my personal favorite:

stp = !git stash && git pull && git stash pop

10y agoHN ↗

If you `git pull --rebase` by default, just set the rebase.autoStash config to true and git will automatically do this for you.

10y agoHN ↗

Can't believe I just learned about rebase.autoStash! That is super handy.

10y agoHN ↗

git it and empty root commit

Why doesn't git init does this by default? It would be nice if every repo had an empty root commit (with no author, date,... so it has the same commit hash), then every two repos would have a common commit. Semantically it would mean that repos would be just specific branches of a hypotetical large repo.

10y agoHN ↗

That would prevent merging a second repository into the first in its entirity.

10y agoHN ↗

You can already merge two divergent histories with the --allow-unrelated-histories option. (At the data level, merge commits don't care that their parents have a common ancestor, but the git merge command does this as a safety check.)

In other words, all repos already are subsets of the ur-repo. The semantically-interesting data in git is not the commits themselves, but the branch names given to specific commits in specific (groups of) repos. :)

10y agoHN ↗

Interesting, now I learned that a git repo can have any number of commits that have no parents. It can be achieved by fetching an unrelated repo and merging with --allow-unrelated-histories or by using git checkout --orphan.

safety check

Why is this considered unsafe? Only to avoid pulling from unrelated repos by accident? The man says for --allow-unrelated histories:

As that is a very rare occasion, no configuration variable to enable this by default exists and will not be added.

Well this is quite harsh, it looks like it's considered to be rather unsafe.

10y agoHN ↗

Yeah, I imagine it's unsafe because "git pull" implicitly creates a merge commit when it can't otherwise update, and your remote URL might now be pointing at a different history. Maybe github.com/someproject/somecode got replaced by a clean rewrite and you're actually looking for someproject/somecode-old. Maybe you were pointing at an unofficial git-svn import and now there's an official git migration. And so forth.

(Personally, I think the right way to address this is for "git pull" not to implicitly create merge commits...)

10y agoHN ↗

The first commit of a repository can not be rebased like regular commits

That's not necessarily true. This can be done with:

  git rebase -i --root
10y agoHN ↗

This doesn't even look like I wrote it, but I thought I did and use it all the time: "What was that (private) branch I worked on a few weeks ago?"

Just put this shell script in /usr/local/bin or wherever as "git-branch-dates"

  git branch-dates
  #!/bin/bash

  for k in `git branch|perl -pe s/^..//`;do echo -e `git show --pretty=format:"%Cgreen%ci %Cblue%cr%Creset" $k|head -n 1`\\t$k;done|sort -r
10y agoHN ↗

A couple of aliases I use all the time:

    [alias]
        graph = log --oneline --decorate --graph --all
        copy-logs-since = !git log --reverse --format=%B "$1".. | pbcopy

`graph` is similar to `grog` in the article. `git copy-logs-since <tag>` copies log messages since the tag, which I use for release notes. Very handy.

10y agoHN ↗

Nothing esoteric, but I advise every person I teach git about to put this in their .bashrc:

    alias s='git status'
    alias l='git log --graph --oneline --decorate --all --show-signature'
    alias d='git diff'
    alias dc='git diff --cached'
    alias c='git commit -m'
    alias ca='git commit -am'
    alias a='git add -p'
    alias u='git checkout -p'

These probably saved me hours of typing by now. Especially `l` I think should be canon.

10y agoHN ↗

I don't know if you're aware, so this is just a friendly suggestion: if you define these as git aliases instead, you won't use your shell's namespace and have it available for other aliases. As git aliases you would run it like `git s` or `git ca`. If you also alias git to g it will be `g ca`. If you do that, don't forget to call `__git_complete g __git_main` in .bashrc for it to auto-complete aliases for `g` and not just `git`. For example, to define `ca`, run this

    git config --global alias.ca 'commit -am'
10y agoHN ↗

I am of course aware of git aliases (e.g. because OP uses them a lot) but it never occured to me specifically to shorten git to g and find a way to keep autocomplete working.

However, since these are by far my most typed commands (specifically, probably 40% of my executed commands are `s`), I strongly care about the difference even between 1 and 3 keystrokes.

Thanks for your feedback. I wonder if the downvote was because of using bash aliases or for something more substantial...

10y agoHN ↗

I never downvote anything, so I'm not sure, but maybe the use of "advise" and "canon" triggered negative votes?

10y agoHN ↗

Thanks (if you're still reading this thread), I didn't know "advise" carried negative connotations. I guess it's one of those words that I learned from a dictionary and never stopped to notice that English speakers use it only in much more formal situations than the word in my native language that I'm trying to evoke.

Should that maybe have been "recommend"?

10y agoHN ↗

English isn't my 1st language either, but the power of downvotes is misused a lot on the internet, especially when there's no public trail who downvoted. Advise and canon may just carry too strong of a meaning for those who grew up in English-speaking school systems.

10y agoHN ↗

I like "git ready":

    ready = !git checkout master && git fetch -p && git merge --ff-only

The use case is for when you're done with a particular feature branch and ready to start something new. It puts you back on master and updates it in a safe way.

10y agoHN ↗

You baitin... how is a lesser known git command a git alias?

10y agoHN ↗

Author's "commend" function I just call "cane"

git (c)ommit --(a)mend --(n)o-(e)dit

10y agoHN ↗

A few that weren't mentioned below:

  git alias start 'checkout @{u} -B'

All my branches track origin/master (rather than local master), which makes it easy to git push / git pull without extra arguments. This will checkout a new branch from whatever the current branch is tracking. Usage: `git start my-new-feature`

  git alias track 'track = branch --set-upstream-to'

Sets up your current branch to track some other branch. Usage: `git track origin/master`.

  git alias gcbr 'gcbr = !git branch --no-track --no-color --merged | sed 's/[ *]*//' | grep -v master | xargs -n1 git branch -d &> /dev/null || exit 0'

"Garbage Collect BRanches" will delete any branches which are already merged into your current branch (excluding master). Basically, any branch which is "safe" to delete. (This one has been handed down through the ages; I initially found it on the skeleton dotfiles when I started working at Facebook. Thanks, whoever!)

10y agoHN ↗

nitpick: I don't think you need --no-track in `branch --merged` as no branch is being created with that command.

10y agoHN ↗

    git diff origin/master -- test.file

It will show you a diff of a file between the master branch of remote origin and your local branch. I use it way more than I can think of.

10y agoHN ↗

Some of my aliases, first some basic shortcuts:

  freshen = commit --amend --no-edit --date=now
  cont = rebase --continue
  br = branch --column
  recent = branch --sort=committerdate
  idiff = diff --cached
  ff = merge --ff-only
  co = checkout

Show what's left to do in an interactive rebase:

  todo = !cat `git rev-parse --git-dir`/rebase-merge/git-rebase-todo

Set tracking to to origin/<the same branch name>

  upstream = !zsh -c 'git branch --set-upstream-to=origin/$(git symbolic-ref --short HEAD) $(git symbolic-ref --short HEAD)'

I use this to swap my work to home e-mail or vice-versa if I've got it wrong. Need to specify an initial commit.

  allmine = filter-branch --env-filter 'GIT_COMMITTER_EMAIL=my@email.com GIT_AUTHOR_EMAIL=my@email.com'

  vim = "!gvim `git ls-files -m`"
10y agoHN ↗

I'm still getting to know Git, and I appreciate articles like this as they touch on approaches to Git that have benefits in the real world, but I can't help but feel that version control tools should be simpler.

Does anyone have any experience with version control tools that have worked out simpler to use than Git? I've heard good things about Darcs before (aside from its performance issues, which projects like Pijul are designed to address), are there any other tools that are worth investigating?

10y agoHN ↗

It depends what you're using it for. Many people use Windows every day without knowing things like how to remap a network drive--but it's nice to have it there when you need it.

If you're by yourself and just need to keep a linear history all you need is 'git init' 'git add' and 'git commit'--if that's all you ever need, svn or any version control should work about equally well. But if you do need remote repositories, branches, rewriting your history, or commit hooks, it's there waiting if you're using git.

I feel like most of the different version control systems differ on those advanced features or how it scales for special needs. Git is great for decentralized and remote work, Perforce is better at handling histories with large binary files, etc. The basic features are fairly similar between them. Maybe there's a gui you like or a specific workflow, but I feel like that's a subjective choice.

10y agoHN ↗

git commend quietly tacks any staged files onto the last commit you created, re-using your existing commit message. So as long as you haven’t pushed yet, no-one will be the wiser.

Well, they might notice that the author and committer dates don't match, but you can add "--reset-author" to really make this undetectable.

10y agoHN ↗

one thing i wish git would do would be to allow me to specify default options for certain aliases. for example, I quite like the `--short --branch` format of git status. but I can't do this:

  [alias]
     status = "status -sb"

because git just ignores those. i am then forced to setup non-standard commands just to bend git to my will.

in some cases, there are distinct `git-config` options I can set to get the right behavior, but that's way less flexible since stuff like `--force-with-lease` don't have their own config options.

the rationale behind this is, according to the git-config manpage, that "To avoid confusion and troubles with script usage, aliases that hide existing git commands are ignored". but then there's --porcelain for that, so i don't understand that limitation.

sigh.

10y agoHN ↗

For every repo I fork I usually add a remote named upstream pointing to the original.

So then, whenever I need to get my copy up to date with upstream it's:

    update = !git fetch upstream && git merge upstream/master'

It's probably easily changed to make the branch configurable, but I have only ever needed this with master.

10y agoHN ↗

My two favourite aliases :

    mo = ls-files -m
    lol = log --oneline --decorate --graph

`git mo tests/` gets you modified files for the tests folder.

`git lol` I think as the command line version of gitk