Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
What's New In Git 1.8.5 (atlassian.com)
116 points by durdn on Dec 4, 2013 | hide | past | favorite | 71 comments


The changes look good!

Can't help but notice OP's blog platform replaces "--" (dash-dash) with "–" (U+2013, en dash), even in <span>s meant for code highlight. This prevents straightforward copy-paste of arguments like "--prune" to console ;-)


Hey dexen, I'm the author of the post, yes I've reported the issue before to the internal team in charge of the blogging platform. I'll report it again. I've also been lobbying to move our technical blog to a different technology (I'd love a statically generated solution). Hopefully soon(tm).


Since migrating to a new platform might be considerable work you could suggest they install this plugin to disable the em-dash miss-feature: http://wordpress.org/plugins/disabler/


I believe wrapping the text in <code> tags might be a workaround, though I haven't tested it.


–=--


was hoping to see the deadly recursive merge disabled as a default and a one step mechanism to revert a branch.

instead a lot of relatively minor things i don't really care about (i'm sure its useful) - also seeing 'something now implemented in C' instead of 'something now 30% faster (because implemented in C)' is slightly worrying because users don't care about implementation details unless you get something else very very wrong...

my issue with recursive merge is that 'works for 90% of linux kernel changes' or whatever it is is just not the same as 'works reliably'.

git fails to meet my minimum requirements for usable source control because of the extreme difficulties I seem to have with undoing a merge if i don't change settings ahead of time (i.e. there is a bug which is a bad choice of defaults). i give it a good go, i get help from the community, i exhaust documentation and google - it doesn't work for me. maybe i don't understand something - i don't want to understand it, i want it to 'just work' and 'be user friendly'.


I'd be interested to hear your complaints with git-merge-recursive. Unless your common ancestor is multiple ancestors (eg, criss-cross merges), which I can't imagine is really commmon, it shouldn't differ appreciably from git-merge-resolve except that it can do rename detection.


My guess would be the default "fast-forward merge when possible". In this case there is no merge commit. When more is committed on top, you can see no trace of a merge anymore.


my complaint is exactly the comment i made. 'works most of the time in one situation' is not working. its a bit magical - i've only seen it do something insane once and i can't remember the specifics, but it introduced me to the concept and made me want to turn it off.

i really don't mind conflicts. safety over rushing every time...


I'd say I'm quite proficient with git but undoing merges is something I dread having to do because the documentation for it is so complicated.


What do you mean by undoing a merge? I assume you're talking about something more complicated than what I usually do

    git reset --hard HEAD~
That just kicks you back to the commit just prior to your current (merge) commit. Are you talking about trying to undo a merge further back in the commit history?


The scenario is something I try very hard to avoid via workflow, but sometimes it still happens. Somebody merges something and pushes it to the master branch of the repo before it's ready. Now you want to pretend that never happened. You can't just revert it. If anyone can post instructions for easily getting the master branch back to how it was before the merge let me know.


> Somebody merges something and pushes it to the master branch of the repo before it's ready. Now you want to pretend that never happened.

Say 'abc123' is the commit immediately before the merge commit. Run this:

    git rebase -i abc123
In the editor window that appears, delete the commit(s) that you want to "pretend never happened". Exit the editor. Git then replays the commits from abc123 up to HEAD, except without the commits you deleted. Now your local branch is in a state that omits the premature merge commit.


Or comparable to above,

  git reset --hard <previous commit's hash>
and afterward you may have to

  git push --force
...to overwrite the changes in the blessed/central/public/shared repo.


Your problem is not undoing merges, but allowing people to merge that shouldn't.

A good solution like GitHub Enterprise or Stash will allow you to use pull requests, so you can require that code gets reviewed by a third-party before merging.

In the case of Atlassian Stash, you can set branch permissions, which allow only certain users to commit/merge to that branch, and you can also require that a merge request be reviewed by specific individuals before the system allows it to be merged.

