Wow, never autopush all your tags. What a terrible idea! Once a tag escapes into the wild, it's so hard to ever get rid of it. Pushing a tag should require two people turning keys simultaneously or something, honestly.
zdiff3 is also NOT some kind of generic improvement over diff3, like swapping diff algorithms is sorta a generic improvement. It behaves very differently; if you have
[original]
this is a line
[left branch]
This is a line.
This is a second line.
[right branch]
This is a line.
This is another line.
then diff3 shows
<<<<<<<
This is a line.
This is a second line.
|||||||
this is a line
=======
This is a line.
This is another line.
>>>>>>>
while zdiff3 modifies this in a very matter-of-personal-taste way to:
This is a line.
<<<<<<<
This is a second line.
|||||||
this is a line
=======
This is another line.
>>>>>>>
This is incredibly bizarre to my eye, but some prefer how it's already "partially resolved" for them. But it's hardly something to swap in thoughtlessly for the other!
+1 on zdiff3. It makes little sense to me; it seems like it can describe different conflicts in the same way. I think diff3 is the only sane choice (the default 2-sided presentation is also somewhat sane, though questionable. It loses important information but is simpler).
zdiff3 is described here: <
https://git-scm.com/docs/git-merge#_how_conflicts_are_presen...>. The example in there makes zdiff3 look quite confusing, since it makes it look as though a line was deleted on both sides. For a while, I thought including the identical line in the base it must be a typo, but apparently that's how it's supposed to work.
Agreed. If you're using tags for anything other than final releases off of main/master (or other release branches that are supposed to be permanent), you've gone down a very bad path.
It has happened to me in the past to wonder why certain files/folders are ignored by git, only to realise that I had a global git ignore for the particular pattern.
Not sure l’d recommend this as a good default, but perhaps others have better memory than I do.
My gitignore is just a pile of things I _always_ want to ignore:
# OS
.DS_Store
# Editors
.vscode-server/
.idea/
# Javascript
.npm/
node_modules/
...more stuff per language
I find it really nice to just keep all that in one place and not have to configure it per project. There's nothing too broad or situational in there that might get in the way - those kinds of things can go into the project specific .gitignores.
There's also `git status --ignored` to check if you're missing anything.
My reason for having each and every common ignore in each individual repo is that on the off chance that someone else wants to contribute to any of my projects, those files are already ignored for those people too.
And also, sometimes I work from different machines and I don’t really want to have yet another dotfile to sync between all my current and future machines.
(Yes, I know dotfile managers exist, but I literally only care about syncing my zsh config files and one or two other dotfiles mainly so I do that with my own little shell script and basically don’t care about syncing other dotfiles.)
I just mean, it’s intentionally not a fancy setup with all kinds of things.
Just the most essential stuff and some symlinks. For the few dotfiles I really care about.
In an ideal world, I wouldn’t need any dotfiles at all. And my home directories would only contain files that I myself put there. Like my photos, my music, and code that I write. Those kinds of things.
That’s the main reason I don’t like managing all of my dotfiles. Because I don’t really want them in the first place. The only thing I want less in my home dirs but which I also have to live with is all of the other garbage that programs I use put there on their own. Like caches and different weird files that the programs decided should be there.
Generally agree with you but I'm not going to clutter my project's .gitignore with stuff that's in the responsibility of the user to keep in their own ignore list like .DS_Store.
E.g. Each js project gets a /.npm /node_modules, each py proj a .pyc etc...
Editor is generally one per project which checked in config (.vscode) and if you want to use vim, have your own rules for ~ which you likely have anyway.
Also, both are not exclusive: .env can be checked in and in .gitignore
It makes sense to have global defaults specific to your machine if and only if your workflow (editor, personal tooling) creates files that you don't want as a part of any project. Things like vim or emacs temp files, macOS's DS Store files, etc. This doesn't apply if your team uses standardized editor configs.
Tangentially you can set vims temp/swap files to something like ~/.vim/tmp/ so they don’t clutter repo directories. At least that’s my pref for this annoyance/clutter
I have to put a complete one in every project. I work with a lot of junior devs. On many occasions they're committing too much - cause they a) don't have good global config and b) are not selective about git add. A teaching opportunity but annoying to clean up.
This option in general seems odd to me. Wouldn't all contributors to the project need to coordinate their exclusions manually this way? Am I missing something?
You would normally use both this and a project-specific gitignore.
The project-specific file is for stuff that should be shared amongst all users of a particular project. So for a Node project you might have `node_modules` in there, for a Python project you might have `.venv` and `*.pyc`. If your project uses env files, you might have a line like `.env` in there for that.
Meanwhile, the global gitignore is for stuff that's specific to your system. If you're on MacOS, you might have an entire for `.DS_Store`, or if you use Vim a lot you might have an entry for `*~` files (i.e. vim backup files). This is stuff that's specific to you.
Git can then combine the project-specific ignores (located in the project respiratory) and your user-specific ignores (located in your global gitignore file), and ignores anything that matches either case.
It's useful for bespoke developer settings. I like to keep a scratch folder in virtually every repo I work on, so I have `/tomscratch` in my global excludes file. Adding this to .gitignore in every repo I work on would just be noise for everyone else.
I've been putting a .gitignore file containing a single * inside my scratch directories. But ~/.gitignore sounds like a good idea for local things that I do in every repository.
I disagree. If it's minimalistic and focused on your commonalities across projects in a computer, such as VScode stuff which shouldn't be committed to a general repo, I think it's the right and unobtrusive choice.
I agree with you. In fact, I think it's better to free up "configuration" memory, and use it to store "best practice" or "process" in general. I find that convention over configuration works out better on a long run, and when switching between projects - either the developer themselves, or when parts of the team leave and others join.
(personally I think Difftastic's treesitter based approach is superior to Delta, yet I always appreciate it when people link alternatives when discussing apps so thought I'd add this for completeness' sake)
Good stuff here. But I don‘t like the auto pruning suggestion as a reasonable global default. Or at least this shows that users who really see this as useful see no real difference between git and let’s say SVN. The whole idea is that the system is distributed and that you don‘t follow blind another upstream. But I grant the fact that most projects are not developed like the Linux kernel for which git was created.
But there is another issue. I personally like the fact that I keep a history of old branches etc. This can also save me if I or another person in the team deletes a branch by accident. I assume that pruning would maybe not delete the objects right away. Need to check that.
What would actually happen if I have a branch under development and it gets deleted from remote? Will it remove locally for me then? I guess only when my local head is equal to what remote pointed to.
Because Git is distributed, there's no "a branch" foo or whatever; that's a very pointedly centralized-logic, non-DVCS way to treat Git. If Alice has a head named "foo", Bob can fetch Alice as a remote and get a remote-tracking head "alice/foo", which has no bearing on any head named "foo" Bob has; it's just a view of Alice's state.
You can see why you'd want to prune these on fetch, if you want your refs/remotes/alice state to mirror Alice's state; this means removing "alice/foo" if "foo" on Alice is removed. You also might not want to, that's fine. But if you use git fetch as though Git were a DVCS, this is exactly the natural behavior; it's only when you imagine Git is centralized that it starts seeming strange.
So the answer to your final question is "this is not how distributed VCSes work".
(Just don't ask about tags. Ugh, what a nightmare! To be fair, the idea is that you don't go around fetching tags.)
"What would actually happen if I have a branch under development and it gets deleted from remote?"
The remote reference would be deleted but your local one would not, and you could push your local right back up to the remote if you want. Pruning is totally safe.
Ok. Was late yesterday. I reread the passage and understood it wrong. Yes if it keeps the „tracking“ branch information in sync that’s ok. I assumed it autocleans local branches. The initial statement regarding history etc kept me off track.
Yea, I enabled those without thinking through all of my use cases and I lost some archived data I didn't intend to. I definitely should've been doing a better job of maintaining those archives, but definitely not an option for everyone.
The irony here being that GitButler is a GUI for git, whereas most of these config values are for the benefit of the CLI. :P
Since they're being too polite to shill themselves, I'll tempt HN by mentioning that GitButler is open-source and will soon be switching its underlying git support from libgit2 to gitoxide (a rewrite of libgit2 in Rust).
It would be nice to have a way to auto-fastforward other branches when pulling and you have no commits in those branches (There are scripts for that, but it should be in git).
Another nice thing would be a way to exclude file specs in git diff (Hack: add 'true' as a diff program in .git/config and use .gitattributes to exclude by setting diff=true).
EDIT: I was wrong and misunderstanding how these options interact, this doesn't solve the stated problem. Still good to have in your config but not as powerful as desired, sorry. Keeping comment contents mostly for posterity's sake.
Under the hood `git pull` is just `git fetch`+`git merge` so these two configs together would ideally enable the behavior requested (but unfortunately don't):
[fetch]
all = true
[pull]
ff = only
I also disable fast-forwarding with `git merge` since I prefer the branch topology you get without it. This doesn't break natural `git pull` fast-forwarding either, thanks to the above config:
The fast-forward is on branch A. I'd like the git pull to also fast-forward branch B (and branches C,D,Etc.).
They're usually by other devs, I don't usually check these, but when I do, I could save an extra command when switching if git just moved the branch head with the data it already has.
You're right that doesn't fix it and it is annoying. I just learned something about git, thanks!
Thinking on this more I think I'd prefer changing `git checkout/switch <branch>` to automatically use `origin/<branch>` instead (like mook mentions in a sibling comment), rather than changing the `git pull` behavior.
Keeping the local branch refs in place after `git pull` lets you still easily see the local vs remote differences when running `git log`. Changing the behavior of `git checkout` or whatever you use to switch branches to auto fast-forward seems to be a way to have your cake and eat it too, almost.
But, clearly you've thought more on this than I have (having just learned about it lol), what do you think?
These aren't branches I commit regularly to, but I'd rather avoid the detached head state when using 'origin/'. Auto fast-forwarding on switch would be good enough for me.
[alias]
unstage = reset HEAD # remove files from index (tracking)
uncommit = reset --soft HEAD^ # go back before last commit, with files in uncommitted state
This is my global gitconfig (minus personal things like user.email etc):
[core]
pager =
[pull]
ff = only
I don't want git to start a merge without my explicit request, hence the ff-only on git pull. Similarly, opening a pager without me explicitly asking for it is an antipattern and I can't stand tools that do so by default. My terminal has scrollback history TYVM.
[push]
default = upstream
I think this is the default now anyway, but I don't want git assuming anything about remote-local branch relationships.
Tip for Emacs users: One easy way to sort the sections in .gitconfig alphabetically - turn them into org-mode headings. replace-regexp "^[" with "^* ["; then mark-whole-buffer; org-sort; finally, replace the headings back to be normal sections.
I’d bet my money on the GitButler team. They’re experts in a small market and I think they’ll do something great. I don’t think their current product is it, but I think they’ll do something great.
Wow, never autopush all your tags. What a terrible idea! Once a tag escapes into the wild, it's so hard to ever get rid of it. Pushing a tag should require two people turning keys simultaneously or something, honestly.
zdiff3 is also NOT some kind of generic improvement over diff3, like swapping diff algorithms is sorta a generic improvement. It behaves very differently; if you have
then diff3 shows while zdiff3 modifies this in a very matter-of-personal-taste way to: This is incredibly bizarre to my eye, but some prefer how it's already "partially resolved" for them. But it's hardly something to swap in thoughtlessly for the other!+1 on zdiff3. It makes little sense to me; it seems like it can describe different conflicts in the same way. I think diff3 is the only sane choice (the default 2-sided presentation is also somewhat sane, though questionable. It loses important information but is simpler).
zdiff3 is described here: < https://git-scm.com/docs/git-merge#_how_conflicts_are_presen...>. The example in there makes zdiff3 look quite confusing, since it makes it look as though a line was deleted on both sides. For a while, I thought including the identical line in the base it must be a typo, but apparently that's how it's supposed to work.
See also https://lore.kernel.org/git/CAO_smVg=1gFBudrd70V2_AXSPOUTFz=...
Agreed. If you're using tags for anything other than final releases off of main/master (or other release branches that are supposed to be permanent), you've gone down a very bad path.
I use tags for all kinds of personal, often temporary, things, so, yeah, no followTags
> excludesfile = ~/.gitignore
It has happened to me in the past to wonder why certain files/folders are ignored by git, only to realise that I had a global git ignore for the particular pattern.
Not sure l’d recommend this as a good default, but perhaps others have better memory than I do.
My gitignore is just a pile of things I _always_ want to ignore:
I find it really nice to just keep all that in one place and not have to configure it per project. There's nothing too broad or situational in there that might get in the way - those kinds of things can go into the project specific .gitignores.There's also `git status --ignored` to check if you're missing anything.
My reason for having each and every common ignore in each individual repo is that on the off chance that someone else wants to contribute to any of my projects, those files are already ignored for those people too.
And also, sometimes I work from different machines and I don’t really want to have yet another dotfile to sync between all my current and future machines.
(Yes, I know dotfile managers exist, but I literally only care about syncing my zsh config files and one or two other dotfiles mainly so I do that with my own little shell script and basically don’t care about syncing other dotfiles.)
You don't keep your dot files in git? :)
Yes, they are in a git repo :D
I just mean, it’s intentionally not a fancy setup with all kinds of things.
Just the most essential stuff and some symlinks. For the few dotfiles I really care about.
In an ideal world, I wouldn’t need any dotfiles at all. And my home directories would only contain files that I myself put there. Like my photos, my music, and code that I write. Those kinds of things.
That’s the main reason I don’t like managing all of my dotfiles. Because I don’t really want them in the first place. The only thing I want less in my home dirs but which I also have to live with is all of the other garbage that programs I use put there on their own. Like caches and different weird files that the programs decided should be there.
https://github.com/ctsrc/zshrc
Generally agree with you but I'm not going to clutter my project's .gitignore with stuff that's in the responsibility of the user to keep in their own ignore list like .DS_Store. E.g. Each js project gets a /.npm /node_modules, each py proj a .pyc etc... Editor is generally one per project which checked in config (.vscode) and if you want to use vim, have your own rules for ~ which you likely have anyway. Also, both are not exclusive: .env can be checked in and in .gitignore
I always put IDE-specific ignores in a global gitignore since not every project gitignore has an entry for every IDE.
It makes sense to have global defaults specific to your machine if and only if your workflow (editor, personal tooling) creates files that you don't want as a part of any project. Things like vim or emacs temp files, macOS's DS Store files, etc. This doesn't apply if your team uses standardized editor configs.
Tangentially you can set vims temp/swap files to something like ~/.vim/tmp/ so they don’t clutter repo directories. At least that’s my pref for this annoyance/clutter
I have to put a complete one in every project. I work with a lot of junior devs. On many occasions they're committing too much - cause they a) don't have good global config and b) are not selective about git add. A teaching opportunity but annoying to clean up.
Just a view from the other end of the spectrum.
This option in general seems odd to me. Wouldn't all contributors to the project need to coordinate their exclusions manually this way? Am I missing something?
You would normally use both this and a project-specific gitignore.
The project-specific file is for stuff that should be shared amongst all users of a particular project. So for a Node project you might have `node_modules` in there, for a Python project you might have `.venv` and `*.pyc`. If your project uses env files, you might have a line like `.env` in there for that.
Meanwhile, the global gitignore is for stuff that's specific to your system. If you're on MacOS, you might have an entire for `.DS_Store`, or if you use Vim a lot you might have an entry for `*~` files (i.e. vim backup files). This is stuff that's specific to you.
Git can then combine the project-specific ignores (located in the project respiratory) and your user-specific ignores (located in your global gitignore file), and ignores anything that matches either case.
I imagine it includes things like .DS_STORE.
It's useful for bespoke developer settings. I like to keep a scratch folder in virtually every repo I work on, so I have `/tomscratch` in my global excludes file. Adding this to .gitignore in every repo I work on would just be noise for everyone else.
Similarly, I globally exclude `*.tmp`. I can pipe logs or anything else to some `example.tmp` and never worry about checking them in.
I've been putting a .gitignore file containing a single * inside my scratch directories. But ~/.gitignore sounds like a good idea for local things that I do in every repository.
Agreed, I don't want my checkout implicitly ignoring a file somebody else's checkout wouldn't.
Probably not what you meant, but git checkout is not affected by .gitignore anyway. If a file is already in the repository, git will keep tracking it.
I disagree. If it's minimalistic and focused on your commonalities across projects in a computer, such as VScode stuff which shouldn't be committed to a general repo, I think it's the right and unobtrusive choice.
https://git-scm.com/docs/git-check-ignore is useful to diagnose why a file is ignored
I agree with you. In fact, I think it's better to free up "configuration" memory, and use it to store "best practice" or "process" in general. I find that convention over configuration works out better on a long run, and when switching between projects - either the developer themselves, or when parts of the team leave and others join.
My favorite "took me a long time to discover" Git tweak is to use difft for diffs:
https://difftastic.wilfred.me.uk/
An alternative tool that also improves git diff functionality is Delta:
https://github.com/dandavison/delta
(personally I think Difftastic's treesitter based approach is superior to Delta, yet I always appreciate it when people link alternatives when discussing apps so thought I'd add this for completeness' sake)
Couldn't get it to work, constant permission denied errors
Good stuff here. But I don‘t like the auto pruning suggestion as a reasonable global default. Or at least this shows that users who really see this as useful see no real difference between git and let’s say SVN. The whole idea is that the system is distributed and that you don‘t follow blind another upstream. But I grant the fact that most projects are not developed like the Linux kernel for which git was created. But there is another issue. I personally like the fact that I keep a history of old branches etc. This can also save me if I or another person in the team deletes a branch by accident. I assume that pruning would maybe not delete the objects right away. Need to check that.
What would actually happen if I have a branch under development and it gets deleted from remote? Will it remove locally for me then? I guess only when my local head is equal to what remote pointed to.
Because Git is distributed, there's no "a branch" foo or whatever; that's a very pointedly centralized-logic, non-DVCS way to treat Git. If Alice has a head named "foo", Bob can fetch Alice as a remote and get a remote-tracking head "alice/foo", which has no bearing on any head named "foo" Bob has; it's just a view of Alice's state.
You can see why you'd want to prune these on fetch, if you want your refs/remotes/alice state to mirror Alice's state; this means removing "alice/foo" if "foo" on Alice is removed. You also might not want to, that's fine. But if you use git fetch as though Git were a DVCS, this is exactly the natural behavior; it's only when you imagine Git is centralized that it starts seeming strange.
So the answer to your final question is "this is not how distributed VCSes work".
(Just don't ask about tags. Ugh, what a nightmare! To be fair, the idea is that you don't go around fetching tags.)
"What would actually happen if I have a branch under development and it gets deleted from remote?"
The remote reference would be deleted but your local one would not, and you could push your local right back up to the remote if you want. Pruning is totally safe.
Ok. Was late yesterday. I reread the passage and understood it wrong. Yes if it keeps the „tracking“ branch information in sync that’s ok. I assumed it autocleans local branches. The initial statement regarding history etc kept me off track.
Yea, I enabled those without thinking through all of my use cases and I lost some archived data I didn't intend to. I definitely should've been doing a better job of maintaining those archives, but definitely not an option for everyone.
I had the same thought, I got an alias set up in my ~/.gitconfig to clean up branches that gets deleted from remote when I'm ready
`gone = !git branch -vv | awk '/: gone]/{print $1}' | xargs git branch -D`
dupe of https://news.ycombinator.com/item?id=43169435
The irony here being that GitButler is a GUI for git, whereas most of these config values are for the benefit of the CLI. :P
Since they're being too polite to shill themselves, I'll tempt HN by mentioning that GitButler is open-source and will soon be switching its underlying git support from libgit2 to gitoxide (a rewrite of libgit2 in Rust).
GitButler relicensed[1] to FSL, so it's source-available now, open-source after two years.
[1]: https://blog.gitbutler.com/gitbutler-is-now-fair-source/
This was posted already earlier today, lots of comments on the other thread:
https://news.ycombinator.com/item?id=43169435
It would be nice to have a way to auto-fastforward other branches when pulling and you have no commits in those branches (There are scripts for that, but it should be in git).
Another nice thing would be a way to exclude file specs in git diff (Hack: add 'true' as a diff program in .git/config and use .gitattributes to exclude by setting diff=true).
EDIT: I was wrong and misunderstanding how these options interact, this doesn't solve the stated problem. Still good to have in your config but not as powerful as desired, sorry. Keeping comment contents mostly for posterity's sake.
Under the hood `git pull` is just `git fetch`+`git merge` so these two configs together would ideally enable the behavior requested (but unfortunately don't):
I also disable fast-forwarding with `git merge` since I prefer the branch topology you get without it. This doesn't break natural `git pull` fast-forwarding either, thanks to the above config: Commands to set all these:I meant:
(we're in branch A)
>git pull
...
From ...
rev1..rev2 A -> origin/A
rev3..rev4 B -> origin/B
Updating rev1..rev2
Fast-forward
---
The fast-forward is on branch A. I'd like the git pull to also fast-forward branch B (and branches C,D,Etc.). They're usually by other devs, I don't usually check these, but when I do, I could save an extra command when switching if git just moved the branch head with the data it already has.
I work around it with:
There's no reason to clutter `git branch list` output with branches I don't care about.I do care, I just won't be committing there 99% of the time.
Your idea is interesting, didn't know that's useful. Detached head is a bit annoying though.
You're right that doesn't fix it and it is annoying. I just learned something about git, thanks!
Thinking on this more I think I'd prefer changing `git checkout/switch <branch>` to automatically use `origin/<branch>` instead (like mook mentions in a sibling comment), rather than changing the `git pull` behavior.
Keeping the local branch refs in place after `git pull` lets you still easily see the local vs remote differences when running `git log`. Changing the behavior of `git checkout` or whatever you use to switch branches to auto fast-forward seems to be a way to have your cake and eat it too, almost.
But, clearly you've thought more on this than I have (having just learned about it lol), what do you think?
These aren't branches I commit regularly to, but I'd rather avoid the detached head state when using 'origin/'. Auto fast-forwarding on switch would be good enough for me.
skwp/git-workflows-book > .gitconfig appendix: https://github.com/skwp/git-workflows-book?tab=readme-ov-fil... :
https://learngitbranching.js.org/charmbracelet/git-lfs-transfer: https://github.com/charmbracelet/git-lfs-transfer
jj-vcs/jj: https://github.com/jj-vcs/jj
Might be worth mentioning that fsmonitor is Windows and Mac OS only.
Also I wish GitHub and GitLab had colorMoved.
Correct, however it looks like fsmonitor might (someday?) be available on Linux too: https://lore.kernel.org/git/pull.1352.git.git.1665326258.git...
Edit: or go third-party? https://github.com/jgavris/rs-git-fsmonitor I don't have any experience with it though.
Curious what I might have missed that you all put in your `~/.gitconfig`
This is my global gitconfig (minus personal things like user.email etc):
I don't want git to start a merge without my explicit request, hence the ff-only on git pull. Similarly, opening a pager without me explicitly asking for it is an antipattern and I can't stand tools that do so by default. My terminal has scrollback history TYVM. I think this is the default now anyway, but I don't want git assuming anything about remote-local branch relationships. Simple git tree-log shorthand. I always tend to forget when I stashed something, so I need the explicit reminder. The second one makes git stash more like git show.Most of my personalizations are through aliases, but another no-brainer is
[interactive] singleKey = true
and I'd recommend are
[gc] reflogExpire = never reflogExpireUnreachable = 150 (or another high number)
.DS_Store
*.swp
That's .gitignore. GP is asking about what you have in your .gitconfig.
You can however put those in your global `core.excludesfile` path as defined in .gitconfig
If that last one is vim, you probably actually want `*.sw?` as vim will create a new temporary file if you decided to edit anyway when one exists.
It could clobber other tools' file endings, though.
Tip for Emacs users: One easy way to sort the sections in .gitconfig alphabetically - turn them into org-mode headings. replace-regexp "^[" with "^* ["; then mark-whole-buffer; org-sort; finally, replace the headings back to be normal sections.
I’d bet my money on the GitButler team. They’re experts in a small market and I think they’ll do something great. I don’t think their current product is it, but I think they’ll do something great.
[dead]
[flagged]