Preventing the merge problem in the first place is a much better approach than trying to fix it afterwards.


> Preventing the merge problem in the first place is a much better approach than trying to fix it afterwards.

In a perfect world, all problems would be prevented. Unfortunately, we live in the real world, where being able to fix it afterwards is very important.


Exactly. We use github enterprise. Our normal workflow prevents this problem, but shit happens.


nope.

if i merge two branches, push that to the remote and i wan't to undo it.


If no one has pulled your branch, just do a force push (use --dry-run to double-check your push).


So push your fix? Push hard if necessary, and if your remote forbids non-fastforward pushes even with force, then delete the remote branch and recreate it under the same name with the commit that you want.


In cases where forced pushes are disabled, deleting whole branches are typically disabled too, and generally for good reason (if someone else already pulled the branch with the removed commit they can end up in a mess and/or just repushing the unwanted thing by accident.

So actually reverting a merge in git isn't that painful. It's typically just git revert -m 1 sha_1_of_merge_commit

The problem is that if you then do more work on the branch and fix the problems that caused the merge to be reverted, redoing the merge doesn't work the way you expect; the commits that were previously merged don't get re-merged. So re-merging the branch consists of reverting the revert and then merging to get the new commits. Which is painful in-as-much as it requires you to know about the previous revert.

My favoured solution to this is to never merge, always rebase, which also has the property of giving a nice linear history that can actually be understood. However some people prefer a merge-based workflow (and if you happen to use github it is very difficult to enforce anything else, due to the Big Green Button), so it is a real problem.


Where I work we have forced pushes disabled, but allow "delete the branch/recreate the branch" as an inconvenient workaround for people who really need it. Of course the remote is set up to never prune the seemingly now unreachable commits, and it keeps a record of what branches exist/have existed and what they have been pointed at. I think if you push sensitive information you can get it actually removed if you contact the VC team, but everything is set up to be auditable and safe by default.

I agree with your "always rebase" method, the readable history is well worth it alone.


Interesting. Can you explain your rebase workflow a bit? I never thought of merge and rebase as overlapping that much, although I only use rebase to combine commits together.


It's pretty straightforward -- always rebase your feature branch on top of your upstream before you merge, ensuring that the merge is a fast-forward and your feature branch's history is at the end.

This also gives you a chance to fix up any conflicts before merging, instead of possibly having to merge twice.


I'd imagine that the gp is working in a collaborative environment and before pushing to the origin for a merge commit if the ancestors are different, that gp will instead rebate on whatever changes have been made in the origin repo first so that the repo maintainer can just do a fast forward merge.


"While we wait for the next major git release which will bring about some serious updates"

Is this referring to git 1.9? Are there any resources to learn what that will bring?


I think that's referring to git 2.0—I get regular warnings when pushing repos about the coming changes. This stack overflow question[1] (which was closed, unfortunately) points to these "what's cooking in git" posts [2], but as far as I can tell, there isn't a really nice walkthrough of the new features yet.

1: http://stackoverflow.com/questions/16308484/any-information-...

2:http://search.gmane.org/?query=%22what%27s+cooking+in+git.gi...


Thanks! As @chbrosso says, these aren't exactly easy to digest, but it's nice to have something to skim through. :-)


This refers to Git 2.0, that will bring breaking changes, like the default push mode. I don't know any other source than the weekly "what's cooking in Git" posts by Git maintainer on the mailing list, but it's fairly hard to read if you're not used to Git internals.


Yay, now I can stop doing "git fetch && git rebase -p origin/master" and go back to "git pull --rebase".


>HEAD has a new alias, instead of typing four capital letters you can say “@”

fwiw you've been able to type "head" for quite a while (forever?). TYPING IN ALL CAPS is a bit of a pain, I agree, but at least for me "head" is somewhere on the same level of typing-complexity as @, possibly easier.


Uh, where can you use "head"?

    $ git show head
    fatal: ambiguous argument 'head': unknown revision or path not in the working tree.
    Use '--' to separate paths from revisions, like this:
    'git <command> [<revision>...] -- [<file>...]'
    $ git show head --
    fatal: bad revision 'head'
    $ git version
    git version 1.8.4.2


most likely on case-insensitive file systems, like the default HFS+ on the Mac and NTFS on Windows.

EDIT: to expand, reference lookups are filysystem lookups:

    01:42:24 (gh-pages) [bpowers@fina myproject]$ strace git show HEAD 2>&1 | grep HEAD
    execve("/usr/bin/git", ["git", "show", "HEAD"], [/* 79 vars */]) = 0
    lstat(".git/HEAD", {st_mode=S_IFREG|0664, st_size=25, ...}) = 0
EDIT2: formatting


Aaah, good point, that's probably the cause. Yeah, this is on a Mac.


It's also definitely more transparent for a new user.

git rebase @~4 looks like Perl :)


Sigh, why use "@" for HEAD? The currently checked-out commit should be called ".", just like the current directory. Doesn't this make more sense? A Unix person thinks "." means "this one" or "current".

I only complain because in hg, "." is the equivalent to git's HEAD, and it's already difficult enough for git users to understand any system other than git after they spend time learning its idiosyncratic and inconsistent UI.


I've used unix pretty much my entire computing life and outside of dealing with files and directories I've NEVER thought of . as meaning anything but the directory entry I'm in.

Comparing git to hg here is bad, as pointed out previously, '.' really does mean something more akin to what unix has always meant. I'd argue hg is more in the wrong here with reusing '.' as having other meanings for something that could semantically refer to a directory under version control.

Bitch about gits interface all you want, but not using '.' is between you and me a crap argument if you're going to invoke the "unix person" gods.


Well, another problem with git's interface is that it allows interpreting an argument as a path or as a ref, most notably in "git checkout" which does a bunch of different unrelated things. If "git checkout ." couldn't be interpreted as a current directory, this problem doesn't exist. It's another case of a wildly inconsistent UI, the same command doing a bunch of unrelated things with the same syntax!


To be honest I don't see the issue at all here. Fundamentally the following all make sense to me intuitively:

    git checkout dir (lets assume cwd is in a git repo)
    cd dir && git checkout .
And for completeness: git checkout file

I don't buy the argument that something could be a path or a ref is bad in this case. It allows for both common cases simply. Want to update a single file/directory git checkout that file. Want to checkout a revision? Checkout the sha1hash.

Basically what I'm saying here, is reusing '.' is more of a problem for hg in that they're repurposing an already common idiom in unix.

If my arguments to why aren't convincing we'll have to disagree that this is a git "fault" or "inconsistent ui". I think of it as neither however.


This is not "completeness". These are fundamentally different things, like I explained in another comment. You even used a different verb for the file case, "update a file", which coincidentally matches closer the verb that hg uses, "update".

Here, this is a funnier way to explain why "git checkout" is such a weird command:

http://stevelosh.com/blog/2013/04/git-koans/#one-thing-well


I'm not sure where I used completeness in my argument prior?

Updating a file and updating a directory to me are effectively the same thing. The fact that git checkout can accept either a filename/directoryname, or a revision strikes me as no worse/better than the alternative in hg.

That is:

     git checkout file/directory
     git checkout revision
Is no better/worse than:

     hg revert file/directory
     hg update -r revision (or wherever . is actually used, i'll admit I've never used it in mercurial)
In fact in many ways the git methodology maps closer to how english verbs are overloaded as well. That is, we're checking out some file/directory named whatever back to its last known good value, or we're checking out everything to a specific revision (I'll omit every option possible for brevity). Its rather easily parsed and explained to humans. The mercurial versions to be honest seem more pedantic than intuitive. I have to use both and vastly prefer the git way in this regard. But to each his own.

The link is cute but I couldn't get past the first few paragraphs. It is trying a bit too hard to be funny/cute for my tastes. That said I looked at the listings of things that are messed up and sure I don't argue that git has an "intuitive" interface by any means in regards to listing tags/revisions. But this one point I don't see as its most glaring of issues. If its that big of an issue just do what I do and create a git alias in .gitconfig to call the arcane incantation of magick. Then it doesn't really matter how inconsistent it is.

Eh enough not working for me, tschuss!


There's nothing unrelated about git checkout, just your understanding of what it does. It "updates files in the working tree" to what is in the specified ref, or if you don't specify the ref, to what is in the index.

The fact that you think of those as two separate operations is because you are used to systems that treat them as such.


@ is the traditional identifier for "the adventurer" in roguelikes, possibly because it looks rather like a top-down view of indiana jones wearing a fedora.

roguelikes and unices grew up together, and there's a fair amount of intermingling of concepts and jargon.

...okay the reason is probably more like "'@' means 'you are here'", but I like this justification better because it's how it was explained to me at age ~7 when my dad was teaching me how to play nethack.


Haha, that's a cute explanation.

In hg, "@" is a special bookmark that gets checked out in new clones. There it means "this is where you probably want to be".


No, "git checkout ." already has meaning, you can't reuse . for a committish value.


Ah, so it's a problem with working around the existing awful UI. So, "git checkout" does four different things parsing all of which requires a lot of context: (1) switch branches (2) visit an arbitrary commit (3) revert a file (4) create a new branch.

And it's not as if "git checkout HEAD" makes any sense either, this is a no-op, since you can't be on anything but HEAD.


Actually (1) (2) and (3) are doing the exact same thing. You are retrieving (checking out... checkout) files from storage.

"git checkout HEAD" makes perfect sense in this case, cause you are checking out files from the commit pointed at by HEAD. Of course, checkout doesn't overwrite files in the working directory that are modified, unless you provide the -f option.

(4) is a convinience option and is more like "git branch <name> && git checkout <name>" Mercurial also does 'crazy stuff' like this. Like "hg pull --update" which is really just "hg pull && hg update".

I also don't understand what about "Detached HEAD" requires a lesson? Detached HEAD just means that the current HEAD doesn't isn't pointed at by a branch pointer. You have the same thing in Mercurial, it just doesn't warn about it. It's not really that big a deal in Mercurial either, as anonomous heads aren't in danger of being garbage collected.


> I also don't understand what about "Detached HEAD" requires a lesson?

git lectures you when you do this. That's the lesson.


Ah, right. That's only the first time though right? Don't really consider this as a negative.


The first time per repo, unless you disable it globally. It's a negative because if your UI is so complicated that you need to lecture your users, then you are doing something wrong. Your users should be able to use the software without getting lectured.


How do you twist this to be a result of complicated UI? Git gives a helpfull reminder that commits on a detached HEAD results in a anonomous branch. The only difference from say Mercurial, is that Git warns about this.

There is no difference in "git checkout <changeset" and "hg update <changeset>". NONE. WHAT. SO. EVER. Except that Git gives you a warning that commits on this changeset results in an anonomous branch. And Git is filled with helpfull texts like this. Git status tells you how to revert files. Git rebase tells you how to abort.

I personally find this as something positive, as I don't have to lookup documentation whenever I want to do something.


> There is no difference in "git checkout <changeset" and "hg update <changeset>". NONE. WHAT. SO. EVER.

There is, but you seem to be hellbent on ignoring it. And you're shouting.

The difference is that a commit made there is apparently "lost" in the git UI, because it's unreferenced. After 90 days, it's actually lost, or sooner if you do a casual "git gc". This is a problem that the user needs to understand and solve.

Commits in hg don't have to be referenced if you want to keep them. Hence committing at a past location in history doesn't require a lecture in hg. There is no problem to solve when you commit at an earlir commit on hg.


> There is, but you seem to be hellbent on ignoring it. And you're shouting.

There isn't. There is a difference in the way git and mercurial handles anonomous branches (git removes them after a while), which i've already said a couple of times. There is however, no difference in how checkout and update works.

And I'm not shouting, that would make people at this office stare, and I don't like them staring. I instead wrote in all caps as I've repeated the fact that the two commands does the exact same thing, but you seem to be hellbent on ignoring that simple fact.

The commit is not lost in the git UI. You can either use "git reflog" or "git fsck --lost-found" to retrieve anonomous heads. If you want to keep them around, give them a name and you're done.

If you really want to view all branches, even anonomous ones, like you can with hg log, you can use "git log --graph --decorate $(git rev-list -g --all)"

> After 90 days, it's actually lost, or sooner if you do a casual "git gc".

If the reflog contains the last 90 days (doesn't it default to 14 days?), that commit will remain for 90 days, unless you delete the reflog. A "git gc" doesn't change this. As the commit is still in the reflog, "git gc" can't remove it, becouse the commits are referenced by the reflog. As a side-note, git push internally calls git gc (last I checked).

> There is no problem to solve when you commit at an earlir commit on hg.

Actually, this creates a new head which means you either have to use a named branch or --force the push. In git you don't have to make this choice. You do however have to tell git that you want the changes to stick around by adding a reference (branch or tag) to that anonomous branch within 14 days (before the commits exit the reflog).

If you're just testing something out, and find out you don't really need that code, you leave it unreferenced, and it will be removed eventually. I prefer this to manually using "hg strip" whenever storage gets low.

Besides, if you create a head with work you want to keep around, why wouldn't you reference it?


(1) and (2) are the same. Branches are pointers to commits.


No, you can checkout any commit, not just a commit with a branch ref on it. It's a different thing because one of these require a detached HEAD lecture and the other one doesn't.


Detached just means that your HEAD is a commit, not a ref. There is no particular reason for git-commit to care if a commit is pointed to by a branch, some other sort of ref, is reachable from a commit that is pointed to by a branch or other sort of ref, or detached from everything at all. If you read the code, it is doing the same thing in either case, only differentiating between them to print more useful messages for the users benefit and make sure the user is doing what they really want to do in situations that may be destructive (without falling back on reflog). They really are the same thing.


> They really are the same thing.

They're only the same thing at a fundamental level, just like eventually all a computer is doing is moving bits around and all data is a bit stream. This ignores that hey are not treated as the same thing from the UI point of view. If they were, they would not require a lecture to understand how to recover a commit made on a detached HEAD, a lecture that git provides.


They are treated as the same thing at the UI level, hence your complaint. If they were treated as unrelated concepts then they would not both be serviced by git-checkout, despite sharing an underlying implementation.

They are the same thing both in implementation and UI.

The various consequences of detached commits and what happens when you try to modify the working tree when there are uncommitted changes are both concepts that permeate all of git. Handling these things in a graceful manner does not somehow mean that checking out branches or commits are fundamentally different concepts.

If you want an example of muddled UI, git-checkout -b is an infinitely better example. Creating a branch is conceptually unrelated to checking out a branch; git-checkout just provides the -b flag for convenience because typically you want to check out the branch after creating it. It would likely make more sense to provide a --checkout flag of some sort with git-branch, as creating the branch is the primary operation and checking it out is just a secondary convenience. That is a decent complaint.

If this is the sort of thing honestly that bothers you, then just make a git-changebranch alias for git-checkout, pretend that git-checkout can't do branches even though it can do arbitrary commits, and move on with your life.


> they would not require a lecture to understand how to recover a commit made on a detached HEAD, a lecture that git provides.

The only real difference here compared to Mercurial is that Mercurial doesn't issue a warning/lecture... In both cases you will have to know the changeset (or revision number in Mercurial) to get back to that anonomous branch (unless it's tip, but tip has its own issues). Git providing a warning for this is fair imho, as Git garbage collects anonomous branches. That, however, does not change the fact that checking out a random commit and a branch are the same thing in UI and implementation.


What's wrong with HEAD anyway? Do we really need to save 3 characters that badly?

git is already scary enough without stuff like

  git show @~3^2^


Not just three chars, but three capital chars. I remapped caps lock to be the ctrl key, and didn't put caps lock to another key because I rarely use it anyway. For standard key mappings one still needs to hit caps lock or hold shift while typing. Is the change revolutionary? Will it totally revitalize my workflow? No, but it's a nice-to-have.

As for your scary example, if you don't like using "@" as an alias for HEAD, don't do it. <shrug>


I thought HEAD was too hard to type and have been using $h (zsh alias) for HEAD for a few years.

I'll be glad to save another 1 character with 1.8.5.


I would think that introducing an alias for a pointer which could be confused for a filesystem path would be more confusing than "@".


Commits are not directories. I don't know why you would want to muddle the two concepts, but doing so would make git's UI more inconsistent and would break the principle of least suprise moreso than it is right now.

Just because Mercurial does it, doesn't mean that it is thought out or makes sense.


Of course commits are not directories, but it's a good UI analogy. In any context where hg accepts a revision number, it doesn't also accept a path, so there's no possible ambiguity, but git's UI requires lots of ambiguous parsing. An example of this is how many git users get in the habit of cargo-culting "--" to separate paths from options.


> Of course commits are not directories, but it's a good UI analogy

Why is it a good analogy?

Directories are doubly linked trees^, each directory containing references to all child directories and a single parent directory, with some spacial implications (it makes sense to talk about directories being in or containing other directories). Commits make up singly linked DAGs, with each commit only having references to potentially many parent commits but no child commits, with strong temporal implications (it makes sense to talk about commits preceding or following other commits, with distant simultaneity frequently making an appearance). These two concepts really have very little overlap, other than both being graphs.

Even if we just decided that "HEAD"->"." anyway, we can't keep that up for long. What the hell would ".." become? "HEAD^" might seem like the obvious answer, but you would be left to figure out what the "directory equivalent" of "HEAD^2" is. "HEAD~2^2"? The Unix relative directory language is not expressive enough for git, even if you want to force the analogy for some reason.

Directories could be a good analogy for git's tree objects, but you operate on those through the high-level porcelain.

There are certainly problems with git's porcelain, and there are obvious improvements to be made, but this is not one of them.

^barring symbolic links, which we can ignore unless we want to open a whole 'Unix Haters Handbook'-esque can of worms...


>> Of course commits are not directories, but it's a good UI analogy

> Why is it a good analogy?

Because it has associations "here" and "now" and "current". It doesn't fit all of the other meanings of "current directory", but it does fit the the "nowness" of it (just like git's trees don't have clorophyll or bark, that doesn't mean that calling them trees is a bad analogy).


This is absurd; VCR remote controls have concepts that can be loosely mapped to "here"/"now"/"current" (hell, VCRs are better for this since they at least relate to recording state and the temporal relationship of different pieces of data!), but you're not going to get much utility out of designing your VCS UI with a VCR in mind.

Git trees are called trees because they are directed graphs without cycles. Directed graphs without cycles are called "trees" because they look mildly like them, but the name that you give them is of little consequence. That name does not really represent any sort of "UI".


There are often-used commands (log, diff, checkout come to mind) that can either take commits or file names on the command line, and most disambiguate to commits. If you introduce . as an alias for HEAD, you either have to change the disambiguation rule, or you must change the meaning of commonly used idioms. Both approaches break backwards compatibility.


Does anyone know if Git 2 is breaking repository compatibility or just command line compatibility?

I remember Linus wanted to add a "generation" counter (empty commit=0, other commits=1+max(parent commits)) to speed up some merges; And I know I would love changes that would make big files properly supported (e.g. the way bup efficiently handles huge files inside a git repo).


Only command-line, and only in well-announced ways, such as push.default=simple (which you can set now) and "git add -u" run from a subdirectory being applied globally.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: